Skip to content

fix(ci): periodic-security-scan never reports findings, and its thresholds do not match the publish gate #829

Description

@danbarr

Summary

The weekly periodic-security-scan workflow is designed to scan published images and open an issue when vulnerabilities are found. That alerting has never fired. There are two independent problems:

  1. A step-ordering bug aborts the job before the issue-filing logic runs, so nothing is ever reported.
  2. Even with that fixed, its thresholds do not match the publish gate: build-containers blocks publishing at high, while this workflow only files an issue at critical. A high finding is therefore serious enough to block shipping but not serious enough to notify anyone.

Evidence

The last five scheduled runs all failed:

Run Date
30780553811 2026-08-03
30233416175 2026-07-27
29713593389 2026-07-20
29220593745 2026-07-13
28764834213 2026-07-06

And no issue has ever been created by it. There are currently zero issues in the repo carrying the security or grype labels that the workflow is configured to apply.

Problem 1: the job aborts before it can report

Run Grype vulnerability scan (SARIF) uses severity-cutoff: "low" and does not set fail-build. anchore/scan-action defaults fail-build to true, so with a low cutoff and no only-fixed the step fails on essentially any finding, which fails the job.

Upload SARIF to GitHub Security carries if: always() and therefore survives. The three steps that perform the alerting do not, so they are skipped. Step outcomes from scan-images (uvx/mcp-neo4j-cypher/spec.yaml) in the 2026-08-03 run:

success  Log in to Container Registry
failure  Run Grype vulnerability scan (SARIF)
success  Upload SARIF to GitHub Security      <- if: always()
skipped  Run Grype vulnerability scan (JSON)
skipped  Check for critical issues
skipped  Create issue for critical findings
success  Upload scan results

This also explains why GitHub code scanning has periodic-grype-* data while no issues exist: the SARIF upload works, and only the reporting path is dead.

Problem 2: thresholds do not match the publish gate

build-containers gate periodic-security-scan
severity-cutoff high low
only-fixed true unset, so false
alerting threshold blocks publish at high files an issue only when critical > 0

This is inconsistent in both directions:

  • Scanning at low without only-fixed surfaces findings that have no fix available, which nobody can action, and is what makes the step fail unconditionally.
  • Alerting only on criticals means a high finding, which by policy is enough to block a publish, produces no notification at all. high is computed into $GITHUB_OUTPUT at line 117 and then never used in the should_create_issue decision at line 122, which reads like the intent was there and got dropped.

Impact

Nothing has been reported since at least 2026-07-06. Fixing only problem 1 would surface the criticals:

Every high that currently blocks a build would still go unreported, including brightdata-mcp (3x @modelcontextprotocol/sdk), chroma-mcp (5x mcp), adb-mysql-mcp-server (cryptography), astra-db-mcp (3x undici), and onchain-mcp (2x SDK). See #830 for the full backlog.

Put differently: aligning the threshold is the difference between reporting 2 servers and reporting 10.

Suggested fix

Two things need aligning, not just one.

1. Let the job run to completion. Set fail-build: false on both scan steps so Check for critical issues decides the outcome rather than the scanner aborting the job. Alternatively add if: always() to the three downstream steps, but fail-build: false is cleaner given that step already computes the counts.

2. Align the thresholds with the publish gate. Match build-containers: severity-cutoff: high and only-fixed: true on both scan steps, and set should_create_issue=true when critical + high > 0.

That way "the weekly scan is red" and "this image could not be published today" mean the same thing, which is the useful invariant. It also makes a red run actionable, rather than the current state where it is always red because some unfixable low-severity CVE exists somewhere.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingneeds-triageIssue needs initial triage by a maintainer

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions