How the Dependency Scanner Works
In one sentence: the scanner reads the exact package versions in your lockfile and reports which of them fall inside the affected range of a published security advisory. It does not assess whether your app is secure.
Supported files
| File | Supported | Notes |
|---|---|---|
package-lock.json | Yes | lockfileVersion 1, 2, and 3. An older npm-shrinkwrap.json uses the same format and is accepted, though npm 12 no longer reads that file; rename it to package-lock.json. |
yarn.lock | Yes | Yarn Classic (v1) and Yarn Berry lockfiles. |
package.json | No | It lists version ranges such as ^0.74.0, not the versions that were installed, so it cannot be matched against advisories. |
pnpm-lock.yaml, bun.lock, bun.lockb | No | npm install --package-lock-only creates a package-lock.json, though its resolved versions can differ from what pnpm or Bun installed. |
Podfile.lock, Gradle lockfiles | No | Native iOS and Android dependencies are outside this scanner. |
An unsupported or malformed file is rejected on the upload step with the reason and what to upload instead. It is never scanned.
How matching works
- Read installed versions. Every package entry with an exact version is collected, including transitive dependencies. npm aliases are resolved to the real package name. The same version installed at several paths is checked once.
- Skip what is not on the npm registry. Packages installed from git, local folders, links, or workspaces have no registry advisories. They are counted and shown as not checked.
- Look up advisories. Names and versions go to our Cloudflare Worker, which posts them to the npm registry's bulk advisory endpoint, the same endpoint
npm audituses. Each returned advisory includes an affected version range, and each installed version is tested against it. A range we cannot evaluate is treated as affected, so a parsing gap can over-report but never hide a match. - Fall back to OSV.dev. If the npm lookup fails after one retry, your browser queries OSV.dev directly in batches of at most 1,000 packages, follows OSV's pagination, and fetches each advisory's details for its title and severity.
- Merge duplicates. An advisory that appears several times for one package, for example once per affected range, is shown once with every affected version.
Scan states
| State | When | What you see |
|---|---|---|
| Unsupported | The file is not a supported lockfile, or has no registry packages. | An error on the upload step. No scan runs and no email is requested. |
| Complete | Every registry package version was checked. | The report, after you enter a work email. |
| Partial | Some lookups failed after retries. | The report, marked incomplete, with how many package versions were checked. Matches cover checked packages only. |
| Failed | No package version could be checked. | A failure message with a retry. No counts, no report, and no email request. |
A partial or failed scan is never shown as a clean result, and the scan summary we receive with your email carries the same status.
What severity means
Severity (critical, high, medium, low) is the rating published with each advisory. GitHub assigns severity for advisories in the GitHub Advisory Database using CVSS. If OSV.dev details cannot be retrieved, the scanner shows the severity as unknown instead of guessing.
Severity describes the vulnerability in general, not the risk to your app. The CVSS user guide says it directly in a section titled "CVSS Measures Severity, not Risk." Three separate questions decide what to fix first:
- Is the version affected? This is what the scanner answers.
- Is the affected code reachable in your app? For example, a package that only runs at build time, or a function your app never calls, carries different risk from a library that handles untrusted input at runtime. The scanner does not check this.
- Is it being exploited? Signals such as the CISA Known Exploited Vulnerabilities catalog and FIRST's EPSS scores address this. The scanner does not include them. Both cover only vulnerabilities with a CVE ID, and some npm advisories have only a GHSA ID.
What a result does not tell you
- Whether your app is secure. "No published advisories matched" means only that no installed registry package version fell inside a published advisory's range at scan time.
- Anything about your source code, secrets, configuration, network security, or data storage.
- Anything about native iOS or Android dependencies such as CocoaPods or Gradle libraries.
- Vulnerabilities that have not been published yet, or advisories that have not reached the databases.
- Whether a package is used only in development. The scanner checks development dependencies too and does not separate them.
- Which upgrade fixes a match, or which of your dependencies pulls a vulnerable package in.
npm audit,npm explain, andyarn whyanswer those locally.
Differences from npm audit
The primary lookup uses the same bulk advisory endpoint as npm audit, so the advisories that match should agree. The scanner differs in presentation and scope: it lists each advisory once per package rather than per install path, and it does not compute fixes, mark dependent packages as vulnerable, or filter development dependencies. For Yarn projects, yarn audit (Classic) and yarn npm audit (Berry) are the local equivalents.
What is sent and stored
| Step | Data | Sent to |
|---|---|---|
| Upload | Nothing. The file is read in your browser and never uploaded. | No one |
| Scan | Package names and versions, and the detected React Native version | Our Cloudflare Worker, which sends the names and versions to the npm registry |
| Scan, if the npm lookup fails | Package names and versions | OSV.dev, directly from your browser |
| Opening the report | Your email, the scan status and counts, lockfile format, React Native version, and the page address | Our Worker, which notifies our team |
We never store your lockfile or your full package list. When scan storage is enabled on our Worker, a scan that matched advisories is kept with its matched advisories, counts, and basic request details (country, browser user agent, referring page) for up to 90 days. Leads, meaning your email with the scan summary, are kept for up to 24 months. The scanner page may also send anonymous usage events, such as a scan's status, to our analytics and ad measurement tools. See the privacy policy.
How the scanner is tested
The parser and report logic run against lockfiles generated from one synthetic project by npm 6 (lockfileVersion 1), npm 11 (lockfileVersions 2 and 3), Yarn 1.22, and Yarn 4.9, plus hand-written edge cases for aliases, git and workspace packages, and unsupported files. Advisory responses recorded from the npm registry and OSV.dev are replayed through the real Worker code. The tests also simulate a failed primary lookup, a failed fallback, a partial batch failure, pagination, missing severity details, and timeouts, and check that none of them can produce a clean result. They run on every site build. The sample report is generated by the same code from a synthetic lockfile.
Sources
- npm Docs, npm audit (bulk advisory endpoint and range matching)
- OSV.dev API, POST /v1/querybatch and GET /v1/vulns
- GitHub Docs, About the GitHub Advisory Database
- GitHub Docs, About Dependabot alerts ("Alerts can't catch every security issue")
- FIRST, CVSS v3.1 User Guide and EPSS
- CISA, Known Exploited Vulnerabilities Catalog
- OWASP, MASVS-CODE-3
- Yarn, yarn npm audit and yarn audit (Classic)