Skip to content

feat(cli): --registry-password-stdin and --zap-auth-password-stdin#145

Merged
eFAILution merged 1 commit into
feat/argus-portabilityfrom
feat/cli-stdin-password-flags
May 12, 2026
Merged

feat(cli): --registry-password-stdin and --zap-auth-password-stdin#145
eFAILution merged 1 commit into
feat/argus-portabilityfrom
feat/cli-stdin-password-flags

Conversation

@eFAILution
Copy link
Copy Markdown
Collaborator

Description

Closes hardening item #2 from docs/developer/SDK-ROADMAP.md → "Secret Handling & Credential Surface Hardening". 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.

Changes Made

  • Added new scanner/workflow
  • Modified existing scanner/workflow
  • Updated documentation
  • Fixed bug
  • Other (please specify): New CLI flags with slot-based stdin override registry

Details

The flags:

echo "$REGISTRY_TOKEN" | argus scan --registry-password-stdin --config argus.yml
echo "$APP_PASSWORD"   | argus scan zap --zap-auth-password-stdin --target https://app
Flag Credential slot it fills
--registry-password-stdin registry_password — used by container and zap (with app_image_ref) registry auth.
--zap-auth-password-stdin zap_auth_password — used by scanners.zap.auth.password.

Three-form precedence (highest first):

  1. --*-password-stdin (this PR)
  2. <field>_env (env-var name reference)
  3. <field> literal (back-compat, warned for vendor-shaped values)

Implementation:

  • argus/core/secrets.py — adds a module-level _STDIN_OVERRIDES slot registry with set_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 of cmd_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 — call resolve_secret(..., stdin_override=get_stdin_override(slot)). The stdin value never reaches the per-scanner config dict, so it can't leak into argus-audit.json or argus.log.

Errors (cleanly, with diagnostic stderr):

  • TTY stdin: Error: --*-password-stdin requires piped input. Example: echo "$REGISTRY_TOKEN" | argus scan --registry-password-stdin --config argus.yml
  • Both flags set: lists the colliding slots, suggests using <field>_env for the other credential.
  • Empty stdin: Error: stdin was empty. The password must be supplied as the body of the pipe.

Testing

  • Unit tests added/updated
  • Integration tests added/updated
  • Manual testing performed
  • Tested with different scanner combinations

Test Results

14 new tests:

File Tests Covers
argus/tests/core/test_secrets.py::TestStdinOverrideSlots 5 set/get/clear roundtrips, slot isolation, explicit stdin_override= arg still wins
argus/tests/test_cli.py::TestStdinPasswordFlags 9 no-flag noop, both happy paths, newline trim, no-newline preserved, multi-line preserved, both-flags error, TTY error, empty-stdin error

Full suite: 3080 passed, 2 skipped, 7 deselected.

Security Considerations

  • No security impact
  • Security enhancement
  • Potential security implications (explain below)

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:

Surface Before (no stdin flag) After (with stdin flag)
Shell history Credentials in command if pasted echo "$VAR" — only the var name
ps -ef argus scan --registry-password literal would leak argus scan --registry-password-stdin — value never on argv
argus.yml (VCS) Must contain literal or *_env reference Empty for the field — value flows through pipe
argus-audit.json / argus.log Per-scanner config block would serialize the literal Slot lookup bypasses the config dict entirely

The 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.yaml updated — core/secrets.py description in both component blocks now documents the three-form precedence (stdin > _env > literal) and the slot registry plus its leak-resistance property.
  • .ai/workflows.yaml updated
  • .ai/decisions.yaml updated — implementation of an already-decided ADR-024 task; no new ADR warranted.
  • .ai/errors.yaml updated
  • N/A

Checklist

  • Code follows project style guidelines
  • Documentation updated
  • Changelog updated (if applicable)
  • All tests pass
  • Reviewed by at least one maintainer
  • Reviewed CONTRIBUTING.md guidelines

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)

$ argus scan --help | grep -A2 stdin
  --registry-password-stdin
                        Read the private-registry password from stdin and use
                        it for any scanner that needs registry auth ...
  --zap-auth-password-stdin
                        Read the ZAP web-app authentication password from
                        stdin ...
============================== 3080 passed, 2 skipped, 7 deselected in 20.88s ==============================

Diff: 10 files, +407 / -12.

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.
@github-actions
Copy link
Copy Markdown
Contributor

🔒 Argus Container Security Scan

Branch: feat/cli-stdin-password-flags
Commit: ce05f2e

📊 Combined Findings Summary

🚨 Critical ⚠️ High 🟡 Medium 🔵 Low 📦 Total 🔢 Unique
1 55 86 64 206 206

Scanned: 4 containers | Build Failures: 0

📦 Container Breakdown

