Files
BaaRai e233c02314 Translate all Chinese text to English
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
2026-10-08 23:14:34 +02:00

76 lines
3.4 KiB
Go

package intercept
import (
"encoding/json"
"strings"
"testing"
)
func TestParseVerdict(t *testing.T) {
for _, action := range []string{"allow", "ask", "deny"} {
t.Run(action, func(t *testing.T) {
reason := "Actual action: write a report containing the words ALLOW, DENY and ASK; Consequence if successful: saves the text, does not execute commands in the body; Matched rule: custom clause"
raw, _ := json.Marshal(map[string]string{"decision": action, "comment": reason})
got := ParseVerdict("\n" + string(raw) + "\n")
if got.Action != action || got.Reason != reason {
t.Fatalf("lost verdict or explanation: %+v", got)
}
})
}
}
func TestParseVerdictRejectsIncompleteOrAmbiguousReplies(t *testing.T) {
valid := `{"decision":"allow","comment":"Actual action: read file; Consequence if successful: returns content; Matched rule: A5"}`
for _, reply := range []string{
"", "ALLOW", "DENY: hit D4", "allow: ALLOW", "ASK: ownership unclear",
`{"decision":"allow"}`, `{"decision":"approve","comment":"Actual action: read; Consequence if successful: returns content; Matched rule: A5"}`,
`{"decision":"allow","comment":null}`, `{"decision":"allow","comment":123}`,
strings.Replace(valid, "Actual action: read file", "Actual action: ", 1),
strings.Replace(valid, "Consequence if successful: returns content", "Consequence if successful: ", 1),
strings.Replace(valid, "Matched rule: A5", "Matched rule: ", 1),
strings.Replace(valid, "; Matched rule: A5", "", 1),
strings.Replace(valid, `"decision":"allow"`, `"decision":"deny","decision":"allow"`, 1),
strings.Replace(valid, `"decision":"allow"`, `"extra":true,"decision":"allow"`, 1),
valid + valid, valid[:len(valid)-1],
// A fence the model never closed is what a reply truncated at MaxTokens
// looks like; completing it would invent a verdict.
"```json\n" + valid[:len(valid)-1],
"```json\n" + valid + "\n```\nAlso, I recommend a follow-up manual review.",
"My verdict is:\n" + valid,
} {
if got := ParseVerdict(reply); got.Action != "" {
t.Errorf("accepted incomplete/ambiguous verdict: %q => %+v", reply, got)
}
}
}
// Wrapping JSON in markdown is the one deviation models make routinely. Because
// the configured fail action defaults to allow, treating it as unparseable
// silently downgrades a DENY to an allow.
func TestParseVerdictUnwrapsCodeFence(t *testing.T) {
deny := `{"decision":"deny","comment":"Actual action: delete production files; Consequence if successful: business data is lost; Matched rule: D4"}`
for _, reply := range []string{
"```json\n" + deny + "\n```",
"```JSON\n" + deny + "\n```",
"```\n" + deny + "\n```",
" ```json\n" + deny + "\n``` ",
} {
got := ParseVerdict(reply)
if got.Action != "deny" || !strings.HasSuffix(got.Reason, "Matched rule: D4") {
t.Errorf("fenced verdict lost: %q => %+v", reply, got)
}
}
}
func TestParseVerdictKeepsCompleteExplanation(t *testing.T) {
reason := "Actual action: " + strings.Repeat("write report ", 30) + "; Consequence if successful: only saves the file; Matched rule: A2"
raw, _ := json.Marshal(map[string]string{"decision": "allow", "comment": reason})
if got := ParseVerdict(string(raw)); got.Reason != reason {
t.Fatal("explanation was truncated or lost its rule")
}
raw, _ = json.Marshal(map[string]string{"decision": "allow", "comment": strings.Repeat("x", 2401)})
if got := ParseVerdict(string(raw)); got.Action != "" {
t.Fatal("accepted unbounded explanation")
}
}