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

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
  *
@@ -135,6 +161,20 @@ function extractCWEIDs(text) {
135
161
  ids.sort();
136
162
  return ids;
137
163
  }
164
+ /**
165
+ * Return the first candidate whose content is not empty or whitespace-only,
166
+ * as-is (the emptiness test trims, the returned value does not). Returns '' when
167
+ * every candidate is empty or whitespace. The caller supplies its own documented
168
+ * fallback as the last candidate so the substituted value is meaningful per
169
+ * field — this helper never invents a placeholder like "N/A". Use it where a
170
+ * target schema requires a non-empty (often minLength:1) string and a source
171
+ * value may be absent; it is a selection helper, not a normalizer, and never
172
+ * decides to OMIT a field (that stays the caller's call). Go parity: FirstNonEmpty.
173
+ */
174
+ function firstNonEmpty(...candidates) {
175
+ for (const c of candidates) if (c.trim() !== "") return c;
176
+ return "";
177
+ }
138
178
  /** Default maximum input size for converters (50MB) */
139
179
  const DEFAULT_MAX_INPUT_SIZE = 50 * 1024 * 1024;
140
180
  /**
@@ -165,6 +205,37 @@ function parseHdf(input) {
165
205
  return parseJSON(normalizeHdfTimestamps(input));
166
206
  }
167
207
  /**
208
+ * The shared structural-input guard for HDF Results exporters: the prologue
209
+ * every exporter needs — empty guard, input-size validation, parse, and a
210
+ * top-level `baselines` array check — returning the parsed document and its
211
+ * baselines. Extracted from exportmap.runExport so non-exportmap exporters stop
212
+ * reinventing (and skipping) it. The "missing baselines field" wording matches
213
+ * exportmap and hdf-to-oscal-sar, so there is no consumer-visible message churn.
214
+ */
215
+ function requireHdfResults(input, converterName) {
216
+ return requireHdfStructure(input, converterName, "baselines");
217
+ }
218
+ /**
219
+ * Reject malformed HDF input (empty, oversized, non-JSON, top-level array/null,
220
+ * or a missing/wrong-typed structural key) before a converter zero-fills it into
221
+ * a typed document. A parsed JSON `null` (or array, or primitive) is NOT an HDF
222
+ * document object, so it produces the canonical missing-field error rather than
223
+ * a TypeError — matching Go's nil-map no-op on `null`.
224
+ */
225
+ function requireHdfStructure(input, converterName, field) {
226
+ if (input.length === 0) throw new Error(`${converterName}: empty input`);
227
+ validateInputSize(input, converterName);
228
+ const parsed = parseHdf(input);
229
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) throw new Error(`${converterName}: invalid HDF structure: missing ${field} field`);
230
+ const doc = parsed;
231
+ const items = doc[field];
232
+ if (!Array.isArray(items)) throw new Error(`${converterName}: invalid HDF structure: missing ${field} field`);
233
+ return {
234
+ doc,
235
+ items
236
+ };
237
+ }
238
+ /**
168
239
  * Read a timestamp field off a parsed HDF document.
169
240
  *
170
241
  * The generated schema types these as `Date` (startTime, appliedAt, ...), but
@@ -241,7 +312,7 @@ function ecosystemFromPurlType(type) {
241
312
  * - purl alone
242
313
  * - cpe alone
243
314
  */
