feat(pair-corpus-honesty): function-scoped honest scorer with mechanisms and loss counters

Tasks 1.2, 2.1-2.5 and 6.2 (pair-side filters).

- pairs.py: coverage with a source counts as detection and as leak;
  function-scoped matching by line-range containment from the vulnerable
  side's parse (unresolvable labels are counted loss, never silent
  misses); exact or alias sink matching; weak CWE-only labels never match;
  side-by-side inventory/overlay/hunter scorers; per_profile,
  per_mechanism, achievable, known_limit and loss payload; loader rejects
  unknown mechanism/provenance/split and reserved or missing function
  labels on harvested slices; candidate rulesets thread through for the
  improve loop.
- overlay.py: OverlayRecord.function (additive) filled from the shared
  naming helper in one parse; benchmark.py: ExpectedFinding.mechanism.
- cli pairs: --slice over every slice, --profile, --split, --hunter,
  --hunter-model.
- catalogs migrated to the new schema (provenance, split, function,
  mechanism, known_limit for owasp-java-hash); baseline recorded in the
  spec notes.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01T7XD7x5KonMdmGRkp63EQG
This commit is contained in:
Marius Ciepluch
2026-09-05 10:45:43 +02:00
co-authored by Claude Fable 5.1
parent 8ab4207618
commit c0b9e41000
9 changed files with 1915 additions and 127 deletions
+18
View File
@@ -55,6 +55,8 @@ fixed = "github/sven-cwe-078-os-system/fixed.py"
relpath = "vuedj/configtitania/views.py"
min_recall = 1.0
fix_policy = "silent"
provenance = "human"
split = "train"
[[pair.expected]]
cwe = "CWE-78"
@@ -63,6 +65,7 @@ path = "vuedj/configtitania/views.py"
rule_id = "python-os-command"
sink = "os.system"
evidence = "os.system concatenates attacker-controlled username into a shell command"
mechanism = "source_reaches_sink"
[[pair]]
name = "sven-cwe-078-shell-true"
@@ -78,6 +81,8 @@ fixed = "github/sven-cwe-078-shell-true/fixed.py"
relpath = "mobly/controllers/android_device_lib/adb.py"
min_recall = 1.0
fix_policy = "silent"
provenance = "human"
split = "holdout"
[[pair.expected]]
cwe = "CWE-78"
@@ -86,6 +91,7 @@ path = "mobly/controllers/android_device_lib/adb.py"
rule_id = "python-shell-true"
sink = "subprocess"
evidence = "subprocess.Popen runs the command with shell=True"
mechanism = "source_reaches_sink"
[[pair]]
name = "sven-cwe-089-execute-percent"
@@ -101,6 +107,8 @@ fixed = "github/sven-cwe-089-execute-percent/fixed.py"
relpath = "cgi/common.py"
min_recall = 1.0
fix_policy = "silent"
provenance = "human"
split = "train"
[[pair.expected]]
cwe = "CWE-89"
@@ -109,6 +117,7 @@ path = "cgi/common.py"
rule_id = "python-sql-injection"
sink = "execute"
evidence = "execute percent-formats attacker-controlled input into SQL"
mechanism = "source_reaches_sink"
[[pair]]
name = "sven-cwe-089-query-concat"
@@ -124,6 +133,8 @@ fixed = "github/sven-cwe-089-query-concat/fixed.py"
relpath = "modules/query_lastfm.py"
min_recall = 1.0
fix_policy = "silent"
provenance = "human"
split = "holdout"
[[pair.expected]]
cwe = "CWE-89"
@@ -131,6 +142,7 @@ class = "sql injection"
path = "modules/query_lastfm.py"
sink = "SELECT"
evidence = "SQL query concatenates attacker-controlled tag_num"
mechanism = "source_reaches_sink"
[[pair]]
name = "hutool-cve-2018-17297"
@@ -149,6 +161,8 @@ fixed = "github/hutool-cve-2018-17297/fixed/ZipUtil.java"
relpath = "cn/hutool/core/util/ZipUtil.java"
min_recall = 1.0
fix_policy = "silent"
provenance = "human"
split = "train"
[[pair.expected]]
cwe = "CWE-22"
@@ -156,6 +170,7 @@ class = "path traversal"
path = "cn/hutool/core/util/ZipUtil.java"
sink = "ZipEntry"
evidence = "zip entry name is joined onto the extract directory without a zip-slip check"
mechanism = "path_join_user_input"
[[pair]]
name = "node-serialize-cve-2017-5941"
@@ -171,6 +186,8 @@ fixed = "github/node-serialize-cve-2017-5941/fixed.js"
relpath = "index.js"
min_recall = 1.0
fix_policy = "silent"
provenance = "human"
split = "holdout"
[[pair.expected]]
cwe = "CWE-95"
@@ -179,3 +196,4 @@ path = "index.js"
rule_id = "js-eval"
sink = "eval"
evidence = "eval executes attacker-controlled serialized input"
mechanism = "source_reaches_sink"