@aisdlc/core 2.2.4 → 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/dist/index.cjs CHANGED
@@ -5685,6 +5685,214 @@ function scanPnpmStore(pnpmDir, directNames, seen) {
5685
5685
  }
5686
5686
  }
5687
5687
  }
5688
+ function readTextIfExists(filePath) {
5689
+ if (!fs7__namespace.existsSync(filePath)) return null;
5690
+ try {
5691
+ return fs7__namespace.readFileSync(filePath, "utf-8");
5692
+ } catch {
5693
+ return null;
5694
+ }
5695
+ }
5696
+ function extractTomlField(block, key) {
5697
+ const m = block.match(new RegExp(`^\\s*${key}\\s*=\\s*["']([^"']+)["']`, "m"));
5698
+ return m ? m[1].trim() : void 0;
5699
+ }
5700
+ function extractPyprojectDirectDeps(content) {
5701
+ const direct = /* @__PURE__ */ new Set();
5702
+ const matches = content.matchAll(/^([a-zA-Z0-9_.-]+)\s*=\s*(?:["'{]|true)/gm);
5703
+ for (const m of matches) {
5704
+ const key = m[1].toLowerCase();
5705
+ if (!["name", "version", "description", "license", "python", "readme"].includes(key)) {
5706
+ direct.add(key);
5707
+ }
5708
+ }
5709
+ return direct;
5710
+ }
5711
+ function parseSingleTomlBlock(block, lockPath, directNames) {
5712
+ const name = extractTomlField(block, "name");
5713
+ const version = extractTomlField(block, "version");
5714
+ if (!name || !version) return null;
5715
+ const spdx = normalizeSpdxLicense(extractTomlField(block, "license") || "UNKNOWN");
5716
+ return {
5717
+ name,
5718
+ version,
5719
+ spdxLicense: spdx,
5720
+ purl: `pkg:pypi/${name.toLowerCase()}@${version}`,
5721
+ path: lockPath,
5722
+ isDirect: directNames.size === 0 || directNames.has(name.toLowerCase())
5723
+ };
5724
+ }
5725
+ function parseTomlPackageBlocks(content, lockPath, directNames, seen) {
5726
+ for (const block of content.split(/\[\[package\]\]/g).slice(1)) {
5727
+ const dep = parseSingleTomlBlock(block, lockPath, directNames);
5728
+ if (dep && !seen.has(`pypi:${dep.name}@${dep.version}`)) {
5729
+ seen.set(`pypi:${dep.name}@${dep.version}`, dep);
5730
+ }
5731
+ }
5732
+ }
5733
+ function parsePipfileSection(pkgs, isDirect, lockPath, seen) {
5734
+ for (const [name, meta] of Object.entries(pkgs)) {
5735
+ const rawVer = String(meta.version || "").replace(/^==/, "") || "unknown";
5736
+ const spdx = normalizeSpdxLicense(meta.license || "UNKNOWN");
5737
+ const key = `pypi:${name}@${rawVer}`;
5738
+ if (!seen.has(key)) {
5739
+ seen.set(key, {
5740
+ name,
5741
+ version: rawVer,
5742
+ spdxLicense: spdx,
5743
+ purl: `pkg:pypi/${name.toLowerCase()}@${rawVer}`,
5744
+ path: lockPath,
5745
+ isDirect
5746
+ });
5747
+ }
5748
+ }
5749
+ }
5750
+ function scanPythonDependencies(rootDir, seen) {
5751
+ const pyproject = readTextIfExists(path4__namespace.join(rootDir, "pyproject.toml")) || "";
5752
+ const directNames = extractPyprojectDirectDeps(pyproject);
5753
+ for (const lockName of ["poetry.lock", "uv.lock"]) {
5754
+ const lockPath = path4__namespace.join(rootDir, lockName);
5755
+ const content = readTextIfExists(lockPath);
5756
+ if (content) parseTomlPackageBlocks(content, lockPath, directNames, seen);
5757
+ }
5758
+ const pipfileLockPath = path4__namespace.join(rootDir, "Pipfile.lock");
5759
+ const pipfileRaw = readTextIfExists(pipfileLockPath);
5760
+ if (!pipfileRaw) return;
5761
+ try {
5762
+ const data = JSON.parse(pipfileRaw);
5763
+ parsePipfileSection(data.default || {}, true, pipfileLockPath, seen);
5764
+ parsePipfileSection(data.develop || {}, false, pipfileLockPath, seen);
5765
+ } catch {
5766
+ }
5767
+ }
5768
+ function parseGoRequireLine(line, goModPath) {
5769
+ const cleaned = line.replace(/^require\s+/, "").trim();
5770
+ const commentIdx = cleaned.indexOf("//");
5771
+ const codePart = (commentIdx >= 0 ? cleaned.slice(0, commentIdx) : cleaned).trim();
5772
+ const commentPart = commentIdx >= 0 ? cleaned.slice(commentIdx + 2).trim() : "";
5773
+ const parts = codePart.split(/\s+/);
5774
+ if (parts.length < 2) return null;
5775
+ const modName = parts[0];
5776
+ const rawVer = parts[1].replace(/^v/, "");
5777
+ const isIndirect = /\bindirect\b/i.test(commentPart);
5778
+ const licenseMatch = commentPart.match(/(?:spdx|license)\s*:\s*([A-Za-z0-9.+-]+)/i);
5779
+ const spdx = licenseMatch ? normalizeSpdxLicense(licenseMatch[1]) : "UNKNOWN";
5780
+ return {
5781
+ name: modName,
5782
+ version: rawVer,
5783
+ spdxLicense: spdx,
5784
+ purl: `pkg:golang/${modName}@v${rawVer}`,
5785
+ path: goModPath,
5786
+ isDirect: !isIndirect
5787
+ };
5788
+ }
5789
+ function extractGoCandidateLines(content) {
5790
+ const out = [];
5791
+ let inBlock = false;
5792
+ for (const rawLine of content.split(/\r?\n/)) {
5793
+ const trimmed = rawLine.trim();
5794
+ if (/^require\s*\($/.test(trimmed)) inBlock = true;
5795
+ else if (inBlock && trimmed === ")") inBlock = false;
5796
+ else if (inBlock || trimmed.startsWith("require ")) out.push(trimmed);
5797
+ }
5798
+ return out;
5799
+ }
5800
+ function scanGoDependencies(rootDir, seen) {
5801
+ const goModPath = path4__namespace.join(rootDir, "go.mod");
5802
+ const content = readTextIfExists(goModPath);
5803
+ if (!content) return;
5804
+ for (const line of extractGoCandidateLines(content)) {
5805
+ const dep = parseGoRequireLine(line, goModPath);
5806
+ if (dep && !seen.has(`golang:${dep.name}@${dep.version}`)) {
5807
+ seen.set(`golang:${dep.name}@${dep.version}`, dep);
5808
+ }
5809
+ }
5810
+ }
5811
+ function extractCargoDirectDeps(cargoToml) {
5812
+ const map = /* @__PURE__ */ new Map();
5813
+ let inDeps = false;
5814
+ for (const line of cargoToml.split(/\r?\n/)) {
5815
+ const trimmed = line.trim();
5816
+ if (trimmed.startsWith("[")) {
5817
+ inDeps = /^\[(?:dev-|build-)?dependencies\]$/.test(trimmed);
5818
+ continue;
5819
+ }
5820
+ if (!inDeps || !trimmed || trimmed.startsWith("#")) continue;
5821
+ const m = trimmed.match(/^([a-zA-Z0-9_-]+)\s*=\s*(.+)$/);
5822
+ if (m) {
5823
+ const licMatch = m[2].match(/license\s*=\s*["']([^"']+)["']/);
5824
+ map.set(m[1], licMatch ? licMatch[1] : "UNKNOWN");
5825
+ }
5826
+ }
5827
+ return map;
5828
+ }
5829
+ function parseCargoLockBlock(block, rootName, directMeta, cargoLockPath) {
5830
+ const name = extractTomlField(block, "name");
5831
+ const version = extractTomlField(block, "version");
5832
+ if (!name || !version || name === rootName) return null;
5833
+ const blockLic = extractTomlField(block, "license") || directMeta.get(name) || "UNKNOWN";
5834
+ return {
5835
+ name,
5836
+ version,
5837
+ spdxLicense: normalizeSpdxLicense(blockLic),
5838
+ purl: `pkg:cargo/${name}@${version}`,
5839
+ path: cargoLockPath,
5840
+ isDirect: directMeta.size === 0 || directMeta.has(name)
5841
+ };
5842
+ }
5843
+ function scanRustDependencies(rootDir, seen) {
5844
+ const cargoLockPath = path4__namespace.join(rootDir, "Cargo.lock");
5845
+ const lockContent = readTextIfExists(cargoLockPath);
5846
+ if (!lockContent) return;
5847
+ const cargoToml = readTextIfExists(path4__namespace.join(rootDir, "Cargo.toml")) || "";
5848
+ const directMeta = extractCargoDirectDeps(cargoToml);
5849
+ const rootName = extractTomlField(cargoToml, "name");
5850
+ for (const block of lockContent.split(/\[\[package\]\]/g).slice(1)) {
5851
+ const dep = parseCargoLockBlock(block, rootName, directMeta, cargoLockPath);
5852
+ if (dep && !seen.has(`cargo:${dep.name}@${dep.version}`)) {
5853
+ seen.set(`cargo:${dep.name}@${dep.version}`, dep);
5854
+ }
5855
+ }
5856
+ }
5857
+ function extractXmlTag(xml, tag) {
5858
+ const m = xml.match(new RegExp(`<${tag}>([^<]+)</${tag}>`));
5859
+ return m ? m[1].trim() : void 0;
5860
+ }
5861
+ function resolveMavenBlockLicense(block, defaultLic) {
5862
+ const tagLic = extractXmlTag(block, "license");
5863
+ if (tagLic) return tagLic;
5864
+ const commentLic = block.match(/<!--\s*(?:spdx|license)\s*:\s*([A-Za-z0-9.+-]+)\s*-->/i)?.[1];
5865
+ return commentLic || defaultLic;
5866
+ }
5867
+ function parseMavenDepBlock(block, defaultLic, pomPath) {
5868
+ const groupId = extractXmlTag(block, "groupId");
5869
+ const artifactId = extractXmlTag(block, "artifactId");
5870
+ if (!groupId || !artifactId) return null;
5871
+ const version = extractXmlTag(block, "version") || "unknown";
5872
+ const inlineLic = resolveMavenBlockLicense(block, defaultLic);
5873
+ return {
5874
+ name: `${groupId}:${artifactId}`,
5875
+ version,
5876
+ spdxLicense: normalizeSpdxLicense(inlineLic),
5877
+ purl: `pkg:maven/${groupId}/${artifactId}@${version}`,
5878
+ path: pomPath,
5879
+ isDirect: true
5880
+ };
5881
+ }
5882
+ function scanMavenDependencies(rootDir, seen) {
5883
+ const pomPath = path4__namespace.join(rootDir, "pom.xml");
5884
+ const content = readTextIfExists(pomPath);
5885
+ if (!content) return;
5886
+ const projectLicBlock = content.match(/<licenses>([\s\S]*?)<\/licenses>/)?.[1] || "";
5887
+ const defaultLic = extractXmlTag(projectLicBlock, "name") || "UNKNOWN";
5888
+ const depBlocks = content.match(/<dependency>[\s\S]*?<\/dependency>/g) || [];
5889
+ for (const block of depBlocks) {
5890
+ const dep = parseMavenDepBlock(block, defaultLic, pomPath);
5891
+ if (dep && !seen.has(`maven:${dep.name}@${dep.version}`)) {
5892
+ seen.set(`maven:${dep.name}@${dep.version}`, dep);
5893
+ }
5894
+ }
5895
+ }
5688
5896
  function scanWithNativeFs(rootDir, options = {}) {
5689
5897
  const directNames = extractDirectDepNames(rootDir, options.productionOnly);
5690
5898
  const seen = /* @__PURE__ */ new Map();
@@ -5695,6 +5903,10 @@ function scanWithNativeFs(rootDir, options = {}) {
5695
5903
  scanPnpmStore(path4__namespace.join(nmDir, ".pnpm"), directNames, seen);
5696
5904
  }
5697
5905
  }
5906
+ scanPythonDependencies(rootDir, seen);
5907
+ scanGoDependencies(rootDir, seen);
5908
+ scanRustDependencies(rootDir, seen);
5909
+ scanMavenDependencies(rootDir, seen);
5698
5910
  const allDeps = Array.from(seen.values());
5699
5911
  if (options.depth === "direct") {
5700
5912
  return allDeps.filter((d) => d.isDirect);
@@ -5764,10 +5976,17 @@ var SEVERITY_RANK = {
5764
5976
  LOW: 1,
5765
5977
  UNKNOWN: 0
5766
5978
  };
5979
+ function isExemptByVex(v, vexStatements) {
5980
+ if (!vexStatements || vexStatements.length === 0) return false;
5981
+ return vexStatements.some(
5982
+ (stmt) => stmt.id === v.id && (stmt.state === "not_affected" || stmt.state === "fixed") && (!stmt.package || stmt.package === v.packageName)
5983
+ );
5984
+ }
5767
5985
  function evaluateVulnerabilityThresholds(vulnerabilities, options) {
5768
5986
  const minRank = options?.failOnSeverity ? SEVERITY_RANK[options.failOnSeverity] ?? 3 : void 0;
5769
5987
  const maxScore = options?.maxCvssScore;
5770
5988
  const failing = vulnerabilities.filter((v) => {
5989
+ if (isExemptByVex(v, options?.vexStatements)) return false;
5771
5990
  const rankMatch = minRank !== void 0 && SEVERITY_RANK[v.severity] >= minRank;
5772
5991
  const scoreMatch = maxScore !== void 0 && v.cvssV3Score !== void 0 && v.cvssV3Score >= maxScore;
5773
5992
  return rankMatch || scoreMatch;
@@ -5897,42 +6116,77 @@ function buildCycloneDxComponent(dep) {
5897
6116
  }
5898
6117
  return comp;
5899
6118
  }
5900
- function generateCycloneDxSbom(dependencies, options = {}) {
6119
+ function buildVexAnalysis(vex) {
6120
+ if (!vex) return void 0;
6121
+ return {
6122
+ state: vex.state,
6123
+ justification: vex.justification,
6124
+ detail: vex.detail,
6125
+ response: vex.response
6126
+ };
6127
+ }
6128
+ function mapScaVulnToCycloneDx(v, vexStatements) {
6129
+ const matchingVex = vexStatements.find(
6130
+ (stmt) => stmt.id === v.id && (!stmt.package || stmt.package === v.packageName)
6131
+ );
6132
+ return {
6133
+ id: v.id,
6134
+ source: v.primaryUrl ? { name: "NVD/GHSA", url: v.primaryUrl } : void 0,
6135
+ ratings: [{ score: v.cvssV3Score, severity: v.severity.toLowerCase(), method: "CVSSv3", vector: v.cvssV3Vector }],
6136
+ analysis: buildVexAnalysis(matchingVex),
6137
+ affects: [{ ref: `pkg:npm/${v.packageName}@${v.installedVersion}` }]
6138
+ };
6139
+ }
6140
+ function mapStandaloneVex(stmt) {
6141
+ return {
6142
+ id: stmt.id,
6143
+ analysis: buildVexAnalysis(stmt),
6144
+ affects: stmt.package ? [{ ref: stmt.package }] : void 0
6145
+ };
6146
+ }
6147
+ function buildCycloneDxVulnerabilities(vulnerabilities = [], vexStatements = []) {
6148
+ const out = vulnerabilities.map((v) => mapScaVulnToCycloneDx(v, vexStatements));
6149
+ const seenIds = new Set(out.map((item) => item.id));
6150
+ for (const stmt of vexStatements) {
6151
+ if (!seenIds.has(stmt.id)) {
6152
+ out.push(mapStandaloneVex(stmt));
6153
+ seenIds.add(stmt.id);
6154
+ }
6155
+ }
6156
+ return out;
6157
+ }
6158
+ function resolveProjectIdentity(options) {
5901
6159
  let name = options.projectName || "ai-sdlc-project";
5902
6160
  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
- }
6161
+ if (!options.rootDir) return { name, version };
6162
+ const pkgPath = path4__namespace.join(options.rootDir, "package.json");
6163
+ if (!fs7__namespace.existsSync(pkgPath)) return { name, version };
6164
+ try {
6165
+ const pkg = JSON.parse(fs7__namespace.readFileSync(pkgPath, "utf-8"));
6166
+ if (pkg.name) name = pkg.name;
6167
+ if (pkg.version) version = pkg.version;
6168
+ } catch {
5913
6169
  }
5914
- const components = dependencies.map(buildCycloneDxComponent);
6170
+ return { name, version };
6171
+ }
6172
+ function buildBomMetadata(name, version) {
6173
+ return {
6174
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
6175
+ tools: [{ vendor: "AI-SDLC", name: "@aisdlc/core", version: "2.3.0" }],
6176
+ component: { type: "application", name, version }
6177
+ };
6178
+ }
6179
+ function generateCycloneDxSbom(dependencies, options = {}) {
6180
+ const { name, version } = resolveProjectIdentity(options);
6181
+ const cdxVulns = buildCycloneDxVulnerabilities(options.vulnerabilities, options.vexStatements);
5915
6182
  return {
5916
6183
  bomFormat: "CycloneDX",
5917
6184
  specVersion: "1.5",
5918
6185
  version: 1,
5919
6186
  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
6187
+ metadata: buildBomMetadata(name, version),
6188
+ components: dependencies.map(buildCycloneDxComponent),
6189
+ ...cdxVulns.length > 0 ? { vulnerabilities: cdxVulns } : {}
5936
6190
  };
5937
6191
  }
5938
6192
  function tryAtomicWrite(filePath, content) {
@@ -6100,17 +6354,24 @@ function parseExceptions(exceptions) {
6100
6354
  }
6101
6355
  return map;
6102
6356
  }
6357
+ function parseVexStatements(doc) {
6358
+ const rawList = doc?.vex_statements || doc?.exceptions?.vex_statements || [];
6359
+ if (!Array.isArray(rawList)) return [];
6360
+ return rawList.filter((item) => Boolean(item && typeof item.id === "string" && typeof item.state === "string"));
6361
+ }
6103
6362
  function parseLicensePolicy(content) {
6104
6363
  try {
6105
6364
  const doc = yaml3__default.default.load(content);
6106
6365
  const { permitted, restricted, blocked } = classifyCategories(doc?.categories);
6107
6366
  const exception_packages = parseExceptions(doc?.exceptions);
6367
+ const vex_statements = parseVexStatements(doc);
6108
6368
  return {
6109
6369
  version: doc?.version || "1.0",
6110
6370
  permitted: permitted.length > 0 ? permitted : DEFAULT_PERMITTED,
6111
6371
  restricted: restricted.length > 0 ? restricted : DEFAULT_RESTRICTED,
6112
6372
  blocked: blocked.length > 0 ? blocked : DEFAULT_BLOCKED,
6113
6373
  exception_packages,
6374
+ vex_statements,
6114
6375
  rawPolicy: doc
6115
6376
  };
6116
6377
  } catch {
@@ -6119,7 +6380,8 @@ function parseLicensePolicy(content) {
6119
6380
  permitted: DEFAULT_PERMITTED,
6120
6381
  restricted: DEFAULT_RESTRICTED,
6121
6382
  blocked: DEFAULT_BLOCKED,
6122
- exception_packages: {}
6383
+ exception_packages: {},
6384
+ vex_statements: []
6123
6385
  };
6124
6386
  }
6125
6387
  }
@@ -6294,10 +6556,10 @@ function shouldGenerateSbom(options, rawPolicy) {
6294
6556
  if (options.generateSbom === false) return false;
6295
6557
  return Boolean(options.generateSbom || options.sbomPath || rawPolicy?.ci_gates?.generate_sbom);
6296
6558
  }
6297
- function maybeGenerateSbom(options, rootDir, rawPolicy, dependencies) {
6559
+ function maybeGenerateSbom(options, rootDir, rawPolicy, dependencies, vulnerabilities, vexStatements) {
6298
6560
  if (!shouldGenerateSbom(options, rawPolicy)) return void 0;
6299
6561
  const sbomPath = options.sbomPath || path4__namespace.join(rootDir, "reports", "sbom.cdx.json");
6300
- const sbom = generateCycloneDxSbom(dependencies, { rootDir });
6562
+ const sbom = generateCycloneDxSbom(dependencies, { rootDir, vulnerabilities, vexStatements });
6301
6563
  writeCycloneDxSbom(sbom, sbomPath);
6302
6564
  return sbomPath;
6303
6565
  }
@@ -6310,9 +6572,9 @@ function maybeGenerateNotices(options, rootDir, rawPolicy, dependencies) {
6310
6572
  writeThirdPartyNotices(noticesContent, noticesPath);
6311
6573
  return noticesPath;
6312
6574
  }
6313
- function handleDynamicScaArtifacts(options, rootDir, rawPolicy, dependencies) {
6575
+ function handleDynamicScaArtifacts(options, rootDir, rawPolicy, dependencies, vulnerabilities, vexStatements) {
6314
6576
  return {
6315
- sbomPath: maybeGenerateSbom(options, rootDir, rawPolicy, dependencies),
6577
+ sbomPath: maybeGenerateSbom(options, rootDir, rawPolicy, dependencies, vulnerabilities, vexStatements),
6316
6578
  noticesPath: maybeGenerateNotices(options, rootDir, rawPolicy, dependencies)
6317
6579
  };
6318
6580
  }
@@ -6341,7 +6603,7 @@ function buildSkippedVulnResult() {
6341
6603
  skipReason: "Trivy scanner is not installed on host environment. Vulnerability check gracefully skipped."
6342
6604
  };
6343
6605
  }
6344
- function checkVulnerabilities(rootDir, options) {
6606
+ function checkVulnerabilities(rootDir, options, vexStatements) {
6345
6607
  if (options.scanVulnerabilities === false) {
6346
6608
  return { vulnerabilities: [], blocked: false };
6347
6609
  }
@@ -6349,7 +6611,10 @@ function checkVulnerabilities(rootDir, options) {
6349
6611
  if (!res.executed) {
6350
6612
  return buildSkippedVulnResult();
6351
6613
  }
6352
- const evalResult = evaluateVulnerabilityThresholds(res.vulnerabilities, options.vulnerabilityThresholds);
6614
+ const evalResult = evaluateVulnerabilityThresholds(res.vulnerabilities, {
6615
+ ...options.vulnerabilityThresholds,
6616
+ vexStatements
6617
+ });
6353
6618
  return { vulnerabilities: res.vulnerabilities, blocked: evalResult.blocked };
6354
6619
  }
6355
6620
  function assembleDynamicLicenseResult(dependencies, permittedCount, violations, reportMarkdown, artifacts, vulnData) {
@@ -6373,7 +6638,8 @@ function toCleanPolicy(policyWithRaw) {
6373
6638
  permitted: policyWithRaw.permitted,
6374
6639
  restricted: policyWithRaw.restricted,
6375
6640
  blocked: policyWithRaw.blocked,
6376
- exception_packages: policyWithRaw.exception_packages
6641
+ exception_packages: policyWithRaw.exception_packages,
6642
+ vex_statements: policyWithRaw.vex_statements
6377
6643
  };
6378
6644
  }
6379
6645
  function scanDependenciesForVerification(options, rootDir, rawPolicy) {
@@ -6393,8 +6659,15 @@ function verifyLicenses(options = {}) {
6393
6659
  }
6394
6660
  const dependencies = scanDependenciesForVerification(options, rootDir, policyWithRaw.rawPolicy);
6395
6661
  const { violations, permittedCount } = evaluateDependencyList(dependencies, policy);
6396
- const artifacts = handleDynamicScaArtifacts(options, rootDir, policyWithRaw.rawPolicy, dependencies);
6397
- const vulnData = checkVulnerabilities(rootDir, options);
6662
+ const vulnData = checkVulnerabilities(rootDir, options, policy.vex_statements);
6663
+ const artifacts = handleDynamicScaArtifacts(
6664
+ options,
6665
+ rootDir,
6666
+ policyWithRaw.rawPolicy,
6667
+ dependencies,
6668
+ vulnData.vulnerabilities,
6669
+ policy.vex_statements
6670
+ );
6398
6671
  const reportMarkdown = generateLicenseReportMarkdown(dependencies, violations, policy, artifacts.sbomPath);
6399
6672
  return assembleDynamicLicenseResult(dependencies, permittedCount, violations, reportMarkdown, artifacts, vulnData);
6400
6673
  }
@@ -15934,9 +16207,148 @@ function initProject(options = {}) {
15934
16207
  tryInstallGitHook(destDir, options.dryRun)
15935
16208
  );
15936
16209
  }
16210
+ var DEFAULT_DLP_RULES = [
16211
+ {
16212
+ id: "DLP-001-PRIVATE-KEY",
16213
+ description: "PEM / RSA / EC / OPENSSH Private Key Block",
16214
+ pattern: "-----BEGIN (?:[A-Z0-9_-]+ )?PRIVATE KEY-----[\\s\\S]*?-----END (?:[A-Z0-9_-]+ )?PRIVATE KEY-----",
16215
+ replacement: "[REDACTED:PRIVATE_KEY]",
16216
+ enabled: true
16217
+ },
16218
+ {
16219
+ id: "DLP-002-API-TOKEN",
16220
+ description: "Cloud, GitHub, Slack, OpenAI, Stripe, and AWS Access Tokens",
16221
+ 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",
16222
+ replacement: "[REDACTED:API_TOKEN]",
16223
+ enabled: true
16224
+ },
16225
+ {
16226
+ id: "DLP-003-JWT",
16227
+ description: "Plaintext JSON Web Token (JWT)",
16228
+ pattern: "\\beyJ[A-Za-z0-9_-]{10,}\\.eyJ[A-Za-z0-9_-]{10,}\\.[A-Za-z0-9_-]{10,}\\b",
16229
+ replacement: "[REDACTED:JWT_TOKEN]",
16230
+ enabled: true
16231
+ },
16232
+ {
16233
+ id: "DLP-004-CREDIT-CARD",
16234
+ description: "Payment Card Number (13-19 digits formatted)",
16235
+ pattern: "\\b(?:\\d{4}[- ]\\d{4}[- ]\\d{4}[- ]\\d{4}|\\d{16})\\b",
16236
+ replacement: "[REDACTED:CREDIT_CARD]",
16237
+ enabled: true
16238
+ },
16239
+ {
16240
+ id: "DLP-005-EMAIL",
16241
+ description: "Personal or Corporate Email Address",
16242
+ pattern: "\\b[A-Za-z0-9._%+-]+@(?!example\\.com|localhost|test\\.local)[A-Za-z0-9.-]+\\.[A-Za-z]{2,}\\b",
16243
+ replacement: "[REDACTED:EMAIL]",
16244
+ enabled: true
16245
+ },
16246
+ {
16247
+ id: "DLP-006-INTERNAL-URL",
16248
+ description: "Internal Intranet / Corp Service URL",
16249
+ pattern: "https?://(?:[A-Za-z0-9._-]+:[A-Za-z0-9._-]+@|[A-Za-z0-9.-]+\\.(?:internal|corp|intranet|local)\\b)[^\\s\"'`<>]*",
16250
+ replacement: "[REDACTED:INTERNAL_URL]",
16251
+ enabled: true
16252
+ }
16253
+ ];
16254
+ function createDefaultDlpPolicy() {
16255
+ return {
16256
+ version: "1.0",
16257
+ enabled: true,
16258
+ zero_data_retention_required: true,
16259
+ rules: [...DEFAULT_DLP_RULES]
16260
+ };
16261
+ }
16262
+ function normalizeParsedRule(raw, idx) {
16263
+ if (typeof raw.pattern !== "string" || !raw.pattern) return null;
16264
+ return {
16265
+ id: typeof raw.id === "string" ? raw.id : `DLP-CUSTOM-${idx + 1}`,
16266
+ description: typeof raw.description === "string" ? raw.description : "Custom DLP rule",
16267
+ pattern: raw.pattern,
16268
+ replacement: typeof raw.replacement === "string" ? raw.replacement : "[REDACTED]",
16269
+ enabled: raw.enabled !== false
16270
+ };
16271
+ }
16272
+ function parseDlpPolicy(yamlContent) {
16273
+ const fallback = createDefaultDlpPolicy();
16274
+ if (!yamlContent || !yamlContent.trim()) return fallback;
16275
+ try {
16276
+ const doc = yaml3__default.default.load(yamlContent);
16277
+ if (!doc || typeof doc !== "object") return fallback;
16278
+ const rawRules = Array.isArray(doc.rules) ? doc.rules : [];
16279
+ const parsedRules = rawRules.map((r, i) => normalizeParsedRule(r, i)).filter((r) => r !== null);
16280
+ return {
16281
+ version: typeof doc.version === "string" ? doc.version : "1.0",
16282
+ enabled: doc.enabled !== false,
16283
+ zero_data_retention_required: Boolean(doc.zero_data_retention_required ?? true),
16284
+ rules: parsedRules.length > 0 ? parsedRules : fallback.rules
16285
+ };
16286
+ } catch {
16287
+ return fallback;
16288
+ }
16289
+ }
16290
+ function loadDlpPolicy(rootDir, customPolicyPath) {
16291
+ const base = rootDir || process.cwd();
16292
+ const filePath = customPolicyPath ? path4__namespace.isAbsolute(customPolicyPath) ? customPolicyPath : path4__namespace.join(base, customPolicyPath) : path4__namespace.join(base, "dlp-policy.yaml");
16293
+ if (!fs7__namespace.existsSync(filePath)) return createDefaultDlpPolicy();
16294
+ try {
16295
+ return parseDlpPolicy(fs7__namespace.readFileSync(filePath, "utf-8"));
16296
+ } catch {
16297
+ return createDefaultDlpPolicy();
16298
+ }
16299
+ }
16300
+ function applySingleDlpRule(text, rule) {
16301
+ if (rule.enabled === false) return { output: text, matches: 0 };
16302
+ try {
16303
+ const regex = new RegExp(rule.pattern, "g");
16304
+ let matches = 0;
16305
+ const output = text.replace(regex, () => {
16306
+ matches++;
16307
+ return rule.replacement;
16308
+ });
16309
+ return { output, matches };
16310
+ } catch {
16311
+ return { output: text, matches: 0 };
16312
+ }
16313
+ }
16314
+ function sanitizeDlpText(text, policy = createDefaultDlpPolicy()) {
16315
+ if (!policy.enabled || !text) {
16316
+ return { sanitizedText: text, redactionCount: 0, triggeredRuleIds: [] };
16317
+ }
16318
+ let current = text;
16319
+ let totalRedactions = 0;
16320
+ const triggered = [];
16321
+ for (const rule of policy.rules) {
16322
+ const res = applySingleDlpRule(current, rule);
16323
+ if (res.matches > 0) {
16324
+ current = res.output;
16325
+ totalRedactions += res.matches;
16326
+ triggered.push(rule.id);
16327
+ }
16328
+ }
16329
+ return {
16330
+ sanitizedText: current,
16331
+ redactionCount: totalRedactions,
16332
+ triggeredRuleIds: triggered
16333
+ };
16334
+ }
16335
+ function sanitizeDlpPayload(payload, policy = createDefaultDlpPolicy()) {
16336
+ if (!policy.enabled || payload === null || payload === void 0) return payload;
16337
+ if (typeof payload === "string") {
16338
+ return sanitizeDlpText(payload, policy).sanitizedText;
16339
+ }
16340
+ const serialized = JSON.stringify(payload);
16341
+ const { sanitizedText } = sanitizeDlpText(serialized, policy);
16342
+ try {
16343
+ return JSON.parse(sanitizedText);
16344
+ } catch {
16345
+ return payload;
16346
+ }
16347
+ }
15937
16348
 
15938
16349
  exports.ALL_ARCHITECTURE_TEMPLATES = ALL_ARCHITECTURE_TEMPLATES;
15939
16350
  exports.BRANCH_PATTERNS = BRANCH_PATTERNS;
16351
+ exports.DEFAULT_DLP_RULES = DEFAULT_DLP_RULES;
15940
16352
  exports.DEFAULT_EXCLUDED_PATTERNS = DEFAULT_EXCLUDED_PATTERNS;
15941
16353
  exports.DEFAULT_EXCLUDES = DEFAULT_EXCLUDES;
15942
16354
  exports.DEFAULT_EXTENSIONS = DEFAULT_EXTENSIONS;
@@ -16082,6 +16494,7 @@ exports.analyzeBddVariantsForRequirement = analyzeBddVariantsForRequirement;
16082
16494
  exports.appendRevisionHistoryRow = appendRevisionHistoryRow;
16083
16495
  exports.applyRatchetMode = applyRatchetMode;
16084
16496
  exports.buildCycloneDxComponent = buildCycloneDxComponent;
16497
+ exports.buildCycloneDxVulnerabilities = buildCycloneDxVulnerabilities;
16085
16498
  exports.buildFeatureFileContent = buildFeatureFileContent;
16086
16499
  exports.buildGraphElements = buildGraphElements;
16087
16500
  exports.calculateJaccardSimilarity = calculateJaccardSimilarity;
@@ -16098,6 +16511,7 @@ exports.collectHistoricalKpis = collectHistoricalKpis;
16098
16511
  exports.computeCanonicalSha256 = computeCanonicalSha256;
16099
16512
  exports.computeSha256 = computeSha256;
16100
16513
  exports.computeViolationSignature = computeViolationSignature;
16514
+ exports.createDefaultDlpPolicy = createDefaultDlpPolicy;
16101
16515
  exports.createDefaultQualityPolicy = createDefaultQualityPolicy;
16102
16516
  exports.depositProductHandoffSidecar = depositProductHandoffSidecar;
16103
16517
  exports.detectActiveChangeForIntegration = detectActiveChangeForIntegration;
@@ -16163,6 +16577,7 @@ exports.isPlaceholderOrIgnored = isPlaceholderOrIgnored;
16163
16577
  exports.isRequirementArtifact = isRequirementArtifact2;
16164
16578
  exports.isSemgrepInstalled = isSemgrepInstalled;
16165
16579
  exports.isToolAvailable = isToolAvailable;
16580
+ exports.loadDlpPolicy = loadDlpPolicy;
16166
16581
  exports.loadPrivacyPolicy = loadPrivacyPolicy;
16167
16582
  exports.loadProductHandoffSidecar = loadProductHandoffSidecar;
16168
16583
  exports.loadQualityBaseline = loadQualityBaseline;
@@ -16172,6 +16587,7 @@ exports.maskSecret = maskSecret;
16172
16587
  exports.normalizeSpdxLicense = normalizeSpdxLicense;
16173
16588
  exports.normalizeUri = normalizeUri;
16174
16589
  exports.parseCommitTrailers = parseCommitTrailers;
16590
+ exports.parseDlpPolicy = parseDlpPolicy;
16175
16591
  exports.parseFrontmatter = parseFrontmatter;
16176
16592
  exports.parseFrontmatterId = parseFrontmatterId;
16177
16593
  exports.parseGenericDoc = parseGenericDoc;
@@ -16213,15 +16629,21 @@ exports.resolveSchemaForArtifact = resolveSchemaForArtifact;
16213
16629
  exports.resolveTargetFiles = resolveTargetFiles;
16214
16630
  exports.runGitleaksCli = runGitleaksCli;
16215
16631
  exports.runSemgrepCli = runSemgrepCli;
16632
+ exports.sanitizeDlpPayload = sanitizeDlpPayload;
16633
+ exports.sanitizeDlpText = sanitizeDlpText;
16216
16634
  exports.scaffoldSddChange = scaffoldSddChange;
16217
16635
  exports.scanAllProductHandoffs = scanAllProductHandoffs;
16218
16636
  exports.scanAllTestArtifacts = scanAllTestArtifacts;
16219
16637
  exports.scanContentForSecrets = scanContentForSecrets;
16220
16638
  exports.scanFileForPrivacyViolations = scanFileForPrivacyViolations;
16221
16639
  exports.scanFileForSast = scanFileForSast;
16640
+ exports.scanGoDependencies = scanGoDependencies;
16222
16641
  exports.scanInstalledLicenses = scanInstalledLicenses;
16223
16642
  exports.scanManualsInWorkspace = scanManualsInWorkspace;
16643
+ exports.scanMavenDependencies = scanMavenDependencies;
16224
16644
  exports.scanPnpmStore = scanPnpmStore;
16645
+ exports.scanPythonDependencies = scanPythonDependencies;
16646
+ exports.scanRustDependencies = scanRustDependencies;
16225
16647
  exports.scanSubdirectories = scanSubdirectories;
16226
16648
  exports.scanWithNativeFs = scanWithNativeFs;
16227
16649
  exports.scanWithSyft = scanWithSyft;