244
- function buildAffectedPackage$1(opts) {
315
+ function buildAffectedPackage$2(opts) {
245
316
  const pkg = {};
246
317
  if (opts.purl) pkg.purl = opts.purl;
247
318
  if (opts.cpe) pkg.cpe = opts.cpe;
@@ -444,6 +515,16 @@ function buildNoFindingsRequirement(id, codeDesc, startTime) {
444
515
  tags: {}
445
516
  };
446
517
  }
518
+ /**
519
+ * Shared marker converters emit when a source severity is unrated
520
+ * (isUnratedSeverity), so a defaulted impact stays distinguishable from a
521
+ * genuine rated medium. TS peer of MarkUnratedSeverity in converterutil.go.
522
+ */
523
+ const UNRATED_SEVERITY_TAG = "severity_rating";
524
+ const UNRATED_SEVERITY_VALUE = "unrated";
525
+ function markUnratedSeverity(tags, severity) {
526
+ if (isUnratedSeverity(severity)) tags[UNRATED_SEVERITY_TAG] = UNRATED_SEVERITY_VALUE;
527
+ }
447
528
  //#endregion
448
529
  //#region converters/legacyhdf-to-hdf/typescript/converter.ts
449
530
  /**
@@ -457,7 +538,7 @@ function buildNoFindingsRequirement(id, codeDesc, startTime) {
457
538
  * - Overlay flattening: merge overlay/wrapper baselines so every requirement has results
458
539
  */
459
540
  /** Valid severity values per HDF v2.0 schema (matches the Go converter). */
460
- const VALID_SEVERITIES = /* @__PURE__ */ new Set([
541
+ const VALID_SEVERITIES$1 = /* @__PURE__ */ new Set([
461
542
  "critical",
462
543
  "high",
463
544
  "medium",
@@ -471,7 +552,7 @@ const VALID_SEVERITIES = /* @__PURE__ */ new Set([
471
552
  function tagSeverityToSeverity(tagSeverity) {
472
553
  if (typeof tagSeverity !== "string") return null;
473
554
  const normalized = tagSeverity.toLowerCase().trim();
474
- return VALID_SEVERITIES.has(normalized) ? normalized : null;
555
+ return VALID_SEVERITIES$1.has(normalized) ? normalized : null;
475
556
  }
476
557
  /**
477
558
  * Normalize status values from v1.0 to v2.0 format.
@@ -489,8 +570,9 @@ function normalizeStatus$1(status) {
489
570
  }
490
571
  /**
491
572
  * 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).
573
+ * worst-wins ordering in @mitre/hdf-utilities (impact=0 → notApplicable
574
+ * unless the roll-up is error; error > failed > passed > notApplicable >
575
+ * notReviewed).
494
576
  */
495
577
  function computeEffectiveStatus$1(impact, results) {
496
578
  return computeEffectiveStatus({
@@ -499,6 +581,18 @@ function computeEffectiveStatus$1(impact, results) {
499
581
  });
500
582
  }
501
583
  /**
584
+ * Encodes a control-level execution error the source tool reported but did not
585
+ * record as a result, reusing the earliest recorded result time (matching the
586
+ * Go converter).
587
+ */
588
+ function synthesizedErrorResult(results) {
589
+ return {
590
+ status: "error",
591
+ codeDesc: "Synthesized from the source control-level error status: the tool reported an execution error not reflected in any recorded result",
592
+ 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"
593
+ };
594
+ }
595
+ /**
502
596
  * Convert v1.0 result to v2.0 result.
503
597
  * Transforms snake_case field names to camelCase.
504
598
  */
@@ -592,9 +686,10 @@ function convertControl(v1Control) {
592
686
  if (refs) v2Req.refs = refs;
593
687
  }
594
688
  if (v1Control.source_location !== void 0) v2Req.sourceLocation = v1Control.source_location;
595
- if (v1Control.status !== void 0) v2Req.effectiveStatus = normalizeStatus$1(v1Control.status);
689
+ const controlLevelError = v1Control.status !== void 0 && normalizeStatus$1(v1Control.status) === "error";
596
690
  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 ?? []);
691
+ if (controlLevelError && !(v2Req.results ?? []).some((r) => r.status === "error")) v2Req.results = [...v2Req.results ?? [], synthesizedErrorResult(v2Req.results ?? [])];
692
+ v2Req.effectiveStatus = computeEffectiveStatus$1(v1Control.impact, v2Req.results ?? []);
598
693
  v2Req.severity = tagSeverityToSeverity(v1Control.tags?.severity) ?? impactToSeverity(v1Control.impact);
599
694
  const rawNist = v1Control.tags?.nist;
600
695
  if (Array.isArray(rawNist)) {
@@ -774,7 +869,7 @@ function documentTimestamp(v1Data) {
774
869
  }
775
870
  return latest ? formatTimestamp(latest) : void 0;
776
871
  }
777
- function convertV1ToV2(v1Data, converterVersion = "1.0.0") {
872
+ function convertLegacyHdf(v1Data, converterVersion = "1.0.0") {
778
873
  validateInputSize(JSON.stringify(v1Data), "legacyhdf-to-hdf");
779
874
  const v2 = {
780
875
  baselines: (v1Data.profiles || []).map(convertProfile),
@@ -820,7 +915,7 @@ function convertV1ToV2(v1Data, converterVersion = "1.0.0") {
820
915
  * @param data - Unknown data to validate
821
916
  * @returns true if data looks like HDF v1.0
822
917
  */
823
- function isHDFV1(data) {
918
+ function isLegacyHdf(data) {
824
919
  if (typeof data !== "object" || data === null) return false;
825
920
  const obj = data;
826
921
  return typeof obj.version === "string" && Array.isArray(obj.profiles) && typeof obj.platform === "object" && obj.platform !== null;
@@ -1194,7 +1289,7 @@ function trivyLocation(o) {
1194
1289
  if (el && el !== "0" && el !== sl) loc += `-${el}`;
1195
1290
  return loc;
1196
1291
  }
1197
- function buildRequirement$17(id, group) {
1292
+ function buildRequirement$19(id, group) {
1198
1293
  const primary = group[0];
1199
1294
  const impact = Math.max(...group.map(findingImpact));
1200
1295
  const descriptions = [{
@@ -1241,7 +1336,7 @@ function buildBaseline$1(name, findings, resultsChecksum) {
1241
1336
  groups.set(id, [f]);
1242
1337
  }
1243
1338
  }
1244
- return createMinimalBaseline(name, order.map((id) => buildRequirement$17(id, groups.get(id))), { resultsChecksum });
1339
+ return createMinimalBaseline(name, order.map((id) => buildRequirement$19(id, groups.get(id))), { resultsChecksum });
1245
1340
  }
1246
1341
  /** Converts an ASFF document to HDF Results JSON. */
1247
1342
  async function convertAsffToHdf(input, converterVersion = "1.0.0") {
@@ -1289,7 +1384,7 @@ async function convertAsffToHdf(input, converterVersion = "1.0.0") {
1289
1384
  }
1290
1385
  //#endregion
1291
1386
  //#region converters/sarif-to-hdf/typescript/converter.ts
1292
- const IMPACT_MAPPING$9 = {
1387
+ const SARIF_ALIASES = {
1293
1388
  error: .7,
1294
1389
  warning: .5,
1295
1390
  note: .3
@@ -1361,17 +1456,17 @@ function convertResultGroup(ruleId, rule, sarifResults, timestamp) {
1361
1456
  const firstResult = sarifResults[0];
1362
1457
  const { title, description } = deriveMetadata(firstResult, rule);
1363
1458
  let cweIds = extractCweFromRule(rule);
1364
- if (cweIds.length === 0) cweIds = extractCweIds(resolveMessageText(firstResult.message, rule));
1459
+ if (cweIds.length === 0) cweIds = extractCweIds$1(resolveMessageText(firstResult.message, rule));
1365
1460
  const nistControls = mapCWEToNIST(cweIds, DEFAULT_STATIC_ANALYSIS_NIST_TAGS);
1366
1461
  const cciControls = nistToCci(nistControls);
1367
1462
  const ruleLevel = resolveRuleLevel(rule, sarifResults);
1368
- const impact = IMPACT_MAPPING$9[ruleLevel] || .1;
1463
+ const impact = severityToImpactWithAliases(ruleLevel, SARIF_ALIASES, 0) || .1;
1369
1464
  const sourceLocation = firstResult.locations && firstResult.locations.length > 0 ? extractSourceLocation$1(firstResult.locations[0]) : void 0;
1370
1465
  const results = [];
1371
1466
  for (const sr of sarifResults) results.push(...convertSarifResultToHDFResults(sr));
1372
1467
  const descriptions = buildDescriptions$2(description, rule, firstResult);
1373
1468
  const allSuppressions = sarifResults.flatMap((sr) => sr.suppressions ?? []);
1374
- const options = { tags: buildTags$3(firstResult, rule, ruleLevel, cweIds, nistControls, cciControls, allSuppressions) };
1469
+ const options = { tags: buildTags$4(firstResult, rule, ruleLevel, cweIds, nistControls, cciControls, allSuppressions) };
1375
1470
  if (sourceLocation) options.sourceLocation = sourceLocation;
1376
1471
  const req = createRequirement(ruleId, title, descriptions, impact, results, options);
1377
1472
  const controlType = deriveControlTypeFromTags(nistControls);
@@ -1431,7 +1526,7 @@ function packageFromSarifProperties(props) {
1431
1526
  if (props.purl) ecosystem = ecosystemFromPurlType(parsePurl(props.purl)?.type);
1432
1527
  else if (props.ecosystem) ecosystem = ecosystemFromPurlType(props.ecosystem);
1433
1528
  else if (name && version) ecosystem = Ecosystem.Generic;
1434
- return buildAffectedPackage$1({
1529
+ return buildAffectedPackage$2({
1435
1530
  name,
1436
1531
  version,
1437
1532
  ecosystem,
@@ -1520,7 +1615,7 @@ function extractCweFromRule(rule) {
1520
1615
  }
1521
1616
  return [];
1522
1617
  }
1523
- function extractCweIds(text) {
1618
+ function extractCweIds$1(text) {
1524
1619
  return extractCWEIDs(text).map((id) => `CWE-${id}`);
1525
1620
  }
1526
1621
  function mapKindToStatus(kind) {
@@ -1602,7 +1697,7 @@ function buildDescriptions$2(defaultDesc, rule, result) {
1602
1697
  if (result.fixes && result.fixes.length > 0 && result.fixes[0]?.description?.text) descriptions.push(createDescription("fix", result.fixes[0].description.text));
1603
1698
  return descriptions;
1604
1699
  }
1605
- function buildTags$3(result, rule, resolvedLevel, cweIds, nistControls, cciControls, allSuppressions) {
1700
+ function buildTags$4(result, rule, resolvedLevel, cweIds, nistControls, cciControls, allSuppressions) {
1606
1701
  const tags = {
1607
1702
  severity: resolvedLevel,
1608
1703
  cwe: cweIds,
@@ -2181,7 +2276,7 @@ function ruleResultToRequirement(rr, ruleIndex, groupIndex, scanTime) {
2181
2276
  const req = createRequirement(id, title, descriptions, impact, [result], { tags });
2182
2277
  const code = buildCheckCode(check);
2183
2278
  if (code) req.code = code;
2184
- const refs = buildRefs$9(ruleDef?.reference);
2279
+ const refs = buildRefs$10(ruleDef?.reference);
2185
2280
  if (refs.length > 0) req.refs = refs;
2186
2281
  const hdfSeverity = xccdfSeverityToHdf(severity);
2187
2282
  if (hdfSeverity) req.severity = hdfSeverity;
@@ -2338,7 +2433,7 @@ function checkDescriptionData(check) {
2338
2433
  * href, when present, populates url. References that carry nothing are skipped.
2339
2434
  * Inner keys are inserted alphabetically to match Go's map JSON ordering.
2340
2435
  */
2341
- function buildRefs$9(refs) {
2436
+ function buildRefs$10(refs) {
2342
2437
  if (!refs) return [];
2343
2438
  const out = [];
2344
2439
  for (const r of refs) {
@@ -2863,6 +2958,13 @@ function cklbTitle(cl) {
2863
2958
  //#endregion
2864
2959
  //#region shared/typescript/checklist/to-hdf.ts
2865
2960
  const CONVERTER_VERSION = "1.0.0";
2961
+ const VALID_SEVERITIES = /* @__PURE__ */ new Set([
2962
+ "critical",
2963
+ "high",
2964
+ "medium",
2965
+ "low",
2966
+ "informational"
2967
+ ]);
2866
2968
  /**
2867
2969
  * Map the format-neutral Checklist model to an HDF Results object.
2868
2970
  * controlType is derived per-Vuln from CCI->NIST; verificationMethod and
@@ -2934,7 +3036,7 @@ function vulnToRequirement(v, scanTime) {
2934
3036
  if (v.legacyIDs && v.legacyIDs.length) tags["legacy_ids"] = v.legacyIDs;
2935
3037
  if (v.extra && Object.keys(v.extra).length > 0) tags["cklMetadata"] = { ...v.extra };
2936
3038
  const req = createRequirement(v.vulnNum, v.ruleTitle ?? v.vulnNum, descriptions, impact, [result], { tags });
2937
- if (severity) req.severity = severity;
3039
+ if (VALID_SEVERITIES.has(severity)) req.severity = severity;
2938
3040
  const controlType = deriveControlTypeFromTags(nistTags);
2939
3041
  if (controlType !== void 0) req.controlType = controlType;
2940
3042
  return req;
@@ -2985,6 +3087,36 @@ function setIf$1(m, key, val) {
2985
3087
  if (val) m[key] = val;
2986
3088
  }
2987
3089
  //#endregion
3090
+ //#region shared/typescript/status.ts
3091
+ /**
3092
+ * Bridge from schema-typed requirements onto the canonical effective-status
3093
+ * helper in @mitre/hdf-utilities, so every consumer computes status through
3094
+ * the single shared implementation (twin of shared/go/status.go). The stored
3095
+ * effectiveStatus field is never read — it is an output cache (see
3096
+ * status-determination.md).
3097
+ */
3098
+ /** RFC3339 string from a schema timestamp (quicktype Date or raw string). */
3099
+ function stamp(v) {
3100
+ if (v instanceof Date) return v.toISOString();
3101
+ if (typeof v === "string" && v !== "") return v;
3102
+ }
3103
+ /** Maps a requirement onto the canonical effective-status input shape. */
3104
+ function requirementStatusInput(req) {
3105
+ return {
3106
+ impact: req.impact,
3107
+ resultStatuses: (req.results ?? []).map((r) => String(r.status)),
3108
+ overrides: (req.statusOverrides ?? []).map((o) => ({
3109
+ status: o.status ? String(o.status) : void 0,
3110
+ appliedAt: stamp(o.appliedAt),
3111
+ expiresAt: stamp(o.expiresAt)
3112
+ }))
3113
+ };
3114
+ }
3115
+ /** The requirement's canonical effective status via the shared ladder. */
3116
+ function requirementEffectiveStatus(req) {
3117
+ return computeEffectiveStatus(requirementStatusInput(req));
3118
+ }
3119
+ //#endregion
2988
3120
  //#region shared/typescript/checklist/from-hdf.ts
2989
3121
  /**
2990
3122
  * Map HDF Results back to the format-neutral Checklist model. When the HDF
@@ -3077,7 +3209,7 @@ function requirementToVuln(req) {
3077
3209
  };
3078
3210
  }
3079
3211
  function effectiveOrRawStatus(req) {
3080
- return req.effectiveStatus ?? req.results?.[0]?.status;
3212
+ return requirementEffectiveStatus(req);
3081
3213
  }
3082
3214
  function composeFindingDetails(req) {
3083
3215
  const results = req.results ?? [];
@@ -3252,6 +3384,527 @@ function convertHdfToCklb(input) {
3252
3384
  return serializeCklb(hdfToChecklist(input));
3253
3385
  }
3254
3386
  //#endregion
3387
+ //#region converters/kics-to-hdf/typescript/mapping-data.ts
3388
+ const kicsMappingData = {};
3389
+ //#endregion
3390
+ //#region converters/kics-to-hdf/typescript/converter.ts
3391
+ /**
3392
+ * KICS publishes five severities. SARIF collapses CRITICAL and HIGH into
3393
+ * `error`; converting the native format keeps them apart, which is a large part
3394
+ * of why this converter exists. The shared standard map covers four of them,
3395
+ * with INFO at the canonical 0.0 tier like every other converter: the
3396
+ * effective-status layer maps impact-0 requirements to notApplicable, so
3397
+ * info-tier findings stay visible in the output without entering the
3398
+ * compliance ratio. TRACE is KICS-specific and joins the info tier as an
3399
+ * alias — the Go peer of this table is kicsSeverityAliases.
3400
+ */
3401
+ const KICS_SEVERITY_ALIASES = { trace: 0 };
3402
+ const DEFAULT_IMPACT$1 = .5;
3403
+ /**
3404
+ * Records which tier resolved a requirement's NIST tags, so a reviewed mapping
3405
+ * stays distinguishable from a CWE-derived guess and from a static default.
3406
+ * Follows the precedent set by UNRATED_SEVERITY_TAG in converterutil, which
3407
+ * exists so a defaulted impact stays distinguishable from a genuine rated one.
3408
+ *
3409
+ * The per-query table is authoritative, matching how the Checkov and AWS Config
3410
+ * mappers in the v1 line work: the rule-to-control decision is reviewed before
3411
+ * shipping rather than computed at conversion time, which is also what lets
3412
+ * those tables carry control enhancements. CWE is a fallback because it is a
3413
+ * lossy proxy — only 30 of the 102 CWEs KICS uses resolve against the CWE→NIST
3414
+ * table, 52% of queries by volume.
3415
+ */
3416
+ const NIST_MAPPING_TAG = "nistMapping";
3417
+ const NIST_MAPPING_TABLE = "mapped";
3418
+ const NIST_MAPPING_CWE = "cwe-derived";
3419
+ const NIST_MAPPING_FALLBACK = "static-fallback";
3420
+ function impactFor$1(severity) {
3421
+ return severityToImpactWithAliases(severity, KICS_SEVERITY_ALIASES, DEFAULT_IMPACT$1);
3422
+ }
3423
+ /** KICS emits the bare number, e.g. "778". Normalize before the lookup. */
3424
+ function cweIdentifiers(query) {
3425
+ const raw = query.cwe;
3426
+ if (raw === void 0 || raw === null || String(raw).trim() === "") return [];
3427
+ const id = String(raw).trim().replace(/^CWE-/i, "");
3428
+ return /^\d+$/.test(id) ? [`CWE-${id}`] : [];
3429
+ }
3430
+ function locationFor(file) {
3431
+ const parts = [`File: ${file.file_name || "unknown"}`];
3432
+ if (typeof file.line === "number" && file.line > 0) parts.push(`Line: ${file.line}`);
3433
+ if (file.resource_type) parts.push(`Resource type: ${file.resource_type}`);
3434
+ if (file.resource_name && file.resource_name !== "unknown") parts.push(`Resource: ${file.resource_name}`);
3435
+ if (file.search_key) parts.push(`Key: ${file.search_key}`);
3436
+ if (file.similarity_id) parts.push(`Similarity ID: ${file.similarity_id}`);
3437
+ return parts.join("\n");
3438
+ }
3439
+ /**
3440
+ * The remediation pair. SARIF keeps only `actual_value` inside its message, so
3441
+ * a SARIF-derived control can say what the configuration is but never what it
3442
+ * should be.
3443
+ */
3444
+ function evidenceFor(file) {
3445
+ const parts = [];
3446
+ if (file.expected_value) parts.push(`Expected: ${file.expected_value}`);
3447
+ if (file.actual_value) parts.push(`Actual: ${file.actual_value}`);
3448
+ if (file.issue_type) parts.push(`Issue type: ${file.issue_type}`);
3449
+ if (file.search_value) parts.push(`Search value: ${file.search_value}`);
3450
+ return parts.join("\n");
3451
+ }
3452
+ /**
3453
+ * Resolution order: the reviewed per-query table, then the query's CWE, then the
3454
+ * static-analysis defaults. `table` is a parameter so all three tiers are
3455
+ * testable without shipping unreviewed rows in the table itself.
3456
+ */
3457
+ function resolveControls(query, table = kicsMappingData) {
3458
+ const entry = typeof query.query_id === "string" && Object.prototype.hasOwnProperty.call(table, query.query_id) ? table[query.query_id] : void 0;
3459
+ if (entry !== void 0 && entry.nist.length > 0) return {
3460
+ nist: entry.nist,
3461
+ cci: entry.cci,
3462
+ source: NIST_MAPPING_TABLE
3463
+ };
3464
+ const fromCwe = mapCWEToNIST(cweIdentifiers(query), []);
3465
+ if (fromCwe.length > 0) return {
3466
+ nist: fromCwe,
3467
+ cci: nistToCci(fromCwe),
3468
+ source: NIST_MAPPING_CWE
3469
+ };
3470
+ const fallback = [...DEFAULT_STATIC_ANALYSIS_NIST_TAGS];
3471
+ return {
3472
+ nist: fallback,
3473
+ cci: nistToCci(fallback),
3474
+ source: NIST_MAPPING_FALLBACK
3475
+ };
3476
+ }
3477
+ function tagsFor$1(query) {
3478
+ const cwe = cweIdentifiers(query);
3479
+ const { nist, cci, source } = resolveControls(query);
3480
+ const extras = { [NIST_MAPPING_TAG]: source };
3481
+ if (cwe.length > 0) extras.cwe = cwe;
3482
+ if (query.severity) extras.severity = query.severity;
3483
+ if (query.platform) extras.platform = query.platform;
3484
+ if (query.cloud_provider) extras.cloudProvider = query.cloud_provider;
3485
+ if (query.category) extras.category = query.category;
3486
+ if (query.query_url) extras.queryUrl = query.query_url;
3487
+ if (query.risk_score != null) extras.riskScore = String(query.risk_score);
3488
+ if (query.description_id) extras.descriptionId = query.description_id;
3489
+ if (query.experimental) extras.experimental = true;
3490
+ const issueTypes = [...new Set((query.files ?? []).map((f) => f.issue_type).filter(Boolean))];
3491
+ if (issueTypes.length > 0) extras.issueType = issueTypes;
3492
+ const resourceTypes = [...new Set((query.files ?? []).map((f) => f.resource_type).filter(Boolean))];
3493
+ if (resourceTypes.length > 0) extras.resourceType = resourceTypes;
3494
+ markUnratedSeverity(extras, query.severity);
3495
+ return buildNistCciTags(nist, cci, extras);
3496
+ }
3497
+ /**
3498
+ * KICS reports violations only. Its output carries no record of the queries
3499
+ * that ran without finding anything — `queries[]` contains only those that
3500
+ * fired — so no passing requirement can be derived from it, and a converted
3501
+ * profile is failures-only by construction.
3502
+ *
3503
+ * That makes the compliance percentage misleading on its own: a scan where 72
3504
+ * of 2,034 queries fired renders as 100% failed. This requirement carries the
3505
+ * denominator so the ratio is legible.
3506
+ *
3507
+ * Impact 0 and status notApplicable both keep it out of the score: the
3508
+ * effective-status layer (computeEffectiveStatus) maps impact-0 requirements
3509
+ * to notApplicable before statuses are counted, and notApplicable is the one
3510
+ * status the compliance rollup excludes from both numerator and denominator.
3511
+ * Emitting notApplicable as the raw status too keeps raw-status consumers
3512
+ * agreeing with the effective view — Passed would export to CKL as
3513
+ * NotAFinding, a compliant-looking control that was never checked, and count
3514
+ * as a free pass in raw status rollups.
3515
+ */
3516
+ function buildCoverageRequirement$1(report, startTime) {
3517
+ const executed = report.queries_total ?? 0;
3518
+ const fired = (report.queries ?? []).filter((q) => (q.files ?? []).length > 0).length;
3519
+ 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.`;
3520
+ return {
3521
+ id: "kics-scan-coverage",
3522
+ title: "KICS scan coverage",
3523
+ impact: 0,
3524
+ descriptions: [{
3525
+ label: "default",
3526
+ data: summary
3527
+ }],
3528
+ results: [{
3529
+ status: ResultStatus.NotApplicable,
3530
+ codeDesc: summary,
3531
+ startTime
3532
+ }],
3533
+ tags: {
3534
+ queriesExecuted: executed,
3535
+ queriesWithFindings: fired,
3536
+ filesScanned: report.files_scanned ?? 0,
3537
+ filesParsed: report.files_parsed ?? 0,
3538
+ filesFailedToScan: report.files_failed_to_scan ?? 0,
3539
+ queriesFailedToExecute: report.queries_failed_to_execute ?? 0
3540
+ }
3541
+ };
3542
+ }
3543
+ function buildRequirement$18(query, startTime) {
3544
+ const tags = tagsFor$1(query);
3545
+ const descriptions = [{
3546
+ label: "default",
3547
+ data: query.description ?? ""
3548
+ }];
3549
+ const results = (query.files ?? []).map((file) => ({
3550
+ status: ResultStatus.Failed,
3551
+ codeDesc: locationFor(file),
3552
+ message: evidenceFor(file),
3553
+ startTime
3554
+ }));
3555
+ 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 });
3556
+ requirement.verificationMethod = VerificationMethodEnum.Automated;
3557
+ const controlType = deriveControlTypeFromTags(tags.nist);
3558
+ if (controlType !== void 0) requirement.controlType = controlType;
3559
+ return requirement;
3560
+ }
3561
+ /**
3562
+ * Converts native `kics scan --report-formats json` output to HDF.
3563
+ *
3564
+ * KICS also emits SARIF, which carries its CWE taxonomy properly. What SARIF
3565
+ * drops is the remediation pair (`expected_value`), the add-a-block versus
3566
+ * fix-a-value distinction (`issue_type`), the stable finding fingerprint
3567
+ * (`similarity_id`), and one level of severity granularity.
3568
+ *
3569
+ * @param input - KICS JSON report string
3570
+ * @returns HDF JSON string
3571
+ */
3572
+ async function convertKicsToHdf(input, converterVersion = "1.0.0") {
3573
+ validateInputSize(input, "kics");
3574
+ if (!input || input.trim().length === 0) throw new Error("kics: empty input");
3575
+ const report = parseJSON(input);
3576
+ if (!Array.isArray(report.queries) || typeof report.kics_version !== "string") throw new Error("kics: input does not look like a KICS report");
3577
+ const startTime = /* @__PURE__ */ new Date();
3578
+ const requirements = report.queries.filter((q) => Array.isArray(q.files) && q.files.length > 0).map((q) => buildRequirement$18(q, startTime));
3579
+ if (requirements.length === 0) requirements.push(buildNoFindingsRequirement("kics-no-findings", `KICS scanned ${report.files_scanned ?? 0} file(s) and reported no findings.`, startTime));
3580
+ requirements.push(buildCoverageRequirement$1(report, startTime));
3581
+ const baseline = createMinimalBaseline("KICS Scan", requirements, {
3582
+ title: "KICS infrastructure-as-code scan",
3583
+ resultsChecksum: await inputChecksum(input)
3584
+ });
3585
+ return buildHdfResults({
3586
+ generatorName: "kics-to-hdf",
3587
+ converterVersion,
3588
+ toolName: "KICS",
3589
+ toolVersion: report.kics_version,
3590
+ toolFormat: "json",
3591
+ baselines: [baseline],
3592
+ timestamp: startTime
3593
+ });
3594
+ }
3595
+ //#endregion
3596
+ //#region converters/semgrep-to-hdf/typescript/converter.ts
3597
+ /**
3598
+ * Semgrep's native three-level scale mapped onto the canonical impact tiers,
3599
+ * mirroring the sarif converter's error/warning/note aliases. INFO is
3600
+ * deliberately 0.3 (an actionable low, semgrep's analogue of SARIF 'note'),
3601
+ * not the canonical 0.0 info tier. Supply-chain severities
3602
+ * (critical/high/medium/low) resolve through the shared canonical map.
3603
+ */
3604
+ const SEMGREP_SEVERITY_ALIASES = {
3605
+ error: .7,
3606
+ warning: .5,
3607
+ info: .3
3608
+ };
3609
+ /**
3610
+ * An unrecognized or absent severity is treated as moderate; the absent case
3611
+ * additionally carries the shared unrated marker so consumers can tell a
3612
+ * defaulted 0.5 from a genuine medium.
3613
+ */
3614
+ const DEFAULT_IMPACT = .5;
3615
+ /** Fields Semgrep redacts in unauthenticated (OSS) scans. */
3616
+ const REDACTED_PLACEHOLDER = "requires login";
3617
+ const SCAN_ERRORS_ID = "semgrep-scan-errors";
3618
+ const COVERAGE_ID = "semgrep-scan-coverage";
3619
+ function isPresent(value) {
3620
+ return typeof value === "string" && value.length > 0 && value !== REDACTED_PLACEHOLDER;
3621
+ }
3622
+ /**
3623
+ * Array entries are filtered per-entry (non-string members dropped); a bare
3624
+ * empty string is treated as absent, matching the Go StringOrSlice decoder.
3625
+ */
3626
+ function normalizeToArray(value) {
3627
+ if (Array.isArray(value)) return value.filter((entry) => typeof entry === "string");
3628
+ return typeof value === "string" && value !== "" ? [value] : [];
3629
+ }
3630
+ function lineOf(position) {
3631
+ const line = position?.line;
3632
+ return typeof line === "number" && line > 0 ? line : void 0;
3633
+ }
3634
+ /**
3635
+ * Semgrep emits CWEs in prose form -- 'CWE-89: Improper Neutralization of ...'
3636
+ * -- while the mapping keys on the bare number.
3637
+ */
3638
+ function extractCweIds(metadata) {
3639
+ 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));
3640
+ }
3641
+ /**
3642
+ * The parsed CWE ids in the CWE-N catalog form the schema's cwe[] field asks
3643
+ * for, deduplicated in source order.
3644
+ */
3645
+ function cweCatalogFor(metadata) {
3646
+ return [...new Set(extractCweIds(metadata).map((id) => `CWE-${id}`))];
3647
+ }
3648
+ function nistControlsFor(metadata) {
3649
+ const controls = extractCweIds(metadata).map((cweId) => getCweNistControl(cweId)).filter(isPresent);
3650
+ const deduped = [...new Set(controls)];
3651
+ return deduped.length > 0 ? deduped : [...DEFAULT_STATIC_ANALYSIS_NIST_TAGS$1];
3652
+ }
3653
+ function impactFor(result) {
3654
+ const severity = result.extra?.severity;
3655
+ if (!isPresent(severity)) return DEFAULT_IMPACT;
3656
+ return severityToImpactWithAliases(severity, SEMGREP_SEVERITY_ALIASES, DEFAULT_IMPACT);
3657
+ }
3658
+ /**
3659
+ * Semgrep rule ids are dotted paths whose final segment is the rule name. The
3660
+ * JSON output carries no human-readable rule title anywhere -- unlike the SARIF
3661
+ * output, whose rule objects have `name` and `shortDescription` -- so one is
3662
+ * derived. Separator runs collapse and only the first character of each word
3663
+ * is uppercased, matching the Go twin exactly.
3664
+ */
3665
+ function titleFor(checkId) {
3666
+ const segments = checkId.split(".").filter((segment) => segment.length > 0);
3667
+ return (segments[segments.length - 1] ?? checkId).split(/[-_]+/).filter((word) => word.length > 0).map((word) => word[0].toUpperCase() + word.slice(1)).join(" ");
3668
+ }
3669
+ function codeDescFor(result) {
3670
+ const path = isPresent(result.path) ? result.path : "unknown";
3671
+ const startLine = lineOf(result.start);
3672
+ const endLine = lineOf(result.end);
3673
+ if (startLine === void 0) return `Path: ${path}`;
3674
+ return `Path: ${path}, ${endLine === void 0 || endLine === startLine ? `line ${startLine}` : `lines ${startLine}-${endLine}`}`;
3675
+ }
3676
+ /**
3677
+ * Points at the first occurrence of the rule; per-occurrence locations remain
3678
+ * on each result's codeDesc.
3679
+ */
3680
+ function sourceLocationFor(result) {
3681
+ if (!isPresent(result.path)) return;
3682
+ const location = { ref: result.path };
3683
+ const line = lineOf(result.start);
3684
+ if (line !== void 0) location.line = line;
3685
+ return location;
3686
+ }
3687
+ function messageFor(result) {
3688
+ const parts = [];
3689
+ if (isPresent(result.extra?.lines)) parts.push(`Matched code:\n${result.extra.lines}`);
3690
+ if (isPresent(result.extra?.fix)) parts.push(`Suggested fix -- replace the matched code with:\n${result.extra.fix}`);
3691
+ return parts.join("\n\n");
3692
+ }
3693
+ /** Documentation and cross-framework links, deduplicated. */
3694
+ function referencesFor(metadata) {
3695
+ const urls = [
3696
+ ...normalizeToArray(metadata.references),
3697
+ metadata.source,
3698
+ metadata.shortlink,
3699
+ metadata["source-rule-url"],
3700
+ typeof metadata.asvs?.control_url === "string" ? metadata.asvs.control_url : void 0
3701
+ ].filter(isPresent);
3702
+ return [...new Set(urls)];
3703
+ }
3704
+ /** The deduplicated reference URLs in refs[], their structured HDF home. */
3705
+ function refsFor(metadata) {
3706
+ return referencesFor(metadata).map((url) => ({ url }));
3707
+ }
3708
+ function asvsOf(metadata) {
3709
+ const asvs = metadata.asvs;
3710
+ if (asvs && typeof asvs === "object" && !Array.isArray(asvs) && Object.keys(asvs).length > 0) return asvs;
3711
+ }
3712
+ function tagsFor(metadata, checkId, severity) {
3713
+ const nist = [...new Set(nistControlsFor(metadata))];
3714
+ const cci = nistToCci(nist);
3715
+ const extras = { checkId };
3716
+ const cwe = normalizeToArray(metadata.cwe);
3717
+ if (cwe.length > 0) extras.cwe = cwe;
3718
+ const owasp = normalizeToArray(metadata.owasp);
3719
+ if (owasp.length > 0) extras.owasp = owasp;
3720
+ const subcategory = normalizeToArray(metadata.subcategory);
3721
+ if (subcategory.length > 0) extras.subcategory = subcategory;
3722
+ const technology = normalizeToArray(metadata.technology);
3723
+ if (technology.length > 0) extras.technology = technology;
3724
+ const vulnerabilityClass = normalizeToArray(metadata.vulnerability_class);
3725
+ if (vulnerabilityClass.length > 0) extras.vulnerabilityClass = vulnerabilityClass;
3726
+ if (isPresent(severity)) extras.severity = severity;
3727
+ if (isPresent(metadata.confidence)) extras.confidence = metadata.confidence;
3728
+ if (isPresent(metadata.likelihood)) extras.likelihood = metadata.likelihood;
3729
+ if (isPresent(metadata.impact)) extras.semgrepImpact = metadata.impact;
3730
+ if (isPresent(metadata.category)) extras.category = metadata.category;
3731
+ if (isPresent(metadata["bandit-code"])) extras.banditCode = metadata["bandit-code"];
3732
+ const asvs = asvsOf(metadata);
3733
+ if (asvs) extras.asvs = asvs;
3734
+ markUnratedSeverity(extras, isPresent(severity) ? severity : "");
3735
+ return buildNistCciTags(nist, cci, extras);
3736
+ }
3737
+ function codePositionFor(position) {
3738
+ const line = lineOf(position);
3739
+ if (line === void 0) return;
3740
+ const out = { line };
3741
+ const col = position?.col;
3742
+ if (typeof col === "number" && col > 0) out.col = col;
3743
+ return out;
3744
+ }
3745
+ /**
3746
+ * Serializes the curated match envelope into requirement.code for the CODE
3747
+ * tab: the rule source itself is not present in semgrep's JSON output, and the
3748
+ * raw finding bytes are not byte-stable across the Go/TS pair (escape forms
3749
+ * differ), so both languages serialize this envelope field-for-field in the
3750
+ * same order. Rule metadata is deliberately excluded — it is already carried
3751
+ * structurally in tags, cwe[], and refs[] — and redacted fields are filtered
3752
+ * per the converter's redaction policy.
3753
+ */
3754
+ function codeFor(result) {
3755
+ const envelope = { check_id: result.check_id };
3756
+ if (isPresent(result.path)) envelope.path = result.path;
3757
+ const start = codePositionFor(result.start);
3758
+ if (start) envelope.start = start;
3759
+ const end = codePositionFor(result.end);
3760
+ if (end) envelope.end = end;
3761
+ const extra = {};
3762
+ if (isPresent(result.extra?.message)) extra.message = result.extra.message;
3763
+ if (isPresent(result.extra?.severity)) extra.severity = result.extra.severity;
3764
+ if (isPresent(result.extra?.lines)) extra.lines = result.extra.lines;
3765
+ if (isPresent(result.extra?.fix)) extra.fix = result.extra.fix;
3766
+ if (Object.keys(extra).length > 0) envelope.extra = extra;
3767
+ return JSON.stringify(envelope, null, 2);
3768
+ }
3769
+ /** Every occurrence of one rule becomes a result under a single requirement. */
3770
+ function buildRequirement$17(checkId, results, startTime) {
3771
+ const representative = results[0];
3772
+ const metadata = representative.extra?.metadata ?? {};
3773
+ const message = representative.extra?.message;
3774
+ const descriptions = [{
3775
+ label: "default",
3776
+ data: typeof message === "string" ? message : ""
3777
+ }];
3778
+ const requirementResults = results.map((result) => ({
3779
+ status: ResultStatus.Failed,
3780
+ codeDesc: codeDescFor(result),
3781
+ message: messageFor(result),
3782
+ startTime
3783
+ }));
3784
+ const tags = tagsFor(metadata, checkId, representative.extra?.severity);
3785
+ const cwe = cweCatalogFor(metadata);
3786
+ const refs = refsFor(metadata);
3787
+ const sourceLocation = sourceLocationFor(representative);
3788
+ const requirement = createRequirement(checkId, titleFor(checkId), descriptions, impactFor(representative), requirementResults, {
3789
+ tags,
3790
+ code: codeFor(representative),
3791
+ ...cwe.length > 0 ? { cwe } : {},
3792
+ ...refs.length > 0 ? { refs } : {},
3793
+ ...sourceLocation ? { sourceLocation } : {}
3794
+ });
3795
+ requirement.verificationMethod = VerificationMethodEnum.Automated;
3796
+ const controlType = deriveControlTypeFromTags(tags.nist);
3797
+ if (controlType !== void 0) requirement.controlType = controlType;
3798
+ return requirement;
3799
+ }
3800
+ /**
3801
+ * Scan failures become their own requirement so a file that failed to parse is
3802
+ * visible rather than buried: absence of findings in it is not evidence of
3803
+ * compliance. errors[].level drives the status: level 'error' entries are scan
3804
+ * failures (status error), while level 'warn' entries are advisory (e.g.
3805
+ * PartialParsing — the file was partially analyzed), a genuine non-evaluation
3806
+ * of those paths that must not dominate worst-wins rollups, so they map to
3807
+ * notReviewed.
3808
+ */
3809
+ function buildErrorsRequirement(errors, startTime) {
3810
+ const results = errors.map((error) => {
3811
+ const kind = Array.isArray(error.type) ? String(error.type[0]) : String(error.type ?? "Unknown");
3812
+ const level = isPresent(error.level) ? error.level : "";
3813
+ const message = typeof error.message === "string" ? error.message : "";
3814
+ return {
3815
+ status: level.toLowerCase() === "warn" ? ResultStatus.NotReviewed : ResultStatus.Error,
3816
+ codeDesc: `Path: ${isPresent(error.path) ? error.path : "unknown"}`,
3817
+ message: level === "" ? `${kind}: ${message}` : `${kind} [${level}]: ${message}`,
3818
+ startTime
3819
+ };
3820
+ });
3821
+ const tags = buildNistCciTags([...DEFAULT_STATIC_ANALYSIS_NIST_TAGS$1], []);
3822
+ markUnratedSeverity(tags, "");
3823
+ const requirement = createRequirement(SCAN_ERRORS_ID, "Semgrep scan errors", [{
3824
+ label: "default",
3825
+ data: "Errors reported by Semgrep while scanning. A file that failed to parse was not fully analyzed."
3826
+ }], DEFAULT_IMPACT, results, { tags });
3827
+ requirement.verificationMethod = VerificationMethodEnum.Automated;
3828
+ return requirement;
3829
+ }
3830
+ /**
3831
+ * Records the scan's denominator. Semgrep reports violations only, so a
3832
+ * converted profile is failures-only by construction and its compliance ratio
3833
+ * is not a pass rate; this record carries the scanned count and the caveat.
3834
+ * Impact 0 makes the effective-status layer derive notApplicable — the only
3835
+ * status the compliance rollup excludes — and the raw status matches so
3836
+ * raw-status consumers (and CKL export, where Passed would render NotAFinding)
3837
+ * agree with the effective view. Mirrors kics-scan-coverage.
3838
+ */
3839
+ function scannedCount(report) {
3840
+ return Array.isArray(report.paths?.scanned) ? report.paths.scanned.length : 0;
3841
+ }
3842
+ function buildCoverageRequirement(report, ruleCount, startTime) {
3843
+ const filesScanned = scannedCount(report);
3844
+ const errorCount = report.errors?.length ?? 0;
3845
+ 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.`;
3846
+ return createRequirement(COVERAGE_ID, "Semgrep scan coverage", [{
3847
+ label: "default",
3848
+ data: summary
3849
+ }], 0, [{
3850
+ status: ResultStatus.NotApplicable,
3851
+ codeDesc: summary,
3852
+ startTime
3853
+ }], { tags: {
3854
+ filesScanned,
3855
+ rulesWithFindings: ruleCount,
3856
+ scanErrors: errorCount
3857
+ } });
3858
+ }
3859
+ /**
3860
+ * Converts native `semgrep scan --json` output to HDF Results. SARIF input is
3861
+ * detected and delegated to the SARIF converter.
3862
+ *
3863
+ * Native JSON is converted here because SARIF keeps the rule metadata only as
3864
+ * untyped prose tags on the rule object and drops impact, likelihood, the ASVS
3865
+ * control mapping, reference URLs and vulnerability_class outright.
3866
+ *
3867
+ * @param input - Semgrep JSON report string
3868
+ * @returns HDF JSON string
3869
+ */
3870
+ async function convertSemgrepToHdf(input, converterVersion = "1.0.0") {
3871
+ validateInputSize(input, "semgrep");
3872
+ if (!input || input.trim().length === 0) throw new Error("semgrep: empty input");
3873
+ registerAllFingerprints();
3874
+ const detected = detectConverter(input);
3875
+ if (detected && detected.fingerprint.id === "sarif-to-hdf") return convertSarifToHdf(input, converterVersion);
3876
+ const report = parseJSON(input);
3877
+ 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");
3878
+ const startTime = /* @__PURE__ */ new Date();
3879
+ const groups = /* @__PURE__ */ new Map();
3880
+ for (const result of report.results) {
3881
+ if (typeof result?.check_id !== "string" || result.check_id === "") continue;
3882
+ const existing = groups.get(result.check_id);
3883
+ if (existing) existing.push(result);
3884
+ else groups.set(result.check_id, [result]);
3885
+ }
3886
+ const requirements = [];
3887
+ for (const [checkId, results] of groups) requirements.push(buildRequirement$17(checkId, results, startTime));
3888
+ if (groups.size === 0) {
3889
+ const scanned = scannedCount(report);
3890
+ requirements.push(buildNoFindingsRequirement("semgrep-no-findings", `Semgrep scanned ${scanned} file(s) and reported no findings.`, startTime));
3891
+ }
3892
+ if (report.errors.length > 0) requirements.push(buildErrorsRequirement(report.errors, startTime));
3893
+ requirements.push(buildCoverageRequirement(report, groups.size, startTime));
3894
+ const baseline = createMinimalBaseline("Semgrep Scan", requirements, {
3895
+ title: "Semgrep static analysis scan",
3896
+ resultsChecksum: await inputChecksum(input)
3897
+ });
3898
+ return buildHdfResults({
3899
+ generatorName: "semgrep-to-hdf",
3900
+ converterVersion,
3901
+ toolName: "Semgrep",
3902
+ toolVersion: typeof report.version === "string" ? report.version : void 0,
3903
+ baselines: [baseline],
3904
+ timestamp: startTime
3905
+ });
3906
+ }
3907
+ //#endregion
3255
3908
  //#region converters/snyk-to-hdf/typescript/converter.ts
3256
3909
  /**
3257
3910
  * Re-serialize the parsed vulnerability into indented JSON for requirement.code
@@ -3418,6 +4071,7 @@ function buildRequirement$16(vulnID, vulns, scanTime, packageManager) {
3418
4071
  };
3419
4072
  if (rep.identifiers.CVE && rep.identifiers.CVE.length > 0) tags["cve"] = rep.identifiers.CVE;
3420
4073
  if (rep.identifiers.GHSA && rep.identifiers.GHSA.length > 0) tags["ghsaid"] = rep.identifiers.GHSA;
4074
+ markUnratedSeverity(tags, rep.severity);
3421
4075
  const descriptions = [{
3422
4076
  label: "default",
3423
4077
  data: rep.description
@@ -3435,7 +4089,7 @@ function buildRequirement$16(vulnID, vulns, scanTime, packageManager) {
3435
4089
  delete result.message;
3436
4090
  return result;
3437
4091
  });
3438
- const req = createRequirement(vulnID, rep.title, descriptions, severityToImpact$1(rep.severity), results, { tags });
4092
+ const req = createRequirement(vulnID, rep.title, descriptions, severityToImpact$1(rep.severity ?? ""), results, { tags });
3439
4093
  const controlType = deriveControlTypeFromTags(nist);
3440
4094
  if (controlType !== void 0) req.controlType = controlType;
3441
4095
  req.verificationMethod = VerificationMethodEnum.Automated;
@@ -3450,7 +4104,7 @@ function buildRequirement$16(vulnID, vulns, scanTime, packageManager) {
3450
4104
  const version = rep.version;
3451
4105
  if (name && version) {
3452
4106
  const ecosystem = ecosystemFromSnykPackageManager(packageManager);
3453
- const pkg = buildAffectedPackage$1({
4107
+ const pkg = buildAffectedPackage$2({
3454
4108
  name,
3455
4109
  version,
3456
4110
  ecosystem,
@@ -3531,17 +4185,12 @@ async function convertSnykToHdf(input, converterVersion = "1.0.0") {
3531
4185
  }
3532
4186
  //#endregion
3533
4187
  //#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
- ]);
4188
+ const GRYPE_ALIASES = {
4189
+ critical: .9,
4190
+ negligible: 0
4191
+ };
3542
4192
  function getImpact$9(severity) {
3543
- if (!severity) return .5;
3544
- return IMPACT_MAPPING$8.get(severity.toLowerCase()) ?? .5;
4193
+ return severityToImpactWithAliases(severity, GRYPE_ALIASES, .5);
3545
4194
  }
3546
4195
  function isNegligibleOrUnknown(severity) {
3547
4196
  if (!severity) return true;
@@ -3593,7 +4242,7 @@ function buildCodeDesc$9(match) {
3593
4242
  }
3594
4243
  return parts.join(" | ");
3595
4244
  }
3596
- function buildCvssEntries$6(vuln) {
4245
+ function buildCvssEntries$7(vuln) {
3597
4246
  if (!vuln.cvss || vuln.cvss.length === 0) return;
3598
4247
  const entries = [];
3599
4248
  for (const c of vuln.cvss) {
@@ -3636,10 +4285,10 @@ function buildAffectedPackages(match) {
3636
4285
  if (fix && fix.state === "fixed" && fix.versions && fix.versions.length > 0 && fix.versions[0]) pkg.fixedInVersion = fix.versions[0];
3637
4286
  return [pkg];
3638
4287
  }
3639
- const CWE_ID_PATTERN$1 = /^CWE-[1-9]\d*$/;
4288
+ const CWE_ID_PATTERN$2 = /^CWE-[1-9]\d*$/;
3640
4289
  function extractCwe(raw) {
3641
4290
  if (!raw || raw.length === 0) return void 0;
3642
- const out = raw.filter((c) => CWE_ID_PATTERN$1.test(c));
4291
+ const out = raw.filter((c) => CWE_ID_PATTERN$2.test(c));
3643
4292
  return out.length === 0 ? void 0 : out;
3644
4293
  }
3645
4294
  function buildEpss$3(entries) {
@@ -3671,7 +4320,6 @@ function convertMatchToRequirement(match, isIgnored, targetName, startTime) {
3671
4320
  const refs = getReferences(vuln, match.relatedVulnerabilities);
3672
4321
  let status;
3673
4322
  if (isIgnored) status = ResultStatus.NotReviewed;
3674
- else if (isNegligibleOrUnknown(severity)) status = ResultStatus.NotApplicable;
3675
4323
  else status = ResultStatus.Failed;
3676
4324
  const messageParts = [];
3677
4325
  if (isIgnored) messageParts.push("This vulnerability was ignored by configured rules.");
@@ -3686,6 +4334,7 @@ function convertMatchToRequirement(match, isIgnored, targetName, startTime) {
3686
4334
  startTime
3687
4335
  };
3688
4336
  const tags = buildNistCciTags(DEFAULT_STATIC_ANALYSIS_NIST_TAGS, nistToCci(DEFAULT_STATIC_ANALYSIS_NIST_TAGS));
4337
+ markUnratedSeverity(tags, severity);
3689
4338
  const requirement = {
3690
4339
  id: isIgnored ? `Grype-Ignored-Match/${cveId}` : `Grype/${cveId}`,
3691
4340
  title: `Grype found a vulnerability to ${cveId} in ${targetName}`,
@@ -3712,7 +4361,7 @@ function convertMatchToRequirement(match, isIgnored, targetName, startTime) {
3712
4361
  };
3713
4362
  const controlType = deriveControlTypeFromTags(DEFAULT_STATIC_ANALYSIS_NIST_TAGS);
3714
4363
  if (controlType !== void 0) requirement.controlType = controlType;
3715
- const cvss = buildCvssEntries$6(vuln);
4364
+ const cvss = buildCvssEntries$7(vuln);
3716
4365
  if (cvss) requirement.cvss = cvss;
3717
4366
  requirement.affectedPackages = buildAffectedPackages(match);
3718
4367
  const cwe = extractCwe(vuln.cwe);
@@ -3723,7 +4372,7 @@ function convertMatchToRequirement(match, isIgnored, targetName, startTime) {
3723
4372
  if (kev) requirement.kev = kev;
3724
4373
  return requirement;
3725
4374
  }
3726
- function buildComponent$1(report, targetName) {
4375
+ function buildComponent$2(report, targetName) {
3727
4376
  const t = report.source?.target;
3728
4377
  const firstRepoDigest = t?.repoDigests?.find((d) => d);
3729
4378
  const firstTag = t?.tags?.find((tag) => tag);
@@ -3741,10 +4390,8 @@ function buildComponent$1(report, targetName) {
3741
4390
  if (image) component.image = image;
3742
4391
  if (report.distro?.name) component.osName = report.distro.name;
3743
4392
  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
- }];
4393
+ const integrity = digestToChecksums(t.manifestDigest);
4394
+ if (integrity) component.integrity = integrity;
3748
4395
  if (t.architecture) component.labels = { architecture: t.architecture };
3749
4396
  return component;
3750
4397
  }
@@ -3776,7 +4423,7 @@ async function convertGrypeToHdf(input, converterVersion = "1.0.0") {
3776
4423
  toolName: "Grype",
3777
4424
  toolVersion: grypeData.descriptor?.version,
3778
4425
  baselines: [baseline],
3779
- components: [buildComponent$1(grypeData, targetName)],
4426
+ components: [buildComponent$2(grypeData, targetName)],
3780
4427
  timestamp: scanTime ?? void 0
3781
4428
  });
3782
4429
  }
@@ -4024,7 +4671,7 @@ async function convertDefectDojoToHdf(input, converterVersion = "0.1.0") {
4024
4671
  //#region converters/nessus-to-hdf/typescript/converter.ts
4025
4672
  const CVE_SOURCE_RE = /^CVE-\d{4}-\d{4,}$/;
4026
4673
  const CWE_PATTERN = /CWE[- ]?(\d+)/gi;
4027
- const IMPACT_MAPPING$7 = {
4674
+ const NESSUS_ALIASES = {
4028
4675
  "4": .9,
4029
4676
  "3": .7,
4030
4677
  "i": .7,
@@ -4038,7 +4685,7 @@ const IMPACT_MAPPING$7 = {
4038
4685
  * Strip HTML tags from a string
4039
4686
  */
4040
4687
  function parseHtml(html) {
4041
- return html.replace(/<[^>]*>/g, "").trim();
4688
+ return replaceHtmlTags(html, "").trim();
4042
4689
  }
4043
4690
  const NAMED_ENTITIES$3 = {
4044
4691
  lt: "<",
@@ -4168,8 +4815,8 @@ function convertReportItemToRequirement(item, host) {
4168
4815
  const title = isCompliance ? item["compliance-check-name"] || item["pluginName"] : item["pluginName"];
4169
4816
  const descriptions = buildDescriptions(item, isCompliance);
4170
4817
  const impact = calculateImpact(item, isCompliance);
4171
- const tags = buildTags$2(item, isCompliance);
4172
- const refs = buildRefs$8(item);
4818
+ const tags = buildTags$3(item, isCompliance);
4819
+ const refs = buildRefs$9(item);
4173
4820
  const results = [buildResult$1(item, host, isCompliance)];
4174
4821
  const code = JSON.stringify(item, null, 2);
4175
4822
  const nistTags = tags["nist"];
@@ -4188,7 +4835,7 @@ function convertReportItemToRequirement(item, host) {
4188
4835
  if (controlType !== void 0) req.controlType = controlType;
4189
4836
  if (verificationMethod !== void 0) req.verificationMethod = verificationMethod;
4190
4837
  if (!isCompliance) {
4191
- const cvssEntries = buildCvssEntries$5(item);
4838
+ const cvssEntries = buildCvssEntries$6(item);
4192
4839
  if (cvssEntries.length > 0) req.cvss = cvssEntries;
4193
4840
  const cweIDs = buildCweIDs(item);
4194
4841
  if (cweIDs.length > 0) req.cwe = cweIDs;
@@ -4202,7 +4849,7 @@ function convertReportItemToRequirement(item, host) {
4202
4849
  * the schema models cvss as a multi-entry array (Nessus emits one entry per
4203
4850
  * item; multi-vendor convergence may yield more).
4204
4851
  */
4205
- function buildCvssEntries$5(item) {
4852
+ function buildCvssEntries$6(item) {
4206
4853
  const source = (item.cvss_score_source || "").trim();
4207
4854
  if (!source || !CVE_SOURCE_RE.test(source)) return [];
4208
4855
  const hasV3 = !!(item.cvss3_vector || item.cvss3_base_score);
@@ -4342,12 +4989,12 @@ function buildDescriptions(item, isCompliance) {
4342
4989
  }
4343
4990
  function calculateImpact(item, isCompliance) {
4344
4991
  if (isCompliance && item["compliance-reference"]) {
4345
- const catKey = parseComplianceRef(item["compliance-reference"], "CAT")[0]?.toLowerCase();
4346
- return catKey ? IMPACT_MAPPING$7[catKey] ?? .5 : .5;
4992
+ const cat = parseComplianceRef(item["compliance-reference"], "CAT")[0];
4993
+ return cat ? severityToImpactWithAliases(cat, NESSUS_ALIASES, .5) : .5;
4347
4994
  }
4348
- return IMPACT_MAPPING$7[item["severity"]] ?? 0;
4995
+ return severityToImpactWithAliases(item["severity"], NESSUS_ALIASES, 0);
4349
4996
  }
4350
- function buildTags$2(item, isCompliance) {
4997
+ function buildTags$3(item, isCompliance) {
4351
4998
  const tags = { rid: isCompliance ? parseComplianceRef(item["compliance-reference"], "Rule-ID").join(",") : item["pluginID"] };
4352
4999
  if (isCompliance && item["compliance-reference"]) {
4353
5000
  const cciTags = parseComplianceRef(item["compliance-reference"], "CCI");
@@ -4370,7 +5017,7 @@ function buildTags$2(item, isCompliance) {
4370
5017
  if (item.cvss_base_score) tags.cvss_base_score = item.cvss_base_score;
4371
5018
  return tags;
4372
5019
  }
4373
- function buildRefs$8(item) {
5020
+ function buildRefs$9(item) {
4374
5021
  const refs = [];
4375
5022
  if (item.see_also) for (const url of item.see_also.split(/\s+/).filter(Boolean)) refs.push({ url });
4376
5023
  return refs.length > 0 ? refs : void 0;
@@ -4436,31 +5083,29 @@ function isIPAddress(s) {
4436
5083
  //#endregion
4437
5084
  //#region converters/sonarqube-to-hdf/typescript/converter.ts
4438
5085
  /**
4439
- * Deprecated legacy severity to impact mapping.
5086
+ * Deprecated legacy severity aliases to impact scores.
4440
5087
  * 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
5088
+ * Note: SonarQube "CRITICAL" maps to 0.7, not the standard 1.0; INFO resolves
5089
+ * to 0.0 via the shared standard map.
5090
+ */
5091
+ const SONARQUBE_ALIASES = {
5092
+ blocker: 1,
5093
+ critical: .7,
5094
+ major: .5,
5095
+ minor: .3
4448
5096
  };
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. */
5097
+ /**
5098
+ * The one MQR severity the standard map lacks; HIGH/MEDIUM/LOW/INFO already
5099
+ * resolve to 0.7/0.5/0.3/0.0 via severityToImpactWithAliases.
5100
+ */
5101
+ const MQR_ALIASES = { blocker: 1 };
5102
+ /** MQR severities ordered weakest to strongest (lowercase keys; Go parity). */
4458
5103
  const MQR_SEVERITY_RANK = {
4459
- INFO: 0,
4460
- LOW: 1,
4461
- MEDIUM: 2,
4462
- HIGH: 3,
4463
- BLOCKER: 4
5104
+ info: 0,
5105
+ low: 1,
5106
+ medium: 2,
5107
+ high: 3,
5108
+ blocker: 4
4464
5109
  };
4465
5110
  /**
4466
5111
  * Which severity axis drove a requirement's severity/impact. Emitted as the
@@ -4485,17 +5130,18 @@ const byCodeUnit = (a, b) => a < b ? -1 : a > b ? 1 : 0;
4485
5130
  function selectSeverity(issue) {
4486
5131
  const impacts = issue.impacts ?? [];
4487
5132
  if (impacts.length > 0) {
4488
- const worst = impacts.reduce((a, b) => MQR_SEVERITY_RANK[b.severity] > MQR_SEVERITY_RANK[a.severity] ? b : a);
5133
+ const rank = (s) => MQR_SEVERITY_RANK[s.toLowerCase()] ?? 0;
5134
+ const worst = impacts.reduce((a, b) => rank(b.severity) > rank(a.severity) ? b : a);
4489
5135
  return {
4490
5136
  severity: worst.severity,
4491
5137
  source: SEVERITY_SOURCE_MQR,
4492
- impact: MQR_IMPACT_MAPPING[worst.severity] ?? .5
5138
+ impact: severityToImpactWithAliases(worst.severity, MQR_ALIASES, .5)
4493
5139
  };
4494
5140
  }
4495
5141
  return {
4496
- severity: issue.severity,
5142
+ severity: issue.severity ?? "",
4497
5143
  source: SEVERITY_SOURCE_LEGACY,
4498
- impact: SEVERITY_IMPACT_MAPPING[issue.severity] ?? .5
5144
+ impact: severityToImpactWithAliases(issue.severity, SONARQUBE_ALIASES, .5)
4499
5145
  };
4500
5146
  }
4501
5147
  /**
@@ -4504,6 +5150,28 @@ function selectSeverity(issue) {
4504
5150
  * SonarQube is fundamentally a static analysis tool. Matches heimdall2.
4505
5151
  */
4506
5152
  const DEFAULT_NIST_TAGS = ["SA-11"];
5153
+ const OWASP_TAG_PATTERN = /owasp[-:]?a0*(\d{1,2})/gi;
5154
+ const OWASP_2017_PATTERN = /top 10 2017[\s\S]{0,30}?category\s*a0*(\d{1,2})/gi;
5155
+ /** Records a normalized OWASP-2017 category (A1..A10) when n is in range. */
5156
+ function addOwaspId(set, n) {
5157
+ if (n === void 0) return;
5158
+ const v = parseInt(n, 10);
5159
+ if (v >= 1 && v <= 10) set.add(`A${v}`);
5160
+ }
5161
+ /**
5162
+ * Combines CWE-derived and OWASP-derived NIST controls. The SA-11 default
5163
+ * applies only when neither source yields a mapping.
5164
+ */
5165
+ function foldNistControls(cweIds, owaspIds) {
5166
+ const set = /* @__PURE__ */ new Set();
5167
+ for (const c of mapCWEToNIST(cweIds, [])) set.add(c);
5168
+ for (const id of owaspIds) {
5169
+ const ctrl = getOwaspNistControl(id);
5170
+ if (ctrl) for (const c of ctrl.split("|")) set.add(c);
5171
+ }
5172
+ if (set.size === 0) return [...DEFAULT_NIST_TAGS];
5173
+ return Array.from(set).sort();
5174
+ }
4507
5175
  /**
4508
5176
  * Convert SonarQube issues JSON to HDF format
4509
5177
  *
@@ -4593,8 +5261,8 @@ function convertRuleToRequirement(ruleKey, issues, componentMap, ruleMap) {
4593
5261
  const description = extractDescription(rule);
4594
5262
  const firstIssue = issues[0];
4595
5263
  const { severity, source: severitySource, impact } = selectSeverity(firstIssue);
4596
- const { cweIds, owaspTags, allTags } = extractTags(rule, issues);
4597
- const nistControls = mapCWEToNIST(cweIds, DEFAULT_NIST_TAGS);
5264
+ const { cweIds, owaspIds, allTags } = extractTags(rule, issues);
5265
+ const nistControls = foldNistControls(cweIds, owaspIds);
4598
5266
  const cciControls = nistToCci(nistControls);
4599
5267
  const results = issues.map((issue) => createResultFromIssue(issue, componentMap));
4600
5268
  const issueWithLocation = issues.find((i) => i.line !== void 0);
@@ -4604,7 +5272,7 @@ function convertRuleToRequirement(ruleKey, issues, componentMap, ruleMap) {
4604
5272
  severitySource,
4605
5273
  type: firstIssue.type.toLowerCase(),
4606
5274
  cwe: cweIds,
4607
- owasp: owaspTags,
5275
+ owasp: owaspIds,
4608
5276
  nist: nistControls,
4609
5277
  cci: cciControls,
4610
5278
  ...firstIssue.effort ? { effort: firstIssue.effort } : {},
@@ -4635,11 +5303,11 @@ function convertRuleToRequirement(ruleKey, issues, componentMap, ruleMap) {
4635
5303
  function extractDescription(rule) {
4636
5304
  if (!rule) return "";
4637
5305
  if (rule.mdDesc) return rule.mdDesc;
4638
- if (rule.htmlDesc) return rule.htmlDesc.replace(/<[^>]*>/g, " ").replace(/\s+/g, " ").trim();
5306
+ if (rule.htmlDesc) return stripHtml(rule.htmlDesc);
4639
5307
  if (rule.descriptionSections && rule.descriptionSections.length > 0) {
4640
5308
  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);
5309
+ if (rootCause) return stripHtml(rootCause.content);
5310
+ const parts = rule.descriptionSections.map((s) => stripHtml(s.content)).filter((s) => s.length > 0);
4643
5311
  if (parts.length > 0) return parts.join("\n\n");
4644
5312
  }
4645
5313
  return rule.name || "";
@@ -4653,7 +5321,7 @@ function extractTags(rule, issues) {
4653
5321
  for (const tag of ruleTags) {
4654
5322
  const lowerTag = tag.toLowerCase();
4655
5323
  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);
5324
+ for (const m of tag.matchAll(OWASP_TAG_PATTERN)) addOwaspId(owaspSet, m[1]);
4657
5325
  const parts = tag.split(":");
4658
5326
  if (parts.length === 2) {
4659
5327
  const [category, value] = parts;
@@ -4663,20 +5331,24 @@ function extractTags(rule, issues) {
4663
5331
  }
4664
5332
  }
4665
5333
  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);
5334
+ if (tag.toLowerCase().startsWith("cwe-")) for (const id of extractCWEIDs(tag)) cweSet.add(`CWE-${id}`);
5335
+ for (const m of tag.matchAll(OWASP_TAG_PATTERN)) addOwaspId(owaspSet, m[1]);
4669
5336
  }
4670
5337
  if (rule?.htmlDesc || rule?.mdDesc) {
4671
5338
  const desc = (rule.htmlDesc || "") + (rule.mdDesc || "");
4672
5339
  for (const id of extractCWEIDs(desc)) cweSet.add(`CWE-${id}`);
4673
5340
  }
4674
5341
  if (rule?.descriptionSections) for (const section of rule.descriptionSections) for (const id of extractCWEIDs(section.content)) cweSet.add(`CWE-${id}`);
5342
+ if (rule) {
5343
+ const texts = [rule.htmlDesc || "", rule.mdDesc || ""];
5344
+ for (const section of rule.descriptionSections ?? []) texts.push(section.content);
5345
+ for (const text of texts) for (const m of text.matchAll(OWASP_2017_PATTERN)) addOwaspId(owaspSet, m[1]);
5346
+ }
4675
5347
  const allTags = {};
4676
5348
  for (const [category, values] of allTagsMap) allTags[category] = Array.from(values);
4677
5349
  return {
4678
5350
  cweIds: Array.from(cweSet),
4679
- owaspTags: Array.from(owaspSet),
5351
+ owaspIds: Array.from(owaspSet).sort((a, b) => parseInt(a.slice(1), 10) - parseInt(b.slice(1), 10)),
4680
5352
  allTags
4681
5353
  };
4682
5354
  }
@@ -4941,6 +5613,7 @@ function buildRequirement$14(checkId, group, scanTime) {
4941
5613
  const checkTypes = [...new Set(group.map((c) => c.checkType).filter((t) => t !== ""))].sort();
4942
5614
  if (checkTypes.length > 0) tags["check_type"] = checkTypes;
4943
5615
  if (rep.bc_check_id) tags["bc_check_id"] = rep.bc_check_id;
5616
+ markUnratedSeverity(tags, rep.severity);
4944
5617
  const descriptions = [{
4945
5618
  label: "default",
4946
5619
  data: rep.check_name
@@ -5033,14 +5706,6 @@ async function convertCheckovToHdf(input, converterVersion = "1.0.0") {
5033
5706
  //#endregion
5034
5707
  //#region converters/gosec-to-hdf/typescript/converter.ts
5035
5708
  /**
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
5709
  * Returns the first-class CWE identifiers for an issue in "CWE-N" form,
5045
5710
  * or an empty array when the issue carries no CWE.
5046
5711
  */
@@ -5104,10 +5769,11 @@ function issueToResult(issue, scanTime) {
5104
5769
  */
5105
5770
  function buildRequirement$13(ruleId, issues, scanTime) {
5106
5771
  const rep = issues[0];
5107
- const impact = IMPACT_MAPPING$6[rep.severity.toUpperCase()] ?? .5;
5772
+ const impact = severityToImpact(rep.severity);
5108
5773
  const nist = mapCWEToNIST([rep.cwe.id], DEFAULT_REMEDIATION_NIST_TAGS$1);
5109
5774
  const tags = { nist };
5110
5775
  if (rep.confidence) tags.confidence = rep.confidence;
5776
+ markUnratedSeverity(tags, rep.severity);
5111
5777
  const results = issues.map((issue) => issueToResult(issue, scanTime));
5112
5778
  const descriptions = [{
5113
5779
  label: "default",
@@ -5357,7 +6023,7 @@ function buildSourceLocation$3(alert) {
5357
6023
  return { ref: inst.uri };
5358
6024
  }
5359
6025
  const REF_URL_RE = /https?:\/\/[^\s<>"']+/g;
5360
- function buildRefs$7(reference) {
6026
+ function buildRefs$8(reference) {
5361
6027
  if (!reference) return;
5362
6028
  const urls = reference.match(REF_URL_RE);
5363
6029
  if (!urls || urls.length === 0) return;
@@ -5432,7 +6098,7 @@ function buildSiteRequirements(site, startTime) {
5432
6098
  if (code !== void 0) req.code = code;
5433
6099
  const sourceLocation = buildSourceLocation$3(alert);
5434
6100
  if (sourceLocation !== void 0) req.sourceLocation = sourceLocation;
5435
- const refs = buildRefs$7(alert.reference);
6101
+ const refs = buildRefs$8(alert.reference);
5436
6102
  if (refs !== void 0) req.refs = refs;
5437
6103
  requirements.push(req);
5438
6104
  }
@@ -6052,13 +6718,7 @@ function stringSlice(v) {
6052
6718
  * the canonical worst-wins ordering from @mitre/hdf-utilities.
6053
6719
  */
6054
6720
  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);
6721
+ return worstStatus(resultStatuses(req));
6062
6722
  }
6063
6723
  /**
6064
6724
  * Whether an HDF Result_Status is a failing verdict. Only 'failed' fails;
@@ -6069,20 +6729,46 @@ function worstOfResults(req) {
6069
6729
  function isFailing(status) {
6070
6730
  return status === "failed";
6071
6731
  }
6072
- /** Resolve the status context for a requirement. */
6732
+ /**
6733
+ * Resolve the status context for a requirement via the canonical
6734
+ * effective-status ladder — the stored effectiveStatus field is never read
6735
+ * (see status-determination.md; the field is an output cache).
6736
+ */
6073
6737
  function statusOf(req) {
6074
6738
  const raw = worstOfResults(req);
6075
- const effective = getStr(req, "effectiveStatus");
6076
- const overrides = asArr(req.statusOverrides);
6077
- const rollup = effective !== "" ? effective : raw;
6739
+ const overrides = asArr(req.statusOverrides) ?? [];
6740
+ const overrideInputs = overrides.flatMap((oRaw) => {
6741
+ const o = asMap(oRaw);
6742
+ if (!o) return [];
6743
+ return [{
6744
+ status: getStr(o, "status") || void 0,
6745
+ appliedAt: getStr(o, "appliedAt") || void 0,
6746
+ expiresAt: getStr(o, "expiresAt") || void 0
6747
+ }];
6748
+ });
6749
+ const rollup = computeEffectiveStatus({
6750
+ impact: typeof req.impact === "number" ? req.impact : 1,
6751
+ resultStatuses: resultStatuses(req),
6752
+ overrides: overrideInputs
6753
+ });
6754
+ const governed = governingStatusOverride(overrideInputs) !== void 0;
6078
6755
  return {
6079
6756
  raw,
6080
- effective,
6081
6757
  rollup,
6082
- overridden: overrides !== void 0 && overrides.length > 0 || "effectiveStatus" in req,
6083
- suppressed: isFailing(raw) && !isFailing(rollup)
6758
+ overridden: overrides.length > 0,
6759
+ suppressed: governed && isFailing(raw) && !isFailing(rollup)
6084
6760
  };
6085
6761
  }
6762
+ /** Lists results[].status losslessly for the canonical roll-up. */
6763
+ function resultStatuses(req) {
6764
+ const results = asArr(req.results) ?? [];
6765
+ const statuses = [];
6766
+ for (const rRaw of results) {
6767
+ const r = asMap(rRaw);
6768
+ if (r) statuses.push(getStr(r, "status"));
6769
+ }
6770
+ return statuses;
6771
+ }
6086
6772
  function firstComponent(doc) {
6087
6773
  const comps = asArr(doc.components);
6088
6774
  if (!comps || comps.length === 0) return void 0;
@@ -6238,10 +6924,7 @@ function stringifyLine(v) {
6238
6924
  * the missing-baselines error and drives validateInputSize.
6239
6925
  */
6240
6926
  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`);
6927
+ const { doc, items: baselines } = requireHdfResults(input, converterName);
6245
6928
  const docTimestamp = getStr(doc, "timestamp");
6246
6929
  const tool = asMap(doc.tool);
6247
6930
  const generator = asMap(doc.generator);
@@ -6299,6 +6982,19 @@ const CVSS_METHODS = /* @__PURE__ */ new Set([
6299
6982
  "CVSSv4"
6300
6983
  ]);
6301
6984
  /**
6985
+ * Reports whether no rating carries rating information: no CVSS score and no
6986
+ * rated severity token. Covers the empty-ratings default and the fabricated-
6987
+ * "medium" (empty severity, no score) paths, both of which land on impact 0.5
6988
+ * without the source having asserted a severity.
6989
+ */
6990
+ function vulnIsUnrated(ratings) {
6991
+ for (const rating of ratings) {
6992
+ if (rating.method && CVSS_METHODS.has(rating.method) && rating.score !== void 0 && rating.score !== null) return false;
6993
+ if (!isUnratedSeverity(rating.severity)) return false;
6994
+ }
6995
+ return true;
6996
+ }
6997
+ /**
6302
6998
  * Computes the maximum impact across all ratings for a vulnerability.
6303
6999
  * Prefers CVSS score/10 when available, falls back to severityToImpact().
6304
7000
  */
@@ -6328,7 +7024,7 @@ function cvssVersionFromMethod(method, vector) {
6328
7024
  default: return cvssVersionFromVector(vector);
6329
7025
  }
6330
7026
  }
6331
- function buildCvssEntries$4(ratings) {
7027
+ function buildCvssEntries$5(ratings) {
6332
7028
  const entries = [];
6333
7029
  for (const r of ratings) {
6334
7030
  const hasCvssMethod = r.method !== void 0 && CVSS_METHODS.has(r.method);
@@ -6349,7 +7045,7 @@ function buildCvssEntries$4(ratings) {
6349
7045
  * de-duplicated across all three in first-seen order. Returns undefined when the
6350
7046
  * vulnerability carries no links.
6351
7047
  */
6352
- function buildRefs$6(vuln) {
7048
+ function buildRefs$7(vuln) {
6353
7049
  const seen = /* @__PURE__ */ new Set();
6354
7050
  const refs = [];
6355
7051
  const add = (url) => {
@@ -6633,8 +7329,9 @@ async function convertCyclonedxToHdf(input, converterVersion = "1.0.0") {
6633
7329
  nist,
6634
7330
  cci: nistToCci(nist)
6635
7331
  };
7332
+ if (vulnIsUnrated(ratings)) markUnratedSeverity(tags, "");
6636
7333
  const cweIds = cwes.map((c) => `CWE-${c}`);
6637
- const cvssEntries = buildCvssEntries$4(ratings);
7334
+ const cvssEntries = buildCvssEntries$5(ratings);
6638
7335
  const descriptions = [];
6639
7336
  const defaultParts = [];
6640
7337
  if (vuln.description) defaultParts.push(`Description: ${vuln.description}`);
@@ -6667,7 +7364,7 @@ async function convertCyclonedxToHdf(input, converterVersion = "1.0.0") {
6667
7364
  req.code = buildVulnCode$1(vuln);
6668
7365
  if (cvssEntries.length > 0) req.cvss = cvssEntries;
6669
7366
  if (cweIds.length > 0) req.cwe = cweIds;
6670
- const refs = buildRefs$6(vuln);
7367
+ const refs = buildRefs$7(vuln);
6671
7368
  if (refs !== void 0) req.refs = refs;
6672
7369
  const controlType = deriveControlTypeFromTags(nist);
6673
7370
  if (controlType !== void 0) req.controlType = controlType;
@@ -6750,7 +7447,7 @@ function createRow(baseline, requirement, target) {
6750
7447
  const message = firstResult?.message || "";
6751
7448
  const nistControls = extractArrayFromTags(requirement.tags, "nist");
6752
7449
  const cciControls = extractArrayFromTags(requirement.tags, "cci");
6753
- const effectiveStatus = requirement.effectiveStatus ? String(requirement.effectiveStatus) : String(status);
7450
+ const effectiveStatus = requirementEffectiveStatus(requirement);
6754
7451
  const effectiveImpact = (requirement.effectiveImpact ?? requirement.impact).toFixed(1);
6755
7452
  const disposition = requirement.disposition ? String(requirement.disposition) : "";
6756
7453
  return {
@@ -7904,7 +8601,7 @@ async function convertSplunkToHdf(input, converterVersion = "1.0.0") {
7904
8601
  }) : [];
7905
8602
  const options = { tags: control.tags ?? {} };
7906
8603
  if (control.source_location) options.sourceLocation = control.source_location;
7907
- const req = createRequirement(control.id, control.title, descriptions, control.impact, results, options);
8604
+ const req = createRequirement(control.id, control.title, descriptions, control.impact ?? 0, results, options);
7908
8605
  if (!req.title) delete req.title;
7909
8606
  if (control.code) req.code = control.code;
7910
8607
  const nistTagsRaw = control.tags?.["nist"];
@@ -8050,7 +8747,7 @@ function convertHdfToXml(input) {
8050
8747
  }
8051
8748
  //#endregion
8052
8749
  //#region converters/gitlab-to-hdf/typescript/converter.ts
8053
- function buildRefs$5(vuln) {
8750
+ function buildRefs$6(vuln) {
8054
8751
  const refs = [];
8055
8752
  const seen = /* @__PURE__ */ new Set();
8056
8753
  const appendUrl = (url) => {
@@ -8190,6 +8887,7 @@ async function convertGitlabToHdf(input, converterVersion = "1.0.0") {
8190
8887
  const extras = {};
8191
8888
  for (const [key, values] of Object.entries(idTags)) extras[key] = values;
8192
8889
  const tags = buildNistCciTags(nistTags, cciTags, Object.keys(extras).length > 0 ? extras : void 0);
8890
+ markUnratedSeverity(tags, vuln.severity);
8193
8891
  const descriptions = [];
8194
8892
  if (vuln.description) descriptions.push({
8195
8893
  label: "default",
@@ -8224,7 +8922,7 @@ async function convertGitlabToHdf(input, converterVersion = "1.0.0") {
8224
8922
  descriptions,
8225
8923
  verificationMethod: VerificationMethodEnum.Automated
8226
8924
  };
8227
- const refs = buildRefs$5(vuln);
8925
+ const refs = buildRefs$6(vuln);
8228
8926
  if (refs) req.refs = refs;
8229
8927
  const controlType = deriveControlTypeFromTags(nistTags);
8230
8928
  if (controlType !== void 0) req.controlType = controlType;
@@ -8266,6 +8964,307 @@ async function convertGitlabToHdf(input, converterVersion = "1.0.0") {
8266
8964
  return serializeHdf(hdf);
8267
8965
  }
8268
8966
  //#endregion
8967
+ //#region converters/trivy-to-hdf/typescript/converter.ts
8968
+ const BASELINE_NAME = "Trivy Scan";
8969
+ const STATIC_CCI = nistToCci(DEFAULT_STATIC_ANALYSIS_NIST_TAGS);
8970
+ const CWE_ID_PATTERN$1 = /^CWE-[1-9]\d*$/;
8971
+ const SEVERITY_ALIASES = { critical: .9 };
8972
+ /**
8973
+ * Route Trivy output to HDF Results: native Trivy JSON (SchemaVersion 2) is
8974
+ * parsed here; SARIF / CycloneDX / ASFF / GitLab are delegated to their
8975
+ * converters. TypeScript peer of shared/go/../trivy-to-hdf converter.go.
8976
+ */
8977
+ async function convertTrivyToHdf(input, converterVersion = "1.0.0") {
8978
+ validateInputSize(input, "trivy");
8979
+ if (!input?.trim()) throw new Error("trivy: empty input");
8980
+ const probe = parseJSON(input);
8981
+ if (typeof probe !== "object" || probe === null || Array.isArray(probe)) throw new Error("trivy: input is not a JSON object");
8982
+ if (isNativeTrivy(probe)) return convertNative(input, converterVersion);
8983
+ if (probe.bomFormat === "CycloneDX") return convertCyclonedxToHdf(input, converterVersion);
8984
+ if ("runs" in probe && "version" in probe) return convertSarifToHdf(input, converterVersion);
8985
+ if ("Findings" in probe || "ProductArn" in probe) return convertAsffToHdf(input, converterVersion);
8986
+ if ("vulnerabilities" in probe) return convertGitlabToHdf(input, converterVersion);
8987
+ throw new Error("trivy: not a recognized Trivy output format (native JSON, SARIF, CycloneDX, ASFF, or GitLab)");
8988
+ }
8989
+ function isNativeTrivy(m) {
8990
+ return "SchemaVersion" in m && "ArtifactName" in m && "ArtifactType" in m;
8991
+ }
8992
+ async function convertNative(input, converterVersion) {
8993
+ const report = parseJSON(input);
8994
+ const resultsChecksum = await inputChecksum(input);
8995
+ const scanTime = report.CreatedAt ? parseTimestamp(report.CreatedAt) : null;
8996
+ const startTime = scanTime ?? /* @__PURE__ */ new Date("0001-01-01T00:00:00Z");
8997
+ const requirements = [];
8998
+ for (const res of report.Results ?? []) {
8999
+ for (const v of res.Vulnerabilities ?? []) requirements.push(convertVuln(v, res, startTime));
9000
+ for (const m of res.Misconfigurations ?? []) requirements.push(convertMisconf(m, res, startTime));
9001
+ for (const s of res.Secrets ?? []) requirements.push(convertSecret(s, res, startTime));
9002
+ for (const l of res.Licenses ?? []) requirements.push(convertLicense(l, res, startTime));
9003
+ }
9004
+ if (requirements.length === 0) requirements.push(buildNoFindingsRequirement("trivy-no-findings", `Trivy scanned ${report.ArtifactName ?? "the target"} and reported zero findings.`, startTime));
9005
+ const baseline = createMinimalBaseline(BASELINE_NAME, requirements, { resultsChecksum });
9006
+ if (report.ArtifactName) baseline.title = report.ArtifactName;
9007
+ const component = buildComponent$1(report);
9008
+ return buildHdfResults({
9009
+ generatorName: "trivy-to-hdf",
9010
+ converterVersion,
9011
+ toolName: "Trivy",
9012
+ toolVersion: report.Trivy?.Version,
9013
+ baselines: [baseline],
9014
+ components: component ? [component] : void 0,
9015
+ timestamp: scanTime ?? void 0
9016
+ });
9017
+ }
9018
+ function convertVuln(v, res, startTime) {
9019
+ const descriptions = [{
9020
+ label: "default",
9021
+ data: firstNonEmpty(v.Description ?? "", v.Title ?? "", v.VulnerabilityID ?? "")
9022
+ }];
9023
+ if (v.FixedVersion) descriptions.push({
9024
+ label: "fix",
9025
+ data: `Fixed in version ${v.FixedVersion}.`
9026
+ });
9027
+ const extras = { class: res.Class };
9028
+ putIf(extras, "trivy_type", res.Type);
9029
+ putIf(extras, "severity_source", v.SeveritySource);
9030
+ putIf(extras, "data_source", v.DataSource?.Name);
9031
+ putIf(extras, "published_date", v.PublishedDate);
9032
+ if (v.VendorSeverity && Object.keys(v.VendorSeverity).length > 0) extras.vendor_severity = v.VendorSeverity;
9033
+ const tags = buildTags$2(extras);
9034
+ markUnratedSeverity(tags, v.Severity);
9035
+ const req = {
9036
+ id: `Trivy/${v.VulnerabilityID ?? ""}`,
9037
+ title: `Trivy found ${v.VulnerabilityID ?? ""} in ${pkgLabel(v.PkgName, v.InstalledVersion)}`,
9038
+ descriptions,
9039
+ impact: severityToImpactWithAliases(v.Severity, SEVERITY_ALIASES, .5),
9040
+ tags,
9041
+ cwe: filterCwes(v.CweIDs),
9042
+ cvss: buildCvssEntries$4(v.CVSS),
9043
+ refs: buildRefs$5([v.PrimaryURL, ...v.References ?? []]),
9044
+ code: JSON.stringify(v, null, 2),
9045
+ controlType: deriveControlTypeFromTags(DEFAULT_STATIC_ANALYSIS_NIST_TAGS),
9046
+ verificationMethod: VerificationMethodEnum.Automated,
9047
+ results: [{
9048
+ status: ResultStatus.Failed,
9049
+ codeDesc: buildVulnCodeDesc(v, res),
9050
+ startTime,
9051
+ message: `Severity: ${firstNonEmpty(v.Severity ?? "", "UNKNOWN")}`
9052
+ }]
9053
+ };
9054
+ const ap = buildAffectedPackage$1(v);
9055
+ if (ap.purl) req.affectedPackages = [ap];
9056
+ if (v.PkgPath) req.sourceLocation = { ref: v.PkgPath };
9057
+ return req;
9058
+ }
9059
+ function convertMisconf(m, res, startTime) {
9060
+ const descriptions = [{
9061
+ label: "default",
9062
+ data: firstNonEmpty(m.Description ?? "", m.Message ?? "", m.Title ?? "")
9063
+ }];
9064
+ if (m.Resolution) descriptions.push({
9065
+ label: "fix",
9066
+ data: m.Resolution
9067
+ });
9068
+ const tags = buildTags$2({
9069
+ class: res.Class,
9070
+ ...m.Type ? { misconfig_type: m.Type } : {}
9071
+ });
9072
+ markUnratedSeverity(tags, m.Severity);
9073
+ const req = {
9074
+ id: `Trivy/${firstNonEmpty(m.ID ?? "", m.AVDID ?? "")}`,
9075
+ title: m.Title,
9076
+ descriptions,
9077
+ impact: severityToImpactWithAliases(m.Severity, SEVERITY_ALIASES, .5),
9078
+ tags,
9079
+ refs: buildRefs$5([m.PrimaryURL, ...m.References ?? []]),
9080
+ code: JSON.stringify(m, null, 2),
9081
+ controlType: deriveControlTypeFromTags(DEFAULT_STATIC_ANALYSIS_NIST_TAGS),
9082
+ verificationMethod: VerificationMethodEnum.Automated,
9083
+ results: [{
9084
+ status: misconfStatus(m.Status),
9085
+ codeDesc: firstNonEmpty(m.Message ?? "", m.Title ?? ""),
9086
+ startTime
9087
+ }]
9088
+ };
9089
+ if (res.Target) {
9090
+ const sl = { ref: res.Target };
9091
+ if (m.CauseMetadata?.StartLine && m.CauseMetadata.StartLine > 0) sl.line = m.CauseMetadata.StartLine;
9092
+ req.sourceLocation = sl;
9093
+ }
9094
+ return req;
9095
+ }
9096
+ function convertSecret(s, res, startTime) {
9097
+ const tags = buildTags$2({
9098
+ class: res.Class,
9099
+ ...s.Category ? { secret_category: s.Category } : {}
9100
+ });
9101
+ markUnratedSeverity(tags, s.Severity);
9102
+ const req = {
9103
+ id: `Trivy/secret/${s.RuleID ?? ""}@${res.Target ?? ""}:${s.StartLine ?? 0}`,
9104
+ title: s.Title,
9105
+ descriptions: [{
9106
+ label: "default",
9107
+ data: `${firstNonEmpty(s.Title ?? "", s.RuleID ?? "")} detected in ${res.Target ?? ""} (value redacted by Trivy).`
9108
+ }],
9109
+ impact: severityToImpactWithAliases(s.Severity, SEVERITY_ALIASES, .5),
9110
+ tags,
9111
+ code: JSON.stringify(s, null, 2),
9112
+ controlType: deriveControlTypeFromTags(DEFAULT_STATIC_ANALYSIS_NIST_TAGS),
9113
+ verificationMethod: VerificationMethodEnum.Automated,
9114
+ results: [{
9115
+ status: ResultStatus.Failed,
9116
+ codeDesc: s.Match ?? "",
9117
+ startTime
9118
+ }]
9119
+ };
9120
+ if (res.Target) {
9121
+ const sl = { ref: res.Target };
9122
+ if (s.StartLine && s.StartLine > 0) sl.line = s.StartLine;
9123
+ req.sourceLocation = sl;
9124
+ }
9125
+ return req;
9126
+ }
9127
+ function convertLicense(l, res, startTime) {
9128
+ const extras = { class: res.Class };
9129
+ putIf(extras, "license_category", l.Category);
9130
+ putIf(extras, "package", l.PkgName);
9131
+ if (l.Confidence && l.Confidence > 0) extras.confidence = l.Confidence;
9132
+ const tags = buildTags$2(extras);
9133
+ markUnratedSeverity(tags, l.Severity);
9134
+ const req = {
9135
+ id: `Trivy/license/${l.PkgName ?? ""}/${l.Name ?? ""}`,
9136
+ title: `${l.Name ?? ""} (${l.Category ?? ""})`,
9137
+ descriptions: [{
9138
+ label: "default",
9139
+ data: `Package ${l.PkgName ?? ""} uses the ${l.Name ?? ""} license (category: ${l.Category ?? ""}).`
9140
+ }],
9141
+ impact: severityToImpactWithAliases(l.Severity, SEVERITY_ALIASES, .5),
9142
+ tags,
9143
+ refs: buildRefs$5([l.Link]),
9144
+ code: JSON.stringify(l, null, 2),
9145
+ controlType: deriveControlTypeFromTags(DEFAULT_STATIC_ANALYSIS_NIST_TAGS),
9146
+ verificationMethod: VerificationMethodEnum.Automated,
9147
+ results: [{
9148
+ status: ResultStatus.Failed,
9149
+ codeDesc: `${l.PkgName ?? ""}: ${l.Name ?? ""} license`,
9150
+ startTime
9151
+ }]
9152
+ };
9153
+ if (l.FilePath) req.sourceLocation = { ref: l.FilePath };
9154
+ return req;
9155
+ }
9156
+ function buildComponent$1(report) {
9157
+ if (!report.ArtifactName) return void 0;
9158
+ if (report.ArtifactType !== "container_image") return {
9159
+ name: report.ArtifactName,
9160
+ type: TargetType.Artifact
9161
+ };
9162
+ const c = {
9163
+ name: report.ArtifactName,
9164
+ type: TargetType.ContainerImage
9165
+ };
9166
+ const md = report.Metadata;
9167
+ if (!md) return c;
9168
+ if (md.ImageID) c.imageId = md.ImageID;
9169
+ if (md.OS?.Family) c.osName = md.OS.Family;
9170
+ if (md.OS?.Name) c.osVersion = md.OS.Name;
9171
+ const repoDigest = md.RepoDigests?.[0];
9172
+ if (repoDigest) {
9173
+ c.image = repoDigest;
9174
+ const integrity = digestToChecksums(digestPart(repoDigest));
9175
+ if (integrity) c.integrity = integrity;
9176
+ }
9177
+ if (md.ImageConfig?.architecture) c.labels = { architecture: md.ImageConfig.architecture };
9178
+ return c;
9179
+ }
9180
+ function buildTags$2(extras) {
9181
+ return buildNistCciTags(DEFAULT_STATIC_ANALYSIS_NIST_TAGS, STATIC_CCI, extras);
9182
+ }
9183
+ function misconfStatus(status) {
9184
+ switch ((status ?? "").toUpperCase()) {
9185
+ case "PASS": return ResultStatus.Passed;
9186
+ case "EXCEPTION": return ResultStatus.NotApplicable;
9187
+ default: return ResultStatus.Failed;
9188
+ }
9189
+ }
9190
+ function filterCwes(cwes) {
9191
+ if (!cwes) return void 0;
9192
+ const out = cwes.filter((c) => CWE_ID_PATTERN$1.test(c));
9193
+ return out.length > 0 ? out : void 0;
9194
+ }
9195
+ function buildCvssEntries$4(m) {
9196
+ if (!m || Object.keys(m).length === 0) return void 0;
9197
+ const entries = [];
9198
+ for (const src of Object.keys(m).sort()) {
9199
+ const c = m[src];
9200
+ if (!c) continue;
9201
+ if (c.V2Vector || c.V2Score !== void 0) entries.push(buildCvss$3({
9202
+ version: Version.The20,
9203
+ baseVector: c.V2Vector,
9204
+ baseScore: c.V2Score,
9205
+ source: src
9206
+ }));
9207
+ if (c.V3Vector || c.V3Score !== void 0) entries.push(buildCvss$3({
9208
+ version: cvssVersionFromVector(c.V3Vector, Version.The31),
9209
+ baseVector: c.V3Vector,
9210
+ baseScore: c.V3Score,
9211
+ source: src
9212
+ }));
9213
+ if (c.V40Vector || c.V40Score !== void 0) entries.push(buildCvss$3({
9214
+ version: Version.The40,
9215
+ baseVector: c.V40Vector,
9216
+ baseScore: c.V40Score,
9217
+ source: src
9218
+ }));
9219
+ }
9220
+ return entries.length > 0 ? entries : void 0;
9221
+ }
9222
+ function buildAffectedPackage$1(v) {
9223
+ const ap = {};
9224
+ if (v.PkgName) ap.name = v.PkgName;
9225
+ if (v.InstalledVersion) ap.version = v.InstalledVersion;
9226
+ if (v.FixedVersion) ap.fixedInVersion = v.FixedVersion;
9227
+ const purl = v.PkgIdentifier?.PURL;
9228
+ if (purl) {
9229
+ ap.purl = purl;
9230
+ const eco = ecosystemFromPurl(purl);
9231
+ if (eco) ap.ecosystem = eco;
9232
+ }
9233
+ return ap;
9234
+ }
9235
+ function ecosystemFromPurl(purl) {
9236
+ const eco = ecosystemFromPurlType(purl.replace(/^pkg:/, "").split(/[/@?]/)[0] ?? "");
9237
+ return eco === Ecosystem.Generic ? void 0 : eco;
9238
+ }
9239
+ function buildRefs$5(urls) {
9240
+ const seen = /* @__PURE__ */ new Set();
9241
+ const refs = [];
9242
+ for (const u of urls) {
9243
+ if (!u || seen.has(u)) continue;
9244
+ seen.add(u);
9245
+ refs.push({ url: u });
9246
+ }
9247
+ return refs.length > 0 ? refs : void 0;
9248
+ }
9249
+ function buildVulnCodeDesc(v, res) {
9250
+ const parts = [`Package: ${v.PkgName ?? ""}@${v.InstalledVersion ?? ""}`];
9251
+ if (res.Type) parts.push(`Type: ${res.Type}`);
9252
+ if (v.PkgPath) parts.push(`Location: ${v.PkgPath}`);
9253
+ if (v.FixedVersion) parts.push(`Fixed: ${v.FixedVersion}`);
9254
+ return parts.join(" | ");
9255
+ }
9256
+ function pkgLabel(name, version) {
9257
+ if (!version) return name ?? "";
9258
+ return `${name ?? ""}@${version}`;
9259
+ }
9260
+ function digestPart(ref) {
9261
+ const at = ref.lastIndexOf("@");
9262
+ return at >= 0 ? ref.slice(at + 1) : ref;
9263
+ }
9264
+ function putIf(m, key, val) {
9265
+ if (val) m[key] = val;
9266
+ }
9267
+ //#endregion
8269
9268
  //#region converters/trufflehog-to-hdf/typescript/converter.ts
8270
9269
  /** Hardcoded NIST/CCI constants for credential exposure findings. */
8271
9270
  const TRUFFLEHOG_NIST = ["IA-5 (7)"];
@@ -8586,14 +9585,9 @@ async function convertHipcheckToHdf(input, converterVersion = "1.0.0") {
8586
9585
  }
8587
9586
  //#endregion
8588
9587
  //#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
- };
9588
+ const BURPSUITE_ALIASES = { information: .3 };
8595
9589
  function getImpact$7(severity) {
8596
- return IMPACT_MAPPING$5[severity.toLowerCase()] ?? .3;
9590
+ return severityToImpactWithAliases(severity, BURPSUITE_ALIASES, .3);
8597
9591
  }
8598
9592
  /**
8599
9593
  * Extract CWE identifiers from HTML text.
@@ -8778,13 +9772,6 @@ function buildRequirement$11(issueType, issues, startTime) {
8778
9772
  }
8779
9773
  //#endregion
8780
9774
  //#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
9775
  const NAMED_ENTITIES$2 = {
8789
9776
  amp: "&",
8790
9777
  lt: "<",
@@ -8848,10 +9835,9 @@ function getStatus$2(status) {
8848
9835
  function getBacktrace(resultStatus) {
8849
9836
  return resultStatus === "Failed" ? ["DB Protect Failed Check"] : void 0;
8850
9837
  }
8851
- /** Maps DBProtect risk level to HDF impact value */
9838
+ /** Maps DBProtect risk level to HDF impact via the shared standard map (Go parity). */
8852
9839
  function getImpact$6(riskDV) {
8853
- const impact = IMPACT_MAPPING$4[riskDV.toLowerCase()];
8854
- return impact !== void 0 ? impact : .5;
9840
+ return severityToImpactWithAliases(riskDV, {}, .5);
8855
9841
  }
8856
9842
  /** Creates a description string from a finding's task and check category */
8857
9843
  function formatDesc$1(f) {
@@ -8967,6 +9953,7 @@ function buildRequirement$10(checkID, findings, hasStatus) {
8967
9953
  const tags = buildNistCciTags(nist, nistToCci(nist));
8968
9954
  const checkCategory = rep["Check Category"] ?? "";
8969
9955
  if (checkCategory) tags.check_category = checkCategory;
9956
+ markUnratedSeverity(tags, rep["Risk DV"]);
8970
9957
  const descriptions = [{
8971
9958
  label: "default",
8972
9959
  data: formatDesc$1(rep)
@@ -9037,21 +10024,12 @@ async function convertDbprotectToHdf(input, converterVersion = "1.0.0") {
9037
10024
  }
9038
10025
  //#endregion
9039
10026
  //#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
- */
10027
+ const TWISTLOCK_ALIASES = {
10028
+ important: .9,
10029
+ moderate: .5
10030
+ };
9045
10031
  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
- }
10032
+ return severityToImpactWithAliases(severity, TWISTLOCK_ALIASES, .5);
9055
10033
  }
9056
10034
  /**
9057
10035
  * Builds the baseline title from scan result data.
@@ -9247,6 +10225,7 @@ function buildRequirement$9(vuln, packageTypes, distro) {
9247
10225
  const extras = { cveid: [vuln.id] };
9248
10226
  if (typeof vuln.cvss === "number" && vuln.cvss > 0) extras["cvss_base_score"] = vuln.cvss;
9249
10227
  const tags = buildNistCciTags(nist, cciTags, extras);
10228
+ markUnratedSeverity(tags, vuln.severity);
9250
10229
  const descriptions = [{
9251
10230
  label: "default",
9252
10231
  data: vuln.description
@@ -9508,7 +10487,7 @@ function buildAffectedPackageFromComponent(c) {
9508
10487
  let ecosystem;
9509
10488
  if (c.purl) ecosystem = ecosystemFromPurlType(parsePurl(c.purl)?.type);
9510
10489
  else if (c.name && c.version) ecosystem = ecosystemFromPurlType(void 0);
9511
- return buildAffectedPackage$1({
10490
+ return buildAffectedPackage$2({
9512
10491
  name: c.name,
9513
10492
  version: c.version,
9514
10493
  ecosystem,
@@ -9726,6 +10705,7 @@ function buildRequirement$7(entryID, entries, scanTime) {
9726
10705
  cci: nistToCci(nist)
9727
10706
  };
9728
10707
  if (cveIDs.length > 0) tags["cve"] = cveIDs;
10708
+ markUnratedSeverity(tags, rep.severity);
9729
10709
  const descriptions = [{
9730
10710
  label: "default",
9731
10711
  data: formatDescription(rep)
@@ -9779,7 +10759,7 @@ function buildAffectedPackageFromEntry(entry) {
9779
10759
  ecosystem = ecosystemFromXraySource(parsed.scheme);
9780
10760
  }
9781
10761
  const fixed = entry.component_versions?.fixed_versions?.[0];
9782
- return buildAffectedPackage$1({
10762
+ return buildAffectedPackage$2({
9783
10763
  name,
9784
10764
  version,
9785
10765
  ecosystem: ecosystem ?? (name ? Ecosystem.Generic : void 0),
@@ -10011,7 +10991,7 @@ function buildRequirement$6(vuln, scanTime, ml) {
10011
10991
  const refs = buildRefs$3(vuln);
10012
10992
  if (refs) req.refs = refs;
10013
10993
  const cpe23 = (vuln.cpes ?? []).find((c) => /^cpe:2\.3:[aho]:/.test(c));
10014
- const pkg = buildAffectedPackage$1({
10994
+ const pkg = buildAffectedPackage$2({
10015
10995
  name: vuln.package_name,
10016
10996
  version: vuln.package_version,
10017
10997
  ecosystem: vuln.package_name && vuln.package_version ? Ecosystem.Generic : void 0,
@@ -10033,12 +11013,6 @@ function imageTitle(report) {
10033
11013
  function targetNameFromReport(report) {
10034
11014
  return `${report.registry}/${report.repository}:${report.tag}`;
10035
11015
  }
10036
- const DIGEST_ALGORITHMS = {
10037
- sha256: HashAlgorithm.Sha256,
10038
- sha384: HashAlgorithm.Sha384,
10039
- sha512: HashAlgorithm.Sha512,
10040
- blake3: HashAlgorithm.Blake3
10041
- };
10042
11016
  /**
10043
11017
  * Splits NeuVector's base_os "name:version" form (e.g. "alpine:3.12.1") into OS
10044
11018
  * name and version. A value with no ":" is the name with no version; an empty
@@ -10060,28 +11034,6 @@ function splitBaseOS(baseOS) {
10060
11034
  };
10061
11035
  }
10062
11036
  /**
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
11037
  * Assembles the scan-wide containerImage component from the report's image
10086
11038
  * identity. base_os → osName/osVersion, digest → integrity, plus
10087
11039
  * imageId/registry/repository/tag when the report carries them.
@@ -10104,7 +11056,7 @@ function buildComponent(report) {
10104
11056
  if (report.registry) component.registry = report.registry;
10105
11057
  if (report.repository) component.repository = report.repository;
10106
11058
  if (report.tag) component.tag = report.tag;
10107
- const integrity = digestIntegrity(report.digest);
11059
+ const integrity = digestToChecksums(report.digest);
10108
11060
  if (integrity) component.integrity = integrity;
10109
11061
  return component;
10110
11062
  }
@@ -10323,8 +11275,9 @@ function buildRequirement$5(desc, vulns, snippetMap, startTimeStr) {
10323
11275
  label: "tips",
10324
11276
  data: tipsData
10325
11277
  });
10326
- let impact = 0;
10327
- if (vulns.length > 0) impact = roundImpact(parseFloat(vulns[0].InstanceInfo?.InstanceSeverity ?? "0") / 5);
11278
+ const instanceSeverity = parseFloat(vulns[0]?.InstanceInfo?.InstanceSeverity ?? "0");
11279
+ const impact = instanceSeverity > 0 ? roundImpact(instanceSeverity / 5) : 0;
11280
+ if (!(instanceSeverity > 0)) markUnratedSeverity(tags, "");
10328
11281
  const { items: limitedVulns, truncated } = limitArray(vulns);
10329
11282
  /* v8 ignore next -- truncation only triggers with >100K items */
10330
11283
  if (truncated) console.warn(`WARNING: Input truncated at ${limitedVulns.length} vulnerability items (original: ${vulns.length})`);
@@ -10522,6 +11475,7 @@ function buildRequirement$4(rec, scanTime) {
10522
11475
  const extras = {};
10523
11476
  if (rec["CVE ID"]) extras["cve"] = [rec["CVE ID"]];
10524
11477
  const tags = buildNistCciTags(nist, cciTags, Object.keys(extras).length > 0 ? extras : void 0);
11478
+ markUnratedSeverity(tags, rec.Severity);
10525
11479
  const descriptions = [{
10526
11480
  label: "default",
10527
11481
  data: rec.Description
@@ -10610,7 +11564,7 @@ function buildAffectedPackageFromRecord(rec) {
10610
11564
  const version = rec["Package Version"];
10611
11565
  if (!name || !version) return void 0;
10612
11566
  const fixMatch = rec["Fix Status"] ? FIX_VERSION_PATTERN.exec(rec["Fix Status"]) : null;
10613
- return buildAffectedPackage$1({
11567
+ return buildAffectedPackage$2({
10614
11568
  name,
10615
11569
  version,
10616
11570
  ecosystem: ecosystemFromDistro(rec.Distro),
@@ -10680,16 +11634,9 @@ async function convertPrismaToHdf(input, converterVersion = "1.0.0") {
10680
11634
  }
10681
11635
  //#endregion
10682
11636
  //#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
- };
11637
+ const NETSPARKER_ALIASES = { best_practice: 0 };
10691
11638
  function getImpact$2(severity) {
10692
- return IMPACT_MAPPING$3[severity.toLowerCase()] ?? .5;
11639
+ return severityToImpactWithAliases(severity, NETSPARKER_ALIASES, .5);
10693
11640
  }
10694
11641
  const NETSPARKER_US_DATETIME = /^\d{2}\/\d{2}\/\d{4} \d{1,2}:\d{2} (AM|PM)$/;
10695
11642
  function parseNetsparkerTimestamp(s) {
@@ -10824,6 +11771,7 @@ function buildRequirement$3(vuln, initiated) {
10824
11771
  const pci32 = vuln.classification?.pci32;
10825
11772
  if (pci32) extras.pci32 = pci32;
10826
11773
  const tags = buildNistCciTags(nist, cciTags, Object.keys(extras).length > 0 ? extras : void 0);
11774
+ markUnratedSeverity(tags, vuln.severity);
10827
11775
  const descriptions = [{
10828
11776
  label: "default",
10829
11777
  data: formatControlDesc(vuln) || vuln.name || ""
@@ -11219,14 +12167,16 @@ function buildRequirementFromResult(result, filename) {
11219
12167
  const startTime = (startTimeStr ? parseTimestamp(startTimeStr) : null) ?? /* @__PURE__ */ new Date("0001-01-01T00:00:00Z");
11220
12168
  const runTime = computeRunTime(result.response.milestones?.service_started, result.response.milestones?.service_completed);
11221
12169
  const score = result.result.score;
11222
- const status = determineStatus(score);
11223
- const toResult = (codeDesc) => createResult(status, void 0, {
12170
+ const scoreless = score === void 0 || score === null;
12171
+ const status = scoreless ? ResultStatus.NotReviewed : determineStatus(score);
12172
+ const message = scoreless ? "Conveyor result carries no score; verdict not evaluated." : void 0;
12173
+ const toResult = (codeDesc) => createResult(status, message, {
11224
12174
  codeDesc,
11225
12175
  startTime,
11226
12176
  runTime
11227
12177
  });
11228
12178
  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 });
12179
+ const req = createRequirement(result.sha256, filename, descriptions, scoreless ? 0 : scoreToImpact(score), results, { tags });
11230
12180
  req.verificationMethod = VerificationMethodEnum.Automated;
11231
12181
  const controlType = deriveControlTypeFromTags([...nist]);
11232
12182
  if (controlType !== void 0) req.controlType = controlType;
@@ -11301,17 +12251,17 @@ async function convertConveyorToHdf(input, converterVersion = "1.0.0") {
11301
12251
  */
11302
12252
  /** Attribute prefix used by fast-xml-parser — all XML attributes are accessed via `@_name`. */
11303
12253
  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
- ]);
12254
+ /** Veracode severity level (0-5) aliases; mirrors Go's veracodeAliases. */
12255
+ const VERACODE_ALIASES = {
12256
+ "5": .9,
12257
+ "4": .7,
12258
+ "3": .5,
12259
+ "2": .3,
12260
+ "1": .1,
12261
+ "0": 0
12262
+ };
11313
12263
  function veracodeSeverityToImpact(severity) {
11314
- return IMPACT_MAPPING$2.get(severity) ?? .1;
12264
+ return severityToImpactWithAliases(severity, VERACODE_ALIASES, .1);
11315
12265
  }
11316
12266
  /** Get an attribute from a parsed XML node, entity-decoded. */
11317
12267
  function attr(node, name) {
@@ -11556,12 +12506,12 @@ function buildCWERequirements(severities, firstBuildDate) {
11556
12506
  for (const sev of severities) {
11557
12507
  const impact = veracodeSeverityToImpact(attr(sev, "level"));
11558
12508
  const categories = ensureArray(sev.category);
11559
- for (const cat of categories) requirements.push(buildCWERequirement(cat, impact, firstBuildDate));
12509
+ for (const cat of categories) requirements.push(buildCWERequirement(cat, attr(sev, "level"), impact, firstBuildDate));
11560
12510
  }
11561
12511
  return requirements;
11562
12512
  }
11563
12513
  /** Build a single CWE-based requirement from a category. */
11564
- function buildCWERequirement(cat, impact, firstBuildDate) {
12514
+ function buildCWERequirement(cat, sevLevel, impact, firstBuildDate) {
11565
12515
  const cwes = ensureArray(cat.cwe);
11566
12516
  const cweIDs = cwes.map((c) => attr(c, "cweid")).filter(Boolean);
11567
12517
  const nist = mapCWEToNIST(cweIDs, DEFAULT_REMEDIATION_NIST_TAGS$1);
@@ -11574,6 +12524,7 @@ function buildCWERequirement(cat, impact, firstBuildDate) {
11574
12524
  if (values.length > 0) extras[key] = values;
11575
12525
  }
11576
12526
  const tags = buildNistCciTags(nist, cciTags, extras);
12527
+ markUnratedSeverity(tags, sevLevel);
11577
12528
  const cweList = cweIDs.map((id) => `CWE-${id}`);
11578
12529
  const descriptions = [{
11579
12530
  label: "default",
@@ -11685,6 +12636,7 @@ function buildCVERequirement(vuln, components, firstBuildDate) {
11685
12636
  else nist = DEFAULT_REMEDIATION_NIST_TAGS$1;
11686
12637
  const cciTags = nistToCci(nist);
11687
12638
  const tags = buildNistCciTags(nist, cciTags, {});
12639
+ markUnratedSeverity(tags, attr(vuln, "severity"));
11688
12640
  const startTime = parseVeracodeTimestamp(firstBuildDate) ?? /* @__PURE__ */ new Date();
11689
12641
  const results = components.map((comp) => createResult(ResultStatus.Failed, void 0, {
11690
12642
  codeDesc: formatSCACodeDesc(comp),
@@ -12017,14 +12969,6 @@ function repoNameFromURI(uri) {
12017
12969
  //#endregion
12018
12970
  //#region converters/msft-defender-cloud-to-hdf/typescript/converter.ts
12019
12971
  /**
12020
- * Severity to HDF impact mapping.
12021
- */
12022
- const IMPACT_MAPPING$1 = {
12023
- high: .7,
12024
- medium: .5,
12025
- low: .3
12026
- };
12027
- /**
12028
12972
  * Maps Azure status code to HDF ResultStatus.
12029
12973
  */
12030
12974
  function mapStatus(code) {
@@ -12052,7 +12996,7 @@ function extractSubscriptionID(resourcePath) {
12052
12996
  function buildRequirement(assessmentID, assessments, scanTime) {
12053
12997
  const rep = assessments[0];
12054
12998
  const meta = rep.properties.metadata;
12055
- const impact = IMPACT_MAPPING$1[meta.severity.toLowerCase()] ?? .5;
12999
+ const impact = severityToImpact(meta.severity);
12056
13000
  const tags = {};
12057
13001
  if (meta.categories.length > 0) tags["categories"] = meta.categories;
12058
13002
  if (meta.tactics.length > 0) tags["tactics"] = meta.tactics;
@@ -12144,21 +13088,6 @@ async function convertMsftDefenderCloudToHdf(input, converterVersion = "1.0.0")
12144
13088
  //#endregion
12145
13089
  //#region converters/msft-defender-endpoint-to-hdf/typescript/converter.ts
12146
13090
  /**
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
13091
  * An MDE alert is a detection that fired, so every alert is a raw Failed result.
12163
13092
  * Consumer triage (falsePositive, expected-activity) never flips the raw status —
12164
13093
  * it rides in a structured Status_Override that carries effectiveStatus + full
@@ -12262,7 +13191,7 @@ function formatMessage(alert) {
12262
13191
  const parts = [];
12263
13192
  parts.push(`Alert: ${alert.title}`);
12264
13193
  parts.push(`Status: ${alert.status}`);
12265
- parts.push(`Severity: ${alert.severity}`);
13194
+ parts.push(`Severity: ${alert.severity ?? ""}`);
12266
13195
  if (alert.threatDisplayName) parts.push(`Threat: ${alert.threatDisplayName}`);
12267
13196
  if (alert.alertWebUrl) parts.push(`URL: ${alert.alertWebUrl}`);
12268
13197
  return parts.join("\n");
@@ -12357,7 +13286,7 @@ function deriveScanTimestamp(alerts) {
12357
13286
  * Converts a single MDE alert to an HDF EvaluatedRequirement.
12358
13287
  */
12359
13288
  function alertToRequirement(alert, scanTime) {
12360
- const impact = severityToImpact$2(alert.severity);
13289
+ const impact = severityToImpact(alert.severity);
12361
13290
  const codeDesc = formatEvidence$1(alert.evidence ?? []);
12362
13291
  const message = formatMessage(alert);
12363
13292
  const startTime = resolveStartTime(alert, scanTime);
@@ -12644,7 +13573,9 @@ function buildTestResultObj(hdfData, baseline) {
12644
13573
  for (const req of baseline.requirements) {
12645
13574
  const ruleIdRef = sanitizeXccdfId("xccdf_hdf_rule_" + req.id + "_rule");
12646
13575
  const stigId = tagString(req.tags, "stig_id");
12647
- const effective = req.effectiveStatus ? hdfStatusToXccdf(req.effectiveStatus) : void 0;
13576
+ const statusInput = requirementStatusInput(req);
13577
+ const resolved = computeEffectiveStatus(statusInput);
13578
+ const effective = resolved !== worstStatus(statusInput.resultStatuses ?? []) ? hdfStatusToXccdf(resolved) : void 0;
12648
13579
  for (const result of req.results) {
12649
13580
  const status = effective ?? hdfStatusToXccdf(result.status);
12650
13581
  if (status === "pass") {
@@ -12829,11 +13760,69 @@ function toKebabCase(title, fallback) {
12829
13760
  if (!title) return fallback;
12830
13761
  let name = title.toLowerCase();
12831
13762
  name = name.replace(/[^a-z0-9]/g, "-");
12832
- while (name.includes("--")) name = name.replace(/--/g, "-");
12833
- name = name.replace(/^-+|-+$/g, "");
13763
+ name = name.replace(/-+/g, "-");
13764
+ let start = 0;
13765
+ while (start < name.length && name[start] === "-") start++;
13766
+ let end = name.length;
13767
+ while (end > start && name[end - 1] === "-") end--;
13768
+ name = name.slice(start, end);
12834
13769
  if (name.length > 80) name = name.slice(0, 80);
12835
13770
  return name;
12836
13771
  }
13772
+ /**
13773
+ * Encode an arbitrary identifier into OSCAL's TokenDatatype shape:
13774
+ * `^(\p{L}|_)(\p{L}|\p{N}|[.\-_])*$`
13775
+ *
13776
+ * HDF requirement ids come from whatever the source tool numbers its rules with,
13777
+ * and only some of those shapes are already tokens. Measured across this
13778
+ * package's converter fixtures, 46% are not (57% of the distinct ids): package-style ids
13779
+ * carrying '/', CIS control numbers starting with a digit, advisory ids carrying
13780
+ * ':'. Copying one of those into a token-typed OSCAL field produces a document
13781
+ * the target schema rejects while the converter resolves successfully.
13782
+ *
13783
+ * The kept set is deliberately ASCII — [A-Za-z0-9._-] — rather than the wider
13784
+ * \p{L}/\p{N} the pattern permits. Delegating to each platform's Unicode tables
13785
+ * looked equivalent and was not: V8's \p{L}/\p{N} and Go's unicode package are
13786
+ * built from different Unicode versions, and comparing the two implementations
13787
+ * across the whole code-point range turned up 4657 characters they disagree on.
13788
+ * An explicit ASCII set has no such dependency and is identical in both languages
13789
+ * by construction. It costs nothing on real data: no requirement id in this
13790
+ * package's converter fixtures is non-ASCII, and a non-ASCII one
13791
+ * is preserved in full by the caller's source-id prop regardless.
13792
+ *
13793
+ * Every character outside that set becomes '_', and a leading '_' is prepended
13794
+ * when the result would not start with a letter or '_'. An id built only from
13795
+ * the kept characters is returned unchanged; note that this is narrower than
13796
+ * token shape, so a token-valid non-ASCII id such as "café" is still rewritten.
13797
+ *
13798
+ * Two different ids can encode to the same token ('a/b' and 'a:b' both yield
13799
+ * 'a_b'), which is why callers must also record the source id in the emitted
13800
+ * document — for SAR that is a prop on the finding, trimmed because OSCAL's
13801
+ * StringDatatype forbids a padded value.
13802
+ */
13803
+ function oscalToken(s) {
13804
+ if (s === "") return "";
13805
+ const out = [...s].map((ch) => /[A-Za-z0-9.\-_]/.test(ch) ? ch : "_").join("");
13806
+ const [first = ""] = [...out];
13807
+ return /[A-Za-z_]/.test(first) ? out : `_${out}`;
13808
+ }
13809
+ /**
13810
+ * Render a value for a field OSCAL types as StringDatatype, whose pattern
13811
+ * `^\S(.*\S)?$` forbids an empty value and any leading or trailing whitespace.
13812
+ * HDF constrains none of the strings that feed those fields — no minLength
13813
+ * anywhere in hdf-amendments — so a padded or empty value is valid HDF that
13814
+ * would otherwise produce a document the target schema rejects at exit 0.
13815
+ *
13816
+ * Returns '' when nothing survives trimming, which callers treat as "omit this
13817
+ * field" rather than substituting a placeholder: OSCAL marks most of these
13818
+ * fields optional, so leaving one out says exactly as much as the source did.
13819
+ * The companion for token-typed fields is oscalToken.
13820
+ *
13821
+ * Mirrors OSCALString in the Go peer.
13822
+ */
13823
+ function oscalString(s) {
13824
+ return s.trim();
13825
+ }
12837
13826
  //#endregion
12838
13827
  //#region converters/hdf-to-oscal-sar/typescript/converter.ts
12839
13828
  /**
@@ -12896,12 +13885,18 @@ function buildOSCALDocument(hdfResults) {
12896
13885
  else importAP = { href: "#" };
12897
13886
  const subjects = buildSubjects(hdfResults.components);
12898
13887
  const results = [];
12899
- for (const baseline of hdfResults.baselines) results.push(baselineToResult(baseline, timestamp, toolActorUuid, subjects));
13888
+ const resources = [];
13889
+ for (const baseline of hdfResults.baselines) {
13890
+ const { result, resources: baselineResources } = baselineToResult(baseline, timestamp, toolActorUuid, subjects);
13891
+ results.push(result);
13892
+ resources.push(...baselineResources);
13893
+ }
12900
13894
  return { "assessment-results": {
12901
13895
  uuid: crypto.randomUUID(),
12902
13896
  metadata,
12903
13897
  "import-ap": importAP,
12904
- results
13898
+ results,
13899
+ ...resources.length > 0 ? { "back-matter": { resources } } : {}
12905
13900
  } };
12906
13901
  }
12907
13902
  /** Earliest startTime across the results, or undefined if none carry one. */
@@ -12933,7 +13928,8 @@ function assessmentStart(baseline, fallback) {
12933
13928
  return formatAssessmentTime(earliest, fallback);
12934
13929
  }
12935
13930
  /**
12936
- * Converts a single EvaluatedBaseline to an OSCAL Result.
13931
+ * Converts a single EvaluatedBaseline to an OSCAL Result plus any back-matter
13932
+ * resources (embedded check source code) its findings link to.
12937
13933
  */
12938
13934
  function baselineToResult(baseline, timestamp, toolActorUuid, subjects) {
12939
13935
  let title = baseline.name;
@@ -12948,14 +13944,18 @@ function baselineToResult(baseline, timestamp, toolActorUuid, subjects) {
12948
13944
  const findings = [];
12949
13945
  const observations = [];
12950
13946
  const risks = [];
13947
+ const resources = [];
12951
13948
  const includeControls = [];
12952
13949
  const seenControl = /* @__PURE__ */ new Set();
12953
13950
  for (const req of baseline.requirements) {
12954
- const { finding, observation, risk } = requirementToFindingSet(req, timestamp, toolActorUuid, subjects);
13951
+ const nistId = nistTagToControlId(req.id);
13952
+ if (nistId === "") continue;
13953
+ const { finding, observation, risk, resource } = requirementToFindingSet(req, timestamp, toolActorUuid, subjects);
12955
13954
  findings.push(finding);
12956
13955
  if (observation) observations.push(observation);
12957
13956
  if (risk) risks.push(risk);
12958
- const cid = nistTagToControlId(req.id);
13957
+ if (resource) resources.push(resource);
13958
+ const cid = oscalToken(nistId);
12959
13959
  if (cid !== "" && !seenControl.has(cid)) {
12960
13960
  seenControl.add(cid);
12961
13961
  includeControls.push({ "control-id": cid });
@@ -12963,15 +13963,18 @@ function baselineToResult(baseline, timestamp, toolActorUuid, subjects) {
12963
13963
  }
12964
13964
  const controlSelection = includeControls.length > 0 ? { "include-controls": includeControls } : {};
12965
13965
  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
13966
+ result: {
13967
+ uuid: crypto.randomUUID(),
13968
+ title,
13969
+ description,
13970
+ start: assessmentStart(baseline, timestamp),
13971
+ ...resultProps.length > 0 ? { props: resultProps } : {},
13972
+ "reviewed-controls": { "control-selections": [controlSelection] },
13973
+ findings,
13974
+ observations,
13975
+ risks
13976
+ },
13977
+ resources
12975
13978
  };
12976
13979
  }
12977
13980
  /**
@@ -12992,18 +13995,35 @@ function buildSubjects(components) {
12992
13995
  * Converts an EvaluatedRequirement into a Finding, optional Observation, and optional Risk.
12993
13996
  */
12994
13997
  function requirementToFindingSet(req, timestamp, toolActorUuid, subjects) {
12995
- const controlID = nistTagToControlId(req.id);
13998
+ const controlID = oscalToken(nistTagToControlId(req.id));
12996
13999
  const results = req.results ?? [];
12997
14000
  const descriptions = req.descriptions ?? [];
12998
- const { state, reason } = effectiveState(req, results);
14001
+ const { state, reason } = effectiveState(req);
12999
14002
  const findingDesc = extractDefaultDescription(descriptions);
13000
- const props = [];
14003
+ const props = [{
14004
+ name: "hdf-requirement-id",
14005
+ value: oscalString(req.id)
14006
+ }];
13001
14007
  const addProp = (name, value) => {
13002
14008
  if (value !== "") props.push({
13003
14009
  name,
13004
14010
  value
13005
14011
  });
13006
14012
  };
14013
+ const addProseProp = (name, text) => {
14014
+ const preview = previewLine(text);
14015
+ if (preview === "") return;
14016
+ const p = {
14017
+ name,
14018
+ value: preview
14019
+ };
14020
+ if (preview !== text) p.remarks = text;
14021
+ props.push(p);
14022
+ };
14023
+ const descriptionByLabel = (label) => {
14024
+ const d = descriptions.find((x) => x.label === label);
14025
+ return d ? d.data : "";
14026
+ };
13007
14027
  const pushTagValues = (key) => {
13008
14028
  const raw = req.tags?.[key];
13009
14029
  if (Array.isArray(raw)) {
@@ -13012,15 +14032,9 @@ function requirementToFindingSet(req, timestamp, toolActorUuid, subjects) {
13012
14032
  };
13013
14033
  pushTagValues("nist");
13014
14034
  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
- }
14035
+ addProseProp("check", descriptionByLabel("check"));
14036
+ addProseProp("rationale", descriptionByLabel("rationale"));
14037
+ if (req.impact <= 0) addProseProp("fix", descriptionByLabel("fix"));
13024
14038
  if (req.controlType) addProp("control-type", req.controlType);
13025
14039
  if (req.verificationMethod) addProp("verification-method", req.verificationMethod);
13026
14040
  if (req.applicability) addProp("applicability", req.applicability);
@@ -13048,10 +14062,7 @@ function requirementToFindingSet(req, timestamp, toolActorUuid, subjects) {
13048
14062
  href: o.uri,
13049
14063
  rel: "reference"
13050
14064
  });
13051
- else if (typeof o.ref === "string") props.push({
13052
- name: "reference",
13053
- value: o.ref
13054
- });
14065
+ else if (typeof o.ref === "string") addProseProp("reference", o.ref);
13055
14066
  }
13056
14067
  for (const er of req.externalReferences ?? []) if (typeof er.href === "string" && er.href !== "") links.push({
13057
14068
  href: er.href,
@@ -13059,6 +14070,22 @@ function requirementToFindingSet(req, timestamp, toolActorUuid, subjects) {
13059
14070
  });
13060
14071
  let title = req.id;
13061
14072
  if (req.title && req.title !== "") title = req.title;
14073
+ let resource;
14074
+ if (req.code != null && req.code.trim() !== "") {
14075
+ const resourceUuid = crypto.randomUUID();
14076
+ resource = {
14077
+ uuid: resourceUuid,
14078
+ title: `Check source code for ${req.id}`,
14079
+ base64: {
14080
+ value: Buffer.from(req.code, "utf-8").toString("base64"),
14081
+ "media-type": "text/plain"
14082
+ }
14083
+ };
14084
+ links.push({
14085
+ href: `#${resourceUuid}`,
14086
+ rel: "code"
14087
+ });
14088
+ }
13062
14089
  const targetStatus = { state };
13063
14090
  if (reason) targetStatus.reason = reason;
13064
14091
  const remarks = overrideRemarks(req);
@@ -13127,16 +14154,32 @@ function requirementToFindingSet(req, timestamp, toolActorUuid, subjects) {
13127
14154
  return {
13128
14155
  finding,
13129
14156
  observation,
13130
- risk
14157
+ risk,
14158
+ resource
13131
14159
  };
13132
14160
  }
13133
14161
  /**
13134
- * Derives the OSCAL finding target state/reason from the requirement's
13135
- * effectiveStatus when present, otherwise from aggregated raw results.
14162
+ * Reduces prose to a single line legal as an OSCAL StringDatatype prop value:
14163
+ * the first non-empty line, trimmed, truncated to 120 code points.
14164
+ */
14165
+ function previewLine(text) {
14166
+ for (let line of text.split("\n")) {
14167
+ line = line.trim();
14168
+ if (line === "") continue;
14169
+ const maxRunes = 120;
14170
+ const runes = Array.from(line);
14171
+ if (runes.length > maxRunes) line = `${runes.slice(0, maxRunes - 3).join("").trim()}...`;
14172
+ return line;
14173
+ }
14174
+ return "";
14175
+ }
14176
+ /**
14177
+ * Derives the OSCAL finding target state/reason from the canonical ladder —
14178
+ * the stored effectiveStatus field is never read (it is an output cache; see
14179
+ * status-determination.md).
13136
14180
  */
13137
- function effectiveState(req, results) {
13138
- if (req.effectiveStatus) return oscalStateFromStatus(req.effectiveStatus);
13139
- return aggregateStatus(results);
14181
+ function effectiveState(req) {
14182
+ return oscalStateFromStatus(requirementEffectiveStatus(req));
13140
14183
  }
13141
14184
  /** Maps a single HDF result status to an OSCAL finding target state/reason. */
13142
14185
  function oscalStateFromStatus(status) {
@@ -13268,62 +14311,6 @@ function formatFloat(n) {
13268
14311
  return String(n);
13269
14312
  }
13270
14313
  /**
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
14314
  * Finds the "default" labeled description.
13328
14315
  */
13329
14316
  function extractDefaultDescription(descriptions) {
@@ -14522,7 +15509,11 @@ function advisoryURI(publisher, tracking) {
14522
15509
  /* c8 ignore next */
14523
15510
  const id = tracking.id ?? "";
14524
15511
  const ns = publisher.namespace;
14525
- if (ns && id) return `${ns.replace(/\/+$/, "")}/${id}`;
15512
+ if (ns && id) {
15513
+ let end = ns.length;
15514
+ while (end > 0 && ns[end - 1] === "/") end--;
15515
+ return `${ns.slice(0, end)}/${id}`;
15516
+ }
14526
15517
  return id;
14527
15518
  }
14528
15519
  function overlap(ids, scope) {
@@ -17017,6 +18008,6 @@ function capStixExternalRefs(container, label) {
17017
18008
  container.externalReferences = [...other, ...capped];
17018
18009
  }
17019
18010
  //#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 };
18011
+ 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
18012
 
17022
18013
  //# sourceMappingURL=index.js.map