André Ataíde
June 20, 2026

Wardex v2.1: Containerization, CI/CD Integration, and Multi-Framework Expansion

Docker images, GitHub Action, NIST CSF catalog, HTML reports — and what each actually enables.

Problem

Wardex v2.0 implemented a risk-based release gate with HMAC-signed CRA Article 14 artefact lifecycle. Running it required a Go toolchain or a precompiled binary. Integrating into CI/CD meant wrapping the binary in a custom container. Compliance reporting required piping JSON output into separate tools. Framework evaluation was limited to ISO 27001, NIS2, and DORA — which maps to part of the European regulatory landscape but omits the framework most organizations use as their base: NIST CSF.

v2.1 addresses these integration gaps. No new risk model, no new artefact types — the evaluation engine and Article 14 pipeline are unchanged. The work is in deployment surface and framework coverage.

What changed

Containerization (Docker + GHCR)

Wardex v2.1 ships multi-architecture Docker images on GitHub Container Registry. Six targets: linux/amd64, linux/arm64, darwin/amd64, darwin/arm64, windows/amd64, windows/arm64. Entry point accepts all CLI subcommands. Exit codes preserved.

docker pull ghcr.io/had-nu/wardex:2.1.1
docker run ghcr.io/had-nu/wardex:2.1.1 wardex evaluate \
  --config wardex-config.yaml \
  --evidence vulns.yaml

The images are built by GoReleaser and pushed via the release workflow (.goreleaser.yaml, Docker section with extra_files for config templates). Each image contains the same single binary — no init process, no sidecars. Suitable for ephemeral CI runners.

GitHub Action

The repository ships an action.yml that exposes Wardex as a composable GitHub Action. The action builds from Dockerfile in the repo — no external image dependency. Inputs: evidence, config, framework, output-format, output-file.

# .github/workflows/wardex-gate.yml
jobs:
  security-gate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Wardex Release Gate
        uses: had-nu/wardex@v2.1.1
        with:
          evidence: vulns.yaml
          config: .wardex/config.yaml
          framework: nist-csf

      - name: Handle exit codes
        if: failure()
        run: |
          case $? in
            10) echo "BLOCK: Gate risk exceeded" ;;
            11) echo "GAP: Compliance control missing" ;;
            12) echo "EXPLOIT: Active vulnerability" ;;
          esac

Exit code mapping in pkg/gate/gate.go:

CodeMeaningWhen
0PASSRisk within appetite
10BLOCKGate risk exceeded threshold
11GAP_WARNCompliance control not evidenced
12ACTIVE_EXPLOITActively exploited CVE detected

Code 12 triggers CRA Article 14 artefact generation (cmd/art14/art14.go). The artefact is HMAC-SHA256 signed with WARDEX_ACCEPT_SECRET and appended to the JSONL audit log. The pipeline must handle the routing — the action provides the exit code, not the notification infrastructure.

HTML Report Generation

