@nodesecure/js-x-ray
Version:
JavaScript AST XRay analysis
97 lines • 2.94 kB
JavaScript
// Import Internal Dependencies
import { getMemberExpressionIdentifier } from "../estree/index.js";
import { CALL_EXPRESSION_DATA } from "../contants.js";
import { generateWarning } from "../warnings.js";
/**
* @description Detect serialization of process.env which could indicate environment variable exfiltration
* @example
* JSON.stringify(process.env)
* JSON.stringify(process["env"])
* JSON.stringify(process["env"])
* JSON.stringify(process[`env`])
*/
function validateJsonStringify(node, ctx) {
const { tracer } = ctx.sourceFile;
if (ctx.context[CALL_EXPRESSION_DATA]?.identifierOrMemberExpr !== "JSON.stringify") {
return [false];
}
const castedNode = node;
if (castedNode.arguments.length === 0) {
return [false];
}
const firstArg = castedNode.arguments[0];
if (firstArg.type === "MemberExpression") {
const memberExprId = [...getMemberExpressionIdentifier(firstArg)].join(".");
if (memberExprId === "process.env") {
return [true];
}
}
if (firstArg.type === "Identifier") {
const data = tracer.getDataFromIdentifier(firstArg.name);
if (data !== null) {
return [true];
}
}
return [false];
}
/**
* @description Detect direct process.env access (for aggressive mode)
* @example
* process.env
* const env = process.env
*/
function validateProcessEnv(node, ctx) {
if (node.type !== "MemberExpression") {
return [false];
}
const memberExprId = [...getMemberExpressionIdentifier(node)].join(".");
if (memberExprId === "process.env") {
ctx.setEntryPoint("process.env");
return [true];
}
return [false];
}
function defaultHandler(node, ctx) {
const { sourceFile, signals } = ctx;
const warning = generateWarning("serialize-environment", {
value: "JSON.stringify(process.env)",
location: node.loc
});
sourceFile.warnings.push(warning);
return signals.Skip;
}
function processEnvHandler(node, ctx) {
const { sourceFile, signals } = ctx;
// Only trigger warning in aggressive mode
if (sourceFile.sensitivity !== "aggressive") {
return null;
}
const warning = generateWarning("serialize-environment", {
value: "process.env",
location: node.loc
});
sourceFile.warnings.push(warning);
return signals.Skip;
}
function initialize(ctx) {
const { tracer } = ctx.sourceFile;
tracer
.trace("process.env", {
followConsecutiveAssignment: true
})
.trace("JSON.stringify", {
followConsecutiveAssignment: true
});
}
export default {
name: "isSerializeEnv",
validateNode: [validateJsonStringify, validateProcessEnv],
initialize,
main: {
default: defaultHandler,
"process.env": processEnvHandler
},
breakOnMatch: false,
context: {}
};
//# sourceMappingURL=isSerializeEnv.js.map