Generate a CycloneDX SBOM and deterministic, audit-ready risk report from your package-lock.json.
You run npm audit. It says “47 vulnerabilities.”
Cool.
Which ones actually matter?
The one in your production bundle?
The dev-only Jest dependency?
The transitive package you didn’t even know existed?
You don’t know.
So you either:
Ignore everything → ship anyway
Or block everything → break your team
Either way, you lose signal.
The real problem isn’t vulnerabilities — it’s decision-making
Most tools answer:
“What is wrong?”
They don’t answer:
“What should I do about it?”
“Can I prove that decision later?”
That last one is the real problem.
Enter: audit-ready
Instead of scores, it gives you decisions.
Deterministic. Reproducible. Auditable.
🔑 reasonCode replaces CVSS
Every dependency gets exactly one label:
DEV_DEPENDENCY_ONLY
OPTIONAL_DEPENDENCY
TRANSITIVE_NO_EXPLOIT
DIRECT_UNPATCHED
NO_KNOWN_VULNERABILITY
EXEMPTED
No interpretation required.
CI becomes trivial
npx audit-ready scan --fail-on DIRECT_UNPATCHED
Exit 0 → safe
Exit 1 → actionable issue
Not “7 high vulnerabilities.”
👉 A clear, enforceable rule
🧠 The constraint that shapes everything
Same package-lock.json → identical output. Always.
How that’s enforced
Core logic has hard constraints:
no Date
no Math.random()
no process.env
no I/O
And yes, it’s enforced by test:
const banned = ['Date', 'Date.now()', 'Math.random()', 'process.env'];
expect(found).toHaveLength(0);
If determinism breaks → build fails.
⚙️ The engine is intentionally simple
for (const rule of rules) {
if (rule.match(node)) {
return { ...node, reasonCode: rule.reasonCode }
}
}
No scoring. No heuristics.
👉 First match wins
Priority Rule
1 NO_KNOWN_VULNERABILITY
2 DEV_DEPENDENCY_ONLY
3 OPTIONAL_DEPENDENCY
4 TRANSITIVE_NO_EXPLOIT
5 DIRECT_UNPATCHED
Order = logic.
🧾 Output you can actually use
CycloneDX 1.5 SBOM
Human-readable report
SARIF (GitHub Security)
Everything tied to reasonCode.
🔐 Security: this tool audits itself
If you’re generating audit artifacts, your tool has to be trustworthy.
Here’s what that means in practice:
No environment access
The core engine literally cannot read:
environment variables
system time
runtime context
👉 Output depends only on input + tool version
Deterministic PURL generation
Standard encoders (encodeURIComponent, URL) can differ across Node versions.
So PURLs are built manually.
👉 Same package → same PURL → always
Schema validation (input + output)
.audit-policy.json → validated before scan
SBOM → validated before write
If validation fails:
👉 Nothing is written
Immutable output
All models are readonly
Everything is Object.freeze()d
👉 No silent mutation
👉 No post-processing surprises
Exceptions cannot live forever
Every exception requires:
a reason
an expiration date
Expired?
audit-ready audit-exceptions
👉 exit 1
No silent ignores.
Network safety by design
Only one external call:
👉 OSV API with PURLs
If it fails:
SBOM still generated
tool exits with code 2
no retries, no stale cache
Self-audit (this is rare)
audit-ready audit-self
The tool runs its own pipeline on itself.
Same code. Same rules.
👉 If it lies, it exposes itself.
⚠️ What this tool does NOT do
No AI explanations
No “smart” guessing
No monorepo support (yet)
No caching (Phase 3)
npm only
If a case isn’t covered:
👉 it fails loudly
Why this matters
This isn’t about better scanning.
It’s about:
reproducible decisions
CI you can trust
audit trails you can defend
🧪 Try it
npx audit-ready@beta scan --dry-run
🧭 Status
Phase 1: SBOM + triage ✅
Phase 2: policy + exceptions ✅
Phase 3: caching + performance 🚧
Production release planned after Phase 3.
💬 Looking for feedback
weird dependency graphs
incorrect classifications
CI edge cases
👉 https://github.com/neve7er/audit-ready
Final thought
Most tools try to be smart.
This one tries to be predictable.
Because in security:
predictability beats intelligence.
Top comments (0)