scriptguard 1.0.0 → 1.0.2
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.
- package/README.md +191 -44
- package/dist/ai/gemini-client.d.ts +2 -1
- package/dist/ai/gemini-client.d.ts.map +1 -1
- package/dist/ai/gemini-client.js +12 -2
- package/dist/ai/gemini-client.js.map +1 -1
- package/dist/ai/prompts.d.ts.map +1 -1
- package/dist/ai/prompts.js +37 -1
- package/dist/ai/prompts.js.map +1 -1
- package/dist/cli.js +6 -0
- package/dist/cli.js.map +1 -1
- package/dist/scanners/ast.d.ts +11 -0
- package/dist/scanners/ast.d.ts.map +1 -0
- package/dist/scanners/ast.js +267 -0
- package/dist/scanners/ast.js.map +1 -0
- package/dist/scanners/deobfuscation.d.ts +12 -0
- package/dist/scanners/deobfuscation.d.ts.map +1 -0
- package/dist/scanners/deobfuscation.js +169 -0
- package/dist/scanners/deobfuscation.js.map +1 -0
- package/dist/scanners/index.d.ts.map +1 -1
- package/dist/scanners/index.js +1 -1
- package/dist/scanners/index.js.map +1 -1
- package/dist/scanners/lifecycle.d.ts +8 -2
- package/dist/scanners/lifecycle.d.ts.map +1 -1
- package/dist/scanners/lifecycle.js +63 -5
- package/dist/scanners/lifecycle.js.map +1 -1
- package/dist/types/index.d.ts +21 -0
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +3 -1
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/** ScriptGuard — AST-based pattern detection (Layer 2) */
|
|
3
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
4
|
+
if (k2 === undefined) k2 = k;
|
|
5
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
6
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
7
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
8
|
+
}
|
|
9
|
+
Object.defineProperty(o, k2, desc);
|
|
10
|
+
}) : (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
o[k2] = m[k];
|
|
13
|
+
}));
|
|
14
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
15
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
16
|
+
}) : function(o, v) {
|
|
17
|
+
o["default"] = v;
|
|
18
|
+
});
|
|
19
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
20
|
+
var ownKeys = function(o) {
|
|
21
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
22
|
+
var ar = [];
|
|
23
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
24
|
+
return ar;
|
|
25
|
+
};
|
|
26
|
+
return ownKeys(o);
|
|
27
|
+
};
|
|
28
|
+
return function (mod) {
|
|
29
|
+
if (mod && mod.__esModule) return mod;
|
|
30
|
+
var result = {};
|
|
31
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
32
|
+
__setModuleDefault(result, mod);
|
|
33
|
+
return result;
|
|
34
|
+
};
|
|
35
|
+
})();
|
|
36
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
37
|
+
exports.analyzeScriptAST = analyzeScriptAST;
|
|
38
|
+
const acorn_1 = require("acorn");
|
|
39
|
+
const walk = __importStar(require("acorn-walk"));
|
|
40
|
+
/**
|
|
41
|
+
* Dangerous objects that we want to track access to
|
|
42
|
+
*/
|
|
43
|
+
const DANGEROUS_OBJECTS = new Set([
|
|
44
|
+
'process',
|
|
45
|
+
'fs',
|
|
46
|
+
'child_process',
|
|
47
|
+
'net',
|
|
48
|
+
'http',
|
|
49
|
+
'https',
|
|
50
|
+
'url',
|
|
51
|
+
'path',
|
|
52
|
+
'os',
|
|
53
|
+
'crypto',
|
|
54
|
+
'vm',
|
|
55
|
+
'cluster',
|
|
56
|
+
]);
|
|
57
|
+
/**
|
|
58
|
+
* Analyze script content using AST to detect structural patterns
|
|
59
|
+
* that regex cannot see (dynamic require, computed eval, etc.)
|
|
60
|
+
*
|
|
61
|
+
* @param scriptContent - The JavaScript code to analyze
|
|
62
|
+
* @returns Array of AST findings (empty if parse fails or no patterns found)
|
|
63
|
+
*/
|
|
64
|
+
function analyzeScriptAST(scriptContent) {
|
|
65
|
+
// Skip large scripts (>1MB) to prevent memory issues
|
|
66
|
+
if (scriptContent.length > 1_000_000) {
|
|
67
|
+
return [];
|
|
68
|
+
}
|
|
69
|
+
let ast;
|
|
70
|
+
try {
|
|
71
|
+
ast = (0, acorn_1.parse)(scriptContent, {
|
|
72
|
+
ecmaVersion: 'latest',
|
|
73
|
+
sourceType: 'script',
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
// Malformed JavaScript — return empty array (graceful degradation)
|
|
78
|
+
return [];
|
|
79
|
+
}
|
|
80
|
+
const findings = [];
|
|
81
|
+
// Walk the AST and detect patterns
|
|
82
|
+
walk.simple(ast, {
|
|
83
|
+
// Detect: require(variable), require(computed), require('child_' + 'process')
|
|
84
|
+
CallExpression(node) {
|
|
85
|
+
const callee = node.callee;
|
|
86
|
+
// Pattern 1: Dynamic require()
|
|
87
|
+
if (callee.type === 'Identifier' && callee.name === 'require') {
|
|
88
|
+
if (node.arguments.length > 0) {
|
|
89
|
+
const arg = node.arguments[0];
|
|
90
|
+
// Flag if argument is NOT a literal string
|
|
91
|
+
if (!isLiteral(arg)) {
|
|
92
|
+
findings.push({
|
|
93
|
+
pattern: 'ast-dynamic-require',
|
|
94
|
+
description: 'Dynamic require() with non-literal argument — may load arbitrary modules',
|
|
95
|
+
riskLevel: 'high',
|
|
96
|
+
nodeType: 'CallExpression',
|
|
97
|
+
match: extractMatch(scriptContent, node),
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
// Pattern 2: Computed eval() or Function()
|
|
103
|
+
if ((callee.type === 'Identifier' && (callee.name === 'eval' || callee.name === 'Function')) ||
|
|
104
|
+
(callee.type === 'MemberExpression' &&
|
|
105
|
+
callee.property.type === 'Identifier' &&
|
|
106
|
+
(callee.property.name === 'eval' || callee.property.name === 'Function'))) {
|
|
107
|
+
if (node.arguments.length > 0) {
|
|
108
|
+
const arg = node.arguments[0];
|
|
109
|
+
// Flag if argument is NOT a literal string
|
|
110
|
+
if (!isLiteral(arg)) {
|
|
111
|
+
findings.push({
|
|
112
|
+
pattern: 'ast-computed-eval',
|
|
113
|
+
description: 'Computed eval() or Function() — may execute arbitrary code',
|
|
114
|
+
riskLevel: 'critical',
|
|
115
|
+
nodeType: 'CallExpression',
|
|
116
|
+
match: extractMatch(scriptContent, node),
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
// Pattern 3: Computed property access on dangerous objects
|
|
123
|
+
// Example: process.env[computed], fs['read' + 'File']
|
|
124
|
+
MemberExpression(node) {
|
|
125
|
+
if (node.computed) {
|
|
126
|
+
// Check if object is dangerous
|
|
127
|
+
const objectName = getObjectName(node.object);
|
|
128
|
+
if (objectName && DANGEROUS_OBJECTS.has(objectName)) {
|
|
129
|
+
// Flag if property is computed (not a literal)
|
|
130
|
+
if (!isLiteral(node.property)) {
|
|
131
|
+
findings.push({
|
|
132
|
+
pattern: 'ast-computed-property',
|
|
133
|
+
description: `Computed property access on ${objectName} — may bypass keyword detection`,
|
|
134
|
+
riskLevel: 'high',
|
|
135
|
+
nodeType: 'MemberExpression',
|
|
136
|
+
match: extractMatch(scriptContent, node),
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
},
|
|
142
|
+
// Pattern 4: String concatenation building dangerous keywords
|
|
143
|
+
// Example: 'child_' + 'process', 'eval' + variable
|
|
144
|
+
BinaryExpression(node) {
|
|
145
|
+
if (node.operator === '+') {
|
|
146
|
+
const built = tryResolveBinaryExpression(node);
|
|
147
|
+
if (built && containsDangerousKeyword(built)) {
|
|
148
|
+
findings.push({
|
|
149
|
+
pattern: 'ast-string-building',
|
|
150
|
+
description: 'String concatenation building dangerous keywords — possible obfuscation',
|
|
151
|
+
riskLevel: 'medium',
|
|
152
|
+
nodeType: 'BinaryExpression',
|
|
153
|
+
match: extractMatch(scriptContent, node),
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
});
|
|
159
|
+
return findings;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Check if an AST node is a literal (string, number, boolean, null)
|
|
163
|
+
*/
|
|
164
|
+
function isLiteral(node) {
|
|
165
|
+
if (!node)
|
|
166
|
+
return false;
|
|
167
|
+
if (node.type === 'Literal') {
|
|
168
|
+
return true;
|
|
169
|
+
}
|
|
170
|
+
// Template literals with no expressions are literals
|
|
171
|
+
if (node.type === 'TemplateLiteral') {
|
|
172
|
+
return node.expressions.length === 0;
|
|
173
|
+
}
|
|
174
|
+
return false;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Get the name of an object from a MemberExpression or Identifier
|
|
178
|
+
*/
|
|
179
|
+
function getObjectName(node) {
|
|
180
|
+
if (!node)
|
|
181
|
+
return null;
|
|
182
|
+
if (node.type === 'Identifier') {
|
|
183
|
+
return node.name;
|
|
184
|
+
}
|
|
185
|
+
if (node.type === 'MemberExpression') {
|
|
186
|
+
return getObjectName(node.object);
|
|
187
|
+
}
|
|
188
|
+
return null;
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Try to resolve a binary expression to a string
|
|
192
|
+
* Returns null if resolution fails
|
|
193
|
+
*/
|
|
194
|
+
function tryResolveBinaryExpression(node) {
|
|
195
|
+
if (!node)
|
|
196
|
+
return null;
|
|
197
|
+
// If it's a literal, return its value
|
|
198
|
+
if (node.type === 'Literal' && typeof node.value === 'string') {
|
|
199
|
+
return node.value;
|
|
200
|
+
}
|
|
201
|
+
// If it's a binary expression, try to resolve both sides
|
|
202
|
+
if (node.type === 'BinaryExpression' && node.operator === '+') {
|
|
203
|
+
const left = tryResolveBinaryExpression(node.left);
|
|
204
|
+
const right = tryResolveBinaryExpression(node.right);
|
|
205
|
+
if (left !== null && right !== null) {
|
|
206
|
+
return left + right;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return null;
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Check if a string contains dangerous keywords
|
|
213
|
+
*/
|
|
214
|
+
function containsDangerousKeyword(str) {
|
|
215
|
+
const lower = str.toLowerCase();
|
|
216
|
+
const dangerousKeywords = [
|
|
217
|
+
'eval',
|
|
218
|
+
'function',
|
|
219
|
+
'require',
|
|
220
|
+
'exec',
|
|
221
|
+
'spawn',
|
|
222
|
+
'child_process',
|
|
223
|
+
'process.env',
|
|
224
|
+
'fs.',
|
|
225
|
+
'http.',
|
|
226
|
+
'net.',
|
|
227
|
+
];
|
|
228
|
+
for (const keyword of dangerousKeywords) {
|
|
229
|
+
if (lower.includes(keyword)) {
|
|
230
|
+
return true;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
return false;
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Extract the source code for a given AST node
|
|
237
|
+
*/
|
|
238
|
+
function extractMatch(scriptContent, node) {
|
|
239
|
+
if (!node || !node.loc) {
|
|
240
|
+
return '';
|
|
241
|
+
}
|
|
242
|
+
try {
|
|
243
|
+
const lines = scriptContent.split('\n');
|
|
244
|
+
const startLine = node.loc.start.line - 1; // 0-indexed
|
|
245
|
+
const endLine = node.loc.end.line - 1;
|
|
246
|
+
const startCol = node.loc.start.column;
|
|
247
|
+
const endCol = node.loc.end.column;
|
|
248
|
+
if (startLine === endLine) {
|
|
249
|
+
// Single line
|
|
250
|
+
const line = lines[startLine];
|
|
251
|
+
return line.substring(startCol, endCol);
|
|
252
|
+
}
|
|
253
|
+
else {
|
|
254
|
+
// Multi-line
|
|
255
|
+
let result = lines[startLine].substring(startCol);
|
|
256
|
+
for (let i = startLine + 1; i < endLine; i++) {
|
|
257
|
+
result += '\n' + lines[i];
|
|
258
|
+
}
|
|
259
|
+
result += '\n' + lines[endLine].substring(0, endCol);
|
|
260
|
+
return result;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
catch {
|
|
264
|
+
return '';
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
//# sourceMappingURL=ast.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ast.js","sourceRoot":"","sources":["../../src/scanners/ast.ts"],"names":[],"mappings":";AAAA,0DAA0D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+B1D,4CA8GC;AA3ID,iCAA8B;AAC9B,iDAAmC;AAGnC;;GAEG;AACH,MAAM,iBAAiB,GAAG,IAAI,GAAG,CAAC;IAChC,SAAS;IACT,IAAI;IACJ,eAAe;IACf,KAAK;IACL,MAAM;IACN,OAAO;IACP,KAAK;IACL,MAAM;IACN,IAAI;IACJ,QAAQ;IACR,IAAI;IACJ,SAAS;CACV,CAAC,CAAC;AAEH;;;;;;GAMG;AACH,SAAgB,gBAAgB,CAC9B,aAAqB;IAErB,qDAAqD;IACrD,IAAI,aAAa,CAAC,MAAM,GAAG,SAAS,EAAE,CAAC;QACrC,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,IAAI,GAAQ,CAAC;IAEb,IAAI,CAAC;QACH,GAAG,GAAG,IAAA,aAAK,EAAC,aAAa,EAAE;YACzB,WAAW,EAAE,QAAQ;YACrB,UAAU,EAAE,QAAQ;SACrB,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,mEAAmE;QACnE,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,QAAQ,GAAiB,EAAE,CAAC;IAElC,mCAAmC;IACnC,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE;QACf,8EAA8E;QAC9E,cAAc,CAAC,IAAS;YACtB,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;YAE3B,+BAA+B;YAC/B,IAAI,MAAM,CAAC,IAAI,KAAK,YAAY,IAAI,MAAM,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;gBAC9D,IAAI,IAAI,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;oBAC9B,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;oBAE9B,2CAA2C;oBAC3C,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC;wBACpB,QAAQ,CAAC,IAAI,CAAC;4BACZ,OAAO,EAAE,qBAAqB;4BAC9B,WAAW,EAAE,0EAA0E;4BACvF,SAAS,EAAE,MAAM;4BACjB,QAAQ,EAAE,gBAAgB;4BAC1B,KAAK,EAAE,YAAY,CAAC,aAAa,EAAE,IAAI,CAAC;yBACzC,CAAC,CAAC;oBACL,CAAC;gBACH,CAAC;YACH,CAAC;YAED,2CAA2C;YAC3C,IACE,CAAC,MAAM,CAAC,IAAI,KAAK,YAAY,IAAI,CAAC,MAAM,CAAC,IAAI,KAAK,MAAM,IAAI,MAAM,CAAC,IAAI,KAAK,UAAU,CAAC,CAAC;gBACxF,CAAC,MAAM,CAAC,IAAI,KAAK,kBAAkB;oBACjC,MAAM,CAAC,QAAQ,CAAC,IAAI,KAAK,YAAY;oBACrC,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,KAAK,MAAM,IAAI,MAAM,CAAC,QAAQ,CAAC,IAAI,KAAK,UAAU,CAAC,CAAC,EAC3E,CAAC;gBACD,IAAI,IAAI,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;oBAC9B,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;oBAE9B,2CAA2C;oBAC3C,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC;wBACpB,QAAQ,CAAC,IAAI,CAAC;4BACZ,OAAO,EAAE,mBAAmB;4BAC5B,WAAW,EAAE,4DAA4D;4BACzE,SAAS,EAAE,UAAU;4BACrB,QAAQ,EAAE,gBAAgB;4BAC1B,KAAK,EAAE,YAAY,CAAC,aAAa,EAAE,IAAI,CAAC;yBACzC,CAAC,CAAC;oBACL,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;QAED,2DAA2D;QAC3D,sDAAsD;QACtD,gBAAgB,CAAC,IAAS;YACxB,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;gBAClB,+BAA+B;gBAC/B,MAAM,UAAU,GAAG,aAAa,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;gBAC9C,IAAI,UAAU,IAAI,iBAAiB,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC;oBACpD,+CAA+C;oBAC/C,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;wBAC9B,QAAQ,CAAC,IAAI,CAAC;4BACZ,OAAO,EAAE,uBAAuB;4BAChC,WAAW,EAAE,+BAA+B,UAAU,iCAAiC;4BACvF,SAAS,EAAE,MAAM;4BACjB,QAAQ,EAAE,kBAAkB;4BAC5B,KAAK,EAAE,YAAY,CAAC,aAAa,EAAE,IAAI,CAAC;yBACzC,CAAC,CAAC;oBACL,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;QAED,8DAA8D;QAC9D,mDAAmD;QACnD,gBAAgB,CAAC,IAAS;YACxB,IAAI,IAAI,CAAC,QAAQ,KAAK,GAAG,EAAE,CAAC;gBAC1B,MAAM,KAAK,GAAG,0BAA0B,CAAC,IAAI,CAAC,CAAC;gBAC/C,IAAI,KAAK,IAAI,wBAAwB,CAAC,KAAK,CAAC,EAAE,CAAC;oBAC7C,QAAQ,CAAC,IAAI,CAAC;wBACZ,OAAO,EAAE,qBAAqB;wBAC9B,WAAW,EAAE,yEAAyE;wBACtF,SAAS,EAAE,QAAQ;wBACnB,QAAQ,EAAE,kBAAkB;wBAC5B,KAAK,EAAE,YAAY,CAAC,aAAa,EAAE,IAAI,CAAC;qBACzC,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;QACH,CAAC;KACF,CAAC,CAAC;IAEH,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;GAEG;AACH,SAAS,SAAS,CAAC,IAAS;IAC1B,IAAI,CAAC,IAAI;QAAE,OAAO,KAAK,CAAC;IAExB,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QAC5B,OAAO,IAAI,CAAC;IACd,CAAC;IAED,qDAAqD;IACrD,IAAI,IAAI,CAAC,IAAI,KAAK,iBAAiB,EAAE,CAAC;QACpC,OAAO,IAAI,CAAC,WAAW,CAAC,MAAM,KAAK,CAAC,CAAC;IACvC,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;GAEG;AACH,SAAS,aAAa,CAAC,IAAS;IAC9B,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC;IAEvB,IAAI,IAAI,CAAC,IAAI,KAAK,YAAY,EAAE,CAAC;QAC/B,OAAO,IAAI,CAAC,IAAI,CAAC;IACnB,CAAC;IAED,IAAI,IAAI,CAAC,IAAI,KAAK,kBAAkB,EAAE,CAAC;QACrC,OAAO,aAAa,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACpC,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;GAGG;AACH,SAAS,0BAA0B,CAAC,IAAS;IAC3C,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC;IAEvB,sCAAsC;IACtC,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS,IAAI,OAAO,IAAI,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9D,OAAO,IAAI,CAAC,KAAK,CAAC;IACpB,CAAC;IAED,yDAAyD;IACzD,IAAI,IAAI,CAAC,IAAI,KAAK,kBAAkB,IAAI,IAAI,CAAC,QAAQ,KAAK,GAAG,EAAE,CAAC;QAC9D,MAAM,IAAI,GAAG,0BAA0B,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACnD,MAAM,KAAK,GAAG,0BAA0B,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAErD,IAAI,IAAI,KAAK,IAAI,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;YACpC,OAAO,IAAI,GAAG,KAAK,CAAC;QACtB,CAAC;IACH,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;GAEG;AACH,SAAS,wBAAwB,CAAC,GAAW;IAC3C,MAAM,KAAK,GAAG,GAAG,CAAC,WAAW,EAAE,CAAC;IAChC,MAAM,iBAAiB,GAAG;QACxB,MAAM;QACN,UAAU;QACV,SAAS;QACT,MAAM;QACN,OAAO;QACP,eAAe;QACf,aAAa;QACb,KAAK;QACL,OAAO;QACP,MAAM;KACP,CAAC;IAEF,KAAK,MAAM,OAAO,IAAI,iBAAiB,EAAE,CAAC;QACxC,IAAI,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;YAC5B,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;GAEG;AACH,SAAS,YAAY,CAAC,aAAqB,EAAE,IAAS;IACpD,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,aAAa,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACxC,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,YAAY;QACvD,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,CAAC;QACtC,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC;QACvC,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC;QAEnC,IAAI,SAAS,KAAK,OAAO,EAAE,CAAC;YAC1B,cAAc;YACd,MAAM,IAAI,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC;YAC9B,OAAO,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QAC1C,CAAC;aAAM,CAAC;YACN,aAAa;YACb,IAAI,MAAM,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;YAClD,KAAK,IAAI,CAAC,GAAG,SAAS,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,EAAE,CAAC,EAAE,EAAE,CAAC;gBAC7C,MAAM,IAAI,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;YAC5B,CAAC;YACD,MAAM,IAAI,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;YACrD,OAAO,MAAM,CAAC;QAChB,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/** ScriptGuard — Simplified deobfuscation (Layer 3, decode-only, NO code execution) */
|
|
2
|
+
import type { DeobfuscationResult } from '../types/index.js';
|
|
3
|
+
/**
|
|
4
|
+
* Deobfuscate a script by decoding obvious encoding layers.
|
|
5
|
+
* This is a DECODE-ONLY approach — NO code execution.
|
|
6
|
+
*
|
|
7
|
+
* @param scriptContent - The obfuscated JavaScript code
|
|
8
|
+
* @param maxIterations - Maximum decode iterations (default: 2)
|
|
9
|
+
* @returns Deobfuscation result with deobfuscated code and metadata
|
|
10
|
+
*/
|
|
11
|
+
export declare function deobfuscateScript(scriptContent: string, maxIterations?: number): DeobfuscationResult;
|
|
12
|
+
//# sourceMappingURL=deobfuscation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deobfuscation.d.ts","sourceRoot":"","sources":["../../src/scanners/deobfuscation.ts"],"names":[],"mappings":"AAAA,uFAAuF;AAGvF,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAE7D;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAC/B,aAAa,EAAE,MAAM,EACrB,aAAa,GAAE,MAAU,GACxB,mBAAmB,CAiFrB"}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/** ScriptGuard — Simplified deobfuscation (Layer 3, decode-only, NO code execution) */
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.deobfuscateScript = deobfuscateScript;
|
|
5
|
+
const acorn_1 = require("acorn");
|
|
6
|
+
/**
|
|
7
|
+
* Deobfuscate a script by decoding obvious encoding layers.
|
|
8
|
+
* This is a DECODE-ONLY approach — NO code execution.
|
|
9
|
+
*
|
|
10
|
+
* @param scriptContent - The obfuscated JavaScript code
|
|
11
|
+
* @param maxIterations - Maximum decode iterations (default: 2)
|
|
12
|
+
* @returns Deobfuscation result with deobfuscated code and metadata
|
|
13
|
+
*/
|
|
14
|
+
function deobfuscateScript(scriptContent, maxIterations = 2) {
|
|
15
|
+
// Skip large scripts (>1MB) to prevent memory issues
|
|
16
|
+
if (scriptContent.length > 1_000_000) {
|
|
17
|
+
return {
|
|
18
|
+
deobfuscated: scriptContent,
|
|
19
|
+
iterations: 0,
|
|
20
|
+
techniques: [],
|
|
21
|
+
success: false,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
let current = scriptContent;
|
|
25
|
+
const techniques = [];
|
|
26
|
+
let iterations = 0;
|
|
27
|
+
for (let i = 0; i < maxIterations; i++) {
|
|
28
|
+
const previous = current;
|
|
29
|
+
const iterationTechniques = [];
|
|
30
|
+
// Layer 1: Base64 decoding
|
|
31
|
+
current = decodeBase64Layers(current, iterationTechniques);
|
|
32
|
+
// Layer 2: Hex escape decoding
|
|
33
|
+
current = decodeHexEscapes(current, iterationTechniques);
|
|
34
|
+
// Layer 3: Unicode escape decoding
|
|
35
|
+
current = decodeUnicodeEscapes(current, iterationTechniques);
|
|
36
|
+
// Check if we made progress
|
|
37
|
+
if (current === previous) {
|
|
38
|
+
break;
|
|
39
|
+
}
|
|
40
|
+
techniques.push(...iterationTechniques);
|
|
41
|
+
iterations++;
|
|
42
|
+
// Safety: Stop if script becomes too large (exponential unpacking prevention)
|
|
43
|
+
if (current.length > scriptContent.length * 10) {
|
|
44
|
+
// Script grew too much — potential unpacking bomb
|
|
45
|
+
return {
|
|
46
|
+
deobfuscated: scriptContent, // Return original
|
|
47
|
+
iterations: 0,
|
|
48
|
+
techniques: [],
|
|
49
|
+
success: false,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
// Validate that deobfuscated code is still valid JavaScript
|
|
54
|
+
if (iterations > 0) {
|
|
55
|
+
try {
|
|
56
|
+
(0, acorn_1.parse)(current, {
|
|
57
|
+
ecmaVersion: 'latest',
|
|
58
|
+
sourceType: 'script',
|
|
59
|
+
});
|
|
60
|
+
// Deobfuscated successfully and is valid JS
|
|
61
|
+
return {
|
|
62
|
+
deobfuscated: current,
|
|
63
|
+
iterations,
|
|
64
|
+
techniques,
|
|
65
|
+
success: true,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
// Deobfuscation produced invalid syntax — return original
|
|
70
|
+
return {
|
|
71
|
+
deobfuscated: scriptContent,
|
|
72
|
+
iterations: 0,
|
|
73
|
+
techniques: [],
|
|
74
|
+
success: false,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
// No deobfuscation occurred
|
|
79
|
+
return {
|
|
80
|
+
deobfuscated: scriptContent,
|
|
81
|
+
iterations: 0,
|
|
82
|
+
techniques: [],
|
|
83
|
+
success: false,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Decode base64-encoded strings in the script.
|
|
88
|
+
* Detects: atob(...), Buffer.from(..., 'base64'), base64 -d patterns
|
|
89
|
+
*
|
|
90
|
+
* Only decodes OBVIOUS patterns (no nested expressions) to avoid false positives.
|
|
91
|
+
*/
|
|
92
|
+
function decodeBase64Layers(script, techniques) {
|
|
93
|
+
let result = script;
|
|
94
|
+
// Pattern 1: atob('base64string')
|
|
95
|
+
const atobPattern = /atob\s*\(\s*(['"`])([A-Za-z0-9+/=]+)\1\s*\)/g;
|
|
96
|
+
result = result.replace(atobPattern, (match, quote, base64) => {
|
|
97
|
+
try {
|
|
98
|
+
const decoded = Buffer.from(base64, 'base64').toString('utf-8');
|
|
99
|
+
// Only replace if decoded looks like valid text (no control characters)
|
|
100
|
+
if (/^[\x20-\x7E\s]*$/.test(decoded)) {
|
|
101
|
+
techniques.push('base64-atob');
|
|
102
|
+
return JSON.stringify(decoded); // Return as quoted string
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
// Invalid base64 — return original
|
|
107
|
+
}
|
|
108
|
+
return match;
|
|
109
|
+
});
|
|
110
|
+
// Pattern 2: Buffer.from('base64string', 'base64')
|
|
111
|
+
const bufferPattern = /Buffer\.from\s*\(\s*(['"`])([A-Za-z0-9+/=]+)\1\s*,\s*['"`]base64['"`]\s*\)/g;
|
|
112
|
+
result = result.replace(bufferPattern, (match, quote, base64) => {
|
|
113
|
+
try {
|
|
114
|
+
const decoded = Buffer.from(base64, 'base64').toString('utf-8');
|
|
115
|
+
if (/^[\x20-\x7E\s]*$/.test(decoded)) {
|
|
116
|
+
techniques.push('base64-buffer');
|
|
117
|
+
return JSON.stringify(decoded);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
catch {
|
|
121
|
+
// Invalid base64
|
|
122
|
+
}
|
|
123
|
+
return match;
|
|
124
|
+
});
|
|
125
|
+
// Pattern 3: .toString('base64') chains (reverse)
|
|
126
|
+
// This is more complex and may produce false positives, so be conservative
|
|
127
|
+
const toStringPattern = /\.toString\s*\(\s*['"`]base64['"`]\s*\)/g;
|
|
128
|
+
// Don't auto-replace these — they're harder to detect safely
|
|
129
|
+
return result;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Decode hexadecimal escape sequences: \xNN
|
|
133
|
+
*/
|
|
134
|
+
function decodeHexEscapes(script, techniques) {
|
|
135
|
+
let result = script;
|
|
136
|
+
let hasDecoded = false;
|
|
137
|
+
// Match \xNN patterns (where NN are hex digits)
|
|
138
|
+
const hexPattern = /\\x([0-9a-fA-F]{2})/g;
|
|
139
|
+
result = result.replace(hexPattern, (match, hex) => {
|
|
140
|
+
const charCode = parseInt(hex, 16);
|
|
141
|
+
const char = String.fromCharCode(charCode);
|
|
142
|
+
hasDecoded = true;
|
|
143
|
+
return char;
|
|
144
|
+
});
|
|
145
|
+
if (hasDecoded) {
|
|
146
|
+
techniques.push('hex-escape');
|
|
147
|
+
}
|
|
148
|
+
return result;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Decode Unicode escape sequences: \uNNNN
|
|
152
|
+
*/
|
|
153
|
+
function decodeUnicodeEscapes(script, techniques) {
|
|
154
|
+
let result = script;
|
|
155
|
+
let hasDecoded = false;
|
|
156
|
+
// Match \uNNNN patterns (where NNNN are hex digits)
|
|
157
|
+
const unicodePattern = /\\u([0-9a-fA-F]{4})/g;
|
|
158
|
+
result = result.replace(unicodePattern, (match, hex) => {
|
|
159
|
+
const charCode = parseInt(hex, 16);
|
|
160
|
+
const char = String.fromCharCode(charCode);
|
|
161
|
+
hasDecoded = true;
|
|
162
|
+
return char;
|
|
163
|
+
});
|
|
164
|
+
if (hasDecoded) {
|
|
165
|
+
techniques.push('unicode-escape');
|
|
166
|
+
}
|
|
167
|
+
return result;
|
|
168
|
+
}
|
|
169
|
+
//# sourceMappingURL=deobfuscation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deobfuscation.js","sourceRoot":"","sources":["../../src/scanners/deobfuscation.ts"],"names":[],"mappings":";AAAA,uFAAuF;;AAavF,8CAoFC;AA/FD,iCAA8B;AAG9B;;;;;;;GAOG;AACH,SAAgB,iBAAiB,CAC/B,aAAqB,EACrB,gBAAwB,CAAC;IAEzB,qDAAqD;IACrD,IAAI,aAAa,CAAC,MAAM,GAAG,SAAS,EAAE,CAAC;QACrC,OAAO;YACL,YAAY,EAAE,aAAa;YAC3B,UAAU,EAAE,CAAC;YACb,UAAU,EAAE,EAAE;YACd,OAAO,EAAE,KAAK;SACf,CAAC;IACJ,CAAC;IAED,IAAI,OAAO,GAAG,aAAa,CAAC;IAC5B,MAAM,UAAU,GAAa,EAAE,CAAC;IAChC,IAAI,UAAU,GAAG,CAAC,CAAC;IAEnB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,aAAa,EAAE,CAAC,EAAE,EAAE,CAAC;QACvC,MAAM,QAAQ,GAAG,OAAO,CAAC;QACzB,MAAM,mBAAmB,GAAa,EAAE,CAAC;QAEzC,2BAA2B;QAC3B,OAAO,GAAG,kBAAkB,CAAC,OAAO,EAAE,mBAAmB,CAAC,CAAC;QAE3D,+BAA+B;QAC/B,OAAO,GAAG,gBAAgB,CAAC,OAAO,EAAE,mBAAmB,CAAC,CAAC;QAEzD,mCAAmC;QACnC,OAAO,GAAG,oBAAoB,CAAC,OAAO,EAAE,mBAAmB,CAAC,CAAC;QAE7D,4BAA4B;QAC5B,IAAI,OAAO,KAAK,QAAQ,EAAE,CAAC;YACzB,MAAM;QACR,CAAC;QAED,UAAU,CAAC,IAAI,CAAC,GAAG,mBAAmB,CAAC,CAAC;QACxC,UAAU,EAAE,CAAC;QAEb,8EAA8E;QAC9E,IAAI,OAAO,CAAC,MAAM,GAAG,aAAa,CAAC,MAAM,GAAG,EAAE,EAAE,CAAC;YAC/C,kDAAkD;YAClD,OAAO;gBACL,YAAY,EAAE,aAAa,EAAE,kBAAkB;gBAC/C,UAAU,EAAE,CAAC;gBACb,UAAU,EAAE,EAAE;gBACd,OAAO,EAAE,KAAK;aACf,CAAC;QACJ,CAAC;IACH,CAAC;IAED,4DAA4D;IAC5D,IAAI,UAAU,GAAG,CAAC,EAAE,CAAC;QACnB,IAAI,CAAC;YACH,IAAA,aAAK,EAAC,OAAO,EAAE;gBACb,WAAW,EAAE,QAAQ;gBACrB,UAAU,EAAE,QAAQ;aACrB,CAAC,CAAC;YAEH,4CAA4C;YAC5C,OAAO;gBACL,YAAY,EAAE,OAAO;gBACrB,UAAU;gBACV,UAAU;gBACV,OAAO,EAAE,IAAI;aACd,CAAC;QACJ,CAAC;QAAC,MAAM,CAAC;YACP,0DAA0D;YAC1D,OAAO;gBACL,YAAY,EAAE,aAAa;gBAC3B,UAAU,EAAE,CAAC;gBACb,UAAU,EAAE,EAAE;gBACd,OAAO,EAAE,KAAK;aACf,CAAC;QACJ,CAAC;IACH,CAAC;IAED,4BAA4B;IAC5B,OAAO;QACL,YAAY,EAAE,aAAa;QAC3B,UAAU,EAAE,CAAC;QACb,UAAU,EAAE,EAAE;QACd,OAAO,EAAE,KAAK;KACf,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,SAAS,kBAAkB,CAAC,MAAc,EAAE,UAAoB;IAC9D,IAAI,MAAM,GAAG,MAAM,CAAC;IAEpB,kCAAkC;IAClC,MAAM,WAAW,GAAG,8CAA8C,CAAC;IACnE,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE;QAC5D,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;YAChE,wEAAwE;YACxE,IAAI,kBAAkB,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;gBACrC,UAAU,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;gBAC/B,OAAO,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,0BAA0B;YAC5D,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,mCAAmC;QACrC,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC,CAAC,CAAC;IAEH,mDAAmD;IACnD,MAAM,aAAa,GAAG,6EAA6E,CAAC;IACpG,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,aAAa,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE;QAC9D,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;YAChE,IAAI,kBAAkB,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;gBACrC,UAAU,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;gBACjC,OAAO,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;YACjC,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,iBAAiB;QACnB,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC,CAAC,CAAC;IAEH,kDAAkD;IAClD,2EAA2E;IAC3E,MAAM,eAAe,GAAG,0CAA0C,CAAC;IACnE,6DAA6D;IAE7D,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;GAEG;AACH,SAAS,gBAAgB,CAAC,MAAc,EAAE,UAAoB;IAC5D,IAAI,MAAM,GAAG,MAAM,CAAC;IACpB,IAAI,UAAU,GAAG,KAAK,CAAC;IAEvB,gDAAgD;IAChD,MAAM,UAAU,GAAG,sBAAsB,CAAC;IAE1C,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;QACjD,MAAM,QAAQ,GAAG,QAAQ,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;QACnC,MAAM,IAAI,GAAG,MAAM,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC;QAC3C,UAAU,GAAG,IAAI,CAAC;QAClB,OAAO,IAAI,CAAC;IACd,CAAC,CAAC,CAAC;IAEH,IAAI,UAAU,EAAE,CAAC;QACf,UAAU,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IAChC,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;GAEG;AACH,SAAS,oBAAoB,CAAC,MAAc,EAAE,UAAoB;IAChE,IAAI,MAAM,GAAG,MAAM,CAAC;IACpB,IAAI,UAAU,GAAG,KAAK,CAAC;IAEvB,oDAAoD;IACpD,MAAM,cAAc,GAAG,sBAAsB,CAAC;IAE9C,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,cAAc,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;QACrD,MAAM,QAAQ,GAAG,QAAQ,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;QACnC,MAAM,IAAI,GAAG,MAAM,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC;QAC3C,UAAU,GAAG,IAAI,CAAC;QAClB,OAAO,IAAI,CAAC;IACd,CAAC,CAAC,CAAC;IAEH,IAAI,UAAU,EAAE,CAAC;QACf,UAAU,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;IACpC,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/scanners/index.ts"],"names":[],"mappings":"AAAA,sCAAsC;AAEtC,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,SAAS,EAAE,eAAe,EAAE,SAAS,EAAkB,MAAM,mBAAmB,CAAC;AAsDxH,wBAAsB,WAAW,CAAC,OAAO,EAAE,WAAW,GAAG;IAAE,EAAE,CAAC,EAAE,SAAS,CAAA;CAAE,GAAG,OAAO,CAAC,UAAU,CAAC,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/scanners/index.ts"],"names":[],"mappings":"AAAA,sCAAsC;AAEtC,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,SAAS,EAAE,eAAe,EAAE,SAAS,EAAkB,MAAM,mBAAmB,CAAC;AAsDxH,wBAAsB,WAAW,CAAC,OAAO,EAAE,WAAW,GAAG;IAAE,EAAE,CAAC,EAAE,SAAS,CAAA;CAAE,GAAG,OAAO,CAAC,UAAU,CAAC,CAqBhG;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,WAAW,GAAG,UAAU,CAIhE;AAED,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,UAAU,CAS5D;AAED,wBAAgB,UAAU,CAAC,MAAM,EAAE,UAAU,EAAE,SAAS,CAAC,EAAE,SAAS,GAAG,OAAO,CAM7E;AAED,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,eAAe,EAAE,EAAE,QAAQ,EAAE,SAAS,GAAG,eAAe,EAAE,CAQrG"}
|
package/dist/scanners/index.js
CHANGED
|
@@ -86,7 +86,7 @@ function aggregateResults(analyses, startTime) {
|
|
|
86
86
|
}
|
|
87
87
|
async function scanProject(options) {
|
|
88
88
|
const startTime = Date.now();
|
|
89
|
-
const analyses = (0, lifecycle_js_1.scanInstalledPackages)(options.path, options.includeDev);
|
|
89
|
+
const analyses = (0, lifecycle_js_1.scanInstalledPackages)(options.path, options.includeDev, { ast: options.ast, deobfuscate: options.deobfuscate });
|
|
90
90
|
let result = aggregateResults(analyses, startTime);
|
|
91
91
|
// Phase 2: AI analysis (opt-in)
|
|
92
92
|
if (options.ai?.enabled) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/scanners/index.ts"],"names":[],"mappings":";AAAA,sCAAsC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwDtC,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/scanners/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,8 +3,14 @@ import type { PackageAnalysis, RiskLevel } from '../types/index.js';
|
|
|
3
3
|
declare function extractLifecycleScripts(scripts: Record<string, string>): Record<string, string>;
|
|
4
4
|
declare function calculateRiskScore(findings: PackageAnalysis['findings']): number;
|
|
5
5
|
declare function riskLevelFromScore(score: number): RiskLevel;
|
|
6
|
-
export declare function analyzePackage(name: string, version: string, scripts: Record<string, string
|
|
7
|
-
|
|
6
|
+
export declare function analyzePackage(name: string, version: string, scripts: Record<string, string>, options?: {
|
|
7
|
+
ast?: boolean;
|
|
8
|
+
deobfuscate?: boolean;
|
|
9
|
+
}): PackageAnalysis;
|
|
10
|
+
export declare function scanInstalledPackages(projectPath: string, includeDev?: boolean, options?: {
|
|
11
|
+
ast?: boolean;
|
|
12
|
+
deobfuscate?: boolean;
|
|
13
|
+
}): PackageAnalysis[];
|
|
8
14
|
export declare function scanSinglePackage(pkgJsonContent: string): PackageAnalysis;
|
|
9
15
|
export { extractLifecycleScripts, calculateRiskScore, riskLevelFromScore };
|
|
10
16
|
//# sourceMappingURL=lifecycle.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lifecycle.d.ts","sourceRoot":"","sources":["../../src/scanners/lifecycle.ts"],"names":[],"mappings":"AAAA,yFAAyF;AAIzF,OAAO,KAAK,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"lifecycle.d.ts","sourceRoot":"","sources":["../../src/scanners/lifecycle.ts"],"names":[],"mappings":"AAAA,yFAAyF;AAIzF,OAAO,KAAK,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAsCpE,iBAAS,uBAAuB,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAQxF;AAmGD,iBAAS,kBAAkB,CAAC,QAAQ,EAAE,eAAe,CAAC,UAAU,CAAC,GAAG,MAAM,CAKzE;AAED,iBAAS,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,CAKpD;AAED,wBAAgB,cAAc,CAC5B,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC/B,OAAO,CAAC,EAAE;IAAE,GAAG,CAAC,EAAE,OAAO,CAAC;IAAC,WAAW,CAAC,EAAE,OAAO,CAAA;CAAE,GACjD,eAAe,CAgCjB;AAED,wBAAgB,qBAAqB,CACnC,WAAW,EAAE,MAAM,EACnB,UAAU,UAAQ,EAClB,OAAO,CAAC,EAAE;IAAE,GAAG,CAAC,EAAE,OAAO,CAAC;IAAC,WAAW,CAAC,EAAE,OAAO,CAAA;CAAE,GACjD,eAAe,EAAE,CAuDnB;AAED,wBAAgB,iBAAiB,CAAC,cAAc,EAAE,MAAM,GAAG,eAAe,CAOzE;AAED,OAAO,EAAE,uBAAuB,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,CAAC"}
|
|
@@ -43,6 +43,8 @@ exports.riskLevelFromScore = riskLevelFromScore;
|
|
|
43
43
|
const fs = __importStar(require("node:fs"));
|
|
44
44
|
const path = __importStar(require("node:path"));
|
|
45
45
|
const patterns_js_1 = require("./patterns.js");
|
|
46
|
+
const ast_js_1 = require("./ast.js");
|
|
47
|
+
const deobfuscation_js_1 = require("./deobfuscation.js");
|
|
46
48
|
const LIFECYCLE_SCRIPTS = [
|
|
47
49
|
'preinstall',
|
|
48
50
|
'install',
|
|
@@ -83,8 +85,9 @@ function extractLifecycleScripts(scripts) {
|
|
|
83
85
|
}
|
|
84
86
|
return result;
|
|
85
87
|
}
|
|
86
|
-
function analyzeScriptContent(packageName, version, scriptName, scriptContent) {
|
|
88
|
+
function analyzeScriptContent(packageName, version, scriptName, scriptContent, options) {
|
|
87
89
|
const findings = [];
|
|
90
|
+
// Layer 1: Regex pattern matching (existing)
|
|
88
91
|
for (const rule of patterns_js_1.PATTERN_RULES) {
|
|
89
92
|
const match = rule.pattern.exec(scriptContent);
|
|
90
93
|
if (match) {
|
|
@@ -99,6 +102,61 @@ function analyzeScriptContent(packageName, version, scriptName, scriptContent) {
|
|
|
99
102
|
});
|
|
100
103
|
}
|
|
101
104
|
}
|
|
105
|
+
// Layer 2: AST analysis (only if regex found something AND AST is enabled)
|
|
106
|
+
if (findings.length > 0 && options?.ast !== false) {
|
|
107
|
+
try {
|
|
108
|
+
const astFindings = (0, ast_js_1.analyzeScriptAST)(scriptContent);
|
|
109
|
+
if (astFindings.length > 0) {
|
|
110
|
+
// Add AST findings
|
|
111
|
+
findings.push(...astFindings.map((f) => ({
|
|
112
|
+
package: packageName,
|
|
113
|
+
scriptName,
|
|
114
|
+
scriptContent,
|
|
115
|
+
pattern: f.pattern,
|
|
116
|
+
description: f.description,
|
|
117
|
+
riskLevel: f.riskLevel,
|
|
118
|
+
match: f.match,
|
|
119
|
+
})));
|
|
120
|
+
// Add AST findings metadata to first regex finding
|
|
121
|
+
if (findings.length > 0) {
|
|
122
|
+
findings[0].astFindings = astFindings;
|
|
123
|
+
}
|
|
124
|
+
// Layer 3: Deobfuscation (only if AST found something AND deobfuscation is enabled)
|
|
125
|
+
const deobf = options?.deobfuscate === false
|
|
126
|
+
? { deobfuscated: scriptContent, iterations: 0, techniques: [], success: false }
|
|
127
|
+
: (0, deobfuscation_js_1.deobfuscateScript)(scriptContent);
|
|
128
|
+
if (deobf.success && deobf.iterations > 0) {
|
|
129
|
+
// Mark all findings with deobfuscation metadata
|
|
130
|
+
for (const f of findings) {
|
|
131
|
+
f.deobfuscation = deobf;
|
|
132
|
+
}
|
|
133
|
+
// Re-analyze deobfuscated code (recursive call with deobfuscated content)
|
|
134
|
+
// This catches patterns that were hidden by encoding
|
|
135
|
+
try {
|
|
136
|
+
const deobfFindings = analyzeScriptContent(packageName, version, scriptName, deobf.deobfuscated);
|
|
137
|
+
// Add deobfuscated findings if they're different
|
|
138
|
+
for (const deobfFinding of deobfFindings) {
|
|
139
|
+
// Check if this pattern was already found
|
|
140
|
+
const alreadyFound = findings.some((f) => f.pattern === deobfFinding.pattern);
|
|
141
|
+
if (!alreadyFound) {
|
|
142
|
+
// Mark as found via deobfuscation
|
|
143
|
+
deobfFinding.pattern = `${deobfFinding.pattern}-deobfuscated`;
|
|
144
|
+
findings.push(deobfFinding);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
// Recursive analysis failed — continue with original findings
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
catch (error) {
|
|
155
|
+
// AST/deobfuscation failed — continue with regex-only
|
|
156
|
+
// Log warning but don't break the scan
|
|
157
|
+
console.warn(`AST/deobfuscation analysis failed for ${packageName}:${scriptName}: ${error.message}`);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
102
160
|
return findings;
|
|
103
161
|
}
|
|
104
162
|
function calculateRiskScore(findings) {
|
|
@@ -117,11 +175,11 @@ function riskLevelFromScore(score) {
|
|
|
117
175
|
return 'medium';
|
|
118
176
|
return 'low';
|
|
119
177
|
}
|
|
120
|
-
function analyzePackage(name, version, scripts) {
|
|
178
|
+
function analyzePackage(name, version, scripts, options) {
|
|
121
179
|
const lifecycleScripts = extractLifecycleScripts(scripts);
|
|
122
180
|
const allFindings = [];
|
|
123
181
|
for (const [scriptName, scriptContent] of Object.entries(lifecycleScripts)) {
|
|
124
|
-
const scriptFindings = analyzeScriptContent(name, version, scriptName, scriptContent);
|
|
182
|
+
const scriptFindings = analyzeScriptContent(name, version, scriptName, scriptContent, options);
|
|
125
183
|
allFindings.push(...scriptFindings);
|
|
126
184
|
// Flag any lifecycle script that exists without findings as "low" info
|
|
127
185
|
if (scriptFindings.length === 0 && ['postinstall', 'preinstall', 'install'].includes(scriptName)) {
|
|
@@ -146,7 +204,7 @@ function analyzePackage(name, version, scripts) {
|
|
|
146
204
|
riskLevel: riskLevelFromScore(riskScore),
|
|
147
205
|
};
|
|
148
206
|
}
|
|
149
|
-
function scanInstalledPackages(projectPath, includeDev = false) {
|
|
207
|
+
function scanInstalledPackages(projectPath, includeDev = false, options) {
|
|
150
208
|
const nodeModulesPath = path.join(projectPath, 'node_modules');
|
|
151
209
|
if (!fs.existsSync(nodeModulesPath)) {
|
|
152
210
|
throw new Error(`No node_modules found at ${nodeModulesPath}`);
|
|
@@ -174,7 +232,7 @@ function scanInstalledPackages(projectPath, includeDev = false) {
|
|
|
174
232
|
continue;
|
|
175
233
|
visited.add(pkgKey);
|
|
176
234
|
if (pkgJson.scripts && Object.keys(pkgJson.scripts).length > 0) {
|
|
177
|
-
analyses.push(analyzePackage(pkgJson.name || entry.name, pkgJson.version || 'unknown', pkgJson.scripts));
|
|
235
|
+
analyses.push(analyzePackage(pkgJson.name || entry.name, pkgJson.version || 'unknown', pkgJson.scripts, options));
|
|
178
236
|
}
|
|
179
237
|
}
|
|
180
238
|
catch {
|