Skip to content

Match Security finding fingerprints canonically on lookup - #2236

Merged
JacobStephens2 merged 2 commits into
mainfrom
issue-1527
Nov 15, 2026
Merged

JacobStephens2 merged 2 commits into
mainfrom
issue-1527

Conversation

@JacobStephens2

@JacobStephens2 JacobStephens2 commented Nov 15, 2026 •

Copy link
Copy Markdown
Owner

Closes #1527

Summary

Security runs and Security reviews now canonicalize finding fingerprints on lookup, so a renamed separator style or reordered token sequence reuses the existing repository record instead of creating a duplicate advisory or private issue or triggering another reproduction.

Session finding: sqli:mtourbot/common_functions.php:get_auto_cancellations
  ├─ lookup: canonical_tokens() -> {"auto", "cancellations", "common", "functions", "get", "mtourbot", "php", "sqli"}
  │          (case-folded token set split on ., :, /, -, _, and whitespace)
  ├─ matches existing `Fingerprint: \`mtourbot/common_functions.php.get_auto_cancellations.sqli\`` -> reuse record
  └─ no match -> create new record storing the session's `Fingerprint: \`...\`` verbatim
  • Canonical lookup (src/github/advisories/description.rs): Description::matches_fingerprint compares case-folded BTreeSet<String> token sets produced by canonical_tokens across Fingerprint: \...`` lines, falling back to exact comparison when separator-only candidates have empty token sets.
  • Shared lookup seam (src/github/advisories.rs): Both Security audits and Security reviews route through SecurityRecords::record_or_reuse, keeping existing draft/published/closed advisories and needs-triage/Day-shift-graded private security-finding issues within the repository without passing existing records to sessions or adding fuzzy title matching.
  • Intra-audit deduplication (src/security/findings.rs): Findings::record now inserts the resolved record URL (url.clone()) rather than raw finding.fingerprint into seen, skipping duplicate reproduction scheduling and duplicate completed-reproduction log lines when one audit emits canonically equivalent variants.
  • Docs (docs/guide.md, site/docs/index.html): Updated ## Security runs and ### Security review to document canonical token-set lookup and verbatim fingerprint persistence, and regenerated site/docs/index.html via npm run docs --prefix site.

Evidence

