Skip to content

ci: add Trivy filesystem vulnerability audit - #99

Draft
andypost wants to merge 1 commit into
masterfrom
ci/trivy-security-audit
Draft

andypost wants to merge 1 commit into
masterfrom
ci/trivy-security-audit

Conversation

@andypost

@andypost andypost commented Jul 7, 2026

Copy link
Copy Markdown

Summary

Reworked from the CI portion of the stale draft andypost/unit#26. Adds a Trivy filesystem scan that catches known-vulnerable dependencies (Rust Cargo.lock, Go modules, npm, Python requirements, Docker base images) — exactly the class of stale-lockfile CVE that had to be triaged by hand this cycle (e.g. the wasmtime 35→36 and rustls-webpki bumps).

  • .github/workflows/security-audit.yml — runs Trivy on a weekly schedule, on push/PR touching dependency manifests, and via workflow_dispatch. Uploads SARIF to the Security tab and appends a severity-sorted Markdown report to the job summary + artifact.
  • .github/scripts/sort-trivy-results.py — parses Trivy JSON, sorts by severity then CVSS v3, renders the report, and can exit non-zero at a configurable threshold.

What changed vs the andypost/unit#26 draft

  • Report-only by default. Automatic runs (push/PR/schedule) never fail the build — findings surface via SARIF + summary. The blocking severity gate is opt-in through workflow_dispatch, so a newly-disclosed lockfile CVE annotates PRs instead of turning unrelated builds red.
  • Least privilege. security-events: write is scoped to the audit job; the workflow default is contents: read.
  • Dropped the cgroup/rootfs C fix (ships in freeunitorg/freeunit#95) and the docs/security-audit-findings.md file (a public list of suspected-unfixed issues — kept out of the repo for disclosure hygiene).
  • Fixed github/codeql-action/upload-sarif to @v3 (v4 does not exist) and push.branches to a list.

Actions use major-version tags, matching the existing workflows (Dependabot manages the bumps). SHA-pinning all actions is a reasonable repo-wide follow-up but out of scope for one workflow.

Testing

  • Workflow YAML parses; sort-trivy-results.py smoke-tested against a synthetic Trivy JSON: --fail-on-severity NONE exits 0 (report-only), an explicit HIGH gate exits 1, and rows sort CRITICAL → HIGH → LOW with CVSS as the tiebreak.

🤖 Generated with Claude Code

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a new Python script, sort-trivy-results.py, which parses Trivy JSON output and generates a severity-sorted Markdown security audit report. The review feedback identifies two potential runtime crashes: one where an explicitly null CVSS source can cause an AttributeError when fetching the V3Score, and another where a null 'Results' field can cause a TypeError during iteration. Both comments provide actionable code suggestions to make the JSON parsing more robust.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread .github/scripts/sort-trivy-results.py
Comment thread .github/scripts/sort-trivy-results.py
@andypost

andypost commented Jul 7, 2026

Copy link
Copy Markdown
Author

Pushed dbfbed29:

  • CI failure fixed — the action tag is v-prefixed: aquasecurity/trivy-action@v0.36.0. 0.36.0 without the v does not resolve, which is why the run failed in 3s at action setup. Confirmed v0.36.0 exists upstream.
  • Both @gemini-code-assist findings fixed — verified each crashes the old script on a real Trivy edge output: a CVSS source whose value is explicitly null (→ AttributeError) and "Results": null (→ TypeError). Now guarded with isinstance(..., dict) and report.get("Results") or [], plus a non-dict top-level report guard. Smoke-tested all three inputs → clean exit.

Scan the tree (Cargo.lock, go.mod/sum, package-lock, requirements,
Dockerfiles) with Trivy on a weekly schedule, on pushes/PRs touching
dependency manifests, and on demand. Findings are uploaded as SARIF to
the Security tab and rendered into a severity-sorted Markdown report
(sort-trivy-results.py) appended to the job summary and artifact.

Automatic runs are report-only and never fail the build; a blocking
severity gate is opt-in via workflow_dispatch. security-events:write is
scoped to the audit job, not the whole workflow.

The report parser guards Trivy edge outputs: a CVSS source whose value
is explicitly null, a null "Results" field, and a non-dict top-level
report, all of which would otherwise raise.

Reworked from the CI portion of andypost#26; the cgroup/rootfs
config-validation fix from that draft ships separately in
#95.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@andypost
andypost force-pushed the ci/trivy-security-audit branch from dbfbed2 to 0d6abc9 Compare July 7, 2026 19:48
@andypost
andypost requested review from TuzelKO and phpclub July 7, 2026 20:02
@TuzelKO

TuzelKO commented Jul 7, 2026

Copy link
Copy Markdown
Member

@claude review once

@claude

claude Bot commented Jul 7, 2026

Copy link
Copy Markdown

Claude finished @TuzelKO's task in 1m 22s —— View job


Review: Trivy security audit

Overall this is a well-scoped, well-documented workflow. Least-privilege permissions, report-only-by-default with an opt-in gate, and the executable bit on the script (100755) is correctly set. The two gemini-code-assist findings are genuinely fixed — the isinstance(...) guards at sort-trivy-results.py:67/:72/:95 and the report.get("Results") or [] at :98/:102 all check out. Nice.

One thing worth a second look before merge, plus a few minor notes.


🔴 Unrelated dependabot.yml change reverts #104

This PR's single commit removes target-branch: "pre-1.35.6" from .github/dependabot.yml:

   - package-ecosystem: github-actions
     directory: "/"
-    target-branch: "pre-1.35.6"
     schedule:
       interval: weekly

That line was added by the immediately-preceding commit on master0eda8d6 Add target branch for GitHub Actions updates (#104). This PR (0d6abc9) is built directly on top of #104 and deletes it again in the next commit. The change is out of scope for a "Trivy audit" PR and isn't mentioned anywhere in the description, so it looks like an accidental include (likely a stale-branch/rebase artifact) that would silently revert #104 and redirect GitHub-Actions Dependabot PRs back to master.

Suggest dropping this hunk unless the revert is intentional — and if it is, call it out in the PR body. Fix this →


🟡 Minor notes (non-blocking)

  • security-audit.yml:61-81 — Trivy runs a full filesystem scan twice (once for json, once for sarif). On a large repo that roughly doubles the job's most expensive step. Since the JSON already contains everything, you could run the scan once and convert, e.g. a single scan plus a trivy convert --format sarif ... / template step, or use the JSON as the SARIF source. Purely an efficiency nit — functionally correct as written.

  • sort-trivy-results.py:178-179json.load is unguarded. If Trivy produces an empty or malformed trivy-results.json (e.g. the scan step errored out but exit-code: 0 let the job continue), the script dies with a raw JSONDecodeError traceback rather than a clear "no/invalid Trivy output" message. Low priority given it's report-only, but a small try/except would make failures more legible.

  • Fork PRs and security-events: write. The pull_request trigger fires on forks, but forked-PR tokens don't get security-events: write, so the upload-sarif step (:83) will fail on external contributions. If the repo takes fork PRs, consider gating that step on github.event.pull_request.head.repo.fork == false (or switching to pull_request_target carefully). Not an issue for same-repo branches like this one.

  • No concurrency group. Rapid pushes to master touching a manifest can overlap runs. A concurrency: { group: ..., cancel-in-progress: true } block would tidy that up. Trivial.


Summary

The Python script and workflow logic are solid and the earlier review feedback is resolved. The only thing I'd genuinely flag before merge is the dependabot.yml hunk — it appears to unintentionally revert #104. Everything else is optional polish.
· ci/trivy-security-audit

Comment thread .github/dependabot.yml

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

because master is never the default branch as we do all preparations in pre-1.x.y branches

@phpclub phpclub added the X-Needs-Discussion Needs discussion (upstream) label Jul 8, 2026
@phpclub phpclub added this to the 1.35.6 milestone Jul 8, 2026
@andypost
andypost marked this pull request as draft July 12, 2026 23:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

X-Needs-Discussion Needs discussion (upstream)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants