Skip to content

[json] Blocked command output echoes secret-like arguments after audit redaction #24

Description

@jamiesun

问题

当前 --json 模式下,安全检查拦截危险命令时,stdout 的结构化结果仍会在 command 字段和 error 文本中原样回显命令参数。#20 已修复 audit JSONL 对 quoted secret-like 参数的脱敏,但同一条 blocked-command 路径的 CLI JSON 输出仍可能把这些参数写到调用方日志中。

证据

  • Commit/PR: be599110685892848b30a1d5400a40b5441f268f / test: cover state and audit contracts #23;相关修复 PR fix(audit): redact quoted secret values #20
  • 文件路径: internal/app/app.go, internal/app/audit.go, internal/app/agentmode_test.go
  • 相关 diff / CI / 测试信号:
    • internal/app/app.goemitCommandJSON 直接设置 Command: config.Command,并在 execErr != nil 时设置 result.Error = execErr.Error()
    • internal/app/audit.gorecordFailure / refresh 会通过 redactError / redactSensitiveText 脱敏 audit 事件;这说明 audit 面已经修复,但 JSON stdout 面没有复用同一脱敏逻辑。
    • internal/app/agentmode_test.go 新增/保留的 TestRun_BlockedCommandShortCircuits 只断言 error_kind=blockedexit_code=-1success=false,没有覆盖 sensitive argument redaction。
    • 本地在 origin/main 临时 worktree 上运行 go test ./... 全部通过;同一 head 的 GitHub CI run 27876869243CodeQL run 27876868845 均为 success。

使用假值复现:

go run ./cmd/sshx -h=192.0.2.1 --audit-output=<tmp> --json 'sudo rm -rf / password="<fake word pair>" --token "<fake word pair>"'

观察到 audit JSONL 已正确脱敏:

"command":"sudo rm -rf / password=<redacted> --token <redacted>"

但 stdout JSON 仍包含未脱敏的 commanderror 命令文本;复现中只使用假值,以下摘录已进一步替换为占位符:

{"command":"sudo rm -rf / password=\"<fake-secret-fragment> <fake-secret-fragment>\" --token \"<fake-secret-fragment> <fake-secret-fragment>\"","error_kind":"blocked","error":"... Command: sudo rm -rf / password=\"<fake-secret-fragment> <fake-secret-fragment>\" --token \"<fake-secret-fragment> <fake-secret-fragment>\" ..."}

影响

受影响路径是 agent/automation 常用的 --json 结构化输出。危险命令会在联网前被拦截,但如果命令行中同时包含 token、password、api key 等敏感参数,调用方保存 stdout JSON 时仍可能记录完整参数;这与 audit trail 已建立的 redaction 行为不一致。

最小修复方案

在 JSON 输出面复用现有脱敏逻辑:

  • emitCommandJSON 输出 command 时对 config.Command 做 redaction。
  • execErrCommandBlockedError 或其它包含命令文本的错误时,对 execErr.Error() 做 redaction 后再写入 error
  • 保留非敏感命令的现有 JSON contract,避免改动 SSH 执行流程。

验证建议

  • 新增 --json blocked-command 单元测试,输入 quoted assignment/flag fake values,断言 commanderror 都不包含 fake value 的任一片段。
  • 保留现有 audit redaction 测试,确认 audit JSONL 与 stdout JSON 两个输出面都脱敏。
  • 运行 go test ./...

Metadata

Metadata

Assignees

No one assigned

    Labels

    agent-todoReady for automated agent implementationbugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions