You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Implements the first slice of ADR-024. Adds a shared credential
resolver any scanner can call, two optional engine hooks for
passing env vars and bind mounts into containers, and the actual
zap config keys that turn `argus.yml` into the source of truth
for ZAP tuning.
Credential resolver (`argus/core/secrets.py`):
- `resolve_secret(config, field)` accepts either `<field>` (literal,
back-compat) or `<field>_env` (env-var name reference, preferred).
Precedence: stdin_override > _env > literal. Returns None when
nothing is configured or the named env var is unset.
- `looks_like_literal_secret()` flags vendor-prefixed tokens (gh*,
AKIA, AIza, glpat-, sk_live_, npm_, xox*-) at config-load so
pasted secrets don't get committed.
- `validate_env_var_name()` enforces POSIX shell identifier rules.
- Stdlib-only. 42 tests in `argus/tests/core/test_secrets.py`.
Schema integration (`argus/core/schema.py`):
- `_validate_secret_field()` enforces the either-form contract.
Errors on invalid env-var names; warns on vendor-shaped literals
and on dual-set fields.
- Extends `_SCANNER_KNOWN_KEYS` with the new ZAP keys (api_spec,
rules_file, cmd_options, max_duration_minutes, healthcheck_url,
app_image_ref, app_ports, auth, *_env credential variants).
- `_validate_zap_auth()` walks the nested `scanners.zap.auth.*`
block with the same credential rules. 18 tests in
`argus/tests/core/test_schema_scanners.py`.
Engine hooks (`argus/core/engine.py`):
- `container_env(config) -> dict[str, str]` — extra `-e` flags.
None values are filtered so unset env-var refs don't leak as
`NAME=None`.
- `container_mounts(config) -> [(host, container)]` — extra
read-only bind mounts. Non-existent host paths are skipped with
a WARNING rather than aborting the scan. 5 tests in
`argus/tests/test_engine.py`.
ZAP scanner (`argus/scanners/zap.py`):
- `container_args(config)` resolves the right ZAP script
(zap-baseline.py / zap-full-scan.py / zap-api-scan.py) from
`scan_type` and `api_spec`, then assembles -t / -J / -c / -n
/ -T flags and appends `cmd_options` verbatim.
- `container_env(config)` exports `ZAP_REGISTRY_USERNAME` /
`_PASSWORD` for registry auth and `ZAP_AUTH_USERNAME` /
`_PASSWORD` for web-app auth (ZAP context-file placeholders
substitute these natively).
- `container_mounts(config)` binds `rules_file` and
`auth.context_file` into the container at the known
`/zap/wrk/` paths ZAP expects.
- `scan()` (local-binary path) catches FileNotFoundError when
zap-cli is missing rather than letting it escape, and warns
when container-only keys are set on the local backend.
- 21 tests in `argus/tests/scanners/test_zap.py`.
Container scanner (`argus/scanners/container.py`):
- `_build_env()` now calls `resolve_secret()` for parity. No
surface change — existing literal `registry_username` /
`_password` still work; new `*_env` form also works.
Docs:
- `docs/config-reference.md` — new credential-fields section,
ZAP key table, four worked examples (registry auth,
baseline, API scan, authenticated scan).
- `docs/scanners.md` — new "Configuring ZAP via argus.yml"
subsection in the ZAP section.
- `argus.example.yml` — commented ZAP tuning + auth block.
- `.ai/architecture.yaml` — `core/secrets.py` and engine-hook
notes added to the SDK structure (both component copies).
- `.ai/errors.yaml` — modernized the "registry auth failed"
pattern to recommend `*_env` over literal credentials.
Deferred to follow-up PRs (per ADR-024 task list):
- Healthcheck poll (needs an engine pre-scan hook)
- `--registry-password-stdin` CLI flag
- Composite-action trim (.github/actions/scanner-zap surface)
- Migration guide
Full suite: 3065 passed, 2 skipped.
Co-authored-by: eFAILution <eFAILution@users.noreply.github.com>
"core/config.py": "ArgusConfig loading from argus.yml"
36
-
"core/engine.py": "ArgusEngine orchestrating scanners and aggregating results (delegates to core/prewarm.py for background image pre-warm + lazy pulls in parallel mode)"
36
+
"core/engine.py": "ArgusEngine orchestrating scanners and aggregating results (delegates to core/prewarm.py for background image pre-warm + lazy pulls in parallel mode). Container path honors three optional Scanner hooks: container_args/build_args (CLI argv), container_env(config)->dict (extra -e flags; None values are filtered so unset env-var-name refs don't leak as 'NAME=None'), and container_mounts(config)->[(host,container)] (extra read-only bind mounts; non-existent host paths are skipped with a WARNING rather than aborting the scan)."
37
37
"core/prewarm.py": "ImagePrewarmer — best-effort background container image pulls. Dedup'd by image ref, capped concurrency (execution.prewarm_workers, default 4), opt-out via execution.prewarm_images: false. Skipped when pull_policy=never. Pre-warm failure falls back to inline pull in _run_in_container."
"core/sbom.py": "SBOM format detection (CycloneDX JSON/XML, SPDX JSON/tag-value, Syft JSON) for ``argus scan --sbom``"
41
+
"core/secrets.py": "Credential resolution for any scanner config field. resolve_secret(config, field) accepts either <field> (literal, warned at config-load if vendor-shaped via looks_like_literal_secret) or <field>_env (env-var name reference; reads os.environ at scan time). validate_env_var_name enforces POSIX shell identifier rules. Stdlib-only. Used by scanners.container and scanners.zap; future scanners with credential needs use the same helper. See ADR-024."
41
42
"core/findings_view.py": "Shared UI-free logic for findings display — ViewState, SEVERITY_ORDER, finding_detail_rows, compute_summary, diff_scans (scan-over-scan bucketing keyed off (scanner, id, location)). Consumed by argus view terminal (TUI ``D`` keybind, DiffScreen) and argus view browser (web UI ``/diff`` route)."
"core/config.py": "ArgusConfig loading from argus.yml"
468
-
"core/engine.py": "ArgusEngine orchestrating scanners and aggregating results (delegates to core/prewarm.py for background image pre-warm + lazy pulls in parallel mode)"
469
+
"core/engine.py": "ArgusEngine orchestrating scanners and aggregating results (delegates to core/prewarm.py for background image pre-warm + lazy pulls in parallel mode). Container path honors three optional Scanner hooks: container_args/build_args (CLI argv), container_env(config)->dict (extra -e flags; None values are filtered so unset env-var-name refs don't leak as 'NAME=None'), and container_mounts(config)->[(host,container)] (extra read-only bind mounts; non-existent host paths are skipped with a WARNING rather than aborting the scan)."
469
470
"core/prewarm.py": "ImagePrewarmer — best-effort background container image pulls. Dedup'd by image ref, capped concurrency (execution.prewarm_workers, default 4), opt-out via execution.prewarm_images: false. Skipped when pull_policy=never. Pre-warm failure falls back to inline pull in _run_in_container."
"core/sbom.py": "SBOM format detection (CycloneDX JSON/XML, SPDX JSON/tag-value, Syft JSON) for ``argus scan --sbom``"
474
+
"core/secrets.py": "Credential resolution for any scanner config field. resolve_secret(config, field) accepts either <field> (literal, warned at config-load if vendor-shaped via looks_like_literal_secret) or <field>_env (env-var name reference; reads os.environ at scan time). validate_env_var_name enforces POSIX shell identifier rules. Stdlib-only. Used by scanners.container and scanners.zap; future scanners with credential needs use the same helper. See ADR-024."
473
475
"core/findings_view.py": "Shared UI-free logic for findings display — ViewState, SEVERITY_ORDER, finding_detail_rows, compute_summary, diff_scans (scan-over-scan bucketing keyed off (scanner, id, location)). Consumed by argus view terminal (TUI ``D`` keybind, DiffScreen) and argus view browser (web UI ``/diff`` route)."
0 commit comments