mirror of
https://github.com/BaaRai/ARTEX.git
synced 2026-11-13 11:40:53 +08:00
Comments, doc comments, UI labels, log and error messages, LLM prompt strings, markdown docs, test data and assertions. Identifiers, enum keys, routes, placeholders and code structure are unchanged. Notes: - docs/漏洞流量证据.md renamed to docs/vulnerability-traffic-evidence.md - tests that exercised CJK specific handling (GBK zip names, Chinese full text search) now use ASCII equivalents; go build passes and the test suite matches the pre translation baseline (remaining failures need a PostgreSQL DSN and fail identically on the original) - frontend mention syntax now uses English kinds (finding, asset, ...) matching the backend, chat mentions tests pass
101 lines
3.7 KiB
Go
101 lines
3.7 KiB
Go
package notify
|
|
|
|
import (
|
|
"net/url"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// The signature reference values were computed independently with OpenSSL, not
|
|
// generated by this package's own implementation — otherwise they could only
|
|
// prove "the code has not changed", not "the algorithm is right".
|
|
//
|
|
// TS=1700000000000, SECRET=SECtest123
|
|
// DingTalk: printf '%s\n%s' "$TS" "$SECRET" | openssl dgst -sha256 -hmac "$SECRET" -binary | openssl base64 -A
|
|
// -> w3RMHXzixTMdzr8OHJUmVLS4IoPJVdu+Ut1LE48MePE=
|
|
// Feishu: printf '' | openssl dgst -sha256 -hmac "$(printf '%s\n%s' "$TS" "$SECRET")" -binary | openssl base64 -A
|
|
// -> Hd4xFWQU6R6ad4nzy4ETIznzlqebqH7xcTFVmONTudo=
|
|
const (
|
|
signTestTSMillis = int64(1700000000000)
|
|
signTestSecret = "SECtest123"
|
|
dingTalkExpected = "w3RMHXzixTMdzr8OHJUmVLS4IoPJVdu+Ut1LE48MePE="
|
|
feishuExpected = "Hd4xFWQU6R6ad4nzy4ETIznzlqebqH7xcTFVmONTudo="
|
|
)
|
|
|
|
func TestDingTalkSignMatchesReference(t *testing.T) {
|
|
got, err := dingTalkSignedURL("https://oapi.dingtalk.com/robot/send?access_token=tok", signTestSecret, time.UnixMilli(signTestTSMillis))
|
|
if err != nil {
|
|
t.Fatalf("signing failed: %v", err)
|
|
}
|
|
u, err := url.Parse(got)
|
|
if err != nil {
|
|
t.Fatalf("the produced address cannot be parsed: %v", err)
|
|
}
|
|
q := u.Query()
|
|
if q.Get("sign") != dingTalkExpected {
|
|
t.Errorf("signature mismatch\nwant %s\ngot %s", dingTalkExpected, q.Get("sign"))
|
|
}
|
|
if q.Get("timestamp") != "1700000000000" {
|
|
t.Errorf("the timestamp should be in milliseconds and carried as-is, got %q", q.Get("timestamp"))
|
|
}
|
|
// The original query parameter (access_token) must not be overwritten by the
|
|
// signature.
|
|
if q.Get("access_token") != "tok" {
|
|
t.Errorf("the original query parameter was lost, got %q", q.Get("access_token"))
|
|
}
|
|
}
|
|
|
|
func TestFeishuSignMatchesReference(t *testing.T) {
|
|
got := feishuSign("1700000000000", signTestSecret)
|
|
if got != feishuExpected {
|
|
t.Errorf("signature mismatch\nwant %s\ngot %s", feishuExpected, got)
|
|
}
|
|
}
|
|
|
|
// TestSignAlgorithmsDiffer locks the difference between the two platforms'
|
|
// algorithms. They happen to be mirror images of each other's parameter order
|
|
// (DingTalk key=secret, Feishu key=string-to-sign), and copying from the other
|
|
// platform always fails verification; this case ensures a future refactor does
|
|
// not merge the two into one function.
|
|
func TestSignAlgorithmsDiffer(t *testing.T) {
|
|
ts := "1700000000000"
|
|
dingURL, err := dingTalkSignedURL("https://example.com/hook", signTestSecret, time.UnixMilli(signTestTSMillis))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
dq, _ := url.Parse(dingURL)
|
|
if dq.Query().Get("sign") == feishuSign(ts, signTestSecret) {
|
|
t.Fatal("DingTalk and Feishu signatures are identical, which means one of the two algorithm implementations is wrong")
|
|
}
|
|
}
|
|
|
|
func TestDingTalkNoSecretLeavesURLUntouched(t *testing.T) {
|
|
// A bot without signing enabled: the timestamp/sign parameters must not be
|
|
// added out of thin air.
|
|
const hook = "https://oapi.dingtalk.com/robot/send?access_token=tok"
|
|
got, err := dingTalkSignedURL(hook, "", time.UnixMilli(signTestTSMillis))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got != hook {
|
|
t.Fatalf("the address should not change when no secret is configured, got %q", got)
|
|
}
|
|
}
|
|
|
|
func TestValidateHTTPURL(t *testing.T) {
|
|
ok := []string{"https://example.com/hook", "http://10.0.0.1:8080/x?y=1"}
|
|
for _, s := range ok {
|
|
if err := validateHTTPURL(s); err != nil {
|
|
t.Errorf("%q should be accepted: %v", s, err)
|
|
}
|
|
}
|
|
// file:// and the like must not be let through — http.Client's handling of
|
|
// them is beyond the expected scope.
|
|
bad := []string{"", "file:///etc/passwd", "ftp://example.com", "https://", "gopher://x"}
|
|
for _, s := range bad {
|
|
if err := validateHTTPURL(s); err == nil {
|
|
t.Errorf("%q should be rejected", s)
|
|
}
|
|
}
|
|
}
|