fix(engine): pass credentials by name, not value, on docker run#144
Conversation
Closes hardening item #1 from "Secret Handling & Credential Surface Hardening" in docs/developer/SDK-ROADMAP.md. Before: docker run -e NAME=VALUE ... image ... After: docker run -e NAME ... image ... subprocess.run(..., env={**os.environ, NAME: VALUE}) Docker inherits the named env var from the parent process (the python subprocess argus spawns). The value lives only in that subprocess's private environment — not in argv. Closes three specific leak vectors: - `ps -ef | grep "docker run"` no longer shows credentials to other local users on the scan host. - `docker inspect <container>` no longer shows credentials to anyone with docker socket access. - Docker daemon audit logs (if configured) capture only NAME, not VALUE. The subprocess env is private to argus's docker-run child and its own children (the docker daemon's container). Visible only to processes that can already read /proc/<pid>/environ for that specific pid — a strictly tighter access set than the ps-visible argv list. Behavior preservation: - Scanners without container_env: unchanged (subprocess env=None, inherits parent unmodified). - container_env entries with None values: still filtered before the dict is built, so unset env-var-name references don't materialize at all. - container_env that resolves to an all-None dict: drops the env=... override entirely (matches no-container_env behavior). Updated 4 engine hook tests: - test_env_vars_passed_by_name_not_value (renamed from test_env_vars_passed_via_dash_e) — explicit assertion that VALUE never appears on argv, and that it lands in the subprocess env. - test_none_value_skipped — verifies None values don't reach either argv OR subprocess env. - test_no_container_env_method_no_extra_flags — verifies env=None is passed for back-compat scanners. - test_all_none_values_no_subprocess_env_override (new) — guards the "drop override on empty dict" path. .ai/architecture.yaml engine-hook description updated in both copies to reflect the new passthrough strategy. Full suite: 3066 passed, 2 skipped.
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
🔒 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:4efd24ff3b6671770b1a3085c48f1a7722c44e39
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
Description
Closes hardening item (1) from "Secret Handling & Credential Surface Hardening" in
docs/developer/SDK-ROADMAP.md. Switches the engine's container-path env passthrough from-e NAME=VALUE(value on argv) to-e NAME(name only, value in the subprocess's private env). Closes theps/docker inspect/ docker-daemon-audit leak vector identified in the post-#142 security audit.Changes Made
Details
Before:
After:
Docker inherits the named env var from the parent process (the python subprocess argus spawns). The value lives only in that subprocess's private environment — never on argv. Three concrete leak vectors close:
ps -ef | grep "docker run"docker inspect <container>NAME=VALUEThe subprocess env is private to argus's docker-run child and its own children. Visible only to processes that can already read
/proc/<pid>/environfor that specific pid — strictly tighter access than theps-visible argv list.Behavior preservation:
container_env: unchanged (subprocess.runreceivesenv=None, inherits parent unmodified).container_enventries withNonevalues: still filtered before the dict is built. Unset env-var-name references don't materialize at all (closes the "and don't accidentally inherit the host's value for that name" gap explicitly).container_envthat resolves to an all-Nonedict: drops theenv=...override entirely (matches the no-container_envpath; subprocess.run seesenv=None).Testing
Test Results
Updated 3 existing engine-hook tests + added 1 new one in
argus/tests/test_engine.py::TestContainerEnvHook:test_env_vars_passed_by_name_not_value(renamed fromtest_env_vars_passed_via_dash_e) — explicit assertion that VALUE never appears on argv, and that it lands insubprocess.run'senv=dict; PATH preserved from parent env.test_none_value_skipped— verifiesNonevalues don't reach argv OR subprocess env.test_no_container_env_method_no_extra_flags— verifiesenv=Noneis passed for back-compat scanners.test_all_none_values_no_subprocess_env_override(new) — guards the "drop override on empty dict" path.Full suite: 3066 passed, 2 skipped, 7 deselected.
Security Considerations
Security Details
Closes the highest-payoff item from the secret-handling audit. Severity classification per the audit: medium — exploitable only by an attacker who already has local user access to the scan host, but trivial once they do. After this PR, even an attacker with local user access can no longer scrape credentials from
ps -ef; they'd need to read/proc/<pid>/environfor the specific docker-spawning subprocess, which requires the same uid (or root).AI Context Updates (.ai/)
.ai/architecture.yamlupdated — bothcore/engine.pydescription copies updated to describe the new passthrough strategy (NAME-only argv + private subprocess env)..ai/workflows.yamlupdated.ai/decisions.yamlupdated.ai/errors.yamlupdatedChecklist
Related Issues
docs/developer/SDK-ROADMAP.md→ "Secret Handling & Credential Surface Hardening" (added in PR docs(roadmap): capture secret-handling audit + hardening PR queue #143).container_envengine hook landed in PR feat(secrets): credential resolver + zap config-passthrough wiring #142.Screenshots/Logs (if applicable)
Diff: 4 files, +96 / -21.