Dependency checks
depsCatch made-up, brand-new and look-alike packages, known advisories, denied licences and unreviewed install scripts in your dependencies when you run mainsdk check, with a 24-hour cache and a clear notice offline.
dependency-checkFree in your app, Apache-2.0Status Status is where the work is: Planned (in a milestone, not started), Building (tickets in progress), Preview (usable, may change), then the stability it ships at.
Stability The promise an interface ships with.
- Stable Follows semantic versioning; breaking changes need a new major, 12 months of overlap and a codemod.
- Beta Usable in production; may change before it becomes stable.
- Experimental May change or go away; needs an explicit opt-in in main.yaml.
The deps gate of mainsdk check flags made-up, brand-new and look-alike packages, advisories, denied licences and unreviewed install scripts in your dependencies. It runs in the CLI only and is free.
At a glance
deps:
min-age: 7d # a direct dependency first published more recently is reported
licences: { deny: [AGPL-3.0-only, SSPL-1.0], allow: [], unknown: warn }
advisories:
fail-on: high # critical | high | moderate | low | none
ignore:
- { id: GHSA-xxxx-xxxx-xxxx, until: 2027-01-31, reason: "Not reachable: only used in tests" }
scripts: { allow: [esbuild, sharp] }
offline: info # info | fail# mainsdk check runs the deps gate with the others; the CLI asks the registry and OSV, then keeps the answers for 24 hours
npx mainsdk check --jsonGuarantees
Whatever the driver.
- Runs only in the CLI; nothing loads in the app or its request path
- Reads pnpm, npm, yarn (classic and Berry) and bun lockfiles into one component list
- A registry token goes only to the host it is configured for, never to another registry or to OSV
- Offline is an info finding, not a failure, and answers are cached for 24 hours
Drivers
2 drivers. Select one to see its line in main.yaml.
deps:
driver: osvChange this one line to swap providers; your calls stay the same.OSV.dev advisories integration
Codes
| Code | Where it appears | Meaning |
|---|---|---|
MAIN-DEPS-0001 | Checkmainsdk check, before deploy | A direct dependency isn't on its registry: possibly a name a model made up, and anyone may register it (high) |
MAIN-DEPS-0002 | Checkmainsdk check, before deploy | A direct dependency's first version is younger than deps.min-age (high) |
MAIN-DEPS-0003 | Checkmainsdk check, before deploy | A direct dependency's name is one edit away from a popular package it isn't (medium) |
MAIN-DEPS-0004 | Checkmainsdk check, before deploy | A resolved package has a known advisory; at or above advisories.fail-on it fails, below it warns |
MAIN-DEPS-0005 | Checkmainsdk check, before deploy | A package's licence is denied, outside a non-empty allow list, or unknown with unknown: fail |
MAIN-DEPS-0006 | Checkmainsdk check, before deploy | A package runs an install script (preinstall, install, postinstall) and isn't in deps.scripts.allow (warning) |
MAIN-DEPS-0007 | Checkmainsdk check, before deploy | Advisories and package ages weren't checked because the network was unavailable (info, or a failure with offline: fail) |
MAIN-DEPS-0008 | Checkmainsdk check, before deploy | An advisory ignore entry has expired, or has no until or reason |
Features
Nothing here has shipped yet. Each feature lands in its phase; the spec ticket builds it.
| Feature | Phase | Milestone | Spec ticket |
|---|---|---|---|
| Lockfile readers for pnpm, npm, yarn and bun | Phase 1 | 0.5 | dependency-check#lockfiles |
| Unknown, brand-new and look-alike direct dependencies | Phase 1 | 0.5 | dependency-check#registry-rules |
| Advisories from OSV with severity, fixed version and path | Phase 1 | 0.5 | dependency-check#advisories |
| Licence policy over SPDX expressions | Phase 1 | 0.5 | dependency-check#licences |
| Install scripts and the offline cache | Phase 1 | 0.5 | dependency-check#scripts-offline |
| Licences in the seal's SBOM | Phase 1 | 0.5 | dependency-check#seal |
Quick start
npx mainsdk checkThe gate needs no setting. mainsdk check asks the registry and OSV.dev once, keeps the answers for 24 hours under .mainsdk/cache/deps/, and then runs every gate.
Configuration
Add a deps section to main.yaml to change the policy. Every key is optional.
deps:
min-age: 7d # a direct dependency first published more recently is reported
licences:
deny: [AGPL-1.0-or-later, AGPL-3.0-only, AGPL-3.0-or-later, SSPL-1.0] # the default list
allow: [] # when not empty, any licence outside it is reported
unknown: warn # warn | fail
advisories:
fail-on: high # critical | high | moderate | low | none
ignore:
- { id: GHSA-xxxx-xxxx-xxxx, until: 2027-01-31, reason: "Not reachable: only used in tests" }
scripts:
allow: [esbuild, sharp] # packages allowed to run install scripts
offline: info # info | fail; fail is for CI with network access| Key | Type | What it does |
|---|---|---|
min-age | duration | A direct dependency first published more recently is reported (MAIN-DEPS-0002) |
licences.deny | list | Licences that fail the gate. Defaults to the AGPL and SSPL list shown |
licences.allow | list | When not empty, any licence outside it is reported |
licences.unknown | string | warn or fail for a package whose licence cannot be read |
advisories.fail-on | string | critical, high, moderate, low or none; at or above it fails, below it warns |
advisories.ignore | list | Advisories to skip, each with an id, an until date and a reason |
scripts.allow | list | Packages allowed to run install scripts |
offline | string | info or fail when the network is unavailable; fail is for CI |
What you get
The deps gate of mainsdk check reads your lockfile and the installed packages and tells you, before CI or a deploy does, that a dependency is a name a model made up, was published two days ago, is one letter from a popular package, has a known advisory, carries a licence you can't ship under, or runs an install script nobody allowed. Every finding names the package, the version, why and the fix. It runs in the CLI only: nothing of it loads in your app or its request path. It is free.
What it reads
- Lockfiles:
pnpm-lock.yaml(v9),package-lock.json(v3),yarn.lock(classic and Berry) andbun.lock. Every format gives the same list: name, version, direct or transitive, and the path from a direct dependency. Workspace packages are left out. - Direct dependencies: the
dependencies,devDependencies,optionalDependenciesandpeerDependenciesof every workspacepackage.json. - Registries: the
registryand@scope:registrylines of.npmrc(project, then user), else the public npm registry. A token goes only to the host it is configured for, never to another registry and never to OSV. - Installed packages: each
node_modules/<name>/package.json(and pnpm's store) for the licence and install scripts. A package that isn't installed has no licence or script finding.
Offline and the cache
With no network and no cache younger than 24 hours, the gate reports MAIN-DEPS-0007 once and still runs the lockfile, licence and script rules. An unreachable registry or OSV never blocks a local check; after the first failure the CLI stops asking, so an offline run costs one timeout. Set offline: fail in CI, where the network is there.
What it always does
- Runs only in the CLI, never in the app (ADR-0074).
- Sends no credentials to OSV, and registry tokens only to their own host.
- Gives the same component list for the same dependencies in any lockfile format.
Errors and checks
| Code | Meaning |
|---|---|
MAIN-DEPS-0001 | A direct dependency isn't on its registry. Anyone may register a name a model made up. |
MAIN-DEPS-0002 | A direct dependency's first version is younger than deps.min-age. |
MAIN-DEPS-0003 | A direct dependency is one edit away from a popular package it isn't (names under 4 characters are skipped; private registries are skipped). |
MAIN-DEPS-0004 | A resolved package has an advisory, with its id, severity, fixed version and dependency path. At or above fail-on it fails; below it warns. |
MAIN-DEPS-0005 | A licence is denied, outside a non-empty allow, or unknown with unknown: fail. OR passes when any branch passes, AND needs every part, WITH keeps the base licence's result. |
MAIN-DEPS-0006 | A package runs preinstall, install or postinstall and isn't in scripts.allow (a warning). |
MAIN-DEPS-0007 | The network was unavailable, so advisories and ages weren't checked (info, or a failure with offline: fail). |
MAIN-DEPS-0008 | An advisory ignore has expired or has no until or reason; the advisory is reported again. |
Each code's cause and fix is in check codes.
Swapping drivers
deps has no driver. It runs in the CLI against your lockfile, the registry and OSV.dev, so there is nothing to swap.
Testing
The gate's tests run against a fake registry, a fake private registry and a fake OSV server, so none reaches a real host (packages/cli/test/deps.test.ts). In your own CI, run npx mainsdk check with offline: fail so a missing network fails the build.
Next
Was this page helpful?