CVE-2026-1245: binary-parser Code Injection - REFLEX Analysis

CVE Overview

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:

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:

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:

# 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:

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.

# 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.

# 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:

What-if scenarios:

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:

  1. Upgrade: move every project to binary-parser 2.3.0 or later.
  2. Find usage: run npm ls binary-parser --all and search your code for dynamic field names and encodings.
  3. Validate input: reject field names that aren't identifiers and encodings that aren't on the allowlist.
  4. Stop user-defined schemas reaching the parser unless they pass validation first.
  5. Scan in CI with npm audit or OSV-Scanner.
  6. Isolate parser compilation in a service with no secrets and no egress.

Long-term practices:

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.


References