v0.7.0
New: cas cooldown
Fails when any installed (name, version) was published more recently than --min-age days (default 14). Defends against rapid-install supply-chain attacks where a malicious version is live for hours before any scanner sees it. Inspired by StepSecurity's npm-package-cooldown-check, kevinslin/safe-npm, and pnpm's minimumReleaseAge setting.
Three deployment scenarios supported:
| Setup | Recommended flags |
|---|---|
| Public npm only | default --registry registry.npmjs.org |
| CodeArtifact + npm | --ca-domain + --ca-repository (falls back to CA on 404) |
| CodeArtifact-only private | --ca-first + --ca-domain + --ca-repository |
Performance: parallel fetch via ThreadPoolExecutor (--max-workers 20 default). On a 2521-package lockfile: ~16s first run, ~0.5s cached. Down from 2–5 minutes serial.
Disk cache (--cache PATH): publish times are immutable so cached entries are always valid. Aggressively populated from every fetched metadata response. Corrupt cache silently falls back to fresh fetch.
Per-version endpoint fallthrough: when an endpoint returns metadata but the specific version isn't in time[], cas falls through to the next endpoint. Closes the false-positive class observed against real lockfiles where the public npm registry returns a placeholder for org scopes without serving the private versions.
Secure-by-default (breaking)
A (name, version) that no configured endpoint can resolve is now HIGH cooldown_private_unresolvable (was silent INFO in earlier prototypes). Catches typo'd deps, lockfile tampering, and configuration gaps. Opt-out: --allow-private <name>.
Matching change for cas audit --probe-private: packages OSV doesn't know AND public npm doesn't know are now HIGH unaudited_private (was INFO). Opt-out: --allow-private <name>.
Aliased package names
Lockfile entries like node_modules/string-width-cjs with name: "string-width" are now queried against the canonical name. Shared extract_package_name(key, entry) helper applies the same fix across cas cooldown, cas audit, and cas scripts.
195 tests pass (was 164). Lint + mypy strict clean.