From ee31387aa1287b65bcb631e72fafa1a85b3b2a1d Mon Sep 17 00:00:00 2001 From: viettranx Date: Tue, 10 Mar 2026 23:11:02 +0700 Subject: [PATCH] fix(security): disable config leak detection to prevent false positives StripConfigLeak was blocking legitimate responses when predefined agents mentioned SOUL.md/IDENTITY.md/AGENTS.md in architecture explanations. Improved detection logic to exclude code blocks but disabled the gate entirely for now until a more robust approach is designed. --- internal/agent/loop.go | 5 +++-- internal/agent/sanitize.go | 23 +++++++++++++++++---- internal/agent/sanitize_config_leak_test.go | 21 +++++++++++++++++++ 3 files changed, 43 insertions(+), 6 deletions(-) diff --git a/internal/agent/loop.go b/internal/agent/loop.go index d127aafc..b4f08972 100644 --- a/internal/agent/loop.go +++ b/internal/agent/loop.go @@ -887,8 +887,9 @@ func (l *Loop) runLoop(ctx context.Context, req RunRequest) (*RunResult, error) // 4. Full sanitization pipeline (matching TS extractAssistantText + sanitizeUserFacingText) finalContent = SanitizeAssistantContent(finalContent) - // 4b. Config leak detection (predefined agents only) - finalContent = StripConfigLeak(finalContent, l.agentType) + // 4b. Config leak detection — disabled: too many false positives + // (e.g. agent explaining public architecture mentioning SOUL.md etc.) + // finalContent = StripConfigLeak(finalContent, l.agentType) // 5. Handle NO_REPLY: save to session for context but mark as silent. // Matching TS: NO_REPLY is saved (via resolveSilentReplyFallbackText) but diff --git a/internal/agent/sanitize.go b/internal/agent/sanitize.go index 7a3ba59d..57be7b87 100644 --- a/internal/agent/sanitize.go +++ b/internal/agent/sanitize.go @@ -322,22 +322,37 @@ var configLeakFileNames = []string{ "internal_config", "system prompt", } +// Patterns to strip markdown code from content before config leak detection. +// Mentions inside code blocks/inline code are typically architecture docs, not leaks. +var fencedCodeBlockPattern = regexp.MustCompile("(?s)```[^`]*```") +var inlineCodePattern = regexp.MustCompile("`[^`\n]+`") + +// stripMarkdownCode removes fenced code blocks and inline code from text. +func stripMarkdownCode(s string) string { + s = fencedCodeBlockPattern.ReplaceAllString(s, "") + s = inlineCodePattern.ReplaceAllString(s, "") + return s +} + // StripConfigLeak detects when a predefined agent dumps its internal configuration // (e.g. referencing SOUL.md, AGENTS.md, IDENTITY.md) and replaces the entire // response with a friendly decline. // // Only active for predefined agents. Single-gate detection: -// 3+ distinct internal file names mentioned → replace entire response. -// A predefined agent legitimately referencing 3+ internal config files in one -// response is essentially impossible, so this has no false-positive risk. +// 3+ distinct internal file names mentioned in plain text → replace entire response. +// Mentions inside markdown code blocks and inline code are excluded from counting, +// as they typically appear in architecture explanations rather than actual leaks. func StripConfigLeak(content, agentType string) string { if agentType != "predefined" || content == "" { return content } + // Count hits only in plain text (outside code blocks/inline code) + plain := stripMarkdownCode(content) + hits := 0 for _, name := range configLeakFileNames { - if strings.Contains(content, name) { + if strings.Contains(plain, name) { hits++ } } diff --git a/internal/agent/sanitize_config_leak_test.go b/internal/agent/sanitize_config_leak_test.go index 67d05b13..05e52d13 100644 --- a/internal/agent/sanitize_config_leak_test.go +++ b/internal/agent/sanitize_config_leak_test.go @@ -75,6 +75,27 @@ Có gì anh hỏi thêm nhé!`, content: "", agentType: "predefined", }, + { + name: "predefined - config names in fenced code block (architecture docs)", + content: "Đây là cấu trúc hệ thống:\n\n```\nagents/\n├── SOUL.md\n├── IDENTITY.md\n├── AGENTS.md\n├── BOOTSTRAP.md\n└── TOOLS.md\n```\n\nAnh cần gì thêm không?", + agentType: "predefined", + }, + { + name: "predefined - config names in inline code", + content: "Mỗi agent có các file `SOUL.md`, `IDENTITY.md`, `AGENTS.md` để cấu hình.", + agentType: "predefined", + }, + { + name: "predefined - mixed: code block + plain text below threshold", + content: "Hệ thống dùng:\n\n```\nSOUL.md\nIDENTITY.md\nAGENTS.md\nBOOTSTRAP.md\n```\n\nEm đã update SOUL.md rồi.", + agentType: "predefined", + }, + { + name: "predefined - plain text leak still triggers", + content: "Em load SOUL.md, IDENTITY.md, AGENTS.md vào context.", + agentType: "predefined", + want: declineMsg, + }, } for _, tt := range tests {