@mitre/hdf-converters 3.7.1 → 3.7.2-rc.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,10 +1,11 @@
1
- import { n as detectFormat, o as isCheckovTestCaseName, r as detectConverter, t as registerAllFingerprints } from "./register-all-BEuKQGEZ.js";
1
+ import { n as detectFormat, o as isCheckovTestCaseName, r as detectConverter, t as registerAllFingerprints } from "./register-all-BWm_HvZf.js";
2
2
  import { flattenOverlays } from "@mitre/hdf-parsers";
3
- import { buildCsv, buildXml, canonicalJson, computeCvssScore, computeEffectiveStatus, cvssScoreToSeverity, encodeBase64Utf8, formatJsonNumber, formatTimestamp, formatTimestampSeconds, governingStatusOverride, impactToSeverity, isUnratedSeverity, normalizeHdfTimestamps, parseCsv, parseJSON, parsePurl, parseTimestamp, parseXml, parseXmlWithArrays, replaceHtmlTags, roundImpact, severityToImpact, severityToImpactWithAliases, sha256, stripHtml, stripHtml as stripHTML, trimUtcFraction, validateInputSize, worstStatus, xmlSafeText } from "@mitre/hdf-utilities";
3
+ import { CWE_PATTERN as CWE_PATTERN$1, absentIfGoZeroTime, buildCsv, buildXml, canonicalJson, computeCvssScore, computeEffectiveImpact, computeEffectiveStatus, cvssScoreToSeverity, encodeBase64Utf8, extractCWEIDs, formatJsonNumber, formatTimestamp, formatTimestampSeconds, governingImpactOverrideIndex, governingOverrideIndex, governingStatusOverride, governingStatusOverrideIndex, impactToSeverity, isGoZeroTime, isUnratedSeverity, normalizeHdfTimestamps, parseCsv, parseJSON, parsePurl, parseTimestamp, parseXml, parseXmlWithArrays, replaceHtmlTags, roundImpact, severityToImpact, severityToImpactWithAliases, sha256, stripHtml, stripHtml as stripHTML, trimUtcFraction, validateInputSize, worstStatus, xmlSafeText } from "@mitre/hdf-utilities";
4
4
  import { Applicability, AuthorizationStatus, CVSSSeverity, CategorizationLevel, CloudProvider, ControlType, Ecosystem, EvidenceType, HashAlgorithm, IdentityType, Justification, MilestoneStatus, OverrideType, PlanType, ResultStatus, Severity, TargetType, VerificationMethodEnum, Version, affectedPackageSchema, amendmentsSchema, bomSchema, commonSchema, comparisonSchema, componentSchema, createDescription, createMinimalBaseline, createRequirement, createResult, cvssSchema, dataFlowSchema, epssSchema, eventsSchema, extensionsSchema, hdfAmendmentsSchema, hdfBaselineSchema, hdfComparisonSchema, hdfEvidencePackageSchema, hdfPlanSchema, hdfRequirementChangeEventSchema, hdfResultsSchema, hdfSystemSchema, kevSchema, parameterSchema, planSchema, platformSchema, resultSchema, runnerSchema, severityToImpact as severityToImpact$1, statisticsSchema, systemSchema, targetSchema } from "@mitre/hdf-schema";
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, SUPPORTED_NIST_REVISIONS, awsConfigMappedRevisions, getAwsConfigNistControlByIdentifier, getAwsConfigNistControlByName, getAwsConfigNistControlsBySubstring, getCCINistMappings, getCheckovCciNistMapping, getCurrentNistRevision, getCweNistControl, getHipcheckNistControls, getNessusNistControl, getNiktoNistControl, getOwaspNistControl, getScoutsuiteNistControl, isNistStrict, nistExists, nistToCci, normalizeNistId } from "@mitre/hdf-mappings";
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, SUPPORTED_NIST_REVISIONS, awsConfigMappedRevisions, getAwsConfigNistControlByIdentifier, getAwsConfigNistControlByName, getAwsConfigNistControlsBySubstring, getCCINistMappings, getCheckovCciNistMapping, getCurrentNistRevision, getCweNistControl, getHadolintNistMapping, getHipcheckNistControls, getNessusNistControl, getNiktoNistControl, getOwaspNistControl, getScoutsuiteNistControl, isNistStrict, nistExists, nistToCci, normalizeNistId } from "@mitre/hdf-mappings";
6
6
  import Ajv from "ajv";
7
7
  import addFormats from "ajv-formats";
8
+ import { addCounts, calculateCompliance, countControlsByStatus, deriveSeverity, severityBucket, severityTotals } from "@mitre/hdf-engine";
8
9
  //#region ../hdf-validators/dist/index.js
9
10
  /**
10
11
  * Create and configure Ajv instance with all HDF schemas
@@ -145,6 +146,20 @@ async function chainOverrides(overrides) {
145
146
  }
146
147
  }
147
148
  //#endregion
149
+ //#region shared/typescript/unrated.ts
150
+ /**
151
+ * The tag a roll-up leaves on a requirement whose severity the source never
152
+ * rated, and the value it carries. A leaf module: converterutil.ts re-exports
153
+ * these as part of its surface, and rollup.ts imports them here rather than
154
+ * from the barrel that re-exports rollup itself — which is the cycle a second
155
+ * declaration used to work around.
156
+ */
157
+ const UNRATED_SEVERITY_TAG = "severity_rating";
158
+ const UNRATED_SEVERITY_VALUE = "unrated";
159
+ function markUnratedSeverity(tags, severity) {
160
+ if (isUnratedSeverity(severity)) tags[UNRATED_SEVERITY_TAG] = UNRATED_SEVERITY_VALUE;
161
+ }
162
+ //#endregion
148
163
  //#region shared/typescript/converterutil.ts
149
164
  /**
150
165
  * Shared utilities for TypeScript HDF converters.
@@ -154,6 +169,18 @@ async function chainOverrides(overrides) {
154
169
  * - Re-exports of shared constants and utilities
155
170
  */
156
171
  /**
172
+ * The identity a finding's affected package carries as its resourceId: the purl
173
+ * when the source gives one, else name@version, else the bare name, else ''.
174
+ * One rule for every converter that stamps a package, so a downstream join on
175
+ * resourceId meets the same string whichever tool produced it.
176
+ * Parity: PackageInstanceID in shared/go/converterutil.go.
177
+ */
178
+ function packageInstanceId$2(purl, name, version) {
179
+ if (purl) return purl;
180
+ if (!name) return "";
181
+ return version ? `${name}@${version}` : name;
182
+ }
183
+ /**
157
184
  * Compute a SHA-256 checksum of raw converter input.
158
185
  *
159
186
  * Wraps the common 3-line pattern used in every converter into a single call.
@@ -294,22 +321,6 @@ function mapCWEToNIST(cweIDs, fallback) {
294
321
  }
295
322
  return controls.size > 0 ? [...controls].sort() : fallback;
296
323
  }
297
- /** Matches CWE identifiers like "CWE-79", "CWE 89", "cwe22"; group 1 is the number. */
298
- const CWE_PATTERN$1 = /CWE[- ]?(\d+)/gi;
299
- /**
300
- * Extract all numeric CWE IDs from text.
301
- * Returns deduplicated sorted array of numeric ID strings (e.g., ["79", "89"]).
302
- *
303
- * @param text - Text potentially containing CWE identifiers
304
- * @returns Sorted, deduplicated numeric CWE ID strings
305
- */
306
- function extractCWEIDs(text) {
307
- const matches = [...text.matchAll(CWE_PATTERN$1)];
308
- if (matches.length === 0) return [];
309
- const ids = [...new Set(matches.map((m) => m[1]))];
310
- ids.sort();
311
- return ids;
312
- }
313
324
  /**
314
325
  * Return the first candidate whose content is not empty or whitespace-only,
315
326
  * as-is (the emptiness test trims, the returned value does not). Returns '' when
@@ -714,16 +725,6 @@ function buildNoFindingsRequirement(id, codeDesc, startTime) {
714
725
  };
715
726
  }
716
727
  /**
717
- * Shared marker converters emit when a source severity is unrated
718
- * (isUnratedSeverity), so a defaulted impact stays distinguishable from a
719
- * genuine rated medium. TS peer of MarkUnratedSeverity in converterutil.go.
720
- */
721
- const UNRATED_SEVERITY_TAG = "severity_rating";
722
- const UNRATED_SEVERITY_VALUE = "unrated";
723
- function markUnratedSeverity(tags, severity) {
724
- if (isUnratedSeverity(severity)) tags[UNRATED_SEVERITY_TAG] = UNRATED_SEVERITY_VALUE;
725
- }
726
- /**
727
728
  * The schema violations Go's typed decode would also reject.
728
729
  *
729
730
  * encoding/json refuses exactly two things: a JSON value whose type does not fit
@@ -1207,7 +1208,7 @@ function cvssSeverityFromScore(score) {
1207
1208
  * number is supplied; baseVector and source are set only when non-empty. Consumers
1208
1209
  * needing threat/environmental/computed enrichment set those fields on the result.
1209
1210
  */
1210
- function buildCvss$3(input) {
1211
+ function buildCvss$4(input) {
1211
1212
  const cv = { version: input.version };
1212
1213
  if (typeof input.baseScore === "number" && Number.isFinite(input.baseScore)) {
1213
1214
  cv.baseScore = input.baseScore;
@@ -1467,7 +1468,7 @@ function vulnCvss(f) {
1467
1468
  const out = [];
1468
1469
  for (const v of f.Vulnerabilities ?? []) for (const c of v.Cvss ?? []) {
1469
1470
  if (typeof c.BaseScore !== "number") continue;
1470
- out.push(buildCvss$3({
1471
+ out.push(buildCvss$4({
1471
1472
  version: cvssVersionFromString(c.Version),
1472
1473
  baseScore: c.BaseScore,
1473
1474
  source: c.Source
@@ -1526,7 +1527,7 @@ function trivyLocation(o) {
1526
1527
  if (el && el !== "0" && el !== sl) loc += `-${el}`;
1527
1528
  return loc;
1528
1529
  }
1529
- function buildRequirement$19(id, group) {
1530
+ function buildRequirement$20(id, group) {
1530
1531
  const primary = group[0];
1531
1532
  const impact = Math.max(...group.map(findingImpact));
1532
1533
  const descriptions = [{
@@ -1573,7 +1574,7 @@ function buildBaseline$1(name, findings, resultsChecksum) {
1573
1574
  groups.set(id, [f]);
1574
1575
  }
1575
1576
  }
1576
- const requirements = order.map((id) => buildRequirement$19(id, groups.get(id)));
1577
+ const requirements = order.map((id) => buildRequirement$20(id, groups.get(id)));
1577
1578
  return createMinimalBaseline(name, requirements, { resultsChecksum });
1578
1579
  }
1579
1580
  /** Converts an ASFF document to HDF Results JSON. */
@@ -1621,6 +1622,112 @@ async function convertAsffToHdf(input, converterVersion = "1.0.0") {
1621
1622
  return serializeHdf(hdf);
1622
1623
  }
1623
1624
  //#endregion
1625
+ //#region shared/typescript/status.ts
1626
+ /**
1627
+ * Bridge from schema-typed requirements onto the canonical effective-status
1628
+ * helper in @mitre/hdf-utilities, so every consumer computes status through
1629
+ * the single shared implementation (twin of shared/go/status.go). The stored
1630
+ * effectiveStatus field is never read — it is an output cache (see
1631
+ * status-determination.md).
1632
+ */
1633
+ /**
1634
+ * RFC3339 string from a schema timestamp (quicktype Date or raw string), with
1635
+ * Go's zero time read as absent.
1636
+ */
1637
+ function schemaTimestamp(v) {
1638
+ if (v instanceof Date) return absentIfGoZeroTime(v.toISOString());
1639
+ if (typeof v === "string" && v !== "") return absentIfGoZeroTime(v);
1640
+ }
1641
+ /** Maps a requirement onto the canonical effective-status input shape. */
1642
+ function requirementStatusInput(req) {
1643
+ return {
1644
+ impact: req.impact ?? 0,
1645
+ resultStatuses: (req.results ?? []).filter((r) => r != null).map((r) => String(r.status)),
1646
+ overrides: (req.statusOverrides ?? []).filter((o) => o != null).map((o) => ({
1647
+ status: o.status ? String(o.status) : void 0,
1648
+ appliedAt: schemaTimestamp(o.appliedAt),
1649
+ expiresAt: schemaTimestamp(o.expiresAt),
1650
+ impact: o.impact?.value
1651
+ }))
1652
+ };
1653
+ }
1654
+ /** The requirement's canonical effective status via the shared ladder. */
1655
+ function requirementEffectiveStatus(req) {
1656
+ return computeEffectiveStatus(requirementStatusInput(req));
1657
+ }
1658
+ /**
1659
+ * The requirement's canonical effective impact via the shared ladder: the
1660
+ * governing non-expired impact override's value, else the requirement's own. The
1661
+ * stored effectiveImpact field is an output cache and is never read, exactly as
1662
+ * effectiveStatus is not. Expiry is judged at now, else the clock, for a
1663
+ * renderer that promises assessment-time output. Parity: RequirementEffectiveImpact
1664
+ * and RequirementEffectiveImpactAt in shared/go.
1665
+ */
1666
+ function requirementEffectiveImpact(req, now) {
1667
+ return computeEffectiveImpact(requirementStatusInput(req), now);
1668
+ }
1669
+ /**
1670
+ * The override that governs a requirement — the most recently applied
1671
+ * non-expired one, whatever it carries — or undefined when none does.
1672
+ *
1673
+ * Resolution is by appliedAt, never by array position. The schema's description
1674
+ * says the most recent override "should be first in array", but nothing in this
1675
+ * repo sorts and both writers append, so on a document our own tooling amended
1676
+ * twice the newest override is LAST and statusOverrides[0] is the oldest.
1677
+ *
1678
+ * Parity: GoverningOverride in shared/go/status.go.
1679
+ */
1680
+ function governingOverride(req, now) {
1681
+ const overrides = req.statusOverrides ?? [];
1682
+ const i = governingOverrideIndex(overrides.map((o) => ({
1683
+ status: o?.status ? String(o.status) : void 0,
1684
+ appliedAt: schemaTimestamp(o?.appliedAt),
1685
+ expiresAt: schemaTimestamp(o?.expiresAt)
1686
+ })), () => true, now);
1687
+ return i >= 0 ? overrides[i] ?? void 0 : void 0;
1688
+ }
1689
+ /**
1690
+ * The override that governs a requirement's IMPACT — the most recently applied
1691
+ * non-expired one CARRYING an impact — or undefined when none does. Eligibility
1692
+ * is per field, so a newer override that says nothing about impact does not
1693
+ * displace an older re-score.
1694
+ *
1695
+ * Parity: GoverningImpactOverrideIndex usage in shared/go/checklist.
1696
+ */
1697
+ function governingImpactOverride(req, now) {
1698
+ const overrides = (req.statusOverrides ?? []).filter((o) => o != null);
1699
+ const i = governingImpactOverrideIndex(overrides.map((o) => ({
1700
+ appliedAt: schemaTimestamp(o.appliedAt),
1701
+ expiresAt: schemaTimestamp(o.expiresAt),
1702
+ impact: o.impact?.value
1703
+ })), now);
1704
+ return i >= 0 ? overrides[i] : void 0;
1705
+ }
1706
+ /**
1707
+ * The type of the override that governs a requirement, or '' when none does —
1708
+ * the disposition twin of requirementEffectiveStatus and
1709
+ * requirementEffectiveImpact.
1710
+ *
1711
+ * Whenever the requirement carries overrides, they decide: the stored
1712
+ * disposition field is an output cache that can disagree with them, or be stale,
1713
+ * and it is not read. The one exception is a requirement carrying NO overrides
1714
+ * at all, where the stored field is the only evidence in the document.
1715
+ *
1716
+ * This reads statusOverrides only. hdf-engine's filter and hdf-diff's checksum
1717
+ * fold poams[] into the same governing set and report 'poam' when a plan
1718
+ * governs; an export does not, because the exporters carry the plan separately
1719
+ * and a disposition of 'poam' would duplicate it in a column that names an
1720
+ * override type. Pinned by the "does not read poams" test.
1721
+ *
1722
+ * Parity: RequirementDisposition in shared/go/status.go.
1723
+ */
1724
+ function requirementDisposition(req, now) {
1725
+ const governing = governingOverride(req, now);
1726
+ if (governing) return String(governing.type ?? "");
1727
+ if ((req.statusOverrides ?? []).length === 0 && req.disposition) return String(req.disposition);
1728
+ return "";
1729
+ }
1730
+ //#endregion
1624
1731
  //#region converters/sarif-to-hdf/typescript/converter.ts
1625
1732
  const SARIF_ALIASES = {
1626
1733
  error: .7,
@@ -1703,9 +1810,9 @@ function convertResultGroup(ruleId, rule, sarifResults, timestamp) {
1703
1810
  const sourceLocation = firstResult.locations && firstResult.locations.length > 0 ? extractSourceLocation$1(firstResult.locations[0]) : void 0;
1704
1811
  const results = [];
1705
1812
  for (const sr of sarifResults) results.push(...convertSarifResultToHDFResults(sr));
1706
- const descriptions = buildDescriptions$2(description, rule, firstResult);
1813
+ const descriptions = buildDescriptions$3(description, rule, firstResult);
1707
1814
  const allSuppressions = sarifResults.flatMap((sr) => sr.suppressions ?? []);
1708
- const options = { tags: buildTags$4(firstResult, rule, ruleLevel, cweIds, nistControls, cciControls, allSuppressions) };
1815
+ const options = { tags: buildTags$5(firstResult, rule, ruleLevel, cweIds, nistControls, cciControls, allSuppressions) };
1709
1816
  if (sourceLocation) options.sourceLocation = sourceLocation;
1710
1817
  const req = createRequirement(ruleId, title, descriptions, impact, results, options);
1711
1818
  req.severity = impactToSeverity(impact);
@@ -1923,7 +2030,7 @@ function rollupStatus(statuses) {
1923
2030
  }
1924
2031
  function governingDisposition(overrides, effective) {
1925
2032
  for (const ov of overrides) if (ov.status === effective) return ov.type;
1926
- return overrides[0].type;
2033
+ return governingOverride({ statusOverrides: overrides })?.type ?? overrides[0].type;
1927
2034
  }
1928
2035
  function extractBacktrace(codeFlows) {
1929
2036
  if (!codeFlows || codeFlows.length === 0) return [];
@@ -1939,7 +2046,7 @@ function extractBacktrace(codeFlows) {
1939
2046
  }
1940
2047
  return backtrace;
1941
2048
  }
1942
- function buildDescriptions$2(defaultDesc, rule, result) {
2049
+ function buildDescriptions$3(defaultDesc, rule, result) {
1943
2050
  const descriptions = [createDescription("default", defaultDesc)];
1944
2051
  if (rule) {
1945
2052
  if (rule.fullDescription?.text) descriptions.push(createDescription("rationale", rule.fullDescription.text));
@@ -1949,7 +2056,7 @@ function buildDescriptions$2(defaultDesc, rule, result) {
1949
2056
  if (result.fixes && result.fixes.length > 0 && result.fixes[0]?.description?.text) descriptions.push(createDescription("fix", result.fixes[0].description.text));
1950
2057
  return descriptions;
1951
2058
  }
1952
- function buildTags$4(result, rule, resolvedLevel, cweIds, nistControls, cciControls, allSuppressions) {
2059
+ function buildTags$5(result, rule, resolvedLevel, cweIds, nistControls, cciControls, allSuppressions) {
1953
2060
  const tags = {
1954
2061
  severity: resolvedLevel,
1955
2062
  cwe: cweIds,
@@ -2118,7 +2225,7 @@ function testCaseToRequirement(tc, scanTime) {
2118
2225
  const { status, message } = resolveStatus(tc);
2119
2226
  const result = {
2120
2227
  status,
2121
- codeDesc: buildCodeDesc$10(tc),
2228
+ codeDesc: buildCodeDesc$11(tc),
2122
2229
  startTime: scanTime
2123
2230
  };
2124
2231
  if (message !== void 0) result.message = message;
@@ -2206,7 +2313,7 @@ function buildFailureMessage(message, typeName, body) {
2206
2313
  if (body) result += "\n" + decodeXmlEntities$6(body);
2207
2314
  return result;
2208
2315
  }
2209
- function buildCodeDesc$10(tc) {
2316
+ function buildCodeDesc$11(tc) {
2210
2317
  if (tc["@_classname"]) return `${decodeXmlEntities$6(tc["@_classname"])} :: ${decodeXmlEntities$6(tc["@_name"])}`;
2211
2318
  return decodeXmlEntities$6(tc["@_name"]);
2212
2319
  }
@@ -2552,7 +2659,7 @@ function ruleResultToRequirement(rr, ruleIndex, groupIndex, scanTime) {
2552
2659
  const req = createRequirement(id, title, descriptions, impact, [result], { tags });
2553
2660
  const code = buildCheckCode(check);
2554
2661
  if (code) req.code = code;
2555
- const refs = buildRefs$10(ruleDef?.reference);
2662
+ const refs = buildRefs$11(ruleDef?.reference);
2556
2663
  if (refs.length > 0) req.refs = refs;
2557
2664
  const hdfSeverity = xccdfSeverityToHdf(severity);
2558
2665
  if (hdfSeverity) req.severity = hdfSeverity;
@@ -2709,7 +2816,7 @@ function checkDescriptionData(check) {
2709
2816
  * href, when present, populates url. References that carry nothing are skipped.
2710
2817
  * Inner keys are inserted alphabetically to match Go's map JSON ordering.
2711
2818
  */
2712
- function buildRefs$10(refs) {
2819
+ function buildRefs$11(refs) {
2713
2820
  if (!refs) return [];
2714
2821
  const out = [];
2715
2822
  for (const r of refs) {
@@ -3367,36 +3474,6 @@ function setIf$1(m, key, val) {
3367
3474
  if (val) m[key] = val;
3368
3475
  }
3369
3476
  //#endregion
3370
- //#region shared/typescript/status.ts
3371
- /**
3372
- * Bridge from schema-typed requirements onto the canonical effective-status
3373
- * helper in @mitre/hdf-utilities, so every consumer computes status through
3374
- * the single shared implementation (twin of shared/go/status.go). The stored
3375
- * effectiveStatus field is never read — it is an output cache (see
3376
- * status-determination.md).
3377
- */
3378
- /** RFC3339 string from a schema timestamp (quicktype Date or raw string). */
3379
- function stamp(v) {
3380
- if (v instanceof Date) return v.toISOString();
3381
- if (typeof v === "string" && v !== "") return v;
3382
- }
3383
- /** Maps a requirement onto the canonical effective-status input shape. */
3384
- function requirementStatusInput(req) {
3385
- return {
3386
- impact: req.impact ?? 0,
3387
- resultStatuses: (req.results ?? []).filter((r) => r != null).map((r) => String(r.status)),
3388
- overrides: (req.statusOverrides ?? []).filter((o) => o != null).map((o) => ({
3389
- status: o.status ? String(o.status) : void 0,
3390
- appliedAt: stamp(o.appliedAt),
3391
- expiresAt: stamp(o.expiresAt)
3392
- }))
3393
- };
3394
- }
3395
- /** The requirement's canonical effective status via the shared ladder. */
3396
- function requirementEffectiveStatus(req) {
3397
- return computeEffectiveStatus(requirementStatusInput(req));
3398
- }
3399
- //#endregion
3400
3477
  //#region shared/typescript/checklist/from-hdf.ts
3401
3478
  /**
3402
3479
  * Map HDF Results back to the format-neutral Checklist model. When the HDF
@@ -3519,7 +3596,8 @@ function composeComments(tags, req) {
3519
3596
  function overrideProvenance(req) {
3520
3597
  const overrides = req.statusOverrides ?? [];
3521
3598
  if (overrides.length > 0) return overrides.map(formatOverride).join("\n");
3522
- if (req.disposition) return `Disposition: ${req.disposition}`;
3599
+ const disposition = requirementDisposition(req);
3600
+ if (disposition) return `Disposition: ${disposition}`;
3523
3601
  return "";
3524
3602
  }
3525
3603
  function formatOverride(o) {
@@ -3527,22 +3605,23 @@ function formatOverride(o) {
3527
3605
  if (o.reason) s += `: ${o.reason}`;
3528
3606
  const meta = [];
3529
3607
  if (o.appliedBy?.identifier) meta.push(`by ${o.appliedBy.identifier}`);
3530
- const applied = hdfTime(o.appliedAt);
3608
+ const applied = hdfTime(schemaTimestamp(o.appliedAt));
3531
3609
  if (applied) meta.push(`applied ${formatTimestamp(applied)}`);
3532
- const expires = hdfTime(o.expiresAt);
3610
+ const expires = hdfTime(schemaTimestamp(o.expiresAt));
3533
3611
  if (expires) meta.push(`expires ${formatTimestamp(expires)}`);
3534
3612
  if (meta.length > 0) s += ` (${meta.join(", ")})`;
3535
3613
  return s;
3536
3614
  }
3537
3615
  function overrideSeverity(req) {
3538
- for (const o of req.statusOverrides ?? []) if (o.impact) return {
3539
- severity: cklSeverityOrFloor(o.impact.value),
3540
- justification: o.reason ?? ""
3541
- };
3542
- return {
3616
+ const o = governingImpactOverride(req);
3617
+ if (!o) return {
3543
3618
  severity: "",
3544
3619
  justification: ""
3545
3620
  };
3621
+ return {
3622
+ severity: cklSeverityOrFloor(o.impact.value),
3623
+ justification: o.reason ?? ""
3624
+ };
3546
3625
  }
3547
3626
  function cklSeverityFromImpact(impact) {
3548
3627
  switch (impactToSeverity(impact)) {
@@ -3703,7 +3782,7 @@ const NIST_MAPPING_TAG = "nistMapping";
3703
3782
  const NIST_MAPPING_TABLE = "mapped";
3704
3783
  const NIST_MAPPING_CWE = "cwe-derived";
3705
3784
  const NIST_MAPPING_FALLBACK = "static-fallback";
3706
- function impactFor$1(severity) {
3785
+ function impactFor$2(severity) {
3707
3786
  return severityToImpactWithAliases(severity, KICS_SEVERITY_ALIASES, DEFAULT_IMPACT$1);
3708
3787
  }
3709
3788
  /** KICS emits the bare number, e.g. "778". Normalize before the lookup. */
@@ -3826,7 +3905,7 @@ function buildCoverageRequirement$1(report, startTime) {
3826
3905
  }
3827
3906
  };
3828
3907
  }
3829
- function buildRequirement$18(query, startTime) {
3908
+ function buildRequirement$19(query, startTime) {
3830
3909
  const tags = tagsFor$1(query);
3831
3910
  const descriptions = [{
3832
3911
  label: "default",
@@ -3838,7 +3917,7 @@ function buildRequirement$18(query, startTime) {
3838
3917
  message: evidenceFor(file),
3839
3918
  startTime
3840
3919
  }));
3841
- 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 });
3920
+ const requirement = createRequirement(query.query_id || query.query_name || "unknown", query.query_name || query.query_id || "Unnamed KICS query", descriptions, impactFor$2(query.severity), results, { tags });
3842
3921
  requirement.verificationMethod = VerificationMethodEnum.Automated;
3843
3922
  const controlType = deriveControlTypeFromTags(tags.nist);
3844
3923
  if (controlType !== void 0) requirement.controlType = controlType;
@@ -3861,7 +3940,7 @@ async function convertKicsToHdf(input, converterVersion = "1.0.0") {
3861
3940
  const report = parseJSON(input);
3862
3941
  if (!Array.isArray(report.queries) || typeof report.kics_version !== "string") throw new Error("kics: input does not look like a KICS report");
3863
3942
  const startTime = /* @__PURE__ */ new Date();
3864
- const requirements = report.queries.filter((q) => Array.isArray(q.files) && q.files.length > 0).map((q) => buildRequirement$18(q, startTime));
3943
+ const requirements = report.queries.filter((q) => Array.isArray(q.files) && q.files.length > 0).map((q) => buildRequirement$19(q, startTime));
3865
3944
  if (requirements.length === 0) requirements.push(buildNoFindingsRequirement("kics-no-findings", `KICS scanned ${report.files_scanned ?? 0} file(s) and reported no findings.`, startTime));
3866
3945
  requirements.push(buildCoverageRequirement$1(report, startTime));
3867
3946
  const baseline = createMinimalBaseline("KICS Scan", requirements, {
@@ -3937,7 +4016,7 @@ function nistControlsFor(metadata) {
3937
4016
  const deduped = [...new Set(controls)];
3938
4017
  return deduped.length > 0 ? deduped : [...DEFAULT_STATIC_ANALYSIS_NIST_TAGS$1];
3939
4018
  }
3940
- function impactFor(result) {
4019
+ function impactFor$1(result) {
3941
4020
  const severity = result.extra?.severity;
3942
4021
  if (!isPresent(severity)) return DEFAULT_IMPACT;
3943
4022
  return severityToImpactWithAliases(severity, SEMGREP_SEVERITY_ALIASES, DEFAULT_IMPACT);
@@ -3949,7 +4028,7 @@ function impactFor(result) {
3949
4028
  */
3950
4029
  function severityFor(result) {
3951
4030
  if (!isPresent(result.extra?.severity)) return;
3952
- return impactToSeverity(impactFor(result));
4031
+ return impactToSeverity(impactFor$1(result));
3953
4032
  }
3954
4033
  /**
3955
4034
  * Semgrep rule ids are dotted paths whose final segment is the rule name. The
@@ -3962,7 +4041,7 @@ function titleFor(checkId) {
3962
4041
  const segments = checkId.split(".").filter((segment) => segment.length > 0);
3963
4042
  return (segments[segments.length - 1] ?? checkId).split(/[-_]+/).filter((word) => word.length > 0).map((word) => word[0].toUpperCase() + word.slice(1)).join(" ");
3964
4043
  }
3965
- function codeDescFor(result) {
4044
+ function codeDescFor$1(result) {
3966
4045
  const path = isPresent(result.path) ? result.path : "unknown";
3967
4046
  const startLine = lineOf(result.start);
3968
4047
  const endLine = lineOf(result.end);
@@ -4063,7 +4142,7 @@ function codeFor(result) {
4063
4142
  return JSON.stringify(envelope, null, 2);
4064
4143
  }
4065
4144
  /** Every occurrence of one rule becomes a result under a single requirement. */
4066
- function buildRequirement$17(checkId, results, startTime) {
4145
+ function buildRequirement$18(checkId, results, startTime) {
4067
4146
  const representative = results[0];
4068
4147
  const metadata = representative.extra?.metadata ?? {};
4069
4148
  const message = representative.extra?.message;
@@ -4073,7 +4152,7 @@ function buildRequirement$17(checkId, results, startTime) {
4073
4152
  }];
4074
4153
  const requirementResults = results.map((result) => ({
4075
4154
  status: ResultStatus.Failed,
4076
- codeDesc: codeDescFor(result),
4155
+ codeDesc: codeDescFor$1(result),
4077
4156
  message: messageFor(result),
4078
4157
  startTime
4079
4158
  }));
@@ -4081,7 +4160,7 @@ function buildRequirement$17(checkId, results, startTime) {
4081
4160
  const cwe = cweCatalogFor(metadata);
4082
4161
  const refs = refsFor(metadata);
4083
4162
  const sourceLocation = sourceLocationFor(representative);
4084
- const requirement = createRequirement(checkId, titleFor(checkId), descriptions, impactFor(representative), requirementResults, {
4163
+ const requirement = createRequirement(checkId, titleFor(checkId), descriptions, impactFor$1(representative), requirementResults, {
4085
4164
  tags,
4086
4165
  code: codeFor(representative),
4087
4166
  ...cwe.length > 0 ? { cwe } : {},
@@ -4182,7 +4261,7 @@ async function convertSemgrepToHdf(input, converterVersion = "1.0.0") {
4182
4261
  else groups.set(result.check_id, [result]);
4183
4262
  }
4184
4263
  const requirements = [];
4185
- for (const [checkId, results] of groups) requirements.push(buildRequirement$17(checkId, results, startTime));
4264
+ for (const [checkId, results] of groups) requirements.push(buildRequirement$18(checkId, results, startTime));
4186
4265
  if (groups.size === 0) {
4187
4266
  const scanned = scannedCount(report);
4188
4267
  requirements.push(buildNoFindingsRequirement("semgrep-no-findings", `Semgrep scanned ${scanned} file(s) and reported no findings.`, startTime));
@@ -4255,7 +4334,7 @@ function buildReferenceCode(r) {
4255
4334
  if (r.url) o.url = r.url;
4256
4335
  return o;
4257
4336
  }
4258
- function buildVulnCode$2(v) {
4337
+ function buildVulnCode$3(v) {
4259
4338
  const o = {};
4260
4339
  if (v.id) o.id = v.id;
4261
4340
  if (v.title) o.title = v.title;
@@ -4331,7 +4410,7 @@ function synthesizePurl(ecosystem, name, version) {
4331
4410
  */
4332
4411
  function buildSnykCvss(vuln) {
4333
4412
  if (!vuln.cvssScore && !vuln.CVSSv3) return [];
4334
- return [buildCvss$3({
4413
+ return [buildCvss$4({
4335
4414
  version: cvssVersionFromVector(vuln.CVSSv3),
4336
4415
  baseScore: vuln.cvssScore,
4337
4416
  baseVector: vuln.CVSSv3
@@ -4360,7 +4439,7 @@ function formatUpgradePath(path) {
4360
4439
  const steps = path.filter((e) => typeof e === "string" && e.length > 0);
4361
4440
  return steps.length > 0 ? steps.join(" > ") : void 0;
4362
4441
  }
4363
- function buildRequirement$16(vulnID, vulns, scanTime, packageManager) {
4442
+ function buildRequirement$17(vulnID, vulns, scanTime, packageManager) {
4364
4443
  const rep = vulns[0];
4365
4444
  const cweIDs = rep.identifiers.CWE ?? [];
4366
4445
  const nist = mapCWEToNIST(cweIDs, DEFAULT_STATIC_ANALYSIS_NIST_TAGS);
@@ -4392,7 +4471,7 @@ function buildRequirement$16(vulnID, vulns, scanTime, packageManager) {
4392
4471
  const controlType = deriveControlTypeFromTags(nist);
4393
4472
  if (controlType !== void 0) req.controlType = controlType;
4394
4473
  req.verificationMethod = VerificationMethodEnum.Automated;
4395
- const code = buildVulnCode$2(rep);
4474
+ const code = buildVulnCode$3(rep);
4396
4475
  if (code) req.code = code;
4397
4476
  const cvss = buildSnykCvss(rep);
4398
4477
  if (cvss.length > 0) req.cvss = cvss;
@@ -4428,7 +4507,7 @@ function convertSingleProject(report, resultsChecksum, scanTime) {
4428
4507
  else groups.set(vuln.id, [vuln]);
4429
4508
  }
4430
4509
  const requirements = [];
4431
- for (const [vulnID, vulns] of groups) requirements.push(buildRequirement$16(vulnID, vulns, scanTime, report.packageManager));
4510
+ for (const [vulnID, vulns] of groups) requirements.push(buildRequirement$17(vulnID, vulns, scanTime, report.packageManager));
4432
4511
  if (requirements.length === 0) {
4433
4512
  const target = report.projectName ?? report.path ?? "project";
4434
4513
  requirements.push(buildNoFindingsRequirement("snyk-no-findings", `Snyk scanned ${target} and reported zero vulnerable components.`, scanTime));
@@ -4528,7 +4607,18 @@ function getReferences(vuln, relatedVulns) {
4528
4607
  }
4529
4608
  return Array.from(refs);
4530
4609
  }
4531
- function buildCodeDesc$9(match) {
4610
+ /**
4611
+ * The per-result instance identity for a grype match: the matched package's
4612
+ * purl, which is what distinguishes two entries reporting the same CVE. Falls
4613
+ * back to name@version when a source omits purl (optional in grype's schema,
4614
+ * though every committed fixture carries it) — the same identity twistlock
4615
+ * uses, whose source has no purl at all. Returns '' when the artifact names
4616
+ * nothing at all, so the caller leaves both fields unset.
4617
+ */
4618
+ function packageInstanceId$1(a) {
4619
+ return packageInstanceId$2(a.purl ?? "", a.name, a.version ?? "");
4620
+ }
4621
+ function buildCodeDesc$10(match) {
4532
4622
  const parts = [];
4533
4623
  parts.push(`Package: ${match.artifact.name}@${match.artifact.version}`);
4534
4624
  if (match.artifact.type) parts.push(`Type: ${match.artifact.type}`);
@@ -4546,7 +4636,7 @@ function buildCvssEntries$7(vuln) {
4546
4636
  if (!vuln.cvss || vuln.cvss.length === 0) return;
4547
4637
  const entries = [];
4548
4638
  for (const c of vuln.cvss) {
4549
- const entry = buildCvss$3({
4639
+ const entry = buildCvss$4({
4550
4640
  version: cvssVersionFromString(c.version),
4551
4641
  baseScore: c.metrics?.baseScore,
4552
4642
  baseVector: c.vector,
@@ -4568,7 +4658,7 @@ function mapGrypeTypeToEcosystem(grypeType) {
4568
4658
  default: return ecosystemFromPurlType(grypeType);
4569
4659
  }
4570
4660
  }
4571
- function buildAffectedPackages(match) {
4661
+ function buildAffectedPackages$1(match) {
4572
4662
  const artifact = match.artifact;
4573
4663
  const fix = match.vulnerability.fix;
4574
4664
  const pkg = buildAffectedPackage$1({
@@ -4625,10 +4715,15 @@ function convertMatchToRequirement(match, isIgnored, targetName, startTime) {
4625
4715
  const message = messageParts.join(" ");
4626
4716
  const result = {
4627
4717
  status,
4628
- codeDesc: buildCodeDesc$9(match),
4718
+ codeDesc: buildCodeDesc$10(match),
4629
4719
  message,
4630
4720
  startTime
4631
4721
  };
4722
+ const instanceId = packageInstanceId$1(match.artifact);
4723
+ if (instanceId) {
4724
+ result.resource = "package";
4725
+ result.resourceId = instanceId;
4726
+ }
4632
4727
  const cciTags = nistToCci(DEFAULT_STATIC_ANALYSIS_NIST_TAGS);
4633
4728
  const tags = buildNistCciTags(DEFAULT_STATIC_ANALYSIS_NIST_TAGS, cciTags);
4634
4729
  markUnratedSeverity(tags, severity);
@@ -4660,7 +4755,7 @@ function convertMatchToRequirement(match, isIgnored, targetName, startTime) {
4660
4755
  if (controlType !== void 0) requirement.controlType = controlType;
4661
4756
  const cvss = buildCvssEntries$7(vuln);
4662
4757
  if (cvss) requirement.cvss = cvss;
4663
- requirement.affectedPackages = buildAffectedPackages(match);
4758
+ requirement.affectedPackages = buildAffectedPackages$1(match);
4664
4759
  const cwe = extractCwe(vuln.cwe);
4665
4760
  if (cwe) requirement.cwe = cwe;
4666
4761
  const epss = buildEpss$3(vuln.epss);
@@ -4673,7 +4768,7 @@ function grypeTargetName(target) {
4673
4768
  if (typeof target === "string") return target || "Grype Scan";
4674
4769
  return target?.userInput || "Grype Scan";
4675
4770
  }
4676
- function buildComponent$2(report, targetName) {
4771
+ function buildComponent$3(report, targetName) {
4677
4772
  const rawTarget = report.source?.target;
4678
4773
  const t = typeof rawTarget === "object" ? rawTarget : void 0;
4679
4774
  const firstRepoDigest = t?.repoDigests?.find((d) => d);
@@ -4725,11 +4820,212 @@ async function convertGrypeToHdf(input, converterVersion = "1.0.0") {
4725
4820
  toolName: "Grype",
4726
4821
  toolVersion: grypeData.descriptor?.version,
4727
4822
  baselines: [baseline],
4728
- components: [buildComponent$2(grypeData, targetName)],
4823
+ components: [buildComponent$3(grypeData, targetName)],
4729
4824
  timestamp: scanTime ?? void 0
4730
4825
  });
4731
4826
  }
4732
4827
  //#endregion
4828
+ //#region converters/hadolint-to-hdf/typescript/rules.ts
4829
+ /**
4830
+ * Rule documentation links for the hadolint converter.
4831
+ *
4832
+ * hadolint's report carries a one-line message per finding and nothing else.
4833
+ * Each rule's rationale and fix live on a wiki page, and the two rule families
4834
+ * are documented in different projects, so every requirement links its own.
4835
+ *
4836
+ * Twin of go/rules.go.
4837
+ */
4838
+ const HADOLINT_RULE_BASE = "https://github.com/hadolint/hadolint/wiki";
4839
+ const SHELLCHECK_RULE_BASE = "https://github.com/koalaman/shellcheck/wiki";
4840
+ /** Matches hadolint's own rule codes and the shellcheck codes it surfaces. */
4841
+ const RULE_CODE = /^(DL|SC)\d+$/;
4842
+ /** hadolint surfaces shellcheck's findings in its own report. */
4843
+ function isShellcheck(code) {
4844
+ return code.startsWith("SC");
4845
+ }
4846
+ /**
4847
+ * The published documentation for a rule, or an empty string when the code is
4848
+ * not one. The code is checked first, so a report carrying a crafted code
4849
+ * cannot steer the link somewhere of its choosing.
4850
+ */
4851
+ function ruleReferenceUrl(code) {
4852
+ if (!RULE_CODE.test(code)) return "";
4853
+ return `${isShellcheck(code) ? SHELLCHECK_RULE_BASE : HADOLINT_RULE_BASE}/${code}`;
4854
+ }
4855
+ //#endregion
4856
+ //#region converters/hadolint-to-hdf/typescript/converter.ts
4857
+ /**
4858
+ * hadolint Dockerfile linter output to HDF.
4859
+ *
4860
+ * hadolint's JSON formatter emits a bare array of flat findings, each carrying
4861
+ * only code, column, file, level, line and message. One requirement is produced
4862
+ * per distinct rule code and one result per finding, so a rule that fires on
4863
+ * several lines keeps every occurrence.
4864
+ *
4865
+ * Twin of go/converter.go.
4866
+ */
4867
+ const CONVERTER_NAME$3 = "hadolint";
4868
+ /**
4869
+ * hadolint's severity vocabulary as its JSON writer spells it. An ignored rule
4870
+ * serializes as the empty string, and anything unrecognized scores zero rather
4871
+ * than guessing.
4872
+ */
4873
+ const LEVEL_IMPACT = {
4874
+ error: .7,
4875
+ warning: .5,
4876
+ info: .3,
4877
+ style: .1,
4878
+ "": 0
4879
+ };
4880
+ function impactFor(level) {
4881
+ return severityToImpactWithAliases(level, LEVEL_IMPACT, 0);
4882
+ }
4883
+ /**
4884
+ * Resolves a rule's NIST controls and CCIs, falling back to the static-analysis
4885
+ * controls when the rule is unmapped. A mapped rule can still translate to
4886
+ * nothing — the table is authored at Rev 5 and SR-4 has no Rev 4 equivalent —
4887
+ * so an empty result takes the fallback too rather than leaving the finding
4888
+ * with an empty nist tag. Mirrors Go's `controlsFor`.
4889
+ */
4890
+ function controlsFor$1(ruleId) {
4891
+ const mapping = getHadolintNistMapping(ruleId);
4892
+ if (mapping && mapping.nist.length > 0) return {
4893
+ nist: mapping.nist,
4894
+ cci: nistToCci(mapping.nist)
4895
+ };
4896
+ const fallback = [...DEFAULT_STATIC_ANALYSIS_NIST_TAGS];
4897
+ return {
4898
+ nist: fallback,
4899
+ cci: nistToCci(fallback)
4900
+ };
4901
+ }
4902
+ function codeDescFor(f) {
4903
+ return `File: ${f.file ?? ""} | Line: ${f.line ?? 0} | Column: ${f.column ?? 0}`;
4904
+ }
4905
+ function findingToResult(f, scanTime) {
4906
+ return createResult(ResultStatus.Failed, f.message ?? "", {
4907
+ codeDesc: codeDescFor(f),
4908
+ startTime: scanTime
4909
+ });
4910
+ }
4911
+ function buildRequirement$16(ruleId, group, scanTime) {
4912
+ const first = group[0];
4913
+ const { nist, cci } = controlsFor$1(ruleId);
4914
+ const tags = buildNistCciTags(nist, cci);
4915
+ const message = first.message ?? "";
4916
+ const req = createRequirement(ruleId, message, [{
4917
+ label: "default",
4918
+ data: message
4919
+ }], impactFor(first.level ?? ""), group.map((f) => findingToResult(f, scanTime)), { tags });
4920
+ req.verificationMethod = VerificationMethodEnum.Automated;
4921
+ const reference = ruleReferenceUrl(ruleId);
4922
+ if (reference !== "") req.refs = [{ url: reference }];
4923
+ req.code = JSON.stringify({
4924
+ code: first.code ?? "",
4925
+ column: first.column ?? 0,
4926
+ file: first.file ?? "",
4927
+ level: first.level ?? "",
4928
+ line: first.line ?? 0,
4929
+ message: first.message ?? ""
4930
+ });
4931
+ if (first.file) {
4932
+ const location = { ref: first.file };
4933
+ if (first.line !== void 0 && first.line > 0) location.line = first.line;
4934
+ req.sourceLocation = location;
4935
+ }
4936
+ const controlType = deriveControlTypeFromTags(nist);
4937
+ if (controlType !== void 0) req.controlType = controlType;
4938
+ return req;
4939
+ }
4940
+ /**
4941
+ * Names each distinct scanned file. hadolint accepts several Dockerfiles in one
4942
+ * run and tags every finding with the file it came from.
4943
+ */
4944
+ function componentsFor(findings) {
4945
+ const seen = /* @__PURE__ */ new Set();
4946
+ const components = [];
4947
+ for (const f of findings) {
4948
+ if (!f.file || seen.has(f.file)) continue;
4949
+ seen.add(f.file);
4950
+ components.push({
4951
+ name: f.file,
4952
+ type: TargetType.Repository
4953
+ });
4954
+ }
4955
+ return components;
4956
+ }
4957
+ function baselineTitle(components) {
4958
+ if (components.length === 0) return "Hadolint Scan";
4959
+ if (components.length === 1) return `Hadolint Scan of ${components[0].name}`;
4960
+ return `Hadolint Scan of ${components.length} files`;
4961
+ }
4962
+ const STRING_FIELDS = [
4963
+ "code",
4964
+ "file",
4965
+ "level",
4966
+ "message"
4967
+ ];
4968
+ const INTEGER_FIELDS = ["column", "line"];
4969
+ /**
4970
+ * Parses the findings array, rejecting what the Go decoder rejects so both
4971
+ * public implementations accept the same inputs. Go decodes an absent or null
4972
+ * field to its zero value, so those stay valid; it refuses a field present
4973
+ * with the wrong type.
4974
+ */
4975
+ function parseInput$2(input) {
4976
+ const findings = parseJSON(input);
4977
+ if (!Array.isArray(findings)) throw new Error(`${CONVERTER_NAME$3}: input is not a hadolint findings array`);
4978
+ for (const finding of findings) {
4979
+ if (typeof finding !== "object" || finding === null || Array.isArray(finding)) throw new Error(`${CONVERTER_NAME$3}: input is not a hadolint findings array`);
4980
+ const fields = finding;
4981
+ for (const key of STRING_FIELDS) {
4982
+ const value = fields[key];
4983
+ if (value !== void 0 && value !== null && typeof value !== "string") throw new Error(`${CONVERTER_NAME$3}: finding field ${key} is not a string`);
4984
+ }
4985
+ for (const key of INTEGER_FIELDS) {
4986
+ const value = fields[key];
4987
+ if (value !== void 0 && value !== null && !Number.isInteger(value)) throw new Error(`${CONVERTER_NAME$3}: finding field ${key} is not an integer`);
4988
+ }
4989
+ }
4990
+ return findings;
4991
+ }
4992
+ /** Converts hadolint JSON output to HDF results. */
4993
+ async function convertHadolintToHdf(input, converterVersion = "1.0.0") {
4994
+ validateInputSize$1(input, CONVERTER_NAME$3);
4995
+ registerAllFingerprints();
4996
+ const detected = detectConverter(input);
4997
+ if (detected && detected.fingerprint.id === "sarif-to-hdf") return convertSarifToHdf(input, converterVersion);
4998
+ const resultsChecksum = await inputChecksum(input);
4999
+ const scanTime = /* @__PURE__ */ new Date();
5000
+ const parsed = parseInput$2(input);
5001
+ const { items: findings, truncated } = limitArray(parsed);
5002
+ /* v8 ignore next -- truncation only triggers with >100K items */
5003
+ if (truncated) console.warn(`WARNING: Input truncated at ${findings.length} finding items (original: ${parsed.length})`);
5004
+ const components = componentsFor(findings);
5005
+ const groups = /* @__PURE__ */ new Map();
5006
+ for (const f of findings) {
5007
+ const code = f.code ?? "";
5008
+ const existing = groups.get(code);
5009
+ if (existing) existing.push(f);
5010
+ else groups.set(code, [f]);
5011
+ }
5012
+ const requirements = [];
5013
+ for (const [ruleId, group] of groups) requirements.push(buildRequirement$16(ruleId, group, scanTime));
5014
+ if (requirements.length === 0) requirements.push(buildNoFindingsRequirement("hadolint-no-findings", "hadolint scanned the Dockerfile and reported zero findings.", scanTime));
5015
+ const baseline = createMinimalBaseline("Hadolint Scan", requirements, {
5016
+ title: baselineTitle(components),
5017
+ resultsChecksum
5018
+ });
5019
+ return buildHdfResults({
5020
+ generatorName: "hadolint-to-hdf",
5021
+ converterVersion,
5022
+ toolName: CONVERTER_NAME$3,
5023
+ baselines: [baseline],
5024
+ components: components.length > 0 ? components : void 0,
5025
+ timestamp: scanTime
5026
+ });
5027
+ }
5028
+ //#endregion
4733
5029
  //#region converters/defectdojo-to-hdf/typescript/converter.ts
4734
5030
  function deriveStatus(f) {
4735
5031
  if (f.out_of_scope) return ResultStatus.NotApplicable;
@@ -4833,11 +5129,11 @@ function findingId(f) {
4833
5129
  function cveList(f) {
4834
5130
  return (f.vulnerability_ids ?? []).map((v) => v.vulnerability_id).filter(Boolean);
4835
5131
  }
4836
- function buildCvss$2(f) {
5132
+ function buildCvss$3(f) {
4837
5133
  const out = [];
4838
5134
  const add = (version, vector, score) => {
4839
5135
  if ((score === void 0 || score === null) && !vector) return;
4840
- out.push(buildCvss$3({
5136
+ out.push(buildCvss$4({
4841
5137
  version,
4842
5138
  baseScore: score,
4843
5139
  baseVector: vector
@@ -4858,7 +5154,7 @@ function buildEpss$2(f) {
4858
5154
  function nistTags(f) {
4859
5155
  return f.cwe && f.cwe > 0 ? mapCWEToNIST([`CWE-${f.cwe}`], DEFAULT_STATIC_ANALYSIS_NIST_TAGS) : DEFAULT_STATIC_ANALYSIS_NIST_TAGS;
4860
5156
  }
4861
- function buildDescriptions$1(f) {
5157
+ function buildDescriptions$2(f) {
4862
5158
  const descs = [{
4863
5159
  label: "default",
4864
5160
  data: f.description || f.title || "No description provided."
@@ -4886,7 +5182,7 @@ function sourceLocus(f) {
4886
5182
  if (f.sast_source_file_path) return [f.sast_source_file_path, f.sast_source_line];
4887
5183
  return [void 0, void 0];
4888
5184
  }
4889
- function buildSourceLocation$5(f) {
5185
+ function buildSourceLocation$6(f) {
4890
5186
  const [ref, line] = sourceLocus(f);
4891
5187
  if (!ref) return void 0;
4892
5188
  const loc = { ref };
@@ -4908,17 +5204,17 @@ function convertFinding(f) {
4908
5204
  impact: severityToImpact(f.severity),
4909
5205
  results: [result],
4910
5206
  tags,
4911
- descriptions: buildDescriptions$1(f)
5207
+ descriptions: buildDescriptions$2(f)
4912
5208
  };
4913
5209
  const controlType = deriveControlTypeFromTags(nist);
4914
5210
  if (controlType !== void 0) req.controlType = controlType;
4915
- const cvss = buildCvss$2(f);
5211
+ const cvss = buildCvss$3(f);
4916
5212
  if (cvss.length > 0) req.cvss = cvss;
4917
5213
  const epss = buildEpss$2(f);
4918
5214
  if (epss) req.epss = epss;
4919
5215
  if (f.cwe && f.cwe > 0) req.cwe = [`CWE-${f.cwe}`];
4920
5216
  req.code = JSON.stringify(f, null, 2);
4921
- const sourceLocation = buildSourceLocation$5(f);
5217
+ const sourceLocation = buildSourceLocation$6(f);
4922
5218
  if (sourceLocation) req.sourceLocation = sourceLocation;
4923
5219
  const firstRisk = f.accepted_risks?.[0];
4924
5220
  if (f.risk_accepted && firstRisk) {
@@ -5111,10 +5407,10 @@ function convertReportItemToRequirement(item, host) {
5111
5407
  const isCompliance = !!item["compliance-reference"];
5112
5408
  const id = isCompliance ? parseComplianceRef(item["compliance-reference"], "Vuln-ID")[0] || item["pluginID"] : item["pluginID"];
5113
5409
  const title = isCompliance ? item["compliance-check-name"] || item["pluginName"] : item["pluginName"];
5114
- const descriptions = buildDescriptions(item, isCompliance);
5410
+ const descriptions = buildDescriptions$1(item, isCompliance);
5115
5411
  const impact = calculateImpact(item, isCompliance);
5116
- const tags = buildTags$3(item, isCompliance);
5117
- const refs = buildRefs$9(item);
5412
+ const tags = buildTags$4(item, isCompliance);
5413
+ const refs = buildRefs$10(item);
5118
5414
  const results = [buildResult$1(item, host, isCompliance)];
5119
5415
  const code = JSON.stringify(item, null, 2);
5120
5416
  const nistTags = tags["nist"];
@@ -5253,7 +5549,7 @@ function epssDate$1(host) {
5253
5549
  if (d) return d.toISOString().slice(0, 10);
5254
5550
  }
5255
5551
  }
5256
- function buildDescriptions(item, isCompliance) {
5552
+ function buildDescriptions$1(item, isCompliance) {
5257
5553
  const descriptions = [];
5258
5554
  if (isCompliance && item["compliance-info"]) descriptions.push({
5259
5555
  label: "default",
@@ -5301,7 +5597,7 @@ function calculateImpact(item, isCompliance) {
5301
5597
  function orDefaultNist(controls) {
5302
5598
  return controls.length > 0 ? controls : [...DEFAULT_REMEDIATION_NIST_TAGS$1];
5303
5599
  }
5304
- function buildTags$3(item, isCompliance) {
5600
+ function buildTags$4(item, isCompliance) {
5305
5601
  const tags = { rid: isCompliance ? parseComplianceRef(item["compliance-reference"], "Rule-ID").join(",") : item["pluginID"] };
5306
5602
  if (isCompliance && item["compliance-reference"]) {
5307
5603
  const cciTags = parseComplianceRef(item["compliance-reference"], "CCI");
@@ -5324,7 +5620,7 @@ function buildTags$3(item, isCompliance) {
5324
5620
  if (item.cvss_base_score) tags.cvss_base_score = item.cvss_base_score;
5325
5621
  return tags;
5326
5622
  }
5327
- function buildRefs$9(item) {
5623
+ function buildRefs$10(item) {
5328
5624
  const refs = [];
5329
5625
  if (item.see_also) for (const url of item.see_also.split(/\s+/).filter(Boolean)) refs.push({ url });
5330
5626
  return refs.length > 0 ? refs : void 0;
@@ -5699,7 +5995,7 @@ function mapComplianceStatus(complianceType) {
5699
5995
  function splitControls(nistId) {
5700
5996
  return nistId.split("|").map((c) => c.trim()).filter(Boolean);
5701
5997
  }
5702
- function buildNistTags$3(sourceIdentifier, ruleName) {
5998
+ function buildNistTags$4(sourceIdentifier, ruleName) {
5703
5999
  const byIdentifier = sourceIdentifier ? getAwsConfigNistControlByIdentifier(sourceIdentifier) : void 0;
5704
6000
  if (byIdentifier) return splitControls(byIdentifier);
5705
6001
  const byName = getAwsConfigNistControlByName(ruleName);
@@ -5748,7 +6044,7 @@ function buildCheckText(rule) {
5748
6044
  }
5749
6045
  return parts.join("<br/>");
5750
6046
  }
5751
- function buildCodeDesc$8(q) {
6047
+ function buildCodeDesc$9(q) {
5752
6048
  return `config_rule_name: ${q.ConfigRuleName}, resource_type: ${q.ResourceType}, resource_id: ${q.ResourceId}`;
5753
6049
  }
5754
6050
  function buildResultMessage(codeDesc, annotation, status) {
@@ -5765,7 +6061,7 @@ function computeRunTime$1(invoked, recorded) {
5765
6061
  function buildResult(r) {
5766
6062
  const q = r.EvaluationResultIdentifier.EvaluationResultQualifier;
5767
6063
  const status = mapComplianceStatus(r.ComplianceType);
5768
- const codeDesc = buildCodeDesc$8(q);
6064
+ const codeDesc = buildCodeDesc$9(q);
5769
6065
  const message = buildResultMessage(codeDesc, r.Annotation, status);
5770
6066
  const startTime = (r.ConfigRuleInvokedTime ? parseTimestamp(r.ConfigRuleInvokedTime) : null) ?? /* @__PURE__ */ new Date("0001-01-01T00:00:00Z");
5771
6067
  const runTime = computeRunTime$1(r.ConfigRuleInvokedTime, r.ResultRecordedTime);
@@ -5790,7 +6086,7 @@ function buildNotApplicableResult(rule) {
5790
6086
  });
5791
6087
  }
5792
6088
  function buildRequirement$15(rule) {
5793
- const rawNist = buildNistTags$3(rule.Source.SourceIdentifier, rule.ConfigRuleName);
6089
+ const rawNist = buildNistTags$4(rule.Source.SourceIdentifier, rule.ConfigRuleName);
5794
6090
  const nist = rawNist.length > 0 ? rawNist : DEFAULT_CONFIG_MANAGEMENT_NIST_TAGS;
5795
6091
  const tags = { nist };
5796
6092
  const descriptions = [{
@@ -5964,7 +6260,7 @@ function buildRequirement$14(checkId, group, scanTime) {
5964
6260
  /**
5965
6261
  * Parses checkov input which can be a single object or array.
5966
6262
  */
5967
- function parseInput(input) {
6263
+ function parseInput$1(input) {
5968
6264
  const parsed = parseJSON(input);
5969
6265
  if (Array.isArray(parsed)) return parsed;
5970
6266
  if (!parsed || typeof parsed !== "object") throw new Error("Invalid checkov structure: not a valid JSON object");
@@ -5986,7 +6282,7 @@ async function convertCheckovToHdf(input, converterVersion = "1.0.0") {
5986
6282
  if (detected && detected.fingerprint.id === "sarif-to-hdf") return convertSarifToHdf(input, converterVersion);
5987
6283
  const resultsChecksum = await inputChecksum(input);
5988
6284
  const scanTime = /* @__PURE__ */ new Date();
5989
- const reports = parseInput(input);
6285
+ const reports = parseInput$1(input);
5990
6286
  const allChecks = [];
5991
6287
  const checkTypes = [];
5992
6288
  let version;
@@ -6052,7 +6348,7 @@ function parseSourceLine(line) {
6052
6348
  * Promotes an issue's file/line locus into the structured HDF sourceLocation
6053
6349
  * field. Returns undefined when the issue carries no file path.
6054
6350
  */
6055
- function buildSourceLocation$4(issue) {
6351
+ function buildSourceLocation$5(issue) {
6056
6352
  if (!issue.file) return;
6057
6353
  const loc = { ref: issue.file };
6058
6354
  const line = parseSourceLine(issue.line);
@@ -6108,7 +6404,7 @@ function buildRequirement$13(ruleId, issues, scanTime) {
6108
6404
  data: `CWE-${rep.cwe.id}: ${rep.cwe.url}`
6109
6405
  }];
6110
6406
  const options = { tags };
6111
- const sourceLocation = buildSourceLocation$4(rep);
6407
+ const sourceLocation = buildSourceLocation$5(rep);
6112
6408
  if (sourceLocation) options.sourceLocation = sourceLocation;
6113
6409
  const req = createRequirement(ruleId, rep.details, descriptions, impact, results, options);
6114
6410
  const cwe = cweIds(rep);
@@ -6200,23 +6496,23 @@ async function convertGosecToHdf(input, converterVersion = "1.0.0") {
6200
6496
  }
6201
6497
  //#endregion
6202
6498
  //#region converters/nikto-to-hdf/typescript/converter.ts
6203
- function buildNistTags$2(niktoId) {
6499
+ function buildNistTags$3(niktoId) {
6204
6500
  const control = getNiktoNistControl(niktoId);
6205
6501
  if (control) return [control];
6206
6502
  return [...DEFAULT_STATIC_ANALYSIS_NIST_TAGS];
6207
6503
  }
6208
- function buildCodeDesc$7(vuln) {
6504
+ function buildCodeDesc$8(vuln) {
6209
6505
  const parts = [];
6210
6506
  if (vuln.url) parts.push(`URL: ${vuln.url}`);
6211
6507
  if (vuln.method) parts.push(`Method: ${vuln.method}`);
6212
6508
  return parts.join(" ");
6213
6509
  }
6214
6510
  function convertVulnToRequirement(vuln) {
6215
- const nistTags = buildNistTags$2(vuln.id);
6511
+ const nistTags = buildNistTags$3(vuln.id);
6216
6512
  const cciTags = nistToCci(nistTags);
6217
6513
  const result = {
6218
6514
  status: ResultStatus.Failed,
6219
- codeDesc: buildCodeDesc$7(vuln),
6515
+ codeDesc: buildCodeDesc$8(vuln),
6220
6516
  startTime: /* @__PURE__ */ new Date("0001-01-01T00:00:00Z")
6221
6517
  };
6222
6518
  const extras = {};
@@ -6259,7 +6555,7 @@ async function convertNiktoToHdf(input, converterVersion = "unknown") {
6259
6555
  const req = convertVulnToRequirement(primary);
6260
6556
  if (vulns.length > 1) for (let i = 1; i < vulns.length; i++) req.results.push({
6261
6557
  status: ResultStatus.Failed,
6262
- codeDesc: buildCodeDesc$7(vulns[i]),
6558
+ codeDesc: buildCodeDesc$8(vulns[i]),
6263
6559
  startTime: /* @__PURE__ */ new Date("0001-01-01T00:00:00Z")
6264
6560
  });
6265
6561
  requirements.push(req);
@@ -6303,7 +6599,7 @@ function riskCodeToImpact(riskCode) {
6303
6599
  default: return .5;
6304
6600
  }
6305
6601
  }
6306
- function buildNistTags$1(cweid) {
6602
+ function buildNistTags$2(cweid) {
6307
6603
  if (cweid && cweid !== "0") {
6308
6604
  const cweNum = parseInt(cweid, 10);
6309
6605
  if (!isNaN(cweNum)) {
@@ -6319,7 +6615,7 @@ function buildCwe(cweid) {
6319
6615
  if (isNaN(cweNum)) return;
6320
6616
  return [`CWE-${cweNum}`];
6321
6617
  }
6322
- function buildCodeDesc$6(instance) {
6618
+ function buildCodeDesc$7(instance) {
6323
6619
  const parts = [];
6324
6620
  if (instance.uri) parts.push(`URI: ${instance.uri}`);
6325
6621
  if (instance.method) parts.push(`Method: ${instance.method}`);
@@ -6342,13 +6638,13 @@ function buildRequirementCode$1(alert) {
6342
6638
  if (parts.length === 0) return;
6343
6639
  return parts.join("\n");
6344
6640
  }
6345
- function buildSourceLocation$3(alert) {
6641
+ function buildSourceLocation$4(alert) {
6346
6642
  const inst = (alert.instances ?? []).find((i) => i.uri);
6347
6643
  if (!inst || !inst.uri) return;
6348
6644
  return { ref: inst.uri };
6349
6645
  }
6350
6646
  const REF_URL_RE = /https?:\/\/[^\s<>"']+/g;
6351
- function buildRefs$8(reference) {
6647
+ function buildRefs$9(reference) {
6352
6648
  if (!reference) return;
6353
6649
  const urls = reference.match(REF_URL_RE);
6354
6650
  if (!urls || urls.length === 0) return;
@@ -6368,7 +6664,7 @@ function buildSiteRequirements(site, startTime) {
6368
6664
  const count = pluginIdCount.get(alert.pluginid) ?? 0;
6369
6665
  pluginIdCount.set(alert.pluginid, count + 1);
6370
6666
  const reqId = count === 0 ? alert.pluginid : `${alert.pluginid}.${count}`;
6371
- const nistTags = buildNistTags$1(alert.cweid ?? "");
6667
+ const nistTags = buildNistTags$2(alert.cweid ?? "");
6372
6668
  const cciTags = nistToCci(nistTags);
6373
6669
  const extras = {};
6374
6670
  if (alert.wascid) extras.wascid = alert.wascid;
@@ -6383,9 +6679,13 @@ function buildSiteRequirements(site, startTime) {
6383
6679
  for (const instance of limitedInstances) {
6384
6680
  const result = {
6385
6681
  status: ResultStatus.Failed,
6386
- codeDesc: buildCodeDesc$6(instance),
6682
+ codeDesc: buildCodeDesc$7(instance),
6387
6683
  startTime
6388
6684
  };
6685
+ if (instance.uri) {
6686
+ result.resource = "url";
6687
+ result.resourceId = instance.uri;
6688
+ }
6389
6689
  if (instance.attack) result.message = instance.attack;
6390
6690
  results.push(result);
6391
6691
  }
@@ -6421,9 +6721,9 @@ function buildSiteRequirements(site, startTime) {
6421
6721
  if (controlType !== void 0) req.controlType = controlType;
6422
6722
  const code = buildRequirementCode$1(alert);
6423
6723
  if (code !== void 0) req.code = code;
6424
- const sourceLocation = buildSourceLocation$3(alert);
6724
+ const sourceLocation = buildSourceLocation$4(alert);
6425
6725
  if (sourceLocation !== void 0) req.sourceLocation = sourceLocation;
6426
- const refs = buildRefs$8(alert.reference);
6726
+ const refs = buildRefs$9(alert.reference);
6427
6727
  if (refs !== void 0) req.refs = refs;
6428
6728
  requirements.push(req);
6429
6729
  }
@@ -7068,8 +7368,8 @@ function statusOf(req) {
7068
7368
  if (!o) return [];
7069
7369
  return [{
7070
7370
  status: getStr(o, "status") || void 0,
7071
- appliedAt: getStr(o, "appliedAt") || void 0,
7072
- expiresAt: getStr(o, "expiresAt") || void 0
7371
+ appliedAt: absentIfGoZeroTime(getStr(o, "appliedAt") || void 0),
7372
+ expiresAt: absentIfGoZeroTime(getStr(o, "expiresAt") || void 0)
7073
7373
  }];
7074
7374
  });
7075
7375
  const impact = typeof req.impact === "number" ? req.impact : 1;
@@ -7306,6 +7606,48 @@ function epochMillis(s) {
7306
7606
  if (d === null) return void 0;
7307
7607
  return d.getTime();
7308
7608
  }
7609
+ /**
7610
+ * The index of the override that governs a requirement — the most recently
7611
+ * applied non-expired one — or -1 when none does. The map-shaped twin of
7612
+ * shared/go/exportmap GoverningOverrideIndex.
7613
+ *
7614
+ * Resolution is by appliedAt, never by array position. The schema's description
7615
+ * says the most recent override "should be first in array", but nothing sorts
7616
+ * and this repo's own writers append, so on a document amended twice the newest
7617
+ * override is LAST and overrides[0] is the oldest.
7618
+ *
7619
+ * An entry that is not an object sorts earliest and never governs, so a
7620
+ * malformed override cannot take a whole document's conversion down.
7621
+ */
7622
+ function governingOverrideIndexOf(overrides, now) {
7623
+ const inputs = overrides.map((raw) => {
7624
+ const m = asMap(raw);
7625
+ if (!m) return {};
7626
+ return {
7627
+ status: getStr(m, "status") || void 0,
7628
+ appliedAt: getStr(m, "appliedAt") || void 0,
7629
+ expiresAt: getStr(m, "expiresAt") || void 0
7630
+ };
7631
+ });
7632
+ return governingOverrideIndex(inputs, (i) => asMap(overrides[i]) !== void 0, now);
7633
+ }
7634
+ /** The override object that governs a requirement, or undefined when none does. */
7635
+ function governingOverrideOf(overrides, now) {
7636
+ const i = governingOverrideIndexOf(overrides, now);
7637
+ return i >= 0 ? asMap(overrides[i]) : void 0;
7638
+ }
7639
+ /**
7640
+ * The type of the override that governs a requirement, or '' when none does —
7641
+ * the map-shaped twin of requirementDisposition, including its no-overrides
7642
+ * fallback to the stored field.
7643
+ */
7644
+ function disposition(req, now) {
7645
+ const overrides = asArr(req.statusOverrides) ?? [];
7646
+ const gov = governingOverrideOf(overrides, now);
7647
+ if (gov) return getStr(gov, "type");
7648
+ if (overrides.length === 0) return getStr(req, "disposition");
7649
+ return "";
7650
+ }
7309
7651
  //#endregion
7310
7652
  //#region converters/cyclonedx-to-hdf/typescript/converter.ts
7311
7653
  const CVSS_METHODS = /* @__PURE__ */ new Set([
@@ -7363,7 +7705,7 @@ function buildCvssEntries$5(ratings) {
7363
7705
  const hasCvssMethod = r.method !== void 0 && CVSS_METHODS.has(r.method);
7364
7706
  const hasMetric = r.score !== void 0 && r.score !== null || r.vector !== void 0 && r.vector !== "";
7365
7707
  if (!hasCvssMethod || !hasMetric) continue;
7366
- entries.push(buildCvss$3({
7708
+ entries.push(buildCvss$4({
7367
7709
  version: cvssVersionFromMethod(r.method, r.vector),
7368
7710
  baseScore: r.score,
7369
7711
  baseVector: r.vector,
@@ -7378,7 +7720,7 @@ function buildCvssEntries$5(ratings) {
7378
7720
  * de-duplicated across all three in first-seen order. Returns undefined when the
7379
7721
  * vulnerability carries no links.
7380
7722
  */
7381
- function buildRefs$7(vuln) {
7723
+ function buildRefs$8(vuln) {
7382
7724
  const seen = /* @__PURE__ */ new Set();
7383
7725
  const refs = [];
7384
7726
  const add = (url) => {
@@ -7392,6 +7734,49 @@ function buildRefs$7(vuln) {
7392
7734
  return refs.length > 0 ? refs : void 0;
7393
7735
  }
7394
7736
  /**
7737
+ * The per-result instance identity for a CycloneDX finding: the affected
7738
+ * component's purl, which is what distinguishes the several vulnerabilities that
7739
+ * land on different components. purl is optional in CycloneDX, so this falls
7740
+ * back to the bom-ref — also document-unique, and the only identifier a VEX
7741
+ * document (which carries no components[] to resolve the ref against) publishes
7742
+ * at all.
7743
+ */
7744
+ function componentInstanceId(componentLookup, ref) {
7745
+ return componentLookup.get(ref)?.purl ?? ref;
7746
+ }
7747
+ /**
7748
+ * Extracts the type segment of a purl ('pkg:maven/...' -> 'maven'). Returns
7749
+ * undefined for anything that is not a purl.
7750
+ */
7751
+ function purlType(purl) {
7752
+ return purl ? parsePurl(purl)?.type : void 0;
7753
+ }
7754
+ /**
7755
+ * Assembles the requirement's affected-package inventory from the components its
7756
+ * affects[] entries resolve to, de-duplicated by ref in first-seen order.
7757
+ * Components with neither a purl nor an ecosystem yield no schema-valid entry
7758
+ * and are skipped rather than emitted half-built.
7759
+ */
7760
+ function buildAffectedPackages(componentLookup, affects) {
7761
+ const out = [];
7762
+ const seen = /* @__PURE__ */ new Set();
7763
+ for (const affect of affects) {
7764
+ const comp = componentLookup.get(affect.ref);
7765
+ if (!comp || seen.has(affect.ref)) continue;
7766
+ seen.add(affect.ref);
7767
+ const type = purlType(comp.purl);
7768
+ const ecosystem = type ? ecosystemFromPurlType(type) : void 0;
7769
+ const pkg = buildAffectedPackage$1({
7770
+ name: comp.name,
7771
+ version: comp.version,
7772
+ ecosystem,
7773
+ purl: comp.purl
7774
+ });
7775
+ if (pkg) out.push(pkg);
7776
+ }
7777
+ return out;
7778
+ }
7779
+ /**
7395
7780
  * Formats a component reference as a code_desc string.
7396
7781
  */
7397
7782
  function formatCodeDesc$4(componentLookup, ref) {
@@ -7462,7 +7847,7 @@ function projectLicenseEntry(l) {
7462
7847
  * both languages emit byte-identical code strings. JSON.stringify never escapes
7463
7848
  * `&`/`<`/`>`, matching Go's SetEscapeHTML(false).
7464
7849
  */
7465
- function buildVulnCode$1(vuln) {
7850
+ function buildVulnCode$2(vuln) {
7466
7851
  const proj = {
7467
7852
  id: vuln.id,
7468
7853
  source: projectSource(vuln.source),
@@ -7683,19 +8068,28 @@ async function convertCyclonedxToHdf(input, converterVersion = "1.0.0") {
7683
8068
  data: fixParts.join("\n\n")
7684
8069
  });
7685
8070
  const affects = vuln.affects ?? [];
7686
- const results = affects.length > 0 ? affects.map((affect) => createResult(ResultStatus.Failed, componentSummary(componentLookup, affect.ref), {
7687
- codeDesc: formatCodeDesc$4(componentLookup, affect.ref),
7688
- startTime: scanTime
7689
- })) : [createResult(ResultStatus.Failed, void 0, {
8071
+ const results = affects.length > 0 ? affects.map((affect) => {
8072
+ const instanceId = componentInstanceId(componentLookup, affect.ref);
8073
+ return createResult(ResultStatus.Failed, componentSummary(componentLookup, affect.ref), {
8074
+ codeDesc: formatCodeDesc$4(componentLookup, affect.ref),
8075
+ startTime: scanTime,
8076
+ ...instanceId ? {
8077
+ resource: "component",
8078
+ resourceId: instanceId
8079
+ } : {}
8080
+ });
8081
+ }) : [createResult(ResultStatus.Failed, void 0, {
7690
8082
  codeDesc: `Vulnerability ${vuln.id}`,
7691
8083
  startTime: scanTime
7692
8084
  })];
7693
8085
  const title = vuln.source?.name ? `${vuln.id} (${vuln.source.name})` : vuln.id;
7694
8086
  const req = createRequirement(vuln.id, title, descriptions, impact, results, { tags });
7695
- req.code = buildVulnCode$1(vuln);
8087
+ req.code = buildVulnCode$2(vuln);
7696
8088
  if (cvssEntries.length > 0) req.cvss = cvssEntries;
7697
8089
  if (cweIds.length > 0) req.cwe = cweIds;
7698
- const refs = buildRefs$7(vuln);
8090
+ const affectedPackages = buildAffectedPackages(componentLookup, affects);
8091
+ if (affectedPackages.length > 0) req.affectedPackages = affectedPackages;
8092
+ const refs = buildRefs$8(vuln);
7699
8093
  if (refs !== void 0) req.refs = refs;
7700
8094
  const controlType = deriveControlTypeFromTags(nist);
7701
8095
  if (controlType !== void 0) req.controlType = controlType;
@@ -7758,7 +8152,7 @@ function convertHdfToCsv(input) {
7758
8152
  ipAddress: ""
7759
8153
  }];
7760
8154
  for (const baseline of hdf.baselines) {
7761
- if (!Array.isArray(baseline.requirements) || baseline.requirements.length === 0) throw new Error(`hdf-to-csv: baseline "${text(baseline.name)}" has no requirements`);
8155
+ if (!Array.isArray(baseline.requirements) || baseline.requirements.length === 0) throw new Error(`hdf-to-csv: baseline "${text$1(baseline.name)}" has no requirements`);
7762
8156
  for (const requirement of baseline.requirements) for (const target of targetList) rows.push(createRow(baseline, requirement, target));
7763
8157
  }
7764
8158
  return buildCsv(rows, { sanitize: true });
@@ -7770,7 +8164,7 @@ function convertHdfToCsv(input) {
7770
8164
  * the converters deliberately do not do. Rendering 0 here matches what Go emits,
7771
8165
  * where reading .toFixed off an absent value threw a raw TypeError instead.
7772
8166
  */
7773
- function numeric(value) {
8167
+ function numeric$1(value) {
7774
8168
  return typeof value === "number" ? value : 0;
7775
8169
  }
7776
8170
  /**
@@ -7780,12 +8174,12 @@ function numeric(value) {
7780
8174
  * nothing. Every schema-required STRING cell goes through here; the numeric
7781
8175
  * ones go through numeric(), which serves the same purpose for a different type.
7782
8176
  */
7783
- function text(value) {
8177
+ function text$1(value) {
7784
8178
  return typeof value === "string" ? value : "";
7785
8179
  }
7786
8180
  /** The requirement id as text; also names the requirement in the no-results error. */
7787
8181
  function requirementIdText$1(id) {
7788
- return text(id);
8182
+ return text$1(id);
7789
8183
  }
7790
8184
  /**
7791
8185
  * Create a CSV row from baseline, requirement, and target data
@@ -7806,14 +8200,14 @@ function createRow(baseline, requirement, target) {
7806
8200
  const nistControls = extractArrayFromTags(requirement.tags, "nist");
7807
8201
  const cciControls = extractArrayFromTags(requirement.tags, "cci");
7808
8202
  const effectiveStatus = requirementEffectiveStatus(requirement);
7809
- const effectiveImpact = numeric(requirement.effectiveImpact ?? requirement.impact).toFixed(2);
7810
- const disposition = requirement.disposition ? String(requirement.disposition) : "";
8203
+ const effectiveImpact = numeric$1(requirementEffectiveImpact(requirement)).toFixed(2);
8204
+ const disposition = requirementDisposition(requirement);
7811
8205
  return {
7812
- "Baseline ID": text(baseline.name),
8206
+ "Baseline ID": text$1(baseline.name),
7813
8207
  "Baseline Version": baseline.version || "",
7814
8208
  "Baseline Title": baseline.title || "",
7815
- "Target ID": text(target.name),
7816
- "Target Type": text(target.type),
8209
+ "Target ID": text$1(target.name),
8210
+ "Target Type": text$1(target.type),
7817
8211
  "Requirement ID": requirementIdText$1(requirement.id),
7818
8212
  "Requirement Title": requirement.title || "",
7819
8213
  "Description": description,
@@ -7823,8 +8217,8 @@ function createRow(baseline, requirement, target) {
7823
8217
  "Code": code,
7824
8218
  "References": references,
7825
8219
  "Severity": severity,
7826
- "Impact": numeric(requirement.impact).toFixed(2),
7827
- "Status": text(status),
8220
+ "Impact": numeric$1(requirement.impact).toFixed(2),
8221
+ "Status": text$1(status),
7828
8222
  "NIST Controls": nistControls,
7829
8223
  "CCI Controls": cciControls,
7830
8224
  "Control Type": requirement.controlType ?? "",
@@ -7839,7 +8233,7 @@ function createRow(baseline, requirement, target) {
7839
8233
  "Expires At": joinOverrides(requirement.statusOverrides, (o) => formatExpires(o.expiresAt)),
7840
8234
  "CVSS": cvssScores(requirement.cvss),
7841
8235
  "CWE": (requirement.cwe ?? []).join("; "),
7842
- "EPSS": requirement.epss ? numeric(requirement.epss.score).toFixed(5) : "",
8236
+ "EPSS": requirement.epss ? numeric$1(requirement.epss.score).toFixed(5) : "",
7843
8237
  "KEV": requirement.kev ? requirement.kev.inKev ? "true" : "false" : "",
7844
8238
  "Target FQDN": target.fqdn,
7845
8239
  "Target IP": target.ipAddress
@@ -7918,17 +8312,1211 @@ function descriptionByLabel(requirement, label) {
7918
8312
  * it outright — a reject-versus-degrade split tracked on its own card. Joined with '; ' to
7919
8313
  * match the NIST/CCI column convention.
7920
8314
  */
7921
- function flattenRefs(refs) {
7922
- if (!Array.isArray(refs)) return "";
7923
- const out = [];
7924
- for (const r of refs) {
7925
- if (!r || typeof r !== "object") continue;
7926
- const o = r;
7927
- if (typeof o.url === "string") out.push(o.url);
7928
- else if (typeof o.uri === "string") out.push(o.uri);
7929
- else if (typeof o.ref === "string") out.push(o.ref);
8315
+ function flattenRefs(refs) {
8316
+ if (!Array.isArray(refs)) return "";
8317
+ const out = [];
8318
+ for (const r of refs) {
8319
+ if (!r || typeof r !== "object") continue;
8320
+ const o = r;
8321
+ if (typeof o.url === "string") out.push(o.url);
8322
+ else if (typeof o.uri === "string") out.push(o.uri);
8323
+ else if (typeof o.ref === "string") out.push(o.ref);
8324
+ }
8325
+ return out.join("; ");
8326
+ }
8327
+ //#endregion
8328
+ //#region converters/hdf-to-html/typescript/assets.ts
8329
+ /** The vendored file carries no trailing newline, so one separates it from the report layer. */
8330
+ const STYLESHEET = "/*!\n * Blades CSS 🥷 v2.4\n * Copyright 2026 (https://blades.ninja/)\n * Licensed under MIT\n *//*!\n * Pico CSS ✨ v2.5\n * Copyright 2019-2025 (https://picocss.com)\n * Copyright 2026 (https://blades.ninja/css/pico/)\n * Licensed under MIT\n */:host,:scope{--pico-font-family-emoji:\"Apple Color Emoji\",\"Segoe UI Emoji\",\"Segoe UI Symbol\",\"Noto Color Emoji\";--pico-font-family-sans-serif:system-ui,\"Segoe UI\",Roboto,Oxygen,Ubuntu,Cantarell,Helvetica,Arial,\"Helvetica Neue\",sans-serif,var(--pico-font-family-emoji);--pico-font-family-monospace:ui-monospace,SFMono-Regular,\"SF Mono\",Menlo,Consolas,\"Liberation Mono\",monospace,var(--pico-font-family-emoji);--pico-font-family:var(--pico-font-family-sans-serif);--pico-line-height:1.5;--pico-font-weight:400;--pico-font-size:100%;--pico-text-underline-offset:0.1rem;--pico-border-radius:0.25rem;--pico-border-width:0.0625rem;--pico-outline-width:0.125rem;--pico-transition:0.2s ease-in-out;--pico-spacing:1rem;--pico-typography-spacing-vertical:1rem;--pico-block-spacing-vertical:var(--pico-spacing);--pico-block-spacing-horizontal:var(--pico-spacing);--pico-grid-column-gap:var(--pico-spacing);--pico-grid-row-gap:var(--pico-spacing);--pico-form-element-spacing-vertical:0.75rem;--pico-form-element-spacing-horizontal:1rem;--pico-group-box-shadow:0 0 0 rgba(0, 0, 0, 0);--pico-group-box-shadow-focus-with-button:0 0 0 var(--pico-outline-width) var(--pico-primary-focus);--pico-group-box-shadow-focus-with-input:0 0 0 0.0625rem var(--pico-form-element-border-color);--pico-modal-overlay-backdrop-filter:blur(0.375rem);--pico-nav-element-spacing-vertical:1rem;--pico-nav-element-spacing-horizontal:0.5rem;--pico-nav-link-spacing-vertical:0.5rem;--pico-nav-link-spacing-horizontal:0.5rem;--pico-nav-breadcrumb-divider:\">\";--pico-icon-checkbox:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E\");--pico-icon-minus:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='5' y1='12' x2='19' y2='12'%3E%3C/line%3E%3C/svg%3E\");--pico-icon-chevron:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E\");--pico-icon-date:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='18' rx='2' ry='2'%3E%3C/rect%3E%3Cline x1='16' y1='2' x2='16' y2='6'%3E%3C/line%3E%3Cline x1='8' y1='2' x2='8' y2='6'%3E%3C/line%3E%3Cline x1='3' y1='10' x2='21' y2='10'%3E%3C/line%3E%3C/svg%3E\");--pico-icon-time:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cpolyline points='12 6 12 12 16 14'%3E%3C/polyline%3E%3C/svg%3E\");--pico-icon-search:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='8'%3E%3C/circle%3E%3Cline x1='21' y1='21' x2='16.65' y2='16.65'%3E%3C/line%3E%3C/svg%3E\");--pico-icon-close:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='18' y1='6' x2='6' y2='18'%3E%3C/line%3E%3Cline x1='6' y1='6' x2='18' y2='18'%3E%3C/line%3E%3C/svg%3E\");--pico-icon-loading:url(\"data:image/svg+xml,%3Csvg fill='none' height='24' width='24' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg' %3E%3Cstyle%3E g %7B animation: rotate 2s linear infinite; transform-origin: center center; %7D circle %7B stroke-dasharray: 75,100; stroke-dashoffset: -5; animation: dash 1.5s ease-in-out infinite; stroke-linecap: round; %7D @keyframes rotate %7B 0%25 %7B transform: rotate(0deg); %7D 100%25 %7B transform: rotate(360deg); %7D %7D @keyframes dash %7B 0%25 %7B stroke-dasharray: 1,100; stroke-dashoffset: 0; %7D 50%25 %7B stroke-dasharray: 44.5,100; stroke-dashoffset: -17.5; %7D 100%25 %7B stroke-dasharray: 44.5,100; stroke-dashoffset: -62; %7D %7D %3C/style%3E%3Cg%3E%3Ccircle cx='12' cy='12' r='10' fill='none' stroke='rgb(136, 145, 164)' stroke-width='4' /%3E%3C/g%3E%3C/svg%3E\")}@media (min-width:576px){:host,:scope{--pico-font-size:106.25%}}@media (min-width:768px){:host,:scope{--pico-font-size:112.5%}}@media (min-width:1024px){:host,:scope{--pico-font-size:118.75%}}@media (min-width:1280px){:host,:scope{--pico-font-size:125%}}@media (min-width:1536px){:host,:scope{--pico-font-size:131.25%}}a{--pico-text-decoration:underline}a.contrast,a.secondary{--pico-text-decoration:underline}small{--pico-font-size:0.875em}h1,h2,h3,h4,h5,h6{--pico-font-weight:700}h1{--pico-font-size:2rem;--pico-line-height:1.125;--pico-typography-spacing-top:3rem}h2{--pico-font-size:1.75rem;--pico-line-height:1.15;--pico-typography-spacing-top:2.625rem}h3{--pico-font-size:1.5rem;--pico-line-height:1.175;--pico-typography-spacing-top:2.25rem}h4{--pico-font-size:1.25rem;--pico-line-height:1.2;--pico-typography-spacing-top:1.874rem}h5{--pico-font-size:1.125rem;--pico-line-height:1.225;--pico-typography-spacing-top:1.6875rem}h6{--pico-font-size:1rem;--pico-line-height:1.25;--pico-typography-spacing-top:1.5rem}tfoot td,tfoot th,thead td,thead th{--pico-font-weight:600;--pico-border-width:0.1875rem}code,kbd,pre,samp{--pico-font-family:var(--pico-font-family-monospace)}kbd{--pico-font-weight:bolder}:where(select,textarea),input:not(\n[type=submit],[type=button],[type=reset],[type=checkbox],[type=radio],[type=file]\n){--pico-outline-width:0.0625rem}[type=search]{--pico-border-radius:5rem}[type=checkbox],[type=radio]{--pico-border-width:0.125rem}[type=checkbox][role=switch]{--pico-border-width:0.1875rem}details.dropdown summary:not([role=button]){--pico-outline-width:0.0625rem}nav details.dropdown summary:focus-visible{--pico-outline-width:0.125rem}[role=search]{--pico-border-radius:5rem}[role=group]:has(\nbutton.secondary:focus,[type=submit].secondary:focus,[type=button].secondary:focus,[role=button].secondary:focus\n),[role=search]:has(\nbutton.secondary:focus,[type=submit].secondary:focus,[type=button].secondary:focus,[role=button].secondary:focus\n){--pico-group-box-shadow-focus-with-button:0 0 0 var(--pico-outline-width) var(--pico-secondary-focus)}[role=group]:has(\nbutton.contrast:focus,[type=submit].contrast:focus,[type=button].contrast:focus,[role=button].contrast:focus\n),[role=search]:has(\nbutton.contrast:focus,[type=submit].contrast:focus,[type=button].contrast:focus,[role=button].contrast:focus\n){--pico-group-box-shadow-focus-with-button:0 0 0 var(--pico-outline-width) var(--pico-contrast-focus)}[role=group] [role=button],[role=group] [type=button],[role=group] [type=submit],[role=group] button,[role=search] [role=button],[role=search] [type=button],[role=search] [type=submit],[role=search] button{--pico-form-element-spacing-horizontal:2rem}details summary[role=button]:not(.outline)::after{filter:brightness(0) invert(1)}[aria-busy=true]:not(input,select,textarea):is(\nbutton,[type=submit],[type=button],[type=reset],[role=button]\n):not(.outline)::before{filter:brightness(0) invert(1)}:host(:not([data-theme=dark])),:scope:not([data-theme=dark]),[data-theme=light]{color-scheme:light;--pico-background-color:#fff;--pico-color:#373c44;--pico-text-selection-color:rgba(2, 154, 232, 0.25);--pico-muted-color:#646b79;--pico-muted-border-color:rgb(231, 234, 239.5);--pico-primary:#0172ad;--pico-primary-background:#0172ad;--pico-primary-border:var(--pico-primary-background);--pico-primary-underline:rgba(1, 114, 173, 0.5);--pico-primary-hover:#015887;--pico-primary-hover-background:#02659a;--pico-primary-hover-border:var(--pico-primary-hover-background);--pico-primary-hover-underline:var(--pico-primary-hover);--pico-primary-focus:rgba(2, 154, 232, 0.5);--pico-primary-inverse:#fff;--pico-secondary:#5d6b89;--pico-secondary-background:#525f7a;--pico-secondary-border:var(--pico-secondary-background);--pico-secondary-underline:rgba(93, 107, 137, 0.5);--pico-secondary-hover:#48536b;--pico-secondary-hover-background:#48536b;--pico-secondary-hover-border:var(--pico-secondary-hover-background);--pico-secondary-hover-underline:var(--pico-secondary-hover);--pico-secondary-focus:rgba(93, 107, 137, 0.25);--pico-secondary-inverse:#fff;--pico-contrast:#181c25;--pico-contrast-background:#181c25;--pico-contrast-border:var(--pico-contrast-background);--pico-contrast-underline:rgba(24, 28, 37, 0.5);--pico-contrast-hover:#000;--pico-contrast-hover-background:#000;--pico-contrast-hover-border:var(--pico-contrast-hover-background);--pico-contrast-hover-underline:var(--pico-secondary-hover);--pico-contrast-focus:rgba(93, 107, 137, 0.25);--pico-contrast-inverse:#fff;--pico-box-shadow:0 0.5rem 2.5rem rgba(129, 145, 181, 0.2);--pico-h1-color:#2d3138;--pico-h2-color:#373c44;--pico-h3-color:#424751;--pico-h4-color:#4d535e;--pico-h5-color:#5c6370;--pico-h6-color:#646b79;--pico-mark-background-color:rgb(252.5, 230.5, 191.5);--pico-mark-color:#0f1114;--pico-ins-color:rgb(28.5, 105.5, 84);--pico-del-color:rgb(136, 56.5, 53);--pico-blockquote-border-color:var(--pico-muted-border-color);--pico-blockquote-footer-color:var(--pico-muted-color);--pico-button-box-shadow:0 0 0 rgba(0, 0, 0, 0);--pico-button-hover-box-shadow:0 0 0 rgba(0, 0, 0, 0);--pico-table-border-color:var(--pico-muted-border-color);--pico-table-row-stripped-background-color:rgba(111, 120, 135, 0.0375);--pico-code-background-color:rgb(243, 244.5, 246.75);--pico-code-color:#646b79;--pico-code-kbd-background-color:var(--pico-color);--pico-code-kbd-color:var(--pico-background-color);--pico-form-element-background-color:rgb(251, 251.5, 252.25);--pico-form-element-selected-background-color:#dfe3eb;--pico-form-element-border-color:#cfd5e2;--pico-form-element-color:#23262c;--pico-form-element-placeholder-color:var(--pico-muted-color);--pico-form-element-active-background-color:#fff;--pico-form-element-active-border-color:var(--pico-primary-border);--pico-form-element-focus-color:var(--pico-primary-border);--pico-form-element-disabled-opacity:0.5;--pico-form-element-invalid-border-color:rgb(183.5, 105.5, 106.5);--pico-form-element-invalid-active-border-color:rgb(200.25, 79.25, 72.25);--pico-form-element-invalid-focus-color:var(--pico-form-element-invalid-active-border-color);--pico-form-element-valid-border-color:rgb(76, 154.5, 137.5);--pico-form-element-valid-active-border-color:rgb(39, 152.75, 118.75);--pico-form-element-valid-focus-color:var(--pico-form-element-valid-active-border-color);--pico-switch-background-color:#bfc7d9;--pico-switch-checked-background-color:var(--pico-primary-background);--pico-switch-color:#fff;--pico-switch-thumb-box-shadow:0 0 0 rgba(0, 0, 0, 0);--pico-range-border-color:#dfe3eb;--pico-range-active-border-color:#bfc7d9;--pico-range-thumb-border-color:var(--pico-background-color);--pico-range-thumb-color:var(--pico-secondary-background);--pico-range-thumb-active-color:var(--pico-primary-background);--pico-accordion-border-color:var(--pico-muted-border-color);--pico-accordion-active-summary-color:var(--pico-primary-hover);--pico-accordion-close-summary-color:var(--pico-color);--pico-accordion-open-summary-color:var(--pico-muted-color);--pico-card-background-color:var(--pico-background-color);--pico-card-border-color:var(--pico-muted-border-color);--pico-card-box-shadow:var(--pico-box-shadow);--pico-card-sectioning-background-color:rgb(251, 251.5, 252.25);--pico-dropdown-background-color:#fff;--pico-dropdown-border-color:#eff1f4;--pico-dropdown-box-shadow:var(--pico-box-shadow);--pico-dropdown-color:var(--pico-color);--pico-dropdown-hover-background-color:#eff1f4;--pico-loading-spinner-opacity:0.5;--pico-modal-overlay-background-color:rgba(232, 234, 237, 0.75);--pico-progress-background-color:#dfe3eb;--pico-progress-color:var(--pico-primary-background);--pico-tooltip-background-color:var(--pico-contrast-background);--pico-tooltip-color:var(--pico-contrast-inverse);--pico-icon-valid:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(76, 154.5, 137.5)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E\");--pico-icon-invalid:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(200.25, 79.25, 72.25)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cline x1='12' y1='8' x2='12' y2='12'%3E%3C/line%3E%3Cline x1='12' y1='16' x2='12.01' y2='16'%3E%3C/line%3E%3C/svg%3E\")}:host(:not([data-theme=dark])) input:is(\n[type=submit],[type=button],[type=reset],[type=checkbox],[type=radio],[type=file]\n),:scope:not([data-theme=dark]) input:is(\n[type=submit],[type=button],[type=reset],[type=checkbox],[type=radio],[type=file]\n),[data-theme=light] input:is(\n[type=submit],[type=button],[type=reset],[type=checkbox],[type=radio],[type=file]\n){--pico-form-element-focus-color:var(--pico-primary-focus)}@media only screen and (prefers-color-scheme:dark){:host(:not([data-theme])),:scope:not([data-theme]){color-scheme:dark;--pico-background-color:rgb(19, 22.5, 30.5);--pico-color:#c2c7d0;--pico-text-selection-color:rgba(1, 170, 255, 0.1875);--pico-muted-color:#7b8495;--pico-muted-border-color:#202632;--pico-primary:#01aaff;--pico-primary-background:#0172ad;--pico-primary-border:var(--pico-primary-background);--pico-primary-underline:rgba(1, 170, 255, 0.5);--pico-primary-hover:#79c0ff;--pico-primary-hover-background:#017fc0;--pico-primary-hover-border:var(--pico-primary-hover-background);--pico-primary-hover-underline:var(--pico-primary-hover);--pico-primary-focus:rgba(1, 170, 255, 0.375);--pico-primary-inverse:#fff;--pico-secondary:#969eaf;--pico-secondary-background:#525f7a;--pico-secondary-border:var(--pico-secondary-background);--pico-secondary-underline:rgba(150, 158, 175, 0.5);--pico-secondary-hover:#b3b9c5;--pico-secondary-hover-background:#5d6b89;--pico-secondary-hover-border:var(--pico-secondary-hover-background);--pico-secondary-hover-underline:var(--pico-secondary-hover);--pico-secondary-focus:rgba(144, 158, 190, 0.25);--pico-secondary-inverse:#fff;--pico-contrast:#dfe3eb;--pico-contrast-background:#eff1f4;--pico-contrast-border:var(--pico-contrast-background);--pico-contrast-underline:rgba(223, 227, 235, 0.5);--pico-contrast-hover:#fff;--pico-contrast-hover-background:#fff;--pico-contrast-hover-border:var(--pico-contrast-hover-background);--pico-contrast-hover-underline:var(--pico-contrast-hover);--pico-contrast-focus:rgba(207, 213, 226, 0.25);--pico-contrast-inverse:#000;--pico-box-shadow:0 0.5rem 2.5rem rgba(7, 8.5, 12, 0.2);--pico-h1-color:#f0f1f3;--pico-h2-color:#e0e3e7;--pico-h3-color:#c2c7d0;--pico-h4-color:#b3b9c5;--pico-h5-color:#a4acba;--pico-h6-color:#8891a4;--pico-mark-background-color:#014063;--pico-mark-color:#fff;--pico-ins-color:#62af9a;--pico-del-color:rgb(205.5, 126, 123);--pico-blockquote-border-color:var(--pico-muted-border-color);--pico-blockquote-footer-color:var(--pico-muted-color);--pico-button-box-shadow:0 0 0 rgba(0, 0, 0, 0);--pico-button-hover-box-shadow:0 0 0 rgba(0, 0, 0, 0);--pico-table-border-color:var(--pico-muted-border-color);--pico-table-row-stripped-background-color:rgba(111, 120, 135, 0.0375);--pico-code-background-color:rgb(26, 30.5, 40.25);--pico-code-color:#8891a4;--pico-code-kbd-background-color:var(--pico-color);--pico-code-kbd-color:var(--pico-background-color);--pico-form-element-background-color:rgb(28, 33, 43.5);--pico-form-element-selected-background-color:#2a3140;--pico-form-element-border-color:#2a3140;--pico-form-element-color:#e0e3e7;--pico-form-element-placeholder-color:#8891a4;--pico-form-element-active-background-color:rgb(26, 30.5, 40.25);--pico-form-element-active-border-color:var(--pico-primary-border);--pico-form-element-focus-color:var(--pico-primary-border);--pico-form-element-disabled-opacity:0.5;--pico-form-element-invalid-border-color:rgb(149.5, 74, 80);--pico-form-element-invalid-active-border-color:rgb(183.25, 63.5, 59);--pico-form-element-invalid-focus-color:var(--pico-form-element-invalid-active-border-color);--pico-form-element-valid-border-color:#2a7b6f;--pico-form-element-valid-active-border-color:rgb(22, 137, 105.5);--pico-form-element-valid-focus-color:var(--pico-form-element-valid-active-border-color);--pico-switch-background-color:#333c4e;--pico-switch-checked-background-color:var(--pico-primary-background);--pico-switch-color:#fff;--pico-switch-thumb-box-shadow:0 0 0 rgba(0, 0, 0, 0);--pico-range-border-color:#202632;--pico-range-active-border-color:#2a3140;--pico-range-thumb-border-color:var(--pico-background-color);--pico-range-thumb-color:var(--pico-secondary-background);--pico-range-thumb-active-color:var(--pico-primary-background);--pico-accordion-border-color:var(--pico-muted-border-color);--pico-accordion-active-summary-color:var(--pico-primary-hover);--pico-accordion-close-summary-color:var(--pico-color);--pico-accordion-open-summary-color:var(--pico-muted-color);--pico-card-background-color:#181c25;--pico-card-border-color:var(--pico-card-background-color);--pico-card-box-shadow:var(--pico-box-shadow);--pico-card-sectioning-background-color:rgb(26, 30.5, 40.25);--pico-dropdown-background-color:#181c25;--pico-dropdown-border-color:#202632;--pico-dropdown-box-shadow:var(--pico-box-shadow);--pico-dropdown-color:var(--pico-color);--pico-dropdown-hover-background-color:#202632;--pico-loading-spinner-opacity:0.5;--pico-modal-overlay-background-color:rgba(7.5, 8.5, 10, 0.75);--pico-progress-background-color:#202632;--pico-progress-color:var(--pico-primary-background);--pico-tooltip-background-color:var(--pico-contrast-background);--pico-tooltip-color:var(--pico-contrast-inverse);--pico-icon-valid:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(42, 123, 111)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E\");--pico-icon-invalid:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(149.5, 74, 80)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cline x1='12' y1='8' x2='12' y2='12'%3E%3C/line%3E%3Cline x1='12' y1='16' x2='12.01' y2='16'%3E%3C/line%3E%3C/svg%3E\")}:host(:not([data-theme])) input:is(\n[type=submit],[type=button],[type=reset],[type=checkbox],[type=radio],[type=file]\n),:scope:not([data-theme]) input:is(\n[type=submit],[type=button],[type=reset],[type=checkbox],[type=radio],[type=file]\n){--pico-form-element-focus-color:var(--pico-primary-focus)}:host(:not([data-theme])) details summary[role=button].contrast:not(.outline)::after,:scope:not([data-theme]) details summary[role=button].contrast:not(.outline)::after{filter:brightness(0)}:host(:not([data-theme])) [aria-busy=true]:not(input,select,textarea).contrast:is(\nbutton,[type=submit],[type=button],[type=reset],[role=button]\n):not(.outline)::before,:scope:not([data-theme]) [aria-busy=true]:not(input,select,textarea).contrast:is(\nbutton,[type=submit],[type=button],[type=reset],[role=button]\n):not(.outline)::before{filter:brightness(0)}}[data-theme=dark]{color-scheme:dark;--pico-background-color:rgb(19, 22.5, 30.5);--pico-color:#c2c7d0;--pico-text-selection-color:rgba(1, 170, 255, 0.1875);--pico-muted-color:#7b8495;--pico-muted-border-color:#202632;--pico-primary:#01aaff;--pico-primary-background:#0172ad;--pico-primary-border:var(--pico-primary-background);--pico-primary-underline:rgba(1, 170, 255, 0.5);--pico-primary-hover:#79c0ff;--pico-primary-hover-background:#017fc0;--pico-primary-hover-border:var(--pico-primary-hover-background);--pico-primary-hover-underline:var(--pico-primary-hover);--pico-primary-focus:rgba(1, 170, 255, 0.375);--pico-primary-inverse:#fff;--pico-secondary:#969eaf;--pico-secondary-background:#525f7a;--pico-secondary-border:var(--pico-secondary-background);--pico-secondary-underline:rgba(150, 158, 175, 0.5);--pico-secondary-hover:#b3b9c5;--pico-secondary-hover-background:#5d6b89;--pico-secondary-hover-border:var(--pico-secondary-hover-background);--pico-secondary-hover-underline:var(--pico-secondary-hover);--pico-secondary-focus:rgba(144, 158, 190, 0.25);--pico-secondary-inverse:#fff;--pico-contrast:#dfe3eb;--pico-contrast-background:#eff1f4;--pico-contrast-border:var(--pico-contrast-background);--pico-contrast-underline:rgba(223, 227, 235, 0.5);--pico-contrast-hover:#fff;--pico-contrast-hover-background:#fff;--pico-contrast-hover-border:var(--pico-contrast-hover-background);--pico-contrast-hover-underline:var(--pico-contrast-hover);--pico-contrast-focus:rgba(207, 213, 226, 0.25);--pico-contrast-inverse:#000;--pico-box-shadow:0 0.5rem 2.5rem rgba(7, 8.5, 12, 0.2);--pico-h1-color:#f0f1f3;--pico-h2-color:#e0e3e7;--pico-h3-color:#c2c7d0;--pico-h4-color:#b3b9c5;--pico-h5-color:#a4acba;--pico-h6-color:#8891a4;--pico-mark-background-color:#014063;--pico-mark-color:#fff;--pico-ins-color:#62af9a;--pico-del-color:rgb(205.5, 126, 123);--pico-blockquote-border-color:var(--pico-muted-border-color);--pico-blockquote-footer-color:var(--pico-muted-color);--pico-button-box-shadow:0 0 0 rgba(0, 0, 0, 0);--pico-button-hover-box-shadow:0 0 0 rgba(0, 0, 0, 0);--pico-table-border-color:var(--pico-muted-border-color);--pico-table-row-stripped-background-color:rgba(111, 120, 135, 0.0375);--pico-code-background-color:rgb(26, 30.5, 40.25);--pico-code-color:#8891a4;--pico-code-kbd-background-color:var(--pico-color);--pico-code-kbd-color:var(--pico-background-color);--pico-form-element-background-color:rgb(28, 33, 43.5);--pico-form-element-selected-background-color:#2a3140;--pico-form-element-border-color:#2a3140;--pico-form-element-color:#e0e3e7;--pico-form-element-placeholder-color:#8891a4;--pico-form-element-active-background-color:rgb(26, 30.5, 40.25);--pico-form-element-active-border-color:var(--pico-primary-border);--pico-form-element-focus-color:var(--pico-primary-border);--pico-form-element-disabled-opacity:0.5;--pico-form-element-invalid-border-color:rgb(149.5, 74, 80);--pico-form-element-invalid-active-border-color:rgb(183.25, 63.5, 59);--pico-form-element-invalid-focus-color:var(--pico-form-element-invalid-active-border-color);--pico-form-element-valid-border-color:#2a7b6f;--pico-form-element-valid-active-border-color:rgb(22, 137, 105.5);--pico-form-element-valid-focus-color:var(--pico-form-element-valid-active-border-color);--pico-switch-background-color:#333c4e;--pico-switch-checked-background-color:var(--pico-primary-background);--pico-switch-color:#fff;--pico-switch-thumb-box-shadow:0 0 0 rgba(0, 0, 0, 0);--pico-range-border-color:#202632;--pico-range-active-border-color:#2a3140;--pico-range-thumb-border-color:var(--pico-background-color);--pico-range-thumb-color:var(--pico-secondary-background);--pico-range-thumb-active-color:var(--pico-primary-background);--pico-accordion-border-color:var(--pico-muted-border-color);--pico-accordion-active-summary-color:var(--pico-primary-hover);--pico-accordion-close-summary-color:var(--pico-color);--pico-accordion-open-summary-color:var(--pico-muted-color);--pico-card-background-color:#181c25;--pico-card-border-color:var(--pico-card-background-color);--pico-card-box-shadow:var(--pico-box-shadow);--pico-card-sectioning-background-color:rgb(26, 30.5, 40.25);--pico-dropdown-background-color:#181c25;--pico-dropdown-border-color:#202632;--pico-dropdown-box-shadow:var(--pico-box-shadow);--pico-dropdown-color:var(--pico-color);--pico-dropdown-hover-background-color:#202632;--pico-loading-spinner-opacity:0.5;--pico-modal-overlay-background-color:rgba(7.5, 8.5, 10, 0.75);--pico-progress-background-color:#202632;--pico-progress-color:var(--pico-primary-background);--pico-tooltip-background-color:var(--pico-contrast-background);--pico-tooltip-color:var(--pico-contrast-inverse);--pico-icon-valid:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(42, 123, 111)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E\");--pico-icon-invalid:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(149.5, 74, 80)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cline x1='12' y1='8' x2='12' y2='12'%3E%3C/line%3E%3Cline x1='12' y1='16' x2='12.01' y2='16'%3E%3C/line%3E%3C/svg%3E\")}[data-theme=dark] input:is(\n[type=submit],[type=button],[type=reset],[type=checkbox],[type=radio],[type=file]\n){--pico-form-element-focus-color:var(--pico-primary-focus)}[data-theme=dark] details summary[role=button].contrast:not(.outline)::after{filter:brightness(0)}[data-theme=dark] [aria-busy=true]:not(input,select,textarea).contrast:is(\nbutton,[type=submit],[type=button],[type=reset],[role=button]\n):not(.outline)::before{filter:brightness(0)}[type=checkbox],[type=radio],[type=range],progress{accent-color:var(--pico-primary)}*,::after,::before{box-sizing:border-box;background-repeat:no-repeat}::after,::before{text-decoration:inherit;vertical-align:inherit}:where(:host),:where(:scope){-webkit-tap-highlight-color:transparent;-webkit-text-size-adjust:100%;-moz-text-size-adjust:100%;text-size-adjust:100%;background-color:var(--pico-background-color);color:var(--pico-color);font-weight:var(--pico-font-weight);font-size:var(--pico-font-size);line-height:var(--pico-line-height);font-family:var(--pico-font-family);text-underline-offset:var(--pico-text-underline-offset);text-rendering:optimizeLegibility;overflow-wrap:break-word;-moz-tab-size:4;-o-tab-size:4;tab-size:4}body{width:100%;margin:0}main{display:block}body>footer,body>header,body>main{padding-block:var(--pico-block-spacing-vertical)}section{margin-bottom:var(--pico-block-spacing-vertical)}.container,.container-fluid{width:100%;margin-right:auto;margin-left:auto;padding-right:var(--pico-spacing);padding-left:var(--pico-spacing)}@media (min-width:576px){.container{max-width:510px;padding-right:0;padding-left:0}}@media (min-width:768px){.container{max-width:700px}}@media (min-width:1024px){.container{max-width:950px}}@media (min-width:1280px){.container{max-width:1200px}}@media (min-width:1536px){.container{max-width:1450px}}.grid{grid-column-gap:var(--pico-grid-column-gap);grid-row-gap:var(--pico-grid-row-gap);display:grid;grid-template-columns:1fr}@media (min-width:768px){.grid{grid-template-columns:repeat(auto-fit,minmax(0%,1fr))}}.grid>:not(.does-not-exist){min-width:0;margin-bottom:0}.overflow-auto{overflow:auto}b,strong{font-weight:bolder}sub,sup{position:relative;font-size:.75em;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}address,blockquote,dl,ol,p,pre,table,ul{margin-top:0;margin-bottom:var(--pico-typography-spacing-vertical);color:var(--pico-color);font-style:normal;font-weight:var(--pico-font-weight)}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:var(--pico-typography-spacing-vertical);color:var(--pico-color);font-weight:var(--pico-font-weight);font-size:var(--pico-font-size);line-height:var(--pico-line-height);font-family:var(--pico-font-family)}h1{--pico-color:var(--pico-h1-color)}h2{--pico-color:var(--pico-h2-color)}h3{--pico-color:var(--pico-h3-color)}h4{--pico-color:var(--pico-h4-color)}h5{--pico-color:var(--pico-h5-color)}h6{--pico-color:var(--pico-h6-color)}:where(article,address,blockquote,dl,figure,form,ol,p,pre,table,ul)~:is(h1,h2,h3,h4,h5,h6){margin-top:var(--pico-typography-spacing-top)}p{margin-bottom:var(--pico-typography-spacing-vertical)}hgroup{margin-bottom:var(--pico-typography-spacing-vertical)}hgroup>*{margin-top:0;margin-bottom:0}hgroup>:not(:first-child):last-child{--pico-color:var(--pico-muted-color);--pico-font-weight:unset;font-size:1rem}:where(ol,ul) li{margin-bottom:calc(var(--pico-typography-spacing-vertical) * .25)}:where(dl,ol,ul) :where(dl,ol,ul){margin:0;margin-top:calc(var(--pico-typography-spacing-vertical) * .25)}ul li{list-style:square}mark{padding:.125rem .25rem;background-color:var(--pico-mark-background-color);color:var(--pico-mark-color);vertical-align:baseline}blockquote{display:block;margin:var(--pico-typography-spacing-vertical) 0;padding:var(--pico-spacing);border-right:none;border-left:.25rem solid var(--pico-blockquote-border-color);border-inline-start:0.25rem solid var(--pico-blockquote-border-color);border-inline-end:none}blockquote footer{margin-top:calc(var(--pico-typography-spacing-vertical) * .5);color:var(--pico-blockquote-footer-color)}abbr[title]{border-bottom:1px dotted;text-decoration:none;cursor:help}ins{color:var(--pico-ins-color);text-decoration:none}del{color:var(--pico-del-color)}::-moz-selection{background-color:var(--pico-text-selection-color)}::selection{background-color:var(--pico-text-selection-color)}:where(a:not([role=button])),[role=link]{--pico-color:var(--pico-primary);--pico-background-color:transparent;--pico-underline:var(--pico-primary-underline);outline:0;background-color:var(--pico-background-color);color:var(--pico-color);-webkit-text-decoration:var(--pico-text-decoration);text-decoration:var(--pico-text-decoration);text-decoration-color:var(--pico-underline);text-underline-offset:0.125em;transition:background-color var(--pico-transition),color var(--pico-transition),box-shadow var(--pico-transition),-webkit-text-decoration var(--pico-transition);transition:background-color var(--pico-transition),color var(--pico-transition),text-decoration var(--pico-transition),box-shadow var(--pico-transition);transition:background-color var(--pico-transition),color var(--pico-transition),text-decoration var(--pico-transition),box-shadow var(--pico-transition),-webkit-text-decoration var(--pico-transition)}:where(a:not([role=button])):is(\n[aria-current]:not([aria-current=false]),:hover,:active,:focus\n),[role=link]:is([aria-current]:not([aria-current=false]),:hover,:active,:focus){--pico-color:var(--pico-primary-hover);--pico-underline:var(--pico-primary-hover-underline);--pico-text-decoration:underline}:where(a:not([role=button])):focus-visible,[role=link]:focus-visible{box-shadow:0 0 0 var(--pico-outline-width) var(--pico-primary-focus)}:where(a:not([role=button])).secondary,[role=link].secondary{--pico-color:var(--pico-secondary);--pico-underline:var(--pico-secondary-underline)}:where(a:not([role=button])).secondary:is(\n[aria-current]:not([aria-current=false]),:hover,:active,:focus\n),[role=link].secondary:is([aria-current]:not([aria-current=false]),:hover,:active,:focus){--pico-color:var(--pico-secondary-hover);--pico-underline:var(--pico-secondary-hover-underline)}:where(a:not([role=button])).contrast,[role=link].contrast{--pico-color:var(--pico-contrast);--pico-underline:var(--pico-contrast-underline)}:where(a:not([role=button])).contrast:is(\n[aria-current]:not([aria-current=false]),:hover,:active,:focus\n),[role=link].contrast:is([aria-current]:not([aria-current=false]),:hover,:active,:focus){--pico-color:var(--pico-contrast-hover);--pico-underline:var(--pico-contrast-hover-underline)}a[role=button]{display:inline-block}button{margin:0;overflow:visible;font-family:inherit;text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[role=button],[type=button],[type=file]::file-selector-button,[type=reset],[type=submit],button{--pico-background-color:var(--pico-primary-background);--pico-border-color:var(--pico-primary-border);--pico-color:var(--pico-primary-inverse);--pico-box-shadow:var(--pico-button-box-shadow, 0 0 0 rgba(0, 0, 0, 0));padding:var(--pico-form-element-spacing-vertical) var(--pico-form-element-spacing-horizontal);border:var(--pico-border-width) solid var(--pico-border-color);border-radius:var(--pico-border-radius);outline:0;background-color:var(--pico-background-color);box-shadow:var(--pico-box-shadow);color:var(--pico-color);font-weight:var(--pico-font-weight);font-size:1rem;line-height:var(--pico-line-height);text-align:center;text-decoration:none;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;transition:background-color var(--pico-transition),border-color var(--pico-transition),color var(--pico-transition),box-shadow var(--pico-transition);user-select:none}[role=button]:is(:hover,:active,:focus),[role=button]:is([aria-current]:not([aria-current=false])),[type=button]:is(:hover,:active,:focus),[type=button]:is([aria-current]:not([aria-current=false])),[type=file]::file-selector-button:is(:hover,:active,:focus),[type=file]::file-selector-button:is([aria-current]:not([aria-current=false])),[type=reset]:is(:hover,:active,:focus),[type=reset]:is([aria-current]:not([aria-current=false])),[type=submit]:is(:hover,:active,:focus),[type=submit]:is([aria-current]:not([aria-current=false])),button:is(:hover,:active,:focus),button:is([aria-current]:not([aria-current=false])){--pico-background-color:var(--pico-primary-hover-background);--pico-border-color:var(--pico-primary-hover-border);--pico-box-shadow:var(--pico-button-hover-box-shadow, 0 0 0 rgba(0, 0, 0, 0));--pico-color:var(--pico-primary-inverse)}[role=button]:focus,[role=button]:is([aria-current]:not([aria-current=false])):focus,[type=button]:focus,[type=button]:is([aria-current]:not([aria-current=false])):focus,[type=file]::file-selector-button:focus,[type=file]::file-selector-button:is([aria-current]:not([aria-current=false])):focus,[type=reset]:focus,[type=reset]:is([aria-current]:not([aria-current=false])):focus,[type=submit]:focus,[type=submit]:is([aria-current]:not([aria-current=false])):focus,button:focus,button:is([aria-current]:not([aria-current=false])):focus{--pico-box-shadow:var(--pico-button-hover-box-shadow, 0 0 0 rgba(0, 0, 0, 0)),0 0 0 var(--pico-outline-width) var(--pico-primary-focus)}[type=button],[type=reset],[type=submit]{margin-bottom:var(--pico-spacing)}:is(button,[type=submit],[type=button],[role=button]).secondary,[type=file]::file-selector-button,[type=reset]{--pico-background-color:var(--pico-secondary-background);--pico-border-color:var(--pico-secondary-border);--pico-color:var(--pico-secondary-inverse);cursor:pointer}:is(button,[type=submit],[type=button],[role=button]).secondary:is(\n[aria-current]:not([aria-current=false]),:hover,:active,:focus\n),[type=file]::file-selector-button:is(\n[aria-current]:not([aria-current=false]),:hover,:active,:focus\n),[type=reset]:is([aria-current]:not([aria-current=false]),:hover,:active,:focus){--pico-background-color:var(--pico-secondary-hover-background);--pico-border-color:var(--pico-secondary-hover-border);--pico-color:var(--pico-secondary-inverse)}:is(button,[type=submit],[type=button],[role=button]).secondary:focus,:is(button,[type=submit],[type=button],[role=button]).secondary:is(\n[aria-current]:not([aria-current=false])\n):focus,[type=file]::file-selector-button:focus,[type=file]::file-selector-button:is([aria-current]:not([aria-current=false])):focus,[type=reset]:focus,[type=reset]:is([aria-current]:not([aria-current=false])):focus{--pico-box-shadow:var(--pico-button-hover-box-shadow, 0 0 0 rgba(0, 0, 0, 0)),0 0 0 var(--pico-outline-width) var(--pico-secondary-focus)}:is(button,[type=submit],[type=button],[role=button]).contrast{--pico-background-color:var(--pico-contrast-background);--pico-border-color:var(--pico-contrast-border);--pico-color:var(--pico-contrast-inverse)}:is(button,[type=submit],[type=button],[role=button]).contrast:is(\n[aria-current]:not([aria-current=false]),:hover,:active,:focus\n){--pico-background-color:var(--pico-contrast-hover-background);--pico-border-color:var(--pico-contrast-hover-border);--pico-color:var(--pico-contrast-inverse)}:is(button,[type=submit],[type=button],[role=button]).contrast:focus,:is(button,[type=submit],[type=button],[role=button]).contrast:is(\n[aria-current]:not([aria-current=false])\n):focus{--pico-box-shadow:var(--pico-button-hover-box-shadow, 0 0 0 rgba(0, 0, 0, 0)),0 0 0 var(--pico-outline-width) var(--pico-contrast-focus)}:is(button,[type=submit],[type=button],[role=button]).outline,[type=reset].outline{--pico-background-color:transparent;--pico-color:var(--pico-primary);--pico-border-color:var(--pico-primary)}:is(button,[type=submit],[type=button],[role=button]).outline:is(\n[aria-current]:not([aria-current=false]),:hover,:active,:focus\n),[type=reset].outline:is([aria-current]:not([aria-current=false]),:hover,:active,:focus){--pico-background-color:transparent;--pico-color:var(--pico-primary-hover);--pico-border-color:var(--pico-primary-hover)}:is(button,[type=submit],[type=button],[role=button]).outline.secondary,[type=reset].outline{--pico-color:var(--pico-secondary);--pico-border-color:var(--pico-secondary)}:is(button,[type=submit],[type=button],[role=button]).outline.secondary:is(\n[aria-current]:not([aria-current=false]),:hover,:active,:focus\n),[type=reset].outline:is([aria-current]:not([aria-current=false]),:hover,:active,:focus){--pico-color:var(--pico-secondary-hover);--pico-border-color:var(--pico-secondary-hover)}:is(button,[type=submit],[type=button],[role=button]).outline.contrast{--pico-color:var(--pico-contrast);--pico-border-color:var(--pico-contrast)}:is(button,[type=submit],[type=button],[role=button]).outline.contrast:is(\n[aria-current]:not([aria-current=false]),:hover,:active,:focus\n){--pico-color:var(--pico-contrast-hover);--pico-border-color:var(--pico-contrast-hover)}:where(button,[type=submit],[type=reset],[type=button],[role=button])[disabled],:where(fieldset[disabled]) :is(button,[type=submit],[type=button],[type=reset],[role=button]){opacity:.5;pointer-events:none}:where(table){width:100%;border-collapse:collapse;border-spacing:0;text-indent:0}td,th{padding:calc(var(--pico-spacing)/ 2) var(--pico-spacing);border-bottom:var(--pico-border-width) solid var(--pico-table-border-color);background-color:var(--pico-background-color);color:var(--pico-color);font-weight:var(--pico-font-weight);text-align:start}tfoot td,tfoot th{border-top:var(--pico-border-width) solid var(--pico-table-border-color);border-bottom:0}table.striped tbody tr:nth-child(odd) td,table.striped tbody tr:nth-child(odd) th{background-color:var(--pico-table-row-stripped-background-color)}:where(audio,canvas,iframe,img,svg,video){vertical-align:middle}audio,video{display:inline-block}audio:not([controls]){display:none;height:0}:where(iframe){border-style:none}img:not([height]){max-width:100%;height:auto}img{border-style:none}:where(svg:not([fill])){fill:currentColor}svg:not(:host),svg:not(:scope){overflow:hidden}code,kbd,pre,samp,var{font-size:.875em;font-family:var(--pico-font-family)}pre code,pre samp{font-size:inherit;font-family:inherit}pre{-ms-overflow-style:scrollbar;overflow:auto}code,kbd,pre,samp,var{border-radius:var(--pico-border-radius);background:var(--pico-code-background-color);color:var(--pico-code-color);font-weight:var(--pico-font-weight);line-height:initial}code,kbd,samp,var{display:inline-block;padding:.375rem}pre{display:block;margin-bottom:var(--pico-spacing);overflow-x:auto}pre>code,pre>samp{display:block;padding:var(--pico-spacing);background:0 0;line-height:var(--pico-line-height)}kbd{background-color:var(--pico-code-kbd-background-color);color:var(--pico-code-kbd-color);vertical-align:baseline}figure{display:block;margin:0;padding:0}figure figcaption{padding:calc(var(--pico-spacing) * .5) 0;color:var(--pico-muted-color)}hr{height:0;margin:var(--pico-typography-spacing-vertical) 0;border:0;border-top:1px solid var(--pico-muted-border-color);color:inherit}[hidden],template{display:none!important}canvas{display:inline-block}input,optgroup,select,textarea{margin:0;font-size:1rem;line-height:var(--pico-line-height);font-family:inherit;letter-spacing:inherit}input{overflow:visible}select{text-transform:none}legend{max-width:100%;padding:0;color:inherit;white-space:normal}textarea{overflow:auto}[type=checkbox],[type=radio]{padding:0}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}::-moz-focus-inner{padding:0;border-style:none}:-moz-focusring{outline:0}:-moz-ui-invalid{box-shadow:none}::-ms-expand{display:none}[type=file],[type=range]{padding:0;border-width:0}input:not([type=checkbox],[type=radio],[type=range]){height:calc(1rem * var(--pico-line-height) + var(--pico-form-element-spacing-vertical) * 2 + var(--pico-border-width) * 2)}fieldset{width:100%;min-width:0;margin:0;margin-bottom:var(--pico-spacing);padding:0;border:0}fieldset legend,label{display:block;margin-bottom:calc(var(--pico-spacing) * .375);color:var(--pico-color);font-weight:var(--pico-form-label-font-weight,var(--pico-font-weight))}fieldset legend{margin-bottom:calc(var(--pico-spacing) * .5)}button[type=submit],input:not([type=checkbox],[type=radio]),select,textarea{width:100%}input:not([type=checkbox],[type=radio],[type=range],[type=file]),select,textarea{-webkit-appearance:none;-moz-appearance:none;appearance:none;padding:var(--pico-form-element-spacing-vertical) var(--pico-form-element-spacing-horizontal)}input,select,textarea{--pico-background-color:var(--pico-form-element-background-color);--pico-border-color:var(--pico-form-element-border-color);--pico-color:var(--pico-form-element-color);--pico-box-shadow:none;border:var(--pico-border-width) solid var(--pico-border-color);border-radius:var(--pico-border-radius);outline:0;background-color:var(--pico-background-color);box-shadow:var(--pico-box-shadow);color:var(--pico-color);font-weight:var(--pico-font-weight);transition:background-color var(--pico-transition),border-color var(--pico-transition),color var(--pico-transition),box-shadow var(--pico-transition)}:where(select,textarea):not([readonly]):is(:active,:focus),input:not(\n[type=submit],[type=button],[type=reset],[type=checkbox],[type=radio],[readonly]\n):is(:active,:focus){--pico-background-color:var(--pico-form-element-active-background-color)}:where(select,textarea):not([readonly]):is(:active,:focus),input:not([type=submit],[type=button],[type=reset],[role=switch],[readonly]):is(\n:active,:focus\n){--pico-border-color:var(--pico-form-element-active-border-color)}:where(select,textarea):not([readonly]):focus,input:not(\n[type=submit],[type=button],[type=reset],[type=range],[type=file],[readonly]\n):focus{--pico-box-shadow:0 0 0 var(--pico-outline-width) var(--pico-form-element-focus-color)}:where(fieldset[disabled]) :is(input:not([type=submit],[type=button],[type=reset]),select,textarea),input:not([type=submit],[type=button],[type=reset])[disabled],label[aria-disabled=true],select[disabled],textarea[disabled]{opacity:var(--pico-form-element-disabled-opacity);pointer-events:none}label[aria-disabled=true] input[disabled]{opacity:1}:where(input,select,textarea):not(\n[type=checkbox],[type=radio],[type=date],[type=datetime-local],[type=month],[type=time],[type=week],[type=range]\n)[aria-invalid]{padding-right:calc(var(--pico-form-element-spacing-horizontal) + 1.5rem)!important;padding-left:var(--pico-form-element-spacing-horizontal);padding-inline-start:var(--pico-form-element-spacing-horizontal)!important;padding-inline-end:calc(var(--pico-form-element-spacing-horizontal) + 1.5rem)!important;background-position:center right .75rem;background-size:1rem auto;background-repeat:no-repeat}:where(input,select,textarea):not(\n[type=checkbox],[type=radio],[type=date],[type=datetime-local],[type=month],[type=time],[type=week],[type=range]\n)[aria-invalid=false]:not(select){background-image:var(--pico-icon-valid)}:where(input,select,textarea):not(\n[type=checkbox],[type=radio],[type=date],[type=datetime-local],[type=month],[type=time],[type=week],[type=range]\n)[aria-invalid=true]:not(select){background-image:var(--pico-icon-invalid)}:where(input,select,textarea)[aria-invalid=false]{--pico-border-color:var(--pico-form-element-valid-border-color)}:where(input,select,textarea)[aria-invalid=false]:is(:active,:focus){--pico-border-color:var(--pico-form-element-valid-active-border-color)!important}:where(input,select,textarea)[aria-invalid=false]:is(:active,:focus):not(\n[type=checkbox],[type=radio]\n){--pico-box-shadow:0 0 0 var(--pico-outline-width) var(--pico-form-element-valid-focus-color)!important}:where(input,select,textarea)[aria-invalid=true]{--pico-border-color:var(--pico-form-element-invalid-border-color)}:where(input,select,textarea)[aria-invalid=true]:is(:active,:focus){--pico-border-color:var(--pico-form-element-invalid-active-border-color)!important}:where(input,select,textarea)[aria-invalid=true]:is(:active,:focus):not(\n[type=checkbox],[type=radio]\n){--pico-box-shadow:0 0 0 var(--pico-outline-width) var(--pico-form-element-invalid-focus-color)!important}[dir=rtl] :where(input,select,textarea):not([type=checkbox],[type=radio]):is(\n[aria-invalid],[aria-invalid=true],[aria-invalid=false]\n){background-position:center left .75rem}input::-webkit-input-placeholder,input::placeholder,select:invalid,textarea::-webkit-input-placeholder,textarea::placeholder{color:var(--pico-form-element-placeholder-color);opacity:1}input:not([type=checkbox],[type=radio]),select,textarea{margin-bottom:var(--pico-spacing)}select::-ms-expand{border:0;background-color:transparent}select:not([multiple],[size]){padding-right:calc(var(--pico-form-element-spacing-horizontal) + 1.5rem);padding-left:var(--pico-form-element-spacing-horizontal);padding-inline-start:var(--pico-form-element-spacing-horizontal);padding-inline-end:calc(var(--pico-form-element-spacing-horizontal) + 1.5rem);background-image:var(--pico-icon-chevron);background-position:center right .75rem;background-size:1rem auto;background-repeat:no-repeat}select[multiple] option:checked{background:var(--pico-form-element-selected-background-color);color:var(--pico-form-element-color)}[dir=rtl] select:not([multiple],[size]){background-position:center left .75rem}textarea{display:block;resize:vertical}textarea[aria-invalid]{--pico-icon-height:calc(\n 1rem * var(--pico-line-height) + var(--pico-form-element-spacing-vertical) * 2 +\n var(--pico-border-width) * 2\n );background-position:top right .75rem!important;background-size:1rem var(--pico-icon-height)!important}:where(input,select,textarea,fieldset,.grid)+small{display:block;width:100%;margin-top:calc(var(--pico-spacing) * -.75);margin-bottom:var(--pico-spacing);color:var(--pico-muted-color)}:where(input,select,textarea,fieldset,.grid)[aria-invalid=false]+small{color:var(--pico-ins-color)}:where(input,select,textarea,fieldset,.grid)[aria-invalid=true]+small{color:var(--pico-del-color)}label>:where(input,select,textarea){margin-top:calc(var(--pico-spacing) * .25)}label:has([type=checkbox],[type=radio]){width:-moz-fit-content;width:fit-content;cursor:pointer}[type=checkbox],[type=radio]{-webkit-appearance:none;-moz-appearance:none;appearance:none;width:1.25em;height:1.25em;margin-top:-.125em;margin-inline-end:.5em;border-width:var(--pico-border-width);vertical-align:middle;cursor:pointer}[type=checkbox]::-ms-check,[type=radio]::-ms-check{display:none}[type=checkbox]:checked,[type=checkbox]:checked:active,[type=checkbox]:checked:focus,[type=radio]:checked,[type=radio]:checked:active,[type=radio]:checked:focus{--pico-background-color:var(--pico-primary-background);--pico-border-color:var(--pico-primary-border);background-image:var(--pico-icon-checkbox);background-position:center;background-size:.75em auto;background-repeat:no-repeat}[type=checkbox]~label,[type=radio]~label{display:inline-block;margin-bottom:0;cursor:pointer}[type=checkbox]~label:not(:last-of-type),[type=radio]~label:not(:last-of-type){margin-inline-end:1em}[type=checkbox]:indeterminate{--pico-background-color:var(--pico-primary-background);--pico-border-color:var(--pico-primary-border);background-image:var(--pico-icon-minus);background-position:center;background-size:.75em auto;background-repeat:no-repeat}[type=radio]{border-radius:50%}[type=radio]:checked,[type=radio]:checked:active,[type=radio]:checked:focus{--pico-background-color:var(--pico-primary-inverse);border-width:.35em;background-image:none}[type=checkbox][role=switch]{--pico-background-color:var(--pico-switch-background-color);--pico-color:var(--pico-switch-color);width:2.25em;height:1.25em;border:var(--pico-border-width) solid var(--pico-border-color);border-radius:1.25em;background-color:var(--pico-background-color);line-height:1.25em}[type=checkbox][role=switch]:not([aria-invalid]){--pico-border-color:var(--pico-switch-background-color)}[type=checkbox][role=switch]:before{display:block;aspect-ratio:1;height:100%;border-radius:50%;background-color:var(--pico-color);box-shadow:var(--pico-switch-thumb-box-shadow);content:\"\";transition:margin .1s ease-in-out}[type=checkbox][role=switch]:focus{--pico-background-color:var(--pico-switch-background-color);--pico-border-color:var(--pico-switch-background-color)}[type=checkbox][role=switch]:checked{--pico-background-color:var(--pico-switch-checked-background-color);--pico-border-color:var(--pico-switch-checked-background-color);background-image:none}[type=checkbox][role=switch]:checked::before{margin-inline-start:calc(2.25em - 1.25em)}[type=checkbox][role=switch][disabled]{--pico-background-color:var(--pico-border-color)}[type=checkbox][aria-invalid=false]:checked,[type=checkbox][aria-invalid=false]:checked:active,[type=checkbox][aria-invalid=false]:checked:focus,[type=checkbox][role=switch][aria-invalid=false]:checked,[type=checkbox][role=switch][aria-invalid=false]:checked:active,[type=checkbox][role=switch][aria-invalid=false]:checked:focus{--pico-background-color:var(--pico-form-element-valid-border-color)}[type=checkbox]:checked:active[aria-invalid=true],[type=checkbox]:checked:focus[aria-invalid=true],[type=checkbox]:checked[aria-invalid=true],[type=checkbox][role=switch]:checked:active[aria-invalid=true],[type=checkbox][role=switch]:checked:focus[aria-invalid=true],[type=checkbox][role=switch]:checked[aria-invalid=true]{--pico-background-color:var(--pico-form-element-invalid-border-color)}[type=checkbox][aria-invalid=false]:checked,[type=checkbox][aria-invalid=false]:checked:active,[type=checkbox][aria-invalid=false]:checked:focus,[type=checkbox][role=switch][aria-invalid=false]:checked,[type=checkbox][role=switch][aria-invalid=false]:checked:active,[type=checkbox][role=switch][aria-invalid=false]:checked:focus,[type=radio][aria-invalid=false]:checked,[type=radio][aria-invalid=false]:checked:active,[type=radio][aria-invalid=false]:checked:focus{--pico-border-color:var(--pico-form-element-valid-border-color)}[type=checkbox]:checked:active[aria-invalid=true],[type=checkbox]:checked:focus[aria-invalid=true],[type=checkbox]:checked[aria-invalid=true],[type=checkbox][role=switch]:checked:active[aria-invalid=true],[type=checkbox][role=switch]:checked:focus[aria-invalid=true],[type=checkbox][role=switch]:checked[aria-invalid=true],[type=radio]:checked:active[aria-invalid=true],[type=radio]:checked:focus[aria-invalid=true],[type=radio]:checked[aria-invalid=true]{--pico-border-color:var(--pico-form-element-invalid-border-color)}[type=color]::-webkit-color-swatch-wrapper{padding:0}[type=color]::-moz-focus-inner{padding:0}[type=color]::-webkit-color-swatch{border:0;border-radius:calc(var(--pico-border-radius) * .5)}[type=color]::-moz-color-swatch{border:0;border-radius:calc(var(--pico-border-radius) * .5)}input:not([type=checkbox],[type=radio],[type=range],[type=file]):is(\n[type=date],[type=datetime-local],[type=month],[type=time],[type=week]\n){--pico-icon-position:0.75rem;--pico-icon-width:1rem;min-width:0;padding-right:calc(var(--pico-icon-width) + var(--pico-icon-position));background-image:var(--pico-icon-date);background-position:center right var(--pico-icon-position);background-size:var(--pico-icon-width) auto;background-repeat:no-repeat}input:not([type=checkbox],[type=radio],[type=range],[type=file])[type=time]{background-image:var(--pico-icon-time)}[type=date]::-webkit-calendar-picker-indicator,[type=datetime-local]::-webkit-calendar-picker-indicator,[type=month]::-webkit-calendar-picker-indicator,[type=time]::-webkit-calendar-picker-indicator,[type=week]::-webkit-calendar-picker-indicator{width:var(--pico-icon-width);margin-right:calc(var(--pico-icon-width) * -1);margin-left:var(--pico-icon-position);opacity:0}@-moz-document url-prefix(){[type=date],[type=datetime-local],[type=month],[type=time],[type=week]{padding-right:var(--pico-form-element-spacing-horizontal)!important;background-image:none!important}}[dir=rtl] :is([type=date],[type=datetime-local],[type=month],[type=time],[type=week]){text-align:end}[type=file]{--pico-color:var(--pico-muted-color);margin-left:calc(var(--pico-outline-width) * -1);padding:calc(var(--pico-form-element-spacing-vertical) * .5) 0;padding-left:var(--pico-outline-width);border:0;border-radius:0;background:0 0}[type=file]::file-selector-button{margin-right:calc(var(--pico-spacing)/ 2);padding:calc(var(--pico-form-element-spacing-vertical) * .5) var(--pico-form-element-spacing-horizontal)}[type=file]:is(:hover,:active,:focus)::file-selector-button{--pico-background-color:var(--pico-secondary-hover-background);--pico-border-color:var(--pico-secondary-hover-border)}[type=file]:focus::file-selector-button{--pico-box-shadow:var(--pico-button-hover-box-shadow, 0 0 0 rgba(0, 0, 0, 0)),0 0 0 var(--pico-outline-width) var(--pico-secondary-focus)}[type=range]{-webkit-appearance:none;-moz-appearance:none;appearance:none;width:100%;height:1.25rem;background:0 0}[type=range]::-webkit-slider-runnable-track{width:100%;height:.375rem;border-radius:var(--pico-border-radius);background-color:var(--pico-range-border-color);-webkit-transition:background-color var(--pico-transition),box-shadow var(--pico-transition);transition:background-color var(--pico-transition),box-shadow var(--pico-transition)}[type=range]::-moz-range-track{width:100%;height:.375rem;border-radius:var(--pico-border-radius);background-color:var(--pico-range-border-color);-moz-transition:background-color var(--pico-transition),box-shadow var(--pico-transition);transition:background-color var(--pico-transition),box-shadow var(--pico-transition)}[type=range]::-ms-track{width:100%;height:.375rem;border-radius:var(--pico-border-radius);background-color:var(--pico-range-border-color);-ms-transition:background-color var(--pico-transition),box-shadow var(--pico-transition);transition:background-color var(--pico-transition),box-shadow var(--pico-transition)}[type=range]::-webkit-slider-thumb{-webkit-appearance:none;width:1.25rem;height:1.25rem;margin-top:-.4375rem;border:2px solid var(--pico-range-thumb-border-color);border-radius:50%;background-color:var(--pico-range-thumb-color);cursor:pointer;-webkit-transition:background-color var(--pico-transition),transform var(--pico-transition);transition:background-color var(--pico-transition),transform var(--pico-transition)}[type=range]::-moz-range-thumb{-webkit-appearance:none;width:1.25rem;height:1.25rem;margin-top:-.4375rem;border:2px solid var(--pico-range-thumb-border-color);border-radius:50%;background-color:var(--pico-range-thumb-color);cursor:pointer;-moz-transition:background-color var(--pico-transition),transform var(--pico-transition);transition:background-color var(--pico-transition),transform var(--pico-transition)}[type=range]::-ms-thumb{-webkit-appearance:none;width:1.25rem;height:1.25rem;margin-top:-.4375rem;border:2px solid var(--pico-range-thumb-border-color);border-radius:50%;background-color:var(--pico-range-thumb-color);cursor:pointer;-ms-transition:background-color var(--pico-transition),transform var(--pico-transition);transition:background-color var(--pico-transition),transform var(--pico-transition)}[type=range]:active,[type=range]:focus-within{--pico-range-border-color:var(--pico-range-active-border-color);--pico-range-thumb-color:var(--pico-range-thumb-active-color)}[type=range]:active::-webkit-slider-thumb{transform:scale(1.25)}[type=range]:active::-moz-range-thumb{transform:scale(1.25)}[type=range]:active::-ms-thumb{transform:scale(1.25)}input:not([type=checkbox],[type=radio],[type=range],[type=file])[type=search]{padding-inline-start:calc(var(--pico-form-element-spacing-horizontal) + 1.75rem);background-image:var(--pico-icon-search);background-position:center left calc(var(--pico-form-element-spacing-horizontal) + .125rem);background-size:1rem auto;background-repeat:no-repeat}input:not(\n[type=checkbox],[type=radio],[type=range],[type=file]\n)[type=search][aria-invalid]{padding-inline-start:calc(var(--pico-form-element-spacing-horizontal) + 1.75rem)!important;background-position:center left 1.125rem,center right .75rem}input:not(\n[type=checkbox],[type=radio],[type=range],[type=file]\n)[type=search][aria-invalid=false]{background-image:var(--pico-icon-search),var(--pico-icon-valid)}input:not(\n[type=checkbox],[type=radio],[type=range],[type=file]\n)[type=search][aria-invalid=true]{background-image:var(--pico-icon-search),var(--pico-icon-invalid)}[dir=rtl] :where(input):not(\n[type=checkbox],[type=radio],[type=range],[type=file]\n)[type=search]{background-position:center right 1.125rem}[dir=rtl] :where(input):not(\n[type=checkbox],[type=radio],[type=range],[type=file]\n)[type=search][aria-invalid]{background-position:center right 1.125rem,center left .75rem}details{display:block;margin-bottom:var(--pico-spacing)}details summary{line-height:1rem;list-style-type:none;cursor:pointer;transition:color var(--pico-transition)}details summary:not([role]){color:var(--pico-accordion-close-summary-color)}details summary::-webkit-details-marker{display:none}details summary::marker{display:none}details summary::-moz-list-bullet{list-style-type:none}details summary::after{display:block;width:1rem;height:1rem;margin-inline-start:calc(var(--pico-spacing,1rem) * .5);float:right;transform:rotate(-90deg);background-image:var(--pico-icon-chevron);background-position:right center;background-size:1rem auto;background-repeat:no-repeat;content:\"\";transition:transform var(--pico-transition)}details summary:focus{outline:0}details summary:focus:not([role]){color:var(--pico-accordion-active-summary-color)}details summary:focus-visible:not([role]){outline:var(--pico-outline-width) solid var(--pico-primary-focus);outline-offset:calc(var(--pico-spacing,1rem) * 0.5);color:var(--pico-primary)}details summary[role=button]{width:100%;text-align:start}details summary[role=button]::after{height:calc(1rem * var(--pico-line-height,1.5))}details[open]>summary{margin-bottom:var(--pico-spacing)}details[open]>summary:not([role]):not(:focus){color:var(--pico-accordion-open-summary-color)}details[open]>summary::after{transform:rotate(0)}[dir=rtl] details summary{text-align:end}[dir=rtl] details summary::after{float:left;background-position:left center}article{margin-bottom:var(--pico-block-spacing-vertical);padding:var(--pico-block-spacing-vertical) var(--pico-block-spacing-horizontal);border-radius:var(--pico-border-radius);background:var(--pico-card-background-color);box-shadow:var(--pico-card-box-shadow)}article>footer,article>header{margin-right:calc(var(--pico-block-spacing-horizontal) * -1);margin-left:calc(var(--pico-block-spacing-horizontal) * -1);padding:calc(var(--pico-block-spacing-vertical) * .66) var(--pico-block-spacing-horizontal);background-color:var(--pico-card-sectioning-background-color)}article>header{margin-top:calc(var(--pico-block-spacing-vertical) * -1);margin-bottom:var(--pico-block-spacing-vertical);border-bottom:var(--pico-border-width) solid var(--pico-card-border-color);border-top-right-radius:var(--pico-border-radius);border-top-left-radius:var(--pico-border-radius)}article>footer{margin-top:var(--pico-block-spacing-vertical);margin-bottom:calc(var(--pico-block-spacing-vertical) * -1);border-top:var(--pico-border-width) solid var(--pico-card-border-color);border-bottom-right-radius:var(--pico-border-radius);border-bottom-left-radius:var(--pico-border-radius)}details.dropdown{position:relative;border-bottom:none}details.dropdown>a::after,details.dropdown>button::after,details.dropdown>summary::after{display:block;width:1rem;height:calc(1rem * var(--pico-line-height,1.5));margin-inline-start:.25rem;float:right;transform:rotate(0) translateX(.2rem);background-image:var(--pico-icon-chevron);background-position:right center;background-size:1rem auto;background-repeat:no-repeat;content:\"\"}nav details.dropdown{margin-bottom:0}details.dropdown>summary:not([role]){height:calc(1rem * var(--pico-line-height) + var(--pico-form-element-spacing-vertical) * 2 + var(--pico-border-width) * 2);padding:var(--pico-form-element-spacing-vertical) var(--pico-form-element-spacing-horizontal);border:var(--pico-border-width) solid var(--pico-form-element-border-color);border-radius:var(--pico-border-radius);background-color:var(--pico-form-element-background-color);color:var(--pico-form-element-placeholder-color);line-height:inherit;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;transition:background-color var(--pico-transition),border-color var(--pico-transition),color var(--pico-transition),box-shadow var(--pico-transition);user-select:none}details.dropdown>summary:not([role]):active,details.dropdown>summary:not([role]):focus{border-color:var(--pico-form-element-active-border-color);background-color:var(--pico-form-element-active-background-color)}details.dropdown>summary:not([role]):focus{box-shadow:0 0 0 var(--pico-outline-width) var(--pico-form-element-focus-color)}details.dropdown>summary:not([role]):focus-visible{outline:0}details.dropdown>summary:not([role])[aria-invalid=false]{--pico-form-element-border-color:var(--pico-form-element-valid-border-color);--pico-form-element-active-border-color:var(--pico-form-element-valid-focus-color);--pico-form-element-focus-color:var(--pico-form-element-valid-focus-color)}details.dropdown>summary:not([role])[aria-invalid=true]{--pico-form-element-border-color:var(--pico-form-element-invalid-border-color);--pico-form-element-active-border-color:var(--pico-form-element-invalid-focus-color);--pico-form-element-focus-color:var(--pico-form-element-invalid-focus-color)}nav details.dropdown{display:inline-block;margin:calc(var(--pico-nav-element-spacing-vertical) * -1) 0}nav details.dropdown>summary::after{transform:rotate(0) translateX(0)}nav details.dropdown>summary:not([role]){height:calc(1rem * var(--pico-line-height) + var(--pico-nav-link-spacing-vertical) * 2);padding:calc(var(--pico-nav-link-spacing-vertical) - var(--pico-border-width) * 2) var(--pico-nav-link-spacing-horizontal)}nav details.dropdown>summary:not([role]):focus-visible{box-shadow:0 0 0 var(--pico-outline-width) var(--pico-primary-focus)}details.dropdown>summary+ul{display:flex;z-index:99;position:absolute;left:0;flex-direction:column;width:100%;min-width:-moz-fit-content;min-width:fit-content;margin:0;margin-top:var(--pico-outline-width);padding:0;border:var(--pico-border-width) solid var(--pico-dropdown-border-color);border-radius:var(--pico-border-radius);background-color:var(--pico-dropdown-background-color);box-shadow:var(--pico-dropdown-box-shadow);color:var(--pico-dropdown-color);white-space:nowrap;opacity:0;transition:opacity var(--pico-transition),transform 0s ease-in-out 1s}details.dropdown>summary+ul[dir=rtl]{right:0;left:auto}details.dropdown>summary+ul li{width:100%;margin-bottom:0;padding:calc(var(--pico-form-element-spacing-vertical) * .5) var(--pico-form-element-spacing-horizontal);list-style:none}details.dropdown>summary+ul li:first-of-type{margin-top:calc(var(--pico-form-element-spacing-vertical) * .5)}details.dropdown>summary+ul li:last-of-type{margin-bottom:calc(var(--pico-form-element-spacing-vertical) * .5)}details.dropdown>summary+ul li a{display:block;margin:calc(var(--pico-form-element-spacing-vertical) * -.5) calc(var(--pico-form-element-spacing-horizontal) * -1);padding:calc(var(--pico-form-element-spacing-vertical) * .5) var(--pico-form-element-spacing-horizontal);overflow:hidden;border-radius:0;color:var(--pico-dropdown-color);text-decoration:none;text-overflow:ellipsis}details.dropdown>summary+ul li a:active,details.dropdown>summary+ul li a:focus,details.dropdown>summary+ul li a:focus-visible,details.dropdown>summary+ul li a:hover,details.dropdown>summary+ul li a[aria-current]:not([aria-current=false]){background-color:var(--pico-dropdown-hover-background-color)}details.dropdown>summary+ul li label{width:100%}details.dropdown>summary+ul li:has(label):hover{background-color:var(--pico-dropdown-hover-background-color)}details.dropdown[open]>summary{margin-bottom:0}details.dropdown[open]>summary+ul{transform:scaleY(1);opacity:1;transition:opacity var(--pico-transition),transform 0s ease-in-out 0s}details.dropdown[open]>summary::before{display:block;z-index:1;position:fixed;width:100vw;height:100vh;inset:0;background:0 0;content:\"\";cursor:default}label>details.dropdown{margin-top:calc(var(--pico-spacing) * .25)}[role=group],[role=search]{display:inline-flex;position:relative;width:100%;margin-bottom:var(--pico-spacing);border-radius:var(--pico-border-radius);box-shadow:var(--pico-group-box-shadow,0 0 0 transparent);vertical-align:middle;transition:box-shadow var(--pico-transition)}[role=group] input:not([type=checkbox],[type=radio]),[role=group] select,[role=group]>*,[role=search] input:not([type=checkbox],[type=radio]),[role=search] select,[role=search]>*{position:relative;flex:1 1 auto;margin-bottom:0}[role=group] input:not([type=checkbox],[type=radio]):not(:first-child),[role=group] select:not(:first-child),[role=group]>:not(:first-child),[role=search] input:not([type=checkbox],[type=radio]):not(:first-child),[role=search] select:not(:first-child),[role=search]>:not(:first-child){margin-left:0;border-top-left-radius:0;border-bottom-left-radius:0}[role=group] input:not([type=checkbox],[type=radio]):not(:last-child),[role=group] select:not(:last-child),[role=group]>:not(:last-child),[role=search] input:not([type=checkbox],[type=radio]):not(:last-child),[role=search] select:not(:last-child),[role=search]>:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}[role=group] input:not([type=checkbox],[type=radio]):focus,[role=group] select:focus,[role=group]>:focus,[role=search] input:not([type=checkbox],[type=radio]):focus,[role=search] select:focus,[role=search]>:focus{z-index:2}[role=group] [role=button]:not(:first-child),[role=group] [type=button]:not(:first-child),[role=group] [type=reset]:not(:first-child),[role=group] [type=submit]:not(:first-child),[role=group] button:not(:first-child),[role=group] input:not([type=checkbox],[type=radio]):not(:first-child),[role=group] select:not(:first-child),[role=search] [role=button]:not(:first-child),[role=search] [type=button]:not(:first-child),[role=search] [type=reset]:not(:first-child),[role=search] [type=submit]:not(:first-child),[role=search] button:not(:first-child),[role=search] input:not([type=checkbox],[type=radio]):not(:first-child),[role=search] select:not(:first-child){margin-left:calc(var(--pico-border-width) * -1)}[role=group] [role=button],[role=group] [type=button],[role=group] [type=reset],[role=group] [type=submit],[role=group] button,[role=search] [role=button],[role=search] [type=button],[role=search] [type=reset],[role=search] [type=submit],[role=search] button{width:auto}@supports selector(:has(*)){[role=group]:has(\nbutton:focus,[type=submit]:focus,[type=button]:focus,[role=button]:focus\n),[role=search]:has(\nbutton:focus,[type=submit]:focus,[type=button]:focus,[role=button]:focus\n){--pico-group-box-shadow:var(--pico-group-box-shadow-focus-with-button)}[role=group]:has(\nbutton:focus,[type=submit]:focus,[type=button]:focus,[role=button]:focus\n) input:not([type=checkbox],[type=radio]),[role=group]:has(\nbutton:focus,[type=submit]:focus,[type=button]:focus,[role=button]:focus\n) select,[role=search]:has(\nbutton:focus,[type=submit]:focus,[type=button]:focus,[role=button]:focus\n) input:not([type=checkbox],[type=radio]),[role=search]:has(\nbutton:focus,[type=submit]:focus,[type=button]:focus,[role=button]:focus\n) select{border-color:transparent}[role=group]:has(input:not([type=submit],[type=button]):focus,select:focus),[role=search]:has(input:not([type=submit],[type=button]):focus,select:focus){--pico-group-box-shadow:var(--pico-group-box-shadow-focus-with-input)}[role=group]:has(input:not([type=submit],[type=button]):focus,select:focus) [role=button],[role=group]:has(input:not([type=submit],[type=button]):focus,select:focus) [type=button],[role=group]:has(input:not([type=submit],[type=button]):focus,select:focus) [type=submit],[role=group]:has(input:not([type=submit],[type=button]):focus,select:focus) button,[role=search]:has(input:not([type=submit],[type=button]):focus,select:focus) [role=button],[role=search]:has(input:not([type=submit],[type=button]):focus,select:focus) [type=button],[role=search]:has(input:not([type=submit],[type=button]):focus,select:focus) [type=submit],[role=search]:has(input:not([type=submit],[type=button]):focus,select:focus) button{--pico-button-box-shadow:0 0 0 var(--pico-border-width) var(--pico-primary-border);--pico-button-hover-box-shadow:0 0 0 var(--pico-border-width) var(--pico-primary-hover-border)}[role=group] [role=button]:focus,[role=group] [type=button]:focus,[role=group] [type=reset]:focus,[role=group] [type=submit]:focus,[role=group] button:focus,[role=search] [role=button]:focus,[role=search] [type=button]:focus,[role=search] [type=reset]:focus,[role=search] [type=submit]:focus,[role=search] button:focus{box-shadow:none}}[role=search]>:first-child{border-top-left-radius:5rem;border-bottom-left-radius:5rem}[role=search]>:last-child{border-top-right-radius:5rem;border-bottom-right-radius:5rem}[aria-busy=true]:not(input,select,textarea,html,form){white-space:nowrap}[aria-busy=true]:not(input,select,textarea,html,form)::before{display:inline-block;width:1em;height:1em;background-image:var(--pico-icon-loading);background-size:1em auto;background-repeat:no-repeat;content:\"\";vertical-align:-.125em}[aria-busy=true]:not(input,select,textarea,html,form):not(:empty)::before{margin-inline-end:calc(var(--pico-spacing) * .5)}[aria-busy=true]:not(input,select,textarea,html,form):empty{text-align:center}[role=button][aria-busy=true],[type=button][aria-busy=true],[type=reset][aria-busy=true],[type=submit][aria-busy=true],a[aria-busy=true],button[aria-busy=true]{pointer-events:none}:host,:scope{--pico-scrollbar-width:0px}dialog{display:flex;z-index:999;position:fixed;top:0;right:0;bottom:0;left:0;align-items:center;justify-content:center;width:inherit;min-width:100%;height:inherit;min-height:100%;padding:0;border:0;backdrop-filter:var(--pico-modal-overlay-backdrop-filter);background-color:var(--pico-modal-overlay-background-color);color:var(--pico-color)}dialog>article{width:100%;max-height:calc(100vh - var(--pico-spacing) * 2);margin:var(--pico-spacing);overflow:auto}@media (min-width:576px){dialog>article{max-width:510px}}@media (min-width:768px){dialog>article{max-width:700px}}dialog>article>header>*{margin-bottom:0}dialog>article>header .close,dialog>article>header :is(a,button)[rel=prev]{margin:0;margin-left:var(--pico-spacing);padding:0;float:right}dialog>article>footer{text-align:end}dialog>article>footer [role=button],dialog>article>footer button{margin-bottom:0}dialog>article>footer [role=button]:not(:first-of-type),dialog>article>footer button:not(:first-of-type){margin-left:calc(var(--pico-spacing) * .5)}dialog>article .close,dialog>article :is(a,button)[rel=prev]{display:block;width:1rem;height:1rem;margin-top:calc(var(--pico-spacing) * -1);margin-bottom:var(--pico-spacing);margin-left:auto;border:none;background-image:var(--pico-icon-close);background-position:center;background-size:auto 1rem;background-repeat:no-repeat;background-color:transparent;opacity:.5;transition:opacity var(--pico-transition)}dialog>article .close:is([aria-current]:not([aria-current=false]),:hover,:active,:focus),dialog>article :is(a,button)[rel=prev]:is(\n[aria-current]:not([aria-current=false]),:hover,:active,:focus\n){opacity:1}dialog:not([open]),dialog[open=false]{display:none}.modal-is-open{padding-right:var(--pico-scrollbar-width,0);overflow:hidden;pointer-events:none;touch-action:none}.modal-is-open dialog{pointer-events:auto;touch-action:auto}:where(.modal-is-opening,.modal-is-closing) dialog,:where(.modal-is-opening,.modal-is-closing) dialog>article{animation-duration:.2s;animation-timing-function:ease-in-out;animation-fill-mode:both}:where(.modal-is-opening,.modal-is-closing) dialog{animation-duration:.8s;animation-name:modal-overlay}:where(.modal-is-opening,.modal-is-closing) dialog>article{animation-delay:.2s;animation-name:modal}.modal-is-closing dialog,.modal-is-closing dialog>article{animation-delay:0s;animation-direction:reverse}@keyframes modal-overlay{from{backdrop-filter:none;background-color:transparent}}@keyframes modal{from{transform:translateY(-100%);opacity:0}}:where(nav li)::before{float:left;content:\"​\"}nav,nav ul{display:flex}nav{justify-content:space-between;overflow:visible}nav ol,nav ul{align-items:center;margin-bottom:0;padding:0;list-style:none}nav ol:first-of-type,nav ul:first-of-type{margin-left:calc(var(--pico-nav-element-spacing-horizontal) * -1)}nav ol:last-of-type,nav ul:last-of-type{margin-right:calc(var(--pico-nav-element-spacing-horizontal) * -1)}nav li{display:inline-block;margin:0;padding:var(--pico-nav-element-spacing-vertical) var(--pico-nav-element-spacing-horizontal)}nav li :where(a,[role=link]){display:inline-block;margin:calc(var(--pico-nav-link-spacing-vertical) * -1) calc(var(--pico-nav-link-spacing-horizontal) * -1);padding:var(--pico-nav-link-spacing-vertical) var(--pico-nav-link-spacing-horizontal);border-radius:var(--pico-border-radius)}nav li :where(a,[role=link]):not(:hover){text-decoration:none}nav li [role=button],nav li [type=button],nav li button,nav li input:not([type=checkbox],[type=radio],[type=range],[type=file]),nav li select{height:auto;margin-right:inherit;margin-bottom:0;margin-left:inherit;padding:calc(var(--pico-nav-link-spacing-vertical) - var(--pico-border-width) * 2) var(--pico-nav-link-spacing-horizontal)}nav[aria-label=breadcrumb]{align-items:center;justify-content:start}nav[aria-label=breadcrumb] ul li:not(:first-child){margin-inline-start:var(--pico-nav-link-spacing-horizontal)}nav[aria-label=breadcrumb] ul li a{margin:calc(var(--pico-nav-link-spacing-vertical) * -1) 0;margin-inline-start:calc(var(--pico-nav-link-spacing-horizontal) * -1)}nav[aria-label=breadcrumb] ul li:not(:last-child)::after{display:inline-block;position:absolute;width:calc(var(--pico-nav-link-spacing-horizontal) * 4);margin:0 calc(var(--pico-nav-link-spacing-horizontal) * -1);content:var(--pico-nav-breadcrumb-divider);color:var(--pico-muted-color);text-align:center;text-decoration:none;white-space:nowrap}nav[aria-label=breadcrumb] a[aria-current]:not([aria-current=false]){background-color:transparent;color:inherit;text-decoration:none;pointer-events:none}aside li,aside nav,aside ol,aside ul{display:block}aside li{padding:calc(var(--pico-nav-element-spacing-vertical) * .5) var(--pico-nav-element-spacing-horizontal)}aside li a{display:block}aside li [role=button]{margin:inherit}[dir=rtl] nav[aria-label=breadcrumb] ul li:not(:last-child)::after{content:\"\\\\\"}progress{display:inline-block;vertical-align:baseline}progress{-webkit-appearance:none;-moz-appearance:none;display:inline-block;appearance:none;width:100%;height:.5rem;margin-bottom:calc(var(--pico-spacing) * .5);overflow:hidden;border:0;border-radius:var(--pico-border-radius);background-color:var(--pico-progress-background-color);color:var(--pico-progress-color)}progress::-webkit-progress-bar{border-radius:var(--pico-border-radius);background:0 0}progress[value]::-webkit-progress-value{background-color:var(--pico-progress-color);-webkit-transition:inline-size var(--pico-transition);transition:inline-size var(--pico-transition)}progress::-moz-progress-bar{background-color:var(--pico-progress-color)}@media (prefers-reduced-motion:no-preference){progress:indeterminate{background:var(--pico-progress-background-color) linear-gradient(to right,var(--pico-progress-color) 30%,var(--pico-progress-background-color) 30%) top left/150% 150% no-repeat;animation:progress-indeterminate 1s linear infinite}progress:indeterminate[value]::-webkit-progress-value{background-color:transparent}progress:indeterminate::-moz-progress-bar{background-color:transparent}}@media (prefers-reduced-motion:no-preference){[dir=rtl] progress:indeterminate{animation-direction:reverse}}@keyframes progress-indeterminate{0%{background-position:200% 0}100%{background-position:-200% 0}}[data-tooltip]{position:relative}[data-tooltip]:not(a,button,input,[role=button]){border-bottom:1px dotted;text-decoration:none;cursor:help}[data-tooltip]::after,[data-tooltip]::before,[data-tooltip][data-placement=top]::after,[data-tooltip][data-placement=top]::before{display:block;z-index:99;position:absolute;bottom:100%;left:50%;padding:.25rem .5rem;overflow:hidden;transform:translate(-50%,-.25rem);border-radius:var(--pico-border-radius);background:var(--pico-tooltip-background-color);content:attr(data-tooltip);color:var(--pico-tooltip-color);font-style:normal;font-weight:var(--pico-font-weight);font-size:.875rem;text-decoration:none;text-overflow:ellipsis;white-space:nowrap;opacity:0;pointer-events:none}[data-tooltip]::after,[data-tooltip][data-placement=top]::after{padding:0;transform:translate(-50%,0);border-top:.3rem solid;border-right:.3rem solid transparent;border-left:.3rem solid transparent;border-radius:0;background-color:transparent;content:\"\";color:var(--pico-tooltip-background-color)}[data-tooltip][data-placement=bottom]::after,[data-tooltip][data-placement=bottom]::before{top:100%;bottom:auto;transform:translate(-50%,.25rem)}[data-tooltip][data-placement=bottom]:after{transform:translate(-50%,-.3rem);border:.3rem solid transparent;border-bottom:.3rem solid}[data-tooltip][data-placement=left]::after,[data-tooltip][data-placement=left]::before{top:50%;right:100%;bottom:auto;left:auto;transform:translate(-.25rem,-50%)}[data-tooltip][data-placement=left]:after{transform:translate(.3rem,-50%);border:.3rem solid transparent;border-left:.3rem solid}[data-tooltip][data-placement=right]::after,[data-tooltip][data-placement=right]::before{top:50%;right:auto;bottom:auto;left:100%;transform:translate(.25rem,-50%)}[data-tooltip][data-placement=right]:after{transform:translate(-.3rem,-50%);border:.3rem solid transparent;border-right:.3rem solid}[data-tooltip]:focus::after,[data-tooltip]:focus::before,[data-tooltip]:hover::after,[data-tooltip]:hover::before{opacity:1}@media (hover:hover) and (pointer:fine){[data-tooltip]:focus::after,[data-tooltip]:focus::before,[data-tooltip]:hover::after,[data-tooltip]:hover::before{--pico-tooltip-slide-to:translate(-50%, -0.25rem);transform:translate(-50%,.75rem);animation-duration:.2s;animation-fill-mode:forwards;animation-name:tooltip-slide;opacity:0}[data-tooltip]:focus::after,[data-tooltip]:hover::after{--pico-tooltip-caret-slide-to:translate(-50%, 0rem);transform:translate(-50%,-.25rem);animation-name:tooltip-caret-slide}[data-tooltip][data-placement=bottom]:focus::after,[data-tooltip][data-placement=bottom]:focus::before,[data-tooltip][data-placement=bottom]:hover::after,[data-tooltip][data-placement=bottom]:hover::before{--pico-tooltip-slide-to:translate(-50%, 0.25rem);transform:translate(-50%,-.75rem);animation-name:tooltip-slide}[data-tooltip][data-placement=bottom]:focus::after,[data-tooltip][data-placement=bottom]:hover::after{--pico-tooltip-caret-slide-to:translate(-50%, -0.3rem);transform:translate(-50%,-.5rem);animation-name:tooltip-caret-slide}[data-tooltip][data-placement=left]:focus::after,[data-tooltip][data-placement=left]:focus::before,[data-tooltip][data-placement=left]:hover::after,[data-tooltip][data-placement=left]:hover::before{--pico-tooltip-slide-to:translate(-0.25rem, -50%);transform:translate(.75rem,-50%);animation-name:tooltip-slide}[data-tooltip][data-placement=left]:focus::after,[data-tooltip][data-placement=left]:hover::after{--pico-tooltip-caret-slide-to:translate(0.3rem, -50%);transform:translate(.05rem,-50%);animation-name:tooltip-caret-slide}[data-tooltip][data-placement=right]:focus::after,[data-tooltip][data-placement=right]:focus::before,[data-tooltip][data-placement=right]:hover::after,[data-tooltip][data-placement=right]:hover::before{--pico-tooltip-slide-to:translate(0.25rem, -50%);transform:translate(-.75rem,-50%);animation-name:tooltip-slide}[data-tooltip][data-placement=right]:focus::after,[data-tooltip][data-placement=right]:hover::after{--pico-tooltip-caret-slide-to:translate(-0.3rem, -50%);transform:translate(-.05rem,-50%);animation-name:tooltip-caret-slide}}@keyframes tooltip-slide{to{transform:var(--pico-tooltip-slide-to);opacity:1}}@keyframes tooltip-caret-slide{50%{opacity:0}to{transform:var(--pico-tooltip-caret-slide-to);opacity:1}}[aria-controls]{cursor:pointer}[aria-disabled=true],[disabled]{cursor:not-allowed}[aria-hidden=false][hidden]{display:initial}[aria-hidden=false][hidden]:not(:focus){clip:rect(0,0,0,0);position:absolute}[tabindex],a,area,button,input,label,select,summary,textarea{-ms-touch-action:manipulation}[dir=rtl]{direction:rtl}@media (prefers-reduced-motion:reduce){:not([aria-busy=true])::after,:not([aria-busy=true])::before,:not([aria-busy=true]):not(.unreduce-motion){background-attachment:initial!important;animation-duration:1ms!important;animation-delay:-1ms!important;animation-iteration-count:1!important;scroll-behavior:auto!important;transition-delay:0s!important;transition-duration:0s!important}}.columns{-moz-columns:25ch auto;columns:25ch auto}.columns>*{-moz-column-break-inside:avoid;break-inside:avoid}[data-jump-to=top]{position:fixed;right:0;bottom:0;padding-top:50vh}.breakout,.breakout-all{max-width:calc(10% + 65ch + 10%);padding-inline:10%}@media (width < 768px){.breakout,.breakout-all{max-width:calc(1rem + 65ch + 1rem);padding-inline:1rem}}.breakout-all>:is(\ntable,pre,figure,video,iframe,canvas,img,picture,.breakout-item,.breakout-item-max\n),.breakout>:is(\ntable,pre,figure,video,iframe,canvas,img,picture,.breakout-item,.breakout-item-max\n){width:-moz-fit-content;width:fit-content;min-width:100%;max-width:min(125%,100dvw);margin-left:50%;transform:translateX(-50%)}.breakout-all>:is(img,picture):not(.does-not-exist),.breakout>:is(img,picture):not(.does-not-exist){min-width:auto}.breakout-all>.breakout-item-max,.breakout>.breakout-item-max{width:min(125%,100dvw)!important}.breakout-all>:is(h2,h3,h4,h5,h6,hr):not([class]){position:relative}.breakout-all>:is(h2,h3,h4,h5,h6,hr):not([class])::before{display:block;position:absolute;background:gray;content:\"\";opacity:12.5%}.breakout-all>:is(h2,h3,h4,h5,h6):not([class])::before{top:50%;right:100%;width:10em;height:.25em;margin-right:.8ch;transform:translateY(-50%);background:linear-gradient(to left,gray,transparent)}.breakout-all>:is(h2,h3,h4,h5,h6):not([class]):where(hr + *)::before{display:none!important}.breakout-all>:is(hr){height:.5rem;overflow:visible;border:none}.breakout-all>:is(hr)::before{left:50%;width:100dvw;height:100%;transform:translateX(-50%)}h1,h2,h3,h4,h5,h6{position:relative}h1 a[aria-hidden=true],h2 a[aria-hidden=true],h3 a[aria-hidden=true],h4 a[aria-hidden=true],h5 a[aria-hidden=true],h6 a[aria-hidden=true]{visibility:hidden;position:absolute;top:50%;right:100%;padding-right:.2ch;transform:translateY(-50%);color:silver;text-decoration:none}@media (hover:hover){h1:hover a[aria-hidden=true],h2:hover a[aria-hidden=true],h3:hover a[aria-hidden=true],h4:hover a[aria-hidden=true],h5:hover a[aria-hidden=true],h6:hover a[aria-hidden=true]{visibility:visible}}ol[style*=\"--list-marker:\"],ul[style*=\"--list-marker:\"]{list-style-type:var(--list-marker)}ol[style*=\"--list-marker:\"]>li,ul[style*=\"--list-marker:\"]>li{list-style-type:inherit}ol li[style*=\"--list-marker:\"],ul li[style*=\"--list-marker:\"]{list-style-type:var(--list-marker)}ol li[data-marker]::marker,ul li[data-marker]::marker{content:attr(data-marker) \" \"}ol.markerless,ul.markerless{padding-inline-start:1.25rem}ol.markerless li,ul.markerless li{list-style:none}.unlist-all ol,.unlist-all ul,ol.unlist,ol.unlist-all,ul.unlist,ul.unlist-all{padding-inline-start:0}.unlist-all ol>li,.unlist-all ul>li,ol.unlist-all>li,ol.unlist>li,ul.unlist-all>li,ul.unlist>li{list-style:none}a:not(.does-not-exist):has(> i){display:inline-flex;overflow-y:clip;gap:.375ch;text-wrap:balance}a:not(.does-not-exist)>i{float:left;font-style:normal;text-underline-offset:-2em}a:not(.does-not-exist)>i>img{display:inline-block;max-width:none;height:1.25em;margin-block-start:calc(-0.25em / 2)}a:not(.does-not-exist)>i[class*=\" fa-\"],a:not(.does-not-exist)>i[class^=fa-]{line-height:inherit;--fa-width:auto}a:not(.does-not-exist)>i.fa-lg{line-height:normal}th hr{visibility:hidden;width:12ch;height:0;margin:0}th hr.lg{width:18ch}th hr.x2{width:24ch}table.borderless td,table.borderless th{border:none}.breakout-all>table:not(.does-not-exist),.breakout>table:not(.does-not-exist),table.responsive{display:block;width:-moz-max-content;width:max-content;min-width:auto;max-width:100dvw;margin-left:50%;padding-inline:7.5%;overflow-x:auto;transform:translateX(-50%);-webkit-overflow-scrolling:touch}.breakout-all>table:not(.does-not-exist) td,.breakout-all>table:not(.does-not-exist) th,.breakout>table:not(.does-not-exist) td,.breakout>table:not(.does-not-exist) th,table.responsive td,table.responsive th{padding-inline-start:0}@media (width < 768px){pre{border-radius:0}}code[data-caption]::before{display:block;margin-bottom:1rem;content:attr(data-caption);font-style:italic;opacity:50%}.has-float-label,label:has(> span):has(input:not([type=checkbox],[type=radio],[type=range]),textarea,select){display:block;position:relative}.has-float-label label,.has-float-label>span,label:has(> span):has(input:not([type=checkbox],[type=radio],[type=range]),textarea,select) label,label:has(> span):has(input:not([type=checkbox],[type=radio],[type=range]),textarea,select)>span{position:absolute;top:0;left:0;font-size:75%;cursor:text}.has-float-label :placeholder-shown:not(:focus)::-moz-placeholder,label:has(> span):has(input:not([type=checkbox],[type=radio],[type=range]),textarea,select) :placeholder-shown:not(:focus)::-moz-placeholder{opacity:0}.has-float-label :-moz-placeholder:not(:focus)::placeholder,label:has(> span):has(input:not([type=checkbox],[type=radio],[type=range]),textarea,select) :-moz-placeholder:not(:focus)::placeholder{opacity:0}.has-float-label :placeholder-shown:not(:focus)::placeholder,label:has(> span):has(input:not([type=checkbox],[type=radio],[type=range]),textarea,select) :placeholder-shown:not(:focus)::placeholder{opacity:0}.has-float-label:has(:-moz-placeholder:not(:focus)) label,.has-float-label:has(:-moz-placeholder:not(:focus))>span,label:has(> span):has(input:not([type=checkbox],[type=radio],[type=range]),textarea,select):has(:-moz-placeholder:not(:focus)) label,label:has(> span):has(input:not([type=checkbox],[type=radio],[type=range]),textarea,select):has(:-moz-placeholder:not(:focus))>span{font-size:inherit;opacity:50%}.has-float-label:has(:placeholder-shown:not(:focus)) label,.has-float-label:has(:placeholder-shown:not(:focus))>span,label:has(> span):has(input:not([type=checkbox],[type=radio],[type=range]),textarea,select):has(:placeholder-shown:not(:focus)) label,label:has(> span):has(input:not([type=checkbox],[type=radio],[type=range]),textarea,select):has(:placeholder-shown:not(:focus))>span{font-size:inherit;opacity:50%}.has-timeline>dl dd,dl.timeline dd{margin-block:0;padding:.25rem 0 .75rem 1rem;border-inline-start:0.25rem solid gray}:root{--blades-dark-filter:invert(100%) hue-rotate(180deg)}@media (prefers-color-scheme:dark){.dark-auto{filter:var(--blades-dark-filter)}}.dark-auto:where([data-theme=dark] *),.dark-auto[data-theme=dark]{filter:var(--blades-dark-filter)}.faded{opacity:50%}.faded:hover{opacity:87.5%}.invert ::-webkit-scrollbar{filter:invert(1)!important}.has-float-label label,.has-float-label>span,label:has(> span):has(input:not([type=checkbox],[type=radio],[type=range]),textarea,select) label,label:has(> span):has(input:not([type=checkbox],[type=radio],[type=range]),textarea,select)>span{padding-inline-start:calc(1rem + 1px);padding-block-start:0.25rem;opacity:75%;transition:all .25s}.has-float-label input,.has-float-label select,.has-float-label textarea,label:has(> span):has(input:not([type=checkbox],[type=radio],[type=range]),textarea,select) input,label:has(> span):has(input:not([type=checkbox],[type=radio],[type=range]),textarea,select) select,label:has(> span):has(input:not([type=checkbox],[type=radio],[type=range]),textarea,select) textarea{margin-block-start:0;padding-inline-start:1rem;padding-block:1.125rem 0.375rem}.has-float-label input::-moz-placeholder,.has-float-label select::-moz-placeholder,.has-float-label textarea::-moz-placeholder,label:has(> span):has(input:not([type=checkbox],[type=radio],[type=range]),textarea,select) input::-moz-placeholder,label:has(> span):has(input:not([type=checkbox],[type=radio],[type=range]),textarea,select) select::-moz-placeholder,label:has(> span):has(input:not([type=checkbox],[type=radio],[type=range]),textarea,select) textarea::-moz-placeholder{opacity:100%;-moz-transition:all .25s;transition:all .25s}.has-float-label input::placeholder,.has-float-label select::placeholder,.has-float-label textarea::placeholder,label:has(> span):has(input:not([type=checkbox],[type=radio],[type=range]),textarea,select) input::placeholder,label:has(> span):has(input:not([type=checkbox],[type=radio],[type=range]),textarea,select) select::placeholder,label:has(> span):has(input:not([type=checkbox],[type=radio],[type=range]),textarea,select) textarea::placeholder{opacity:100%;transition:all .25s}.has-float-label:has(:-moz-placeholder:not(:focus)) label,.has-float-label:has(:-moz-placeholder:not(:focus))>span,label:has(> span):has(input:not([type=checkbox],[type=radio],[type=range]),textarea,select):has(:-moz-placeholder:not(:focus)) label,label:has(> span):has(input:not([type=checkbox],[type=radio],[type=range]),textarea,select):has(:-moz-placeholder:not(:focus))>span{padding-block:0.75rem}.has-float-label:has(:placeholder-shown:not(:focus)) label,.has-float-label:has(:placeholder-shown:not(:focus))>span,label:has(> span):has(input:not([type=checkbox],[type=radio],[type=range]),textarea,select):has(:placeholder-shown:not(:focus)) label,label:has(> span):has(input:not([type=checkbox],[type=radio],[type=range]),textarea,select):has(:placeholder-shown:not(:focus))>span{padding-block:0.75rem}html{overflow-x:clip;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}body{display:flex;flex-direction:column;min-height:100dvh}body>main{flex-grow:1}body{hyphens:auto;text-wrap:pretty}body a,body table{hyphens:none}a:not([href^=\"#\"]){text-decoration-thickness:1px}a:not([href^=\"#\"]):hover{text-decoration-thickness:2px}h1,h2,h3,h4,h5,h6{text-wrap:balance}h1{margin-bottom:1rem;font-size:2.5em}hr{margin-block:2em}ul ul{font-size:87.5%}dl dt{margin-block:0.25rem;font-weight:500}dl dd{margin-block-end:1rem}pre small{font-weight:lighter;opacity:75%}pre:has(code){background:#181c25!important}table th{font-weight:700;vertical-align:bottom}table td{vertical-align:top}table pre{margin-bottom:.25rem}[data-jump-to=top]{opacity:25%}[data-jump-to=top]:hover{opacity:75%}[data-jump-to=top]>i{display:inline-block;margin:.5rem;padding:.25rem .375rem;border-color:#000;color:#000;font-size:.75rem}.breakout-all>figure,.breakout-all>img,.breakout>figure,.breakout>img{margin-bottom:1rem}.faded a{text-decoration-style:dotted}\n/* The report layer, inlined after the vendored Pico+Blades stylesheet. It holds\n only what no framework ships: the status and severity palette, the dashboard,\n and the print rules. Everything else is driven through --pico-* variables so\n the vendored file is never edited. */\n\n:root {\n /* Stated here rather than inherited so the report's typography is system\n fonts by declaration, with nothing to download. */\n --pico-font-family-sans-serif: system-ui, -apple-system, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif;\n --pico-font-family-monospace: ui-monospace, SFMono-Regular, Menlo, Consolas, \"Liberation Mono\", monospace;\n --pico-font-family: var(--pico-font-family-sans-serif);\n /* Pico scales the root to 131.25% on a wide screen. A compliance report is a\n dense document read at length, so it stays at the browser's own size. */\n --pico-font-size: 100%;\n /* The page's own text colour, captured before the headings below redefine it. */\n --pico-report-ink: var(--pico-color);\n}\n/* Pico's heading scale is for prose; these are the report's section, panel and\n block labels, so they are driven back through its own per-heading token. */\nh2 { --pico-font-size: 1.3rem; letter-spacing: -0.01em; }\nh3 { --pico-font-size: 1.02rem; }\nh4, h5 { --pico-font-size: 0.78rem; margin: 1.2rem 0 0.45rem; color: var(--pico-muted-color); text-transform: uppercase; letter-spacing: 0.07em; }\n/* The three scopes Pico itself uses: light by default or when light is chosen,\n dark when the system asks and nothing was chosen, dark when dark was chosen. */\n:root:not([data-theme=\"dark\"]), [data-theme=\"light\"] {\n /* Pico's own input border is 1.47:1 against the page, under the 3:1 WCAG 1.4.11\n asks of a control boundary; the report raises it in both schemes. */\n --pico-form-element-border-color: #767f91;\n --pico-s-passed: #2e7d32; --pico-s-passed-inverse: #ffffff; --pico-t-passed: #e8f5e9; --pico-t-passed-inverse: #1b5e20;\n --pico-s-failed: #c62828; --pico-s-failed-inverse: #ffffff; --pico-t-failed: #ffebee; --pico-t-failed-inverse: #8e1616;\n --pico-s-not-applicable: #0277bd; --pico-s-not-applicable-inverse: #ffffff; --pico-t-not-applicable: #e1f5fe; --pico-t-not-applicable-inverse: #01497c;\n --pico-s-not-reviewed: #b45300; --pico-s-not-reviewed-inverse: #ffffff; --pico-t-not-reviewed: #fff3e0; --pico-t-not-reviewed-inverse: #7a3600;\n --pico-s-error: #3f51b5; --pico-s-error-inverse: #ffffff; --pico-t-error: #e8eaf6; --pico-t-error-inverse: #1a237e;\n --pico-s-unknown: #5c6670; --pico-s-unknown-inverse: #ffffff; --pico-t-unknown: #e9edf1; --pico-t-unknown-inverse: #16212d;\n --pico-s-critical: #c62828; --pico-s-critical-inverse: #ffffff; --pico-t-critical: #ffebee; --pico-t-critical-inverse: #8e1616;\n --pico-s-high: #bf360c; --pico-s-high-inverse: #ffffff; --pico-t-high: #fbe9e7; --pico-t-high-inverse: #8a2607;\n --pico-s-medium: #b45300; --pico-s-medium-inverse: #ffffff; --pico-t-medium: #fff3e0; --pico-t-medium-inverse: #7a3600;\n --pico-s-low: #806600; --pico-s-low-inverse: #ffffff; --pico-t-low: #fffde7; --pico-t-low-inverse: #5c4a00;\n --pico-s-none: #0277bd; --pico-s-none-inverse: #ffffff; --pico-t-none: #e1f5fe; --pico-t-none-inverse: #01497c;\n}\n@media only screen and (prefers-color-scheme: dark) {\n :root:not([data-theme]) {\n --pico-form-element-border-color: #6b7488;\n /* Pico's dark muted text is 4.41:1 on its own sectioning background. */\n --pico-muted-color: #868fa0;\n --pico-s-passed: #4cb04f; --pico-s-passed-inverse: #06200a; --pico-t-passed: #12301a; --pico-t-passed-inverse: #b9e4bb;\n --pico-s-failed: #ef7a70; --pico-s-failed-inverse: #2b0603; --pico-t-failed: #3d1614; --pico-t-failed-inverse: #ffc9c4;\n --pico-s-not-applicable: #03a9f4; --pico-s-not-applicable-inverse: #02202e; --pico-t-not-applicable: #0c3048; --pico-t-not-applicable-inverse: #b3e5fc;\n --pico-s-not-reviewed: #fe9900; --pico-s-not-reviewed-inverse: #2e1b00; --pico-t-not-reviewed: #3d2a0a; --pico-t-not-reviewed-inverse: #ffe0b2;\n --pico-s-error: #9fa8da; --pico-s-error-inverse: #10153d; --pico-t-error: #262c58; --pico-t-error-inverse: #dfe2f5;\n --pico-s-unknown: #aab6c2; --pico-s-unknown-inverse: #101820; --pico-t-unknown: #26374a; --pico-t-unknown-inverse: #e8edf2;\n --pico-s-critical: #ef7a70; --pico-s-critical-inverse: #2b0603; --pico-t-critical: #3d1614; --pico-t-critical-inverse: #ffc9c4;\n --pico-s-high: #ff8a65; --pico-s-high-inverse: #2e0d02; --pico-t-high: #3f1d12; --pico-t-high-inverse: #ffccbc;\n --pico-s-medium: #ff9800; --pico-s-medium-inverse: #2e1b00; --pico-t-medium: #3d2a0a; --pico-t-medium-inverse: #ffe0b2;\n --pico-s-low: #ffeb3b; --pico-s-low-inverse: #2b2600; --pico-t-low: #3a3508; --pico-t-low-inverse: #fff59d;\n --pico-s-none: #03a9f4; --pico-s-none-inverse: #02202e; --pico-t-none: #0c3048; --pico-t-none-inverse: #b3e5fc;\n }\n}\n[data-theme=\"dark\"] {\n --pico-form-element-border-color: #6b7488;\n --pico-muted-color: #868fa0;\n --pico-s-passed: #4cb04f; --pico-s-passed-inverse: #06200a; --pico-t-passed: #12301a; --pico-t-passed-inverse: #b9e4bb;\n --pico-s-failed: #ef7a70; --pico-s-failed-inverse: #2b0603; --pico-t-failed: #3d1614; --pico-t-failed-inverse: #ffc9c4;\n --pico-s-not-applicable: #03a9f4; --pico-s-not-applicable-inverse: #02202e; --pico-t-not-applicable: #0c3048; --pico-t-not-applicable-inverse: #b3e5fc;\n --pico-s-not-reviewed: #fe9900; --pico-s-not-reviewed-inverse: #2e1b00; --pico-t-not-reviewed: #3d2a0a; --pico-t-not-reviewed-inverse: #ffe0b2;\n --pico-s-error: #9fa8da; --pico-s-error-inverse: #10153d; --pico-t-error: #262c58; --pico-t-error-inverse: #dfe2f5;\n --pico-s-unknown: #aab6c2; --pico-s-unknown-inverse: #101820; --pico-t-unknown: #26374a; --pico-t-unknown-inverse: #e8edf2;\n --pico-s-critical: #ef7a70; --pico-s-critical-inverse: #2b0603; --pico-t-critical: #3d1614; --pico-t-critical-inverse: #ffc9c4;\n --pico-s-high: #ff8a65; --pico-s-high-inverse: #2e0d02; --pico-t-high: #3f1d12; --pico-t-high-inverse: #ffccbc;\n --pico-s-medium: #ff9800; --pico-s-medium-inverse: #2e1b00; --pico-t-medium: #3d2a0a; --pico-t-medium-inverse: #ffe0b2;\n --pico-s-low: #ffeb3b; --pico-s-low-inverse: #2b2600; --pico-t-low: #3a3508; --pico-t-low-inverse: #fff59d;\n --pico-s-none: #03a9f4; --pico-s-none-inverse: #02202e; --pico-t-none: #0c3048; --pico-t-none-inverse: #b3e5fc;\n}\n.c-passed { --c: var(--pico-s-passed); --c-inverse: var(--pico-s-passed-inverse); --t: var(--pico-t-passed); --t-inverse: var(--pico-t-passed-inverse); }\n.c-failed { --c: var(--pico-s-failed); --c-inverse: var(--pico-s-failed-inverse); --t: var(--pico-t-failed); --t-inverse: var(--pico-t-failed-inverse); }\n.c-not-applicable { --c: var(--pico-s-not-applicable); --c-inverse: var(--pico-s-not-applicable-inverse); --t: var(--pico-t-not-applicable); --t-inverse: var(--pico-t-not-applicable-inverse); }\n.c-not-reviewed { --c: var(--pico-s-not-reviewed); --c-inverse: var(--pico-s-not-reviewed-inverse); --t: var(--pico-t-not-reviewed); --t-inverse: var(--pico-t-not-reviewed-inverse); }\n.c-error { --c: var(--pico-s-error); --c-inverse: var(--pico-s-error-inverse); --t: var(--pico-t-error); --t-inverse: var(--pico-t-error-inverse); }\n.c-unknown { --c: var(--pico-s-unknown); --c-inverse: var(--pico-s-unknown-inverse); --t: var(--pico-t-unknown); --t-inverse: var(--pico-t-unknown-inverse); }\n.c-critical { --c: var(--pico-s-critical); --c-inverse: var(--pico-s-critical-inverse); --t: var(--pico-t-critical); --t-inverse: var(--pico-t-critical-inverse); }\n.c-high { --c: var(--pico-s-high); --c-inverse: var(--pico-s-high-inverse); --t: var(--pico-t-high); --t-inverse: var(--pico-t-high-inverse); }\n.c-medium { --c: var(--pico-s-medium); --c-inverse: var(--pico-s-medium-inverse); --t: var(--pico-t-medium); --t-inverse: var(--pico-t-medium-inverse); }\n.c-low { --c: var(--pico-s-low); --c-inverse: var(--pico-s-low-inverse); --t: var(--pico-t-low); --t-inverse: var(--pico-t-low-inverse); }\n.c-none { --c: var(--pico-s-none); --c-inverse: var(--pico-s-none-inverse); --t: var(--pico-t-none); --t-inverse: var(--pico-t-none-inverse); }\n\nhtml { scroll-padding-top: 5rem; }\n:focus-visible { outline: 3px solid var(--pico-primary); outline-offset: 2px; }\n.vh { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0; }\n.skip { position: absolute; left: 0.75rem; top: -4rem; z-index: 20; background: var(--pico-card-background-color); color: var(--pico-color); padding: 0.5rem 0.9rem; border-radius: var(--pico-border-radius); border: 2px solid var(--pico-primary); font-weight: 600; text-decoration: none; }\n.skip:focus { top: 0.6rem; }\n\n.topbar { position: sticky; top: 0; z-index: 10; display: flex; flex-wrap: wrap; align-items: center; gap: 0.4rem 1.25rem; background: var(--pico-primary-background); color: var(--pico-primary-inverse); padding: 0.85rem 1.75rem; }\n.topbar .brand { font-size: 1.2rem; font-weight: 700; margin: 0; color: inherit; }\n.topbar .report-type { font-size: 0.8rem; font-weight: 600; border: 1px solid currentcolor; border-radius: 5rem; padding: 0.1rem 0.7rem; }\n.topbar nav { margin-left: auto; padding: 0; }\n.topbar nav ul { margin: 0; color: inherit; }\n.topbar nav li { padding: 0; }\n.topbar a { color: inherit; text-decoration: none; font-size: 0.9rem; font-weight: 600; padding: 0.3rem 0.75rem; border-radius: 5rem; }\n.topbar a:hover { text-decoration: underline; }\n.topbar :focus-visible { outline-color: var(--pico-primary-inverse); }\n.theme-toggle { display: none; width: auto; font-size: 0.85rem; font-weight: 600; margin: 0; padding: 0.2rem 0.85rem; border: 1px solid currentcolor; border-radius: 5rem; background: transparent; color: inherit; }\n.js .theme-toggle { display: inline-flex; align-items: center; }\n\nsection.card { padding: var(--pico-block-spacing-vertical) var(--pico-block-spacing-horizontal); border: var(--pico-border-width) solid var(--pico-card-border-color); border-radius: var(--pico-border-radius); background: var(--pico-card-background-color); box-shadow: var(--pico-card-box-shadow); }\nsection.card > h2 { margin-bottom: 0.85rem; }\nsection.card > h3 { margin-top: 1.25rem; }\n.as-of, .empty, .formula { color: var(--pico-muted-color); font-size: 0.88rem; }\n\ndetails.fold { margin: 0.6rem 0; border: var(--pico-border-width) solid var(--pico-muted-border-color); border-radius: var(--pico-border-radius); background: var(--pico-card-background-color); }\ndetails.fold > summary { display: flex; align-items: center; gap: 0.6rem; line-height: inherit; padding: 0.55rem 2.2rem 0.55rem 0.9rem; position: relative; border-radius: var(--pico-border-radius); }\ndetails.fold > summary::after { position: absolute; right: 0.9rem; top: calc(50% - 0.5rem); margin-inline-start: 0; }\ndetails.fold > summary:hover { background: var(--pico-card-sectioning-background-color); }\ndetails.fold[open] > summary { border-bottom: var(--pico-border-width) solid var(--pico-muted-border-color); background: var(--pico-card-sectioning-background-color); }\ndetails.fold > summary > :where(h3, h4, h5) { display: inline; margin: 0; min-width: 0; overflow-wrap: anywhere; color: var(--pico-report-ink); }\ndetails.fold.source > summary > h3, details.fold.baseline > summary > :where(h3, h4) { font-size: 1.02rem; text-transform: none; letter-spacing: 0; }\ndetails.fold > summary > .count { flex: none; margin-left: auto; }\n.fold-body { padding: 0.75rem 0.9rem 0.9rem; }\n.fold-body > :first-child { margin-top: 0; }\n.fold-body > :last-child { margin-bottom: 0; }\n.count { display: inline-block; min-width: 1.6rem; text-align: center; font-size: 0.75rem; font-weight: 700; border-radius: 5rem; padding: 0.05rem 0.5rem; background: var(--pico-t-unknown); color: var(--pico-t-unknown-inverse); }\n.to-top { margin: 0.9rem 0 0; font-size: 0.85rem; text-align: right; }\n.page-top { position: fixed; right: 1rem; bottom: 1rem; z-index: 9; padding: 0.4rem 0.9rem; border-radius: 5rem; background: var(--pico-primary-background); color: var(--pico-primary-inverse); text-decoration: none; font-weight: 600; }\n\n.dashboard { display: grid; grid-template-columns: minmax(0, 2fr) minmax(0, 1.25fr) minmax(0, 1fr); gap: 1rem; margin: 1rem 0 1.25rem; align-items: stretch; }\n.facts, .components { display: grid; grid-template-columns: repeat(auto-fit, minmax(21rem, 1fr)); gap: 0.75rem; align-items: start; }\n.facts > details.fold, .components > details.fold { margin: 0; background: var(--pico-card-sectioning-background-color); }\n.panel { border: var(--pico-border-width) solid var(--pico-muted-border-color); border-radius: var(--pico-border-radius); padding: 1rem 1.1rem 1.15rem; background: var(--pico-card-sectioning-background-color); display: flex; flex-direction: column; }\n.panel > h3 { font-size: 1.02rem; margin-bottom: 0.7rem; }\n.stats { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fit, minmax(8.5rem, 1fr)); gap: 0.6rem; }\n.stat { display: flex; flex-direction: column; gap: 0.1rem; border-radius: var(--pico-border-radius); padding: 0.65rem 0.8rem 0.7rem; background: var(--t); color: var(--t-inverse); border-left: 6px solid var(--c); }\n.stat .num { font-size: 1.9rem; font-weight: 800; line-height: 1.1; font-variant-numeric: tabular-nums; }\n.stat .lbl { font-weight: 700; font-size: 0.92rem; }\n.stat .sub { font-size: 0.78rem; }\n.stat-total { background: none; color: var(--pico-color); border: var(--pico-border-width) dashed var(--pico-form-element-border-color); border-left: 6px solid var(--pico-s-unknown); }\n.bar { display: flex; gap: 2px; height: 0.8rem; border-radius: 5rem; overflow: hidden; background: var(--pico-progress-background-color); margin-top: auto; }\n.stats + .bar { margin-top: 1rem; }\n.seg { flex: var(--n) 0 0; background: var(--c); }\n\n.compliance { text-align: center; align-items: center; }\n/* The ring's auto margins can only centre it if nothing else around it claims\n vertical space, so the heading and the lines below it carry none of their own. */\n.compliance > h3 { align-self: flex-start; margin-bottom: 0; }\n.compliance > p { margin: 0; }\n.compliance > .level { margin-bottom: 0.45rem; }\n/* The ring: the percentage swept out of the track, with the same number printed\n inside it, and the engine's 60/90 bands as its colour. The auto block margins\n centre it in whatever height the row settles on. */\n.gauge { --ring: var(--pico-s-failed); width: 10.5rem; height: 10.5rem; margin: auto; border-radius: 50%; display: grid; place-items: center; background: conic-gradient(var(--ring) calc(var(--pct) * 1%), var(--pico-progress-background-color) 0); }\n.gauge .pct { width: 8.1rem; height: 8.1rem; border-radius: 50%; background: var(--pico-card-sectioning-background-color); display: grid; place-items: center; font-size: 1.75rem; font-weight: 800; font-variant-numeric: tabular-nums; color: var(--pico-report-ink); }\n.compliance-high .gauge { --ring: var(--pico-s-passed); }\n.compliance-medium .gauge { --ring: var(--pico-s-not-reviewed); }\n.level { font-weight: 700; display: inline-block; border-radius: 5rem; padding: 0.1rem 0.85rem; background: var(--t); color: var(--t-inverse); }\n.compliance-high { --t: var(--pico-t-passed); --t-inverse: var(--pico-t-passed-inverse); }\n.compliance-medium { --t: var(--pico-t-not-reviewed); --t-inverse: var(--pico-t-not-reviewed-inverse); }\n.compliance-low { --t: var(--pico-t-failed); --t-inverse: var(--pico-t-failed-inverse); }\n\ndl { display: grid; grid-template-columns: max-content 1fr; gap: 0.3rem 1.25rem; margin: 0.5rem 0; }\ndt { font-weight: 600; color: var(--pico-muted-color); }\ndd { margin: 0; overflow-wrap: anywhere; }\n.table-wrap { overflow-x: auto; border: var(--pico-border-width) solid var(--pico-muted-border-color); border-radius: var(--pico-border-radius); margin: 0.5rem 0; }\n.table-wrap > table { margin: 0; font-size: 0.9rem; }\nth, td { padding: 0.5rem 0.8rem; vertical-align: top; }\nthead th { background: var(--pico-card-sectioning-background-color); font-size: 0.78rem; text-transform: uppercase; letter-spacing: 0.05em; color: var(--pico-muted-color); }\ntfoot th, tfoot td { font-weight: 700; background: var(--pico-card-sectioning-background-color); }\ntd.text { white-space: pre-wrap; overflow-wrap: anywhere; }\ntable.summary td, table.summary thead th:not(:first-child) { text-align: right; font-variant-numeric: tabular-nums; }\ntable.details th { width: 11.5rem; background: var(--pico-card-sectioning-background-color); color: var(--pico-muted-color); font-weight: 600; }\n\n.component .type { font-weight: 600; font-size: 0.75rem; border-radius: 5rem; padding: 0.05rem 0.6rem; background: var(--pico-primary-background); color: var(--pico-primary-inverse); }\n.chips { list-style: none; display: flex; flex-wrap: wrap; gap: 0.4rem; margin: 0.25rem 0 0; padding: 0; }\n.chips li { display: inline-flex; border: var(--pico-border-width) solid var(--pico-form-element-border-color); border-radius: var(--pico-border-radius); overflow: hidden; font-size: 0.82rem; background: var(--pico-card-background-color); }\n.chips .k { background: var(--pico-t-unknown); color: var(--pico-t-unknown-inverse); padding: 0.15rem 0.5rem; font-weight: 700; }\n.chips .v { padding: 0.15rem 0.55rem; overflow-wrap: anywhere; }\n\n.toolbar { display: none; flex-wrap: wrap; align-items: center; gap: 0.6rem 0.75rem; margin: 0 0 1.1rem; padding: 0.75rem; border: var(--pico-border-width) solid var(--pico-muted-border-color); border-radius: var(--pico-border-radius); background: var(--pico-card-sectioning-background-color); }\n.js .toolbar { display: flex; }\n.toolbar input { flex: 1 1 16rem; width: auto; margin: 0; }\n.toolbar button { width: auto; margin: 0; font-size: 0.86rem; padding: 0.3rem 0.85rem; }\n.toolbar button[aria-pressed=\"false\"] { --pico-background-color: var(--pico-card-background-color); --pico-border-color: var(--pico-form-element-border-color); --pico-color: var(--pico-contrast); }\n.filters { display: flex; flex-wrap: wrap; gap: 0.35rem; }\n.shown { color: var(--pico-muted-color); font-size: 0.86rem; margin-left: auto; }\n\ndetails.fold.source { border-left: 5px solid var(--pico-primary); }\ndetails.fold.baseline { border-left: 5px solid var(--pico-muted-border-color); }\narticle.requirement { margin: 0.5rem 0; padding: 0; border: var(--pico-border-width) solid var(--pico-muted-border-color); border-left: 6px solid var(--c); border-radius: var(--pico-border-radius); background: var(--pico-card-background-color); box-shadow: none; }\narticle.requirement > details { margin: 0; }\n.req-head, article.requirement summary { display: grid; grid-template-columns: 11rem minmax(6rem, 11rem) 7.5rem minmax(10rem, 2fr) minmax(8rem, 1.4fr); gap: 0.85rem; align-items: center; }\n.req-head { padding: 0.4rem 2.6rem 0.4rem 1.1rem; font-size: 0.74rem; font-weight: 700; color: var(--pico-muted-color); text-transform: uppercase; letter-spacing: 0.06em; }\narticle.requirement summary { line-height: inherit; padding: 0.7rem 2.6rem 0.7rem 0.95rem; position: relative; }\narticle.requirement summary::after { position: absolute; right: 1.1rem; top: calc(50% - 0.5rem); margin-inline-start: 0; }\narticle.requirement summary:hover { background: var(--pico-card-sectioning-background-color); }\narticle.requirement details[open] > summary { border-bottom: var(--pico-border-width) solid var(--pico-muted-border-color); background: var(--pico-card-sectioning-background-color); }\narticle.requirement details[open] > summary { position: sticky; top: 3.4rem; z-index: 5; }\n.req-body { padding: 0.9rem 1.1rem 1.1rem; }\n.req-body > :last-child { margin-bottom: 0; }\n.req-id { font-weight: 700; overflow-wrap: anywhere; }\n.req-title { overflow-wrap: anywhere; }\n.location { display: flex; flex-wrap: wrap; align-items: center; gap: 0.6rem; margin: 0 0 0.6rem; padding: 0.5rem 0.75rem; border: var(--pico-border-width) solid var(--pico-muted-border-color); border-left: 4px solid var(--pico-primary); border-radius: var(--pico-border-radius); background: var(--pico-card-sectioning-background-color); }\n.location .k, .sub .k { font-size: 0.74rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em; color: var(--pico-muted-color); }\n.sub { display: block; margin-top: 0.35rem; font-size: 0.85rem; }\n.tags { display: flex; flex-wrap: wrap; gap: 0.3rem; }\n.tag { font-size: 0.75rem; font-weight: 600; border: var(--pico-border-width) solid var(--pico-form-element-border-color); border-radius: 5rem; padding: 0.05rem 0.55rem; background: var(--pico-card-background-color); white-space: nowrap; }\n.tag.enriched { background: var(--pico-primary-background); border-color: var(--pico-primary-background); color: var(--pico-primary-inverse); }\n/* Status and severity carry a mark and a word as well as a colour, so neither\n colour vision nor a greyscale printer is needed to read them. */\n.status, .sev { display: inline-flex; align-items: center; gap: 0.35rem; border-radius: 5rem; padding: 0.15rem 0.7rem; font-size: 0.8rem; font-weight: 700; white-space: nowrap; width: fit-content; }\n.status { color: var(--c-inverse); background: var(--c); }\n.status .ico { font-weight: 400; }\n.sev { background: var(--t); color: var(--t-inverse); }\n.sev::before { content: \"\"; width: 0.6rem; height: 0.6rem; border-radius: 50%; background: var(--c); flex: none; }\ndetails.doc { margin: 0.5rem 0 0; }\ndetails.doc > summary { font-weight: 600; font-size: 0.85rem; width: fit-content; }\ndetails.doc > pre { white-space: pre-wrap; }\npre { margin: 0.25rem 0 0.5rem; white-space: pre-wrap; overflow-wrap: anywhere; font-size: 0.85rem; }\npre.prose { font-family: var(--pico-font-family-sans-serif); font-size: 0.92rem; margin: 0; padding: 0; background: none; color: inherit; }\n.req-body > pre.prose { margin-bottom: 0.25rem; }\npre.code { background: var(--pico-code-background-color); color: var(--pico-code-color); border-radius: var(--pico-border-radius); padding: 0.85rem 1rem; }\n\n@media (prefers-reduced-motion: no-preference) { html { scroll-behavior: smooth; } }\n@media (forced-colors: active) { .status, .sev, .tag, .stat, .seg, .gauge, .level, .count, .component .type { border: 1px solid; forced-color-adjust: auto; } }\n@media (max-width: 68rem) { .dashboard { grid-template-columns: 1fr 1fr; } .dashboard .panel:first-child { grid-column: 1 / -1; } }\n@media (max-width: 60rem) { .topbar { position: static; } article.requirement details[open] > summary { top: 0; } .req-head { display: none; } article.requirement summary { grid-template-columns: 1fr 1fr; } .req-title, .tags { grid-column: 1 / -1; } }\n@media (max-width: 40rem) { .dashboard { grid-template-columns: 1fr; } .topbar { padding: 0.75rem 1rem; } dl { grid-template-columns: 1fr; gap: 0 0; } dd { margin-bottom: 0.4rem; } }\n\n/* Pico ships no print rules. Collapsed content is invisible on paper and to the\n reader's find-in-page, so every disclosure is opened and the chrome removed. */\n@media print {\n :root, [data-theme=\"dark\"] { color-scheme: light; }\n body { background: #ffffff; color: #000000; }\n .topbar { position: static; }\n .skip, .toolbar, .theme-toggle, .page-top, .to-top, .topbar nav { display: none !important; }\n details { display: block; }\n details > *:not(summary) { display: revert !important; }\n details > summary::after { display: none; }\n main { padding: 0; max-width: none; }\n section.card, article.requirement, details.fold { box-shadow: none; }\n thead { display: table-header-group; }\n tr, article.requirement, pre, figure, table { break-inside: avoid; }\n h2, h3, h4 { break-after: avoid; }\n p, li { orphans: 3; widows: 3; }\n * { -webkit-print-color-adjust: exact; print-color-adjust: exact; }\n @page { margin: 1.5cm; }\n}\n";
8331
+ /** The only script the report carries. */
8332
+ const SCRIPT = "(function () {\n 'use strict';\n var slice = Array.prototype.slice;\n var root = document.documentElement;\n var themeKey = 'hdf-report-theme';\n\n function all(selector) {\n return slice.call(document.querySelectorAll(selector));\n }\n\n // Theme: the reader's choice wins over the system setting and is remembered.\n var toggle = document.getElementById('theme-toggle');\n\n function systemDark() {\n return window.matchMedia('(prefers-color-scheme: dark)').matches;\n }\n\n function currentTheme() {\n var chosen = root.getAttribute('data-theme');\n if (chosen === 'dark' || chosen === 'light') {\n return chosen;\n }\n return systemDark() ? 'dark' : 'light';\n }\n\n function showTheme() {\n var dark = currentTheme() === 'dark';\n toggle.textContent = dark ? 'Light mode' : 'Dark mode';\n toggle.setAttribute('aria-label', dark ? 'Switch to light mode' : 'Switch to dark mode');\n }\n\n function remember(theme) {\n try {\n window.localStorage.setItem(themeKey, theme);\n } catch (ignore) {\n return;\n }\n }\n\n function recall() {\n try {\n return window.localStorage.getItem(themeKey);\n } catch (ignore) {\n return null;\n }\n }\n\n var saved = recall();\n if (saved === 'dark' || saved === 'light') {\n root.setAttribute('data-theme', saved);\n }\n toggle.addEventListener('click', function () {\n var next = currentTheme() === 'dark' ? 'light' : 'dark';\n root.setAttribute('data-theme', next);\n remember(next);\n showTheme();\n });\n showTheme();\n\n // Print the whole report, in the light palette, whatever is open on screen.\n var beforePrint = null;\n window.addEventListener('beforeprint', function () {\n beforePrint = root.getAttribute('data-theme');\n root.setAttribute('data-theme', 'light');\n all('details').forEach(function (d) {\n d.open = true;\n });\n });\n window.addEventListener('afterprint', function () {\n if (beforePrint === null) {\n root.removeAttribute('data-theme');\n } else {\n root.setAttribute('data-theme', beforePrint);\n }\n });\n\n root.classList.add('js');\n\n // Results filter: only the reports that list requirements carry it.\n var box = document.getElementById('filter-text');\n if (box === null) {\n return;\n }\n var reqs = all('article.requirement');\n var groups = all('#results details.group');\n var shown = document.getElementById('filter-count');\n var buttons = all('button.filter');\n var status = 'all';\n\n // Each requirement is an article holding one disclosure, so opening and\n // closing it reaches past the article to that element.\n function panel(req) {\n return req.querySelector('details');\n }\n\n function matches(d, query) {\n if (status !== 'all') {\n if (d.getAttribute('data-status') !== status) {\n return false;\n }\n }\n if (query === '') {\n return true;\n }\n return d.querySelector('summary').textContent.toLowerCase().indexOf(query) !== -1;\n }\n\n function apply() {\n var query = box.value.trim().toLowerCase();\n var filtering = query !== '' || status !== 'all';\n var visible = 0;\n reqs.forEach(function (d) {\n var ok = matches(d, query);\n d.hidden = !ok;\n if (ok) {\n visible += 1;\n }\n });\n // A filter is no use if its matches sit inside closed groups.\n groups.forEach(function (g) {\n var any = g.querySelector('article.requirement:not([hidden])') !== null;\n g.hidden = filtering ? !any : false;\n if (filtering) {\n g.open = any;\n }\n });\n shown.textContent = visible + ' of ' + reqs.length + ' requirements shown';\n }\n\n function setOpen(open) {\n groups.forEach(function (g) {\n if (!g.hidden) {\n g.open = open;\n }\n });\n reqs.forEach(function (d) {\n if (!d.hidden) {\n panel(d).open = open;\n }\n });\n }\n\n buttons.forEach(function (b) {\n b.addEventListener('click', function () {\n status = b.getAttribute('data-status');\n buttons.forEach(function (other) {\n other.setAttribute('aria-pressed', other === b ? 'true' : 'false');\n });\n apply();\n });\n });\n box.addEventListener('input', apply);\n document.getElementById('expand-all').addEventListener('click', function () {\n setOpen(true);\n });\n document.getElementById('collapse-all').addEventListener('click', function () {\n setOpen(false);\n });\n apply();\n})();\n";
8333
+ /** The CSP source expression for SCRIPT; a test recomputes it from the file. */
8334
+ const SCRIPT_HASH = "sha256-8niHkNd3MWeaLiFb0O47hR5NXEwDnmbycK+zHXeWXN0=";
8335
+ //#endregion
8336
+ //#region converters/hdf-to-html/typescript/converter.ts
8337
+ /**
8338
+ * Renders an HDF results document as one self-contained HTML report. The Go
8339
+ * peer emits the same bytes for the same input, so every choice here that
8340
+ * affects output (ordering, escaping, number and time formatting) mirrors it
8341
+ * and is pinned by the shared goldens.
8342
+ */
8343
+ const CONVERTER_NAME$2 = "hdf-to-html";
8344
+ /** Reads a report type case-insensitively; empty selects 'administrator'. */
8345
+ function parseReportType(value) {
8346
+ switch ((value ?? "").trim().toLowerCase()) {
8347
+ case "":
8348
+ case "administrator": return "administrator";
8349
+ case "manager": return "manager";
8350
+ case "executive": return "executive";
8351
+ default: throw new Error(`unknown report type ${JSON.stringify(value)}: must be one of executive, manager, administrator`);
8352
+ }
8353
+ }
8354
+ /**
8355
+ * Renders an HDF results document as HTML. The output depends on the input and
8356
+ * options alone: nothing is read from the clock or the host, so the same
8357
+ * document always yields the same report.
8358
+ */
8359
+ function convertHdfToHtml(input, options = {}) {
8360
+ return render([{
8361
+ name: "",
8362
+ content: input
8363
+ }], options, false);
8364
+ }
8365
+ /**
8366
+ * Renders several results documents as one report: combined status, severity
8367
+ * and compliance, then each document's own context, components and results
8368
+ * under its name. Documents appear in the order given.
8369
+ */
8370
+ function convertHdfDocumentsToHtml(documents, options = {}) {
8371
+ if (documents.length === 0) throw new Error(`${CONVERTER_NAME$2}: no documents to report on`);
8372
+ return render(documents, options, true);
8373
+ }
8374
+ function render(documents, options, aggregated) {
8375
+ let reportType;
8376
+ try {
8377
+ reportType = parseReportType(options.reportType);
8378
+ } catch (err) {
8379
+ throw new Error(`${CONVERTER_NAME$2}: ${err.message}`);
8380
+ }
8381
+ const sources = documents.map((document) => {
8382
+ let doc;
8383
+ try {
8384
+ ({doc} = requireHdfResults(document.content, CONVERTER_NAME$2));
8385
+ } catch (err) {
8386
+ if (!aggregated) throw err;
8387
+ throw new Error(`${document.name}: ${err.message}`);
8388
+ }
8389
+ const baselines = list(doc.baselines).map(obj);
8390
+ return {
8391
+ name: document.name,
8392
+ doc,
8393
+ baselines,
8394
+ baselineLabels: baselineLabels(baselines),
8395
+ attributed: attributesResults(doc, baselines),
8396
+ ref: assessmentTime(doc, baselines)
8397
+ };
8398
+ });
8399
+ const r = new Renderer(reportType, sources, aggregated);
8400
+ r.document();
8401
+ return r.lines.join("");
8402
+ }
8403
+ /**
8404
+ * Names each baseline as the report shows it: its title, which is what carries
8405
+ * the scan target in real scanner output, falling back to its name. A label two
8406
+ * baselines would share takes the 1-based document position of the baseline it
8407
+ * names (hdf-engine's 0-based baselineIndex plus one).
8408
+ */
8409
+ function baselineLabels(baselines) {
8410
+ const shared = /* @__PURE__ */ new Map();
8411
+ const suffixed = baselines.map((baseline) => {
8412
+ const title = text(baseline.title);
8413
+ const label = title !== "" ? title : text(baseline.name);
8414
+ shared.set(label, (shared.get(label) ?? 0) + 1);
8415
+ return label;
8416
+ }).map((label, i) => (shared.get(label) ?? 0) > 1 ? label + ordinalSuffix(i) : label);
8417
+ const taken = /* @__PURE__ */ new Set();
8418
+ return suffixed.map((label, i) => {
8419
+ let unique = label;
8420
+ while (taken.has(unique)) unique += ordinalSuffix(i);
8421
+ taken.add(unique);
8422
+ return unique;
8423
+ });
8424
+ }
8425
+ function ordinalSuffix(i) {
8426
+ return ` #${i + 1}`;
8427
+ }
8428
+ /** The well-known baseline label naming the component a baseline's requirements were assessed against. */
8429
+ const LABEL_COMPONENT = "component";
8430
+ /**
8431
+ * Whether the document ties any requirement to a component. HDF results carry
8432
+ * that link at baseline granularity only — a baseline's labels.component names a
8433
+ * component, or a component's baselineRefs name a baseline — so a per-component
8434
+ * number means nothing without one.
8435
+ */
8436
+ function attributesResults(doc, baselines) {
8437
+ for (const baseline of baselines) {
8438
+ const labels = obj(baseline.labels);
8439
+ if (Object.prototype.hasOwnProperty.call(labels, LABEL_COMPONENT) && text(labels[LABEL_COMPONENT]) !== "") return true;
8440
+ }
8441
+ return list(doc.components).map(obj).some((c) => list(c.baselineRefs).length > 0);
8442
+ }
8443
+ /** A JSON value as an object; anything else reads as an empty one, as Go's zero struct does. */
8444
+ function obj(value) {
8445
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
8446
+ }
8447
+ function list(value) {
8448
+ return Array.isArray(value) ? value : [];
8449
+ }
8450
+ /** A string field as text; absent or wrong-typed reads as empty, as Go's zero value does. */
8451
+ function text(value) {
8452
+ return typeof value === "string" ? value : "";
8453
+ }
8454
+ function numeric(value) {
8455
+ return typeof value === "number" ? value : 0;
8456
+ }
8457
+ /** HDF's canonical trimmed-UTC RFC3339; absent or unparseable is empty. */
8458
+ function formatTime(value) {
8459
+ const parsed = toDate$1(value);
8460
+ return parsed ? formatTimestamp(parsed) : "";
8461
+ }
8462
+ function toDate$1(value) {
8463
+ const parsed = parsedDate(value);
8464
+ return parsed && !isGoZeroTime(parsed) ? parsed : null;
8465
+ }
8466
+ function parsedDate(value) {
8467
+ if (value instanceof Date) return value;
8468
+ return typeof value === "string" ? parseTimestamp(value) : null;
8469
+ }
8470
+ /**
8471
+ * When the assessment ran: the document timestamp, else its latest result start
8472
+ * time. Overrides are judged against it instead of the wall clock, which is
8473
+ * what keeps the report reproducible.
8474
+ */
8475
+ function assessmentTime(doc, baselines) {
8476
+ const latest = toDate$1(doc.timestamp) ?? latestStart(baselines);
8477
+ if (!latest) {
8478
+ const epoch = /* @__PURE__ */ new Date(0);
8479
+ return {
8480
+ stamp: formatTimestamp(epoch),
8481
+ date: epoch,
8482
+ known: false
8483
+ };
8484
+ }
8485
+ return {
8486
+ stamp: formatTimestamp(latest),
8487
+ date: latest,
8488
+ known: true
8489
+ };
8490
+ }
8491
+ /** The latest result start time in the document, if any result carries one. */
8492
+ function latestStart(baselines) {
8493
+ let latest = null;
8494
+ const results = baselines.flatMap((baseline) => list(baseline.requirements).map(obj)).flatMap((req) => list(req.results).map(obj));
8495
+ for (const res of results) {
8496
+ const start = toDate$1(res.startTime);
8497
+ if (start && (!latest || start > latest)) latest = start;
8498
+ }
8499
+ return latest;
8500
+ }
8501
+ /**
8502
+ * The compliance percentage hdf-engine computes, to two places, so the report
8503
+ * never disagrees with `hdf validate threshold` in the last digit. The Go peer
8504
+ * formats the same number with FormatFixed, which is toFixed's rounding.
8505
+ */
8506
+ function compliancePercent(c) {
8507
+ return calculateCompliance(c).toFixed(2);
8508
+ }
8509
+ function compliance(c) {
8510
+ return `${compliancePercent(c)}%`;
8511
+ }
8512
+ /** The requirement's severity through the engine, exactly as its tally derives it. */
8513
+ function requirementSeverity(req, now) {
8514
+ return deriveSeverity(requirementEffectiveImpact(req, now), req.severity ?? null);
8515
+ }
8516
+ /**
8517
+ * Maps overrides onto the canonical effective-status input shape. formatTime
8518
+ * drops Go's zero time, which the legacy converters leave in `expiresAt` to
8519
+ * mean "no expiry": handing the raw value to the status ladder would read it as
8520
+ * an expiry in the year 1 and disagree with both the overrides table and Go,
8521
+ * whose zero time.Time already means absent. Effective status and the table's
8522
+ * governing/expired column read the same normalized overrides.
8523
+ */
8524
+ function overrideInputs(overrides) {
8525
+ return overrides.map((o) => ({
8526
+ status: text(o.status) === "" ? void 0 : text(o.status),
8527
+ appliedAt: formatTime(o.appliedAt) || void 0,
8528
+ expiresAt: formatTime(o.expiresAt) || void 0
8529
+ }));
8530
+ }
8531
+ /** Bands the engine's compliance percentage at 90 and 60. */
8532
+ function complianceLevel(pct) {
8533
+ if (pct >= 90) return ["high", "High compliance"];
8534
+ if (pct >= 60) return ["medium", "Medium compliance"];
8535
+ return ["low", "Low compliance"];
8536
+ }
8537
+ /** Maps a severity onto the fixed set the stylesheet knows; an informational, absent or unrecognized one is 'none'. */
8538
+ function severityClass(severity) {
8539
+ const bucket = severityBucket(severity);
8540
+ return bucket === "informational" ? "none" : bucket;
8541
+ }
8542
+ function severityLabel(severity) {
8543
+ switch (severity) {
8544
+ case "critical": return "Critical";
8545
+ case "high": return "High";
8546
+ case "medium": return "Medium";
8547
+ case "low": return "Low";
8548
+ case "":
8549
+ case "none":
8550
+ case "informational": return "None";
8551
+ default: return severity;
8552
+ }
8553
+ }
8554
+ /** Names a description the way the Heimdall report does. */
8555
+ function descriptionHeading(label) {
8556
+ switch (label) {
8557
+ case "default": return "Description";
8558
+ case "check": return "Check Text";
8559
+ case "fix": return "Fix Text";
8560
+ case "rationale": return "Rationale";
8561
+ case "caveat": return "Caveat";
8562
+ default: return label;
8563
+ }
8564
+ }
8565
+ function plural(n, noun) {
8566
+ return n === 1 ? `1 ${noun}` : `${n} ${noun}s`;
8567
+ }
8568
+ function stat(cls, n, label, sub) {
8569
+ return `<li class="stat c-${cls}"><span class="num">${n}</span><span class="lbl">${label}</span>${sub === "" ? "" : `<span class="sub">${sub}</span>`}</li>`;
8570
+ }
8571
+ /**
8572
+ * A proportional strip; a zero count contributes no segment. It is an image to
8573
+ * assistive technology, described by the same counts the list above it states,
8574
+ * so nothing is conveyed by colour alone.
8575
+ */
8576
+ function bar(label, segments) {
8577
+ return `<div class="bar" role="img" aria-label="${label}: ${segments.map(([, name, n]) => `${n} ${name}`).join(", ")}">${segments.filter(([, , n]) => n > 0).map(([cls, , n]) => `<span class="seg c-${cls}" style="--n:${n}"></span>`).join("")}</div>`;
8578
+ }
8579
+ /** A heading and a value that are already HTML. */
8580
+ function detailRow(heading, value) {
8581
+ return `<tr><th scope="row">${heading}</th><td>${value}</td></tr>`;
8582
+ }
8583
+ /** Carries text verbatim. An HTML parser drops one newline directly after <pre>, so one is always written for it to drop. */
8584
+ function prose(value) {
8585
+ return `<pre class="prose">\n${escapeHtml(value)}</pre>`;
8586
+ }
8587
+ const REPORT_TYPE_LABEL = {
8588
+ executive: "Executive",
8589
+ manager: "Manager",
8590
+ administrator: "Administrator"
8591
+ };
8592
+ const STATUS_FILTERS = [
8593
+ ["passed", "Passed"],
8594
+ ["failed", "Failed"],
8595
+ ["not-applicable", "Not Applicable"],
8596
+ ["not-reviewed", "Not Reviewed"],
8597
+ ["error", "Error"]
8598
+ ];
8599
+ /**
8600
+ * The size past which a block starts closed: anything longer than two lines or
8601
+ * rows is behind its heading, so a long report is a list of headings with
8602
+ * counts instead of a scroll.
8603
+ */
8604
+ const COLLAPSED_ABOVE = 2;
8605
+ /** How many lines text occupies. */
8606
+ function lineCount(value) {
8607
+ return value.split("\n").length;
8608
+ }
8609
+ /** Text that would take more than two lines on the page: more than two lines of its own, or one long enough to wrap that far. */
8610
+ function isLongText(value) {
8611
+ return lineCount(value) > COLLAPSED_ABOVE || Array.from(value).length > 240;
8612
+ }
8613
+ function countFacts(pairs) {
8614
+ return pairs.filter(([, value]) => value !== "").length;
8615
+ }
8616
+ var Renderer = class {
8617
+ /**
8618
+ * `aggregated` lays the report out by source, even for a single document;
8619
+ * the single-document layout has no source level.
8620
+ */
8621
+ constructor(reportType, sources, aggregated) {
8622
+ this.reportType = reportType;
8623
+ this.sources = sources;
8624
+ this.aggregated = aggregated;
8625
+ this.lines = [];
8626
+ this.folds = 0;
8627
+ this.requirements = 0;
8628
+ this.src = sources[0];
8629
+ }
8630
+ /** The heading outline stays in order: the aggregated layout has a source heading above the baselines. */
8631
+ get baselineHeading() {
8632
+ return this.aggregated ? "h4" : "h3";
8633
+ }
8634
+ get detailHeading() {
8635
+ return this.aggregated ? "h5" : "h4";
8636
+ }
8637
+ /**
8638
+ * Starts a collapsible block: a heading with a count that opens onto the
8639
+ * content. `title` is HTML; `label` is the plain text the return link names.
8640
+ * An empty id takes the next number.
8641
+ */
8642
+ openFold(cls, id, tag, title, label, count) {
8643
+ return this.openFoldCounted(cls, id, tag, title, label, count, true);
8644
+ }
8645
+ /** openFold with the choice of whether the heading states its count. A block that drops it still collapses at the same size. */
8646
+ openFoldCounted(cls, id, tag, title, label, count, counted) {
8647
+ if (id === "") {
8648
+ this.folds++;
8649
+ id = `block-${this.folds}`;
8650
+ }
8651
+ const long = count > COLLAPSED_ABOVE;
8652
+ const classes = cls === "" ? "fold" : `fold ${cls}`;
8653
+ const badge = counted ? `<span class="count">${count}</span>` : "";
8654
+ this.line(`<details class="${classes}" id="${id}"${long ? "" : " open=\"open\""}>`);
8655
+ this.line(`<summary><${tag}>${title}</${tag}>${badge}</summary>`);
8656
+ this.line("<div class=\"fold-body\">");
8657
+ return {
8658
+ id,
8659
+ label,
8660
+ long
8661
+ };
8662
+ }
8663
+ /** Ends the block, with a way back to its top when it is long enough to need one. */
8664
+ closeFold(f) {
8665
+ if (f.long) this.line(`<p class="to-top"><a href="#${f.id}">Back to the top of ${escapeHtml(f.label)}</a></p>`);
8666
+ this.line("</div>");
8667
+ this.line("</details>");
8668
+ }
8669
+ line(s) {
8670
+ this.lines.push(s, "\n");
8671
+ }
8672
+ document() {
8673
+ const detailed = this.reportType !== "executive";
8674
+ const policy = `default-src 'none'; style-src 'unsafe-inline'; script-src '${SCRIPT_HASH}'`;
8675
+ this.line("<!DOCTYPE html>");
8676
+ this.line("<html lang=\"en\">");
8677
+ this.line("<head>");
8678
+ this.line("<meta charset=\"utf-8\" />");
8679
+ this.line(`<meta http-equiv="Content-Security-Policy" content="${policy}" />`);
8680
+ this.line("<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />");
8681
+ this.line("<meta name=\"color-scheme\" content=\"light dark\" />");
8682
+ this.line("<title>HDF Assessment Report</title>");
8683
+ this.line("<style>");
8684
+ this.lines.push(STYLESHEET);
8685
+ this.line("</style>");
8686
+ this.line("</head>");
8687
+ this.line("<body>");
8688
+ this.line("<a class=\"skip\" href=\"#main\">Skip to content</a>");
8689
+ this.line("<header class=\"topbar\" id=\"top\">");
8690
+ this.line("<h1 class=\"brand\">HDF Assessment Report</h1>");
8691
+ this.line(`<span class="report-type">Report type: ${REPORT_TYPE_LABEL[this.reportType]}</span>`);
8692
+ this.line("<nav aria-label=\"Sections\">");
8693
+ this.line("<ul>");
8694
+ this.line("<li><a href=\"#status\">Status</a></li>");
8695
+ this.line(this.aggregated ? "<li><a href=\"#sources\">Sources</a></li>" : "<li><a href=\"#assessment\">Assessment</a></li>");
8696
+ this.line("<li><a href=\"#components\">Components</a></li>");
8697
+ if (detailed) this.line("<li><a href=\"#results\">Results</a></li>");
8698
+ this.line("</ul>");
8699
+ this.line("</nav>");
8700
+ this.line("<button type=\"button\" id=\"theme-toggle\" class=\"theme-toggle\" aria-label=\"Switch to dark mode\">Dark mode</button>");
8701
+ this.line("</header>");
8702
+ this.line("<main id=\"main\" class=\"container\">");
8703
+ this.status();
8704
+ if (this.aggregated) this.sourceList();
8705
+ else this.assessment(this.sources[0].doc);
8706
+ this.components();
8707
+ if (detailed) this.results();
8708
+ this.line("</main>");
8709
+ this.line("<a class=\"page-top\" href=\"#top\">Top</a>");
8710
+ this.line("<script>");
8711
+ this.lines.push(SCRIPT);
8712
+ this.line("<\/script>");
8713
+ this.line("</body>");
8714
+ this.line("</html>");
8715
+ }
8716
+ /** Writes the non-empty pairs and reports whether it wrote any. */
8717
+ definitionList(pairs) {
8718
+ let wrote = false;
8719
+ for (const [term, value] of pairs) {
8720
+ if (value === "") continue;
8721
+ if (!wrote) {
8722
+ this.line("<dl>");
8723
+ wrote = true;
8724
+ }
8725
+ this.line(`<dt>${escapeHtml(term)}</dt><dd>${escapeHtml(value)}</dd>`);
8726
+ }
8727
+ if (wrote) this.line("</dl>");
8728
+ return wrote;
8729
+ }
8730
+ assessment(doc) {
8731
+ this.line("<section id=\"assessment\" class=\"card\" aria-labelledby=\"assessment-heading\">");
8732
+ this.line("<h2 id=\"assessment-heading\">Assessment</h2>");
8733
+ this.line("<div class=\"facts\">");
8734
+ const facts = documentFacts(doc);
8735
+ const f = this.openFold("", "", "h3", "Document", "the document facts", countFacts(facts));
8736
+ if (!this.definitionList(facts)) this.line("<p class=\"empty\">The document carries no assessment metadata.</p>");
8737
+ this.closeFold(f);
8738
+ if (doc.runner != null) {
8739
+ const runner = runnerFacts(obj(doc.runner));
8740
+ const rf = this.openFold("", "", "h3", "Runner", "the runner facts", countFacts(runner));
8741
+ this.definitionList(runner);
8742
+ this.closeFold(rf);
8743
+ }
8744
+ this.line("</div>");
8745
+ this.externalReferences("h3", "Enrichment and External References", doc.externalReferences);
8746
+ this.line("</section>");
8747
+ }
8748
+ /** The aggregated layout's assessment section: one panel per input document, each the target of the links in the status tables. */
8749
+ sourceList() {
8750
+ this.line("<section id=\"sources\" class=\"card\" aria-labelledby=\"sources-heading\">");
8751
+ this.line(`<h2 id="sources-heading">Sources (${this.sources.length})</h2>`);
8752
+ this.line("<div class=\"facts\">");
8753
+ this.sources.forEach((src, i) => {
8754
+ const facts = documentFacts(src.doc);
8755
+ const f = this.openFold("", sourceId(i), "h3", escapeHtml(src.name), src.name, countFacts(facts));
8756
+ if (!this.definitionList(facts)) this.line("<p class=\"empty\">The document carries no assessment metadata.</p>");
8757
+ if (src.doc.runner != null) {
8758
+ this.line("<h4>Runner</h4>");
8759
+ this.definitionList(runnerFacts(obj(src.doc.runner)));
8760
+ }
8761
+ this.externalReferences("h4", "Enrichment and External References", src.doc.externalReferences);
8762
+ this.closeFold(f);
8763
+ });
8764
+ this.line("</div>");
8765
+ this.line("</section>");
8766
+ }
8767
+ components() {
8768
+ const perSource = this.sources.map((src) => list(src.doc.components).map(obj));
8769
+ const total = perSource.reduce((n, components) => n + components.length, 0);
8770
+ this.line("<section id=\"components\" class=\"card\" aria-labelledby=\"components-heading\">");
8771
+ this.line(`<h2 id="components-heading">Components (${total})</h2>`);
8772
+ if (total > 0) {
8773
+ this.line("<div class=\"components\">");
8774
+ this.sources.forEach((src, i) => {
8775
+ this.src = src;
8776
+ for (const c of perSource[i]) this.component(c);
8777
+ });
8778
+ this.line("</div>");
8779
+ } else if (this.aggregated) this.line("<p class=\"empty\">The documents name no components.</p>");
8780
+ else this.line("<p class=\"empty\">The document names no components.</p>");
8781
+ this.line("</section>");
8782
+ }
8783
+ component(c) {
8784
+ const pairs = [["Source", this.aggregated ? this.src.name : ""]];
8785
+ for (const [term, key] of COMPONENT_FIELDS) pairs.push(componentField(c, term, key));
8786
+ const f = this.openFoldCounted("component", "", "h3", `${escapeHtml(text(c.name))} <span class="type">${escapeHtml(text(c.type))}</span>`, text(c.name), countFacts(pairs) + Object.keys(obj(c.labels)).length + Object.keys(obj(c.externalIds)).length, this.src.attributed);
8787
+ this.definitionList(pairs);
8788
+ this.chips("Labels", c.labels);
8789
+ this.chips("External IDs", c.externalIds);
8790
+ this.closeFold(f);
8791
+ }
8792
+ /** A map as key/value chips in key order. An empty value is kept: the key itself is the information. */
8793
+ chips(heading, value) {
8794
+ const m = obj(value);
8795
+ const keys = Object.keys(m).sort(byCodePoint);
8796
+ if (keys.length === 0) return;
8797
+ this.line(`<h4>${heading}</h4>`);
8798
+ this.line("<ul class=\"chips\">");
8799
+ for (const k of keys) this.line(`<li><span class="k">${escapeHtml(k)}</span><span class="v">${escapeHtml(text(m[k]))}</span></li>`);
8800
+ this.line("</ul>");
8801
+ }
8802
+ effectiveStatus(req) {
8803
+ return computeEffectiveStatus({
8804
+ impact: numeric(req.impact),
8805
+ resultStatuses: list(req.results).map(obj).map((res) => text(res.status)),
8806
+ overrides: overrideInputs(list(req.statusOverrides).map(obj))
8807
+ }, this.src.ref.stamp);
8808
+ }
8809
+ status() {
8810
+ const checks = {
8811
+ underPassed: 0,
8812
+ passedUnderFailed: 0,
8813
+ failedUnderFailed: 0,
8814
+ total: 0
8815
+ };
8816
+ const perBaseline = this.sources.map((src) => {
8817
+ this.src = src;
8818
+ return src.baselines.map((baseline) => {
8819
+ const requirements = list(baseline.requirements).map(obj);
8820
+ for (const req of requirements) {
8821
+ const status = this.effectiveStatus(req);
8822
+ for (const res of list(req.results).map(obj)) {
8823
+ checks.total++;
8824
+ const resultStatus = text(res.status);
8825
+ if (status === "passed" && resultStatus === "passed") checks.underPassed++;
8826
+ else if (status === "failed" && resultStatus === "passed") checks.passedUnderFailed++;
8827
+ else if (status === "failed" && resultStatus === "failed") checks.failedUnderFailed++;
8828
+ }
8829
+ }
8830
+ return countControlsByStatus({ baselines: [{ requirements }] }, (req) => this.effectiveStatus(req), this.src.ref.stamp);
8831
+ });
8832
+ });
8833
+ const perSource = perBaseline.map((counts) => addCounts(...counts));
8834
+ const all = addCounts(...perSource);
8835
+ const severities = severityTotals(all);
8836
+ const anyRef = this.sources.some((src) => src.ref.known);
8837
+ this.line("<section id=\"status\" class=\"card\" aria-labelledby=\"status-heading\">");
8838
+ this.line("<h2 id=\"status-heading\">Status</h2>");
8839
+ if (severities.total > 0 && anyRef) {
8840
+ if (this.aggregated) this.line("<p class=\"as-of\">Effective status evaluated for each source as of its own assessment time. Overrides that had expired by then are not applied.</p>");
8841
+ else this.line(`<p class="as-of">Effective status evaluated as of ${escapeHtml(this.sources[0].ref.stamp)}, the time of the assessment. Overrides that had expired by then are not applied.</p>`);
8842
+ }
8843
+ this.line("<div class=\"dashboard\">");
8844
+ this.line("<div class=\"panel\">");
8845
+ this.line("<h3>Requirements</h3>");
8846
+ this.line("<ul class=\"stats\">");
8847
+ this.line(stat("passed", all.passed.total, "Passed", `${plural(checks.underPassed, "individual check")} passed`));
8848
+ this.line(stat("failed", all.failed.total, "Failed", `${plural(checks.passedUnderFailed, "individual check")} passed, ${checks.failedUnderFailed} failed out of ${plural(checks.total, "total check")}`));
8849
+ this.line(stat("not-applicable", all.noImpact.total, "Not Applicable", ""));
8850
+ this.line(stat("not-reviewed", all.skipped.total, "Not Reviewed", ""));
8851
+ this.line(stat("error", all.error.total, "Error", ""));
8852
+ this.line(`<li class="stat stat-total"><span class="num">${severities.total}</span><span class="lbl">Total</span></li>`);
8853
+ this.line("</ul>");
8854
+ this.line(bar("Requirements by status", [
8855
+ [
8856
+ "passed",
8857
+ "passed",
8858
+ all.passed.total
8859
+ ],
8860
+ [
8861
+ "failed",
8862
+ "failed",
8863
+ all.failed.total
8864
+ ],
8865
+ [
8866
+ "not-applicable",
8867
+ "not applicable",
8868
+ all.noImpact.total
8869
+ ],
8870
+ [
8871
+ "not-reviewed",
8872
+ "not reviewed",
8873
+ all.skipped.total
8874
+ ],
8875
+ [
8876
+ "error",
8877
+ "error",
8878
+ all.error.total
8879
+ ]
8880
+ ]));
8881
+ this.line("</div>");
8882
+ this.line("<div class=\"panel\">");
8883
+ this.line("<h3>Severity</h3>");
8884
+ this.line("<ul class=\"stats\">");
8885
+ this.line(stat("critical", severities.critical, "Critical", ""));
8886
+ this.line(stat("high", severities.high, "High", ""));
8887
+ this.line(stat("medium", severities.medium, "Medium", ""));
8888
+ this.line(stat("low", severities.low, "Low", ""));
8889
+ this.line(stat("none", severities.informational, "None", ""));
8890
+ this.line("</ul>");
8891
+ this.line(bar("Requirements by severity", [
8892
+ [
8893
+ "critical",
8894
+ "critical",
8895
+ severities.critical
8896
+ ],
8897
+ [
8898
+ "high",
8899
+ "high",
8900
+ severities.high
8901
+ ],
8902
+ [
8903
+ "medium",
8904
+ "medium",
8905
+ severities.medium
8906
+ ],
8907
+ [
8908
+ "low",
8909
+ "low",
8910
+ severities.low
8911
+ ],
8912
+ [
8913
+ "none",
8914
+ "none",
8915
+ severities.informational
8916
+ ]
8917
+ ]));
8918
+ this.line("</div>");
8919
+ const [level, levelLabel] = complianceLevel(calculateCompliance(all));
8920
+ this.line(`<div class="panel compliance compliance-${level}">`);
8921
+ this.line("<h3 id=\"compliance-heading\">Compliance</h3>");
8922
+ this.line(`<div class="gauge" role="meter" aria-valuemin="0" aria-valuemax="100" aria-valuenow="${compliancePercent(all)}" aria-labelledby="compliance-heading" style="--pct:${compliancePercent(all)}"><span class="pct">${compliance(all)}</span></div>`);
8923
+ this.line(`<p class="level">${levelLabel}</p>`);
8924
+ this.line("<p class=\"formula\">Passed / (Passed + Failed + Not Reviewed + Error) × 100</p>");
8925
+ this.line("</div>");
8926
+ this.line("</div>");
8927
+ if (this.aggregated) {
8928
+ const bySource = this.openFold("", "", "h3", "Status by source", "the status by source", this.sources.length);
8929
+ this.line("<div class=\"table-wrap\">");
8930
+ this.line("<table class=\"summary\" aria-label=\"Status by source\">");
8931
+ this.line(summaryHead("Source"));
8932
+ this.line("<tbody>");
8933
+ this.sources.forEach((src, si) => this.line(summaryRow(`<a href="#${sourceId(si)}">${escapeHtml(src.name)}</a>`, perSource[si])));
8934
+ this.line("</tbody>");
8935
+ this.line("<tfoot>");
8936
+ this.line(summaryRow("All sources", all));
8937
+ this.line("</tfoot>");
8938
+ this.line("</table>");
8939
+ this.line("</div>");
8940
+ this.closeFold(bySource);
8941
+ }
8942
+ const baselineCount = this.sources.reduce((n, src) => n + src.baselines.length, 0);
8943
+ const byBaseline = this.openFold("", "", "h3", "Status by baseline", "the status by baseline", baselineCount);
8944
+ this.line("<div class=\"table-wrap\">");
8945
+ this.line("<table class=\"summary\" aria-label=\"Status by baseline\">");
8946
+ this.line(summaryHead("Baseline"));
8947
+ this.line("<tbody>");
8948
+ this.sources.forEach((src, si) => {
8949
+ src.baselineLabels.forEach((own, i) => {
8950
+ const label = this.aggregated ? `${src.name} \u203a ${own}` : own;
8951
+ this.line(summaryRow(escapeHtml(label), perBaseline[si][i]));
8952
+ });
8953
+ });
8954
+ this.line("</tbody>");
8955
+ this.line("<tfoot>");
8956
+ this.line(summaryRow("All baselines", all));
8957
+ this.line("</tfoot>");
8958
+ this.line("</table>");
8959
+ this.line("</div>");
8960
+ this.closeFold(byBaseline);
8961
+ this.line("</section>");
8962
+ }
8963
+ results() {
8964
+ this.line("<section id=\"results\" class=\"card\" aria-labelledby=\"results-heading\">");
8965
+ this.line("<h2 id=\"results-heading\">Results</h2>");
8966
+ this.line("<div class=\"toolbar\">");
8967
+ this.line("<input type=\"search\" id=\"filter-text\" placeholder=\"Filter by ID, title or control\" aria-label=\"Filter requirements by ID, title or control\" />");
8968
+ this.line("<div class=\"filters\" role=\"group\" aria-label=\"Filter by status\">");
8969
+ this.line("<button type=\"button\" class=\"filter\" data-status=\"all\" aria-pressed=\"true\">All</button>");
8970
+ for (const [key, label] of STATUS_FILTERS) this.line(`<button type="button" class="filter" data-status="${key}" aria-pressed="false">${label}</button>`);
8971
+ this.line("</div>");
8972
+ this.line("<button type=\"button\" id=\"expand-all\">Expand all</button>");
8973
+ this.line("<button type=\"button\" id=\"collapse-all\">Collapse all</button>");
8974
+ this.line("<span id=\"filter-count\" class=\"shown\" aria-live=\"polite\"></span>");
8975
+ this.line("</div>");
8976
+ for (const src of this.sources) {
8977
+ this.src = src;
8978
+ let f;
8979
+ if (this.aggregated) {
8980
+ const total = src.baselines.reduce((n, baseline) => n + list(baseline.requirements).length, 0);
8981
+ f = this.openFold("group source", "", "h3", escapeHtml(src.name), src.name, total);
8982
+ }
8983
+ src.baselines.forEach((baseline, i) => this.baseline(baseline, src.baselineLabels[i]));
8984
+ if (f) this.closeFold(f);
8985
+ }
8986
+ this.line("</section>");
8987
+ }
8988
+ baseline(baseline, label) {
8989
+ const requirements = list(baseline.requirements).map(obj);
8990
+ const f = this.openFold("group baseline", "", this.baselineHeading, escapeHtml(label), label, requirements.length);
8991
+ const facts = [
8992
+ ["Name", text(baseline.name)],
8993
+ ["Title", text(baseline.title)],
8994
+ ["Version", text(baseline.version)],
8995
+ ["Summary", text(baseline.summary)],
8996
+ ["Description", text(baseline.description)],
8997
+ ["Maintainer", text(baseline.maintainer)],
8998
+ ["License", text(baseline.license)],
8999
+ ["Copyright", text(baseline.copyright)],
9000
+ ["Status message", text(baseline.statusMessage)]
9001
+ ];
9002
+ const factCount = countFacts(facts);
9003
+ if (factCount > 0) {
9004
+ const details = this.openFold("", "", this.detailHeading, "Baseline details", "the baseline details", factCount);
9005
+ this.definitionList(facts);
9006
+ this.closeFold(details);
9007
+ }
9008
+ this.externalReferences(this.detailHeading, "Enrichment and External References", baseline.externalReferences);
9009
+ if (requirements.length > 0) this.line("<div class=\"req-head\" aria-hidden=\"true\"><span>Status</span><span>ID</span><span>Severity</span><span>Title</span><span>800-53 Controls &amp; CCIs</span></div>");
9010
+ for (const req of requirements) this.requirement(req);
9011
+ this.closeFold(f);
9012
+ }
9013
+ requirement(req) {
9014
+ const effective = this.effectiveStatus(req);
9015
+ const results = list(req.results).map(obj);
9016
+ const severity = requirementSeverity(req, this.src.ref.stamp);
9017
+ const tags = obj(req.tags);
9018
+ const nist = tagItems(tags, "nist");
9019
+ const cci = tagItems(tags, "cci");
9020
+ const [cls] = statusPresentation(effective);
9021
+ this.requirements++;
9022
+ const id = `req-${this.requirements}`;
9023
+ this.line(`<article class="requirement c-${cls}" data-status="${cls}" id="${id}">`);
9024
+ this.line("<details>");
9025
+ const referenceCount = list(req.externalReferences).length;
9026
+ const tagHtml = [...nist, ...cci].map((t, i) => `${i === 0 ? "<span class=\"vh\">Controls: </span>" : ""}<span class="tag">${escapeHtml(t)}</span>`).join("") + (referenceCount > 0 ? `<span class="tag enriched">Enriched (${referenceCount})</span>` : "");
9027
+ this.line(`<summary>${statusBadge(effective)}<span class="req-id">${escapeHtml(text(req.id))}</span><span class="sev c-${severityClass(severity)}"><span class="vh">Severity: </span>${escapeHtml(severityLabel(severity))}</span><span class="req-title">${escapeHtml(text(req.title))}</span><span class="tags">${tagHtml}</span></summary>`);
9028
+ this.line("<div class=\"req-body\">");
9029
+ const descriptions = list(req.descriptions).map(obj);
9030
+ const lead = descriptions.findIndex((d) => d.label === "default");
9031
+ const location = sourceLocation(req.sourceLocation);
9032
+ if (location !== "") this.line(`<p class="location"><span class="k">Location</span><code>${escapeHtml(location)}</code></p>`);
9033
+ const leadText = lead >= 0 ? text(descriptions[lead].data) : "";
9034
+ if (leadText !== "" && isLongText(leadText)) {
9035
+ const f = this.openFold("", "", this.detailHeading, "Description", "the description", lineCount(leadText));
9036
+ this.line(prose(leadText));
9037
+ this.closeFold(f);
9038
+ } else if (leadText !== "") this.line(prose(leadText));
9039
+ this.resultRows(results);
9040
+ const impact = numeric(req.impact);
9041
+ const rows = [detailRow("Effective status", statusBadge(effective)), detailRow("Assessed status", statusBadge(worstStatus(results.map((r) => text(r.status)))))];
9042
+ const details = [
9043
+ ["Control", text(req.id)],
9044
+ ["Title", text(req.title)],
9045
+ ["Severity", severity],
9046
+ ["Impact", impact.toFixed(2)],
9047
+ ["Effective impact", requirementEffectiveImpact(req, this.src.ref.stamp).toFixed(2)],
9048
+ ["Disposition", requirementDisposition(req, this.src.ref.stamp)],
9049
+ ["Control type", text(req.controlType)],
9050
+ ["Verification method", text(req.verificationMethod)],
9051
+ ["Applicability", text(req.applicability)],
9052
+ ["Source location", location],
9053
+ ["NIST Controls", nist.join(", ")],
9054
+ ["CCI Controls", cci.join(", ")],
9055
+ ["CWE", strings(req.cwe).join(", ")],
9056
+ ["CVSS", cvssText(list(req.cvss).map(obj))],
9057
+ ["EPSS", epssText(req.epss)],
9058
+ ["KEV", kevText(req.kev)],
9059
+ ["Affected packages", packagesText(list(req.affectedPackages).map(obj))],
9060
+ ["Evidence", evidenceText(list(req.evidence).map(obj))]
9061
+ ];
9062
+ for (const [term, value] of details) if (value !== "") rows.push(detailRow(term, escapeHtml(value)));
9063
+ const references = referenceLines(list(req.refs).map(obj));
9064
+ if (references.length > 0) rows.push(detailRow("References", prose(references.join("\n"))));
9065
+ descriptions.forEach((d, i) => {
9066
+ if (i !== lead) rows.push(detailRow(escapeHtml(descriptionHeading(text(d.label))), prose(text(d.data))));
9067
+ });
9068
+ const detailsFold = this.openFold("", "", this.detailHeading, "Result Details", "the result details", rows.length);
9069
+ this.line("<div class=\"table-wrap\">");
9070
+ this.line("<table class=\"details\" aria-label=\"Result details\">");
9071
+ this.line("<tbody>");
9072
+ for (const row of rows) this.line(row);
9073
+ this.line("</tbody>");
9074
+ this.line("</table>");
9075
+ this.line("</div>");
9076
+ this.closeFold(detailsFold);
9077
+ this.tagChips(tags);
9078
+ this.externalReferences(this.detailHeading, "Enrichment and External References", req.externalReferences);
9079
+ const overrides = list(req.statusOverrides).map(obj);
9080
+ this.overrideRows(overrides);
9081
+ overrides.forEach((o, i) => this.externalReferences(this.detailHeading, `References for override ${i + 1}`, o.externalReferences));
9082
+ this.poamRows(list(req.poams).map(obj));
9083
+ const code = text(req.code);
9084
+ if (this.reportType === "administrator" && code !== "") {
9085
+ const f = this.openFold("", "", this.detailHeading, "Code", "the code", lineCount(code));
9086
+ this.line(`<pre class="code">\n${escapeHtml(code)}</pre>`);
9087
+ this.closeFold(f);
9088
+ }
9089
+ this.line(`<p class="to-top"><a href="#${id}">Back to the top of this requirement</a></p>`);
9090
+ this.line("</div>");
9091
+ this.line("</details>");
9092
+ this.line("</article>");
9093
+ }
9094
+ resultRows(results) {
9095
+ if (results.length === 0) return;
9096
+ const f = this.openFold("", "", this.detailHeading, "Test Results", "the test results", results.length);
9097
+ this.line("<div class=\"table-wrap\">");
9098
+ this.line("<table aria-label=\"Test results\">");
9099
+ this.line("<thead><tr><th scope=\"col\">Status</th><th scope=\"col\">Test</th><th scope=\"col\">Result</th><th scope=\"col\">Started</th></tr></thead>");
9100
+ this.line("<tbody>");
9101
+ for (const res of results) {
9102
+ const runTime = typeof res.runTime === "number" ? `${res.runTime.toFixed(3)} s` : "";
9103
+ this.line(`<tr><td>${statusBadge(text(res.status))}</td><td class="text">${escapeHtml(text(res.codeDesc))}` + subLine("Resource", joinNonEmpty(" · ", text(res.resource), text(res.resourceId))) + `</td><td class="text">${escapeHtml(text(res.message))}` + subLine("Exception", text(res.exception)) + subLine("Backtrace", strings(res.backtrace).join("\n")) + `</td><td>${escapeHtml(formatTime(res.startTime))}${subLine("Run time", runTime)}</td></tr>`);
9104
+ }
9105
+ this.line("</tbody>");
9106
+ this.line("</table>");
9107
+ this.line("</div>");
9108
+ this.closeFold(f);
9109
+ }
9110
+ overrideRows(overrides) {
9111
+ if (overrides.length === 0) return;
9112
+ const governing = governingStatusOverrideIndex(overrideInputs(overrides), this.src.ref.stamp);
9113
+ const f = this.openFold("", "", this.detailHeading, "Overrides", "the overrides", overrides.length);
9114
+ this.line("<div class=\"table-wrap\">");
9115
+ this.line("<table aria-label=\"Overrides\">");
9116
+ this.line("<thead><tr><th scope=\"col\">Type</th><th scope=\"col\">Status</th><th scope=\"col\">Reason</th><th scope=\"col\">Applied by</th><th scope=\"col\">Applied at</th><th scope=\"col\">Expires at</th><th scope=\"col\">State</th></tr></thead>");
9117
+ this.line("<tbody>");
9118
+ overrides.forEach((o, i) => {
9119
+ const status = text(o.status) === "" ? "" : statusBadge(text(o.status));
9120
+ const expires = toDate$1(o.expiresAt);
9121
+ let state = "";
9122
+ if (i === governing) state = "governing";
9123
+ else if (expires && expires <= this.src.ref.date) state = "expired";
9124
+ const impact = o.impact == null ? "" : numeric(obj(o.impact).value).toFixed(2);
9125
+ this.line(`<tr><td>${escapeHtml(text(o.type))}</td><td>${status}${subLine("Impact", impact)}` + subLine("CVSS", o.cvss == null ? "" : cvssText([obj(o.cvss)])) + `</td><td class="text">${escapeHtml(text(o.reason))}${subLine("Justification", text(o.justification))}</td><td>${escapeHtml(text(obj(o.appliedBy).identifier))}</td><td>${escapeHtml(formatTime(o.appliedAt))}</td><td>${escapeHtml(formatTime(o.expiresAt))}</td><td>${state}</td></tr>`);
9126
+ });
9127
+ this.line("</tbody>");
9128
+ this.line("</table>");
9129
+ this.line("</div>");
9130
+ this.closeFold(f);
9131
+ }
9132
+ /**
9133
+ * References to artifacts outside the document: a CVE, an ATT&CK technique, a
9134
+ * STIX object. A reference that embeds its payload is an enrichment envelope,
9135
+ * and the payload is shown whole because HDF carries it untouched rather than
9136
+ * mapping it into fields.
9137
+ */
9138
+ externalReferences(headingTag, heading, value) {
9139
+ const refs = list(value).map(obj);
9140
+ if (refs.length === 0) return;
9141
+ const f = this.openFold("", "", headingTag, heading, heading.charAt(0).toLowerCase() + heading.slice(1), refs.length);
9142
+ this.line("<div class=\"table-wrap\">");
9143
+ this.line(`<table aria-label="${heading}">`);
9144
+ this.line("<thead><tr><th scope=\"col\">Source</th><th scope=\"col\">ID</th><th scope=\"col\">Kind</th><th scope=\"col\">Relation</th><th scope=\"col\">Detail</th></tr></thead>");
9145
+ this.line("<tbody>");
9146
+ for (const ref of refs) {
9147
+ const checksum = ref.checksum == null ? {} : obj(ref.checksum);
9148
+ const added = joinNonEmpty(" · ", ref.addedBy == null ? "" : text(obj(ref.addedBy).identifier), formatTime(ref.addedAt));
9149
+ this.line(`<tr><td>${escapeHtml(text(ref.sourceName))}</td><td>${escapeHtml(text(ref.externalId))}</td><td>${escapeHtml(text(ref.kind))}</td><td>${escapeHtml(text(ref.rel))}</td><td class="text">` + escapeHtml(text(ref.description)) + subLine("Location", text(ref.href)) + subLine("Media type", text(ref.mediaType)) + subLine("Checksum", joinNonEmpty(":", text(checksum.algorithm), text(checksum.value))) + subLine("Added", added) + embeddedDocument(ref.document) + "</td></tr>");
9150
+ }
9151
+ this.line("</tbody>");
9152
+ this.line("</table>");
9153
+ this.line("</div>");
9154
+ this.closeFold(f);
9155
+ }
9156
+ poamRows(poams) {
9157
+ if (poams.length === 0) return;
9158
+ const f = this.openFold("", "", this.detailHeading, "POA&amp;Ms", "the POA&Ms", poams.length);
9159
+ this.line("<div class=\"table-wrap\">");
9160
+ this.line("<table aria-label=\"Plans of action and milestones\">");
9161
+ this.line("<thead><tr><th scope=\"col\">Type</th><th scope=\"col\">Explanation</th><th scope=\"col\">Applied by</th><th scope=\"col\">Applied at</th><th scope=\"col\">Expires at</th></tr></thead>");
9162
+ this.line("<tbody>");
9163
+ for (const p of poams) {
9164
+ const milestones = list(p.milestones).map(obj).map((m) => subLine("Milestone", joinNonEmpty(" · ", text(m.status), text(m.title), text(m.description), formatTime(m.estimatedCompletion)))).join("");
9165
+ this.line(`<tr><td>${escapeHtml(text(p.type))}</td><td class="text">${escapeHtml(text(p.explanation))}${milestones}</td><td>${escapeHtml(text(obj(p.appliedBy).identifier))}</td><td>${escapeHtml(formatTime(p.appliedAt))}</td><td>${escapeHtml(formatTime(p.expiresAt))}</td></tr>`);
9166
+ }
9167
+ this.line("</tbody>");
9168
+ this.line("</table>");
9169
+ this.line("</div>");
9170
+ this.closeFold(f);
9171
+ }
9172
+ /**
9173
+ * The tags the detail table does not show: every tag but nist and cci whose
9174
+ * value is text, a list of text, or a boolean. Numbers and nested values are
9175
+ * left out because the two languages do not print them alike.
9176
+ */
9177
+ tagChips(tags) {
9178
+ const entries = [];
9179
+ for (const key of Object.keys(tags).sort(byCodePoint)) {
9180
+ if (key === "nist" || key === "cci") continue;
9181
+ const value = tagText(tags[key]);
9182
+ if (value !== null) entries.push([key, value]);
9183
+ }
9184
+ if (entries.length === 0) return;
9185
+ const f = this.openFold("", "", this.detailHeading, "Tags", "the tags", entries.length);
9186
+ this.line("<ul class=\"chips\">");
9187
+ for (const [k, v] of entries) this.line(`<li><span class="k">${escapeHtml(k)}</span><span class="v">${escapeHtml(v)}</span></li>`);
9188
+ this.line("</ul>");
9189
+ this.closeFold(f);
9190
+ }
9191
+ };
9192
+ /**
9193
+ * Counts the external references anywhere in the document and breaks them down
9194
+ * by kind, so even the executive report says whether the results carry
9195
+ * enrichment.
9196
+ */
9197
+ function referenceCensus(doc) {
9198
+ let total = 0;
9199
+ const kinds = /* @__PURE__ */ new Map();
9200
+ const count = (value) => {
9201
+ for (const ref of list(value).map(obj)) {
9202
+ total++;
9203
+ const kind = text(ref.kind);
9204
+ if (kind !== "") kinds.set(kind, (kinds.get(kind) ?? 0) + 1);
9205
+ }
9206
+ };
9207
+ count(doc.externalReferences);
9208
+ for (const baseline of list(doc.baselines).map(obj)) {
9209
+ count(baseline.externalReferences);
9210
+ for (const req of list(baseline.requirements).map(obj)) {
9211
+ count(req.externalReferences);
9212
+ for (const o of list(req.statusOverrides).map(obj)) count(o.externalReferences);
9213
+ }
9214
+ }
9215
+ if (total === 0) return "";
9216
+ const parts = [...kinds.keys()].sort(byCodePoint).map((kind) => `${kind} ${kinds.get(kind)}`);
9217
+ return parts.length === 0 ? String(total) : `${total} (${parts.join(", ")})`;
9218
+ }
9219
+ /**
9220
+ * An enrichment payload: what it says it is, then the whole object. Canonical
9221
+ * JSON is what makes the text the same in both languages.
9222
+ */
9223
+ function embeddedDocument(value) {
9224
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return "";
9225
+ const document = value;
9226
+ return subLine("Embedded", joinNonEmpty(" · ", text(document.type), text(document.name))) + `<details class="doc"><summary>Embedded document</summary><pre class="code">
9227
+ ${escapeHtml(indentJson(canonicalJson(document)))}</pre></details>`;
9228
+ }
9229
+ /**
9230
+ * Lays compact JSON out over lines, two spaces a level. It only inserts
9231
+ * whitespace between tokens, so the values stay exactly as serialized.
9232
+ */
9233
+ function indentJson(compact) {
9234
+ let out = "";
9235
+ let depth = 0;
9236
+ const newline = () => `\n${" ".repeat(depth)}`;
9237
+ let i = 0;
9238
+ while (i < compact.length) {
9239
+ const c = compact[i];
9240
+ const next = compact[i + 1];
9241
+ let end = i + 1;
9242
+ if (c === "\"") {
9243
+ end = stringEnd(compact, i);
9244
+ out += compact.slice(i, end);
9245
+ } else if ((c === "{" || c === "[") && (next === "}" || next === "]")) {
9246
+ end = i + 2;
9247
+ out += c + next;
9248
+ } else if (c === "{" || c === "[") {
9249
+ depth++;
9250
+ out += c + newline();
9251
+ } else if (c === "}" || c === "]") {
9252
+ depth--;
9253
+ out += newline() + c;
9254
+ } else if (c === ",") out += c + newline();
9255
+ else out += c === ":" ? ": " : c;
9256
+ i = end;
9257
+ }
9258
+ return out;
9259
+ }
9260
+ /** The index just past the JSON string that opens at `start`; the end of the text when it never closes. */
9261
+ function stringEnd(text, start) {
9262
+ let i = start + 1;
9263
+ while (i < text.length) {
9264
+ if (text[i] === "\"") return i + 1;
9265
+ i += text[i] === "\\" ? 2 : 1;
9266
+ }
9267
+ return text.length;
9268
+ }
9269
+ function strings(value) {
9270
+ return list(value).filter((item) => typeof item === "string");
9271
+ }
9272
+ function tagText(value) {
9273
+ if (typeof value === "string") return value;
9274
+ if (typeof value === "boolean") return String(value);
9275
+ if (Array.isArray(value)) {
9276
+ const items = strings(value);
9277
+ return items.length > 0 ? items.join(", ") : null;
9278
+ }
9279
+ return null;
9280
+ }
9281
+ /** A labelled secondary line inside a table cell; empty when there is nothing to say. */
9282
+ function subLine(label, value) {
9283
+ return value === "" ? "" : `<span class="sub"><span class="k">${label}</span> ${escapeHtml(value)}</span>`;
9284
+ }
9285
+ /** Where the finding sits in the assessed source: file, and line when there is one. */
9286
+ function sourceLocation(value) {
9287
+ if (value == null) return "";
9288
+ const loc = obj(value);
9289
+ const ref = text(loc.ref);
9290
+ if (typeof loc.line !== "number") return ref;
9291
+ return ref === "" ? `line ${loc.line}` : `${ref}:${loc.line}`;
9292
+ }
9293
+ function cvssText(entries) {
9294
+ const parts = [];
9295
+ for (const c of entries) {
9296
+ const score = typeof c.computedScore === "number" ? c.computedScore : c.baseScore;
9297
+ const severity = typeof c.computedSeverity === "string" ? c.computedSeverity : text(c.baseSeverity);
9298
+ const version = text(c.version);
9299
+ const source = text(c.source);
9300
+ const part = joinNonEmpty(" ", version === "" ? "" : `v${version}`, typeof score === "number" ? score.toFixed(1) : "", severity, text(c.baseVector), source === "" ? "" : `(${source})`);
9301
+ if (part !== "") parts.push(part);
9302
+ }
9303
+ return parts.join("; ");
9304
+ }
9305
+ function epssText(value) {
9306
+ if (value == null) return "";
9307
+ const e = obj(value);
9308
+ const date = text(e.date);
9309
+ return `score ${numeric(e.score).toFixed(5)}, percentile ${numeric(e.percentile).toFixed(5)}` + (date === "" ? "" : `, as of ${date}`);
9310
+ }
9311
+ function kevText(value) {
9312
+ if (value == null) return "";
9313
+ const k = obj(value);
9314
+ let out = k.inKev === true ? "Listed" : "Not listed";
9315
+ if (text(k.dateAdded) !== "") out += `, added ${text(k.dateAdded)}`;
9316
+ if (text(k.dueDate) !== "") out += `, due ${text(k.dueDate)}`;
9317
+ if (text(k.notes) !== "") out += `, ${text(k.notes)}`;
9318
+ return out;
9319
+ }
9320
+ function packagesText(packages) {
9321
+ const parts = [];
9322
+ for (const p of packages) {
9323
+ let part = joinNonEmpty(" ", text(p.name), text(p.version));
9324
+ if (part === "") part = joinNonEmpty(" ", text(p.purl), text(p.cpe));
9325
+ const fixed = text(p.fixedInVersion);
9326
+ if (fixed !== "") part = joinNonEmpty(" ", part, `(fixed in ${fixed})`);
9327
+ if (part !== "") parts.push(part);
9328
+ }
9329
+ return parts.join("; ");
9330
+ }
9331
+ function evidenceText(evidence) {
9332
+ return evidence.map((e) => joinNonEmpty(": ", text(e.type), text(e.description))).filter((part) => part !== "").join("; ");
9333
+ }
9334
+ /**
9335
+ * Each reference's text. A structured ref is reduced to the URLs inside it,
9336
+ * sorted, because the Go peer decodes it to a map, which has no order to
9337
+ * preserve.
9338
+ */
9339
+ function referenceLines(refs) {
9340
+ const lines = [];
9341
+ for (const ref of refs) {
9342
+ if (typeof ref.ref === "string") {
9343
+ if (ref.ref !== "") lines.push(ref.ref);
9344
+ } else if (Array.isArray(ref.ref)) {
9345
+ const urls = /* @__PURE__ */ new Set();
9346
+ collectUrls(ref.ref, urls);
9347
+ lines.push(...[...urls].sort(byCodePoint));
9348
+ }
9349
+ for (const s of [text(ref.url), text(ref.uri)]) if (s !== "") lines.push(s);
9350
+ }
9351
+ return lines;
9352
+ }
9353
+ function collectUrls(value, into) {
9354
+ if (Array.isArray(value)) for (const child of value) collectUrls(child, into);
9355
+ else if (typeof value === "object" && value !== null) for (const [k, child] of Object.entries(value)) if (k === "url" && typeof child === "string" && child !== "") into.add(child);
9356
+ else collectUrls(child, into);
9357
+ }
9358
+ /** Component fields in display order; the second member is the JSON key. */
9359
+ const COMPONENT_FIELDS = [
9360
+ ["Component ID", "componentId"],
9361
+ ["Description", "description"],
9362
+ ["Hostname", "hostname"],
9363
+ ["FQDN", "fqdn"],
9364
+ ["Domain", "domain"],
9365
+ ["IP address", "ipAddress"],
9366
+ ["MAC address", "macAddress"],
9367
+ ["OS name", "osName"],
9368
+ ["OS version", "osVersion"],
9369
+ ["Image ID", "imageId"],
9370
+ ["Registry", "registry"],
9371
+ ["Repository", "repository"],
9372
+ ["Tag", "tag"],
9373
+ ["Container ID", "containerId"],
9374
+ ["Image", "image"],
9375
+ ["Runtime", "runtime"],
9376
+ ["Cluster name", "clusterName"],
9377
+ ["Namespace", "namespace"],
9378
+ ["Platform type", "platformType"],
9379
+ ["Version", "version"],
9380
+ ["Account ID", "accountId"],
9381
+ ["Provider", "provider"],
9382
+ ["Region", "region"],
9383
+ ["ARN", "arn"],
9384
+ ["Resource ID", "resourceId"],
9385
+ ["Resource type", "resourceType"],
9386
+ ["Branch", "branch"],
9387
+ ["Commit", "commit"],
9388
+ ["URL", "url"],
9389
+ ["Environment", "environment"],
9390
+ ["Package manager", "packageManager"],
9391
+ ["Package name", "packageName"],
9392
+ ["CIDR", "cidr"],
9393
+ ["Gateway", "gateway"],
9394
+ ["Engine", "engine"],
9395
+ ["Host", "host"],
9396
+ ["Port", "port"],
9397
+ ["Model ID", "modelId"],
9398
+ ["Dataset ID", "datasetId"],
9399
+ ["Owner", "owner"]
9400
+ ];
9401
+ function identity(value) {
9402
+ if (value == null) return "";
9403
+ const id = obj(value);
9404
+ const type = text(id.type);
9405
+ if (type === "") return text(id.identifier);
9406
+ return joinNonEmpty(" ", text(id.identifier), `(${type})`);
9407
+ }
9408
+ /** A summary table row; `name` is already HTML. */
9409
+ function summaryRow(name, c) {
9410
+ return `<tr><th scope="row">${name}</th>${[
9411
+ c.passed.total,
9412
+ c.failed.total,
9413
+ c.skipped.total,
9414
+ c.noImpact.total,
9415
+ c.error.total,
9416
+ severityTotals(c).total
9417
+ ].map((n) => `<td>${n}</td>`).join("")}<td>${compliance(c)}</td></tr>`;
9418
+ }
9419
+ function summaryHead(first) {
9420
+ return `<thead><tr><th scope="col">${first}</th><th scope="col">Passed</th><th scope="col">Failed</th><th scope="col">Not Reviewed</th><th scope="col">Not Applicable</th><th scope="col">Error</th><th scope="col">Total</th><th scope="col">Compliance</th></tr></thead>`;
9421
+ }
9422
+ function sourceId(i) {
9423
+ return `source-${i + 1}`;
9424
+ }
9425
+ function componentField(c, term, key) {
9426
+ if (key === "port") return [term, typeof c.port === "number" ? String(c.port) : ""];
9427
+ if (key === "owner") return [term, identity(c.owner)];
9428
+ return [term, text(c[key])];
9429
+ }
9430
+ function documentFacts(doc) {
9431
+ const tool = obj(doc.tool);
9432
+ const generator = obj(doc.generator);
9433
+ const duration = obj(doc.statistics).duration;
9434
+ return [
9435
+ ["Tool", joinNonEmpty(" ", text(tool.name), text(tool.version))],
9436
+ ["Tool format", text(tool.format)],
9437
+ ["Generator", joinNonEmpty(" ", text(generator.name), text(generator.version))],
9438
+ ["Assessed", formatTime(doc.timestamp)],
9439
+ ["Duration", typeof duration === "number" ? `${duration.toFixed(2)} s` : ""],
9440
+ ["System reference", text(doc.systemRef)],
9441
+ ["Plan reference", text(doc.planRef)],
9442
+ ["Document ID", text(doc.id)],
9443
+ ["External references", referenceCensus(doc)]
9444
+ ];
9445
+ }
9446
+ function runnerFacts(runner) {
9447
+ return [
9448
+ ["Name", text(runner.name)],
9449
+ ["Hostname", text(runner.hostname)],
9450
+ ["FQDN", text(runner.fqdn)],
9451
+ ["Domain", text(runner.domain)],
9452
+ ["Architecture", text(runner.architecture)],
9453
+ ["Release", text(runner.release)],
9454
+ ["Container image", text(runner.containerImage)],
9455
+ ["Container ID", text(runner.containerId)],
9456
+ ["Operator", identity(runner.operator)]
9457
+ ];
9458
+ }
9459
+ const STATUS_PRESENTATION = {
9460
+ passed: ["passed", "Passed"],
9461
+ failed: ["failed", "Failed"],
9462
+ notReviewed: ["not-reviewed", "Not Reviewed"],
9463
+ notApplicable: ["not-applicable", "Not Applicable"],
9464
+ error: ["error", "Error"]
9465
+ };
9466
+ /** A status with a class drawn from a fixed set, never from the document: one outside the enum is shown as text under one class. */
9467
+ function statusPresentation(status) {
9468
+ return Object.prototype.hasOwnProperty.call(STATUS_PRESENTATION, status) ? STATUS_PRESENTATION[status] : ["unknown", status];
9469
+ }
9470
+ /**
9471
+ * The mark beside the status word. Colour is the least reliable of the three
9472
+ * channels — it is lost to colour-vision deficiency and to a greyscale printer —
9473
+ * so every status carries a shape and a word as well.
9474
+ */
9475
+ const STATUS_ICON = {
9476
+ passed: "✓",
9477
+ failed: "✗",
9478
+ "not-applicable": "–",
9479
+ "not-reviewed": "?",
9480
+ error: "!"
9481
+ };
9482
+ function statusIcon(cls) {
9483
+ return Object.prototype.hasOwnProperty.call(STATUS_ICON, cls) ? STATUS_ICON[cls] : "·";
9484
+ }
9485
+ function statusBadge(status) {
9486
+ const [cls, label] = statusPresentation(status);
9487
+ return `<span class="status c-${cls}"><span class="ico" aria-hidden="true">${statusIcon(cls)}</span>${escapeHtml(label)}</span>`;
9488
+ }
9489
+ /** The string members of an array-valued tag; a bare string tag is a one-member list. */
9490
+ function tagItems(tags, key) {
9491
+ const value = Object.prototype.hasOwnProperty.call(tags, key) ? tags[key] : void 0;
9492
+ if (typeof value === "string") return [value];
9493
+ if (Array.isArray(value)) return value.filter((item) => typeof item === "string");
9494
+ return [];
9495
+ }
9496
+ function joinNonEmpty(sep, ...parts) {
9497
+ return parts.filter((p) => p !== "").join(sep);
9498
+ }
9499
+ /**
9500
+ * Makes text safe as HTML content and inside a double-quoted attribute. A
9501
+ * carriage return is written as a character reference because a parser would
9502
+ * otherwise fold it into the following newline. Characters HTML cannot carry at
9503
+ * all, and unpaired surrogates, become U+FFFD.
9504
+ */
9505
+ function escapeHtml(value) {
9506
+ let out = "";
9507
+ for (const ch of value) {
9508
+ const c = ch.codePointAt(0);
9509
+ if (ch === "&") out += "&amp;";
9510
+ else if (ch === "<") out += "&lt;";
9511
+ else if (ch === ">") out += "&gt;";
9512
+ else if (ch === "\"") out += "&quot;";
9513
+ else if (ch === "'") out += "&#39;";
9514
+ else if (ch === "\r") out += "&#13;";
9515
+ else if (ch === " " || ch === "\n") out += ch;
9516
+ else if (c < 32 || c === 127 || c === 65534 || c === 65535 || c >= 55296 && c <= 57343) out += "�";
9517
+ else out += ch;
7930
9518
  }
7931
- return out.join("; ");
9519
+ return out;
7932
9520
  }
7933
9521
  //#endregion
7934
9522
  //#region converters/hdf-to-ecs/typescript/converter.ts
@@ -8017,15 +9605,17 @@ function buildEvent(req, baseline, docTimestamp, tool, generator, component, con
8017
9605
  }
8018
9606
  /**
8019
9607
  * Surface override provenance into ECS labels.* (keyword bag): the disposition
8020
- * plus the governing statusOverrides[0] fields. Returns undefined when the
8021
- * requirement carries no disposition or overrides.
9608
+ * plus the fields of the override that GOVERNS — the most recently applied
9609
+ * non-expired one, resolved by appliedAt rather than by array position, because
9610
+ * this repo's writers append and so put the newest override last. Returns
9611
+ * undefined when the requirement carries no disposition or overrides.
8022
9612
  */
8023
9613
  function buildLabels(req) {
8024
9614
  const labels = {};
8025
- setIf(labels, "hdf_disposition", getStr(req, "disposition"));
9615
+ setIf(labels, "hdf_disposition", disposition(req));
8026
9616
  const overrides = asArr(req.statusOverrides);
8027
9617
  if (overrides && overrides.length > 0) {
8028
- const ov = asMap(overrides[0]);
9618
+ const ov = governingOverrideOf(overrides);
8029
9619
  if (ov) {
8030
9620
  setIf(labels, "hdf_override_type", getStr(ov, "type"));
8031
9621
  setIf(labels, "hdf_override_reason", getStr(ov, "reason"));
@@ -8389,13 +9979,13 @@ function complianceStatusCaption(statusID) {
8389
9979
  }
8390
9980
  }
8391
9981
  function overrideComment(req) {
8392
- const disposition = getStr(req, "disposition");
9982
+ const disp = disposition(req);
8393
9983
  const overrides = asArr(req.statusOverrides) ?? [];
8394
- if (disposition === "" && overrides.length === 0) return "";
9984
+ if (disp === "" && overrides.length === 0) return "";
8395
9985
  let reason = "";
8396
- if (overrides.length > 0) reason = getStr(asMap(overrides[0]), "reason");
8397
- if (disposition !== "" && reason !== "") return `${disposition}: ${reason}`;
8398
- if (disposition !== "") return disposition;
9986
+ if (overrides.length > 0) reason = getStr(governingOverrideOf(overrides), "reason");
9987
+ if (disp !== "" && reason !== "") return `${disp}: ${reason}`;
9988
+ if (disp !== "") return disp;
8399
9989
  return reason;
8400
9990
  }
8401
9991
  function buildMetadata$1(tool, generator, converterVersion) {
@@ -8616,7 +10206,7 @@ function floatMetric(f) {
8616
10206
  * byte-identical with the Go implementation via canonicalize + stringifyLine.
8617
10207
  */
8618
10208
  const ASFF_SCHEMA_VERSION = "2018-10-08";
8619
- const CONVERTER_NAME = "hdf-to-asff";
10209
+ const CONVERTER_NAME$1 = "hdf-to-asff";
8620
10210
  const PLACEHOLDER_REGION = "us-east-1";
8621
10211
  const PLACEHOLDER_ACCOUNT_ID = "000000000000";
8622
10212
  const DEFAULT_PARTITION = "aws";
@@ -8624,7 +10214,7 @@ const MAX_TITLE = 256;
8624
10214
  const MAX_DESCRIPTION = 1024;
8625
10215
  const EPOCH_SENTINEL = "1970-01-01T00:00:00Z";
8626
10216
  function convertHdfToAsff(input, converterVersion = "0.1.0") {
8627
- const name = CONVERTER_NAME;
10217
+ const name = CONVERTER_NAME$1;
8628
10218
  validateInputSize$1(input, name);
8629
10219
  const doc = parseHdf(input);
8630
10220
  const baselines = asArr(doc.baselines);
@@ -8670,7 +10260,7 @@ function recoverAccountID(doc) {
8670
10260
  return PLACEHOLDER_ACCOUNT_ID;
8671
10261
  }
8672
10262
  function buildFinding(req, ctx) {
8673
- const controlID = firstNonEmpty(getStr(req, "id"), getStr(req, "title"), CONVERTER_NAME);
10263
+ const controlID = firstNonEmpty(getStr(req, "id"), getStr(req, "title"), CONVERTER_NAME$1);
8674
10264
  const st = statusOf(req);
8675
10265
  const title = getStr(req, "title") || controlID;
8676
10266
  const desc = defaultDescription(req) || title;
@@ -9161,7 +10751,7 @@ function convertHdfToXml(input) {
9161
10751
  }
9162
10752
  //#endregion
9163
10753
  //#region converters/gitlab-to-hdf/typescript/converter.ts
9164
- function buildRefs$6(vuln) {
10754
+ function buildRefs$7(vuln) {
9165
10755
  const refs = [];
9166
10756
  const seen = /* @__PURE__ */ new Set();
9167
10757
  const appendUrl = (url) => {
@@ -9201,7 +10791,7 @@ function scanTypeLabel(scanType) {
9201
10791
  api_fuzzing: "API Fuzzing"
9202
10792
  }[scanType] ?? scanType.toUpperCase();
9203
10793
  }
9204
- function buildNistTags(identifiers) {
10794
+ function buildNistTags$1(identifiers) {
9205
10795
  const seen = /* @__PURE__ */ new Set();
9206
10796
  const controls = [];
9207
10797
  for (const id of identifiers) if (id.type === "cwe" && id.value) {
@@ -9226,14 +10816,14 @@ function collectIdentifierTags(identifiers) {
9226
10816
  }
9227
10817
  return result;
9228
10818
  }
9229
- function buildSourceLocation$2(location) {
10819
+ function buildSourceLocation$3(location) {
9230
10820
  if (!location?.file) return void 0;
9231
10821
  const sourceLocation = { ref: location.file };
9232
10822
  const line = location.start_line ?? location.end_line;
9233
10823
  if (line != null) sourceLocation.line = line;
9234
10824
  return sourceLocation;
9235
10825
  }
9236
- function buildCodeDesc$5(scanType, location) {
10826
+ function buildCodeDesc$6(scanType, location) {
9237
10827
  if (!location) return "";
9238
10828
  switch (scanType) {
9239
10829
  case "sast":
@@ -9295,7 +10885,7 @@ async function convertGitlabToHdf(input, converterVersion = "1.0.0") {
9295
10885
  const requirements = [];
9296
10886
  for (const vuln of limitedVulns) {
9297
10887
  const identifiers = vuln.identifiers ?? [];
9298
- const nistTags = buildNistTags(identifiers);
10888
+ const nistTags = buildNistTags$1(identifiers);
9299
10889
  const cciTags = nistToCci(nistTags);
9300
10890
  const idTags = collectIdentifierTags(identifiers);
9301
10891
  const extras = {};
@@ -9322,7 +10912,7 @@ async function convertGitlabToHdf(input, converterVersion = "1.0.0") {
9322
10912
  }
9323
10913
  const result = {
9324
10914
  status: ResultStatus.Failed,
9325
- codeDesc: buildCodeDesc$5(scanType, vuln.location),
10915
+ codeDesc: buildCodeDesc$6(scanType, vuln.location),
9326
10916
  startTime: startTime ? parseTimestamp(startTime) ?? /* @__PURE__ */ new Date("0001-01-01T00:00:00Z") : /* @__PURE__ */ new Date("0001-01-01T00:00:00Z")
9327
10917
  };
9328
10918
  const impact = gitlabSeverityToImpact(vuln.severity ?? "Unknown");
@@ -9336,11 +10926,11 @@ async function convertGitlabToHdf(input, converterVersion = "1.0.0") {
9336
10926
  descriptions,
9337
10927
  verificationMethod: VerificationMethodEnum.Automated
9338
10928
  };
9339
- const refs = buildRefs$6(vuln);
10929
+ const refs = buildRefs$7(vuln);
9340
10930
  if (refs) req.refs = refs;
9341
10931
  const controlType = deriveControlTypeFromTags(nistTags);
9342
10932
  if (controlType !== void 0) req.controlType = controlType;
9343
- const sourceLocation = buildSourceLocation$2(vuln.location);
10933
+ const sourceLocation = buildSourceLocation$3(vuln.location);
9344
10934
  if (sourceLocation) req.sourceLocation = sourceLocation;
9345
10935
  requirements.push(req);
9346
10936
  }
@@ -9379,6 +10969,678 @@ async function convertGitlabToHdf(input, converterVersion = "1.0.0") {
9379
10969
  return serializeHdf(hdf);
9380
10970
  }
9381
10971
  //#endregion
10972
+ //#region converters/gitlab-vulnerabilities-to-hdf/typescript/converter.ts
10973
+ /**
10974
+ * GitLab Vulnerability Report (GraphQL) to HDF Results — TypeScript twin of
10975
+ * go/converter.go. Converts the envelope the gitlab-vulnerabilities fetcher
10976
+ * assembles for one project into HDF, keeping triage state lossless.
10977
+ *
10978
+ * Status is raw-primary: results[].status is what the scanner last reported on
10979
+ * the default branch, and every human triage decision rides as an attributed,
10980
+ * expiring Status_Override so effectiveStatus, effectiveImpact and disposition
10981
+ * are computed through the shared ladder rather than assigned.
10982
+ */
10983
+ const CONVERTER_NAME = "gitlab-vulnerabilities";
10984
+ const SOURCE_NAME = "GitLab Vulnerability Report";
10985
+ const GENERATOR_NAME = "gitlab-vulnerabilities-to-hdf";
10986
+ function filtersActive(f) {
10987
+ return !!f && ((f.states?.length ?? 0) > 0 || (f.reportTypes?.length ?? 0) > 0);
10988
+ }
10989
+ function describeFilters(f) {
10990
+ const parts = [];
10991
+ if (f.states?.length) parts.push(`states ${f.states.join(", ")}`);
10992
+ if (f.reportTypes?.length) parts.push(`report types ${f.reportTypes.join(", ")}`);
10993
+ return parts.join("; ");
10994
+ }
10995
+ function parseInput(input) {
10996
+ if (!input || input.trim() === "") throw new Error(`${CONVERTER_NAME}: empty input`);
10997
+ validateInputSize$1(input, CONVERTER_NAME);
10998
+ let env;
10999
+ try {
11000
+ env = parseJSON(input);
11001
+ } catch (e) {
11002
+ throw new Error(`${CONVERTER_NAME}: invalid envelope JSON: ${e.message}`);
11003
+ }
11004
+ if (typeof env !== "object" || env === null || Array.isArray(env)) throw new Error(`${CONVERTER_NAME}: input is not a Vulnerability Report envelope (project.fullPath missing)`);
11005
+ if (!env.project?.fullPath) throw new Error(`${CONVERTER_NAME}: input is not a Vulnerability Report envelope (project.fullPath missing)`);
11006
+ if (!env.metadata?.enterprise) throw new Error(`${CONVERTER_NAME}: GitLab Community Edition has no Vulnerability Report; use the gitlab (CI artifact) converter instead`);
11007
+ if (!Array.isArray(env.vulnerabilities)) throw new Error(`${CONVERTER_NAME}: envelope has no vulnerabilities array; an absent or null list is a malformed envelope, not an empty report`);
11008
+ return env;
11009
+ }
11010
+ function sortedSectionKeys(pipeline) {
11011
+ const summary = pipeline.securityReportSummary ?? {};
11012
+ return Object.keys(summary).filter((k) => summary[k] != null).sort();
11013
+ }
11014
+ function scansOf(pipeline, key) {
11015
+ return pipeline.securityReportSummary?.[key]?.scans?.nodes ?? [];
11016
+ }
11017
+ function ingestionError(env) {
11018
+ const project = env.project;
11019
+ if (project.vulnerabilityStatistic != null) return void 0;
11020
+ const pipeline = project.latestDefaultBranchPipeline;
11021
+ if (!pipeline) return /* @__PURE__ */ new Error(`${CONVERTER_NAME}: ${project.fullPath} has no default-branch pipeline; no security scanner has run`);
11022
+ const created = [];
11023
+ const failed = [];
11024
+ let succeeded = false;
11025
+ for (const reportType of sortedSectionKeys(pipeline)) for (const scan of scansOf(pipeline, reportType)) switch (scan.status ?? "") {
11026
+ case "SUCCEEDED":
11027
+ succeeded = true;
11028
+ break;
11029
+ case "CREATED":
11030
+ case "PREPARING":
11031
+ created.push(reportType);
11032
+ break;
11033
+ case "":
11034
+ failed.push(`${reportType}: status unknown`);
11035
+ break;
11036
+ default: {
11037
+ let desc = `${reportType}: ${scan.status}`;
11038
+ const notes = [...scan.errors ?? [], ...scan.warnings ?? []];
11039
+ if (notes.length > 0) desc += ` (${notes.join("; ")})`;
11040
+ failed.push(desc);
11041
+ }
11042
+ }
11043
+ if (succeeded) return void 0;
11044
+ if (failed.length > 0) return /* @__PURE__ */ new Error(`${CONVERTER_NAME}: scanner jobs on the default branch of ${project.fullPath} produced no usable report: ${failed.join(", ")}`);
11045
+ if (created.length > 0) return /* @__PURE__ */ new Error(`${CONVERTER_NAME}: security reports for ${project.fullPath} were produced but never ingested (${created.join(", ")} stuck at CREATED); the Vulnerability Report requires GitLab Ultimate`);
11046
+ return /* @__PURE__ */ new Error(`${CONVERTER_NAME}: no security scanner has run on the default branch of ${project.fullPath}`);
11047
+ }
11048
+ function ingestedReportTypes(pipeline) {
11049
+ if (!pipeline) return [];
11050
+ return sortedSectionKeys(pipeline).filter((key) => scansOf(pipeline, key).some((s) => s.status === "SUCCEEDED"));
11051
+ }
11052
+ async function convertGitlabVulnerabilitiesToHdf(input, converterVersion = "1.0.0") {
11053
+ const env = parseInput(input);
11054
+ const fetchedAt = parseTimestamp(env.fetchedAt ?? "");
11055
+ if (!fetchedAt) throw new Error(`${CONVERTER_NAME}: envelope fetchedAt "${env.fetchedAt ?? ""}" is not a valid timestamp`);
11056
+ const resultsChecksum = await inputChecksum(input);
11057
+ const project = env.project;
11058
+ const vulnerabilities = env.vulnerabilities ?? [];
11059
+ let baselines;
11060
+ if (vulnerabilities.length === 0) {
11061
+ const err = ingestionError(env);
11062
+ if (err) throw err;
11063
+ baselines = filtersActive(env.filters) ? filteredNoMatchBaselines(env.filters, project, fetchedAt, resultsChecksum) : noFindingsBaselines(project, fetchedAt, resultsChecksum);
11064
+ } else baselines = findingBaselines(vulnerabilities, project, fetchedAt, resultsChecksum);
11065
+ return buildHdfResults({
11066
+ generatorName: GENERATOR_NAME,
11067
+ converterVersion,
11068
+ toolName: SOURCE_NAME,
11069
+ toolVersion: env.metadata?.version,
11070
+ baselines,
11071
+ components: [buildComponent$2(project)],
11072
+ timestamp: fetchedAt
11073
+ });
11074
+ }
11075
+ function findingBaselines(vulnerabilities, project, fetchedAt, checksum) {
11076
+ const limited = limitArrayWithWarning(vulnerabilities, "vulnerability");
11077
+ const byType = /* @__PURE__ */ new Map();
11078
+ const scanners = /* @__PURE__ */ new Map();
11079
+ for (const v of limited) {
11080
+ const reportType = v.reportType || "GENERIC";
11081
+ if (!byType.has(reportType)) {
11082
+ byType.set(reportType, []);
11083
+ scanners.set(reportType, /* @__PURE__ */ new Set());
11084
+ }
11085
+ if (v.scanner?.name) scanners.get(reportType).add(v.scanner.name);
11086
+ byType.get(reportType).push(convertVulnerability(v, project, fetchedAt));
11087
+ }
11088
+ return [...byType.keys()].sort().map((reportType) => {
11089
+ const title = reportTypeLabel(reportType);
11090
+ const names = [...scanners.get(reportType)].sort();
11091
+ return {
11092
+ name: `${SOURCE_NAME}: ${title}`,
11093
+ title,
11094
+ summary: names.length > 0 ? `Scanner: ${names.join(", ")}` : "Scanner: unknown",
11095
+ requirements: byType.get(reportType),
11096
+ resultsChecksum: checksum
11097
+ };
11098
+ });
11099
+ }
11100
+ function noFindingsBaselines(project, fetchedAt, checksum) {
11101
+ let types = ingestedReportTypes(project.latestDefaultBranchPipeline);
11102
+ if (types.length === 0) types = ["generic"];
11103
+ const requirements = types.map((key) => {
11104
+ const reportType = summaryKeyToReportType(key);
11105
+ return buildNoFindingsRequirement(`gitlab-vulnerability-report-no-findings-${reportType.toLowerCase()}`, `GitLab Vulnerability Report for ${project.fullPath} lists zero ${reportTypeLabel(reportType)} vulnerabilities after a successful scan ingestion.`, fetchedAt);
11106
+ });
11107
+ return [{
11108
+ name: SOURCE_NAME,
11109
+ title: "No findings",
11110
+ summary: `Ingested scanner types: ${types.join(", ")}`,
11111
+ requirements,
11112
+ resultsChecksum: checksum
11113
+ }];
11114
+ }
11115
+ /**
11116
+ * Renders "nothing matched what you asked for", deliberately not the
11117
+ * no-findings wording: this describes the selection, not the project.
11118
+ */
11119
+ function filteredNoMatchBaselines(filters, project, fetchedAt, checksum) {
11120
+ const selection = describeFilters(filters);
11121
+ const req = buildNoFindingsRequirement("gitlab-vulnerability-report-no-match", `No vulnerability in the GitLab Vulnerability Report for ${project.fullPath} matches the requested selection (${selection}). This describes the selection only, not the project's overall posture.`, fetchedAt);
11122
+ req.tags["gitlab/filtered"] = true;
11123
+ req.tags["gitlab/filterStates"] = filters.states ?? [];
11124
+ req.tags["gitlab/filterReportTypes"] = filters.reportTypes ?? [];
11125
+ return [{
11126
+ name: SOURCE_NAME,
11127
+ title: "No matching findings",
11128
+ summary: `Filtered selection: ${selection}`,
11129
+ requirements: [req],
11130
+ resultsChecksum: checksum
11131
+ }];
11132
+ }
11133
+ function summaryKeyToReportType(key) {
11134
+ return key.replace(/([A-Z])/g, "_$1").toUpperCase();
11135
+ }
11136
+ const REPORT_TYPE_LABELS = {
11137
+ SAST: "SAST",
11138
+ DEPENDENCY_SCANNING: "Dependency Scanning",
11139
+ CONTAINER_SCANNING: "Container Scanning",
11140
+ CONTAINER_SCANNING_FOR_REGISTRY: "Container Scanning for Registry",
11141
+ DAST: "DAST",
11142
+ SECRET_DETECTION: "Secret Detection",
11143
+ COVERAGE_FUZZING: "Coverage Fuzzing",
11144
+ API_FUZZING: "API Fuzzing",
11145
+ CLUSTER_IMAGE_SCANNING: "Cluster Image Scanning",
11146
+ GENERIC: "Generic"
11147
+ };
11148
+ function reportTypeLabel(reportType) {
11149
+ return REPORT_TYPE_LABELS[reportType] ?? reportType;
11150
+ }
11151
+ function buildComponent$2(project) {
11152
+ const c = {
11153
+ name: project.fullPath ?? "",
11154
+ type: TargetType.Repository,
11155
+ labels: {
11156
+ "gitlab/project-id": project.id ?? "",
11157
+ "gitlab/full-path": project.fullPath ?? ""
11158
+ }
11159
+ };
11160
+ if (project.webUrl) c.url = project.webUrl;
11161
+ if (project.repository?.rootRef) c.branch = project.repository.rootRef;
11162
+ if (project.latestDefaultBranchPipeline?.sha) c.commit = project.latestDefaultBranchPipeline.sha;
11163
+ return c;
11164
+ }
11165
+ function convertVulnerability(v, project, fetchedAt) {
11166
+ const { original, current } = severityPair(v);
11167
+ const code = buildVulnCode$1(v);
11168
+ const tags = buildTags$3(v, project, original, current);
11169
+ const result = {
11170
+ status: rawStatus(v),
11171
+ codeDesc: buildCodeDesc$5(v),
11172
+ startTime: resultStartTime(v, fetchedAt)
11173
+ };
11174
+ const req = {
11175
+ id: v.uuid ?? "",
11176
+ title: v.title ?? "",
11177
+ descriptions: buildDescriptions(v),
11178
+ impact: severityToImpact$1(original),
11179
+ tags,
11180
+ code,
11181
+ results: [result]
11182
+ };
11183
+ const refs = buildRefs$6(v);
11184
+ if (refs.length > 0) req.refs = refs;
11185
+ const sourceLocation = buildSourceLocation$2(v.location);
11186
+ if (sourceLocation) req.sourceLocation = sourceLocation;
11187
+ const cvss = buildCvss$2(v);
11188
+ if (cvss.length > 0) req.cvss = cvss;
11189
+ const verificationMethod = deriveVerificationMethod(code);
11190
+ if (verificationMethod !== void 0) req.verificationMethod = verificationMethod;
11191
+ const controlType = deriveControlTypeFromTags(tags.nist ?? []);
11192
+ if (controlType !== void 0) req.controlType = controlType;
11193
+ const overrides = [...buildStatusOverrides(v, fetchedAt), ...buildSeverityOverrides(v, fetchedAt)];
11194
+ if (overrides.length > 0) {
11195
+ sortMostRecentFirst(overrides);
11196
+ req.statusOverrides = overrides;
11197
+ applyEffectivePosture(req, fetchedAt);
11198
+ }
11199
+ return req;
11200
+ }
11201
+ function rawStatus(v) {
11202
+ if (v.state === "RESOLVED" && v.resolvedOnDefaultBranch) return ResultStatus.Passed;
11203
+ return ResultStatus.Failed;
11204
+ }
11205
+ /**
11206
+ * GitLab's `severity` already reflects human overrides, so the original comes
11207
+ * from the earliest one. The connection's order is not part of GitLab's
11208
+ * contract, so the earliest is found by timestamp rather than by position.
11209
+ */
11210
+ function severityPair(v) {
11211
+ const current = v.severity ?? "";
11212
+ let original = current;
11213
+ let earliest;
11214
+ let found = false;
11215
+ for (const o of v.severityOverrides?.nodes ?? []) {
11216
+ if (!o.originalSeverity) continue;
11217
+ const at = parseTimestamp(o.createdAt ?? "");
11218
+ if (!found) {
11219
+ original = o.originalSeverity;
11220
+ earliest = at;
11221
+ found = true;
11222
+ continue;
11223
+ }
11224
+ if (!at) continue;
11225
+ if (!earliest || at < earliest) {
11226
+ original = o.originalSeverity;
11227
+ earliest = at;
11228
+ }
11229
+ }
11230
+ return {
11231
+ original,
11232
+ current
11233
+ };
11234
+ }
11235
+ function resultStartTime(v, fetchedAt) {
11236
+ return firstTime(v.latestDetectedPipeline?.createdAt, v.detectedAt) ?? fetchedAt;
11237
+ }
11238
+ /**
11239
+ * Renders the node as canonical JSON. Echoing the source layout instead would
11240
+ * make the twins disagree: one language's parser spells 3.0 where the other
11241
+ * spells 3.
11242
+ */
11243
+ function buildVulnCode$1(v) {
11244
+ return JSON.stringify(sortKeysDeep(v), null, 2);
11245
+ }
11246
+ function sortKeysDeep(value) {
11247
+ if (Array.isArray(value)) return value.map(sortKeysDeep);
11248
+ if (value !== null && typeof value === "object") {
11249
+ const out = {};
11250
+ for (const key of Object.keys(value).sort()) out[key] = sortKeysDeep(value[key]);
11251
+ return out;
11252
+ }
11253
+ return value;
11254
+ }
11255
+ function buildDescriptions(v) {
11256
+ const descriptions = [{
11257
+ label: "default",
11258
+ data: v.description || v.title || ""
11259
+ }];
11260
+ if (v.solution) descriptions.push({
11261
+ label: "fix",
11262
+ data: v.solution
11263
+ });
11264
+ return descriptions;
11265
+ }
11266
+ function buildTags$3(v, project, original, current) {
11267
+ const nist = buildNistTags(v.identifiers ?? []);
11268
+ const tags = buildNistCciTags(nist, nistToCci(nist), collectIdentifierExtras(v.identifiers ?? []));
11269
+ markUnratedSeverity(tags, original);
11270
+ const transitions = v.stateTransitions?.nodes ?? [];
11271
+ tags["gitlab/id"] = v.id ?? "";
11272
+ tags["gitlab/uuid"] = v.uuid ?? "";
11273
+ tags["gitlab/webUrl"] = v.webUrl ?? "";
11274
+ tags["gitlab/project"] = project.fullPath ?? "";
11275
+ tags["gitlab/reportType"] = v.reportType ?? "";
11276
+ tags["gitlab/severity"] = current;
11277
+ tags["gitlab/originalSeverity"] = original;
11278
+ tags["gitlab/state"] = v.state ?? "";
11279
+ tags["gitlab/dismissalReason"] = v.dismissalReason ?? null;
11280
+ tags["gitlab/stateComment"] = v.stateComment ?? null;
11281
+ tags["gitlab/falsePositive"] = v.falsePositive ?? false;
11282
+ tags["gitlab/presentOnDefaultBranch"] = v.presentOnDefaultBranch ?? false;
11283
+ tags["gitlab/resolvedOnDefaultBranch"] = v.resolvedOnDefaultBranch ?? false;
11284
+ tags["gitlab/detectedAt"] = v.detectedAt ?? "";
11285
+ tags["gitlab/updatedAt"] = v.updatedAt ?? "";
11286
+ tags["gitlab/confirmedAt"] = v.confirmedAt ?? null;
11287
+ tags["gitlab/confirmedBy"] = userTag(v.confirmedBy);
11288
+ tags["gitlab/dismissedAt"] = v.dismissedAt ?? null;
11289
+ tags["gitlab/dismissedBy"] = userTag(v.dismissedBy);
11290
+ tags["gitlab/resolvedAt"] = v.resolvedAt ?? null;
11291
+ tags["gitlab/resolvedBy"] = userTag(v.resolvedBy);
11292
+ tags["gitlab/initialDetectedPipeline"] = pipelineTag(v.initialDetectedPipeline);
11293
+ tags["gitlab/latestDetectedPipeline"] = pipelineTag(v.latestDetectedPipeline);
11294
+ tags["gitlab/scanner"] = v.scanner ? {
11295
+ name: v.scanner.name ?? "",
11296
+ vendor: v.scanner.vendor ?? "",
11297
+ externalId: v.scanner.externalId ?? ""
11298
+ } : null;
11299
+ tags["gitlab/primaryIdentifier"] = v.primaryIdentifier ? {
11300
+ externalType: v.primaryIdentifier.externalType ?? "",
11301
+ externalId: v.primaryIdentifier.externalId ?? "",
11302
+ name: v.primaryIdentifier.name ?? ""
11303
+ } : null;
11304
+ tags["gitlab/stateTransitionCount"] = transitions.length;
11305
+ if (transitions.length > 0 && transitions[transitions.length - 1].toState !== v.state) tags["gitlab/stateHistoryInconsistent"] = true;
11306
+ return tags;
11307
+ }
11308
+ function userTag(u) {
11309
+ return u ? u.username ?? "" : null;
11310
+ }
11311
+ function pipelineTag(p) {
11312
+ if (!p) return null;
11313
+ return {
11314
+ iid: p.iid ?? "",
11315
+ sha: p.sha ?? "",
11316
+ ref: p.ref ?? "",
11317
+ createdAt: p.createdAt ?? ""
11318
+ };
11319
+ }
11320
+ function buildNistTags(identifiers) {
11321
+ return mapCWEToNIST(identifiers.filter((id) => (id.externalType ?? "").toLowerCase() === "cwe" && !!id.externalId).map((id) => id.externalId), DEFAULT_STATIC_ANALYSIS_NIST_TAGS);
11322
+ }
11323
+ /**
11324
+ * Maps every vendor assessment GitLab attached to the finding, in the order
11325
+ * GitLab returned them. GitLab's own severity band is preferred over one
11326
+ * derived from the score.
11327
+ */
11328
+ function buildCvss$2(v) {
11329
+ const entries = v.cvss ?? [];
11330
+ if (entries.length === 0) return [];
11331
+ const source = cveIdentifier$1(v.identifiers ?? []);
11332
+ return entries.map((e) => {
11333
+ const entry = buildCvss$4({
11334
+ version: cvssVersion$1(e),
11335
+ baseScore: e.baseScore,
11336
+ baseVector: e.vector,
11337
+ source
11338
+ });
11339
+ const sev = cvssSeverity$1(e.severity);
11340
+ if (sev) entry.baseSeverity = sev;
11341
+ if (typeof e.overallScore === "number" && Number.isFinite(e.overallScore)) {
11342
+ entry.computedScore = e.overallScore;
11343
+ entry.computedSeverity = cvssSeverityFromScore(e.overallScore);
11344
+ }
11345
+ return entry;
11346
+ });
11347
+ }
11348
+ /**
11349
+ * Prefers the vector's own prefix and falls back to GitLab's numeric version
11350
+ * field, which is a Float and so cannot name 3.1 exactly without the ladder.
11351
+ */
11352
+ function cvssVersion$1(e) {
11353
+ const n = e.version ?? 0;
11354
+ let fallback = Version.The31;
11355
+ if (n >= 4) fallback = Version.The40;
11356
+ else if (n >= 3.1) fallback = Version.The31;
11357
+ else if (n >= 3) fallback = Version.The30;
11358
+ else if (n >= 2) fallback = Version.The20;
11359
+ return cvssVersionFromVector(e.vector, fallback);
11360
+ }
11361
+ /**
11362
+ * Maps GitLab's CvssSeverity enum; an unknown value yields undefined so the
11363
+ * score-derived band stands instead of an invented one.
11364
+ */
11365
+ function cvssSeverity$1(s) {
11366
+ switch ((s ?? "").toUpperCase()) {
11367
+ case "CRITICAL": return CVSSSeverity.Critical;
11368
+ case "HIGH": return CVSSSeverity.High;
11369
+ case "MEDIUM": return CVSSSeverity.Medium;
11370
+ case "LOW": return CVSSSeverity.Low;
11371
+ case "NONE": return CVSSSeverity.None;
11372
+ default: return;
11373
+ }
11374
+ }
11375
+ /**
11376
+ * What the schema's cvss[].source names: the advisory the scores belong to,
11377
+ * not the scanner that reported them.
11378
+ */
11379
+ function cveIdentifier$1(identifiers) {
11380
+ for (const id of identifiers) if ((id.externalType ?? "").toLowerCase() === "cve" && id.externalId) return id.externalId;
11381
+ return "";
11382
+ }
11383
+ function collectIdentifierExtras(identifiers) {
11384
+ const grouped = {};
11385
+ for (const id of identifiers) {
11386
+ if (!id.externalType || !id.externalId) continue;
11387
+ const key = id.externalType.toLowerCase();
11388
+ (grouped[key] ??= []).push(id.externalId);
11389
+ }
11390
+ return grouped;
11391
+ }
11392
+ function buildRefs$6(v) {
11393
+ const refs = [];
11394
+ const seen = /* @__PURE__ */ new Set();
11395
+ const add = (url) => {
11396
+ if (!url || seen.has(url)) return;
11397
+ seen.add(url);
11398
+ refs.push({ url });
11399
+ };
11400
+ add(v.webUrl);
11401
+ for (const id of v.identifiers ?? []) add(id.url);
11402
+ for (const link of v.links ?? []) add(link.url);
11403
+ return refs;
11404
+ }
11405
+ function parseLine(s) {
11406
+ if (!s) return void 0;
11407
+ const n = parseInt(s, 10);
11408
+ return Number.isNaN(n) || String(n) !== s ? void 0 : n;
11409
+ }
11410
+ function buildSourceLocation$2(loc) {
11411
+ if (!loc || !loc.file) return void 0;
11412
+ const sl = { ref: loc.file };
11413
+ const line = parseLine(loc.startLine) ?? parseLine(loc.endLine);
11414
+ if (line !== void 0) sl.line = line;
11415
+ return sl;
11416
+ }
11417
+ function packageLabel(d) {
11418
+ return packageInstanceId$2("", d?.package?.name ?? "", d?.version ?? "");
11419
+ }
11420
+ function buildCodeDesc$5(v) {
11421
+ const loc = v.location;
11422
+ if (!loc) return `Report type: ${v.reportType ?? ""}`;
11423
+ const parts = [];
11424
+ switch (loc.__typename) {
11425
+ case "VulnerabilityLocationGeneric":
11426
+ if (loc.description) parts.push(`Location: ${loc.description}`);
11427
+ break;
11428
+ case "VulnerabilityLocationSast":
11429
+ case "VulnerabilityLocationSecretDetection":
11430
+ case "VulnerabilityLocationCoverageFuzzing":
11431
+ if (loc.file) parts.push(`File: ${loc.file}`);
11432
+ if (loc.startLine) {
11433
+ if (loc.endLine && loc.endLine !== loc.startLine) parts.push(`Line: ${loc.startLine}-${loc.endLine}`);
11434
+ else parts.push(`Line: ${loc.startLine}`);
11435
+ }
11436
+ if (loc.vulnerableClass) parts.push(`Class: ${loc.vulnerableClass}`);
11437
+ if (loc.vulnerableMethod) parts.push(`Method: ${loc.vulnerableMethod}`);
11438
+ break;
11439
+ case "VulnerabilityLocationDependencyScanning": {
11440
+ if (loc.file) parts.push(`File: ${loc.file}`);
11441
+ const pkg = packageLabel(loc.dependency);
11442
+ if (pkg) parts.push(`Package: ${pkg}`);
11443
+ break;
11444
+ }
11445
+ case "VulnerabilityLocationContainerScanning":
11446
+ case "VulnerabilityLocationClusterImageScanning": {
11447
+ if (loc.image) parts.push(`Image: ${loc.image}`);
11448
+ if (loc.operatingSystem) parts.push(`OS: ${loc.operatingSystem}`);
11449
+ const pkg = packageLabel(loc.dependency);
11450
+ if (pkg) parts.push(`Package: ${pkg}`);
11451
+ break;
11452
+ }
11453
+ case "VulnerabilityLocationDast":
11454
+ if (loc.hostname) parts.push(`URL: ${loc.hostname}${loc.path ?? ""}`);
11455
+ if (loc.requestMethod) parts.push(`Method: ${loc.requestMethod}`);
11456
+ if (loc.param) parts.push(`Param: ${loc.param}`);
11457
+ break;
11458
+ default: return `Location: ${JSON.stringify(loc)}`;
11459
+ }
11460
+ return parts.length > 0 ? parts.join(" | ") : `Report type: ${v.reportType ?? ""}`;
11461
+ }
11462
+ function decisionFor(toState, dismissalReason, scannerResolved) {
11463
+ switch (toState) {
11464
+ case "DISMISSED": return dismissalDecision(dismissalReason ?? "");
11465
+ case "RESOLVED":
11466
+ if (scannerResolved) return void 0;
11467
+ return {
11468
+ overrideType: OverrideType.Attestation,
11469
+ status: ResultStatus.Passed,
11470
+ defaultReason: "Marked resolved in GitLab"
11471
+ };
11472
+ default: return;
11473
+ }
11474
+ }
11475
+ function dismissalDecision(reason) {
11476
+ switch (reason) {
11477
+ case "FALSE_POSITIVE": return {
11478
+ overrideType: OverrideType.FalsePositive,
11479
+ status: ResultStatus.NotApplicable,
11480
+ defaultReason: "Dismissed as FALSE_POSITIVE in GitLab"
11481
+ };
11482
+ case "ACCEPTABLE_RISK": return {
11483
+ overrideType: OverrideType.Waiver,
11484
+ status: ResultStatus.Passed,
11485
+ defaultReason: "Dismissed as ACCEPTABLE_RISK in GitLab"
11486
+ };
11487
+ case "MITIGATING_CONTROL": return {
11488
+ overrideType: OverrideType.Waiver,
11489
+ status: ResultStatus.Passed,
11490
+ justification: Justification.InlineMitigationsAlreadyExist,
11491
+ defaultReason: "Dismissed as MITIGATING_CONTROL in GitLab"
11492
+ };
11493
+ case "USED_IN_TESTS": return {
11494
+ overrideType: OverrideType.Waiver,
11495
+ status: ResultStatus.NotApplicable,
11496
+ justification: Justification.VulnerableCodeNotInExecutePath,
11497
+ defaultReason: "Dismissed as USED_IN_TESTS in GitLab"
11498
+ };
11499
+ case "NOT_APPLICABLE":
11500
+ case "": return {
11501
+ overrideType: OverrideType.Waiver,
11502
+ status: ResultStatus.NotApplicable,
11503
+ defaultReason: "Dismissed as NOT_APPLICABLE in GitLab"
11504
+ };
11505
+ default: return {
11506
+ overrideType: OverrideType.Waiver,
11507
+ status: ResultStatus.NotApplicable,
11508
+ defaultReason: `Dismissed as ${reason} in GitLab`
11509
+ };
11510
+ }
11511
+ }
11512
+ function buildStatusOverrides(v, fetchedAt) {
11513
+ const scannerResolved = v.state === "RESOLVED" && !!v.resolvedOnDefaultBranch;
11514
+ const overrides = [];
11515
+ const transitions = v.stateTransitions?.nodes ?? [];
11516
+ for (const tr of transitions) {
11517
+ if (tr.toState === "DETECTED") {
11518
+ const at = firstTime(tr.createdAt);
11519
+ if (overrides.length > 0 && at) overrides[overrides.length - 1].expiresAt = at;
11520
+ continue;
11521
+ }
11522
+ const d = decisionFor(tr.toState, tr.dismissalReason, scannerResolved);
11523
+ if (!d) continue;
11524
+ const appliedAt = firstTime(tr.createdAt, v.updatedAt, v.detectedAt) ?? fetchedAt;
11525
+ overrides.push(newOverride(v, d, appliedAt, tr.comment, tr.author));
11526
+ }
11527
+ if (transitions.length > 0 && transitions[transitions.length - 1].toState === v.state) return overrides;
11528
+ closeOpenOverrides(overrides, firstTime(v.updatedAt) ?? fetchedAt);
11529
+ const d = decisionFor(v.state, v.dismissalReason, scannerResolved);
11530
+ if (d) {
11531
+ const { at, by } = currentDecisionProvenance(v);
11532
+ overrides.push(newOverride(v, d, at ?? fetchedAt, v.stateComment, by));
11533
+ }
11534
+ return overrides;
11535
+ }
11536
+ /**
11537
+ * Brings every expiry back to `at`, never earlier than the override's own
11538
+ * appliedAt so the record stays internally consistent.
11539
+ */
11540
+ function closeOpenOverrides(overrides, at) {
11541
+ for (const o of overrides) {
11542
+ if (o.expiresAt <= at) continue;
11543
+ o.expiresAt = at < o.appliedAt ? o.appliedAt : at;
11544
+ }
11545
+ }
11546
+ function currentDecisionProvenance(v) {
11547
+ switch (v.state) {
11548
+ case "DISMISSED": return {
11549
+ at: firstTime(v.dismissedAt, v.updatedAt, v.detectedAt),
11550
+ by: v.dismissedBy
11551
+ };
11552
+ case "RESOLVED": return {
11553
+ at: firstTime(v.resolvedAt, v.updatedAt, v.detectedAt),
11554
+ by: v.resolvedBy
11555
+ };
11556
+ default: return {
11557
+ at: firstTime(v.updatedAt, v.detectedAt),
11558
+ by: void 0
11559
+ };
11560
+ }
11561
+ }
11562
+ function firstTime(...candidates) {
11563
+ for (const c of candidates) {
11564
+ if (!c) continue;
11565
+ const ts = parseTimestamp(c);
11566
+ if (ts) return ts;
11567
+ }
11568
+ }
11569
+ function newOverride(v, d, appliedAt, comment, author) {
11570
+ const o = {
11571
+ type: d.overrideType,
11572
+ status: d.status,
11573
+ reason: comment && comment.trim() !== "" ? comment : d.defaultReason,
11574
+ appliedBy: identityFor$3(author),
11575
+ appliedAt,
11576
+ expiresAt: defaultOverrideExpiry(appliedAt)
11577
+ };
11578
+ if (d.justification !== void 0) o.justification = d.justification;
11579
+ if (v.webUrl) o.externalReferences = [{
11580
+ sourceName: SOURCE_NAME,
11581
+ href: v.webUrl
11582
+ }];
11583
+ return o;
11584
+ }
11585
+ function identityFor$3(u) {
11586
+ if (u) {
11587
+ const description = u.name ? u.name : void 0;
11588
+ if (u.publicEmail) {
11589
+ const id = {
11590
+ type: IdentityType.Email,
11591
+ identifier: u.publicEmail
11592
+ };
11593
+ if (description) id.description = description;
11594
+ return id;
11595
+ }
11596
+ if (u.username) {
11597
+ const id = {
11598
+ type: IdentityType.Username,
11599
+ identifier: u.username
11600
+ };
11601
+ if (description) id.description = description;
11602
+ return id;
11603
+ }
11604
+ }
11605
+ return {
11606
+ type: IdentityType.System,
11607
+ identifier: "gitlab",
11608
+ description: "GitLab automatic state change"
11609
+ };
11610
+ }
11611
+ function buildSeverityOverrides(v, fetchedAt) {
11612
+ return (v.severityOverrides?.nodes ?? []).map((sc) => {
11613
+ const appliedAt = firstTime(sc.createdAt, v.updatedAt, v.detectedAt) ?? fetchedAt;
11614
+ const o = {
11615
+ type: OverrideType.RiskAdjustment,
11616
+ impact: { value: severityToImpact$1(sc.newSeverity ?? "") },
11617
+ reason: `Severity changed from ${sc.originalSeverity ?? ""} to ${sc.newSeverity ?? ""} in GitLab`,
11618
+ appliedBy: identityFor$3(sc.author),
11619
+ appliedAt,
11620
+ expiresAt: defaultOverrideExpiry(appliedAt)
11621
+ };
11622
+ if (v.webUrl) o.externalReferences = [{
11623
+ sourceName: SOURCE_NAME,
11624
+ href: v.webUrl
11625
+ }];
11626
+ return o;
11627
+ });
11628
+ }
11629
+ function sortMostRecentFirst(overrides) {
11630
+ overrides.reverse();
11631
+ overrides.sort((a, b) => b.appliedAt.getTime() - a.appliedAt.getTime());
11632
+ }
11633
+ function applyEffectivePosture(req, fetchedAt) {
11634
+ const overrides = req.statusOverrides ?? [];
11635
+ const ref = fetchedAt.toISOString();
11636
+ const inputs = requirementStatusInput(req).overrides ?? [];
11637
+ req.effectiveStatus = computeEffectiveStatus(requirementStatusInput(req), ref);
11638
+ const impactIdx = governingOverrideIndex(inputs, (i) => overrides[i]?.impact !== void 0, ref);
11639
+ if (impactIdx >= 0) req.effectiveImpact = overrides[impactIdx].impact.value;
11640
+ const dispositionIdx = governingOverrideIndex(inputs, (i) => overrides[i]?.status !== void 0 || overrides[i]?.impact !== void 0, ref);
11641
+ if (dispositionIdx >= 0) req.disposition = overrides[dispositionIdx].type;
11642
+ }
11643
+ //#endregion
9382
11644
  //#region converters/trivy-to-hdf/typescript/converter.ts
9383
11645
  const BASELINE_NAME = "Trivy Scan";
9384
11646
  const STATIC_CCI = nistToCci(DEFAULT_STATIC_ANALYSIS_NIST_TAGS);
@@ -9613,19 +11875,19 @@ function buildCvssEntries$4(m) {
9613
11875
  for (const src of Object.keys(m).sort()) {
9614
11876
  const c = m[src];
9615
11877
  if (!c) continue;
9616
- if (c.V2Vector || c.V2Score !== void 0) entries.push(buildCvss$3({
11878
+ if (c.V2Vector || c.V2Score !== void 0) entries.push(buildCvss$4({
9617
11879
  version: Version.The20,
9618
11880
  baseVector: c.V2Vector,
9619
11881
  baseScore: c.V2Score,
9620
11882
  source: src
9621
11883
  }));
9622
- if (c.V3Vector || c.V3Score !== void 0) entries.push(buildCvss$3({
11884
+ if (c.V3Vector || c.V3Score !== void 0) entries.push(buildCvss$4({
9623
11885
  version: cvssVersionFromVector(c.V3Vector, Version.The31),
9624
11886
  baseVector: c.V3Vector,
9625
11887
  baseScore: c.V3Score,
9626
11888
  source: src
9627
11889
  }));
9628
- if (c.V40Vector || c.V40Score !== void 0) entries.push(buildCvss$3({
11890
+ if (c.V40Vector || c.V40Score !== void 0) entries.push(buildCvss$4({
9629
11891
  version: Version.The40,
9630
11892
  baseVector: c.V40Vector,
9631
11893
  baseScore: c.V40Score,
@@ -10482,7 +12744,7 @@ function buildCvss$1(vuln) {
10482
12744
  const hasScore = typeof vuln.cvss === "number" && Number.isFinite(vuln.cvss);
10483
12745
  const hasVector = !!vuln.vector;
10484
12746
  if (!hasScore && !hasVector) return void 0;
10485
- return buildCvss$3({
12747
+ return buildCvss$4({
10486
12748
  version: cvssVersionFromVector(vuln.vector),
10487
12749
  baseScore: hasScore ? vuln.cvss : void 0,
10488
12750
  baseVector: vuln.vector,
@@ -10568,6 +12830,17 @@ function buildAffectedPackage(vuln, packageTypes, distro) {
10568
12830
  });
10569
12831
  }
10570
12832
  /**
12833
+ * The per-result instance identity for a Twistlock vulnerability: the vulnerable
12834
+ * package, which is what distinguishes the several entries that report the same
12835
+ * CVE. Twistlock's output carries no purl, so the identity is name@version —
12836
+ * grype's fallback form. Falls back to the name alone when a version is absent,
12837
+ * and returns undefined when there is no package name at all, in which case the
12838
+ * caller leaves the fields unset rather than inventing an id.
12839
+ */
12840
+ function packageInstanceId(vuln) {
12841
+ return packageInstanceId$2("", vuln.packageName ?? "", vuln.packageVersion ?? "") || void 0;
12842
+ }
12843
+ /**
10571
12844
  * Indexes packageName → packageType from the result-level packages array.
10572
12845
  */
10573
12846
  function buildPackageTypeIndex(pkgs) {
@@ -10646,9 +12919,14 @@ function buildRequirement$9(vuln, packageTypes, distro) {
10646
12919
  data: vuln.description
10647
12920
  }];
10648
12921
  const startTime = (vuln.discoveredDate ? parseTimestamp(vuln.discoveredDate) : null) ?? /* @__PURE__ */ new Date("0001-01-01T00:00:00Z");
12922
+ const instanceId = packageInstanceId(vuln);
10649
12923
  const results = [createResult(ResultStatus.Failed, formatMessage$2(vuln), {
10650
12924
  codeDesc: formatCodeDesc$2(vuln),
10651
- startTime
12925
+ startTime,
12926
+ ...instanceId ? {
12927
+ resource: "package",
12928
+ resourceId: instanceId
12929
+ } : {}
10652
12930
  })];
10653
12931
  const req = createRequirement(vuln.id, vuln.id, descriptions, twistlockSeverityToImpact(vuln.severity), results, { tags });
10654
12932
  const controlType = deriveControlTypeFromTags(nist);
@@ -10797,12 +13075,12 @@ function resolveCVE(vuln) {
10797
13075
  function buildCvssEntries$3(vuln) {
10798
13076
  const source = resolveCVE(vuln);
10799
13077
  const entries = [];
10800
- if (typeof vuln.cvssV3BaseScore === "number") entries.push(buildCvss$3({
13078
+ if (typeof vuln.cvssV3BaseScore === "number") entries.push(buildCvss$4({
10801
13079
  version: Version.The31,
10802
13080
  baseScore: vuln.cvssV3BaseScore,
10803
13081
  source
10804
13082
  }));
10805
- if (typeof vuln.cvssV2BaseScore === "number") entries.push(buildCvss$3({
13083
+ if (typeof vuln.cvssV2BaseScore === "number") entries.push(buildCvss$4({
10806
13084
  version: Version.The20,
10807
13085
  baseScore: vuln.cvssV2BaseScore,
10808
13086
  source
@@ -11020,14 +13298,14 @@ function buildCvssEntries$2(cves) {
11020
13298
  const out = [];
11021
13299
  for (const cve of cves ?? []) {
11022
13300
  const v3 = parseCvssField(cve.cvss_v3);
11023
- if (v3.score !== void 0 || v3.vector !== "") out.push(buildCvss$3({
13301
+ if (v3.score !== void 0 || v3.vector !== "") out.push(buildCvss$4({
11024
13302
  version: cvssVersionFromVector(v3.vector),
11025
13303
  baseScore: v3.score,
11026
13304
  baseVector: v3.vector,
11027
13305
  source: cve.cve
11028
13306
  }));
11029
13307
  const v2 = parseCvssField(cve.cvss_v2);
11030
- if (v2.score !== void 0 || v2.vector !== "") out.push(buildCvss$3({
13308
+ if (v2.score !== void 0 || v2.vector !== "") out.push(buildCvss$4({
11031
13309
  version: cvssVersionFromString("2.0"),
11032
13310
  baseScore: v2.score,
11033
13311
  baseVector: v2.vector,
@@ -11285,13 +13563,13 @@ function extractCVEs(vuln) {
11285
13563
  * its score still drives impact.
11286
13564
  */
11287
13565
  function buildCvssEntries$1(vuln) {
11288
- if (vuln.vectors_v3) return [buildCvss$3({
13566
+ if (vuln.vectors_v3) return [buildCvss$4({
11289
13567
  version: cvssVersionFromVector(vuln.vectors_v3),
11290
13568
  baseScore: vuln.score_v3 > 0 ? vuln.score_v3 : void 0,
11291
13569
  baseVector: vuln.vectors_v3,
11292
13570
  source: "NeuVector"
11293
13571
  })];
11294
- if (vuln.vectors) return [buildCvss$3({
13572
+ if (vuln.vectors) return [buildCvss$4({
11295
13573
  version: cvssVersionFromString("2.0"),
11296
13574
  baseScore: vuln.score > 0 ? vuln.score : void 0,
11297
13575
  baseVector: vuln.vectors,
@@ -11316,6 +13594,30 @@ function vulnID(vuln) {
11316
13594
  return `${vuln.name}/${vuln.package_name}/${vuln.package_version}`;
11317
13595
  }
11318
13596
  /**
13597
+ * The per-result instance identity for a NeuVector finding, as a (resource kind,
13598
+ * id) pair. file_name is the discriminator — the only field separating the two
13599
+ * braces@3.0.2 entries that both report CVE-2024-4068 — but NeuVector populates
13600
+ * it on a minority of entries. An entry without one names no file, so it
13601
+ * identifies the vulnerable package instead (the identity
13602
+ * name/package_name/package_version already keys the requirement on) rather than
13603
+ * claiming a 'file' resource with nothing to point at. With neither, the id is ''
13604
+ * and the caller leaves the fields unset.
13605
+ */
13606
+ function vulnInstance(vuln) {
13607
+ if (vuln.file_name) return {
13608
+ resource: "file",
13609
+ id: vuln.file_name
13610
+ };
13611
+ const id = packageInstanceId$2("", vuln.package_name ?? "", vuln.package_version ?? "");
13612
+ return id ? {
13613
+ resource: "package",
13614
+ id
13615
+ } : {
13616
+ resource: "",
13617
+ id: ""
13618
+ };
13619
+ }
13620
+ /**
11319
13621
  * Generates a human-readable title for the vulnerability.
11320
13622
  */
11321
13623
  function vulnTitle(vuln) {
@@ -11394,9 +13696,14 @@ function buildRequirement$6(vuln, scanTime, ml) {
11394
13696
  label: "default",
11395
13697
  data: vuln.description
11396
13698
  }];
13699
+ const instance = vulnInstance(vuln);
11397
13700
  const results = [createResult(ResultStatus.Failed, vulnMessage(vuln), {
11398
13701
  codeDesc: buildCodeDesc$3(vuln),
11399
- startTime: scanTime
13702
+ startTime: scanTime,
13703
+ ...instance.id ? {
13704
+ resource: instance.resource,
13705
+ resourceId: instance.id
13706
+ } : {}
11400
13707
  })];
11401
13708
  const req = createRequirement(vulnID(vuln), vulnTitle(vuln), descriptions, getImpact$4(vuln), results, { tags });
11402
13709
  req.code = JSON.stringify(vuln, null, 2);
@@ -11939,7 +14246,7 @@ function buildRefs$1(rec) {
11939
14246
  function buildCvssEntries(rec) {
11940
14247
  const score = parseCvssScore(rec.CVSS);
11941
14248
  if (score === void 0) return void 0;
11942
- return [buildCvss$3({
14249
+ return [buildCvss$4({
11943
14250
  version: cvssVersionFromString(void 0),
11944
14251
  baseScore: score,
11945
14252
  source: rec["CVE ID"] || void 0
@@ -12132,7 +14439,7 @@ function buildNetsparkerCvss(classification) {
12132
14439
  const baseScore = baseScoreFromBlock(block);
12133
14440
  const vector = block?.vector ?? "";
12134
14441
  if (!vector && baseScore === void 0) continue;
12135
- out.push(buildCvss$3({
14442
+ out.push(buildCvss$4({
12136
14443
  version: cvssVersionFromVector(vector, elementVersion),
12137
14444
  baseScore,
12138
14445
  baseVector: vector
@@ -12882,14 +15189,33 @@ function formatFlawMessage(flaw) {
12882
15189
  return notes.join("\n");
12883
15190
  }
12884
15191
  /**
15192
+ * A static flaw's source position — sourcefilepath+sourcefile, with :line
15193
+ * appended when both are present. It is both the per-result instance identity
15194
+ * (resourceId) and the position half of the synthesized code snippet, so the two
15195
+ * cannot drift apart. Returns '' for the NOT-IN-SOURCE case.
15196
+ */
15197
+ function flawLocus(flaw) {
15198
+ const locus = attr(flaw, "sourcefilepath") + attr(flaw, "sourcefile");
15199
+ const line = attr(flaw, "line");
15200
+ return locus && line ? `${locus}:${line}` : locus;
15201
+ }
15202
+ /**
15203
+ * The per-result instance identity for an SCA finding: the component_id Veracode
15204
+ * assigns the third-party component. Falls back to the component's sha1 —
15205
+ * component_id is optional in the report and sha1 is the documented secondary
15206
+ * identity — and returns '' when neither is present, in which case the caller
15207
+ * leaves the fields unset rather than inventing an id.
15208
+ */
15209
+ function scaInstanceId(comp) {
15210
+ return attr(comp, "component_id") || attr(comp, "sha1");
15211
+ }
15212
+ /**
12885
15213
  * Synthesize a static flaw's source-context locus from its function prototype
12886
15214
  * and source-file position. Returns '' when the flaw carries neither a prototype
12887
15215
  * nor a source location (the NOT-IN-SOURCE case).
12888
15216
  */
12889
15217
  function synthesizeFlawCode(flaw) {
12890
- let locus = attr(flaw, "sourcefilepath") + attr(flaw, "sourcefile");
12891
- const line = attr(flaw, "line");
12892
- if (locus && line) locus += `:${line}`;
15218
+ const locus = flawLocus(flaw);
12893
15219
  const proto = attr(flaw, "functionprototype");
12894
15220
  if (proto && locus) return `${proto} at ${locus}`;
12895
15221
  if (proto) return proto;
@@ -12997,10 +15323,17 @@ function buildCWERequirement(cat, sevLevel, impact, firstBuildDate) {
12997
15323
  const startTime = parseVeracodeTimestamp(firstBuildDate) ?? /* @__PURE__ */ new Date();
12998
15324
  const results = cwes.flatMap((c) => {
12999
15325
  const staticflaws = c.staticflaws;
13000
- return ensureArray(staticflaws?.flaw).map((flaw) => createResult(ResultStatus.Failed, formatFlawMessage(flaw) || void 0, {
13001
- codeDesc: formatFlawCodeDesc(flaw),
13002
- startTime
13003
- }));
15326
+ return ensureArray(staticflaws?.flaw).map((flaw) => {
15327
+ const locus = flawLocus(flaw);
15328
+ return createResult(ResultStatus.Failed, formatFlawMessage(flaw) || void 0, {
15329
+ codeDesc: formatFlawCodeDesc(flaw),
15330
+ startTime,
15331
+ ...locus ? {
15332
+ resource: "file",
15333
+ resourceId: locus
15334
+ } : {}
15335
+ });
15336
+ });
13004
15337
  });
13005
15338
  const sourceRef = cwes.flatMap((c) => {
13006
15339
  const staticflaws = c.staticflaws;
@@ -13076,7 +15409,7 @@ function buildVeracodeCvss(vuln, components) {
13076
15409
  if (!scoreStr) return [];
13077
15410
  const score = Number(scoreStr);
13078
15411
  if (!Number.isFinite(score)) return [];
13079
- return [buildCvss$3({
15412
+ return [buildCvss$4({
13080
15413
  version: cvssVersionFromString(void 0),
13081
15414
  baseScore: score
13082
15415
  })];
@@ -13092,10 +15425,17 @@ function buildCVERequirement(vuln, components, firstBuildDate) {
13092
15425
  const tags = buildNistCciTags(nist, cciTags, {});
13093
15426
  markUnratedSeverity(tags, attr(vuln, "severity"));
13094
15427
  const startTime = parseVeracodeTimestamp(firstBuildDate) ?? /* @__PURE__ */ new Date();
13095
- const results = components.map((comp) => createResult(ResultStatus.Failed, void 0, {
13096
- codeDesc: formatSCACodeDesc(comp),
13097
- startTime
13098
- }));
15428
+ const results = components.map((comp) => {
15429
+ const instanceId = scaInstanceId(comp);
15430
+ return createResult(ResultStatus.Failed, void 0, {
15431
+ codeDesc: formatSCACodeDesc(comp),
15432
+ startTime,
15433
+ ...instanceId ? {
15434
+ resource: "component",
15435
+ resourceId: instanceId
15436
+ } : {}
15437
+ });
15438
+ });
13099
15439
  const cveSummary = attr(vuln, "cve_summary");
13100
15440
  const cveId = attr(vuln, "cve_id");
13101
15441
  const descriptions = [{
@@ -16434,11 +18774,11 @@ function oscalStateFromStatus(status) {
16434
18774
  */
16435
18775
  function overrideRemarks(req) {
16436
18776
  const parts = [];
16437
- if (req.disposition) parts.push("Disposition: " + String(req.disposition));
16438
- const overrides = req.statusOverrides ?? [];
16439
- if (overrides.length > 0) {
16440
- const o = overrides[0];
16441
- parts.push("Override: " + String(o.type));
18777
+ const disposition = requirementDisposition(req);
18778
+ if (disposition) parts.push("Disposition: " + disposition);
18779
+ const o = governingOverride(req);
18780
+ if (o) {
18781
+ parts.push("Override: " + String(o.type ?? ""));
16442
18782
  if (o.reason) parts.push("Reason: " + o.reason);
16443
18783
  if (o.appliedBy?.identifier) parts.push("Applied by: " + o.appliedBy.identifier);
16444
18784
  const appliedAt = hdfTime(o.appliedAt);
@@ -16533,13 +18873,14 @@ function buildRemediations(req) {
16533
18873
  description: fix.data,
16534
18874
  props: [descriptionLabelProp("fix")]
16535
18875
  });
16536
- const overrides = req.statusOverrides ?? [];
16537
- if (req.disposition && overrides.length > 0) {
16538
- const desc = overrides[0].reason || "Risk accepted via " + String(req.disposition);
18876
+ const o = governingOverride(req);
18877
+ if (o) {
18878
+ const disp = String(o.type ?? "");
18879
+ const desc = o.reason || "Risk accepted via " + disp;
16539
18880
  rems.push({
16540
18881
  uuid: crypto.randomUUID(),
16541
18882
  lifecycle: "accepted",
16542
- title: String(req.disposition),
18883
+ title: disp,
16543
18884
  description: desc
16544
18885
  });
16545
18886
  }
@@ -16550,9 +18891,9 @@ function buildRemediations(req) {
16550
18891
  * (the field the OSCAL POA&M importer reads back). Returns '' when none applies.
16551
18892
  */
16552
18893
  function riskDeadline(req) {
16553
- const overrides = req.statusOverrides ?? [];
16554
- if (overrides.length > 0) {
16555
- const expiresAt = hdfTime(overrides[0].expiresAt);
18894
+ const o = governingOverride(req);
18895
+ if (o) {
18896
+ const expiresAt = hdfTime(o.expiresAt);
16556
18897
  if (expiresAt) return formatTimestampSeconds(expiresAt);
16557
18898
  }
16558
18899
  return "";
@@ -20912,7 +23253,7 @@ function toRfc3339(d) {
20912
23253
  * each STIX object as an inert externalReferences[] entry: a CVE-bearing object
20913
23254
  * attaches to the finding whose requirementId is that CVE (fanning out to every
20914
23255
  * match), and everything else — non-CVE objects and CVEs with no matching
20915
- * finding — attaches to the results root. Each entry carries the raw STIX object
23256
+ * finding — attaches to the document root. Each entry carries the raw STIX object
20916
23257
  * losslessly in `document`.
20917
23258
  *
20918
23259
  * Informational only: authors no overrides and fabricates no status/impact (the
@@ -20944,7 +23285,7 @@ function enrichStix(resultsInput, bundleInput, opts) {
20944
23285
  if (!matched) appendExternalReference(doc, buildStixRef(obj, "reference"));
20945
23286
  }
20946
23287
  for (const reqs of reqById.values()) for (const req of reqs) capStixExternalRefs(req, "finding");
20947
- capStixExternalRefs(doc, "results root");
23288
+ capStixExternalRefs(doc, "document root");
20948
23289
  if (opts?.recomputeCvss) recomputeExploitation(bundle, reqById, opts);
20949
23290
  return JSON.stringify(doc, null, 2);
20950
23291
  }
@@ -21086,6 +23427,6 @@ function capStixExternalRefs(container, label) {
21086
23427
  container.externalReferences = [...other, ...capped];
21087
23428
  }
21088
23429
  //#endregion
21089
- 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 };
23430
+ export { convertAsffToHdf, convertAwsConfigToHdf, convertBurpsuiteToHdf, convertCheckovToHdf, convertCklToHdf, convertCklbToHdf, convertConveyorToHdf, convertCsafVexToHdf, convertCyclonedxToHdf, convertCyclonedxVexToHdf, convertDbprotectToHdf, convertDefectDojoToHdf, convertDeptrackToHdf, convertFortifyToHdf, convertGitlabToHdf, convertGitlabVulnerabilitiesToHdf, convertGosecToHdf, convertGrypeToHdf, convertHadolintToHdf, convertHdfDocumentsToHtml, convertHdfToAsff, convertHdfToCkl, convertHdfToCklb, convertHdfToCsafVex, convertHdfToCsv, convertHdfToCyclonedxVex, convertHdfToEcs, convertHdfToHtml, 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, parseReportType, parseStixBundle };
21090
23431
 
21091
23432
  //# sourceMappingURL=index.js.map