@nodesecure/js-x-ray
Version:
JavaScript AST XRay analysis
99 lines • 3.21 kB
JavaScript
// Import Internal Dependencies
import { getCallExpressionIdentifier } from "../estree/index.js";
import { VariableTracer } from "../VariableTracer.js";
import { CALL_EXPRESSION_DATA } from "../contants.js";
import { rootLocation, toArrayLocation } from "../utils/toArrayLocation.js";
import { generateWarning } from "../warnings.js";
// CONSTANTS
const kSensitiveModules = new Set(["os", "dns"]);
const kSensitiveMethods = [
"os.userInfo",
"os.networkInterfaces",
"os.cpus",
"dns.getServers"
];
function validateNode(node, ctx) {
if (ctx.sourceFile.sensitivity === "aggressive") {
return [false];
}
if (ctx.context?.[CALL_EXPRESSION_DATA]?.identifierOrMemberExpr !== "JSON.stringify") {
return [false];
}
const castedNode = node;
if (castedNode.arguments.length === 0) {
return [false];
}
return [true];
}
function main(node, ctx) {
const { sourceFile } = ctx;
const firstArg = node.arguments[0];
if (firstArg.type !== "CallExpression") {
return;
}
const id = getCallExpressionIdentifier(firstArg);
if (!id) {
return;
}
const data = sourceFile.tracer.getDataFromIdentifier(id);
if (kSensitiveMethods.some((method) => data?.identifierOrMemberExpr === method
&& sourceFile.tracer.importedModules.has(method.split(".")[0]))) {
const arrayLocation = ctx.context?.[data?.identifierOrMemberExpr];
if (arrayLocation) {
arrayLocation.push(toArrayLocation(firstArg.loc ?? rootLocation()));
}
else {
ctx.context[data?.identifierOrMemberExpr] = [toArrayLocation(firstArg.loc ?? rootLocation())];
}
}
}
function initialize(ctx) {
const { sourceFile, context } = ctx;
const { tracer } = sourceFile;
tracer
.trace("JSON.stringify", {
followConsecutiveAssignment: true
})
.trace("os.userInfo", {
moduleName: "os",
followConsecutiveAssignment: true
})
.trace("os.networkInterfaces", {
moduleName: "os",
followConsecutiveAssignment: true
})
.trace("os.cpus", {
moduleName: "os",
followConsecutiveAssignment: true
})
.trace("dns.getServers", {
moduleName: "dns",
followConsecutiveAssignment: true
});
if (sourceFile.sensitivity !== "aggressive") {
return;
}
tracer.on(VariableTracer.ImportEvent, ({ moduleName, location }) => {
if (kSensitiveModules.has(moduleName) && !(moduleName in context)) {
context[moduleName] = [toArrayLocation(location ?? undefined)];
}
});
}
function finalize(ctx) {
const { sourceFile, context } = ctx;
if (context && Object.keys(context).length > 0) {
const warning = generateWarning("data-exfiltration", { value: Object.keys(context).join(", ") });
sourceFile.warnings.push({ ...warning, location: Object.values(context).flat() });
}
}
const dateExifiltration = {
name: "dataExfiltration",
validateNode,
initialize,
finalize,
main,
breakOnMatch: false,
context: {}
};
export default dateExifiltration;
//# sourceMappingURL=data-exfiltration.js.map