@nodesecure/js-x-ray
Version:
JavaScript AST XRay analysis
91 lines • 3.51 kB
JavaScript
// Import Node.js Dependencies
import path from "node:path";
// Import Internal Dependencies
import { Hex } from "../../utils/index.js";
import { arrayExpressionToString, getCallExpressionArguments, getMemberExpressionIdentifier } from "../../estree/index.js";
import { isCallExpression, isLiteral } from "../../estree/types.js";
import { walkEnter } from "../../walker/index.js";
export class RequireCallExpressionWalker {
tracer;
dependencies = new Set();
triggerWarning = true;
constructor(tracer) {
this.tracer = tracer;
}
reset() {
this.dependencies.clear();
this.triggerWarning = true;
}
walk(callExprNode) {
this.reset();
// we need the `this` context of doWalk.enter
const self = this;
walkEnter(callExprNode, function enter(node) {
if (!isCallExpression(node) ||
node.arguments.length === 0) {
return;
}
const castedNode = node;
const rootArgument = castedNode.arguments.at(0);
if (rootArgument.type === "Literal" &&
typeof rootArgument.value === "string" &&
Hex.isHex(rootArgument.value)) {
self.dependencies.add(Buffer.from(rootArgument.value, "hex").toString());
this.skip();
return;
}
const fullName = castedNode.callee.type === "MemberExpression" ?
[...getMemberExpressionIdentifier(castedNode.callee)].join(".") :
castedNode.callee.name;
const tracedFullName = self.tracer.getDataFromIdentifier(fullName)?.identifierOrMemberExpr ?? fullName;
switch (tracedFullName) {
case "atob":
self.#handleAtob(castedNode);
break;
case "Buffer.from":
self.#handleBufferFrom(castedNode);
break;
case "require.resolve":
self.#handleRequireResolve(rootArgument);
break;
case "path.join":
case "path.resolve":
self.#handlePathJoin(castedNode);
break;
}
});
return {
dependencies: this.dependencies,
triggerWarning: this.triggerWarning
};
}
#handleAtob(node) {
const nodeArguments = getCallExpressionArguments(node, {
externalIdentifierLookup: (name) => this.tracer.literalIdentifiers.get(name)?.value ?? null
});
if (nodeArguments !== null && nodeArguments.length > 0) {
this.dependencies.add(Buffer.from(nodeArguments.at(0), "base64").toString());
}
}
#handleBufferFrom(node) {
const [element] = node.arguments;
if (element.type === "ArrayExpression") {
const depName = [...arrayExpressionToString(element)].join("").trim();
this.dependencies.add(depName);
}
}
#handleRequireResolve(node) {
if (isLiteral(node)) {
this.dependencies.add(node.value);
}
}
#handlePathJoin(node) {
if (!node.arguments.every((arg) => isLiteral(arg))) {
return;
}
const constructedPath = path.posix.join(...node.arguments.map((arg) => arg.value));
this.dependencies.add(constructedPath);
this.triggerWarning = false;
}
}
//# sourceMappingURL=RequireCallExpressionWalker.js.map