diff --git a/package.json b/package.json index bda996e..91aef23 100644 --- a/package.json +++ b/package.json @@ -1,61 +1,61 @@ -{ - "name": "@nodesecure/js-x-ray", - "version": "6.1.1", - "description": "JavaScript AST XRay analysis", - "type": "module", - "exports": "./index.js", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "scripts": { - "lint": "eslint src test", - "prepublishOnly": "pkg-ok", - "test-only": "cross-env esm-tape-runner 'test/**/*.spec.js' | tap-monkey", - "test": "c8 --all --src ./src --reporter=lcov npm run test-only", - "check": "cross-env npm run lint && npm run test-only" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/NodeSecure/js-x-ray.git" - }, - "keywords": [ - "ast", - "nsecure", - "nodesecure", - "analysis", - "dependencies", - "security" - ], - "files": [ - "src", - "types", - "index.js", - "index.d.ts" - ], - "author": "GENTILHOMME Thomas ", - "license": "MIT", - "bugs": { - "url": "https://github.com/NodeSecure/js-x-ray/issues" - }, - "homepage": "https://github.com/NodeSecure/js-x-ray#readme", - "dependencies": { - "@nodesecure/estree-ast-utils": "^1.3.1", - "@nodesecure/sec-literal": "^1.2.0", - "estree-walker": "^3.0.1", - "is-minified-code": "^2.0.0", - "meriyah": "^4.3.3", - "safe-regex": "^2.1.1" - }, - "devDependencies": { - "@nodesecure/eslint-config": "^1.6.0", - "@slimio/is": "^2.0.0", - "@small-tech/esm-tape-runner": "^2.0.0", - "@small-tech/tap-monkey": "^1.4.0", - "@types/node": "^20.3.0", - "c8": "^8.0.0", - "cross-env": "^7.0.3", - "eslint": "^8.31.0", - "pkg-ok": "^3.0.0", - "tape": "^5.6.1" - } -} +{ + "name": "@nodesecure/js-x-ray", + "version": "6.1.1", + "description": "JavaScript AST XRay analysis", + "type": "module", + "exports": "./index.js", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "scripts": { + "lint": "eslint src test", + "prepublishOnly": "pkg-ok", + "test-only": "cross-env esm-tape-runner 'test/**/*.spec.js' | tap-monkey", + "test": "c8 --all --src ./src --reporter=lcov npm run test-only", + "check": "cross-env npm run lint && npm run test-only" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/NodeSecure/js-x-ray.git" + }, + "keywords": [ + "ast", + "nsecure", + "nodesecure", + "analysis", + "dependencies", + "security" + ], + "files": [ + "src", + "types", + "index.js", + "index.d.ts" + ], + "author": "GENTILHOMME Thomas ", + "license": "MIT", + "bugs": { + "url": "https://github.com/NodeSecure/js-x-ray/issues" + }, + "homepage": "https://github.com/NodeSecure/js-x-ray#readme", + "dependencies": { + "@nodesecure/estree-ast-utils": "^1.4.1", + "@nodesecure/sec-literal": "^1.2.0", + "estree-walker": "^3.0.1", + "is-minified-code": "^2.0.0", + "meriyah": "^4.3.3", + "safe-regex": "^2.1.1" + }, + "devDependencies": { + "@nodesecure/eslint-config": "^1.6.0", + "@slimio/is": "^2.0.0", + "@small-tech/esm-tape-runner": "^2.0.0", + "@small-tech/tap-monkey": "^1.4.0", + "@types/node": "^20.3.0", + "c8": "^8.0.0", + "cross-env": "^7.0.3", + "eslint": "^8.31.0", + "pkg-ok": "^3.0.0", + "tape": "^5.6.1" + } +} diff --git a/src/probes/isRequire.js b/src/probes/isRequire.js index 8076345..391dc4b 100644 --- a/src/probes/isRequire.js +++ b/src/probes/isRequire.js @@ -7,7 +7,8 @@ import { concatBinaryExpression, arrayExpressionToString, getMemberExpressionIdentifier, - getCallExpressionIdentifier + getCallExpressionIdentifier, + getCallExpressionArguments } from "@nodesecure/estree-ast-utils"; function validateNode(node, { tracer }) { @@ -89,7 +90,7 @@ function main(node, options) { // require(Buffer.from("...", "hex").toString()); case "CallExpression": { - walkRequireCallExpression(arg) + walkRequireCallExpression(arg, tracer) .forEach((depName) => analysis.dependencies.add(depName, node.loc, true)); analysis.addWarning("unsafe-import", null, node.loc); @@ -103,7 +104,7 @@ function main(node, options) { } } -function walkRequireCallExpression(nodeToWalk) { +function walkRequireCallExpression(nodeToWalk, tracer) { const dependencies = new Set(); walk(nodeToWalk, { @@ -122,8 +123,19 @@ function walkRequireCallExpression(nodeToWalk) { const fullName = node.callee.type === "MemberExpression" ? [...getMemberExpressionIdentifier(node.callee)].join(".") : node.callee.name; + const tracedFullName = tracer.getDataFromIdentifier(fullName)?.name ?? fullName; + + switch (tracedFullName) { + case "atob": { + const nodeArguments = getCallExpressionArguments(node, { tracer }); + if (nodeArguments !== null) { + dependencies.add( + Buffer.from(nodeArguments.at(0), "base64").toString() + ); + } - switch (fullName) { + break; + } case "Buffer.from": { const [element] = node.arguments; diff --git a/test/probes/isRequire.spec.js b/test/probes/isRequire.spec.js index 1e34e5c..68ccc75 100644 --- a/test/probes/isRequire.spec.js +++ b/test/probes/isRequire.spec.js @@ -395,3 +395,23 @@ test("(require CallExpression): it should detect MemberExpression require.resolv tape.end(); }); + +test("(require CallExpression): it should detect obfuscated atob value", (tape) => { + const str = ` + const myFunc = atob; + const ff = myFunc('b3' + 'M='); + const dep = require(ff); + `; + const ast = parseScript(str); + const sastAnalysis = getSastAnalysis(str, isRequire) + .execute(ast.body); + + tape.strictEqual(sastAnalysis.warnings().length, 0); + + const dependencies = sastAnalysis.dependencies(); + tape.strictEqual(Object.keys(dependencies).length, 1); + tape.ok("os" in dependencies); + + tape.end(); +}); +