Skip to content

Commit facb858

Browse files
authored
chore(tracer): enhance docs, types and syntax (#391)
* chore(tracer): enhance docs, types and syntax * chore(tracer): enhance docs, types and syntax
1 parent 7927535 commit facb858

File tree

6 files changed

+127
-105
lines changed

6 files changed

+127
-105
lines changed

.changeset/light-zoos-punch.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@nodesecure/tracer": minor
3+
---
4+
5+
Enhance docs, types and syntax

workspaces/tracer/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
"homepage": "https://github.com/NodeSecure/js-x-ray/tree/master/workspaces/tracer#readme",
2828
"dependencies": {
2929
"@nodesecure/estree-ast-utils": "^4.0.0",
30-
"meriyah": "^6.1.3"
30+
"meriyah": "^6.1.3",
31+
"ts-pattern": "^5.7.1"
3132
}
3233
}

workspaces/tracer/src/VariableTracer.ts

Lines changed: 104 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,19 @@ import {
88
getCallExpressionIdentifier,
99
getCallExpressionArguments,
1010
getVariableDeclarationIdentifiers,
11-
extractLogicalExpression
11+
extractLogicalExpression,
12+
isLiteral
1213
} from "@nodesecure/estree-ast-utils";
14+
import { match } from "ts-pattern";
1315

1416
// Import Internal Dependencies
1517
import {
1618
notNullOrUndefined,
1719
isEvilIdentifierPath,
1820
isNeutralCallable,
1921
getSubMemberExpressionSegments,
20-
makePrefixRemover
22+
makePrefixRemover,
23+
stripNodePrefix
2124
} from "./utils/index.js";
2225

2326
// CONSTANTS
@@ -36,7 +39,6 @@ const kRequirePatterns = new Set([
3639
"process.mainModule.require",
3740
"process.getBuiltinModule"
3841
]);
39-
const kNodeModulePrefix = "node:";
4042
const kUnsafeGlobalCallExpression = new Set(["eval", "Function"]);
4143

4244
export interface DataIdentifierOptions {
@@ -276,6 +278,29 @@ export class VariableTracer extends EventEmitter {
276278
}
277279
}
278280

