问题
PR #18 新增的 audit redaction 对带空格的 quoted password/token 值只脱敏第一个空白前的片段,后续片段仍会写入 audit JSONL 的 command 和 outcome.message。这会破坏审计日志“不记录 secret”的安全边界。
证据
go run ./cmd/sshx -h=192.0.2.1 --audit-output=<tmp> --json 'sudo rm -rf / password="alpha bravo" --token "charlie delta"'
生成的 audit JSONL 中仍包含 quoted value 的尾部片段:
"command":"sudo rm -rf / password=<redacted> bravo\" --token <redacted> delta\""
outcome.message 也包含相同的残留片段。
影响
受影响路径是默认开启的本地 audit trail。只要远程命令或错误消息中包含 password="... ..."、--token "... ..." 等带空格的敏感值,audit 文件会保留敏感值的后半段。虽然复现使用的是假值,但真实命令中的 token、口令短语或带空格 secret 会被部分泄漏到 ~/.sshx/audit/sshx-YYYY-MM-DD.jsonl。
最小修复方案
扩展 redaction 解析,至少覆盖 quoted assignment/flag values:
key="..." / key='...'
--token "..." / --token '...'
- 保留现有 unquoted token 行为
避免引入 shell 执行或完整 shell parser;可以先用更明确的 quoted-value 正则在 unquoted 规则之前处理,并补齐单元测试。
验证建议
- 新增
redactSensitiveText 单元测试,覆盖 quoted assignment、quoted flag、unquoted assignment、unquoted flag。
- 运行
go test ./...。
- 手工复现上面的 blocked-command audit smoke,确认 JSONL 中不再出现 quoted value 的任何残留片段。
问题
PR #18 新增的 audit redaction 对带空格的 quoted password/token 值只脱敏第一个空白前的片段,后续片段仍会写入 audit JSONL 的
command和outcome.message。这会破坏审计日志“不记录 secret”的安全边界。证据
2517a2efa26686db10f0c407bd71a7d50f1d7bb7/ feat(cli): add local audit trail #18internal/app/audit.gosensitiveAssignmentRE/sensitiveFlagRE,二者的 value 捕获均使用非空白 token 范围:([^\s&;]+)/([^\s]+)。redactSensitiveText直接用这些正则替换,现有测试只覆盖了不含空格的假值。2026-06-20T23:33:12+08:00使用假值复现:生成的 audit JSONL 中仍包含 quoted value 的尾部片段:
outcome.message也包含相同的残留片段。影响
受影响路径是默认开启的本地 audit trail。只要远程命令或错误消息中包含
password="... ..."、--token "... ..."等带空格的敏感值,audit 文件会保留敏感值的后半段。虽然复现使用的是假值,但真实命令中的 token、口令短语或带空格 secret 会被部分泄漏到~/.sshx/audit/sshx-YYYY-MM-DD.jsonl。最小修复方案
扩展 redaction 解析,至少覆盖 quoted assignment/flag values:
key="..."/key='...'--token "..."/--token '...'避免引入 shell 执行或完整 shell parser;可以先用更明确的 quoted-value 正则在 unquoted 规则之前处理,并补齐单元测试。
验证建议
redactSensitiveText单元测试,覆盖 quoted assignment、quoted flag、unquoted assignment、unquoted flag。go test ./...。