Skip to content

SocialGouv/no-package-malware

Repository files navigation

NoPackageMalware – Secure npm registry gateway for real‑world teams

Because npm install shouldn’t feel like Russian roulette.

NoPackageMalware is a self‑hosted security gate in front of the public npm registry, built on Verdaccio. It combines:

  • a strict fail‑closed security policy,
  • LLM‑assisted malware detection,
  • per‑org policies and budgets, and
  • a ready‑to‑deploy Docker/Helm stack with high‑availability options.

It is designed for platform and security teams who want to keep using the public npm ecosystem without accepting blind npm install risk.


Features

Secure registry gateway on top of Verdaccio

  • Drop‑in npm proxy: front your developers’ npm, yarn or pnpm with two Verdaccio endpoints (strict & lenient).
  • Fail‑closed by design: if the decision engine or workers are down, new or suspicious versions are not installable.
  • Two‑stage enforcement:
    • Manifest filter (verdaccio-security-gate-filter) hides known BLOCKED / ERROR / PENDING versions and decorates risky ones with deprecated warnings and security metadata.
    • Download guard (verdaccio-download-guard-middleware) intercepts tarball downloads and calls /api/security/scan-download to allow/block each exact version.

GPT‑assisted malware detection with heuristics

  • Static heuristics run on the tarball (Node.js package contents):
    • Detects install‑time hooks (preinstall, install, postinstall, prepare) and other suspicious signals.
    • Produces an initial risk score and summary.
  • LLM‑based analysis (OpenAI GPT via services/api/src/llm.ts & workers):
    • Enriches heuristics with a deeper semantic review (when enabled).
    • Combines heuristics + LLM into a single final risk score, risk level, and flags.
  • Configurable GPT usage:
    • Bring‑your‑own OpenAI API key (via Kubernetes Secret or .env).
    • GPT can be disabled for air‑gapped / cost‑sensitive setups (heuristics‑only mode).

Per‑org policies, tokens and budgets

  • Org & token model stored in MongoDB (services/api/src/db.ts):
    • OrgDoc – organizations.
    • TokenDoc – hashed tokens linked to orgs.
    • SecurityPolicyDoc – named policies per org (strict, lenient, …).
    • PolicyOverrideDoc – per‑package allow/block overrides.
  • Token‑based auth for Verdaccio (verdaccio-security-token-auth):
    • npm/yarn tokens are validated by /api/auth/token/validate.
    • Maps tokens to orgs and policies for downstream decisions and budgets.
  • Fine‑grained policies per org:
    • Risk thresholds (warn_threshold, block_threshold).
    • Default action (block vs warn).
    • Optional scan budgets and GPT budgets.
  • Daily scan budgets per org (services/api/src/budget.ts + Redis):
    • Limit how many new package versions an org can trigger per day.
    • When exceeded, new versions are marked with ERROR and a clear summary.
  • Per‑token audit rate limiting (/api/security/scan-download + verdaccio-download-guard-middleware):
    • 24‑hour and 7‑day sliding windows, stored in Redis.
    • Prevents a single token from abusing the scanning infrastructure.
  • GPT budgets per org/day (workers):
    • Workers track how many GPT calls an org has used (max_gpt_calls_per_day).
    • When exhausted, workers fall back to heuristics‑only and record that GPT was skipped.

Multi‑gate registry: strict vs lenient

  • Two Verdaccio instances out of the box:
    • Strict gate – aggressive thresholds, safest defaults.
    • Lenient gate – more permissive, suitable for exploratory/dev installs.
  • Policy is enforced via headers (X-Security-Policy) and per‑org configuration, not hard‑coded at the plugin level.
  • Both instances share the same storage, Mongo and Redis, but differ in policy.

