@mitre/hdf-converters 3.5.1 → 3.6.0-rc.2

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.js CHANGED
@@ -1,6 +1,6 @@
1
- import { n as detectFormat, r as detectConverter, t as registerAllFingerprints } from "./register-all-As5mLT-V.js";
1
+ import { n as detectFormat, r as detectConverter, t as registerAllFingerprints } from "./register-all-CDfYvwQF.js";
2
2
  import { flattenOverlays } from "@mitre/hdf-parsers";
3
- import { buildCsv, buildXml, computeCvssScore, computeEffectiveStatus, cvssScoreToSeverity, formatTimestamp, formatTimestampSeconds, impactToSeverity, normalizeHdfTimestamps, parseCsv, parseJSON, parsePurl, parseTimestamp, parseXml, parseXmlWithArrays, roundImpact, severityToImpact, sha256, stripHtml as stripHTML, trimUtcFraction, worstStatus } from "@mitre/hdf-utilities";
3
+ import { buildCsv, buildXml, computeCvssScore, computeEffectiveStatus, cvssScoreToSeverity, formatTimestamp, formatTimestampSeconds, governingStatusOverride, impactToSeverity, isUnratedSeverity, normalizeHdfTimestamps, parseCsv, parseJSON, parsePurl, parseTimestamp, parseXml, parseXmlWithArrays, replaceHtmlTags, roundImpact, severityToImpact, severityToImpactWithAliases, sha256, stripHtml, stripHtml as stripHTML, trimUtcFraction, worstStatus } from "@mitre/hdf-utilities";
4
4
  import { Applicability, AuthorizationStatus, CVSSSeverity, CategorizationLevel, ControlType, Ecosystem, EvidenceType, HashAlgorithm, IdentityType, Justification, MilestoneStatus, OverrideType, PlanType, ResultStatus, Severity, TargetType, VerificationMethodEnum, Version, createDescription, createMinimalBaseline, createRequirement, createResult, severityToImpact as severityToImpact$1 } from "@mitre/hdf-schema";
5
5
  import { DEFAULT_COMPONENT_MANAGEMENT_NIST_TAGS, DEFAULT_CONFIG_MANAGEMENT_NIST_TAGS, DEFAULT_REMEDIATION_NIST_TAGS, DEFAULT_STATIC_ANALYSIS_NIST_TAGS, DEFAULT_STATIC_ANALYSIS_NIST_TAGS as DEFAULT_STATIC_ANALYSIS_NIST_TAGS$1, awsConfigMappedRevisions, getAwsConfigNistControlByIdentifier, getAwsConfigNistControlByName, getAwsConfigNistControlsBySubstring, getCCINistMappings, getCurrentNistRevision, getCweNistControl, getHipcheckNistControls, getNessusNistControl, getNiktoNistControl, getOwaspNistControl, getScoutsuiteNistControl, isNistStrict, nistToCci } from "@mitre/hdf-mappings";
6
6
  //#region shared/typescript/converterutil.ts
@@ -26,6 +26,32 @@ async function inputChecksum(input) {
26
26
  value: await sha256(input)
27
27
  };
28
28
  }
29
+ const DIGEST_HASH_ALGORITHMS = {
30
+ sha256: HashAlgorithm.Sha256,
31
+ sha384: HashAlgorithm.Sha384,
32
+ sha512: HashAlgorithm.Sha512,
33
+ blake3: HashAlgorithm.Blake3
34
+ };
35
+ /**
36
+ * Convert a "<algo>:<hex>" digest into a Component.integrity checksum, folding
37
+ * the algorithm prefix into Checksum.algorithm. Returns undefined for an empty
38
+ * digest, one with no algorithm prefix, or one whose algorithm the closed
39
+ * Hash_Algorithm enum cannot represent (e.g. sha1/md5) — it never mislabels a
40
+ * digest under a wrong algorithm. Callers whose digest is embedded in a larger
41
+ * reference ("name@sha256:hex") must extract the "<algo>:<hex>" portion first.
42
+ * Go peer: DigestToChecksums.
43
+ */
44
+ function digestToChecksums(digest) {
45
+ if (!digest) return void 0;
46
+ const i = digest.indexOf(":");
47
+ if (i < 0) return void 0;
48
+ const algorithm = DIGEST_HASH_ALGORITHMS[digest.slice(0, i)];
49
+ if (!algorithm) return void 0;
50
+ return [{
51
+ algorithm,
52
+ value: digest.slice(i + 1)
53
+ }];
54
+ }
29
55
  /**
30
56
  * Compute an Integrity object (for root-level document integrity) from raw input.
31
57
  *
@@ -165,6 +191,37 @@ function parseHdf(input) {
165
191
  return parseJSON(normalizeHdfTimestamps(input));
166
192
  }
167
193
  /**
194
+ * The shared structural-input guard for HDF Results exporters: the prologue
195
+ * every exporter needs — empty guard, input-size validation, parse, and a
196
+ * top-level `baselines` array check — returning the parsed document and its
197
+ * baselines. Extracted from exportmap.runExport so non-exportmap exporters stop
198
+ * reinventing (and skipping) it. The "missing baselines field" wording matches
199
+ * exportmap and hdf-to-oscal-sar, so there is no consumer-visible message churn.
200
+ */
201
+ function requireHdfResults(input, converterName) {
202
+ return requireHdfStructure(input, converterName, "baselines");
203
+ }
204
+ /**
205
+ * Reject malformed HDF input (empty, oversized, non-JSON, top-level array/null,
206
+ * or a missing/wrong-typed structural key) before a converter zero-fills it into
207
+ * a typed document. A parsed JSON `null` (or array, or primitive) is NOT an HDF
208
+ * document object, so it produces the canonical missing-field error rather than
209
+ * a TypeError — matching Go's nil-map no-op on `null`.
210
+ */
211
+ function requireHdfStructure(input, converterName, field) {
212
+ if (input.length === 0) throw new Error(`${converterName}: empty input`);
213
+ validateInputSize(input, converterName);
214
+ const parsed = parseHdf(input);
215
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) throw new Error(`${converterName}: invalid HDF structure: missing ${field} field`);
216
+ const doc = parsed;
217
+ const items = doc[field];
218
+ if (!Array.isArray(items)) throw new Error(`${converterName}: invalid HDF structure: missing ${field} field`);
219
+ return {
220
+ doc,
221
+ items
222
+ };
223
+ }
224
+ /**
168
225
  * Read a timestamp field off a parsed HDF document.
169
226
  *
170
227
  * The generated schema types these as `Date` (startTime, appliedAt, ...), but
@@ -241,7 +298,7 @@ function ecosystemFromPurlType(type) {
241
298
  * - purl alone
242
299
  * - cpe alone
243
300
  */