Container Image 🚨 Crit ⚠️ High 🟡 Med 🔵 Low Total Unique Status
cli ghcr.io/huntridge-labs/argus/cli:ce05f2ebe5cb58957e1167d55cb30d2db571fa4a 1 39 32 1 73 73
scanner-bandit ghcr.io/huntridge-labs/argus/scanner-bandit:ce05f2ebe5cb58957e1167d55cb30d2db571fa4a 0 0 2 0 2 2
scanner-opengrep ghcr.io/huntridge-labs/argus/scanner-opengrep:ce05f2ebe5cb58957e1167d55cb30d2db571fa4a 0 7 44 63 114 114
scanner-supply-chain ghcr.io/huntridge-labs/argus/scanner-supply-chain:ce05f2ebe5cb58957e1167d55cb30d2db571fa4a 0 9 8 0 17 17

🔍 Detailed Findings by Container

🚨 cli - 73 vulnerabilities (33 unique)

Image: ghcr.io/huntridge-labs/argus/cli:ce05f2ebe5cb58957e1167d55cb30d2db571fa4a

Combined (Deduplicated)

🚨 Critical ⚠️ High 🟡 Medium 🔵 Low Total Unique
1 39 32 1 73 33
🔷 Trivy Scanner (73 findings, 33 unique)
CVE Severity Package Version Fixed
CVE-2025-68121 🚨 CRITICAL stdlib v1.24.11 1.24.13, 1.25.7, 1.26.0-rc.3
CVE-2026-32280 ⚠️ HIGH stdlib v1.26.1 1.25.9, 1.26.2
CVE-2026-32281 ⚠️ HIGH stdlib v1.26.1 1.25.9, 1.26.2
CVE-2026-32283 ⚠️ HIGH stdlib v1.26.1 1.25.9, 1.26.2
CVE-2026-33810 ⚠️ HIGH stdlib v1.26.1 1.26.2
CVE-2026-33811 ⚠️ HIGH stdlib v1.26.1 1.25.10, 1.26.3
CVE-2026-33814 ⚠️ HIGH stdlib v1.26.1 1.25.10, 1.26.3
CVE-2026-39820 ⚠️ HIGH stdlib v1.26.1 1.25.10, 1.26.3
CVE-2026-39836 ⚠️ HIGH stdlib v1.26.1 1.25.10, 1.26.3
CVE-2026-42499 ⚠️ HIGH stdlib v1.26.1 1.25.10, 1.26.3
CVE-2025-61726 ⚠️ HIGH stdlib v1.24.11 1.24.12, 1.25.6
CVE-2025-61728 ⚠️ HIGH stdlib v1.24.11 1.24.12, 1.25.6
CVE-2026-25679 ⚠️ HIGH stdlib v1.24.11 1.25.8, 1.26.1
CVE-2026-32280 ⚠️ HIGH stdlib v1.24.11 1.25.9, 1.26.2
CVE-2026-32281 ⚠️ HIGH stdlib v1.24.11 1.25.9, 1.26.2
CVE-2026-32283 ⚠️ HIGH stdlib v1.24.11 1.25.9, 1.26.2
CVE-2026-33811 ⚠️ HIGH stdlib v1.24.11 1.25.10, 1.26.3
CVE-2026-33814 ⚠️ HIGH stdlib v1.24.11 1.25.10, 1.26.3
CVE-2026-39820 ⚠️ HIGH stdlib v1.24.11 1.25.10, 1.26.3
CVE-2026-39836 ⚠️ HIGH stdlib v1.24.11 1.25.10, 1.26.3
CVE-2026-42499 ⚠️ HIGH stdlib v1.24.11 1.25.10, 1.26.3
CVE-2026-34040 ⚠️ HIGH github.com/docker/docker v28.5.2+incompatible 29.3.1
CVE-2026-45022 ⚠️ HIGH github.com/go-git/go-git/v5 v5.18.0 5.19.0
CVE-2026-33811 ⚠️ HIGH stdlib v1.26.2 1.25.10, 1.26.3
CVE-2026-33814 ⚠️ HIGH stdlib v1.26.2 1.25.10, 1.26.3
CVE-2026-39820 ⚠️ HIGH stdlib v1.26.2 1.25.10, 1.26.3
CVE-2026-39836 ⚠️ HIGH stdlib v1.26.2 1.25.10, 1.26.3
CVE-2026-42499 ⚠️ HIGH stdlib v1.26.2 1.25.10, 1.26.3
CVE-2026-45022 ⚠️ HIGH github.com/go-git/go-git/v5 v5.18.0 5.19.0
CVE-2026-33811 ⚠️ HIGH stdlib v1.26.2 1.25.10, 1.26.3
CVE-2026-33814 ⚠️ HIGH stdlib v1.26.2 1.25.10, 1.26.3
CVE-2026-39820 ⚠️ HIGH stdlib v1.26.2 1.25.10, 1.26.3
CVE-2026-39836 ⚠️ HIGH stdlib v1.26.2 1.25.10, 1.26.3
CVE-2026-42499 ⚠️ HIGH stdlib v1.26.2 1.25.10, 1.26.3
CVE-2026-45022 ⚠️ HIGH github.com/go-git/go-git/v5 v5.17.2 5.19.0
CVE-2026-33811 ⚠️ HIGH stdlib v1.25.9 1.25.10, 1.26.3
CVE-2026-33814 ⚠️ HIGH stdlib v1.25.9 1.25.10, 1.26.3
CVE-2026-39820 ⚠️ HIGH stdlib v1.25.9 1.25.10, 1.26.3
CVE-2026-39836 ⚠️ HIGH stdlib v1.25.9 1.25.10, 1.26.3
CVE-2026-42499 ⚠️ HIGH stdlib v1.25.9 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
CVE-2025-11579 🟡 MEDIUM github.com/nwaples/rardecode/v2 v2.1.0 2.2.0
CVE-2025-58058 🟡 MEDIUM github.com/ulikunitz/xz v0.5.12 0.5.15

