node720-rasp · in-process runtime self-protection

Block the exploit. Not the user.

node720-rasp enforces at the dangerous call — inside your Node process, no sidecar, no proxy. The same detector core as the SAST engine, now deciding the moment a sink fires. Add it with one env var; every block ships with the real exploit that proves it.

27
gold-proven blocks
real exploit → blocked · in CI
0
benign twins blocked
no over-fire · every fixture
20
detector classes
one shared core
84%
of the addressable
CVE census · 599/717
01 — HOW TO RUN

One env var. Zero code changes.

node720-rasp loads before your app and wraps the dangerous sinks in-process. No diff to your source, no sidecar container, no network hop. Point Node at the bootstrap:

$ NODE_OPTIONS='--require /opt/node720/boot.cjs' node server.js # zero-code bootstrap # …or call it explicitly at the top of your entrypoint: > require('node720-rasp').protect() # SECURE preset
in-process
Sink-side enforcement
It decides at the dangerous callexec, query, deserialize — not by guessing at a proxy or WAF in front.
taint
Per-request containment
Untrusted input is tracked through the request via AsyncLocalStorage to the sink, so a value slot passes and a structural span is blocked.
version-gated
Virtual patching
Each rule is keyed to the installed package version — it only fires where the CVE actually lives, and goes quiet once you upgrade.
preset
SECURE by default · tunable
Block on a confirmed exploit, allow benign traffic. RaspBlockError on a hit; tune or disable per detector.
02 — WHAT WE TESTED & WHAT BLOCKS

Every block is a real exploit. 27/27 proven in CI.

A RASP claim is only worth the exploit behind it. We keep two numbers apart on purpose: what is exploit-proven (a real attack fired, a block asserted) versus what is mapped in the coverage census. They are not the same thing, and blending them would hide the only one that matters: did a real exploit actually get stopped?

A · PROVEN — 27 executed gold fixtures

Each detection ships with a gold fixture that runs in CI: fire the real attack → assert it blocks → assert the benign twin passes. No fixture, no credit. This is the honest, reproducible floor of what node720-rasp stops:

27
gold-proven blocks
real exploit → blocked · in CI
0
benign twins blocked
no over-fire · every fixture
20
detector classes
one shared core
7
new 2026 CVE blocks
landed this cycle

B · CENSUS — coverage of the CVE landscape

A wider manifest of the Node.js CVE landscape, scored by whether an in-process RASP can reach the class at all. This is a mapped/wrapped count — broader than the 27 exploit-proven fixtures, and we label it that way:

84%
of addressable blocked
599 / 717 classes
398
fundamental — out of reach
design / logic CVEs, not a sink
118
addressable gap
reachable, not yet wrapped
15
sink classes in the
shared detector catalog

// The two are kept separate on purpose. The census 599 is a mapped/wrapped count, not 599 executed exploits — only the 27 gold fixtures are fire-and-block proven in CI. We never credit a census block without either a gold fixture or a clean detector mapping, and 398 CVEs are fundamental (logic/design flaws with no dangerous call to intercept) — honestly out of reach for any in-process RASP. The headline is the 27; the 84% is the map. Census manifest: threat-intel/coverage-360.json; gold fixtures: test/cve-*.test.js.

03 — REAL BLOCKS

What the runtime actually stops.

Two headline blocks that a static scan can't make — a Cypher injection stopped at the graph driver, and a tar symlink-chain that only a per-archive state machine can catch — then the full ledger of detector → the real CVE it blocks in CI.

neo4j-driver  CVE-2026-41274 · detector cypher · CWE-943
Blocked · RaspBlockError
// a tainted label/relationship is fine as a VALUE param, // but here it lands in graph-PATTERN position — structure. app.post('/graph', (req, res) => { const label = req.body.label // untrusted source session.run(`MATCH (n:` + label + `) RETURN n`) // cypher sink }) // ▲ tainted span = query structure, not a value
What node720-rasp does at the call
source · taintedreq.body.label contained per-request
wrap · neo4j session.runthe dangerous primitive is intercepted in-process
tokenizer differentiala value slot would pass; this span adds a graph pattern
block · structural injectiontainted span is Cypher structureRaspBlockError
tar  CVE-2026-26960 · detector path · CWE-59 · stateful
Blocked · runtime-only
// a crafted archive plants a symlink, then a LATER entry // writes THROUGH it — escaping the extraction root. tar.x({ file: upload }) // entry 1: evil -> /etc (symlink) // entry 2: evil/passwd (writes through) // node720-rasp holds the per-archive link graph and // canonicalizes every entry → entry 2 escapes root → blocked.
Why a static scan can't see this

The escape isn't in any single entry — it's the relationship between two. A file scanner sees one entry at a time and finds nothing wrong.

node720-rasp keeps a per-archive state machine (the symlink graph) and re-canonicalizes each entry through the links committed so far. Entry 2 resolves outside the root → blocked. This is a runtime-only capability — the kind of block that justifies an in-process RASP.

The full ledger — detector → the real CVE it blocks, gold-proven in CI:

CWE-918
SSRF · outbound egress
An app forwarding/proxying a tainted target — the tunnel to 169.254.169.254 cloud-metadata is blocked at the egress call, host and route.
CVE-2026-42038
CWE-1321
proto · inherited-dispatch RCE
parse-server resolves a handler off a tainted key reaching an inherited ({})[k] dispatch — the prototype gadget is blocked before it dispatches.
CVE-2026-32886 / 30939
CWE-89
sql · raw-driver injection
A tainted structural span reaching a raw query() on mssql / Prisma $queryRaw / ClickHouse — value slot passes, keyword/operator is blocked.
CVE-2026-42233
CWE-78
command · client argv injection
k8s-client / dockerode building an exec argv from request data — the tainted argument vector is blocked at the spawn.
CVE-2025-66404
CWE-943
cypher · graph-pattern injection
neo4j session.run — a tainted span in pattern position (label/relationship), not a parameter, is blocked as query structure.
CVE-2026-41274
CWE-59
path · stateful archive containment
tar symlink-chain — a per-archive link graph canonicalizes each entry; a write that escapes the root through a planted link is blocked. Runtime-only.
CVE-2026-26960
CWE-22
outboundpath · encoded traversal
A ..%2f..%2f segment the WHATWG parser keeps opaque but an upstream decodes — node720 decodes & normalizes, blocks the route climbing above the committed prefix.
CVE-2026-44373
proof
Every row above is a gold fixture
Each ships a CI test that fires the real exploit, asserts the block, and asserts the benign twin stays clean — fire→block→benign-clean. No fixture, no credit.
27 / 27
PROTECT YOUR PROCESS

Stop the call before it fires.

node720-rasp is the same detector core as the SAST engine, now deciding in-process at the dangerous sink. One env var, no code change, version-gated virtual patching — and every block ships with the exploit that proves it.

$ NODE_OPTIONS='--require /opt/node720/boot.cjs' node server.js > require('node720-rasp').protect() # or call it explicitly · SECURE preset

→ Full install & CI setup    → The same core, as SAST    → Watch one detector think