Skip to content

fix(a2a): redact structured error metadata - #106

Open
XnLemon wants to merge 130 commits into
mainfrom
codex/phase2-a2a-error-redaction
Open

fix(a2a): redact structured error metadata#106
XnLemon wants to merge 130 commits into
mainfrom
codex/phase2-a2a-error-redaction

Conversation

@XnLemon

@XnLemon XnLemon commented Jul 11, 2026

Copy link
Copy Markdown
Owner

Objective

Close the next Phase2 cross-protocol redaction gap by preventing A2A structured error metadata and task error message bodies from exposing sensitive runtime details.

Completed Changes

  • Added a fail-closed A2A error text redaction helper backed by the platform redactor.
  • Redacted ResponseError metadata fields written through WithResponseErrorMetadata, including Type, Message, Code, and Param.
  • Redacted metadata and fallback error fields reconstructed through ResponseErrorFromMetadata.
  • Redacted A2A structured task error Status.Message.Parts so the response body cannot bypass redacted metadata.
  • Added regression tests for metadata write/read redaction, fallback metadata redaction, and structured task error message part redaction.

Validation

  • go test ./internal/a2a -count=1
  • go test ./server/a2a -count=1 -run 'TestBuildTaskErrorMetadata|TestBuildTaskErrorMessage|TestMessageProcessor_ProcessMessage_StructuredTaskError|TestMessageProcessor_ProcessBatchStreamingEvents_StructuredTaskError|TestMessageProcessor_ProcessBatchStreamingEvents_StopAgentStructuredTaskError|Test.*ResponseError|Test.*StructuredError|Test.*ErrorMetadata|Test.*FinalArtifact|Test.*FailedTask'
  • go test ./agent/a2aagent -count=1 -run 'TestDefaultA2AEventConverter_ConvertStreamingToEvents_FailedStatus|TestDefaultA2AEventConverter_ConvertStreamingToEvents_CompletedStatusStructuredError|TestDefaultA2AEventConverter_ConvertStreamingToEvents_FinalArtifactStructuredError|TestDefaultA2AEventConverter_ConvertToEvents_FailedTask|TestA2AAgent'
  • go vet ./internal/a2a ./server/a2a ./agent/a2aagent
  • git diff --check
  • Subagent open-code-review loop completed with 0 High / 0 Medium / 0 Low after fixing the A2A Status.Message.Parts bypass.

Known Risks / Limitations

  • This increment targets A2A structured error metadata and task error message bodies; broader internal diagnostic traces are intentionally left unchanged.
  • Redacted error metadata no longer preserves raw provider/task details when those fields contain secrets, by design.

Follow-up Work

  • Continue Phase2 closure by scanning remaining flow/processor model-visible error event paths outside A2A metadata.

XnLemon added 30 commits July 8, 2026 11:47
@XnLemon
XnLemon force-pushed the codex/phase2-gateway-server-error-redaction branch from 292d8b8 to b8a1f25 Compare July 11, 2026 13:39
@XnLemon
XnLemon force-pushed the codex/phase2-a2a-error-redaction branch from 625262a to 7b0e57c Compare July 11, 2026 13:39
* fix(flow): redact processor error events

* test(flow): capture callback error events before exit

* fix(platform): preserve adjacent redacted fields

---------

Co-authored-by: XnLemon <xianingawa@gmail.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
agent/claudecode/claude_agent.go (1)

104-113: 🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

Add api-key to the sensitive marker list (agent/claudecode/claude_agent.go:217-225).
api_key/apikey are covered, but common header forms like X-Api-Key: still bypass the hook scrubber.

中文

