Skip to main content

Integrating with the platform

Cyscan on its own is great for shift-left in one repo. The Cybrium platform adds the layers that only make sense with state: reachability, multi-repo dashboards, historical trend, team triage, and compliance evidence bundles.

The bridge between the two is SARIF — cyscan emits it, the platform ingests it.

How the integration works

┌──────────────── developer / CI ────────────────┐
│ │
│ cyscan scan . --format sarif → out.sarif │
│ │
└──────────────────────────────────────┬──────────┘


POST /api/scans/upload-sarif/ │ Authorization: Bearer sk-...

┌──────────────── platform ─────────────────────┐
│ │
│ parses SARIF → creates ScanJob │
│ upserts Finding rows (dedup by rule/file/line)│
│ enriches supply-chain with reachability │
│ routes to tenant schema via API-key org binding│
│ │
└────────────────────────────────────────────────┘

1. Get an API key

Platform UI → Settings → API Keys → Create API key

  • Name: anything, e.g. ci-github-actions
  • Scopes: at minimum scan:upload and findings:write
  • Expiry: 90 days (recommended) or leave blank for non-expiring

You'll see the raw sk-... key once — copy it now. It's tenant-scoped automatically to the org you're browsing.

Prefer browser-based login?

Use the Cybrium CLI: cybrium login opens a browser, mints a 90-day key, stores it in ~/.cybrium/config.yml. Then the CLI handles SARIF uploads for you:

cyscan scan . --format sarif | cybrium findings upload -

2. Upload SARIF

cyscan scan . --format sarif > out.sarif

curl -X POST https://app.cybrium.ai/api/scans/upload-sarif/ \
-H "Authorization: Bearer sk-..." \
-F "sarif=@out.sarif" \
-F "title=CI scan on $(git rev-parse --short HEAD)"

Response:

{
"upload_id": "4d775be0-87d6-48d0-bde4-3a168e2cc31f",
"scan_id": "3c647cd6-23c6-45dd-a5d6-446be91d6b9f",
"tool": "cyscan",
"payload_sha256": "ef8c41f46ba3d1cf...",
"findings_created": 5,
"findings_updated": 0,
"findings_skipped": 0,
"idempotent": false,
"created_at": "2026-04-14T14:29:21Z"
}

Idempotency

Re-uploading the same file returns "idempotent": true with no changes — content sha256 is the dedup key. Use this to retry fearlessly in CI.

Attaching to an existing scan

Pass scan_id to append findings to an existing scan rather than creating a new one:

curl -X POST .../upload-sarif/ \
-H "Authorization: Bearer sk-..." \
-F "sarif=@out.sarif" \
-F "scan_id=3c647cd6-23c6-45dd-a5d6-446be91d6b9f"

GitHub Actions recipe

name: Cyscan
on: [push, pull_request]

jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Install cyscan
run: |
curl -sSL "https://github.com/cybrium-ai/cyscan/releases/latest/download/cyscan_latest_x86_64-unknown-linux-gnu.tar.gz" \
| tar -xz -C /tmp
sudo mv /tmp/cyscan_*/cyscan /usr/local/bin/
sudo mv /tmp/cyscan_*/rules /opt/cyscan-rules
echo "CYSCAN_RULES=/opt/cyscan-rules" >> $GITHUB_ENV

- name: Scan + upload
env:
CYBRIUM_API_KEY: ${{ secrets.CYBRIUM_API_KEY }}
run: |
cyscan scan . --format sarif > cyscan.sarif
curl --fail -X POST https://app.cybrium.ai/api/scans/upload-sarif/ \
-H "Authorization: Bearer ${CYBRIUM_API_KEY}" \
-F "sarif=@cyscan.sarif" \
-F "title=${GITHUB_REPOSITORY} @ ${GITHUB_SHA:0:7}"

Reachability enrichment (platform-only)

Cyscan's supply-chain findings arrive tagged with packageCoordinate (e.g. event-stream@3.3.6). The platform's reachability engine walks your uploaded call graph and annotates each finding with a verdict:

  • reached — your code actually calls a vulnerable symbol
  • unreached — the dep is present but no call path exists
  • unknown — not enough data (no call graph yet; common for first upload)

Unreached findings are collapsed under a "hidden by reachability" counter in the UI. Reached ones stay front and centre. Customers typically see a ~20x reduction in apparent CVE noise after the first full-project reachability pass.

This layer is proprietary — the OSS cyscan binary emits the coordinate data, the platform does the graph work.

Scopes reference

Each endpoint requires a specific scope on the API key:

EndpointRequired scope
POST /api/scans/upload-sarif/scan:upload
GET /api/scans/scan:read
GET /api/scans/findings/findings:read
PATCH /api/scans/findings/:id/findings:write

A key with no scopes is legacy full-access; new keys created from the UI default to scan:upload + findings:write + findings:read.

Rate limits

  • Login attempts: 10 per 10 minutes per IP
  • Device-code activation: 30 per 10 minutes per IP
  • SARIF upload: 60 uploads per minute per API key

Hit one and you get a 403 with a plain-English detail. Wait out the window and retry.

Next step

  • Cybrium CLI — the higher-level wrapper that handles auth, upload, and more