@nodesecure/js-x-ray 9.0.0 → 9.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +9 -209
  3. package/package.json +62 -77
  4. package/src/{AstAnalyser.js → AstAnalyser.ts} +283 -222
  5. package/src/{Deobfuscator.js → Deobfuscator.ts} +228 -195
  6. package/src/{EntryFilesAnalyser.js → EntryFilesAnalyser.ts} +206 -167
  7. package/src/JsSourceParser.ts +77 -0
  8. package/src/NodeCounter.ts +90 -0
  9. package/src/{ProbeRunner.js → ProbeRunner.ts} +167 -144
  10. package/src/SourceFile.ts +226 -0
  11. package/src/index.ts +5 -0
  12. package/src/obfuscators/freejsobfuscator.ts +17 -0
  13. package/src/obfuscators/{jjencode.js → jjencode.ts} +39 -27
  14. package/src/obfuscators/jsfuck.ts +19 -0
  15. package/src/obfuscators/{obfuscator-io.js → obfuscator-io.ts} +25 -13
  16. package/src/obfuscators/{trojan-source.js → trojan-source.ts} +3 -1
  17. package/src/probes/{isArrayExpression.js → isArrayExpression.ts} +12 -3
  18. package/src/probes/{isBinaryExpression.js → isBinaryExpression.ts} +74 -55
  19. package/src/probes/isESMExport.ts +50 -0
  20. package/src/probes/{isFetch.js → isFetch.ts} +28 -19
  21. package/src/probes/isImportDeclaration.ts +58 -0
  22. package/src/probes/{isLiteral.js → isLiteral.ts} +91 -70
  23. package/src/probes/isLiteralRegex.ts +42 -0
  24. package/src/probes/{isRegexObject.js → isRegexObject.ts} +71 -49
  25. package/src/probes/isRequire/RequireCallExpressionWalker.ts +142 -0
  26. package/src/probes/isRequire/{isRequire.js → isRequire.ts} +195 -148
  27. package/src/probes/isSerializeEnv.ts +65 -0
  28. package/src/probes/isSyncIO.ts +96 -0
  29. package/src/probes/isUnsafeCallee.ts +89 -0
  30. package/src/probes/isUnsafeCommand.ts +133 -0
  31. package/src/probes/isWeakCrypto.ts +69 -0
  32. package/src/types/estree.ts +35 -0
  33. package/src/utils/extractNode.ts +22 -0
  34. package/src/utils/index.ts +4 -0
  35. package/src/utils/{exportAssignmentHasRequireLeave.js → isOneLineExpressionExport.ts} +70 -40
  36. package/src/utils/notNullOrUndefined.ts +5 -0
  37. package/src/utils/toArrayLocation.ts +22 -0
  38. package/src/warnings.ts +146 -0
  39. package/index.d.ts +0 -46
  40. package/index.js +0 -4
  41. package/src/JsSourceParser.js +0 -57
  42. package/src/NodeCounter.js +0 -76
  43. package/src/SourceFile.js +0 -147
  44. package/src/obfuscators/freejsobfuscator.js +0 -9
  45. package/src/obfuscators/jsfuck.js +0 -11
  46. package/src/probes/isESMExport.js +0 -31
  47. package/src/probes/isImportDeclaration.js +0 -33
  48. package/src/probes/isLiteralRegex.js +0 -31
  49. package/src/probes/isRequire/RequireCallExpressionWalker.js +0 -93
  50. package/src/probes/isUnsafeCallee.js +0 -35
  51. package/src/probes/isWeakCrypto.js +0 -37
  52. package/src/utils/extractNode.js +0 -14
  53. package/src/utils/index.js +0 -8
  54. package/src/utils/isNode.js +0 -5
  55. package/src/utils/isOneLineExpressionExport.js +0 -24
  56. package/src/utils/isUnsafeCallee.js +0 -28
  57. package/src/utils/notNullOrUndefined.js +0 -3
  58. package/src/utils/rootLocation.js +0 -3
  59. package/src/utils/toArrayLocation.js +0 -11
  60. package/src/warnings.js +0 -77
  61. package/types/api.d.ts +0 -177
  62. package/types/warnings.d.ts +0 -36
