hackmyagent 0.17.7 → 0.17.9
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/.integrity-manifest.json +1 -1
- package/dist/arp/enforcement/kill-switch.d.ts.map +1 -1
- package/dist/arp/enforcement/kill-switch.js +8 -0
- package/dist/arp/enforcement/kill-switch.js.map +1 -1
- package/dist/cli.js +237 -30
- package/dist/cli.js.map +1 -1
- package/dist/hardening/scanner.d.ts +36 -0
- package/dist/hardening/scanner.d.ts.map +1 -1
- package/dist/hardening/scanner.js +120 -2
- package/dist/hardening/scanner.js.map +1 -1
- package/dist/hardening/security-check.d.ts +2 -1
- package/dist/hardening/security-check.d.ts.map +1 -1
- package/dist/nanomind-core/analyzers/capability-analyzer.d.ts +2 -1
- package/dist/nanomind-core/analyzers/capability-analyzer.d.ts.map +1 -1
- package/dist/nanomind-core/analyzers/capability-analyzer.js +44 -16
- package/dist/nanomind-core/analyzers/capability-analyzer.js.map +1 -1
- package/dist/nanomind-core/analyzers/credential-analyzer.d.ts +2 -1
- package/dist/nanomind-core/analyzers/credential-analyzer.d.ts.map +1 -1
- package/dist/nanomind-core/analyzers/credential-analyzer.js +57 -30
- package/dist/nanomind-core/analyzers/credential-analyzer.js.map +1 -1
- package/dist/nanomind-core/analyzers/governance-analyzer.d.ts +2 -1
- package/dist/nanomind-core/analyzers/governance-analyzer.d.ts.map +1 -1
- package/dist/nanomind-core/analyzers/governance-analyzer.js +7 -1
- package/dist/nanomind-core/analyzers/governance-analyzer.js.map +1 -1
- package/dist/nanomind-core/analyzers/prompt-analyzer.d.ts +2 -1
- package/dist/nanomind-core/analyzers/prompt-analyzer.d.ts.map +1 -1
- package/dist/nanomind-core/analyzers/prompt-analyzer.js +6 -1
- package/dist/nanomind-core/analyzers/prompt-analyzer.js.map +1 -1
- package/dist/nanomind-core/analyzers/scope-analyzer.d.ts +2 -1
- package/dist/nanomind-core/analyzers/scope-analyzer.d.ts.map +1 -1
- package/dist/nanomind-core/analyzers/scope-analyzer.js +6 -1
- package/dist/nanomind-core/analyzers/scope-analyzer.js.map +1 -1
- package/dist/nanomind-core/inference/analm-infer-llamacpp.py +113 -0
- package/dist/nanomind-core/inference/security-analyst.d.ts +1 -1
- package/dist/nanomind-core/inference/security-analyst.d.ts.map +1 -1
- package/dist/nanomind-core/inference/security-analyst.js +108 -21
- package/dist/nanomind-core/inference/security-analyst.js.map +1 -1
- package/dist/nanomind-core/orchestrate.d.ts +2 -1
- package/dist/nanomind-core/orchestrate.d.ts.map +1 -1
- package/dist/nanomind-core/orchestrate.js +1 -1
- package/dist/nanomind-core/orchestrate.js.map +1 -1
- package/dist/nanomind-core/scanner-bridge.d.ts +2 -2
- package/dist/nanomind-core/scanner-bridge.d.ts.map +1 -1
- package/dist/nanomind-core/scanner-bridge.js +67 -12
- package/dist/nanomind-core/scanner-bridge.js.map +1 -1
- package/dist/plugins/credvault.d.ts.map +1 -1
- package/dist/plugins/credvault.js +16 -6
- package/dist/plugins/credvault.js.map +1 -1
- package/dist/plugins/signcrypt.d.ts.map +1 -1
- package/dist/plugins/signcrypt.js +11 -8
- package/dist/plugins/signcrypt.js.map +1 -1
- package/dist/plugins/skillguard.d.ts.map +1 -1
- package/dist/plugins/skillguard.js +12 -9
- package/dist/plugins/skillguard.js.map +1 -1
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -221,7 +221,15 @@ Examples:
|
|
|
221
221
|
const targetDir = statSync(resolved).isFile() ? dirname(resolved) : resolved;
|
|
222
222
|
const { orchestrateNanoMind } = await Promise.resolve().then(() => __importStar(require('./nanomind-core/orchestrate.js')));
|
|
223
223
|
const nmResult = await orchestrateNanoMind(targetDir, [], { silent: !!options.json, analm: options.analm });
|
|
224
|
-
|
|
224
|
+
// Apply .hmaignore filtering (paths + check IDs)
|
|
225
|
+
const { loadHmaIgnore: loadIgnore, isPathIgnored: pathIgnored, isCheckIgnored: checkIgnored } = await Promise.resolve().then(() => __importStar(require('./hardening/scanner.js')));
|
|
226
|
+
const skillIgnoreRules = await loadIgnore(targetDir);
|
|
227
|
+
let skillFindings = nmResult.mergedFindings;
|
|
228
|
+
if (skillIgnoreRules.paths.length > 0 || skillIgnoreRules.checkIds.length > 0) {
|
|
229
|
+
skillFindings = skillFindings.filter((f) => !(f.file && pathIgnored(f.file, skillIgnoreRules.paths)) &&
|
|
230
|
+
!checkIgnored(f.checkId, skillIgnoreRules.checkIds));
|
|
231
|
+
}
|
|
232
|
+
const issues = skillFindings.filter((f) => !f.passed);
|
|
225
233
|
const critical = issues.filter((f) => f.severity === 'critical');
|
|
226
234
|
const high = issues.filter((f) => f.severity === 'high');
|
|
227
235
|
if (options.json) {
|
|
@@ -1876,8 +1884,8 @@ function generateScanHtmlReport(scanResult, targetDir) {
|
|
|
1876
1884
|
<td>${f.fixMessage ? escapeHtml(f.fixMessage) : ''}</td>
|
|
1877
1885
|
</tr>`).join('');
|
|
1878
1886
|
const projectTypeLabel = {
|
|
1879
|
-
cli: 'CLI Tool', library: 'Library',
|
|
1880
|
-
mcp: 'MCP Server', openclaw: 'OpenClaw Agent', all: 'Project',
|
|
1887
|
+
cli: 'CLI Tool', library: 'Library', sdk: 'SDK/API Client', webapp: 'Web App',
|
|
1888
|
+
api: 'API Server', mcp: 'MCP Server', openclaw: 'OpenClaw Agent', all: 'Project',
|
|
1881
1889
|
};
|
|
1882
1890
|
return `<!DOCTYPE html>
|
|
1883
1891
|
<html lang="en">
|
|
@@ -2484,6 +2492,7 @@ Examples:
|
|
|
2484
2492
|
deep: isDeep,
|
|
2485
2493
|
analm: options.analm,
|
|
2486
2494
|
silent: format !== 'text',
|
|
2495
|
+
projectType: result.projectType,
|
|
2487
2496
|
});
|
|
2488
2497
|
{
|
|
2489
2498
|
// Re-apply all filters after NanoMind merge (merge uses allFindings which is unfiltered)
|
|
@@ -2498,6 +2507,7 @@ Examples:
|
|
|
2498
2507
|
result.findings = refiltered.filter((f) => !f.passed && f.file && scanner.findingAppliesTo(f, projectType));
|
|
2499
2508
|
}
|
|
2500
2509
|
// Recalculate score from filtered findings (score was set pre-NanoMind)
|
|
2510
|
+
// findings already filtered by project type above, so just exclude passed/fixed
|
|
2501
2511
|
const forScore = (result.findings || []).filter((f) => !f.passed && !f.fixed);
|
|
2502
2512
|
result.score = scanner.calculateScore(forScore).score;
|
|
2503
2513
|
}
|
|
@@ -2748,6 +2758,7 @@ Examples:
|
|
|
2748
2758
|
const projectTypeLabel = {
|
|
2749
2759
|
cli: 'CLI Tool',
|
|
2750
2760
|
library: 'Library',
|
|
2761
|
+
sdk: 'SDK/API Client',
|
|
2751
2762
|
webapp: 'Web App',
|
|
2752
2763
|
api: 'API Server',
|
|
2753
2764
|
mcp: 'MCP Server',
|
|
@@ -3263,7 +3274,7 @@ Examples:
|
|
|
3263
3274
|
// NanoMind semantic analysis (defense-in-depth)
|
|
3264
3275
|
try {
|
|
3265
3276
|
const { orchestrateNanoMind } = await Promise.resolve().then(() => __importStar(require('./nanomind-core/orchestrate.js')));
|
|
3266
|
-
const nmResult = await orchestrateNanoMind(targetDir, result.findings, { silent: !!options.json });
|
|
3277
|
+
const nmResult = await orchestrateNanoMind(targetDir, result.findings, { silent: !!options.json, projectType: result.projectType });
|
|
3267
3278
|
// Re-apply .hmaignore filters and recalculate score after NanoMind merge
|
|
3268
3279
|
const hRefiltered = await scanner.reapplyIgnoreFilters(nmResult.mergedFindings, targetDir);
|
|
3269
3280
|
result.findings = hRefiltered;
|
|
@@ -3359,6 +3370,197 @@ Examples:
|
|
|
3359
3370
|
process.exit(1);
|
|
3360
3371
|
}
|
|
3361
3372
|
});
|
|
3373
|
+
function detectNemoClawDirectory(providedDir) {
|
|
3374
|
+
const os = require('os');
|
|
3375
|
+
const fs = require('fs');
|
|
3376
|
+
const path = require('path');
|
|
3377
|
+
if (providedDir && providedDir !== '') {
|
|
3378
|
+
return providedDir.startsWith('/') ? providedDir : path.join(process.cwd(), providedDir);
|
|
3379
|
+
}
|
|
3380
|
+
const homeDir = os.homedir();
|
|
3381
|
+
const candidates = [
|
|
3382
|
+
path.join(homeDir, '.nemoclaw'),
|
|
3383
|
+
path.join(homeDir, '.openshell'),
|
|
3384
|
+
path.join(homeDir, '.openclaw'),
|
|
3385
|
+
];
|
|
3386
|
+
for (const candidate of candidates) {
|
|
3387
|
+
if (fs.existsSync(candidate)) {
|
|
3388
|
+
return candidate;
|
|
3389
|
+
}
|
|
3390
|
+
}
|
|
3391
|
+
return process.cwd();
|
|
3392
|
+
}
|
|
3393
|
+
function filterNemoClawFindings(findings) {
|
|
3394
|
+
return findings.filter((f) => {
|
|
3395
|
+
const checkId = f.checkId.toUpperCase();
|
|
3396
|
+
return checkId.startsWith('HMA-NMC-');
|
|
3397
|
+
});
|
|
3398
|
+
}
|
|
3399
|
+
function assessNemoClawRiskLevel(findings) {
|
|
3400
|
+
const criticalCount = findings.filter((f) => f.severity === 'critical').length;
|
|
3401
|
+
const highCount = findings.filter((f) => f.severity === 'high').length;
|
|
3402
|
+
const mediumCount = findings.filter((f) => f.severity === 'medium').length;
|
|
3403
|
+
if (criticalCount > 0) {
|
|
3404
|
+
return {
|
|
3405
|
+
level: 'Critical',
|
|
3406
|
+
color: colors.brightRed,
|
|
3407
|
+
description: `${criticalCount} critical finding(s) with recommended fixes available.`,
|
|
3408
|
+
};
|
|
3409
|
+
}
|
|
3410
|
+
if (highCount > 0) {
|
|
3411
|
+
return {
|
|
3412
|
+
level: 'High',
|
|
3413
|
+
color: colors.red,
|
|
3414
|
+
description: `${highCount} high-severity finding(s) detected. Fixes available below.`,
|
|
3415
|
+
};
|
|
3416
|
+
}
|
|
3417
|
+
if (mediumCount > 0) {
|
|
3418
|
+
return {
|
|
3419
|
+
level: 'Moderate',
|
|
3420
|
+
color: colors.yellow,
|
|
3421
|
+
description: 'Some findings detected. Review the recommendations below.',
|
|
3422
|
+
};
|
|
3423
|
+
}
|
|
3424
|
+
if (findings.length === 0) {
|
|
3425
|
+
return {
|
|
3426
|
+
level: 'None',
|
|
3427
|
+
color: colors.dim,
|
|
3428
|
+
description: `No NemoClaw installation detected. Run \`${CLI_PREFIX} secure\` for a full scan.`,
|
|
3429
|
+
};
|
|
3430
|
+
}
|
|
3431
|
+
return {
|
|
3432
|
+
level: 'Low',
|
|
3433
|
+
color: colors.green,
|
|
3434
|
+
description: 'No critical or high findings detected.',
|
|
3435
|
+
};
|
|
3436
|
+
}
|
|
3437
|
+
program
|
|
3438
|
+
.command('secure-nemoclaw')
|
|
3439
|
+
.description(`Security scan for NVIDIA NemoClaw installations
|
|
3440
|
+
|
|
3441
|
+
Performs focused security checks for NemoClaw sandbox deployments:
|
|
3442
|
+
- Secrets: NVIDIA API key exposure in configs, logs, Docker, shell history
|
|
3443
|
+
- Network: Gateway/k3s/inference port binding, Docker socket, egress policies
|
|
3444
|
+
- Skills: Blueprint integrity, skill verification, directory permissions
|
|
3445
|
+
- Process: Sandbox privileges, seccomp/Landlock enforcement, root execution
|
|
3446
|
+
- OpenClaw layer: Inherited misconfigs that survive NemoClaw sandboxing
|
|
3447
|
+
|
|
3448
|
+
Auto-detects ~/.nemoclaw, ~/.openshell, or ~/.openclaw directories.
|
|
3449
|
+
Exit code 1 if critical/high issues found.
|
|
3450
|
+
|
|
3451
|
+
Examples:
|
|
3452
|
+
$ hackmyagent secure-nemoclaw Scan auto-detected directory
|
|
3453
|
+
$ hackmyagent secure-nemoclaw ~/.nemoclaw Scan specific directory
|
|
3454
|
+
$ hackmyagent secure-nemoclaw --json JSON output for CI`)
|
|
3455
|
+
.argument('[directory]', 'Directory to scan (default: ~/.nemoclaw or ~/.openshell)', '')
|
|
3456
|
+
.option('--json', 'Output as JSON (for scripting/CI)')
|
|
3457
|
+
.option('-v, --verbose', 'Show all checks including passed ones')
|
|
3458
|
+
.action(async (directory, options) => {
|
|
3459
|
+
try {
|
|
3460
|
+
const targetDir = detectNemoClawDirectory(directory);
|
|
3461
|
+
if (!options.json) {
|
|
3462
|
+
console.log(`\nNemoClaw Security Report`);
|
|
3463
|
+
console.log(`━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n`);
|
|
3464
|
+
console.log(`Scanning ${targetDir}...\n`);
|
|
3465
|
+
}
|
|
3466
|
+
const scanner = new index_1.HardeningScanner();
|
|
3467
|
+
const result = await scanner.scan({ targetDir, autoFix: false });
|
|
3468
|
+
const findings = result.findings;
|
|
3469
|
+
// Enrich with taxonomy
|
|
3470
|
+
const { enrichWithTaxonomy } = require('./hardening/taxonomy');
|
|
3471
|
+
enrichWithTaxonomy(findings);
|
|
3472
|
+
// NanoMind semantic analysis (defense-in-depth)
|
|
3473
|
+
let mergedFindings = findings;
|
|
3474
|
+
try {
|
|
3475
|
+
const { orchestrateNanoMind } = await Promise.resolve().then(() => __importStar(require('./nanomind-core/orchestrate.js')));
|
|
3476
|
+
const nmResult = await orchestrateNanoMind(targetDir, findings, { silent: !!options.json });
|
|
3477
|
+
mergedFindings = nmResult.mergedFindings;
|
|
3478
|
+
}
|
|
3479
|
+
catch { /* NanoMind unavailable */ }
|
|
3480
|
+
// Re-apply .hmaignore filtering after NanoMind merge (paths + check IDs)
|
|
3481
|
+
try {
|
|
3482
|
+
const { loadHmaIgnore: loadIgnore, isPathIgnored: pathIgnored, isCheckIgnored: checkIgnored } = await Promise.resolve().then(() => __importStar(require('./hardening/scanner.js')));
|
|
3483
|
+
const ncIgnoreRules = await loadIgnore(targetDir);
|
|
3484
|
+
if (ncIgnoreRules.paths.length > 0 || ncIgnoreRules.checkIds.length > 0) {
|
|
3485
|
+
mergedFindings = mergedFindings.filter((f) => !(f.file && pathIgnored(f.file, ncIgnoreRules.paths)) &&
|
|
3486
|
+
!checkIgnored(f.checkId, ncIgnoreRules.checkIds));
|
|
3487
|
+
}
|
|
3488
|
+
}
|
|
3489
|
+
catch { /* ignore filter unavailable */ }
|
|
3490
|
+
const issues = mergedFindings.filter((f) => !f.passed);
|
|
3491
|
+
const passedFindings = mergedFindings.filter((f) => f.passed);
|
|
3492
|
+
if (options.json) {
|
|
3493
|
+
const jsonOutput = {
|
|
3494
|
+
target: targetDir,
|
|
3495
|
+
riskLevel: assessNemoClawRiskLevel(issues).level,
|
|
3496
|
+
totalChecks: mergedFindings.length,
|
|
3497
|
+
issues: issues.length,
|
|
3498
|
+
passed: passedFindings.length,
|
|
3499
|
+
findings: mergedFindings,
|
|
3500
|
+
};
|
|
3501
|
+
writeJsonStdout(jsonOutput);
|
|
3502
|
+
return;
|
|
3503
|
+
}
|
|
3504
|
+
// Risk assessment
|
|
3505
|
+
const risk = assessNemoClawRiskLevel(issues);
|
|
3506
|
+
console.log(`Risk Level: ${risk.color}${risk.level}${RESET()}`);
|
|
3507
|
+
console.log(`${risk.description}\n`);
|
|
3508
|
+
// Summary stats
|
|
3509
|
+
console.log(`Checks: ${findings.length} total | ${issues.length} issues | ${passedFindings.length} passed\n`);
|
|
3510
|
+
// Show issues
|
|
3511
|
+
if (issues.length > 0) {
|
|
3512
|
+
console.log(`${colors.red}Findings:${RESET()}\n`);
|
|
3513
|
+
for (const finding of issues) {
|
|
3514
|
+
const display = SEVERITY_DISPLAY[finding.severity];
|
|
3515
|
+
const location = finding.file
|
|
3516
|
+
? finding.line
|
|
3517
|
+
? `${finding.file}:${finding.line}`
|
|
3518
|
+
: finding.file
|
|
3519
|
+
: '';
|
|
3520
|
+
const sevLabel = finding.severity.charAt(0).toUpperCase() + finding.severity.slice(1);
|
|
3521
|
+
console.log(`${display.color()}${display.symbol} [${finding.checkId}] ${sevLabel}${RESET()}`);
|
|
3522
|
+
console.log(` ${finding.description}`);
|
|
3523
|
+
if (location) {
|
|
3524
|
+
console.log(` File: ${location}`);
|
|
3525
|
+
}
|
|
3526
|
+
if (finding.fix) {
|
|
3527
|
+
console.log(` ${colors.cyan}Recommended fix:${RESET()} ${finding.fix}`);
|
|
3528
|
+
}
|
|
3529
|
+
console.log();
|
|
3530
|
+
}
|
|
3531
|
+
}
|
|
3532
|
+
else {
|
|
3533
|
+
console.log(`${colors.green}No NemoClaw-specific issues found.${RESET()}\n`);
|
|
3534
|
+
}
|
|
3535
|
+
// Show passed checks in verbose mode
|
|
3536
|
+
if (options.verbose && passedFindings.length > 0) {
|
|
3537
|
+
console.log(`${colors.green}Passed Checks:${RESET()}`);
|
|
3538
|
+
for (const finding of passedFindings) {
|
|
3539
|
+
console.log(` ${colors.green}[ok]${RESET()} [${finding.checkId}] ${finding.name}`);
|
|
3540
|
+
}
|
|
3541
|
+
console.log();
|
|
3542
|
+
}
|
|
3543
|
+
// Shodan self-check guidance
|
|
3544
|
+
if (issues.some((f) => f.category === 'network')) {
|
|
3545
|
+
console.log(`${colors.yellow}Internet Exposure Check:${RESET()}`);
|
|
3546
|
+
console.log(` Check if your instance is visible on Shodan:`);
|
|
3547
|
+
console.log(` https://www.shodan.io/host/<YOUR-IP>`);
|
|
3548
|
+
console.log(` Known NemoClaw dorks: port:18789, port:6443 ssl.cert.subject.cn:"k3s-serving"\n`);
|
|
3549
|
+
}
|
|
3550
|
+
console.log(`━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━`);
|
|
3551
|
+
console.log(`Run '${CLI_PREFIX} secure-openclaw' for OpenClaw-specific checks.`);
|
|
3552
|
+
console.log(`Run '${CLI_PREFIX} secure' for a full security scan.\n`);
|
|
3553
|
+
// Exit with non-zero if critical/high issues remain
|
|
3554
|
+
const criticalOrHigh = issues.filter((f) => f.severity === 'critical' || f.severity === 'high');
|
|
3555
|
+
if (criticalOrHigh.length > 0) {
|
|
3556
|
+
process.exit(1);
|
|
3557
|
+
}
|
|
3558
|
+
}
|
|
3559
|
+
catch (error) {
|
|
3560
|
+
console.error(`Error: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
3561
|
+
process.exit(1);
|
|
3562
|
+
}
|
|
3563
|
+
});
|
|
3362
3564
|
program
|
|
3363
3565
|
.command('scan')
|
|
3364
3566
|
.description(`Scan external target for exposed MCP endpoints
|
|
@@ -6705,10 +6907,30 @@ const DOCS_AND_GENERATED_PATTERNS = [
|
|
|
6705
6907
|
/\bHISTORY/i, // history files
|
|
6706
6908
|
/\bLICENSE/i, // license files
|
|
6707
6909
|
/\bCONTRIBUTING/i, // contributing guides
|
|
6910
|
+
/\.tmLanguage\.json$/i, // TextMate grammars
|
|
6911
|
+
/\.schema\.json$/i, // JSON schema definitions
|
|
6912
|
+
/\.nls\.json$/i, // localization/NLS files
|
|
6913
|
+
/\bcglicenses/i, // CG license files
|
|
6914
|
+
/\blicenses?\//i, // license directories
|
|
6915
|
+
];
|
|
6916
|
+
/**
|
|
6917
|
+
* Check IDs for security-sensitive pattern matches that produce false
|
|
6918
|
+
* positives on documentation and generated files. These checks look for
|
|
6919
|
+
* credential patterns, prompt injection, governance gaps, and skill
|
|
6920
|
+
* definitions — all of which appear naturally in docs that DESCRIBE
|
|
6921
|
+
* these concepts without being vulnerable.
|
|
6922
|
+
*/
|
|
6923
|
+
const DOCS_FALSE_POSITIVE_PREFIXES = [
|
|
6924
|
+
'AST-GOV', // governance checks
|
|
6925
|
+
'AST-GOVERN', // governance checks
|
|
6926
|
+
'AST-PROMPT', // prompt security checks
|
|
6927
|
+
'AST-HEARTBEAT', // heartbeat/liveness checks
|
|
6928
|
+
'AST-CRED', // credential pattern checks
|
|
6929
|
+
'AST-INJECT', // injection pattern checks
|
|
6930
|
+
'AST-EXFIL', // exfiltration pattern checks
|
|
6931
|
+
'SKILL-', // skill definition checks
|
|
6932
|
+
'SUPPLY-', // supply chain checks
|
|
6708
6933
|
];
|
|
6709
|
-
function isDocsOrGenerated(filePath) {
|
|
6710
|
-
return DOCS_AND_GENERATED_PATTERNS.some(p => p.test(filePath));
|
|
6711
|
-
}
|
|
6712
6934
|
/**
|
|
6713
6935
|
* Build scripts, CI/CD pipelines, and infrastructure files.
|
|
6714
6936
|
* These are not runtime attack surfaces — findings here are lower risk.
|
|
@@ -6730,30 +6952,15 @@ const BUILD_CI_PATTERNS = [
|
|
|
6730
6952
|
/\btools?\//i, // tools/ directory
|
|
6731
6953
|
/\binfra\//i, // infra/ directory
|
|
6732
6954
|
];
|
|
6733
|
-
function isBuildOrCiFile(filePath) {
|
|
6734
|
-
return BUILD_CI_PATTERNS.some(p => p.test(filePath));
|
|
6735
|
-
}
|
|
6736
|
-
/**
|
|
6737
|
-
* Check IDs for security-sensitive pattern matches that produce false
|
|
6738
|
-
* positives on documentation and generated files. These checks look for
|
|
6739
|
-
* credential patterns, prompt injection, governance gaps, and skill
|
|
6740
|
-
* definitions — all of which appear naturally in docs that DESCRIBE
|
|
6741
|
-
* these concepts without being vulnerable.
|
|
6742
|
-
*/
|
|
6743
|
-
const DOCS_FALSE_POSITIVE_PREFIXES = [
|
|
6744
|
-
'AST-GOV', // governance checks
|
|
6745
|
-
'AST-GOVERN', // governance checks
|
|
6746
|
-
'AST-PROMPT', // prompt security checks
|
|
6747
|
-
'AST-HEARTBEAT', // heartbeat/liveness checks
|
|
6748
|
-
'AST-CRED', // credential pattern checks
|
|
6749
|
-
'AST-INJECT', // injection pattern checks
|
|
6750
|
-
'AST-EXFIL', // exfiltration pattern checks
|
|
6751
|
-
'SKILL-', // skill definition checks
|
|
6752
|
-
'SUPPLY-', // supply chain checks
|
|
6753
|
-
];
|
|
6754
6955
|
function isTestFile(filePath) {
|
|
6755
6956
|
return TEST_FILE_PATTERNS.some(p => p.test(filePath));
|
|
6756
6957
|
}
|
|
6958
|
+
function isDocsOrGenerated(filePath) {
|
|
6959
|
+
return DOCS_AND_GENERATED_PATTERNS.some(p => p.test(filePath));
|
|
6960
|
+
}
|
|
6961
|
+
function isBuildOrCiFile(filePath) {
|
|
6962
|
+
return BUILD_CI_PATTERNS.some(p => p.test(filePath));
|
|
6963
|
+
}
|
|
6757
6964
|
function isAiToolingFile(filePath) {
|
|
6758
6965
|
return AI_TOOLING_PATH_PATTERNS.some(p => p.test(filePath));
|
|
6759
6966
|
}
|
|
@@ -6762,8 +6969,8 @@ function isAiToolingFile(filePath) {
|
|
|
6762
6969
|
* packages (e.g. "Missing .gitignore" on an npm tarball). Also filters
|
|
6763
6970
|
* governance findings on AI tooling files, removes false-positive
|
|
6764
6971
|
* pattern matches on documentation/generated files, and demotes test
|
|
6765
|
-
* file findings. Mutates `result.findings` in place and
|
|
6766
|
-
* the score.
|
|
6972
|
+
* and build file findings. Mutates `result.findings` in place and
|
|
6973
|
+
* recalculates the score.
|
|
6767
6974
|
*/
|
|
6768
6975
|
function filterLocalOnlyFindings(result, scanner) {
|
|
6769
6976
|
result.findings = result.findings.filter(f => {
|