wardex assess --output html renders compliance gap analysis as a standalone HTML page. Dark/light mode, SVG risk bars, framework badge summary. Template at pkg/report/templates/report.html (213 lines, embedded via //go:embed).

wardex assess controls.yaml \
  --framework iso27001 \
  --output html \
  --out-file compliance-posture.html

Generated page includes: control coverage table, Layer Delta (documented vs implemented), risk heatmap per asset, and Art14 artefact audit trail. No JavaScript runtime required.

NIST Cybersecurity Framework 2.0

31 controls mapped across Govern, Identify, Protect, Detect, Respond, Recover. File: pkg/catalog/nist_csf.yaml. Same input schema as ISO 27001, NIS2, and DORA — the assess command evaluates all four with a single control inventory:

wardex assess controls.yaml --framework iso27001   # Existing
wardex assess controls.yaml --framework nis2       # Existing
wardex assess controls.yaml --framework dora       # Existing
wardex assess controls.yaml --framework nist-csf   # v2.1

Framework-agnostic assessment means one YAML control file serves multiple regulatory regimes. The mapping from control to framework requirement is in the YAML — the engine reads the framework field on each control and filters accordingly (pkg/catalog/catalog.go:30).

CycloneDX SBOM and Cosign Signatures

Every v2.1 release includes:

cosign verify-blob \
  --signature wardex-2.1.1-linux-amd64.tar.gz.sig \
  --certificate wardex-2.1.1-linux-amd64.tar.gz.crt \
  --certificate-identity-regexp 'https://github.com/had-nu/wardex/.github/workflows/.*' \
  wardex-2.1.1-linux-amd64.tar.gz

No SLSA attestation — deferred from the v2.0 hardening spec and not implemented in v2.1. The signing setup (.goreleaser.yaml) uses cosign v3 format with explicit --output-signature and --output-certificate flags.

Integration patterns

Self-hosted pipelines

The Docker image works with any orchestrator that supports OCI images. Entry point is the binary — no assumptions about storage, networking, or sidecars.

# Dockerfile for pipeline gate
FROM ghcr.io/had-nu/wardex:2.1.1 AS wardex

FROM alpine:3.20
COPY --from=wardex /wardex /usr/local/bin/wardex
COPY wardex-config.yaml controls.yaml /config/
ENTRYPOINT ["wardex", "evaluate", "--config", "/config/wardex-config.yaml", "--evidence"]

This pattern copies only the binary — 18 MB static Go binary, no runtime dependencies. Evidence files are provided at invoke time via volume mount or as subsequent arguments.

SIEM ingestion

Wardex commands accept --output json. The JSON schemas are stable within a major version. Ingestion into Splunk, Elastic, or Datadog requires shipping the JSON output to their respective agents — no native connectors exist.

wardex evaluate --evidence vulns.yaml --config wardex-config.yaml --output json
wardex art14 list --output json

Exit code 12 events produce a JSON record with CVE, EPSS, CVSS, and Art14 artefact ID. The consuming pipeline decides what to do with it.

Art14 compliance workflow

The artefact lifecycle is CLI-native. No web UI, no database, no ticket system integration.

# Detect active exploit → artefact created automatically
wardex evaluate --evidence vulns.yaml --config wardex-config.yaml

# Review pending notifications
wardex art14 list

# Acknowledge within 72h window
wardex art14 mark-dispatched wardex-art14-CVE-2024-3094-1718882320

# Confirm remediation
wardex art14 finalize wardex-art14-CVE-2024-3094-1718882320 \
  --patch-date "$(date -Iseconds)"

The JSONL audit log (.wardex/audit.log) is the source of truth for the CRA compliance timeline. Each artefact includes cra_status with awareness_timestamp, early_warning_deadline, report_deadline, and correction_deadline. Deadlines are computed from detection timestamp, verified on mark-dispatched and finalize. Expiry detection is available via wardex art14 list --expired.

Architectural choices

Action builds from source, not from GHCR. The action.yml uses image: Dockerfile rather than image: docker://ghcr.io/had-nu/wardex:2.1.1. This guarantees the action runs the exact code at the referenced commit — no skew between the action version and the image tag. Cold start is ~1-2 min for the first invocation; subsequent runs on the same runner cache the layer. For security gates that run in parallel with compilation jobs, this is negligible.

Wardex is a CLI that produces signed artifacts and exit codes. No daemon, no database, no webhook receiver. The audit log is a JSONL file on the filesystem. The HTML report is a static page. Integration with SIEMs, ticket systems, or notification channels is the consuming pipeline's responsibility — Wardex provides the data (JSON stdout, exit codes, signed artefacts) and the contract (stable schema within a major version). This keeps adoption immediate and troubleshooting trivial.

SBOM + signing enables, but doesn't enforce, supply-chain verification. Release artifacts include CycloneDX SBOM and cosign .sig + .crt per binary. Whether the consumer verifies before deployment is their process. The chain stops at "this binary was built by the GitHub Actions workflow at revision X." No SLSA attestation in v2.1 — deferred from the v2.0 hardening spec and not yet implemented.

Multi-framework support is a data design decision, not a modelling one. The risk formula (CVSS × EPSS × layer delta) is framework-agnostic. Each control in the YAML declares which frameworks it applies to (pkg/catalog/*.yaml). The assess command filters, not adapts. Adding a framework is a catalog maintenance task, not an engine change.

No persistent EPSS cache. Each wardex enrich invocation hits the FIRST.org API. In pipelines scanning hundreds of CVEs, this may cause throttling. File-based caching with TTL is planned for v2.2 and is the subject of an ongoing integration specification for ENISA EUVD data.

Audit log is ephemeral in CI. The JSONL artefact log lives in the runner filesystem. Persisting it across pipeline runs requires actions/upload-artifact or equivalent — not built into the tool. Same pattern as the EPSS cache: external state is the environment's concern.

CRA Article 14 enforcement begins September 2026. Wardex produces the signed, timestamped artefacts that constitute regulatory evidence. The tool's scope is the evidence — the pipeline and compliance workflow around it belong to the operator.