dpndncY

Dependency Admission / Firewall Events

Dependency Admission evaluates new dependencies and dependency version changes before they enter a repository or build.

The firewall proxy still exists, but the default product posture is not "block every risky package in the current backlog." The useful enforcement point is the delta: packages newly added in a PR, packages upgraded in a lockfile, and packages that would be denied if introduced today.

It reuses existing dpndncY signals:

  • OSV vulnerability lookup
  • registry trust checks
  • prior dependency review records
  • tenant admission policy
  • unverified upgrade candidates

#Decisions

Every package/version receives one decision:

  • allow - no policy violation detected
  • warn - risk exists, but build can continue by default
  • review - manual approval recommended
  • block - dependency should not be installed or merged

Default posture:

  • mode: observe - block-level policy findings are shown as review decisions until a tenant explicitly switches to enforcement.
  • failOn: block - CI and proxy enforcement only fail on block-level decisions by default.

Default block-level rules are intentionally narrow:

  • prior dependency review blocked the same version
  • package/version is not present in the expected registry
  • registry trust check classifies package as untrusted
  • likely typosquatting risk is detected
  • CISA KEV signal is present and a verified fixed version exists
  • license is explicitly blocklisted

Default review rules:

  • package is suspicious or newly published
  • registry status cannot be verified
  • critical vulnerability has no fixed version
  • critical vulnerability has a verified fixed version
  • CISA KEV signal is present but no verified fix exists
  • high vulnerability has a fixed version

#UI

The feature is visible in two places:

  • /app?page=firewall shows the tenant-wide Firewall Events audit log with blocked, review, warn, and allow decisions from proxy, CLI, CI, and API checks.
  • Each scan result has a Dependencies -> Admission subview. It compares the scan to the previous run and shows only new packages, version changes, and unchanged packages that would be denied if introduced today.

Admins can allowlist or blocklist packages directly from the tenant-wide event log. They can also lock a scan as the target's admission baseline or create scan-scoped temporary exceptions from a scan's Admission table. Recorded events keep the policy reason, risk signals, unverified upgrade candidates, and exception metadata when an exception allowed the package.

The Firewall Events page also shows:

  • active temporary exceptions
  • recent policy-impact simulation
  • admission metrics aggregated from recent events

#PR / CI Admission Path

Use the dependency review CI script as the main enforcement path for normal teams. It compares changed manifests between a base and head ref, evaluates new packages through Dependency Admission, and runs package-diff review for version upgrades:

node scripts/dependency-review-ci.js

Relevant environment variables:

DPNDNCY_SERVER_URL
DPNDNCY_API_TOKEN
DPNDNCY_TENANT_ID
DPNDNCY_REVIEW_BASE_REF
DPNDNCY_REVIEW_HEAD_REF
DPNDNCY_ADMISSION_FAIL_ON=block
DPNDNCY_REVIEW_FAIL_ON_RISK=HIGH
DPNDNCY_REVIEW_FAIL_ON_BLOCKING=true

This keeps existing backlog noise out of the gate. CI fails only when the PR introduces a dependency that meets the configured admission or upgrade-review threshold.

#API

Evaluate one package:

POST /api/firewall/evaluate
Content-Type: application/json

{
  "ecosystem": "npm",
  "name": "express",
  "version": "4.18.2",
  "source": "package-lock.json"
}

Evaluate a lockfile-sized batch:

POST /api/firewall/evaluate-batch
Content-Type: application/json

{
  "packages": [
    { "ecosystem": "npm", "name": "express", "version": "4.18.2" },
    { "ecosystem": "pypi", "name": "requests", "version": "2.31.0" }
  ]
}

Evaluate manifest content server-side:

POST /api/firewall/evaluate-manifest
Content-Type: application/json

{
  "path": "package-lock.json",
  "content": "{...}"
}

Policy and audit routes:

GET  /api/firewall/policies
POST /api/firewall/policies
POST /api/firewall/override
GET  /api/firewall/events

Admission diff, exceptions, and impact routes:

GET    /api/scans/:id/admission-diff
POST   /api/scans/:id/admission-baseline
GET    /api/admission/baselines
GET    /api/admission/exceptions?active=true
POST   /api/admission/exceptions
DELETE /api/admission/exceptions/:id
GET    /api/admission/metrics
GET    /api/admission/policy-simulator?limit=8&maxPackages=80

Temporary exception example:

POST /api/admission/exceptions
Content-Type: application/json

