@@ -10,7 +10,13 @@ import { generateMarkdownReport } from './reports/markdown.js';
1010import { generateSarifReport } from './reports/sarif.js' ;
1111import { calculateSecurityScore } from './score.js' ;
1212import { normalizeLocalPath , safeResolveInside } from './pathSafety.js' ;
13- import { loadIgnore , applyIgnore , compileIgnorePatterns , DEFAULT_IGNORE_PATTERNS } from './ignore.js' ;
13+ import {
14+ loadIgnore ,
15+ applyIgnore ,
16+ compileIgnorePatterns ,
17+ DEFAULT_IGNORE_PATTERNS ,
18+ DEFAULT_IGNORE_DIRS ,
19+ } from './ignore.js' ;
1420import { loadBaseline , applyBaseline , writeBaseline } from './baseline.js' ;
1521
1622/**
@@ -865,12 +871,26 @@ export function normalizeOsvScannerFindings(osvJson = {}, rootDir = process.cwd(
865871 return findings ;
866872}
867873
874+ /**
875+ * Build Semgrep `--exclude <dir>` argument pairs from the canonical ignore-dir
876+ * list so Semgrep skips the SAME build outputs / vendored dirs as the heuristic
877+ * detector. Without this Semgrep scanned `.output`, `dist`, `.nuxt`, etc. and
878+ * produced critical false positives from compiled bundles (e.g. prototype
879+ * pollution in `_nitro.mjs`).
880+ *
881+ * @param {string[] } [dirs]
882+ * @returns {string[] }
883+ */
884+ export function semgrepExcludeArgs ( dirs = DEFAULT_IGNORE_DIRS ) {
885+ return dirs . flatMap ( ( dir ) => [ '--exclude' , dir ] ) ;
886+ }
887+
868888async function runSemgrep ( rootDir ) {
869889 try {
870890 const versionResult = await execTool ( 'semgrep' , [ '--version' ] , { timeout : VERSION_CHECK_TIMEOUT_MS } ) ;
871891 const scanResult = await execTool (
872892 'semgrep' ,
873- [ '--config' , 'auto' , '--json' , '--quiet' , '--exclude' , 'node_modules' , '--exclude' , 'csreview-reports' , rootDir ] ,
893+ [ '--config' , 'auto' , '--json' , '--quiet' , ... semgrepExcludeArgs ( ) , rootDir ] ,
874894 {
875895 cwd : rootDir ,
876896 timeout : 120000 ,
0 commit comments