Skip to content

Migrating from argus 0.6.x to 1.x

argus's primary surface in 1.x is the Python SDK / argus CLI driven by a single argus.yml config file (ADR-013). The composite actions under .github/actions/scanner-* are now thin wrappers around the SDK — same scans, same canonical argus-results.json, but with most tuning living in argus.yml so the same configuration works identically on GitHub Actions, GitLab CI, Jenkins, Azure DevOps, or a laptop. This page shows what changed and how to update existing workflows.

The full design rationale is in ADR-024. Full config schema reference lives at docs/config-reference.md.


TL;DR — the new shape

0.6.x (everything on the composite action):

- uses: huntridge-labs/argus/.github/actions/scanner-zap@1.1.0
  with:
    target_url: 'http://localhost:8080'
    scan_type: 'baseline'
    api_spec: 'http://localhost:8080/openapi.json'
    rules_file_name: '.zap/rules.tsv'
    max_duration_minutes: 30
    cmd_options: '-z -config view.locale=en_GB'
    healthcheck_url: 'http://localhost:8080/healthz'
    registry_username: ${{ secrets.REGISTRY_USER }}
    registry_password: ${{ secrets.REGISTRY_TOKEN }}
    fail_on_severity: 'high'

1.x — composite action stays minimal, the tuning moves into argus.yml:

# argus.yml — checked into the repo
scanners:
  zap:
    target_url: "http://localhost:8080"
    scan_type: baseline
    api_spec: "http://localhost:8080/openapi.json"
    rules_file: ".zap/rules.tsv"
    max_duration_minutes: 30
    cmd_options:
      - "-z"
      - "-config view.locale=en_GB"
    healthcheck_url: "http://localhost:8080/healthz"
    registry_username_env: REGISTRY_USER
    registry_password_env: REGISTRY_TOKEN
# .github/workflows/security.yml — workflow stays small
- name: Run argus
  env:
    REGISTRY_USER: ${{ secrets.REGISTRY_USER }}
    REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }}
  uses: huntridge-labs/argus/.github/actions/scanner-zap@1.1.0
  with:
    fail_on_severity: 'high'

The same argus.yml works whether you invoke argus via the composite action, the SDK on any other CI platform, or locally — that's the point of the refactor.


Quick-reference table

0.6.x — composite action input 1.x — location
api_spec scanners.zap.api_spec
rules_file_name scanners.zap.rules_file
cmd_options scanners.zap.cmd_options (now list[string])
max_duration_minutes scanners.zap.max_duration_minutes
healthcheck_url scanners.zap.healthcheck_url
registry_username scanners.zap.registry_username_env (env-var name)
registry_password scanners.zap.registry_password_env (env-var name)
target_url unchanged — still a with: input on the action
app_image_ref, app_ports unchanged — still with: inputs
fail_on_severity, post_pr_comment, enable_code_security unchanged — still with: inputs

What changed for each input

api_spec

Switches the scan from zap-baseline.py to zap-api-scan.py. Used to discover endpoints from an OpenAPI / Swagger spec rather than crawling from a base URL.

# 0.6.x
with:
  scan_type: 'api'
  api_spec: 'http://localhost:8080/openapi.json'
# 1.x
scanners:
  zap:
    api_spec: "http://localhost:8080/openapi.json"
    # scan_type is implicit when api_spec is set

rules_file_namerules_file

Path to a ZAP .tsv ignore-rules file. Argus mounts it into the container at /zap/wrk/rules.tsv.

# 1.x
scanners:
  zap:
    rules_file: ".zap/rules.tsv"

cmd_options

Extra ZAP CLI flags. In 0.6.x this was a single string; in 1.x it's a YAML list so quoting nested arguments isn't a fight.

# 0.6.x
with:
  cmd_options: '-z -config view.locale=en_GB'
# 1.x
scanners:
  zap:
    cmd_options:
      - "-z"
      - "-config view.locale=en_GB"

max_duration_minutes

Hard cap on scan duration (translates to -T <minutes>).

# 1.x
scanners:
  zap:
    max_duration_minutes: 30

healthcheck_url

Argus polls this URL for 2xx readiness (default timeout 60s) before invoking ZAP. Useful when the SUT needs a moment to come up.

# 1.x
scanners:
  zap:
    healthcheck_url: "http://localhost:8080/healthz"

Registry auth — registry_username / registry_password

This is the biggest shape change. In 0.6.x you piped a secret value directly into the with: block. In 1.x you name the env var that holds the value; argus reads from os.environ at scan time. The value never lands in argus's per-scanner config dict, so it can't leak into argus-audit.json or argus.log.

# 0.6.x — secret VALUE on the with: block
with:
  registry_username: ${{ secrets.REGISTRY_USER }}
  registry_password: ${{ secrets.REGISTRY_TOKEN }}
# 1.x — argus.yml holds only the env-var NAME
scanners:
  zap:
    registry_username_env: REGISTRY_USER
    registry_password_env: REGISTRY_TOKEN