281+
#reverseAtob(
282+
node: ESTree.CallExpression,
283+
id: ESTree.Identifier
284+
) {
285+
const callExprArguments = getCallExpressionArguments(
286+
node,
287+
{
288+
externalIdentifierLookup: (name) => this.literalIdentifiers.get(name) ?? null
289+
}
290+
);
291+
if (callExprArguments === null) {
292+
return;
293+
}
294+
295+
const callExprArgumentNode = callExprArguments.at(0);
296+
if (typeof callExprArgumentNode === "string") {
297+
this.literalIdentifiers.set(
298+
id.name,
299+
Buffer.from(callExprArgumentNode, "base64").toString()
300+
);
301+
}
302+
}
303+
279304
#walkImportDeclaration(
280305
node: ESTree.ImportDeclaration
281306
): void {
@@ -315,7 +340,7 @@ export class VariableTracer extends EventEmitter {
315340
id: ESTree.Identifier | ESTree.ObjectPattern
316341
): void {
317342
const moduleNameLiteral = node.arguments
318-
.find((argumentNode) => isLiteralNode(argumentNode)
343+
.find((argumentNode) => isLiteral(argumentNode)
319344
&& this.#traced.has(stripNodePrefix(argumentNode.value))) as ESTree.Literal | undefined;
320345
if (!moduleNameLiteral) {
321346
return;
@@ -335,44 +360,17 @@ export class VariableTracer extends EventEmitter {
335360
}
336361
}
337362

338-
#walkVariableDeclarationWithIdentifier(
339-
variableDeclaratorNode: ESTree.VariableDeclarator
340-
): void {
341-
const { init } = variableDeclaratorNode;
342-
if (init === null) {
343-
return void 0;
344-
}
345-
346-
switch (init.type) {
347-
/**
348-
* var root = freeGlobal || freeSelf || Function('return this')();
349-
*/
350-
case "LogicalExpression": {
351-
for (const { node } of extractLogicalExpression(init)) {
352-
this.#walkVariableDeclarationInitialization(
353-
variableDeclaratorNode,
354-
node
355-
);
356-
}
357-
358-
return void 0;
359-
}
360-
361-
default:
362-
return this.#walkVariableDeclarationInitialization(
363-
variableDeclaratorNode
364-
);
365-
}
366-
}
367-
368-
#walkVariableDeclarationInitialization(
363+
#walkVariableDeclaratorInitialization(
369364
variableDeclaratorNode: ESTree.VariableDeclarator,
370-
childNode = variableDeclaratorNode.init
365+
childNode: ESTree.Node | null = variableDeclaratorNode.init
371366
): void {
372367
if (childNode === null) {
373368
return;
374369
}
375-
const { id } = variableDeclaratorNode as any;
370+
const { id } = variableDeclaratorNode;
371+
if (id.type !== "Identifier") {
372+
return;
373+
}
376374

377375
switch (childNode.type) {
378376
// let foo = "10"; <-- "foo" is the key and "10" the value
@@ -381,33 +379,45 @@ export class VariableTracer extends EventEmitter {
381379
break;
382380
}
383381

382+
/**
383+
* import os from "node:os";
384+
*
385+
* const foo = {
386+
* host: os.hostname(), <-- Property
387+
* ...{ bar: "hello world"} <-- SpreadElement
388+
* };
389+
* ^ ObjectExpression
390+
*/
384391
case "ObjectExpression": {
385-
for (const prop of childNode.properties) {
386-
switch (prop.type) {
387-
case "Property": {
388-
this.#walkVariableDeclarationInitialization(variableDeclaratorNode,
389-
prop.value as ESTree.VariableDeclarator["init"]);
390-
break;
391-
}
392-
case "SpreadElement": {
393-
this.#walkVariableDeclarationInitialization(variableDeclaratorNode,
394-
prop.argument as ESTree.VariableDeclarator["init"]);
395-
break;
396-
}
397-
}
392+
for (const property of childNode.properties) {
393+
const node = match(property)
394+
.with({ type: "Property" }, (prop) => prop.value)
395+
.with({ type: "SpreadElement" }, (prop) => prop.argument)
396+
.otherwise(() => null);
397+
398+
node && this.#walkVariableDeclaratorInitialization(
399+
variableDeclaratorNode,
400+
node
401+
);
398402
}
399403
break;
400404
}
401405

402406
case "ArrayExpression": {
403407
for (const element of childNode.elements) {
404-
this.#walkVariableDeclarationInitialization(variableDeclaratorNode, element);
408+
this.#walkVariableDeclaratorInitialization(
409+
variableDeclaratorNode,
410+
element
411+
);
405412
}
406413
break;
407414
}
408415

409416
case "SpreadElement": {
410-
this.#walkVariableDeclarationInitialization(variableDeclaratorNode, childNode.argument);
417+
this.#walkVariableDeclaratorInitialization(
418+
variableDeclaratorNode,
419+
childNode.argument
420+
);
411421
break;
412422
}
413423

@@ -448,23 +458,7 @@ export class VariableTracer extends EventEmitter {
448458
this.#walkRequireCallExpression(childNode, id);
449459
}
450460
else if (tracedFullIdentifierName === "atob") {
451-
const callExprArguments = getCallExpressionArguments(
452-
childNode,
453-
{
454-
externalIdentifierLookup: (name) => this.literalIdentifiers.get(name) ?? null
455-
}
456-
);
457-
if (callExprArguments === null) {
458-
break;
459-
}
460-
461-
const callExprArgumentNode = callExprArguments.at(0);
462-
if (typeof callExprArgumentNode === "string") {
463-
this.literalIdentifiers.set(
464-
id.name,
465-
Buffer.from(callExprArgumentNode, "base64").toString()
466-
);
467-
}
461+
this.#reverseAtob(childNode, id);
468462
}
469463

470464
break;
@@ -523,7 +517,7 @@ export class VariableTracer extends EventEmitter {
523517
}
524518

