TL;DR
How SCANOSS detects vulnerabilities
Before building the workflow, it helps to understand the moving parts. Rather than relying solely on dependency manifests, SCANOSS fingerprints your source code locally and matches it against the SCANOSS Knowledge Base. From those matches it derives components, and from components it derives vulnerabilities. The important idea is that vulnerabilities are attached to components (PURLs), not to raw code. And a scan surfaces components in two distinct ways, both of which carry vulnerability risk:- Matched components — open-source code identified inside your source files by
fingerprint (file and snippet matches). These represent code that was copied, vendored,
or reused from an upstream project, and they appear as entries with
"id": "file"and a top-levelpurlarray (e.g.pkg:github/miguelgrinberg/flasky). A known CVE in that upstream project may well be present in the copied code. - Declared dependencies — packages your project pulls in through manifests
(
requirements.txt,package.json,pom.xml, …). These appear as entries with"id": "dependency"and adependenciesarray, each element a package such aspkg:pypi/flask. This is the classic SCA surface.
Prerequisites
This guide uses the following tools:- SCANOSS-PY
scancode-toolkit- SCANOSS API Key
Getting started
Let’s install the CLI and its extras.- Install
scanoss-pyfrom PyPI:
On modern Linux distros with an externally managed Python (Ubuntu 23.04+, Fedora 38+, Debian 11+), use pipx install scanoss instead.
- Install
scancode-toolkitto enable dependency scanning:
--dependencies flag will fail. Since the declared-dependency
surface is where many known CVEs live, installing it is strongly recommended for
vulnerability scanning.
- Store your API key in an environment variable so it never ends up hard-coded in a command or committed to a repo:
Step 1: Scan the project
The first phase identifies components. Let’s scan a codebase and its declared dependencies, writing structured results to a file.- Fingerprints your source files locally using the Winnowing algorithm.
- Matches those fingerprints against the SCANOSS Knowledge Base.
- Analyses declared dependencies from manifests (
package.json,requirements.txt,pom.xml, …) when--dependenciesis enabled. - Writes the complete results — detected components, versions, PURLs, and licences — to
results.json.
results.json is keyed by file path and contains the two entry types
described earlier — matched components ("id": "file") and declared dependencies
("id": "dependency"). The next step extracts PURLs from each.
Step 2: Extract PURLs from both surfaces
Vulnerability queries operate on PURLs, so the second thing to do is pull them out of the scan results. Because the two entry types have different shapes, you extract from each with a slightly different filter, then combine them into one list to query together.Matched components
These are the"id": "file" entries. Their PURLs live in a top-level purl array,
and the matched version is the top-level version. Capture the version first, then emit
one line per PURL:
Declared dependencies
These are the"id": "dependency" entries. Their packages are nested in dependencies[],
each with a string purl, requirement, and version:
Combine both into one list
For a single vulnerability pass you want every PURL from both surfaces. Onejq
invocation with two branches does it: the select(.id=="file") branch handles matched
components and the .dependencies[]? branch handles dependencies. For any given object
only one branch produces output, so there’s no cross-contamination:
{purl, requirement} shape the query expects. Matched components use their matched
version as the requirement; dependencies use their declared requirement:
Step 3: Query vulnerabilities
With PURLs in hand, the third phase resolves them to CVEs. There are two ways to do this: the CLI, which is fastest for local and scripted use, and the REST API, which is the integration point for other services and languages. Both take the same PURLs in and return the same vulnerability objects out.Using the CLI
Thecomponent vulnerabilities command (alias comp vulns) queries the Knowledge Base. It requires
an API key.
Query a single component:
--output (-o) to save
the results to a file instead of printing to STDOUT:
vulnerabilities array.
Using the REST API
The CLI is a convenience layer over the SCANOSS REST API. Calling the API directly is useful when you’re integrating from a language without the SDK, debugging with--trace,
or wiring vulnerability data into another service.
Every request goes to https://api.scanoss.com and authenticates with your API key in the
X-Api-Key header (reuse the $SCANOSS_API_KEY you exported earlier). There are two
resource types (vulnerabilities and CPEs) and two shapes for each, a single component via
GET, or many components via POST:
For the complete API reference, including request and response definitions, see the
Vulnerability API documentation and the
PAPI definitions.
| Endpoint | Method | Purpose |
|---|---|---|
/v2/vulnerabilities/component | GET | Known vulnerabilities for one component |
/v2/vulnerabilities/components | POST | Known vulnerabilities for many components |
/v2/vulnerabilities/cpes/component | GET | CPE identifiers for one component |
/v2/vulnerabilities/cpes/components | POST | CPE identifiers for many components |
Query one component’s vulnerabilities
Get known vulnerabilities for a single component, including CVE details, severity, and scoring data. Requestpurl— the requested componentversion— the specific version that was analysedrequirement— echoes the version constraint from the requestvulnerabilities— the list of known vulnerabilities affecting the component
cvss scoring array, and Exploit Prediction Scoring System (EPSS) data.
The cvss field is an array of CVSS objects (allowing multiple versions or sources), each
containing:
cvss— the CVSS vector string (e.g.CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H)cvss_score— the numerical score (0.0 to 10.0)cvss_severity— the severity rating derived from the score (None,Low,Medium,High,Critical)
Query many components’ vulnerabilities
Get known vulnerabilities for multiple components in a single request. This is the endpoint the batchcomp vulns -i purl-list.json command calls under the hood.
Request
components array — one
entry per requested PURL, each with its own vulnerabilities list.
Query CPE identifiers
CPEs (Common Platform Enumeration) are the identifiers vulnerability databases key on. Retrieving them is useful when you want to cross-reference a component against an external CVE feed yourself. Request (single component)components array as the vulnerabilities POST:
Step 4: Interpret and prioritise the findings
Raw CVE lists aren’t a plan. For each finding, weigh four signals together:- Severity — the coarse label (
HIGH/CRITICALfirst). - CVSS — the numeric
cvss_scoreand its vector give a precise, comparable measure of impact and exploitability. - EPSS —
percentileestimates the likelihood of exploitation in the wild. Two HIGH CVEs with very different EPSS percentiles deserve very different urgency. - Fix availability — the
summarytypically names the fixed version (e.g. “prior to 4.17.21”), which is your remediation target. Compare the component’sversionagainstlatestfrom the scan to gauge how big the upgrade is.
component status:
Putting it all together
A full vulnerability scan flows like this:- Scan (
scanoss-py scan --dependencies) fingerprints the project and matches it against the Knowledge Base, producingresults.jsonwith both surfaces: matched components ("id": "file") and declared dependencies ("id": "dependency"). - Extract (
jq) pullspurl@versionpairs from both surfaces — the top-levelpurl[]on matched components and the nesteddependencies[]on dependency entries — and merges them into one list. - Query — either
comp vulnsfrom the CLI or aPOST /v2/vulnerabilities/componentscall to the API — resolves each PURL to its known CVEs, with severity, CVSS, and EPSS. - Interpret ranks the findings by severity, CVSS, and EPSS, keeps the two surfaces labelled, and reads the fix version out of each summary.