From ba373cfec625949f778e873c7c28cbe114ce3619 Mon Sep 17 00:00:00 2001 From: Hans Ott Date: Fri, 19 Dec 2025 11:10:43 +0100 Subject: [PATCH 01/10] Enable Function sink again, with support for eval By using a native addon that hooks into V8 --- library/agent/protect.ts | 3 +- library/node_internals/.gitignore | 4 ++ library/sinks/Function.ts | 108 +++++++++++++++++++++++------- scripts/build.js | 44 +++++++++++- 4 files changed, 134 insertions(+), 25 deletions(-) create mode 100644 library/node_internals/.gitignore diff --git a/library/agent/protect.ts b/library/agent/protect.ts index aa72c3ff2..9b30de501 100644 --- a/library/agent/protect.ts +++ b/library/agent/protect.ts @@ -56,6 +56,7 @@ import { AiSDK } from "../sinks/AiSDK"; import { Mistral } from "../sinks/Mistral"; import { Anthropic } from "../sinks/Anthropic"; import { GoogleGenAi } from "../sinks/GoogleGenAi"; +import { Function } from "../sinks/Function"; import type { FetchListsAPI } from "./api/FetchListsAPI"; import { FetchListsAPINodeHTTP } from "./api/FetchListsAPINodeHTTP"; import shouldEnableFirewall from "../helpers/shouldEnableFirewall"; @@ -168,7 +169,7 @@ export function getWrappers() { new ClickHouse(), new Prisma(), new AwsSDKVersion3(), - // new Function(), Disabled because functionName.constructor === Function is false after patching global + new Function(), new AwsSDKVersion2(), new AiSDK(), new GoogleGenAi(), diff --git a/library/node_internals/.gitignore b/library/node_internals/.gitignore new file mode 100644 index 000000000..7475ea809 --- /dev/null +++ b/library/node_internals/.gitignore @@ -0,0 +1,4 @@ +# Ignore everything in this directory# Ignore everything in this directory +* +# Except this file +!.gitignore diff --git a/library/sinks/Function.ts b/library/sinks/Function.ts index 8bdbefb50..2811255c2 100644 --- a/library/sinks/Function.ts +++ b/library/sinks/Function.ts @@ -1,42 +1,104 @@ +import { join } from "node:path"; +import { getInstance } from "../agent/AgentSingleton"; import { getContext } from "../agent/Context"; import { Hooks } from "../agent/hooks/Hooks"; +import { InterceptorResult } from "../agent/hooks/InterceptorResult"; +import { onInspectionInterceptorResult } from "../agent/hooks/onInspectionInterceptorResult"; import { Wrapper } from "../agent/Wrapper"; +import { getMajorNodeVersion } from "../helpers/getNodeVersion"; import { checkContextForJsInjection } from "../vulnerabilities/js-injection/checkContextForJsInjection"; +import { existsSync } from "node:fs"; export class Function implements Wrapper { - private inspectFunction(args: any[]) { + private inspectFunction(code: string): InterceptorResult { const context = getContext(); - if (!context || !Array.isArray(args) || args.length === 0) { + if (!context) { return undefined; } - const findLastStringArg = (args: any[]) => { - for (let i = args.length - 1; i >= 0; --i) { - if (typeof args[i] === "string") { - return args[i]; - } - } - return undefined; - }; + return checkContextForJsInjection({ + js: code, + operation: "new Function/eval", + context, + }); + } - const lastStringArg = findLastStringArg(args); + wrap(_: Hooks) { + const majorVersion = getMajorNodeVersion(); + const arch = process.arch; + const platform = process.platform; - if (lastStringArg) { - return checkContextForJsInjection({ - js: lastStringArg, - operation: "new Function", - context, - }); + const nodeInternalsDir = join(__dirname, "..", "node_internals"); + const binaryPath = join( + nodeInternalsDir, + `zen-internals-node-${platform}-${arch}-node${majorVersion}.node` + ); + if (!existsSync(binaryPath)) { + // oxlint-disable-next-line no-console + console.warn( + `Cannot find native addon for Node.js ${majorVersion} on ${platform}-${arch}. Function sink will not be instrumented.` + ); + return; } - return undefined; - } + const bindings: { + setCodeGenerationCallback: ( + callback: (code: string) => string | undefined + ) => void; + } = require(binaryPath); + if (!bindings || typeof bindings.setCodeGenerationCallback !== "function") { + // oxlint-disable-next-line no-console + console.warn( + `Native addon for Node.js ${majorVersion} on ${platform}-${arch} is invalid. Function sink will not be instrumented.` + ); + return; + } + + bindings.setCodeGenerationCallback((code: string) => { + const agent = getInstance(); + if (!agent) { + return; + } - wrap(hooks: Hooks) { - hooks.addGlobal("Function", { - kind: "eval_op", - inspectArgs: this.inspectFunction, + const context = getContext(); + if (context) { + const matches = agent.getConfig().getEndpoints(context); + + if (matches.find((match) => match.forceProtectionOff)) { + return; + } + } + + let inspectionResult: InterceptorResult | undefined; + const start = performance.now(); + try { + inspectionResult = this.inspectFunction(code); + } catch (error: any) { + agent.onErrorThrownByInterceptor({ + error: error, + method: "", + module: "Function/eval", + }); + } + + if (inspectionResult) { + try { + onInspectionInterceptorResult( + context, + agent, + inspectionResult, + { name: "Function/eval", type: "global" }, + start, + "", + "eval_op" + ); + } catch (error) { + // In blocking mode, onInspectionInterceptorResult would throw to block the operation + // To block the code generation, we need to return a string that will be used for the thrown error message + return (error as Error).message; + } + } }); } } diff --git a/scripts/build.js b/scripts/build.js index bd2bbd175..9e92b3bfa 100644 --- a/scripts/build.js +++ b/scripts/build.js @@ -1,4 +1,4 @@ -const { rm, copyFile, mkdir, readFile, writeFile } = require("fs/promises"); +const { rm, copyFile, cp, mkdir, readFile, writeFile } = require("fs/promises"); const { join } = require("path"); const { exec } = require("child_process"); const { fileExists, findFilesWithExtension } = require("./helpers/fs"); @@ -27,10 +27,18 @@ const INTERNALS_VERSION = "v0.1.56"; const INTERNALS_URL = `https://github.com/AikidoSec/zen-internals/releases/download/${INTERNALS_VERSION}`; // --- +// Node Internals configuration +const NODE_INTERNALS_VERSION = "1.0.0"; +const NODE_INTERNALS_URL = `https://github.com/AikidoSec/zen-internals-node/releases/download/${NODE_INTERNALS_VERSION}`; +// 17 is not included on purpose +const NODE_VERSIONS = [16, 18, 19, 20, 21, 22, 23, 24, 25]; +// --- + const rootDir = join(__dirname, ".."); const buildDir = join(rootDir, "build"); const libDir = join(rootDir, "library"); const internalsDir = join(libDir, "internals"); +const nodeInternalsDir = join(libDir, "node_internals"); const instrumentationWasmDir = join(rootDir, "instrumentation-wasm"); const instrumentationWasmOutDir = join( libDir, @@ -48,6 +56,7 @@ async function main() { await dlZenInternals(); await buildInstrumentationWasm(); + await dlNodeInternals(); if (process.argv.includes("--only-wasm")) { console.log("Built only WASM files as requested."); @@ -73,6 +82,9 @@ async function main() { join(internalsDir, "zen_internals_bg.wasm"), join(buildDir, "internals", "zen_internals_bg.wasm") ); + await cp(nodeInternalsDir, join(buildDir, "node_internals"), { + recursive: true, + }); await copyFile( join(instrumentationWasmOutDir, "node_code_instrumentation_bg.wasm"), join( @@ -91,6 +103,36 @@ async function main() { process.exit(0); } +async function dlNodeInternals() { + await mkdir(nodeInternalsDir, { recursive: true }); + + // TODO parallelize downloads + for (const nodeVersion of NODE_VERSIONS) { + for (const platform of ["linux", "darwin", "win32"]) { + let archs = ["x64", "arm64"]; + if (platform === "win32") { + // Only x64 builds are available for Windows + archs = ["x64"]; + } + if (nodeVersion === 16) { + // Only x64 builds are available for Node 16 + archs = ["x64"]; + } + for (const arch of archs) { + // zen-internals-node-linux-x64-node20.node + const filename = `zen-internals-node-${platform}-${arch}-node${nodeVersion}.node`; + const url = `${NODE_INTERNALS_URL}/${filename}`; + const destPath = join(nodeInternalsDir, filename); + + console.log( + `Downloading Node Internals for Node ${nodeVersion} ${platform} ${arch}...` + ); + await downloadFile(url, destPath); + } + } + } +} + // Download Zen Internals tarball and verify checksum async function dlZenInternals() { const tarballFile = "zen_internals.tgz"; From 36aed4ff7f8b3ed0921d2999bec18cf02679898d Mon Sep 17 00:00:00 2001 From: Hans Ott Date: Fri, 19 Dec 2025 11:13:04 +0100 Subject: [PATCH 02/10] Update error messages --- library/sinks/Function.test.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/library/sinks/Function.test.ts b/library/sinks/Function.test.ts index f330a8a29..3479f98e8 100644 --- a/library/sinks/Function.test.ts +++ b/library/sinks/Function.test.ts @@ -81,7 +81,7 @@ t.test("it detects JS injections using Function", async (t) => { if (error instanceof Error) { t.same( error.message, - "Zen has blocked a JavaScript injection: new Function(...) originating from body.calc" + "Zen has blocked a JavaScript injection: new Function/eval(...) originating from body.calc" ); } @@ -92,7 +92,7 @@ t.test("it detects JS injections using Function", async (t) => { if (error2 instanceof Error) { t.same( error2.message, - "Zen has blocked a JavaScript injection: new Function(...) originating from body.calc" + "Zen has blocked a JavaScript injection: new Function/eval(...) originating from body.calc" ); } @@ -107,7 +107,7 @@ t.test("it detects JS injections using Function", async (t) => { if (error3 instanceof Error) { t.same( error3.message, - "Zen has blocked a JavaScript injection: new Function(...) originating from body.calc" + "Zen has blocked a JavaScript injection: new Function/eval(...) originating from body.calc" ); } }); From 606a31cbdee6ff0fe7dacb00437c6f5fbb4ef14e Mon Sep 17 00:00:00 2001 From: Hans Ott Date: Fri, 19 Dec 2025 11:21:37 +0100 Subject: [PATCH 03/10] Parallel downloads + version check --- scripts/build.js | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/scripts/build.js b/scripts/build.js index 9e92b3bfa..38fdaa186 100644 --- a/scripts/build.js +++ b/scripts/build.js @@ -106,7 +106,17 @@ async function main() { async function dlNodeInternals() { await mkdir(nodeInternalsDir, { recursive: true }); - // TODO parallelize downloads + // Check if the wanted version of Node Internals is already installed + const versionCacheFile = join(nodeInternalsDir, ".installed_version"); + const installedVersion = (await fileExists(versionCacheFile)) + ? await readFile(versionCacheFile, "utf8") + : null; + if (installedVersion === NODE_INTERNALS_VERSION) { + console.log("Node Internals already installed. Skipping download."); + return; + } + + const downloads = []; for (const nodeVersion of NODE_VERSIONS) { for (const platform of ["linux", "darwin", "win32"]) { let archs = ["x64", "arm64"]; @@ -127,10 +137,14 @@ async function dlNodeInternals() { console.log( `Downloading Node Internals for Node ${nodeVersion} ${platform} ${arch}...` ); - await downloadFile(url, destPath); + downloads.push(downloadFile(url, destPath)); } } } + + await Promise.all(downloads); + + await writeFile(versionCacheFile, NODE_INTERNALS_VERSION); } // Download Zen Internals tarball and verify checksum From ba8b62fe4acbb12120c732e10560e9a20fcaa81d Mon Sep 17 00:00:00 2001 From: Hans Ott Date: Fri, 19 Dec 2025 11:23:38 +0100 Subject: [PATCH 04/10] Prefix warning messages with AIKIDO: --- library/sinks/Function.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/sinks/Function.ts b/library/sinks/Function.ts index 2811255c2..7b87ecf80 100644 --- a/library/sinks/Function.ts +++ b/library/sinks/Function.ts @@ -37,7 +37,7 @@ export class Function implements Wrapper { if (!existsSync(binaryPath)) { // oxlint-disable-next-line no-console console.warn( - `Cannot find native addon for Node.js ${majorVersion} on ${platform}-${arch}. Function sink will not be instrumented.` + `AIKIDO: Cannot find native addon for Node.js ${majorVersion} on ${platform}-${arch}. Function sink will not be instrumented.` ); return; } @@ -50,7 +50,7 @@ export class Function implements Wrapper { if (!bindings || typeof bindings.setCodeGenerationCallback !== "function") { // oxlint-disable-next-line no-console console.warn( - `Native addon for Node.js ${majorVersion} on ${platform}-${arch} is invalid. Function sink will not be instrumented.` + `AIKIDO: Native addon for Node.js ${majorVersion} on ${platform}-${arch} is invalid. Function sink will not be instrumented.` ); return; } From 5b646fccbdb84264c490b6b3369d5a0ea8e3a7bc Mon Sep 17 00:00:00 2001 From: Hans Ott Date: Fri, 19 Dec 2025 11:45:40 +0100 Subject: [PATCH 05/10] Add tests for eval (direct and indirect) --- library/sinks/Function.test.ts | 49 ++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/library/sinks/Function.test.ts b/library/sinks/Function.test.ts index 3479f98e8..450a7cb4a 100644 --- a/library/sinks/Function.test.ts +++ b/library/sinks/Function.test.ts @@ -1,4 +1,5 @@ /* oxlint-disable no-implied-eval */ +/* oxlint-disable no-eval */ import * as t from "tap"; import { runWithContext, type Context } from "../agent/Context"; import { createTestAgent } from "../helpers/createTestAgent"; @@ -111,4 +112,52 @@ t.test("it detects JS injections using Function", async (t) => { ); } }); + + t.same(eval("1 + 1"), 2); + t.same(eval("const x = 2 + 2; x"), 4); + t.same(eval("(() => 3 * 3)()"), 9); + + // Indirect eval + t.same((0, eval)("1 + 1"), 2); + t.same(eval.call(null, "2 + 2"), 4); + + runWithContext(safeContext, () => { + t.same(eval("1 + 1"), 2); + t.same(eval("const x = 1+ 1; x"), 2); + }); + + runWithContext(dangerousContext, () => { + t.same(eval("1 + 1"), 2); + t.same(eval("const x = 1 + 1; x"), 2); + + const error4 = t.throws(() => eval("1 + 1; console.log('hello')")); + t.ok(error4 instanceof Error); + if (error4 instanceof Error) { + t.same( + error4.message, + "Zen has blocked a JavaScript injection: new Function/eval(...) originating from body.calc" + ); + } + + const error5 = t.throws(() => + eval("const test = 1 + 1; console.log('hello')") + ); + t.ok(error5 instanceof Error); + if (error5 instanceof Error) { + t.same( + error5.message, + "Zen has blocked a JavaScript injection: new Function/eval(...) originating from body.calc" + ); + } + + // Indirect eval should also be blocked + const error6 = t.throws(() => (0, eval)("1 + 1; console.log('hello')")); + t.ok(error6 instanceof Error); + if (error6 instanceof Error) { + t.same( + error6.message, + "Zen has blocked a JavaScript injection: new Function/eval(...) originating from body.calc" + ); + } + }); }); From 6b2251c84dde9e4012a4f88a4a52a508e324f9d4 Mon Sep 17 00:00:00 2001 From: Hans Ott Date: Fri, 19 Dec 2025 11:57:19 +0100 Subject: [PATCH 06/10] Refactor - Extract loading native addon to a function - Use `inspectArgs` (which is also used for ESM instrumentation) - Use `getLibraryRoot()` for path to binaries (needed for bundling later) --- library/sinks/Function.ts | 79 ++++++++++++++++++++------------------- 1 file changed, 41 insertions(+), 38 deletions(-) diff --git a/library/sinks/Function.ts b/library/sinks/Function.ts index 7b87ecf80..bb2124861 100644 --- a/library/sinks/Function.ts +++ b/library/sinks/Function.ts @@ -3,16 +3,25 @@ import { getInstance } from "../agent/AgentSingleton"; import { getContext } from "../agent/Context"; import { Hooks } from "../agent/hooks/Hooks"; import { InterceptorResult } from "../agent/hooks/InterceptorResult"; -import { onInspectionInterceptorResult } from "../agent/hooks/onInspectionInterceptorResult"; +import { inspectArgs } from "../agent/hooks/wrapExport"; import { Wrapper } from "../agent/Wrapper"; +import { getLibraryRoot } from "../helpers/getLibraryRoot"; import { getMajorNodeVersion } from "../helpers/getNodeVersion"; import { checkContextForJsInjection } from "../vulnerabilities/js-injection/checkContextForJsInjection"; import { existsSync } from "node:fs"; export class Function implements Wrapper { - private inspectFunction(code: string): InterceptorResult { - const context = getContext(); + private inspectFunction(args: unknown[]): InterceptorResult { + if (args.length === 0) { + return undefined; + } + const code = args[0]; + if (!code || typeof code !== "string") { + return undefined; + } + + const context = getContext(); if (!context) { return undefined; } @@ -24,12 +33,12 @@ export class Function implements Wrapper { }); } - wrap(_: Hooks) { + private loadNativeAddon() { const majorVersion = getMajorNodeVersion(); const arch = process.arch; const platform = process.platform; - const nodeInternalsDir = join(__dirname, "..", "node_internals"); + const nodeInternalsDir = join(getLibraryRoot(), "node_internals"); const binaryPath = join( nodeInternalsDir, `zen-internals-node-${platform}-${arch}-node${majorVersion}.node` @@ -55,6 +64,15 @@ export class Function implements Wrapper { return; } + return bindings; + } + + wrap(_: Hooks) { + const bindings = this.loadNativeAddon(); + if (!bindings) { + return; + } + bindings.setCodeGenerationCallback((code: string) => { const agent = getInstance(); if (!agent) { @@ -62,42 +80,27 @@ export class Function implements Wrapper { } const context = getContext(); - if (context) { - const matches = agent.getConfig().getEndpoints(context); - - if (matches.find((match) => match.forceProtectionOff)) { - return; - } + if (!context) { + return; } - let inspectionResult: InterceptorResult | undefined; - const start = performance.now(); try { - inspectionResult = this.inspectFunction(code); - } catch (error: any) { - agent.onErrorThrownByInterceptor({ - error: error, - method: "", - module: "Function/eval", - }); - } - - if (inspectionResult) { - try { - onInspectionInterceptorResult( - context, - agent, - inspectionResult, - { name: "Function/eval", type: "global" }, - start, - "", - "eval_op" - ); - } catch (error) { - // In blocking mode, onInspectionInterceptorResult would throw to block the operation - // To block the code generation, we need to return a string that will be used for the thrown error message - return (error as Error).message; - } + inspectArgs( + [code], + this.inspectFunction, + context, + agent, + { + name: "Function/eval", + type: "global", + }, + "", + "eval_op" + ); + } catch (error) { + // In blocking mode, onInspectionInterceptorResult would throw to block the operation + // To block the code generation, we need to return a string that will be used for the thrown error message + return (error as Error).message; } }); } From bbcfffee44f4f7191b06386fa4778b8120c8447b Mon Sep 17 00:00:00 2001 From: Hans Ott Date: Fri, 19 Dec 2025 12:12:45 +0100 Subject: [PATCH 07/10] Upload native addon --- .github/workflows/build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ed5df0c0f..f2073f25f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -58,4 +58,5 @@ jobs: path: | build/ library/internals/ + library/node_internals/ library/agent/hooks/instrumentation/wasm/ From 9ffadf773e81d6bfefe4d3bc88d8b50aa16e1035 Mon Sep 17 00:00:00 2001 From: Hans Ott Date: Mon, 5 Jan 2026 17:51:13 +0100 Subject: [PATCH 08/10] Add opt-out flag for native addon --- docs/troubleshooting.md | 8 ++++++++ library/sinks/Function.ts | 5 +++++ 2 files changed, 13 insertions(+) diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md index f52e96c9d..4bf654fd5 100644 --- a/docs/troubleshooting.md +++ b/docs/troubleshooting.md @@ -39,6 +39,14 @@ In addition the output contains the name and version of each supported and instr If you are using a bundler like esbuild or a framework that uses bundling please ensure to follow the steps described in the [bundler](./bundler.md) docs. Applications using ESM at runtime are not supported yet. TypeScript code is often translated to CommonJS. +## Disable code generation hook + +Zen uses a native addon to protect against code injection via `eval()` and `new Function()`. In the rare case that you experience fatal V8 errors, you can disable it: + +```bash +AIKIDO_DISABLE_CODE_GENERATION_HOOK=true node server.js +``` + ## Contact support If you still can’t resolve the issue: diff --git a/library/sinks/Function.ts b/library/sinks/Function.ts index bb2124861..9e52e282e 100644 --- a/library/sinks/Function.ts +++ b/library/sinks/Function.ts @@ -5,6 +5,7 @@ import { Hooks } from "../agent/hooks/Hooks"; import { InterceptorResult } from "../agent/hooks/InterceptorResult"; import { inspectArgs } from "../agent/hooks/wrapExport"; import { Wrapper } from "../agent/Wrapper"; +import { envToBool } from "../helpers/envToBool"; import { getLibraryRoot } from "../helpers/getLibraryRoot"; import { getMajorNodeVersion } from "../helpers/getNodeVersion"; import { checkContextForJsInjection } from "../vulnerabilities/js-injection/checkContextForJsInjection"; @@ -68,6 +69,10 @@ export class Function implements Wrapper { } wrap(_: Hooks) { + if (envToBool(process.env.AIKIDO_DISABLE_CODE_GENERATION_HOOK)) { + return; + } + const bindings = this.loadNativeAddon(); if (!bindings) { return; From a3b0295c0ecab214d689f51eae9c853d55de1aed Mon Sep 17 00:00:00 2001 From: Hans Ott Date: Mon, 5 Jan 2026 17:52:54 +0100 Subject: [PATCH 09/10] Improve error message --- library/sinks/Function.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/sinks/Function.ts b/library/sinks/Function.ts index 9e52e282e..f35d69ca0 100644 --- a/library/sinks/Function.ts +++ b/library/sinks/Function.ts @@ -47,7 +47,7 @@ export class Function implements Wrapper { if (!existsSync(binaryPath)) { // oxlint-disable-next-line no-console console.warn( - `AIKIDO: Cannot find native addon for Node.js ${majorVersion} on ${platform}-${arch}. Function sink will not be instrumented.` + `AIKIDO: Cannot find native addon for Node.js ${majorVersion} on ${platform}-${arch}. Code injection attacks via eval() and new Function() will not be blocked. You can request support at https://github.com/AikidoSec/firewall-node/issues` ); return; } From cf1873737d0835ced607ee9f0d53ef57d1a023a3 Mon Sep 17 00:00:00 2001 From: Hans Ott Date: Mon, 5 Jan 2026 17:55:08 +0100 Subject: [PATCH 10/10] Rename Function to FunctionSink (to avoid name collision) --- library/agent/protect.ts | 4 ++-- library/sinks/{Function.test.ts => FunctionSink.test.ts} | 4 ++-- library/sinks/{Function.ts => FunctionSink.ts} | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) rename library/sinks/{Function.test.ts => FunctionSink.test.ts} (97%) rename library/sinks/{Function.ts => FunctionSink.ts} (98%) diff --git a/library/agent/protect.ts b/library/agent/protect.ts index 9b30de501..b6d57e7ba 100644 --- a/library/agent/protect.ts +++ b/library/agent/protect.ts @@ -56,7 +56,7 @@ import { AiSDK } from "../sinks/AiSDK"; import { Mistral } from "../sinks/Mistral"; import { Anthropic } from "../sinks/Anthropic"; import { GoogleGenAi } from "../sinks/GoogleGenAi"; -import { Function } from "../sinks/Function"; +import { FunctionSink } from "../sinks/FunctionSink"; import type { FetchListsAPI } from "./api/FetchListsAPI"; import { FetchListsAPINodeHTTP } from "./api/FetchListsAPINodeHTTP"; import shouldEnableFirewall from "../helpers/shouldEnableFirewall"; @@ -169,7 +169,7 @@ export function getWrappers() { new ClickHouse(), new Prisma(), new AwsSDKVersion3(), - new Function(), + new FunctionSink(), new AwsSDKVersion2(), new AiSDK(), new GoogleGenAi(), diff --git a/library/sinks/Function.test.ts b/library/sinks/FunctionSink.test.ts similarity index 97% rename from library/sinks/Function.test.ts rename to library/sinks/FunctionSink.test.ts index 450a7cb4a..2d257cf47 100644 --- a/library/sinks/Function.test.ts +++ b/library/sinks/FunctionSink.test.ts @@ -3,7 +3,7 @@ import * as t from "tap"; import { runWithContext, type Context } from "../agent/Context"; import { createTestAgent } from "../helpers/createTestAgent"; -import { Function as FunctionWrapper } from "./Function"; +import { FunctionSink } from "./FunctionSink"; const dangerousContext: Context = { remoteAddress: "::1", @@ -37,7 +37,7 @@ const safeContext: Context = { t.test("it detects JS injections using Function", async (t) => { const agent = createTestAgent(); - agent.start([new FunctionWrapper()]); + agent.start([new FunctionSink()]); t.same(new Function("return 1 + 1")(), 2); t.same(new Function("1 + 1")(), undefined); diff --git a/library/sinks/Function.ts b/library/sinks/FunctionSink.ts similarity index 98% rename from library/sinks/Function.ts rename to library/sinks/FunctionSink.ts index f35d69ca0..abc0e2d96 100644 --- a/library/sinks/Function.ts +++ b/library/sinks/FunctionSink.ts @@ -11,7 +11,7 @@ import { getMajorNodeVersion } from "../helpers/getNodeVersion"; import { checkContextForJsInjection } from "../vulnerabilities/js-injection/checkContextForJsInjection"; import { existsSync } from "node:fs"; -export class Function implements Wrapper { +export class FunctionSink implements Wrapper { private inspectFunction(args: unknown[]): InterceptorResult { if (args.length === 0) { return undefined;