@mitre/hdf-converters 3.3.0 → 3.3.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/detect.d.ts.map +1 -1
- package/dist/detect.js +1 -1
- package/dist/index.d.ts +2 -12
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +456 -265
- package/dist/index.js.map +1 -1
- package/dist/{register-all-C3lYICDC.js → register-all-DxZN6IJ4.js} +22 -2
- package/dist/register-all-DxZN6IJ4.js.map +1 -0
- package/package.json +7 -7
- package/dist/register-all-C3lYICDC.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { n as detectConverter, t as registerAllFingerprints } from "./register-all-
|
|
1
|
+
import { n as detectConverter, t as registerAllFingerprints } from "./register-all-DxZN6IJ4.js";
|
|
2
2
|
import { flattenOverlays } from "@mitre/hdf-parsers";
|
|
3
|
-
import { buildCsv, buildXml, cvssScoreToSeverity, parseCsv, parseJSON, parsePurl, parseTimestamp, parseXml, parseXmlWithArrays, sha256, stripHtml as stripHTML } from "@mitre/hdf-utilities";
|
|
3
|
+
import { buildCsv, buildXml, cvssScoreToSeverity, formatTimestamp, formatTimestampSeconds, impactToSeverity, parseCsv, parseJSON, parsePurl, parseTimestamp, parseXml, parseXmlWithArrays, sha256, stripHtml as stripHTML, trimUtcFraction } from "@mitre/hdf-utilities";
|
|
4
4
|
import { Applicability, AuthorizationStatus, CVSSSeverity, CategorizationLevel, ControlType, Ecosystem, EvidenceType, HashAlgorithm, IdentityType, Justification, MilestoneStatus, OverrideType, PlanType, ResultStatus, TargetType, VerificationMethodEnum, Version, createDescription, createMinimalBaseline, createRequirement, createResult, severityToImpact } from "@mitre/hdf-schema";
|
|
5
|
-
import { DEFAULT_COMPONENT_MANAGEMENT_NIST_TAGS, DEFAULT_REMEDIATION_NIST_TAGS, DEFAULT_STATIC_ANALYSIS_NIST_TAGS, DEFAULT_STATIC_ANALYSIS_NIST_TAGS as DEFAULT_STATIC_ANALYSIS_NIST_TAGS$1, getAwsConfigNistControlByIdentifier, getAwsConfigNistControlByName, getCCINistMappings, getCweNistControl, getNessusNistControl, getNiktoNistControl, getOwaspNistControl, getScoutsuiteNistControl, nistToCci } from "@mitre/hdf-mappings";
|
|
5
|
+
import { DEFAULT_COMPONENT_MANAGEMENT_NIST_TAGS, DEFAULT_REMEDIATION_NIST_TAGS, DEFAULT_STATIC_ANALYSIS_NIST_TAGS, DEFAULT_STATIC_ANALYSIS_NIST_TAGS as DEFAULT_STATIC_ANALYSIS_NIST_TAGS$1, awsConfigMappedRevisions, getAwsConfigNistControlByIdentifier, getAwsConfigNistControlByName, getCCINistMappings, getCurrentNistRevision, getCweNistControl, getNessusNistControl, getNiktoNistControl, getOwaspNistControl, getScoutsuiteNistControl, isNistStrict, nistToCci } from "@mitre/hdf-mappings";
|
|
6
6
|
//#region shared/typescript/converterutil.ts
|
|
7
7
|
/**
|
|
8
8
|
* Shared utilities for TypeScript HDF converters.
|
|
@@ -249,7 +249,21 @@ function buildHdfResults(opts) {
|
|
|
249
249
|
if (opts.components) hdf.components = opts.components;
|
|
250
250
|
if (opts.timestamp) hdf.timestamp = opts.timestamp;
|
|
251
251
|
if (opts.statistics) hdf.statistics = opts.statistics;
|
|
252
|
-
return
|
|
252
|
+
return serializeHdf(hdf);
|
|
253
|
+
}
|
|
254
|
+
const ISO_UTC_WITH_FRACTION = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+Z$/;
|
|
255
|
+
function hdfTimestampReplacer(_key, value) {
|
|
256
|
+
if (typeof value === "string" && ISO_UTC_WITH_FRACTION.test(value)) return trimUtcFraction(value);
|
|
257
|
+
return value;
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Serialize an HDF document to pretty-printed JSON, normalizing Date-derived
|
|
261
|
+
* timestamps to canonical trimmed-UTC RFC3339 (byte-identical to the Go
|
|
262
|
+
* converters). Use this instead of a bare `JSON.stringify` for any HDF output
|
|
263
|
+
* so the fractional-second trim is applied consistently.
|
|
264
|
+
*/
|
|
265
|
+
function serializeHdf(doc, space = 2) {
|
|
266
|
+
return JSON.stringify(doc, hdfTimestampReplacer, space);
|
|
253
267
|
}
|
|
254
268
|
/**
|
|
255
269
|
* Captures a NIST 800-53 control identifier and its base sub-control number,
|
|
@@ -413,14 +427,13 @@ function buildNoFindingsRequirement(id, codeDesc, startTime) {
|
|
|
413
427
|
* - Results: snake_case → camelCase for all fields
|
|
414
428
|
* - Overlay flattening: merge overlay/wrapper baselines so every requirement has results
|
|
415
429
|
*/
|
|
416
|
-
/** Valid severity values per HDF v2.0 schema. */
|
|
417
|
-
const VALID_SEVERITIES = new Set([
|
|
430
|
+
/** Valid severity values per HDF v2.0 schema (matches the Go converter). */
|
|
431
|
+
const VALID_SEVERITIES = /* @__PURE__ */ new Set([
|
|
418
432
|
"critical",
|
|
419
433
|
"high",
|
|
420
434
|
"medium",
|
|
421
435
|
"low",
|
|
422
|
-
"informational"
|
|
423
|
-
"none"
|
|
436
|
+
"informational"
|
|
424
437
|
]);
|
|
425
438
|
/**
|
|
426
439
|
* Convert tags.severity string to a valid severity value.
|
|
@@ -432,17 +445,6 @@ function tagSeverityToSeverity(tagSeverity) {
|
|
|
432
445
|
return VALID_SEVERITIES.has(normalized) ? normalized : null;
|
|
433
446
|
}
|
|
434
447
|
/**
|
|
435
|
-
* Derive severity from numeric impact score.
|
|
436
|
-
* Follows InSpec conventions: 0.9+ critical, 0.7+ high, 0.5+ medium, >0 low, 0 none.
|
|
437
|
-
*/
|
|
438
|
-
function impactToSeverity$2(impact) {
|
|
439
|
-
if (impact >= .9) return "critical";
|
|
440
|
-
if (impact >= .7) return "high";
|
|
441
|
-
if (impact >= .5) return "medium";
|
|
442
|
-
if (impact > 0) return "low";
|
|
443
|
-
return "none";
|
|
444
|
-
}
|
|
445
|
-
/**
|
|
446
448
|
* Normalize status values from v1.0 to v2.0 format.
|
|
447
449
|
* Converts snake_case to camelCase.
|
|
448
450
|
*/
|
|
@@ -491,35 +493,79 @@ function computeEffectiveStatus(impact, results) {
|
|
|
491
493
|
* Convert v1.0 result to v2.0 result.
|
|
492
494
|
* Transforms snake_case field names to camelCase.
|
|
493
495
|
*/
|
|
496
|
+
const LEGACY_ZERO_TIME = "0001-01-01T00:00:00Z";
|
|
497
|
+
function legacyStartTime(s) {
|
|
498
|
+
if (s === void 0) return LEGACY_ZERO_TIME;
|
|
499
|
+
const d = parseTimestamp(s);
|
|
500
|
+
return d ? formatTimestamp(d) : LEGACY_ZERO_TIME;
|
|
501
|
+
}
|
|
494
502
|
function convertResult(v1Result) {
|
|
495
503
|
const v2Result = { status: normalizeStatus$1(v1Result.status) };
|
|
496
|
-
|
|
504
|
+
v2Result.codeDesc = v1Result.code_desc ?? "";
|
|
497
505
|
if (v1Result.run_time !== void 0) v2Result.runTime = v1Result.run_time;
|
|
498
|
-
|
|
506
|
+
v2Result.startTime = legacyStartTime(v1Result.start_time);
|
|
499
507
|
if (v1Result.message !== void 0) v2Result.message = v1Result.message;
|
|
508
|
+
else if (v1Result.skip_message !== void 0) v2Result.message = v1Result.skip_message;
|
|
500
509
|
if (v1Result.exception !== void 0) v2Result.exception = v1Result.exception;
|
|
501
510
|
if (v1Result.backtrace !== void 0) v2Result.backtrace = v1Result.backtrace;
|
|
502
|
-
if (v1Result.resource_class !== void 0) v2Result.
|
|
503
|
-
if (v1Result.resource_params !== void 0) v2Result.resourceParams = v1Result.resource_params;
|
|
511
|
+
if (v1Result.resource_class !== void 0) v2Result.resource = v1Result.resource_class;
|
|
504
512
|
if (v1Result.resource_id !== void 0) v2Result.resourceId = v1Result.resource_id;
|
|
505
|
-
if (v1Result.skip_message !== void 0) v2Result.skipMessage = v1Result.skip_message;
|
|
506
|
-
const knownFields = new Set([
|
|
507
|
-
"status",
|
|
508
|
-
"code_desc",
|
|
509
|
-
"run_time",
|
|
510
|
-
"start_time",
|
|
511
|
-
"message",
|
|
512
|
-
"exception",
|
|
513
|
-
"backtrace",
|
|
514
|
-
"resource_class",
|
|
515
|
-
"resource_params",
|
|
516
|
-
"resource_id",
|
|
517
|
-
"skip_message"
|
|
518
|
-
]);
|
|
519
|
-
for (const [key, value] of Object.entries(v1Result)) if (!knownFields.has(key) && !(key in v2Result)) v2Result[key] = value;
|
|
520
513
|
return v2Result;
|
|
521
514
|
}
|
|
522
515
|
/**
|
|
516
|
+
* Convert a v1 ref value (a string or an array of objects) to the v2
|
|
517
|
+
* Reference.ref union value. Returns undefined when there is no content (e.g.
|
|
518
|
+
* an empty array), so callers can drop empty references — matching the Go
|
|
519
|
+
* converter's toRef.
|
|
520
|
+
*/
|
|
521
|
+
function toRefValue(raw) {
|
|
522
|
+
if (typeof raw === "string") return raw === "" ? void 0 : raw;
|
|
523
|
+
if (Array.isArray(raw)) {
|
|
524
|
+
const maps = raw.filter((e) => !!e && typeof e === "object" && !Array.isArray(e));
|
|
525
|
+
return maps.length === 0 ? void 0 : maps;
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
/**
|
|
529
|
+
* Convert a single v1 refs[] element (a bare string or an object with
|
|
530
|
+
* ref/url/uri) to a v2 Reference object. Returns null when the element carries
|
|
531
|
+
* no usable content. Key order (ref, url, uri) matches the Go Reference struct.
|
|
532
|
+
*/
|
|
533
|
+
function convertRef(raw) {
|
|
534
|
+
if (typeof raw === "string") return raw === "" ? null : { ref: raw };
|
|
535
|
+
if (raw && typeof raw === "object" && !Array.isArray(raw)) {
|
|
536
|
+
const m = raw;
|
|
537
|
+
const out = {};
|
|
538
|
+
let has = false;
|
|
539
|
+
const refVal = toRefValue(m.ref);
|
|
540
|
+
if (refVal !== void 0) {
|
|
541
|
+
out.ref = refVal;
|
|
542
|
+
has = true;
|
|
543
|
+
}
|
|
544
|
+
if (typeof m.url === "string" && m.url !== "") {
|
|
545
|
+
out.url = m.url;
|
|
546
|
+
has = true;
|
|
547
|
+
}
|
|
548
|
+
if (typeof m.uri === "string" && m.uri !== "") {
|
|
549
|
+
out.uri = m.uri;
|
|
550
|
+
has = true;
|
|
551
|
+
}
|
|
552
|
+
return has ? out : null;
|
|
553
|
+
}
|
|
554
|
+
return null;
|
|
555
|
+
}
|
|
556
|
+
/**
|
|
557
|
+
* Map v1 control-level refs to v2 requirement refs, dropping empty/contentless
|
|
558
|
+
* entries. Returns undefined when nothing maps.
|
|
559
|
+
*/
|
|
560
|
+
function convertRefs(refs) {
|
|
561
|
+
const out = [];
|
|
562
|
+
for (const raw of refs) {
|
|
563
|
+
const ref = convertRef(raw);
|
|
564
|
+
if (ref) out.push(ref);
|
|
565
|
+
}
|
|
566
|
+
return out.length ? out : void 0;
|
|
567
|
+
}
|
|
568
|
+
/**
|
|
523
569
|
* Convert v1.0 control to v2.0 requirement.
|
|
524
570
|
* Transforms field names and structure.
|
|
525
571
|
*/
|
|
@@ -529,37 +575,23 @@ function convertControl(v1Control) {
|
|
|
529
575
|
impact: v1Control.impact
|
|
530
576
|
};
|
|
531
577
|
if (v1Control.title !== void 0) v2Req.title = v1Control.title;
|
|
532
|
-
if (v1Control.desc !== void 0) v2Req.desc = v1Control.desc;
|
|
533
578
|
if (v1Control.descriptions !== void 0) v2Req.descriptions = v1Control.descriptions;
|
|
534
|
-
if (v1Control.refs !== void 0) v2Req.refs = v1Control.refs;
|
|
535
579
|
if (v1Control.tags !== void 0) v2Req.tags = v1Control.tags;
|
|
536
580
|
if (v1Control.code !== void 0) v2Req.code = v1Control.code;
|
|
581
|
+
if (Array.isArray(v1Control.refs)) {
|
|
582
|
+
const refs = convertRefs(v1Control.refs);
|
|
583
|
+
if (refs) v2Req.refs = refs;
|
|
584
|
+
}
|
|
537
585
|
if (v1Control.source_location !== void 0) v2Req.sourceLocation = v1Control.source_location;
|
|
538
|
-
if (v1Control.waiver_data !== void 0) v2Req.waiverData = v1Control.waiver_data;
|
|
539
586
|
if (v1Control.status !== void 0) v2Req.effectiveStatus = normalizeStatus$1(v1Control.status);
|
|
540
587
|
if (v1Control.results && Array.isArray(v1Control.results)) v2Req.results = v1Control.results.map(convertResult);
|
|
541
588
|
if (!v2Req.effectiveStatus) v2Req.effectiveStatus = computeEffectiveStatus(v1Control.impact, v2Req.results ?? []);
|
|
542
|
-
v2Req.severity = tagSeverityToSeverity(v1Control.tags?.severity) ?? impactToSeverity
|
|
589
|
+
v2Req.severity = tagSeverityToSeverity(v1Control.tags?.severity) ?? impactToSeverity(v1Control.impact);
|
|
543
590
|
const rawNist = v1Control.tags?.nist;
|
|
544
591
|
if (Array.isArray(rawNist)) {
|
|
545
592
|
const controlType = deriveControlTypeFromTags(rawNist.filter((v) => typeof v === "string"));
|
|
546
593
|
if (controlType !== void 0) v2Req.controlType = controlType;
|
|
547
594
|
}
|
|
548
|
-
const knownFields = new Set([
|
|
549
|
-
"id",
|
|
550
|
-
"title",
|
|
551
|
-
"desc",
|
|
552
|
-
"descriptions",
|
|
553
|
-
"impact",
|
|
554
|
-
"refs",
|
|
555
|
-
"tags",
|
|
556
|
-
"code",
|
|
557
|
-
"source_location",
|
|
558
|
-
"waiver_data",
|
|
559
|
-
"results",
|
|
560
|
-
"status"
|
|
561
|
-
]);
|
|
562
|
-
for (const [key, value] of Object.entries(v1Control)) if (!knownFields.has(key) && !(key in v2Req)) v2Req[key] = value;
|
|
563
595
|
return v2Req;
|
|
564
596
|
}
|
|
565
597
|
/**
|
|
@@ -572,7 +604,7 @@ function convertGroup(v1Group) {
|
|
|
572
604
|
requirements: v1Group.controls
|
|
573
605
|
};
|
|
574
606
|
if (v1Group.title !== void 0) v2Group.title = v1Group.title;
|
|
575
|
-
const knownFields = new Set([
|
|
607
|
+
const knownFields = /* @__PURE__ */ new Set([
|
|
576
608
|
"id",
|
|
577
609
|
"title",
|
|
578
610
|
"controls"
|
|
@@ -598,7 +630,7 @@ function convertDependency(v1Dep) {
|
|
|
598
630
|
if (v1Dep.compliance !== void 0) v2Dep.compliance = v1Dep.compliance;
|
|
599
631
|
if (v1Dep.status !== void 0) v2Dep.status = v1Dep.status;
|
|
600
632
|
if (v1Dep.skip_message !== void 0) v2Dep.skipMessage = v1Dep.skip_message;
|
|
601
|
-
const knownFields = new Set([
|
|
633
|
+
const knownFields = /* @__PURE__ */ new Set([
|
|
602
634
|
"name",
|
|
603
635
|
"url",
|
|
604
636
|
"path",
|
|
@@ -616,6 +648,26 @@ function convertDependency(v1Dep) {
|
|
|
616
648
|
return v2Dep;
|
|
617
649
|
}
|
|
618
650
|
/**
|
|
651
|
+
* Map v1 profile `supports` entries (InSpec hyphenated keys) to v2
|
|
652
|
+
* SupportedPlatform objects. Entries that map no recognized key are dropped.
|
|
653
|
+
* Key order (platform, platformFamily, platformName, release) matches the Go
|
|
654
|
+
* SupportedPlatform struct. Returns undefined when nothing maps.
|
|
655
|
+
*/
|
|
656
|
+
function convertSupports(supports) {
|
|
657
|
+
const out = [];
|
|
658
|
+
for (const raw of supports) {
|
|
659
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) continue;
|
|
660
|
+
const s = raw;
|
|
661
|
+
const sp = {};
|
|
662
|
+
if (typeof s.platform === "string" && s.platform !== "") sp.platform = s.platform;
|
|
663
|
+
if (typeof s["platform-family"] === "string" && s["platform-family"] !== "") sp.platformFamily = s["platform-family"];
|
|
664
|
+
if (typeof s["platform-name"] === "string" && s["platform-name"] !== "") sp.platformName = s["platform-name"];
|
|
665
|
+
if (typeof s.release === "string" && s.release !== "") sp.release = s.release;
|
|
666
|
+
if (Object.keys(sp).length > 0) out.push(sp);
|
|
667
|
+
}
|
|
668
|
+
return out.length ? out : void 0;
|
|
669
|
+
}
|
|
670
|
+
/**
|
|
619
671
|
* Convert v1.0 profile to v2.0 baseline.
|
|
620
672
|
* Transforms field names and nested structures.
|
|
621
673
|
*/
|
|
@@ -627,9 +679,12 @@ function convertProfile(v1Profile) {
|
|
|
627
679
|
if (v1Profile.summary !== void 0) v2Baseline.summary = v1Profile.summary;
|
|
628
680
|
if (v1Profile.license !== void 0) v2Baseline.license = v1Profile.license;
|
|
629
681
|
if (v1Profile.copyright !== void 0) v2Baseline.copyright = v1Profile.copyright;
|
|
630
|
-
if (v1Profile.copyright_email !== void 0) v2Baseline.
|
|
631
|
-
if (v1Profile.supports
|
|
632
|
-
|
|
682
|
+
if (v1Profile.copyright_email !== void 0) v2Baseline.copyrightEmail = v1Profile.copyright_email;
|
|
683
|
+
if (v1Profile.supports?.length) {
|
|
684
|
+
const supports = convertSupports(v1Profile.supports);
|
|
685
|
+
if (supports) v2Baseline.supports = supports;
|
|
686
|
+
}
|
|
687
|
+
if (v1Profile.attributes?.length) v2Baseline.inputs = v1Profile.attributes;
|
|
633
688
|
if (v1Profile.status !== void 0) v2Baseline.status = v1Profile.status;
|
|
634
689
|
if (v1Profile.sha256) v2Baseline.integrity = {
|
|
635
690
|
algorithm: "sha256",
|
|
@@ -638,30 +693,9 @@ function convertProfile(v1Profile) {
|
|
|
638
693
|
if (v1Profile.parent_profile !== void 0) v2Baseline.parentBaseline = v1Profile.parent_profile;
|
|
639
694
|
if (v1Profile.status_message !== void 0) v2Baseline.statusMessage = v1Profile.status_message;
|
|
640
695
|
if (v1Profile.skip_message !== void 0) v2Baseline.skipMessage = v1Profile.skip_message;
|
|
641
|
-
if (v1Profile.groups
|
|
696
|
+
if (v1Profile.groups?.length) v2Baseline.groups = v1Profile.groups.map(convertGroup);
|
|
642
697
|
if (v1Profile.controls && Array.isArray(v1Profile.controls)) v2Baseline.requirements = v1Profile.controls.map(convertControl);
|
|
643
|
-
if (v1Profile.depends
|
|
644
|
-
const knownFields = new Set([
|
|
645
|
-
"name",
|
|
646
|
-
"version",
|
|
647
|
-
"title",
|
|
648
|
-
"maintainer",
|
|
649
|
-
"summary",
|
|
650
|
-
"license",
|
|
651
|
-
"copyright",
|
|
652
|
-
"copyright_email",
|
|
653
|
-
"supports",
|
|
654
|
-
"attributes",
|
|
655
|
-
"groups",
|
|
656
|
-
"controls",
|
|
657
|
-
"sha256",
|
|
658
|
-
"depends",
|
|
659
|
-
"parent_profile",
|
|
660
|
-
"status",
|
|
661
|
-
"status_message",
|
|
662
|
-
"skip_message"
|
|
663
|
-
]);
|
|
664
|
-
for (const [key, value] of Object.entries(v1Profile)) if (!knownFields.has(key) && !(key in v2Baseline)) v2Baseline[key] = value;
|
|
698
|
+
if (v1Profile.depends?.length) v2Baseline.depends = v1Profile.depends.map(convertDependency);
|
|
665
699
|
return v2Baseline;
|
|
666
700
|
}
|
|
667
701
|
/**
|
|
@@ -694,17 +728,21 @@ function convertV1ToV2(v1Data) {
|
|
|
694
728
|
baselines: (v1Data.profiles || []).map(convertProfile),
|
|
695
729
|
statistics: v1Data.statistics || {}
|
|
696
730
|
};
|
|
697
|
-
if (v1Data.platform)
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
731
|
+
if (v1Data.platform) {
|
|
732
|
+
const component = {
|
|
733
|
+
type: "host",
|
|
734
|
+
name: v1Data.platform.name
|
|
735
|
+
};
|
|
736
|
+
if (v1Data.platform.target_id || v1Data.platform.release !== void 0) {
|
|
737
|
+
component.osName = v1Data.platform.name;
|
|
738
|
+
if (v1Data.platform.release !== void 0) component.osVersion = v1Data.platform.release;
|
|
739
|
+
}
|
|
740
|
+
v2.components = [component];
|
|
741
|
+
}
|
|
704
742
|
if (v1Data.generator) v2.generator = v1Data.generator;
|
|
705
743
|
v2.tool = { name: "Heimdall Data Format v1" };
|
|
706
744
|
if (v1Data.timestamp) v2.timestamp = v1Data.timestamp;
|
|
707
|
-
const knownV1Fields = new Set([
|
|
745
|
+
const knownV1Fields = /* @__PURE__ */ new Set([
|
|
708
746
|
"version",
|
|
709
747
|
"platform",
|
|
710
748
|
"profiles",
|
|
@@ -1069,8 +1107,9 @@ async function convertJunitToHdf(input) {
|
|
|
1069
1107
|
if (!input || !input.trim()) throw new Error("Empty input");
|
|
1070
1108
|
validateInputSize(input, "junit");
|
|
1071
1109
|
const { suites, name } = parseJUnitXML(input);
|
|
1072
|
-
const
|
|
1073
|
-
|
|
1110
|
+
const scanTime = resolveScanTime(suites);
|
|
1111
|
+
const requirements = buildRequirements(suites, scanTime);
|
|
1112
|
+
if (requirements.length === 0) requirements.push(buildNoFindingsRequirement("junit-no-findings", `JUnit scanned ${noFindingsTarget(name, suites)} and reported zero findings.`, scanTime));
|
|
1074
1113
|
return buildHdfResults({
|
|
1075
1114
|
generatorName: "junit-to-hdf",
|
|
1076
1115
|
converterVersion: CONVERTER_VERSION$2,
|
|
@@ -1081,9 +1120,16 @@ async function convertJunitToHdf(input) {
|
|
|
1081
1120
|
type: TargetType.Application,
|
|
1082
1121
|
name
|
|
1083
1122
|
}],
|
|
1084
|
-
timestamp:
|
|
1123
|
+
timestamp: scanTime
|
|
1085
1124
|
});
|
|
1086
1125
|
}
|
|
1126
|
+
function resolveScanTime(suites) {
|
|
1127
|
+
for (const suite of suites) if (suite.timestamp) {
|
|
1128
|
+
const parsed = parseTimestamp(suite.timestamp);
|
|
1129
|
+
if (parsed) return parsed;
|
|
1130
|
+
}
|
|
1131
|
+
return /* @__PURE__ */ new Date();
|
|
1132
|
+
}
|
|
1087
1133
|
function parseJUnitXML(input) {
|
|
1088
1134
|
const parsed = parseXmlWithArrays(input, ARRAY_TAGS$2);
|
|
1089
1135
|
if (parsed.testsuites) return {
|
|
@@ -1100,7 +1146,7 @@ function parseJUnitXML(input) {
|
|
|
1100
1146
|
}
|
|
1101
1147
|
throw new Error("Input is not a JUnit XML document: expected <testsuites> or <testsuite> root element");
|
|
1102
1148
|
}
|
|
1103
|
-
function buildRequirements(suites) {
|
|
1149
|
+
function buildRequirements(suites, scanTime) {
|
|
1104
1150
|
const { items: limitedSuites, truncated: truncatedSuites } = limitArray(suites);
|
|
1105
1151
|
/* v8 ignore next -- truncation only triggers with >100K items */
|
|
1106
1152
|
if (truncatedSuites) console.warn(`WARNING: Input truncated at ${limitedSuites.length} test suite items (original: ${suites.length})`);
|
|
@@ -1110,19 +1156,21 @@ function buildRequirements(suites) {
|
|
|
1110
1156
|
const { items: limitedTestCases, truncated: truncatedTC } = limitArray(testcases);
|
|
1111
1157
|
/* v8 ignore next -- truncation only triggers with >100K items */
|
|
1112
1158
|
if (truncatedTC) console.warn(`WARNING: Input truncated at ${limitedTestCases.length} test case items (original: ${testcases.length})`);
|
|
1113
|
-
for (const tc of limitedTestCases) requirements.push(testCaseToRequirement(tc,
|
|
1159
|
+
for (const tc of limitedTestCases) requirements.push(testCaseToRequirement(tc, scanTime));
|
|
1114
1160
|
}
|
|
1115
1161
|
return requirements;
|
|
1116
1162
|
}
|
|
1117
|
-
function testCaseToRequirement(tc,
|
|
1163
|
+
function testCaseToRequirement(tc, scanTime) {
|
|
1118
1164
|
const id = buildID(tc);
|
|
1119
1165
|
const { status, message } = resolveStatus(tc);
|
|
1120
|
-
const resultOptions = {
|
|
1166
|
+
const resultOptions = {
|
|
1167
|
+
codeDesc: buildCodeDesc$9(tc),
|
|
1168
|
+
startTime: scanTime
|
|
1169
|
+
};
|
|
1121
1170
|
if (tc.time) {
|
|
1122
1171
|
const parsed = parseFloat(tc.time);
|
|
1123
1172
|
if (!isNaN(parsed)) resultOptions.runTime = parsed;
|
|
1124
1173
|
}
|
|
1125
|
-
if (suiteTimestamp) resultOptions.startTime = suiteTimestamp;
|
|
1126
1174
|
const result = createResult(status, message ?? "", resultOptions);
|
|
1127
1175
|
const descriptions = [{
|
|
1128
1176
|
label: "default",
|
|
@@ -1227,6 +1275,13 @@ const STATUS_MAP = {
|
|
|
1227
1275
|
* @param input - Raw XML string (XCCDF Benchmark with TestResult, or ARF asset-report-collection)
|
|
1228
1276
|
* @returns Stringified HDF Results JSON
|
|
1229
1277
|
*/
|
|
1278
|
+
function parseStartTime(raw) {
|
|
1279
|
+
if (raw) {
|
|
1280
|
+
const t = parseTimestamp(raw);
|
|
1281
|
+
if (t) return t;
|
|
1282
|
+
}
|
|
1283
|
+
return /* @__PURE__ */ new Date();
|
|
1284
|
+
}
|
|
1230
1285
|
async function convertXccdfResultsToHdf(input) {
|
|
1231
1286
|
if (!input || !input.trim()) throw new Error("Empty input");
|
|
1232
1287
|
validateInputSize(input, "xccdf-results");
|
|
@@ -1245,20 +1300,21 @@ async function convertBenchmarkResultsToHdf(benchmark, rawInput) {
|
|
|
1245
1300
|
const { items: limitedRuleResults, truncated: truncatedRR } = limitArray(ruleResults);
|
|
1246
1301
|
/* v8 ignore next -- truncation only triggers with >100K items */
|
|
1247
1302
|
if (truncatedRR) console.warn(`WARNING: Input truncated at ${limitedRuleResults.length} rule-result items (original: ${ruleResults.length})`);
|
|
1248
|
-
const
|
|
1303
|
+
const scanTime = parseStartTime(testResult["start-time"]);
|
|
1304
|
+
const requirements = limitedRuleResults.map((rr) => ruleResultToRequirement(rr, ruleIndex, scanTime));
|
|
1249
1305
|
if (requirements.length === 0) {
|
|
1250
1306
|
const target = xccdfTargetName(testResult, benchmark);
|
|
1251
|
-
requirements.push(buildNoFindingsRequirement("xccdf-results-no-findings", `XCCDF scanned ${target} and reported zero findings.`,
|
|
1307
|
+
requirements.push(buildNoFindingsRequirement("xccdf-results-no-findings", `XCCDF scanned ${target} and reported zero findings.`, scanTime));
|
|
1252
1308
|
}
|
|
1253
1309
|
const resultsChecksum = await inputChecksum(rawInput);
|
|
1254
1310
|
const baseline = createMinimalBaseline(extractText(benchmark.title) || "XCCDF Benchmark", requirements, { resultsChecksum });
|
|
1255
1311
|
const components = buildTargets(testResult);
|
|
1256
|
-
const timestamp =
|
|
1312
|
+
const timestamp = scanTime;
|
|
1257
1313
|
let durationSeconds;
|
|
1258
1314
|
if (testResult["start-time"] && testResult["end-time"]) {
|
|
1259
|
-
const start =
|
|
1260
|
-
const end =
|
|
1261
|
-
if (
|
|
1315
|
+
const start = parseTimestamp(testResult["start-time"])?.getTime();
|
|
1316
|
+
const end = parseTimestamp(testResult["end-time"])?.getTime();
|
|
1317
|
+
if (start !== void 0 && end !== void 0 && end >= start) durationSeconds = (end - start) / 1e3;
|
|
1262
1318
|
}
|
|
1263
1319
|
const hdf = {
|
|
1264
1320
|
baselines: [baseline],
|
|
@@ -1274,7 +1330,7 @@ async function convertBenchmarkResultsToHdf(benchmark, rawInput) {
|
|
|
1274
1330
|
timestamp
|
|
1275
1331
|
};
|
|
1276
1332
|
if (durationSeconds !== void 0) hdf.statistics = { duration: durationSeconds };
|
|
1277
|
-
return
|
|
1333
|
+
return serializeHdf(hdf);
|
|
1278
1334
|
}
|
|
1279
1335
|
async function convertArfCollection(arc, rawInput) {
|
|
1280
1336
|
const resultsChecksum = await inputChecksum(rawInput);
|
|
@@ -1291,20 +1347,24 @@ async function convertArfCollection(arc, rawInput) {
|
|
|
1291
1347
|
for (const report of arc.reports?.report ?? []) {
|
|
1292
1348
|
const testResult = report.content?.TestResult;
|
|
1293
1349
|
if (!testResult?.id) continue;
|
|
1294
|
-
if (testResult["start-time"] && !firstTimestamp)
|
|
1350
|
+
if (testResult["start-time"] && !firstTimestamp) {
|
|
1351
|
+
const t = parseTimestamp(testResult["start-time"]);
|
|
1352
|
+
if (t) firstTimestamp = t;
|
|
1353
|
+
}
|
|
1295
1354
|
if (testResult["start-time"] && testResult["end-time"]) {
|
|
1296
|
-
const start =
|
|
1297
|
-
const end =
|
|
1298
|
-
if (
|
|
1355
|
+
const start = parseTimestamp(testResult["start-time"])?.getTime();
|
|
1356
|
+
const end = parseTimestamp(testResult["end-time"])?.getTime();
|
|
1357
|
+
if (start !== void 0 && end !== void 0 && end >= start) totalDuration += (end - start) / 1e3;
|
|
1299
1358
|
}
|
|
1359
|
+
const scanTime = parseStartTime(testResult["start-time"]);
|
|
1300
1360
|
const ruleResults = testResult["rule-result"] ?? [];
|
|
1301
1361
|
const { items: limitedARFRuleResults, truncated: truncatedARFRR } = limitArray(ruleResults);
|
|
1302
1362
|
/* v8 ignore next -- truncation only triggers with >100K items */
|
|
1303
1363
|
if (truncatedARFRR) console.warn(`WARNING: Input truncated at ${limitedARFRuleResults.length} rule-result items (original: ${ruleResults.length})`);
|
|
1304
|
-
const requirements = limitedARFRuleResults.map((rr) => ruleResultToRequirement(rr, ruleIndex));
|
|
1364
|
+
const requirements = limitedARFRuleResults.map((rr) => ruleResultToRequirement(rr, ruleIndex, scanTime));
|
|
1305
1365
|
if (requirements.length === 0) {
|
|
1306
1366
|
const target = xccdfTargetName(testResult, benchmark);
|
|
1307
|
-
requirements.push(buildNoFindingsRequirement("xccdf-results-no-findings", `XCCDF scanned ${target} and reported zero findings.`,
|
|
1367
|
+
requirements.push(buildNoFindingsRequirement("xccdf-results-no-findings", `XCCDF scanned ${target} and reported zero findings.`, scanTime));
|
|
1308
1368
|
}
|
|
1309
1369
|
let baselineName = "";
|
|
1310
1370
|
if (benchmark) baselineName = extractText(benchmark.title) || "";
|
|
@@ -1337,7 +1397,7 @@ async function convertArfCollection(arc, rawInput) {
|
|
|
1337
1397
|
timestamp: firstTimestamp ?? /* @__PURE__ */ new Date()
|
|
1338
1398
|
};
|
|
1339
1399
|
if (totalDuration > 0) hdf.statistics = { duration: totalDuration };
|
|
1340
|
-
return
|
|
1400
|
+
return serializeHdf(hdf);
|
|
1341
1401
|
}
|
|
1342
1402
|
/**
|
|
1343
1403
|
* Find the XCCDF Benchmark embedded in an ARF data-stream-collection.
|
|
@@ -1393,7 +1453,7 @@ function buildRuleIndex(benchmark) {
|
|
|
1393
1453
|
/**
|
|
1394
1454
|
* Convert a single <rule-result> into an EvaluatedRequirement.
|
|
1395
1455
|
*/
|
|
1396
|
-
function ruleResultToRequirement(rr, ruleIndex) {
|
|
1456
|
+
function ruleResultToRequirement(rr, ruleIndex, scanTime) {
|
|
1397
1457
|
const ruleId = rr.idref ?? "";
|
|
1398
1458
|
const ruleDef = ruleIndex.get(ruleId);
|
|
1399
1459
|
const id = extractVersion$1(ruleDef?.version) || ruleId;
|
|
@@ -1411,7 +1471,16 @@ function ruleResultToRequirement(rr, ruleIndex) {
|
|
|
1411
1471
|
label: "fix",
|
|
1412
1472
|
data: fixtext
|
|
1413
1473
|
});
|
|
1414
|
-
|
|
1474
|
+
if (descriptions.length === 0) descriptions.push({
|
|
1475
|
+
label: "default",
|
|
1476
|
+
data: ""
|
|
1477
|
+
});
|
|
1478
|
+
const status = STATUS_MAP[(rr.result ?? "").toLowerCase()] ?? ResultStatus.NotReviewed;
|
|
1479
|
+
const perRuleTime = rr.time ? parseTimestamp(rr.time) : null;
|
|
1480
|
+
const result = createResult(status, "", {
|
|
1481
|
+
codeDesc: `XCCDF rule ${id}`,
|
|
1482
|
+
startTime: perRuleTime ?? scanTime
|
|
1483
|
+
});
|
|
1415
1484
|
const tags = {};
|
|
1416
1485
|
let nistTags = [];
|
|
1417
1486
|
const cciIds = extractCCIs(rr.ident ?? ruleDef?.ident ?? []);
|
|
@@ -1589,7 +1658,7 @@ const VULN_ATTR_ORDER = [
|
|
|
1589
1658
|
"Class",
|
|
1590
1659
|
"STIG_UUID"
|
|
1591
1660
|
];
|
|
1592
|
-
const CORE_VULN_ATTR = new Set([
|
|
1661
|
+
const CORE_VULN_ATTR = /* @__PURE__ */ new Set([
|
|
1593
1662
|
"Vuln_Num",
|
|
1594
1663
|
"Severity",
|
|
1595
1664
|
"Group_Title",
|
|
@@ -1625,7 +1694,9 @@ function parseCkl(input) {
|
|
|
1625
1694
|
webDBSite: str(a["WEB_DB_SITE"]),
|
|
1626
1695
|
webDBInstance: str(a["WEB_DB_INSTANCE"])
|
|
1627
1696
|
},
|
|
1628
|
-
stigs: istigs.map((is) => {
|
|
1697
|
+
stigs: istigs.map((is, i) => {
|
|
1698
|
+
const vulns = is.VULN ?? [];
|
|
1699
|
+
if (vulns.length === 0) throw new Error(`parse ckl: <iSTIG> block ${i + 1} contains no <VULN> rules`);
|
|
1629
1700
|
const si = is.STIG_INFO?.SI_DATA ?? [];
|
|
1630
1701
|
const siVal = (name) => str(si.find((d) => d.SID_NAME === name)?.SID_DATA);
|
|
1631
1702
|
return {
|
|
@@ -1635,7 +1706,7 @@ function parseCkl(input) {
|
|
|
1635
1706
|
releaseInfo: siVal("releaseinfo"),
|
|
1636
1707
|
uuid: siVal("uuid"),
|
|
1637
1708
|
classification: siVal("classification"),
|
|
1638
|
-
vulns:
|
|
1709
|
+
vulns: vulns.map(cklVulnToModel)
|
|
1639
1710
|
};
|
|
1640
1711
|
})
|
|
1641
1712
|
};
|
|
@@ -1645,7 +1716,7 @@ function cklVulnToModel(v) {
|
|
|
1645
1716
|
const attr = (name) => str(data.find((sd) => sd.VULN_ATTRIBUTE === name)?.ATTRIBUTE_DATA);
|
|
1646
1717
|
const ccis = [];
|
|
1647
1718
|
const extra = {};
|
|
1648
|
-
const promoted = new Set([
|
|
1719
|
+
const promoted = /* @__PURE__ */ new Set([
|
|
1649
1720
|
"CCI_REF",
|
|
1650
1721
|
"Vuln_Num",
|
|
1651
1722
|
"Severity",
|
|
@@ -1794,16 +1865,20 @@ function parseCklb(input) {
|
|
|
1794
1865
|
techArea: nz(t.technology_area),
|
|
1795
1866
|
classification: nz(t.classification)
|
|
1796
1867
|
};
|
|
1797
|
-
const stigs = doc.stigs.map((s) =>
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1868
|
+
const stigs = doc.stigs.map((s, i) => {
|
|
1869
|
+
const rules = s.rules ?? [];
|
|
1870
|
+
if (rules.length === 0) throw new Error(`parse cklb: stigs[${i}] contains no rules[]`);
|
|
1871
|
+
return {
|
|
1872
|
+
stigID: nz(s.stig_id),
|
|
1873
|
+
title: nz(s.stig_name) || nz(s.display_name),
|
|
1874
|
+
displayName: nz(s.display_name),
|
|
1875
|
+
version: nz(s.version),
|
|
1876
|
+
releaseInfo: nz(s.release_info),
|
|
1877
|
+
uuid: nz(s.uuid),
|
|
1878
|
+
referenceIdentifier: nz(s.reference_identifier),
|
|
1879
|
+
vulns: rules.map(cklbRuleToModel)
|
|
1880
|
+
};
|
|
1881
|
+
});
|
|
1807
1882
|
return {
|
|
1808
1883
|
format: "cklb",
|
|
1809
1884
|
cklbVersion: nz(doc.cklb_version),
|
|
@@ -1906,8 +1981,9 @@ const CONVERTER_VERSION = "1.0.0";
|
|
|
1906
1981
|
* Original-format metadata is stashed in extensions/tags for round-trip.
|
|
1907
1982
|
*/
|
|
1908
1983
|
function checklistToHdf(cl, resultsChecksum, generatorName) {
|
|
1984
|
+
const scanTime = /* @__PURE__ */ new Date();
|
|
1909
1985
|
const hdf = {
|
|
1910
|
-
baselines: cl.stigs.map((s) => stigToBaseline(s, resultsChecksum)),
|
|
1986
|
+
baselines: cl.stigs.map((s) => stigToBaseline(s, resultsChecksum, scanTime)),
|
|
1911
1987
|
generator: {
|
|
1912
1988
|
name: generatorName,
|
|
1913
1989
|
version: CONVERTER_VERSION
|
|
@@ -1916,7 +1992,7 @@ function checklistToHdf(cl, resultsChecksum, generatorName) {
|
|
|
1916
1992
|
name: "DISA STIG Viewer",
|
|
1917
1993
|
format: cl.format === "cklb" ? "CKLB" : "CKL"
|
|
1918
1994
|
},
|
|
1919
|
-
timestamp:
|
|
1995
|
+
timestamp: scanTime
|
|
1920
1996
|
};
|
|
1921
1997
|
const component = assetToComponent(cl.asset);
|
|
1922
1998
|
if (component) hdf.components = [component];
|
|
@@ -1924,15 +2000,15 @@ function checklistToHdf(cl, resultsChecksum, generatorName) {
|
|
|
1924
2000
|
if (Object.keys(ext).length > 0) hdf.extensions = ext;
|
|
1925
2001
|
return hdf;
|
|
1926
2002
|
}
|
|
1927
|
-
function stigToBaseline(s, resultsChecksum) {
|
|
1928
|
-
const baseline = createMinimalBaseline("STIG Checklist Scan", s.vulns.map(vulnToRequirement), { resultsChecksum });
|
|
2003
|
+
function stigToBaseline(s, resultsChecksum, scanTime) {
|
|
2004
|
+
const baseline = createMinimalBaseline("STIG Checklist Scan", s.vulns.map((v) => vulnToRequirement(v, scanTime)), { resultsChecksum });
|
|
1929
2005
|
if (s.title) baseline.title = s.title;
|
|
1930
2006
|
if (s.version) baseline.version = s.version;
|
|
1931
2007
|
const ext = baselineExtensions(s);
|
|
1932
2008
|
if (Object.keys(ext).length > 0) baseline.extensions = ext;
|
|
1933
2009
|
return baseline;
|
|
1934
2010
|
}
|
|
1935
|
-
function vulnToRequirement(v) {
|
|
2011
|
+
function vulnToRequirement(v, scanTime) {
|
|
1936
2012
|
const severity = (v.severity ?? "").toLowerCase();
|
|
1937
2013
|
const impact = severity ? severityToImpact(severity) : .5;
|
|
1938
2014
|
const descriptions = [{
|
|
@@ -1948,7 +2024,10 @@ function vulnToRequirement(v) {
|
|
|
1948
2024
|
data: stripHTML(v.fixText)
|
|
1949
2025
|
});
|
|
1950
2026
|
const message = [v.findingDetails, v.comments].map((s) => (s ?? "").trim()).filter(Boolean).join("\n\n");
|
|
1951
|
-
const result = createResult(statusToHdf(v.status), message, {
|
|
2027
|
+
const result = createResult(statusToHdf(v.status), message, {
|
|
2028
|
+
codeDesc: `STIG rule ${v.ruleVer ?? ""}`,
|
|
2029
|
+
startTime: scanTime
|
|
2030
|
+
});
|
|
1952
2031
|
const tags = {};
|
|
1953
2032
|
let nistTags = [];
|
|
1954
2033
|
if (v.ccis.length > 0) {
|
|
@@ -2238,7 +2317,7 @@ function synthesizePurl(ecosystem, name, version) {
|
|
|
2238
2317
|
if (ecosystem === Ecosystem.Generic) return void 0;
|
|
2239
2318
|
return `pkg:${ecosystem}/${name}@${version}`;
|
|
2240
2319
|
}
|
|
2241
|
-
function buildRequirement$16(vulnID, vulns, packageManager) {
|
|
2320
|
+
function buildRequirement$16(vulnID, vulns, scanTime, packageManager) {
|
|
2242
2321
|
const rep = vulns[0];
|
|
2243
2322
|
const cweIDs = rep.identifiers.CWE ?? [];
|
|
2244
2323
|
const nist = mapCWEToNIST(cweIDs, DEFAULT_STATIC_ANALYSIS_NIST_TAGS);
|
|
@@ -2253,7 +2332,10 @@ function buildRequirement$16(vulnID, vulns, packageManager) {
|
|
|
2253
2332
|
label: "default",
|
|
2254
2333
|
data: rep.description
|
|
2255
2334
|
}];
|
|
2256
|
-
const results = vulns.map((vuln) => createResult(ResultStatus.Failed, void 0, {
|
|
2335
|
+
const results = vulns.map((vuln) => createResult(ResultStatus.Failed, void 0, {
|
|
2336
|
+
codeDesc: formatDependencyPath(vuln.from),
|
|
2337
|
+
startTime: scanTime
|
|
2338
|
+
}));
|
|
2257
2339
|
const req = createRequirement(vulnID, rep.title, descriptions, severityToImpact(rep.severity), results, { tags });
|
|
2258
2340
|
const controlType = deriveControlTypeFromTags(nist);
|
|
2259
2341
|
if (controlType !== void 0) req.controlType = controlType;
|
|
@@ -2276,7 +2358,7 @@ function buildRequirement$16(vulnID, vulns, packageManager) {
|
|
|
2276
2358
|
/**
|
|
2277
2359
|
* Converts a single Snyk project report to an HDF baseline.
|
|
2278
2360
|
*/
|
|
2279
|
-
function convertSingleProject(report, resultsChecksum) {
|
|
2361
|
+
function convertSingleProject(report, resultsChecksum, scanTime) {
|
|
2280
2362
|
const { items: limitedVulns, truncated: truncatedVulns } = limitArray(report.vulnerabilities);
|
|
2281
2363
|
/* v8 ignore next -- truncation only triggers with >100K items */
|
|
2282
2364
|
if (truncatedVulns) console.warn(`WARNING: Input truncated at ${limitedVulns.length} vulnerability items (original: ${report.vulnerabilities.length})`);
|
|
@@ -2287,10 +2369,10 @@ function convertSingleProject(report, resultsChecksum) {
|
|
|
2287
2369
|
else groups.set(vuln.id, [vuln]);
|
|
2288
2370
|
}
|
|
2289
2371
|
const requirements = [];
|
|
2290
|
-
for (const [vulnID, vulns] of groups) requirements.push(buildRequirement$16(vulnID, vulns, report.packageManager));
|
|
2372
|
+
for (const [vulnID, vulns] of groups) requirements.push(buildRequirement$16(vulnID, vulns, scanTime, report.packageManager));
|
|
2291
2373
|
if (requirements.length === 0) {
|
|
2292
2374
|
const target = report.projectName ?? report.path ?? "project";
|
|
2293
|
-
requirements.push(buildNoFindingsRequirement("snyk-no-findings", `Snyk scanned ${target} and reported zero vulnerable components.`,
|
|
2375
|
+
requirements.push(buildNoFindingsRequirement("snyk-no-findings", `Snyk scanned ${target} and reported zero vulnerable components.`, scanTime));
|
|
2294
2376
|
}
|
|
2295
2377
|
return createMinimalBaseline("Snyk Scan", requirements, {
|
|
2296
2378
|
resultsChecksum,
|
|
@@ -2314,6 +2396,7 @@ async function convertSnykToHdf(input) {
|
|
|
2314
2396
|
const detected = detectConverter(input);
|
|
2315
2397
|
if (detected && detected.fingerprint.id === "sarif-to-hdf") return convertSarifToHdf(input);
|
|
2316
2398
|
const resultsChecksum = await inputChecksum(input);
|
|
2399
|
+
const scanTime = /* @__PURE__ */ new Date();
|
|
2317
2400
|
const parsed = parseJSON(input);
|
|
2318
2401
|
if (!parsed || typeof parsed !== "object") throw new Error("snyk: invalid JSON");
|
|
2319
2402
|
let baselines;
|
|
@@ -2322,10 +2405,10 @@ async function convertSnykToHdf(input) {
|
|
|
2322
2405
|
const { items: limitedProjects, truncated: truncatedProjects } = limitArray(parsed);
|
|
2323
2406
|
/* v8 ignore next -- truncation only triggers with >100K items */
|
|
2324
2407
|
if (truncatedProjects) console.warn(`WARNING: Input truncated at ${limitedProjects.length} project items (original: ${parsed.length})`);
|
|
2325
|
-
baselines = limitedProjects.map((report) => convertSingleProject(report, resultsChecksum));
|
|
2408
|
+
baselines = limitedProjects.map((report) => convertSingleProject(report, resultsChecksum, scanTime));
|
|
2326
2409
|
targetName = limitedProjects[0]?.projectName ?? limitedProjects[0]?.path ?? "";
|
|
2327
2410
|
} else {
|
|
2328
|
-
baselines = [convertSingleProject(parsed, resultsChecksum)];
|
|
2411
|
+
baselines = [convertSingleProject(parsed, resultsChecksum, scanTime)];
|
|
2329
2412
|
targetName = parsed.projectName ?? parsed.path ?? "";
|
|
2330
2413
|
}
|
|
2331
2414
|
return buildHdfResults({
|
|
@@ -2338,12 +2421,12 @@ async function convertSnykToHdf(input) {
|
|
|
2338
2421
|
name: targetName,
|
|
2339
2422
|
type: TargetType.Application
|
|
2340
2423
|
}],
|
|
2341
|
-
timestamp:
|
|
2424
|
+
timestamp: scanTime
|
|
2342
2425
|
});
|
|
2343
2426
|
}
|
|
2344
2427
|
//#endregion
|
|
2345
2428
|
//#region converters/grype-to-hdf/typescript/converter.ts
|
|
2346
|
-
const IMPACT_MAPPING$8 = new Map([
|
|
2429
|
+
const IMPACT_MAPPING$8 = /* @__PURE__ */ new Map([
|
|
2347
2430
|
["critical", .9],
|
|
2348
2431
|
["high", .7],
|
|
2349
2432
|
["medium", .5],
|
|
@@ -2581,7 +2664,7 @@ async function convertGrypeToHdf(input) {
|
|
|
2581
2664
|
type: TargetType.Artifact,
|
|
2582
2665
|
name: targetName
|
|
2583
2666
|
}],
|
|
2584
|
-
timestamp: grypeData.descriptor?.timestamp ?
|
|
2667
|
+
timestamp: (grypeData.descriptor?.timestamp ? parseTimestamp(grypeData.descriptor.timestamp) : null) ?? /* @__PURE__ */ new Date()
|
|
2585
2668
|
});
|
|
2586
2669
|
}
|
|
2587
2670
|
//#endregion
|
|
@@ -2664,8 +2747,8 @@ function calculateTiming(hosts) {
|
|
|
2664
2747
|
const lastHost = hosts[hosts.length - 1];
|
|
2665
2748
|
const startTimeStr = getHostPropertyValue(firstHost, "HOST_START");
|
|
2666
2749
|
const endTimeStr = getHostPropertyValue(lastHost, "HOST_END") || getHostPropertyValue(lastHost, "HOST_START");
|
|
2667
|
-
const startTime = startTimeStr ? new Date(
|
|
2668
|
-
const endTime = endTimeStr ?
|
|
2750
|
+
const startTime = startTimeStr ? parseTimestamp(startTimeStr) ?? /* @__PURE__ */ new Date() : /* @__PURE__ */ new Date();
|
|
2751
|
+
const endTime = endTimeStr ? parseTimestamp(endTimeStr) ?? startTime : startTime;
|
|
2669
2752
|
return {
|
|
2670
2753
|
startTime,
|
|
2671
2754
|
endTime,
|
|
@@ -2689,7 +2772,7 @@ function convertReportHostToBaseline(host, policyName, version, resultsChecksum)
|
|
|
2689
2772
|
if (requirements.length === 0) {
|
|
2690
2773
|
const target = host.name || getHostPropertyValue(host, "host-ip") || "host";
|
|
2691
2774
|
const startTimeStr = getHostPropertyValue(host, "HOST_START");
|
|
2692
|
-
const startTime = startTimeStr ? new Date(
|
|
2775
|
+
const startTime = startTimeStr ? parseTimestamp(startTimeStr) ?? /* @__PURE__ */ new Date() : /* @__PURE__ */ new Date();
|
|
2693
2776
|
requirements = [buildNoFindingsRequirement("nessus-no-findings", `Nessus scanned ${target} and reported zero findings.`, startTime)];
|
|
2694
2777
|
}
|
|
2695
2778
|
return createMinimalBaseline(`Nessus ${policyName}`, requirements, {
|
|
@@ -2856,8 +2939,8 @@ function buildEpss(item, host) {
|
|
|
2856
2939
|
function epssDate(host) {
|
|
2857
2940
|
const hs = getHostPropertyValue(host, "HOST_START");
|
|
2858
2941
|
if (hs) {
|
|
2859
|
-
const d =
|
|
2860
|
-
if (
|
|
2942
|
+
const d = parseTimestamp(hs);
|
|
2943
|
+
if (d) return d.toISOString().slice(0, 10);
|
|
2861
2944
|
}
|
|
2862
2945
|
}
|
|
2863
2946
|
function buildDescriptions(item, isCompliance) {
|
|
@@ -2928,7 +3011,7 @@ function buildResult$1(item, host, isCompliance) {
|
|
|
2928
3011
|
const codeDesc = getCodeDesc(item);
|
|
2929
3012
|
const message = item.plugin_output || item["compliance-actual-value"];
|
|
2930
3013
|
const startTimeStr = getHostPropertyValue(host, "HOST_START");
|
|
2931
|
-
const startTime = startTimeStr ? new Date(
|
|
3014
|
+
const startTime = startTimeStr ? parseTimestamp(startTimeStr) ?? /* @__PURE__ */ new Date() : /* @__PURE__ */ new Date();
|
|
2932
3015
|
return {
|
|
2933
3016
|
status,
|
|
2934
3017
|
codeDesc,
|
|
@@ -3163,7 +3246,7 @@ function createResultFromIssue(issue, componentMap) {
|
|
|
3163
3246
|
const codeDesc = `${component?.path || component?.longName || issue.component}${issue.line ? ` LINE : ${issue.line}` : ""}`;
|
|
3164
3247
|
return createResult(status, issue.message, {
|
|
3165
3248
|
codeDesc,
|
|
3166
|
-
startTime:
|
|
3249
|
+
startTime: parseTimestamp(issue.creationDate) ?? /* @__PURE__ */ new Date("0001-01-01T00:00:00Z")
|
|
3167
3250
|
});
|
|
3168
3251
|
}
|
|
3169
3252
|
function extractSourceLocation(issue, componentMap) {
|
|
@@ -3200,6 +3283,28 @@ function buildNistTags$3(sourceIdentifier, ruleName) {
|
|
|
3200
3283
|
if (byName) return [byName];
|
|
3201
3284
|
return [];
|
|
3202
3285
|
}
|
|
3286
|
+
/**
|
|
3287
|
+
* Flag rules whose NIST mappings exist at a revision other than the one
|
|
3288
|
+
* currently selected — they emit no NIST tags here even though a mapping
|
|
3289
|
+
* exists elsewhere, a likely sign of a wrong revision selection. Rules
|
|
3290
|
+
* unmapped at every revision are not flagged. Throws in strict mode; otherwise
|
|
3291
|
+
* logs a single aggregated warning.
|
|
3292
|
+
*/
|
|
3293
|
+
function checkRevisionAlignment(rules) {
|
|
3294
|
+
const rev = getCurrentNistRevision();
|
|
3295
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3296
|
+
const lines = [];
|
|
3297
|
+
for (const rule of rules) {
|
|
3298
|
+
const covered = awsConfigMappedRevisions(rule.Source.SourceIdentifier, rule.ConfigRuleName);
|
|
3299
|
+
if (covered.length === 0 || covered.includes(rev) || seen.has(rule.ConfigRuleName)) continue;
|
|
3300
|
+
seen.add(rule.ConfigRuleName);
|
|
3301
|
+
lines.push(` - ${rule.ConfigRuleName} (mapped at Rev ${covered.join(", ")})`);
|
|
3302
|
+
}
|
|
3303
|
+
if (lines.length === 0) return;
|
|
3304
|
+
const detail = `${lines.length} AWS Config rule(s) have NIST 800-53 mappings at a different revision than the requested Rev ${rev}; their NIST tags were omitted:\n${lines.join("\n")}`;
|
|
3305
|
+
if (isNistStrict()) throw new Error(`aws-config: ${detail}\nre-run with a matching revision, or disable strict mode to convert with the gaps`);
|
|
3306
|
+
console.warn(`WARNING: ${detail}`);
|
|
3307
|
+
}
|
|
3203
3308
|
function buildCheckText(rule) {
|
|
3204
3309
|
const parts = [`ARN: ${rule.ConfigRuleArn || "N/A"}`, `Source Identifier: ${rule.Source.SourceIdentifier || "N/A"}`];
|
|
3205
3310
|
if (rule.InputParameters && rule.InputParameters !== "{}") {
|
|
@@ -3220,7 +3325,7 @@ function buildResult(r) {
|
|
|
3220
3325
|
const status = mapComplianceStatus(r.ComplianceType);
|
|
3221
3326
|
const codeDesc = buildCodeDesc$7(q);
|
|
3222
3327
|
const message = buildResultMessage(codeDesc, r.Annotation, status);
|
|
3223
|
-
const startTime = r.ConfigRuleInvokedTime ?
|
|
3328
|
+
const startTime = (r.ConfigRuleInvokedTime ? parseTimestamp(r.ConfigRuleInvokedTime) : null) ?? /* @__PURE__ */ new Date("0001-01-01T00:00:00Z");
|
|
3224
3329
|
return createResult(status, message ?? codeDesc, {
|
|
3225
3330
|
codeDesc,
|
|
3226
3331
|
...startTime ? { startTime } : {}
|
|
@@ -3280,6 +3385,7 @@ async function convertAwsConfigToHdf(input) {
|
|
|
3280
3385
|
const { items: limitedRules, truncated: truncatedRules } = limitArray(data.ConfigRules);
|
|
3281
3386
|
/* v8 ignore next -- truncation only triggers with >100K items */
|
|
3282
3387
|
if (truncatedRules) console.warn(`WARNING: Input truncated at ${limitedRules.length} ConfigRule items (original: ${data.ConfigRules.length})`);
|
|
3388
|
+
checkRevisionAlignment(limitedRules);
|
|
3283
3389
|
const baseline = {
|
|
3284
3390
|
...createMinimalBaseline("AWS Config", limitedRules.map(buildRequirement$15), { resultsChecksum }),
|
|
3285
3391
|
title: "AWS Config Compliance Results",
|
|
@@ -3329,17 +3435,20 @@ function getImpact$8(severity) {
|
|
|
3329
3435
|
/**
|
|
3330
3436
|
* Converts a single CheckovCheck to an HDF RequirementResult.
|
|
3331
3437
|
*/
|
|
3332
|
-
function checkToResult(check) {
|
|
3438
|
+
function checkToResult(check, scanTime) {
|
|
3333
3439
|
const status = mapStatus$2(check.check_result.result);
|
|
3334
3440
|
const codeDesc = `Resource: ${check.resource}\nFile: ${check.file_path} (lines ${JSON.stringify(check.file_line_range)})`;
|
|
3335
3441
|
let message;
|
|
3336
3442
|
if (status === ResultStatus.NotReviewed && check.check_result.suppress_comment) message = check.check_result.suppress_comment;
|
|
3337
|
-
return createResult(status, message ?? "", {
|
|
3443
|
+
return createResult(status, message ?? "", {
|
|
3444
|
+
codeDesc,
|
|
3445
|
+
startTime: scanTime
|
|
3446
|
+
});
|
|
3338
3447
|
}
|
|
3339
3448
|
/**
|
|
3340
3449
|
* Converts a group of checks sharing a check_id into one EvaluatedRequirement.
|
|
3341
3450
|
*/
|
|
3342
|
-
function buildRequirement$14(checkId, checks) {
|
|
3451
|
+
function buildRequirement$14(checkId, checks, scanTime) {
|
|
3343
3452
|
const rep = checks[0];
|
|
3344
3453
|
const impact = getImpact$8(rep.severity);
|
|
3345
3454
|
const tags = { nist: [...DEFAULT_STATIC_ANALYSIS_NIST_TAGS] };
|
|
@@ -3351,7 +3460,7 @@ function buildRequirement$14(checkId, checks) {
|
|
|
3351
3460
|
label: "check",
|
|
3352
3461
|
data: rep.guideline
|
|
3353
3462
|
});
|
|
3354
|
-
const results = checks.map(checkToResult);
|
|
3463
|
+
const results = checks.map((check) => checkToResult(check, scanTime));
|
|
3355
3464
|
const req = createRequirement(checkId, rep.check_name, descriptions, impact, results, { tags });
|
|
3356
3465
|
req.verificationMethod = VerificationMethodEnum.Automated;
|
|
3357
3466
|
const controlType = deriveControlTypeFromTags([...DEFAULT_STATIC_ANALYSIS_NIST_TAGS]);
|
|
@@ -3382,6 +3491,7 @@ async function convertCheckovToHdf(input) {
|
|
|
3382
3491
|
const detected = detectConverter(input);
|
|
3383
3492
|
if (detected && detected.fingerprint.id === "sarif-to-hdf") return convertSarifToHdf(input);
|
|
3384
3493
|
const resultsChecksum = await inputChecksum(input);
|
|
3494
|
+
const scanTime = /* @__PURE__ */ new Date();
|
|
3385
3495
|
const reports = parseInput(input);
|
|
3386
3496
|
const allChecks = [];
|
|
3387
3497
|
const checkTypes = [];
|
|
@@ -3403,11 +3513,11 @@ async function convertCheckovToHdf(input) {
|
|
|
3403
3513
|
else groups.set(check.check_id, [check]);
|
|
3404
3514
|
}
|
|
3405
3515
|
const requirements = [];
|
|
3406
|
-
for (const [checkId, checks] of groups) requirements.push(buildRequirement$14(checkId, checks));
|
|
3516
|
+
for (const [checkId, checks] of groups) requirements.push(buildRequirement$14(checkId, checks, scanTime));
|
|
3407
3517
|
const format = checkTypes.join(", ");
|
|
3408
3518
|
if (requirements.length === 0) {
|
|
3409
3519
|
const target = format || "input";
|
|
3410
|
-
requirements.push(buildNoFindingsRequirement("checkov-no-findings", `Checkov scanned ${target} and reported zero findings.`,
|
|
3520
|
+
requirements.push(buildNoFindingsRequirement("checkov-no-findings", `Checkov scanned ${target} and reported zero findings.`, scanTime));
|
|
3411
3521
|
}
|
|
3412
3522
|
const baseline = createMinimalBaseline("Checkov Scan", requirements, { resultsChecksum });
|
|
3413
3523
|
return buildHdfResults({
|
|
@@ -3417,7 +3527,7 @@ async function convertCheckovToHdf(input) {
|
|
|
3417
3527
|
toolVersion: version,
|
|
3418
3528
|
toolFormat: format,
|
|
3419
3529
|
baselines: [baseline],
|
|
3420
|
-
timestamp:
|
|
3530
|
+
timestamp: scanTime
|
|
3421
3531
|
});
|
|
3422
3532
|
}
|
|
3423
3533
|
//#endregion
|
|
@@ -3448,19 +3558,22 @@ function formatSkipMessage(suppressions) {
|
|
|
3448
3558
|
/**
|
|
3449
3559
|
* Converts a single GosecIssue to an HDF RequirementResult.
|
|
3450
3560
|
*/
|
|
3451
|
-
function issueToResult(issue) {
|
|
3561
|
+
function issueToResult(issue, scanTime) {
|
|
3452
3562
|
const suppressed = isSuppressed(issue);
|
|
3453
3563
|
const status = suppressed ? ResultStatus.NotReviewed : ResultStatus.Failed;
|
|
3454
3564
|
let message;
|
|
3455
3565
|
if (suppressed) message = formatSkipMessage(issue.suppressions) ?? "No justification provided";
|
|
3456
3566
|
else message = `${issue.confidence} confidence of rule violation at:\n${issue.code}`;
|
|
3457
3567
|
const codeDesc = `Rule ${issue.rule_id} violation detected at:\nFile: ${issue.file}\nLine: ${issue.line}\nColumn: ${issue.column}`;
|
|
3458
|
-
return createResult(status, message, {
|
|
3568
|
+
return createResult(status, message, {
|
|
3569
|
+
codeDesc,
|
|
3570
|
+
startTime: scanTime
|
|
3571
|
+
});
|
|
3459
3572
|
}
|
|
3460
3573
|
/**
|
|
3461
3574
|
* Converts a group of issues sharing a rule_id into one EvaluatedRequirement.
|
|
3462
3575
|
*/
|
|
3463
|
-
function buildRequirement$13(ruleId, issues) {
|
|
3576
|
+
function buildRequirement$13(ruleId, issues, scanTime) {
|
|
3464
3577
|
const rep = issues[0];
|
|
3465
3578
|
const impact = IMPACT_MAPPING$6[rep.severity.toUpperCase()] ?? .5;
|
|
3466
3579
|
const nist = mapCWEToNIST([rep.cwe.id], DEFAULT_REMEDIATION_NIST_TAGS$1);
|
|
@@ -3471,7 +3584,7 @@ function buildRequirement$13(ruleId, issues) {
|
|
|
3471
3584
|
url: rep.cwe.url
|
|
3472
3585
|
}
|
|
3473
3586
|
};
|
|
3474
|
-
const results = issues.map(issueToResult);
|
|
3587
|
+
const results = issues.map((issue) => issueToResult(issue, scanTime));
|
|
3475
3588
|
const descriptions = [{
|
|
3476
3589
|
label: "default",
|
|
3477
3590
|
data: rep.details
|
|
@@ -3511,9 +3624,10 @@ async function convertGosecToHdf(input) {
|
|
|
3511
3624
|
if (existing) existing.push(issue);
|
|
3512
3625
|
else groups.set(issue.rule_id, [issue]);
|
|
3513
3626
|
}
|
|
3627
|
+
const scanTime = /* @__PURE__ */ new Date();
|
|
3514
3628
|
const requirements = [];
|
|
3515
|
-
for (const [ruleId, issues] of groups) requirements.push(buildRequirement$13(ruleId, issues));
|
|
3516
|
-
if (requirements.length === 0) requirements.push(buildNoFindingsRequirement("gosec-no-findings", "gosec scanned Go codebase and reported zero findings.",
|
|
3629
|
+
for (const [ruleId, issues] of groups) requirements.push(buildRequirement$13(ruleId, issues, scanTime));
|
|
3630
|
+
if (requirements.length === 0) requirements.push(buildNoFindingsRequirement("gosec-no-findings", "gosec scanned Go codebase and reported zero findings.", scanTime));
|
|
3517
3631
|
const baseline = createMinimalBaseline("gosec Scan", requirements, { resultsChecksum });
|
|
3518
3632
|
return buildHdfResults({
|
|
3519
3633
|
generatorName: "gosec-to-hdf",
|
|
@@ -3521,7 +3635,7 @@ async function convertGosecToHdf(input) {
|
|
|
3521
3635
|
toolName: "gosec",
|
|
3522
3636
|
toolVersion: report.GosecVersion || void 0,
|
|
3523
3637
|
baselines: [baseline],
|
|
3524
|
-
timestamp:
|
|
3638
|
+
timestamp: scanTime
|
|
3525
3639
|
});
|
|
3526
3640
|
}
|
|
3527
3641
|
//#endregion
|
|
@@ -3661,6 +3775,15 @@ function selectSite(sites) {
|
|
|
3661
3775
|
}
|
|
3662
3776
|
return best;
|
|
3663
3777
|
}
|
|
3778
|
+
const ZAP_RFC1123_LIKE = /^[A-Za-z]{3}, \d{1,2} [A-Za-z]{3} \d{4} \d{2}:\d{2}:\d{2}$/;
|
|
3779
|
+
function parseZapTimestamp(s) {
|
|
3780
|
+
const trimmed = s.trim();
|
|
3781
|
+
if (ZAP_RFC1123_LIKE.test(trimmed)) {
|
|
3782
|
+
const d = /* @__PURE__ */ new Date(`${trimmed} GMT`);
|
|
3783
|
+
if (!isNaN(d.getTime())) return d;
|
|
3784
|
+
}
|
|
3785
|
+
return parseTimestamp(s) ?? void 0;
|
|
3786
|
+
}
|
|
3664
3787
|
async function convertZapToHdf(input) {
|
|
3665
3788
|
validateInputSize(input, "zap");
|
|
3666
3789
|
registerAllFingerprints();
|
|
@@ -3762,12 +3885,15 @@ async function convertZapToHdf(input) {
|
|
|
3762
3885
|
},
|
|
3763
3886
|
tool
|
|
3764
3887
|
};
|
|
3765
|
-
if (zapData["@generated"])
|
|
3766
|
-
|
|
3888
|
+
if (zapData["@generated"]) {
|
|
3889
|
+
const ts = parseZapTimestamp(zapData["@generated"]);
|
|
3890
|
+
if (ts) hdf.timestamp = ts;
|
|
3891
|
+
}
|
|
3892
|
+
return serializeHdf(hdf);
|
|
3767
3893
|
}
|
|
3768
3894
|
//#endregion
|
|
3769
3895
|
//#region converters/cyclonedx-to-hdf/typescript/converter.ts
|
|
3770
|
-
const CVSS_METHODS = new Set([
|
|
3896
|
+
const CVSS_METHODS = /* @__PURE__ */ new Set([
|
|
3771
3897
|
"CVSSv2",
|
|
3772
3898
|
"CVSSv3",
|
|
3773
3899
|
"CVSSv31",
|
|
@@ -3832,6 +3958,8 @@ async function convertCyclonedxToHdf(input) {
|
|
|
3832
3958
|
if ((!bom.components || bom.components.length === 0) && (!bom.vulnerabilities || bom.vulnerabilities.length === 0)) throw new Error("cyclonedx: input has neither components nor vulnerabilities");
|
|
3833
3959
|
if (!bom.vulnerabilities || bom.vulnerabilities.length === 0) throw new Error("cyclonedx: this file is an SBOM inventory with no vulnerabilities; to import SBOM data into a system document, use:\n hdf system create --from <sbom-file> --component-name <name>");
|
|
3834
3960
|
const resultsChecksum = await inputChecksum(input);
|
|
3961
|
+
const parsedTimestamp = bom.metadata?.timestamp ? parseTimestamp(bom.metadata.timestamp) ?? void 0 : void 0;
|
|
3962
|
+
const scanTime = parsedTimestamp && !isNaN(parsedTimestamp.getTime()) ? parsedTimestamp : /* @__PURE__ */ new Date();
|
|
3835
3963
|
const allComponents = flattenComponents(bom.components ?? []);
|
|
3836
3964
|
const componentLookup = /* @__PURE__ */ new Map();
|
|
3837
3965
|
for (const comp of allComponents) if (comp["bom-ref"]) componentLookup.set(comp["bom-ref"], comp);
|
|
@@ -3870,7 +3998,13 @@ async function convertCyclonedxToHdf(input) {
|
|
|
3870
3998
|
data: fixParts.join("\n\n")
|
|
3871
3999
|
});
|
|
3872
4000
|
const affects = vuln.affects ?? [];
|
|
3873
|
-
const results = affects.length > 0 ? affects.map((affect) => createResult(ResultStatus.Failed, void 0, {
|
|
4001
|
+
const results = affects.length > 0 ? affects.map((affect) => createResult(ResultStatus.Failed, void 0, {
|
|
4002
|
+
codeDesc: formatCodeDesc$4(componentLookup, affect.ref),
|
|
4003
|
+
startTime: scanTime
|
|
4004
|
+
})) : [createResult(ResultStatus.Failed, void 0, {
|
|
4005
|
+
codeDesc: `Vulnerability ${vuln.id}`,
|
|
4006
|
+
startTime: scanTime
|
|
4007
|
+
})];
|
|
3874
4008
|
const title = vuln.source?.name ? `${vuln.id} (${vuln.source.name})` : vuln.id;
|
|
3875
4009
|
const req = createRequirement(vuln.id, title, descriptions, impact, results, { tags });
|
|
3876
4010
|
const controlType = deriveControlTypeFromTags(nist);
|
|
@@ -3889,7 +4023,7 @@ async function convertCyclonedxToHdf(input) {
|
|
|
3889
4023
|
name: targetName,
|
|
3890
4024
|
type: TargetType.Application
|
|
3891
4025
|
}],
|
|
3892
|
-
timestamp:
|
|
4026
|
+
timestamp: scanTime
|
|
3893
4027
|
});
|
|
3894
4028
|
}
|
|
3895
4029
|
//#endregion
|
|
@@ -4060,7 +4194,7 @@ async function convertSplunkToHdf(input) {
|
|
|
4060
4194
|
const descriptions = convertDescriptions(control.descriptions);
|
|
4061
4195
|
const results = Array.isArray(control.results) ? control.results.map((result) => createResult(mapStatus$1(result.status), result.message, {
|
|
4062
4196
|
codeDesc: result.code_desc,
|
|
4063
|
-
startTime:
|
|
4197
|
+
startTime: parseTimestamp(result.start_time) ?? /* @__PURE__ */ new Date("0001-01-01T00:00:00Z"),
|
|
4064
4198
|
runTime: result.run_time,
|
|
4065
4199
|
exception: result.exception,
|
|
4066
4200
|
backtrace: result.backtrace
|
|
@@ -4083,7 +4217,7 @@ async function convertSplunkToHdf(input) {
|
|
|
4083
4217
|
allBaselines.push(createMinimalBaseline(profile.name, requirements, baselineOptions));
|
|
4084
4218
|
}
|
|
4085
4219
|
}
|
|
4086
|
-
|
|
4220
|
+
return serializeHdf({
|
|
4087
4221
|
baselines: allBaselines,
|
|
4088
4222
|
components: [{
|
|
4089
4223
|
name: targetName,
|
|
@@ -4095,8 +4229,7 @@ async function convertSplunkToHdf(input) {
|
|
|
4095
4229
|
name: "splunk-to-hdf",
|
|
4096
4230
|
version: "1.0.0"
|
|
4097
4231
|
}
|
|
4098
|
-
};
|
|
4099
|
-
return JSON.stringify(hdf, null, 2);
|
|
4232
|
+
});
|
|
4100
4233
|
}
|
|
4101
4234
|
//#endregion
|
|
4102
4235
|
//#region converters/hdf-to-xml/typescript/converter.ts
|
|
@@ -4306,7 +4439,7 @@ async function convertGitlabToHdf(input) {
|
|
|
4306
4439
|
const result = {
|
|
4307
4440
|
status: ResultStatus.Failed,
|
|
4308
4441
|
codeDesc: buildCodeDesc$4(scanType, vuln.location),
|
|
4309
|
-
startTime: startTime ? new Date(
|
|
4442
|
+
startTime: startTime ? parseTimestamp(startTime) ?? /* @__PURE__ */ new Date("0001-01-01T00:00:00Z") : /* @__PURE__ */ new Date("0001-01-01T00:00:00Z")
|
|
4310
4443
|
};
|
|
4311
4444
|
const impact = gitlabSeverityToImpact(vuln.severity ?? "Unknown");
|
|
4312
4445
|
const req = {
|
|
@@ -4324,7 +4457,7 @@ async function convertGitlabToHdf(input) {
|
|
|
4324
4457
|
}
|
|
4325
4458
|
const label = scanTypeLabel(scanType);
|
|
4326
4459
|
if (requirements.length === 0) {
|
|
4327
|
-
const ts = startTime ? new Date(
|
|
4460
|
+
const ts = startTime ? parseTimestamp(startTime) ?? /* @__PURE__ */ new Date() : /* @__PURE__ */ new Date();
|
|
4328
4461
|
requirements.push(buildNoFindingsRequirement("gitlab-no-findings", `GitLab ${label} scan via ${scannerName} reported zero findings.`, ts));
|
|
4329
4462
|
}
|
|
4330
4463
|
const baseline = createMinimalBaseline("GitLab Security Scan", requirements, {
|
|
@@ -4352,8 +4485,11 @@ async function convertGitlabToHdf(input) {
|
|
|
4352
4485
|
},
|
|
4353
4486
|
tool
|
|
4354
4487
|
};
|
|
4355
|
-
if (report.scan?.end_time)
|
|
4356
|
-
|
|
4488
|
+
if (report.scan?.end_time) {
|
|
4489
|
+
const endTime = parseTimestamp(report.scan.end_time);
|
|
4490
|
+
if (endTime) hdf.timestamp = endTime;
|
|
4491
|
+
}
|
|
4492
|
+
return serializeHdf(hdf);
|
|
4357
4493
|
}
|
|
4358
4494
|
//#endregion
|
|
4359
4495
|
//#region converters/trufflehog-to-hdf/typescript/converter.ts
|
|
@@ -4420,8 +4556,8 @@ function buildCodeDesc$3(f) {
|
|
|
4420
4556
|
*/
|
|
4421
4557
|
function getTimestamp(f) {
|
|
4422
4558
|
if (f.SourceMetadata?.Data?.Git?.timestamp) {
|
|
4423
|
-
const ts =
|
|
4424
|
-
if (
|
|
4559
|
+
const ts = parseTimestamp(f.SourceMetadata.Data.Git.timestamp);
|
|
4560
|
+
if (ts) return ts;
|
|
4425
4561
|
}
|
|
4426
4562
|
return /* @__PURE__ */ new Date();
|
|
4427
4563
|
}
|
|
@@ -4512,7 +4648,7 @@ async function convertTrufflehogToHdf(input) {
|
|
|
4512
4648
|
name: repoURL,
|
|
4513
4649
|
type: TargetType.Repository
|
|
4514
4650
|
}];
|
|
4515
|
-
return
|
|
4651
|
+
return serializeHdf(hdf);
|
|
4516
4652
|
}
|
|
4517
4653
|
//#endregion
|
|
4518
4654
|
//#region converters/burpsuite-to-hdf/typescript/converter.ts
|
|
@@ -4594,8 +4730,8 @@ async function convertBurpsuiteToHdf(input) {
|
|
|
4594
4730
|
},
|
|
4595
4731
|
tool
|
|
4596
4732
|
};
|
|
4597
|
-
if (exportTime) hdf.timestamp =
|
|
4598
|
-
return
|
|
4733
|
+
if (exportTime) hdf.timestamp = parseTimestamp(exportTime) ?? void 0;
|
|
4734
|
+
return serializeHdf(hdf);
|
|
4599
4735
|
}
|
|
4600
4736
|
function buildRequirement$11(issueType, issues) {
|
|
4601
4737
|
const rep = issues[0];
|
|
@@ -4702,15 +4838,31 @@ function formatSummary(f) {
|
|
|
4702
4838
|
`IP Address, Port, Instance : ${f["IP Address, Port, Instance"] ?? ""}`
|
|
4703
4839
|
].join("\n");
|
|
4704
4840
|
}
|
|
4841
|
+
const MONTH_ABBR = {
|
|
4842
|
+
Jan: "01",
|
|
4843
|
+
Feb: "02",
|
|
4844
|
+
Mar: "03",
|
|
4845
|
+
Apr: "04",
|
|
4846
|
+
May: "05",
|
|
4847
|
+
Jun: "06",
|
|
4848
|
+
Jul: "07",
|
|
4849
|
+
Aug: "08",
|
|
4850
|
+
Sep: "09",
|
|
4851
|
+
Oct: "10",
|
|
4852
|
+
Nov: "11",
|
|
4853
|
+
Dec: "12"
|
|
4854
|
+
};
|
|
4705
4855
|
/**
|
|
4706
|
-
* Parses DBProtect date
|
|
4856
|
+
* Parses DBProtect date formats ("Feb 18 2021 15:57" or "2021-02-18 15:55").
|
|
4857
|
+
* Month-name values are normalized to ISO so parseTimestamp interprets them as
|
|
4858
|
+
* UTC, matching the Go peer and keeping output host-timezone-independent.
|
|
4707
4859
|
*/
|
|
4708
4860
|
function parseDate(dateStr) {
|
|
4709
4861
|
const trimmed = dateStr.trim();
|
|
4710
|
-
|
|
4711
|
-
|
|
4712
|
-
|
|
4713
|
-
return
|
|
4862
|
+
const ZERO = /* @__PURE__ */ new Date("0001-01-01T00:00:00Z");
|
|
4863
|
+
if (!trimmed) return ZERO;
|
|
4864
|
+
const month = /^([A-Z][a-z]{2}) (\d{1,2}) (\d{4}) (\d{2}:\d{2})$/.exec(trimmed);
|
|
4865
|
+
return parseTimestamp(month ? `${month[3]}-${MONTH_ABBR[month[1]] ?? "00"}-${month[2].padStart(2, "0")} ${month[4]}` : trimmed) ?? ZERO;
|
|
4714
4866
|
}
|
|
4715
4867
|
/**
|
|
4716
4868
|
* Builds a single EvaluatedRequirement from a group of findings sharing a Check ID.
|
|
@@ -4989,7 +5141,7 @@ function buildRequirement$9(vuln, packageTypes, distro) {
|
|
|
4989
5141
|
label: "default",
|
|
4990
5142
|
data: vuln.description
|
|
4991
5143
|
}];
|
|
4992
|
-
const startTime = vuln.discoveredDate ?
|
|
5144
|
+
const startTime = (vuln.discoveredDate ? parseTimestamp(vuln.discoveredDate) : null) ?? /* @__PURE__ */ new Date("0001-01-01T00:00:00Z");
|
|
4993
5145
|
const results = [createResult(ResultStatus.Failed, void 0, {
|
|
4994
5146
|
codeDesc: formatCodeDesc$2(vuln),
|
|
4995
5147
|
startTime
|
|
@@ -5121,7 +5273,7 @@ function buildRequirement$8(finding, timestamp) {
|
|
|
5121
5273
|
const codeDesc = finding.vulnerability.recommendation ?? "No recommendation available";
|
|
5122
5274
|
const results = [createResult(ResultStatus.Failed, void 0, {
|
|
5123
5275
|
codeDesc,
|
|
5124
|
-
startTime: timestamp ?
|
|
5276
|
+
startTime: (timestamp ? parseTimestamp(timestamp) : null) ?? /* @__PURE__ */ new Date("0001-01-01T00:00:00Z")
|
|
5125
5277
|
})];
|
|
5126
5278
|
const req = createRequirement(finding.matrix, getTitle$1(finding), descriptions, getImpact$5(finding.vulnerability.severity), results, { tags });
|
|
5127
5279
|
req.verificationMethod = VerificationMethodEnum.Automated;
|
|
@@ -5242,7 +5394,7 @@ function formatCodeDesc$1(entry) {
|
|
|
5242
5394
|
/**
|
|
5243
5395
|
* Builds a single EvaluatedRequirement from a group of entries sharing an ID.
|
|
5244
5396
|
*/
|
|
5245
|
-
function buildRequirement$7(entryID, entries) {
|
|
5397
|
+
function buildRequirement$7(entryID, entries, scanTime) {
|
|
5246
5398
|
const rep = entries[0];
|
|
5247
5399
|
const cweIDs = extractCWEs$1(rep);
|
|
5248
5400
|
const nist = mapCWEToNIST(cweIDs, DEFAULT_STATIC_ANALYSIS_NIST_TAGS);
|
|
@@ -5255,7 +5407,10 @@ function buildRequirement$7(entryID, entries) {
|
|
|
5255
5407
|
label: "default",
|
|
5256
5408
|
data: formatDescription(rep)
|
|
5257
5409
|
}];
|
|
5258
|
-
const results = entries.map((entry) => createResult(ResultStatus.Failed, void 0, {
|
|
5410
|
+
const results = entries.map((entry) => createResult(ResultStatus.Failed, void 0, {
|
|
5411
|
+
codeDesc: formatCodeDesc$1(entry),
|
|
5412
|
+
startTime: scanTime
|
|
5413
|
+
}));
|
|
5259
5414
|
const req = createRequirement(entryID, rep.summary, descriptions, severityToImpact(rep.severity), results, { tags });
|
|
5260
5415
|
const controlType = deriveControlTypeFromTags(nist);
|
|
5261
5416
|
if (controlType !== void 0) req.controlType = controlType;
|
|
@@ -5315,6 +5470,7 @@ async function convertJfrogXrayToHdf(input) {
|
|
|
5315
5470
|
const resultsChecksum = await inputChecksum(input);
|
|
5316
5471
|
const parsed = parseJSON(input);
|
|
5317
5472
|
if (!parsed || typeof parsed !== "object" || !Array.isArray(parsed.data)) throw new Error("jfrog-xray: invalid JSON structure");
|
|
5473
|
+
const scanTime = /* @__PURE__ */ new Date();
|
|
5318
5474
|
const { items: limitedEntries, truncated } = limitArray(parsed.data);
|
|
5319
5475
|
/* v8 ignore next -- truncation only triggers with >100K items */
|
|
5320
5476
|
if (truncated) console.warn(`WARNING: Input truncated at ${limitedEntries.length} entries (original: ${parsed.data.length})`);
|
|
@@ -5331,8 +5487,8 @@ async function convertJfrogXrayToHdf(input) {
|
|
|
5331
5487
|
else groups.set(id, [entry]);
|
|
5332
5488
|
}
|
|
5333
5489
|
const requirements = [];
|
|
5334
|
-
for (const [entryID, entries] of groups) requirements.push(buildRequirement$7(entryID, entries));
|
|
5335
|
-
if (requirements.length === 0) requirements.push(buildNoFindingsRequirement("jfrog-xray-no-findings", "JFrog Xray scanned the target artifact and reported zero vulnerable components.",
|
|
5490
|
+
for (const [entryID, entries] of groups) requirements.push(buildRequirement$7(entryID, entries, scanTime));
|
|
5491
|
+
if (requirements.length === 0) requirements.push(buildNoFindingsRequirement("jfrog-xray-no-findings", "JFrog Xray scanned the target artifact and reported zero vulnerable components.", scanTime));
|
|
5336
5492
|
return buildHdfResults({
|
|
5337
5493
|
generatorName: "jfrog-xray-to-hdf",
|
|
5338
5494
|
converterVersion: "1.0.0",
|
|
@@ -5343,7 +5499,7 @@ async function convertJfrogXrayToHdf(input) {
|
|
|
5343
5499
|
name: "JFrog Xray Scan",
|
|
5344
5500
|
type: TargetType.Application
|
|
5345
5501
|
}],
|
|
5346
|
-
timestamp:
|
|
5502
|
+
timestamp: scanTime
|
|
5347
5503
|
});
|
|
5348
5504
|
}
|
|
5349
5505
|
//#endregion
|
|
@@ -5387,7 +5543,7 @@ function vulnMessage(vuln) {
|
|
|
5387
5543
|
/**
|
|
5388
5544
|
* Builds a single EvaluatedRequirement from a NeuVector vulnerability.
|
|
5389
5545
|
*/
|
|
5390
|
-
function buildRequirement$6(vuln) {
|
|
5546
|
+
function buildRequirement$6(vuln, scanTime) {
|
|
5391
5547
|
const cweIDs = extractCWEs(vuln.description);
|
|
5392
5548
|
const nist = mapCWEToNIST(cweIDs, DEFAULT_REMEDIATION_NIST_TAGS);
|
|
5393
5549
|
const tags = {
|
|
@@ -5399,7 +5555,10 @@ function buildRequirement$6(vuln) {
|
|
|
5399
5555
|
label: "default",
|
|
5400
5556
|
data: vuln.description
|
|
5401
5557
|
}];
|
|
5402
|
-
const results = [createResult(ResultStatus.Failed, vulnMessage(vuln), {
|
|
5558
|
+
const results = [createResult(ResultStatus.Failed, vulnMessage(vuln), {
|
|
5559
|
+
codeDesc: "",
|
|
5560
|
+
startTime: scanTime
|
|
5561
|
+
})];
|
|
5403
5562
|
const req = createRequirement(vulnID(vuln), vulnTitle(vuln), descriptions, getImpact$4(vuln), results, { tags });
|
|
5404
5563
|
const controlType = deriveControlTypeFromTags(nist);
|
|
5405
5564
|
if (controlType !== void 0) req.controlType = controlType;
|
|
@@ -5450,17 +5609,17 @@ async function convertNeuvectorToHdf(input) {
|
|
|
5450
5609
|
const { items: limitedVulns, truncated } = limitArray(scan.report.vulnerabilities ?? []);
|
|
5451
5610
|
/* v8 ignore next -- truncation only triggers with >100K items */
|
|
5452
5611
|
if (truncated) console.warn(`WARNING: Input truncated at ${limitedVulns.length} vulnerability items (original: ${scan.report.vulnerabilities.length})`);
|
|
5612
|
+
const scanTime = /* @__PURE__ */ new Date();
|
|
5453
5613
|
const seen = /* @__PURE__ */ new Set();
|
|
5454
5614
|
const requirements = [];
|
|
5455
5615
|
for (const vuln of limitedVulns) {
|
|
5456
5616
|
const id = vulnID(vuln);
|
|
5457
5617
|
if (seen.has(id)) continue;
|
|
5458
5618
|
seen.add(id);
|
|
5459
|
-
requirements.push(buildRequirement$6(vuln));
|
|
5619
|
+
requirements.push(buildRequirement$6(vuln, scanTime));
|
|
5460
5620
|
}
|
|
5461
|
-
const now = /* @__PURE__ */ new Date();
|
|
5462
5621
|
const target = targetNameFromReport(scan.report);
|
|
5463
|
-
if (requirements.length === 0) requirements.push(buildNoFindingsRequirement("neuvector-no-findings", `NeuVector scanned ${target} and reported zero vulnerable components.`,
|
|
5622
|
+
if (requirements.length === 0) requirements.push(buildNoFindingsRequirement("neuvector-no-findings", `NeuVector scanned ${target} and reported zero vulnerable components.`, scanTime));
|
|
5464
5623
|
return buildHdfResults({
|
|
5465
5624
|
generatorName: "neuvector-to-hdf",
|
|
5466
5625
|
converterVersion: "1.0.0",
|
|
@@ -5478,7 +5637,7 @@ async function convertNeuvectorToHdf(input) {
|
|
|
5478
5637
|
registry: scan.report.registry
|
|
5479
5638
|
}
|
|
5480
5639
|
}],
|
|
5481
|
-
timestamp:
|
|
5640
|
+
timestamp: scanTime
|
|
5482
5641
|
});
|
|
5483
5642
|
}
|
|
5484
5643
|
//#endregion
|
|
@@ -5553,7 +5712,7 @@ function buildRequirement$5(desc, vulns, snippetMap, startTimeStr) {
|
|
|
5553
5712
|
const results = limitedVulns.map((vuln) => ({
|
|
5554
5713
|
status: ResultStatus.Failed,
|
|
5555
5714
|
codeDesc: buildCodeDesc$2(vuln, snippetMap),
|
|
5556
|
-
startTime: new Date(
|
|
5715
|
+
startTime: parseTimestamp(startTimeStr) ?? /* @__PURE__ */ new Date()
|
|
5557
5716
|
}));
|
|
5558
5717
|
const req = {
|
|
5559
5718
|
id: desc.classID ?? "unknown",
|
|
@@ -5619,8 +5778,8 @@ async function convertFortifyToHdf(input) {
|
|
|
5619
5778
|
format: "FVDL"
|
|
5620
5779
|
}
|
|
5621
5780
|
};
|
|
5622
|
-
if (createdDate) hdfResult.timestamp = new Date(
|
|
5623
|
-
return
|
|
5781
|
+
if (createdDate) hdfResult.timestamp = parseTimestamp(startTimeStr) ?? /* @__PURE__ */ new Date();
|
|
5782
|
+
return serializeHdf(hdfResult);
|
|
5624
5783
|
}
|
|
5625
5784
|
//#endregion
|
|
5626
5785
|
//#region converters/prisma-to-hdf/typescript/converter.ts
|
|
@@ -5704,7 +5863,7 @@ function makeTitle(rec) {
|
|
|
5704
5863
|
/**
|
|
5705
5864
|
* Builds a single EvaluatedRequirement from a Prisma record.
|
|
5706
5865
|
*/
|
|
5707
|
-
function buildRequirement$4(rec) {
|
|
5866
|
+
function buildRequirement$4(rec, scanTime) {
|
|
5708
5867
|
const id = makeRequirementID(rec);
|
|
5709
5868
|
const title = makeTitle(rec);
|
|
5710
5869
|
const codeDesc = makeCodeDesc(rec);
|
|
@@ -5718,7 +5877,10 @@ function buildRequirement$4(rec) {
|
|
|
5718
5877
|
label: "default",
|
|
5719
5878
|
data: rec.Description
|
|
5720
5879
|
}];
|
|
5721
|
-
const results = [createResult(ResultStatus.Failed, message, {
|
|
5880
|
+
const results = [createResult(ResultStatus.Failed, message, {
|
|
5881
|
+
codeDesc,
|
|
5882
|
+
startTime: scanTime
|
|
5883
|
+
})];
|
|
5722
5884
|
const req = createRequirement(id, title, descriptions, getImpact$3(rec.Severity), results, { tags });
|
|
5723
5885
|
const controlType = deriveControlTypeFromTags(nist);
|
|
5724
5886
|
if (controlType !== void 0) req.controlType = controlType;
|
|
@@ -5781,8 +5943,8 @@ function groupByHostname(records) {
|
|
|
5781
5943
|
/**
|
|
5782
5944
|
* Builds an HDF baseline from all records for a single host.
|
|
5783
5945
|
*/
|
|
5784
|
-
function buildBaseline(hostname, records, resultsChecksum) {
|
|
5785
|
-
return createMinimalBaseline("Prisma Cloud Scan", limitArrayWithWarning(records, "finding").map((rec) => buildRequirement$4(rec)), {
|
|
5946
|
+
function buildBaseline(hostname, records, resultsChecksum, scanTime) {
|
|
5947
|
+
return createMinimalBaseline("Prisma Cloud Scan", limitArrayWithWarning(records, "finding").map((rec) => buildRequirement$4(rec, scanTime)), {
|
|
5786
5948
|
resultsChecksum,
|
|
5787
5949
|
title: `Prisma Cloud Scan (${hostname})`
|
|
5788
5950
|
});
|
|
@@ -5801,16 +5963,17 @@ async function convertPrismaToHdf(input) {
|
|
|
5801
5963
|
for (const col of REQUIRED_COLUMNS) if (!headers.includes(col)) throw new Error(`prisma: missing required CSV column "${col}"`);
|
|
5802
5964
|
const records = parseCsv(input);
|
|
5803
5965
|
const resultsChecksum = await inputChecksum(input);
|
|
5966
|
+
const scanTime = /* @__PURE__ */ new Date();
|
|
5804
5967
|
const baselines = [];
|
|
5805
5968
|
const components = [];
|
|
5806
|
-
if (records.length === 0) baselines.push(createMinimalBaseline("Prisma Cloud Scan", [buildNoFindingsRequirement("prisma-no-findings", "Prisma Cloud scanned the workload and reported zero vulnerable components.",
|
|
5969
|
+
if (records.length === 0) baselines.push(createMinimalBaseline("Prisma Cloud Scan", [buildNoFindingsRequirement("prisma-no-findings", "Prisma Cloud scanned the workload and reported zero vulnerable components.", scanTime)], {
|
|
5807
5970
|
resultsChecksum,
|
|
5808
5971
|
title: "Prisma Cloud Scan"
|
|
5809
5972
|
}));
|
|
5810
5973
|
else {
|
|
5811
5974
|
const hostGroups = groupByHostname(records);
|
|
5812
5975
|
for (const [hostname, hostRecords] of hostGroups) {
|
|
5813
|
-
baselines.push(buildBaseline(hostname, hostRecords, resultsChecksum));
|
|
5976
|
+
baselines.push(buildBaseline(hostname, hostRecords, resultsChecksum, scanTime));
|
|
5814
5977
|
components.push({
|
|
5815
5978
|
name: hostname,
|
|
5816
5979
|
type: TargetType.Host
|
|
@@ -5824,7 +5987,7 @@ async function convertPrismaToHdf(input) {
|
|
|
5824
5987
|
toolFormat: "CSV",
|
|
5825
5988
|
baselines,
|
|
5826
5989
|
components,
|
|
5827
|
-
timestamp:
|
|
5990
|
+
timestamp: scanTime
|
|
5828
5991
|
});
|
|
5829
5992
|
}
|
|
5830
5993
|
//#endregion
|
|
@@ -5840,6 +6003,15 @@ const IMPACT_MAPPING$3 = {
|
|
|
5840
6003
|
function getImpact$2(severity) {
|
|
5841
6004
|
return IMPACT_MAPPING$3[severity.toLowerCase()] ?? .5;
|
|
5842
6005
|
}
|
|
6006
|
+
const NETSPARKER_US_DATETIME = /^\d{2}\/\d{2}\/\d{4} \d{1,2}:\d{2} (AM|PM)$/;
|
|
6007
|
+
function parseNetsparkerTimestamp(s) {
|
|
6008
|
+
const trimmed = s.trim();
|
|
6009
|
+
if (NETSPARKER_US_DATETIME.test(trimmed)) {
|
|
6010
|
+
const d = /* @__PURE__ */ new Date(`${trimmed} GMT`);
|
|
6011
|
+
if (!isNaN(d.getTime())) return d;
|
|
6012
|
+
}
|
|
6013
|
+
return parseTimestamp(s);
|
|
6014
|
+
}
|
|
5843
6015
|
function formatCodeDesc(request) {
|
|
5844
6016
|
const parts = [];
|
|
5845
6017
|
parts.push(`http-request : ${request?.content ?? ""}`);
|
|
@@ -5921,7 +6093,7 @@ function buildRequirement$3(vuln, initiated) {
|
|
|
5921
6093
|
});
|
|
5922
6094
|
const codeDesc = formatCodeDesc(vuln["http-request"]);
|
|
5923
6095
|
const message = formatMessage$1(vuln["http-response"]);
|
|
5924
|
-
const startTime = initiated ?
|
|
6096
|
+
const startTime = (initiated ? parseNetsparkerTimestamp(initiated) : null) ?? /* @__PURE__ */ new Date("0001-01-01T00:00:00Z");
|
|
5925
6097
|
const results = [{
|
|
5926
6098
|
status: ResultStatus.Failed,
|
|
5927
6099
|
codeDesc,
|
|
@@ -5967,8 +6139,7 @@ async function convertNetsparkerToHdf(input) {
|
|
|
5967
6139
|
const targetName = target.url ?? "Unknown";
|
|
5968
6140
|
const requirements = limitedVulns.map((vuln) => buildRequirement$3(vuln, initiated));
|
|
5969
6141
|
if (requirements.length === 0) {
|
|
5970
|
-
const
|
|
5971
|
-
const startTime = isNaN(initiatedDate.getTime()) ? /* @__PURE__ */ new Date() : initiatedDate;
|
|
6142
|
+
const startTime = (initiated ? parseNetsparkerTimestamp(initiated) : null) ?? /* @__PURE__ */ new Date();
|
|
5972
6143
|
requirements.push(buildNoFindingsRequirement("netsparker-no-findings", `${toolName} scanned ${targetName} and reported zero findings.`, startTime));
|
|
5973
6144
|
}
|
|
5974
6145
|
return buildHdfResults({
|
|
@@ -6075,7 +6246,7 @@ function buildRequirement$2(ruleID, finding, startTime) {
|
|
|
6075
6246
|
});
|
|
6076
6247
|
const resultObj = createResult(getStatus$1(finding.checked_items, finding.flagged_items), getMessage(finding.checked_items, finding.flagged_items, finding.items), {
|
|
6077
6248
|
codeDesc: finding.description,
|
|
6078
|
-
startTime: startTime ?
|
|
6249
|
+
startTime: (startTime ? parseTimestamp(startTime) : null) ?? /* @__PURE__ */ new Date("0001-01-01T00:00:00Z")
|
|
6079
6250
|
});
|
|
6080
6251
|
const req = createRequirement(ruleID, finding.description, descriptions, getImpact$1(finding.level), [resultObj], { tags });
|
|
6081
6252
|
const controlType = deriveControlTypeFromTags(nist);
|
|
@@ -6120,7 +6291,7 @@ async function convertScoutsuiteToHdf(input) {
|
|
|
6120
6291
|
provider: report.provider_code ?? report.provider_name
|
|
6121
6292
|
}
|
|
6122
6293
|
}],
|
|
6123
|
-
timestamp: report.last_run.time ?
|
|
6294
|
+
timestamp: report.last_run.time ? parseTimestamp(report.last_run.time) ?? /* @__PURE__ */ new Date() : /* @__PURE__ */ new Date()
|
|
6124
6295
|
});
|
|
6125
6296
|
}
|
|
6126
6297
|
//#endregion
|
|
@@ -6217,7 +6388,7 @@ function buildRequirementFromResult(result, filename) {
|
|
|
6217
6388
|
}];
|
|
6218
6389
|
const scannerName = result.response.service_name;
|
|
6219
6390
|
const startTimeStr = result.response.milestones?.service_started ?? "";
|
|
6220
|
-
const startTime = startTimeStr ?
|
|
6391
|
+
const startTime = (startTimeStr ? parseTimestamp(startTimeStr) : null) ?? /* @__PURE__ */ new Date("0001-01-01T00:00:00Z");
|
|
6221
6392
|
const score = result.result.score;
|
|
6222
6393
|
const status = determineStatus(score);
|
|
6223
6394
|
let results;
|
|
@@ -6305,7 +6476,7 @@ async function convertConveyorToHdf(input) {
|
|
|
6305
6476
|
/** Attribute prefix used by fast-xml-parser — all XML attributes are accessed via `@_name`. */
|
|
6306
6477
|
const A = "@_";
|
|
6307
6478
|
/** Veracode severity level (0-5) to HDF impact mapping. */
|
|
6308
|
-
const IMPACT_MAPPING$2 = new Map([
|
|
6479
|
+
const IMPACT_MAPPING$2 = /* @__PURE__ */ new Map([
|
|
6309
6480
|
["5", .9],
|
|
6310
6481
|
["4", .7],
|
|
6311
6482
|
["3", .5],
|
|
@@ -6327,9 +6498,7 @@ function decodeXmlEntities(s) {
|
|
|
6327
6498
|
/** Parse Veracode timestamp format ("2021-12-29 22:16:36 UTC") to Date. */
|
|
6328
6499
|
function parseVeracodeTimestamp(ts) {
|
|
6329
6500
|
if (!ts) return void 0;
|
|
6330
|
-
|
|
6331
|
-
const d = new Date(decoded.replace(" UTC", "Z").replace(" ", "T"));
|
|
6332
|
-
return isNaN(d.getTime()) ? void 0 : d;
|
|
6501
|
+
return parseTimestamp(decodeXmlEntities(ts).replace(" UTC", "Z").replace(" ", "T")) ?? void 0;
|
|
6333
6502
|
}
|
|
6334
6503
|
/** Format description paragraphs into text. */
|
|
6335
6504
|
function formatDesc(desc) {
|
|
@@ -6647,7 +6816,7 @@ function buildRequirement$1(cs, profiles, createdDateTime) {
|
|
|
6647
6816
|
});
|
|
6648
6817
|
}
|
|
6649
6818
|
const codeDesc = cs.implementationStatus || "No implementation status provided";
|
|
6650
|
-
const startTime = createdDateTime ?
|
|
6819
|
+
const startTime = (createdDateTime ? parseTimestamp(createdDateTime) : null) ?? /* @__PURE__ */ new Date("0001-01-01T00:00:00Z");
|
|
6651
6820
|
const req = createRequirement(id, title, descriptions, impact, [createResult(status, void 0, {
|
|
6652
6821
|
codeDesc,
|
|
6653
6822
|
...startTime ? { startTime } : {}
|
|
@@ -6829,7 +6998,7 @@ function extractSubscriptionID(resourcePath) {
|
|
|
6829
6998
|
/**
|
|
6830
6999
|
* Converts a group of assessments sharing an assessment ID into one EvaluatedRequirement.
|
|
6831
7000
|
*/
|
|
6832
|
-
function buildRequirement(assessmentID, assessments) {
|
|
7001
|
+
function buildRequirement(assessmentID, assessments, scanTime) {
|
|
6833
7002
|
const rep = assessments[0];
|
|
6834
7003
|
const meta = rep.properties.metadata;
|
|
6835
7004
|
const impact = IMPACT_MAPPING$1[meta.severity.toLowerCase()] ?? .5;
|
|
@@ -6850,7 +7019,7 @@ function buildRequirement(assessmentID, assessments) {
|
|
|
6850
7019
|
label: "fix",
|
|
6851
7020
|
data: meta.remediationDescription
|
|
6852
7021
|
});
|
|
6853
|
-
const results = assessments.map(buildResultFromAssessment);
|
|
7022
|
+
const results = assessments.map((a) => buildResultFromAssessment(a, scanTime));
|
|
6854
7023
|
const req = createRequirement(assessmentID, rep.properties.displayName, descriptions, impact, results, { tags });
|
|
6855
7024
|
req.verificationMethod = VerificationMethodEnum.Automated;
|
|
6856
7025
|
return req;
|
|
@@ -6858,10 +7027,13 @@ function buildRequirement(assessmentID, assessments) {
|
|
|
6858
7027
|
/**
|
|
6859
7028
|
* Converts a single assessment into an HDF RequirementResult.
|
|
6860
7029
|
*/
|
|
6861
|
-
function buildResultFromAssessment(a) {
|
|
7030
|
+
function buildResultFromAssessment(a, scanTime) {
|
|
6862
7031
|
const status = mapStatus(a.properties.status.code);
|
|
6863
7032
|
const codeDesc = `Resource: ${a.properties.resourceDetails.id}`;
|
|
6864
|
-
return createResult(status, a.properties.status.description || a.properties.status.cause || void 0, {
|
|
7033
|
+
return createResult(status, a.properties.status.description || a.properties.status.cause || void 0, {
|
|
7034
|
+
codeDesc,
|
|
7035
|
+
startTime: scanTime
|
|
7036
|
+
});
|
|
6865
7037
|
}
|
|
6866
7038
|
/**
|
|
6867
7039
|
* Converts Microsoft Defender for Cloud assessment output to HDF format.
|
|
@@ -6871,6 +7043,7 @@ function buildResultFromAssessment(a) {
|
|
|
6871
7043
|
*/
|
|
6872
7044
|
async function convertMsftDefenderCloudToHdf(input) {
|
|
6873
7045
|
validateInputSize(input, "msft-defender-cloud");
|
|
7046
|
+
const scanTime = /* @__PURE__ */ new Date();
|
|
6874
7047
|
const resultsChecksum = await inputChecksum(input);
|
|
6875
7048
|
const raw = parseJSON(input);
|
|
6876
7049
|
if (!raw || typeof raw !== "object") throw new Error("Invalid Defender for Cloud structure: not a valid JSON object");
|
|
@@ -6885,11 +7058,11 @@ async function convertMsftDefenderCloudToHdf(input) {
|
|
|
6885
7058
|
else groups.set(a.name, [a]);
|
|
6886
7059
|
}
|
|
6887
7060
|
const requirements = [];
|
|
6888
|
-
for (const [assessmentID, assessments] of groups) requirements.push(buildRequirement(assessmentID, assessments));
|
|
7061
|
+
for (const [assessmentID, assessments] of groups) requirements.push(buildRequirement(assessmentID, assessments, scanTime));
|
|
6889
7062
|
const subscriptionID = limitedAssessments.length > 0 ? extractSubscriptionID(limitedAssessments[0].id) : "";
|
|
6890
7063
|
if (requirements.length === 0) {
|
|
6891
7064
|
const targetName = subscriptionID || "Unknown";
|
|
6892
|
-
requirements.push(buildNoFindingsRequirement("msft-defender-cloud-no-findings", `Microsoft Defender for Cloud scanned ${targetName} and reported zero findings.`,
|
|
7065
|
+
requirements.push(buildNoFindingsRequirement("msft-defender-cloud-no-findings", `Microsoft Defender for Cloud scanned ${targetName} and reported zero findings.`, scanTime));
|
|
6893
7066
|
}
|
|
6894
7067
|
const baseline = createMinimalBaseline("Microsoft Defender for Cloud Assessments", requirements, { resultsChecksum });
|
|
6895
7068
|
const components = [];
|
|
@@ -6910,7 +7083,7 @@ async function convertMsftDefenderCloudToHdf(input) {
|
|
|
6910
7083
|
toolFormat: "JSON",
|
|
6911
7084
|
baselines: [baseline],
|
|
6912
7085
|
components,
|
|
6913
|
-
timestamp:
|
|
7086
|
+
timestamp: scanTime
|
|
6914
7087
|
});
|
|
6915
7088
|
}
|
|
6916
7089
|
//#endregion
|
|
@@ -7007,13 +7180,26 @@ function buildTags$1(alert) {
|
|
|
7007
7180
|
return tags;
|
|
7008
7181
|
}
|
|
7009
7182
|
/**
|
|
7183
|
+
* Resolves a clean per-finding source timestamp, falling back to the conversion time.
|
|
7184
|
+
*/
|
|
7185
|
+
function resolveStartTime(alert, scanTime) {
|
|
7186
|
+
const first = alert.firstActivityDateTime ? parseTimestamp(alert.firstActivityDateTime) : null;
|
|
7187
|
+
if (first) return first;
|
|
7188
|
+
const created = alert.createdDateTime ? parseTimestamp(alert.createdDateTime) : null;
|
|
7189
|
+
if (created) return created;
|
|
7190
|
+
return scanTime;
|
|
7191
|
+
}
|
|
7192
|
+
/**
|
|
7010
7193
|
* Converts a single MDE alert to an HDF EvaluatedRequirement.
|
|
7011
7194
|
*/
|
|
7012
|
-
function alertToRequirement(alert) {
|
|
7195
|
+
function alertToRequirement(alert, scanTime) {
|
|
7013
7196
|
const impact = severityToImpact$1(alert.severity);
|
|
7014
7197
|
const status = statusToResultStatus(alert.status, alert.classification);
|
|
7015
7198
|
const codeDesc = formatEvidence(alert.evidence ?? []);
|
|
7016
|
-
const results = [createResult(status, formatMessage(alert), {
|
|
7199
|
+
const results = [createResult(status, formatMessage(alert), {
|
|
7200
|
+
codeDesc,
|
|
7201
|
+
startTime: resolveStartTime(alert, scanTime)
|
|
7202
|
+
})];
|
|
7017
7203
|
const descriptions = [{
|
|
7018
7204
|
label: "default",
|
|
7019
7205
|
data: alert.description
|
|
@@ -7043,10 +7229,11 @@ async function convertMsftDefenderEndpointToHdf(input) {
|
|
|
7043
7229
|
const response = parseJSON(input);
|
|
7044
7230
|
if (!response || typeof response !== "object") throw new Error("Invalid Microsoft Defender for Endpoint structure: not a valid JSON object");
|
|
7045
7231
|
if (!Array.isArray(response.value)) throw new Error("Invalid Microsoft Defender for Endpoint structure: missing or invalid value array");
|
|
7232
|
+
const scanTime = /* @__PURE__ */ new Date();
|
|
7046
7233
|
const { items: limitedAlerts, truncated } = limitArray(response.value);
|
|
7047
7234
|
/* v8 ignore next -- truncation only triggers with >100K items */
|
|
7048
7235
|
if (truncated) console.warn(`WARNING: Input truncated at ${limitedAlerts.length} alert items (original: ${response.value.length})`);
|
|
7049
|
-
const requirements = limitedAlerts.map(alertToRequirement);
|
|
7236
|
+
const requirements = limitedAlerts.map((alert) => alertToRequirement(alert, scanTime));
|
|
7050
7237
|
const seenTargets = /* @__PURE__ */ new Set();
|
|
7051
7238
|
const components = [];
|
|
7052
7239
|
for (const alert of limitedAlerts) {
|
|
@@ -7056,14 +7243,14 @@ async function convertMsftDefenderEndpointToHdf(input) {
|
|
|
7056
7243
|
components.push(target);
|
|
7057
7244
|
}
|
|
7058
7245
|
}
|
|
7059
|
-
if (requirements.length === 0) requirements.push(buildNoFindingsRequirement("msft-defender-endpoint-no-findings", "Microsoft Defender for Endpoint scanned the tenant and reported zero findings.",
|
|
7246
|
+
if (requirements.length === 0) requirements.push(buildNoFindingsRequirement("msft-defender-endpoint-no-findings", "Microsoft Defender for Endpoint scanned the tenant and reported zero findings.", scanTime));
|
|
7060
7247
|
return buildHdfResults({
|
|
7061
7248
|
generatorName: "msft-defender-endpoint-to-hdf",
|
|
7062
7249
|
converterVersion: "1.0.0",
|
|
7063
7250
|
toolName: "Microsoft Defender for Endpoint",
|
|
7064
7251
|
baselines: [createMinimalBaseline("Microsoft Defender for Endpoint Scan", requirements, { resultsChecksum })],
|
|
7065
7252
|
components,
|
|
7066
|
-
timestamp:
|
|
7253
|
+
timestamp: scanTime
|
|
7067
7254
|
});
|
|
7068
7255
|
}
|
|
7069
7256
|
//#endregion
|
|
@@ -7097,7 +7284,7 @@ function wrap(value) {
|
|
|
7097
7284
|
return { "#text": value };
|
|
7098
7285
|
}
|
|
7099
7286
|
/** Map HDF impact (0.0-1.0) to XCCDF severity string. */
|
|
7100
|
-
function impactToSeverity$
|
|
7287
|
+
function impactToSeverity$2(impact) {
|
|
7101
7288
|
if (impact >= .7) return "high";
|
|
7102
7289
|
if (impact >= .4) return "medium";
|
|
7103
7290
|
if (impact >= .1) return "low";
|
|
@@ -7151,7 +7338,7 @@ function buildRuleObj(req) {
|
|
|
7151
7338
|
const ruleId = sanitizeXccdfId("xccdf_hdf_rule_" + req.id + "_rule");
|
|
7152
7339
|
const rule = {
|
|
7153
7340
|
[`${ATTR}id`]: ruleId,
|
|
7154
|
-
[`${ATTR}severity`]: impactToSeverity$
|
|
7341
|
+
[`${ATTR}severity`]: impactToSeverity$2(req.impact),
|
|
7155
7342
|
[`${ATTR}selected`]: "true",
|
|
7156
7343
|
title: wrap(req.title ?? req.id)
|
|
7157
7344
|
};
|
|
@@ -7341,7 +7528,7 @@ function nistTagToControlId(tag) {
|
|
|
7341
7528
|
* Converts a 0.0-1.0 impact value to an OSCAL severity string.
|
|
7342
7529
|
* This is the reverse of extractRiskSeverity.
|
|
7343
7530
|
*/
|
|
7344
|
-
function impactToSeverity(impact) {
|
|
7531
|
+
function impactToSeverity$1(impact) {
|
|
7345
7532
|
if (impact >= .9) return "critical";
|
|
7346
7533
|
if (impact >= .7) return "high";
|
|
7347
7534
|
if (impact >= .4) return "moderate";
|
|
@@ -7496,7 +7683,7 @@ function requirementToFindingSet(req, timestamp) {
|
|
|
7496
7683
|
let risk;
|
|
7497
7684
|
if (req.impact > 0) {
|
|
7498
7685
|
const riskUUID = crypto.randomUUID();
|
|
7499
|
-
const severity = impactToSeverity(req.impact);
|
|
7686
|
+
const severity = impactToSeverity$1(req.impact);
|
|
7500
7687
|
risk = {
|
|
7501
7688
|
uuid: riskUUID,
|
|
7502
7689
|
title: `Risk for ${req.id}`,
|
|
@@ -8614,7 +8801,7 @@ function buildVulnerability(group) {
|
|
|
8614
8801
|
v.flags.push({
|
|
8615
8802
|
label: String(o.justification),
|
|
8616
8803
|
product_ids: pids,
|
|
8617
|
-
date: new Date(o.appliedAt)
|
|
8804
|
+
date: formatTimestampSeconds(new Date(o.appliedAt))
|
|
8618
8805
|
});
|
|
8619
8806
|
}
|
|
8620
8807
|
emitted = true;
|
|
@@ -8678,7 +8865,7 @@ function buildVulnerability(group) {
|
|
|
8678
8865
|
return v;
|
|
8679
8866
|
}
|
|
8680
8867
|
function buildDocument(amendments, converterVersion) {
|
|
8681
|
-
const now = (/* @__PURE__ */ new Date())
|
|
8868
|
+
const now = formatTimestampSeconds(/* @__PURE__ */ new Date());
|
|
8682
8869
|
const publisherName = amendments.appliedBy?.identifier || "HDF Amendments Export";
|
|
8683
8870
|
const trackingId = amendments.amendmentId || "HDF-VEX-EXPORT";
|
|
8684
8871
|
const docVersion = amendments.version || "1";
|
|
@@ -8758,7 +8945,7 @@ async function convertHdfToOpenVex(input, converterVersion) {
|
|
|
8758
8945
|
"@id": await buildDocumentID(input, amendments),
|
|
8759
8946
|
author,
|
|
8760
8947
|
role,
|
|
8761
|
-
timestamp: (earliest ?? /* @__PURE__ */ new Date())
|
|
8948
|
+
timestamp: formatTimestampSeconds(earliest ?? /* @__PURE__ */ new Date()),
|
|
8762
8949
|
version: 1,
|
|
8763
8950
|
statements
|
|
8764
8951
|
};
|
|
@@ -8775,7 +8962,7 @@ function overrideToStatement(o) {
|
|
|
8775
8962
|
"@id": `https://nvd.nist.gov/vuln/detail/${o.requirementId}`
|
|
8776
8963
|
},
|
|
8777
8964
|
status: String(canonical),
|
|
8778
|
-
timestamp: new Date(o.appliedAt)
|
|
8965
|
+
timestamp: formatTimestampSeconds(new Date(o.appliedAt)),
|
|
8779
8966
|
products: productsFor(o)
|
|
8780
8967
|
};
|
|
8781
8968
|
if (canonical === VexStatus.NotAffected) {
|
|
@@ -8942,7 +9129,7 @@ function allMilestonesCompleted(o) {
|
|
|
8942
9129
|
}
|
|
8943
9130
|
function buildMetadata(a, docTime, converterVersion) {
|
|
8944
9131
|
const metadata = {
|
|
8945
|
-
timestamp: docTime
|
|
9132
|
+
timestamp: formatTimestampSeconds(docTime),
|
|
8946
9133
|
tools: [{
|
|
8947
9134
|
vendor: "mitre",
|
|
8948
9135
|
name: "hdf-to-cyclonedx-vex",
|
|
@@ -9655,7 +9842,7 @@ async function convertOscalPoamToHdf(input) {
|
|
|
9655
9842
|
for (const item of poam["poam-items"]) overrides.push(poamItemToOverride(item, riskMap, poam));
|
|
9656
9843
|
const systemRef = poam["import-ssp"]?.href || void 0;
|
|
9657
9844
|
const appliedBy = extractAppliedBy(poam.metadata);
|
|
9658
|
-
|
|
9845
|
+
return serializeHdf({
|
|
9659
9846
|
name: toKebabCase(poam.metadata.title, "oscal-poam"),
|
|
9660
9847
|
overrides,
|
|
9661
9848
|
integrity,
|
|
@@ -9666,8 +9853,7 @@ async function convertOscalPoamToHdf(input) {
|
|
|
9666
9853
|
name: "oscal-poam-to-hdf",
|
|
9667
9854
|
version: "1.0.0"
|
|
9668
9855
|
}
|
|
9669
|
-
};
|
|
9670
|
-
return JSON.stringify(amendments, null, 2);
|
|
9856
|
+
});
|
|
9671
9857
|
}
|
|
9672
9858
|
function buildRiskMap$1(risks) {
|
|
9673
9859
|
const m = /* @__PURE__ */ new Map();
|
|
@@ -9740,8 +9926,8 @@ function poamItemAppliedBy(poam) {
|
|
|
9740
9926
|
}
|
|
9741
9927
|
function poamItemAppliedAt(poam) {
|
|
9742
9928
|
if (poam.metadata["last-modified"]) {
|
|
9743
|
-
const t =
|
|
9744
|
-
if (
|
|
9929
|
+
const t = parseTimestamp(String(poam.metadata["last-modified"]));
|
|
9930
|
+
if (t) return t;
|
|
9745
9931
|
}
|
|
9746
9932
|
return /* @__PURE__ */ new Date();
|
|
9747
9933
|
}
|
|
@@ -9799,18 +9985,23 @@ async function convertOscalSarToHdf(input) {
|
|
|
9799
9985
|
if (!doc["assessment-results"]) throw new Error("oscal-assessment-results: input is not an assessment-results document (root key is not 'assessment-results')");
|
|
9800
9986
|
const sar = doc["assessment-results"];
|
|
9801
9987
|
const meta = extractMetadata(sar.metadata);
|
|
9988
|
+
const scanTime = /* @__PURE__ */ new Date();
|
|
9802
9989
|
const baselines = [];
|
|
9803
9990
|
for (const result of sar.results) {
|
|
9804
|
-
|
|
9991
|
+
if (!result.findings || result.findings.length === 0) {
|
|
9992
|
+
console.warn(`WARNING: Skipping assessment result "${result.title || result.uuid}": no findings (empty result set)`);
|
|
9993
|
+
continue;
|
|
9994
|
+
}
|
|
9995
|
+
const baseline = await resultToEvaluatedBaseline(result, sar, input, scanTime);
|
|
9805
9996
|
baselines.push(baseline);
|
|
9806
9997
|
}
|
|
9807
9998
|
const planRef = sar["import-ap"]?.href || void 0;
|
|
9808
9999
|
let timestamp;
|
|
9809
10000
|
if (meta.lastModified) {
|
|
9810
|
-
const t =
|
|
9811
|
-
if (
|
|
10001
|
+
const t = parseTimestamp(meta.lastModified);
|
|
10002
|
+
if (t) timestamp = t;
|
|
9812
10003
|
}
|
|
9813
|
-
return
|
|
10004
|
+
return serializeHdf({
|
|
9814
10005
|
baselines,
|
|
9815
10006
|
generator: {
|
|
9816
10007
|
name: "oscal-assessment-results-to-hdf",
|
|
@@ -9822,9 +10013,9 @@ async function convertOscalSarToHdf(input) {
|
|
|
9822
10013
|
},
|
|
9823
10014
|
timestamp: timestamp ?? /* @__PURE__ */ new Date(),
|
|
9824
10015
|
planRef
|
|
9825
|
-
}
|
|
10016
|
+
});
|
|
9826
10017
|
}
|
|
9827
|
-
async function resultToEvaluatedBaseline(result, sar, rawInput) {
|
|
10018
|
+
async function resultToEvaluatedBaseline(result, sar, rawInput, scanTime) {
|
|
9828
10019
|
const checksum = await inputChecksum(rawInput);
|
|
9829
10020
|
const obsMap = buildObservationMap(result.observations ?? []);
|
|
9830
10021
|
const riskMap = buildRiskMap(result.risks ?? []);
|
|
@@ -9841,7 +10032,7 @@ async function resultToEvaluatedBaseline(result, sar, rawInput) {
|
|
|
9841
10032
|
}
|
|
9842
10033
|
const requirements = [];
|
|
9843
10034
|
for (const controlId of controlOrder) {
|
|
9844
|
-
const req = findingsToEvaluatedRequirement(controlId, controlMap.get(controlId), obsMap, riskMap, result);
|
|
10035
|
+
const req = findingsToEvaluatedRequirement(controlId, controlMap.get(controlId), obsMap, riskMap, result, scanTime);
|
|
9845
10036
|
requirements.push(req);
|
|
9846
10037
|
}
|
|
9847
10038
|
const baseline = createMinimalBaseline(sarBaselineName(result, sar), requirements, {
|
|
@@ -9853,26 +10044,26 @@ async function resultToEvaluatedBaseline(result, sar, rawInput) {
|
|
|
9853
10044
|
if (result.description) baseline.description = result.description;
|
|
9854
10045
|
return baseline;
|
|
9855
10046
|
}
|
|
9856
|
-
function findingsToEvaluatedRequirement(controlId, findings, obsMap, riskMap, result) {
|
|
10047
|
+
function findingsToEvaluatedRequirement(controlId, findings, obsMap, riskMap, result, scanTime) {
|
|
9857
10048
|
const nistTag = controlIdToNistTag(controlId);
|
|
9858
10049
|
const title = findings[0].title || nistTag;
|
|
9859
10050
|
const impact = sarFindingsImpact(findings, riskMap);
|
|
9860
10051
|
const descriptions = sarBuildDescriptions(findings, obsMap);
|
|
9861
10052
|
const results = [];
|
|
9862
|
-
for (const f of findings) results.push(findingToRequirementResult(f, obsMap, riskMap, result));
|
|
10053
|
+
for (const f of findings) results.push(findingToRequirementResult(f, obsMap, riskMap, result, scanTime));
|
|
9863
10054
|
const req = createRequirement(nistTag, title, descriptions, impact, results, { tags: { nist: [nistTag] } });
|
|
9864
10055
|
const controlType = deriveControlTypeFromTags([nistTag]);
|
|
9865
10056
|
if (controlType !== void 0) req.controlType = controlType;
|
|
9866
10057
|
return req;
|
|
9867
10058
|
}
|
|
9868
|
-
function findingToRequirementResult(f, obsMap, riskMap, result) {
|
|
10059
|
+
function findingToRequirementResult(f, obsMap, riskMap, result, scanTime) {
|
|
9869
10060
|
const status = mapFindingStatus(f);
|
|
9870
10061
|
const codeDesc = buildCodeDesc(f, obsMap);
|
|
9871
10062
|
const message = buildRiskMessage(f, riskMap);
|
|
9872
10063
|
const startTime = parseResultStartTime(result);
|
|
9873
10064
|
return createResult(status, message || void 0, {
|
|
9874
10065
|
codeDesc,
|
|
9875
|
-
startTime: startTime.getTime() > 0 ? startTime :
|
|
10066
|
+
startTime: startTime.getTime() > 0 ? startTime : scanTime
|
|
9876
10067
|
});
|
|
9877
10068
|
}
|
|
9878
10069
|
function extractControlIdFromFinding(f) {
|
|
@@ -9967,8 +10158,8 @@ function buildRiskMap(risks) {
|
|
|
9967
10158
|
}
|
|
9968
10159
|
function parseResultStartTime(result) {
|
|
9969
10160
|
if (result.start) {
|
|
9970
|
-
const t =
|
|
9971
|
-
if (
|
|
10161
|
+
const t = parseTimestamp(String(result.start));
|
|
10162
|
+
if (t) return t;
|
|
9972
10163
|
}
|
|
9973
10164
|
return /* @__PURE__ */ new Date(0);
|
|
9974
10165
|
}
|