Skip to content

SBOM Analysis ​

Upload a CycloneDX or SPDX document and get back the known vulnerabilities in its components, along with what the scan did not cover.

Endpoints ​

POST /api/v1/sbom             # analyse a document
POST /api/v1/sbom/scan        # the same operation, kept for existing callers
POST /api/v1/sbom/time-travel # exposure at a past date
GET  /api/v1/sbom/formats     # accepted formats, PURL types and limits

No credential is required. An earlier version asked for a GitHub token in exchange for a larger component cap; it accepted any token able to read a profile, so it gated nothing while asking people uploading a dependency inventory to hand over a third-party credential as well. A caller still sending that header is not refused, and the header never leaves the service.

The document is the request body. It is processed in memory and is not stored.

Request ​

bash
curl -X POST https://api.vulnpatch.dev/api/v1/sbom/scan \
  -H 'Content-Type: application/json' \
  --data-binary @sbom.json
LimitValue
Payload1 MB
Components100
Rate2 requests per 10 seconds per address, enforced per Cloudflare location

A document with more components than the cap is analysed up to the cap and reports the remainder in notAnalyzed. Split a larger document and send it in parts.

Response ​

json
{
  "success": true,
  "data": {
    "summary": {
      "totalComponents": 50,
      "analyzedComponents": 50,
      "failedComponents": 0,
      "truncated": false,
      "notAnalyzed": 0,
      "maliciousScreened": true,
      "maliciousSuspects": 2,
      "vulnerableComponents": 5,
      "totalVulnerabilities": 47
    },
    "vulnerableComponents": [],
    "maliciousSuspects": [],
    "allComponents": []
  }
}

Reading the coverage before reading the result ​

A count of zero vulnerable components means nothing on its own. Compare analyzedComponents against totalComponents first.

FieldMeaning
analyzedComponentsComponents actually examined
failedComponentsComponents whose lookup did not complete. These were not checked, and they are not clean
truncated, notAnalyzedThe document held more components than the cap, and this many were never examined
maliciousScreenedFalse when the known-malicious set could not be read. No component was screened, and none may be treated as cleared
completeTrue only when every component was examined. This is the field to gate on

A scan in which no component could be checked answers 503, not 200, so a step using curl --fail does not pass a scan that read nothing. A partial scan answers 200 with complete: false.

A vulnerability carries cvssScore: null when the source published a severity word rather than a score. No number is derived from the word.

Malicious package screening ​

Every component is checked against a local set of known-malicious package names before any network call. A match appears in maliciousSuspects carrying confirmed: false.

Advisories name specific versions and this set holds names, so a match is a prompt to read the advisory rather than a verdict on the version in your document. axios is listed on the strength of two published versions and is otherwise ordinary.

Time travel ​

bash
curl -X POST https://api.vulnpatch.dev/api/v1/sbom/time-travel \
  -H 'Content-Type: application/json' \
  -d '{"sbom": { ... }, "date": "2024-01-01"}'

Answers what was known about these components on a given date, separating vulnerabilities already disclosed then from those disclosed since.

What leaves the worker ​

Component identifiers, meaning ecosystem, name and version, are sent to OSV.dev for matching. The document itself, its metadata and its dependency structure are not: the parser reduces the document to package URLs before anything is sent. Request bodies are never logged.