Skip to content

ci(security): give the image scans their own code-scanning tool (#2266) - #2396

Merged
cyberantonz merged 3 commits into
mainfrom
ci/drop-image-sarif-upload
Aug 11, 2026
Merged

ci(security): give the image scans their own code-scanning tool (#2266)#2396
cyberantonz merged 3 commits into
mainfrom
ci/drop-image-sarif-upload

Conversation

@Gregory91G

@Gregory91G Gregory91G commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Splits the noisy half of the Trivy check away from the useful half, without giving up the image findings. Follow-up to #2266.

The mechanism

Code scanning creates one check per tool, not per category, and reports that tool's missing configurations. Today the Trivy tool owns nineteen categories:

category produced by present on a PR
trivy-fs trivy.yml (pull_request, push, schedule) yes
trivy-helm trivy.yml yes
trivy-image:* × 17 trivy-images.yml (schedule only) never

trivy-images.yml scans published images, which a pull request does not have, so those seventeen can never be produced anywhere except the nightly cron. The Trivy check therefore reports them missing on every analysis this repository uploads — on PRs and on pushes to main alike:

#2371  Trivy  failure  17 configurations not found
#2367  Trivy  neutral  17 configurations not found
main   Trivy  neutral  17 configurations not found

Semgrep OSS sits beside it reporting success — No new alerts in code changed by this pull request, for one reason only: its single category is produced everywhere.

The statement is accurate and permanently unactionable, and it costs more than noise — the check is genuinely useful for trivy-fs, trivy-helm and semgrep, so if one of those ever stopped uploading, this is what would say so, under seventeen bullet points nobody reads.

What this PR does

One step, before the upload, rewrites runs[].tool.driver.name in the image SARIF from Trivy to Trivy Images with jq. That is the field code scanning keys the tool on, so the two sets separate:

  • Trivy keeps trivy-fs and trivy-helm — both produced by every PR, so its check goes clean, like Semgrep's.
  • Trivy Images owns the seventeen image categories, produced by the nightly.

What stays exactly as it is

The scan, the per-image summary table, Fail on a fixable critical (#2238), the per-image category:, the .trivyignore handling, and the alerts themselves. The image findings remain in the Security tab — only the tool they are filed under changes. +14 lines, nothing removed.

What to look at in the nightly

https://github.com/constructorfabric/insight/actions/workflows/trivy-images.yml — filter Event: schedule. One job per image; the run page's Summary block carries a table per image, grouped by package:

Ecosystem Package Max CVEs Fixed in

Latest run for reference: https://github.com/constructorfabric/insight/actions/runs/31359228269, and its insight-toolbox job https://github.com/constructorfabric/insight/actions/runs/31359228269/job/93364623071 — that image is where 10 of the 11 open image alerts sit.

A red job in that workflow means a fixable critical appeared in a published image. Everything else is reported and not gated there; the gate that blocks a bad build lives in .github/actions/image-cve-gate.

Alerts, after this lands, filtered by the new tool:
https://github.com/constructorfabric/insight/security/code-scanning?query=is%3Aopen+tool%3A%22Trivy+Images%22

Test plan

  • The rewrite runs against a real Trivy image SARIF: driver.name TrivyTrivy Images, every other driver field identical, 25 results and 24 rules preserved, still valid JSON.
  • jq assignment creates the intermediate objects: a SARIF with no tool at all comes out as {"driver":{"name":"Trivy Images"}}, so a malformed input is renamed rather than silently skipped.
  • Output goes through a temporary file and mv; redirecting onto the input would truncate it before jq reads it.
  • .github/workflows/trivy-images.yml parses; the scan job now has six steps in the order Checkout, scan, summary, rename, upload, gate.
  • actionlint: 8 findings on main, 8 on the branch, identical set; none in trivy-images.yml.
  • After merge, first nightly: alerts appear under tool Trivy Images, the summary tables are unchanged.
  • First PR after that nightly: the Trivy check reports success instead of listing configurations.

Known unknown

Whether a check appears for Trivy Images on pull requests, where it uploads nothing. Both existing tools always upload, so there is no way to observe this before merging. If one does appear and is red, the fallback is to drop the upload entirely — that was this branch's previous revision and it is one revert away.

After merge, one admin action

The seventeen categories already recorded under the old tool do not move; the Trivy check keeps listing them until their analyses are deleted. That is 261 analyses, and DELETE /repos/.../code-scanning/analyses/{id} answers 403 You are not authorized to delete analyses for a write account, so it needs someone in @constructorfabric/admins. Order matters: this PR first, deletion after, otherwise the nightly re-registers them under Trivy the same night.

Summary by CodeRabbit

  • Chores
    • Improved security scan reporting by labeling container image findings as “Trivy Images” in uploaded results.
    • SARIF uploads continue only when a report is available.

@Gregory91G Gregory91G self-assigned this Aug 10, 2026
@Gregory91G Gregory91G added github_actions Pull requests that update GitHub Actions code security review labels Aug 10, 2026
@Gregory91G
Gregory91G requested a review from cyberantonz August 10, 2026 11:10
@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The Trivy image workflow now renames the SARIF tool identifier to Trivy Images when a SARIF file exists, before the workflow uploads the file.

Changes

Trivy SARIF normalization

Layer / File(s) Summary
Normalize SARIF tool name
.github/workflows/trivy-images.yml
Adds a conditional jq step that updates each SARIF run’s tool name and replaces the original file before upload.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

Suggested reviewers: ktursunov

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes separating image scan findings into their own code-scanning tool.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ci/drop-image-sarif-upload

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Code scanning reports "N configurations not found" per tool, not per category,
and the Trivy tool currently owns nineteen: trivy-fs and trivy-helm, which
every pull request produces, plus seventeen trivy-image:* that only this
schedule-only workflow can produce, because it scans published images and a
pull request has none. So the Trivy check carries a permanent list of missing
configurations — neutral usually, failure when the change touches files they
could have covered — and buries the case it exists for, a scanner that quietly
stopped uploading.

Semgrep OSS sits next to it green for exactly this reason: its one category is
produced everywhere.

Renaming the driver in the image SARIF splits the two apart. Trivy keeps only
the categories every run produces and its check goes clean; the image findings
stay in the Security tab under their own tool. Nothing about the scan, the
summary or the critical gate changes.

The seventeen categories already stored under Trivy do not move and will keep
the old check red until an admin deletes their analyses; a write account gets
403 from that endpoint.

Signed-off-by: Gregory Gogin <grigoriy.gogin@constructor.tech>
@Gregory91G
Gregory91G force-pushed the ci/drop-image-sarif-upload branch from 030afdc to 320935d Compare August 10, 2026 11:22
@Gregory91G Gregory91G changed the title ci(security): stop feeding image scans into Code Scanning (#2266) ci(security): give the image scans their own code-scanning tool (#2266) Aug 10, 2026
Comment thread .github/workflows/trivy-images.yml Outdated
# can produce these categories — keep them off the Trivy check PRs see (#2266).
if: ${{ always() && hashFiles('trivy-image.sarif') != '' }}
run: |
python3 - <<'PY'

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why not to use jq?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

thanks. fixed

Same two fields of behaviour, eight lines shorter, and it matches how the rest
of the workflows read and reshape JSON.

Verified against a real Trivy image SARIF: the jq output parses equal to what
the python version produced, only driver.name differs from the input, and the
25 results and 24 rules survive. jq's assignment also creates the intermediate
objects, so a SARIF without tool.driver still comes out renamed rather than
untouched — that was the reason for setdefault.

The output goes to a temporary file and is moved over the original; redirecting
onto the input would truncate it before jq reads it.

Signed-off-by: Gregory Gogin <grigoriy.gogin@constructor.tech>

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/trivy-images.yml:
- Around line 141-145: Add id normalize_sarif to the SARIF normalization step
containing jq and mv, then update the upload step’s if condition to require
steps.normalize_sarif.outcome == 'success' in addition to the existing checks.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 7810269f-eebb-4652-821f-ec12de881f30

📥 Commits

Reviewing files that changed from the base of the PR and between 8079810 and 47641c3.

📒 Files selected for processing (1)
  • .github/workflows/trivy-images.yml

Comment on lines +141 to +145
if: ${{ always() && hashFiles('trivy-image.sarif') != '' }}
run: |
set -euo pipefail
jq '.runs[].tool.driver.name = "Trivy Images"' trivy-image.sarif > renamed.sarif
mv renamed.sarif trivy-image.sarif

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== workflow files =="
fd -a 'trivy-images.yml|trivy' .github 2>/dev/null || true

echo "== target section =="
if [ -f .github/workflows/trivy-images.yml ]; then
  nl -ba .github/workflows/trivy-images.yml | sed -n '120,165p'
fi

echo "== normalization/upload references =="
rg -n "normalize_sarif|trivy-image\.sarif|rename|rm|jq|Upload SARIF|actionlint" .github/workflows/trivy-images.yml || true

echo "== nearby workflow structure =="
rg -n "jobs:|trivy-image-scan|upload|sarif" .github/workflows/trivy-images.yml || true

Repository: constructorfabric/insight

Length of output: 346


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path
p = Path('.github/workflows/trivy-images.yml')
if not p.exists():
    raise SystemExit(0)
text = p.read_text()
lines = text.splitlines()

start = next((i for i,l in enumerate(lines,1) if 'Give the image scans their own tool name' in l), None)
upload_start = next((i for i,l in enumerate(lines,1) if 'Upload SARIF to GitHub Code Scanning' in l), None)

for section in [(start, 'normalize section'), (upload_start, 'upload section')]:
    idx, name = section
    if idx is None:
        print(f'{name}: not found')
        continue
    # print until next indent 8 at same or lower level? rough
    end = next((i for i,l in enumerate(lines[idx:], idx+1) if l.startswith('      - name: ') or l.startswith('    - name: ') or i==len(lines)), min(len(lines)+1, idx+25))
    print(f"\n== {name} lines {idx}-{end-1} ==")
    for n,line in enumerate(lines[idx:min(end, len(lines)+1)], idx):
        print(f'{n}:{line}')
PY

Repository: constructorfabric/insight

Length of output: 1262


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path
p = Path('.github/workflows/trivy-images.yml')
text = p.read_text()
lines = text.splitlines()

print("== normalization step context ==")
for i in range(128, 147):
    print(f"{i+1}: {lines[i]}")

print("\n== upload step context ==")
for i in range(146, 155):
    print(f"{i+1}: {lines[i]}")

print("\n== normalize_sarif id and success guard references ==")
for i,l in enumerate(lines):
    if 'normalize_sarif' in l or "steps." in l:
        print(f"{i+1}: {l}")

PY

Repository: constructorfabric/insight

Length of output: 1978


Gate the upload on successful SARIF normalization.

The normalize step does not set an id, and the upload step still uses hash-only existence checks. If jq or mv fails, the original trivy-image.sarif may remain and still be uploaded. Add id: normalize_sarif to the normalize step and require steps.normalize_sarif.outcome == 'success' in the upload condition.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/trivy-images.yml around lines 141 - 145, Add id
normalize_sarif to the SARIF normalization step containing jq and mv, then
update the upload step’s if condition to require steps.normalize_sarif.outcome
== 'success' in addition to the existing checks.

@Gregory91G
Gregory91G requested a review from cyberantonz August 10, 2026 15:37
@Gregory91G
Gregory91G requested a review from a team as a code owner August 10, 2026 22:52
@cyberantonz
cyberantonz added this pull request to the merge queue Aug 11, 2026
Merged via the queue into main with commit f3e752e Aug 11, 2026
25 checks passed
@cyberantonz
cyberantonz deleted the ci/drop-image-sarif-upload branch August 11, 2026 04:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

github_actions Pull requests that update GitHub Actions code security review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants