zen-internals-node: The Firewall Hidden Inside V8
zen-internals-node turns eval() and new Function() into a policy check, so Node.js code can be stopped at the instant it tries to become executable.
- zen-internals-node moves security from the request edge to the exact moment V8 tries to turn text into code.
- The repo is tiny at the source level but operationally heavy, because prebuilt binaries and platform guards are part of the promise.
- Its most interesting trick is also its riskiest, because it keeps a stable Node-facing shell while reaching past N-API into V8 internals.
- Compared with WAFs or ordinary middleware, it wins by seeing intent before execution, not after traffic has already crossed the fence.
The novelty is not another denylist. It is the location of the veto. Most tools see requests, payloads, or stack traces. zen-internals-node sees the moment a string is about to become code, which makes eval() and new Function() enforceable in a way normal middleware never can.
A security hook at the exact line between text and execution
Aikido Security frames Zen as an embedded firewall inside the app, not a box at the edge. That matters because application context changes the security question. Instead of asking what a request looks like, the runtime can ask what the code is about to do, and who it is allowed to do it for.
Zen by Aikido is an embedded Web Application Firewall that autonomously protects Node.js apps against common and critical attacks.
How the addon blocks code generation
Inside src/binding.cc, a V8 callback intercepts code generation from strings. The native layer forwards the source text into a JavaScript policy function, then interprets the return value. If the policy returns a string, the addon treats that as a block signal, sets a custom error message, and tells V8 to abort execution.
v8::Local<v8::Value> v8_value;
std::memcpy(&v8_value, &argv[0], sizeof(v8_value));
That memcpy bridge is the story in miniature. N-API is the friendly face, but the enforcement path cuts past the abstraction boundary into V8's internals. It works because the addon needs control at a layer ordinary addons do not reach, and it is fragile for exactly the same reason.
That synchrony is the point. The policy cannot drift into a queue or a background task, because the JavaScript engine is waiting. The flip side is obvious: if the callback is slow or recursive, it can stall the isolate and take the event loop down with it.
Why the repo is mostly delivery plumbing
The source looks minimal, but index.js and binding.gyp do a lot of hidden work. The loader skips Node 17, picks a prebuilt binary by platform and architecture, and keeps production users away from local compiler setup. binding.gyp locks the build to C++20, which is another sign that the real product is not just the hook, it is the ability to ship that hook reliably across environments.
| Layer | What it can see | Strength | Trade-off |
|---|---|---|---|
| Edge WAF | Requests on the wire | Easy to deploy broadly | Sees patterns, not application intent |
| App middleware | Inputs after routing | Lives close to your code | Usually too late to stop code generation |
| zen-internals-node | Strings at the V8 compile boundary | Can veto eval and new Function before execution | Native, version-sensitive, and operationally heavier |
What this beats, and what it does not
Against an edge WAF, this is deeper and sharper. Against ordinary Express-style middleware, it is earlier and more decisive. But the cost is real: native maintenance, version skew, and a very small margin for implementation mistakes. The repo is a reminder that the most powerful security controls often live at the highest operational risk.
Runtime Application Self Protection (RASP) takes a different approach which doesn’t rely on written rules but actively monitors application behaviour through native integrations, leading to an increased protection for 0-Days.