Governance UI & access workflows

  • Security reports dashboard (services/app/src/App.tsx):
    • Filters by package name, status and risk level.
    • Paginated list of reports with risk scores and summaries.
    • Detail pane with human‑readable explanations and last scan time.
  • Access‑admin dashboard & request‑access flow:
    • Admin and request‑access SPAs are wired from the same frontend bundle.
    • Access‑admin API exposes:
      • /api/access-requests (create access request),
      • /api/admin/* (login, list/approve/reject access requests, manage tokens and rate limits),
      • /request-access redirect to the public request‑access portal.
  • Runtime‑configurable URLs (services/app/public/runtime-config.js):
    • Point dashboards and portals to any API/registry endpoints without rebuilding the app.

Production‑ready infrastructure & HA

  • Docker images (single Dockerfile):
    • no-package-malware-api – unified API + workers (rootless USER 1000).
    • no-package-malware-app – SPA served via nginx‑unprivileged (USER 101).
    • no-package-malware-registry – Verdaccio strict/lenient stack (USER 1001).
  • One‑command Docker Compose stack:
    • secure-registry/infra/docker-compose.app.yml runs Mongo, Redis, API, APP, strict & lenient registries.
  • Kubernetes Helm chart with HA options (charts/no-package-malware):
    • Optional in‑cluster MongoDB subchart (with replicaSet toggles).
    • Optional in‑cluster Valkey (Redis‑compatible) subchart (standalone or sentinel).
    • ha.enabled to bump replicas for API, APP and Verdaccio:
      • e.g. api=2, app=2, verdaccioStrict=2, verdaccioLenient=2.
    • Ingress templates for API, dashboard and registry endpoints.
    • Secrets and values for OpenAI, admin credentials and Mongo connectivity.
  • Non‑root by default:
    • All application containers declare an explicit numeric USER for compliance with restrictive clusters.
  • Health & readiness probes:
    • API: /health/live, /health/ready, /health.
    • Workers: /health/live, /health/ready on a dedicated port.

Built‑in malware fixtures, tests and examples

  • Malware fixtures & fake registry (examples/malware-fixtures):
    • Benign packages, env exfiltration examples, install‑script malware, etc.
    • Local “malware” Verdaccio instance for safe experimentation.
  • End‑to‑end tests (secure-registry/tests/e2e):
    • Cover GPT flows and malware reports scenarios.
  • Example consumer app (examples/consumer-app):
    • Minimal Node app that resolves dependencies via the strict registry.
    • Shows how an existing project can plug into the gate using .npmrc.

Architecture overview

At a high level, the stack consists of:

  • Verdaccio registries (strict, lenient, and a local malware playground):
    • Front door for all npm/yarn/pnpm traffic.
    • Apply security filters and middlewares.
  • Unified security API (services/api):
    • Security decision API (bulk report, scan‑download, reports listing).
    • Access‑admin API and request‑access endpoints.
    • Health endpoints and background worker startup.
  • Security workers (services/api/src/workers.ts):
    • Consume scan jobs from the security-scan queue.
    • Download and unpack tarballs.
    • Run heuristics + optional LLM analysis.
    • Update security_reports in MongoDB.
  • Data stores:
    • MongoDB – security reports, orgs, tokens, policies, overrides.
    • Redis / Valkey – scan budgets, GPT budgets, per‑token audit rate‑limits, HTTP rate limiting, and job queues (via BullMQ).
  • SPA:
    • Dashboards for security reports and access‑admin.
    • Request‑access UX for onboarding users/teams.

For more details, see:


How it works end‑to‑end

1. Install flow

  1. A client (npm, yarn, pnpm) installs from the strict or lenient Verdaccio endpoint.
  2. Verdaccio fetches the package manifest (GET /<name>) and passes it through the verdaccio-security-gate-filter plugin.
  3. The filter calls POST /api/security/bulk-report/:name with the list of versions present in the manifest.
  4. The API does not create scans here; it only:
    • fetches existing reports from MongoDB,
    • returns a version → report mapping.
  5. The filter:
    • removes versions with status BLOCKED, PENDING, or ERROR,
    • keeps APPROVED and APPROVED_WITH_WARNINGS versions,
    • adds a deprecated message and security metadata for APPROVED_WITH_WARNINGS.
  6. Verdaccio responds to the client with a filtered manifest.
  7. When the client picks a version, Verdaccio serves the tarball route (GET /<name>/-/<name>-<version>.tgz). This request is intercepted by verdaccio-download-guard-middleware, which calls:
    • POST /api/security/scan-download/:name/:version
  8. The scan-download endpoint:
    • Enforces per‑org scan budgets and per‑token audit rate limits.
    • Creates or updates a security_reports entry for the exact version (initially PENDING with a “triggered by download” summary if first‑seen).
    • Enqueues a ScanJob into the BullMQ queue when allowed.
    • Waits (blocking) for the report to move out of PENDING, up to a configurable timeout.
  9. Workers pick up the job, download the tarball from the public registry, run heuristics + optional GPT analysis, and update the report to one of:
    • APPROVED,
    • APPROVED_WITH_WARNINGS,
    • BLOCKED,
    • or ERROR.
  10. Once the report is updated (or timeout hits), the scan-download API returns the effective status, risk score and summary to the middleware.
  11. The middleware either:
    • lets the tarball download proceed (APPROVED or APPROVED_WITH_WARNINGS), or
    • blocks the download with clear JSON explaining why (BLOCKED, PENDING, ERROR).

If the decision API or workers are unavailable, the middleware fails closed:

  • tarball downloads are blocked with a 5xx JSON error,
  • so “unknown” isn’t silently treated as “safe”.

2. Tokens, orgs and policies

The unified API models organizations and security policies in MongoDB (via services/api/src/db.ts):

  • Token validation (POST /api/auth/token/validate):
    • Verdaccio auth plugin calls this endpoint with the raw npm _authToken.
    • On success, the API responds with the org and token ids.
  • Policy resolution:
    • Respects X-Security-Policy headers (strict / lenient / custom).
    • If a valid security token is present, resolves an org‑specific policy.
    • Falls back to built‑in defaults when none are configured.
  • Risk → status mapping (mapRiskToStatusForPolicy):
    • Threshold‑based mapping using the active EffectivePolicy.
    • Produces the final status exposed to Verdaccio and the dashboard.

3. Budgets and rate limiting

  • Scan budgets per org (decision API):
    • Cap the number of new version scans an org can trigger per day.
    • Exceeded budgets result in ERROR reports with a clear summary.
  • Audit rate limits per token (download guard):
    • Use Redis counters keyed by token id.
    • Separate limits for the current 24h window and last 7 days.
  • GPT budgets per org/day (workers):
    • Workers look up policies for the job’s org and enforce a maximum number of GPT calls per day.
    • GPT failures can be configured to result in ERROR to preserve fail‑closed behavior.

4. Dashboards and access workflows

  • Security dashboard (services/app):
    • Calls /api/security/reports to list reports.
    • Lets you filter by package, status and risk level.
  • Access‑admin & request‑access:
    • Expose public /api/access-requests and admin /api/admin/* endpoints.
    • Support a bootstrap default admin (admin / admin) for local/dev use and Secrets for production.
    • Access‑admin redirects /request-access to a configurable SPA URL (set via ACCESS_REQUEST_PORTAL_URL or Helm values).

Running locally

1. One‑time setup

cp .env.example .env
# Edit .env to set at least OPENAI_API_KEY (or set GPT_ENABLED=false to disable GPT)
pnpm install
pnpm -r build

2. Start the full dev stack

pnpm dev

This will:

  • Start MongoDB and Redis via secure-registry/infra/docker-compose.yml.
  • Run the unified API (security decision + access‑admin + workers) in dev mode on http://localhost:4000 using services/api.
  • Start Verdaccio instances:
    • Strict gate at http://localhost:${VERDACCIO_STRICT_PORT:-4873} using secure-registry/verdaccio/strict/config.yaml.
    • Lenient gate at http://localhost:${VERDACCIO_LENIENT_PORT:-4874} using secure-registry/verdaccio/lenient/config.yaml.
    • Local malware playground at http://localhost:${VERDACCIO_MALWARE_PORT:-4875} using secure-registry/verdaccio/malware/config.yaml.
  • Start the unified web app in services/app with Vite dev server on http://localhost:5174:
    • http://localhost:5174/dashboard/security – security reports dashboard
    • http://localhost:5174/dashboard/access-admin – access admin dashboard
    • http://localhost:5174/request-access – public request‑access portal

You can also use the Taskfile if you’re on Devbox:

# from the project root
task dev

3. Seed MongoDB with an org, token and policies

pnpm --filter @secure-registry/security-decision-api seed

This script:

  • Ensures an org (default demo-org) exists.
  • Creates or reuses a demo token.
  • Upserts strict and lenient policies with sensible defaults.

It logs the token you should use as your npm _authToken.

4. Configure npm to use the secure registry

Strict instance:

npm set always-auth true
npm set registry http://localhost:4873/
npm set //localhost:4873/:_authToken=<your-token>

Lenient instance:

npm set always-auth true
npm set registry http://localhost:4874/
npm set //localhost:4874/:_authToken=<your-token>

From there you can:

  • Observe WARN deprecated messages for APPROVED_WITH_WARNINGS versions.
  • See installs fail when a version is BLOCKED, PENDING or ERROR.
  • Trigger scan budgets and rate‑limits and inspect the resulting reports.

Docker deployment (unified services)

For a production‑like single‑host setup you can use the provided multi‑stage Dockerfile and Docker Compose files.

Build the images

From the repo root:

# Unified API (Node, rootless USER 1000)
docker build -t no-package-malware-api --target api-runtime .

# Unified SPA (nginx-unprivileged, rootless USER 101)
docker build -t no-package-malware-app --target app-runtime .

# Verdaccio registry (strict/lenient, rootless USER 1001)
docker build -t no-package-malware-registry --target registry-runtime .

Run the stack with docker-compose

A single compose file defines the full stack (Mongo, Redis, API, APP, strict & lenient registries):

cd secure-registry/infra
# Start Mongo, Redis, unified API, unified SPA and strict/lenient registries
docker compose -f docker-compose.app.yml up

Available endpoints:

  • Unified API: http://localhost:4000
  • Unified SPA: http://localhost:4173
  • Verdaccio strict gate: http://localhost:4873
  • Verdaccio lenient gate: http://localhost:4874

All application containers are rootless and declare an explicit numeric USER so Kubernetes and platform policies can enforce non‑root execution.


Deploying to Kubernetes (Helm)

The charts/no-package-malware chart packages the full stack for Kubernetes.

Quick start (from this repo)

cd charts/no-package-malware
helm dependency update
helm install npm-gate .

You’ll typically also:

  • Create a Secret with your OpenAI API key and set openai.apiKeySecret.secretName.
  • Configure MongoDB connection (mongo.uri) or enable the in‑cluster MongoDB subchart.
  • Configure ingress hosts for API, app and registries.

Enabling HA

In your values file:

ha:
  enabled: true

mongodb:
  enabled: true
  replicaSet:
    enabled: true
    secondaries: 2

valkeyha:
  enabled: true
  architecture: standalone

This will:

  • Run multiple replicas of the API, APP, and strict/lenient registries.
  • Deploy MongoDB and Valkey in‑cluster (or let you point to existing services).
  • Let you wire readiness probes to your service mesh / ingress controller.

See charts/no-package-malware/values.yaml for all available knobs.


Getting started as a consumer

An example consumer app is provided in examples/consumer-app.

  1. Start the secure registry stack as described above.
  2. Seed at least one org, token and policies.
  3. Update examples/consumer-app/.npmrc with your strict registry URL and token.
  4. Run:
cd examples/consumer-app
npm install
npm start

The app performs a simple require('left-pad') via the secure registry. Depending on the selected version’s status under your policy, installs will succeed, warn, or fail.


License

Apache‑2.0 (see package.json).

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors