Secret-bearing argv flags outside the built-in three reach CLIDetail.Argv unscrubbed
A secret-bearing argv flag that is neither one of three built-in names nor
declared by its driver reaches CLIDetail.Argv verbatim, and no later layer
scrubs it. argv renders to stdout, the JUnit XML and the detail view, so the
value lands in a CI artifact.
oras push --api-key hunter2 is the shape. Nothing in the chain catches it:
| Layer | What it does | Catches --api-key? |
|---|---|---|
| Executor | redact.RedactArgv(argv, e.secretFlags...) (pkg/conformance/cliexec/executor.go:214) |
Only if the driver declared it via WithSecretFlags |
| Built-in set | secretFlags is an exact-match set of --password, --auth, --token (pkg/conformance/redact/redact.go:68-72) |
No |
| Render boundary | redact.RedactArgv(c.Argv) (internal/report/detail.go:120), called with no driver flags, so it re-applies the built-in three only |
No |
| Five-pattern scan | ScanContent runs on stdout and stderr (internal/report/detail.go:130) |
Never runs on argv at all |
So the defense-in-depth story that covers every other secret-bearing output
field has a hole at exactly one field. RedactField
(pkg/conformance/redact/redact.go:74-88) matches password, token,
secret, api[_-]?key and authorization case-insensitively as substrings,
and sits twenty lines from the three-name exact-match set that guards argv.
pkg/client/npm/executor.go:80 declares --_auth and
pkg/conformance/maven/cliexec.go:229 declares mavenSecretFlags(), so this is
a latent gap rather than a confirmed leak in the two drivers that exist today.
Whether those two lists are complete is worth checking as part of the fix. The
next driver is where it bites.
Why it went unnoticed: S04 states the rule twice, with two different sets
The narrow reading, in §CLI drivers / Invocation contract
(docs/specs/S04-contracts.md:1632), §Security Considerations (:2752) and
AC #51 (:2364):
replacing recognized secret-bearing flag values (
--password,--auth,--token, and driver-specific equivalents declared by the implementation) with[REDACTED]
The five-pattern reading, in §Security Considerations / Field-name redaction
(:2767) and §Resolutions / Cross-spec consistency (:3112):
The same five-pattern set is enforced at the CLI driver layer (
RunOpts.ExtraEnvkeys and argv flag names).
Resolved by adopting the five-pattern set everywhere — CLI driver scrubbing,
RunOpts.ExtraEnvkeys, slog field-name redaction, andfailure_detailcontent scanning — so the rule reads the same regardless of which layer applies it.
These are not the same rule and not the same mechanism: one is exact match on a
whole flag name, the other is a case-insensitive substring pattern. A driver
author who reads :2767 or :3112 concludes argv flag names are already
pattern-matched and skips the WithSecretFlags call. That is the decision this
contradiction changes.
This is the same field an earlier review flagged as the one secret-bearing
output field left undefended at the boundary. The fix landed then was
RedactArgv at the render boundary, which closed the "a caller built Argv by
hand" case but not the pattern case, because RedactArgv carries the three-name
set.
Fix
Decide which reading is normative, then make all five passages and the code agree:
- (a) Pattern-match argv. Give the argv layer the same five patterns as
RedactField, matched against the flag name with the--/-prefix and any=valuestripped. This is what:2767and:3112already promise, and it subsumes mostWithSecretFlagsdeclarations. Apply it at the render boundary too, so a hand-builtArgvis covered. - (b) Keep exact match. Reword
:2767and:3112to describe the argv layer as it is, an exact-match name list plus driver declarations, and say why it differs from the field-name layer. Then add a guard that a driver declaring a CLI executor cannot ship without an explicit secret-flag review, because under (b) nothing catches an omission.
Either way, AC #51's fixture should exercise a representative outside
{--password, --token}. --api-key also covers the [_-]? alternation no
current fixture reaches.
How this was found
By reading S04 end to end and enumerating its repeated facts, rather than by reviewing a diff. The contradiction is old and is not visible from any single MR: every edit to S04 so far has been narrow and single-anchor (an amendment row from another spec, a review finding, a targeted fix), and each was verified at its own anchor. No MR has ever owned the question "does S04 still agree with itself".