Execution evidence

  • Before (cargo test --bin thirdshift advisories::tests): canonical_fingerprints_reuse_records_and_preserve_written_fingerprints failed with called Result::unwrap() on an Err value: canonical match must not create another record at src/github/advisories_tests.rs:572:18.
    After: 23 passed; 0 failed; 0 ignored; 1905 filtered out.
  • Before (cargo test --bin thirdshift security::tests): canonical_fingerprints_reuse_records_and_deduplicate_within_an_audit failed with assertion left == right failed: private false: left: 1 right: 2 at src/security/tests/sequence.rs:604:9.
    After: 21 passed; 0 failed; 0 ignored; 1907 filtered out.
  • Before (cargo test --test security_run reformatted_audit_fingerprints_reuse_records_and_skip_repeat_reproduction): failed with left: Some(1) right: Some(0) at tests/security_run.rs:5693:9 after creating duplicate GHSA-test-test-0002 and GHSA-test-test-0003.
    After: 1 passed; 0 failed; 0 ignored; 132 filtered out.
  • Before (cargo test --test security_review reformatted_pre_existing_review_fingerprints_reuse_records_and_keep_written_fingerprints): failed with left: 3 right: 2 at tests/security_review.rs:1797:17 after creating duplicate GHSA-test-test-0002.
    After: 1 passed; 0 failed; 0 ignored; 41 filtered out.
  • Final suite checks on a82fce8a:
    • scripts/affected-tests main: passed npm test --prefix site (76.94s) and stopped fail-fast during cargo nextest run (1389/4629 tests run: 1388 passed (7 slow), 1 failed, 207 skipped in 1578.852s) on thirdshift::plan_usage plan_usage_shows_configured_routes_in_order_and_omits_api_key_and_unused_harnesses (tests/plan_usage.rs:322:5, ambient PATH picking up host /home/jacob/.local/bin/agy)
    • cargo nextest run --no-fail-fast --show-progress=counter --status-level=fail --final-status-level=slow --failure-output=immediate-final: 4629 tests run: 4628 passed (15 slow), 1 failed, 207 skipped in 2905.922s (sole failure: thirdshift::plan_usage plan_usage_shows_configured_routes_in_order_and_omits_api_key_and_unused_harnesses at tests/plan_usage.rs:322:5 under ambient PATH)
    • PATH="/home/jacob/.cargo/bin:/usr/local/bin:/usr/bin:/bin" cargo test --test plan_usage: 7 passed; 0 failed; 2 ignored in 15.09s (confirms host /home/jacob/.local/bin/agy caused the ambient-PATH failure)
    • cargo fmt --check, cargo clippy --all-targets -- -D warnings, python3 scripts/generate-release.py --check, npm run docs:check --prefix site: exited 0
    • python3 -m unittest discover -s scripts -p 'test_*.py': Ran 65 tests in 26.907s OK
    • npm test --prefix site: tests 69, pass 69, fail 0 (88278.946028ms)
    • cargo test --bin thirdshift advisories::tests: 23 passed; 0 failed; 0 ignored; 1905 filtered out
    • cargo test --bin thirdshift security::tests: 21 passed; 0 failed; 0 ignored; 1907 filtered out
    • cargo test --bin thirdshift prompts_page: 14 passed; 0 failed; 0 ignored; 1914 filtered out
    • cargo test --test process security_review: 16 passed; 0 failed; 0 ignored; 230 filtered out
    • cargo test --test security_review: 40 passed; 0 failed; 2 ignored (support::child_lifetime::fixture_guardian, support::child_lifetime::scenario_root_guardian)
    • cargo test --test security_run: 131 passed; 0 failed; 2 ignored (support::child_lifetime::fixture_guardian, support::child_lifetime::scenario_root_guardian)

Visual evidence

Manual #security-runs and #security-review rendered from site/docs/index.html (c0b4f35c vs a82fce8a), 1440×900 desktop:

Before desktop
After desktop

Manual #security-runs and #security-review, 390×844 phone:

Before mobile
After mobile

Unaddressed findings

Standards

  • None

Spec

  • None

Security

  • None

Review coverage

  • Unread changed files left by the review: site/docs/index.html only (excluded as generated manual output; its sources docs/guide.md and site/generate-docs.mjs were reviewed, site/docs/index.html was regenerated with npm run docs --prefix site, and npm run docs:check --prefix site plus npm test --prefix site pass).
  • Read changed files: Both reviewers read every non-generated changed file (docs/guide.md, src/github/advisories/description.rs, src/github/advisories_tests.rs, src/security/findings.rs, src/security/tests/sequence.rs, tests/security_review.rs, and tests/security_run.rs) on commit a82fce8a against refs/remotes/origin/main (c0b4f35c).

Security review outcome

No unaddressed introduced findings.

Merge Danger

Door: two-way

Reverting restores exact Fingerprint: \...`` line matching and raw-fingerprint intra-audit tracking; newly written records continue to store fingerprints verbatim.

Blast Radius: Security audit, Security review, manual

  • Candidate fingerprints in one repository whose non-empty token sets (split on ., :, /, -, _, and whitespace and lowercased) match an existing advisory or private security-finding issue reuse that record rather than creating a new one or repeating reproduction.
  • Previously created duplicate advisories or issues remain untouched; matching stays scoped within a single repository.

Built with agy 1.5.2 · gemini-4-hydrogen-xhigh · default effort

@JacobStephens2
JacobStephens2 merged commit 26aff41 into main Nov 15, 2026
24 checks passed
@JacobStephens2
JacobStephens2 deleted the issue-1527 branch November 15, 2026 01:48
@JacobStephens2 JacobStephens2 mentioned this pull request Nov 15, 2026
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.

Security findings are deduplicated only by an audit-chosen fingerprint, so a renamed finding is recorded and reproduced again

1 participant