# 1.x — workflow exports the secret to the env
- name: Run argus
  env:
    REGISTRY_USER: ${{ secrets.REGISTRY_USER }}
    REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }}
  uses: huntridge-labs/argus/.github/actions/scanner-zap@1.1.0
  with:
    fail_on_severity: 'high'

The literal form (registry_username: ${{ secrets.X }}) is still accepted for back-compat, but argus validate will warn if the value matches a known vendor secret prefix (gh*, AKIA, AIza, glpat-, etc.). Migrate to the _env form when convenient.

If your invocation is interactive (one-off scan, not CI), the CLI also accepts --registry-password-stdin mirroring docker login --password-stdin:

echo "$REGISTRY_TOKEN" | argus scan --registry-password-stdin --config argus.yml

See docs/config-reference.md → Credential fields for the full credential-precedence table.


scanner-container — multi-image scanning

scanner-container's scan_mode input was removed alongside the ZAP changes. The auto-discover / matrix semantic now lives in argus.yml under the top-level containers: block — same posture as scanners.zap.* — so the same configuration works across every CI platform and locally.

Quick-reference table

0.6.x — composite action input 1.x — location
scan_mode: discover containers.discover: true + containers.search_paths: [...]
scan_mode: manifest containers.images: [...] (parse-container-config action emits a matrix)
allow_failure fail_on_severity: none (the canonical "informational scan" knob)
skip_summary Use security-summary composite's scan_statuses input

scan_mode: discovercontainers.discover

Auto-find every Dockerfile* in the named paths, build each locally, scan the built artifact, clean up.

# 0.6.x
- uses: huntridge-labs/argus/.github/actions/scanner-container@1.1.0
  with:
    scan_mode: discover
# 1.x — argus.yml
containers:
  discover: true
  search_paths:
    - "services"
    - "tools"
  scanners:
    - trivy
    - grype
    - syft

scan_mode: manifestcontainers.images

Explicit list of images to scan. In 0.6.x this came from a separate config file consumed by parse-container-config; in 1.x it's a first-class key in argus.yml. The parse-container-config action still works and emits a GHA matrix from the list when you want one job per image.

# 1.x — argus.yml
containers:
  images:
    - image: "nginx:1.27"
    - image: "ghcr.io/myorg/api:1.4.0@sha256:..."
    - dockerfile: "services/worker/Dockerfile"
      name: "worker"
  scanners:
    - trivy
    - grype

You can mix discovery and explicit entries — discovered Dockerfiles augment the list rather than replacing it. Worked examples in docs/container-scanning.md.

allow_failure / skip_summary

These mapped 1:1 to existing argus patterns and didn't need their own composite knobs:

  • allow_failure: true → set fail_on_severity: none on the composite. The action runs and emits findings; the job never fails on argus's behalf. The canonical "scan-but-don't-gate" knob, used the same way everywhere else.
  • skip_summary: true → the per-scanner summary still gets written to artifacts; what 0.6.x's skip_summary actually controlled was whether the run participated in the aggregated security-summary PR comment. In 1.x the security-summary composite's scan_statuses input is the explicit gate — pass the scanner's result per the silent-failure-gating pattern (ADR-016) and the aggregator includes or excludes it cleanly.

What stayed on the composite action

The action's with: surface keeps the inputs that are cross-scanner or cross-CI-platform-meaningless:

Input Why it stays
target_url Target identification — composite-level concern
app_image_ref / app_ports / app_env Container-target lifecycle, GHA-specific orchestration
startup_timeout GHA runner-side wait
scan_type Top-level scan-mode selector; can also live in argus.yml
scan_name Artifact naming
fail_on_severity Common across every scanner action
post_pr_comment GitHub-Actions-specific output channel
enable_code_security GitHub-Actions-specific (SARIF upload)

Web-app authentication (new in 1.x)

If your SUT needs login to scan past public pages, 1.x adds a scanners.zap.auth block that mounts a ZAP context file into the container. Argus stays out of the DOM-mapping business — you author the context (login URL, form selectors, logged-in regex) using ZAP's native format (the ZAP GUI can record it for you), and argus mounts it at /zap/wrk/context.xml.

scanners:
  zap:
    target_url: "https://app.example.com"
    auth:
      context_file: ".zap/context.xml"
      username_env: ZAP_APP_USER
      password_env: ZAP_APP_PASSWORD

The container reads ZAP_AUTH_USERNAME / ZAP_AUTH_PASSWORD env vars (argus exports the resolved credentials under those names); ZAP's {%username%} / {%password%} placeholders in the context file pick them up.


What to do next

  1. Run argus init in your repo root. It detects languages, frameworks, and existing config files, then writes a starter argus.yml. Edit to taste.
  2. Move the seven 0.6.x inputs above from your workflow's with: block into argus.yml under scanners.zap.* per the table.
  3. Wire registry / web-app secrets by setting env vars at the workflow-step level (env: block) and referencing the env-var names from argus.yml (the *_env keys).
  4. Validate with argus validate --strict — catches typos in the config and flags any leftover literal secret values that should move to env-var references.
  5. Run the scan: argus scan --config argus.yml, or invoke the trimmed composite action.

Questions or migration edge cases not covered above? Open an issue at https://github.com/huntridge-labs/argus/issues.