Skip to content

Commit

Permalink
feat: trace process.getBuiltinModule (#294)
Browse files Browse the repository at this point in the history
  • Loading branch information
fraxken authored Aug 16, 2024
1 parent 6c6279c commit b69ced9
Show file tree
Hide file tree
Showing 5 changed files with 115 additions and 7 deletions.
10 changes: 8 additions & 2 deletions src/probes/isRequire/isRequire.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ function validateNodeRequire(node, { tracer }) {
return [false];
}

const data = tracer.getDataFromIdentifier(id);
const data = tracer.getDataFromIdentifier(id, {
removeGlobalIdentifier: true
});

return [
data !== null && data.name === "require",
Expand Down Expand Up @@ -135,8 +137,12 @@ function main(node, options) {

export default {
name: "isRequire",
validateNode: [validateNodeRequire, validateNodeEvalRequire],
validateNode: [
validateNodeRequire,
validateNodeEvalRequire
],
main,
teardown,
breakOnMatch: true,
breakGroup: "import"
};
21 changes: 21 additions & 0 deletions test/probes/isRequire.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,27 @@ test("it should execute probe using process.mainModule.require (detected by the
assert.ok(dependencies.has("http"));
});

test("it should execute probe using process.getBuiltinModule (detected by the VariableTracer)", () => {
const str = `
if (globalThis.process?.getBuiltinModule) {
const fs = globalThis.process.getBuiltinModule('fs');
const module = globalThis.process.getBuiltinModule('module');
const require = module.createRequire(import.meta.url);
const foo = require('foo');
}
`;
const ast = parseScript(str);
const sastAnalysis = getSastAnalysis(str, isRequire)
.execute(ast.body);

assert.strictEqual(sastAnalysis.warnings().length, 0);
const dependencies = sastAnalysis.dependencies();
assert.deepEqual(
[...dependencies.keys()],
["fs", "module", "foo"]
);
});

test("it should execute probe on a variable reassignments of require (detected by the VariableTracer)", () => {
const str = `
const r = require;
Expand Down
12 changes: 10 additions & 2 deletions workspaces/estree-ast-utils/src/utils/VariableTracer.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
// Import Node.js Dependencies
import EventEmitter from "node:events";

export interface DataIdentifierOptions {
/**
* @default false
*/
removeGlobalIdentifier?: boolean;
}

declare class VariableTracer extends EventEmitter {
static AssignmentEvent: Symbol;

Expand All @@ -14,11 +21,12 @@ declare class VariableTracer extends EventEmitter {
moduleName?: string;
name?: string;
}): VariableTracer;
getDataFromIdentifier(identifierOrMemberExpr: string): null | {
removeGlobalIdentifier(identifierOrMemberExpr: string): string;
getDataFromIdentifier(identifierOrMemberExpr: string, options: DataIdentifierOptions): null | {
name: string;
identifierOrMemberExpr: string;
assignmentMemory: string[];
}
};
walk(node: any): void;
}

Expand Down
40 changes: 37 additions & 3 deletions workspaces/estree-ast-utils/src/utils/VariableTracer.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,18 @@ import { extractLogicalExpression } from "../extractLogicalExpression.js";

// CONSTANTS
const kGlobalIdentifiersToTrace = new Set([
"global", "globalThis", "root", "GLOBAL", "window"
"globalThis",
"global",
"root",
"GLOBAL",
"window"
]);
const kRequirePatterns = new Set([
"require", "require.resolve", "require.main", "process.mainModule.require"
"require",
"require.resolve",
"require.main",
"process.mainModule.require",
"process.getBuiltinModule"
]);
const kUnsafeGlobalCallExpression = new Set(["eval", "Function"]);

Expand Down Expand Up @@ -96,7 +104,33 @@ export class VariableTracer extends EventEmitter {
/**
* @param {!string} identifierOrMemberExpr An identifier like "foo" or "foo.bar"
*/
getDataFromIdentifier(identifierOrMemberExpr) {
removeGlobalIdentifier(identifierOrMemberExpr) {
if (!identifierOrMemberExpr.includes(".")) {
return identifierOrMemberExpr;
}

const globalIdentifier = [...kGlobalIdentifiersToTrace]
.find((globalId) => identifierOrMemberExpr.startsWith(globalId));

return globalIdentifier ?
identifierOrMemberExpr.slice(globalIdentifier.length + 1) :
identifierOrMemberExpr;
}

/**
* @param {!string} identifierOrMemberExpr An identifier like "foo" or "foo.bar"
* @param {object} [options={}]
*/
getDataFromIdentifier(
identifierOrMemberExpr,
options = {}
) {
const { removeGlobalIdentifier = false } = options;
if (removeGlobalIdentifier) {
// eslint-disable-next-line no-param-reassign
identifierOrMemberExpr = this.removeGlobalIdentifier(identifierOrMemberExpr);
}

const isMemberExpr = identifierOrMemberExpr.includes(".");
const isTracingIdentifier = this.#traced.has(identifierOrMemberExpr);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,3 +144,42 @@ test("it should be able to Trace a global assignment using a LogicalExpression",
assert.strictEqual(eventOne.identifierOrMemberExpr, "require");
assert.strictEqual(eventOne.id, "foo");
});

test("it should be able to Trace assignment of process.getBuiltinModule", () => {
const helpers = createTracer(true);
const assignments = helpers.getAssignmentArray();

helpers.walkOnCode(`
if (globalThis.process?.getBuiltinModule) {
const foo = globalThis.process.getBuiltinModule;
const fs = foo('fs');
}
`);

const foo = helpers.tracer.getDataFromIdentifier("foo");
assert.deepEqual(foo, {
name: "require",
identifierOrMemberExpr: "process.getBuiltinModule",
assignmentMemory: ["foo"]
});
assert.strictEqual(assignments.length, 1);

const [eventOne] = assignments;
assert.strictEqual(eventOne.identifierOrMemberExpr, "process.getBuiltinModule");
assert.strictEqual(eventOne.id, "foo");

assert.strictEqual(
helpers.tracer.getDataFromIdentifier("globalThis.process.getBuiltinModule"),
null
);

const getBuiltinModule = helpers.tracer.getDataFromIdentifier(
"globalThis.process.getBuiltinModule",
{ removeGlobalIdentifier: true }
);
assert.deepEqual(getBuiltinModule, {
name: "require",
identifierOrMemberExpr: "process.getBuiltinModule",
assignmentMemory: ["foo"]
});
});

0 comments on commit b69ced9

Please sign in to comment.