diff --git a/library/vulnerabilities/js-injection/checkContextForJsInjection.test.ts b/library/vulnerabilities/js-injection/checkContextForJsInjection.test.ts new file mode 100644 index 00000000..64f8af6c --- /dev/null +++ b/library/vulnerabilities/js-injection/checkContextForJsInjection.test.ts @@ -0,0 +1,35 @@ +import * as t from "tap"; +import { checkContextForJsInjection } from "./checkContextForJsInjection"; + +t.test("it returns correct path", async () => { + t.same( + checkContextForJsInjection({ + js: "const x = 1 + 1; fetch();", + operation: "eval", + context: { + cookies: {}, + headers: {}, + remoteAddress: "ip", + method: "POST", + url: "url", + query: {}, + body: { + calc: "1 + 1; fetch()", + }, + source: "express", + route: "/", + routeParams: {}, + }, + }), + { + operation: "eval", + kind: "js_injection", + source: "body", + pathToPayload: ".calc", + metadata: { + js: "const x = 1 + 1; fetch();", + }, + payload: "1 + 1; fetch()", + } + ); +}); diff --git a/library/vulnerabilities/js-injection/detectJsInjection.test.ts b/library/vulnerabilities/js-injection/detectJsInjection.test.ts new file mode 100644 index 00000000..4f707c05 --- /dev/null +++ b/library/vulnerabilities/js-injection/detectJsInjection.test.ts @@ -0,0 +1,92 @@ +import * as t from "tap"; +import { detectJsInjection } from "./detectJsInjection"; + +t.test("it detects JS injections", async (t) => { + t.same( + detectJsInjection( + '1 + 1; console.log("hello")', + '1 + 1; console.log("hello")' + ), + true + ); + t.same(detectJsInjection("const x = 1 + 1; fetch();", "+ 1; fetch()"), true); + t.same( + detectJsInjection("const test = 'Hello World!'; //';", "Hello World!'; //"), + true + ); + t.same( + detectJsInjection( + "if (username === 'admin' || 1 === 1) { return true; } //') {}", + "admin' || 1 === 1) { return true; } //" + ), + true + ); + t.same( + detectJsInjection( + "packet.readDateTimeString('abc'); process.exit(1); // ');", + "abc'); process.exit(1); //" + ), + true + ); + t.same( + detectJsInjection( + "const window={}; alert('!'); return window.__NUXT__", + "alert('!');" + ), + true + ); + t.same( + detectJsInjection( + "const obj = { test: 'value', isAdmin: true }; //'};", + "value', isAdmin: true }; //" + ), + true + ); +}); + +t.test("does not detect JS injections", async (t) => { + t.same(detectJsInjection("1 + 1", "1 + 1"), false); + t.same(detectJsInjection("1 + 1", "const x = 1 + 1; x"), false); + t.same(detectJsInjection("1 + 1", "1 + 1; console.log('hello')"), false); + t.same(detectJsInjection("1 + 1", "1"), false); + t.same(detectJsInjection("1 + 1", "abc"), false); + t.same(detectJsInjection("const x = 'test'", "test"), false); + t.same(detectJsInjection("const x = 'test'", ""), false); + t.same(detectJsInjection("const test = 'abcde_123';", "abcde_123"), false); + t.same(detectJsInjection("const test = [1, 2, 3];", "1, 2, 3"), false); + + t.same( + detectJsInjection("const test = 'Hello World!';", "Hello World!"), + false + ); + t.same( + detectJsInjection( + "if(username === 'admin' || 1 === 1) { return true; }", + "admin" + ), + false + ); + t.same( + detectJsInjection("const obj = { test: 'value', isAdmin: true };", "value"), + false + ); +}); + +t.test("test source type", async (t) => { + t.same( + detectJsInjection( + "const test: string = 'Hello World!'; console.log('test'); //';", + "Hello World!'; console.log('test'); //", + 0 + ), + false // Cannot be parsed as JS, it's TS + ); + t.same( + detectJsInjection( + "const test: string = 'Hello World!'; console.log('test'); //';", + "Hello World!'; console.log('test'); //", + 1 + ), + true + ); +}); diff --git a/library/vulnerabilities/js-injection/detectJsInjection.ts b/library/vulnerabilities/js-injection/detectJsInjection.ts index 2a49abdb..951170d2 100644 --- a/library/vulnerabilities/js-injection/detectJsInjection.ts +++ b/library/vulnerabilities/js-injection/detectJsInjection.ts @@ -2,14 +2,26 @@ import { shouldReturnEarly } from "./shouldReturnEarly"; // eslint-disable-next-line camelcase import { wasm_detect_js_injection } from "../../internals/zen_internals"; -export function detectJsInjection(query: string, userInput: string) { - const queryLowercase = query.toLowerCase(); +/** + * Detects if the user input is a JS injection + * The sourceType is used to determine the source of the user input + * https://github.com/AikidoSec/zen-internals/blob/4b7bf2c7796155731dc2736a04e3f4d99cdc712b/src/js_injection/helpers/select_sourcetype_based_on_enum.rs#L4 + */ +export function detectJsInjection( + code: string, + userInput: string, + sourceType = 0 +): boolean { + const codeLowercase = code.toLowerCase(); const userInputLowercase = userInput.toLowerCase(); - if (shouldReturnEarly(queryLowercase, userInputLowercase)) { + if (shouldReturnEarly(codeLowercase, userInputLowercase)) { return false; } - // The source type is currently hardcoded to 0 (CJS & ESM) - return wasm_detect_js_injection(queryLowercase, userInputLowercase, 0); + return wasm_detect_js_injection( + codeLowercase, + userInputLowercase, + sourceType + ); }