CVE-2026-1245: binary-parser Code Injection - REFLEX Analysis
CVE Overview
- CVE ID: CVE-2026-1245
- Component/Software:
binary-parser(npm package) - Vulnerable Versions: All versions before 2.3.0
- Severity: 6.5 MEDIUM (CVSS 3.1, AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:N)
- CWE: CWE-94 (Code Injection)
- Impact: Arbitrary JavaScript execution when untrusted input is used as a parser field name or string encoding
- Date: Disclosed 20 January 2026 (CERT/CC VU#102648). The fixed release, 2.3.0, shipped on 26 November 2025, before disclosure.
- Credit: Reported by Maor Caplan; fixed by maintainer Keichi Takahashi
- Safe Version: Upgrade to binary-parser 2.3.0 or later
Legacy System Support
For organisations unable to upgrade immediately, use the temporary mitigations in the Fortify section while you plan your upgrade path.
A parser that writes its own code. binary-parser is fast because it generates a JavaScript function for each parser you define. If an attacker gets to name a field, they get to write part of that function.
An IoT platform lets customers describe their device's packet format in a JSON schema. The backend turns each schema into a binary-parser definition so it can decode telemetry. An attacker signs up for a trial account and submits a schema with a field name that closes the generated statement and appends its own JavaScript. The next time the platform compiles a parser for that schema, the injected statement runs inside the ingestion service. It has the same access as the service, including its database credentials.
REFLEX Analysis
Reconnaissance
From an attacker's perspective:
Most applications that use binary-parser are not exploitable. They define parsers in code, with fixed field names. The attacker's job is to find the minority that build parsers from external input.
Signals that point to those applications:
- User-defined formats: products that let users describe packet layouts, file formats or protocol frames, common in IoT, telemetry and reverse-engineering tools.
- Public source: repositories where
new Parser()calls take field names or encodings from variables, config files or request bodies. - Advisory watching: CERT/CC and GitHub advisories name the injection points, so the attacker knows exactly what to look for.
- Error messages: parser compilation errors returned to the client hint that user input reaches the code generator.
Developer insight: A library is only as safe as the way you call it. Attackers look for the unusual call pattern, not just the vulnerable version.
Evaluate
Vulnerability assessment:
CVE-2026-1245 is exploitable when these conditions align:
- The application uses
binary-parserbefore 2.3.0. - Untrusted input reaches a field name (the first argument to
.uint8(),.string()and friends) or theencodingoption of.string().
Applications that only use static, hard-coded parser definitions are not affected.
Technical root cause: binary-parser builds the source of a parse function as a string and compiles it with the Function constructor. Before 2.3.0 it spliced field names and encodings into that source without validation. A field name becomes part of an assignment like vars.<name> = .... An encoding becomes part of new TextDecoder('<encoding>'). Either one can close the expression and append new statements.
// VULNERABLE: field name and encoding come from a user-supplied schema
const { Parser } = require("binary-parser");
const parser = new Parser().string(schema.fieldName, {
zeroTerminated: true,
encoding: schema.encoding,
});
Other options that accept strings, such as a length that names another field, also feed the code generator. The safe rule is simple: don't let untrusted users define parser schemas.
Assessment challenges:
npm lstells you whether you have the package. It doesn't tell you whether you call it unsafely. That needs a code search.- The package may arrive through another library. Check the dependency tree, then check how that library builds its parsers.
# Is it installed, and through which path?
npm ls binary-parser --all
npm explain binary-parser
Developer insight: Code generation turns data into code. Any library that does it needs the same care as eval.
Fortify
Prevention and hardening:
Upgrade first. Update to binary-parser 2.3.0 or later. The fix validates field names against a JavaScript identifier pattern and checks encodings against an allowlist. Anything else throws an error before code generation.
If you cannot upgrade immediately, apply the same checks yourself before building a parser:
// TEMPORARY MITIGATION: mirrors the upstream fix in 2.3.0
const FIELD_NAME = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;
const ENCODINGS = new Set([
"utf8", "utf-8", "ascii", "hex", "base64", "base64url", "latin1", "binary",
]);
function checkFieldName(name) {
if (!FIELD_NAME.test(name)) {
throw new Error(`Invalid field name: ${name}`);
}
return name;
}
function checkEncoding(encoding) {
if (!ENCODINGS.has(String(encoding).toLowerCase())) {
throw new Error(`Invalid encoding: ${encoding}`);
}
return encoding;
}
Rejecting bad input is safer than rewriting it. Silently replacing characters can turn two different names into the same one.
Development practices:
// BAD: user input reaches the code generator
const parser = new Parser().string(req.body.field, {
zeroTerminated: true,
encoding: req.body.encoding,
});
// BETTER: map user choices onto names and encodings you control
const FIELDS = { name: "name", id: "id", type: "type" };
const field = FIELDS[req.body.field];
if (!field) throw new Error("Unsupported field");
const parser = new Parser().string(field, { zeroTerminated: true, encoding: "utf8" });
// BEST: static parser definitions with no dynamic names
const parser = new Parser()
.string("name", { zeroTerminated: true })
.uint32("id")
.string("type", { zeroTerminated: true });
Long-term fortification:
- Commit your lockfile and install with
npm ci, so the patched version is what actually ships. - Scan in CI with
npm audit --audit-level=moderateor OSV-Scanner. - Flag code generation in review: add a lint rule or code search for libraries that compile code from strings.
Developer insight: When a library generates code, prefer static configuration over dynamic configuration every time.
Limit
Damage containment:
Injected code runs inside your Node.js process with all its privileges. A worker thread does not change that: it shares the process, its environment and its access to child_process. Real containment means a separate process with fewer rights.
- Separate the parser service: if you must compile user-defined parsers, do it in a dedicated service or container.
- Give that container nothing to steal: no database credentials, no cloud credentials, no secrets in environment variables.
- Block egress: the parser service rarely needs the internet. Deny outbound traffic by default.
- Harden the container: non-root user, read-only root filesystem, all capabilities dropped.
# Kubernetes container settings for a parser service
securityContext:
runAsNonRoot: true
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
capabilities:
drop: ["ALL"]
seccompProfile:
type: RuntimeDefault
Developer insight: Decide in advance what a compromised parser could reach, and make that list short.
Expose
Detection and visibility:
Exploitation requires an unusual field name or encoding. Those are easy to log and easy to spot.
- Log schema changes: when a user creates or edits a parser schema, log the field names and encodings with the user ID.
- Alert on rejected names: after upgrading,
Invalid field nameandInvalid encodingerrors mean someone tried. Count them. - Watch process behaviour: a Node.js ingestion service should not spawn shells. Falco or Tetragon can flag
nodestartingsh,bashorcurl.
# Find parser definitions, then check which take names or encodings from variables
grep -rnE "new Parser\(\)" --include="*.js" --include="*.ts" . \
| grep -v node_modules
# Count rejected names after upgrading
grep -cE "Invalid (field name|encoding)" /var/log/app/*.log
Developer insight: Watch the places where users can shape code, not just the code they run.
Exercise
Practice and preparedness:
Inventory drill: can you list every service that depends on binary-parser, with versions, in ten minutes?
# In each repository
npm ls binary-parser --all
# Across many repositories, using lockfiles
osv-scanner scan source -r /path/to/repos
Injection test in a test environment only: point a local, non-production instance at a vulnerable version and submit a schema whose field name runs a harmless statement. Then upgrade and confirm the same schema is rejected.
// test-injection.js: run in a disposable test environment only
const { Parser } = require("binary-parser");
const field = 'a; console.log("INJECTED CODE EXECUTED"); //';
const parser = new Parser().uint8(field);
parser.parse(Buffer.from([1]));
// < 2.3.0: prints "INJECTED CODE EXECUTED"
// >= 2.3.0: throws "Invalid field name"
Team preparedness checklist:
- Do you know which services build parsers from external input?
- Does CI fail on known vulnerable npm dependencies?
- Are user-supplied schemas validated before they reach any code generator?
- Do parser services run without secrets and without egress?
- Would you notice a Node.js service spawning a shell?
What-if scenarios:
- Schema upload abuse: a trial user uploads a malicious schema. Which service compiles it, and what can that service reach?
- Similar libraries: which other libraries in your tree generate code from strings, such as template engines or schema compilers? Are any of them fed by user input?
Developer insight: Find the code generators in your stack before an attacker does.
Key Takeaways for Developers
Most important lesson: Libraries that generate code turn data into code. Untrusted input must never reach them unvalidated.
CRITICAL: Upgrade to binary-parser 2.3.0 or later.
Immediate actions:
- Upgrade: move every project to
binary-parser2.3.0 or later. - Find usage: run
npm ls binary-parser --alland search your code for dynamic field names and encodings. - Validate input: reject field names that aren't identifiers and encodings that aren't on the allowlist.
- Stop user-defined schemas reaching the parser unless they pass validation first.
- Scan in CI with
npm auditor OSV-Scanner. - Isolate parser compilation in a service with no secrets and no egress.
Long-term practices:
- Know your code generators: treat any library that compiles strings like
eval. - Prefer static configuration for parsers, templates and schemas.
- Commit lockfiles and install with
npm ci. - Review dependency updates for security fixes, even ones released before a CVE is public.
Related patterns: NPM Supply Chain Attack covers how npm packages reach your build and how to control them. Node.js and npm Dependency Sprawl covers finding and managing the packages you depend on.