Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,6 @@ type WarningName = "parsing-error"
| "encoded-literal"
| "unsafe-regex"
| "unsafe-stmt"
| "unsafe-assign"
| "short-identifiers"
| "suspicious-literal"
| "obfuscated-code"
Expand Down Expand Up @@ -115,7 +114,6 @@ This section describe all the possible warnings returned by JSXRay. Click on the
| [unsafe-import](./docs/unsafe-import.md) | ❌ | Unable to follow an import (require, require.resolve) statement/expr. |
| [unsafe-regex](./docs/unsafe-regex.md) | ❌ | A RegEx as been detected as unsafe and may be used for a ReDoS Attack. |
| [unsafe-stmt](./docs//unsafe-stmt.md) | ❌ | Usage of dangerous statement like `eval()` or `Function("")`. |
| [unsafe-assign](./docs/unsafe-assign.md) | ❌ | Assignment of a protected global like `process` or `require`. |
| [encoded-literal](./docs/encoded-literal.md) | ❌ | An encoded literal has been detected (it can be an hexa value, unicode sequence or a base64 string) |
| [short-identifiers](./docs/short-identifiers.md) | ❌ | This mean that all identifiers has an average length below 1.5. |
| [suspicious-literal](./docs/suspicious-literal.md) | ❌ | A suspicious literal has been found in the source code. |
Expand Down
19 changes: 0 additions & 19 deletions docs/unsafe-assign.md

This file was deleted.

14 changes: 7 additions & 7 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
},
"homepage": "https://github.com/NodeSecure/js-x-ray#readme",
"dependencies": {
"@nodesecure/estree-ast-utils": "^1.1.0",
"@nodesecure/estree-ast-utils": "^1.3.0",
"@nodesecure/sec-literal": "^1.1.0",
"estree-walker": "^3.0.1",
"is-minified-code": "^2.0.0",
Expand Down
10 changes: 4 additions & 6 deletions src/Analysis.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { VariableTracer } from "@nodesecure/estree-ast-utils";
// Import Internal Dependencies
import { rootLocation, toArrayLocation } from "./utils.js";
import { generateWarning } from "./warnings.js";
import { processMainModuleRequire } from "./constants.js";
import ASTDeps from "./ASTDeps.js";
import { isObfuscatedCode, hasTrojanSource } from "./obfuscators/index.js";
import { runOnProbes } from "./probes/index.js";
Expand Down Expand Up @@ -36,13 +35,12 @@ export default class Analysis {
constructor() {
this.tracer = new VariableTracer()
.enableDefaultTracing()
.trace("crypto.createHash", { followConsecutiveAssignment: true });
this.dependencies = new ASTDeps();
.trace("crypto.createHash", {
followConsecutiveAssignment: true, moduleName: "crypto"
});

this.globalParts = new Map();
this.dependencies = new ASTDeps();
this.handledEncodedLiteralValues = new Map();

this.requireIdentifiers = new Set(["require", processMainModuleRequire]);
this.warnings = [];
this.literalScores = [];
}
Expand Down
35 changes: 0 additions & 35 deletions src/constants.js

This file was deleted.

21 changes: 19 additions & 2 deletions src/obfuscators/trojan-source.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,24 @@
import { unsafeUnicodeControlCharacters } from "../constants.js";
/**
* Dangerous Unicode control characters that can be used by hackers
* to perform trojan source.
*/
const kUnsafeUnicodeControlCharacters = [
"\u202A",
"\u202B",
"\u202D",
"\u202E",
"\u202C",
"\u2066",
"\u2067",
"\u2068",
"\u2069",
"\u200E",
"\u200F",
"\u061C"
];

export function verify(sourceString) {
for (const unsafeCharacter of unsafeUnicodeControlCharacters) {
for (const unsafeCharacter of kUnsafeUnicodeControlCharacters) {
if (sourceString.includes(unsafeCharacter)) {
return true;
}
Expand Down
2 changes: 0 additions & 2 deletions src/probes/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import isLiteral from "./isLiteral.js";
import isLiteralRegex from "./isLiteralRegex.js";
import isRegexObject from "./isRegexObject.js";
import isVariableDeclaration from "./isVariableDeclaration.js";
import isAssignmentExprOrMemberExpr from "./isAssignmentExprOrMemberExpr.js";
import isRequire from "./isRequire.js";
import isImportDeclaration from "./isImportDeclaration.js";
import isMemberExpression from "./isMemberExpression.js";
Expand All @@ -22,7 +21,6 @@ const kListOfProbes = [
isLiteralRegex,
isRegexObject,
isVariableDeclaration,
isAssignmentExprOrMemberExpr,
isRequire,
isImportDeclaration,
isMemberExpression,
Expand Down
29 changes: 0 additions & 29 deletions src/probes/isAssignmentExprOrMemberExpr.js

This file was deleted.

5 changes: 1 addition & 4 deletions src/probes/isLiteral.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,6 @@ import { builtinModules } from "repl";
// Import Third-party Dependencies
import { Hex } from "@nodesecure/sec-literal";

// Import Internal Dependencies
import { globalParts } from "../constants.js";

// CONSTANTS
const kNodeDeps = new Set(builtinModules);

Expand Down Expand Up @@ -36,7 +33,7 @@ function main(node, options) {
analysis.dependencies.add(value, node.loc);
analysis.addWarning("unsafe-import", null, node.loc);
}
else if (globalParts.has(value) || !Hex.isSafe(node.value)) {
else if (value === "require" || !Hex.isSafe(node.value)) {
analysis.addWarning("encoded-literal", node.value, node.loc);
}
}
Expand Down
4 changes: 1 addition & 3 deletions src/probes/isLiteralRegex.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
// Require Internal Dependencies
import { isLiteralRegex } from "../utils.js";

// Require Third-party Dependencies
import { isLiteralRegex } from "@nodesecure/estree-ast-utils";
import safeRegex from "safe-regex";

/**
Expand Down
4 changes: 1 addition & 3 deletions src/probes/isRegexObject.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
// Import Internal Dependencies
import { isLiteralRegex } from "../utils.js";

// Import Third-party Dependencies
import { isLiteralRegex } from "@nodesecure/estree-ast-utils";
import safeRegex from "safe-regex";

/**
Expand Down
49 changes: 12 additions & 37 deletions src/probes/isRequire.js
Original file line number Diff line number Diff line change
@@ -1,59 +1,34 @@
/* eslint-disable consistent-return */

// Import Internal Dependencies
import { isRequireGlobalMemberExpr } from "../utils.js";

// Import Third-party Dependencies
import { Hex } from "@nodesecure/sec-literal";
import { walk } from "estree-walker";
import {
concatBinaryExpression,
arrayExpressionToString,
getMemberExpressionIdentifier
getMemberExpressionIdentifier,
getCallExpressionIdentifier
} from "@nodesecure/estree-ast-utils";

function validateNode(node, analysis) {
return [
isRequireIdentifiers(node, analysis) ||
isRequireResolve(node) ||
isRequireMemberExpr(node)
];
}

function isRequireResolve(node) {
if (node.type !== "CallExpression" || node.callee.type !== "MemberExpression") {
return false;
}

return node.callee.object.name === "require" && node.callee.property.name === "resolve";
}

function isRequireMemberExpr(node) {
if (node.type !== "CallExpression" || node.callee.type !== "MemberExpression") {
return false;
function validateNode(node, { tracer }) {
const id = getCallExpressionIdentifier(node);
if (id === null) {
return [false];
}

return isRequireGlobalMemberExpr(
[...getMemberExpressionIdentifier(node.callee)].join(".")
);
}
const data = tracer.getDataFromIdentifier(id);

function isRequireIdentifiers(node, analysis) {
if (node.type !== "CallExpression") {
return false;
}
const fullName = node.callee.type === "MemberExpression" ?
[...getMemberExpressionIdentifier(node.callee)].join(".") :
node.callee.name;

return analysis.requireIdentifiers.has(fullName);
return [
data !== null && data.name === "require",
data?.identifierOrMemberExpr ?? void 0
];
}

function main(node, options) {
const { analysis } = options;
const { tracer } = analysis;

const arg = node.arguments[0];
const arg = node.arguments.at(0);
switch (arg.type) {
// const foo = "http"; require(foo);
case "Identifier":
Expand Down
Loading