no-unsafe-buffer-alloc
Disallow `Buffer.allocUnsafe()` and `Buffer.allocUnsafeSlow()`, which return uninitialized memory.
Keywords: no-unsafe-buffer-alloc, Buffer.allocUnsafe, Buffer.allocUnsafeSlow, uninitialized memory, CWE-908, Buffer.alloc, Node.js, ESLint rule CWE: CWE-908: Use of Uninitialized Resource
Disallow Buffer.allocUnsafe() and Buffer.allocUnsafeSlow(). Both hand back a buffer over memory that was never zeroed — until every byte is overwritten, the buffer contains whatever the allocator last stored there.
This rule is part of eslint-plugin-node-security.
Quick Summary
| Aspect | Details |
|---|---|
| Severity | High (Security — memory disclosure) |
| Auto-Fix | 💡 Suggestion (Buffer.alloc) |
| Category | Node Security |
| CWE | CWE-908 |
| Recommended | warn — the rule is unconditional (see below) |
| Best For | Codebases that send, hash, or persist buffers built by hand |
This rule is unconditional — read this before enabling it
The rule performs no dataflow or taint analysis. It reports every Buffer.allocUnsafe() / Buffer.allocUnsafeSlow() call it sees. It does not attempt to prove whether the resulting buffer is fully overwritten before it is read, and it therefore reports correct uses too:
// Reported, even though this is safe — the copy overwrites every byte.
const buf = Buffer.allocUnsafe(src.length);
src.copy(buf);That is deliberate. Proving "every byte is written before any read" requires interprocedural dataflow, which this plugin does not do. Deciding whether a given allocUnsafe is safe is left to the reviewer — the rule's job is to make sure someone looks.
There is exactly one structural exemption, and it is a parent-node check rather than variable tracking: an allocation zeroed in the same expression.
Buffer.allocUnsafe(64).fill(0); // not reported — equivalent to Buffer.alloc(64)For the complementary read-side analysis — buffers read past their written region (CWE-126) — see no-buffer-overread.
Why allocUnsafe is dangerous
Buffer.alloc(size) zero-fills. Buffer.allocUnsafe(size) skips that step and returns a slice of a pre-allocated pool, which is why it is faster. The bytes it returns are whatever the previous owner of that memory left behind: decrypted request bodies, session tokens, private keys.
Any byte not overwritten before the buffer is read, hashed, written to disk, or sent over the network discloses that memory to whoever receives it. This is the same class of defect as CVE-2018-7166, where Node's own randomFillSync fallback returned unzeroed memory.
Buffer.allocUnsafeSlow(size) has the identical hazard — it only differs in allocating outside the shared pool.
| Allocator | Zero-filled | Reported by this rule |
|---|---|---|
Buffer.alloc(size) | ✅ yes | no |
Buffer.allocUnsafe(size) | ❌ no | yes |
Buffer.allocUnsafeSlow(size) | ❌ no | yes |
Buffer.allocUnsafe(size).fill(0) | ✅ yes | no (structural exemption) |
Examples
❌ Incorrect
const buf = Buffer.allocUnsafe(1024);
res.end(buf); // leaks prior heap contents
const header = Buffer.allocUnsafe(16);
header.writeUInt32BE(len, 0); // 12 bytes still uninitialized
socket.write(header);
const slow = Buffer.allocUnsafeSlow(64);✅ Correct
const buf = Buffer.alloc(1024); // zero-filled
const header = Buffer.alloc(16);
header.writeUInt32BE(len, 0); // remaining bytes are zeros
const scratch = Buffer.allocUnsafe(64).fill(0); // zeroed at the call siteOptions
None. The rule takes no configuration.
Error Message Format
🔒 NODE-SECURITY CWE-908 | Uninitialized Buffer Allocation | HIGH
Fix: Use `Buffer.alloc(size)` (zero-filled), or keep `allocUnsafe` only where
the very next statement overwrites the whole buffer.Concretely: turn the rule off (or scope it with an override) in code where allocUnsafe is a measured, reviewed performance decision and every allocation is provably overwritten — stream framing layers, codecs, parsers with their own buffer pools. Because the rule is unconditional, it will report all of them.
Known False Positives
- Any
allocUnsafewhose buffer is fully overwritten through a variable (const b = Buffer.allocUnsafe(n); src.copy(b);). Documented above; this is the rule's main noise source and the reason it ships aswarn.
Known False Negatives
- Computed access (
Buffer['allocUnsafe'](n)) and dynamic dispatch (global['Buffer'].allocUnsafe(n)) are not resolved. - Aliases (
const alloc = Buffer.allocUnsafe; alloc(n);) are not tracked. - Buffer shims and polyfills that ship their own
allocUnsafeare out of scope.
Landscape
| Package | Rule | Behavior |
|---|---|---|
eslint-plugin-security-node | detect-buffer-unsafe-allocation | Unconditional; ships recommended: false |
@microsoft/eslint-plugin-sdl | no-unsafe-alloc | Unconditional |
eslint-plugin-node-security | no-unsafe-buffer-alloc | Unconditional, plus allocUnsafeSlow, the .fill() exemption, and a Buffer.alloc suggestion |
Did this rule catch something? Star the repo to get new CWE coverage as we ship it — or follow the AI-code-security benchmarks behind these rules.