@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,35 +0,0 @@
1
- // Import Internal Dependencies
2
- import { isUnsafeCallee } from "../utils/index.js";
3
- import { ProbeSignals } from "../ProbeRunner.js";
4
-
5
- /**
6
- * @description Detect unsafe statement
7
- * @example
8
- * eval("this");
9
- * Function("return this")();
10
- */
11
- function validateNode(node) {
12
- return isUnsafeCallee(node);
13
- }
14
-
15
- function main(node, options) {
16
- const { sourceFile, data: calleeName } = options;
17
-
18
- if (
19
- calleeName === "Function" &&
20
- node.callee.arguments.length > 0 &&
21
- node.callee.arguments[0].value === "return this"
22
- ) {
23
- return ProbeSignals.Skip;
24
- }
25
- sourceFile.addWarning("unsafe-stmt", calleeName, node.loc);
26
-
27
- return ProbeSignals.Skip;
28
- }
29
-
30
- export default {
31
- name: "isUnsafeCallee",
32
- validateNode,
33
- main,
34
- breakOnMatch: false
35
- };
@@ -1,37 +0,0 @@
1
- // Import Third-party Dependencies
2
- import { getCallExpressionIdentifier } from "@nodesecure/estree-ast-utils";
3
-
4
- // CONSTANTS
5
- const kWeakAlgorithms = new Set([
6
- "md5",
7
- "sha1",
8
- "ripemd160",
9
- "md4",
10
- "md2"
11
- ]);
12
-
13
- function validateNode(node, { tracer }) {
14
- const id = getCallExpressionIdentifier(node);
15
- if (id === null || !tracer.importedModules.has("crypto")) {
16
- return [false];
17
- }
18
-
19
- const data = tracer.getDataFromIdentifier(id);
20
-
21
- return [data !== null && data.identifierOrMemberExpr === "crypto.createHash"];
22
- }
23
-
24
- function main(node, { sourceFile }) {
25
- const arg = node.arguments.at(0);
26
-
27
- if (kWeakAlgorithms.has(arg.value)) {
28
- sourceFile.addWarning("weak-crypto", arg.value, node.loc);
29
- }
30
- }
31
-
32
- export default {
33
- name: "isWeakCrypto",
34
- validateNode,
35
- main,
36
- breakOnMatch: false
37
- };
@@ -1,14 +0,0 @@
1
- // Import Internal Dependencies
2
- import { notNullOrUndefined } from "./notNullOrUndefined.js";
3
-
4
- export function extractNode(expectedType) {
5
- return (callback, nodes) => {
6
- const finalNodes = Array.isArray(nodes) ? nodes : [nodes];
7
-
8
- for (const node of finalNodes) {
9
- if (notNullOrUndefined(node) && node.type === expectedType) {
10
- callback(node);
11
- }
12
- }
13
- };
14
- }
@@ -1,8 +0,0 @@
1
- export * from "./exportAssignmentHasRequireLeave.js";
2
- export * from "./extractNode.js";
3
- export * from "./isOneLineExpressionExport.js";
4
- export * from "./isUnsafeCallee.js";
5
- export * from "./notNullOrUndefined.js";
6
- export * from "./rootLocation.js";
7
- export * from "./toArrayLocation.js";
8
- export * from "./isNode.js";
@@ -1,5 +0,0 @@
1
- export function isNode(value) {
2
- return (
3
- value !== null && typeof value === "object" && "type" in value && typeof value.type === "string"
4
- );
5
- }
@@ -1,24 +0,0 @@
1
- // Import Internal Dependencies
2
- import { exportAssignmentHasRequireLeave } from "./exportAssignmentHasRequireLeave.js";
3
-
4
- export function isOneLineExpressionExport(body) {
5
- if (body.length === 0 || body.length > 1) {
6
- return false;
7
- }
8
-
9
- const [firstNode] = body;
10
- if (firstNode.type !== "ExpressionStatement") {
11
- return false;
12
- }
13
-
14
- switch (firstNode.expression.type) {
15
- // module.exports = require('...');
16
- case "AssignmentExpression":
17
- return exportAssignmentHasRequireLeave(firstNode.expression.right);
18
- // require('...');
19
- case "CallExpression":
20
- return exportAssignmentHasRequireLeave(firstNode.expression);
21
- default:
22
- return false;
23
- }
24
- }
@@ -1,28 +0,0 @@
1
- // Import Third-party Dependencies
2
- import { getCallExpressionIdentifier } from "@nodesecure/estree-ast-utils";
3
-
4
- function isEvalCallee(node) {
5
- const identifier = getCallExpressionIdentifier(node, {
6
- resolveCallExpression: false
7
- });
8
-
9
- return identifier === "eval";
10
- }
11
-
12
- function isFunctionCallee(node) {
13
- const identifier = getCallExpressionIdentifier(node);
14
-
15
- return identifier === "Function" && node.callee.type === "CallExpression";
16
- }
17
-
18
- export function isUnsafeCallee(node) {
19
- if (isEvalCallee(node)) {
20
- return [true, "eval"];
21
- }
22
-
23
- if (isFunctionCallee(node)) {
24
- return [true, "Function"];
25
- }
26
-
27
- return [false, null];
28
- }
@@ -1,3 +0,0 @@
1
- export function notNullOrUndefined(value) {
2
- return value !== null && value !== void 0;
3
- }
@@ -1,3 +0,0 @@
1
- export function rootLocation() {
2
- return { start: { line: 0, column: 0 }, end: { line: 0, column: 0 } };
3
- }
@@ -1,11 +0,0 @@
1
- // Import Internal Dependencies
2
- import { rootLocation } from "./rootLocation.js";
3
-
4
- export function toArrayLocation(location = rootLocation()) {
5
- const { start, end = start } = location;
6
-
7
- return [
8
- [start.line || 0, start.column || 0],
9
- [end.line || 0, end.column || 0]
10
- ];
11
- }
package/src/warnings.js DELETED
@@ -1,77 +0,0 @@
1
- // Import Internal Dependencies
2
- import { toArrayLocation } from "./utils/toArrayLocation.js";
3
- import { notNullOrUndefined } from "./utils/notNullOrUndefined.js";
4
-
5
- export const warnings = Object.freeze({
6
- "parsing-error": {
7
- i18n: "sast_warnings.parsing_error",
8
- severity: "Information"
9
- },
10
- "unsafe-import": {
11
- i18n: "sast_warnings.unsafe_import",
12
- severity: "Warning"
13
- },
14
- "unsafe-regex": {
15
- i18n: "sast_warnings.unsafe_regex",
16
- severity: "Warning"
17
- },
18
- "unsafe-stmt": {
19
- code: "unsafe-stmt",
20
- i18n: "sast_warnings.unsafe_stmt",
21
- severity: "Warning"
22
- },
23
- "encoded-literal": {
24
- i18n: "sast_warnings.encoded_literal",
25
- severity: "Information"
26
- },
27
- "short-identifiers": {
28
- i18n: "sast_warnings.short_identifiers",
29
- severity: "Warning"
30
- },
31
- "suspicious-literal": {
32
- i18n: "sast_warnings.suspicious_literal",
33
- severity: "Warning"
34
- },
35
- "suspicious-file": {
36
- i18n: "sast_warnings.suspicious_file",
37
- severity: "Critical",
38
- experimental: false
39
- },
40
- "obfuscated-code": {
41
- i18n: "sast_warnings.obfuscated_code",
42
- severity: "Critical",
43
- experimental: true
44
- },
45
- "weak-crypto": {
46
- i18n: "sast_warnings.weak_crypto",
47
- severity: "Information",
48
- experimental: false
49
- },
50
- "shady-link": {
51
- i18n: "sast_warnings.shady_link",
52
- severity: "Warning",
53
- experimental: false
54
- }
55
- });
56
-
57
- export function generateWarning(kind, options) {
58
- const { location, file = null, value = null, source = "JS-X-Ray" } = options;
59
-
60
- if (kind === "encoded-literal") {
61
- return Object.assign(
62
- { kind, value, location: [toArrayLocation(location)], source },
63
- warnings[kind]
64
- );
65
- }
66
-
67
- const result = { kind, location: toArrayLocation(location), source };
68
- if (notNullOrUndefined(file)) {
69
- result.file = file;
70
- }
71
- if (notNullOrUndefined(value)) {
72
- result.value = value;
73
- }
74
-
75
- return Object.assign(result, warnings[kind]);
76
- }
77
-
package/types/api.d.ts DELETED
@@ -1,177 +0,0 @@
1
- // Third-party
2
- import type { DiGraph, VertexDefinition, VertexBody } from "digraph-js";
3
- import { Statement } from "meriyah";
4
-
5
- // Internal
6
- import {
7
- Warning,
8
- WarningName
9
- } from "./warnings.js";
10
-
11
- export {
12
- AstAnalyser,
13
- AstAnalyserOptions,
14
-
15
- EntryFilesAnalyser,
16
- EntryFilesAnalyserOptions,
17
-
18
- JsSourceParser,
19
- SourceParser,
20
-
21
- RuntimeOptions,
22
- RuntimeFileOptions,
23
-
24
- Report,
25
- ReportOnFile,
26
-
27
- SourceFlags,
28
- SourceLocation,
29
- Dependency
30
- }
31
-
32
- type SourceFlags =
33
- | "fetch"
34
- | "oneline-require"
35
- | "is-minified";
36
-
37
- interface SourceLocation {
38
- start: {
39
- line: number;
40
- column: number;
41
- };
42
- end: {
43
- line: number;
44
- column: number;
45
- }
46
- }
47
-
48
- interface Dependency {
49
- unsafe: boolean;
50
- inTry: boolean;
51
- location?: null | SourceLocation;
52
- }
53
-
54
- interface RuntimeOptions {
55
- /**
56
- * @default true
57
- */
58
- module?: boolean;
59
- /**
60
- * @default false
61
- */
62
- removeHTMLComments?: boolean;
63
- /**
64
- * @default false
65
- */
66
- isMinified?: boolean;
67
- initialize?: (sourceFile: SourceFile) => void;
68
- finalize?: (sourceFile: SourceFile) => void;
69
- }
70
-
71
- interface RuntimeFileOptions extends Omit<RuntimeOptions, "isMinified"> {
72
- packageName?: string;
73
- }
74
-
75
- interface AstAnalyserOptions {
76
- /**
77
- * @default JsSourceParser
78
- */
79
- customParser?: SourceParser;
80
- /**
81
- * @default []
82
- */
83
- customProbes?: Probe[];
84
- /**
85
- * @default false
86
- */
87
- skipDefaultProbes?: boolean;
88
- }
89
-
90
- interface Probe {
91
- validateNode: Function | Function[];
92
- main: Function;
93
- }
94
-
95
- interface Report {
96
- dependencies: Map<string, Dependency>;
97
- warnings: Warning[];
98
- flags: Set<SourceFlags>;
99
- idsLengthAvg: number;
100
- stringScore: number;
101
- }
102
-
103
- type ReportOnFile = {
104
- ok: true,
105
- warnings: Warning[];
106
- dependencies: Map<string, Dependency>;
107
- flags: Set<SourceFlags>;
108
- } | {
109
- ok: false,
110
- warnings: Warning[];
111
- }
112
-
113
- interface SourceParser {
114
- parse(source: string, options: unknown): Statement[];
115
- }
116
-
117
- declare class AstAnalyser {
118
- constructor(options?: AstAnalyserOptions);
119
- analyse: (
120
- str: string,
121
- options?: RuntimeOptions
122
- ) => Report;
123
- analyseFile(
124
- pathToFile: string,
125
- options?: RuntimeFileOptions
126
- ): Promise<ReportOnFile>;
127
- analyseFileSync(
128
- pathToFile: string,
129
- options?: RuntimeFileOptions
130
- ): ReportOnFile;
131
- }
132
-
133
- declare class SourceFile {
134
- flags: Set<SourceFlags>;
135
-
136
- constructor(source: string, options: any);
137
- addDependency(
138
- name: string,
139
- location?: string | null,
140
- unsafe?: boolean
141
- ): void;
142
- addWarning(
143
- name: WarningName,
144
- value: string,
145
- location?: any
146
- ): void;
147
- analyzeLiteral(node: any, inArrayExpr?: boolean): void;
148
- getResult(isMinified: boolean): any;
149
- walk(node: any): "skip" | null;
150
- }
151
-
152
- interface EntryFilesAnalyserOptions {
153
- astAnalyzer?: AstAnalyser;
154
- loadExtensions?: (defaults: string[]) => string[];
155
- rootPath?: string | URL;
156
- ignoreENOENT?: boolean;
157
- }
158
-
159
- declare class EntryFilesAnalyser {
160
- public astAnalyzer: AstAnalyser;
161
- public allowedExtensions: Set<string>;
162
- public dependencies: DiGraph<VertexDefinition<VertexBody>>;
163
-
164
- constructor(options?: EntryFilesAnalyserOptions);
165
-
166
- /**
167
- * Asynchronously analyze a set of entry files yielding analysis reports.
168
- */
169
- analyse(
170
- entryFiles: Iterable<string | URL>,
171
- options?: RuntimeFileOptions
172
- ): AsyncGenerator<ReportOnFile & { file: string }>;
173
- }
174
-
175
- declare class JsSourceParser implements SourceParser {
176
- parse(source: string, options: unknown): Statement[];
177
- }
@@ -1,36 +0,0 @@
1
-
2
- export {
3
- Warning,
4
- WarningDefault,
5
- WarningLocation,
6
- WarningName,
7
- WarningNameWithValue
8
- }
9
-
10
- type WarningNameWithValue = "parsing-error"
11
- | "encoded-literal"
12
- | "unsafe-regex"
13
- | "unsafe-stmt"
14
- | "short-identifiers"
15
- | "suspicious-literal"
16
- | "suspicious-file"
17
- | "obfuscated-code"
18
- | "weak-crypto"
19
- | "shady-link";
20
- type WarningName = WarningNameWithValue | "unsafe-import";
21
-
22
- type WarningLocation = [[number, number], [number, number]];
23
-
24
- interface WarningDefault<T = WarningName> {
25
- kind: T;
26
- file?: string;
27
- value: string;
28
- source: string;
29
- location: null | WarningLocation | WarningLocation[];
30
- i18n: string;
31
- severity: "Information" | "Warning" | "Critical";
32
- experimental?: boolean;
33
- }
34
-
35
- type Warning<T extends WarningDefault = WarningDefault> =
36
- T extends { kind: WarningNameWithValue } ? T : Omit<T, "value">;