Skip to content

feat: apply identify, ignore and ranking-threshold client-side, add s… - #89

Open
mscasso-scanoss wants to merge 3 commits into
mainfrom
mscasso/feature-add-matching-features
Open

feat: apply identify, ignore and ranking-threshold client-side, add s…#89
mscasso-scanoss wants to merge 3 commits into
mainfrom
mscasso/feature-add-matching-features

Conversation

@mscasso-scanoss

Copy link
Copy Markdown

…kip-headers

Migrates four scanoss.py scan options. The three result-side ones were server-side there; here they are applied to the candidate matches the v3 batch scanner returns per file, so no scan settings are sent to the server.

postprocess/bomselect.go settles a file's candidate list in one pass: bom.ignore drops what the user dismissed, the ranking threshold drops what the scanner ranked too weakly, and bom.identify promotes what the user declares is present so it leads. One rule decides every interaction between them — a match an identify rule claims is never dropped — mirroring how bom.include already protected a file from bom.remove. It diverges from the engine, which applies the threshold last and lets it overwrite the identified flag; reproducing that would discard a component the user stated was present over a rule naming no component.

The identify verdict lands on sbom.FileEvidence.Identified per matched file, because that is the granularity a path-scoped rule works at, with sbom.Component.Identified summarising it. CycloneDX carries the summary as a scanoss:identified property; SPDX can represent neither.

Both rules match a component's canonical PURL, not its aliases: one project mined from several registries yields entries that each carry the others' PURLs, so matching aliases made one rule claim every candidate at once.

wfp/headerfilter.go ports scanoss.py's HeaderFilter — the leading licence header, documentation comments and imports are dropped from a file's fingerprint, with a start_line= marker naming the offset. Offsets agree with the reference implementation on 6527 of 6531 real source files; the four that differ each hold a form feed above the first line of code, where Python's str.splitlines() counts a line the WFP's own numbering does not.

All four options also read from scanoss.json, where the file overrides the flag. That is the reverse of this CLI's usual precedence and is deliberate: it is what scanoss.py does, and is documented in CLIENT_HELP.md.

Fixes two pre-existing gaps found while wiring this up: results <id> applied no BOM rules at all, so a resumed scan reported components the project's settings dismissed; and bom.remove's protection honoured only bom.include, not bom.identify.

Verified against the live API on west-1.2.0, time-1.9 and librealsense-master.

…kip-headers

Migrates four scanoss.py scan options. The three result-side ones were
server-side there; here they are applied to the candidate matches the v3 batch
scanner returns per file, so no scan settings are sent to the server.

postprocess/bomselect.go settles a file's candidate list in one pass: bom.ignore
drops what the user dismissed, the ranking threshold drops what the scanner
ranked too weakly, and bom.identify promotes what the user declares is present
so it leads. One rule decides every interaction between them — a match an
identify rule claims is never dropped — mirroring how bom.include already
protected a file from bom.remove. It diverges from the engine, which applies the
threshold last and lets it overwrite the identified flag; reproducing that would
discard a component the user stated was present over a rule naming no component.

The identify verdict lands on sbom.FileEvidence.Identified per matched file,
because that is the granularity a path-scoped rule works at, with
sbom.Component.Identified summarising it. CycloneDX carries the summary as a
scanoss:identified property; SPDX can represent neither.

Both rules match a component's canonical PURL, not its aliases: one project
mined from several registries yields entries that each carry the others' PURLs,
so matching aliases made one rule claim every candidate at once.

wfp/headerfilter.go ports scanoss.py's HeaderFilter — the leading licence
header, documentation comments and imports are dropped from a file's
fingerprint, with a start_line= marker naming the offset. Offsets agree with the
reference implementation on 6527 of 6531 real source files; the four that differ
each hold a form feed above the first line of code, where Python's
str.splitlines() counts a line the WFP's own numbering does not.

All four options also read from scanoss.json, where the file overrides the flag.
That is the reverse of this CLI's usual precedence and is deliberate: it is what
scanoss.py does, and is documented in CLIENT_HELP.md.

Fixes two pre-existing gaps found while wiring this up: `results <id>` applied
no BOM rules at all, so a resumed scan reported components the project's
settings dismissed; and bom.remove's protection honoured only bom.include, not
bom.identify.

Verified against the live API on west-1.2.0, time-1.9 and librealsense-master.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
matiasdaloia and others added 2 commits September 3, 2026 20:20
A licence header is boilerplate shared by every file that carries it, so
fingerprinting it makes unrelated files look alike to the matcher. Filtering it
is the better default, and opting out is --skip-headers=false.

This changes the WFP every scan produces: a fingerprint taken with the filter on
does not match one taken with it off. `scan wfp` still rejects the flag either
way, since it is handed a WFP that is already assembled — only an explicit flag
is rejected, so the new default does not make that command unusable.

The default lives on the CLI flag alone. pkg/wfp and pkg/scanoss stay neutral:
passing no option still means no filtering, so a library caller keeps whatever
behaviour it asked for.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
COMPONENT_DEFAULT_RANK is the engine's sentinel for a component it has no
ranking information about, and its own "accept everything" bound is that value
plus one — so an unranked component passes it. Filtering on rank was reading 999
as a very bad rank instead, and since it exceeds every threshold in range, those
components were dropped under every setting rather than under none.

Found by scanning the commissioning projects: rdkb-2024q4 returns 23 of its 552
components at rank 999, and they account for 2027 matched files that
--ranking-threshold 1 discarded for missing data rather than for explaining a
match poorly.

Rank 0 was already exempt for the same reason — the field is omitted when empty,
so an absent rank is indistinguishable from a zero one.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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.

2 participants