Skip to content

Scanners

# Scanner Configuration Reference Complete configuration reference for all available security scanners.

Architecture

The argus Python SDK (argus scan) is the primary interface for running scanners. Composite actions remain available for GitHub Actions users.

  • Argus SDK (argus/) - Primary interface, works locally and in any CI
  • Composite Actions (.github/actions/scanner-*/) - GitHub Actions integration
  • Example Workflows (examples/github-enterprise/) - Templates for GHES users

SDK usage (recommended):

pip install argus-security
argus scan gitleaks bandit --severity-threshold high

Post-scan triage: Once you have argus-results.json, run argus view terminal for an interactive terminal UI — filter by severity, product, or scanner; export to CSV/JSON/Markdown/SARIF; open an executive dashboard. Install via pip install 'argus-security[terminal]', or combine with scanning in one shot: argus scan --interface=terminal.

Composite action usage (GitHub Actions):

jobs:
  security:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6

      - uses: huntridge-labs/argus/.github/actions/scanner-gitleaks@1.1.0
        with:
          enable_code_security: true
          fail_on_severity: high
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

See examples/github-enterprise/ for GHES templates.


Table of Contents

SAST Scanners

CodeQL

GitHub's semantic code analysis engine for finding security vulnerabilities and coding errors.

Supported languages: python, javascript, typescript, java, csharp, cpp, go, ruby

Configuration & Examples **Configuration:** | Input | Description | Default | Required | |-------|-------------|---------|----------| | `codeql_languages` | Comma-separated list of languages | `python,javascript` | No | | `enable_code_security` | Upload to GitHub Security tab | `false` | No | | `post_pr_comment` | Post findings as PR comments | `true` | No | **Example:**
with:
  scanners: codeql
  codeql_languages: 'python,javascript,go'
  enable_code_security: true

Gitleaks

Scans git history and code for hardcoded secrets, API keys, passwords, and tokens.

Scan behavior: Scans PR changes, new commits, or full history depending on event type.

