diff --git a/README.md b/README.md index d4ede60f..64e7b116 100644 --- a/README.md +++ b/README.md @@ -73,6 +73,38 @@ The analysis will return: `http` (in try), `crypto`, `util` and `fs`. > ⚠️ There is also a lot of suspicious code example in the root cases directory. Feel free to try the tool on these files. +## Warnings + +This section describes how use `warnings` export. + +The structure of the `warnings` is as follows: +``` +/** + * @property {object} warnings - The default values for Constants. + * @property {string} warnings[name] - The default warning name (parsingError, unsafeImport etc...). + * @property {string} warnings[name].i18n - i18n token. + * @property {string} warnings[name].code - Used to perform unit tests. + */ + +export const warnings = Object.freeze({ + parsingError: { + i18n: "sast_warnings.ast_error" + code: "ast-error", + }, + ...otherWarnings + }); +``` + +We make a call to `i18n` through the package `NodeSecure/i18n` to get the translation. + +``` +import * as jsxray from "@nodesecure/js-x-ray"; +import * as i18n from "@nodesecure/i18n"; + +console.log(i18n.getToken(jsxray.warnings.parsingError.i18n)); + +``` + ## Warnings Legends (v2.0+) > Node-secure versions equal or lower than 0.7.0 are no longer compatible with the warnings table below. diff --git a/index.d.ts b/index.d.ts index fae2f080..b2a52178 100644 --- a/index.d.ts +++ b/index.d.ts @@ -55,15 +55,42 @@ declare namespace JSXRay { } interface WarningsNames { - parsingError: "parsing-error", - unsafeImport: "unsafe-import", - unsafeStmt: "unsafe-stmt", - unsafeRegex: "unsafe-regex", - unsafeAssign: "unsafe-assign", - encodedLiteral: "encoded-literal", - shortIdentifiers: "short-identifiers", - suspiciousLiteral: "suspicious-literal", - obfuscatedCode: "obfuscated-code" + parsingError: { + code: "ast-error", + i18n: "sast_warnings.ast_error" + }, + unsafeImport: { + code: "unsafe-import", + i18n: "sast_warnings.unsafe_import" + }, + unsafeRegex: { + code: "unsafe-regex", + i18n: "sast_warnings.unsafe_regex" + }, + unsafeStmt: { + code: "unsafe-stmt", + i18n: "sast_warnings.unsafe_stmt" + }, + unsafeAssign: { + code: "unsafe-assign", + i18n: "sast_warnings.unsafe_assign" + }, + encodedLiteral: { + code: "encoded-literal", + i18n: "sast_warnings.encoded_literal" + }, + shortIdentifiers: { + code: "short-identifiers", + i18n: "sast_warnings.short_identifiers" + }, + suspiciousLiteral: { + code: "suspicious-literal", + i18n: "sast_warnings.suspicious_literal" + }, + obfuscatedCode: { + code: "obfuscated-code", + i18n: "sast_warnings.obfuscated_code" + } } interface RuntimeOptions { @@ -90,9 +117,7 @@ declare namespace JSXRay { export function runASTAnalysisOnFile(pathToFile: string, options?: RuntimeFileOptions): Promise; - export namespace CONSTANTS { - export const Warnings: WarningsNames; - } + export const warnings: WarningsNames; } export = JSXRay; diff --git a/index.js b/index.js index e40e63fc..148d8516 100644 --- a/index.js +++ b/index.js @@ -83,16 +83,43 @@ export async function runASTAnalysisOnFile(pathToFile, options = {}) { } } -export const CONSTANTS = { - Warnings: Object.freeze({ - parsingError: "ast-error", - unsafeImport: "unsafe-import", - unsafeRegex: "unsafe-regex", - unsafeStmt: "unsafe-stmt", - unsafeAssign: "unsafe-assign", - encodedLiteral: "encoded-literal", - shortIdentifiers: "short-identifiers", - suspiciousLiteral: "suspicious-literal", - obfuscatedCode: "obfuscated-code" - }) -}; +export const warnings = Object.freeze({ + parsingError: { + code: "ast-error", + i18n: "sast_warnings.ast_error" + }, + unsafeImport: { + code: "unsafe-import", + i18n: "sast_warnings.unsafe_import" + }, + unsafeRegex: { + code: "unsafe-regex", + i18n: "sast_warnings.unsafe_regex" + }, + unsafeStmt: { + code: "unsafe-stmt", + i18n: "sast_warnings.unsafe_stmt" + }, + unsafeAssign: { + code: "unsafe-assign", + i18n: "sast_warnings.unsafe_assign" + }, + encodedLiteral: { + code: "encoded-literal", + i18n: "sast_warnings.encoded_literal" + }, + shortIdentifiers: { + code: "short-identifiers", + i18n: "sast_warnings.short_identifiers" + }, + suspiciousLiteral: { + code: "suspicious-literal", + i18n: "sast_warnings.suspicious_literal" + }, + obfuscatedCode: { + code: "obfuscated-code", + i18n: "sast_warnings.obfuscated_code" + } +}); + + diff --git a/test/obfuscated.spec.js b/test/obfuscated.spec.js index 13ba0c1d..56be3d74 100644 --- a/test/obfuscated.spec.js +++ b/test/obfuscated.spec.js @@ -7,11 +7,11 @@ import { join, dirname } from "path"; import test from "tape"; // Import Internal Dependencies -import { runASTAnalysis, CONSTANTS, runASTAnalysisOnFile } from "../index.js"; +import { runASTAnalysis, warnings, runASTAnalysisOnFile } from "../index.js"; import { getWarningKind } from "./utils/index.js"; // CONSTANTS -const { Warnings } = CONSTANTS; +const { obfuscatedCode, encodedLiteral } = warnings; const __dirname = dirname(fileURLToPath(import.meta.url)); const FIXTURE_PATH = join(__dirname, "fixtures/obfuscated"); @@ -20,7 +20,7 @@ test("should detect 'jsfuck' obfuscation", (tape) => { const { warnings } = runASTAnalysis(trycatch); tape.strictEqual(warnings.length, 1); - tape.deepEqual(getWarningKind(warnings), [Warnings.obfuscatedCode].sort()); + tape.deepEqual(getWarningKind(warnings), [obfuscatedCode.code].sort()); tape.strictEqual(warnings[0].value, "jsfuck"); tape.end(); }); @@ -30,7 +30,7 @@ test("should detect 'jsfuck' obfuscation", (tape) => { // const { warnings } = runASTAnalysis(trycatch); // tape.strictEqual(warnings.length, 1); -// tape.deepEqual(getWarningKind(warnings), [Warnings.obfuscatedCode].sort()); +// tape.deepEqual(getWarningKind(warnings), [obfuscatedCode.code].sort()); // tape.strictEqual(warnings[0].value, "morse"); // tape.end(); // }); @@ -40,7 +40,7 @@ test("should detect 'jjencode' obfuscation", (tape) => { const { warnings } = runASTAnalysis(trycatch); tape.strictEqual(warnings.length, 1); - tape.deepEqual(getWarningKind(warnings), [Warnings.obfuscatedCode].sort()); + tape.deepEqual(getWarningKind(warnings), [obfuscatedCode.code].sort()); tape.strictEqual(warnings[0].value, "jjencode"); tape.end(); }); @@ -51,7 +51,7 @@ test("should detect 'freejsobfuscator' obfuscation", (tape) => { tape.strictEqual(warnings.length, 3); tape.deepEqual(getWarningKind(warnings), [ - Warnings.encodedLiteral, Warnings.encodedLiteral, Warnings.obfuscatedCode + encodedLiteral.code, encodedLiteral.code, obfuscatedCode.code ].sort()); tape.strictEqual(warnings[2].value, "freejsobfuscator"); tape.end(); @@ -63,7 +63,7 @@ test("should detect 'obfuscator.io' obfuscation (with hexadecimal generator)", ( tape.strictEqual(warnings.length, 1); tape.deepEqual(getWarningKind(warnings), [ - Warnings.obfuscatedCode + obfuscatedCode.code ].sort()); tape.strictEqual(warnings[0].value, "obfuscator.io"); tape.end(); @@ -84,7 +84,7 @@ test("should detect 'trojan-source' when there is one unsafe unicode control cha `); tape.strictEqual(warnings.length, 1); - tape.deepEqual(getWarningKind(warnings), [Warnings.obfuscatedCode]); + tape.deepEqual(getWarningKind(warnings), [obfuscatedCode.code]); tape.deepEqual(warnings[0].value, "trojan-source"); tape.end(); }); @@ -93,7 +93,7 @@ test("should detect 'trojan-source' when there is atleast one unsafe unicode con const { warnings } = await runASTAnalysisOnFile(join(FIXTURE_PATH, "unsafe-unicode-chars.js")); tape.strictEqual(warnings.length, 1); - tape.deepEqual(getWarningKind(warnings), [Warnings.obfuscatedCode]); + tape.deepEqual(getWarningKind(warnings), [obfuscatedCode.code]); tape.deepEqual(warnings[0].value, "trojan-source"); tape.end(); }); diff --git a/test/searchRuntimeDependencies.spec.js b/test/searchRuntimeDependencies.spec.js index 00020f76..903b1277 100644 --- a/test/searchRuntimeDependencies.spec.js +++ b/test/searchRuntimeDependencies.spec.js @@ -7,11 +7,19 @@ import { join, dirname } from "path"; import test from "tape"; // Import Internal Dependencies -import { runASTAnalysis, CONSTANTS } from "../index.js"; +import { runASTAnalysis, warnings } from "../index.js"; import { getWarningKind } from "./utils/index.js"; // CONSTANTS -const { Warnings } = CONSTANTS; +const { + unsafeRegex, + unsafeStmt, + unsafeAssign, + unsafeImport, + shortIdentifiers, + encodedLiteral, + suspiciousLiteral +} = warnings; const __dirname = dirname(fileURLToPath(import.meta.url)); const FIXTURE_PATH = join(__dirname, "fixtures/searchRuntimeDependencies"); @@ -55,7 +63,7 @@ test("should return unsafe-import when a CallExpression is used in a require sta require(evil() + "s"); `); - tape.deepEqual(getWarningKind(warnings), [Warnings.unsafeImport, Warnings.unsafeImport].sort()); + tape.deepEqual(getWarningKind(warnings), [unsafeImport.code, unsafeImport.code].sort()); tape.strictEqual(isOneLineRequire, false); tape.deepEqual([...dependencies], []); tape.end(); @@ -67,7 +75,7 @@ test("should return the string value of the encoded hexadecimal literal", (tape) const foo = "68747470"; `); - tape.deepEqual(getWarningKind(warnings), [Warnings.unsafeImport, Warnings.encodedLiteral].sort()); + tape.deepEqual(getWarningKind(warnings), [unsafeImport.code, encodedLiteral.code].sort()); tape.deepEqual([...dependencies], ["http"]); tape.end(); }); @@ -77,7 +85,7 @@ test("should detect an unsafe import because of the usage of data:text/javascrip import 'data:text/javascript;base64,Y29uc29sZS5sb2coJ2hlbGxvIHdvcmxkJyk7Cg=='; `); - tape.deepEqual(getWarningKind(warnings), [Warnings.unsafeImport].sort()); + tape.deepEqual(getWarningKind(warnings), [unsafeImport.code].sort()); tape.deepEqual([...dependencies], ["data:text/javascript;base64,Y29uc29sZS5sb2coJ2hlbGxvIHdvcmxkJyk7Cg=="]); tape.end(); }); @@ -89,7 +97,7 @@ test("should be capable to reverse the CallExpression Buffer.from call with an A ); `); - tape.deepEqual(getWarningKind(warnings), [Warnings.unsafeImport].sort()); + tape.deepEqual(getWarningKind(warnings), [unsafeImport.code].sort()); tape.deepEqual([...dependencies], ["dl-tar"]); tape.end(); }); @@ -102,7 +110,7 @@ test("should reverse the encoded hexadecimal value even if we can't follow unhex const px = require.resolve(unhex("646c2d746172")); `); - tape.deepEqual(getWarningKind(warnings), [Warnings.unsafeImport].sort()); + tape.deepEqual(getWarningKind(warnings), [unsafeImport.code].sort()); tape.deepEqual([...dependencies], ["dl-tar"]); tape.end(); }); @@ -114,7 +122,7 @@ test("should be capable to reverse the CallExpression Buffer.from with an hexade ); `); - tape.deepEqual(getWarningKind(warnings), [Warnings.unsafeImport].sort()); + tape.deepEqual(getWarningKind(warnings), [unsafeImport.code].sort()); tape.deepEqual([...dependencies], ["dl-tar"]); tape.end(); }); @@ -125,7 +133,7 @@ test("should return an unsafe-assign warning when a protected global is assigned r("http"); `); - tape.deepEqual(getWarningKind(warnings), [Warnings.unsafeAssign].sort()); + tape.deepEqual(getWarningKind(warnings), [unsafeAssign.code].sort()); tape.deepEqual([...dependencies], ["http"]); tape.end(); }); @@ -137,7 +145,7 @@ test("should succesfully follow the require stmt when assigned multiple times an b("http"); `); - tape.deepEqual(getWarningKind(warnings), [Warnings.unsafeAssign, Warnings.unsafeAssign].sort()); + tape.deepEqual(getWarningKind(warnings), [unsafeAssign.code, unsafeAssign.code].sort()); tape.deepEqual([...dependencies], ["http"]); tape.end(); }); @@ -147,7 +155,7 @@ test("should return unsafe-import when trying to require an empty ArrayExpressio require(["", ""]); `); - tape.deepEqual(getWarningKind(warnings), [Warnings.unsafeImport].sort()); + tape.deepEqual(getWarningKind(warnings), [unsafeImport.code].sort()); tape.deepEqual([...dependencies], []); tape.end(); }); @@ -158,7 +166,7 @@ test("should detect unsafe eval statments", (tape) => { const g = eval("this"); `); - tape.deepEqual(getWarningKind(warnings), [Warnings.unsafeStmt, Warnings.unsafeStmt].sort()); + tape.deepEqual(getWarningKind(warnings), [unsafeStmt.code, unsafeStmt.code].sort()); tape.end(); }); @@ -168,7 +176,7 @@ test("should detect unsafe Function statments", (tape) => { const g = Function("return this")(); `); - tape.deepEqual(getWarningKind(warnings), [Warnings.unsafeStmt, Warnings.unsafeStmt].sort()); + tape.deepEqual(getWarningKind(warnings), [unsafeStmt.code, unsafeStmt.code].sort()); tape.end(); }); @@ -177,7 +185,7 @@ test("should detect unsafe-assign of eval", (tape) => { const e = eval; `); - tape.deepEqual(getWarningKind(warnings), [Warnings.unsafeAssign].sort()); + tape.deepEqual(getWarningKind(warnings), [unsafeAssign.code].sort()); tape.end(); }); @@ -191,7 +199,7 @@ test("should be capable of following global parts", (tape) => { `); tape.deepEqual(getWarningKind(warnings), [ - Warnings.unsafeAssign, Warnings.unsafeAssign, Warnings.unsafeAssign + unsafeAssign.code, unsafeAssign.code, unsafeAssign.code ].sort()); tape.deepEqual([...dependencies], ["http", "fs"]); tape.end(); @@ -218,7 +226,7 @@ test("should detect the suspicious string", (tape) => { const suspectString = readFileSync(join(FIXTURE_PATH, "suspect-string.js"), "utf-8"); const { warnings, stringScore } = runASTAnalysis(suspectString); - tape.deepEqual(getWarningKind(warnings), [Warnings.suspiciousLiteral].sort()); + tape.deepEqual(getWarningKind(warnings), [suspiciousLiteral.code].sort()); tape.strictEqual(stringScore, 8); tape.end(); }); @@ -228,11 +236,11 @@ test("should be capable to follow hexa computation members expr", (tape) => { const { warnings, dependencies } = runASTAnalysis(advancedComputation); tape.deepEqual(getWarningKind(warnings), [ - Warnings.encodedLiteral, - Warnings.unsafeAssign, - Warnings.unsafeAssign, - Warnings.unsafeImport, - Warnings.unsafeStmt + encodedLiteral.code, + unsafeAssign.code, + unsafeAssign.code, + unsafeImport.code, + unsafeStmt.code ].sort()); tape.deepEqual([...dependencies], ["./test/data"]); tape.end(); @@ -248,10 +256,9 @@ test("should support runtime analysis of ESM and return http", (tape) => { }); test("should detect two unsafe regex", (tape) => { - const unsafeRegex = readFileSync(join(FIXTURE_PATH, "unsafe-regex.js"), "utf-8"); - const { warnings } = runASTAnalysis(unsafeRegex, { module: false }); - - tape.deepEqual(getWarningKind(warnings), [Warnings.unsafeRegex, Warnings.unsafeRegex].sort()); + const regexUnsafe = readFileSync(join(FIXTURE_PATH, "unsafe-regex.js"), "utf-8"); + const { warnings } = runASTAnalysis(regexUnsafe, { module: false }); + tape.deepEqual(getWarningKind(warnings), [unsafeRegex.code, unsafeRegex.code].sort()); tape.end(); }); @@ -259,7 +266,7 @@ test("should detect short identifiers!", (tape) => { const shortIds = readFileSync(join(FIXTURE_PATH, "short-ids.js"), "utf-8"); const { warnings } = runASTAnalysis(shortIds); - tape.deepEqual(getWarningKind(warnings), [Warnings.shortIdentifiers].sort()); + tape.deepEqual(getWarningKind(warnings), [shortIdentifiers.code].sort()); tape.end(); });