...and 23 more

⚓ Grype Scanner (0 findings, 0 unique)

✅ No vulnerabilities detected by Grype

🟡 scanner-bandit - 2 vulnerabilities (2 unique)

Image: ghcr.io/huntridge-labs/argus/scanner-bandit:ce05f2ebe5cb58957e1167d55cb30d2db571fa4a

Combined (Deduplicated)

🚨 Critical ⚠️ High 🟡 Medium 🔵 Low Total Unique
0 0 2 0 2 2
🔷 Trivy Scanner (2 findings, 2 unique)
CVE Severity Package Version Fixed
CVE-2026-3219 🟡 MEDIUM pip 26.0.1 N/A
CVE-2026-6357 🟡 MEDIUM pip 26.0.1 26.1
⚓ Grype Scanner (0 findings, 0 unique)

✅ No vulnerabilities detected by Grype

⚠️ scanner-opengrep - 114 vulnerabilities (50 unique)

Image: ghcr.io/huntridge-labs/argus/scanner-opengrep:ce05f2ebe5cb58957e1167d55cb30d2db571fa4a

Combined (Deduplicated)

🚨 Critical ⚠️ High 🟡 Medium 🔵 Low Total Unique
0 7 44 63 114 50
🔷 Trivy Scanner (114 findings, 49 unique)
CVE Severity Package Version Fixed
CVE-2026-4878 ⚠️ HIGH libcap2 1:2.75-10+b8 N/A
CVE-2025-69720 ⚠️ HIGH libncursesw6 6.5+20250216-2 N/A
CVE-2026-29111 ⚠️ HIGH libsystemd0 257.9-1~deb13u1 N/A
CVE-2025-69720 ⚠️ HIGH libtinfo6 6.5+20250216-2 N/A
CVE-2026-29111 ⚠️ HIGH libudev1 257.9-1~deb13u1 N/A
CVE-2025-69720 ⚠️ HIGH ncurses-base 6.5+20250216-2 N/A
CVE-2025-69720 ⚠️ HIGH 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 ⚠️ High 🟡 Medium 🔵 Low Total Unique
0 9 8 0 17 17
🔷 Trivy Scanner (17 findings, 17 unique)
CVE Severity Package Version Fixed
CVE-2026-32280 ⚠️ HIGH stdlib v1.26.1 1.25.9, 1.26.2
CVE-2026-32281 ⚠️ HIGH stdlib v1.26.1 1.25.9, 1.26.2
CVE-2026-32283 ⚠️ HIGH stdlib v1.26.1 1.25.9, 1.26.2
CVE-2026-33810 ⚠️ HIGH stdlib v1.26.1 1.26.2
CVE-2026-33811 ⚠️ HIGH stdlib v1.26.1 1.25.10, 1.26.3
CVE-2026-33814 ⚠️ HIGH stdlib v1.26.1 1.25.10, 1.26.3
CVE-2026-39820 ⚠️ HIGH stdlib v1.26.1 1.25.10, 1.26.3
CVE-2026-39836 ⚠️ HIGH stdlib v1.26.1 1.25.10, 1.26.3
CVE-2026-42499 ⚠️ HIGH 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

@eFAILution eFAILution merged commit 8e51955 into feat/argus-portability May 12, 2026
21 checks passed
@eFAILution eFAILution deleted the feat/cli-stdin-password-flags branch May 12, 2026 23:23
@codecov
Copy link
Copy Markdown

codecov Bot commented May 12, 2026

Codecov Report

❌ Patch coverage is 98.17073% with 3 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
argus/scanners/container.py 0.00% 2 Missing ⚠️
argus/cli.py 96.00% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

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.

1 participant