@aisdlc/core 2.2.4 → 2.4.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/dist/index.cjs CHANGED
@@ -294,19 +294,26 @@ function extractPythonFunctions(content, filePath) {
294
294
  }
295
295
  return buildMetricsFromBlocks(blocks, content, filePath);
296
296
  }
297
- function processBraceLine(line, state, blocks, lineIndex, ext) {
298
- if (!state.currentFn) {
299
- const candidate = matchFunctionHeader(line, ext);
300
- if (!candidate) return;
297
+ function tryStartBraceFunction(line, state, ext) {
298
+ if (state.currentFn || state.inBlockComment || state.rawTerm) return;
299
+ const candidate = matchFunctionHeader(line, ext);
300
+ if (candidate) {
301
301
  state.currentFn = candidate;
302
302
  state.braceCount = 0;
303
303
  state.fnLines = [];
304
304
  }
305
- state.fnLines.push(line);
306
- const res = scanBracesInLine(line, state.inBlockComment, ext);
305
+ }
306
+ function updateBraceScanState(line, state, ext) {
307
+ const res = scanBracesInLine(line, state.inBlockComment, state.rawTerm, ext);
307
308
  state.inBlockComment = res.nowInBlock;
309
+ state.rawTerm = res.nowRawTerm;
308
310
  state.braceCount += res.delta;
309
- if (state.braceCount === 0 && state.fnLines.length > 1) {
311
+ }
312
+ function processBraceLine(line, state, blocks, lineIndex, ext) {
313
+ tryStartBraceFunction(line, state, ext);
314
+ if (state.currentFn) state.fnLines.push(line);
315
+ updateBraceScanState(line, state, ext);
316
+ if (state.currentFn && state.braceCount === 0 && state.fnLines.length > 1) {
310
317
  blocks.push({ name: state.currentFn, body: state.fnLines.join("\n"), startLine: lineIndex });
311
318
  state.currentFn = null;
312
319
  state.fnLines = [];
@@ -316,98 +323,153 @@ function extractBraceFunctions(content, filePath) {
316
323
  const ext = path4__namespace.extname(filePath).toLowerCase();
317
324
  const lines = content.split("\n");
318
325
  const blocks = [];
319
- const state = { currentFn: null, braceCount: 0, fnLines: [], inBlockComment: false };
326
+ const state = {
327
+ currentFn: null,
328
+ braceCount: 0,
329
+ fnLines: [],
330
+ inBlockComment: false,
331
+ rawTerm: null
332
+ };
320
333
  for (let i = 0; i < lines.length; i++) {
321
334
  processBraceLine(lines[i], state, blocks, i, ext);
322
335
  }
323
336
  return buildMetricsFromBlocks(blocks, content, filePath);
324
337
  }
338
+ var HEADER_EXCLUSIONS = /* @__PURE__ */ new Set([
339
+ "if",
340
+ "for",
341
+ "foreach",
342
+ "while",
343
+ "switch",
344
+ "catch",
345
+ "select",
346
+ "match",
347
+ "class",
348
+ "struct",
349
+ "interface",
350
+ "record",
351
+ "enum",
352
+ "namespace",
353
+ "using",
354
+ "lock",
355
+ "fixed",
356
+ "synchronized",
357
+ "try",
358
+ "else"
359
+ ]);
325
360
  function matchCStyleHeader(line) {
326
361
  const match = line.match(
327
- /(?:(?:public|private|protected|static|async|virtual|override|fn)\s+)*(?:function\s+([a-zA-Z0-9_$]+)|(?:[a-zA-Z0-9_<>[\]?]+\s+)+([a-zA-Z0-9_$]+)\s*\([^;{]*\)\s*(?:throws\s+[^{]+)?\{|([a-zA-Z0-9_$]+)\s*\([^;{]*\)\s*(?::\s*[^{]+\s*)?\{)/
362
+ /(?:(?:public|private|protected|internal|static|async|virtual|override|sealed|abstract|final|synchronized|default|fn)\s+)*(?:function\s+([a-zA-Z0-9_$]+)|(?:[a-zA-Z0-9_<>[\]?,.\s]+\s+)+([a-zA-Z0-9_$]+)\s*(?:<[^>]+>)?\s*\([^;{}]*\)\s*(?:where\s+[^{]+|throws\s+[^{]+)?\{|([a-zA-Z0-9_$]+)\s*\([^;{}]*\)\s*(?::\s*[^{]+\s*)?\{)/
328
363
  );
329
364
  if (!match) return null;
330
365
  const candidate = match[1] || match[2] || match[3];
331
- const exclusions = ["if", "for", "while", "switch", "catch", "select", "match", "class", "struct"];
332
- return candidate && !exclusions.includes(candidate) ? candidate : null;
366
+ return candidate && !HEADER_EXCLUSIONS.has(candidate) ? candidate : null;
333
367
  }
334
368
  function matchFunctionHeader(line, ext) {
335
369
  const trimmed = line.trim();
336
- if (trimmed.startsWith("//") || trimmed.startsWith("/*") || trimmed.startsWith("*")) return null;
370
+ if (trimmed.startsWith("//") || trimmed.startsWith("/*") || trimmed.startsWith("*") || trimmed.startsWith("#")) {
371
+ return null;
372
+ }
337
373
  if (ext === ".go") {
338
- const match = line.match(/func\s+(?:\([^)]*\)\s*)?([a-zA-Z0-9_$]+)\s*\(/);
374
+ const match = line.match(/func\s+(?:\([^)]*\)\s*)?([a-zA-Z0-9_$]+)\s*(?:\[[^\]]*\])?\s*\(/);
339
375
  return match ? match[1] : null;
340
376
  }
341
377
  if (ext === ".rs") {
342
- const match = line.match(/(?:pub\s+)?(?:async\s+)?(?:unsafe\s+)?fn\s+([a-zA-Z0-9_$]+)\s*\(/);
378
+ const match = line.match(/(?:pub(?:\([^)]*\))?\s+)?(?:async\s+)?(?:unsafe\s+)?(?:const\s+)?fn\s+([a-zA-Z0-9_$]+)\s*(?:<[^>]*>)?\s*\(/);
343
379
  return match ? match[1] : null;
344
380
  }
345
381
  return matchCStyleHeader(line);
346
382
  }
347
- function handleStringChar(char, inString) {
348
- if (char === inString) return { inString: null, skip: false };
349
- return { inString, skip: char === "\\" };
350
- }
351
- function handleBlockState(char, next, inBlock) {
352
- if (inBlock && char === "*" && next === "/") return { inBlock: false, skip: true };
353
- return { inBlock, skip: false };
354
- }
355
- function checkCommentOrQuote(char, next, ext) {
356
- if (char === "/" && next === "/") return { startBlock: false, quote: null, isLineComment: true };
357
- if (char === "/" && next === "*") return { startBlock: true, quote: null, isLineComment: false };
358
- const isQuote = char === '"' || char === "'" || char === "`" && ext === ".go";
359
- return { startBlock: false, quote: isQuote ? char : null, isLineComment: false };
360
- }
361
- function stepSkippedState(char, next, inBlock, inString) {
362
- if (inBlock) {
363
- const res = handleBlockState(char, next, inBlock);
364
- return { inBlock: res.inBlock, inString, skip: res.skip, handled: true };
365
- }
366
- if (inString) {
367
- const res = handleStringChar(char, inString);
368
- return { inBlock, inString: res.inString, skip: res.skip, handled: true };
369
- }
370
- return { inBlock, inString, skip: false, handled: false };
371
- }
372
- function checkCodeChar(char, next, ext) {
373
- const trigger = checkCommentOrQuote(char, next, ext);
374
- if (trigger.isLineComment) return { delta: 0, startBlock: false, quote: null, stop: true };
375
- if (trigger.startBlock) return { delta: 0, startBlock: true, quote: null, stop: false };
376
- if (trigger.quote) return { delta: 0, startBlock: false, quote: trigger.quote, stop: false };
377
- const delta = char === "{" ? 1 : char === "}" ? -1 : 0;
378
- return { delta, startBlock: false, quote: null, stop: false };
379
- }
380
- function applyCodeChar(res, state) {
381
- if (res.stop) return { advance: 0, stop: true };
382
- if (res.startBlock) {
383
+ function detectRustOrGoRawString(sub, ext) {
384
+ if (ext === ".rs") {
385
+ const m = sub.match(/^r(#*)"/);
386
+ return m ? { term: `"${m[1]}`, advance: m[0].length - 1 } : null;
387
+ }
388
+ if (ext === ".go" && sub.startsWith("`")) {
389
+ return { term: "`", advance: 0 };
390
+ }
391
+ return null;
392
+ }
393
+ function detectCsJavaOrCppRawString(sub, ext) {
394
+ if (ext === ".cs" || ext === ".java") {
395
+ const m = sub.match(/^\$*("{3,})/);
396
+ if (m) return { term: m[1], advance: m[0].length - 1 };
397
+ const v = ext === ".cs" ? sub.match(/^(@\$|\$@|@)"/) : null;
398
+ return v ? { term: '"', advance: v[0].length - 1 } : null;
399
+ }
400
+ const cpp = sub.match(/^R"([a-zA-Z0-9_]*)\(/);
401
+ return cpp ? { term: `)${cpp[1]}"`, advance: cpp[0].length - 1 } : null;
402
+ }
403
+ function detectRawStringOpen(line, index, ext) {
404
+ const sub = line.slice(index);
405
+ return detectRustOrGoRawString(sub, ext) || detectCsJavaOrCppRawString(sub, ext);
406
+ }
407
+ function consumeRawOrBlock(line, index, state) {
408
+ if (state.inBlock) {
409
+ if (line[index] === "*" && line[index + 1] === "/") {
410
+ state.inBlock = false;
411
+ return { advance: 1, handled: true };
412
+ }
413
+ return { advance: 0, handled: true };
414
+ }
415
+ if (state.rawTerm) {
416
+ if (line.startsWith(state.rawTerm, index)) {
417
+ const len = state.rawTerm.length - 1;
418
+ state.rawTerm = null;
419
+ return { advance: len, handled: true };
420
+ }
421
+ return { advance: 0, handled: true };
422
+ }
423
+ return { advance: 0, handled: false };
424
+ }
425
+ function consumeNormalQuote(line, index, state) {
426
+ if (!state.inString) return { advance: 0, handled: false };
427
+ const ch = line[index];
428
+ if (ch === "\\") return { advance: 1, handled: true };
429
+ if (ch === state.inString) state.inString = null;
430
+ return { advance: 0, handled: true };
431
+ }
432
+ function applyQuoteOrBrace(ch, state) {
433
+ if (ch === '"' || ch === "'") {
434
+ state.inString = ch;
435
+ return;
436
+ }
437
+ if (ch === "{") state.delta++;
438
+ if (ch === "}") state.delta--;
439
+ }
440
+ function stepActiveCodeToken(line, j, ext, state) {
441
+ const pair = line.slice(j, j + 2);
442
+ if (pair === "//") return { advance: 0, stop: true };
443
+ if (pair === "/*") {
383
444
  state.inBlock = true;
384
445
  return { advance: 1, stop: false };
385
446
  }
386
- if (res.quote) {
387
- state.inString = res.quote;
388
- return { advance: 0, stop: false };
447
+ const rawOpen = detectRawStringOpen(line, j, ext);
448
+ if (rawOpen) {
449
+ state.rawTerm = rawOpen.term;
450
+ return { advance: rawOpen.advance, stop: false };
389
451
  }
390
- state.delta += res.delta;
452
+ applyQuoteOrBrace(line[j], state);
391
453
  return { advance: 0, stop: false };
392
454
  }
393
- function scanBracesInLine(line, wasInBlock, ext) {
394
- const state = { delta: 0, inBlock: wasInBlock, inString: null };
455
+ function scanBracesInLine(line, wasInBlock, wasRawTerm, ext) {
456
+ const state = { delta: 0, inBlock: wasInBlock, rawTerm: wasRawTerm, inString: null };
395
457
  for (let j = 0; j < line.length; j++) {
396
- const char = line[j];
397
- const next = j + 1 < line.length ? line[j + 1] : "";
398
- const skipped = stepSkippedState(char, next, state.inBlock, state.inString);
399
- if (skipped.handled) {
400
- state.inBlock = skipped.inBlock;
401
- state.inString = skipped.inString;
402
- if (skipped.skip) j++;
458
+ const rawOrBlock = consumeRawOrBlock(line, j, state);
459
+ if (rawOrBlock.handled) {
460
+ j += rawOrBlock.advance;
461
+ continue;
462
+ }
463
+ const quoteStep = consumeNormalQuote(line, j, state);
464
+ if (quoteStep.handled) {
465
+ j += quoteStep.advance;
403
466
  continue;
404
467
  }
405
- const res = checkCodeChar(char, next, ext);
406
- const step = applyCodeChar(res, state);
407
- if (step.stop) break;
408
- j += step.advance;
468
+ const codeStep = stepActiveCodeToken(line, j, ext, state);
469
+ if (codeStep.stop) break;
470
+ j += codeStep.advance;
409
471
  }
410
- return { delta: state.delta, nowInBlock: state.inBlock };
472
+ return { delta: state.delta, nowInBlock: state.inBlock, nowRawTerm: state.rawTerm };
411
473
  }
412
474
  function buildMetricsFromBlocks(blocks, content, filePath) {
413
475
  if (blocks.length === 0) {
@@ -431,11 +493,20 @@ function buildMetricsFromBlocks(blocks, content, filePath) {
431
493
  }
432
494
  function detectPolyglotSmells(body) {
433
495
  const smells = [];
434
- if (/#\s*noqa|#\s*type:\s*ignore|@SuppressWarnings/.test(body)) {
496
+ if (/#\s*noqa|#\s*type:\s*ignore|@SuppressWarnings|#pragma\s+warning\s+disable/.test(body)) {
435
497
  smells.push("Supresi\xF3n no autorizada de linter");
436
498
  }
437
499
  return smells;
438
500
  }
501
+ function stripStringsAndComments(body, ext) {
502
+ let cleaned = body;
503
+ if (ext === ".py") {
504
+ cleaned = cleaned.replace(/"""[\s\S]*?"""|'''[\s\S]*?'''/g, '""').replace(/#.*/g, "");
505
+ } else {
506
+ cleaned = cleaned.replace(/\/\*[\s\S]*?\*\/|\/\/.*/g, "").replace(/r(#+)"[\s\S]*?"\1/g, '""').replace(/R"([a-zA-Z0-9_]*)\([\s\S]*?\)\1"/g, '""').replace(/\$*"{3,}[\s\S]*?"{3,}/g, '""').replace(/`[\s\S]*?`/g, '""');
507
+ }
508
+ return cleaned.replace(/"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'/g, '""');
509
+ }
439
510
  function computeHalsteadMI(body, cyclomatic, loc, commentCount) {
440
511
  const cleanText = body.replace(/\/\*[\s\S]*?\*\/|\/\/.*|#.*/g, "");
441
512
  const tokens = cleanText.split(/[\s,;().{}[\]=+\-*/<>!&|:]+/).filter((t) => t.length > 0);
@@ -460,9 +531,11 @@ function countCommentsAndLoc(rawLines, isPy) {
460
531
  function calculatePolyglotMetrics(body, name, filePath, startLine) {
461
532
  const ext = path4__namespace.extname(filePath).toLowerCase();
462
533
  const rawLines = body.split(/\r?\n/).map((l) => l.trim()).filter((l) => l.length > 0);
463
- const { loc, commentCount, cleanLines } = countCommentsAndLoc(rawLines, ext === ".py");
464
- const cyclomatic = computePolyglotCyclomatic(body, ext);
465
- const cognitive = computePolyglotCognitive(cleanLines, ext === ".py");
534
+ const { loc, commentCount } = countCommentsAndLoc(rawLines, ext === ".py");
535
+ const strippedBody = stripStringsAndComments(body, ext);
536
+ const strippedLines = strippedBody.split(/\r?\n/).filter((l) => l.trim().length > 0);
537
+ const cyclomatic = computePolyglotCyclomatic(strippedBody, ext);
538
+ const cognitive = computePolyglotCognitive(strippedLines, ext === ".py");
466
539
  const maintainability = computeHalsteadMI(body, cyclomatic, loc, commentCount);
467
540
  return {
468
541
  functionName: name,
@@ -475,40 +548,60 @@ function calculatePolyglotMetrics(body, name, filePath, startLine) {
475
548
  line: startLine ? startLine + 1 : 1
476
549
  };
477
550
  }
478
- function computePolyglotCyclomatic(body, ext) {
479
- let regex;
551
+ function getCyclomaticPattern(ext) {
480
552
  if (ext === ".py") {
481
- regex = /\b(if|elif|for|while|except)\b|\b(and|or)\b/g;
482
- } else if (ext === ".go") {
483
- regex = /\b(if|for|case|select)\b|&&|\|\|/g;
484
- } else if (ext === ".rs") {
485
- regex = /\b(if|for|while|match)\b|&&|\|\||\?/g;
486
- } else {
487
- regex = /\b(if|else\s+if|for|while|catch|case)\b|\?|&&|\|\|/g;
553
+ return /\b(if|elif|for|while|except|case)\b|\b(and|or)\b/g;
554
+ }
555
+ if (ext === ".go") {
556
+ return /\b(if|for|case|select)\b|&&|\|\|/g;
557
+ }
558
+ if (ext === ".rs") {
559
+ return /\b(if|for|while|match)\b|&&|\|\||\?/g;
560
+ }
561
+ if (ext === ".cs") {
562
+ return /\b(if|else\s+if|for|foreach|while|catch|case|when)\b|\?\?|\?\.(?=[a-zA-Z_])|&&|\|\|/g;
563
+ }
564
+ if (ext === ".java") {
565
+ return /\b(if|else\s+if|for|while|catch|case|when)\b|&&|\|\|/g;
488
566
  }
489
- const cleanBody = body.replace(/"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'/g, "");
490
- const matches = cleanBody.match(regex);
567
+ return /\b(if|else\s+if|for|while|catch|case)\b|\?|&&|\|\|/g;
568
+ }
569
+ function computePolyglotCyclomatic(strippedBody, ext) {
570
+ const regex = getCyclomaticPattern(ext);
571
+ const matches = strippedBody.match(regex);
491
572
  return 1 + (matches ? matches.length : 0);
492
573
  }
493
- function computePolyglotCognitive(lines, isPy) {
574
+ var PY_COGNITIVE_RE = /\b(if|elif|for|while|except|match)\b/;
575
+ var BRACE_COGNITIVE_RE = /\b(if|for|foreach|while|catch|match|select|switch)\b/;
576
+ function computePythonCognitive(lines) {
577
+ let cognitive = 0;
578
+ const baseIndent = lines.length > 0 ? lines[0].length - lines[0].trimStart().length : 0;
579
+ for (let i = 1; i < lines.length; i++) {
580
+ const line = lines[i];
581
+ if (PY_COGNITIVE_RE.test(line)) {
582
+ const relIndent = Math.max(0, line.length - line.trimStart().length - baseIndent - 4);
583
+ cognitive += 1 + Math.floor(relIndent / 4);
584
+ }
585
+ }
586
+ return cognitive;
587
+ }
588
+ function computeBraceCognitive(lines) {
494
589
  let cognitive = 0;
495
590
  let nesting = 0;
496
- for (const line of lines) {
497
- if (isPy) {
498
- const indent = line.length - line.trimStart().length;
499
- if (/\b(if|elif|for|while|except)\b/.test(line)) {
500
- cognitive += 1 + Math.floor(indent / 4);
501
- }
502
- } else {
503
- if (line.includes("{")) nesting++;
504
- if (line.includes("}")) nesting = Math.max(0, nesting - 1);
505
- if (/\b(if|for|while|catch|match|select)\b/.test(line)) {
506
- cognitive += 1 + nesting;
507
- }
591
+ for (let i = 1; i < lines.length; i++) {
592
+ const line = lines[i];
593
+ if (BRACE_COGNITIVE_RE.test(line)) {
594
+ cognitive += 1 + nesting;
508
595
  }
596
+ const opens = (line.match(/\{/g) || []).length;
597
+ const closes = (line.match(/\}/g) || []).length;
598
+ nesting = Math.max(0, nesting + opens - closes);
509
599
  }
510
600
  return cognitive;
511
601
  }
602
+ function computePolyglotCognitive(lines, isPy) {
603
+ return isPy ? computePythonCognitive(lines) : computeBraceCognitive(lines);
604
+ }
512
605
  function walkMdFiles(dir, fileList = [], excludeDirs = [
513
606
  "node_modules",
514
607
  ".git",
@@ -1092,9 +1185,54 @@ function collectTestCorpus(rootDir, allMdFiles) {
1092
1185
  }
1093
1186
  return { gherkinSources, combinedTests: testContents.join("\n") };
1094
1187
  }
1095
- function assembleCoverageResult(reqAudit, taskAudit, useCaseFlows, reqErrors, taskErrors, ucErrors) {
1188
+ function isCucumberScenarioPassed(element) {
1189
+ const steps = Array.isArray(element.steps) ? element.steps : [];
1190
+ if (steps.length === 0) return false;
1191
+ return steps.every((s) => {
1192
+ const res = s.result;
1193
+ return res && res.status === "passed";
1194
+ });
1195
+ }
1196
+ function countCucumberScenarios(raw) {
1197
+ let passed = 0;
1198
+ let failed = 0;
1199
+ for (const feat of raw) {
1200
+ const elements = Array.isArray(feat.elements) ? feat.elements : [];
1201
+ for (const el of elements) {
1202
+ if (isCucumberScenarioPassed(el)) passed++;
1203
+ else failed++;
1204
+ }
1205
+ }
1206
+ return { passed, failed };
1207
+ }
1208
+ function parseCucumberReportFile(reportPath) {
1209
+ if (!fs7__namespace.existsSync(reportPath)) return void 0;
1210
+ try {
1211
+ const raw = JSON.parse(fs7__namespace.readFileSync(reportPath, "utf-8"));
1212
+ if (!Array.isArray(raw)) return void 0;
1213
+ const { passed, failed } = countCucumberScenarios(raw);
1214
+ return {
1215
+ evaluatedFeatures: raw.length,
1216
+ passedScenarios: passed,
1217
+ failedScenarios: failed,
1218
+ passed: passed + failed > 0 && failed === 0
1219
+ };
1220
+ } catch {
1221
+ return void 0;
1222
+ }
1223
+ }
1224
+ function resolveCucumberReport(rootDir, options) {
1225
+ const candidate = options.cucumberReportPath ? path4__namespace.isAbsolute(options.cucumberReportPath) ? options.cucumberReportPath : path4__namespace.join(rootDir, options.cucumberReportPath) : path4__namespace.join(rootDir, "reports", "cucumber-report.json");
1226
+ const parsed = parseCucumberReportFile(candidate);
1227
+ if (parsed) return parsed;
1228
+ if (options.enforceCucumberExecution) {
1229
+ return { evaluatedFeatures: 0, passedScenarios: 0, failedScenarios: 1, passed: false };
1230
+ }
1231
+ return void 0;
1232
+ }
1233
+ function assembleCoverageResult(reqAudit, taskAudit, useCaseFlows, reqErrors, taskErrors, ucErrors, rootDir, options) {
1096
1234
  return {
1097
- success: reqErrors === 0 && taskErrors === 0 && ucErrors === 0,
1235
+ success: reqErrors === 0 && taskErrors === 0 && ucErrors === 0 && (!resolveCucumberReport(rootDir, options) || resolveCucumberReport(rootDir, options).passed),
1098
1236
  totalRequirements: reqAudit.length,
1099
1237
  passedRequirements: reqAudit.length - reqErrors,
1100
1238
  failedRequirements: reqErrors,
@@ -1105,6 +1243,7 @@ function assembleCoverageResult(reqAudit, taskAudit, useCaseFlows, reqErrors, ta
1105
1243
  tasks: taskAudit,
1106
1244
  useCaseFlows,
1107
1245
  failedUseCaseFlows: ucErrors,
1246
+ ...resolveCucumberReport(rootDir, options) ? { cucumberExecution: resolveCucumberReport(rootDir, options) } : {},
1108
1247
  reportMarkdown: generateTestingCoverageReportMarkdown(reqAudit, taskAudit, reqErrors, taskErrors)
1109
1248
  };
1110
1249
  }
@@ -1156,7 +1295,9 @@ function verifyTestingCoverage(options = {}) {
1156
1295
  state.useCaseFlows,
1157
1296
  state.reqErrors,
1158
1297
  state.taskErrors,
1159
- state.ucErrors
1298
+ state.ucErrors,
1299
+ rootDir,
1300
+ options
1160
1301
  );
1161
1302
  }
1162
1303
 
@@ -5532,6 +5673,277 @@ function verifyTasksGovernance(options = {}) {
5532
5673
  );
5533
5674
  return assembleGovernanceResult(data, reportMarkdown);
5534
5675
  }
5676
+ var BddStepRegistry = class {
5677
+ definitions = [];
5678
+ register(pattern, handler) {
5679
+ this.definitions.push({ pattern, handler });
5680
+ return this;
5681
+ }
5682
+ given(pattern, handler) {
5683
+ return this.register(pattern, handler);
5684
+ }
5685
+ when(pattern, handler) {
5686
+ return this.register(pattern, handler);
5687
+ }
5688
+ then(pattern, handler) {
5689
+ return this.register(pattern, handler);
5690
+ }
5691
+ getDefinitions() {
5692
+ return this.definitions;
5693
+ }
5694
+ };
5695
+ var STEP_PREFIX_RE = /^(Given|When|Then|And|But|Dado|Cuando|Entonces|Y|Pero)\s+(.+)$/i;
5696
+ var SCENARIO_HEADER_RE = /^(Scenario|Escenario|Scenario Outline|Esquema del escenario):\s*(.+)$/i;
5697
+ function parseTableRowCells(line) {
5698
+ return line.trim().replace(/^\|/, "").replace(/\|$/, "").split("|").map((c) => c.trim());
5699
+ }
5700
+ function attachStepTableRow(lastStep, cells) {
5701
+ if (!lastStep || cells.length < 2) return;
5702
+ if (!lastStep.table) lastStep.table = {};
5703
+ lastStep.table[cells[0]] = cells[1];
5704
+ }
5705
+ function attachExampleRow(scenario, headers, cells) {
5706
+ if (!scenario) return headers;
5707
+ if (headers.length === 0) return cells;
5708
+ const row = {};
5709
+ for (let i = 0; i < headers.length; i++) {
5710
+ row[headers[i]] = cells[i] ?? "";
5711
+ }
5712
+ scenario.examples.push(row);
5713
+ return headers;
5714
+ }
5715
+ function createInitialParseState() {
5716
+ return {
5717
+ featureName: "Unnamed Feature",
5718
+ featureTags: [],
5719
+ pendingTags: [],
5720
+ backgroundSteps: [],
5721
+ scenarios: [],
5722
+ currentScenario: null,
5723
+ inBackground: false,
5724
+ inExamples: false,
5725
+ exampleHeaders: []
5726
+ };
5727
+ }
5728
+ function handleStructuralHeaderLine(line, state) {
5729
+ if (/^(Feature|Caracter[ií]stica):/i.test(line)) {
5730
+ state.featureName = line.replace(/^(Feature|Caracter[ií]stica):\s*/i, "").trim();
5731
+ state.featureTags.push(...state.pendingTags);
5732
+ state.pendingTags = [];
5733
+ return true;
5734
+ }
5735
+ if (/^(Background|Antecedentes):/i.test(line)) {
5736
+ state.inBackground = true;
5737
+ state.inExamples = false;
5738
+ state.currentScenario = null;
5739
+ return true;
5740
+ }
5741
+ if (/^(Examples|Ejemplos):/i.test(line)) {
5742
+ state.inExamples = true;
5743
+ state.exampleHeaders = [];
5744
+ return true;
5745
+ }
5746
+ return false;
5747
+ }
5748
+ function startScenarioBlock(scenMatch, state) {
5749
+ state.inBackground = false;
5750
+ state.inExamples = false;
5751
+ const block = {
5752
+ name: scenMatch[2].trim(),
5753
+ tags: [...state.featureTags, ...state.pendingTags],
5754
+ isOutline: /outline|esquema/i.test(scenMatch[1]),
5755
+ steps: [],
5756
+ examples: []
5757
+ };
5758
+ state.pendingTags = [];
5759
+ state.scenarios.push(block);
5760
+ state.currentScenario = block;
5761
+ }
5762
+ function appendParsedStep(stepMatch, state) {
5763
+ const step = { keyword: stepMatch[1], text: stepMatch[2].trim() };
5764
+ if (state.inBackground) {
5765
+ state.backgroundSteps.push(step);
5766
+ } else if (state.currentScenario) {
5767
+ state.currentScenario.steps.push(step);
5768
+ }
5769
+ }
5770
+ function handleScenarioOrStepLine(line, state) {
5771
+ const scenMatch = line.match(SCENARIO_HEADER_RE);
5772
+ if (scenMatch) {
5773
+ startScenarioBlock(scenMatch, state);
5774
+ return;
5775
+ }
5776
+ const stepMatch = line.match(STEP_PREFIX_RE);
5777
+ if (stepMatch) {
5778
+ appendParsedStep(stepMatch, state);
5779
+ }
5780
+ }
5781
+ function processFeatureLine(rawLine, state) {
5782
+ const line = rawLine.trim();
5783
+ if (!line || line.startsWith("#")) return;
5784
+ if (line.startsWith("@")) {
5785
+ const tags = line.split(/\s+/).filter((t) => t.startsWith("@"));
5786
+ state.pendingTags.push(...tags);
5787
+ return;
5788
+ }
5789
+ if (line.startsWith("|")) {
5790
+ const cells = parseTableRowCells(line);
5791
+ if (state.inExamples) {
5792
+ state.exampleHeaders = attachExampleRow(state.currentScenario, state.exampleHeaders, cells);
5793
+ } else {
5794
+ const targetList = state.inBackground ? state.backgroundSteps : state.currentScenario?.steps || [];
5795
+ attachStepTableRow(targetList[targetList.length - 1], cells);
5796
+ }
5797
+ return;
5798
+ }
5799
+ if (!handleStructuralHeaderLine(line, state)) {
5800
+ handleScenarioOrStepLine(line, state);
5801
+ }
5802
+ }
5803
+ function parseFeatureDocument(content) {
5804
+ const state = createInitialParseState();
5805
+ for (const rawLine of content.split(/\r?\n/)) {
5806
+ processFeatureLine(rawLine, state);
5807
+ }
5808
+ return {
5809
+ featureName: state.featureName,
5810
+ tags: state.featureTags,
5811
+ backgroundSteps: state.backgroundSteps,
5812
+ scenarios: state.scenarios
5813
+ };
5814
+ }
5815
+ function interpolatePlaceholders(text, row) {
5816
+ let out = text;
5817
+ for (const [k, v] of Object.entries(row)) {
5818
+ out = out.replace(new RegExp(`<${k}>`, "g"), v);
5819
+ }
5820
+ return out;
5821
+ }
5822
+ function expandScenarioOutlines(scenarios) {
5823
+ const expanded = [];
5824
+ for (const sc of scenarios) {
5825
+ if (!sc.isOutline || sc.examples.length === 0) {
5826
+ expanded.push(sc);
5827
+ continue;
5828
+ }
5829
+ sc.examples.forEach((row, idx) => {
5830
+ expanded.push({
5831
+ name: `${sc.name} (Example #${idx + 1})`,
5832
+ tags: sc.tags,
5833
+ isOutline: false,
5834
+ steps: sc.steps.map((st) => ({
5835
+ keyword: st.keyword,
5836
+ text: interpolatePlaceholders(st.text, row),
5837
+ table: st.table
5838
+ })),
5839
+ examples: []
5840
+ });
5841
+ });
5842
+ }
5843
+ return expanded;
5844
+ }
5845
+ function matchStepDefinition(text, definitions) {
5846
+ for (const entry of definitions) {
5847
+ if (typeof entry.pattern === "string") {
5848
+ if (entry.pattern === text) return { entry, args: [] };
5849
+ } else {
5850
+ const m = text.match(entry.pattern);
5851
+ if (m) return { entry, args: m.slice(1) };
5852
+ }
5853
+ }
5854
+ return null;
5855
+ }
5856
+ async function executeSingleStep(step, definitions) {
5857
+ const matched = matchStepDefinition(step.text, definitions);
5858
+ if (!matched) {
5859
+ return { keyword: step.keyword, text: step.text, status: "undefined", durationMs: 0 };
5860
+ }
5861
+ const start = Date.now();
5862
+ try {
5863
+ await matched.entry.handler(matched.args, step.table);
5864
+ return { keyword: step.keyword, text: step.text, status: "passed", durationMs: Date.now() - start };
5865
+ } catch (err) {
5866
+ const msg = err instanceof Error ? err.message : String(err);
5867
+ return {
5868
+ keyword: step.keyword,
5869
+ text: step.text,
5870
+ status: "failed",
5871
+ durationMs: Date.now() - start,
5872
+ errorMessage: msg
5873
+ };
5874
+ }
5875
+ }
5876
+ async function executeScenarioSteps(allSteps, definitions) {
5877
+ const stepResults = [];
5878
+ let status = "passed";
5879
+ for (const step of allSteps) {
5880
+ if (status !== "passed") {
5881
+ stepResults.push({ keyword: step.keyword, text: step.text, status: "skipped", durationMs: 0 });
5882
+ continue;
5883
+ }
5884
+ const res = await executeSingleStep(step, definitions);
5885
+ stepResults.push(res);
5886
+ if (res.status === "failed" || res.status === "undefined") {
5887
+ status = res.status;
5888
+ }
5889
+ }
5890
+ return { stepResults, status };
5891
+ }
5892
+ async function runAllExpandedScenarios(parsed, definitions) {
5893
+ const expanded = expandScenarioOutlines(parsed.scenarios);
5894
+ const out = [];
5895
+ for (const sc of expanded) {
5896
+ const combinedSteps = [...parsed.backgroundSteps, ...sc.steps];
5897
+ const { stepResults, status } = await executeScenarioSteps(combinedSteps, definitions);
5898
+ out.push({ name: sc.name, tags: sc.tags, status, steps: stepResults });
5899
+ }
5900
+ return out;
5901
+ }
5902
+ async function executeBddFeature(featurePath, registry, options = {}) {
5903
+ const rootDir = options.rootDir || process.cwd();
5904
+ const fullPath = path4__namespace.isAbsolute(featurePath) ? featurePath : path4__namespace.join(rootDir, featurePath);
5905
+ const parsed = parseFeatureDocument(fs7__namespace.readFileSync(fullPath, "utf-8"));
5906
+ const scenarios = await runAllExpandedScenarios(parsed, registry.getDefinitions());
5907
+ const report = {
5908
+ uri: path4__namespace.relative(rootDir, fullPath).replace(/\\/g, "/"),
5909
+ featureName: parsed.featureName,
5910
+ tags: parsed.tags,
5911
+ scenarios,
5912
+ passed: scenarios.length > 0 && scenarios.every((s) => s.status === "passed")
5913
+ };
5914
+ if (options.writeReportPath) writeCucumberJsonReport([report], options.writeReportPath);
5915
+ return report;
5916
+ }
5917
+ function mapStepToCucumberJson(st) {
5918
+ return {
5919
+ keyword: `${st.keyword} `,
5920
+ name: st.text,
5921
+ result: {
5922
+ status: st.status,
5923
+ duration: st.durationMs * 1e6,
5924
+ ...st.errorMessage ? { error_message: st.errorMessage } : {}
5925
+ }
5926
+ };
5927
+ }
5928
+ function mapFeatureToCucumberJson(r) {
5929
+ return {
5930
+ uri: r.uri,
5931
+ name: r.featureName,
5932
+ tags: r.tags.map((t) => ({ name: t })),
5933
+ elements: r.scenarios.map((sc) => ({
5934
+ type: "scenario",
5935
+ name: sc.name,
5936
+ tags: sc.tags.map((t) => ({ name: t })),
5937
+ steps: sc.steps.map(mapStepToCucumberJson)
5938
+ }))
5939
+ };
5940
+ }
5941
+ function writeCucumberJsonReport(reports, outputPath) {
5942
+ const dir = path4__namespace.dirname(outputPath);
5943
+ if (!fs7__namespace.existsSync(dir)) fs7__namespace.mkdirSync(dir, { recursive: true });
5944
+ const payload = JSON.stringify(reports.map(mapFeatureToCucumberJson), null, 2);
5945
+ fs7__namespace.writeFileSync(outputPath, payload, "utf-8");
5946
+ }
5535
5947
  function isToolAvailable(toolName) {
5536
5948
  try {
5537
5949
  const cmd = process.platform === "win32" ? "where" : "which";
@@ -5685,6 +6097,214 @@ function scanPnpmStore(pnpmDir, directNames, seen) {
5685
6097
  }
5686
6098
  }
5687
6099
  }
6100
+ function readTextIfExists(filePath) {
6101
+ if (!fs7__namespace.existsSync(filePath)) return null;
6102
+ try {
6103
+ return fs7__namespace.readFileSync(filePath, "utf-8");
6104
+ } catch {
6105
+ return null;
6106
+ }
6107
+ }
6108
+ function extractTomlField(block, key) {
6109
+ const m = block.match(new RegExp(`^\\s*${key}\\s*=\\s*["']([^"']+)["']`, "m"));
6110
+ return m ? m[1].trim() : void 0;
6111
+ }
6112
+ function extractPyprojectDirectDeps(content) {
6113
+ const direct = /* @__PURE__ */ new Set();
6114
+ const matches = content.matchAll(/^([a-zA-Z0-9_.-]+)\s*=\s*(?:["'{]|true)/gm);
6115
+ for (const m of matches) {
6116
+ const key = m[1].toLowerCase();
6117
+ if (!["name", "version", "description", "license", "python", "readme"].includes(key)) {
6118
+ direct.add(key);
6119
+ }
6120
+ }
6121
+ return direct;
6122
+ }
6123
+ function parseSingleTomlBlock(block, lockPath, directNames) {
6124
+ const name = extractTomlField(block, "name");
6125
+ const version = extractTomlField(block, "version");
6126
+ if (!name || !version) return null;
6127
+ const spdx = normalizeSpdxLicense(extractTomlField(block, "license") || "UNKNOWN");
6128
+ return {
6129
+ name,
6130
+ version,
6131
+ spdxLicense: spdx,
6132
+ purl: `pkg:pypi/${name.toLowerCase()}@${version}`,
6133
+ path: lockPath,
6134
+ isDirect: directNames.size === 0 || directNames.has(name.toLowerCase())
6135
+ };
6136
+ }
6137
+ function parseTomlPackageBlocks(content, lockPath, directNames, seen) {
6138
+ for (const block of content.split(/\[\[package\]\]/g).slice(1)) {
6139
+ const dep = parseSingleTomlBlock(block, lockPath, directNames);
6140
+ if (dep && !seen.has(`pypi:${dep.name}@${dep.version}`)) {
6141
+ seen.set(`pypi:${dep.name}@${dep.version}`, dep);
6142
+ }
6143
+ }
6144
+ }
6145
+ function parsePipfileSection(pkgs, isDirect, lockPath, seen) {
6146
+ for (const [name, meta] of Object.entries(pkgs)) {
6147
+ const rawVer = String(meta.version || "").replace(/^==/, "") || "unknown";
6148
+ const spdx = normalizeSpdxLicense(meta.license || "UNKNOWN");
6149
+ const key = `pypi:${name}@${rawVer}`;
6150
+ if (!seen.has(key)) {
6151
+ seen.set(key, {
6152
+ name,
6153
+ version: rawVer,
6154
+ spdxLicense: spdx,
6155
+ purl: `pkg:pypi/${name.toLowerCase()}@${rawVer}`,
6156
+ path: lockPath,
6157
+ isDirect
6158
+ });
6159
+ }
6160
+ }
6161
+ }
6162
+ function scanPythonDependencies(rootDir, seen) {
6163
+ const pyproject = readTextIfExists(path4__namespace.join(rootDir, "pyproject.toml")) || "";
6164
+ const directNames = extractPyprojectDirectDeps(pyproject);
6165
+ for (const lockName of ["poetry.lock", "uv.lock"]) {
6166
+ const lockPath = path4__namespace.join(rootDir, lockName);
6167
+ const content = readTextIfExists(lockPath);
6168
+ if (content) parseTomlPackageBlocks(content, lockPath, directNames, seen);
6169
+ }
6170
+ const pipfileLockPath = path4__namespace.join(rootDir, "Pipfile.lock");
6171
+ const pipfileRaw = readTextIfExists(pipfileLockPath);
6172
+ if (!pipfileRaw) return;
6173
+ try {
6174
+ const data = JSON.parse(pipfileRaw);
6175
+ parsePipfileSection(data.default || {}, true, pipfileLockPath, seen);
6176
+ parsePipfileSection(data.develop || {}, false, pipfileLockPath, seen);
6177
+ } catch {
6178
+ }
6179
+ }
6180
+ function parseGoRequireLine(line, goModPath) {
6181
+ const cleaned = line.replace(/^require\s+/, "").trim();
6182
+ const commentIdx = cleaned.indexOf("//");
6183
+ const codePart = (commentIdx >= 0 ? cleaned.slice(0, commentIdx) : cleaned).trim();
6184
+ const commentPart = commentIdx >= 0 ? cleaned.slice(commentIdx + 2).trim() : "";
6185
+ const parts = codePart.split(/\s+/);
6186
+ if (parts.length < 2) return null;
6187
+ const modName = parts[0];
6188
+ const rawVer = parts[1].replace(/^v/, "");
6189
+ const isIndirect = /\bindirect\b/i.test(commentPart);
6190
+ const licenseMatch = commentPart.match(/(?:spdx|license)\s*:\s*([A-Za-z0-9.+-]+)/i);
6191
+ const spdx = licenseMatch ? normalizeSpdxLicense(licenseMatch[1]) : "UNKNOWN";
6192
+ return {
6193
+ name: modName,
6194
+ version: rawVer,
6195
+ spdxLicense: spdx,
6196
+ purl: `pkg:golang/${modName}@v${rawVer}`,
6197
+ path: goModPath,
6198
+ isDirect: !isIndirect
6199
+ };
6200
+ }
6201
+ function extractGoCandidateLines(content) {
6202
+ const out = [];
6203
+ let inBlock = false;
6204
+ for (const rawLine of content.split(/\r?\n/)) {
6205
+ const trimmed = rawLine.trim();
6206
+ if (/^require\s*\($/.test(trimmed)) inBlock = true;
6207
+ else if (inBlock && trimmed === ")") inBlock = false;
6208
+ else if (inBlock || trimmed.startsWith("require ")) out.push(trimmed);
6209
+ }
6210
+ return out;
6211
+ }
6212
+ function scanGoDependencies(rootDir, seen) {
6213
+ const goModPath = path4__namespace.join(rootDir, "go.mod");
6214
+ const content = readTextIfExists(goModPath);
6215
+ if (!content) return;
6216
+ for (const line of extractGoCandidateLines(content)) {
6217
+ const dep = parseGoRequireLine(line, goModPath);
6218
+ if (dep && !seen.has(`golang:${dep.name}@${dep.version}`)) {
6219
+ seen.set(`golang:${dep.name}@${dep.version}`, dep);
6220
+ }
6221
+ }
6222
+ }
6223
+ function extractCargoDirectDeps(cargoToml) {
6224
+ const map = /* @__PURE__ */ new Map();
6225
+ let inDeps = false;
6226
+ for (const line of cargoToml.split(/\r?\n/)) {
6227
+ const trimmed = line.trim();
6228
+ if (trimmed.startsWith("[")) {
6229
+ inDeps = /^\[(?:dev-|build-)?dependencies\]$/.test(trimmed);
6230
+ continue;
6231
+ }
6232
+ if (!inDeps || !trimmed || trimmed.startsWith("#")) continue;
6233
+ const m = trimmed.match(/^([a-zA-Z0-9_-]+)\s*=\s*(.+)$/);
6234
+ if (m) {
6235
+ const licMatch = m[2].match(/license\s*=\s*["']([^"']+)["']/);
6236
+ map.set(m[1], licMatch ? licMatch[1] : "UNKNOWN");
6237
+ }
6238
+ }
6239
+ return map;
6240
+ }
6241
+ function parseCargoLockBlock(block, rootName, directMeta, cargoLockPath) {
6242
+ const name = extractTomlField(block, "name");
6243
+ const version = extractTomlField(block, "version");
6244
+ if (!name || !version || name === rootName) return null;
6245
+ const blockLic = extractTomlField(block, "license") || directMeta.get(name) || "UNKNOWN";
6246
+ return {
6247
+ name,
6248
+ version,
6249
+ spdxLicense: normalizeSpdxLicense(blockLic),
6250
+ purl: `pkg:cargo/${name}@${version}`,
6251
+ path: cargoLockPath,
6252
+ isDirect: directMeta.size === 0 || directMeta.has(name)
6253
+ };
6254
+ }
6255
+ function scanRustDependencies(rootDir, seen) {
6256
+ const cargoLockPath = path4__namespace.join(rootDir, "Cargo.lock");
6257
+ const lockContent = readTextIfExists(cargoLockPath);
6258
+ if (!lockContent) return;
6259
+ const cargoToml = readTextIfExists(path4__namespace.join(rootDir, "Cargo.toml")) || "";
6260
+ const directMeta = extractCargoDirectDeps(cargoToml);
6261
+ const rootName = extractTomlField(cargoToml, "name");
6262
+ for (const block of lockContent.split(/\[\[package\]\]/g).slice(1)) {
6263
+ const dep = parseCargoLockBlock(block, rootName, directMeta, cargoLockPath);
6264
+ if (dep && !seen.has(`cargo:${dep.name}@${dep.version}`)) {
6265
+ seen.set(`cargo:${dep.name}@${dep.version}`, dep);
6266
+ }
6267
+ }
6268
+ }
6269
+ function extractXmlTag(xml, tag) {
6270
+ const m = xml.match(new RegExp(`<${tag}>([^<]+)</${tag}>`));
6271
+ return m ? m[1].trim() : void 0;
6272
+ }
6273
+ function resolveMavenBlockLicense(block, defaultLic) {
6274
+ const tagLic = extractXmlTag(block, "license");
6275
+ if (tagLic) return tagLic;
6276
+ const commentLic = block.match(/<!--\s*(?:spdx|license)\s*:\s*([A-Za-z0-9.+-]+)\s*-->/i)?.[1];
6277
+ return commentLic || defaultLic;
6278
+ }
6279
+ function parseMavenDepBlock(block, defaultLic, pomPath) {
6280
+ const groupId = extractXmlTag(block, "groupId");
6281
+ const artifactId = extractXmlTag(block, "artifactId");
6282
+ if (!groupId || !artifactId) return null;
6283
+ const version = extractXmlTag(block, "version") || "unknown";
6284
+ const inlineLic = resolveMavenBlockLicense(block, defaultLic);
6285
+ return {
6286
+ name: `${groupId}:${artifactId}`,
6287
+ version,
6288
+ spdxLicense: normalizeSpdxLicense(inlineLic),
6289
+ purl: `pkg:maven/${groupId}/${artifactId}@${version}`,
6290
+ path: pomPath,
6291
+ isDirect: true
6292
+ };
6293
+ }
6294
+ function scanMavenDependencies(rootDir, seen) {
6295
+ const pomPath = path4__namespace.join(rootDir, "pom.xml");
6296
+ const content = readTextIfExists(pomPath);
6297
+ if (!content) return;
6298
+ const projectLicBlock = content.match(/<licenses>([\s\S]*?)<\/licenses>/)?.[1] || "";
6299
+ const defaultLic = extractXmlTag(projectLicBlock, "name") || "UNKNOWN";
6300
+ const depBlocks = content.match(/<dependency>[\s\S]*?<\/dependency>/g) || [];
6301
+ for (const block of depBlocks) {
6302
+ const dep = parseMavenDepBlock(block, defaultLic, pomPath);
6303
+ if (dep && !seen.has(`maven:${dep.name}@${dep.version}`)) {
6304
+ seen.set(`maven:${dep.name}@${dep.version}`, dep);
6305
+ }
6306
+ }
6307
+ }
5688
6308
  function scanWithNativeFs(rootDir, options = {}) {
5689
6309
  const directNames = extractDirectDepNames(rootDir, options.productionOnly);
5690
6310
  const seen = /* @__PURE__ */ new Map();
@@ -5695,6 +6315,10 @@ function scanWithNativeFs(rootDir, options = {}) {
5695
6315
  scanPnpmStore(path4__namespace.join(nmDir, ".pnpm"), directNames, seen);
5696
6316
  }
5697
6317
  }
6318
+ scanPythonDependencies(rootDir, seen);
6319
+ scanGoDependencies(rootDir, seen);
6320
+ scanRustDependencies(rootDir, seen);
6321
+ scanMavenDependencies(rootDir, seen);
5698
6322
  const allDeps = Array.from(seen.values());
5699
6323
  if (options.depth === "direct") {
5700
6324
  return allDeps.filter((d) => d.isDirect);
@@ -5764,10 +6388,17 @@ var SEVERITY_RANK = {
5764
6388
  LOW: 1,
5765
6389
  UNKNOWN: 0
5766
6390
  };
6391
+ function isExemptByVex(v, vexStatements) {
6392
+ if (!vexStatements || vexStatements.length === 0) return false;
6393
+ return vexStatements.some(
6394
+ (stmt) => stmt.id === v.id && (stmt.state === "not_affected" || stmt.state === "fixed") && (!stmt.package || stmt.package === v.packageName)
6395
+ );
6396
+ }
5767
6397
  function evaluateVulnerabilityThresholds(vulnerabilities, options) {
5768
6398
  const minRank = options?.failOnSeverity ? SEVERITY_RANK[options.failOnSeverity] ?? 3 : void 0;
5769
6399
  const maxScore = options?.maxCvssScore;
5770
6400
  const failing = vulnerabilities.filter((v) => {
6401
+ if (isExemptByVex(v, options?.vexStatements)) return false;
5771
6402
  const rankMatch = minRank !== void 0 && SEVERITY_RANK[v.severity] >= minRank;
5772
6403
  const scoreMatch = maxScore !== void 0 && v.cvssV3Score !== void 0 && v.cvssV3Score >= maxScore;
5773
6404
  return rankMatch || scoreMatch;
@@ -5897,42 +6528,77 @@ function buildCycloneDxComponent(dep) {
5897
6528
  }
5898
6529
  return comp;
5899
6530
  }
5900
- function generateCycloneDxSbom(dependencies, options = {}) {
6531
+ function buildVexAnalysis(vex) {
6532
+ if (!vex) return void 0;
6533
+ return {
6534
+ state: vex.state,
6535
+ justification: vex.justification,
6536
+ detail: vex.detail,
6537
+ response: vex.response
6538
+ };
6539
+ }
6540
+ function mapScaVulnToCycloneDx(v, vexStatements) {
6541
+ const matchingVex = vexStatements.find(
6542
+ (stmt) => stmt.id === v.id && (!stmt.package || stmt.package === v.packageName)
6543
+ );
6544
+ return {
6545
+ id: v.id,
6546
+ source: v.primaryUrl ? { name: "NVD/GHSA", url: v.primaryUrl } : void 0,
6547
+ ratings: [{ score: v.cvssV3Score, severity: v.severity.toLowerCase(), method: "CVSSv3", vector: v.cvssV3Vector }],
6548
+ analysis: buildVexAnalysis(matchingVex),
6549
+ affects: [{ ref: `pkg:npm/${v.packageName}@${v.installedVersion}` }]
6550
+ };
6551
+ }
6552
+ function mapStandaloneVex(stmt) {
6553
+ return {
6554
+ id: stmt.id,
6555
+ analysis: buildVexAnalysis(stmt),
6556
+ affects: stmt.package ? [{ ref: stmt.package }] : void 0
6557
+ };
6558
+ }
6559
+ function buildCycloneDxVulnerabilities(vulnerabilities = [], vexStatements = []) {
6560
+ const out = vulnerabilities.map((v) => mapScaVulnToCycloneDx(v, vexStatements));
6561
+ const seenIds = new Set(out.map((item) => item.id));
6562
+ for (const stmt of vexStatements) {
6563
+ if (!seenIds.has(stmt.id)) {
6564
+ out.push(mapStandaloneVex(stmt));
6565
+ seenIds.add(stmt.id);
6566
+ }
6567
+ }
6568
+ return out;
6569
+ }
6570
+ function resolveProjectIdentity(options) {
5901
6571
  let name = options.projectName || "ai-sdlc-project";
5902
6572
  let version = options.projectVersion || "1.0.0";
5903
- if (options.rootDir) {
5904
- const pkgPath = path4__namespace.join(options.rootDir, "package.json");
5905
- if (fs7__namespace.existsSync(pkgPath)) {
5906
- try {
5907
- const pkg = JSON.parse(fs7__namespace.readFileSync(pkgPath, "utf-8"));
5908
- if (pkg.name) name = pkg.name;
5909
- if (pkg.version) version = pkg.version;
5910
- } catch {
5911
- }
5912
- }
6573
+ if (!options.rootDir) return { name, version };
6574
+ const pkgPath = path4__namespace.join(options.rootDir, "package.json");
6575
+ if (!fs7__namespace.existsSync(pkgPath)) return { name, version };
6576
+ try {
6577
+ const pkg = JSON.parse(fs7__namespace.readFileSync(pkgPath, "utf-8"));
6578
+ if (pkg.name) name = pkg.name;
6579
+ if (pkg.version) version = pkg.version;
6580
+ } catch {
5913
6581
  }
5914
- const components = dependencies.map(buildCycloneDxComponent);
6582
+ return { name, version };
6583
+ }
6584
+ function buildBomMetadata(name, version) {
6585
+ return {
6586
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
6587
+ tools: [{ vendor: "AI-SDLC", name: "@aisdlc/core", version: "2.4.0" }],
6588
+ component: { type: "application", name, version }
6589
+ };
6590
+ }
6591
+ function generateCycloneDxSbom(dependencies, options = {}) {
6592
+ const { name, version } = resolveProjectIdentity(options);
6593
+ const cdxVulns = buildCycloneDxVulnerabilities(options.vulnerabilities, options.vexStatements);
5915
6594
  return {
5916
6595
  bomFormat: "CycloneDX",
5917
6596
  specVersion: "1.5",
5918
6597
  version: 1,
5919
6598
  serialNumber: `urn:uuid:${crypto__namespace.randomUUID()}`,
5920
- metadata: {
5921
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
5922
- tools: [
5923
- {
5924
- vendor: "AI-SDLC",
5925
- name: "@aisdlc/core",
5926
- version: "1.0.0"
5927
- }
5928
- ],
5929
- component: {
5930
- type: "application",
5931
- name,
5932
- version
5933
- }
5934
- },
5935
- components
6599
+ metadata: buildBomMetadata(name, version),
6600
+ components: dependencies.map(buildCycloneDxComponent),
6601
+ ...cdxVulns.length > 0 ? { vulnerabilities: cdxVulns } : {}
5936
6602
  };
5937
6603
  }
5938
6604
  function tryAtomicWrite(filePath, content) {
@@ -6100,17 +6766,24 @@ function parseExceptions(exceptions) {
6100
6766
  }
6101
6767
  return map;
6102
6768
  }
6769
+ function parseVexStatements(doc) {
6770
+ const rawList = doc?.vex_statements || doc?.exceptions?.vex_statements || [];
6771
+ if (!Array.isArray(rawList)) return [];
6772
+ return rawList.filter((item) => Boolean(item && typeof item.id === "string" && typeof item.state === "string"));
6773
+ }
6103
6774
  function parseLicensePolicy(content) {
6104
6775
  try {
6105
6776
  const doc = yaml3__default.default.load(content);
6106
6777
  const { permitted, restricted, blocked } = classifyCategories(doc?.categories);
6107
6778
  const exception_packages = parseExceptions(doc?.exceptions);
6779
+ const vex_statements = parseVexStatements(doc);
6108
6780
  return {
6109
6781
  version: doc?.version || "1.0",
6110
6782
  permitted: permitted.length > 0 ? permitted : DEFAULT_PERMITTED,
6111
6783
  restricted: restricted.length > 0 ? restricted : DEFAULT_RESTRICTED,
6112
6784
  blocked: blocked.length > 0 ? blocked : DEFAULT_BLOCKED,
6113
6785
  exception_packages,
6786
+ vex_statements,
6114
6787
  rawPolicy: doc
6115
6788
  };
6116
6789
  } catch {
@@ -6119,7 +6792,8 @@ function parseLicensePolicy(content) {
6119
6792
  permitted: DEFAULT_PERMITTED,
6120
6793
  restricted: DEFAULT_RESTRICTED,
6121
6794
  blocked: DEFAULT_BLOCKED,
6122
- exception_packages: {}
6795
+ exception_packages: {},
6796
+ vex_statements: []
6123
6797
  };
6124
6798
  }
6125
6799
  }
@@ -6294,10 +6968,10 @@ function shouldGenerateSbom(options, rawPolicy) {
6294
6968
  if (options.generateSbom === false) return false;
6295
6969
  return Boolean(options.generateSbom || options.sbomPath || rawPolicy?.ci_gates?.generate_sbom);
6296
6970
  }
6297
- function maybeGenerateSbom(options, rootDir, rawPolicy, dependencies) {
6971
+ function maybeGenerateSbom(options, rootDir, rawPolicy, dependencies, vulnerabilities, vexStatements) {
6298
6972
  if (!shouldGenerateSbom(options, rawPolicy)) return void 0;
6299
6973
  const sbomPath = options.sbomPath || path4__namespace.join(rootDir, "reports", "sbom.cdx.json");
6300
- const sbom = generateCycloneDxSbom(dependencies, { rootDir });
6974
+ const sbom = generateCycloneDxSbom(dependencies, { rootDir, vulnerabilities, vexStatements });
6301
6975
  writeCycloneDxSbom(sbom, sbomPath);
6302
6976
  return sbomPath;
6303
6977
  }
@@ -6310,9 +6984,9 @@ function maybeGenerateNotices(options, rootDir, rawPolicy, dependencies) {
6310
6984
  writeThirdPartyNotices(noticesContent, noticesPath);
6311
6985
  return noticesPath;
6312
6986
  }
6313
- function handleDynamicScaArtifacts(options, rootDir, rawPolicy, dependencies) {
6987
+ function handleDynamicScaArtifacts(options, rootDir, rawPolicy, dependencies, vulnerabilities, vexStatements) {
6314
6988
  return {
6315
- sbomPath: maybeGenerateSbom(options, rootDir, rawPolicy, dependencies),
6989
+ sbomPath: maybeGenerateSbom(options, rootDir, rawPolicy, dependencies, vulnerabilities, vexStatements),
6316
6990
  noticesPath: maybeGenerateNotices(options, rootDir, rawPolicy, dependencies)
6317
6991
  };
6318
6992
  }
@@ -6341,7 +7015,7 @@ function buildSkippedVulnResult() {
6341
7015
  skipReason: "Trivy scanner is not installed on host environment. Vulnerability check gracefully skipped."
6342
7016
  };
6343
7017
  }
6344
- function checkVulnerabilities(rootDir, options) {
7018
+ function checkVulnerabilities(rootDir, options, vexStatements) {
6345
7019
  if (options.scanVulnerabilities === false) {
6346
7020
  return { vulnerabilities: [], blocked: false };
6347
7021
  }
@@ -6349,7 +7023,10 @@ function checkVulnerabilities(rootDir, options) {
6349
7023
  if (!res.executed) {
6350
7024
  return buildSkippedVulnResult();
6351
7025
  }
6352
- const evalResult = evaluateVulnerabilityThresholds(res.vulnerabilities, options.vulnerabilityThresholds);
7026
+ const evalResult = evaluateVulnerabilityThresholds(res.vulnerabilities, {
7027
+ ...options.vulnerabilityThresholds,
7028
+ vexStatements
7029
+ });
6353
7030
  return { vulnerabilities: res.vulnerabilities, blocked: evalResult.blocked };
6354
7031
  }
6355
7032
  function assembleDynamicLicenseResult(dependencies, permittedCount, violations, reportMarkdown, artifacts, vulnData) {
@@ -6373,7 +7050,8 @@ function toCleanPolicy(policyWithRaw) {
6373
7050
  permitted: policyWithRaw.permitted,
6374
7051
  restricted: policyWithRaw.restricted,
6375
7052
  blocked: policyWithRaw.blocked,
6376
- exception_packages: policyWithRaw.exception_packages
7053
+ exception_packages: policyWithRaw.exception_packages,
7054
+ vex_statements: policyWithRaw.vex_statements
6377
7055
  };
6378
7056
  }
6379
7057
  function scanDependenciesForVerification(options, rootDir, rawPolicy) {
@@ -6393,8 +7071,15 @@ function verifyLicenses(options = {}) {
6393
7071
  }
6394
7072
  const dependencies = scanDependenciesForVerification(options, rootDir, policyWithRaw.rawPolicy);
6395
7073
  const { violations, permittedCount } = evaluateDependencyList(dependencies, policy);
6396
- const artifacts = handleDynamicScaArtifacts(options, rootDir, policyWithRaw.rawPolicy, dependencies);
6397
- const vulnData = checkVulnerabilities(rootDir, options);
7074
+ const vulnData = checkVulnerabilities(rootDir, options, policy.vex_statements);
7075
+ const artifacts = handleDynamicScaArtifacts(
7076
+ options,
7077
+ rootDir,
7078
+ policyWithRaw.rawPolicy,
7079
+ dependencies,
7080
+ vulnData.vulnerabilities,
7081
+ policy.vex_statements
7082
+ );
6398
7083
  const reportMarkdown = generateLicenseReportMarkdown(dependencies, violations, policy, artifacts.sbomPath);
6399
7084
  return assembleDynamicLicenseResult(dependencies, permittedCount, violations, reportMarkdown, artifacts, vulnData);
6400
7085
  }
@@ -15934,9 +16619,149 @@ function initProject(options = {}) {
15934
16619
  tryInstallGitHook(destDir, options.dryRun)
15935
16620
  );
15936
16621
  }
16622
+ var DEFAULT_DLP_RULES = [
16623
+ {
16624
+ id: "DLP-001-PRIVATE-KEY",
16625
+ description: "PEM / RSA / EC / OPENSSH Private Key Block",
16626
+ pattern: "-----BEGIN (?:[A-Z0-9_-]+ )?PRIVATE KEY-----[\\s\\S]*?-----END (?:[A-Z0-9_-]+ )?PRIVATE KEY-----",
16627
+ replacement: "[REDACTED:PRIVATE_KEY]",
16628
+ enabled: true
16629
+ },
16630
+ {
16631
+ id: "DLP-002-API-TOKEN",
16632
+ description: "Cloud, GitHub, Slack, OpenAI, Stripe, and AWS Access Tokens",
16633
+ pattern: "\\b(?:gh[pousr]_[A-Za-z0-9_]{36,255}|github_pat_[0-9a-zA-Z_]{82}|sk-(?:proj-|live-)?[a-zA-Z0-9_-]{32,}|AKIA[0-9A-Z]{16}|xox[baprs]-[0-9]{10,13}-[0-9]{10,13}[a-zA-Z0-9-]*|(?:sk|rk)_(?:live|test)_[0-9a-zA-Z]{24,99})\\b",
16634
+ replacement: "[REDACTED:API_TOKEN]",
16635
+ enabled: true
16636
+ },
16637
+ {
16638
+ id: "DLP-003-JWT",
16639
+ description: "Plaintext JSON Web Token (JWT)",
16640
+ pattern: "\\beyJ[A-Za-z0-9_-]{10,}\\.eyJ[A-Za-z0-9_-]{10,}\\.[A-Za-z0-9_-]{10,}\\b",
16641
+ replacement: "[REDACTED:JWT_TOKEN]",
16642
+ enabled: true
16643
+ },
16644
+ {
16645
+ id: "DLP-004-CREDIT-CARD",
16646
+ description: "Payment Card Number (13-19 digits formatted)",
16647
+ pattern: "\\b(?:\\d{4}[- ]\\d{4}[- ]\\d{4}[- ]\\d{4}|\\d{16})\\b",
16648
+ replacement: "[REDACTED:CREDIT_CARD]",
16649
+ enabled: true
16650
+ },
16651
+ {
16652
+ id: "DLP-005-EMAIL",
16653
+ description: "Personal or Corporate Email Address",
16654
+ pattern: "\\b[A-Za-z0-9._%+-]+@(?!example\\.com|localhost|test\\.local)[A-Za-z0-9.-]+\\.[A-Za-z]{2,}\\b",
16655
+ replacement: "[REDACTED:EMAIL]",
16656
+ enabled: true
16657
+ },
16658
+ {
16659
+ id: "DLP-006-INTERNAL-URL",
16660
+ description: "Internal Intranet / Corp Service URL",
16661
+ pattern: "https?://(?:[A-Za-z0-9._-]+:[A-Za-z0-9._-]+@|[A-Za-z0-9.-]+\\.(?:internal|corp|intranet|local)\\b)[^\\s\"'`<>]*",
16662
+ replacement: "[REDACTED:INTERNAL_URL]",
16663
+ enabled: true
16664
+ }
16665
+ ];
16666
+ function createDefaultDlpPolicy() {
16667
+ return {
16668
+ version: "1.0",
16669
+ enabled: true,
16670
+ zero_data_retention_required: true,
16671
+ rules: [...DEFAULT_DLP_RULES]
16672
+ };
16673
+ }
16674
+ function normalizeParsedRule(raw, idx) {
16675
+ if (typeof raw.pattern !== "string" || !raw.pattern) return null;
16676
+ return {
16677
+ id: typeof raw.id === "string" ? raw.id : `DLP-CUSTOM-${idx + 1}`,
16678
+ description: typeof raw.description === "string" ? raw.description : "Custom DLP rule",
16679
+ pattern: raw.pattern,
16680
+ replacement: typeof raw.replacement === "string" ? raw.replacement : "[REDACTED]",
16681
+ enabled: raw.enabled !== false
16682
+ };
16683
+ }
16684
+ function parseDlpPolicy(yamlContent) {
16685
+ const fallback = createDefaultDlpPolicy();
16686
+ if (!yamlContent || !yamlContent.trim()) return fallback;
16687
+ try {
16688
+ const doc = yaml3__default.default.load(yamlContent);
16689
+ if (!doc || typeof doc !== "object") return fallback;
16690
+ const rawRules = Array.isArray(doc.rules) ? doc.rules : [];
16691
+ const parsedRules = rawRules.map((r, i) => normalizeParsedRule(r, i)).filter((r) => r !== null);
16692
+ return {
16693
+ version: typeof doc.version === "string" ? doc.version : "1.0",
16694
+ enabled: doc.enabled !== false,
16695
+ zero_data_retention_required: Boolean(doc.zero_data_retention_required ?? true),
16696
+ rules: parsedRules.length > 0 ? parsedRules : fallback.rules
16697
+ };
16698
+ } catch {
16699
+ return fallback;
16700
+ }
16701
+ }
16702
+ function loadDlpPolicy(rootDir, customPolicyPath) {
16703
+ const base = rootDir || process.cwd();
16704
+ const filePath = customPolicyPath ? path4__namespace.isAbsolute(customPolicyPath) ? customPolicyPath : path4__namespace.join(base, customPolicyPath) : path4__namespace.join(base, "dlp-policy.yaml");
16705
+ if (!fs7__namespace.existsSync(filePath)) return createDefaultDlpPolicy();
16706
+ try {
16707
+ return parseDlpPolicy(fs7__namespace.readFileSync(filePath, "utf-8"));
16708
+ } catch {
16709
+ return createDefaultDlpPolicy();
16710
+ }
16711
+ }
16712
+ function applySingleDlpRule(text, rule) {
16713
+ if (rule.enabled === false) return { output: text, matches: 0 };
16714
+ try {
16715
+ const regex = new RegExp(rule.pattern, "g");
16716
+ let matches = 0;
16717
+ const output = text.replace(regex, () => {
16718
+ matches++;
16719
+ return rule.replacement;
16720
+ });
16721
+ return { output, matches };
16722
+ } catch {
16723
+ return { output: text, matches: 0 };
16724
+ }
16725
+ }
16726
+ function sanitizeDlpText(text, policy = createDefaultDlpPolicy()) {
16727
+ if (!policy.enabled || !text) {
16728
+ return { sanitizedText: text, redactionCount: 0, triggeredRuleIds: [] };
16729
+ }
16730
+ let current = text;
16731
+ let totalRedactions = 0;
16732
+ const triggered = [];
16733
+ for (const rule of policy.rules) {
16734
+ const res = applySingleDlpRule(current, rule);
16735
+ if (res.matches > 0) {
16736
+ current = res.output;
16737
+ totalRedactions += res.matches;
16738
+ triggered.push(rule.id);
16739
+ }
16740
+ }
16741
+ return {
16742
+ sanitizedText: current,
16743
+ redactionCount: totalRedactions,
16744
+ triggeredRuleIds: triggered
16745
+ };
16746
+ }
16747
+ function sanitizeDlpPayload(payload, policy = createDefaultDlpPolicy()) {
16748
+ if (!policy.enabled || payload === null || payload === void 0) return payload;
16749
+ if (typeof payload === "string") {
16750
+ return sanitizeDlpText(payload, policy).sanitizedText;
16751
+ }
16752
+ const serialized = JSON.stringify(payload);
16753
+ const { sanitizedText } = sanitizeDlpText(serialized, policy);
16754
+ try {
16755
+ return JSON.parse(sanitizedText);
16756
+ } catch {
16757
+ return payload;
16758
+ }
16759
+ }
15937
16760
 
15938
16761
  exports.ALL_ARCHITECTURE_TEMPLATES = ALL_ARCHITECTURE_TEMPLATES;
15939
16762
  exports.BRANCH_PATTERNS = BRANCH_PATTERNS;
16763
+ exports.BddStepRegistry = BddStepRegistry;
16764
+ exports.DEFAULT_DLP_RULES = DEFAULT_DLP_RULES;
15940
16765
  exports.DEFAULT_EXCLUDED_PATTERNS = DEFAULT_EXCLUDED_PATTERNS;
15941
16766
  exports.DEFAULT_EXCLUDES = DEFAULT_EXCLUDES;
15942
16767
  exports.DEFAULT_EXTENSIONS = DEFAULT_EXTENSIONS;
@@ -16082,6 +16907,7 @@ exports.analyzeBddVariantsForRequirement = analyzeBddVariantsForRequirement;
16082
16907
  exports.appendRevisionHistoryRow = appendRevisionHistoryRow;
16083
16908
  exports.applyRatchetMode = applyRatchetMode;
16084
16909
  exports.buildCycloneDxComponent = buildCycloneDxComponent;
16910
+ exports.buildCycloneDxVulnerabilities = buildCycloneDxVulnerabilities;
16085
16911
  exports.buildFeatureFileContent = buildFeatureFileContent;
16086
16912
  exports.buildGraphElements = buildGraphElements;
16087
16913
  exports.calculateJaccardSimilarity = calculateJaccardSimilarity;
@@ -16098,6 +16924,7 @@ exports.collectHistoricalKpis = collectHistoricalKpis;
16098
16924
  exports.computeCanonicalSha256 = computeCanonicalSha256;
16099
16925
  exports.computeSha256 = computeSha256;
16100
16926
  exports.computeViolationSignature = computeViolationSignature;
16927
+ exports.createDefaultDlpPolicy = createDefaultDlpPolicy;
16101
16928
  exports.createDefaultQualityPolicy = createDefaultQualityPolicy;
16102
16929
  exports.depositProductHandoffSidecar = depositProductHandoffSidecar;
16103
16930
  exports.detectActiveChangeForIntegration = detectActiveChangeForIntegration;
@@ -16109,6 +16936,7 @@ exports.emitCiOutput = emitCiOutput;
16109
16936
  exports.evaluateCompoundLicense = evaluateCompoundLicense;
16110
16937
  exports.evaluateSingleDependency = evaluateSingleDependency;
16111
16938
  exports.evaluateVulnerabilityThresholds = evaluateVulnerabilityThresholds;
16939
+ exports.executeBddFeature = executeBddFeature;
16112
16940
  exports.extractBaselineEntriesFromResults = extractBaselineEntriesFromResults;
16113
16941
  exports.extractCitations = extractCitations;
16114
16942
  exports.extractCommitKpisFromRange = extractCommitKpisFromRange;
@@ -16163,6 +16991,7 @@ exports.isPlaceholderOrIgnored = isPlaceholderOrIgnored;
16163
16991
  exports.isRequirementArtifact = isRequirementArtifact2;
16164
16992
  exports.isSemgrepInstalled = isSemgrepInstalled;
16165
16993
  exports.isToolAvailable = isToolAvailable;
16994
+ exports.loadDlpPolicy = loadDlpPolicy;
16166
16995
  exports.loadPrivacyPolicy = loadPrivacyPolicy;
16167
16996
  exports.loadProductHandoffSidecar = loadProductHandoffSidecar;
16168
16997
  exports.loadQualityBaseline = loadQualityBaseline;
@@ -16172,6 +17001,8 @@ exports.maskSecret = maskSecret;
16172
17001
  exports.normalizeSpdxLicense = normalizeSpdxLicense;
16173
17002
  exports.normalizeUri = normalizeUri;
16174
17003
  exports.parseCommitTrailers = parseCommitTrailers;
17004
+ exports.parseDlpPolicy = parseDlpPolicy;
17005
+ exports.parseFeatureDocument = parseFeatureDocument;
16175
17006
  exports.parseFrontmatter = parseFrontmatter;
16176
17007
  exports.parseFrontmatterId = parseFrontmatterId;
16177
17008
  exports.parseGenericDoc = parseGenericDoc;
@@ -16213,15 +17044,21 @@ exports.resolveSchemaForArtifact = resolveSchemaForArtifact;
16213
17044
  exports.resolveTargetFiles = resolveTargetFiles;
16214
17045
  exports.runGitleaksCli = runGitleaksCli;
16215
17046
  exports.runSemgrepCli = runSemgrepCli;
17047
+ exports.sanitizeDlpPayload = sanitizeDlpPayload;
17048
+ exports.sanitizeDlpText = sanitizeDlpText;
16216
17049
  exports.scaffoldSddChange = scaffoldSddChange;
16217
17050
  exports.scanAllProductHandoffs = scanAllProductHandoffs;
16218
17051
  exports.scanAllTestArtifacts = scanAllTestArtifacts;
16219
17052
  exports.scanContentForSecrets = scanContentForSecrets;
16220
17053
  exports.scanFileForPrivacyViolations = scanFileForPrivacyViolations;
16221
17054
  exports.scanFileForSast = scanFileForSast;
17055
+ exports.scanGoDependencies = scanGoDependencies;
16222
17056
  exports.scanInstalledLicenses = scanInstalledLicenses;
16223
17057
  exports.scanManualsInWorkspace = scanManualsInWorkspace;
17058
+ exports.scanMavenDependencies = scanMavenDependencies;
16224
17059
  exports.scanPnpmStore = scanPnpmStore;
17060
+ exports.scanPythonDependencies = scanPythonDependencies;
17061
+ exports.scanRustDependencies = scanRustDependencies;
16225
17062
  exports.scanSubdirectories = scanSubdirectories;
16226
17063
  exports.scanWithNativeFs = scanWithNativeFs;
16227
17064
  exports.scanWithSyft = scanWithSyft;
@@ -16252,6 +17089,7 @@ exports.walkMdFiles = walkMdFiles;
16252
17089
  exports.walkProjectFiles = walkProjectFiles;
16253
17090
  exports.walkSourceFiles = walkSourceFiles;
16254
17091
  exports.walkTaskFiles = walkTaskFiles;
17092
+ exports.writeCucumberJsonReport = writeCucumberJsonReport;
16255
17093
  exports.writeCycloneDxSbom = writeCycloneDxSbom;
16256
17094
  exports.writeQualityBaseline = writeQualityBaseline;
16257
17095
  exports.writeReleaseKpiReport = writeReleaseKpiReport;