Repository navigation
research: compare the identity methods of SonarQube, Bandit and Semgrep - #56
Merged
Merged
Conversation
Line-number identity alone is a straw man, and every finding came from CodeGuard. Add the matching methods real tools ship, ported from their source at pinned versions and checked against the original tools, and let the replay take its findings from Bandit or Semgrep as well. Schemes: - sonar: SonarQube 26.10 issue tracking, a six-pass matcher with block-hash code-move detection (Tracker, AbstractTracker, BlockRecognizer, BlockHashSequence), including a port of java.util.HashMap's iteration order, which breaks ties. - bandit: `bandit -b baseline.json` (Bandit 1.9.4): Issue.__eq__ fields and set membership, so duplicates of a baselined issue stay hidden. - semgrep: Semgrep 1.180.0 match_based_id, as emitted in JSON and SARIF. Multi-pass matchers plug into matching.scheme_pairs through trackers.MATCHERS; Observation gains the message and file lines they need. Fidelity: fidelity/sonar compiles the unmodified SonarQube tracker with a small Java harness; the port agrees with it on generated cases and on HashMap key orders (recorded in tests/fixtures). The Semgrep port reproduces every ID Semgrep emitted on Flask and DefectDojo (recorded). Live tests run Bandit's baseline mode and Semgrep's JSON and SARIF output when the tools are installed. Analyzers: `replay --analyzer codeguard|bandit|semgrep`. Bandit runs in process; Semgrep runs offline with local rules in semgrep_rules.yml. Research-only dependencies go in research/finding_identity/requirements.txt. Signed-off-by: Vichitra Attri <mevichitra@gmail.com>
Signed-off-by: Vichitra Attri <mevichitra@gmail.com>
Keep both sides: the controlled transformation experiment from main and the real-tool schemes and analyzers from this branch. requirements.txt lists the dependencies of both. Signed-off-by: Vichitra Attri <mevichitra@gmail.com>
Owner
Author
|
Re-ran all six replays on the merged head (7f3924e, which includes main's line-splitting change). Flask was at 559a677 and DefectDojo Generated by Claude Code |
5 of 11 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds the finding-identity methods that real tools ship to the replay harness, and lets the replay take its findings from Bandit or Semgrep as well as CodeGuard. Each port is pinned to a tool version, cites the file and function it ports, and is checked against the original tool:
sonar: SonarQube 26.10.0.133078 issue tracking (sonar-coreissue/tracking/Tracker.java,AbstractTracker.java,BlockRecognizer.java,BlockHashSequence.java,hash/SourceLineHashesComputer.java). It is a six-pass matcher with block-hash code-move detection. Its tie-breaking depends onjava.util.HashMapiteration order, which is ported too, including treeified buckets and removals.bandit:bandit -b baseline.json(Bandit 1.9.4,core/manager.py_compare_baseline_results,core/issue.pyIssue.__eq__). Equality ignores location (it compares text, severity, CWE, confidence, file and test), and the baseline check is set membership, so once one issue is baselined any number of equal new ones stay hidden.semgrep: Semgrep 1.180.0match_based_id(rule_match.pyget_match_based_key/get_match_based_id/RuleMatches.add,rule.pyformula_string), as emitted in JSONextra.fingerprintand SARIFfingerprints["matchBasedId/v1"]. Applies to Semgrep findings only.replay --analyzer codeguard|bandit|semgrep: Bandit runs in process. Semgrep runs offline with the local rules insemgrep_rules.yml; every proxy points at a closed port, and a placeholder token un-redacts the IDs. Research-only dependencies are inresearch/finding_identity/requirements.txt.Multi-pass matchers plug into
matching.scheme_pairsthroughtrackers.MATCHERS.Observationgains the analyzer message and the file lines the Sonar tracker needs.replay.pyandsummarize.pyare unchanged.Type of change
research/, not part of the package)Checklist
git commit -son all commitspytestpasses locally (652 passed), andpython -m pytest research/finding_identity/tests(74 passed, 1 skipped)ruff check src/ tests/andruff check research/,ruff format --check research/cleanmypy src/andMYPYPATH=src python -m mypy --strict research/finding_identityclean (the latter needs main's optionalmatplotlibinstalled)research/Fidelity evidence
SonarQube:
fidelity/sonar/build.shcompiles the unmodified tracker sources at the pinned tag with the dependency jars that tag declares (checked by SHA-256).SonarTrackerHarness.javarunsTracker.trackNonClosedon generated cases. Results:HashMapkey orders, 0 mismatches against Guava'sArrayListMultimap;400 cases and 400 orders are recorded in
tests/fixtures/sonar_tracker.json.Semgrep: the port reproduces all 361 IDs Semgrep emitted on whole-repository scans of Flask and DefectDojo (156 with a non-zero index); these are recorded in
tests/fixtures/semgrep_match_based_id.json. During the replays it also reproduced all 716 emitted IDs (217 Flask + 499 DefectDojo). A live test checks that SARIF carries the same IDs as JSON.Bandit: a live test runs
bandit, thenbandit -bon the next version. The port flags exactly the issues Bandit reports as new, including hiding a newly added duplicate of a baselinedB602. The in-process analyzer reports what the command line reports.Test evidence
Results per analyzer
python -m research.finding_identity replay https://github.com/pallets/flask --commits 100and... https://github.com/DefectDojo/django-DefectDojo --ref dev --commits 200, with each--analyzer. All runs had 0 parse failures. These tables were produced on c1c37a3 (this branch before merging main). Main's later CodeGuard change (source lines split on LF/CR only) can shift thecodeguard-family columns slightly; a re-run on the merged head will follow in a comment.flask: 100 commit pairs, 75 file changes with findings, 275 persisting findings (274 moved, 1 edited), 24 introduced
flask-bandit: 100 commit pairs, 107 file changes with findings, 5624 persisting findings (5567 moved, 57 edited), 126 introduced
flask-semgrep: 100 commit pairs, 78 file changes with findings, 188 persisting findings (186 moved, 2 edited), 4 introduced
django-DefectDojo: 200 commit pairs, 104 file changes with findings, 420 persisting findings (416 moved, 4 edited), 21 introduced
django-DefectDojo-bandit: 200 commit pairs, 47 file changes with findings, 208 persisting findings (206 moved, 2 edited), 26 introduced
django-DefectDojo-semgrep: 200 commit pairs, 78 file changes with findings, 325 persisting findings (325 moved, 0 edited), 12 introduced
Notes for reviewer
bandit(which compares the message) reports 3.85% of unchanged DefectDojo findings as new.py-sql-formatmatches on different lines share a key.