api-key 加入敏感标记列表(agent/claudecode/claude_agent.go:217-225)。
api_key / apikey 已覆盖,但像 X-Api-Key: 这类常见头格式仍会绕过 hook 脱敏。

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@agent/claudecode/claude_agent.go` around lines 104 - 113, Update the
sensitive marker list used by the raw output hook scrubber in the relevant
handler to include “api-key” alongside the existing “api_key” and “apikey”
markers, preserving the current matching behavior so header forms such as
“X-Api-Key:” are redacted.
🧹 Nitpick comments (2)
internal/flow/error_redaction.go (1)

13-28: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Minor duplication with internal/a2a/error_redaction.go.

RedactErrorText here and redactErrorText in internal/a2a/error_redaction.go are functionally identical fail-closed wrappers around platform.NewRedactor().Redact. Given the two live in different packages (a2a-private vs. flow-public) this is a reasonable boundary, but if a third consumer emerges, consider hoisting the wrapper into platform itself to avoid a third copy.

中文 本文件的 `RedactErrorText` 与 `internal/a2a/error_redaction.go` 中的 `redactErrorText` 在功能上完全一致,都是对 `platform.NewRedactor().Redact` 的 fail-closed 包装。鉴于两者分属不同包(a2a 私有 vs. flow 公开),当前的边界划分是合理的;但如果未来出现第三个使用方,建议将该包装函数提升到 `platform` 包中,避免第三份重复代码。
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/flow/error_redaction.go` around lines 13 - 28, The current
duplication is acceptable across the separate a2a-private and flow-public
package boundaries; make no change to RedactError or RedactErrorText. Only if a
third consumer is introduced, move the shared fail-closed redaction wrapper into
platform and update both existing callers to reuse it.
agent/claudecode/claude_agent.go (1)

217-235: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Consider adding hyphenated api-key marker.

Current markers cover api_key/apikey but not the hyphenated api-key form, which is a common HTTP header variant (e.g. X-Api-Key:). Worth adding for parity with the other marker forms already covered.

