Identity benchmark

Resolving a component name written one way to the identifier an advisory database uses. Four baselines and this resolver, scored on the same rows by the same scorer.

Our F1 is 0.1548. The best baseline is 0.1576.
We lose on the headline metric. The result is published because a benchmark whose author always comes first is marketing, and worth nothing as evidence.
What we win on is the column on the right. Across all rows this resolver assigns an identifier to 0 of the 250 unknown-class rows. prefix-strip assigns one to 4. In a tool whose output is submission evidence, a confident wrong identifier produces a clean vulnerability verdict for a component nobody actually identified, and that is a worse failure than declining to answer.
Those 250 rows are not 250 components. 36 are real components from the corpus that carry no identifier at all. The other 214 are path-stripped names built from components that do have one, where the identifier cannot be recovered from the final segment alone. Both are rows a resolver must decline, and the smaller number is the one to quote.

What the columns mean

Precision
Of the answers given, how many were right. An abstention is never counted as a wrong answer.
Ours is 1.000 on every subset. That is the number this product is built around.
Recall
Of the rows with a right answer available, how many were answered correctly.
F1
The harmonic mean of the two, which weights them equally.
We do not think they are equal here, and saying so while losing on F1 is the only honest order to say it in.
Abstained
How often the resolver declined to answer. High abstention is the cost of high precision, and it is a deliberate one.
Wrong on unknowns
How many rows with no correct identifier were nonetheless assigned one.
The benchmark has an unknown class for exactly this. A resolver that always guesses scores well on the other columns and fails here.

Clean

Names exactly as their tools wrote them, with no perturbation applied.

ResolverPrecisionRecallF1AbstainedWrong on unknowns
always-abstain0.0000.0000.0000100.0%0
exact-match0.9760.0850.157191.6%0
normalised-match best baseline, tied0.9770.0900.164191.2%0
prefix-strip best baseline, tied0.9770.0900.164191.2%0
StratifyPro1.0000.0870.160991.6%0

Perturbed

The same names after one of seven documented shape changes: an archive extension, a vendor prefix, a parenthetical, a case change, added spaces, a version, or a stripped path.

ResolverPrecisionRecallF1AbstainedWrong on unknowns
always-abstain0.0000.0000.0000100.0%0
exact-match0.0000.0000.0000100.0%0
normalised-match0.9420.0690.127893.4%4
prefix-strip best baseline0.9520.0840.154692.0%4
StratifyPro1.0000.0820.152092.5%0

All rows

Both of the above together. This is the headline set.

ResolverPrecisionRecallF1AbstainedWrong on unknowns
always-abstain0.0000.0000.0000100.0%0
exact-match0.9760.0270.051997.5%0
normalised-match0.9550.0750.139292.7%4
prefix-strip best baseline0.9600.0860.157691.7%4
StratifyPro1.0000.0840.154892.2%0

Reproducing this

The dataset is generated, not committed by hand: python3 bench/identity/build.py rebuilds it and --check fails if it would change. scripts/bench-publish.py produces this page’s numbers by emitting one prediction per row and scoring them with bench/identity/run.py, the same scorer that produced the baselines. There is deliberately no second scorer.

3,326 rows. No regulator has reviewed this tool. A benchmark score is not a claim about any particular file.