525519
if (childNode.object.type === "CallExpression") {
526-
this.#walkVariableDeclarationInitialization(variableDeclaratorNode, childNode.object);
520+
this.#walkVariableDeclaratorInitialization(variableDeclaratorNode, childNode.object);
527521
}
528522
break;
529523
}
@@ -537,7 +531,7 @@ export class VariableTracer extends EventEmitter {
537531
if (init === null) {
538532
return;
539533
}
540-
const id = variableDeclaratorNode.id as any;
534+
const { id } = variableDeclaratorNode as any;
541535

542536
switch (init.type) {
543537
// const { process } = eval("this");
@@ -574,44 +568,53 @@ export class VariableTracer extends EventEmitter {
574568
}
575569
}
576570

577-
walk(node: ESTree.Node): void {
571+
#walkVariableDeclarator(
572+
node: ESTree.VariableDeclarator
573+
): void {
574+
// var foo; <-- no initialization here.
575+
if (!notNullOrUndefined(node.init)) {
576+
return;
577+
}
578+
579+
/**
580+
* const { foo } = {};
581+
* ^ ^ ObjectPattern (example)
582+
*/
583+
if (node.id.type !== "Identifier") {
584+
this.#walkVariableDeclarationWithAnythingElse(node);
585+
586+
return;
587+
}
588+
589+
// var root = freeGlobal || freeSelf || Function('return this')();
590+
if (node.init.type === "LogicalExpression") {
591+
for (const extractedNode of extractLogicalExpression(node.init)) {
592+
this.#walkVariableDeclaratorInitialization(
593+
node,
594+
extractedNode.node
595+
);
596+
}
597+
}
598+
// const foo = "bar";
599+
else {
600+
this.#walkVariableDeclaratorInitialization(node);
601+
}
602+
}
603+
604+
walk(
605+
node: ESTree.Node
606+
): void {
578607
switch (node.type) {
579608
case "ImportDeclaration": {
580609
this.#walkImportDeclaration(node);
581610
break;
582611
}
583612
case "VariableDeclaration": {
584-
for (const variableDeclaratorNode of node.declarations) {
585-
// var foo; <-- no initialization here.
586-
if (!notNullOrUndefined(variableDeclaratorNode.init)) {
587-
continue;
588-
}
589-
590-
if (variableDeclaratorNode.id.type === "Identifier") {
591-
this.#walkVariableDeclarationWithIdentifier(variableDeclaratorNode);
592-
}
593-
else {
594-
this.#walkVariableDeclarationWithAnythingElse(variableDeclaratorNode);
595-
}
596-
}
613+
node.declarations.forEach(
614+
(node) => this.#walkVariableDeclarator(node)
615+
);
597616
break;
598617
}
599618
}
600619
}
601620
}
602-
603-
function stripNodePrefix(value: any): any {
604-
if (typeof value !== "string") {
605-
return value;
606-
}
607-
608-
return value.startsWith(kNodeModulePrefix) ?
609-
value.slice(kNodeModulePrefix.length) :
610-
value;
611-
}
612-
613-
function isLiteralNode(
614-
node: ESTree.Node
615-
): node is ESTree.Literal {
616-
return node.type === "Literal";
617-
}

workspaces/tracer/src/utils/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ export * from "./isEvilIdentifierPath.js";
22
export * from "./getSubMemberExpressionSegments.js";
33
export * from "./notNullOrUndefined.js";
44
export * from "./makePrefixRemover.js";
5+
export * from "./stripNodePrefix.js";
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
export function notNullOrUndefined(
2-
value: any
3-
): value is NonNullable<any> {
1+
export function notNullOrUndefined<T>(
2+
value: T
3+
): value is NonNullable<T> {
44
return value !== null && value !== void 0;
55
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// CONSTANTS
2+
const kNodeModulePrefix = "node:";
3+
4+
export function stripNodePrefix(value: any): any {
5+
if (typeof value !== "string") {
6+
return value;
7+
}
8+
9+
return value.startsWith(kNodeModulePrefix) ?
10+
value.slice(kNodeModulePrefix.length) :
11+
value;
12+
}

0 commit comments

Comments
 (0)