中文 当前标记覆盖了 `api_key`/`apikey`,但未覆盖带连字符的 `api-key` 形式(例如常见的 `X-Api-Key:` 头)。建议补充该变体,与其他标记形式保持一致覆盖。
🔒️ Proposed addition
 	sensitiveMarkers := [][]byte{
 		[]byte("authorization:"),
 		[]byte("authorization="),
 		[]byte("api_key"),
 		[]byte("apikey"),
+		[]byte("api-key"),
 		[]byte("cookie:"),
 		[]byte("cookie="),
 	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@agent/claudecode/claude_agent.go` around lines 217 - 235, Update
sensitiveCLIOutputIndex by adding the hyphenated “api-key” marker to
sensitiveMarkers, preserving the existing case-insensitive matching and
earliest-marker selection behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@platform/redaction.go`:
- Around line 20-23: Update the fallback Authorization pattern in the redaction
rules so it masks the entire header value through the line ending, including
unrecognized multi-segment schemes and fields such as Credential and Signature.
Preserve the existing specialized patterns, and add a regression test covering
an unrecognized multi-part Authorization value.

---

Outside diff comments:
In `@agent/claudecode/claude_agent.go`:
- Around line 104-113: Update the sensitive marker list used by the raw output
hook scrubber in the relevant handler to include “api-key” alongside the
existing “api_key” and “apikey” markers, preserving the current matching
behavior so header forms such as “X-Api-Key:” are redacted.

---

Nitpick comments:
In `@agent/claudecode/claude_agent.go`:
- Around line 217-235: Update sensitiveCLIOutputIndex by adding the hyphenated
“api-key” marker to sensitiveMarkers, preserving the existing case-insensitive
matching and earliest-marker selection behavior.

In `@internal/flow/error_redaction.go`:
- Around line 13-28: The current duplication is acceptable across the separate
a2a-private and flow-public package boundaries; make no change to RedactError or
RedactErrorText. Only if a third consumer is introduced, move the shared
fail-closed redaction wrapper into platform and update both existing callers to
reuse it.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: a149c175-9387-43ce-b3dd-7db075a72e1f

📥 Commits

Reviewing files that changed from the base of the PR and between b8a1f25 and 229c572.

📒 Files selected for processing (18)
  • agent/claudecode/claude_agent.go
  • internal/a2a/a2a.go
  • internal/a2a/a2a_test.go
  • internal/a2a/error_redaction.go
  • internal/flow/error_redaction.go
  • internal/flow/error_redaction_test.go
  • internal/flow/llmflow/llmflow.go
  • internal/flow/llmflow/llmflow_test.go
  • internal/flow/processor/content.go
  • internal/flow/processor/content_test.go
  • internal/flow/processor/functioncall.go
  • internal/flow/processor/functioncall_test.go
  • internal/flow/processor/transfer.go
  • internal/flow/processor/transfer_test.go
  • platform/redaction.go
  • platform/types_test.go
  • server/a2a/server.go
  • server/a2a/server_test.go

Comment thread platform/redaction.go Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@platform/redaction.go`:
- Around line 69-78: Update the fallback logic in the Authorization handling
block to skip re-redaction only when strings.TrimSpace(match[idx+1:]) exactly
equals "****", not when it merely has that prefix. Preserve re-redaction for
values with trailing Credential, Signature, or other fields, and add a
regression test covering a known Bearer or Basic scheme with trailing fields.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 468ed611-fa8c-4e11-b79a-3856c908c9e7

📥 Commits

Reviewing files that changed from the base of the PR and between 229c572 and da9b182.

📒 Files selected for processing (4)
  • agent/claudecode/claude_agent.go
  • agent/claudecode/claude_agent_test.go
  • platform/redaction.go
  • platform/types_test.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • platform/types_test.go
  • agent/claudecode/claude_agent.go

Comment thread platform/redaction.go
Comment on lines +69 to +78
if strings.HasPrefix(strings.TrimSpace(lower), "authorization") {
if idx := strings.IndexAny(match, ":="); idx >= 0 {
// Specialized patterns redact known schemes first. Preserve the
// remaining line so adjacent sensitive fields are handled separately.
if strings.HasPrefix(strings.TrimSpace(match[idx+1:]), "****") {
return match
}
if match[idx] == ':' {
return match[:idx+1] + " ****"
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Do not preserve a redacted prefix when the Authorization value has a suffix.

For Authorization: Bearer raw-token Credential=AKIA..., the specialized matcher first produces Authorization: **** Credential=AKIA.... The fallback then returns that whole match unchanged because the remainder merely starts with ****, leaking Credential/Signature fields. Only skip re-redaction when the trimmed remainder is exactly ****, and add a regression test for a known Bearer/Basic scheme with trailing fields.

中文

当 Authorization 值仍带有后缀时,不要保留已脱敏前缀。

对于 Authorization: Bearer raw-token Credential=AKIA...,专用匹配器会先生成 Authorization: **** Credential=AKIA...。随后兜底规则因剩余内容只是以 **** 开头而原样返回整行,导致 Credential/Signature 字段泄露。只有当去空白后的剩余内容恰好等于 **** 时才应跳过再次脱敏,并补充已知 Bearer/Basic 方案带尾部字段的回归测试。

This matters because the same redacted text is surfaced through the A2A and flow error paths. As per path instructions, this Go review prioritizes semantic and security compatibility and is bilingual.

Proposed fix
-			if strings.HasPrefix(strings.TrimSpace(match[idx+1:]), "****") {
+			if strings.TrimSpace(match[idx+1:]) == "****" {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if strings.HasPrefix(strings.TrimSpace(lower), "authorization") {
if idx := strings.IndexAny(match, ":="); idx >= 0 {
// Specialized patterns redact known schemes first. Preserve the
// remaining line so adjacent sensitive fields are handled separately.
if strings.HasPrefix(strings.TrimSpace(match[idx+1:]), "****") {
return match
}
if match[idx] == ':' {
return match[:idx+1] + " ****"
}
if idx := strings.IndexAny(match, ":="); idx >= 0 {
// Specialized patterns redact known schemes first. Preserve the
// remaining line so adjacent sensitive fields are handled separately.
if strings.TrimSpace(match[idx+1:]) == "****" {
return match
}
if match[idx] == ':' {
return match[:idx+1] + " ****"
}
Suggested change
if strings.HasPrefix(strings.TrimSpace(lower), "authorization") {
if idx := strings.IndexAny(match, ":="); idx >= 0 {
// Specialized patterns redact known schemes first. Preserve the
// remaining line so adjacent sensitive fields are handled separately.
if strings.HasPrefix(strings.TrimSpace(match[idx+1:]), "****") {
return match
}
if match[idx] == ':' {
return match[:idx+1] + " ****"
}
if idx := strings.IndexAny(match, ":="); idx >= 0 {
// Specialized patterns redact known schemes first. Preserve the
// remaining line so adjacent sensitive fields are handled separately.
if strings.TrimSpace(match[idx+1:]) == "****" {
return match
}
if match[idx] == ':' {
return match[:idx+1] + " ****"
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@platform/redaction.go` around lines 69 - 78, Update the fallback logic in the
Authorization handling block to skip re-redaction only when
strings.TrimSpace(match[idx+1:]) exactly equals "****", not when it merely has
that prefix. Preserve re-redaction for values with trailing Credential,
Signature, or other fields, and add a regression test covering a known Bearer or
Basic scheme with trailing fields.

Source: Path instructions

@XnLemon
XnLemon changed the base branch from codex/phase2-gateway-server-error-redaction to main July 13, 2026 02:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants