ReviewOS

pantry-pm/pantry

security(registry): add a fail-closed publish-time malware scanner

#222
Closed chris opened this 2 months ago ·
2 months ago

Goal

Introduce a reusable malware-scanning boundary that examines uploaded package bytes before any artifact or version metadata becomes installable.

Context

npm now scans newly published packages before availability and may allow, hold, or block a publish: https://github.blog/changelog/2026-07-28-npm-publish-time-malware-scanning-and-dual-use-metadata/

Pantry currently validates identity, metadata, size, and duplicate versions, then persists the tarball immediately. It does not inspect uploaded content for malware.

Scope

  • Define a scanner interface and stable verdict model: clean, review, blocked, and error.
  • Provide a production ClamAV/clamd implementation using the INSTREAM protocol, with timeouts and bounded responses.
  • Compute a scan identifier/hash and capture engine/signature/version/timing metadata.
  • Fail closed when scanning is required but unavailable or times out.
  • Make development/test behavior explicit and injectable; never silently disable production scanning.
  • Bound archive size, decompression behavior, scanner response size, and network time.
  • Avoid logging tarball content, tokens, or sensitive disclosure text.

Acceptance criteria

  • Clean EICAR-free fixtures return clean.
  • EICAR fixtures return blocked with the signature name.
  • Scanner outage and timeout return a non-success verdict and do not publish.
  • Configuration is environment-driven and validated at startup/request time.
  • Unit tests cover protocol framing, partial TCP responses, limits, timeouts, and malformed responses.
  • The implementation has no external paid-service dependency.

Non-goals

  • Replacing a full antivirus signature feed with handwritten heuristics.
  • Executing uploaded package lifecycle scripts.
  • Allowing a scanner outage to publish unscanned artifacts.
9 days ago

Implementation status (2026-07-28): code complete in the current worktree, pending review/merge and production deployment.

Implemented:

  • ClamAV clamd INSTREAM adapter with bounded framing, timeouts, artifact-size limits, exact SHA-256, engine/signature metadata, and fail-closed handling for malformed responses or daemon failures.
  • Production defaults to required scanning; disabling it requires an explicit break-glass acknowledgement.
  • Verdict contract: clean publishes, blocked returns 422, review returns 202 without publishing, scanner errors return retryable 503.
  • Structured audit events, in-process counters by surface/verdict, liveness/readiness separation, and scanner status endpoint.
  • Unit coverage for clean, EICAR-style signature detection, malformed responses, unavailable daemon, and production configuration.

Verification: registry suite 440/440, Zig suite 731/731, TypeScript typecheck, server bundle, docs contracts, formatting, and diff checks all pass.

9 days ago

Reusable implementation update (local worktree, pending merge/deploy): the scanner now supports both buffered archives and bounded streaming scans over clamd INSTREAM. It verifies the exact observed byte count and SHA-256 while scanning, distinguishes clean/blocked/review/error outcomes, fails closed when required, exposes readiness/metrics, and persists an exact clean attestation. Storage implementations independently reject missing, non-clean, or digest-mismatched attestations in production. Tests cover clean, EICAR-style blocked, malformed daemon responses, unavailable scanner, digest/size mismatch, and production configuration. Keep open until merged and deployed with clamd health verified.

9 days ago

Implemented on main in a65b2bda8, 673517231, and 02b16e2ff. The fail-closed buffered/streaming scanner, exact attestation enforcement, health/metrics, native scan-before-promote flow, and all Registry API gates are landed.

Sign in to comment on this issue.