@aspect-guard/core 0.5.0 → 0.5.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/dist/index.d.ts +78 -2
- package/dist/index.js +151 -118
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -160,6 +160,11 @@ declare class ExtensionGuardScanner {
|
|
|
160
160
|
private hashDatabase;
|
|
161
161
|
constructor(options?: Partial<ScanOptions>);
|
|
162
162
|
scan(options?: Partial<ScanOptions>): Promise<FullScanReport>;
|
|
163
|
+
/**
|
|
164
|
+
* Scan multiple extensions concurrently with a configurable pool size.
|
|
165
|
+
* Uses a simple semaphore pattern to limit concurrent operations.
|
|
166
|
+
*/
|
|
167
|
+
private scanExtensionsConcurrently;
|
|
163
168
|
private scanExtension;
|
|
164
169
|
private calculateTrustScore;
|
|
165
170
|
private calculateRiskLevel;
|
|
@@ -260,6 +265,77 @@ declare class RuleRegistry {
|
|
|
260
265
|
}
|
|
261
266
|
declare const ruleRegistry: RuleRegistry;
|
|
262
267
|
|
|
268
|
+
/**
|
|
269
|
+
* Shared pattern matching utilities for detection rules.
|
|
270
|
+
* Reduces code duplication across rules and improves maintainability.
|
|
271
|
+
*/
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Pattern definition for matching in file content
|
|
275
|
+
*/
|
|
276
|
+
interface MatchPattern {
|
|
277
|
+
/** Name of the pattern for evidence reporting */
|
|
278
|
+
name: string;
|
|
279
|
+
/** Regex pattern (should have 'g' flag for multiple matches) */
|
|
280
|
+
pattern: RegExp;
|
|
281
|
+
/** If specified, use this capture group for the matched value */
|
|
282
|
+
matchGroup?: number;
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Options for file filtering
|
|
286
|
+
*/
|
|
287
|
+
interface FileFilterOptions {
|
|
288
|
+
/** File extensions to include (e.g., ['.js', '.ts']) */
|
|
289
|
+
extensions?: string[];
|
|
290
|
+
/** Regex patterns to exclude files */
|
|
291
|
+
excludePatterns?: RegExp[];
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Options for pattern matching
|
|
295
|
+
*/
|
|
296
|
+
interface MatchOptions {
|
|
297
|
+
/** Maximum length for snippet in evidence */
|
|
298
|
+
maxSnippetLength?: number;
|
|
299
|
+
/** Function to validate matched value (return false to skip) */
|
|
300
|
+
validate?: (value: string, content: string, matchIndex: number) => boolean;
|
|
301
|
+
}
|
|
302
|
+
/** Default code file extensions */
|
|
303
|
+
declare const CODE_EXTENSIONS: string[];
|
|
304
|
+
/** Simple JS/TS extensions */
|
|
305
|
+
declare const JS_TS_EXTENSIONS: string[];
|
|
306
|
+
/**
|
|
307
|
+
* Check if a file should be processed based on extension
|
|
308
|
+
*/
|
|
309
|
+
declare function hasExtension(filePath: string, extensions: string[]): boolean;
|
|
310
|
+
/**
|
|
311
|
+
* Check if a file matches any exclusion pattern
|
|
312
|
+
*/
|
|
313
|
+
declare function isExcluded(filePath: string, patterns: RegExp[]): boolean;
|
|
314
|
+
/**
|
|
315
|
+
* Get line number from content index (1-based)
|
|
316
|
+
*/
|
|
317
|
+
declare function getLineNumber(content: string, index: number): number;
|
|
318
|
+
/**
|
|
319
|
+
* Get line content at a specific line number (1-based)
|
|
320
|
+
*/
|
|
321
|
+
declare function getLineContent(lines: string[], lineNumber: number): string;
|
|
322
|
+
/**
|
|
323
|
+
* Check if an index position is inside a comment
|
|
324
|
+
*/
|
|
325
|
+
declare function isInComment(content: string, matchIndex: number): boolean;
|
|
326
|
+
/**
|
|
327
|
+
* Match all patterns in a single file and generate evidence
|
|
328
|
+
*/
|
|
329
|
+
declare function matchPatternsInFile(filePath: string, content: string, patterns: MatchPattern[], options?: MatchOptions): Evidence[];
|
|
330
|
+
/**
|
|
331
|
+
* Match patterns across all files with filtering
|
|
332
|
+
*/
|
|
333
|
+
declare function matchPatternsInFiles(files: Map<string, string>, patterns: MatchPattern[], filterOptions?: FileFilterOptions, matchOptions?: MatchOptions): Evidence[];
|
|
334
|
+
/**
|
|
335
|
+
* Check if content within a context window matches a pattern
|
|
336
|
+
*/
|
|
337
|
+
declare function hasPatternInContext(content: string, matchIndex: number, matchLength: number, contextPattern: RegExp, contextWindow?: number): boolean;
|
|
338
|
+
|
|
263
339
|
declare function registerBuiltInRules(): void;
|
|
264
340
|
|
|
265
341
|
/**
|
|
@@ -747,6 +823,6 @@ declare function generateBaseline(_extensionPaths: string[], _outputPath?: strin
|
|
|
747
823
|
errors: string[];
|
|
748
824
|
}>;
|
|
749
825
|
|
|
750
|
-
declare const VERSION = "0.5.
|
|
826
|
+
declare const VERSION = "0.5.2";
|
|
751
827
|
|
|
752
|
-
export { ALL_POPULAR_EXTENSIONS, type AdjustFindingsOptions, type AuditReport, type BundleDetectionResult, DETECTION_RULES, type DetectedIDE, type DetectionRule, type Evidence, type ExtensionCategory, ExtensionGuardScanner, type ExtensionHash, type ExtensionInfo, type ExtensionManifest, type Finding, type FindingCategory, type FullScanReport, type HashDatabase, IDE_PATHS, type InspectOptions, type IntegrityInfo, type IntegrityResult, type IntegrityStatus, JsonReporter, MEGA_POPULAR_EXTENSIONS, MarkdownReporter, POPULAR_EXTENSIONS, type PolicyAction, type PolicyConfig, PolicyEngine, type PolicyRules, type PolicyViolation, type PopularExtension, type Reporter, type ReporterOptions, type RiskLevel, RuleEngine, type RuleEngineOptions, SEVERITY_ORDER, SarifReporter, type ScanOptions, type ScanResult, type ScanSummary, type Severity, TRUSTED_EXTENSION_IDS, TRUSTED_PUBLISHERS, VERIFIED_PUBLISHERS, VERSION, addHash, adjustFindings, categorizeExtension, clearHashCache, collectFiles, compareSeverity, computeExtensionHashes, createHashRecord, detectBundle, detectIDEPaths, expandPath, generateBaseline, getDefaultDatabasePath, getHash, getIDEExtensionPath, getPopularityTier, getSupportedIDEs, isAtLeastSeverity, isBundleOutputPath, isIDEInstalled, isMegaPopular, isPopular, isTrustedExtension, isTrustedPublisher, isVerifiedPublisher, loadHashDatabase, loadPolicyConfig, readExtension, readExtensionsFromDirectory, registerBuiltInRules, ruleRegistry, saveHashDatabase, sha256, shouldCollectFile, shouldReduceSeverityForBundle, verifyIntegrity };
|
|
828
|
+
export { ALL_POPULAR_EXTENSIONS, type AdjustFindingsOptions, type AuditReport, type BundleDetectionResult, CODE_EXTENSIONS, DETECTION_RULES, type DetectedIDE, type DetectionRule, type Evidence, type ExtensionCategory, ExtensionGuardScanner, type ExtensionHash, type ExtensionInfo, type ExtensionManifest, type FileFilterOptions, type Finding, type FindingCategory, type FullScanReport, type HashDatabase, IDE_PATHS, type InspectOptions, type IntegrityInfo, type IntegrityResult, type IntegrityStatus, JS_TS_EXTENSIONS, JsonReporter, MEGA_POPULAR_EXTENSIONS, MarkdownReporter, type MatchOptions, type MatchPattern, POPULAR_EXTENSIONS, type PolicyAction, type PolicyConfig, PolicyEngine, type PolicyRules, type PolicyViolation, type PopularExtension, type Reporter, type ReporterOptions, type RiskLevel, RuleEngine, type RuleEngineOptions, SEVERITY_ORDER, SarifReporter, type ScanOptions, type ScanResult, type ScanSummary, type Severity, TRUSTED_EXTENSION_IDS, TRUSTED_PUBLISHERS, VERIFIED_PUBLISHERS, VERSION, addHash, adjustFindings, categorizeExtension, clearHashCache, collectFiles, compareSeverity, computeExtensionHashes, createHashRecord, detectBundle, detectIDEPaths, expandPath, generateBaseline, getDefaultDatabasePath, getHash, getIDEExtensionPath, getLineContent, getLineNumber, getPopularityTier, getSupportedIDEs, hasExtension, hasPatternInContext, isAtLeastSeverity, isBundleOutputPath, isExcluded, isIDEInstalled, isInComment, isMegaPopular, isPopular, isTrustedExtension, isTrustedPublisher, isVerifiedPublisher, loadHashDatabase, loadPolicyConfig, matchPatternsInFile, matchPatternsInFiles, readExtension, readExtensionsFromDirectory, registerBuiltInRules, ruleRegistry, saveHashDatabase, sha256, shouldCollectFile, shouldReduceSeverityForBundle, verifyIntegrity };
|
package/dist/index.js
CHANGED
|
@@ -1110,8 +1110,89 @@ var critDataExfiltration = {
|
|
|
1110
1110
|
}
|
|
1111
1111
|
};
|
|
1112
1112
|
|
|
1113
|
+
// src/rules/pattern-matcher.ts
|
|
1114
|
+
var CODE_EXTENSIONS = [".js", ".ts", ".jsx", ".tsx", ".mjs", ".cjs"];
|
|
1115
|
+
var JS_TS_EXTENSIONS = [".js", ".ts"];
|
|
1116
|
+
function hasExtension(filePath, extensions) {
|
|
1117
|
+
return extensions.some((ext) => filePath.endsWith(ext));
|
|
1118
|
+
}
|
|
1119
|
+
function isExcluded(filePath, patterns) {
|
|
1120
|
+
return patterns.some((pattern) => pattern.test(filePath));
|
|
1121
|
+
}
|
|
1122
|
+
function getLineNumber(content, index) {
|
|
1123
|
+
return content.slice(0, index).split("\n").length;
|
|
1124
|
+
}
|
|
1125
|
+
function getLineContent(lines, lineNumber) {
|
|
1126
|
+
return lines[lineNumber - 1]?.trim() ?? "";
|
|
1127
|
+
}
|
|
1128
|
+
function isInComment(content, matchIndex) {
|
|
1129
|
+
const lineStart = content.lastIndexOf("\n", matchIndex) + 1;
|
|
1130
|
+
const lineContent = content.slice(lineStart, matchIndex);
|
|
1131
|
+
if (lineContent.includes("//")) {
|
|
1132
|
+
return true;
|
|
1133
|
+
}
|
|
1134
|
+
const beforeMatch = content.slice(0, matchIndex);
|
|
1135
|
+
const lastBlockStart = beforeMatch.lastIndexOf("/*");
|
|
1136
|
+
const lastBlockEnd = beforeMatch.lastIndexOf("*/");
|
|
1137
|
+
return lastBlockStart > lastBlockEnd;
|
|
1138
|
+
}
|
|
1139
|
+
function matchPatternsInFile(filePath, content, patterns, options = {}) {
|
|
1140
|
+
const evidences = [];
|
|
1141
|
+
const lines = content.split("\n");
|
|
1142
|
+
const maxSnippetLength = options.maxSnippetLength ?? 100;
|
|
1143
|
+
for (const { name, pattern, matchGroup } of patterns) {
|
|
1144
|
+
pattern.lastIndex = 0;
|
|
1145
|
+
let match;
|
|
1146
|
+
while ((match = pattern.exec(content)) !== null) {
|
|
1147
|
+
const matchIndex = match.index;
|
|
1148
|
+
const matchedValue = matchGroup !== void 0 ? match[matchGroup] : match[0];
|
|
1149
|
+
if (!matchedValue) {
|
|
1150
|
+
continue;
|
|
1151
|
+
}
|
|
1152
|
+
if (options.validate && !options.validate(matchedValue, content, matchIndex)) {
|
|
1153
|
+
continue;
|
|
1154
|
+
}
|
|
1155
|
+
const lineNumber = getLineNumber(content, matchIndex);
|
|
1156
|
+
const lineContent = getLineContent(lines, lineNumber);
|
|
1157
|
+
evidences.push({
|
|
1158
|
+
filePath,
|
|
1159
|
+
lineNumber,
|
|
1160
|
+
lineContent: lineContent.length > maxSnippetLength ? lineContent.slice(0, maxSnippetLength) + "..." : lineContent,
|
|
1161
|
+
matchedPattern: name,
|
|
1162
|
+
snippet: matchedValue.length > maxSnippetLength ? matchedValue.slice(0, maxSnippetLength) + "..." : matchedValue
|
|
1163
|
+
});
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
return evidences;
|
|
1167
|
+
}
|
|
1168
|
+
function matchPatternsInFiles(files, patterns, filterOptions = {}, matchOptions = {}) {
|
|
1169
|
+
const evidences = [];
|
|
1170
|
+
const extensions = filterOptions.extensions ?? JS_TS_EXTENSIONS;
|
|
1171
|
+
const excludePatterns = filterOptions.excludePatterns ?? [];
|
|
1172
|
+
for (const [filePath, content] of files) {
|
|
1173
|
+
if (!hasExtension(filePath, extensions)) {
|
|
1174
|
+
continue;
|
|
1175
|
+
}
|
|
1176
|
+
if (excludePatterns.length > 0 && isExcluded(filePath, excludePatterns)) {
|
|
1177
|
+
continue;
|
|
1178
|
+
}
|
|
1179
|
+
if (!content || content.trim().length === 0) {
|
|
1180
|
+
continue;
|
|
1181
|
+
}
|
|
1182
|
+
const fileEvidences = matchPatternsInFile(filePath, content, patterns, matchOptions);
|
|
1183
|
+
evidences.push(...fileEvidences);
|
|
1184
|
+
}
|
|
1185
|
+
return evidences;
|
|
1186
|
+
}
|
|
1187
|
+
function hasPatternInContext(content, matchIndex, matchLength, contextPattern, contextWindow = 200) {
|
|
1188
|
+
const startIndex = Math.max(0, matchIndex - contextWindow);
|
|
1189
|
+
const endIndex = Math.min(content.length, matchIndex + matchLength + contextWindow);
|
|
1190
|
+
const context = content.slice(startIndex, endIndex);
|
|
1191
|
+
return contextPattern.test(context);
|
|
1192
|
+
}
|
|
1193
|
+
|
|
1113
1194
|
// src/rules/built-in/crit-remote-execution.ts
|
|
1114
|
-
var
|
|
1195
|
+
var PATTERNS = [
|
|
1115
1196
|
{ name: "eval", pattern: /\beval\s*\(/g },
|
|
1116
1197
|
{ name: "Function-constructor", pattern: /new\s+Function\s*\(/g },
|
|
1117
1198
|
{
|
|
@@ -1124,9 +1205,9 @@ var DANGEROUS_PATTERNS = [
|
|
|
1124
1205
|
},
|
|
1125
1206
|
{ name: "child_process-spawn-shell", pattern: /\.spawn\s*\([^)]*\{[^}]*shell\s*:\s*true/g },
|
|
1126
1207
|
{ name: "vm-runInContext", pattern: /vm\.run(?:InContext|InNewContext|InThisContext)\s*\(/g },
|
|
1127
|
-
{ name: "vm-Script", pattern: /new\s+vm\.Script\s*\(/g }
|
|
1208
|
+
{ name: "vm-Script", pattern: /new\s+vm\.Script\s*\(/g },
|
|
1209
|
+
{ name: "dynamic-require", pattern: /require\s*\(\s*(?:[^'"`\s)]|`[^`]*\$\{)/g }
|
|
1128
1210
|
];
|
|
1129
|
-
var DYNAMIC_REQUIRE = /require\s*\(\s*(?:[^'"`\s)]|`[^`]*\$\{)/g;
|
|
1130
1211
|
var critRemoteExecution = {
|
|
1131
1212
|
id: "EG-CRIT-002",
|
|
1132
1213
|
name: "Remote Code Execution",
|
|
@@ -1136,40 +1217,7 @@ var critRemoteExecution = {
|
|
|
1136
1217
|
mitreAttackId: "T1059",
|
|
1137
1218
|
enabled: true,
|
|
1138
1219
|
detect(files, _manifest) {
|
|
1139
|
-
|
|
1140
|
-
for (const [filePath, content] of files) {
|
|
1141
|
-
if (!filePath.endsWith(".js") && !filePath.endsWith(".ts")) {
|
|
1142
|
-
continue;
|
|
1143
|
-
}
|
|
1144
|
-
const lines = content.split("\n");
|
|
1145
|
-
for (const { name, pattern } of DANGEROUS_PATTERNS) {
|
|
1146
|
-
pattern.lastIndex = 0;
|
|
1147
|
-
let match2;
|
|
1148
|
-
while ((match2 = pattern.exec(content)) !== null) {
|
|
1149
|
-
const lineNumber = content.slice(0, match2.index).split("\n").length;
|
|
1150
|
-
evidences.push({
|
|
1151
|
-
filePath,
|
|
1152
|
-
lineNumber,
|
|
1153
|
-
lineContent: lines[lineNumber - 1]?.trim(),
|
|
1154
|
-
matchedPattern: name,
|
|
1155
|
-
snippet: match2[0]
|
|
1156
|
-
});
|
|
1157
|
-
}
|
|
1158
|
-
}
|
|
1159
|
-
DYNAMIC_REQUIRE.lastIndex = 0;
|
|
1160
|
-
let match;
|
|
1161
|
-
while ((match = DYNAMIC_REQUIRE.exec(content)) !== null) {
|
|
1162
|
-
const lineNumber = content.slice(0, match.index).split("\n").length;
|
|
1163
|
-
evidences.push({
|
|
1164
|
-
filePath,
|
|
1165
|
-
lineNumber,
|
|
1166
|
-
lineContent: lines[lineNumber - 1]?.trim(),
|
|
1167
|
-
matchedPattern: "dynamic-require",
|
|
1168
|
-
snippet: match[0]
|
|
1169
|
-
});
|
|
1170
|
-
}
|
|
1171
|
-
}
|
|
1172
|
-
return evidences;
|
|
1220
|
+
return matchPatternsInFiles(files, PATTERNS);
|
|
1173
1221
|
}
|
|
1174
1222
|
};
|
|
1175
1223
|
|
|
@@ -1199,41 +1247,40 @@ var critCredentialAccess = {
|
|
|
1199
1247
|
mitreAttackId: "T1552.004",
|
|
1200
1248
|
enabled: true,
|
|
1201
1249
|
detect(files, _manifest) {
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1250
|
+
return matchPatternsInFiles(
|
|
1251
|
+
files,
|
|
1252
|
+
SENSITIVE_PATHS,
|
|
1253
|
+
{},
|
|
1254
|
+
{
|
|
1255
|
+
validate: (value, content, matchIndex) => hasPatternInContext(content, matchIndex, value.length, FILE_READ_CONTEXT, 200)
|
|
1206
1256
|
}
|
|
1207
|
-
|
|
1208
|
-
for (const { name, pattern } of SENSITIVE_PATHS) {
|
|
1209
|
-
pattern.lastIndex = 0;
|
|
1210
|
-
let match;
|
|
1211
|
-
while ((match = pattern.exec(content)) !== null) {
|
|
1212
|
-
const startIndex = Math.max(0, match.index - 200);
|
|
1213
|
-
const endIndex = Math.min(content.length, match.index + match[0].length + 200);
|
|
1214
|
-
const context = content.slice(startIndex, endIndex);
|
|
1215
|
-
if (FILE_READ_CONTEXT.test(context)) {
|
|
1216
|
-
const lineNumber = content.slice(0, match.index).split("\n").length;
|
|
1217
|
-
evidences.push({
|
|
1218
|
-
filePath,
|
|
1219
|
-
lineNumber,
|
|
1220
|
-
lineContent: lines[lineNumber - 1]?.trim(),
|
|
1221
|
-
matchedPattern: name,
|
|
1222
|
-
snippet: match[0]
|
|
1223
|
-
});
|
|
1224
|
-
}
|
|
1225
|
-
}
|
|
1226
|
-
}
|
|
1227
|
-
}
|
|
1228
|
-
return evidences;
|
|
1257
|
+
);
|
|
1229
1258
|
}
|
|
1230
1259
|
};
|
|
1231
1260
|
|
|
1232
1261
|
// src/rules/built-in/high-suspicious-network.ts
|
|
1233
|
-
var
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1262
|
+
var PATTERNS2 = [
|
|
1263
|
+
// HTTP requests to IP addresses instead of domains
|
|
1264
|
+
{
|
|
1265
|
+
name: "http-to-ip",
|
|
1266
|
+
pattern: /(?:fetch|axios(?:\.(?:get|post|put|delete|request))?|https?\.(?:get|post|request)|XMLHttpRequest)\s*\([^)]*['"`]https?:\/\/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/g
|
|
1267
|
+
},
|
|
1268
|
+
// Dynamic URL construction (template literals or concatenation)
|
|
1269
|
+
{
|
|
1270
|
+
name: "dynamic-url",
|
|
1271
|
+
pattern: /(?:fetch|axios|https?\.request)\s*\(\s*(?:`[^`]*\$\{|['"][^'"]*['"]\s*\+\s*\w)/g
|
|
1272
|
+
},
|
|
1273
|
+
// WebSocket connections to IP addresses
|
|
1274
|
+
{
|
|
1275
|
+
name: "websocket-to-ip",
|
|
1276
|
+
pattern: /new\s+WebSocket\s*\(\s*['"`]wss?:\/\/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/g
|
|
1277
|
+
},
|
|
1278
|
+
// Unusual ports
|
|
1279
|
+
{
|
|
1280
|
+
name: "unusual-port",
|
|
1281
|
+
pattern: /['"`]https?:\/\/[^'"`:]+:(?!443|80|8080|3000|8443|5000)[0-9]{2,5}/g
|
|
1282
|
+
}
|
|
1283
|
+
];
|
|
1237
1284
|
var highSuspiciousNetwork = {
|
|
1238
1285
|
id: "EG-HIGH-002",
|
|
1239
1286
|
name: "Suspicious Network Activity",
|
|
@@ -1243,34 +1290,7 @@ var highSuspiciousNetwork = {
|
|
|
1243
1290
|
mitreAttackId: "T1071",
|
|
1244
1291
|
enabled: true,
|
|
1245
1292
|
detect(files, _manifest) {
|
|
1246
|
-
|
|
1247
|
-
for (const [filePath, content] of files) {
|
|
1248
|
-
if (!filePath.endsWith(".js") && !filePath.endsWith(".ts")) {
|
|
1249
|
-
continue;
|
|
1250
|
-
}
|
|
1251
|
-
const lines = content.split("\n");
|
|
1252
|
-
const patterns = [
|
|
1253
|
-
{ pattern: HTTP_TO_IP, name: "http-to-ip" },
|
|
1254
|
-
{ pattern: DYNAMIC_URL, name: "dynamic-url" },
|
|
1255
|
-
{ pattern: WEBSOCKET_TO_IP, name: "websocket-to-ip" },
|
|
1256
|
-
{ pattern: UNUSUAL_PORTS, name: "unusual-port" }
|
|
1257
|
-
];
|
|
1258
|
-
for (const { pattern, name } of patterns) {
|
|
1259
|
-
pattern.lastIndex = 0;
|
|
1260
|
-
let match;
|
|
1261
|
-
while ((match = pattern.exec(content)) !== null) {
|
|
1262
|
-
const lineNumber = content.slice(0, match.index).split("\n").length;
|
|
1263
|
-
evidences.push({
|
|
1264
|
-
filePath,
|
|
1265
|
-
lineNumber,
|
|
1266
|
-
lineContent: lines[lineNumber - 1]?.trim(),
|
|
1267
|
-
matchedPattern: name,
|
|
1268
|
-
snippet: match[0].slice(0, 100)
|
|
1269
|
-
});
|
|
1270
|
-
}
|
|
1271
|
-
}
|
|
1272
|
-
}
|
|
1273
|
-
return evidences;
|
|
1293
|
+
return matchPatternsInFiles(files, PATTERNS2);
|
|
1274
1294
|
}
|
|
1275
1295
|
};
|
|
1276
1296
|
|
|
@@ -1496,23 +1516,6 @@ function calculateSecretEntropy(str) {
|
|
|
1496
1516
|
}
|
|
1497
1517
|
return entropy;
|
|
1498
1518
|
}
|
|
1499
|
-
function isInComment(content, matchIndex) {
|
|
1500
|
-
const lineStart = content.lastIndexOf("\n", matchIndex) + 1;
|
|
1501
|
-
const lineContent = content.slice(lineStart, matchIndex);
|
|
1502
|
-
if (lineContent.includes("//")) {
|
|
1503
|
-
return true;
|
|
1504
|
-
}
|
|
1505
|
-
const beforeMatch = content.slice(0, matchIndex);
|
|
1506
|
-
const lastBlockStart = beforeMatch.lastIndexOf("/*");
|
|
1507
|
-
const lastBlockEnd = beforeMatch.lastIndexOf("*/");
|
|
1508
|
-
if (lastBlockStart > lastBlockEnd) {
|
|
1509
|
-
return true;
|
|
1510
|
-
}
|
|
1511
|
-
return false;
|
|
1512
|
-
}
|
|
1513
|
-
function getLineNumber(content, index) {
|
|
1514
|
-
return content.slice(0, index).split("\n").length;
|
|
1515
|
-
}
|
|
1516
1519
|
var highHardcodedSecret = {
|
|
1517
1520
|
id: "EG-HIGH-006",
|
|
1518
1521
|
name: "Hardcoded Secrets",
|
|
@@ -2178,11 +2181,8 @@ var ExtensionGuardScanner = class {
|
|
|
2178
2181
|
}
|
|
2179
2182
|
ide.extensionCount = allExtensions[i].length;
|
|
2180
2183
|
}
|
|
2181
|
-
const
|
|
2182
|
-
|
|
2183
|
-
const result = await this.scanExtension(ext);
|
|
2184
|
-
results.push(result);
|
|
2185
|
-
}
|
|
2184
|
+
const extensions = Array.from(extensionMap.values()).map(({ ext }) => ext);
|
|
2185
|
+
const results = await this.scanExtensionsConcurrently(extensions, mergedOptions.concurrency);
|
|
2186
2186
|
const summary = this.calculateSummary(results);
|
|
2187
2187
|
return {
|
|
2188
2188
|
scanId: randomUUID2(),
|
|
@@ -2199,6 +2199,29 @@ var ExtensionGuardScanner = class {
|
|
|
2199
2199
|
scanDurationMs: Date.now() - startTime
|
|
2200
2200
|
};
|
|
2201
2201
|
}
|
|
2202
|
+
/**
|
|
2203
|
+
* Scan multiple extensions concurrently with a configurable pool size.
|
|
2204
|
+
* Uses a simple semaphore pattern to limit concurrent operations.
|
|
2205
|
+
*/
|
|
2206
|
+
async scanExtensionsConcurrently(extensions, concurrency) {
|
|
2207
|
+
const results = new Array(extensions.length);
|
|
2208
|
+
let currentIndex = 0;
|
|
2209
|
+
const worker = async () => {
|
|
2210
|
+
while (currentIndex < extensions.length) {
|
|
2211
|
+
const index = currentIndex++;
|
|
2212
|
+
const ext = extensions[index];
|
|
2213
|
+
if (ext) {
|
|
2214
|
+
results[index] = await this.scanExtension(ext);
|
|
2215
|
+
}
|
|
2216
|
+
}
|
|
2217
|
+
};
|
|
2218
|
+
const workers = Array.from(
|
|
2219
|
+
{ length: Math.min(concurrency, extensions.length) },
|
|
2220
|
+
() => worker()
|
|
2221
|
+
);
|
|
2222
|
+
await Promise.all(workers);
|
|
2223
|
+
return results.filter((r) => r !== void 0);
|
|
2224
|
+
}
|
|
2202
2225
|
async scanExtension(ext) {
|
|
2203
2226
|
const startTime = Date.now();
|
|
2204
2227
|
const files = await collectFiles(ext.installPath);
|
|
@@ -2996,12 +3019,14 @@ var PolicyEngine = class {
|
|
|
2996
3019
|
};
|
|
2997
3020
|
|
|
2998
3021
|
// src/index.ts
|
|
2999
|
-
var VERSION = "0.5.
|
|
3022
|
+
var VERSION = "0.5.2";
|
|
3000
3023
|
export {
|
|
3001
3024
|
ALL_POPULAR_EXTENSIONS,
|
|
3025
|
+
CODE_EXTENSIONS,
|
|
3002
3026
|
DETECTION_RULES,
|
|
3003
3027
|
ExtensionGuardScanner,
|
|
3004
3028
|
IDE_PATHS,
|
|
3029
|
+
JS_TS_EXTENSIONS,
|
|
3005
3030
|
JsonReporter,
|
|
3006
3031
|
MEGA_POPULAR_EXTENSIONS,
|
|
3007
3032
|
MarkdownReporter,
|
|
@@ -3029,11 +3054,17 @@ export {
|
|
|
3029
3054
|
getDefaultDatabasePath,
|
|
3030
3055
|
getHash,
|
|
3031
3056
|
getIDEExtensionPath,
|
|
3057
|
+
getLineContent,
|
|
3058
|
+
getLineNumber,
|
|
3032
3059
|
getPopularityTier,
|
|
3033
3060
|
getSupportedIDEs,
|
|
3061
|
+
hasExtension,
|
|
3062
|
+
hasPatternInContext,
|
|
3034
3063
|
isAtLeastSeverity,
|
|
3035
3064
|
isBundleOutputPath,
|
|
3065
|
+
isExcluded,
|
|
3036
3066
|
isIDEInstalled,
|
|
3067
|
+
isInComment,
|
|
3037
3068
|
isMegaPopular,
|
|
3038
3069
|
isPopular,
|
|
3039
3070
|
isTrustedExtension,
|
|
@@ -3041,6 +3072,8 @@ export {
|
|
|
3041
3072
|
isVerifiedPublisher,
|
|
3042
3073
|
loadHashDatabase,
|
|
3043
3074
|
loadPolicyConfig,
|
|
3075
|
+
matchPatternsInFile,
|
|
3076
|
+
matchPatternsInFiles,
|
|
3044
3077
|
readExtension,
|
|
3045
3078
|
readExtensionsFromDirectory,
|
|
3046
3079
|
registerBuiltInRules,
|