Configuration & Examples **Configuration:** | Input | Description | Default | Required | |-------|-------------|---------|----------| | `gitleaks_enable_comments` | Enable inline PR comments | `true` | No | | `gitleaks_notify_user_list` | Users to notify (e.g., `@user1,@user2`) | `''` | No | | `gitleaks_enable_summary` | Enable job summary | `true` | No | | `gitleaks_enable_upload_artifact` | Upload SARIF artifact | `true` | No | | `gitleaks_config` | Path to custom config file | `''` | No | | `enable_code_security` | Upload to GitHub Security tab | `false` | No | | `fail_on_severity` | Fail on any secret detection | `none` | No | **Required secrets:** | Secret | Description | Required | |--------|-------------|----------| | `GITLEAKS_LICENSE` | License key from [gitleaks.io](https://gitleaks.io) | Yes (for organizations) | **Scan behavior by event type:** - `pull_request`: Scans all changes in the PR - `push`: Scans only new commits - `workflow_dispatch`/`schedule`: Full repository history scan **Example:**
with:
  scanners: gitleaks
  gitleaks_enable_comments: true
  gitleaks_notify_user_list: '@security-team'
  fail_on_severity: critical
secrets:
  GITLEAKS_LICENSE: ${{ secrets.GITLEAKS_LICENSE }}

Bandit

Python security linter for finding common security issues using static analysis.

Severity levels: LOW, MEDIUM, HIGH

Configuration & Examples **Configuration:** | Input | Description | Default | Required | |-------|-------------|---------|----------| | `enable_code_security` | Upload to GitHub Security tab | `false` | No | | `post_pr_comment` | Post findings as PR comments | `true` | No | | `fail_on_severity` | Fail on any finding | `none` | No | **Example:**
with:
  scanners: bandit
  enable_code_security: true
  fail_on_severity: high

OpenGrep (Semgrep)

Fast, customizable static analysis with extensive rule sets for multiple languages.

Configuration & Examples **Configuration:** | Input | Description | Default | Required | |-------|-------------|---------|----------| | `enable_code_security` | Upload to GitHub Security tab | `false` | No | | `post_pr_comment` | Post findings as PR comments | `true` | No | | `fail_on_severity` | Severity threshold | `none` | No | **Example:**
with:
  scanners: opengrep
  enable_code_security: true
  fail_on_severity: medium

Container Scanners

Trivy Container

Comprehensive vulnerability scanner for container images and filesystems.

Configuration & Examples **Configuration:** | Input | Description | Default | Required | |-------|-------------|---------|----------| | `image_ref` | Container image to scan | - | Yes | | `registry_username` | Username for private registry authentication | `''` | No | | `enable_code_security` | Upload to GitHub Security tab | `false` | No | | `post_pr_comment` | Post findings as PR comments | `false` | No | | `fail_on_severity` | Severity threshold | `none` | No | **Required secrets (for private registries):** | Secret | Description | Required | |--------|-------------|----------| | `registry_password` | Password/token for registry authentication | No | **Example:**
# Public image
with:
  scanners: trivy-container
  image_ref: 'nginx:latest'
  enable_code_security: true
  fail_on_severity: critical

# Private registry
with:
  scanners: trivy-container
  image_ref: 'ghcr.io/myorg/myapp:latest'
  registry_username: ${{ github.actor }}
  enable_code_security: true
  fail_on_severity: critical
secrets:
  registry_password: ${{ secrets.GITHUB_TOKEN }}

Grype

Fast, accurate vulnerability scanner with excellent detection rates.

Configuration & Examples **Configuration:** | Input | Description | Default | Required | |-------|-------------|---------|----------| | `image_ref` | Container image to scan | - | Yes | | `registry_username` | Username for private registry authentication | `''` | No | | `enable_code_security` | Upload to GitHub Security tab | `false` | No | | `post_pr_comment` | Post findings as PR comments | `false` | No | | `fail_on_severity` | Severity threshold | `none` | No | **Required secrets (for private registries):** | Secret | Description | Required | |--------|-------------|----------| | `registry_password` | Password/token for registry authentication | No | **Example:**
# Public image
with:
  scanners: grype
  image_ref: 'nginx:latest'
  fail_on_severity: high

# Private registry
with:
  scanners: grype
  image_ref: 'ghcr.io/myorg/myapp:latest'
  registry_username: ${{ github.actor }}
  fail_on_severity: high
secrets:
  registry_password: ${{ secrets.GITHUB_TOKEN }}

Syft (SBOM)

Generates detailed Software Bill of Materials (SBOM) for images and filesystems.

Configuration & Examples **Configuration:** | Input | Description | Default | Required | |-------|-------------|---------|----------| | `scan-path` | Directory or file path to scan | `.` | No | | `scan-image` | Container image to scan | - | No | | `registry_username` | Username for private registry authentication | `''` | No | | `enable_code_security` | Upload to GitHub Security tab | `false` | No | **Required secrets (for private registries):** | Secret | Description | Required | |--------|-------------|----------| | `registry_password` | Password/token for registry authentication | No | **Example:**
# Scan filesystem
with:
  scanners: sbom
  scan-path: 'dist/'

# Scan public container image
with:
  scanners: sbom
  scan-image: 'nginx:latest'

# Scan private container image
with:
  scanners: sbom
  scan-image: 'ghcr.io/myorg/myapp:latest'
  registry_username: ${{ github.actor }}
secrets:
  registry_password: ${{ secrets.GITHUB_TOKEN }}

Exposed-port surface

Reports the network endpoints a container image declares via Dockerfile EXPOSE — separate from whether those endpoints have known CVEs. "Image exposes 6379/tcp" is a different question from "image has a vulnerable Redis package" and most security reviewers want both. Runs as a sub-scanner inside the container scanner (no new module); the data is free since the container scanner already runs docker inspect on every pull.

Output shape: one Finding per declared port:

INFO   EXPOSE-8080-tcp    Port 8080/tcp declared exposed
MEDIUM EXPOSE-22-tcp      Port 22/tcp (SSH) declared exposed
MEDIUM EXPOSE-3306-tcp    Port 3306/tcp (MySQL) declared exposed

Findings flow through every reporter (terminal, markdown, sarif, json, github, gitlab, junit), --severity-threshold filtering, audit trail, and the view-terminal / view-browser UIs without per-reporter custom code.

Built-in risky-defaults watchlist (MEDIUM severity by default — each entry's rationale is cited in argus/scanners/container.py::RISKY_PORTS):

Port Service Port Service
21/tcp FTP 3306/tcp MySQL
22/tcp SSH 3389/tcp RDP
23/tcp Telnet 5432/tcp PostgreSQL
25/tcp SMTP 6379/tcp Redis
110/tcp POP3 9200/tcp Elasticsearch
143/tcp IMAP 11211/tcp Memcached
161/udp SNMP 27017/tcp MongoDB
389/tcp LDAP
445/tcp SMB

Configuring via argus.yml:

scanners:
  container:
    image_ref: "myapp:latest"
    # Default sub-scanner set; remove "exposure" to opt out
    scanners: "trivy,grype,syft,exposure,services"
    # Override the built-in WARN list (replaces the defaults).
    # Pass [] to demote every declared port to INFO.
    expose_warn_ports:
      - 22/tcp
      - 3306/tcp
      - 8080/tcp           # promote app port to WARN
    # Suppress findings entirely for ports the team has accepted.
    expose_ignore_ports:
      - 443/tcp
      - 9090/tcp

Both lists accept "PORT/PROTO" strings; bare "PORT" defaults to tcp. Schema validator errors on malformed entries at config-load time. See docs/config-reference.md for the full schema.

Out of scope: runtime port enumeration (start the container, probe with nmap/ss). Static EXPOSE data is the bulk of the value at a fraction of the operational cost.

Service enumeration

Walks the container image's filesystem at scan time, parses systemd unit files (/etc/systemd/system, /lib/systemd/system, /usr/lib/systemd/system) and SysV init scripts (/etc/init.d/), and emits one Finding per declared service. Runs as a sub-scanner inside the container scanner — same Docker requirement, no new heavy dependencies. Works on distroless / scratch images because file extraction goes through docker create + docker cp (tar stream) rather than docker run. Per-file reads are bounded at 1 MB to prevent hostile-image memory blow-up.

Output shape: one Finding per service:

INFO   SVC-nginx       Service "nginx" declared (systemd unit)
MEDIUM SVC-sshd        Service "sshd" declared (systemd unit)
MEDIUM SVC-postgresql  Service "postgresql" declared (systemd unit)

Findings flow through every reporter (terminal, markdown, sarif, json, github, gitlab, junit), --severity-threshold filtering, audit trail, and the view-terminal / view-browser UIs without per-reporter custom code.

Built-in risky-defaults watchlist (MEDIUM severity by default — each entry cites a "why" in argus/scanners/container.py::RISKY_SERVICES):

Service Service Service
sshd postgresql redis-server
telnetd mysqld redis
vsftpd mariadb mongod
memcached elasticsearch snmpd
rpcbind nfs-server

Configuring via argus.yml:

scanners:
  container:
    image_ref: "myapp:latest"
    # Default sub-scanner set; remove "services" to opt out
    scanners: "trivy,grype,syft,exposure,services"
    # Override the built-in WARN list (replaces the defaults).
    # Pass [] to demote every declared service to INFO.
    services_warn:
      - sshd
      - postgresql
      - nginx               # promote app service to WARN
    # Suppress findings entirely for services the team has accepted.
    services_ignore:
      - cron
      - rsyslog

Matching is case-insensitive. .timer, .socket, .target, and .mount units are skipped (only .service units and init.d scripts are reported). Schema validator errors on malformed entries at config-load time. See docs/config-reference.md for the full schema.

Out of scope: runtime service probing (start the container, observe what binds). Static unit-file declarations are the bulk of the value at a fraction of the operational cost.

Infrastructure Scanners

Trivy IaC

Scans Infrastructure as Code files for misconfigurations and security issues.

Supported frameworks: Terraform, CloudFormation, Kubernetes, Dockerfile

Configuration & Examples **Configuration:** | Input | Description | Default | Required | |-------|-------------|---------|----------| | `iac_path` | Path to IaC directory | `infrastructure` | No | | `enable_code_security` | Upload to GitHub Security tab | `false` | No | | `post_pr_comment` | Post findings as PR comments | `false` | No | | `fail_on_severity` | Severity threshold | `none` | No | **Example:**
with:
  scanners: trivy-iac
  iac_path: 'terraform/'
  enable_code_security: true
  fail_on_severity: high

Checkov

Policy as Code scanner for cloud infrastructure configurations.

Configuration & Examples **Configuration:** | Input | Description | Default | Required | |-------|-------------|---------|----------| | `iac_path` | Path to IaC directory | `infrastructure` | No | | `framework` | IaC framework | `terraform` | No | | `enable_code_security` | Upload to GitHub Security tab | `false` | No | | `post_pr_comment` | Post findings as PR comments | `false` | No | | `fail_on_severity` | Fail on any check failure | `none` | No | **Example:**
with:
  scanners: checkov
  iac_path: 'infrastructure/'
  framework: terraform
  enable_code_security: true

Malware Scanner

ClamAV

Open-source antivirus engine for detecting trojans, viruses, and malware.

Configuration & Examples **Configuration:** | Input | Description | Default | Required | |-------|-------------|---------|----------| | `clamav_scan_path` | Path to scan | `.` | No | | `enable_code_security` | Upload to GitHub Security tab | `false` | No | | `post_pr_comment` | Post findings as PR comments | `true` | No | | `fail_on_severity` | Fail if malware detected | `none` | No | **Example:**
# Scan entire repository
with:
  scanners: clamav

# Scan specific directory
with:
  scanners: clamav
  clamav_scan_path: 'uploads/'
  fail_on_severity: critical

DAST Scanners

ZAP

ZAP (Zed Attack Proxy) provides Dynamic Application Security Testing (DAST) for running web applications and APIs.

Key Features: - Config-file driven: Define multiple scans with different targets, types, and settings in a single YAML/JSON file - Parallel scan groups: Run URL-based and container-based scans in parallel pipelines - Flexible defaults: Set defaults once, override per-scan as needed - Multiple target modes: URL (already running), docker-run (single container), or compose (multi-container) - Multiple scan types: baseline, full, or API scans with OpenAPI/Swagger specs


1. Create a ZAP config file (e.g., .github/zap-config.yml):

# Simple flat config - single target mode
defaults:
  max_duration_minutes: 10
  fail_on_severity: medium
  allow_failure: false

target:
  mode: url

scans:
  - name: baseline-scan
    type: baseline
    target_url: https://example.com

  - name: api-scan
    type: api
    target_url: https://api.example.com
    api_spec: https://api.example.com/openapi.json

2. Run the scan:

# Via SDK
python -m argus scan zap --config argus.yml

Or via composite action in GitHub Actions:

jobs:
  zap-scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
      - uses: huntridge-labs/argus/.github/actions/scanner-zap@1.1.0
        with:
          zap_config_file: .github/zap-config.yml

ADR-024 decided that ZAP-specific tuning lives in the standard argus.yml under scanners.zap.*, working identically across SDK-direct, composite- action, and any-CI use. The composite action surface stays minimal (target identification + common cross-scanner inputs); everything else configures from one place.

scanners:
  zap:
    enabled: true
    target_url: "https://app.example.com"

    # Tuning (container backend; all optional)
    scan_type: baseline                 # baseline | full | api (api is implicit
                                         # when api_spec is set)
    api_spec: "https://app.example.com/openapi.json"
    rules_file: ".zap/rules.tsv"         # mounted at /zap/wrk/rules.tsv
    max_duration_minutes: 30
    cmd_options:
      - "-z"
      - "-config view.locale=en_GB"

    # Registry auth for app_image_ref pulls (private images).
    # Name the env var; argus reads os.environ at scan time.
    # Setting registry_password directly is back-compat-supported
    # but warned at config-load if the value looks like a vendor secret.
    registry_username_env: REGISTRY_USER
    registry_password_env: REGISTRY_TOKEN

    # Web-app authentication — ZAP context-file passthrough.
    # User authors the context file (DOM selectors, logged-in regex,
    # session management); argus mounts it and exports ZAP_AUTH_USERNAME
    # / ZAP_AUTH_PASSWORD into the container for the {%username%} /
    # {%password%} placeholders to substitute.
    auth:
      context_file: ".zap/context.xml"
      username_env: ZAP_APP_USER
      password_env: ZAP_APP_PASSWORD

See docs/config-reference.md for the full credential-field contract and the list of every scanners.zap.* key.


Configuration Options #### Configuration Options **Via workflow inputs (legacy/simple mode):** | Input | Description | Default | Required | |-------|-------------|---------|----------| | `scanners` | Include `zap` (opt-in; not included in `all`) | - | Yes | | `zap_config_file` | Path to ZAP config file (YAML/JSON). **Recommended approach** - drives all scan configuration. | `''` | No | | `zap_scan_mode` | `url`, `docker-run`, or `compose` (ignored if `zap_config_file` set) | `url` | No | | `zap_target_urls` | Comma-separated URLs to scan (ignored if `zap_config_file` set) | `''` | Conditional | | `zap_scan_type` | `baseline`, `full`, or `api` (ignored if `zap_config_file` set) | `baseline` | No | | `zap_api_spec` | OpenAPI/Swagger spec URL or path (ignored if `zap_config_file` set) | `''` | Conditional | | `allow_failure` | Allow workflow to continue on failures | `false` | No | | `severity_threshold` | Minimum severity to fail (`none`, `low`, `medium`, `high`, `critical`) | `high` | No | > **Note**: When `zap_config_file` is provided, it takes precedence and other `zap_*` inputs are ignored.

Config File Reference #### Config File Reference **Schema URL**: [`zap-config.schema.json`](https://github.com/huntridge-labs/argus/blob/1.1.0/.github/actions/parse-zap-config/schemas/zap-config.schema.json) **Two config styles supported:** 1. **Flat** - single target, multiple scans 2. **Grouped** - multiple scan groups with different targets (enables parallel pipelines) ##### Flat Config Example All scans share the same target configuration:
target:
  mode: url  # or docker-run, compose

defaults:
  max_duration_minutes: 10
  fail_on_severity: medium
  allow_failure: false
  post_pr_comment: true

scans:
  - name: baseline-scan
    type: baseline
    target_url: https://app.example.com

  - name: api-scan
    type: api
    target_url: https://api.example.com
    api_spec: https://api.example.com/openapi.json
    fail_on_severity: high  # Override default
##### Grouped Config Example (Parallel Pipelines) Create separate scan groups with their own targets - ideal for running URL scans and container scans in parallel:
defaults:
  max_duration_minutes: 10
  fail_on_severity: medium
  allow_failure: true

scan_groups:
  # Group 1: URL-based scans (external targets)
  - name: url-scans
    description: "External URL Scans"
    target:
      mode: url
    scans:
      - name: baseline-prod
        type: baseline
        target_url: https://example.com

      - name: api-prod
        type: api
        target_url: https://api.example.com
        api_spec: https://api.example.com/openapi.json

  # Group 2: Container scans (start app, then scan)
  - name: docker-scans
    description: "Container-based Scans"
    target:
      mode: docker-run
      image: ghcr.io/myorg/myapp:latest
      ports: "8080:8080"
    defaults:
      target_url: http://localhost:8080
    scans:
      - name: baseline-container
        type: baseline

      - name: full-container
        type: full
        max_duration_minutes: 20
##### Target Configuration **`target.mode`** options: - **`url`** (default): Scan already-running endpoints - **`docker-run`**: Start a single container, then scan - **`compose`**: Start a docker-compose stack, then scan **Docker-run mode example:**
target:
  mode: docker-run
  image: myapp:latest
  ports: "3000:3000,8080:8080"
  healthcheck_url: http://localhost:3000/health

  # Optional: build from local Dockerfile
  build:
    context: .
    dockerfile: ./Dockerfile
    tag: myapp:test

  # Optional: private registry auth
  registry:
    host: ghcr.io
    username: ${{ github.actor }}
    auth_secret: GITHUB_TOKEN  # Secret name
**Compose mode example:**
target:
  mode: compose
  compose_file: docker-compose.test.yml
  compose_build: true
  healthcheck_url: http://localhost:8080/health
##### Scan Configuration **Required fields:** - `name`: Unique scan identifier (alphanumeric, hyphens, underscores) - `type`: `baseline`, `full`, or `api` **Common fields:** - `target_url`: Target URL to scan (required for baseline/full scans) - `api_spec`: OpenAPI/Swagger spec URL or file path (required for api scans) - `max_duration_minutes`: Maximum scan duration (1-120, default: 10) - `fail_on_severity`: Fail threshold - `none`, `low`, `medium`, `high`, `critical` (default: `none`) - `allow_failure`: Continue workflow on failure (default: `false`) - `post_pr_comment`: Post scan results as PR comment (default: `false`) **Advanced fields:** - `healthcheck_url`: Override target healthcheck (waits for 200 response before scanning) - `rules_file`: Path to ZAP rules file (.tsv) to ignore specific alerts - `context_file`: Path to ZAP context file for session/auth - `cmd_options`: Additional ZAP CLI options (e.g., `-z "-config api.addrs.addr.name=.*"`) **Authentication (header-based):**
scans:
  - name: authenticated-scan
    type: baseline
    target_url: https://app.example.com
    auth:
      header_name: Authorization
      header_secret: API_TOKEN  # GitHub secret name
      # OR for non-secret values:
      # header_value: "Bearer ${MY_TOKEN}"
##### Defaults Set defaults at root or per-group that apply to all scans (scans can override):
defaults:
  max_duration_minutes: 15
  fail_on_severity: medium
  allow_failure: false
  post_pr_comment: true
  target_url: http://localhost:8080  # Default target
  rules_file: .zap/rules.tsv
  auth:
    header_name: X-API-Key
    header_secret: API_KEY

Complete Examples #### Complete Examples ##### Example 1: Simple URL Scan
# .github/zap-config.yml
target:
  mode: url

scans:
  - name: baseline
    type: baseline
    target_url: https://example.com
    max_duration_minutes: 5
    fail_on_severity: high
# Via SDK
python -m argus scan zap --config argus.yml
##### Example 2: Container Scan with Build
# .github/zap-config.yml
target:
  mode: docker-run
  build:
    context: .
    dockerfile: Dockerfile
    tag: app:test
  ports: "8080:8080"
  healthcheck_url: http://localhost:8080/health

defaults:
  max_duration_minutes: 10
  fail_on_severity: medium
  target_url: http://localhost:8080

scans:
  - name: baseline
    type: baseline

  - name: api
    type: api
    api_spec: http://localhost:8080/openapi.json
##### Example 3: Parallel Pipelines (Grouped)
# .github/zap-config.yml
defaults:
  max_duration_minutes: 10
  fail_on_severity: medium

scan_groups:
  - name: url-scans
    description: "Production URL Scans"
    target:
      mode: url
    scans:
      - name: prod-baseline
        type: baseline
        target_url: https://example.com

      - name: prod-api
        type: api
        target_url: https://api.example.com
        api_spec: https://api.example.com/openapi.json

  - name: container-scans
    description: "Local Container Scans"
    target:
      mode: docker-run
      image: myapp:latest
      ports: "3000:3000"
    defaults:
      target_url: http://localhost:3000
    scans:
      - name: container-baseline
        type: baseline

      - name: container-full
        type: full
        max_duration_minutes: 20
This creates two parallel scan pipelines in your GitHub Actions workflow - one for URL scans and one for container scans.

Legacy Input-Based Configuration #### Legacy Input-Based Configuration For simple single-scan scenarios via composite actions, you can pass inputs directly (no config file): **URL-only scan:**
jobs:
  zap-scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
      - uses: huntridge-labs/argus/.github/actions/scanner-zap@1.1.0
        with:
          zap_scan_mode: url
          zap_target_urls: https://example.com
          fail_on_severity: medium
> **Note**: Input-based configuration is limited to single scans. Use config files for multiple scans or advanced features.

Common Configuration Patterns

Enable GitHub Security Tab (Actions)

Upload SARIF results when using composite actions:

with:
  enable_code_security: true

Disable PR Comments (Actions)

Useful for scheduled scans using composite actions:

with:
  post_pr_comment: false

Scanner Selection Patterns

SDK:

# SAST only
python -m argus scan codeql opengrep bandit gitleaks

# Infrastructure only
python -m argus scan trivy-iac checkov

# Container only
python -m argus scan container

# Focused mix
python -m argus scan gitleaks trivy-iac checkov

Composite actions: Use individual scanner-* actions in your workflow steps.