Vulnerability scanning

Your dependencies are checked before anything is built

Every from-source deploy resolves the lockfile at the commit being deployed and checks it against OSV.dev, the open vulnerability database. The scan runs immediately after checkout and before the builder starts, so a blocked deploy costs you no build minutes. By default, a critical finding stops the deploy.

What gets scanned

The lockfile at the root of the checked-out commit, and nothing else. Sproobo reads the exact resolved versions your app would install and asks OSV which of them have published advisories:

  • package-lock.json (npm), yarn.lock (yarn), pnpm-lock.yaml (pnpm 9 or newer), and bun.lock (bun 1.2 or newer).
  • Not your base image, its OS packages, or anything installed outside the lockfile. This is dependency scanning, not image scanning.

The scan is skipped, with a line in the build log saying so, when there is no lockfile at the repo root, when only a binary bun.lockb is present, or when pnpm-lock.yaml is in the pre-pnpm-9 format the parser cannot read. A skip deliberately records no report at all rather than a clean one: a scan that did not happen must never be filed as a scan that found nothing.

Rollbacks are never scanned and never blocked. A rollback re-points at an image that already exists, so there is no checkout and no lockfile to read. The emergency lever always works.

The four policies

Each app carries one scan policy:

  • off. No scan runs and no report is recorded.
  • warn. Scan and record the findings, never block.
  • block_critical. Fail the deploy on findings rated critical. This is the default for every app.
  • block_all. Fail the deploy on any finding, at any severity.

Blocking by default is deliberate. A warn-by-default gate leaves every app one forgotten opt-in away from shipping a known-critical dependency, which is the failure it exists to prevent. If that is not the trade you want for a given app, change the policy explicitly and the choice is recorded in your audit trail.

What a blocked deploy looks like

The deployment is marked failed and the build log carries the itemized reason, listing up to the first ten blocking findings with the fix version wherever OSV knows one. The last line is the headline the deployments list and the Slack card show:

Vulnerability scan findings at or above the blocking threshold:
  CRITICAL GHSA-xxxx-yyyy-zzzz: some-package@1.2.3 (fix: 1.2.4) — Prototype pollution in ...
  CRITICAL GHSA-aaaa-bbbb-cccc: other-package@0.9.0 (fix: 0.9.2) — Remote code execution in ...
Deployment blocked by the vulnerability scan policy (block_critical): 2 finding(s) at or above the blocking threshold — upgrade the affected dependencies, or have an org ADMIN adjust the app's scan policy.

Your previous release keeps serving throughout. A block stops a new release from going out. It never takes a running app down, and it never touches the container currently handling traffic.

Two ways forward: upgrade the affected dependencies and push again, or have an org ADMIN change the app's policy. There is no per-deploy override flag, on purpose, since a bypass that lives in the deploy command is one that gets pasted into a script and never removed.

How severity is decided

A finding's severity comes from the advisory, resolved in a fixed order so the same advisory always lands in the same bucket:

  • The advisory's own label wins when it has one. GitHub advisories use CRITICAL, HIGH, MODERATE and LOW, and MODERATE maps onto the medium bucket.
  • Otherwise a CVSS v3.1 base score is computed from the advisory's vector: 9.0 and above is critical, 7.0 and above is high, 4.0 and above is medium, anything lower is low.
  • An advisory carrying only a CVSS v4 vector and no label resolves to medium by design, rather than being guessed into a blocking bucket.

Unchanged dependencies are not re-queried

Reports are cached against the exact content of your lockfile for 24 hours. Deploy twice in an afternoon without touching a dependency and the second deploy reuses the first scan instead of going back to OSV.

The cache skips the OSV round-trip, never the gate. Cached findings are re-evaluated against your current policy on every single deploy, so tightening a policy takes effect on the very next deploy even when nothing in the lockfile changed. The reverse also holds: because advisories are published against versions that already exist, a clean result is only trusted for a day before it is checked again.

An OSV outage will not wedge your deploys.

The scan fails open by contract. A missing lockfile, an unreachable OSV, or a database hiccup is logged and the deploy proceeds. The only thing that ever stops a deploy is a real policy violation on findings actually retrieved. A policy value that is somehow not one of the four behaves as warn, so corrupt data can never block you by surprise.

Worth stating the other half plainly, because it is the cost of that choice: a scan you did not get is a scan that did not protect you. A deploy that sailed through during an OSV outage was not cleared, it was merely not checked. The Security tab shows you when the newest scan actually ran, which is the figure to look at after any incident.

Where findings show up

  • The Security tab on the app, which shows the newest scan from a real deploy: counts by severity, the individual findings, and the fix version where one exists.
  • The release detail panel, for the scan attached to a specific release.
  • The scan history, where results from PR previews appear flagged as previews. Preview results are kept out of the app's headline security state, so a dependency-bump PR never paints production clean while it still ships the vulnerable set.
  • A rollback advisory. When you roll back, Sproobo reports what the target release contained at its last scan, read from records already on disk. It never queries OSV on the recovery path and never blocks the rollback. It is there so you know what you are rolling back into, not to stop you.

The thirty newest reports are kept per app. Reports belonging to a release you can still roll back to are exempt from that limit, so the advisory above cannot go blank on you.

Changing the policy

The Vulnerability scanning card on the app's page in the dashboard. Changing it requires the ADMIN role, records both the old and the new value in your audit trail, and applies on the app's next deploy.

This setting is dashboard-only today. It has no CLI or MCP equivalent, so an AI agent cannot weaken an app's scan policy on your behalf.

Next

  • The deploy pipeline the scan sits in front of is on Apps & deploys.
  • How previews are scanned, and why their results are quarantined, is on Preview deployments.
  • The wider trust boundary, including what Sproobo does and does not hold, is on Security model.