@mitre/hdf-converters 3.7.0 → 3.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1016 -39
- package/dist/index.js.map +1 -1
- package/package.json +9 -9
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { n as detectFormat, o as isCheckovTestCaseName, r as detectConverter, t as registerAllFingerprints } from "./register-all-BEuKQGEZ.js";
|
|
2
2
|
import { flattenOverlays } from "@mitre/hdf-parsers";
|
|
3
3
|
import { buildCsv, buildXml, canonicalJson, computeCvssScore, computeEffectiveStatus, cvssScoreToSeverity, encodeBase64Utf8, formatJsonNumber, formatTimestamp, formatTimestampSeconds, governingStatusOverride, impactToSeverity, isUnratedSeverity, normalizeHdfTimestamps, parseCsv, parseJSON, parsePurl, parseTimestamp, parseXml, parseXmlWithArrays, replaceHtmlTags, roundImpact, severityToImpact, severityToImpactWithAliases, sha256, stripHtml, stripHtml as stripHTML, trimUtcFraction, validateInputSize, worstStatus, xmlSafeText } from "@mitre/hdf-utilities";
|
|
4
|
-
import { Applicability, AuthorizationStatus, CVSSSeverity, CategorizationLevel, ControlType, Ecosystem, EvidenceType, HashAlgorithm, IdentityType, Justification, MilestoneStatus, OverrideType, PlanType, ResultStatus, Severity, TargetType, VerificationMethodEnum, Version, affectedPackageSchema, amendmentsSchema, bomSchema, commonSchema, comparisonSchema, componentSchema, createDescription, createMinimalBaseline, createRequirement, createResult, cvssSchema, dataFlowSchema, epssSchema, eventsSchema, extensionsSchema, hdfAmendmentsSchema, hdfBaselineSchema, hdfComparisonSchema, hdfEvidencePackageSchema, hdfPlanSchema, hdfRequirementChangeEventSchema, hdfResultsSchema, hdfSystemSchema, kevSchema, parameterSchema, planSchema, platformSchema, resultSchema, runnerSchema, severityToImpact as severityToImpact$1, statisticsSchema, systemSchema, targetSchema } from "@mitre/hdf-schema";
|
|
4
|
+
import { Applicability, AuthorizationStatus, CVSSSeverity, CategorizationLevel, CloudProvider, ControlType, Ecosystem, EvidenceType, HashAlgorithm, IdentityType, Justification, MilestoneStatus, OverrideType, PlanType, ResultStatus, Severity, TargetType, VerificationMethodEnum, Version, affectedPackageSchema, amendmentsSchema, bomSchema, commonSchema, comparisonSchema, componentSchema, createDescription, createMinimalBaseline, createRequirement, createResult, cvssSchema, dataFlowSchema, epssSchema, eventsSchema, extensionsSchema, hdfAmendmentsSchema, hdfBaselineSchema, hdfComparisonSchema, hdfEvidencePackageSchema, hdfPlanSchema, hdfRequirementChangeEventSchema, hdfResultsSchema, hdfSystemSchema, kevSchema, parameterSchema, planSchema, platformSchema, resultSchema, runnerSchema, severityToImpact as severityToImpact$1, statisticsSchema, systemSchema, targetSchema } from "@mitre/hdf-schema";
|
|
5
5
|
import { DEFAULT_COMPONENT_MANAGEMENT_NIST_TAGS, DEFAULT_CONFIG_MANAGEMENT_NIST_TAGS, DEFAULT_REMEDIATION_NIST_TAGS, DEFAULT_STATIC_ANALYSIS_NIST_TAGS, DEFAULT_STATIC_ANALYSIS_NIST_TAGS as DEFAULT_STATIC_ANALYSIS_NIST_TAGS$1, SUPPORTED_NIST_REVISIONS, awsConfigMappedRevisions, getAwsConfigNistControlByIdentifier, getAwsConfigNistControlByName, getAwsConfigNistControlsBySubstring, getCCINistMappings, getCheckovCciNistMapping, getCurrentNistRevision, getCweNistControl, getHipcheckNistControls, getNessusNistControl, getNiktoNistControl, getOwaspNistControl, getScoutsuiteNistControl, isNistStrict, nistExists, nistToCci, normalizeNistId } from "@mitre/hdf-mappings";
|
|
6
6
|
import Ajv from "ajv";
|
|
7
7
|
import addFormats from "ajv-formats";
|
|
@@ -5292,16 +5292,25 @@ function calculateImpact(item, isCompliance) {
|
|
|
5292
5292
|
}
|
|
5293
5293
|
return severityToImpactWithAliases(item["severity"], NESSUS_ALIASES, 0);
|
|
5294
5294
|
}
|
|
5295
|
+
/**
|
|
5296
|
+
* Substitutes the shared remediation fallback when a lookup resolved nothing, so
|
|
5297
|
+
* the CCI path and the plugin-family path behave alike. A finding the mapping
|
|
5298
|
+
* tables do not cover is still a flaw to remediate, and an empty nist tag would
|
|
5299
|
+
* drop it from every NIST-based view.
|
|
5300
|
+
*/
|
|
5301
|
+
function orDefaultNist(controls) {
|
|
5302
|
+
return controls.length > 0 ? controls : [...DEFAULT_REMEDIATION_NIST_TAGS$1];
|
|
5303
|
+
}
|
|
5295
5304
|
function buildTags$3(item, isCompliance) {
|
|
5296
5305
|
const tags = { rid: isCompliance ? parseComplianceRef(item["compliance-reference"], "Rule-ID").join(",") : item["pluginID"] };
|
|
5297
5306
|
if (isCompliance && item["compliance-reference"]) {
|
|
5298
5307
|
const cciTags = parseComplianceRef(item["compliance-reference"], "CCI");
|
|
5299
5308
|
tags.cci = cciTags;
|
|
5300
5309
|
const mappedControls = cciTags.flatMap((cci) => getCCINistMappings(cci) ?? []);
|
|
5301
|
-
tags.nist = [...new Set(mappedControls)].sort();
|
|
5310
|
+
tags.nist = orDefaultNist([...new Set(mappedControls)].sort());
|
|
5302
5311
|
} else {
|
|
5303
5312
|
const nistControls = getNessusNistControl(item["pluginFamily"], item["pluginID"]);
|
|
5304
|
-
tags.nist = nistControls ? nistControls.split("|") : [];
|
|
5313
|
+
tags.nist = orDefaultNist(nistControls ? nistControls.split("|") : []);
|
|
5305
5314
|
}
|
|
5306
5315
|
if (isCompliance && item["compliance-reference"]) {
|
|
5307
5316
|
const stigId = parseComplianceRef(item["compliance-reference"], "STIG-ID").join(",");
|
|
@@ -14173,6 +14182,15 @@ var oscal_vocabulary_default = {
|
|
|
14173
14182
|
"hdfField": "baselines[].requirements[].id, overrides[].requirementId",
|
|
14174
14183
|
"legacy": true
|
|
14175
14184
|
},
|
|
14185
|
+
{
|
|
14186
|
+
"name": "requirement-title",
|
|
14187
|
+
"ns": "https://mitre.github.io/hdf-libs/ns/oscal",
|
|
14188
|
+
"objects": ["assessment-results.results[].findings[]"],
|
|
14189
|
+
"meaning": "The exact HDF requirement title the finding reports. Requirement_Core.title is prose that may carry line breaks (§2); the finding title is display text OSCAL types single-line, so the exact title is preserved here while the display title is normalized (§4.3).",
|
|
14190
|
+
"valueFormat": "String (§1.7)",
|
|
14191
|
+
"hdfField": "baselines[].requirements[].title",
|
|
14192
|
+
"legacy": false
|
|
14193
|
+
},
|
|
14176
14194
|
{
|
|
14177
14195
|
"name": "baseline-version",
|
|
14178
14196
|
"ns": "https://mitre.github.io/hdf-libs/ns/oscal",
|
|
@@ -14182,6 +14200,24 @@ var oscal_vocabulary_default = {
|
|
|
14182
14200
|
"hdfField": "baselines[].version",
|
|
14183
14201
|
"legacy": true
|
|
14184
14202
|
},
|
|
14203
|
+
{
|
|
14204
|
+
"name": "baseline-name",
|
|
14205
|
+
"ns": "https://mitre.github.io/hdf-libs/ns/oscal",
|
|
14206
|
+
"objects": ["assessment-results.results[]"],
|
|
14207
|
+
"meaning": "The exact HDF baseline name the result reports, so an HDF-produced SAR round-trips the name that hdf-diff and baseline references key on. The result title is display text and is not injective, so two baselines sharing a title would otherwise collide (§4.3, §4.5).",
|
|
14208
|
+
"valueFormat": "String (§1.7)",
|
|
14209
|
+
"hdfField": "baselines[].name",
|
|
14210
|
+
"legacy": false
|
|
14211
|
+
},
|
|
14212
|
+
{
|
|
14213
|
+
"name": "baseline-title",
|
|
14214
|
+
"ns": "https://mitre.github.io/hdf-libs/ns/oscal",
|
|
14215
|
+
"objects": ["assessment-results.results[]"],
|
|
14216
|
+
"meaning": "The exact HDF baseline title the result reports, distinct from the baseline name (baseline-name). The result title is display text OSCAL types single-line, so a title carrying a line terminator is preserved here while the display title is normalized (§4.3).",
|
|
14217
|
+
"valueFormat": "String (§1.7)",
|
|
14218
|
+
"hdfField": "baselines[].title",
|
|
14219
|
+
"legacy": false
|
|
14220
|
+
},
|
|
14185
14221
|
{
|
|
14186
14222
|
"name": "nist",
|
|
14187
14223
|
"ns": "https://mitre.github.io/hdf-libs/ns/oscal",
|
|
@@ -14353,6 +14389,393 @@ var oscal_vocabulary_default = {
|
|
|
14353
14389
|
"hdfField": null,
|
|
14354
14390
|
"legacy": false
|
|
14355
14391
|
},
|
|
14392
|
+
{
|
|
14393
|
+
"name": "component-name",
|
|
14394
|
+
"ns": "https://mitre.github.io/hdf-libs/ns/oscal",
|
|
14395
|
+
"objects": ["assessment-results.results[].observations[].subjects[]", "assessment-results.results[].local-definitions.components[]"],
|
|
14396
|
+
"meaning": "The exact HDF component name the assessment subject reconstitutes. The subject title is display text OSCAL types single-line, so a name carrying a line terminator is preserved here while the display title is normalized (§4.3).",
|
|
14397
|
+
"valueFormat": "String (§1.7)",
|
|
14398
|
+
"hdfField": "components[].name",
|
|
14399
|
+
"legacy": false
|
|
14400
|
+
},
|
|
14401
|
+
{
|
|
14402
|
+
"name": "component-description",
|
|
14403
|
+
"ns": "https://mitre.github.io/hdf-libs/ns/oscal",
|
|
14404
|
+
"objects": ["assessment-results.results[].observations[].subjects[]", "assessment-results.results[].local-definitions.components[]"],
|
|
14405
|
+
"meaning": "The description of the component the assessment subject reconstitutes.",
|
|
14406
|
+
"valueFormat": "String (§1.7)",
|
|
14407
|
+
"hdfField": "components[].description",
|
|
14408
|
+
"legacy": false
|
|
14409
|
+
},
|
|
14410
|
+
{
|
|
14411
|
+
"name": "component-hostname",
|
|
14412
|
+
"ns": "https://mitre.github.io/hdf-libs/ns/oscal",
|
|
14413
|
+
"objects": ["assessment-results.results[].observations[].subjects[]", "assessment-results.results[].local-definitions.components[]"],
|
|
14414
|
+
"meaning": "A host component's short OS-reported hostname.",
|
|
14415
|
+
"valueFormat": "String (§1.7)",
|
|
14416
|
+
"hdfField": "components[].hostname",
|
|
14417
|
+
"legacy": false
|
|
14418
|
+
},
|
|
14419
|
+
{
|
|
14420
|
+
"name": "component-fqdn",
|
|
14421
|
+
"ns": "https://mitre.github.io/hdf-libs/ns/oscal",
|
|
14422
|
+
"objects": ["assessment-results.results[].observations[].subjects[]", "assessment-results.results[].local-definitions.components[]"],
|
|
14423
|
+
"meaning": "A host component's fully qualified domain name.",
|
|
14424
|
+
"valueFormat": "String (§1.7)",
|
|
14425
|
+
"hdfField": "components[].fqdn",
|
|
14426
|
+
"legacy": false
|
|
14427
|
+
},
|
|
14428
|
+
{
|
|
14429
|
+
"name": "component-domain",
|
|
14430
|
+
"ns": "https://mitre.github.io/hdf-libs/ns/oscal",
|
|
14431
|
+
"objects": ["assessment-results.results[].observations[].subjects[]", "assessment-results.results[].local-definitions.components[]"],
|
|
14432
|
+
"meaning": "A host component's directory domain.",
|
|
14433
|
+
"valueFormat": "String (§1.7)",
|
|
14434
|
+
"hdfField": "components[].domain",
|
|
14435
|
+
"legacy": false
|
|
14436
|
+
},
|
|
14437
|
+
{
|
|
14438
|
+
"name": "component-ip-address",
|
|
14439
|
+
"ns": "https://mitre.github.io/hdf-libs/ns/oscal",
|
|
14440
|
+
"objects": ["assessment-results.results[].observations[].subjects[]", "assessment-results.results[].local-definitions.components[]"],
|
|
14441
|
+
"meaning": "A host component's IP address.",
|
|
14442
|
+
"valueFormat": "String (§1.7)",
|
|
14443
|
+
"hdfField": "components[].ipAddress",
|
|
14444
|
+
"legacy": false
|
|
14445
|
+
},
|
|
14446
|
+
{
|
|
14447
|
+
"name": "component-mac-address",
|
|
14448
|
+
"ns": "https://mitre.github.io/hdf-libs/ns/oscal",
|
|
14449
|
+
"objects": ["assessment-results.results[].observations[].subjects[]", "assessment-results.results[].local-definitions.components[]"],
|
|
14450
|
+
"meaning": "A host component's MAC address.",
|
|
14451
|
+
"valueFormat": "String (§1.7)",
|
|
14452
|
+
"hdfField": "components[].macAddress",
|
|
14453
|
+
"legacy": false
|
|
14454
|
+
},
|
|
14455
|
+
{
|
|
14456
|
+
"name": "component-os-name",
|
|
14457
|
+
"ns": "https://mitre.github.io/hdf-libs/ns/oscal",
|
|
14458
|
+
"objects": ["assessment-results.results[].observations[].subjects[]", "assessment-results.results[].local-definitions.components[]"],
|
|
14459
|
+
"meaning": "A host component's operating system name.",
|
|
14460
|
+
"valueFormat": "String (§1.7)",
|
|
14461
|
+
"hdfField": "components[].osName",
|
|
14462
|
+
"legacy": false
|
|
14463
|
+
},
|
|
14464
|
+
{
|
|
14465
|
+
"name": "component-os-version",
|
|
14466
|
+
"ns": "https://mitre.github.io/hdf-libs/ns/oscal",
|
|
14467
|
+
"objects": ["assessment-results.results[].observations[].subjects[]", "assessment-results.results[].local-definitions.components[]"],
|
|
14468
|
+
"meaning": "A host component's operating system version.",
|
|
14469
|
+
"valueFormat": "String (§1.7)",
|
|
14470
|
+
"hdfField": "components[].osVersion",
|
|
14471
|
+
"legacy": false
|
|
14472
|
+
},
|
|
14473
|
+
{
|
|
14474
|
+
"name": "component-image-id",
|
|
14475
|
+
"ns": "https://mitre.github.io/hdf-libs/ns/oscal",
|
|
14476
|
+
"objects": ["assessment-results.results[].observations[].subjects[]", "assessment-results.results[].local-definitions.components[]"],
|
|
14477
|
+
"meaning": "A container image component's image id.",
|
|
14478
|
+
"valueFormat": "String (§1.7)",
|
|
14479
|
+
"hdfField": "components[].imageId",
|
|
14480
|
+
"legacy": false
|
|
14481
|
+
},
|
|
14482
|
+
{
|
|
14483
|
+
"name": "component-registry",
|
|
14484
|
+
"ns": "https://mitre.github.io/hdf-libs/ns/oscal",
|
|
14485
|
+
"objects": ["assessment-results.results[].observations[].subjects[]", "assessment-results.results[].local-definitions.components[]"],
|
|
14486
|
+
"meaning": "A container image component's registry.",
|
|
14487
|
+
"valueFormat": "String (§1.7)",
|
|
14488
|
+
"hdfField": "components[].registry",
|
|
14489
|
+
"legacy": false
|
|
14490
|
+
},
|
|
14491
|
+
{
|
|
14492
|
+
"name": "component-repository",
|
|
14493
|
+
"ns": "https://mitre.github.io/hdf-libs/ns/oscal",
|
|
14494
|
+
"objects": ["assessment-results.results[].observations[].subjects[]", "assessment-results.results[].local-definitions.components[]"],
|
|
14495
|
+
"meaning": "A container image component's repository.",
|
|
14496
|
+
"valueFormat": "String (§1.7)",
|
|
14497
|
+
"hdfField": "components[].repository",
|
|
14498
|
+
"legacy": false
|
|
14499
|
+
},
|
|
14500
|
+
{
|
|
14501
|
+
"name": "component-tag",
|
|
14502
|
+
"ns": "https://mitre.github.io/hdf-libs/ns/oscal",
|
|
14503
|
+
"objects": ["assessment-results.results[].observations[].subjects[]", "assessment-results.results[].local-definitions.components[]"],
|
|
14504
|
+
"meaning": "A container image component's tag.",
|
|
14505
|
+
"valueFormat": "String (§1.7)",
|
|
14506
|
+
"hdfField": "components[].tag",
|
|
14507
|
+
"legacy": false
|
|
14508
|
+
},
|
|
14509
|
+
{
|
|
14510
|
+
"name": "component-container-id",
|
|
14511
|
+
"ns": "https://mitre.github.io/hdf-libs/ns/oscal",
|
|
14512
|
+
"objects": ["assessment-results.results[].observations[].subjects[]", "assessment-results.results[].local-definitions.components[]"],
|
|
14513
|
+
"meaning": "A container instance component's running container id.",
|
|
14514
|
+
"valueFormat": "String (§1.7)",
|
|
14515
|
+
"hdfField": "components[].containerId",
|
|
14516
|
+
"legacy": false
|
|
14517
|
+
},
|
|
14518
|
+
{
|
|
14519
|
+
"name": "component-image",
|
|
14520
|
+
"ns": "https://mitre.github.io/hdf-libs/ns/oscal",
|
|
14521
|
+
"objects": ["assessment-results.results[].observations[].subjects[]", "assessment-results.results[].local-definitions.components[]"],
|
|
14522
|
+
"meaning": "A container instance component's source image.",
|
|
14523
|
+
"valueFormat": "String (§1.7)",
|
|
14524
|
+
"hdfField": "components[].image",
|
|
14525
|
+
"legacy": false
|
|
14526
|
+
},
|
|
14527
|
+
{
|
|
14528
|
+
"name": "component-runtime",
|
|
14529
|
+
"ns": "https://mitre.github.io/hdf-libs/ns/oscal",
|
|
14530
|
+
"objects": ["assessment-results.results[].observations[].subjects[]", "assessment-results.results[].local-definitions.components[]"],
|
|
14531
|
+
"meaning": "A container instance component's runtime.",
|
|
14532
|
+
"valueFormat": "String (§1.7)",
|
|
14533
|
+
"hdfField": "components[].runtime",
|
|
14534
|
+
"legacy": false
|
|
14535
|
+
},
|
|
14536
|
+
{
|
|
14537
|
+
"name": "component-platform-type",
|
|
14538
|
+
"ns": "https://mitre.github.io/hdf-libs/ns/oscal",
|
|
14539
|
+
"objects": ["assessment-results.results[].observations[].subjects[]", "assessment-results.results[].local-definitions.components[]"],
|
|
14540
|
+
"meaning": "A container platform component's platform type.",
|
|
14541
|
+
"valueFormat": "String (§1.7)",
|
|
14542
|
+
"hdfField": "components[].platformType",
|
|
14543
|
+
"legacy": false
|
|
14544
|
+
},
|
|
14545
|
+
{
|
|
14546
|
+
"name": "component-cluster-name",
|
|
14547
|
+
"ns": "https://mitre.github.io/hdf-libs/ns/oscal",
|
|
14548
|
+
"objects": ["assessment-results.results[].observations[].subjects[]", "assessment-results.results[].local-definitions.components[]"],
|
|
14549
|
+
"meaning": "A container platform component's cluster name.",
|
|
14550
|
+
"valueFormat": "String (§1.7)",
|
|
14551
|
+
"hdfField": "components[].clusterName",
|
|
14552
|
+
"legacy": false
|
|
14553
|
+
},
|
|
14554
|
+
{
|
|
14555
|
+
"name": "component-namespace",
|
|
14556
|
+
"ns": "https://mitre.github.io/hdf-libs/ns/oscal",
|
|
14557
|
+
"objects": ["assessment-results.results[].observations[].subjects[]", "assessment-results.results[].local-definitions.components[]"],
|
|
14558
|
+
"meaning": "A container platform component's namespace.",
|
|
14559
|
+
"valueFormat": "String (§1.7)",
|
|
14560
|
+
"hdfField": "components[].namespace",
|
|
14561
|
+
"legacy": false
|
|
14562
|
+
},
|
|
14563
|
+
{
|
|
14564
|
+
"name": "component-version",
|
|
14565
|
+
"ns": "https://mitre.github.io/hdf-libs/ns/oscal",
|
|
14566
|
+
"objects": ["assessment-results.results[].observations[].subjects[]", "assessment-results.results[].local-definitions.components[]"],
|
|
14567
|
+
"meaning": "A component's version (container platform, application, artifact, database, aiModel or dataset).",
|
|
14568
|
+
"valueFormat": "String (§1.7)",
|
|
14569
|
+
"hdfField": "components[].version",
|
|
14570
|
+
"legacy": false
|
|
14571
|
+
},
|
|
14572
|
+
{
|
|
14573
|
+
"name": "component-provider",
|
|
14574
|
+
"ns": "https://mitre.github.io/hdf-libs/ns/oscal",
|
|
14575
|
+
"objects": ["assessment-results.results[].observations[].subjects[]", "assessment-results.results[].local-definitions.components[]"],
|
|
14576
|
+
"meaning": "A cloud account or cloud resource component's cloud provider.",
|
|
14577
|
+
"valueFormat": "An HDF cloud provider value (aws, azure, gcp, oci, other)",
|
|
14578
|
+
"hdfField": "components[].provider",
|
|
14579
|
+
"legacy": false
|
|
14580
|
+
},
|
|
14581
|
+
{
|
|
14582
|
+
"name": "component-account-id",
|
|
14583
|
+
"ns": "https://mitre.github.io/hdf-libs/ns/oscal",
|
|
14584
|
+
"objects": ["assessment-results.results[].observations[].subjects[]", "assessment-results.results[].local-definitions.components[]"],
|
|
14585
|
+
"meaning": "A cloud account component's account identifier.",
|
|
14586
|
+
"valueFormat": "String (§1.7)",
|
|
14587
|
+
"hdfField": "components[].accountId",
|
|
14588
|
+
"legacy": false
|
|
14589
|
+
},
|
|
14590
|
+
{
|
|
14591
|
+
"name": "component-region",
|
|
14592
|
+
"ns": "https://mitre.github.io/hdf-libs/ns/oscal",
|
|
14593
|
+
"objects": ["assessment-results.results[].observations[].subjects[]", "assessment-results.results[].local-definitions.components[]"],
|
|
14594
|
+
"meaning": "A cloud account or cloud resource component's region.",
|
|
14595
|
+
"valueFormat": "String (§1.7)",
|
|
14596
|
+
"hdfField": "components[].region",
|
|
14597
|
+
"legacy": false
|
|
14598
|
+
},
|
|
14599
|
+
{
|
|
14600
|
+
"name": "component-resource-type",
|
|
14601
|
+
"ns": "https://mitre.github.io/hdf-libs/ns/oscal",
|
|
14602
|
+
"objects": ["assessment-results.results[].observations[].subjects[]", "assessment-results.results[].local-definitions.components[]"],
|
|
14603
|
+
"meaning": "A cloud resource component's resource type.",
|
|
14604
|
+
"valueFormat": "String (§1.7)",
|
|
14605
|
+
"hdfField": "components[].resourceType",
|
|
14606
|
+
"legacy": false
|
|
14607
|
+
},
|
|
14608
|
+
{
|
|
14609
|
+
"name": "component-resource-id",
|
|
14610
|
+
"ns": "https://mitre.github.io/hdf-libs/ns/oscal",
|
|
14611
|
+
"objects": ["assessment-results.results[].observations[].subjects[]", "assessment-results.results[].local-definitions.components[]"],
|
|
14612
|
+
"meaning": "A cloud resource component's provider-specific resource id.",
|
|
14613
|
+
"valueFormat": "String (§1.7)",
|
|
14614
|
+
"hdfField": "components[].resourceId",
|
|
14615
|
+
"legacy": false
|
|
14616
|
+
},
|
|
14617
|
+
{
|
|
14618
|
+
"name": "component-arn",
|
|
14619
|
+
"ns": "https://mitre.github.io/hdf-libs/ns/oscal",
|
|
14620
|
+
"objects": ["assessment-results.results[].observations[].subjects[]", "assessment-results.results[].local-definitions.components[]"],
|
|
14621
|
+
"meaning": "A cloud resource component's Amazon Resource Name.",
|
|
14622
|
+
"valueFormat": "String (§1.7)",
|
|
14623
|
+
"hdfField": "components[].arn",
|
|
14624
|
+
"legacy": false
|
|
14625
|
+
},
|
|
14626
|
+
{
|
|
14627
|
+
"name": "component-url",
|
|
14628
|
+
"ns": "https://mitre.github.io/hdf-libs/ns/oscal",
|
|
14629
|
+
"objects": ["assessment-results.results[].observations[].subjects[]", "assessment-results.results[].local-definitions.components[]"],
|
|
14630
|
+
"meaning": "A repository or application component's URL.",
|
|
14631
|
+
"valueFormat": "String (§1.7), a URL",
|
|
14632
|
+
"hdfField": "components[].url",
|
|
14633
|
+
"legacy": false
|
|
14634
|
+
},
|
|
14635
|
+
{
|
|
14636
|
+
"name": "component-branch",
|
|
14637
|
+
"ns": "https://mitre.github.io/hdf-libs/ns/oscal",
|
|
14638
|
+
"objects": ["assessment-results.results[].observations[].subjects[]", "assessment-results.results[].local-definitions.components[]"],
|
|
14639
|
+
"meaning": "A repository component's scanned branch.",
|
|
14640
|
+
"valueFormat": "String (§1.7)",
|
|
14641
|
+
"hdfField": "components[].branch",
|
|
14642
|
+
"legacy": false
|
|
14643
|
+
},
|
|
14644
|
+
{
|
|
14645
|
+
"name": "component-commit",
|
|
14646
|
+
"ns": "https://mitre.github.io/hdf-libs/ns/oscal",
|
|
14647
|
+
"objects": ["assessment-results.results[].observations[].subjects[]", "assessment-results.results[].local-definitions.components[]"],
|
|
14648
|
+
"meaning": "A repository component's scanned commit SHA.",
|
|
14649
|
+
"valueFormat": "String (§1.7)",
|
|
14650
|
+
"hdfField": "components[].commit",
|
|
14651
|
+
"legacy": false
|
|
14652
|
+
},
|
|
14653
|
+
{
|
|
14654
|
+
"name": "component-environment",
|
|
14655
|
+
"ns": "https://mitre.github.io/hdf-libs/ns/oscal",
|
|
14656
|
+
"objects": ["assessment-results.results[].observations[].subjects[]", "assessment-results.results[].local-definitions.components[]"],
|
|
14657
|
+
"meaning": "An application component's environment.",
|
|
14658
|
+
"valueFormat": "String (§1.7)",
|
|
14659
|
+
"hdfField": "components[].environment",
|
|
14660
|
+
"legacy": false
|
|
14661
|
+
},
|
|
14662
|
+
{
|
|
14663
|
+
"name": "component-package-manager",
|
|
14664
|
+
"ns": "https://mitre.github.io/hdf-libs/ns/oscal",
|
|
14665
|
+
"objects": ["assessment-results.results[].observations[].subjects[]", "assessment-results.results[].local-definitions.components[]"],
|
|
14666
|
+
"meaning": "An artifact component's package manager.",
|
|
14667
|
+
"valueFormat": "String (§1.7)",
|
|
14668
|
+
"hdfField": "components[].packageManager",
|
|
14669
|
+
"legacy": false
|
|
14670
|
+
},
|
|
14671
|
+
{
|
|
14672
|
+
"name": "component-package-name",
|
|
14673
|
+
"ns": "https://mitre.github.io/hdf-libs/ns/oscal",
|
|
14674
|
+
"objects": ["assessment-results.results[].observations[].subjects[]", "assessment-results.results[].local-definitions.components[]"],
|
|
14675
|
+
"meaning": "An artifact component's package name.",
|
|
14676
|
+
"valueFormat": "String (§1.7)",
|
|
14677
|
+
"hdfField": "components[].packageName",
|
|
14678
|
+
"legacy": false
|
|
14679
|
+
},
|
|
14680
|
+
{
|
|
14681
|
+
"name": "component-cidr",
|
|
14682
|
+
"ns": "https://mitre.github.io/hdf-libs/ns/oscal",
|
|
14683
|
+
"objects": ["assessment-results.results[].observations[].subjects[]", "assessment-results.results[].local-definitions.components[]"],
|
|
14684
|
+
"meaning": "A network component's CIDR block.",
|
|
14685
|
+
"valueFormat": "String (§1.7)",
|
|
14686
|
+
"hdfField": "components[].cidr",
|
|
14687
|
+
"legacy": false
|
|
14688
|
+
},
|
|
14689
|
+
{
|
|
14690
|
+
"name": "component-gateway",
|
|
14691
|
+
"ns": "https://mitre.github.io/hdf-libs/ns/oscal",
|
|
14692
|
+
"objects": ["assessment-results.results[].observations[].subjects[]", "assessment-results.results[].local-definitions.components[]"],
|
|
14693
|
+
"meaning": "A network component's gateway address.",
|
|
14694
|
+
"valueFormat": "String (§1.7)",
|
|
14695
|
+
"hdfField": "components[].gateway",
|
|
14696
|
+
"legacy": false
|
|
14697
|
+
},
|
|
14698
|
+
{
|
|
14699
|
+
"name": "component-engine",
|
|
14700
|
+
"ns": "https://mitre.github.io/hdf-libs/ns/oscal",
|
|
14701
|
+
"objects": ["assessment-results.results[].observations[].subjects[]", "assessment-results.results[].local-definitions.components[]"],
|
|
14702
|
+
"meaning": "A database component's engine.",
|
|
14703
|
+
"valueFormat": "String (§1.7)",
|
|
14704
|
+
"hdfField": "components[].engine",
|
|
14705
|
+
"legacy": false
|
|
14706
|
+
},
|
|
14707
|
+
{
|
|
14708
|
+
"name": "component-host",
|
|
14709
|
+
"ns": "https://mitre.github.io/hdf-libs/ns/oscal",
|
|
14710
|
+
"objects": ["assessment-results.results[].observations[].subjects[]", "assessment-results.results[].local-definitions.components[]"],
|
|
14711
|
+
"meaning": "A database component's host.",
|
|
14712
|
+
"valueFormat": "String (§1.7)",
|
|
14713
|
+
"hdfField": "components[].host",
|
|
14714
|
+
"legacy": false
|
|
14715
|
+
},
|
|
14716
|
+
{
|
|
14717
|
+
"name": "component-port",
|
|
14718
|
+
"ns": "https://mitre.github.io/hdf-libs/ns/oscal",
|
|
14719
|
+
"objects": ["assessment-results.results[].observations[].subjects[]", "assessment-results.results[].local-definitions.components[]"],
|
|
14720
|
+
"meaning": "A database component's port.",
|
|
14721
|
+
"valueFormat": "Decimal number, a TCP port",
|
|
14722
|
+
"hdfField": "components[].port",
|
|
14723
|
+
"legacy": false
|
|
14724
|
+
},
|
|
14725
|
+
{
|
|
14726
|
+
"name": "component-model-id",
|
|
14727
|
+
"ns": "https://mitre.github.io/hdf-libs/ns/oscal",
|
|
14728
|
+
"objects": ["assessment-results.results[].observations[].subjects[]", "assessment-results.results[].local-definitions.components[]"],
|
|
14729
|
+
"meaning": "An aiModel component's provider/registry model id.",
|
|
14730
|
+
"valueFormat": "String (§1.7)",
|
|
14731
|
+
"hdfField": "components[].modelId",
|
|
14732
|
+
"legacy": false
|
|
14733
|
+
},
|
|
14734
|
+
{
|
|
14735
|
+
"name": "component-dataset-id",
|
|
14736
|
+
"ns": "https://mitre.github.io/hdf-libs/ns/oscal",
|
|
14737
|
+
"objects": ["assessment-results.results[].observations[].subjects[]", "assessment-results.results[].local-definitions.components[]"],
|
|
14738
|
+
"meaning": "A dataset component's provider/registry dataset id.",
|
|
14739
|
+
"valueFormat": "String (§1.7)",
|
|
14740
|
+
"hdfField": "components[].datasetId",
|
|
14741
|
+
"legacy": false
|
|
14742
|
+
},
|
|
14743
|
+
{
|
|
14744
|
+
"name": "component-label-key",
|
|
14745
|
+
"ns": "https://mitre.github.io/hdf-libs/ns/oscal",
|
|
14746
|
+
"objects": ["assessment-results.results[].observations[].subjects[]", "assessment-results.results[].local-definitions.components[]"],
|
|
14747
|
+
"meaning": "The key of a component label, grouped component-label-<n>, n the label's 1-based position in sorted key order.",
|
|
14748
|
+
"valueFormat": "String (§1.7)",
|
|
14749
|
+
"hdfField": "components[].labels (key)",
|
|
14750
|
+
"legacy": false
|
|
14751
|
+
},
|
|
14752
|
+
{
|
|
14753
|
+
"name": "component-label-value",
|
|
14754
|
+
"ns": "https://mitre.github.io/hdf-libs/ns/oscal",
|
|
14755
|
+
"objects": ["assessment-results.results[].observations[].subjects[]", "assessment-results.results[].local-definitions.components[]"],
|
|
14756
|
+
"meaning": "The value of the component label its group names.",
|
|
14757
|
+
"valueFormat": "String (§1.7)",
|
|
14758
|
+
"hdfField": "components[].labels (value)",
|
|
14759
|
+
"legacy": false
|
|
14760
|
+
},
|
|
14761
|
+
{
|
|
14762
|
+
"name": "component-external-id-key",
|
|
14763
|
+
"ns": "https://mitre.github.io/hdf-libs/ns/oscal",
|
|
14764
|
+
"objects": ["assessment-results.results[].observations[].subjects[]", "assessment-results.results[].local-definitions.components[]"],
|
|
14765
|
+
"meaning": "The scheme of a component external id, grouped component-external-id-<n>, n its 1-based position in sorted key order.",
|
|
14766
|
+
"valueFormat": "String (§1.7)",
|
|
14767
|
+
"hdfField": "components[].externalIds (key)",
|
|
14768
|
+
"legacy": false
|
|
14769
|
+
},
|
|
14770
|
+
{
|
|
14771
|
+
"name": "component-external-id-value",
|
|
14772
|
+
"ns": "https://mitre.github.io/hdf-libs/ns/oscal",
|
|
14773
|
+
"objects": ["assessment-results.results[].observations[].subjects[]", "assessment-results.results[].local-definitions.components[]"],
|
|
14774
|
+
"meaning": "The value of the component external id its group names.",
|
|
14775
|
+
"valueFormat": "String (§1.7)",
|
|
14776
|
+
"hdfField": "components[].externalIds (value)",
|
|
14777
|
+
"legacy": false
|
|
14778
|
+
},
|
|
14356
14779
|
{
|
|
14357
14780
|
"name": "amendment-id",
|
|
14358
14781
|
"ns": "https://mitre.github.io/hdf-libs/ns/oscal",
|
|
@@ -14776,6 +15199,10 @@ function validateVocabularyTable(raw) {
|
|
|
14776
15199
|
}
|
|
14777
15200
|
const table = validateVocabularyTable(oscal_vocabulary_default);
|
|
14778
15201
|
const byName = table.byName;
|
|
15202
|
+
/** NIST's default namespace, which a prop with no ns belongs to. */
|
|
15203
|
+
function vocabularyDefaultNamespace() {
|
|
15204
|
+
return table.defaultNamespace;
|
|
15205
|
+
}
|
|
14779
15206
|
function mustVocabularyRow(name) {
|
|
14780
15207
|
const row = byName.get(name);
|
|
14781
15208
|
if (!row) throw new Error(`oscal: "${name}" is not a row of the OSCAL vocabulary`);
|
|
@@ -15209,6 +15636,346 @@ function oscalString(s) {
|
|
|
15209
15636
|
return s.trim();
|
|
15210
15637
|
}
|
|
15211
15638
|
//#endregion
|
|
15639
|
+
//#region converters/oscal-to-hdf/typescript/component-props.ts
|
|
15640
|
+
/**
|
|
15641
|
+
* The one mapping from HDF component identity fields to the HDF-namespaced props
|
|
15642
|
+
* that carry them on an OSCAL assessment subject (ADR-0014 §4.5), driven by both
|
|
15643
|
+
* the SAR exporter and the SAR importer. Mirrors the Go peer component_props.go.
|
|
15644
|
+
*/
|
|
15645
|
+
/** The hdf-results bounds on Component.port. */
|
|
15646
|
+
const MIN_PORT = 1;
|
|
15647
|
+
const MAX_PORT = 65535;
|
|
15648
|
+
/**
|
|
15649
|
+
* The whole grammar a port prop may use. Go and JavaScript disagree over what a
|
|
15650
|
+
* lenient numeric parse accepts ("80abc", "1e3"), so the grammar is spelled out
|
|
15651
|
+
* rather than left to either language's parser.
|
|
15652
|
+
*/
|
|
15653
|
+
const DECIMAL_DIGITS = /^[0-9]+$/;
|
|
15654
|
+
function componentPort(v) {
|
|
15655
|
+
if (!DECIMAL_DIGITS.test(v)) return void 0;
|
|
15656
|
+
const n = Number(v);
|
|
15657
|
+
if (!Number.isSafeInteger(n) || n < MIN_PORT || n > MAX_PORT) return void 0;
|
|
15658
|
+
return n;
|
|
15659
|
+
}
|
|
15660
|
+
/**
|
|
15661
|
+
* The Cloud_Provider enum in sorted order, read from the generated schema types'
|
|
15662
|
+
* runtime enum rather than a hand-kept copy; the Go peer reports the same order so
|
|
15663
|
+
* the two warnings read alike.
|
|
15664
|
+
*/
|
|
15665
|
+
function cloudProviderValues() {
|
|
15666
|
+
return Object.values(CloudProvider).map(String).sort();
|
|
15667
|
+
}
|
|
15668
|
+
function validCloudProvider(v) {
|
|
15669
|
+
return Object.values(CloudProvider).some((p) => String(p) === v);
|
|
15670
|
+
}
|
|
15671
|
+
function stringField(prop, field) {
|
|
15672
|
+
return {
|
|
15673
|
+
prop,
|
|
15674
|
+
field,
|
|
15675
|
+
get: (c) => c[field],
|
|
15676
|
+
set: (c, v) => {
|
|
15677
|
+
c[field] = v;
|
|
15678
|
+
return true;
|
|
15679
|
+
}
|
|
15680
|
+
};
|
|
15681
|
+
}
|
|
15682
|
+
function stringMapField(field, prefix) {
|
|
15683
|
+
return {
|
|
15684
|
+
field,
|
|
15685
|
+
prefix,
|
|
15686
|
+
keyProp: `${prefix}-key`,
|
|
15687
|
+
valueProp: `${prefix}-value`,
|
|
15688
|
+
get: (c) => c[field],
|
|
15689
|
+
set: (c, m) => {
|
|
15690
|
+
c[field] = m;
|
|
15691
|
+
}
|
|
15692
|
+
};
|
|
15693
|
+
}
|
|
15694
|
+
/**
|
|
15695
|
+
* The table order is the order the exporter emits props in, and the test pins it to
|
|
15696
|
+
* the components[] rows of the vocabulary table.
|
|
15697
|
+
*/
|
|
15698
|
+
const COMPONENT_PROPS = [
|
|
15699
|
+
{
|
|
15700
|
+
prop: "component-name",
|
|
15701
|
+
field: "name",
|
|
15702
|
+
required: true,
|
|
15703
|
+
get: (c) => c.name,
|
|
15704
|
+
set: (c, v) => {
|
|
15705
|
+
c.name = v;
|
|
15706
|
+
return true;
|
|
15707
|
+
}
|
|
15708
|
+
},
|
|
15709
|
+
stringField("component-description", "description"),
|
|
15710
|
+
stringField("component-hostname", "hostname"),
|
|
15711
|
+
stringField("component-fqdn", "fqdn"),
|
|
15712
|
+
stringField("component-domain", "domain"),
|
|
15713
|
+
stringField("component-ip-address", "ipAddress"),
|
|
15714
|
+
stringField("component-mac-address", "macAddress"),
|
|
15715
|
+
stringField("component-os-name", "osName"),
|
|
15716
|
+
stringField("component-os-version", "osVersion"),
|
|
15717
|
+
stringField("component-image-id", "imageId"),
|
|
15718
|
+
stringField("component-registry", "registry"),
|
|
15719
|
+
stringField("component-repository", "repository"),
|
|
15720
|
+
stringField("component-tag", "tag"),
|
|
15721
|
+
stringField("component-container-id", "containerId"),
|
|
15722
|
+
stringField("component-image", "image"),
|
|
15723
|
+
stringField("component-runtime", "runtime"),
|
|
15724
|
+
stringField("component-platform-type", "platformType"),
|
|
15725
|
+
stringField("component-cluster-name", "clusterName"),
|
|
15726
|
+
stringField("component-namespace", "namespace"),
|
|
15727
|
+
stringField("component-version", "version"),
|
|
15728
|
+
{
|
|
15729
|
+
prop: "component-provider",
|
|
15730
|
+
field: "provider",
|
|
15731
|
+
accepts: `one of ${cloudProviderValues().join(", ")}`,
|
|
15732
|
+
get: (c) => c.provider ?? void 0,
|
|
15733
|
+
set: (c, v) => {
|
|
15734
|
+
if (!validCloudProvider(v)) return false;
|
|
15735
|
+
c.provider = v;
|
|
15736
|
+
return true;
|
|
15737
|
+
}
|
|
15738
|
+
},
|
|
15739
|
+
stringField("component-account-id", "accountId"),
|
|
15740
|
+
stringField("component-region", "region"),
|
|
15741
|
+
stringField("component-resource-type", "resourceType"),
|
|
15742
|
+
stringField("component-resource-id", "resourceId"),
|
|
15743
|
+
stringField("component-arn", "arn"),
|
|
15744
|
+
stringField("component-url", "url"),
|
|
15745
|
+
stringField("component-branch", "branch"),
|
|
15746
|
+
stringField("component-commit", "commit"),
|
|
15747
|
+
stringField("component-environment", "environment"),
|
|
15748
|
+
stringField("component-package-manager", "packageManager"),
|
|
15749
|
+
stringField("component-package-name", "packageName"),
|
|
15750
|
+
stringField("component-cidr", "cidr"),
|
|
15751
|
+
stringField("component-gateway", "gateway"),
|
|
15752
|
+
stringField("component-engine", "engine"),
|
|
15753
|
+
stringField("component-host", "host"),
|
|
15754
|
+
{
|
|
15755
|
+
prop: "component-port",
|
|
15756
|
+
field: "port",
|
|
15757
|
+
accepts: `a decimal integer in ${MIN_PORT}..${MAX_PORT}`,
|
|
15758
|
+
get: (c) => c.port === void 0 ? void 0 : String(c.port),
|
|
15759
|
+
set: (c, v) => {
|
|
15760
|
+
const n = componentPort(v);
|
|
15761
|
+
if (n === void 0) return false;
|
|
15762
|
+
c.port = n;
|
|
15763
|
+
return true;
|
|
15764
|
+
}
|
|
15765
|
+
},
|
|
15766
|
+
stringField("component-model-id", "modelId"),
|
|
15767
|
+
stringField("component-dataset-id", "datasetId")
|
|
15768
|
+
];
|
|
15769
|
+
const COMPONENT_GROUP_PROPS = [stringMapField("labels", "component-label"), stringMapField("externalIds", "component-external-id")];
|
|
15770
|
+
/**
|
|
15771
|
+
* Carries a component's identity fields as HDF-namespaced props, in table order. A
|
|
15772
|
+
* present-but-empty optional string is carried by an empty-field marker (§1.7.3);
|
|
15773
|
+
* an absent field emits nothing. Mirrors the Go peer.
|
|
15774
|
+
*/
|
|
15775
|
+
function componentSubjectProps(c) {
|
|
15776
|
+
const props = [];
|
|
15777
|
+
for (const e of COMPONENT_PROPS) {
|
|
15778
|
+
const v = e.get(c);
|
|
15779
|
+
if (v === void 0) continue;
|
|
15780
|
+
if (v === "") {
|
|
15781
|
+
if (!e.required) props.push(emptyFieldProp(e.field));
|
|
15782
|
+
continue;
|
|
15783
|
+
}
|
|
15784
|
+
pushVocabularyProp(props, e.prop, v);
|
|
15785
|
+
}
|
|
15786
|
+
for (const g of COMPONENT_GROUP_PROPS) appendComponentMap(props, g, g.get(c));
|
|
15787
|
+
return props;
|
|
15788
|
+
}
|
|
15789
|
+
/**
|
|
15790
|
+
* Fills a component's identity fields from the HDF-namespaced props on its
|
|
15791
|
+
* subject, leaving a field the props do not carry as the caller set it. A value
|
|
15792
|
+
* outside its HDF field's domain — which a foreign or hand-edited SAR can carry —
|
|
15793
|
+
* is dropped with a warning rather than written into a document the HDF validator
|
|
15794
|
+
* would reject. Mirrors the Go peer.
|
|
15795
|
+
*/
|
|
15796
|
+
function readComponentSubjectProps(c, props) {
|
|
15797
|
+
for (const e of COMPONENT_PROPS) {
|
|
15798
|
+
const v = readComponentProp(props, e);
|
|
15799
|
+
if (v === void 0) continue;
|
|
15800
|
+
if (!e.set(c, v)) emitConverterWarning(`Dropping ${e.prop} ${JSON.stringify(v)} on component ${JSON.stringify(c.name)}: not ${e.accepts}`);
|
|
15801
|
+
}
|
|
15802
|
+
for (const g of COMPONENT_GROUP_PROPS) {
|
|
15803
|
+
const m = readComponentMap(props, g);
|
|
15804
|
+
if (m) g.set(c, m);
|
|
15805
|
+
}
|
|
15806
|
+
}
|
|
15807
|
+
/**
|
|
15808
|
+
* Reads the HDF value the props carry for one entry. A required field has no
|
|
15809
|
+
* empty-field marker to consult, so its prop is read wherever it sits rather than
|
|
15810
|
+
* through the optional-string helper.
|
|
15811
|
+
*/
|
|
15812
|
+
function readComponentProp(props, e) {
|
|
15813
|
+
if (e.required) return findVocabularyProp(props, e.prop)?.value;
|
|
15814
|
+
return vocabularyString(props, e.prop, e.field, "");
|
|
15815
|
+
}
|
|
15816
|
+
/**
|
|
15817
|
+
* Carries a component string map as grouped key/value props in sorted key order,
|
|
15818
|
+
* one group per entry, so the importer can rebuild the map. An empty key or value
|
|
15819
|
+
* is carried by an empty-field marker in the same group (§1.7.3). Keys sort by
|
|
15820
|
+
* Unicode code point, which is what Go's map-key sort produces; the default sort
|
|
15821
|
+
* compares UTF-16 code units and would number the groups differently.
|
|
15822
|
+
*/
|
|
15823
|
+
function appendComponentMap(props, g, m) {
|
|
15824
|
+
if (!m) return;
|
|
15825
|
+
Object.keys(m).sort(byCodePoint).forEach((k, i) => {
|
|
15826
|
+
const group = `${g.prefix}-${i + 1}`;
|
|
15827
|
+
props.push(groupedComponentProp(g.keyProp, "key", group, k));
|
|
15828
|
+
props.push(groupedComponentProp(g.valueProp, "value", group, m[k]));
|
|
15829
|
+
});
|
|
15830
|
+
}
|
|
15831
|
+
/** Builds one grouped map prop, degrading to an empty-field marker when empty. */
|
|
15832
|
+
function groupedComponentProp(name, field, group, value) {
|
|
15833
|
+
if (value === "") return {
|
|
15834
|
+
...emptyFieldProp(field),
|
|
15835
|
+
group
|
|
15836
|
+
};
|
|
15837
|
+
return {
|
|
15838
|
+
...vocabularyProp(name, value),
|
|
15839
|
+
group
|
|
15840
|
+
};
|
|
15841
|
+
}
|
|
15842
|
+
/**
|
|
15843
|
+
* Rebuilds a component string map from grouped key/value props. Groups are
|
|
15844
|
+
* numbered from 1 in the order the exporter emitted them (sorted key order), so
|
|
15845
|
+
* reading stops at the first group with neither a key nor a value.
|
|
15846
|
+
*/
|
|
15847
|
+
function readComponentMap(props, g) {
|
|
15848
|
+
const m = {};
|
|
15849
|
+
for (let n = 1;; n++) {
|
|
15850
|
+
const group = `${g.prefix}-${n}`;
|
|
15851
|
+
const key = vocabularyString(props, g.keyProp, "key", group);
|
|
15852
|
+
const value = vocabularyString(props, g.valueProp, "value", group);
|
|
15853
|
+
if (key === void 0 && value === void 0) break;
|
|
15854
|
+
m[key ?? ""] = value ?? "";
|
|
15855
|
+
}
|
|
15856
|
+
return Object.keys(m).length > 0 ? m : void 0;
|
|
15857
|
+
}
|
|
15858
|
+
//#endregion
|
|
15859
|
+
//#region converters/oscal-to-hdf/typescript/carriage.ts
|
|
15860
|
+
/**
|
|
15861
|
+
* Foreign-prop carriage through HDF for OSCAL SAR (ADR-0014 §3). The importer
|
|
15862
|
+
* stores every OSCAL prop it does not consume in the reserved requirement tag
|
|
15863
|
+
* `oscal-props`, and the exporter re-emits them. Mirrors the Go peer in
|
|
15864
|
+
* converters/oscal-to-hdf/go/carriage.go.
|
|
15865
|
+
*/
|
|
15866
|
+
/** The reserved HDF requirement tag that carries unconsumed OSCAL SAR props. */
|
|
15867
|
+
const OSCAL_PROPS_TAG = "oscal-props";
|
|
15868
|
+
/**
|
|
15869
|
+
* Appends a carriage entry for every prop that HDF does not consume (ADR-0014
|
|
15870
|
+
* §3.1). `on` is the OSCAL object the props hang on. Source order is preserved.
|
|
15871
|
+
*/
|
|
15872
|
+
function carryForeignProps(entries, on, props) {
|
|
15873
|
+
for (const p of props ?? []) {
|
|
15874
|
+
if (consumedVocabularyProp(p)) continue;
|
|
15875
|
+
const entry = {
|
|
15876
|
+
on,
|
|
15877
|
+
name: p.name,
|
|
15878
|
+
value: p.value
|
|
15879
|
+
};
|
|
15880
|
+
if (p.ns) entry.ns = p.ns;
|
|
15881
|
+
if (p.class) entry.class = p.class;
|
|
15882
|
+
if (p.group) entry.group = p.group;
|
|
15883
|
+
if (p.uuid) entry.uuid = p.uuid;
|
|
15884
|
+
if (p.remarks) entry.remarks = p.remarks;
|
|
15885
|
+
entries.push(entry);
|
|
15886
|
+
}
|
|
15887
|
+
}
|
|
15888
|
+
const isStr = (v) => typeof v === "string";
|
|
15889
|
+
const CARRIAGE_ON = [
|
|
15890
|
+
"finding",
|
|
15891
|
+
"observation",
|
|
15892
|
+
"risk"
|
|
15893
|
+
];
|
|
15894
|
+
const CARRIAGE_OPTIONAL_MEMBERS = [
|
|
15895
|
+
"ns",
|
|
15896
|
+
"class",
|
|
15897
|
+
"group",
|
|
15898
|
+
"uuid",
|
|
15899
|
+
"remarks"
|
|
15900
|
+
];
|
|
15901
|
+
const CARRIAGE_MEMBERS = /* @__PURE__ */ new Set([
|
|
15902
|
+
"on",
|
|
15903
|
+
"name",
|
|
15904
|
+
"value",
|
|
15905
|
+
...CARRIAGE_OPTIONAL_MEMBERS
|
|
15906
|
+
]);
|
|
15907
|
+
/**
|
|
15908
|
+
* Whether one decoded oscal-props entry conforms to
|
|
15909
|
+
* shared/oscal-props.schema.json. A foreign document may put anything in the tag,
|
|
15910
|
+
* and an entry the emitter would turn into a non-string OSCAL prop member, or
|
|
15911
|
+
* place on no object at all, is not carriage but corruption. Mirrors the Go peer.
|
|
15912
|
+
*/
|
|
15913
|
+
function validCarriedProp(entry) {
|
|
15914
|
+
if (typeof entry !== "object" || entry === null || Array.isArray(entry)) return false;
|
|
15915
|
+
const o = entry;
|
|
15916
|
+
for (const k of Object.keys(o)) if (!CARRIAGE_MEMBERS.has(k)) return false;
|
|
15917
|
+
if (!isStr(o.on) || !CARRIAGE_ON.includes(o.on)) return false;
|
|
15918
|
+
if (!isStr(o.name) || o.name.length === 0) return false;
|
|
15919
|
+
if (!isStr(o.value)) return false;
|
|
15920
|
+
for (const k of CARRIAGE_OPTIONAL_MEMBERS) if (k in o && !isStr(o[k])) return false;
|
|
15921
|
+
return true;
|
|
15922
|
+
}
|
|
15923
|
+
/**
|
|
15924
|
+
* Reads the oscal-props entries stored on requirement `requirementId`'s tags.
|
|
15925
|
+
* Carriage is best-effort preservation, so reading is per-entry tolerant: a
|
|
15926
|
+
* conforming entry is kept, a malformed one is dropped, and the requirement's
|
|
15927
|
+
* drops are reported once. Mirrors the Go peer.
|
|
15928
|
+
*/
|
|
15929
|
+
function readCarriedProps(tags, requirementId) {
|
|
15930
|
+
const raw = tags?.[OSCAL_PROPS_TAG];
|
|
15931
|
+
if (raw === void 0) return [];
|
|
15932
|
+
if (!Array.isArray(raw)) {
|
|
15933
|
+
emitConverterWarning(`Dropping the oscal-props tag on requirement ${JSON.stringify(requirementId)}: it is not an array`);
|
|
15934
|
+
return [];
|
|
15935
|
+
}
|
|
15936
|
+
const entries = raw.filter(validCarriedProp);
|
|
15937
|
+
const dropped = raw.length - entries.length;
|
|
15938
|
+
if (dropped > 0) emitConverterWarning(`Dropping ${dropped} malformed oscal-props ${dropped === 1 ? "entry" : "entries"} on requirement ${JSON.stringify(requirementId)}`);
|
|
15939
|
+
return entries;
|
|
15940
|
+
}
|
|
15941
|
+
/** The entries whose `on` matches, in carried order. */
|
|
15942
|
+
function carriedFor(entries, on) {
|
|
15943
|
+
return entries.filter((e) => e.on === on);
|
|
15944
|
+
}
|
|
15945
|
+
/**
|
|
15946
|
+
* Re-emits carried entries onto an object's props after its own props (ADR-0014
|
|
15947
|
+
* §3.4), skipping any entry whose (ns, name, value) is already present — an
|
|
15948
|
+
* absent ns compares equal to NIST's default namespace.
|
|
15949
|
+
*/
|
|
15950
|
+
function appendCarriedProps(props, entries) {
|
|
15951
|
+
const seen = /* @__PURE__ */ new Set();
|
|
15952
|
+
for (const p of props) seen.add(carriageKey(p.ns, p.name, p.value));
|
|
15953
|
+
for (const e of entries) {
|
|
15954
|
+
const key = carriageKey(e.ns, e.name, e.value);
|
|
15955
|
+
if (seen.has(key)) continue;
|
|
15956
|
+
seen.add(key);
|
|
15957
|
+
const prop = {
|
|
15958
|
+
name: e.name,
|
|
15959
|
+
value: e.value
|
|
15960
|
+
};
|
|
15961
|
+
if (e.ns) prop.ns = e.ns;
|
|
15962
|
+
if (e.class) prop.class = e.class;
|
|
15963
|
+
if (e.group) prop.group = e.group;
|
|
15964
|
+
if (e.uuid) prop.uuid = e.uuid;
|
|
15965
|
+
if (e.remarks) prop.remarks = e.remarks;
|
|
15966
|
+
props.push(prop);
|
|
15967
|
+
}
|
|
15968
|
+
return props;
|
|
15969
|
+
}
|
|
15970
|
+
function carriageKey(ns, name, value) {
|
|
15971
|
+
const namespace = ns === void 0 || ns === "" ? vocabularyDefaultNamespace() : ns;
|
|
15972
|
+
return JSON.stringify([
|
|
15973
|
+
namespace,
|
|
15974
|
+
name,
|
|
15975
|
+
value
|
|
15976
|
+
]);
|
|
15977
|
+
}
|
|
15978
|
+
//#endregion
|
|
15212
15979
|
//#region converters/hdf-to-oscal-sar/typescript/converter.ts
|
|
15213
15980
|
/**
|
|
15214
15981
|
* Converts HDF Results to OSCAL Assessment Results (SAR) format.
|
|
@@ -15283,7 +16050,7 @@ function buildOSCALDocument(hdfResults) {
|
|
|
15283
16050
|
parties: [{
|
|
15284
16051
|
uuid: toolActorUuid,
|
|
15285
16052
|
type: "organization",
|
|
15286
|
-
name: toolPartyName(hdfResults)
|
|
16053
|
+
name: normalizePropValue(toolPartyName(hdfResults))
|
|
15287
16054
|
}]
|
|
15288
16055
|
};
|
|
15289
16056
|
let importAP;
|
|
@@ -15343,6 +16110,8 @@ function baselineToResult(baseline, timestamp, toolActorUuid, subjects) {
|
|
|
15343
16110
|
let description = "Converted from HDF results";
|
|
15344
16111
|
if (baseline.description && baseline.description !== "") description = baseline.description;
|
|
15345
16112
|
const resultProps = [];
|
|
16113
|
+
pushVocabularyProp(resultProps, "baseline-name", baseline.name);
|
|
16114
|
+
if (baseline.title && baseline.title !== "") pushVocabularyProp(resultProps, "baseline-title", baseline.title);
|
|
15346
16115
|
if (typeof baseline.version === "string") pushVocabularyProp(resultProps, "baseline-version", baseline.version);
|
|
15347
16116
|
const findings = [];
|
|
15348
16117
|
const observations = [];
|
|
@@ -15368,10 +16137,11 @@ function baselineToResult(baseline, timestamp, toolActorUuid, subjects) {
|
|
|
15368
16137
|
return {
|
|
15369
16138
|
result: {
|
|
15370
16139
|
uuid: crypto.randomUUID(),
|
|
15371
|
-
title,
|
|
16140
|
+
title: normalizePropValue(title),
|
|
15372
16141
|
description,
|
|
15373
16142
|
start: assessmentStart(baseline, timestamp),
|
|
15374
16143
|
...resultProps.length > 0 ? { props: resultProps } : {},
|
|
16144
|
+
...subjects.length > 0 ? { "local-definitions": { components: componentDefsFromSubjects(subjects) } } : {},
|
|
15375
16145
|
"reviewed-controls": { "control-selections": [controlSelection] },
|
|
15376
16146
|
...findings.length > 0 ? { findings } : {},
|
|
15377
16147
|
...observations.length > 0 ? { observations } : {},
|
|
@@ -15384,7 +16154,9 @@ function baselineToResult(baseline, timestamp, toolActorUuid, subjects) {
|
|
|
15384
16154
|
* Turns the top-level HDF components[] into OSCAL assessment subjects. Each
|
|
15385
16155
|
* component's UUID (componentId when present, otherwise a fresh one) identifies
|
|
15386
16156
|
* the subject; the HDF component type is a valid OSCAL subject type token and
|
|
15387
|
-
* its name becomes the subject title.
|
|
16157
|
+
* its name becomes the subject title. A subject holds only uuid/type/title, so
|
|
16158
|
+
* each component's type-specific identity fields ride as HDF-namespaced props on
|
|
16159
|
+
* the subject (ADR-0014 §1.5) and reconstitute on read-back.
|
|
15388
16160
|
*
|
|
15389
16161
|
* A component with no type is skipped rather than given one. OSCAL requires both
|
|
15390
16162
|
* subject-uuid and type on a subject-reference, so the type cannot simply be
|
|
@@ -15395,11 +16167,60 @@ function baselineToResult(baseline, timestamp, toolActorUuid, subjects) {
|
|
|
15395
16167
|
*/
|
|
15396
16168
|
function buildSubjects(components) {
|
|
15397
16169
|
if (!Array.isArray(components) || components.length === 0) return [];
|
|
15398
|
-
|
|
15399
|
-
|
|
15400
|
-
|
|
15401
|
-
|
|
15402
|
-
|
|
16170
|
+
const subjects = [];
|
|
16171
|
+
for (const c of components) {
|
|
16172
|
+
if (oscalString(c.type ?? "") === "") {
|
|
16173
|
+
emitConverterWarning(`hdf-to-oscal-sar: component "${c.name}" has no type, which an OSCAL assessment subject requires; not carried`);
|
|
16174
|
+
continue;
|
|
16175
|
+
}
|
|
16176
|
+
const subject = {
|
|
16177
|
+
"subject-uuid": c.componentId && c.componentId !== "" ? c.componentId : crypto.randomUUID(),
|
|
16178
|
+
type: String(c.type),
|
|
16179
|
+
title: normalizePropValue(c.name)
|
|
16180
|
+
};
|
|
16181
|
+
const props = componentSubjectProps(c);
|
|
16182
|
+
if (props.length > 0) subject.props = props;
|
|
16183
|
+
subjects.push(subject);
|
|
16184
|
+
warnUncarriedComponentFields(c);
|
|
16185
|
+
}
|
|
16186
|
+
return subjects;
|
|
16187
|
+
}
|
|
16188
|
+
/**
|
|
16189
|
+
* Turns the assessment subjects into result-level OSCAL system-components so the
|
|
16190
|
+
* assessed components survive a round trip even when no requirement produces an
|
|
16191
|
+
* observation (ADR-0014 §4.5). Each component keeps the subject's identity
|
|
16192
|
+
* (uuid/type/title and the HDF-namespaced props); the OSCAL-required description
|
|
16193
|
+
* and status have no HDF counterpart, so they are display fallbacks the importer
|
|
16194
|
+
* never reads back — the exact identity lives entirely in the props. Mirrors Go.
|
|
16195
|
+
*/
|
|
16196
|
+
function componentDefsFromSubjects(subjects) {
|
|
16197
|
+
return subjects.map((s) => {
|
|
16198
|
+
const display = s.title !== "" ? s.title : "Assessed component";
|
|
16199
|
+
return {
|
|
16200
|
+
uuid: s["subject-uuid"],
|
|
16201
|
+
type: s.type,
|
|
16202
|
+
title: display,
|
|
16203
|
+
description: display,
|
|
16204
|
+
...s.props ? { props: s.props } : {},
|
|
16205
|
+
status: { state: "other" }
|
|
16206
|
+
};
|
|
16207
|
+
});
|
|
16208
|
+
}
|
|
16209
|
+
/**
|
|
16210
|
+
* Reports component identity beyond the type-specific fields a SAR subject can
|
|
16211
|
+
* hold — owner, BOMs, artifact integrity, and the migration-only fields — so it
|
|
16212
|
+
* is not silently dropped (matching the lossy-conversion warning UX). Mirrors Go.
|
|
16213
|
+
*/
|
|
16214
|
+
function warnUncarriedComponentFields(c) {
|
|
16215
|
+
const dropped = [];
|
|
16216
|
+
if (c.owner) dropped.push("owner");
|
|
16217
|
+
if (Array.isArray(c.boms) && c.boms.length > 0) dropped.push("boms");
|
|
16218
|
+
if (Array.isArray(c.integrity) && c.integrity.length > 0) dropped.push("integrity");
|
|
16219
|
+
if (Array.isArray(c.baselineRefs) && c.baselineRefs.length > 0) dropped.push("baselineRefs");
|
|
16220
|
+
if (Array.isArray(c.inputOverrides) && c.inputOverrides.length > 0) dropped.push("inputOverrides");
|
|
16221
|
+
if (c.targetSelector && Object.keys(c.targetSelector).length > 0) dropped.push("targetSelector");
|
|
16222
|
+
if (dropped.length === 0) return;
|
|
16223
|
+
emitConverterWarning(`hdf-to-oscal-sar: component "${c.name}" carries ${dropped.join(", ")}, which an OSCAL SAR assessment subject cannot represent; not carried`);
|
|
15403
16224
|
}
|
|
15404
16225
|
/**
|
|
15405
16226
|
* Converts an EvaluatedRequirement into a Finding, optional Observation, and optional Risk.
|
|
@@ -15462,13 +16283,18 @@ function requirementToFindingSet(req, timestamp, toolActorUuid, subjects) {
|
|
|
15462
16283
|
rel: "reference"
|
|
15463
16284
|
});
|
|
15464
16285
|
let title = req.id;
|
|
15465
|
-
if (req.title && req.title !== "")
|
|
16286
|
+
if (req.title && req.title !== "") {
|
|
16287
|
+
title = req.title;
|
|
16288
|
+
pushVocabularyProp(props, "requirement-title", req.title);
|
|
16289
|
+
}
|
|
16290
|
+
const carried = readCarriedProps(req.tags, req.id);
|
|
16291
|
+
appendCarriedProps(props, carriedFor(carried, "finding"));
|
|
15466
16292
|
let resource;
|
|
15467
16293
|
if (req.code != null && req.code.trim() !== "") {
|
|
15468
16294
|
const resourceUuid = crypto.randomUUID();
|
|
15469
16295
|
resource = {
|
|
15470
16296
|
uuid: resourceUuid,
|
|
15471
|
-
title: `Check source code for ${req.id}
|
|
16297
|
+
title: normalizePropValue(`Check source code for ${req.id}`),
|
|
15472
16298
|
props: [vocabularyProp("type", "evidence")],
|
|
15473
16299
|
base64: {
|
|
15474
16300
|
value: encodeBase64Utf8(req.code),
|
|
@@ -15493,7 +16319,7 @@ function requirementToFindingSet(req, timestamp, toolActorUuid, subjects) {
|
|
|
15493
16319
|
};
|
|
15494
16320
|
const finding = {
|
|
15495
16321
|
uuid: crypto.randomUUID(),
|
|
15496
|
-
title,
|
|
16322
|
+
title: normalizePropValue(title),
|
|
15497
16323
|
description: findingDesc || title,
|
|
15498
16324
|
props: props.length > 0 ? props : void 0,
|
|
15499
16325
|
links: links.length > 0 ? links : void 0,
|
|
@@ -15502,15 +16328,16 @@ function requirementToFindingSet(req, timestamp, toolActorUuid, subjects) {
|
|
|
15502
16328
|
let observation;
|
|
15503
16329
|
if (results.length > 0) {
|
|
15504
16330
|
const obsUUID = crypto.randomUUID();
|
|
15505
|
-
const obsDesc = buildObservationDescription(results);
|
|
15506
16331
|
const relevantEvidence = buildRelevantEvidence(req);
|
|
16332
|
+
const obsProps = appendCarriedProps([], carriedFor(carried, "observation"));
|
|
15507
16333
|
observation = {
|
|
15508
16334
|
uuid: obsUUID,
|
|
15509
|
-
description:
|
|
16335
|
+
description: "",
|
|
15510
16336
|
methods: ["TEST"],
|
|
15511
16337
|
collected: formatAssessmentTime(earliestResultTime(results), timestamp),
|
|
15512
16338
|
...subjects.length > 0 ? { subjects } : {},
|
|
15513
|
-
...relevantEvidence.length > 0 ? { "relevant-evidence": relevantEvidence } : {}
|
|
16339
|
+
...relevantEvidence.length > 0 ? { "relevant-evidence": relevantEvidence } : {},
|
|
16340
|
+
...obsProps.length > 0 ? { props: obsProps } : {}
|
|
15514
16341
|
};
|
|
15515
16342
|
finding["related-observations"] = [{ "observation-uuid": obsUUID }];
|
|
15516
16343
|
} else warnUncarriedProse(req);
|
|
@@ -15525,9 +16352,10 @@ function requirementToFindingSet(req, timestamp, toolActorUuid, subjects) {
|
|
|
15525
16352
|
}
|
|
15526
16353
|
const remediations = buildRemediations(req);
|
|
15527
16354
|
const deadline = riskDeadline(req);
|
|
16355
|
+
const riskProps = appendCarriedProps([], carriedFor(carried, "risk"));
|
|
15528
16356
|
risk = {
|
|
15529
16357
|
uuid: riskUUID,
|
|
15530
|
-
title: `Risk for ${req.id}
|
|
16358
|
+
title: normalizePropValue(`Risk for ${req.id}`),
|
|
15531
16359
|
description: `Impact: ${req.impact.toFixed(1)} (${severity})`,
|
|
15532
16360
|
statement: `Impact: ${req.impact.toFixed(1)} (${severity})`,
|
|
15533
16361
|
status: riskStatusFromState(state),
|
|
@@ -15543,10 +16371,12 @@ function requirementToFindingSet(req, timestamp, toolActorUuid, subjects) {
|
|
|
15543
16371
|
}]
|
|
15544
16372
|
}],
|
|
15545
16373
|
...remediations.length > 0 ? { remediations } : {},
|
|
15546
|
-
...deadline ? { deadline } : {}
|
|
16374
|
+
...deadline ? { deadline } : {},
|
|
16375
|
+
...riskProps.length > 0 ? { props: riskProps } : {}
|
|
15547
16376
|
};
|
|
15548
16377
|
finding["related-risks"] = [{ "risk-uuid": riskUUID }];
|
|
15549
16378
|
}
|
|
16379
|
+
if (observation) observation.description = observationDisplayDescription(roundTripStatus(state), observation, risk, title);
|
|
15550
16380
|
return {
|
|
15551
16381
|
finding,
|
|
15552
16382
|
observation,
|
|
@@ -15740,17 +16570,50 @@ function extractDefaultDescription(descriptions) {
|
|
|
15740
16570
|
return "";
|
|
15741
16571
|
}
|
|
15742
16572
|
/**
|
|
15743
|
-
*
|
|
16573
|
+
* Synthesizes an observation's display description from the OSCAL objects the
|
|
16574
|
+
* exporter emits, mirroring how the reverse SAR importer reconstructs a result's
|
|
16575
|
+
* status, codeDesc and message. Deriving it from the emitted observation and risk
|
|
16576
|
+
* — not from the HDF result's stored codeDesc/message — makes the SAR round trip
|
|
16577
|
+
* idempotent on observation.description (ADR-0014 §3.5): the string already equals
|
|
16578
|
+
* what the importer reads back and the next export re-synthesizes, so the first
|
|
16579
|
+
* HDF-produced export equals the second. observation.description is display text,
|
|
16580
|
+
* not a prose home (ADR-0014 §2); the requirement's real prose rides its own homes
|
|
16581
|
+
* (finding.target.description, relevant-evidence, risk.remediations).
|
|
15744
16582
|
*/
|
|
15745
|
-
function
|
|
16583
|
+
function observationDisplayDescription(status, obs, risk, fallbackTitle) {
|
|
16584
|
+
let desc = `[${status}] ${reconstructedCodeDesc(obs, fallbackTitle)}`;
|
|
16585
|
+
const msg = reconstructedRiskMessage(risk);
|
|
16586
|
+
if (msg !== "") desc += ": " + msg;
|
|
16587
|
+
return desc;
|
|
16588
|
+
}
|
|
16589
|
+
/**
|
|
16590
|
+
* The HDF status the emitted finding's target state maps back to on import
|
|
16591
|
+
* (oscal-to-hdf mapFindingStatus): an unrecognized state is notReviewed, as there.
|
|
16592
|
+
*/
|
|
16593
|
+
function roundTripStatus(state) {
|
|
16594
|
+
return oscalStatusToHdf(state) ?? "notReviewed";
|
|
16595
|
+
}
|
|
16596
|
+
/** Mirrors oscal-to-hdf buildCodeDesc for the single observation the exporter emits. */
|
|
16597
|
+
function reconstructedCodeDesc(obs, fallbackTitle) {
|
|
15746
16598
|
const parts = [];
|
|
15747
|
-
|
|
15748
|
-
|
|
15749
|
-
|
|
15750
|
-
|
|
16599
|
+
if (obs.methods && obs.methods.length > 0) parts.push("Methods: " + obs.methods.join(", "));
|
|
16600
|
+
for (const subj of obs.subjects ?? []) {
|
|
16601
|
+
let subjDesc = subj.type;
|
|
16602
|
+
if (subj.title) subjDesc = subj.title + " (" + subj.type + ")";
|
|
16603
|
+
parts.push("Subject: " + subjDesc);
|
|
15751
16604
|
}
|
|
15752
|
-
if (parts.length === 0) return
|
|
15753
|
-
return parts.join("
|
|
16605
|
+
if (parts.length === 0) return fallbackTitle;
|
|
16606
|
+
return parts.join("; ");
|
|
16607
|
+
}
|
|
16608
|
+
/**
|
|
16609
|
+
* Mirrors oscal-to-hdf buildRiskMessage for the single risk the exporter emits
|
|
16610
|
+
* (undefined when the requirement's impact is 0).
|
|
16611
|
+
*/
|
|
16612
|
+
function reconstructedRiskMessage(risk) {
|
|
16613
|
+
if (!risk) return "";
|
|
16614
|
+
let msg = risk.title;
|
|
16615
|
+
if (risk.description) msg += ": " + risk.description;
|
|
16616
|
+
return msg;
|
|
15754
16617
|
}
|
|
15755
16618
|
/**
|
|
15756
16619
|
* Maps OSCAL finding state to risk status.
|
|
@@ -15796,7 +16659,7 @@ const OVERRIDE_APPLIED_TITLE = "Override applied";
|
|
|
15796
16659
|
* through some other producer's validation.
|
|
15797
16660
|
*/
|
|
15798
16661
|
function poamTitle(a) {
|
|
15799
|
-
return firstNonEmpty(a.name, a.amendmentId ?? "", "HDF Amendments");
|
|
16662
|
+
return normalizePropValue(firstNonEmpty(a.name, a.amendmentId ?? "", "HDF Amendments"));
|
|
15800
16663
|
}
|
|
15801
16664
|
/**
|
|
15802
16665
|
* Stands in for an empty requirementId: HDF puts no minLength on it, and OSCAL
|
|
@@ -15807,7 +16670,7 @@ function poamTitle(a) {
|
|
|
15807
16670
|
const UNIDENTIFIED_REQUIREMENT_TITLE = "Unidentified requirement";
|
|
15808
16671
|
/** The title of the risk and POA&M item an override produces. */
|
|
15809
16672
|
function requirementTitle(override) {
|
|
15810
|
-
return firstNonEmpty(override.requirementId, UNIDENTIFIED_REQUIREMENT_TITLE);
|
|
16673
|
+
return normalizePropValue(firstNonEmpty(override.requirementId, UNIDENTIFIED_REQUIREMENT_TITLE));
|
|
15811
16674
|
}
|
|
15812
16675
|
/**
|
|
15813
16676
|
* Supplies the text OSCAL requires for a risk's description and statement. HDF
|
|
@@ -15843,7 +16706,7 @@ var PartyRegistry = class {
|
|
|
15843
16706
|
]);
|
|
15844
16707
|
const existing = this.byKey.get(key);
|
|
15845
16708
|
if (existing) return existing.uuid;
|
|
15846
|
-
const name =
|
|
16709
|
+
const name = normalizePropValue(id.identifier ?? "");
|
|
15847
16710
|
const props = [];
|
|
15848
16711
|
pushVocabularyProp(props, "identity-identifier", id.identifier ?? "");
|
|
15849
16712
|
pushVocabularyProp(props, "identity-type", String(id.type));
|
|
@@ -16287,7 +17150,7 @@ function referenceResource(ref, b) {
|
|
|
16287
17150
|
pushOptionalString(props, "reference-kind", "kind", ref.kind);
|
|
16288
17151
|
return {
|
|
16289
17152
|
uuid: crypto.randomUUID(),
|
|
16290
|
-
title: ref.sourceName,
|
|
17153
|
+
title: normalizePropValue(ref.sourceName ?? ""),
|
|
16291
17154
|
...ref.description ? { description: ref.description } : {},
|
|
16292
17155
|
...props.length > 0 ? { props } : {},
|
|
16293
17156
|
...ref.href ? { rlinks: [{ href: ref.href }] } : {},
|
|
@@ -18521,6 +19384,12 @@ function definitionBaselineName(compDef) {
|
|
|
18521
19384
|
* mapping they had before it: type "poam", with their own extension props not
|
|
18522
19385
|
* carried (§3.6).
|
|
18523
19386
|
*
|
|
19387
|
+
* Foreign-prop carriage is SAR-only (ADR-0014 §3.6): unlike a SAR requirement's
|
|
19388
|
+
* tags, Standalone_Override has no extension slot (unevaluatedProperties: false),
|
|
19389
|
+
* so a foreign POA&M's own extension props are a stated loss on import — there is
|
|
19390
|
+
* no oscal-props tag here and none is invented. HDF-produced POA&Ms are
|
|
19391
|
+
* unaffected; every HDF field round-trips through §4.6.
|
|
19392
|
+
*
|
|
18524
19393
|
* Mirrors the Go implementation in converters/oscal-to-hdf/go/converter_poam.go.
|
|
18525
19394
|
*/
|
|
18526
19395
|
/**
|
|
@@ -19208,7 +20077,7 @@ async function convertOscalSarToHdf(input) {
|
|
|
19208
20077
|
emitConverterWarning(`Skipping assessment result "${title}": no finding has a target-id`);
|
|
19209
20078
|
continue;
|
|
19210
20079
|
}
|
|
19211
|
-
const baseline = await resultToEvaluatedBaseline(result, groups,
|
|
20080
|
+
const baseline = await resultToEvaluatedBaseline(result, groups, input, scanTime);
|
|
19212
20081
|
baselines.push(baseline);
|
|
19213
20082
|
}
|
|
19214
20083
|
const planRef = sar["import-ap"]?.href || void 0;
|
|
@@ -19217,6 +20086,7 @@ async function convertOscalSarToHdf(input) {
|
|
|
19217
20086
|
const t = parseTimestamp(meta.lastModified);
|
|
19218
20087
|
if (t) timestamp = t;
|
|
19219
20088
|
}
|
|
20089
|
+
const components = sarComponents(sar);
|
|
19220
20090
|
return serializeHdf({
|
|
19221
20091
|
baselines,
|
|
19222
20092
|
generator: {
|
|
@@ -19228,10 +20098,76 @@ async function convertOscalSarToHdf(input) {
|
|
|
19228
20098
|
format: "OSCAL"
|
|
19229
20099
|
},
|
|
19230
20100
|
timestamp: timestamp ?? /* @__PURE__ */ new Date(),
|
|
19231
|
-
planRef
|
|
20101
|
+
planRef,
|
|
20102
|
+
...components.length > 0 ? { components } : {}
|
|
19232
20103
|
});
|
|
19233
20104
|
}
|
|
19234
20105
|
/**
|
|
20106
|
+
* The set of OSCAL subject types that are HDF component types. A foreign SAR's
|
|
20107
|
+
* subject types (component, inventory-item, party, …) are not among them, so
|
|
20108
|
+
* those subjects do not reconstitute as HDF components. Mirrors the Go peer.
|
|
20109
|
+
*/
|
|
20110
|
+
const HDF_COMPONENT_TYPES = /* @__PURE__ */ new Set([
|
|
20111
|
+
"host",
|
|
20112
|
+
"containerImage",
|
|
20113
|
+
"containerInstance",
|
|
20114
|
+
"containerPlatform",
|
|
20115
|
+
"cloudAccount",
|
|
20116
|
+
"cloudResource",
|
|
20117
|
+
"repository",
|
|
20118
|
+
"application",
|
|
20119
|
+
"artifact",
|
|
20120
|
+
"network",
|
|
20121
|
+
"database",
|
|
20122
|
+
"aiModel",
|
|
20123
|
+
"dataset"
|
|
20124
|
+
]);
|
|
20125
|
+
/**
|
|
20126
|
+
* Reconstitutes the top-level HDF components from a SAR. The authoritative home is
|
|
20127
|
+
* each result's local-definitions.components (ADR-0014 §4.5), which survives even
|
|
20128
|
+
* when no requirement produced an observation; the per-observation subjects are
|
|
20129
|
+
* read too for documents exported before the result-level home existed. Components are deduplicated by uuid in first-seen document order — a
|
|
20130
|
+
* component appears identically at the result level and on every observation — and
|
|
20131
|
+
* one whose type is not an HDF component type is left alone so foreign SARs gain no
|
|
20132
|
+
* invalid components. Mirrors Go.
|
|
20133
|
+
*/
|
|
20134
|
+
function sarComponents(sar) {
|
|
20135
|
+
const components = [];
|
|
20136
|
+
const seen = /* @__PURE__ */ new Set();
|
|
20137
|
+
const add = (subj) => {
|
|
20138
|
+
const uuid = subj["subject-uuid"];
|
|
20139
|
+
if (!uuid || seen.has(uuid) || !HDF_COMPONENT_TYPES.has(subj.type)) return;
|
|
20140
|
+
seen.add(uuid);
|
|
20141
|
+
components.push(subjectToComponent(subj));
|
|
20142
|
+
};
|
|
20143
|
+
for (const result of sar.results) {
|
|
20144
|
+
for (const sc of result["local-definitions"]?.components ?? []) add({
|
|
20145
|
+
"subject-uuid": sc.uuid,
|
|
20146
|
+
type: sc.type,
|
|
20147
|
+
title: sc.title,
|
|
20148
|
+
props: sc.props
|
|
20149
|
+
});
|
|
20150
|
+
for (const obs of result.observations ?? []) for (const subj of obs.subjects ?? []) add(subj);
|
|
20151
|
+
}
|
|
20152
|
+
return components;
|
|
20153
|
+
}
|
|
20154
|
+
/**
|
|
20155
|
+
* Rebuilds one HDF component from an assessment subject: the subject uuid is the
|
|
20156
|
+
* componentId (ADR-0014 §4.5 SSP analog), and every type-specific identity field
|
|
20157
|
+
* comes from the HDF-namespaced props the exporter stamped on the subject. The
|
|
20158
|
+
* subject title is the name a foreign subject carries no component-name prop for
|
|
20159
|
+
* (§4.3, §4.5). Mirrors the Go peer.
|
|
20160
|
+
*/
|
|
20161
|
+
function subjectToComponent(subj) {
|
|
20162
|
+
const c = {
|
|
20163
|
+
type: subj.type,
|
|
20164
|
+
name: subj.title ?? "",
|
|
20165
|
+
componentId: subj["subject-uuid"]
|
|
20166
|
+
};
|
|
20167
|
+
readComponentSubjectProps(c, subj.props);
|
|
20168
|
+
return c;
|
|
20169
|
+
}
|
|
20170
|
+
/**
|
|
19235
20171
|
* Groups findings by requirement id in first-seen order (a Map keeps insertion
|
|
19236
20172
|
* order), within the finding cap, and returns the findings skipped for an empty
|
|
19237
20173
|
* target-id. Mirrors Go's groupFindingsByRequirement.
|
|
@@ -19268,24 +20204,25 @@ function sarRequirementId(f) {
|
|
|
19268
20204
|
const controlId = confirmedControlId(targetId);
|
|
19269
20205
|
return controlId === void 0 ? targetId : controlIdToNistTag(controlId);
|
|
19270
20206
|
}
|
|
19271
|
-
async function resultToEvaluatedBaseline(result, groups,
|
|
20207
|
+
async function resultToEvaluatedBaseline(result, groups, rawInput, scanTime) {
|
|
19272
20208
|
const checksum = await inputChecksum(rawInput);
|
|
19273
20209
|
const obsMap = buildObservationMap(result.observations ?? []);
|
|
19274
20210
|
const riskMap = buildRiskMap(result.risks ?? []);
|
|
19275
20211
|
const requirements = [];
|
|
19276
20212
|
for (const [id, findings] of groups) requirements.push(findingsToEvaluatedRequirement(id, findings, obsMap, riskMap, result, scanTime));
|
|
19277
|
-
const name = sarBaselineName(result
|
|
20213
|
+
const name = sarBaselineName(result);
|
|
19278
20214
|
const baseline = createMinimalBaseline(name, requirements, {
|
|
19279
20215
|
resultsChecksum: checksum,
|
|
19280
20216
|
integrity: await inputIntegrity(rawInput),
|
|
19281
20217
|
status: "loaded",
|
|
19282
|
-
title: result
|
|
20218
|
+
title: sarBaselineTitle(result)
|
|
19283
20219
|
});
|
|
19284
20220
|
if (result.description) baseline.description = result.description;
|
|
19285
20221
|
return baseline;
|
|
19286
20222
|
}
|
|
19287
20223
|
function findingsToEvaluatedRequirement(id, findings, obsMap, riskMap, result, scanTime) {
|
|
19288
|
-
const
|
|
20224
|
+
const firstFinding = findings[0];
|
|
20225
|
+
const title = sarRequirementTitle(firstFinding) || id;
|
|
19289
20226
|
const impact = sarFindingsImpact(findings, riskMap);
|
|
19290
20227
|
const descriptions = sarBuildDescriptions(findings, obsMap, riskMap);
|
|
19291
20228
|
const refs = sarBuildRefs(findings, obsMap);
|
|
@@ -19293,6 +20230,8 @@ function findingsToEvaluatedRequirement(id, findings, obsMap, riskMap, result, s
|
|
|
19293
20230
|
for (const f of findings) results.push(findingToRequirementResult(f, obsMap, riskMap, result, scanTime));
|
|
19294
20231
|
const nistTags = sarConfirmedNistTags(findings);
|
|
19295
20232
|
const tags = buildNistCciTags(nistTags, nistToCci(nistTags));
|
|
20233
|
+
const carried = sarCarriedProps(findings, obsMap, riskMap);
|
|
20234
|
+
if (carried.length > 0) tags[OSCAL_PROPS_TAG] = carried;
|
|
19296
20235
|
const req = createRequirement(id, title, descriptions, impact, results, {
|
|
19297
20236
|
tags,
|
|
19298
20237
|
...refs ? { refs } : {}
|
|
@@ -19325,6 +20264,33 @@ function sarConfirmedNistTags(findings) {
|
|
|
19325
20264
|
}
|
|
19326
20265
|
return controlIdsToNistTags(controlIds);
|
|
19327
20266
|
}
|
|
20267
|
+
/**
|
|
20268
|
+
* Collects the carriage entries for a requirement (ADR-0014 §3.1): every
|
|
20269
|
+
* unconsumed prop on its finding(s), then on their related observations, then on
|
|
20270
|
+
* their related risks. Observations and risks are read once each (deduplicated
|
|
20271
|
+
* by UUID). Mirrors Go's sarCarriedProps.
|
|
20272
|
+
*/
|
|
20273
|
+
function sarCarriedProps(findings, obsMap, riskMap) {
|
|
20274
|
+
const entries = [];
|
|
20275
|
+
for (const f of findings) carryForeignProps(entries, "finding", f.props);
|
|
20276
|
+
const seenObs = /* @__PURE__ */ new Set();
|
|
20277
|
+
for (const f of findings) for (const ref of f["related-observations"] ?? []) {
|
|
20278
|
+
const uuid = ref["observation-uuid"];
|
|
20279
|
+
if (!uuid || seenObs.has(uuid)) continue;
|
|
20280
|
+
seenObs.add(uuid);
|
|
20281
|
+
const obs = obsMap.get(uuid);
|
|
20282
|
+
if (obs) carryForeignProps(entries, "observation", obs.props);
|
|
20283
|
+
}
|
|
20284
|
+
const seenRisk = /* @__PURE__ */ new Set();
|
|
20285
|
+
for (const f of findings) for (const ref of f["related-risks"] ?? []) {
|
|
20286
|
+
const uuid = ref["risk-uuid"];
|
|
20287
|
+
if (!uuid || seenRisk.has(uuid)) continue;
|
|
20288
|
+
seenRisk.add(uuid);
|
|
20289
|
+
const risk = riskMap.get(uuid);
|
|
20290
|
+
if (risk) carryForeignProps(entries, "risk", risk.props);
|
|
20291
|
+
}
|
|
20292
|
+
return entries;
|
|
20293
|
+
}
|
|
19328
20294
|
function mapFindingStatus(f) {
|
|
19329
20295
|
const status = oscalStatusToHdf(f.target.status.state);
|
|
19330
20296
|
if (status === "passed") return ResultStatus.Passed;
|
|
@@ -19553,8 +20519,19 @@ function parseResultStartTime(result) {
|
|
|
19553
20519
|
}
|
|
19554
20520
|
return /* @__PURE__ */ new Date(0);
|
|
19555
20521
|
}
|
|
19556
|
-
function sarBaselineName(result
|
|
19557
|
-
|
|
20522
|
+
function sarBaselineName(result) {
|
|
20523
|
+
const match = findVocabularyProp(result.props, "baseline-name");
|
|
20524
|
+
if (match) return match.value;
|
|
20525
|
+
const kebab = toKebabCase(result.title ?? "", "");
|
|
20526
|
+
return kebab === "" ? result.uuid : `${kebab}--${result.uuid}`;
|
|
20527
|
+
}
|
|
20528
|
+
function sarBaselineTitle(result) {
|
|
20529
|
+
const match = findVocabularyProp(result.props, "baseline-title");
|
|
20530
|
+
return match ? match.value : result.title ?? "";
|
|
20531
|
+
}
|
|
20532
|
+
function sarRequirementTitle(f) {
|
|
20533
|
+
const match = findVocabularyProp(f.props, "requirement-title");
|
|
20534
|
+
return match ? match.value : f.title ?? "";
|
|
19558
20535
|
}
|
|
19559
20536
|
//#endregion
|
|
19560
20537
|
//#region converters/oscal-to-hdf/typescript/converter-ssp.ts
|