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.













