From e0793debc387b5670b2653a835d00a31264cf71e Mon Sep 17 00:00:00 2001 From: AskaEth Date: Mon, 26 Oct 2026 21:55:25 +0800 Subject: [PATCH] fix(restrict): redact sensitive param summary before it reaches audit/panel --- internal/capability/restrict/sensitive.go | 13 ++++++- .../capability/restrict/sensitive_test.go | 38 +++++++++++++++++++ 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/internal/capability/restrict/sensitive.go b/internal/capability/restrict/sensitive.go index 9a469bb..79f159a 100644 --- a/internal/capability/restrict/sensitive.go +++ b/internal/capability/restrict/sensitive.go @@ -3,6 +3,8 @@ package restrict import ( "strings" "unicode/utf8" + + "git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/trace" ) // SensitiveOp 是 design/11 §4「敏感清单」里的操作类别。它只用于审计与前端 @@ -45,7 +47,8 @@ var SensitiveOps = []SensitiveOp{ } // sensitiveSummaryRunes 截断动作摘要:代审小字与审计只需一眼看出「想干什么」, -// 同时限制进入前端/日志的文本块大小(也降低把长参数里的秘密整段搬进日志的机会)。 +// 同时限制进入前端/日志的文本块大小。它只管**长度** —— 秘密由 summarize 里的 +// trace.RedactText 负责打码,不靠截断「顺手甩掉」。 const sensitiveSummaryRunes = 80 // SensitiveCall 判定一次工具调用是否属敏感操作(design/11 §4)。 @@ -135,8 +138,14 @@ func httpCall(args map[string]any) (SensitiveOp, string, bool) { // summarize 拼接「动作 + 目标」摘要并按 sensitiveSummaryRunes 截断(不切开 // 多字节字符);目标为空时只返回动作。 +// +// 目标先过 trace.RedactText 打码:这个摘要会进审计与前端小字两个读面,而 +// terminal 的 command / http 的 url 里常常就带着凭据(`curl -H 'api_key=…'`、 +// `?token=…`),截断并不能让它们变得安全。RedactText 自身已按同一档(80 rune) +// 截断,后面的 clipRunes 保留是为了让长度上限继续由本包的 +// sensitiveSummaryRunes 掌握 —— 两处同档,叠加不改变结果。 func summarize(action, target string) string { - target = strings.TrimSpace(target) + target = trace.RedactText(strings.TrimSpace(target)) if target == "" { return action } diff --git a/internal/capability/restrict/sensitive_test.go b/internal/capability/restrict/sensitive_test.go index a26fef4..1cd13ff 100644 --- a/internal/capability/restrict/sensitive_test.go +++ b/internal/capability/restrict/sensitive_test.go @@ -77,3 +77,41 @@ func TestSensitiveCall_SummaryIsClipped(t *testing.T) { t.Fatalf("被截断的摘要应以省略号结尾: %q", detail) } } + +// TestSensitiveCall_SummaryIsRedacted 锁定「参数摘要进审计与前端小字之前必须 +// 打码」:terminal 的 command、http 的 url 里常常就带着凭据,截断不是脱敏。 +func TestSensitiveCall_SummaryIsRedacted(t *testing.T) { + t.Parallel() + const secret = "sk-live-9f8e7d6c5b4a3210" + cases := []struct { + name string + args map[string]any + start string // 摘要应保留的开头(打码不能把摘要打没) + }{ + { + name: "terminal", + args: map[string]any{"command": "curl -H 'api_key=" + secret + "' https://x"}, + start: "terminal ", + }, + { + name: "http", + args: map[string]any{"method": "POST", "url": "https://x/hook?token=" + secret}, + start: "http POST ", + }, + } + for _, tc := range cases { + _, detail, sensitive := SensitiveCall(tc.name, tc.args) + if !sensitive { + t.Fatalf("%s 应判定为敏感", tc.name) + } + if strings.Contains(detail, secret) { + t.Fatalf("%s 摘要泄露了凭据原文: %q", tc.name, detail) + } + if !strings.Contains(detail, "[redacted]") { + t.Fatalf("%s 摘要应带打码标记: %q", tc.name, detail) + } + if !strings.HasPrefix(detail, tc.start) { + t.Fatalf("%s 摘要应以 %q 开头(动作不能被一起打掉): %q", tc.name, tc.start, detail) + } + } +}