ferret-scan 2.2.0 → 2.3.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.
- package/CHANGELOG.md +12 -0
- package/README.md +15 -11
- package/bin/ferret.js +104 -8
- package/dist/__tests__/AgentMonitor.test.d.ts +6 -0
- package/dist/__tests__/AgentMonitor.test.js +235 -0
- package/dist/__tests__/AtlasNavigatorReporter.test.d.ts +6 -0
- package/dist/__tests__/AtlasNavigatorReporter.test.js +193 -0
- package/dist/__tests__/CorrelationAnalyzer.test.d.ts +6 -0
- package/dist/__tests__/CorrelationAnalyzer.test.js +211 -0
- package/dist/__tests__/IndicatorMatcher.test.d.ts +6 -0
- package/dist/__tests__/IndicatorMatcher.test.js +245 -0
- package/dist/__tests__/MarketplaceScanner.test.d.ts +5 -0
- package/dist/__tests__/MarketplaceScanner.test.js +212 -0
- package/dist/__tests__/RuleGenerator.test.d.ts +6 -0
- package/dist/__tests__/RuleGenerator.test.js +207 -0
- package/dist/__tests__/ThreatFeed.test.d.ts +6 -0
- package/dist/__tests__/ThreatFeed.test.js +359 -0
- package/dist/__tests__/WatchMode.test.d.ts +6 -0
- package/dist/__tests__/WatchMode.test.js +104 -0
- package/dist/__tests__/astAnalyzerExtra.test.d.ts +6 -0
- package/dist/__tests__/astAnalyzerExtra.test.js +67 -0
- package/dist/__tests__/astAnalyzerFull.test.d.ts +6 -0
- package/dist/__tests__/astAnalyzerFull.test.js +138 -0
- package/dist/__tests__/astAnalyzerPatterns.test.d.ts +6 -0
- package/dist/__tests__/astAnalyzerPatterns.test.js +143 -0
- package/dist/__tests__/atlas.test.d.ts +6 -0
- package/dist/__tests__/atlas.test.js +319 -0
- package/dist/__tests__/atlasCatalog.test.d.ts +6 -0
- package/dist/__tests__/atlasCatalog.test.js +200 -0
- package/dist/__tests__/atlasCatalogExtra.test.d.ts +6 -0
- package/dist/__tests__/atlasCatalogExtra.test.js +215 -0
- package/dist/__tests__/baseline.test.d.ts +6 -0
- package/dist/__tests__/baseline.test.js +321 -0
- package/dist/__tests__/baselineExtra.test.d.ts +6 -0
- package/dist/__tests__/baselineExtra.test.js +317 -0
- package/dist/__tests__/capabilityMapping.test.d.ts +5 -0
- package/dist/__tests__/capabilityMapping.test.js +49 -0
- package/dist/__tests__/capabilityMappingExtra.test.d.ts +5 -0
- package/dist/__tests__/capabilityMappingExtra.test.js +200 -0
- package/dist/__tests__/complianceExtra.test.d.ts +6 -0
- package/dist/__tests__/complianceExtra.test.js +121 -0
- package/dist/__tests__/config.test.js +1 -1
- package/dist/__tests__/configLoader.test.d.ts +6 -0
- package/dist/__tests__/configLoader.test.js +225 -0
- package/dist/__tests__/configLoaderExtra.test.d.ts +6 -0
- package/dist/__tests__/configLoaderExtra.test.js +186 -0
- package/dist/__tests__/correlationAnalyzerExtra.test.d.ts +5 -0
- package/dist/__tests__/correlationAnalyzerExtra.test.js +98 -0
- package/dist/__tests__/correlationAnalyzerFull.test.d.ts +6 -0
- package/dist/__tests__/correlationAnalyzerFull.test.js +154 -0
- package/dist/__tests__/customRules.extra.test.d.ts +6 -0
- package/dist/__tests__/customRules.extra.test.js +245 -0
- package/dist/__tests__/customRules.test.d.ts +7 -0
- package/dist/__tests__/customRules.test.js +347 -0
- package/dist/__tests__/dependencyRisk.test.d.ts +5 -0
- package/dist/__tests__/dependencyRisk.test.js +248 -0
- package/dist/__tests__/dependencyRiskExtra.test.d.ts +6 -0
- package/dist/__tests__/dependencyRiskExtra.test.js +177 -0
- package/dist/__tests__/featureExitCodes.test.d.ts +7 -0
- package/dist/__tests__/featureExitCodes.test.js +332 -0
- package/dist/__tests__/fileDiscoveryConfigOnly.test.d.ts +6 -0
- package/dist/__tests__/fileDiscoveryConfigOnly.test.js +195 -0
- package/dist/__tests__/fileDiscoveryExtra.test.d.ts +6 -0
- package/dist/__tests__/fileDiscoveryExtra.test.js +149 -0
- package/dist/__tests__/fixer.extra.test.d.ts +6 -0
- package/dist/__tests__/fixer.extra.test.js +135 -0
- package/dist/__tests__/fixerApply.test.d.ts +6 -0
- package/dist/__tests__/fixerApply.test.js +132 -0
- package/dist/__tests__/gitHooks.test.d.ts +7 -0
- package/dist/__tests__/gitHooks.test.js +188 -0
- package/dist/__tests__/htmlReporter.extra.test.d.ts +5 -0
- package/dist/__tests__/htmlReporter.extra.test.js +126 -0
- package/dist/__tests__/interactiveTui.test.d.ts +6 -0
- package/dist/__tests__/interactiveTui.test.js +180 -0
- package/dist/__tests__/interactiveTuiCommands.test.d.ts +6 -0
- package/dist/__tests__/interactiveTuiCommands.test.js +187 -0
- package/dist/__tests__/interactiveTuiMore.test.d.ts +6 -0
- package/dist/__tests__/interactiveTuiMore.test.js +194 -0
- package/dist/__tests__/interactiveTuiSession.test.d.ts +6 -0
- package/dist/__tests__/interactiveTuiSession.test.js +173 -0
- package/dist/__tests__/llmAnalysis.test.d.ts +6 -0
- package/dist/__tests__/llmAnalysis.test.js +229 -0
- package/dist/__tests__/llmAnalysisBuildExcerpt.test.d.ts +6 -0
- package/dist/__tests__/llmAnalysisBuildExcerpt.test.js +132 -0
- package/dist/__tests__/llmAnalysisExtra.test.d.ts +6 -0
- package/dist/__tests__/llmAnalysisExtra.test.js +214 -0
- package/dist/__tests__/llmAnalysisFilters.test.d.ts +6 -0
- package/dist/__tests__/llmAnalysisFilters.test.js +181 -0
- package/dist/__tests__/llmAnalysisMitre.test.d.ts +6 -0
- package/dist/__tests__/llmAnalysisMitre.test.js +192 -0
- package/dist/__tests__/llmGroqTPM.test.d.ts +6 -0
- package/dist/__tests__/llmGroqTPM.test.js +89 -0
- package/dist/__tests__/llmProviderRetry.test.d.ts +6 -0
- package/dist/__tests__/llmProviderRetry.test.js +172 -0
- package/dist/__tests__/mcpValidator.extra.test.d.ts +5 -0
- package/dist/__tests__/mcpValidator.extra.test.js +270 -0
- package/dist/__tests__/patternMatcherExtra.test.d.ts +7 -0
- package/dist/__tests__/patternMatcherExtra.test.js +198 -0
- package/dist/__tests__/patternsCommon.test.d.ts +6 -0
- package/dist/__tests__/patternsCommon.test.js +107 -0
- package/dist/__tests__/policyEnforcement.test.d.ts +5 -0
- package/dist/__tests__/policyEnforcement.test.js +510 -0
- package/dist/__tests__/quarantineExtra.test.d.ts +5 -0
- package/dist/__tests__/quarantineExtra.test.js +214 -0
- package/dist/__tests__/redactionExtra.test.d.ts +6 -0
- package/dist/__tests__/redactionExtra.test.js +228 -0
- package/dist/__tests__/scanDiff.test.d.ts +7 -0
- package/dist/__tests__/scanDiff.test.js +266 -0
- package/dist/__tests__/scanFull.test.d.ts +6 -0
- package/dist/__tests__/scanFull.test.js +158 -0
- package/dist/__tests__/scannerDampening.test.d.ts +6 -0
- package/dist/__tests__/scannerDampening.test.js +160 -0
- package/dist/__tests__/scannerExtra.test.d.ts +6 -0
- package/dist/__tests__/scannerExtra.test.js +194 -0
- package/dist/__tests__/scannerMitre.test.d.ts +5 -0
- package/dist/__tests__/scannerMitre.test.js +141 -0
- package/dist/__tests__/scannerSSRF.test.d.ts +5 -0
- package/dist/__tests__/scannerSSRF.test.js +149 -0
- package/dist/__tests__/schemas.test.d.ts +6 -0
- package/dist/__tests__/schemas.test.js +125 -0
- package/dist/__tests__/webhooks.extra.test.d.ts +6 -0
- package/dist/__tests__/webhooks.extra.test.js +144 -0
- package/dist/__tests__/webhooks.test.d.ts +6 -0
- package/dist/__tests__/webhooks.test.js +154 -0
- package/dist/features/customRules.js +22 -29
- package/dist/features/mcpTrustScore.d.ts +17 -0
- package/dist/features/mcpTrustScore.js +74 -0
- package/dist/features/mcpValidator.d.ts +2 -0
- package/dist/features/mcpValidator.js +13 -0
- package/dist/features/policyEnforcement.d.ts +22 -22
- package/dist/intelligence/ThreatFeed.js +207 -62
- package/dist/remediation/Quarantine.js +24 -6
- package/dist/reporters/ConsoleReporter.js +10 -0
- package/dist/reporters/HtmlReporter.js +5 -0
- package/dist/reporters/SarifReporter.d.ts +1 -0
- package/dist/reporters/SarifReporter.js +1 -0
- package/dist/scanner/IAnalyzer.d.ts +19 -0
- package/dist/scanner/IAnalyzer.js +5 -0
- package/dist/scanner/Scanner.js +64 -125
- package/dist/scanner/analyzers/CapabilityAnalyzer.d.ts +8 -0
- package/dist/scanner/analyzers/CapabilityAnalyzer.js +19 -0
- package/dist/scanner/analyzers/DependencyAnalyzer.d.ts +8 -0
- package/dist/scanner/analyzers/DependencyAnalyzer.js +18 -0
- package/dist/scanner/analyzers/EntropyAnalyzer.d.ts +8 -0
- package/dist/scanner/analyzers/EntropyAnalyzer.js +12 -0
- package/dist/scanner/analyzers/LlmAnalyzer.d.ts +17 -0
- package/dist/scanner/analyzers/LlmAnalyzer.js +36 -0
- package/dist/scanner/analyzers/McpAnalyzer.d.ts +8 -0
- package/dist/scanner/analyzers/McpAnalyzer.js +19 -0
- package/dist/scanner/analyzers/SemanticAnalyzer.d.ts +8 -0
- package/dist/scanner/analyzers/SemanticAnalyzer.js +21 -0
- package/dist/scanner/analyzers/ThreatIntelAnalyzer.d.ts +8 -0
- package/dist/scanner/analyzers/ThreatIntelAnalyzer.js +21 -0
- package/dist/types.d.ts +17 -0
- package/dist/types.js +1 -1
- package/dist/utils/safeRegex.d.ts +12 -51
- package/dist/utils/safeRegex.js +45 -62
- package/dist/utils/schemas.d.ts +64 -64
- package/package.json +24 -18
package/dist/scanner/Scanner.js
CHANGED
|
@@ -9,18 +9,18 @@ import { discoverFiles } from './FileDiscovery.js';
|
|
|
9
9
|
import { matchRules } from './PatternMatcher.js';
|
|
10
10
|
import { getRulesForScan } from '../rules/index.js';
|
|
11
11
|
import { loadCustomRules, loadCustomRulesSource } from '../features/customRules.js';
|
|
12
|
-
import { analyzeFile as analyzeFileSemantics, shouldAnalyze as shouldAnalyzeSemantics, getMemoryUsage } from '../analyzers/AstAnalyzer.js';
|
|
13
12
|
import { analyzeCorrelations, shouldAnalyzeCorrelations } from '../analyzers/CorrelationAnalyzer.js';
|
|
14
|
-
import { loadThreatDatabase } from '../intelligence/ThreatFeed.js';
|
|
15
|
-
import { matchIndicators, shouldMatchIndicators } from '../intelligence/IndicatorMatcher.js';
|
|
16
|
-
import { analyzeEntropy, entropyFindingsToFindings } from '../features/entropyAnalysis.js';
|
|
17
|
-
import { validateMcpConfigContent, mcpAssessmentsToFindings } from '../features/mcpValidator.js';
|
|
18
|
-
import { analyzeDependencies, dependencyAssessmentsToFindings } from '../features/dependencyRisk.js';
|
|
19
|
-
import { analyzeCapabilitiesContent, capabilityProfileToFindings } from '../features/capabilityMapping.js';
|
|
20
13
|
import { parseIgnoreComments, shouldIgnoreFinding } from '../features/ignoreComments.js';
|
|
21
14
|
import { annotateFindingsWithMitreAtlas, setMitreAtlasTechniqueCatalog } from '../mitre/atlas.js';
|
|
22
15
|
import { loadMitreAtlasTechniqueCatalog } from '../mitre/atlasCatalog.js';
|
|
23
|
-
import { createLlmProvider
|
|
16
|
+
import { createLlmProvider } from '../features/llmAnalysis.js';
|
|
17
|
+
import { EntropyAnalyzer } from './analyzers/EntropyAnalyzer.js';
|
|
18
|
+
import { McpAnalyzer } from './analyzers/McpAnalyzer.js';
|
|
19
|
+
import { DependencyAnalyzer } from './analyzers/DependencyAnalyzer.js';
|
|
20
|
+
import { CapabilityAnalyzer } from './analyzers/CapabilityAnalyzer.js';
|
|
21
|
+
import { LlmAnalyzer } from './analyzers/LlmAnalyzer.js';
|
|
22
|
+
import { SemanticAnalyzer } from './analyzers/SemanticAnalyzer.js';
|
|
23
|
+
import { ThreatIntelAnalyzer } from './analyzers/ThreatIntelAnalyzer.js';
|
|
24
24
|
import logger from '../utils/logger.js';
|
|
25
25
|
import ora from 'ora';
|
|
26
26
|
function looksLikeDocumentationPath(filePath) {
|
|
@@ -242,14 +242,27 @@ async function loadCustomRulesForScan(config) {
|
|
|
242
242
|
}
|
|
243
243
|
return rules.filter(r => r.enabled && config.categories.includes(r.category) && config.severities.includes(r.severity));
|
|
244
244
|
}
|
|
245
|
+
/**
|
|
246
|
+
* Build the ordered list of analyzers for a scan.
|
|
247
|
+
*/
|
|
248
|
+
function buildAnalyzers(llmProvider, llmRuntime) {
|
|
249
|
+
return [
|
|
250
|
+
new EntropyAnalyzer(),
|
|
251
|
+
new McpAnalyzer(),
|
|
252
|
+
new DependencyAnalyzer(),
|
|
253
|
+
new CapabilityAnalyzer(),
|
|
254
|
+
new LlmAnalyzer(llmProvider, llmRuntime),
|
|
255
|
+
new SemanticAnalyzer(),
|
|
256
|
+
new ThreatIntelAnalyzer(),
|
|
257
|
+
];
|
|
258
|
+
}
|
|
245
259
|
/**
|
|
246
260
|
* Scan a single file
|
|
247
261
|
*/
|
|
248
|
-
async function scanFile(file, config, rules,
|
|
262
|
+
async function scanFile(file, config, rules, analyzers) {
|
|
249
263
|
try {
|
|
250
264
|
const content = await readFile(file.path, 'utf-8');
|
|
251
265
|
const allFindings = [];
|
|
252
|
-
const fileErrors = [];
|
|
253
266
|
let ignoreState;
|
|
254
267
|
if (config.ignoreComments && content.includes('ferret-')) {
|
|
255
268
|
const parsed = parseIgnoreComments(content, file.type);
|
|
@@ -262,124 +275,20 @@ async function scanFile(file, config, rules, llmProvider, llmRuntime) {
|
|
|
262
275
|
contextLines: config.contextLines,
|
|
263
276
|
});
|
|
264
277
|
allFindings.push(...patternFindings);
|
|
265
|
-
//
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
allFindings.push(...converted);
|
|
271
|
-
}
|
|
272
|
-
catch (entropyError) {
|
|
273
|
-
const entropyMessage = entropyError instanceof Error ? entropyError.message : String(entropyError);
|
|
274
|
-
logger.warn(`Entropy analysis error for ${file.relativePath}: ${entropyMessage}`);
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
// MCP configuration validation if enabled
|
|
278
|
-
if (config.mcpValidation && file.component === 'mcp' && file.type === 'json') {
|
|
279
|
-
const mcpResult = validateMcpConfigContent(content);
|
|
280
|
-
if (mcpResult.valid && mcpResult.assessments.length > 0) {
|
|
281
|
-
const mcpFindings = mcpAssessmentsToFindings(mcpResult.assessments, file.path);
|
|
282
|
-
// Normalize relative path (feature module uses basename)
|
|
283
|
-
for (const f of mcpFindings) {
|
|
284
|
-
f.relativePath = file.relativePath;
|
|
285
|
-
}
|
|
286
|
-
allFindings.push(...mcpFindings);
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
// Dependency analysis if enabled
|
|
290
|
-
if (config.dependencyAnalysis && basename(file.path).toLowerCase() === 'package.json') {
|
|
291
|
-
try {
|
|
292
|
-
const depResult = analyzeDependencies(file.path, config.dependencyAudit);
|
|
293
|
-
const depFindings = dependencyAssessmentsToFindings(depResult);
|
|
294
|
-
for (const f of depFindings) {
|
|
295
|
-
f.relativePath = file.relativePath;
|
|
296
|
-
}
|
|
297
|
-
allFindings.push(...depFindings);
|
|
298
|
-
}
|
|
299
|
-
catch (depError) {
|
|
300
|
-
const depMessage = depError instanceof Error ? depError.message : String(depError);
|
|
301
|
-
logger.warn(`Dependency analysis error for ${file.relativePath}: ${depMessage}`);
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
// Capability mapping if enabled (best-effort, JSON-only)
|
|
305
|
-
if (config.capabilityMapping && file.type === 'json') {
|
|
306
|
-
try {
|
|
307
|
-
const profile = analyzeCapabilitiesContent(file.path, content);
|
|
308
|
-
if (profile) {
|
|
309
|
-
const capFindings = capabilityProfileToFindings(profile);
|
|
310
|
-
for (const f of capFindings) {
|
|
311
|
-
f.relativePath = file.relativePath;
|
|
312
|
-
}
|
|
313
|
-
allFindings.push(...capFindings);
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
catch (capError) {
|
|
317
|
-
const capMessage = capError instanceof Error ? capError.message : String(capError);
|
|
318
|
-
logger.warn(`Capability mapping error for ${file.relativePath}: ${capMessage}`);
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
// LLM-assisted analysis (optional; networked)
|
|
322
|
-
if (config.llmAnalysis && llmProvider && !llmRuntime.disabled && llmRuntime.analyzed < config.llm.maxFiles) {
|
|
323
|
-
const llmResult = await analyzeWithLlm(llmProvider, config.llm, file, content, allFindings);
|
|
324
|
-
if (llmResult.ran) {
|
|
325
|
-
llmRuntime.analyzed += 1;
|
|
326
|
-
}
|
|
327
|
-
allFindings.push(...llmResult.findings);
|
|
328
|
-
if (llmResult.error) {
|
|
329
|
-
fileErrors.push(`LLM analysis: ${llmResult.error}`);
|
|
330
|
-
if (!llmRuntime.disabled && /\bHTTP 429\b/i.test(llmResult.error)) {
|
|
331
|
-
llmRuntime.disabled = true;
|
|
332
|
-
llmRuntime.disabledReason = 'rate limited (HTTP 429)';
|
|
333
|
-
logger.warn('LLM disabled for remainder of scan due to rate limiting (HTTP 429)');
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
// Semantic analysis if enabled and applicable
|
|
338
|
-
if (config.semanticAnalysis && shouldAnalyzeSemantics(file, config)) {
|
|
339
|
-
// Monitor memory usage
|
|
340
|
-
const memBefore = getMemoryUsage();
|
|
341
|
-
if (memBefore.used > 1000) { // More than 1GB used
|
|
342
|
-
logger.warn(`High memory usage (${memBefore.used}MB) - skipping semantic analysis for ${file.relativePath}`);
|
|
343
|
-
}
|
|
344
|
-
else {
|
|
345
|
-
try {
|
|
346
|
-
logger.debug(`Running semantic analysis on ${file.relativePath}`);
|
|
347
|
-
const semanticFindings = await analyzeFileSemantics(file, content, rules);
|
|
348
|
-
// Convert SemanticFinding to Finding for compatibility
|
|
349
|
-
allFindings.push(...semanticFindings);
|
|
350
|
-
const memAfter = getMemoryUsage();
|
|
351
|
-
logger.debug(`Semantic analysis memory: ${memAfter.used - memBefore.used}MB delta`);
|
|
352
|
-
}
|
|
353
|
-
catch (semanticError) {
|
|
354
|
-
const semanticMessage = semanticError instanceof Error ? semanticError.message : String(semanticError);
|
|
355
|
-
logger.warn(`Semantic analysis error for ${file.relativePath}: ${semanticMessage}`);
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
// Threat intelligence matching if enabled
|
|
360
|
-
if (config.threatIntel && shouldMatchIndicators(file, config)) {
|
|
278
|
+
// Run each analyzer in order via the registry
|
|
279
|
+
const ctx = { file, content, config, rules, existingFindings: allFindings };
|
|
280
|
+
for (const analyzer of analyzers) {
|
|
281
|
+
if (!analyzer.shouldRun(ctx))
|
|
282
|
+
continue;
|
|
361
283
|
try {
|
|
362
|
-
const
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
minConfidence: 50,
|
|
366
|
-
enablePatternMatching: true,
|
|
367
|
-
maxMatchesPerFile: 50
|
|
368
|
-
});
|
|
369
|
-
allFindings.push(...threatFindings);
|
|
370
|
-
logger.debug(`Found ${threatFindings.length} threat intelligence matches`);
|
|
284
|
+
const found = await analyzer.analyze(ctx);
|
|
285
|
+
allFindings.push(...found);
|
|
286
|
+
ctx.existingFindings = allFindings;
|
|
371
287
|
}
|
|
372
|
-
catch (
|
|
373
|
-
|
|
374
|
-
logger.warn(`Threat intelligence error for ${file.relativePath}: ${threatMessage}`);
|
|
288
|
+
catch (err) {
|
|
289
|
+
logger.warn(`${analyzer.name} error for ${file.relativePath}: ${err instanceof Error ? err.message : String(err)}`);
|
|
375
290
|
}
|
|
376
291
|
}
|
|
377
|
-
if (fileErrors.length > 0 && ignoreState) {
|
|
378
|
-
return { findings: allFindings, errors: fileErrors, ignoreState };
|
|
379
|
-
}
|
|
380
|
-
if (fileErrors.length > 0) {
|
|
381
|
-
return { findings: allFindings, errors: fileErrors };
|
|
382
|
-
}
|
|
383
292
|
if (ignoreState) {
|
|
384
293
|
return { findings: allFindings, ignoreState };
|
|
385
294
|
}
|
|
@@ -406,6 +315,30 @@ function isLocalEndpoint(urlStr) {
|
|
|
406
315
|
return false;
|
|
407
316
|
}
|
|
408
317
|
}
|
|
318
|
+
function buildMcpTrustSummary(trustFindings) {
|
|
319
|
+
const summary = { total: 0, high: 0, medium: 0, low: 0, critical: 0, lowestScore: 100 };
|
|
320
|
+
const seen = new Set();
|
|
321
|
+
for (const f of trustFindings) {
|
|
322
|
+
const server = String(f.metadata?.['serverName'] ?? f.file);
|
|
323
|
+
if (seen.has(server))
|
|
324
|
+
continue;
|
|
325
|
+
seen.add(server);
|
|
326
|
+
summary.total++;
|
|
327
|
+
const score = typeof f.metadata?.['trustScore'] === 'number'
|
|
328
|
+
? f.metadata['trustScore']
|
|
329
|
+
: (f.severity === 'CRITICAL' ? 20 : 45);
|
|
330
|
+
summary.lowestScore = Math.min(summary.lowestScore, score);
|
|
331
|
+
if (score >= 80)
|
|
332
|
+
summary.high++;
|
|
333
|
+
else if (score >= 60)
|
|
334
|
+
summary.medium++;
|
|
335
|
+
else if (score >= 40)
|
|
336
|
+
summary.low++;
|
|
337
|
+
else
|
|
338
|
+
summary.critical++;
|
|
339
|
+
}
|
|
340
|
+
return summary;
|
|
341
|
+
}
|
|
409
342
|
/**
|
|
410
343
|
* Main scan function
|
|
411
344
|
*/
|
|
@@ -459,6 +392,8 @@ export async function scan(config) {
|
|
|
459
392
|
'Review privacy/compliance requirements before enabling this feature.');
|
|
460
393
|
}
|
|
461
394
|
}
|
|
395
|
+
// Build analyzer registry (uses llmProvider + llmRuntime by reference)
|
|
396
|
+
const analyzers = buildAnalyzers(llmProvider, llmRuntime);
|
|
462
397
|
// Discover files with spinner
|
|
463
398
|
let spinner = null;
|
|
464
399
|
if (showProgress) {
|
|
@@ -520,7 +455,7 @@ export async function scan(config) {
|
|
|
520
455
|
lastYield = Date.now();
|
|
521
456
|
}
|
|
522
457
|
}
|
|
523
|
-
const result = await scanFile(file, config, rulesForScan,
|
|
458
|
+
const result = await scanFile(file, config, rulesForScan, analyzers);
|
|
524
459
|
if (result.errors && result.errors.length > 0) {
|
|
525
460
|
for (const err of result.errors) {
|
|
526
461
|
errors.push({
|
|
@@ -588,6 +523,9 @@ export async function scan(config) {
|
|
|
588
523
|
const sortedFindings = sortFindings(filteredFindings);
|
|
589
524
|
const endTime = new Date();
|
|
590
525
|
const duration = endTime.getTime() - startTime.getTime();
|
|
526
|
+
// Build MCP trust summary from trust-score findings emitted by mcpValidator
|
|
527
|
+
const mcpTrustFindings = sortedFindings.filter(f => f.metadata?.['issueType'] === 'trust-score');
|
|
528
|
+
const mcpTrustSummary = mcpTrustFindings.length > 0 ? buildMcpTrustSummary(mcpTrustFindings) : undefined;
|
|
591
529
|
const result = {
|
|
592
530
|
success: true,
|
|
593
531
|
startTime,
|
|
@@ -604,6 +542,7 @@ export async function scan(config) {
|
|
|
604
542
|
summary: calculateSummary(sortedFindings),
|
|
605
543
|
errors,
|
|
606
544
|
ignoredFindings,
|
|
545
|
+
...(mcpTrustSummary !== undefined ? { mcpTrustSummary } : {}),
|
|
607
546
|
};
|
|
608
547
|
logger.info(`Scan complete: ${result.summary.total} findings in ${result.analyzedFiles} files (${duration}ms)`);
|
|
609
548
|
return result;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Finding } from '../../types.js';
|
|
2
|
+
import type { IAnalyzer, AnalyzerContext } from '../IAnalyzer.js';
|
|
3
|
+
export declare class CapabilityAnalyzer implements IAnalyzer {
|
|
4
|
+
readonly name = "CapabilityAnalyzer";
|
|
5
|
+
shouldRun(ctx: AnalyzerContext): boolean;
|
|
6
|
+
analyze(ctx: AnalyzerContext): Promise<Finding[]>;
|
|
7
|
+
}
|
|
8
|
+
//# sourceMappingURL=CapabilityAnalyzer.d.ts.map
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { analyzeCapabilitiesContent, capabilityProfileToFindings } from '../../features/capabilityMapping.js';
|
|
2
|
+
export class CapabilityAnalyzer {
|
|
3
|
+
name = 'CapabilityAnalyzer';
|
|
4
|
+
shouldRun(ctx) {
|
|
5
|
+
return ctx.config.capabilityMapping && ctx.file.type === 'json';
|
|
6
|
+
}
|
|
7
|
+
async analyze(ctx) {
|
|
8
|
+
const profile = analyzeCapabilitiesContent(ctx.file.path, ctx.content);
|
|
9
|
+
if (!profile) {
|
|
10
|
+
return [];
|
|
11
|
+
}
|
|
12
|
+
const capFindings = capabilityProfileToFindings(profile);
|
|
13
|
+
for (const f of capFindings) {
|
|
14
|
+
f.relativePath = ctx.file.relativePath;
|
|
15
|
+
}
|
|
16
|
+
return capFindings;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=CapabilityAnalyzer.js.map
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Finding } from '../../types.js';
|
|
2
|
+
import type { IAnalyzer, AnalyzerContext } from '../IAnalyzer.js';
|
|
3
|
+
export declare class DependencyAnalyzer implements IAnalyzer {
|
|
4
|
+
readonly name = "DependencyAnalyzer";
|
|
5
|
+
shouldRun(ctx: AnalyzerContext): boolean;
|
|
6
|
+
analyze(ctx: AnalyzerContext): Promise<Finding[]>;
|
|
7
|
+
}
|
|
8
|
+
//# sourceMappingURL=DependencyAnalyzer.d.ts.map
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { basename } from 'node:path';
|
|
2
|
+
import { analyzeDependencies, dependencyAssessmentsToFindings } from '../../features/dependencyRisk.js';
|
|
3
|
+
export class DependencyAnalyzer {
|
|
4
|
+
name = 'DependencyAnalyzer';
|
|
5
|
+
shouldRun(ctx) {
|
|
6
|
+
return (ctx.config.dependencyAnalysis &&
|
|
7
|
+
basename(ctx.file.path).toLowerCase() === 'package.json');
|
|
8
|
+
}
|
|
9
|
+
async analyze(ctx) {
|
|
10
|
+
const depResult = analyzeDependencies(ctx.file.path, ctx.config.dependencyAudit);
|
|
11
|
+
const depFindings = dependencyAssessmentsToFindings(depResult);
|
|
12
|
+
for (const f of depFindings) {
|
|
13
|
+
f.relativePath = ctx.file.relativePath;
|
|
14
|
+
}
|
|
15
|
+
return depFindings;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=DependencyAnalyzer.js.map
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Finding } from '../../types.js';
|
|
2
|
+
import type { IAnalyzer, AnalyzerContext } from '../IAnalyzer.js';
|
|
3
|
+
export declare class EntropyAnalyzer implements IAnalyzer {
|
|
4
|
+
readonly name = "EntropyAnalyzer";
|
|
5
|
+
shouldRun(ctx: AnalyzerContext): boolean;
|
|
6
|
+
analyze(ctx: AnalyzerContext): Promise<Finding[]>;
|
|
7
|
+
}
|
|
8
|
+
//# sourceMappingURL=EntropyAnalyzer.d.ts.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { analyzeEntropy, entropyFindingsToFindings } from '../../features/entropyAnalysis.js';
|
|
2
|
+
export class EntropyAnalyzer {
|
|
3
|
+
name = 'EntropyAnalyzer';
|
|
4
|
+
shouldRun(ctx) {
|
|
5
|
+
return ctx.config.entropyAnalysis;
|
|
6
|
+
}
|
|
7
|
+
async analyze(ctx) {
|
|
8
|
+
const entropyFindings = analyzeEntropy(ctx.content, ctx.file);
|
|
9
|
+
return entropyFindingsToFindings(entropyFindings, ctx.file, ctx.content);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=EntropyAnalyzer.js.map
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Finding } from '../../types.js';
|
|
2
|
+
import type { IAnalyzer, AnalyzerContext } from '../IAnalyzer.js';
|
|
3
|
+
import { type LlmProvider } from '../../features/llmAnalysis.js';
|
|
4
|
+
export interface LlmRuntime {
|
|
5
|
+
analyzed: number;
|
|
6
|
+
disabled: boolean;
|
|
7
|
+
disabledReason?: string;
|
|
8
|
+
}
|
|
9
|
+
export declare class LlmAnalyzer implements IAnalyzer {
|
|
10
|
+
private readonly llmProvider;
|
|
11
|
+
private readonly llmRuntime;
|
|
12
|
+
readonly name = "LlmAnalyzer";
|
|
13
|
+
constructor(llmProvider: LlmProvider | null, llmRuntime: LlmRuntime);
|
|
14
|
+
shouldRun(ctx: AnalyzerContext): boolean;
|
|
15
|
+
analyze(ctx: AnalyzerContext): Promise<Finding[]>;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=LlmAnalyzer.d.ts.map
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { analyzeWithLlm } from '../../features/llmAnalysis.js';
|
|
2
|
+
import logger from '../../utils/logger.js';
|
|
3
|
+
export class LlmAnalyzer {
|
|
4
|
+
llmProvider;
|
|
5
|
+
llmRuntime;
|
|
6
|
+
name = 'LlmAnalyzer';
|
|
7
|
+
constructor(llmProvider, llmRuntime) {
|
|
8
|
+
this.llmProvider = llmProvider;
|
|
9
|
+
this.llmRuntime = llmRuntime;
|
|
10
|
+
}
|
|
11
|
+
shouldRun(ctx) {
|
|
12
|
+
return (ctx.config.llmAnalysis &&
|
|
13
|
+
this.llmProvider !== null &&
|
|
14
|
+
!this.llmRuntime.disabled &&
|
|
15
|
+
this.llmRuntime.analyzed < ctx.config.llm.maxFiles);
|
|
16
|
+
}
|
|
17
|
+
async analyze(ctx) {
|
|
18
|
+
// shouldRun already guards llmProvider non-null, but TypeScript needs the cast
|
|
19
|
+
const provider = this.llmProvider;
|
|
20
|
+
const llmResult = await analyzeWithLlm(provider, ctx.config.llm, ctx.file, ctx.content, ctx.existingFindings);
|
|
21
|
+
if (llmResult.ran) {
|
|
22
|
+
this.llmRuntime.analyzed += 1;
|
|
23
|
+
}
|
|
24
|
+
if (llmResult.error) {
|
|
25
|
+
if (!this.llmRuntime.disabled && /\bHTTP 429\b/i.test(llmResult.error)) {
|
|
26
|
+
this.llmRuntime.disabled = true;
|
|
27
|
+
this.llmRuntime.disabledReason = 'rate limited (HTTP 429)';
|
|
28
|
+
logger.warn('LLM disabled for remainder of scan due to rate limiting (HTTP 429)');
|
|
29
|
+
}
|
|
30
|
+
// Re-throw so the caller's catch block records it as a file error
|
|
31
|
+
throw new Error(`LLM analysis: ${llmResult.error}`);
|
|
32
|
+
}
|
|
33
|
+
return llmResult.findings;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=LlmAnalyzer.js.map
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Finding } from '../../types.js';
|
|
2
|
+
import type { IAnalyzer, AnalyzerContext } from '../IAnalyzer.js';
|
|
3
|
+
export declare class McpAnalyzer implements IAnalyzer {
|
|
4
|
+
readonly name = "McpAnalyzer";
|
|
5
|
+
shouldRun(ctx: AnalyzerContext): boolean;
|
|
6
|
+
analyze(ctx: AnalyzerContext): Promise<Finding[]>;
|
|
7
|
+
}
|
|
8
|
+
//# sourceMappingURL=McpAnalyzer.d.ts.map
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { validateMcpConfigContent, mcpAssessmentsToFindings } from '../../features/mcpValidator.js';
|
|
2
|
+
export class McpAnalyzer {
|
|
3
|
+
name = 'McpAnalyzer';
|
|
4
|
+
shouldRun(ctx) {
|
|
5
|
+
return ctx.config.mcpValidation && ctx.file.component === 'mcp' && ctx.file.type === 'json';
|
|
6
|
+
}
|
|
7
|
+
async analyze(ctx) {
|
|
8
|
+
const mcpResult = validateMcpConfigContent(ctx.content);
|
|
9
|
+
if (!mcpResult.valid || mcpResult.assessments.length === 0) {
|
|
10
|
+
return [];
|
|
11
|
+
}
|
|
12
|
+
const mcpFindings = mcpAssessmentsToFindings(mcpResult.assessments, ctx.file.path);
|
|
13
|
+
for (const f of mcpFindings) {
|
|
14
|
+
f.relativePath = ctx.file.relativePath;
|
|
15
|
+
}
|
|
16
|
+
return mcpFindings;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=McpAnalyzer.js.map
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Finding } from '../../types.js';
|
|
2
|
+
import type { IAnalyzer, AnalyzerContext } from '../IAnalyzer.js';
|
|
3
|
+
export declare class SemanticAnalyzer implements IAnalyzer {
|
|
4
|
+
readonly name = "SemanticAnalyzer";
|
|
5
|
+
shouldRun(ctx: AnalyzerContext): boolean;
|
|
6
|
+
analyze(ctx: AnalyzerContext): Promise<Finding[]>;
|
|
7
|
+
}
|
|
8
|
+
//# sourceMappingURL=SemanticAnalyzer.d.ts.map
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { analyzeFile as analyzeFileSemantics, shouldAnalyze as shouldAnalyzeSemantics, getMemoryUsage, } from '../../analyzers/AstAnalyzer.js';
|
|
2
|
+
import logger from '../../utils/logger.js';
|
|
3
|
+
export class SemanticAnalyzer {
|
|
4
|
+
name = 'SemanticAnalyzer';
|
|
5
|
+
shouldRun(ctx) {
|
|
6
|
+
return ctx.config.semanticAnalysis && shouldAnalyzeSemantics(ctx.file, ctx.config);
|
|
7
|
+
}
|
|
8
|
+
async analyze(ctx) {
|
|
9
|
+
const memBefore = getMemoryUsage();
|
|
10
|
+
if (memBefore.used > 1000) {
|
|
11
|
+
logger.warn(`High memory usage (${memBefore.used}MB) - skipping semantic analysis for ${ctx.file.relativePath}`);
|
|
12
|
+
return [];
|
|
13
|
+
}
|
|
14
|
+
logger.debug(`Running semantic analysis on ${ctx.file.relativePath}`);
|
|
15
|
+
const semanticFindings = await analyzeFileSemantics(ctx.file, ctx.content, ctx.rules);
|
|
16
|
+
const memAfter = getMemoryUsage();
|
|
17
|
+
logger.debug(`Semantic analysis memory: ${memAfter.used - memBefore.used}MB delta`);
|
|
18
|
+
return semanticFindings;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=SemanticAnalyzer.js.map
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Finding } from '../../types.js';
|
|
2
|
+
import type { IAnalyzer, AnalyzerContext } from '../IAnalyzer.js';
|
|
3
|
+
export declare class ThreatIntelAnalyzer implements IAnalyzer {
|
|
4
|
+
readonly name = "ThreatIntelAnalyzer";
|
|
5
|
+
shouldRun(ctx: AnalyzerContext): boolean;
|
|
6
|
+
analyze(ctx: AnalyzerContext): Promise<Finding[]>;
|
|
7
|
+
}
|
|
8
|
+
//# sourceMappingURL=ThreatIntelAnalyzer.d.ts.map
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { loadThreatDatabase } from '../../intelligence/ThreatFeed.js';
|
|
2
|
+
import { matchIndicators, shouldMatchIndicators } from '../../intelligence/IndicatorMatcher.js';
|
|
3
|
+
import logger from '../../utils/logger.js';
|
|
4
|
+
export class ThreatIntelAnalyzer {
|
|
5
|
+
name = 'ThreatIntelAnalyzer';
|
|
6
|
+
shouldRun(ctx) {
|
|
7
|
+
return ctx.config.threatIntel && shouldMatchIndicators(ctx.file, ctx.config);
|
|
8
|
+
}
|
|
9
|
+
async analyze(ctx) {
|
|
10
|
+
const threatDB = loadThreatDatabase();
|
|
11
|
+
logger.debug(`Running threat intelligence matching on ${ctx.file.relativePath}`);
|
|
12
|
+
const threatFindings = matchIndicators(threatDB, ctx.file, ctx.content, {
|
|
13
|
+
minConfidence: 50,
|
|
14
|
+
enablePatternMatching: true,
|
|
15
|
+
maxMatchesPerFile: 50,
|
|
16
|
+
});
|
|
17
|
+
logger.debug(`Found ${threatFindings.length} threat intelligence matches`);
|
|
18
|
+
return threatFindings;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=ThreatIntelAnalyzer.js.map
|
package/dist/types.d.ts
CHANGED
|
@@ -217,6 +217,23 @@ export interface ScanResult {
|
|
|
217
217
|
errors: ScanError[];
|
|
218
218
|
/** Findings that were suppressed via inline ignore directives */
|
|
219
219
|
ignoredFindings?: number;
|
|
220
|
+
/** Aggregate MCP server trust score summary (present when MCP configs were scanned) */
|
|
221
|
+
mcpTrustSummary?: McpTrustSummary;
|
|
222
|
+
}
|
|
223
|
+
/** Aggregated MCP server trust scores across a scan */
|
|
224
|
+
export interface McpTrustSummary {
|
|
225
|
+
/** Total number of MCP servers evaluated */
|
|
226
|
+
total: number;
|
|
227
|
+
/** Servers with HIGH trust (score 80–100) */
|
|
228
|
+
high: number;
|
|
229
|
+
/** Servers with MEDIUM trust (score 60–79) */
|
|
230
|
+
medium: number;
|
|
231
|
+
/** Servers with LOW trust (score 40–59) */
|
|
232
|
+
low: number;
|
|
233
|
+
/** Servers with CRITICAL trust (score 0–39) */
|
|
234
|
+
critical: number;
|
|
235
|
+
/** Lowest individual trust score seen */
|
|
236
|
+
lowestScore: number;
|
|
220
237
|
}
|
|
221
238
|
/** Summary statistics for a scan */
|
|
222
239
|
export interface ScanSummary {
|
package/dist/types.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Safe regex runtime utilities with bounded runtime and match limits
|
|
2
|
+
* Safe regex runtime utilities with bounded runtime and match limits.
|
|
3
3
|
*
|
|
4
|
+
* Uses Google RE2 (linear-time engine) when available for categorically
|
|
5
|
+
* safe pattern execution. Falls back to the screened native JS engine.
|
|
4
6
|
* Prevents ReDoS attacks and runaway regex matching in user-controlled patterns.
|
|
5
7
|
*/
|
|
6
8
|
export interface BoundedOptions {
|
|
@@ -16,19 +18,19 @@ export interface BoundedResult {
|
|
|
16
18
|
truncated: boolean;
|
|
17
19
|
}
|
|
18
20
|
/**
|
|
19
|
-
* Compile a pattern string into a RegExp
|
|
21
|
+
* Compile a pattern string into a RegExp (or RE2 instance when available).
|
|
20
22
|
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
+
* Tries RE2 first — it is a linear-time engine that categorically eliminates
|
|
24
|
+
* ReDoS. If RE2 is unavailable or rejects the pattern (e.g. lookaheads), falls
|
|
25
|
+
* back to the static ReDoS screener + native RegExp.
|
|
23
26
|
*
|
|
24
27
|
* @param raw The raw pattern string
|
|
25
28
|
* @param flags Regex flags (default: 'gi')
|
|
26
|
-
* @returns Compiled RegExp or null if pattern is unsafe
|
|
29
|
+
* @returns Compiled RegExp/RE2 or null if pattern is unsafe/invalid
|
|
27
30
|
*
|
|
28
31
|
* @example
|
|
29
32
|
* ```typescript
|
|
30
33
|
* const safe = compileSafePattern('test\\d+'); // OK
|
|
31
|
-
* const unsafe = compileSafePattern('(a+)+b'); // null - ReDoS risk
|
|
32
34
|
* const invalid = compileSafePattern('[unclosed'); // null - syntax error
|
|
33
35
|
* ```
|
|
34
36
|
*/
|
|
@@ -36,59 +38,18 @@ export declare function compileSafePattern(raw: string, flags?: string): RegExp
|
|
|
36
38
|
/**
|
|
37
39
|
* Run a regex against content with bounded runtime and match limits.
|
|
38
40
|
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
* @param pattern The compiled RegExp to run
|
|
43
|
-
* @param content The content to search
|
|
44
|
-
* @param options Runtime limits
|
|
45
|
-
* @returns Result containing matches and truncation status
|
|
46
|
-
*
|
|
47
|
-
* @example
|
|
48
|
-
* ```typescript
|
|
49
|
-
* const pattern = /test\d+/g;
|
|
50
|
-
* const { matches, truncated } = runBounded(pattern, content, { maxMs: 500 });
|
|
51
|
-
* if (truncated) {
|
|
52
|
-
* console.warn('Regex operation was truncated');
|
|
53
|
-
* }
|
|
54
|
-
* ```
|
|
41
|
+
* When RE2 is active the time budget is largely redundant (RE2 is linear),
|
|
42
|
+
* but the match-count ceiling still prevents unbounded result arrays.
|
|
55
43
|
*/
|
|
56
44
|
export declare function runBounded(pattern: RegExp, content: string, options?: BoundedOptions): BoundedResult;
|
|
57
45
|
/**
|
|
58
46
|
* Safe pattern matching that combines compilation and bounded runtime.
|
|
59
|
-
*
|
|
60
|
-
* This is a convenience wrapper that safely compiles a pattern and runs
|
|
61
|
-
* it with bounds, handling both compilation failures and runtime limits.
|
|
62
|
-
*
|
|
63
|
-
* @param rawPattern The raw pattern string
|
|
64
|
-
* @param content The content to search
|
|
65
|
-
* @param flags Regex flags (default: 'gi')
|
|
66
|
-
* @param options Runtime limits
|
|
67
|
-
* @returns Match result or null if pattern is unsafe
|
|
68
|
-
*
|
|
69
|
-
* @example
|
|
70
|
-
* ```typescript
|
|
71
|
-
* const result = safeMatch('test\\d+', content);
|
|
72
|
-
* if (result === null) {
|
|
73
|
-
* console.warn('Unsafe or invalid pattern');
|
|
74
|
-
* } else if (result.truncated) {
|
|
75
|
-
* console.warn('Pattern operation was bounded');
|
|
76
|
-
* } else {
|
|
77
|
-
* console.log(`Found ${result.matches.length} matches`);
|
|
78
|
-
* }
|
|
79
|
-
* ```
|
|
80
47
|
*/
|
|
81
48
|
export declare function safeMatch(rawPattern: string, content: string, flags?: string, options?: BoundedOptions): BoundedResult | null;
|
|
82
49
|
/**
|
|
83
50
|
* Test if a pattern matches content safely, returning boolean result.
|
|
84
|
-
*
|
|
85
|
-
* This is equivalent to RegExp.test() but with safety checks and bounds.
|
|
86
|
-
* Returns false for unsafe patterns or bounded operations.
|
|
87
|
-
*
|
|
88
|
-
* @param rawPattern The raw pattern string
|
|
89
|
-
* @param content The content to test
|
|
90
|
-
* @param flags Regex flags (default: 'i')
|
|
91
|
-
* @returns True if pattern matches safely, false otherwise
|
|
92
51
|
*/
|
|
93
52
|
export declare function safeTest(rawPattern: string, content: string, flags?: string): boolean;
|
|
53
|
+
/** Returns true when RE2 is active (linear-time engine). */
|
|
54
|
+
export declare function isRE2Active(): boolean;
|
|
94
55
|
//# sourceMappingURL=safeRegex.d.ts.map
|