Automate the SBOM scanner. curl-friendly. CI-ready.
The same engine that powers /sbom is a plain HTTP API. Point it at a CycloneDX, SPDX, package.json, or requirements.txt and it returns JSON, SARIF, CSV, or a CycloneDX VEX document. Perfect for GitHub Actions, nightly jobs, or a tiny Python script. Full OpenAPI spec lives at /api/docs.
Endpoints
/api/sbom/scanScan an SBOM/manifest. Accepts JSON, multipart, or raw CycloneDX/SPDX. Query: format (json|sarif|csv|vex), include=vex, fail_on=any|low|medium|high|critical.
/api/sbom/{scan_id}Re-fetch a stored scan in any format. Query: format (json|sarif|csv|vex).
/api/sbom/{scan_id}/export.pdfBranded PDF report with VEX-style findings table.
/api/sbom/vexBuild a CycloneDX VEX 1.5 document from an array of triage decisions.
Authentication
The API is public: no key is required and you get 5 scans/min per IP out of the box. For CI/CD you'll want a key so a shared pool of runners doesn't throttle itself. Ask the admin to create one for you.
X-API-Key. Default rate limit for keyed clients: 60 req/min (configurable per key).Recipes
1. Minimal public scan
# Public rate limit: 5 scans / minute per IP, 20 / hour
curl -s -X POST "https://cveanalytics.in/api/sbom/scan" \
-H "Content-Type: application/json" \
--data '{"content":"{\"name\":\"my-app\",\"dependencies\":{\"lodash\":\"4.17.15\"}}", "name":"my-app"}'2. Authenticated scan with API key
# With API key (ask the admin for one). Default limit: 60 req/min export CVEA_KEY="cvea_your_key_here" curl -s -X POST "https://cveanalytics.in/api/sbom/scan" \ -H "Authorization: Bearer $CVEA_KEY" \ -H "Content-Type: application/json" \ --data-binary @sbom.json
3. File upload (multipart/form-data)
# Upload the SBOM as a file (great for ci/cd runners) curl -s -X POST "https://cveanalytics.in/api/sbom/scan" \ -H "Authorization: Bearer $CVEA_KEY" \ -F "file=@sbom.cdx.json" \ -F "name=my-service"
4. Raw CycloneDX body
# Raw CycloneDX / SPDX body (what most tools produce) curl -s -X POST "https://cveanalytics.in/api/sbom/scan" \ -H "Content-Type: application/vnd.cyclonedx+json" \ -H "X-SBOM-Name: my-app" \ --data-binary @sbom.cdx.json
5. SARIF output for GitHub Code Scanning
# SARIF output (ingestible by GitHub Code Scanning) curl -s -X POST "https://cveanalytics.in/api/sbom/scan?format=sarif" \ -H "Authorization: Bearer $CVEA_KEY" \ -H "Content-Type: application/vnd.cyclonedx+json" \ --data-binary @sbom.cdx.json \ -o report.sarif
6. CSV output for spreadsheets / Jira
# CSV output (spreadsheet / Jira import)
curl -s -X POST "https://cveanalytics.in/api/sbom/scan?format=csv" \
-H "Content-Type: application/json" \
--data '{"content": "$(cat requirements.txt | jq -Rs .)"}' \
-o findings.csv7. Pipeline gating with fail_on
The endpoint returns HTTP 422 when findings at or above the requested severity exist, so your pipeline can fail without extra tooling.
# Pipeline gating: exit non-zero when HIGH+ findings exist
RES=$(curl -s -o /tmp/report.json -w "%{http_code}" \
-X POST "https://cveanalytics.in/api/sbom/scan?fail_on=high" \
-H "Authorization: Bearer $CVEA_KEY" \
-H "Content-Type: application/vnd.cyclonedx+json" \
--data-binary @sbom.cdx.json)
if [ "$RES" = "422" ]; then
echo "::error::High+ vulnerabilities found - failing the build"
cat /tmp/report.json | jq '.severity_counts'
exit 1
fi8. Re-fetch a prior scan
# Re-fetch a prior scan (great for multi-step pipelines) SCAN_ID="4e31e967de9b4109926524536b139500" curl -s "https://cveanalytics.in/api/sbom/$SCAN_ID?format=sarif" -o report.sarif
9. GitHub Action — SARIF + Code Scanning
# .github/workflows/sbom-scan.yml
name: SBOM Vulnerability Scan
on: [push, pull_request]
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Generate CycloneDX SBOM
uses: anchore/sbom-action@v0
with:
format: cyclonedx-json
output-file: sbom.cdx.json
- name: Scan with CVE/Analytics
id: scan
env:
CVEA_KEY: ${{ secrets.CVEA_API_KEY }}
run: |
HTTP=$(curl -s -o report.sarif -w "%{http_code}" \
-X POST "https://cveanalytics.in/api/sbom/scan?format=sarif&fail_on=high" \
-H "Authorization: Bearer $CVEA_KEY" \
-H "Content-Type: application/vnd.cyclonedx+json" \
--data-binary @sbom.cdx.json)
echo "http=$HTTP" >> $GITHUB_OUTPUT
- name: Upload SARIF to GitHub Code Scanning
if: always()
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: report.sarif
- name: Fail on high+ severity
if: steps.scan.outputs.http == '422'
run: exit 110. Python one-liner
# scan.py — one-shot programmatic client
import os, sys, requests
API = "https://cveanalytics.in/api"
KEY = os.environ["CVEA_API_KEY"]
SBOM = open("sbom.cdx.json","rb").read()
r = requests.post(
f"{API}/sbom/scan",
params={"format": "json", "fail_on": "high"},
headers={
"Authorization": f"Bearer {KEY}",
"Content-Type": "application/vnd.cyclonedx+json",
},
data=SBOM,
timeout=60,
)
print("status:", r.status_code)
print("findings:", r.json().get("stats", {}))
if r.status_code == 422:
print("Gate fired. Severity counts:", r.json()["severity_counts"])
sys.exit(1)Severity derivation
We parse the OSV-provided CVSS v3.x vector string on every finding and map the computed base score to CRITICAL / HIGH / MEDIUM / LOW using FIRST.org's standard buckets. When a finding has no vector, severity falls back to UNKNOWN and does not trip fail_on gates (set fail_on=any if you want zero-tolerance builds).