Block deployments of actively exploited vulnerabilities

Your CI pipeline can move fast. But without a security gate, it can deploy software with known active exploits just as fast. attestd adds a single deterministic check before any build reaches production.

why the naive approach fails

Most pipelines already run a scanner. The gap is not "no CVE data." It is a gate that can fail the job without a human reading the report.

  • CVSS-based gates produce noise. A score of 7.4 does not tell CI whether to exit 1 or continue.
  • Manifest scanners return issue lists. A merge bot or deploy job still needs a categorical fail condition.
  • Compromised packages often have no CVE at the moment CI pulls them. A CVE-only step returns clean.
  • Local artifact scanners need an image or SBOM first. A version pin in a lockfile is available earlier, before the build artifact exists.
what to branch on

Fail the job on categorical fields. Keep the exception path for humans, not the default path.

FieldMeaning for this use case
risk_stateFail on critical by default. Optionally fail on high for production deploy jobs.
actively_exploitedStrong fail condition even when risk_state is high rather than critical. Confirmed exploitation in the wild.
supply_chain.compromisedFail immediately. Confirmed malicious publish, independent of CVE coverage.
fixed_versionPrint in the job log so the fix PR has a concrete upgrade target.
risk_factorsHuman-readable reasons for the fail. Surface them with ::error:: or your CI annotation format.
the request
bash
curl "https://api.attestd.io/v1/check?product=nginx&version=${VERSION}" \
  -H "Authorization: Bearer ${ATTESTD_API_KEY}"
integration
.github/workflows/deploy.yml
- name: attestd risk check
  env:
    ATTESTD_API_KEY: ${{ secrets.ATTESTD_API_KEY }}
    VERSION: ${{ env.NGINX_VERSION }}
  run: |
    RESULT=$(curl -sS "https://api.attestd.io/v1/check?product=nginx&version=$VERSION" \
      -H "Authorization: Bearer $ATTESTD_API_KEY")

    RISK=$(echo "$RESULT" | jq -r '.risk_state')
    COMPROMISED=$(echo "$RESULT" | jq -r '.supply_chain.compromised // false')
    FIXED=$(echo "$RESULT" | jq -r '.fixed_version // empty')

    if [ "$COMPROMISED" = "true" ] || [ "$RISK" = "critical" ]; then
      echo "::error::Deployment blocked (risk_state=$RISK compromised=$COMPROMISED)"
      echo "$RESULT" | jq -r '.risk_factors // [] | join(", ")'
      [ -n "$FIXED" ] && echo "Upgrade to: $FIXED"
      exit 1
    fi

    echo "Risk check passed. Proceeding with deployment."
how to wire it
  1. 01

    Add a check step before deploy

    Call /v1/check for each pinned runtime dependency you care about: base images, language runtimes, and high-risk packages. Fail on critical or supply_chain.compromised.

  2. 02

    Wire the API key as a CI secret

    Store ATTESTD_API_KEY in GitHub Actions, GitLab CI, or your runner secret store. Never commit it. Free tier is enough to validate the gate.

  3. 03

    Annotate failures with the upgrade path

    Log fixed_version and risk_factors in the job output so the next PR has a concrete version bump instead of a vague "security failed" message.

operational outcome

Automated deployments stop before reaching production.

When an actively exploited critical vulnerability or a confirmed supply chain compromise is detected, the CI step fails with a clear error and the fixed version. No human review needed for the gate itself. Only for the exception.

in the wild

[email protected] and [email protected] affected an estimated 10% of cloud environments within two hours of publication. A CI gate that checked those versions at install time would have blocked the compromised release before any downstream build pulled it.

The same supply chain pattern shows up in ViteVenom and JoyFill: packages with no CVE that return supply_chain.compromised: true. A CVE-only scanner would have greenlit the install.

For CI-adjacent tool comparisons, see Attestd vs Grype (artifact scan vs name@version lookup) and Attestd vs Sonatype Guide (deterministic risk_state vs CVSS advisory output in a gate).