Skip to content

Fix Opengrep differential scan re-reporting pre-existing findings as new (SEC-1975) - #26

Merged
picatz merged 3 commits into
mainfrom
fix-opengrep-baseline-diff
Jun 26, 2026
Merged

Fix Opengrep differential scan re-reporting pre-existing findings as new (SEC-1975)#26
picatz merged 3 commits into
mainfrom
fix-opengrep-baseline-diff

Conversation

@picatz

@picatz picatz commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Problem

The sast/opengrep action is meant to be differential — report only findings a PR newly introduces. But PRs that merely edit a workflow (e.g. pinning a uses: ref to a SHA) were failing opengrep/scan with missing-explicit-permissions findings on workflows whose missing-permissions: gap is pre-existing on the base branch.

Root cause: opengrep's --baseline-commit classifies findings new/old by a location- and content-sensitive fingerprint of the matched range. Whole-file "absence" rules (missing-permissions matches a whole job) span a large range, so editing any line inside it changes the fingerprint and the pre-existing finding is mis-classified as new. Confirmed empirically: editing a uses: line inside the job block leaves the finding's start/end lines unchanged but changes its fingerprint.

This blocked the SHA-pinning campaign and any unrelated workflow edit, and a high false-positive rate erodes trust in the required gate.

Fix

Replace --baseline-commit with a stable-identity diff:

  • Full-scan both the PR head tree and the base tree (checked out in a git worktree at base.sha, scanned from its own root so paths are repo-relative and comparable) with the same ruleset.
  • In opengrep-report.sh, a finding is new only if its (rule, file) key gained findings: new = max(0, head_count − baseline_count). Never keyed on line number or matched text — both shift when a line inside a whole-block match is edited.
  • When a key gains findings, the head findings least like the baseline (content- and start-line-novel) are surfaced first so the annotation points at the genuinely new finding.
  • A missing or failed baseline scan conservatively treats every head finding as new, so a baseline problem never hides a finding.

Behavior

  • PR that only pins uses: SHAs in a workflow with a pre-existing missing-permissions: gap → no new finding for that pre-existing issue.
  • PR that genuinely introduces a gap (new workflow/job without permissions:) → still reported and still blocks.
  • Line shifts, in-place edits, and fixed findings all behave correctly (covered by tests).

Tests

sast/opengrep/tests/report-diff-test.sh — hermetic bash+jq unit tests for the diff (edit-in-block, line shift, added gap, second same-rule finding, empty baseline, no change, removed finding), run by the new Opengrep action tests workflow. Also validated end-to-end against real opengrep/semgrep output for the actual permissions rule.

Opengrep's --baseline-commit classifies a finding as new/old via a
location- and content-sensitive fingerprint of the matched range. Whole-file
"absence" rules (e.g. missing-permissions, which matches a whole job) span a
large range, so editing any line inside it (such as pinning an action SHA)
changes the fingerprint and the pre-existing finding is mis-reported as new.
This blocked the SHA-pinning campaign and any workflow edit on repos lacking
an explicit permissions: block (SEC-1975).

Replace --baseline-commit with a stable-identity diff: full-scan both the head
tree and the base tree (checked out in a git worktree) with the same ruleset,
then in opengrep-report.sh treat a finding as new only when its (rule, file)
key gained findings — new count = max(0, head_count - baseline_count), never
keyed on line number or matched text. Edits inside a pre-existing match no
longer re-report it; a genuinely new gap (added job/file/line) still is, and
still blocks. A missing/failed baseline scan conservatively treats every head
finding as new so a baseline problem never hides a finding.

Add report-diff-test.sh (hermetic bash+jq unit tests for the diff) and a CI
workflow to run it.
@picatz
picatz requested a review from a team as a code owner June 24, 2026 20:50
@picatz
picatz requested a review from Copilot June 24, 2026 20:55

Copilot AI 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.

Pull request overview

This PR fixes false “new finding” reports from the sast/opengrep differential scan by replacing --baseline-commit with a stable-identity diff: full-scan both base and head, then compute “new” based on per (rule, file) finding-count deltas, avoiding fingerprint instability for whole-block absence rules.

Changes:

  • Update the composite action to run full scans for both PR head and baseline (via git worktree) and pass both JSONs to the report script.
  • Add stable-identity diff logic to opengrep-report.sh to compute new findings from baseline/head scans.
  • Add hermetic bash+jq unit tests plus a dedicated workflow to run them on PRs.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
sast/opengrep/tests/report-diff-test.sh Adds unit tests for the new baseline/head diff logic.
sast/opengrep/scripts/opengrep-report.sh Implements stable-identity diffing and updates reporting inputs accordingly.
sast/opengrep/README.md Updates documentation to reflect the new baseline/head full-scan diff approach.
sast/opengrep/action.yml Switches from --baseline-commit to dual full-scan + report-time diff, adding baseline worktree scanning.
.github/workflows/test-opengrep-action.yml Adds CI workflow to run the new diff unit tests.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread sast/opengrep/scripts/opengrep-report.sh
Comment thread sast/opengrep/scripts/opengrep-report.sh
Comment thread sast/opengrep/tests/report-diff-test.sh
- Remove the new-findings temp file via an EXIT trap (path baked into the
  trap so it survives the local going out of scope under set -u).
- Add a regression test asserting same-(rule,file) findings interleaved with
  another key still group correctly (jq group_by sorts internally, so it is
  order-independent; this guards that guarantee).
Comment thread sast/opengrep/action.yml
keeravani
keeravani previously approved these changes Jun 25, 2026
A failed/errored baseline scan left baseline-findings.json.tmp behind. It's
under RUNNER_TEMP (cleaned at job end) and unused by the report step, so it's
harmless, but remove it for tidiness.

@keeravani keeravani left a comment

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.

LGTM!

@picatz
picatz merged commit 1ddf447 into main Jun 26, 2026
4 checks passed
@picatz
picatz deleted the fix-opengrep-baseline-diff branch June 26, 2026 14:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants