One of the biggest headaches when integrating Semgrep into a CI/CD pipeline is dealing with result volatility. You run it on a feature branch, get a list of findings, and then the main branch has a slightly different set. It's tough to answer the simple question: "What *new* issues did my branch actually introduce?"
I've been using a simple script to cut through that noise for a few client projects now. The core idea is straightforward: run Semgrep on both branches, diff the results, and output only the net-new findings. This lets developers focus on what their code actually changed, not on the existing technical debt in the base branch.
Here's the basic workflow my script automates:
* It checks out the target base branch (e.g., `main`) and runs Semgrep with your config, outputting results to a temporary JSON file.
* It checks out your feature branch and runs Semgrep again, producing a second JSON file.
* It parses and compares the two result sets, filtering based on a unique fingerprint (I typically use the combination of `check_id`, `path`, and `start line`).
* Finally, it outputs a clean list of findings that are unique to the feature branch.
The main value is in the filtering logic. You have to decide what constitutes a "unique" finding. I've found that relying on the Semgrep-generated fingerprint isn't always consistent across runs, so building your own key from a few fields is more reliable.
This approach has been a game-changer for adoption. Teams stop getting frustrated by "inherited" findings and can trust that the CI failure is directly tied to their recent changes. It turns Semgrep from a blunt instrument into a precise one.
Has anyone else built something similar? I'm curious how you handle the diff logic, especially with findings that might have shifted lines slightly.
-mike
Integrate or die
Sure, because a bespoke script that depends on internal Semgrep JSON formatting is the maintainable path forward. What happens when they change the output schema in the next minor version? Now your CI is broken and you're spelunking through release notes.
You're also assuming everyone wants to diff on those three fields. I've seen line numbers shift from trivial formatting changes, making your fingerprint useless. The real diff is often in the *metadata* - severity changes, confidence drift - which your script ignores.
Your vendor is not your friend.
Your core point about fingerprint stability is correct, but the broader problem is treating the output as the source of truth. Relying on JSON structure from a CLI tool for a critical pipeline gate is inherently fragile. You're coupling your CI stability to another team's API design choices.
A more durable approach is to version-pin the Semgrep binary and use its internal comparison logic if available, or move the diff responsibility upstream. The registry or policy-as-code layer that *generates* the scan config should ideally be able to track findings across commits, shifting the burden from your ad-hoc script to the scanning platform itself.
Ultimately, you're building a bespoke version of what should be a feature of the SAST product. The script is a stopgap that highlights a gap in the toolchain.
Boring is beautiful