feat(cli): --registry-password-stdin and --zap-auth-password-stdin#145
Conversation
Closes hardening item #2 from "Secret Handling & Credential Surface Hardening" in docs/developer/SDK-ROADMAP.md. Two new flags on `argus scan` let users supply credentials via stdin, mirroring `docker login --password-stdin`. The value never appears on argv, in shell history, in argus.yml, or in any persisted argus artifact (argus-audit.json, argus.log, argus-results.*). Implementation: - argus/core/secrets.py: module-level `_STDIN_OVERRIDES` slot registry with set_stdin_override / get_stdin_override / clear_stdin_overrides. Slots are stable cross-scanner names ("registry_password", "zap_auth_password") so the same stdin value can fill the same logical credential across scanners (e.g., registry password for container + zap), while distinct credentials (registry vs. ZAP web-app auth) stay in separate slots. - argus/cli.py: --registry-password-stdin → slot: registry_password --zap-auth-password-stdin → slot: zap_auth_password New helper _consume_stdin_password_flags(args) runs at the top of cmd_scan. Errors out cleanly when stdin is a TTY, when more than one stdin flag is set (stdin is single-stream), or when stdin is empty. Reads stdin once, strips a single trailing newline (multi-line tokens preserved for PEM-style payloads), routes the value into the slot registry. - argus/scanners/container.py + zap.py: container_env / _build_env pass `stdin_override=get_stdin_override(slot)` to resolve_secret. Highest precedence: stdin > _env > literal > None. Test coverage: - 5 new tests in argus/tests/core/test_secrets.py::TestStdinOverrideSlots cover the slot APIs (set/get/clear/isolation/explicit-override-arg). - 9 new tests in argus/tests/test_cli.py::TestStdinPasswordFlags cover the CLI helper: no-flag noop, single-flag happy path for both flags, trailing-newline trim, no-newline preserved, multi-line preserved, both-flags error, TTY error, empty-stdin error. Docs + .ai/: - docs/config-reference.md: new "Third form — CLI stdin" subsection in the credential-fields section with a precedence table and worked examples. The validation-rules list now sits below it. - docs/cli-reference.md: regenerated from cli.py via scripts/ci/check_cli_docs.py --fix. - .ai/architecture.yaml: core/secrets.py description updated in both SDK structure blocks to document the three-form precedence and the slot registry; explicit note that the stdin path never reaches the per-scanner config dict. - docs/developer/SDK-ROADMAP.md: hardening item (2) flipped to shipped with implementation summary. Full suite: 3080 passed, 2 skipped.
🔒 Argus Container Security ScanBranch: 📊 Combined Findings Summary
Scanned: 4 containers | Build Failures: 0 📦 Container Breakdown
🔍 Detailed Findings by Container🚨 cli - 73 vulnerabilities (33 unique)Image: Combined (Deduplicated)
🔷 Trivy Scanner (73 findings, 33 unique)
...and 23 more ⚓ Grype Scanner (0 findings, 0 unique)✅ No vulnerabilities detected by Grype 🟡 scanner-bandit - 2 vulnerabilities (2 unique)Image: Combined (Deduplicated)
🔷 Trivy Scanner (2 findings, 2 unique)
⚓ Grype Scanner (0 findings, 0 unique)✅ No vulnerabilities detected by Grype
|
| 🚨 Critical | 🟡 Medium | 🔵 Low | Total | Unique | |
|---|---|---|---|---|---|
| 0 | 7 | 44 | 63 | 114 | 50 |
🔷 Trivy Scanner (114 findings, 49 unique)
| CVE | Severity | Package | Version | Fixed |
|---|---|---|---|---|
| CVE-2026-4878 | libcap2 | 1:2.75-10+b8 | N/A | |
| CVE-2025-69720 | libncursesw6 | 6.5+20250216-2 | N/A | |
| CVE-2026-29111 | libsystemd0 | 257.9-1~deb13u1 | N/A | |
| CVE-2025-69720 | libtinfo6 | 6.5+20250216-2 | N/A | |
| CVE-2026-29111 | libudev1 | 257.9-1~deb13u1 | N/A | |
| CVE-2025-69720 | ncurses-base | 6.5+20250216-2 | N/A | |
| CVE-2025-69720 | ncurses-bin | 6.5+20250216-2 | N/A | |
| CVE-2026-27456 | 🟡 MEDIUM | bsdutils | 1:2.41-5 | N/A |
| CVE-2026-3184 | 🟡 MEDIUM | bsdutils | 1:2.41-5 | N/A |
| CVE-2026-27456 | 🟡 MEDIUM | libblkid1 | 2.41-5 | N/A |
| CVE-2026-3184 | 🟡 MEDIUM | libblkid1 | 2.41-5 | N/A |
| CVE-2026-4046 | 🟡 MEDIUM | libc-bin | 2.41-12+deb13u2 | N/A |
| CVE-2026-4437 | 🟡 MEDIUM | libc-bin | 2.41-12+deb13u2 | N/A |
| CVE-2026-4438 | 🟡 MEDIUM | libc-bin | 2.41-12+deb13u2 | N/A |
| CVE-2026-5435 | 🟡 MEDIUM | libc-bin | 2.41-12+deb13u2 | N/A |
| CVE-2026-5450 | 🟡 MEDIUM | libc-bin | 2.41-12+deb13u2 | N/A |
| CVE-2026-5928 | 🟡 MEDIUM | libc-bin | 2.41-12+deb13u2 | N/A |
| CVE-2026-6238 | 🟡 MEDIUM | libc-bin | 2.41-12+deb13u2 | N/A |
| CVE-2026-4046 | 🟡 MEDIUM | libc6 | 2.41-12+deb13u2 | N/A |
| CVE-2026-4437 | 🟡 MEDIUM | libc6 | 2.41-12+deb13u2 | N/A |
| CVE-2026-4438 | 🟡 MEDIUM | libc6 | 2.41-12+deb13u2 | N/A |
| CVE-2026-5435 | 🟡 MEDIUM | libc6 | 2.41-12+deb13u2 | N/A |
| CVE-2026-5450 | 🟡 MEDIUM | libc6 | 2.41-12+deb13u2 | N/A |
| CVE-2026-5928 | 🟡 MEDIUM | libc6 | 2.41-12+deb13u2 | N/A |
| CVE-2026-6238 | 🟡 MEDIUM | libc6 | 2.41-12+deb13u2 | N/A |
| CVE-2026-27456 | 🟡 MEDIUM | liblastlog2-2 | 2.41-5 | N/A |
| CVE-2026-3184 | 🟡 MEDIUM | liblastlog2-2 | 2.41-5 | N/A |
| CVE-2026-34743 | 🟡 MEDIUM | liblzma5 | 5.8.1-1 | N/A |
| CVE-2026-27456 | 🟡 MEDIUM | libmount1 | 2.41-5 | N/A |
| CVE-2026-3184 | 🟡 MEDIUM | libmount1 | 2.41-5 | N/A |
| CVE-2026-27456 | 🟡 MEDIUM | libsmartcols1 | 2.41-5 | N/A |
| CVE-2026-3184 | 🟡 MEDIUM | libsmartcols1 | 2.41-5 | N/A |
| CVE-2026-40225 | 🟡 MEDIUM | libsystemd0 | 257.9-1~deb13u1 | N/A |
| CVE-2026-40226 | 🟡 MEDIUM | libsystemd0 | 257.9-1~deb13u1 | N/A |
| CVE-2026-4105 | 🟡 MEDIUM | libsystemd0 | 257.9-1~deb13u1 | N/A |
| CVE-2026-40225 | 🟡 MEDIUM | libudev1 | 257.9-1~deb13u1 | N/A |
| CVE-2026-40226 | 🟡 MEDIUM | libudev1 | 257.9-1~deb13u1 | N/A |
| CVE-2026-4105 | 🟡 MEDIUM | libudev1 | 257.9-1~deb13u1 | N/A |
| CVE-2026-27456 | 🟡 MEDIUM | libuuid1 | 2.41-5 | N/A |
| CVE-2026-3184 | 🟡 MEDIUM | libuuid1 | 2.41-5 | N/A |
| CVE-2026-27456 | 🟡 MEDIUM | login | 1:4.16.0-2+really2.41-5 | N/A |
| CVE-2026-3184 | 🟡 MEDIUM | login | 1:4.16.0-2+really2.41-5 | N/A |
| CVE-2026-27456 | 🟡 MEDIUM | mount | 2.41-5 | N/A |
| CVE-2026-3184 | 🟡 MEDIUM | mount | 2.41-5 | N/A |
| CVE-2026-5958 | 🟡 MEDIUM | sed | 4.9-2 | N/A |
| CVE-2026-5704 | 🟡 MEDIUM | tar | 1.35+dfsg-3.1 | N/A |
| CVE-2026-27456 | 🟡 MEDIUM | util-linux | 2.41-5 | N/A |
| CVE-2026-3184 | 🟡 MEDIUM | util-linux | 2.41-5 | N/A |
| CVE-2026-27171 | 🟡 MEDIUM | zlib1g | 1:1.3.dfsg+really1.3.1-1+b1 | N/A |
| CVE-2026-3219 | 🟡 MEDIUM | pip | 26.0.1 | N/A |
...and 64 more
⚓ Grype Scanner (0 findings, 0 unique)
✅ No vulnerabilities detected by Grype
⚠️ scanner-supply-chain - 17 vulnerabilities (17 unique)
Image: ghcr.io/huntridge-labs/argus/scanner-supply-chain:ce05f2ebe5cb58957e1167d55cb30d2db571fa4a
Combined (Deduplicated)
| 🚨 Critical | 🟡 Medium | 🔵 Low | Total | Unique | |
|---|---|---|---|---|---|
| 0 | 9 | 8 | 0 | 17 | 17 |
🔷 Trivy Scanner (17 findings, 17 unique)
| CVE | Severity | Package | Version | Fixed |
|---|---|---|---|---|
| CVE-2026-32280 | stdlib | v1.26.1 | 1.25.9, 1.26.2 | |
| CVE-2026-32281 | stdlib | v1.26.1 | 1.25.9, 1.26.2 | |
| CVE-2026-32283 | stdlib | v1.26.1 | 1.25.9, 1.26.2 | |
| CVE-2026-33810 | stdlib | v1.26.1 | 1.26.2 | |
| CVE-2026-33811 | stdlib | v1.26.1 | 1.25.10, 1.26.3 | |
| CVE-2026-33814 | stdlib | v1.26.1 | 1.25.10, 1.26.3 | |
| CVE-2026-39820 | stdlib | v1.26.1 | 1.25.10, 1.26.3 | |
| CVE-2026-39836 | stdlib | v1.26.1 | 1.25.10, 1.26.3 | |
| CVE-2026-42499 | stdlib | v1.26.1 | 1.25.10, 1.26.3 | |
| CVE-2026-3219 | 🟡 MEDIUM | pip | 26.0.1 | N/A |
| CVE-2026-6357 | 🟡 MEDIUM | pip | 26.0.1 | 26.1 |
| CVE-2026-32282 | 🟡 MEDIUM | stdlib | v1.26.1 | 1.25.9, 1.26.2 |
| CVE-2026-32288 | 🟡 MEDIUM | stdlib | v1.26.1 | 1.25.9, 1.26.2 |
| CVE-2026-32289 | 🟡 MEDIUM | stdlib | v1.26.1 | 1.25.9, 1.26.2 |
| CVE-2026-39823 | 🟡 MEDIUM | stdlib | v1.26.1 | 1.25.10, 1.26.3 |
| CVE-2026-39825 | 🟡 MEDIUM | stdlib | v1.26.1 | 1.25.10, 1.26.3 |
| CVE-2026-39826 | 🟡 MEDIUM | stdlib | v1.26.1 | 1.25.10, 1.26.3 |
⚓ Grype Scanner (0 findings, 0 unique)
✅ No vulnerabilities detected by Grype
Generated by Argus
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
Description
Closes hardening item #2 from
docs/developer/SDK-ROADMAP.md→ "Secret Handling & Credential Surface Hardening". Two new flags onargus scanlet users supply credentials via stdin, mirroringdocker login --password-stdin. The value never appears on argv, in shell history, inargus.yml, or in any persisted argus artifact.Changes Made
Details
The flags:
--registry-password-stdinregistry_password— used bycontainerandzap(withapp_image_ref) registry auth.--zap-auth-password-stdinzap_auth_password— used byscanners.zap.auth.password.Three-form precedence (highest first):
--*-password-stdin(this PR)<field>_env(env-var name reference)<field>literal (back-compat, warned for vendor-shaped values)Implementation:
argus/core/secrets.py— adds a module-level_STDIN_OVERRIDESslot registry withset_stdin_override/get_stdin_override/clear_stdin_overrides. Slots are stable cross-scanner names so the same stdin value can fill the same logical credential across scanners, while distinct credentials stay isolated.argus/cli.py—_consume_stdin_password_flags(args)runs at the top ofcmd_scan. Errors cleanly when stdin is a TTY, when more than one stdin flag is set (stdin is single-stream), or when stdin is empty. Reads stdin once, strips a single trailing newline (preserves multi-line PEM-style payloads), routes the value into the slot registry.argus/scanners/container.py+argus/scanners/zap.py— callresolve_secret(..., stdin_override=get_stdin_override(slot)). The stdin value never reaches the per-scanner config dict, so it can't leak intoargus-audit.jsonorargus.log.Errors (cleanly, with diagnostic stderr):
Error: --*-password-stdin requires piped input. Example: echo "$REGISTRY_TOKEN" | argus scan --registry-password-stdin --config argus.yml<field>_envfor the other credential.Error: stdin was empty. The password must be supplied as the body of the pipe.Testing
Test Results
14 new tests:
argus/tests/core/test_secrets.py::TestStdinOverrideSlotsstdin_override=arg still winsargus/tests/test_cli.py::TestStdinPasswordFlagsFull suite: 3080 passed, 2 skipped, 7 deselected.
Security Considerations
Security Details
Direct implementation of hardening item (2) from the secret-handling audit. The argv-leak vector closed for env-var-name references in PR #144 now has a complementary path for ad-hoc / interactive runs where setting an env var is friction:
echo "$VAR"— only the var nameps -efargus scan --registry-password literalwould leakargus scan --registry-password-stdin— value never on argvargus.yml(VCS)*_envreferenceargus-audit.json/argus.logThe stdin path is not stored or logged anywhere argus persists. Process-local memory only; Python strings remain in memory until GC, which is true of any credential handling in Python (noted as known limitation in module docstring).
AI Context Updates (.ai/)
.ai/architecture.yamlupdated —core/secrets.pydescription in both component blocks now documents the three-form precedence (stdin > _env > literal) and the slot registry plus its leak-resistance property..ai/workflows.yamlupdated.ai/decisions.yamlupdated — implementation of an already-decided ADR-024 task; no new ADR warranted..ai/errors.yamlupdatedChecklist
Related Issues
Closes hardening item (2) in
docs/developer/SDK-ROADMAP.md→ "Secret Handling & Credential Surface Hardening". Items 3-5 (cosign verify, security policy doc, audit-trail defensive redact) remain queued.Screenshots/Logs (if applicable)
Diff: 10 files, +407 / -12.