244
- function buildAffectedPackage$1(opts) {
301
+ function buildAffectedPackage$2(opts) {
245
302
  const pkg = {};
246
303
  if (opts.purl) pkg.purl = opts.purl;
247
304
  if (opts.cpe) pkg.cpe = opts.cpe;
@@ -444,6 +501,16 @@ function buildNoFindingsRequirement(id, codeDesc, startTime) {
444
501
  tags: {}
445
502
  };
446
503
  }
504
+ /**
505
+ * Shared marker converters emit when a source severity is unrated
506
+ * (isUnratedSeverity), so a defaulted impact stays distinguishable from a
507
+ * genuine rated medium. TS peer of MarkUnratedSeverity in converterutil.go.
508
+ */
509
+ const UNRATED_SEVERITY_TAG = "severity_rating";
510
+ const UNRATED_SEVERITY_VALUE = "unrated";
511
+ function markUnratedSeverity(tags, severity) {
512
+ if (isUnratedSeverity(severity)) tags[UNRATED_SEVERITY_TAG] = UNRATED_SEVERITY_VALUE;
513
+ }
447
514
  //#endregion
448
515
  //#region converters/legacyhdf-to-hdf/typescript/converter.ts
449
516
  /**
@@ -457,7 +524,7 @@ function buildNoFindingsRequirement(id, codeDesc, startTime) {
457
524
  * - Overlay flattening: merge overlay/wrapper baselines so every requirement has results
458
525
  */
459
526
  /** Valid severity values per HDF v2.0 schema (matches the Go converter). */
460
- const VALID_SEVERITIES = /* @__PURE__ */ new Set([
527
+ const VALID_SEVERITIES$1 = /* @__PURE__ */ new Set([
461
528
  "critical",
462
529
  "high",
463
530
  "medium",
@@ -471,7 +538,7 @@ const VALID_SEVERITIES = /* @__PURE__ */ new Set([
471
538
  function tagSeverityToSeverity(tagSeverity) {
472
539
  if (typeof tagSeverity !== "string") return null;
473
540
  const normalized = tagSeverity.toLowerCase().trim();
474
- return VALID_SEVERITIES.has(normalized) ? normalized : null;
541
+ return VALID_SEVERITIES$1.has(normalized) ? normalized : null;
475
542
  }
476
543
  /**
477
544
  * Normalize status values from v1.0 to v2.0 format.
@@ -489,8 +556,9 @@ function normalizeStatus$1(status) {
489
556
  }
490
557
  /**
491
558
  * Compute effectiveStatus from impact and v2 results via the canonical
492
- * worst-wins ordering in @mitre/hdf-utilities (impact=0 → notApplicable;
493
- * error > failed > passed > notApplicable > notReviewed).
559
+ * worst-wins ordering in @mitre/hdf-utilities (impact=0 → notApplicable
560
+ * unless the roll-up is error; error > failed > passed > notApplicable >
561
+ * notReviewed).
494
562
  */
495
563
  function computeEffectiveStatus$1(impact, results) {
496
564
  return computeEffectiveStatus({
@@ -499,6 +567,18 @@ function computeEffectiveStatus$1(impact, results) {
499
567
  });
500
568
  }
501
569
  /**
570
+ * Encodes a control-level execution error the source tool reported but did not
571
+ * record as a result, reusing the earliest recorded result time (matching the
572
+ * Go converter).
573
+ */
574
+ function synthesizedErrorResult(results) {
575
+ return {
576
+ status: "error",
577
+ codeDesc: "Synthesized from the source control-level error status: the tool reported an execution error not reflected in any recorded result",
578
+ startTime: results.map((r) => r.startTime).filter((t) => typeof t === "string" && t !== "" && t !== "0001-01-01T00:00:00Z").sort()[0] ?? "0001-01-01T00:00:00Z"
579
+ };
580
+ }
581
+ /**
502
582
  * Convert v1.0 result to v2.0 result.
503
583
  * Transforms snake_case field names to camelCase.
504
584
  */
@@ -592,9 +672,10 @@ function convertControl(v1Control) {
592
672
  if (refs) v2Req.refs = refs;
593
673
  }
594
674
  if (v1Control.source_location !== void 0) v2Req.sourceLocation = v1Control.source_location;
595
- if (v1Control.status !== void 0) v2Req.effectiveStatus = normalizeStatus$1(v1Control.status);
675
+ const controlLevelError = v1Control.status !== void 0 && normalizeStatus$1(v1Control.status) === "error";
596
676
  if (v1Control.results && Array.isArray(v1Control.results)) v2Req.results = v1Control.results.map(convertResult);
597
- if (!v2Req.effectiveStatus) v2Req.effectiveStatus = computeEffectiveStatus$1(v1Control.impact, v2Req.results ?? []);
677
+ if (controlLevelError && !(v2Req.results ?? []).some((r) => r.status === "error")) v2Req.results = [...v2Req.results ?? [], synthesizedErrorResult(v2Req.results ?? [])];
678
+ v2Req.effectiveStatus = computeEffectiveStatus$1(v1Control.impact, v2Req.results ?? []);
598
679
  v2Req.severity = tagSeverityToSeverity(v1Control.tags?.severity) ?? impactToSeverity(v1Control.impact);
599
680
  const rawNist = v1Control.tags?.nist;
600
681
  if (Array.isArray(rawNist)) {
@@ -774,7 +855,7 @@ function documentTimestamp(v1Data) {
774
855
  }
775
856
  return latest ? formatTimestamp(latest) : void 0;
776
857
  }
777
- function convertV1ToV2(v1Data, converterVersion = "1.0.0") {
858
+ function convertLegacyHdf(v1Data, converterVersion = "1.0.0") {
778
859
  validateInputSize(JSON.stringify(v1Data), "legacyhdf-to-hdf");
779
860
  const v2 = {
780
861
  baselines: (v1Data.profiles || []).map(convertProfile),
@@ -820,7 +901,7 @@ function convertV1ToV2(v1Data, converterVersion = "1.0.0") {
820
901
  * @param data - Unknown data to validate
821
902
  * @returns true if data looks like HDF v1.0
822
903
  */
823
- function isHDFV1(data) {
904
+ function isLegacyHdf(data) {
824
905
  if (typeof data !== "object" || data === null) return false;
825
906
  const obj = data;
826
907
  return typeof obj.version === "string" && Array.isArray(obj.profiles) && typeof obj.platform === "object" && obj.platform !== null;
@@ -1194,7 +1275,7 @@ function trivyLocation(o) {
1194
1275
  if (el && el !== "0" && el !== sl) loc += `-${el}`;
1195
1276
  return loc;
1196
1277
  }
1197
- function buildRequirement$17(id, group) {
1278
+ function buildRequirement$19(id, group) {
1198
1279
  const primary = group[0];
1199
1280
  const impact = Math.max(...group.map(findingImpact));
1200
1281
  const descriptions = [{
@@ -1241,7 +1322,7 @@ function buildBaseline$1(name, findings, resultsChecksum) {
1241
1322
  groups.set(id, [f]);
1242
1323
  }
1243
1324
  }
1244
- return createMinimalBaseline(name, order.map((id) => buildRequirement$17(id, groups.get(id))), { resultsChecksum });
1325
+ return createMinimalBaseline(name, order.map((id) => buildRequirement$19(id, groups.get(id))), { resultsChecksum });
1245
1326
  }
1246
1327
  /** Converts an ASFF document to HDF Results JSON. */
1247
1328
  async function convertAsffToHdf(input, converterVersion = "1.0.0") {
@@ -1289,7 +1370,7 @@ async function convertAsffToHdf(input, converterVersion = "1.0.0") {
1289
1370
  }
1290
1371
  //#endregion
1291
1372
  //#region converters/sarif-to-hdf/typescript/converter.ts
1292
- const IMPACT_MAPPING$9 = {
1373
+ const SARIF_ALIASES = {
1293
1374
  error: .7,
1294
1375
  warning: .5,
1295
1376
  note: .3
@@ -1354,26 +1435,28 @@ function convertRun(run, version, resultsChecksum, timestamp) {
1354
1435
  }
1355
1436
  function buildRuleMap(run) {
1356
1437
  const map = /* @__PURE__ */ new Map();
1357
- if (run.tool?.driver?.rules) for (const rule of run.tool.driver.rules) map.set(rule.id, rule);
1438
+ for (const ext of run.tool?.extensions ?? []) for (const rule of ext.rules ?? []) map.set(rule.id, rule);
1439
+ for (const rule of run.tool?.driver?.rules ?? []) map.set(rule.id, rule);
1358
1440
  return map;
1359
1441
  }
1360
1442
  function convertResultGroup(ruleId, rule, sarifResults, timestamp) {
1361
1443
  const firstResult = sarifResults[0];
1362
1444
  const { title, description } = deriveMetadata(firstResult, rule);
1363
1445
  let cweIds = extractCweFromRule(rule);
1364
- if (cweIds.length === 0) cweIds = extractCweIds(resolveMessageText(firstResult.message, rule));
1446
+ if (cweIds.length === 0) cweIds = extractCweIds$1(resolveMessageText(firstResult.message, rule));
1365
1447
  const nistControls = mapCWEToNIST(cweIds, DEFAULT_STATIC_ANALYSIS_NIST_TAGS);
1366
1448
  const cciControls = nistToCci(nistControls);
1367
1449
  const ruleLevel = resolveRuleLevel(rule, sarifResults);
1368
- const impact = IMPACT_MAPPING$9[ruleLevel] || .1;
1450
+ const impact = (securitySeverityImpact(rule) ?? severityToImpactWithAliases(ruleLevel, SARIF_ALIASES, 0)) || .1;
1369
1451
  const sourceLocation = firstResult.locations && firstResult.locations.length > 0 ? extractSourceLocation$1(firstResult.locations[0]) : void 0;
1370
1452
  const results = [];
1371
1453
  for (const sr of sarifResults) results.push(...convertSarifResultToHDFResults(sr));
1372
1454
  const descriptions = buildDescriptions$2(description, rule, firstResult);
1373
1455
  const allSuppressions = sarifResults.flatMap((sr) => sr.suppressions ?? []);
1374
- const options = { tags: buildTags$3(firstResult, rule, ruleLevel, cweIds, nistControls, cciControls, allSuppressions) };
1456
+ const options = { tags: buildTags$4(firstResult, rule, ruleLevel, cweIds, nistControls, cciControls, allSuppressions) };
1375
1457
  if (sourceLocation) options.sourceLocation = sourceLocation;
1376
1458
  const req = createRequirement(ruleId, title, descriptions, impact, results, options);
1459
+ req.severity = impactToSeverity(impact);
1377
1460
  const controlType = deriveControlTypeFromTags(nistControls);
1378
1461
  if (controlType !== void 0) req.controlType = controlType;
1379
1462
  req.verificationMethod = VerificationMethodEnum.Automated;
@@ -1431,7 +1514,7 @@ function packageFromSarifProperties(props) {
1431
1514
  if (props.purl) ecosystem = ecosystemFromPurlType(parsePurl(props.purl)?.type);
1432
1515
  else if (props.ecosystem) ecosystem = ecosystemFromPurlType(props.ecosystem);
1433
1516
  else if (name && version) ecosystem = Ecosystem.Generic;
1434
- return buildAffectedPackage$1({
1517
+ return buildAffectedPackage$2({
1435
1518
  name,
1436
1519
  version,
1437
1520
  ecosystem,
@@ -1440,6 +1523,20 @@ function packageFromSarifProperties(props) {
1440
1523
  fixedInVersion: props.fixedInVersion
1441
1524
  });
1442
1525
  }
1526
+ const DECIMAL_SCORE = /^[+-]?(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?$/;
1527
+ function securitySeverityImpact(rule) {
1528
+ const raw = rule?.properties?.["security-severity"];
1529
+ if (raw === void 0 || raw === null) return void 0;
1530
+ let score;
1531
+ if (typeof raw === "string") {
1532
+ const trimmed = raw.trim();
1533
+ if (!DECIMAL_SCORE.test(trimmed)) return void 0;
1534
+ score = Number(trimmed);
1535
+ } else if (typeof raw === "number") score = raw;
1536
+ else return;
1537
+ if (!Number.isFinite(score) || score < 0 || score > 10) return void 0;
1538
+ return roundImpact(score / 10);
1539
+ }
1443
1540
  function resolveRuleLevel(rule, results) {
1444
1541
  if (rule?.defaultConfiguration?.level) return rule.defaultConfiguration.level;
1445
1542
  for (const r of results) if (!r.kind || r.kind === "fail") {
@@ -1520,7 +1617,7 @@ function extractCweFromRule(rule) {
1520
1617
  }
1521
1618
  return [];
1522
1619
  }
1523
- function extractCweIds(text) {
1620
+ function extractCweIds$1(text) {
1524
1621
  return extractCWEIDs(text).map((id) => `CWE-${id}`);
1525
1622
  }
1526
1623
  function mapKindToStatus(kind) {
@@ -1602,7 +1699,7 @@ function buildDescriptions$2(defaultDesc, rule, result) {
1602
1699
  if (result.fixes && result.fixes.length > 0 && result.fixes[0]?.description?.text) descriptions.push(createDescription("fix", result.fixes[0].description.text));
1603
1700
  return descriptions;
1604
1701
  }
1605
- function buildTags$3(result, rule, resolvedLevel, cweIds, nistControls, cciControls, allSuppressions) {
1702
+ function buildTags$4(result, rule, resolvedLevel, cweIds, nistControls, cciControls, allSuppressions) {
1606
1703
  const tags = {
1607
1704
  severity: resolvedLevel,
1608
1705
  cwe: cweIds,
@@ -2181,7 +2278,7 @@ function ruleResultToRequirement(rr, ruleIndex, groupIndex, scanTime) {
2181
2278
  const req = createRequirement(id, title, descriptions, impact, [result], { tags });
2182
2279
  const code = buildCheckCode(check);
2183
2280
  if (code) req.code = code;
2184
- const refs = buildRefs$9(ruleDef?.reference);
2281
+ const refs = buildRefs$10(ruleDef?.reference);
2185
2282
  if (refs.length > 0) req.refs = refs;
2186
2283
  const hdfSeverity = xccdfSeverityToHdf(severity);
2187
2284
  if (hdfSeverity) req.severity = hdfSeverity;
@@ -2338,7 +2435,7 @@ function checkDescriptionData(check) {
2338
2435
  * href, when present, populates url. References that carry nothing are skipped.
2339
2436
  * Inner keys are inserted alphabetically to match Go's map JSON ordering.
2340
2437
  */
2341
- function buildRefs$9(refs) {
2438
+ function buildRefs$10(refs) {
2342
2439
  if (!refs) return [];
2343
2440
  const out = [];
2344
2441
  for (const r of refs) {
@@ -2863,6 +2960,13 @@ function cklbTitle(cl) {
2863
2960
  //#endregion
2864
2961
  //#region shared/typescript/checklist/to-hdf.ts
2865
2962
  const CONVERTER_VERSION = "1.0.0";
2963
+ const VALID_SEVERITIES = /* @__PURE__ */ new Set([
2964
+ "critical",
2965
+ "high",
2966
+ "medium",
2967
+ "low",
2968
+ "informational"
2969
+ ]);
2866
2970
  /**
2867
2971
  * Map the format-neutral Checklist model to an HDF Results object.
2868
2972
  * controlType is derived per-Vuln from CCI->NIST; verificationMethod and
@@ -2934,7 +3038,7 @@ function vulnToRequirement(v, scanTime) {
2934
3038
  if (v.legacyIDs && v.legacyIDs.length) tags["legacy_ids"] = v.legacyIDs;
2935
3039
  if (v.extra && Object.keys(v.extra).length > 0) tags["cklMetadata"] = { ...v.extra };
2936
3040
  const req = createRequirement(v.vulnNum, v.ruleTitle ?? v.vulnNum, descriptions, impact, [result], { tags });
2937
- if (severity) req.severity = severity;
3041
+ if (VALID_SEVERITIES.has(severity)) req.severity = severity;
2938
3042
  const controlType = deriveControlTypeFromTags(nistTags);
2939
3043
  if (controlType !== void 0) req.controlType = controlType;
2940
3044
  return req;
@@ -2985,6 +3089,36 @@ function setIf$1(m, key, val) {
2985
3089
  if (val) m[key] = val;
2986
3090
  }
2987
3091
  //#endregion
3092
+ //#region shared/typescript/status.ts
3093
+ /**
3094
+ * Bridge from schema-typed requirements onto the canonical effective-status
3095
+ * helper in @mitre/hdf-utilities, so every consumer computes status through
3096
+ * the single shared implementation (twin of shared/go/status.go). The stored
3097
+ * effectiveStatus field is never read — it is an output cache (see
3098
+ * status-determination.md).
3099
+ */
3100
+ /** RFC3339 string from a schema timestamp (quicktype Date or raw string). */
3101
+ function stamp(v) {
3102
+ if (v instanceof Date) return v.toISOString();
3103
+ if (typeof v === "string" && v !== "") return v;
3104
+ }
3105
+ /** Maps a requirement onto the canonical effective-status input shape. */
3106
+ function requirementStatusInput(req) {
3107
+ return {
3108
+ impact: req.impact,
3109
+ resultStatuses: (req.results ?? []).map((r) => String(r.status)),
3110
+ overrides: (req.statusOverrides ?? []).map((o) => ({
3111
+ status: o.status ? String(o.status) : void 0,
3112
+ appliedAt: stamp(o.appliedAt),
3113
+ expiresAt: stamp(o.expiresAt)
3114
+ }))
3115
+ };
3116
+ }
3117
+ /** The requirement's canonical effective status via the shared ladder. */
3118
+ function requirementEffectiveStatus(req) {
3119
+ return computeEffectiveStatus(requirementStatusInput(req));
3120
+ }
3121
+ //#endregion
2988
3122
  //#region shared/typescript/checklist/from-hdf.ts
2989
3123
  /**
2990
3124
  * Map HDF Results back to the format-neutral Checklist model. When the HDF
@@ -3077,7 +3211,7 @@ function requirementToVuln(req) {
3077
3211
  };
3078
3212
  }
3079
3213
  function effectiveOrRawStatus(req) {
3080
- return req.effectiveStatus ?? req.results?.[0]?.status;
3214
+ return requirementEffectiveStatus(req);
3081
3215
  }
3082
3216
  function composeFindingDetails(req) {
3083
3217
  const results = req.results ?? [];
@@ -3252,6 +3386,527 @@ function convertHdfToCklb(input) {
3252
3386
  return serializeCklb(hdfToChecklist(input));
3253
3387
  }
3254
3388
  //#endregion
3389
+ //#region converters/kics-to-hdf/typescript/mapping-data.ts
3390
+ const kicsMappingData = {};
3391
+ //#endregion
3392
+ //#region converters/kics-to-hdf/typescript/converter.ts
3393
+ /**
3394
+ * KICS publishes five severities. SARIF collapses CRITICAL and HIGH into
3395
+ * `error`; converting the native format keeps them apart, which is a large part
3396
+ * of why this converter exists. The shared standard map covers four of them,
3397
+ * with INFO at the canonical 0.0 tier like every other converter: the
3398
+ * effective-status layer maps impact-0 requirements to notApplicable, so
3399
+ * info-tier findings stay visible in the output without entering the
3400
+ * compliance ratio. TRACE is KICS-specific and joins the info tier as an
3401
+ * alias — the Go peer of this table is kicsSeverityAliases.
3402
+ */
3403
+ const KICS_SEVERITY_ALIASES = { trace: 0 };
3404
+ const DEFAULT_IMPACT$1 = .5;
3405
+ /**
3406
+ * Records which tier resolved a requirement's NIST tags, so a reviewed mapping
3407
+ * stays distinguishable from a CWE-derived guess and from a static default.
3408
+ * Follows the precedent set by UNRATED_SEVERITY_TAG in converterutil, which
3409
+ * exists so a defaulted impact stays distinguishable from a genuine rated one.
3410
+ *
3411
+ * The per-query table is authoritative, matching how the Checkov and AWS Config
3412
+ * mappers in the v1 line work: the rule-to-control decision is reviewed before
3413
+ * shipping rather than computed at conversion time, which is also what lets
3414
+ * those tables carry control enhancements. CWE is a fallback because it is a
3415
+ * lossy proxy — only 30 of the 102 CWEs KICS uses resolve against the CWE→NIST
3416
+ * table, 52% of queries by volume.
3417
+ */
3418
+ const NIST_MAPPING_TAG = "nistMapping";
3419
+ const NIST_MAPPING_TABLE = "mapped";
3420
+ const NIST_MAPPING_CWE = "cwe-derived";
3421
+ const NIST_MAPPING_FALLBACK = "static-fallback";
3422
+ function impactFor$1(severity) {
3423
+ return severityToImpactWithAliases(severity, KICS_SEVERITY_ALIASES, DEFAULT_IMPACT$1);
3424
+ }
3425
+ /** KICS emits the bare number, e.g. "778". Normalize before the lookup. */
3426
+ function cweIdentifiers(query) {
3427
+ const raw = query.cwe;
3428
+ if (raw === void 0 || raw === null || String(raw).trim() === "") return [];
3429
+ const id = String(raw).trim().replace(/^CWE-/i, "");
3430
+ return /^\d+$/.test(id) ? [`CWE-${id}`] : [];
3431
+ }
3432
+ function locationFor(file) {
3433
+ const parts = [`File: ${file.file_name || "unknown"}`];
3434
+ if (typeof file.line === "number" && file.line > 0) parts.push(`Line: ${file.line}`);
3435
+ if (file.resource_type) parts.push(`Resource type: ${file.resource_type}`);
3436
+ if (file.resource_name && file.resource_name !== "unknown") parts.push(`Resource: ${file.resource_name}`);
3437
+ if (file.search_key) parts.push(`Key: ${file.search_key}`);
3438
+ if (file.similarity_id) parts.push(`Similarity ID: ${file.similarity_id}`);
3439
+ return parts.join("\n");
3440
+ }
3441
+ /**
3442
+ * The remediation pair. SARIF keeps only `actual_value` inside its message, so
3443
+ * a SARIF-derived control can say what the configuration is but never what it
3444
+ * should be.
3445
+ */
3446
+ function evidenceFor(file) {
3447
+ const parts = [];
3448
+ if (file.expected_value) parts.push(`Expected: ${file.expected_value}`);
3449
+ if (file.actual_value) parts.push(`Actual: ${file.actual_value}`);
3450
+ if (file.issue_type) parts.push(`Issue type: ${file.issue_type}`);
3451
+ if (file.search_value) parts.push(`Search value: ${file.search_value}`);
3452
+ return parts.join("\n");
3453
+ }
3454
+ /**
3455
+ * Resolution order: the reviewed per-query table, then the query's CWE, then the
3456
+ * static-analysis defaults. `table` is a parameter so all three tiers are
3457
+ * testable without shipping unreviewed rows in the table itself.
3458
+ */
3459
+ function resolveControls(query, table = kicsMappingData) {
3460
+ const entry = typeof query.query_id === "string" && Object.prototype.hasOwnProperty.call(table, query.query_id) ? table[query.query_id] : void 0;
3461
+ if (entry !== void 0 && entry.nist.length > 0) return {
3462
+ nist: entry.nist,
3463
+ cci: entry.cci,
3464
+ source: NIST_MAPPING_TABLE
3465
+ };
3466
+ const fromCwe = mapCWEToNIST(cweIdentifiers(query), []);
3467
+ if (fromCwe.length > 0) return {
3468
+ nist: fromCwe,
3469
+ cci: nistToCci(fromCwe),
3470
+ source: NIST_MAPPING_CWE
3471
+ };
3472
+ const fallback = [...DEFAULT_STATIC_ANALYSIS_NIST_TAGS];
3473
+ return {
3474
+ nist: fallback,
3475
+ cci: nistToCci(fallback),
3476
+ source: NIST_MAPPING_FALLBACK
3477
+ };
3478
+ }
3479
+ function tagsFor$1(query) {
3480
+ const cwe = cweIdentifiers(query);
3481
+ const { nist, cci, source } = resolveControls(query);
3482
+ const extras = { [NIST_MAPPING_TAG]: source };
3483
+ if (cwe.length > 0) extras.cwe = cwe;
3484
+ if (query.severity) extras.severity = query.severity;
3485
+ if (query.platform) extras.platform = query.platform;
3486
+ if (query.cloud_provider) extras.cloudProvider = query.cloud_provider;
3487
+ if (query.category) extras.category = query.category;
3488
+ if (query.query_url) extras.queryUrl = query.query_url;
3489
+ if (query.risk_score != null) extras.riskScore = String(query.risk_score);
3490
+ if (query.description_id) extras.descriptionId = query.description_id;
3491
+ if (query.experimental) extras.experimental = true;
3492
+ const issueTypes = [...new Set((query.files ?? []).map((f) => f.issue_type).filter(Boolean))];
3493
+ if (issueTypes.length > 0) extras.issueType = issueTypes;
3494
+ const resourceTypes = [...new Set((query.files ?? []).map((f) => f.resource_type).filter(Boolean))];
3495
+ if (resourceTypes.length > 0) extras.resourceType = resourceTypes;
3496
+ markUnratedSeverity(extras, query.severity);
3497
+ return buildNistCciTags(nist, cci, extras);
3498
+ }
3499
+ /**
3500
+ * KICS reports violations only. Its output carries no record of the queries
3501
+ * that ran without finding anything — `queries[]` contains only those that
3502
+ * fired — so no passing requirement can be derived from it, and a converted
3503
+ * profile is failures-only by construction.
3504
+ *
3505
+ * That makes the compliance percentage misleading on its own: a scan where 72
3506
+ * of 2,034 queries fired renders as 100% failed. This requirement carries the
3507
+ * denominator so the ratio is legible.
3508
+ *
3509
+ * Impact 0 and status notApplicable both keep it out of the score: the
3510
+ * effective-status layer (computeEffectiveStatus) maps impact-0 requirements
3511
+ * to notApplicable before statuses are counted, and notApplicable is the one
3512
+ * status the compliance rollup excludes from both numerator and denominator.
3513
+ * Emitting notApplicable as the raw status too keeps raw-status consumers
3514
+ * agreeing with the effective view — Passed would export to CKL as
3515
+ * NotAFinding, a compliant-looking control that was never checked, and count
3516
+ * as a free pass in raw status rollups.
3517
+ */
3518
+ function buildCoverageRequirement$1(report, startTime) {
3519
+ const executed = report.queries_total ?? 0;
3520
+ const fired = (report.queries ?? []).filter((q) => (q.files ?? []).length > 0).length;
3521
+ const summary = `KICS executed ${executed} queries against ${report.files_scanned ?? 0} file(s) (${report.files_parsed ?? 0} parsed); ${fired} produced findings. KICS reports violations only and does not enumerate the queries that ran without finding anything, so no passing requirements can be derived from its output and the compliance ratio should not be read as a pass rate.`;
3522
+ return {
3523
+ id: "kics-scan-coverage",
3524
+ title: "KICS scan coverage",
3525
+ impact: 0,
3526
+ descriptions: [{
3527
+ label: "default",
3528
+ data: summary
3529
+ }],
3530
+ results: [{
3531
+ status: ResultStatus.NotApplicable,
3532
+ codeDesc: summary,
3533
+ startTime
3534
+ }],
3535
+ tags: {
3536
+ queriesExecuted: executed,
3537
+ queriesWithFindings: fired,
3538
+ filesScanned: report.files_scanned ?? 0,
3539
+ filesParsed: report.files_parsed ?? 0,
3540
+ filesFailedToScan: report.files_failed_to_scan ?? 0,
3541
+ queriesFailedToExecute: report.queries_failed_to_execute ?? 0
3542
+ }
3543
+ };
3544
+ }
3545
+ function buildRequirement$18(query, startTime) {
3546
+ const tags = tagsFor$1(query);
3547
+ const descriptions = [{
3548
+ label: "default",
3549
+ data: query.description ?? ""
3550
+ }];
3551
+ const results = (query.files ?? []).map((file) => ({
3552
+ status: ResultStatus.Failed,
3553
+ codeDesc: locationFor(file),
3554
+ message: evidenceFor(file),
3555
+ startTime
3556
+ }));
3557
+ const requirement = createRequirement(query.query_id || query.query_name || "unknown", query.query_name || query.query_id || "Unnamed KICS query", descriptions, impactFor$1(query.severity), results, { tags });
3558
+ requirement.verificationMethod = VerificationMethodEnum.Automated;
3559
+ const controlType = deriveControlTypeFromTags(tags.nist);
3560
+ if (controlType !== void 0) requirement.controlType = controlType;
3561
+ return requirement;
3562
+ }
3563
+ /**
3564
+ * Converts native `kics scan --report-formats json` output to HDF.
3565
+ *
3566
+ * KICS also emits SARIF, which carries its CWE taxonomy properly. What SARIF
3567
+ * drops is the remediation pair (`expected_value`), the add-a-block versus
3568
+ * fix-a-value distinction (`issue_type`), the stable finding fingerprint
3569
+ * (`similarity_id`), and one level of severity granularity.
3570
+ *
3571
+ * @param input - KICS JSON report string
3572
+ * @returns HDF JSON string
3573
+ */
3574
+ async function convertKicsToHdf(input, converterVersion = "1.0.0") {
3575
+ validateInputSize(input, "kics");
3576
+ if (!input || input.trim().length === 0) throw new Error("kics: empty input");
3577
+ const report = parseJSON(input);
3578
+ if (!Array.isArray(report.queries) || typeof report.kics_version !== "string") throw new Error("kics: input does not look like a KICS report");
3579
+ const startTime = /* @__PURE__ */ new Date();
3580
+ const requirements = report.queries.filter((q) => Array.isArray(q.files) && q.files.length > 0).map((q) => buildRequirement$18(q, startTime));
3581
+ if (requirements.length === 0) requirements.push(buildNoFindingsRequirement("kics-no-findings", `KICS scanned ${report.files_scanned ?? 0} file(s) and reported no findings.`, startTime));
3582
+ requirements.push(buildCoverageRequirement$1(report, startTime));
3583
+ const baseline = createMinimalBaseline("KICS Scan", requirements, {
3584
+ title: "KICS infrastructure-as-code scan",
3585
+ resultsChecksum: await inputChecksum(input)
3586
+ });
3587
+ return buildHdfResults({
3588
+ generatorName: "kics-to-hdf",
3589
+ converterVersion,
3590
+ toolName: "KICS",
3591
+ toolVersion: report.kics_version,
3592
+ toolFormat: "json",
3593
+ baselines: [baseline],
3594
+ timestamp: startTime
3595
+ });
3596
+ }
3597
+ //#endregion
3598
+ //#region converters/semgrep-to-hdf/typescript/converter.ts
3599
+ /**
3600
+ * Semgrep's native three-level scale mapped onto the canonical impact tiers,
3601
+ * mirroring the sarif converter's error/warning/note aliases. INFO is
3602
+ * deliberately 0.3 (an actionable low, semgrep's analogue of SARIF 'note'),
3603
+ * not the canonical 0.0 info tier. Supply-chain severities
3604
+ * (critical/high/medium/low) resolve through the shared canonical map.
3605
+ */
3606
+ const SEMGREP_SEVERITY_ALIASES = {
3607
+ error: .7,
3608
+ warning: .5,
3609
+ info: .3
3610
+ };
3611
+ /**
3612
+ * An unrecognized or absent severity is treated as moderate; the absent case
3613
+ * additionally carries the shared unrated marker so consumers can tell a
3614
+ * defaulted 0.5 from a genuine medium.
3615
+ */
3616
+ const DEFAULT_IMPACT = .5;
3617
+ /** Fields Semgrep redacts in unauthenticated (OSS) scans. */
3618
+ const REDACTED_PLACEHOLDER = "requires login";
3619
+ const SCAN_ERRORS_ID = "semgrep-scan-errors";
3620
+ const COVERAGE_ID = "semgrep-scan-coverage";
3621
+ function isPresent(value) {
3622
+ return typeof value === "string" && value.length > 0 && value !== REDACTED_PLACEHOLDER;
3623
+ }
3624
+ /**
3625
+ * Array entries are filtered per-entry (non-string members dropped); a bare
3626
+ * empty string is treated as absent, matching the Go StringOrSlice decoder.
3627
+ */
3628
+ function normalizeToArray(value) {
3629
+ if (Array.isArray(value)) return value.filter((entry) => typeof entry === "string");
3630
+ return typeof value === "string" && value !== "" ? [value] : [];
3631
+ }
3632
+ function lineOf(position) {
3633
+ const line = position?.line;
3634
+ return typeof line === "number" && line > 0 ? line : void 0;
3635
+ }
3636
+ /**
3637
+ * Semgrep emits CWEs in prose form -- 'CWE-89: Improper Neutralization of ...'
3638
+ * -- while the mapping keys on the bare number.
3639
+ */
3640
+ function extractCweIds(metadata) {
3641
+ return normalizeToArray(metadata.cwe).map((entry) => /CWE-(\d+)/i.exec(entry)?.[1]).filter((id) => id !== void 0).map((id) => Number.parseInt(id, 10)).filter((id) => Number.isFinite(id));
3642
+ }
3643
+ /**
3644
+ * The parsed CWE ids in the CWE-N catalog form the schema's cwe[] field asks
3645
+ * for, deduplicated in source order.
3646
+ */
3647
+ function cweCatalogFor(metadata) {
3648
+ return [...new Set(extractCweIds(metadata).map((id) => `CWE-${id}`))];
3649
+ }
3650
+ function nistControlsFor(metadata) {
3651
+ const controls = extractCweIds(metadata).map((cweId) => getCweNistControl(cweId)).filter(isPresent);
3652
+ const deduped = [...new Set(controls)];
3653
+ return deduped.length > 0 ? deduped : [...DEFAULT_STATIC_ANALYSIS_NIST_TAGS$1];
3654
+ }
3655
+ function impactFor(result) {
3656
+ const severity = result.extra?.severity;
3657
+ if (!isPresent(severity)) return DEFAULT_IMPACT;
3658
+ return severityToImpactWithAliases(severity, SEMGREP_SEVERITY_ALIASES, DEFAULT_IMPACT);
3659
+ }
3660
+ /**
3661
+ * Semgrep rule ids are dotted paths whose final segment is the rule name. The
3662
+ * JSON output carries no human-readable rule title anywhere -- unlike the SARIF
3663
+ * output, whose rule objects have `name` and `shortDescription` -- so one is
3664
+ * derived. Separator runs collapse and only the first character of each word
3665
+ * is uppercased, matching the Go twin exactly.
3666
+ */
3667
+ function titleFor(checkId) {
3668
+ const segments = checkId.split(".").filter((segment) => segment.length > 0);
3669
+ return (segments[segments.length - 1] ?? checkId).split(/[-_]+/).filter((word) => word.length > 0).map((word) => word[0].toUpperCase() + word.slice(1)).join(" ");
3670
+ }
3671
+ function codeDescFor(result) {
3672
+ const path = isPresent(result.path) ? result.path : "unknown";
3673
+ const startLine = lineOf(result.start);
3674
+ const endLine = lineOf(result.end);
3675
+ if (startLine === void 0) return `Path: ${path}`;
3676
+ return `Path: ${path}, ${endLine === void 0 || endLine === startLine ? `line ${startLine}` : `lines ${startLine}-${endLine}`}`;
3677
+ }
3678
+ /**
3679
+ * Points at the first occurrence of the rule; per-occurrence locations remain
3680
+ * on each result's codeDesc.
3681
+ */
3682
+ function sourceLocationFor(result) {
3683
+ if (!isPresent(result.path)) return;
3684
+ const location = { ref: result.path };
3685
+ const line = lineOf(result.start);
3686
+ if (line !== void 0) location.line = line;
3687
+ return location;
3688
+ }
3689
+ function messageFor(result) {
3690
+ const parts = [];
3691
+ if (isPresent(result.extra?.lines)) parts.push(`Matched code:\n${result.extra.lines}`);
3692
+ if (isPresent(result.extra?.fix)) parts.push(`Suggested fix -- replace the matched code with:\n${result.extra.fix}`);
3693
+ return parts.join("\n\n");
3694
+ }
3695
+ /** Documentation and cross-framework links, deduplicated. */
3696
+ function referencesFor(metadata) {
3697
+ const urls = [
3698
+ ...normalizeToArray(metadata.references),
3699
+ metadata.source,
3700
+ metadata.shortlink,
3701
+ metadata["source-rule-url"],
3702
+ typeof metadata.asvs?.control_url === "string" ? metadata.asvs.control_url : void 0
3703
+ ].filter(isPresent);
3704
+ return [...new Set(urls)];
3705
+ }
3706
+ /** The deduplicated reference URLs in refs[], their structured HDF home. */
3707
+ function refsFor(metadata) {
3708
+ return referencesFor(metadata).map((url) => ({ url }));
3709
+ }
3710
+ function asvsOf(metadata) {
3711
+ const asvs = metadata.asvs;
3712
+ if (asvs && typeof asvs === "object" && !Array.isArray(asvs) && Object.keys(asvs).length > 0) return asvs;
3713
+ }
3714
+ function tagsFor(metadata, checkId, severity) {
3715
+ const nist = [...new Set(nistControlsFor(metadata))];
3716
+ const cci = nistToCci(nist);
3717
+ const extras = { checkId };
3718
+ const cwe = normalizeToArray(metadata.cwe);
3719
+ if (cwe.length > 0) extras.cwe = cwe;
3720
+ const owasp = normalizeToArray(metadata.owasp);
3721
+ if (owasp.length > 0) extras.owasp = owasp;
3722
+ const subcategory = normalizeToArray(metadata.subcategory);
3723
+ if (subcategory.length > 0) extras.subcategory = subcategory;
3724
+ const technology = normalizeToArray(metadata.technology);
3725
+ if (technology.length > 0) extras.technology = technology;
3726
+ const vulnerabilityClass = normalizeToArray(metadata.vulnerability_class);
3727
+ if (vulnerabilityClass.length > 0) extras.vulnerabilityClass = vulnerabilityClass;
3728
+ if (isPresent(severity)) extras.severity = severity;
3729
+ if (isPresent(metadata.confidence)) extras.confidence = metadata.confidence;
3730
+ if (isPresent(metadata.likelihood)) extras.likelihood = metadata.likelihood;
3731
+ if (isPresent(metadata.impact)) extras.semgrepImpact = metadata.impact;
3732
+ if (isPresent(metadata.category)) extras.category = metadata.category;
3733
+ if (isPresent(metadata["bandit-code"])) extras.banditCode = metadata["bandit-code"];
3734
+ const asvs = asvsOf(metadata);
3735
+ if (asvs) extras.asvs = asvs;
3736
+ markUnratedSeverity(extras, isPresent(severity) ? severity : "");
3737
+ return buildNistCciTags(nist, cci, extras);
3738
+ }
3739
+ function codePositionFor(position) {
3740
+ const line = lineOf(position);
3741
+ if (line === void 0) return;
3742
+ const out = { line };
3743
+ const col = position?.col;
3744
+ if (typeof col === "number" && col > 0) out.col = col;
3745
+ return out;
3746
+ }
3747
+ /**
3748
+ * Serializes the curated match envelope into requirement.code for the CODE
3749
+ * tab: the rule source itself is not present in semgrep's JSON output, and the
3750
+ * raw finding bytes are not byte-stable across the Go/TS pair (escape forms
3751
+ * differ), so both languages serialize this envelope field-for-field in the
3752
+ * same order. Rule metadata is deliberately excluded — it is already carried
3753
+ * structurally in tags, cwe[], and refs[] — and redacted fields are filtered
3754
+ * per the converter's redaction policy.
3755
+ */
3756
+ function codeFor(result) {
3757
+ const envelope = { check_id: result.check_id };
3758
+ if (isPresent(result.path)) envelope.path = result.path;
3759
+ const start = codePositionFor(result.start);
3760
+ if (start) envelope.start = start;
3761
+ const end = codePositionFor(result.end);
3762
+ if (end) envelope.end = end;
3763
+ const extra = {};
3764
+ if (isPresent(result.extra?.message)) extra.message = result.extra.message;
3765
+ if (isPresent(result.extra?.severity)) extra.severity = result.extra.severity;
3766
+ if (isPresent(result.extra?.lines)) extra.lines = result.extra.lines;
3767
+ if (isPresent(result.extra?.fix)) extra.fix = result.extra.fix;
3768
+ if (Object.keys(extra).length > 0) envelope.extra = extra;
3769
+ return JSON.stringify(envelope, null, 2);
3770
+ }
3771
+ /** Every occurrence of one rule becomes a result under a single requirement. */
3772
+ function buildRequirement$17(checkId, results, startTime) {
3773
+ const representative = results[0];
3774
+ const metadata = representative.extra?.metadata ?? {};
3775
+ const message = representative.extra?.message;
3776
+ const descriptions = [{
3777
+ label: "default",
3778
+ data: typeof message === "string" ? message : ""
3779
+ }];
3780
+ const requirementResults = results.map((result) => ({
3781
+ status: ResultStatus.Failed,
3782
+ codeDesc: codeDescFor(result),
3783
+ message: messageFor(result),
3784
+ startTime
3785
+ }));
3786
+ const tags = tagsFor(metadata, checkId, representative.extra?.severity);
3787
+ const cwe = cweCatalogFor(metadata);
3788
+ const refs = refsFor(metadata);
3789
+ const sourceLocation = sourceLocationFor(representative);
3790
+ const requirement = createRequirement(checkId, titleFor(checkId), descriptions, impactFor(representative), requirementResults, {
3791
+ tags,
3792
+ code: codeFor(representative),
3793
+ ...cwe.length > 0 ? { cwe } : {},
3794
+ ...refs.length > 0 ? { refs } : {},
3795
+ ...sourceLocation ? { sourceLocation } : {}
3796
+ });
3797
+ requirement.verificationMethod = VerificationMethodEnum.Automated;
3798
+ const controlType = deriveControlTypeFromTags(tags.nist);
3799
+ if (controlType !== void 0) requirement.controlType = controlType;
3800
+ return requirement;
3801
+ }
3802
+ /**
3803
+ * Scan failures become their own requirement so a file that failed to parse is
3804
+ * visible rather than buried: absence of findings in it is not evidence of
3805
+ * compliance. errors[].level drives the status: level 'error' entries are scan
3806
+ * failures (status error), while level 'warn' entries are advisory (e.g.
3807
+ * PartialParsing — the file was partially analyzed), a genuine non-evaluation
3808
+ * of those paths that must not dominate worst-wins rollups, so they map to
3809
+ * notReviewed.
3810
+ */
3811
+ function buildErrorsRequirement(errors, startTime) {
3812
+ const results = errors.map((error) => {
3813
+ const kind = Array.isArray(error.type) ? String(error.type[0]) : String(error.type ?? "Unknown");
3814
+ const level = isPresent(error.level) ? error.level : "";
3815
+ const message = typeof error.message === "string" ? error.message : "";
3816
+ return {
3817
+ status: level.toLowerCase() === "warn" ? ResultStatus.NotReviewed : ResultStatus.Error,
3818
+ codeDesc: `Path: ${isPresent(error.path) ? error.path : "unknown"}`,
3819
+ message: level === "" ? `${kind}: ${message}` : `${kind} [${level}]: ${message}`,
3820
+ startTime
3821
+ };
3822
+ });
3823
+ const tags = buildNistCciTags([...DEFAULT_STATIC_ANALYSIS_NIST_TAGS$1], []);
3824
+ markUnratedSeverity(tags, "");
3825
+ const requirement = createRequirement(SCAN_ERRORS_ID, "Semgrep scan errors", [{
3826
+ label: "default",
3827
+ data: "Errors reported by Semgrep while scanning. A file that failed to parse was not fully analyzed."
3828
+ }], DEFAULT_IMPACT, results, { tags });
3829
+ requirement.verificationMethod = VerificationMethodEnum.Automated;
3830
+ return requirement;
3831
+ }
3832
+ /**
3833
+ * Records the scan's denominator. Semgrep reports violations only, so a
3834
+ * converted profile is failures-only by construction and its compliance ratio
3835
+ * is not a pass rate; this record carries the scanned count and the caveat.
3836
+ * Impact 0 makes the effective-status layer derive notApplicable — the only
3837
+ * status the compliance rollup excludes — and the raw status matches so
3838
+ * raw-status consumers (and CKL export, where Passed would render NotAFinding)
3839
+ * agree with the effective view. Mirrors kics-scan-coverage.
3840
+ */
3841
+ function scannedCount(report) {
3842
+ return Array.isArray(report.paths?.scanned) ? report.paths.scanned.length : 0;
3843
+ }
3844
+ function buildCoverageRequirement(report, ruleCount, startTime) {
3845
+ const filesScanned = scannedCount(report);
3846
+ const errorCount = report.errors?.length ?? 0;
3847
+ const summary = `Semgrep scanned ${filesScanned} file(s); ${ruleCount} rule(s) produced findings and ${errorCount} scan error(s) were reported. Semgrep reports violations only and does not enumerate the rules that ran without finding anything, so no passing requirements can be derived from its output and the compliance ratio should not be read as a pass rate.`;
3848
+ return createRequirement(COVERAGE_ID, "Semgrep scan coverage", [{
3849
+ label: "default",
3850
+ data: summary
3851
+ }], 0, [{
3852
+ status: ResultStatus.NotApplicable,
3853
+ codeDesc: summary,
3854
+ startTime
3855
+ }], { tags: {
3856
+ filesScanned,
3857
+ rulesWithFindings: ruleCount,
3858
+ scanErrors: errorCount
3859
+ } });
3860
+ }
3861
+ /**
3862
+ * Converts native `semgrep scan --json` output to HDF Results. SARIF input is
3863
+ * detected and delegated to the SARIF converter.
3864
+ *
3865
+ * Native JSON is converted here because SARIF keeps the rule metadata only as
3866
+ * untyped prose tags on the rule object and drops impact, likelihood, the ASVS
3867
+ * control mapping, reference URLs and vulnerability_class outright.
3868
+ *
3869
+ * @param input - Semgrep JSON report string
3870
+ * @returns HDF JSON string
3871
+ */
3872
+ async function convertSemgrepToHdf(input, converterVersion = "1.0.0") {
3873
+ validateInputSize(input, "semgrep");
3874
+ if (!input || input.trim().length === 0) throw new Error("semgrep: empty input");
3875
+ registerAllFingerprints();
3876
+ const detected = detectConverter(input);
3877
+ if (detected && detected.fingerprint.id === "sarif-to-hdf") return convertSarifToHdf(input, converterVersion);
3878
+ const report = parseJSON(input);
3879
+ if (report === null || typeof report !== "object" || !Array.isArray(report.results) || !Array.isArray(report.errors)) throw new Error("semgrep: input does not look like a Semgrep report");
3880
+ const startTime = /* @__PURE__ */ new Date();
3881
+ const groups = /* @__PURE__ */ new Map();
3882
+ for (const result of report.results) {
3883
+ if (typeof result?.check_id !== "string" || result.check_id === "") continue;
3884
+ const existing = groups.get(result.check_id);
3885
+ if (existing) existing.push(result);
3886
+ else groups.set(result.check_id, [result]);
3887
+ }
3888
+ const requirements = [];
3889
+ for (const [checkId, results] of groups) requirements.push(buildRequirement$17(checkId, results, startTime));
3890
+ if (groups.size === 0) {
3891
+ const scanned = scannedCount(report);
3892
+ requirements.push(buildNoFindingsRequirement("semgrep-no-findings", `Semgrep scanned ${scanned} file(s) and reported no findings.`, startTime));
3893
+ }
3894
+ if (report.errors.length > 0) requirements.push(buildErrorsRequirement(report.errors, startTime));
3895
+ requirements.push(buildCoverageRequirement(report, groups.size, startTime));
3896
+ const baseline = createMinimalBaseline("Semgrep Scan", requirements, {
3897
+ title: "Semgrep static analysis scan",
3898
+ resultsChecksum: await inputChecksum(input)
3899
+ });
3900
+ return buildHdfResults({
3901
+ generatorName: "semgrep-to-hdf",
3902
+ converterVersion,
3903
+ toolName: "Semgrep",
3904
+ toolVersion: typeof report.version === "string" ? report.version : void 0,
3905
+ baselines: [baseline],
3906
+ timestamp: startTime
3907
+ });
3908
+ }
3909
+ //#endregion
3255
3910
  //#region converters/snyk-to-hdf/typescript/converter.ts
3256
3911
  /**
3257
3912
  * Re-serialize the parsed vulnerability into indented JSON for requirement.code
@@ -3418,6 +4073,7 @@ function buildRequirement$16(vulnID, vulns, scanTime, packageManager) {
3418
4073
  };
3419
4074
  if (rep.identifiers.CVE && rep.identifiers.CVE.length > 0) tags["cve"] = rep.identifiers.CVE;
3420
4075
  if (rep.identifiers.GHSA && rep.identifiers.GHSA.length > 0) tags["ghsaid"] = rep.identifiers.GHSA;
4076
+ markUnratedSeverity(tags, rep.severity);
3421
4077
  const descriptions = [{
3422
4078
  label: "default",
3423
4079
  data: rep.description
@@ -3435,7 +4091,7 @@ function buildRequirement$16(vulnID, vulns, scanTime, packageManager) {
3435
4091
  delete result.message;
3436
4092
  return result;
3437
4093
  });
3438
- const req = createRequirement(vulnID, rep.title, descriptions, severityToImpact$1(rep.severity), results, { tags });
4094
+ const req = createRequirement(vulnID, rep.title, descriptions, severityToImpact$1(rep.severity ?? ""), results, { tags });
3439
4095
  const controlType = deriveControlTypeFromTags(nist);
3440
4096
  if (controlType !== void 0) req.controlType = controlType;
3441
4097
  req.verificationMethod = VerificationMethodEnum.Automated;
@@ -3450,7 +4106,7 @@ function buildRequirement$16(vulnID, vulns, scanTime, packageManager) {
3450
4106
  const version = rep.version;
3451
4107
  if (name && version) {
3452
4108
  const ecosystem = ecosystemFromSnykPackageManager(packageManager);
3453
- const pkg = buildAffectedPackage$1({
4109
+ const pkg = buildAffectedPackage$2({
3454
4110
  name,
3455
4111
  version,
3456
4112
  ecosystem,
@@ -3531,17 +4187,12 @@ async function convertSnykToHdf(input, converterVersion = "1.0.0") {
3531
4187
  }
3532
4188
  //#endregion
3533
4189
  //#region converters/grype-to-hdf/typescript/converter.ts
3534
- const IMPACT_MAPPING$8 = /* @__PURE__ */ new Map([
3535
- ["critical", .9],
3536
- ["high", .7],
3537
- ["medium", .5],
3538
- ["low", .3],
3539
- ["negligible", 0],
3540
- ["unknown", .5]
3541
- ]);
4190
+ const GRYPE_ALIASES = {
4191
+ critical: .9,
4192
+ negligible: 0
4193
+ };
3542
4194
  function getImpact$9(severity) {
3543
- if (!severity) return .5;
3544
- return IMPACT_MAPPING$8.get(severity.toLowerCase()) ?? .5;
4195
+ return severityToImpactWithAliases(severity, GRYPE_ALIASES, .5);
3545
4196
  }
3546
4197
  function isNegligibleOrUnknown(severity) {
3547
4198
  if (!severity) return true;
@@ -3593,7 +4244,7 @@ function buildCodeDesc$9(match) {
3593
4244
  }
3594
4245
  return parts.join(" | ");
3595
4246
  }
3596
- function buildCvssEntries$6(vuln) {
4247
+ function buildCvssEntries$7(vuln) {
3597
4248
  if (!vuln.cvss || vuln.cvss.length === 0) return;
3598
4249
  const entries = [];
3599
4250
  for (const c of vuln.cvss) {
@@ -3636,10 +4287,10 @@ function buildAffectedPackages(match) {
3636
4287
  if (fix && fix.state === "fixed" && fix.versions && fix.versions.length > 0 && fix.versions[0]) pkg.fixedInVersion = fix.versions[0];
3637
4288
  return [pkg];
3638
4289
  }
3639
- const CWE_ID_PATTERN$1 = /^CWE-[1-9]\d*$/;
4290
+ const CWE_ID_PATTERN$2 = /^CWE-[1-9]\d*$/;
3640
4291
  function extractCwe(raw) {
3641
4292
  if (!raw || raw.length === 0) return void 0;
3642
- const out = raw.filter((c) => CWE_ID_PATTERN$1.test(c));
4293
+ const out = raw.filter((c) => CWE_ID_PATTERN$2.test(c));
3643
4294
  return out.length === 0 ? void 0 : out;
3644
4295
  }
3645
4296
  function buildEpss$3(entries) {
@@ -3671,7 +4322,6 @@ function convertMatchToRequirement(match, isIgnored, targetName, startTime) {
3671
4322
  const refs = getReferences(vuln, match.relatedVulnerabilities);
3672
4323
  let status;
3673
4324
  if (isIgnored) status = ResultStatus.NotReviewed;
3674
- else if (isNegligibleOrUnknown(severity)) status = ResultStatus.NotApplicable;
3675
4325
  else status = ResultStatus.Failed;
3676
4326
  const messageParts = [];
3677
4327
  if (isIgnored) messageParts.push("This vulnerability was ignored by configured rules.");
@@ -3686,6 +4336,7 @@ function convertMatchToRequirement(match, isIgnored, targetName, startTime) {
3686
4336
  startTime
3687
4337
  };
3688
4338
  const tags = buildNistCciTags(DEFAULT_STATIC_ANALYSIS_NIST_TAGS, nistToCci(DEFAULT_STATIC_ANALYSIS_NIST_TAGS));
4339
+ markUnratedSeverity(tags, severity);
3689
4340
  const requirement = {
3690
4341
  id: isIgnored ? `Grype-Ignored-Match/${cveId}` : `Grype/${cveId}`,
3691
4342
  title: `Grype found a vulnerability to ${cveId} in ${targetName}`,
@@ -3712,7 +4363,7 @@ function convertMatchToRequirement(match, isIgnored, targetName, startTime) {
3712
4363
  };
3713
4364
  const controlType = deriveControlTypeFromTags(DEFAULT_STATIC_ANALYSIS_NIST_TAGS);
3714
4365
  if (controlType !== void 0) requirement.controlType = controlType;
3715
- const cvss = buildCvssEntries$6(vuln);
4366
+ const cvss = buildCvssEntries$7(vuln);
3716
4367
  if (cvss) requirement.cvss = cvss;
3717
4368
  requirement.affectedPackages = buildAffectedPackages(match);
3718
4369
  const cwe = extractCwe(vuln.cwe);
@@ -3723,7 +4374,7 @@ function convertMatchToRequirement(match, isIgnored, targetName, startTime) {
3723
4374
  if (kev) requirement.kev = kev;
3724
4375
  return requirement;
3725
4376
  }
3726
- function buildComponent$1(report, targetName) {
4377
+ function buildComponent$2(report, targetName) {
3727
4378
  const t = report.source?.target;
3728
4379
  const firstRepoDigest = t?.repoDigests?.find((d) => d);
3729
4380
  const firstTag = t?.tags?.find((tag) => tag);
@@ -3741,10 +4392,8 @@ function buildComponent$1(report, targetName) {
3741
4392
  if (image) component.image = image;
3742
4393
  if (report.distro?.name) component.osName = report.distro.name;
3743
4394
  if (report.distro?.version) component.osVersion = report.distro.version;
3744
- if (t.manifestDigest) component.integrity = [{
3745
- algorithm: HashAlgorithm.Sha256,
3746
- value: t.manifestDigest.replace(/^sha256:/, "")
3747
- }];
4395
+ const integrity = digestToChecksums(t.manifestDigest);
4396
+ if (integrity) component.integrity = integrity;
3748
4397
  if (t.architecture) component.labels = { architecture: t.architecture };
3749
4398
  return component;
3750
4399
  }
@@ -3776,7 +4425,7 @@ async function convertGrypeToHdf(input, converterVersion = "1.0.0") {
3776
4425
  toolName: "Grype",
3777
4426
  toolVersion: grypeData.descriptor?.version,
3778
4427
  baselines: [baseline],
3779
- components: [buildComponent$1(grypeData, targetName)],
4428
+ components: [buildComponent$2(grypeData, targetName)],
3780
4429
  timestamp: scanTime ?? void 0
3781
4430
  });
3782
4431
  }
@@ -4024,7 +4673,7 @@ async function convertDefectDojoToHdf(input, converterVersion = "0.1.0") {
4024
4673
  //#region converters/nessus-to-hdf/typescript/converter.ts
4025
4674
  const CVE_SOURCE_RE = /^CVE-\d{4}-\d{4,}$/;
4026
4675
  const CWE_PATTERN = /CWE[- ]?(\d+)/gi;
4027
- const IMPACT_MAPPING$7 = {
4676
+ const NESSUS_ALIASES = {
4028
4677
  "4": .9,
4029
4678
  "3": .7,
4030
4679
  "i": .7,
@@ -4038,7 +4687,7 @@ const IMPACT_MAPPING$7 = {
4038
4687
  * Strip HTML tags from a string
4039
4688
  */
4040
4689
  function parseHtml(html) {
4041
- return html.replace(/<[^>]*>/g, "").trim();
4690
+ return replaceHtmlTags(html, "").trim();
4042
4691
  }
4043
4692
  const NAMED_ENTITIES$3 = {
4044
4693
  lt: "<",
@@ -4168,8 +4817,8 @@ function convertReportItemToRequirement(item, host) {
4168
4817
  const title = isCompliance ? item["compliance-check-name"] || item["pluginName"] : item["pluginName"];
4169
4818
  const descriptions = buildDescriptions(item, isCompliance);
4170
4819
  const impact = calculateImpact(item, isCompliance);
4171
- const tags = buildTags$2(item, isCompliance);
4172
- const refs = buildRefs$8(item);
4820
+ const tags = buildTags$3(item, isCompliance);
4821
+ const refs = buildRefs$9(item);
4173
4822
  const results = [buildResult$1(item, host, isCompliance)];
4174
4823
  const code = JSON.stringify(item, null, 2);
4175
4824
  const nistTags = tags["nist"];
@@ -4188,7 +4837,7 @@ function convertReportItemToRequirement(item, host) {
4188
4837
  if (controlType !== void 0) req.controlType = controlType;
4189
4838
  if (verificationMethod !== void 0) req.verificationMethod = verificationMethod;
4190
4839
  if (!isCompliance) {
4191
- const cvssEntries = buildCvssEntries$5(item);
4840
+ const cvssEntries = buildCvssEntries$6(item);
4192
4841
  if (cvssEntries.length > 0) req.cvss = cvssEntries;
4193
4842
  const cweIDs = buildCweIDs(item);
4194
4843
  if (cweIDs.length > 0) req.cwe = cweIDs;
@@ -4202,7 +4851,7 @@ function convertReportItemToRequirement(item, host) {
4202
4851
  * the schema models cvss as a multi-entry array (Nessus emits one entry per
4203
4852
  * item; multi-vendor convergence may yield more).
4204
4853
  */
4205
- function buildCvssEntries$5(item) {
4854
+ function buildCvssEntries$6(item) {
4206
4855
  const source = (item.cvss_score_source || "").trim();
4207
4856
  if (!source || !CVE_SOURCE_RE.test(source)) return [];
4208
4857
  const hasV3 = !!(item.cvss3_vector || item.cvss3_base_score);
@@ -4342,12 +4991,12 @@ function buildDescriptions(item, isCompliance) {
4342
4991
  }
4343
4992
  function calculateImpact(item, isCompliance) {
4344
4993
  if (isCompliance && item["compliance-reference"]) {
4345
- const catKey = parseComplianceRef(item["compliance-reference"], "CAT")[0]?.toLowerCase();
4346
- return catKey ? IMPACT_MAPPING$7[catKey] ?? .5 : .5;
4994
+ const cat = parseComplianceRef(item["compliance-reference"], "CAT")[0];
4995
+ return cat ? severityToImpactWithAliases(cat, NESSUS_ALIASES, .5) : .5;
4347
4996
  }
4348
- return IMPACT_MAPPING$7[item["severity"]] ?? 0;
4997
+ return severityToImpactWithAliases(item["severity"], NESSUS_ALIASES, 0);
4349
4998
  }
4350
- function buildTags$2(item, isCompliance) {
4999
+ function buildTags$3(item, isCompliance) {
4351
5000
  const tags = { rid: isCompliance ? parseComplianceRef(item["compliance-reference"], "Rule-ID").join(",") : item["pluginID"] };
4352
5001
  if (isCompliance && item["compliance-reference"]) {
4353
5002
  const cciTags = parseComplianceRef(item["compliance-reference"], "CCI");
@@ -4370,7 +5019,7 @@ function buildTags$2(item, isCompliance) {
4370
5019
  if (item.cvss_base_score) tags.cvss_base_score = item.cvss_base_score;
4371
5020
  return tags;
4372
5021
  }
4373
- function buildRefs$8(item) {
5022
+ function buildRefs$9(item) {
4374
5023
  const refs = [];
4375
5024
  if (item.see_also) for (const url of item.see_also.split(/\s+/).filter(Boolean)) refs.push({ url });
4376
5025
  return refs.length > 0 ? refs : void 0;
@@ -4436,31 +5085,29 @@ function isIPAddress(s) {
4436
5085
  //#endregion
4437
5086
  //#region converters/sonarqube-to-hdf/typescript/converter.ts
4438
5087
  /**
4439
- * Deprecated legacy severity to impact mapping.
5088
+ * Deprecated legacy severity aliases to impact scores.
4440
5089
  * Canonical reference: heimdall2 sonarqube-mapper.ts IMPACT_MAPPING.
4441
- */
4442
- const SEVERITY_IMPACT_MAPPING = {
4443
- BLOCKER: 1,
4444
- CRITICAL: .7,
4445
- MAJOR: .5,
4446
- MINOR: .3,
4447
- INFO: 0
5090
+ * Note: SonarQube "CRITICAL" maps to 0.7, not the standard 1.0; INFO resolves
5091
+ * to 0.0 via the shared standard map.
5092
+ */
5093
+ const SONARQUBE_ALIASES = {
5094
+ blocker: 1,
5095
+ critical: .7,
5096
+ major: .5,
5097
+ minor: .3
4448
5098
  };
4449
- /** MQR software-quality severity to impact mapping. */
4450
- const MQR_IMPACT_MAPPING = {
4451
- BLOCKER: 1,
4452
- HIGH: .7,
4453
- MEDIUM: .5,
4454
- LOW: .3,
4455
- INFO: 0
4456
- };
4457
- /** MQR severities ordered weakest to strongest. */
5099
+ /**
5100
+ * The one MQR severity the standard map lacks; HIGH/MEDIUM/LOW/INFO already
5101
+ * resolve to 0.7/0.5/0.3/0.0 via severityToImpactWithAliases.
5102
+ */
5103
+ const MQR_ALIASES = { blocker: 1 };
5104
+ /** MQR severities ordered weakest to strongest (lowercase keys; Go parity). */
4458
5105
  const MQR_SEVERITY_RANK = {
4459
- INFO: 0,
4460
- LOW: 1,
4461
- MEDIUM: 2,
4462
- HIGH: 3,
4463
- BLOCKER: 4
5106
+ info: 0,
5107
+ low: 1,
5108
+ medium: 2,
5109
+ high: 3,
5110
+ blocker: 4
4464
5111
  };
4465
5112
  /**
4466
5113
  * Which severity axis drove a requirement's severity/impact. Emitted as the
@@ -4485,17 +5132,18 @@ const byCodeUnit = (a, b) => a < b ? -1 : a > b ? 1 : 0;
4485
5132
  function selectSeverity(issue) {
4486
5133
  const impacts = issue.impacts ?? [];
4487
5134
  if (impacts.length > 0) {
4488
- const worst = impacts.reduce((a, b) => MQR_SEVERITY_RANK[b.severity] > MQR_SEVERITY_RANK[a.severity] ? b : a);
5135
+ const rank = (s) => MQR_SEVERITY_RANK[s.toLowerCase()] ?? 0;
5136
+ const worst = impacts.reduce((a, b) => rank(b.severity) > rank(a.severity) ? b : a);
4489
5137
  return {
4490
5138
  severity: worst.severity,
4491
5139
  source: SEVERITY_SOURCE_MQR,
4492
- impact: MQR_IMPACT_MAPPING[worst.severity] ?? .5
5140
+ impact: severityToImpactWithAliases(worst.severity, MQR_ALIASES, .5)
4493
5141
  };
4494
5142
  }
4495
5143
  return {
4496
- severity: issue.severity,
5144
+ severity: issue.severity ?? "",
4497
5145
  source: SEVERITY_SOURCE_LEGACY,
4498
- impact: SEVERITY_IMPACT_MAPPING[issue.severity] ?? .5
5146
+ impact: severityToImpactWithAliases(issue.severity, SONARQUBE_ALIASES, .5)
4499
5147
  };
4500
5148
  }
4501
5149
  /**
@@ -4504,6 +5152,28 @@ function selectSeverity(issue) {
4504
5152
  * SonarQube is fundamentally a static analysis tool. Matches heimdall2.
4505
5153
  */
4506
5154
  const DEFAULT_NIST_TAGS = ["SA-11"];
5155
+ const OWASP_TAG_PATTERN = /owasp[-:]?a0*(\d{1,2})/gi;
5156
+ const OWASP_2017_PATTERN = /top 10 2017[\s\S]{0,30}?category\s*a0*(\d{1,2})/gi;
5157
+ /** Records a normalized OWASP-2017 category (A1..A10) when n is in range. */
5158
+ function addOwaspId(set, n) {
5159
+ if (n === void 0) return;
5160
+ const v = parseInt(n, 10);
5161
+ if (v >= 1 && v <= 10) set.add(`A${v}`);
5162
+ }
5163
+ /**
5164
+ * Combines CWE-derived and OWASP-derived NIST controls. The SA-11 default
5165
+ * applies only when neither source yields a mapping.
5166
+ */
5167
+ function foldNistControls(cweIds, owaspIds) {
5168
+ const set = /* @__PURE__ */ new Set();
5169
+ for (const c of mapCWEToNIST(cweIds, [])) set.add(c);
5170
+ for (const id of owaspIds) {
5171
+ const ctrl = getOwaspNistControl(id);
5172
+ if (ctrl) for (const c of ctrl.split("|")) set.add(c);
5173
+ }
5174
+ if (set.size === 0) return [...DEFAULT_NIST_TAGS];
5175
+ return Array.from(set).sort();
5176
+ }
4507
5177
  /**
4508
5178
  * Convert SonarQube issues JSON to HDF format
4509
5179
  *
@@ -4593,8 +5263,8 @@ function convertRuleToRequirement(ruleKey, issues, componentMap, ruleMap) {
4593
5263
  const description = extractDescription(rule);
4594
5264
  const firstIssue = issues[0];
4595
5265
  const { severity, source: severitySource, impact } = selectSeverity(firstIssue);
4596
- const { cweIds, owaspTags, allTags } = extractTags(rule, issues);
4597
- const nistControls = mapCWEToNIST(cweIds, DEFAULT_NIST_TAGS);
5266
+ const { cweIds, owaspIds, allTags } = extractTags(rule, issues);
5267
+ const nistControls = foldNistControls(cweIds, owaspIds);
4598
5268
  const cciControls = nistToCci(nistControls);
4599
5269
  const results = issues.map((issue) => createResultFromIssue(issue, componentMap));
4600
5270
  const issueWithLocation = issues.find((i) => i.line !== void 0);
@@ -4604,7 +5274,7 @@ function convertRuleToRequirement(ruleKey, issues, componentMap, ruleMap) {
4604
5274
  severitySource,
4605
5275
  type: firstIssue.type.toLowerCase(),
4606
5276
  cwe: cweIds,
4607
- owasp: owaspTags,
5277
+ owasp: owaspIds,
4608
5278
  nist: nistControls,
4609
5279
  cci: cciControls,
4610
5280
  ...firstIssue.effort ? { effort: firstIssue.effort } : {},
@@ -4635,11 +5305,11 @@ function convertRuleToRequirement(ruleKey, issues, componentMap, ruleMap) {
4635
5305
  function extractDescription(rule) {
4636
5306
  if (!rule) return "";
4637
5307
  if (rule.mdDesc) return rule.mdDesc;
4638
- if (rule.htmlDesc) return rule.htmlDesc.replace(/<[^>]*>/g, " ").replace(/\s+/g, " ").trim();
5308
+ if (rule.htmlDesc) return stripHtml(rule.htmlDesc);
4639
5309
  if (rule.descriptionSections && rule.descriptionSections.length > 0) {
4640
5310
  const rootCause = rule.descriptionSections.find((s) => s.key === "root_cause");
4641
- if (rootCause) return rootCause.content.replace(/<[^>]*>/g, " ").replace(/\s+/g, " ").trim();
4642
- const parts = rule.descriptionSections.map((s) => s.content.replace(/<[^>]*>/g, " ").replace(/\s+/g, " ").trim()).filter((s) => s.length > 0);
5311
+ if (rootCause) return stripHtml(rootCause.content);
5312
+ const parts = rule.descriptionSections.map((s) => stripHtml(s.content)).filter((s) => s.length > 0);
4643
5313
  if (parts.length > 0) return parts.join("\n\n");
4644
5314
  }
4645
5315
  return rule.name || "";
@@ -4653,7 +5323,7 @@ function extractTags(rule, issues) {
4653
5323
  for (const tag of ruleTags) {
4654
5324
  const lowerTag = tag.toLowerCase();
4655
5325
  if (lowerTag.startsWith("cwe-") || lowerTag.includes("cwe")) for (const id of extractCWEIDs(tag)) cweSet.add(`CWE-${id}`);
4656
- if (lowerTag.includes("owasp")) owaspSet.add(tag);
5326
+ for (const m of tag.matchAll(OWASP_TAG_PATTERN)) addOwaspId(owaspSet, m[1]);
4657
5327
  const parts = tag.split(":");
4658
5328
  if (parts.length === 2) {
4659
5329
  const [category, value] = parts;
@@ -4663,20 +5333,24 @@ function extractTags(rule, issues) {
4663
5333
  }
4664
5334
  }
4665
5335
  for (const issue of issues) if (issue.tags) for (const tag of issue.tags) {
4666
- const lowerTag = tag.toLowerCase();
4667
- if (lowerTag.startsWith("cwe-")) for (const id of extractCWEIDs(tag)) cweSet.add(`CWE-${id}`);
4668
- if (lowerTag.includes("owasp")) owaspSet.add(tag);
5336
+ if (tag.toLowerCase().startsWith("cwe-")) for (const id of extractCWEIDs(tag)) cweSet.add(`CWE-${id}`);
5337
+ for (const m of tag.matchAll(OWASP_TAG_PATTERN)) addOwaspId(owaspSet, m[1]);
4669
5338
  }
4670
5339
  if (rule?.htmlDesc || rule?.mdDesc) {
4671
5340
  const desc = (rule.htmlDesc || "") + (rule.mdDesc || "");
4672
5341
  for (const id of extractCWEIDs(desc)) cweSet.add(`CWE-${id}`);
4673
5342
  }
4674
5343
  if (rule?.descriptionSections) for (const section of rule.descriptionSections) for (const id of extractCWEIDs(section.content)) cweSet.add(`CWE-${id}`);
5344
+ if (rule) {
5345
+ const texts = [rule.htmlDesc || "", rule.mdDesc || ""];
5346
+ for (const section of rule.descriptionSections ?? []) texts.push(section.content);
5347
+ for (const text of texts) for (const m of text.matchAll(OWASP_2017_PATTERN)) addOwaspId(owaspSet, m[1]);
5348
+ }
4675
5349
  const allTags = {};
4676
5350
  for (const [category, values] of allTagsMap) allTags[category] = Array.from(values);
4677
5351
  return {
4678
5352
  cweIds: Array.from(cweSet),
4679
- owaspTags: Array.from(owaspSet),
5353
+ owaspIds: Array.from(owaspSet).sort((a, b) => parseInt(a.slice(1), 10) - parseInt(b.slice(1), 10)),
4680
5354
  allTags
4681
5355
  };
4682
5356
  }
@@ -4941,6 +5615,7 @@ function buildRequirement$14(checkId, group, scanTime) {
4941
5615
  const checkTypes = [...new Set(group.map((c) => c.checkType).filter((t) => t !== ""))].sort();
4942
5616
  if (checkTypes.length > 0) tags["check_type"] = checkTypes;
4943
5617
  if (rep.bc_check_id) tags["bc_check_id"] = rep.bc_check_id;
5618
+ markUnratedSeverity(tags, rep.severity);
4944
5619
  const descriptions = [{
4945
5620
  label: "default",
4946
5621
  data: rep.check_name
@@ -5033,14 +5708,6 @@ async function convertCheckovToHdf(input, converterVersion = "1.0.0") {
5033
5708
  //#endregion
5034
5709
  //#region converters/gosec-to-hdf/typescript/converter.ts
5035
5710
  /**
5036
- * Severity to HDF impact mapping for gosec.
5037
- */
5038
- const IMPACT_MAPPING$6 = {
5039
- HIGH: .7,
5040
- MEDIUM: .5,
5041
- LOW: .3
5042
- };
5043
- /**
5044
5711
  * Returns the first-class CWE identifiers for an issue in "CWE-N" form,
5045
5712
  * or an empty array when the issue carries no CWE.
5046
5713
  */
@@ -5104,10 +5771,11 @@ function issueToResult(issue, scanTime) {
5104
5771
  */
5105
5772
  function buildRequirement$13(ruleId, issues, scanTime) {
5106
5773
  const rep = issues[0];
5107
- const impact = IMPACT_MAPPING$6[rep.severity.toUpperCase()] ?? .5;
5774
+ const impact = severityToImpact(rep.severity);
5108
5775
  const nist = mapCWEToNIST([rep.cwe.id], DEFAULT_REMEDIATION_NIST_TAGS$1);
5109
5776
  const tags = { nist };
5110
5777
  if (rep.confidence) tags.confidence = rep.confidence;
5778
+ markUnratedSeverity(tags, rep.severity);
5111
5779
  const results = issues.map((issue) => issueToResult(issue, scanTime));
5112
5780
  const descriptions = [{
5113
5781
  label: "default",
@@ -5357,7 +6025,7 @@ function buildSourceLocation$3(alert) {
5357
6025
  return { ref: inst.uri };
5358
6026
  }
5359
6027
  const REF_URL_RE = /https?:\/\/[^\s<>"']+/g;
5360
- function buildRefs$7(reference) {
6028
+ function buildRefs$8(reference) {
5361
6029
  if (!reference) return;
5362
6030
  const urls = reference.match(REF_URL_RE);
5363
6031
  if (!urls || urls.length === 0) return;
@@ -5432,7 +6100,7 @@ function buildSiteRequirements(site, startTime) {
5432
6100
  if (code !== void 0) req.code = code;
5433
6101
  const sourceLocation = buildSourceLocation$3(alert);
5434
6102
  if (sourceLocation !== void 0) req.sourceLocation = sourceLocation;
5435
- const refs = buildRefs$7(alert.reference);
6103
+ const refs = buildRefs$8(alert.reference);
5436
6104
  if (refs !== void 0) req.refs = refs;
5437
6105
  requirements.push(req);
5438
6106
  }
@@ -6052,13 +6720,7 @@ function stringSlice(v) {
6052
6720
  * the canonical worst-wins ordering from @mitre/hdf-utilities.
6053
6721
  */
6054
6722
  function worstOfResults(req) {
6055
- const results = asArr(req.results) ?? [];
6056
- const statuses = [];
6057
- for (const rRaw of results) {
6058
- const r = asMap(rRaw);
6059
- if (r) statuses.push(getStr(r, "status"));
6060
- }
6061
- return worstStatus(statuses);
6723
+ return worstStatus(resultStatuses(req));
6062
6724
  }
6063
6725
  /**
6064
6726
  * Whether an HDF Result_Status is a failing verdict. Only 'failed' fails;
@@ -6069,20 +6731,46 @@ function worstOfResults(req) {
6069
6731
  function isFailing(status) {
6070
6732
  return status === "failed";
6071
6733
  }
6072
- /** Resolve the status context for a requirement. */
6734
+ /**
6735
+ * Resolve the status context for a requirement via the canonical
6736
+ * effective-status ladder — the stored effectiveStatus field is never read
6737
+ * (see status-determination.md; the field is an output cache).
6738
+ */
6073
6739
  function statusOf(req) {
6074
6740
  const raw = worstOfResults(req);
6075
- const effective = getStr(req, "effectiveStatus");
6076
- const overrides = asArr(req.statusOverrides);
6077
- const rollup = effective !== "" ? effective : raw;
6741
+ const overrides = asArr(req.statusOverrides) ?? [];
6742
+ const overrideInputs = overrides.flatMap((oRaw) => {
6743
+ const o = asMap(oRaw);
6744
+ if (!o) return [];
6745
+ return [{
6746
+ status: getStr(o, "status") || void 0,
6747
+ appliedAt: getStr(o, "appliedAt") || void 0,
6748
+ expiresAt: getStr(o, "expiresAt") || void 0
6749
+ }];
6750
+ });
6751
+ const rollup = computeEffectiveStatus({
6752
+ impact: typeof req.impact === "number" ? req.impact : 1,
6753
+ resultStatuses: resultStatuses(req),
6754
+ overrides: overrideInputs
6755
+ });
6756
+ const governed = governingStatusOverride(overrideInputs) !== void 0;
6078
6757
  return {
6079
6758
  raw,
6080
- effective,
6081
6759
  rollup,
6082
- overridden: overrides !== void 0 && overrides.length > 0 || "effectiveStatus" in req,
6083
- suppressed: isFailing(raw) && !isFailing(rollup)
6760
+ overridden: overrides.length > 0,
6761
+ suppressed: governed && isFailing(raw) && !isFailing(rollup)
6084
6762
  };
6085
6763
  }
6764
+ /** Lists results[].status losslessly for the canonical roll-up. */
6765
+ function resultStatuses(req) {
6766
+ const results = asArr(req.results) ?? [];
6767
+ const statuses = [];
6768
+ for (const rRaw of results) {
6769
+ const r = asMap(rRaw);
6770
+ if (r) statuses.push(getStr(r, "status"));
6771
+ }
6772
+ return statuses;
6773
+ }
6086
6774
  function firstComponent(doc) {
6087
6775
  const comps = asArr(doc.components);
6088
6776
  if (!comps || comps.length === 0) return void 0;
@@ -6238,10 +6926,7 @@ function stringifyLine(v) {
6238
6926
  * the missing-baselines error and drives validateInputSize.
6239
6927
  */
6240
6928
  function runExport(input, converterName, build) {
6241
- validateInputSize(input, converterName);
6242
- const doc = parseHdf(input);
6243
- const baselines = asArr(doc.baselines);
6244
- if (!baselines) throw new Error(`${converterName}: invalid HDF structure: missing baselines field`);
6929
+ const { doc, items: baselines } = requireHdfResults(input, converterName);
6245
6930
  const docTimestamp = getStr(doc, "timestamp");
6246
6931
  const tool = asMap(doc.tool);
6247
6932
  const generator = asMap(doc.generator);
@@ -6299,6 +6984,19 @@ const CVSS_METHODS = /* @__PURE__ */ new Set([
6299
6984
  "CVSSv4"
6300
6985
  ]);
6301
6986
  /**
6987
+ * Reports whether no rating carries rating information: no CVSS score and no
6988
+ * rated severity token. Covers the empty-ratings default and the fabricated-
6989
+ * "medium" (empty severity, no score) paths, both of which land on impact 0.5
6990
+ * without the source having asserted a severity.
6991
+ */
6992
+ function vulnIsUnrated(ratings) {
6993
+ for (const rating of ratings) {
6994
+ if (rating.method && CVSS_METHODS.has(rating.method) && rating.score !== void 0 && rating.score !== null) return false;
6995
+ if (!isUnratedSeverity(rating.severity)) return false;
6996
+ }
6997
+ return true;
6998
+ }
6999
+ /**
6302
7000
  * Computes the maximum impact across all ratings for a vulnerability.
6303
7001
  * Prefers CVSS score/10 when available, falls back to severityToImpact().
6304
7002
  */
@@ -6328,7 +7026,7 @@ function cvssVersionFromMethod(method, vector) {
6328
7026
  default: return cvssVersionFromVector(vector);
6329
7027
  }
6330
7028
  }
6331
- function buildCvssEntries$4(ratings) {
7029
+ function buildCvssEntries$5(ratings) {
6332
7030
  const entries = [];
6333
7031
  for (const r of ratings) {
6334
7032
  const hasCvssMethod = r.method !== void 0 && CVSS_METHODS.has(r.method);
@@ -6349,7 +7047,7 @@ function buildCvssEntries$4(ratings) {
6349
7047
  * de-duplicated across all three in first-seen order. Returns undefined when the
6350
7048
  * vulnerability carries no links.
6351
7049
  */
6352
- function buildRefs$6(vuln) {
7050
+ function buildRefs$7(vuln) {
6353
7051
  const seen = /* @__PURE__ */ new Set();
6354
7052
  const refs = [];
6355
7053
  const add = (url) => {
@@ -6633,8 +7331,9 @@ async function convertCyclonedxToHdf(input, converterVersion = "1.0.0") {
6633
7331
  nist,
6634
7332
  cci: nistToCci(nist)
6635
7333
  };
7334
+ if (vulnIsUnrated(ratings)) markUnratedSeverity(tags, "");
6636
7335
  const cweIds = cwes.map((c) => `CWE-${c}`);
6637
- const cvssEntries = buildCvssEntries$4(ratings);
7336
+ const cvssEntries = buildCvssEntries$5(ratings);
6638
7337
  const descriptions = [];
6639
7338
  const defaultParts = [];
6640
7339
  if (vuln.description) defaultParts.push(`Description: ${vuln.description}`);
@@ -6667,7 +7366,7 @@ async function convertCyclonedxToHdf(input, converterVersion = "1.0.0") {
6667
7366
  req.code = buildVulnCode$1(vuln);
6668
7367
  if (cvssEntries.length > 0) req.cvss = cvssEntries;
6669
7368
  if (cweIds.length > 0) req.cwe = cweIds;
6670
- const refs = buildRefs$6(vuln);
7369
+ const refs = buildRefs$7(vuln);
6671
7370
  if (refs !== void 0) req.refs = refs;
6672
7371
  const controlType = deriveControlTypeFromTags(nist);
6673
7372
  if (controlType !== void 0) req.controlType = controlType;
@@ -6750,7 +7449,7 @@ function createRow(baseline, requirement, target) {
6750
7449
  const message = firstResult?.message || "";
6751
7450
  const nistControls = extractArrayFromTags(requirement.tags, "nist");
6752
7451
  const cciControls = extractArrayFromTags(requirement.tags, "cci");
6753
- const effectiveStatus = requirement.effectiveStatus ? String(requirement.effectiveStatus) : String(status);
7452
+ const effectiveStatus = requirementEffectiveStatus(requirement);
6754
7453
  const effectiveImpact = (requirement.effectiveImpact ?? requirement.impact).toFixed(1);
6755
7454
  const disposition = requirement.disposition ? String(requirement.disposition) : "";
6756
7455
  return {
@@ -7904,7 +8603,7 @@ async function convertSplunkToHdf(input, converterVersion = "1.0.0") {
7904
8603
  }) : [];
7905
8604
  const options = { tags: control.tags ?? {} };
7906
8605
  if (control.source_location) options.sourceLocation = control.source_location;
7907
- const req = createRequirement(control.id, control.title, descriptions, control.impact, results, options);
8606
+ const req = createRequirement(control.id, control.title, descriptions, control.impact ?? 0, results, options);
7908
8607
  if (!req.title) delete req.title;
7909
8608
  if (control.code) req.code = control.code;
7910
8609
  const nistTagsRaw = control.tags?.["nist"];
@@ -8050,7 +8749,7 @@ function convertHdfToXml(input) {
8050
8749
  }
8051
8750
  //#endregion
8052
8751
  //#region converters/gitlab-to-hdf/typescript/converter.ts
8053
- function buildRefs$5(vuln) {
8752
+ function buildRefs$6(vuln) {
8054
8753
  const refs = [];
8055
8754
  const seen = /* @__PURE__ */ new Set();
8056
8755
  const appendUrl = (url) => {
@@ -8190,6 +8889,7 @@ async function convertGitlabToHdf(input, converterVersion = "1.0.0") {
8190
8889
  const extras = {};
8191
8890
  for (const [key, values] of Object.entries(idTags)) extras[key] = values;
8192
8891
  const tags = buildNistCciTags(nistTags, cciTags, Object.keys(extras).length > 0 ? extras : void 0);
8892
+ markUnratedSeverity(tags, vuln.severity);
8193
8893
  const descriptions = [];
8194
8894
  if (vuln.description) descriptions.push({
8195
8895
  label: "default",
@@ -8224,7 +8924,7 @@ async function convertGitlabToHdf(input, converterVersion = "1.0.0") {
8224
8924
  descriptions,
8225
8925
  verificationMethod: VerificationMethodEnum.Automated
8226
8926
  };
8227
- const refs = buildRefs$5(vuln);
8927
+ const refs = buildRefs$6(vuln);
8228
8928
  if (refs) req.refs = refs;
8229
8929
  const controlType = deriveControlTypeFromTags(nistTags);
8230
8930
  if (controlType !== void 0) req.controlType = controlType;
@@ -8266,6 +8966,333 @@ async function convertGitlabToHdf(input, converterVersion = "1.0.0") {
8266
8966
  return serializeHdf(hdf);
8267
8967
  }
8268
8968
  //#endregion
8969
+ //#region converters/trivy-to-hdf/typescript/converter.ts
8970
+ const BASELINE_NAME = "Trivy Scan";
8971
+ const STATIC_CCI = nistToCci(DEFAULT_STATIC_ANALYSIS_NIST_TAGS);
8972
+ const CWE_ID_PATTERN$1 = /^CWE-[1-9]\d*$/;
8973
+ const IMPACT_MAP = {
8974
+ critical: .9,
8975
+ high: .7,
8976
+ medium: .5,
8977
+ low: .3,
8978
+ unknown: .5,
8979
+ none: 0,
8980
+ info: 0
8981
+ };
8982
+ function severityToImpact$2(severity) {
8983
+ return IMPACT_MAP[(severity || "").toLowerCase()] ?? .5;
8984
+ }
8985
+ /**
8986
+ * Route Trivy output to HDF Results: native Trivy JSON (SchemaVersion 2) is
8987
+ * parsed here; SARIF / CycloneDX / ASFF / GitLab are delegated to their
8988
+ * converters. TypeScript peer of shared/go/../trivy-to-hdf converter.go.
8989
+ */
8990
+ async function convertTrivyToHdf(input, converterVersion = "1.0.0") {
8991
+ validateInputSize(input, "trivy");
8992
+ if (!input?.trim()) throw new Error("trivy: empty input");
8993
+ const probe = parseJSON(input);
8994
+ if (typeof probe !== "object" || probe === null || Array.isArray(probe)) throw new Error("trivy: input is not a JSON object");
8995
+ if (isNativeTrivy(probe)) return convertNative(input, converterVersion);
8996
+ if (probe.bomFormat === "CycloneDX") return convertCyclonedxToHdf(input, converterVersion);
8997
+ if ("runs" in probe && "version" in probe) return convertSarifToHdf(input, converterVersion);
8998
+ if ("Findings" in probe || "ProductArn" in probe) return convertAsffToHdf(input, converterVersion);
8999
+ if ("vulnerabilities" in probe) return convertGitlabToHdf(input, converterVersion);
9000
+ throw new Error("trivy: not a recognized Trivy output format (native JSON, SARIF, CycloneDX, ASFF, or GitLab)");
9001
+ }
9002
+ function isNativeTrivy(m) {
9003
+ return "SchemaVersion" in m && "ArtifactName" in m && "ArtifactType" in m;
9004
+ }
9005
+ async function convertNative(input, converterVersion) {
9006
+ const report = parseJSON(input);
9007
+ const resultsChecksum = await inputChecksum(input);
9008
+ const scanTime = report.CreatedAt ? parseTimestamp(report.CreatedAt) : null;
9009
+ const startTime = scanTime ?? /* @__PURE__ */ new Date("0001-01-01T00:00:00Z");
9010
+ const requirements = [];
9011
+ for (const res of report.Results ?? []) {
9012
+ for (const v of res.Vulnerabilities ?? []) requirements.push(convertVuln(v, res, startTime));
9013
+ for (const m of res.Misconfigurations ?? []) requirements.push(convertMisconf(m, res, startTime));
9014
+ for (const s of res.Secrets ?? []) requirements.push(convertSecret(s, res, startTime));
9015
+ for (const l of res.Licenses ?? []) requirements.push(convertLicense(l, res, startTime));
9016
+ }
9017
+ if (requirements.length === 0) requirements.push(buildNoFindingsRequirement("trivy-no-findings", `Trivy scanned ${report.ArtifactName ?? "the target"} and reported zero findings.`, startTime));
9018
+ const baseline = createMinimalBaseline(BASELINE_NAME, requirements, { resultsChecksum });
9019
+ if (report.ArtifactName) baseline.title = report.ArtifactName;
9020
+ const component = buildComponent$1(report);
9021
+ return buildHdfResults({
9022
+ generatorName: "trivy-to-hdf",
9023
+ converterVersion,
9024
+ toolName: "Trivy",
9025
+ toolVersion: report.Trivy?.Version,
9026
+ baselines: [baseline],
9027
+ components: component ? [component] : void 0,
9028
+ timestamp: scanTime ?? void 0
9029
+ });
9030
+ }
9031
+ function convertVuln(v, res, startTime) {
9032
+ const descriptions = [{
9033
+ label: "default",
9034
+ data: firstNonEmpty(v.Description, v.Title, v.VulnerabilityID)
9035
+ }];
9036
+ if (v.FixedVersion) descriptions.push({
9037
+ label: "fix",
9038
+ data: `Fixed in version ${v.FixedVersion}.`
9039
+ });
9040
+ const extras = { class: res.Class };
9041
+ putIf(extras, "trivy_type", res.Type);
9042
+ putIf(extras, "severity_source", v.SeveritySource);
9043
+ putIf(extras, "data_source", v.DataSource?.Name);
9044
+ putIf(extras, "published_date", v.PublishedDate);
9045
+ if (v.VendorSeverity && Object.keys(v.VendorSeverity).length > 0) extras.vendor_severity = v.VendorSeverity;
9046
+ const tags = buildTags$2(extras);
9047
+ markUnratedSeverity(tags, v.Severity);
9048
+ const req = {
9049
+ id: `Trivy/${v.VulnerabilityID ?? ""}`,
9050
+ title: `Trivy found ${v.VulnerabilityID ?? ""} in ${pkgLabel(v.PkgName, v.InstalledVersion)}`,
9051
+ descriptions,
9052
+ impact: severityToImpact$2(v.Severity ?? ""),
9053
+ tags,
9054
+ cwe: filterCwes(v.CweIDs),
9055
+ cvss: buildCvssEntries$4(v.CVSS),
9056
+ refs: buildRefs$5([v.PrimaryURL, ...v.References ?? []]),
9057
+ code: JSON.stringify(v, null, 2),
9058
+ controlType: deriveControlTypeFromTags(DEFAULT_STATIC_ANALYSIS_NIST_TAGS),
9059
+ verificationMethod: VerificationMethodEnum.Automated,
9060
+ results: [{
9061
+ status: ResultStatus.Failed,
9062
+ codeDesc: buildVulnCodeDesc(v, res),
9063
+ startTime,
9064
+ message: `Severity: ${firstNonEmpty(v.Severity, "UNKNOWN")}`
9065
+ }]
9066
+ };
9067
+ const ap = buildAffectedPackage$1(v);
9068
+ if (ap.purl) req.affectedPackages = [ap];
9069
+ if (v.PkgPath) req.sourceLocation = { ref: v.PkgPath };
9070
+ return req;
9071
+ }
9072
+ function convertMisconf(m, res, startTime) {
9073
+ const descriptions = [{
9074
+ label: "default",
9075
+ data: firstNonEmpty(m.Description, m.Message, m.Title)
9076
+ }];
9077
+ if (m.Resolution) descriptions.push({
9078
+ label: "fix",
9079
+ data: m.Resolution
9080
+ });
9081
+ const tags = buildTags$2({
9082
+ class: res.Class,
9083
+ ...m.Type ? { misconfig_type: m.Type } : {}
9084
+ });
9085
+ markUnratedSeverity(tags, m.Severity);
9086
+ const req = {
9087
+ id: `Trivy/${firstNonEmpty(m.ID, m.AVDID)}`,
9088
+ title: m.Title,
9089
+ descriptions,
9090
+ impact: severityToImpact$2(m.Severity ?? ""),
9091
+ tags,
9092
+ refs: buildRefs$5([m.PrimaryURL, ...m.References ?? []]),
9093
+ code: JSON.stringify(m, null, 2),
9094
+ controlType: deriveControlTypeFromTags(DEFAULT_STATIC_ANALYSIS_NIST_TAGS),
9095
+ verificationMethod: VerificationMethodEnum.Automated,
9096
+ results: [{
9097
+ status: misconfStatus(m.Status),
9098
+ codeDesc: firstNonEmpty(m.Message, m.Title),
9099
+ startTime
9100
+ }]
9101
+ };
9102
+ if (res.Target) {
9103
+ const sl = { ref: res.Target };
9104
+ if (m.CauseMetadata?.StartLine && m.CauseMetadata.StartLine > 0) sl.line = m.CauseMetadata.StartLine;
9105
+ req.sourceLocation = sl;
9106
+ }
9107
+ return req;
9108
+ }
9109
+ function convertSecret(s, res, startTime) {
9110
+ const tags = buildTags$2({
9111
+ class: res.Class,
9112
+ ...s.Category ? { secret_category: s.Category } : {}
9113
+ });
9114
+ markUnratedSeverity(tags, s.Severity);
9115
+ const req = {
9116
+ id: `Trivy/secret/${s.RuleID ?? ""}@${res.Target ?? ""}:${s.StartLine ?? 0}`,
9117
+ title: s.Title,
9118
+ descriptions: [{
9119
+ label: "default",
9120
+ data: `${firstNonEmpty(s.Title, s.RuleID)} detected in ${res.Target ?? ""} (value redacted by Trivy).`
9121
+ }],
9122
+ impact: severityToImpact$2(s.Severity ?? ""),
9123
+ tags,
9124
+ code: JSON.stringify(s, null, 2),
9125
+ controlType: deriveControlTypeFromTags(DEFAULT_STATIC_ANALYSIS_NIST_TAGS),
9126
+ verificationMethod: VerificationMethodEnum.Automated,
9127
+ results: [{
9128
+ status: ResultStatus.Failed,
9129
+ codeDesc: s.Match ?? "",
9130
+ startTime
9131
+ }]
9132
+ };
9133
+ if (res.Target) {
9134
+ const sl = { ref: res.Target };
9135
+ if (s.StartLine && s.StartLine > 0) sl.line = s.StartLine;
9136
+ req.sourceLocation = sl;
9137
+ }
9138
+ return req;
9139
+ }
9140
+ function convertLicense(l, res, startTime) {
9141
+ const extras = { class: res.Class };
9142
+ putIf(extras, "license_category", l.Category);
9143
+ putIf(extras, "package", l.PkgName);
9144
+ if (l.Confidence && l.Confidence > 0) extras.confidence = l.Confidence;
9145
+ const tags = buildTags$2(extras);
9146
+ markUnratedSeverity(tags, l.Severity);
9147
+ const req = {
9148
+ id: `Trivy/license/${l.PkgName ?? ""}/${l.Name ?? ""}`,
9149
+ title: `${l.Name ?? ""} (${l.Category ?? ""})`,
9150
+ descriptions: [{
9151
+ label: "default",
9152
+ data: `Package ${l.PkgName ?? ""} uses the ${l.Name ?? ""} license (category: ${l.Category ?? ""}).`
9153
+ }],
9154
+ impact: severityToImpact$2(l.Severity ?? ""),
9155
+ tags,
9156
+ refs: buildRefs$5([l.Link]),
9157
+ code: JSON.stringify(l, null, 2),
9158
+ controlType: deriveControlTypeFromTags(DEFAULT_STATIC_ANALYSIS_NIST_TAGS),
9159
+ verificationMethod: VerificationMethodEnum.Automated,
9160
+ results: [{
9161
+ status: ResultStatus.Failed,
9162
+ codeDesc: `${l.PkgName ?? ""}: ${l.Name ?? ""} license`,
9163
+ startTime
9164
+ }]
9165
+ };
9166
+ if (l.FilePath) req.sourceLocation = { ref: l.FilePath };
9167
+ return req;
9168
+ }
9169
+ function buildComponent$1(report) {
9170
+ if (!report.ArtifactName) return void 0;
9171
+ if (report.ArtifactType !== "container_image") return {
9172
+ name: report.ArtifactName,
9173
+ type: TargetType.Artifact
9174
+ };
9175
+ const c = {
9176
+ name: report.ArtifactName,
9177
+ type: TargetType.ContainerImage
9178
+ };
9179
+ const md = report.Metadata;
9180
+ if (!md) return c;
9181
+ if (md.ImageID) c.imageId = md.ImageID;
9182
+ if (md.OS?.Family) c.osName = md.OS.Family;
9183
+ if (md.OS?.Name) c.osVersion = md.OS.Name;
9184
+ const repoDigest = md.RepoDigests?.[0];
9185
+ if (repoDigest) {
9186
+ c.image = repoDigest;
9187
+ const integrity = digestToChecksums(digestPart(repoDigest));
9188
+ if (integrity) c.integrity = integrity;
9189
+ }
9190
+ if (md.ImageConfig?.architecture) c.labels = { architecture: md.ImageConfig.architecture };
9191
+ return c;
9192
+ }
9193
+ function buildTags$2(extras) {
9194
+ return buildNistCciTags(DEFAULT_STATIC_ANALYSIS_NIST_TAGS, STATIC_CCI, extras);
9195
+ }
9196
+ function misconfStatus(status) {
9197
+ switch ((status ?? "").toUpperCase()) {
9198
+ case "PASS": return ResultStatus.Passed;
9199
+ case "EXCEPTION": return ResultStatus.NotApplicable;
9200
+ default: return ResultStatus.Failed;
9201
+ }
9202
+ }
9203
+ function filterCwes(cwes) {
9204
+ if (!cwes) return void 0;
9205
+ const out = cwes.filter((c) => CWE_ID_PATTERN$1.test(c));
9206
+ return out.length > 0 ? out : void 0;
9207
+ }
9208
+ function buildCvssEntries$4(m) {
9209
+ if (!m || Object.keys(m).length === 0) return void 0;
9210
+ const entries = [];
9211
+ for (const src of Object.keys(m).sort()) {
9212
+ const c = m[src];
9213
+ if (!c) continue;
9214
+ if (c.V2Vector || c.V2Score !== void 0) entries.push(buildCvss$3({
9215
+ version: Version.The20,
9216
+ baseVector: c.V2Vector,
9217
+ baseScore: c.V2Score,
9218
+ source: src
9219
+ }));
9220
+ if (c.V3Vector || c.V3Score !== void 0) entries.push(buildCvss$3({
9221
+ version: cvssVersionFromVector(c.V3Vector, Version.The31),
9222
+ baseVector: c.V3Vector,
9223
+ baseScore: c.V3Score,
9224
+ source: src
9225
+ }));
9226
+ if (c.V40Vector || c.V40Score !== void 0) entries.push(buildCvss$3({
9227
+ version: Version.The40,
9228
+ baseVector: c.V40Vector,
9229
+ baseScore: c.V40Score,
9230
+ source: src
9231
+ }));
9232
+ }
9233
+ return entries.length > 0 ? entries : void 0;
9234
+ }
9235
+ const PURL_ECOSYSTEMS = {
9236
+ deb: Ecosystem.Deb,
9237
+ rpm: Ecosystem.RPM,
9238
+ maven: Ecosystem.Maven,
9239
+ npm: Ecosystem.Npm,
9240
+ pypi: Ecosystem.Pypi,
9241
+ gem: Ecosystem.Gem,
9242
+ cargo: Ecosystem.Cargo,
9243
+ golang: Ecosystem.Go,
9244
+ nuget: Ecosystem.Nuget
9245
+ };
9246
+ function buildAffectedPackage$1(v) {
9247
+ const ap = {};
9248
+ if (v.PkgName) ap.name = v.PkgName;
9249
+ if (v.InstalledVersion) ap.version = v.InstalledVersion;
9250
+ if (v.FixedVersion) ap.fixedInVersion = v.FixedVersion;
9251
+ const purl = v.PkgIdentifier?.PURL;
9252
+ if (purl) {
9253
+ ap.purl = purl;
9254
+ const eco = ecosystemFromPurl(purl);
9255
+ if (eco) ap.ecosystem = eco;
9256
+ }
9257
+ return ap;
9258
+ }
9259
+ function ecosystemFromPurl(purl) {
9260
+ const type = (purl.replace(/^pkg:/, "").split(/[/@?]/)[0] ?? "").toLowerCase();
9261
+ return PURL_ECOSYSTEMS[type];
9262
+ }
9263
+ function buildRefs$5(urls) {
9264
+ const seen = /* @__PURE__ */ new Set();
9265
+ const refs = [];
9266
+ for (const u of urls) {
9267
+ if (!u || seen.has(u)) continue;
9268
+ seen.add(u);
9269
+ refs.push({ url: u });
9270
+ }
9271
+ return refs.length > 0 ? refs : void 0;
9272
+ }
9273
+ function buildVulnCodeDesc(v, res) {
9274
+ const parts = [`Package: ${v.PkgName ?? ""}@${v.InstalledVersion ?? ""}`];
9275
+ if (res.Type) parts.push(`Type: ${res.Type}`);
9276
+ if (v.PkgPath) parts.push(`Location: ${v.PkgPath}`);
9277
+ if (v.FixedVersion) parts.push(`Fixed: ${v.FixedVersion}`);
9278
+ return parts.join(" | ");
9279
+ }
9280
+ function pkgLabel(name, version) {
9281
+ if (!version) return name ?? "";
9282
+ return `${name ?? ""}@${version}`;
9283
+ }
9284
+ function digestPart(ref) {
9285
+ const at = ref.lastIndexOf("@");
9286
+ return at >= 0 ? ref.slice(at + 1) : ref;
9287
+ }
9288
+ function firstNonEmpty(...vals) {
9289
+ for (const v of vals) if (v) return v;
9290
+ return "";
9291
+ }
9292
+ function putIf(m, key, val) {
9293
+ if (val) m[key] = val;
9294
+ }
9295
+ //#endregion
8269
9296
  //#region converters/trufflehog-to-hdf/typescript/converter.ts
8270
9297
  /** Hardcoded NIST/CCI constants for credential exposure findings. */
8271
9298
  const TRUFFLEHOG_NIST = ["IA-5 (7)"];
@@ -8586,14 +9613,9 @@ async function convertHipcheckToHdf(input, converterVersion = "1.0.0") {
8586
9613
  }
8587
9614
  //#endregion
8588
9615
  //#region converters/burpsuite-to-hdf/typescript/converter.ts
8589
- const IMPACT_MAPPING$5 = {
8590
- "high": .7,
8591
- "medium": .5,
8592
- "low": .3,
8593
- "information": .3
8594
- };
9616
+ const BURPSUITE_ALIASES = { information: .3 };
8595
9617
  function getImpact$7(severity) {
8596
- return IMPACT_MAPPING$5[severity.toLowerCase()] ?? .3;
9618
+ return severityToImpactWithAliases(severity, BURPSUITE_ALIASES, .3);
8597
9619
  }
8598
9620
  /**
8599
9621
  * Extract CWE identifiers from HTML text.
@@ -8778,13 +9800,6 @@ function buildRequirement$11(issueType, issues, startTime) {
8778
9800
  }
8779
9801
  //#endregion
8780
9802
  //#region converters/dbprotect-to-hdf/typescript/converter.ts
8781
- /** Impact mapping from heimdall2 DBProtect mapper */
8782
- const IMPACT_MAPPING$4 = {
8783
- high: .7,
8784
- medium: .5,
8785
- low: .3,
8786
- informational: 0
8787
- };
8788
9803
  const NAMED_ENTITIES$2 = {
8789
9804
  amp: "&",
8790
9805
  lt: "<",
@@ -8848,10 +9863,9 @@ function getStatus$2(status) {
8848
9863
  function getBacktrace(resultStatus) {
8849
9864
  return resultStatus === "Failed" ? ["DB Protect Failed Check"] : void 0;
8850
9865
  }
8851
- /** Maps DBProtect risk level to HDF impact value */
9866
+ /** Maps DBProtect risk level to HDF impact via the shared standard map (Go parity). */
8852
9867
  function getImpact$6(riskDV) {
8853
- const impact = IMPACT_MAPPING$4[riskDV.toLowerCase()];
8854
- return impact !== void 0 ? impact : .5;
9868
+ return severityToImpactWithAliases(riskDV, {}, .5);
8855
9869
  }
8856
9870
  /** Creates a description string from a finding's task and check category */
8857
9871
  function formatDesc$1(f) {
@@ -8967,6 +9981,7 @@ function buildRequirement$10(checkID, findings, hasStatus) {
8967
9981
  const tags = buildNistCciTags(nist, nistToCci(nist));
8968
9982
  const checkCategory = rep["Check Category"] ?? "";
8969
9983
  if (checkCategory) tags.check_category = checkCategory;
9984
+ markUnratedSeverity(tags, rep["Risk DV"]);
8970
9985
  const descriptions = [{
8971
9986
  label: "default",
8972
9987
  data: formatDesc$1(rep)
@@ -9037,21 +10052,12 @@ async function convertDbprotectToHdf(input, converterVersion = "1.0.0") {
9037
10052
  }
9038
10053
  //#endregion
9039
10054
  //#region converters/twistlock-to-hdf/typescript/converter.ts
9040
- /**
9041
- * Maps Twistlock severity strings to HDF impact values.
9042
- * Includes "important" (alias for critical) and "moderate" (alias for medium)
9043
- * which appear in some Twistlock outputs.
9044
- */
10055
+ const TWISTLOCK_ALIASES = {
10056
+ important: .9,
10057
+ moderate: .5
10058
+ };
9045
10059
  function twistlockSeverityToImpact(severity) {
9046
- switch (severity.toLowerCase()) {
9047
- case "critical":
9048
- case "important": return .9;
9049
- case "high": return .7;
9050
- case "medium":
9051
- case "moderate": return .5;
9052
- case "low": return .3;
9053
- default: return .5;
9054
- }
10060
+ return severityToImpactWithAliases(severity, TWISTLOCK_ALIASES, .5);
9055
10061
  }
9056
10062
  /**
9057
10063
  * Builds the baseline title from scan result data.
@@ -9247,6 +10253,7 @@ function buildRequirement$9(vuln, packageTypes, distro) {
9247
10253
  const extras = { cveid: [vuln.id] };
9248
10254
  if (typeof vuln.cvss === "number" && vuln.cvss > 0) extras["cvss_base_score"] = vuln.cvss;
9249
10255
  const tags = buildNistCciTags(nist, cciTags, extras);
10256
+ markUnratedSeverity(tags, vuln.severity);
9250
10257
  const descriptions = [{
9251
10258
  label: "default",
9252
10259
  data: vuln.description
@@ -9508,7 +10515,7 @@ function buildAffectedPackageFromComponent(c) {
9508
10515
  let ecosystem;
9509
10516
  if (c.purl) ecosystem = ecosystemFromPurlType(parsePurl(c.purl)?.type);
9510
10517
  else if (c.name && c.version) ecosystem = ecosystemFromPurlType(void 0);
9511
- return buildAffectedPackage$1({
10518
+ return buildAffectedPackage$2({
9512
10519
  name: c.name,
9513
10520
  version: c.version,
9514
10521
  ecosystem,
@@ -9726,6 +10733,7 @@ function buildRequirement$7(entryID, entries, scanTime) {
9726
10733
  cci: nistToCci(nist)
9727
10734
  };
9728
10735
  if (cveIDs.length > 0) tags["cve"] = cveIDs;
10736
+ markUnratedSeverity(tags, rep.severity);
9729
10737
  const descriptions = [{
9730
10738
  label: "default",
9731
10739
  data: formatDescription(rep)
@@ -9779,7 +10787,7 @@ function buildAffectedPackageFromEntry(entry) {
9779
10787
  ecosystem = ecosystemFromXraySource(parsed.scheme);
9780
10788
  }
9781
10789
  const fixed = entry.component_versions?.fixed_versions?.[0];
9782
- return buildAffectedPackage$1({
10790
+ return buildAffectedPackage$2({
9783
10791
  name,
9784
10792
  version,
9785
10793
  ecosystem: ecosystem ?? (name ? Ecosystem.Generic : void 0),
@@ -10011,7 +11019,7 @@ function buildRequirement$6(vuln, scanTime, ml) {
10011
11019
  const refs = buildRefs$3(vuln);
10012
11020
  if (refs) req.refs = refs;
10013
11021
  const cpe23 = (vuln.cpes ?? []).find((c) => /^cpe:2\.3:[aho]:/.test(c));
10014
- const pkg = buildAffectedPackage$1({
11022
+ const pkg = buildAffectedPackage$2({
10015
11023
  name: vuln.package_name,
10016
11024
  version: vuln.package_version,
10017
11025
  ecosystem: vuln.package_name && vuln.package_version ? Ecosystem.Generic : void 0,
@@ -10033,12 +11041,6 @@ function imageTitle(report) {
10033
11041
  function targetNameFromReport(report) {
10034
11042
  return `${report.registry}/${report.repository}:${report.tag}`;
10035
11043
  }
10036
- const DIGEST_ALGORITHMS = {
10037
- sha256: HashAlgorithm.Sha256,
10038
- sha384: HashAlgorithm.Sha384,
10039
- sha512: HashAlgorithm.Sha512,
10040
- blake3: HashAlgorithm.Blake3
10041
- };
10042
11044
  /**
10043
11045
  * Splits NeuVector's base_os "name:version" form (e.g. "alpine:3.12.1") into OS
10044
11046
  * name and version. A value with no ":" is the name with no version; an empty
@@ -10060,28 +11062,6 @@ function splitBaseOS(baseOS) {
10060
11062
  };
10061
11063
  }
10062
11064
  /**
10063
- * Turns the report digest ("sha256:<hex>") into the component's Integrity
10064
- * checksum, folding the algorithm prefix into Checksum.algorithm. Returns
10065
- * undefined when the report carries no digest.
10066
- */
10067
- function digestIntegrity(digest) {
10068
- if (!digest) return;
10069
- let algorithm = HashAlgorithm.Sha256;
10070
- let value = digest;
10071
- const i = digest.indexOf(":");
10072
- if (i >= 0) {
10073
- const mapped = DIGEST_ALGORITHMS[digest.slice(0, i)];
10074
- if (mapped) {
10075
- algorithm = mapped;
10076
- value = digest.slice(i + 1);
10077
- }
10078
- }
10079
- return [{
10080
- algorithm,
10081
- value
10082
- }];
10083
- }
10084
- /**
10085
11065
  * Assembles the scan-wide containerImage component from the report's image
10086
11066
  * identity. base_os → osName/osVersion, digest → integrity, plus
10087
11067
  * imageId/registry/repository/tag when the report carries them.
@@ -10104,7 +11084,7 @@ function buildComponent(report) {
10104
11084
  if (report.registry) component.registry = report.registry;
10105
11085
  if (report.repository) component.repository = report.repository;
10106
11086
  if (report.tag) component.tag = report.tag;
10107
- const integrity = digestIntegrity(report.digest);
11087
+ const integrity = digestToChecksums(report.digest);
10108
11088
  if (integrity) component.integrity = integrity;
10109
11089
  return component;
10110
11090
  }
@@ -10323,8 +11303,9 @@ function buildRequirement$5(desc, vulns, snippetMap, startTimeStr) {
10323
11303
  label: "tips",
10324
11304
  data: tipsData
10325
11305
  });
10326
- let impact = 0;
10327
- if (vulns.length > 0) impact = roundImpact(parseFloat(vulns[0].InstanceInfo?.InstanceSeverity ?? "0") / 5);
11306
+ const instanceSeverity = parseFloat(vulns[0]?.InstanceInfo?.InstanceSeverity ?? "0");
11307
+ const impact = instanceSeverity > 0 ? roundImpact(instanceSeverity / 5) : 0;
11308
+ if (!(instanceSeverity > 0)) markUnratedSeverity(tags, "");
10328
11309
  const { items: limitedVulns, truncated } = limitArray(vulns);
10329
11310
  /* v8 ignore next -- truncation only triggers with >100K items */
10330
11311
  if (truncated) console.warn(`WARNING: Input truncated at ${limitedVulns.length} vulnerability items (original: ${vulns.length})`);
@@ -10522,6 +11503,7 @@ function buildRequirement$4(rec, scanTime) {
10522
11503
  const extras = {};
10523
11504
  if (rec["CVE ID"]) extras["cve"] = [rec["CVE ID"]];
10524
11505
  const tags = buildNistCciTags(nist, cciTags, Object.keys(extras).length > 0 ? extras : void 0);
11506
+ markUnratedSeverity(tags, rec.Severity);
10525
11507
  const descriptions = [{
10526
11508
  label: "default",
10527
11509
  data: rec.Description
@@ -10610,7 +11592,7 @@ function buildAffectedPackageFromRecord(rec) {
10610
11592
  const version = rec["Package Version"];
10611
11593
  if (!name || !version) return void 0;
10612
11594
  const fixMatch = rec["Fix Status"] ? FIX_VERSION_PATTERN.exec(rec["Fix Status"]) : null;
10613
- return buildAffectedPackage$1({
11595
+ return buildAffectedPackage$2({
10614
11596
  name,
10615
11597
  version,
10616
11598
  ecosystem: ecosystemFromDistro(rec.Distro),
@@ -10680,16 +11662,9 @@ async function convertPrismaToHdf(input, converterVersion = "1.0.0") {
10680
11662
  }
10681
11663
  //#endregion
10682
11664
  //#region converters/netsparker-to-hdf/typescript/converter.ts
10683
- const IMPACT_MAPPING$3 = {
10684
- "critical": 1,
10685
- "high": .7,
10686
- "medium": .5,
10687
- "low": .3,
10688
- "best_practice": 0,
10689
- "information": 0
10690
- };
11665
+ const NETSPARKER_ALIASES = { best_practice: 0 };
10691
11666
  function getImpact$2(severity) {
10692
- return IMPACT_MAPPING$3[severity.toLowerCase()] ?? .5;
11667
+ return severityToImpactWithAliases(severity, NETSPARKER_ALIASES, .5);
10693
11668
  }
10694
11669
  const NETSPARKER_US_DATETIME = /^\d{2}\/\d{2}\/\d{4} \d{1,2}:\d{2} (AM|PM)$/;
10695
11670
  function parseNetsparkerTimestamp(s) {
@@ -10824,6 +11799,7 @@ function buildRequirement$3(vuln, initiated) {
10824
11799
  const pci32 = vuln.classification?.pci32;
10825
11800
  if (pci32) extras.pci32 = pci32;
10826
11801
  const tags = buildNistCciTags(nist, cciTags, Object.keys(extras).length > 0 ? extras : void 0);
11802
+ markUnratedSeverity(tags, vuln.severity);
10827
11803
  const descriptions = [{
10828
11804
  label: "default",
10829
11805
  data: formatControlDesc(vuln) || vuln.name || ""
@@ -11219,14 +12195,16 @@ function buildRequirementFromResult(result, filename) {
11219
12195
  const startTime = (startTimeStr ? parseTimestamp(startTimeStr) : null) ?? /* @__PURE__ */ new Date("0001-01-01T00:00:00Z");
11220
12196
  const runTime = computeRunTime(result.response.milestones?.service_started, result.response.milestones?.service_completed);
11221
12197
  const score = result.result.score;
11222
- const status = determineStatus(score);
11223
- const toResult = (codeDesc) => createResult(status, void 0, {
12198
+ const scoreless = score === void 0 || score === null;
12199
+ const status = scoreless ? ResultStatus.NotReviewed : determineStatus(score);
12200
+ const message = scoreless ? "Conveyor result carries no score; verdict not evaluated." : void 0;
12201
+ const toResult = (codeDesc) => createResult(status, message, {
11224
12202
  codeDesc,
11225
12203
  startTime,
11226
12204
  runTime
11227
12205
  });
11228
12206
  const results = result.result.sections.length > 0 ? result.result.sections.map((section) => toResult(buildCodeDesc$1(section, scannerName))) : [toResult(`No sections reported by ${scannerName}`)];
11229
- const req = createRequirement(result.sha256, filename, descriptions, scoreToImpact(score), results, { tags });
12207
+ const req = createRequirement(result.sha256, filename, descriptions, scoreless ? 0 : scoreToImpact(score), results, { tags });
11230
12208
  req.verificationMethod = VerificationMethodEnum.Automated;
11231
12209
  const controlType = deriveControlTypeFromTags([...nist]);
11232
12210
  if (controlType !== void 0) req.controlType = controlType;
@@ -11301,17 +12279,17 @@ async function convertConveyorToHdf(input, converterVersion = "1.0.0") {
11301
12279
  */
11302
12280
  /** Attribute prefix used by fast-xml-parser — all XML attributes are accessed via `@_name`. */
11303
12281
  const A = "@_";
11304
- /** Veracode severity level (0-5) to HDF impact mapping. */
11305
- const IMPACT_MAPPING$2 = /* @__PURE__ */ new Map([
11306
- ["5", .9],
11307
- ["4", .7],
11308
- ["3", .5],
11309
- ["2", .3],
11310
- ["1", .1],
11311
- ["0", 0]
11312
- ]);
12282
+ /** Veracode severity level (0-5) aliases; mirrors Go's veracodeAliases. */
12283
+ const VERACODE_ALIASES = {
12284
+ "5": .9,
12285
+ "4": .7,
12286
+ "3": .5,
12287
+ "2": .3,
12288
+ "1": .1,
12289
+ "0": 0
12290
+ };
11313
12291
  function veracodeSeverityToImpact(severity) {
11314
- return IMPACT_MAPPING$2.get(severity) ?? .1;
12292
+ return severityToImpactWithAliases(severity, VERACODE_ALIASES, .1);
11315
12293
  }
11316
12294
  /** Get an attribute from a parsed XML node, entity-decoded. */
11317
12295
  function attr(node, name) {
@@ -11556,12 +12534,12 @@ function buildCWERequirements(severities, firstBuildDate) {
11556
12534
  for (const sev of severities) {
11557
12535
  const impact = veracodeSeverityToImpact(attr(sev, "level"));
11558
12536
  const categories = ensureArray(sev.category);
11559
- for (const cat of categories) requirements.push(buildCWERequirement(cat, impact, firstBuildDate));
12537
+ for (const cat of categories) requirements.push(buildCWERequirement(cat, attr(sev, "level"), impact, firstBuildDate));
11560
12538
  }
11561
12539
  return requirements;
11562
12540
  }
11563
12541
  /** Build a single CWE-based requirement from a category. */
11564
- function buildCWERequirement(cat, impact, firstBuildDate) {
12542
+ function buildCWERequirement(cat, sevLevel, impact, firstBuildDate) {
11565
12543
  const cwes = ensureArray(cat.cwe);
11566
12544
  const cweIDs = cwes.map((c) => attr(c, "cweid")).filter(Boolean);
11567
12545
  const nist = mapCWEToNIST(cweIDs, DEFAULT_REMEDIATION_NIST_TAGS$1);
@@ -11574,6 +12552,7 @@ function buildCWERequirement(cat, impact, firstBuildDate) {
11574
12552
  if (values.length > 0) extras[key] = values;
11575
12553
  }
11576
12554
  const tags = buildNistCciTags(nist, cciTags, extras);
12555
+ markUnratedSeverity(tags, sevLevel);
11577
12556
  const cweList = cweIDs.map((id) => `CWE-${id}`);
11578
12557
  const descriptions = [{
11579
12558
  label: "default",
@@ -11685,6 +12664,7 @@ function buildCVERequirement(vuln, components, firstBuildDate) {
11685
12664
  else nist = DEFAULT_REMEDIATION_NIST_TAGS$1;
11686
12665
  const cciTags = nistToCci(nist);
11687
12666
  const tags = buildNistCciTags(nist, cciTags, {});
12667
+ markUnratedSeverity(tags, attr(vuln, "severity"));
11688
12668
  const startTime = parseVeracodeTimestamp(firstBuildDate) ?? /* @__PURE__ */ new Date();
11689
12669
  const results = components.map((comp) => createResult(ResultStatus.Failed, void 0, {
11690
12670
  codeDesc: formatSCACodeDesc(comp),
@@ -12017,14 +12997,6 @@ function repoNameFromURI(uri) {
12017
12997
  //#endregion
12018
12998
  //#region converters/msft-defender-cloud-to-hdf/typescript/converter.ts
12019
12999
  /**
12020
- * Severity to HDF impact mapping.
12021
- */
12022
- const IMPACT_MAPPING$1 = {
12023
- high: .7,
12024
- medium: .5,
12025
- low: .3
12026
- };
12027
- /**
12028
13000
  * Maps Azure status code to HDF ResultStatus.
12029
13001
  */
12030
13002
  function mapStatus(code) {
@@ -12052,7 +13024,7 @@ function extractSubscriptionID(resourcePath) {
12052
13024
  function buildRequirement(assessmentID, assessments, scanTime) {
12053
13025
  const rep = assessments[0];
12054
13026
  const meta = rep.properties.metadata;
12055
- const impact = IMPACT_MAPPING$1[meta.severity.toLowerCase()] ?? .5;
13027
+ const impact = severityToImpact(meta.severity);
12056
13028
  const tags = {};
12057
13029
  if (meta.categories.length > 0) tags["categories"] = meta.categories;
12058
13030
  if (meta.tactics.length > 0) tags["tactics"] = meta.tactics;
@@ -12144,21 +13116,6 @@ async function convertMsftDefenderCloudToHdf(input, converterVersion = "1.0.0")
12144
13116
  //#endregion
12145
13117
  //#region converters/msft-defender-endpoint-to-hdf/typescript/converter.ts
12146
13118
  /**
12147
- * Severity to HDF impact mapping.
12148
- */
12149
- const IMPACT_MAPPING = {
12150
- high: .7,
12151
- medium: .5,
12152
- low: .3,
12153
- informational: 0
12154
- };
12155
- /**
12156
- * Maps MDE severity to HDF impact value.
12157
- */
12158
- function severityToImpact$2(severity) {
12159
- return IMPACT_MAPPING[severity.toLowerCase()] ?? .5;
12160
- }
12161
- /**
12162
13119
  * An MDE alert is a detection that fired, so every alert is a raw Failed result.
12163
13120
  * Consumer triage (falsePositive, expected-activity) never flips the raw status —
12164
13121
  * it rides in a structured Status_Override that carries effectiveStatus + full
@@ -12262,7 +13219,7 @@ function formatMessage(alert) {
12262
13219
  const parts = [];
12263
13220
  parts.push(`Alert: ${alert.title}`);
12264
13221
  parts.push(`Status: ${alert.status}`);
12265
- parts.push(`Severity: ${alert.severity}`);
13222
+ parts.push(`Severity: ${alert.severity ?? ""}`);
12266
13223
  if (alert.threatDisplayName) parts.push(`Threat: ${alert.threatDisplayName}`);
12267
13224
  if (alert.alertWebUrl) parts.push(`URL: ${alert.alertWebUrl}`);
12268
13225
  return parts.join("\n");
@@ -12357,7 +13314,7 @@ function deriveScanTimestamp(alerts) {
12357
13314
  * Converts a single MDE alert to an HDF EvaluatedRequirement.
12358
13315
  */
12359
13316
  function alertToRequirement(alert, scanTime) {
12360
- const impact = severityToImpact$2(alert.severity);
13317
+ const impact = severityToImpact(alert.severity);
12361
13318
  const codeDesc = formatEvidence$1(alert.evidence ?? []);
12362
13319
  const message = formatMessage(alert);
12363
13320
  const startTime = resolveStartTime(alert, scanTime);
@@ -12644,7 +13601,9 @@ function buildTestResultObj(hdfData, baseline) {
12644
13601
  for (const req of baseline.requirements) {
12645
13602
  const ruleIdRef = sanitizeXccdfId("xccdf_hdf_rule_" + req.id + "_rule");
12646
13603
  const stigId = tagString(req.tags, "stig_id");
12647
- const effective = req.effectiveStatus ? hdfStatusToXccdf(req.effectiveStatus) : void 0;
13604
+ const statusInput = requirementStatusInput(req);
13605
+ const resolved = computeEffectiveStatus(statusInput);
13606
+ const effective = resolved !== worstStatus(statusInput.resultStatuses ?? []) ? hdfStatusToXccdf(resolved) : void 0;
12648
13607
  for (const result of req.results) {
12649
13608
  const status = effective ?? hdfStatusToXccdf(result.status);
12650
13609
  if (status === "pass") {
@@ -12829,11 +13788,69 @@ function toKebabCase(title, fallback) {
12829
13788
  if (!title) return fallback;
12830
13789
  let name = title.toLowerCase();
12831
13790
  name = name.replace(/[^a-z0-9]/g, "-");
12832
- while (name.includes("--")) name = name.replace(/--/g, "-");
12833
- name = name.replace(/^-+|-+$/g, "");
13791
+ name = name.replace(/-+/g, "-");
13792
+ let start = 0;
13793
+ while (start < name.length && name[start] === "-") start++;
13794
+ let end = name.length;
13795
+ while (end > start && name[end - 1] === "-") end--;
13796
+ name = name.slice(start, end);
12834
13797
  if (name.length > 80) name = name.slice(0, 80);
12835
13798
  return name;
12836
13799
  }
13800
+ /**
13801
+ * Encode an arbitrary identifier into OSCAL's TokenDatatype shape:
13802
+ * `^(\p{L}|_)(\p{L}|\p{N}|[.\-_])*$`
13803
+ *
13804
+ * HDF requirement ids come from whatever the source tool numbers its rules with,
13805
+ * and only some of those shapes are already tokens. Measured across this
13806
+ * package's converter fixtures, 46% are not (57% of the distinct ids): package-style ids
13807
+ * carrying '/', CIS control numbers starting with a digit, advisory ids carrying
13808
+ * ':'. Copying one of those into a token-typed OSCAL field produces a document
13809
+ * the target schema rejects while the converter resolves successfully.
13810
+ *
13811
+ * The kept set is deliberately ASCII — [A-Za-z0-9._-] — rather than the wider
13812
+ * \p{L}/\p{N} the pattern permits. Delegating to each platform's Unicode tables
13813
+ * looked equivalent and was not: V8's \p{L}/\p{N} and Go's unicode package are
13814
+ * built from different Unicode versions, and comparing the two implementations
13815
+ * across the whole code-point range turned up 4657 characters they disagree on.
13816
+ * An explicit ASCII set has no such dependency and is identical in both languages
13817
+ * by construction. It costs nothing on real data: no requirement id in this
13818
+ * package's converter fixtures is non-ASCII, and a non-ASCII one
13819
+ * is preserved in full by the caller's source-id prop regardless.
13820
+ *
13821
+ * Every character outside that set becomes '_', and a leading '_' is prepended
13822
+ * when the result would not start with a letter or '_'. An id built only from
13823
+ * the kept characters is returned unchanged; note that this is narrower than
13824
+ * token shape, so a token-valid non-ASCII id such as "café" is still rewritten.
13825
+ *
13826
+ * Two different ids can encode to the same token ('a/b' and 'a:b' both yield
13827
+ * 'a_b'), which is why callers must also record the source id in the emitted
13828
+ * document — for SAR that is a prop on the finding, trimmed because OSCAL's
13829
+ * StringDatatype forbids a padded value.
13830
+ */
13831
+ function oscalToken(s) {
13832
+ if (s === "") return "";
13833
+ const out = [...s].map((ch) => /[A-Za-z0-9.\-_]/.test(ch) ? ch : "_").join("");
13834
+ const [first = ""] = [...out];
13835
+ return /[A-Za-z_]/.test(first) ? out : `_${out}`;
13836
+ }
13837
+ /**
13838
+ * Render a value for a field OSCAL types as StringDatatype, whose pattern
13839
+ * `^\S(.*\S)?$` forbids an empty value and any leading or trailing whitespace.
13840
+ * HDF constrains none of the strings that feed those fields — no minLength
13841
+ * anywhere in hdf-amendments — so a padded or empty value is valid HDF that
13842
+ * would otherwise produce a document the target schema rejects at exit 0.
13843
+ *
13844
+ * Returns '' when nothing survives trimming, which callers treat as "omit this
13845
+ * field" rather than substituting a placeholder: OSCAL marks most of these
13846
+ * fields optional, so leaving one out says exactly as much as the source did.
13847
+ * The companion for token-typed fields is oscalToken.
13848
+ *
13849
+ * Mirrors OSCALString in the Go peer.
13850
+ */
13851
+ function oscalString(s) {
13852
+ return s.trim();
13853
+ }
12837
13854
  //#endregion
12838
13855
  //#region converters/hdf-to-oscal-sar/typescript/converter.ts
12839
13856
  /**
@@ -12896,12 +13913,18 @@ function buildOSCALDocument(hdfResults) {
12896
13913
  else importAP = { href: "#" };
12897
13914
  const subjects = buildSubjects(hdfResults.components);
12898
13915
  const results = [];
12899
- for (const baseline of hdfResults.baselines) results.push(baselineToResult(baseline, timestamp, toolActorUuid, subjects));
13916
+ const resources = [];
13917
+ for (const baseline of hdfResults.baselines) {
13918
+ const { result, resources: baselineResources } = baselineToResult(baseline, timestamp, toolActorUuid, subjects);
13919
+ results.push(result);
13920
+ resources.push(...baselineResources);
13921
+ }
12900
13922
  return { "assessment-results": {
12901
13923
  uuid: crypto.randomUUID(),
12902
13924
  metadata,
12903
13925
  "import-ap": importAP,
12904
- results
13926
+ results,
13927
+ ...resources.length > 0 ? { "back-matter": { resources } } : {}
12905
13928
  } };
12906
13929
  }
12907
13930
  /** Earliest startTime across the results, or undefined if none carry one. */
@@ -12933,7 +13956,8 @@ function assessmentStart(baseline, fallback) {
12933
13956
  return formatAssessmentTime(earliest, fallback);
12934
13957
  }
12935
13958
  /**
12936
- * Converts a single EvaluatedBaseline to an OSCAL Result.
13959
+ * Converts a single EvaluatedBaseline to an OSCAL Result plus any back-matter
13960
+ * resources (embedded check source code) its findings link to.
12937
13961
  */
12938
13962
  function baselineToResult(baseline, timestamp, toolActorUuid, subjects) {
12939
13963
  let title = baseline.name;
@@ -12948,14 +13972,18 @@ function baselineToResult(baseline, timestamp, toolActorUuid, subjects) {
12948
13972
  const findings = [];
12949
13973
  const observations = [];
12950
13974
  const risks = [];
13975
+ const resources = [];
12951
13976
  const includeControls = [];
12952
13977
  const seenControl = /* @__PURE__ */ new Set();
12953
13978
  for (const req of baseline.requirements) {
12954
- const { finding, observation, risk } = requirementToFindingSet(req, timestamp, toolActorUuid, subjects);
13979
+ const nistId = nistTagToControlId(req.id);
13980
+ if (nistId === "") continue;
13981
+ const { finding, observation, risk, resource } = requirementToFindingSet(req, timestamp, toolActorUuid, subjects);
12955
13982
  findings.push(finding);
12956
13983
  if (observation) observations.push(observation);
12957
13984
  if (risk) risks.push(risk);
12958
- const cid = nistTagToControlId(req.id);
13985
+ if (resource) resources.push(resource);
13986
+ const cid = oscalToken(nistId);
12959
13987
  if (cid !== "" && !seenControl.has(cid)) {
12960
13988
  seenControl.add(cid);
12961
13989
  includeControls.push({ "control-id": cid });
@@ -12963,15 +13991,18 @@ function baselineToResult(baseline, timestamp, toolActorUuid, subjects) {
12963
13991
  }
12964
13992
  const controlSelection = includeControls.length > 0 ? { "include-controls": includeControls } : {};
12965
13993
  return {
12966
- uuid: crypto.randomUUID(),
12967
- title,
12968
- description,
12969
- start: assessmentStart(baseline, timestamp),
12970
- ...resultProps.length > 0 ? { props: resultProps } : {},
12971
- "reviewed-controls": { "control-selections": [controlSelection] },
12972
- findings,
12973
- observations,
12974
- risks
13994
+ result: {
13995
+ uuid: crypto.randomUUID(),
13996
+ title,
13997
+ description,
13998
+ start: assessmentStart(baseline, timestamp),
13999
+ ...resultProps.length > 0 ? { props: resultProps } : {},
14000
+ "reviewed-controls": { "control-selections": [controlSelection] },
14001
+ findings,
14002
+ observations,
14003
+ risks
14004
+ },
14005
+ resources
12975
14006
  };
12976
14007
  }
12977
14008
  /**
@@ -12992,18 +14023,35 @@ function buildSubjects(components) {
12992
14023
  * Converts an EvaluatedRequirement into a Finding, optional Observation, and optional Risk.
12993
14024
  */
12994
14025
  function requirementToFindingSet(req, timestamp, toolActorUuid, subjects) {
12995
- const controlID = nistTagToControlId(req.id);
14026
+ const controlID = oscalToken(nistTagToControlId(req.id));
12996
14027
  const results = req.results ?? [];
12997
14028
  const descriptions = req.descriptions ?? [];
12998
- const { state, reason } = effectiveState(req, results);
14029
+ const { state, reason } = effectiveState(req);
12999
14030
  const findingDesc = extractDefaultDescription(descriptions);
13000
- const props = [];
14031
+ const props = [{
14032
+ name: "hdf-requirement-id",
14033
+ value: oscalString(req.id)
14034
+ }];
13001
14035
  const addProp = (name, value) => {
13002
14036
  if (value !== "") props.push({
13003
14037
  name,
13004
14038
  value
13005
14039
  });
13006
14040
  };
14041
+ const addProseProp = (name, text) => {
14042
+ const preview = previewLine(text);
14043
+ if (preview === "") return;
14044
+ const p = {
14045
+ name,
14046
+ value: preview
14047
+ };
14048
+ if (preview !== text) p.remarks = text;
14049
+ props.push(p);
14050
+ };
14051
+ const descriptionByLabel = (label) => {
14052
+ const d = descriptions.find((x) => x.label === label);
14053
+ return d ? d.data : "";
14054
+ };
13007
14055
  const pushTagValues = (key) => {
13008
14056
  const raw = req.tags?.[key];
13009
14057
  if (Array.isArray(raw)) {
@@ -13012,15 +14060,9 @@ function requirementToFindingSet(req, timestamp, toolActorUuid, subjects) {
13012
14060
  };
13013
14061
  pushTagValues("nist");
13014
14062
  pushTagValues("cci");
13015
- if (req.code != null) addProp("code", req.code);
13016
- for (const label of [
13017
- "check",
13018
- "fix",
13019
- "rationale"
13020
- ]) {
13021
- const d = descriptions.find((x) => x.label === label);
13022
- addProp(label, d ? d.data : "");
13023
- }
14063
+ addProseProp("check", descriptionByLabel("check"));
14064
+ addProseProp("rationale", descriptionByLabel("rationale"));
14065
+ if (req.impact <= 0) addProseProp("fix", descriptionByLabel("fix"));
13024
14066
  if (req.controlType) addProp("control-type", req.controlType);
13025
14067
  if (req.verificationMethod) addProp("verification-method", req.verificationMethod);
13026
14068
  if (req.applicability) addProp("applicability", req.applicability);
@@ -13048,10 +14090,7 @@ function requirementToFindingSet(req, timestamp, toolActorUuid, subjects) {
13048
14090
  href: o.uri,
13049
14091
  rel: "reference"
13050
14092
  });
13051
- else if (typeof o.ref === "string") props.push({
13052
- name: "reference",
13053
- value: o.ref
13054
- });
14093
+ else if (typeof o.ref === "string") addProseProp("reference", o.ref);
13055
14094
  }
13056
14095
  for (const er of req.externalReferences ?? []) if (typeof er.href === "string" && er.href !== "") links.push({
13057
14096
  href: er.href,
@@ -13059,6 +14098,22 @@ function requirementToFindingSet(req, timestamp, toolActorUuid, subjects) {
13059
14098
  });
13060
14099
  let title = req.id;
13061
14100
  if (req.title && req.title !== "") title = req.title;
14101
+ let resource;
14102
+ if (req.code != null && req.code.trim() !== "") {
14103
+ const resourceUuid = crypto.randomUUID();
14104
+ resource = {
14105
+ uuid: resourceUuid,
14106
+ title: `Check source code for ${req.id}`,
14107
+ base64: {
14108
+ value: Buffer.from(req.code, "utf-8").toString("base64"),
14109
+ "media-type": "text/plain"
14110
+ }
14111
+ };
14112
+ links.push({
14113
+ href: `#${resourceUuid}`,
14114
+ rel: "code"
14115
+ });
14116
+ }
13062
14117
  const targetStatus = { state };
13063
14118
  if (reason) targetStatus.reason = reason;
13064
14119
  const remarks = overrideRemarks(req);
@@ -13127,16 +14182,32 @@ function requirementToFindingSet(req, timestamp, toolActorUuid, subjects) {
13127
14182
  return {
13128
14183
  finding,
13129
14184
  observation,
13130
- risk
14185
+ risk,
14186
+ resource
13131
14187
  };
13132
14188
  }
13133
14189
  /**
13134
- * Derives the OSCAL finding target state/reason from the requirement's
13135
- * effectiveStatus when present, otherwise from aggregated raw results.
14190
+ * Reduces prose to a single line legal as an OSCAL StringDatatype prop value:
14191
+ * the first non-empty line, trimmed, truncated to 120 code points.
13136
14192
  */
13137
- function effectiveState(req, results) {
13138
- if (req.effectiveStatus) return oscalStateFromStatus(req.effectiveStatus);
13139
- return aggregateStatus(results);
14193
+ function previewLine(text) {
14194
+ for (let line of text.split("\n")) {
14195
+ line = line.trim();
14196
+ if (line === "") continue;
14197
+ const maxRunes = 120;
14198
+ const runes = Array.from(line);
14199
+ if (runes.length > maxRunes) line = `${runes.slice(0, maxRunes - 3).join("").trim()}...`;
14200
+ return line;
14201
+ }
14202
+ return "";
14203
+ }
14204
+ /**
14205
+ * Derives the OSCAL finding target state/reason from the canonical ladder —
14206
+ * the stored effectiveStatus field is never read (it is an output cache; see
14207
+ * status-determination.md).
14208
+ */
14209
+ function effectiveState(req) {
14210
+ return oscalStateFromStatus(requirementEffectiveStatus(req));
13140
14211
  }
13141
14212
  /** Maps a single HDF result status to an OSCAL finding target state/reason. */
13142
14213
  function oscalStateFromStatus(status) {
@@ -13268,62 +14339,6 @@ function formatFloat(n) {
13268
14339
  return String(n);
13269
14340
  }
13270
14341
  /**
13271
- * Determines the overall finding status from requirement results.
13272
- */
13273
- function aggregateStatus(results) {
13274
- if (results.length === 0) return {
13275
- state: "not-satisfied",
13276
- reason: "other"
13277
- };
13278
- let hasFailed = false;
13279
- let hasError = false;
13280
- let hasNotReviewed = false;
13281
- let hasNotApplicable = false;
13282
- let allPassed = true;
13283
- for (const r of results) switch (r.status) {
13284
- case "passed": break;
13285
- case "failed":
13286
- hasFailed = true;
13287
- allPassed = false;
13288
- break;
13289
- case "error":
13290
- hasError = true;
13291
- allPassed = false;
13292
- break;
13293
- case "notReviewed":
13294
- hasNotReviewed = true;
13295
- allPassed = false;
13296
- break;
13297
- case "notApplicable":
13298
- hasNotApplicable = true;
13299
- allPassed = false;
13300
- break;
13301
- default:
13302
- allPassed = false;
13303
- break;
13304
- }
13305
- if (allPassed) return {
13306
- state: "satisfied",
13307
- reason: ""
13308
- };
13309
- if (hasFailed || hasError) return {
13310
- state: "not-satisfied",
13311
- reason: ""
13312
- };
13313
- if (hasNotReviewed) return {
13314
- state: "not-satisfied",
13315
- reason: "other"
13316
- };
13317
- if (hasNotApplicable) return {
13318
- state: "not-satisfied",
13319
- reason: "not-applicable"
13320
- };
13321
- return {
13322
- state: "not-satisfied",
13323
- reason: ""
13324
- };
13325
- }
13326
- /**
13327
14342
  * Finds the "default" labeled description.
13328
14343
  */
13329
14344
  function extractDefaultDescription(descriptions) {
@@ -14522,7 +15537,11 @@ function advisoryURI(publisher, tracking) {
14522
15537
  /* c8 ignore next */
14523
15538
  const id = tracking.id ?? "";
14524
15539
  const ns = publisher.namespace;
14525
- if (ns && id) return `${ns.replace(/\/+$/, "")}/${id}`;
15540
+ if (ns && id) {
15541
+ let end = ns.length;
15542
+ while (end > 0 && ns[end - 1] === "/") end--;
15543
+ return `${ns.slice(0, end)}/${id}`;
15544
+ }
14526
15545
  return id;
14527
15546
  }
14528
15547
  function overlap(ids, scope) {
@@ -17017,6 +18036,6 @@ function capStixExternalRefs(container, label) {
17017
18036
  container.externalReferences = [...other, ...capped];
17018
18037
  }
17019
18038
  //#endregion
17020
- export { convertAsffToHdf, convertAwsConfigToHdf, convertBurpsuiteToHdf, convertCheckovToHdf, convertCklToHdf, convertCklbToHdf, convertConveyorToHdf, convertCsafVexToHdf, convertCyclonedxToHdf, convertCyclonedxVexToHdf, convertDbprotectToHdf, convertDefectDojoToHdf, convertDeptrackToHdf, convertFortifyToHdf, convertGitlabToHdf, convertGosecToHdf, convertGrypeToHdf, convertHdfToAsff, convertHdfToCkl, convertHdfToCklb, convertHdfToCsafVex, convertHdfToCsv, convertHdfToCyclonedxVex, convertHdfToEcs, convertHdfToOcsf, convertHdfToOpenVex, convertHdfToOscalPoam, convertHdfToOscalSar, convertHdfToSplunk, convertHdfToXccdf, convertHdfToXml, convertHipcheckToHdf, convertIonchannelToHdf, convertJfrogXrayToHdf, convertJunitToHdf, convertMsftDefenderCloudToHdf, convertMsftDefenderDevopsToHdf, convertMsftDefenderEndpointToHdf, convertMsftSecureScoreToHdf, convertNessusToHdf, convertNetsparkerToHdf, convertNeuvectorToHdf, convertNiktoToHdf, convertOpenVexToHdf, convertOscalCatalogToHdf, convertOscalComponentToHdf, convertOscalPoamToHdf, convertOscalProfileToHdf, convertOscalSapToHdf, convertOscalSarToHdf, convertOscalSspToHdf, convertPrismaToHdf, convertSarifToHdf, convertScoutsuiteToHdf, convertSnykToHdf, convertSonarqubeToHdf, convertSpdxVexToHdf, convertSplunkToHdf, convertTrufflehogToHdf, convertTwistlockToHdf, convertV1ToV2, convertVeracodeToHdf, convertXccdfResultsToHdf, convertZapToHdf, detectOscalDocumentType, detectStixBundle, enrichStix, isHDFV1, parseStixBundle };
18039
+ export { convertAsffToHdf, convertAwsConfigToHdf, convertBurpsuiteToHdf, convertCheckovToHdf, convertCklToHdf, convertCklbToHdf, convertConveyorToHdf, convertCsafVexToHdf, convertCyclonedxToHdf, convertCyclonedxVexToHdf, convertDbprotectToHdf, convertDefectDojoToHdf, convertDeptrackToHdf, convertFortifyToHdf, convertGitlabToHdf, convertGosecToHdf, convertGrypeToHdf, convertHdfToAsff, convertHdfToCkl, convertHdfToCklb, convertHdfToCsafVex, convertHdfToCsv, convertHdfToCyclonedxVex, convertHdfToEcs, convertHdfToOcsf, convertHdfToOpenVex, convertHdfToOscalPoam, convertHdfToOscalSar, convertHdfToSplunk, convertHdfToXccdf, convertHdfToXml, convertHipcheckToHdf, convertIonchannelToHdf, convertJfrogXrayToHdf, convertJunitToHdf, convertKicsToHdf, convertLegacyHdf, convertLegacyHdf as convertV1ToV2, convertMsftDefenderCloudToHdf, convertMsftDefenderDevopsToHdf, convertMsftDefenderEndpointToHdf, convertMsftSecureScoreToHdf, convertNessusToHdf, convertNetsparkerToHdf, convertNeuvectorToHdf, convertNiktoToHdf, convertOpenVexToHdf, convertOscalCatalogToHdf, convertOscalComponentToHdf, convertOscalPoamToHdf, convertOscalProfileToHdf, convertOscalSapToHdf, convertOscalSarToHdf, convertOscalSspToHdf, convertPrismaToHdf, convertSarifToHdf, convertScoutsuiteToHdf, convertSemgrepToHdf, convertSnykToHdf, convertSonarqubeToHdf, convertSpdxVexToHdf, convertSplunkToHdf, convertTrivyToHdf, convertTrufflehogToHdf, convertTwistlockToHdf, convertVeracodeToHdf, convertXccdfResultsToHdf, convertZapToHdf, detectOscalDocumentType, detectStixBundle, enrichStix, isLegacyHdf as isHDFV1, isLegacyHdf, parseStixBundle };
17021
18040
 
17022
18041
  //# sourceMappingURL=index.js.map