{
  "ecosystem": "npm",
  "packageName": "express",
  "version": "4.18.2",
  "scopeType": "scan",
  "scopeId": "scan-id",
  "ttlDays": 14,
  "reason": "Approved for release candidate validation"
}

Scope types are tenant, project, repo, and scan. Non-tenant scopes require a scopeId. Exceptions are temporary and capped at 180 days.

#CLI

CI enforcement:

dpndncy firewall check .
dpndncy firewall check package-lock.json --fail-on review
dpndncy firewall check requirements.txt --json

Exit codes:

  • 0 - no dependency meets the fail threshold
  • 1 - one or more dependencies met the fail threshold
  • 2 - configuration, parsing, or API error

Supported local files:

  • package-lock.json
  • npm-shrinkwrap.json
  • pnpm-lock.yaml
  • yarn.lock
  • package.json
  • requirements.txt
  • pyproject.toml
  • poetry.lock
  • Cargo.lock
  • composer.lock
  • go.mod
  • pom.xml
  • build.gradle and build.gradle.kts
  • packages.lock.json, packages.config, and *.csproj
  • Gemfile.lock

#Registry Proxy

npm:

registry=http://localhost:3001/api/firewall/npm/
//localhost:3001/api/firewall/npm/:_authToken=${DPNDNCY_TOKEN}

PyPI simple index:

pip install --index-url http://localhost:3001/api/firewall/pypi/simple requests

Additional proxy roots:

Maven       /api/firewall/maven/<group>/<artifact>/<version>/<file>
NuGet       /api/firewall/nuget/v3-flatcontainer/<package>/<version>/<file>
Cargo       /api/firewall/cargo/api/v1/crates/<crate>/<version>/download
Composer    /api/firewall/composer/p2/<vendor>/<package>.json
RubyGems    /api/firewall/rubygems/gems/<gem>-<version>.gem
Go          /api/firewall/go/<module>/@v/<version>.zip
Pub         /api/firewall/pub/api/packages/<package>/versions/<version>
CRAN        /api/firewall/cran/src/contrib/<package>_<version>.tar.gz
Conda       /api/firewall/conda/pkgs/main/<subdir>/<package>-<version>-<build>.conda
CPAN        /api/firewall/cpan/authors/id/.../<dist>-<version>.tar.gz
OPAM        /api/firewall/opam/packages/<package>/<package>.<version>/opam
CocoaPods   /api/firewall/cocoapods/Specs/.../<pod>/<version>/<pod>.podspec.json
SwiftPM     /api/firewall/swiftpm/<scope>/<package>/releases/<version>
PEAR        /api/firewall/pear/get/<package>-<version>.tgz

The proxy uses the same evaluator. If the tenant policy is in enforce mode and a request meets the policy fail threshold, the proxy returns 403 with the firewall decision and unverified upgrade candidates. In default observe mode, those same signals are recorded for review without breaking installs.

For ecosystems whose registry metadata contains direct artifact URLs, the firewall rewrites those URLs back through dpndncY so clients cannot bypass enforcement after metadata resolution:

  • npm dist.tarball
  • PyPI simple-index file links
  • Composer dist.url
  • Pub archive_url

The upstream registry base URLs can be overridden with environment variables:

FIREWALL_MAVEN_BASE
FIREWALL_NUGET_BASE
FIREWALL_CARGO_BASE
FIREWALL_CARGOSTATIC_BASE
FIREWALL_COMPOSER_BASE
FIREWALL_RUBYGEMS_BASE
FIREWALL_GO_BASE
FIREWALL_PUB_BASE
FIREWALL_CRAN_BASE
FIREWALL_CONDA_BASE
FIREWALL_CPAN_BASE
FIREWALL_OPAM_BASE
FIREWALL_COCOAPODS_BASE
FIREWALL_SWIFTPM_BASE
FIREWALL_PEAR_BASE

#Storage

The firewall adds:

  • dependency_firewall_policies
  • dependency_firewall_events
  • dependency_firewall_cache
  • dependency_admission_exceptions
  • dependency_admission_baselines

Events are the audit trail for who requested a package, which policy applied, and why the dependency was allowed, warned, reviewed, or blocked.

#Upgrade candidates and evidence

candidateVersions are advisory/registry suggestions requiring a rescan; candidateVersionStatus is unverified. The legacy safeVersions array is empty until independent validation exists. Fix-dependent policy conditions correlate the fix with the same advisory. Prior reviews apply only to the requested version. Evaluator decisions are not automatically DSSE-signed.