@@ -1,49 +1,71 @@
1
- // Import Third-party Dependencies
2
- import { isLiteralRegex } from "@nodesecure/estree-ast-utils";
3
- import safeRegex from "safe-regex";
4
-
5
- /**
6
- * @description Search for Regex Object constructor.
7
- * @see https://github.com/estree/estree/blob/master/es5.md#newexpression
8
- * @example
9
- * new RegExp("...");
10
- */
11
- function validateNode(node) {
12
- return [
13
- isRegexConstructor(node) && node.arguments.length > 0
14
- ];
15
- }
16
-
17
- function main(node, options) {
18
- const { sourceFile } = options;
19
-
20
- const arg = node.arguments[0];
21
- /**
22
- * Note: RegExp Object can contain a RegExpLiteral
23
- * @see https://github.com/estree/estree/blob/master/es5.md#regexpliteral
24
- *
25
- * @example
26
- * new RegExp(/^foo/)
27
- */
28
- const pattern = isLiteralRegex(arg) ? arg.regex.pattern : arg.value;
29
-
30
- // We use the safe-regex package to detect whether or not regex is safe!
31
- if (!safeRegex(pattern)) {
32
- sourceFile.addWarning("unsafe-regex", pattern, node.loc);
33
- }
34
- }
35
-
36
- function isRegexConstructor(node) {
37
- if (node.type !== "NewExpression" || node.callee.type !== "Identifier") {
38
- return false;
39
- }
40
-
41
- return node.callee.name === "RegExp";
42
- }
43
-
44
- export default {
45
- name: "isRegexObject",
46
- validateNode,
47
- main,
48
- breakOnMatch: false
49
- };
1
+ // Import Third-party Dependencies
2
+ import safeRegex from "safe-regex";
3
+ import type { ESTree } from "meriyah";
4
+
5
+ // Import Internal Dependencies
6
+ import { SourceFile } from "../SourceFile.js";
7
+ import { generateWarning } from "../warnings.js";
8
+ import type { Literal, RegExpLiteral } from "../types/estree.js";
9
+
10
+ /**
11
+ * @description Search for Regex Object constructor.
12
+ * @see https://github.com/estree/estree/blob/master/es5.md#newexpression
13
+ * @example
14
+ * new RegExp("...");
15
+ */
16
+ function validateNode(
17
+ node: ESTree.Node
18
+ ): [boolean, any?] {
19
+ return [
20
+ isRegexConstructor(node) && node.arguments.length > 0
21
+ ];
22
+ }
23
+
24
+ function main(
25
+ node: ESTree.NewExpression & {
26
+ callee: ESTree.Identifier;
27
+ },
28
+ options: { sourceFile: SourceFile; }
29
+ ) {
30
+ const { sourceFile } = options;
31
+
32
+ const arg = node.arguments.at(0) as Literal<string> | RegExpLiteral<string>;
33
+ if (!arg) {
34
+ return;
35
+ }
36
+
37
+ /**
38
+ * Note: RegExp Object can contain a RegExpLiteral
39
+ * @see https://github.com/estree/estree/blob/master/es5.md#regexpliteral
40
+ *
41
+ * @example
42
+ * new RegExp(/^foo/)
43
+ */
44
+ const pattern = arg.type === "Literal" && "regex" in arg ?
45
+ arg.regex.pattern :
46
+ arg.value;
47
+
48
+ // We use the safe-regex package to detect whether or not regex is safe!
49
+ if (!safeRegex(pattern)) {
50
+ sourceFile.warnings.push(
51
+ generateWarning("unsafe-regex", { value: pattern, location: node.loc })
52
+ );
53
+ }
54
+ }
55
+
56
+ function isRegexConstructor(
57
+ node: ESTree.Node
58
+ ): node is ESTree.NewExpression {
59
+ if (node.type !== "NewExpression" || node.callee.type !== "Identifier") {
60
+ return false;
61
+ }
62
+
63
+ return node.callee.name === "RegExp";
64
+ }
65
+
66
+ export default {
67
+ name: "isRegexObject",
68
+ validateNode,
69
+ main,
70
+ breakOnMatch: false
71
+ };
@@ -0,0 +1,142 @@
1
+ // Import Node.js Dependencies
2
+ import path from "node:path";
3
+
4
+ // Import Third-party Dependencies
5
+ import { Hex } from "@nodesecure/sec-literal";
6
+ import { walk as doWalk } from "estree-walker";
7
+ import {
8
+ arrayExpressionToString,
9
+ getMemberExpressionIdentifier,
10
+ getCallExpressionArguments
11
+ } from "@nodesecure/estree-ast-utils";
12
+ import type { ESTree } from "meriyah";
13
+ import { VariableTracer } from "@nodesecure/tracer";
14
+
15
+ // Import Internal Dependencies
16
+ import {
17
+ isLiteral,
18
+ isCallExpression
19
+ } from "../../types/estree.js";
20
+
21
+ export class RequireCallExpressionWalker {
22
+ tracer: VariableTracer;
23
+ dependencies = new Set<string>();
24
+ triggerWarning = true;
25
+
26
+ constructor(
27
+ tracer: VariableTracer
28
+ ) {
29
+ this.tracer = tracer;
30
+ }
31
+
32
+ reset() {
33
+ this.dependencies.clear();
34
+ this.triggerWarning = true;
35
+ }
36
+
37
+ walk(
38
+ callExprNode: ESTree.CallExpression
39
+ ) {
40
+ this.reset();
41
+
42
+ // we need the `this` context of doWalk.enter
43
+ const self = this;
44
+ // @ts-expect-error
45
+ doWalk(callExprNode, {
46
+ enter(node: any) {
47
+ if (
48
+ !isCallExpression(node) ||
49
+ node.arguments.length === 0
50
+ ) {
51
+ return;
52
+ }
53
+
54
+ const castedNode = node as ESTree.CallExpression;
55
+ const rootArgument = castedNode.arguments.at(0)!;
56
+ if (
57
+ rootArgument.type === "Literal" &&
58
+ typeof rootArgument.value === "string" &&
59
+ Hex.isHex(rootArgument.value)
60
+ ) {
61
+ self.dependencies.add(Buffer.from(rootArgument.value, "hex").toString());
62
+ this.skip();
63
+
64
+ return;
65
+ }
66
+
67
+ const fullName = castedNode.callee.type === "MemberExpression" ?
68
+ [...getMemberExpressionIdentifier(castedNode.callee)].join(".") :
69
+ castedNode.callee.name;
70
+ const tracedFullName = self.tracer.getDataFromIdentifier(fullName)?.identifierOrMemberExpr ?? fullName;
71
+ switch (tracedFullName) {
72
+ case "atob":
73
+ self.#handleAtob(castedNode);
74
+ break;
75
+ case "Buffer.from":
76
+ self.#handleBufferFrom(castedNode);
77
+ break;
78
+ case "require.resolve":
79
+ self.#handleRequireResolve(rootArgument);
80
+ break;
81
+ case "path.join":
82
+ self.#handlePathJoin(castedNode);
83
+ break;
84
+ }
85
+ }
86
+ });
87
+
88
+ return {
89
+ dependencies: this.dependencies,
90
+ triggerWarning: this.triggerWarning
91
+ };
92
+ }
93
+
94
+ #handleAtob(
95
+ node: ESTree.CallExpression
96
+ ): void {
97
+ const nodeArguments = getCallExpressionArguments(
98
+ node,
99
+ {
100
+ externalIdentifierLookup: (name) => this.tracer.literalIdentifiers.get(name) ?? null
101
+ }
102
+ );
103
+
104
+ if (nodeArguments !== null && nodeArguments.length > 0) {
105
+ this.dependencies.add(
106
+ Buffer.from(nodeArguments.at(0)!, "base64").toString()
107
+ );
108
+ }
109
+ }
110
+
111
+ #handleBufferFrom(
112
+ node: ESTree.CallExpression
113
+ ) {
114
+ const [element] = node.arguments;
115
+ if (element.type === "ArrayExpression") {
116
+ const depName = [...arrayExpressionToString(element)].join("").trim();
117
+ this.dependencies.add(depName);
118
+ }
119
+ }
120
+
121
+ #handleRequireResolve(
122
+ node: ESTree.Node
123
+ ) {
124
+ if (isLiteral(node)) {
125
+ this.dependencies.add(node.value);
126
+ }
127
+ }
128
+
129
+ #handlePathJoin(
130
+ node: ESTree.CallExpression
131
+ ) {
132
+ if (!node.arguments.every((arg) => isLiteral(arg))) {
133
+ return;
134
+ }
135
+
136
+ const constructedPath = path.posix.join(
137
+ ...node.arguments.map((arg) => arg.value)
138
+ );
139
+ this.dependencies.add(constructedPath);
140
+ this.triggerWarning = false;
141
+ }
142
+ }
@@ -1,148 +1,195 @@
1
- /* eslint-disable consistent-return */
2
-
3
- import {
4
- concatBinaryExpression,
5
- arrayExpressionToString,
6
- getCallExpressionIdentifier,
7
- getCallExpressionArguments
8
- } from "@nodesecure/estree-ast-utils";
9
- import { ProbeSignals } from "../../ProbeRunner.js";
10
- import { RequireCallExpressionWalker } from "./RequireCallExpressionWalker.js";
11
-
12
- function validateNodeRequire(node, { tracer }) {
13
- const id = getCallExpressionIdentifier(node, {
14
- resolveCallExpression: false
15
- });
16
- if (id === null) {
17
- return [false];
18
- }
19
-
20
- const data = tracer.getDataFromIdentifier(id, {
21
- removeGlobalIdentifier: true
22
- });
23
-
24
- return [
25
- data !== null && data.name === "require",
26
- id ?? void 0
27
- ];
28
- }
29
-
30
- function validateNodeEvalRequire(node) {
31
- const id = getCallExpressionIdentifier(node);
32
-
33
- if (id !== "eval") {
34
- return [false];
35
- }
36
- if (node.callee.type !== "CallExpression") {
37
- return [false];
38
- }
39
-
40
- const args = getCallExpressionArguments(node.callee);
41
-
42
- return [
43
- args.length > 0 && args.at(0) === "require",
44
- id
45
- ];
46
- }
47
-
48
- function teardown({ sourceFile }) {
49
- sourceFile.dependencyAutoWarning = false;
50
- }
51
-
52
- function main(node, options) {
53
- const { sourceFile, data: calleeName } = options;
54
- const { tracer } = sourceFile;
55
-
56
- if (node.arguments.length === 0) {
57
- return;
58
- }
59
- const arg = node.arguments.at(0);
60
-
61
- if (calleeName === "eval") {
62
- sourceFile.dependencyAutoWarning = true;
63
- }
64
-
65
- switch (arg.type) {
66
- // const foo = "http"; require(foo);
67
- case "Identifier":
68
- if (sourceFile.tracer.literalIdentifiers.has(arg.name)) {
69
- sourceFile.addDependency(
70
- sourceFile.tracer.literalIdentifiers.get(arg.name),
71
- node.loc
72
- );
73
- }
74
- else {
75
- sourceFile.addWarning("unsafe-import", null, node.loc);
76
- }
77
- break;
78
-
79
- // require("http")
80
- case "Literal":
81
- sourceFile.addDependency(arg.value, node.loc);
82
- break;
83
-
84
- // require(["ht", "tp"])
85
- case "ArrayExpression": {
86
- const value = [...arrayExpressionToString(arg, { tracer })]
87
- .join("")
88
- .trim();
89
-
90
- if (value === "") {
91
- sourceFile.addWarning("unsafe-import", null, node.loc);
92
- }
93
- else {
94
- sourceFile.addDependency(value, node.loc);
95
- }
96
- break;
97
- }
98
-
99
- // require("ht" + "tp");
100
- case "BinaryExpression": {
101
- if (arg.operator !== "+") {
102
- sourceFile.addWarning("unsafe-import", null, node.loc);
103
- break;
104
- }
105
-
106
- try {
107
- const iter = concatBinaryExpression(arg, {
108
- tracer, stopOnUnsupportedNode: true
109
- });
110
-
111
- sourceFile.addDependency([...iter].join(""), node.loc);
112
- }
113
- catch {
114
- sourceFile.addWarning("unsafe-import", null, node.loc);
115
- }
116
- break;
117
- }
118
-
119
- // require(Buffer.from("...", "hex").toString());
120
- case "CallExpression": {
121
- const walker = new RequireCallExpressionWalker(tracer);
122
- const { dependencies, triggerWarning } = walker.walk(arg);
123
- dependencies.forEach((depName) => sourceFile.addDependency(depName, node.loc, true));
124
-
125
- if (triggerWarning) {
126
- sourceFile.addWarning("unsafe-import", null, node.loc);
127
- }
128
-
129
- // We skip walking the tree to avoid anymore warnings...
130
- return ProbeSignals.Skip;
131
- }
132
-
133
- default:
134
- sourceFile.addWarning("unsafe-import", null, node.loc);
135
- }
136
- }
137
-
138
- export default {
139
- name: "isRequire",
140
- validateNode: [
141
- validateNodeRequire,
142
- validateNodeEvalRequire
143
- ],
144
- main,
145
- teardown,
146
- breakOnMatch: true,
147
- breakGroup: "import"
148
- };
1
+ /* eslint-disable consistent-return */
2
+
3
+ // Import Third-party Dependencies
4
+ import {
5
+ concatBinaryExpression,
6
+ arrayExpressionToString,
7
+ getCallExpressionIdentifier,
8
+ getCallExpressionArguments
9
+ } from "@nodesecure/estree-ast-utils";
10
+ import type { ESTree } from "meriyah";
11
+
12
+ // Import Internal Dependencies
13
+ import { ProbeSignals } from "../../ProbeRunner.js";
14
+ import { SourceFile } from "../../SourceFile.js";
15
+ import { isLiteral } from "../../types/estree.js";
16
+ import { RequireCallExpressionWalker } from "./RequireCallExpressionWalker.js";
17
+ import { generateWarning } from "../../warnings.js";
18
+
19
+ function validateNodeRequire(
20
+ node: ESTree.Node,
21
+ { tracer }: SourceFile
22
+ ): [boolean, any?] {
23
+ const id = getCallExpressionIdentifier(node, {
24
+ resolveCallExpression: false
25
+ });
26
+ if (id === null) {
27
+ return [false];
28
+ }
29
+
30
+ const data = tracer.getDataFromIdentifier(id, {
31
+ removeGlobalIdentifier: true
32
+ });
33
+
34
+ return [
35
+ data !== null && data.name === "require",
36
+ id ?? void 0
37
+ ];
38
+ }
39
+
40
+ function validateNodeEvalRequire(
41
+ node: ESTree.Node
42
+ ): [boolean, any?] {
43
+ const id = getCallExpressionIdentifier(node);
44
+
45
+ if (id !== "eval") {
46
+ return [false];
47
+ }
48
+
49
+ const castedNode = node as ESTree.CallExpression;
50
+ if (castedNode.callee.type !== "CallExpression") {
51
+ return [false];
52
+ }
53
+
54
+ const args = getCallExpressionArguments(castedNode.callee);
55
+ if (args === null) {
56
+ return [false];
57
+ }
58
+
59
+ return [
60
+ args.length > 0 && args.at(0) === "require",
61
+ id
62
+ ];
63
+ }
64
+
65
+ function teardown(
66
+ { sourceFile }: { sourceFile: SourceFile; }
67
+ ) {
68
+ sourceFile.dependencyAutoWarning = false;
69
+ }
70
+
71
+ function main(
72
+ node: ESTree.CallExpression,
73
+ options: { sourceFile: SourceFile; data?: string; }
74
+ ) {
75
+ const { sourceFile, data: calleeName } = options;
76
+ const { tracer } = sourceFile;
77
+
78
+ if (node.arguments.length === 0) {
79
+ return;
80
+ }
81
+ const arg = node.arguments.at(0);
82
+ if (arg === undefined) {
83
+ return;
84
+ }
85
+
86
+ if (calleeName === "eval") {
87
+ sourceFile.dependencyAutoWarning = true;
88
+ }
89
+ const location = node.loc;
90
+
91
+ switch (arg.type) {
92
+ // const foo = "http"; require(foo);
93
+ case "Identifier":
94
+ if (sourceFile.tracer.literalIdentifiers.has(arg.name)) {
95
+ sourceFile.addDependency(
96
+ sourceFile.tracer.literalIdentifiers.get(arg.name)!,
97
+ node.loc
98
+ );
99
+ }
100
+ else {
101
+ sourceFile.warnings.push(
102
+ generateWarning("unsafe-import", { value: null, location })
103
+ );
104
+ }
105
+ break;
106
+
107
+ // require("http")
108
+ case "Literal":
109
+ if (isLiteral(arg)) {
110
+ sourceFile.addDependency(arg.value, node.loc);
111
+ }
112
+ break;
113
+
114
+ // require(["ht", "tp"])
115
+ case "ArrayExpression": {
116
+ const value = [
117
+ ...arrayExpressionToString(arg, {
118
+ externalIdentifierLookup: (name) => tracer.literalIdentifiers.get(name) ?? null
119
+ })
120
+ ]
121
+ .join("")
122
+ .trim();
123
+
124
+ if (value === "") {
125
+ sourceFile.warnings.push(
126
+ generateWarning("unsafe-import", { value: null, location })
127
+ );
128
+ }
129
+ else {
130
+ sourceFile.addDependency(value, node.loc);
131
+ }
132
+ break;
133
+ }
134
+
135
+ // require("ht" + "tp");
136
+ case "BinaryExpression": {
137
+ if (arg.operator !== "+") {
138
+ sourceFile.warnings.push(
139
+ generateWarning("unsafe-import", { value: null, location })
140
+ );
141
+ break;
142
+ }
143
+
144
+ try {
145
+ const iter = concatBinaryExpression(arg, {
146
+ externalIdentifierLookup: (name) => tracer.literalIdentifiers.get(name) ?? null,
147
+ stopOnUnsupportedNode: true
148
+ });
149
+
150
+ sourceFile.addDependency([...iter].join(""), node.loc);
151
+ }
152
+ catch {
153
+ sourceFile.warnings.push(
154
+ generateWarning("unsafe-import", { value: null, location })
155
+ );
156
+ }
157
+ break;
158
+ }
159
+
160
+ // require(Buffer.from("...", "hex").toString());
161
+ case "CallExpression": {
162
+ const walker = new RequireCallExpressionWalker(tracer);
163
+ const { dependencies, triggerWarning } = walker.walk(arg);
164
+ dependencies.forEach((depName) => sourceFile.addDependency(depName, node.loc, true));
165
+
166
+ if (triggerWarning) {
167
+ sourceFile.warnings.push(
168
+ generateWarning("unsafe-import", { value: null, location })
169
+ );
170
+ }
171
+
172
+ // We skip walking the tree to avoid anymore warnings...
173
+ return ProbeSignals.Skip;
174
+ }
175
+
176
+ default:
177
+ sourceFile.warnings.push(
178
+ generateWarning("unsafe-import", { value: null, location })
179
+ );
180
+ }
181
+
182
+ return;
183
+ }
184
+
185
+ export default {
186
+ name: "isRequire",
187
+ validateNode: [
188
+ validateNodeRequire,
189
+ validateNodeEvalRequire
190
+ ],
191
+ main,
192
+ teardown,
193
+ breakOnMatch: true,
194
+ breakGroup: "import"
195
+ };
@@ -0,0 +1,65 @@
1
+ // Import Third-party Dependencies
2
+ import {
3
+ getCallExpressionIdentifier,
4
+ getMemberExpressionIdentifier
5
+ } from "@nodesecure/estree-ast-utils";
6
+ import type { ESTree } from "meriyah";
7
+
8
+ // Import Internal Dependencies
9
+ import { SourceFile } from "../SourceFile.js";
10
+ import { generateWarning } from "../warnings.js";
11
+ import { ProbeSignals } from "../ProbeRunner.js";
12
+
13
+ /**
14
+ * @description Detect serialization of process.env which could indicate environment variable exfiltration
15
+ * @example
16
+ * JSON.stringify(process.env)
17
+ * JSON.stringify(process["env"])
18
+ * JSON.stringify(process["env"])
19
+ * JSON.stringify(process[`env`])
20
+ */
21
+ function validateNode(
22
+ node: ESTree.Node
23
+ ): [boolean, any?] {
24
+ const id = getCallExpressionIdentifier(node);
25
+ if (id !== "JSON.stringify") {
26
+ return [false];
27
+ }
28
+
29
+ const castedNode = node as ESTree.CallExpression;
30
+ if (castedNode.arguments.length === 0) {
31
+ return [false];
32
+ }
33
+
34
+ const firstArg = castedNode.arguments[0];
35
+ if (firstArg.type === "MemberExpression") {
36
+ const memberExprId = [...getMemberExpressionIdentifier(firstArg)].join(".");
37
+ if (memberExprId === "process.env") {
38
+ return [true];
39
+ }
40
+ }
41
+
42
+ return [false];
43
+ }
44
+
45
+ function main(
46
+ node: ESTree.Node,
47
+ options: { sourceFile: SourceFile; }
48
+ ) {
49
+ const { sourceFile } = options;
50
+
51
+ const warning = generateWarning("serialize-environment", {
52
+ value: "JSON.stringify(process.env)",
53
+ location: node.loc
54
+ });
55
+ sourceFile.warnings.push(warning);
56
+
57
+ return ProbeSignals.Skip;
58
+ }
59
+
60
+ export default {
61
+ name: "isSerializeEnv",
62
+ validateNode,
63
+ main,
64
+ breakOnMatch: false
65
+ };