@mitre/hdf-schema 2.0.0

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.
Files changed (65) hide show
  1. package/LICENSE.md +55 -0
  2. package/README.md +143 -0
  3. package/dist/go/go.mod +4 -0
  4. package/dist/go/hdf.go +2224 -0
  5. package/dist/helpers.d.ts +77 -0
  6. package/dist/helpers.js +242 -0
  7. package/dist/index.d.ts +62 -0
  8. package/dist/index.js +37 -0
  9. package/dist/python/hdf_amendments.py +695 -0
  10. package/dist/python/hdf_baseline.py +782 -0
  11. package/dist/python/hdf_comparison.py +1771 -0
  12. package/dist/python/hdf_evidence_package.py +593 -0
  13. package/dist/python/hdf_plan.py +363 -0
  14. package/dist/python/hdf_results.py +2163 -0
  15. package/dist/python/hdf_system.py +904 -0
  16. package/dist/schemas/hdf-amendments.schema.json +1562 -0
  17. package/dist/schemas/hdf-baseline.schema.json +1787 -0
  18. package/dist/schemas/hdf-comparison.schema.json +3730 -0
  19. package/dist/schemas/hdf-evidence-package.schema.json +1738 -0
  20. package/dist/schemas/hdf-plan.schema.json +1821 -0
  21. package/dist/schemas/hdf-results.schema.json +2810 -0
  22. package/dist/schemas/hdf-system.schema.json +2512 -0
  23. package/dist/ts/hdf-amendments.d.ts +446 -0
  24. package/dist/ts/hdf-amendments.js +77 -0
  25. package/dist/ts/hdf-amendments.ts +457 -0
  26. package/dist/ts/hdf-baseline.d.ts +472 -0
  27. package/dist/ts/hdf-baseline.js +58 -0
  28. package/dist/ts/hdf-baseline.ts +483 -0
  29. package/dist/ts/hdf-comparison.d.ts +1185 -0
  30. package/dist/ts/hdf-comparison.js +216 -0
  31. package/dist/ts/hdf-comparison.ts +1210 -0
  32. package/dist/ts/hdf-evidence-package.d.ts +348 -0
  33. package/dist/ts/hdf-evidence-package.js +39 -0
  34. package/dist/ts/hdf-evidence-package.ts +356 -0
  35. package/dist/ts/hdf-plan.d.ts +204 -0
  36. package/dist/ts/hdf-plan.js +23 -0
  37. package/dist/ts/hdf-plan.ts +205 -0
  38. package/dist/ts/hdf-results.d.ts +1457 -0
  39. package/dist/ts/hdf-results.js +174 -0
  40. package/dist/ts/hdf-results.ts +1481 -0
  41. package/dist/ts/hdf-system.d.ts +609 -0
  42. package/dist/ts/hdf-system.js +102 -0
  43. package/dist/ts/hdf-system.ts +617 -0
  44. package/package.json +98 -0
  45. package/src/schemas/hdf-amendments.schema.json +97 -0
  46. package/src/schemas/hdf-baseline.schema.json +190 -0
  47. package/src/schemas/hdf-comparison.schema.json +107 -0
  48. package/src/schemas/hdf-evidence-package.schema.json +227 -0
  49. package/src/schemas/hdf-plan.schema.json +92 -0
  50. package/src/schemas/hdf-results.schema.json +304 -0
  51. package/src/schemas/hdf-system.schema.json +136 -0
  52. package/src/schemas/primitives/amendments.schema.json +155 -0
  53. package/src/schemas/primitives/common.schema.json +814 -0
  54. package/src/schemas/primitives/comparison.schema.json +809 -0
  55. package/src/schemas/primitives/component.schema.json +518 -0
  56. package/src/schemas/primitives/data-flow.schema.json +158 -0
  57. package/src/schemas/primitives/extensions.schema.json +342 -0
  58. package/src/schemas/primitives/parameter.schema.json +128 -0
  59. package/src/schemas/primitives/plan.schema.json +128 -0
  60. package/src/schemas/primitives/platform.schema.json +32 -0
  61. package/src/schemas/primitives/result.schema.json +133 -0
  62. package/src/schemas/primitives/runner.schema.json +83 -0
  63. package/src/schemas/primitives/statistics.schema.json +71 -0
  64. package/src/schemas/primitives/system.schema.json +132 -0
  65. package/src/schemas/primitives/target.schema.json +523 -0
@@ -0,0 +1,904 @@
1
+ from enum import Enum
2
+ from dataclasses import dataclass
3
+ from typing import Optional, Any, List, Dict, TypeVar, Type, cast, Callable
4
+ from uuid import UUID
5
+ from datetime import datetime
6
+ import dateutil.parser
7
+
8
+
9
+ T = TypeVar("T")
10
+ EnumT = TypeVar("EnumT", bound=Enum)
11
+
12
+
13
+ def from_str(x: Any) -> str:
14
+ assert isinstance(x, str)
15
+ return x
16
+
17
+
18
+ def from_none(x: Any) -> Any:
19
+ assert x is None
20
+ return x
21
+
22
+
23
+ def from_union(fs, x):
24
+ for f in fs:
25
+ try:
26
+ return f(x)
27
+ except:
28
+ pass
29
+ assert False
30
+
31
+
32
+ def to_enum(c: Type[EnumT], x: Any) -> EnumT:
33
+ assert isinstance(x, c)
34
+ return x.value
35
+
36
+
37
+ def to_class(c: Type[T], x: Any) -> dict:
38
+ assert isinstance(x, c)
39
+ return cast(Any, x).to_dict()
40
+
41
+
42
+ def from_list(f: Callable[[Any], T], x: Any) -> List[T]:
43
+ assert isinstance(x, list)
44
+ return [f(y) for y in x]
45
+
46
+
47
+ def from_dict(f: Callable[[Any], T], x: Any) -> Dict[str, T]:
48
+ assert isinstance(x, dict)
49
+ return { k: f(v) for (k, v) in x.items() }
50
+
51
+
52
+ def from_int(x: Any) -> int:
53
+ assert isinstance(x, int) and not isinstance(x, bool)
54
+ return x
55
+
56
+
57
+ def from_datetime(x: Any) -> datetime:
58
+ return dateutil.parser.parse(x)
59
+
60
+
61
+ class AuthorizationStatus(Enum):
62
+ """Current Authorization to Operate (ATO) status.
63
+
64
+ Authorization to Operate (ATO) status for the system.
65
+ """
66
+ AUTHORIZED = "authorized"
67
+ CONDITIONALLY_AUTHORIZED = "conditionallyAuthorized"
68
+ DENIED = "denied"
69
+ NOT_YET_REQUESTED = "notYetRequested"
70
+ PENDING_AUTHORIZATION = "pendingAuthorization"
71
+ REVOKED = "revoked"
72
+
73
+
74
+ class CategorizationLevel(Enum):
75
+ """FIPS 199 security categorization (impact level).
76
+
77
+ FIPS 199 security categorization level (impact level).
78
+ """
79
+ HIGH = "high"
80
+ LOW = "low"
81
+ MODERATE = "moderate"
82
+
83
+
84
+ class TypeEnum(Enum):
85
+ """The type of identifier. Use 'email' for email addresses, 'username' for user accounts,
86
+ 'system' for automated systems, 'simple' for basic string identifiers without additional
87
+ classification, or 'other' for custom identity systems.
88
+ """
89
+ EMAIL = "email"
90
+ OTHER = "other"
91
+ SIMPLE = "simple"
92
+ SYSTEM = "system"
93
+ USERNAME = "username"
94
+
95
+
96
+ @dataclass
97
+ class Identity:
98
+ """Identity of the person or system that approved this override.
99
+
100
+ Represents an identity that performed an action, such as capturing evidence or applying
101
+ an override.
102
+
103
+ Team or individual responsible for this component. Enables per-component ownership when
104
+ different teams manage different parts of a system.
105
+
106
+ Team or individual responsible for this system's authorization and compliance. Maps to
107
+ OSCAL responsible-party with role 'system-owner'.
108
+ """
109
+ identifier: str
110
+ """The identifier value. Example: 'user@example.com', 'jdoe', 'automated-scanner-01'."""
111
+
112
+ type: TypeEnum
113
+ """The type of identifier. Use 'email' for email addresses, 'username' for user accounts,
114
+ 'system' for automated systems, 'simple' for basic string identifiers without additional
115
+ classification, or 'other' for custom identity systems.
116
+ """
117
+ description: Optional[str] = None
118
+ """Optional description of the identity or identity system, particularly useful when type is
119
+ 'other'.
120
+ """
121
+
122
+ @staticmethod
123
+ def from_dict(obj: Any) -> 'Identity':
124
+ assert isinstance(obj, dict)
125
+ identifier = from_str(obj.get("identifier"))
126
+ type = TypeEnum(obj.get("type"))
127
+ description = from_union([from_str, from_none], obj.get("description"))
128
+ return Identity(identifier, type, description)
129
+
130
+ def to_dict(self) -> dict:
131
+ result: dict = {}
132
+ result["identifier"] = from_str(self.identifier)
133
+ result["type"] = to_enum(TypeEnum, self.type)
134
+ if self.description is not None:
135
+ result["description"] = from_union([from_str, from_none], self.description)
136
+ return result
137
+
138
+
139
+ @dataclass
140
+ class InputOverride:
141
+ """An override of a baseline input value for a specific component. Enables system-specific
142
+ tailoring of baseline parameters.
143
+ """
144
+ input_name: str
145
+ """Name of the input being overridden. Must match an Input.name in the referenced baseline."""
146
+
147
+ value: Any
148
+ """The overridden value. Should match the type of the original input."""
149
+
150
+ approved_by: Optional[Identity] = None
151
+ """Identity of the person or system that approved this override."""
152
+
153
+ baseline_ref: Optional[str] = None
154
+ """Name of the baseline this override applies to. If omitted, applies to all baselines that
155
+ define this input.
156
+ """
157
+ justification: Optional[str] = None
158
+ """Rationale for why this override is needed."""
159
+
160
+ @staticmethod
161
+ def from_dict(obj: Any) -> 'InputOverride':
162
+ assert isinstance(obj, dict)
163
+ input_name = from_str(obj.get("inputName"))
164
+ value = obj.get("value")
165
+ approved_by = from_union([Identity.from_dict, from_none], obj.get("approvedBy"))
166
+ baseline_ref = from_union([from_str, from_none], obj.get("baselineRef"))
167
+ justification = from_union([from_str, from_none], obj.get("justification"))
168
+ return InputOverride(input_name, value, approved_by, baseline_ref, justification)
169
+
170
+ def to_dict(self) -> dict:
171
+ result: dict = {}
172
+ result["inputName"] = from_str(self.input_name)
173
+ result["value"] = self.value
174
+ if self.approved_by is not None:
175
+ result["approvedBy"] = from_union([lambda x: to_class(Identity, x), from_none], self.approved_by)
176
+ if self.baseline_ref is not None:
177
+ result["baselineRef"] = from_union([from_str, from_none], self.baseline_ref)
178
+ if self.justification is not None:
179
+ result["justification"] = from_union([from_str, from_none], self.justification)
180
+ return result
181
+
182
+
183
+ class CloudProvider(Enum):
184
+ AWS = "aws"
185
+ AZURE = "azure"
186
+ GCP = "gcp"
187
+ OCI = "oci"
188
+ OTHER = "other"
189
+
190
+
191
+ class SbomFormat(Enum):
192
+ """Format of the SBOM (embedded or referenced). Required when sbom or sbomRef is present."""
193
+
194
+ CYCLONEDX = "cyclonedx"
195
+ SPDX = "spdx"
196
+
197
+
198
+ class BoundaryDescription(Enum):
199
+ """IP address of the host."""
200
+
201
+ APPLICATION = "application"
202
+ ARTIFACT = "artifact"
203
+ CLOUD_ACCOUNT = "cloudAccount"
204
+ CLOUD_RESOURCE = "cloudResource"
205
+ CONTAINER_IMAGE = "containerImage"
206
+ CONTAINER_INSTANCE = "containerInstance"
207
+ CONTAINER_PLATFORM = "containerPlatform"
208
+ DATABASE = "database"
209
+ HOST = "host"
210
+ NETWORK = "network"
211
+ REPOSITORY = "repository"
212
+
213
+
214
+ @dataclass
215
+ class Component:
216
+ """A system component. Uses discriminated union pattern with 'type' field as discriminator.
217
+ Superset of Target with identity, external IDs, and SBOM support.
218
+
219
+ A physical or virtual server, workstation, or network device.
220
+
221
+ Base properties shared by all component types. Extends the Target concept with stable
222
+ identity, external references, and SBOM embedding.
223
+
224
+ A static container image (not running).
225
+
226
+ A running container instance.
227
+
228
+ A container orchestration platform (Kubernetes, OpenShift, ECS, etc.).
229
+
230
+ A cloud provider account (AWS account, Azure subscription, GCP project).
231
+
232
+ A specific cloud resource (EC2 instance, S3 bucket, Azure VM, etc.).
233
+
234
+ A code repository (for SAST tools).
235
+
236
+ A running application or API (for DAST tools).
237
+
238
+ A software artifact or dependency (for SCA tools).
239
+
240
+ A network segment or network device.
241
+
242
+ A database instance.
243
+ """
244
+ name: str
245
+ """Human-readable name for this component."""
246
+
247
+ type: BoundaryDescription
248
+ """Component type discriminator. Same values as Target types."""
249
+
250
+ baseline_refs: Optional[List[str]] = None
251
+ """Names of baselines that apply to this component."""
252
+
253
+ component_id: Optional[UUID] = None
254
+ """Stable UUID (RFC 4122) for this component. Required in hdf-system documents, optional in
255
+ hdf-results. Enables cross-document correlation, diffing, and data flow references.
256
+ """
257
+ description: Optional[str] = None
258
+ """Description of this component's role or purpose."""
259
+
260
+ external_ids: Optional[Dict[str, str]] = None
261
+ """Map of external identifier scheme to value. Well-known schemes: aws (instance ID), azure
262
+ (resource ID), cmdb (asset ID), emass (system ID), cve (CVE ID). Custom schemes are
263
+ allowed.
264
+ """
265
+ input_overrides: Optional[List[InputOverride]] = None
266
+ """System-specific overrides for baseline input values."""
267
+
268
+ labels: Optional[Dict[str, str]] = None
269
+ """Optional key-value labels for flexible grouping. Well-known keys: system, component,
270
+ environment, region, team. Values must be strings.
271
+ """
272
+ owner: Optional[Identity] = None
273
+ """Team or individual responsible for this component. Enables per-component ownership when
274
+ different teams manage different parts of a system.
275
+ """
276
+ sbom: Any
277
+ """Embedded CycloneDX or SPDX SBOM document representing this component's software
278
+ inventory. The sbomFormat field determines which format constraints apply.
279
+ """
280
+ sbom_format: Optional[SbomFormat] = None
281
+ """Format of the SBOM (embedded or referenced). Required when sbom or sbomRef is present."""
282
+
283
+ sbom_ref: Optional[str] = None
284
+ """URI reference to an external CycloneDX or SPDX SBOM document for this component. May be a
285
+ relative path, absolute URI, or fragment identifier.
286
+ """
287
+ target_selector: Optional[Dict[str, str]] = None
288
+ """Label selector to match targets belonging to this component during migration. Targets
289
+ with matching labels are automatically included.
290
+ """
291
+ fqdn: Optional[str] = None
292
+ """Fully qualified domain name."""
293
+
294
+ ip_address: Optional[str] = None
295
+ """IP address of the host."""
296
+
297
+ mac_address: Optional[str] = None
298
+ """MAC address in colon-separated hexadecimal format."""
299
+
300
+ os_name: Optional[str] = None
301
+ """Operating system name."""
302
+
303
+ os_version: Optional[str] = None
304
+ """Operating system version."""
305
+
306
+ digest: Optional[str] = None
307
+ """Image digest for immutable reference."""
308
+
309
+ image_id: Optional[str] = None
310
+ """Container image ID."""
311
+
312
+ registry: Optional[str] = None
313
+ """Container registry. Example: 'docker.io'."""
314
+
315
+ repository: Optional[str] = None
316
+ """Repository name. Example: 'library/nginx'."""
317
+
318
+ tag: Optional[str] = None
319
+ """Image tag. Example: '1.25'."""
320
+
321
+ container_id: Optional[str] = None
322
+ """Running container ID."""
323
+
324
+ image: Optional[str] = None
325
+ """Image the container was started from."""
326
+
327
+ runtime: Optional[str] = None
328
+ """Container runtime. Example: 'docker', 'containerd', 'cri-o'."""
329
+
330
+ cluster_name: Optional[str] = None
331
+ """Cluster name."""
332
+
333
+ namespace: Optional[str] = None
334
+ """Namespace within the cluster, if applicable."""
335
+
336
+ platform_type: Optional[str] = None
337
+ """Platform type. Example: 'kubernetes', 'openshift', 'ecs', 'docker-swarm'."""
338
+
339
+ version: Optional[str] = None
340
+ """Platform version.
341
+
342
+ Application version.
343
+
344
+ Package version.
345
+
346
+ Database version.
347
+ """
348
+ account_id: Optional[str] = None
349
+ """Cloud account identifier."""
350
+
351
+ provider: Optional[CloudProvider] = None
352
+ """Cloud provider."""
353
+
354
+ region: Optional[str] = None
355
+ """Cloud region, if applicable.
356
+
357
+ Cloud region where the resource resides.
358
+ """
359
+ arn: Optional[str] = None
360
+ """Amazon Resource Name (AWS only)."""
361
+
362
+ resource_id: Optional[str] = None
363
+ """Provider-specific resource identifier."""
364
+
365
+ resource_type: Optional[str] = None
366
+ """Type of cloud resource. Example: 'ec2:instance', 's3:bucket'."""
367
+
368
+ branch: Optional[str] = None
369
+ """Branch that was scanned."""
370
+
371
+ commit: Optional[str] = None
372
+ """Commit SHA that was scanned."""
373
+
374
+ url: Optional[str] = None
375
+ """Repository URL.
376
+
377
+ Application URL (for DAST tools).
378
+ """
379
+ environment: Optional[str] = None
380
+ """Environment. Example: 'production', 'staging', 'development'."""
381
+
382
+ checksum: Optional[str] = None
383
+ """Package checksum for verification."""
384
+
385
+ package_manager: Optional[str] = None
386
+ """Package manager. Example: 'npm', 'maven', 'pip', 'nuget'."""
387
+
388
+ package_name: Optional[str] = None
389
+ """Package name."""
390
+
391
+ cidr: Optional[str] = None
392
+ """Network CIDR block."""
393
+
394
+ gateway: Optional[str] = None
395
+ """Network gateway address."""
396
+
397
+ engine: Optional[str] = None
398
+ """Database engine. Example: 'postgresql', 'mysql', 'oracle', 'mssql'."""
399
+
400
+ host: Optional[str] = None
401
+ """Database host."""
402
+
403
+ port: Optional[int] = None
404
+ """Database port."""
405
+
406
+ @staticmethod
407
+ def from_dict(obj: Any) -> 'Component':
408
+ assert isinstance(obj, dict)
409
+ name = from_str(obj.get("name"))
410
+ type = BoundaryDescription(obj.get("type"))
411
+ baseline_refs = from_union([lambda x: from_list(from_str, x), from_none], obj.get("baselineRefs"))
412
+ component_id = from_union([lambda x: UUID(x), from_none], obj.get("componentId"))
413
+ description = from_union([from_str, from_none], obj.get("description"))
414
+ external_ids = from_union([lambda x: from_dict(from_str, x), from_none], obj.get("externalIds"))
415
+ input_overrides = from_union([lambda x: from_list(InputOverride.from_dict, x), from_none], obj.get("inputOverrides"))
416
+ labels = from_union([lambda x: from_dict(from_str, x), from_none], obj.get("labels"))
417
+ owner = from_union([Identity.from_dict, from_none], obj.get("owner"))
418
+ sbom = obj.get("sbom")
419
+ sbom_format = from_union([SbomFormat, from_none], obj.get("sbomFormat"))
420
+ sbom_ref = from_union([from_str, from_none], obj.get("sbomRef"))
421
+ target_selector = from_union([lambda x: from_dict(from_str, x), from_none], obj.get("targetSelector"))
422
+ fqdn = from_union([from_str, from_none], obj.get("fqdn"))
423
+ ip_address = from_union([from_str, from_none], obj.get("ipAddress"))
424
+ mac_address = from_union([from_str, from_none], obj.get("macAddress"))
425
+ os_name = from_union([from_str, from_none], obj.get("osName"))
426
+ os_version = from_union([from_str, from_none], obj.get("osVersion"))
427
+ digest = from_union([from_str, from_none], obj.get("digest"))
428
+ image_id = from_union([from_str, from_none], obj.get("imageId"))
429
+ registry = from_union([from_str, from_none], obj.get("registry"))
430
+ repository = from_union([from_str, from_none], obj.get("repository"))
431
+ tag = from_union([from_str, from_none], obj.get("tag"))
432
+ container_id = from_union([from_str, from_none], obj.get("containerId"))
433
+ image = from_union([from_str, from_none], obj.get("image"))
434
+ runtime = from_union([from_str, from_none], obj.get("runtime"))
435
+ cluster_name = from_union([from_str, from_none], obj.get("clusterName"))
436
+ namespace = from_union([from_str, from_none], obj.get("namespace"))
437
+ platform_type = from_union([from_str, from_none], obj.get("platformType"))
438
+ version = from_union([from_str, from_none], obj.get("version"))
439
+ account_id = from_union([from_str, from_none], obj.get("accountId"))
440
+ provider = from_union([from_none, CloudProvider], obj.get("provider"))
441
+ region = from_union([from_str, from_none], obj.get("region"))
442
+ arn = from_union([from_str, from_none], obj.get("arn"))
443
+ resource_id = from_union([from_str, from_none], obj.get("resourceId"))
444
+ resource_type = from_union([from_str, from_none], obj.get("resourceType"))
445
+ branch = from_union([from_str, from_none], obj.get("branch"))
446
+ commit = from_union([from_str, from_none], obj.get("commit"))
447
+ url = from_union([from_str, from_none], obj.get("url"))
448
+ environment = from_union([from_str, from_none], obj.get("environment"))
449
+ checksum = from_union([from_str, from_none], obj.get("checksum"))
450
+ package_manager = from_union([from_str, from_none], obj.get("packageManager"))
451
+ package_name = from_union([from_str, from_none], obj.get("packageName"))
452
+ cidr = from_union([from_str, from_none], obj.get("cidr"))
453
+ gateway = from_union([from_str, from_none], obj.get("gateway"))
454
+ engine = from_union([from_str, from_none], obj.get("engine"))
455
+ host = from_union([from_str, from_none], obj.get("host"))
456
+ port = from_union([from_int, from_none], obj.get("port"))
457
+ return Component(name, type, baseline_refs, component_id, description, external_ids, input_overrides, labels, owner, sbom, sbom_format, sbom_ref, target_selector, fqdn, ip_address, mac_address, os_name, os_version, digest, image_id, registry, repository, tag, container_id, image, runtime, cluster_name, namespace, platform_type, version, account_id, provider, region, arn, resource_id, resource_type, branch, commit, url, environment, checksum, package_manager, package_name, cidr, gateway, engine, host, port)
458
+
459
+ def to_dict(self) -> dict:
460
+ result: dict = {}
461
+ result["name"] = from_str(self.name)
462
+ result["type"] = to_enum(BoundaryDescription, self.type)
463
+ if self.baseline_refs is not None:
464
+ result["baselineRefs"] = from_union([lambda x: from_list(from_str, x), from_none], self.baseline_refs)
465
+ if self.component_id is not None:
466
+ result["componentId"] = from_union([lambda x: str(x), from_none], self.component_id)
467
+ if self.description is not None:
468
+ result["description"] = from_union([from_str, from_none], self.description)
469
+ if self.external_ids is not None:
470
+ result["externalIds"] = from_union([lambda x: from_dict(from_str, x), from_none], self.external_ids)
471
+ if self.input_overrides is not None:
472
+ result["inputOverrides"] = from_union([lambda x: from_list(lambda x: to_class(InputOverride, x), x), from_none], self.input_overrides)
473
+ if self.labels is not None:
474
+ result["labels"] = from_union([lambda x: from_dict(from_str, x), from_none], self.labels)
475
+ if self.owner is not None:
476
+ result["owner"] = from_union([lambda x: to_class(Identity, x), from_none], self.owner)
477
+ if self.sbom is not None:
478
+ result["sbom"] = self.sbom
479
+ if self.sbom_format is not None:
480
+ result["sbomFormat"] = from_union([lambda x: to_enum(SbomFormat, x), from_none], self.sbom_format)
481
+ if self.sbom_ref is not None:
482
+ result["sbomRef"] = from_union([from_str, from_none], self.sbom_ref)
483
+ if self.target_selector is not None:
484
+ result["targetSelector"] = from_union([lambda x: from_dict(from_str, x), from_none], self.target_selector)
485
+ if self.fqdn is not None:
486
+ result["fqdn"] = from_union([from_str, from_none], self.fqdn)
487
+ if self.ip_address is not None:
488
+ result["ipAddress"] = from_union([from_str, from_none], self.ip_address)
489
+ if self.mac_address is not None:
490
+ result["macAddress"] = from_union([from_str, from_none], self.mac_address)
491
+ if self.os_name is not None:
492
+ result["osName"] = from_union([from_str, from_none], self.os_name)
493
+ if self.os_version is not None:
494
+ result["osVersion"] = from_union([from_str, from_none], self.os_version)
495
+ if self.digest is not None:
496
+ result["digest"] = from_union([from_str, from_none], self.digest)
497
+ if self.image_id is not None:
498
+ result["imageId"] = from_union([from_str, from_none], self.image_id)
499
+ if self.registry is not None:
500
+ result["registry"] = from_union([from_str, from_none], self.registry)
501
+ if self.repository is not None:
502
+ result["repository"] = from_union([from_str, from_none], self.repository)
503
+ if self.tag is not None:
504
+ result["tag"] = from_union([from_str, from_none], self.tag)
505
+ if self.container_id is not None:
506
+ result["containerId"] = from_union([from_str, from_none], self.container_id)
507
+ if self.image is not None:
508
+ result["image"] = from_union([from_str, from_none], self.image)
509
+ if self.runtime is not None:
510
+ result["runtime"] = from_union([from_str, from_none], self.runtime)
511
+ if self.cluster_name is not None:
512
+ result["clusterName"] = from_union([from_str, from_none], self.cluster_name)
513
+ if self.namespace is not None:
514
+ result["namespace"] = from_union([from_str, from_none], self.namespace)
515
+ if self.platform_type is not None:
516
+ result["platformType"] = from_union([from_str, from_none], self.platform_type)
517
+ if self.version is not None:
518
+ result["version"] = from_union([from_str, from_none], self.version)
519
+ if self.account_id is not None:
520
+ result["accountId"] = from_union([from_str, from_none], self.account_id)
521
+ if self.provider is not None:
522
+ result["provider"] = from_union([from_none, lambda x: to_enum(CloudProvider, x)], self.provider)
523
+ if self.region is not None:
524
+ result["region"] = from_union([from_str, from_none], self.region)
525
+ if self.arn is not None:
526
+ result["arn"] = from_union([from_str, from_none], self.arn)
527
+ if self.resource_id is not None:
528
+ result["resourceId"] = from_union([from_str, from_none], self.resource_id)
529
+ if self.resource_type is not None:
530
+ result["resourceType"] = from_union([from_str, from_none], self.resource_type)
531
+ if self.branch is not None:
532
+ result["branch"] = from_union([from_str, from_none], self.branch)
533
+ if self.commit is not None:
534
+ result["commit"] = from_union([from_str, from_none], self.commit)
535
+ if self.url is not None:
536
+ result["url"] = from_union([from_str, from_none], self.url)
537
+ if self.environment is not None:
538
+ result["environment"] = from_union([from_str, from_none], self.environment)
539
+ if self.checksum is not None:
540
+ result["checksum"] = from_union([from_str, from_none], self.checksum)
541
+ if self.package_manager is not None:
542
+ result["packageManager"] = from_union([from_str, from_none], self.package_manager)
543
+ if self.package_name is not None:
544
+ result["packageName"] = from_union([from_str, from_none], self.package_name)
545
+ if self.cidr is not None:
546
+ result["cidr"] = from_union([from_str, from_none], self.cidr)
547
+ if self.gateway is not None:
548
+ result["gateway"] = from_union([from_str, from_none], self.gateway)
549
+ if self.engine is not None:
550
+ result["engine"] = from_union([from_str, from_none], self.engine)
551
+ if self.host is not None:
552
+ result["host"] = from_union([from_str, from_none], self.host)
553
+ if self.port is not None:
554
+ result["port"] = from_union([from_int, from_none], self.port)
555
+ return result
556
+
557
+
558
+ class Designation(Enum):
559
+ """NIST SP 800-53 control designation. 'common': fully provided by another component or
560
+ system. 'system-specific': implemented by the inheriting component(s) only. 'hybrid':
561
+ shared responsibility between provider and inheritor.
562
+ """
563
+ COMMON = "common"
564
+ HYBRID = "hybrid"
565
+ SYSTEM_SPECIFIC = "system-specific"
566
+
567
+
568
+ @dataclass
569
+ class ControlDesignation:
570
+ """Declares a control's designation within a system — whether it is common (provided by
571
+ another component or system), system-specific (implemented locally), or hybrid (shared
572
+ responsibility). Maps to NIST SP 800-53 Appendix C control designations and OSCAL SSP
573
+ by-component provided/inherited semantics.
574
+ """
575
+ control_id: str
576
+ """The control identifier (e.g., 'SC-7', 'AC-2 (1)'). Must match a NIST tag in a baseline
577
+ requirement's tags.
578
+ """
579
+ description: str
580
+ """Justification for this designation — who provides the control, why it's inherited, and
581
+ any relevant authorization references.
582
+ """
583
+ designation: Designation
584
+ """NIST SP 800-53 control designation. 'common': fully provided by another component or
585
+ system. 'system-specific': implemented by the inheriting component(s) only. 'hybrid':
586
+ shared responsibility between provider and inheritor.
587
+ """
588
+ inherited_by: Optional[List[UUID]] = None
589
+ """componentIds that inherit this control. If omitted, all components in the system inherit
590
+ it.
591
+ """
592
+ provided_by: Optional[UUID] = None
593
+ """componentId of a local component that provides this control. Omit when the provider is an
594
+ external system.
595
+ """
596
+ system_ref: Optional[str] = None
597
+ """Reference to another hdf-system document whose component provides this control. Use when
598
+ the provider is in a different system. Omit when the provider is local.
599
+ """
600
+
601
+ @staticmethod
602
+ def from_dict(obj: Any) -> 'ControlDesignation':
603
+ assert isinstance(obj, dict)
604
+ control_id = from_str(obj.get("controlId"))
605
+ description = from_str(obj.get("description"))
606
+ designation = Designation(obj.get("designation"))
607
+ inherited_by = from_union([lambda x: from_list(lambda x: UUID(x), x), from_none], obj.get("inheritedBy"))
608
+ provided_by = from_union([lambda x: UUID(x), from_none], obj.get("providedBy"))
609
+ system_ref = from_union([from_str, from_none], obj.get("systemRef"))
610
+ return ControlDesignation(control_id, description, designation, inherited_by, provided_by, system_ref)
611
+
612
+ def to_dict(self) -> dict:
613
+ result: dict = {}
614
+ result["controlId"] = from_str(self.control_id)
615
+ result["description"] = from_str(self.description)
616
+ result["designation"] = to_enum(Designation, self.designation)
617
+ if self.inherited_by is not None:
618
+ result["inheritedBy"] = from_union([lambda x: from_list(lambda x: str(x), x), from_none], self.inherited_by)
619
+ if self.provided_by is not None:
620
+ result["providedBy"] = from_union([lambda x: str(x), from_none], self.provided_by)
621
+ if self.system_ref is not None:
622
+ result["systemRef"] = from_union([from_str, from_none], self.system_ref)
623
+ return result
624
+
625
+
626
+ class Direction(Enum):
627
+ """Data flow direction. 'unidirectional' means data flows from→to only. 'bidirectional'
628
+ means data flows in both directions (e.g., request/response).
629
+ """
630
+ BIDIRECTIONAL = "bidirectional"
631
+ UNIDIRECTIONAL = "unidirectional"
632
+
633
+
634
+ @dataclass
635
+ class DataFlow:
636
+ """A data flow between two endpoints. The 'from' endpoint is always a local component; the
637
+ 'to' endpoint can be local, cross-system, or external. Use 'direction' to indicate
638
+ whether data flows one-way or both ways.
639
+ """
640
+ data_flow_from: UUID
641
+ """UUID of the local component that is one end of this data flow. Always references a
642
+ component in the current system document.
643
+ """
644
+ to: Any
645
+ """The other end of this data flow. Can be a local component (UUID), a cross-system
646
+ component reference, or an external endpoint.
647
+ """
648
+ authentication: Optional[str] = None
649
+ """Authentication mechanism used for this connection. Examples: 'mTLS', 'OAuth2', 'API key',
650
+ 'SAML', 'Kerberos'.
651
+ """
652
+ description: Optional[str] = None
653
+ """Human-readable description of this data flow's purpose and the data exchanged."""
654
+
655
+ direction: Optional[Direction] = None
656
+ """Data flow direction. 'unidirectional' means data flows from→to only. 'bidirectional'
657
+ means data flows in both directions (e.g., request/response).
658
+ """
659
+ port: Optional[int] = None
660
+ """Network port number."""
661
+
662
+ protocol: Optional[str] = None
663
+ """Communication protocol. Examples: 'http', 'https', 'grpc', 'ssh', 'jdbc', 'k8s-api',
664
+ 'socket', 'sftp'.
665
+ """
666
+
667
+ @staticmethod
668
+ def from_dict(obj: Any) -> 'DataFlow':
669
+ assert isinstance(obj, dict)
670
+ data_flow_from = UUID(obj.get("from"))
671
+ to = obj.get("to")
672
+ authentication = from_union([from_str, from_none], obj.get("authentication"))
673
+ description = from_union([from_str, from_none], obj.get("description"))
674
+ direction = from_union([Direction, from_none], obj.get("direction"))
675
+ port = from_union([from_int, from_none], obj.get("port"))
676
+ protocol = from_union([from_str, from_none], obj.get("protocol"))
677
+ return DataFlow(data_flow_from, to, authentication, description, direction, port, protocol)
678
+
679
+ def to_dict(self) -> dict:
680
+ result: dict = {}
681
+ result["from"] = str(self.data_flow_from)
682
+ result["to"] = self.to
683
+ if self.authentication is not None:
684
+ result["authentication"] = from_union([from_str, from_none], self.authentication)
685
+ if self.description is not None:
686
+ result["description"] = from_union([from_str, from_none], self.description)
687
+ if self.direction is not None:
688
+ result["direction"] = from_union([lambda x: to_enum(Direction, x), from_none], self.direction)
689
+ if self.port is not None:
690
+ result["port"] = from_union([from_int, from_none], self.port)
691
+ if self.protocol is not None:
692
+ result["protocol"] = from_union([from_str, from_none], self.protocol)
693
+ return result
694
+
695
+
696
+ @dataclass
697
+ class Generator:
698
+ """Information about the tool that generated this system document.
699
+
700
+ Information about the tool that generated this HDF file.
701
+ """
702
+ name: str
703
+ """The name of the software that produced this HDF file. Example: 'gosec-to-hdf'."""
704
+
705
+ version: str
706
+ """The version of the tool. Example: '5.22.3'."""
707
+
708
+ @staticmethod
709
+ def from_dict(obj: Any) -> 'Generator':
710
+ assert isinstance(obj, dict)
711
+ name = from_str(obj.get("name"))
712
+ version = from_str(obj.get("version"))
713
+ return Generator(name, version)
714
+
715
+ def to_dict(self) -> dict:
716
+ result: dict = {}
717
+ result["name"] = from_str(self.name)
718
+ result["version"] = from_str(self.version)
719
+ return result
720
+
721
+
722
+ class HashAlgorithm(Enum):
723
+ """The hash algorithm used for the checksum.
724
+
725
+ Supported cryptographic hash algorithms for checksums and integrity verification.
726
+ """
727
+ SHA256 = "sha256"
728
+ SHA384 = "sha384"
729
+ SHA512 = "sha512"
730
+
731
+
732
+ @dataclass
733
+ class Integrity:
734
+ """Cryptographic integrity information for verifying this system document has not been
735
+ tampered with.
736
+
737
+ Cryptographic integrity information for verifying the HDF file has not been tampered
738
+ with. If algorithm is provided, checksum must also be provided, and vice versa.
739
+ """
740
+ algorithm: Optional[HashAlgorithm] = None
741
+ """The hash algorithm used for the checksum."""
742
+
743
+ checksum: Optional[str] = None
744
+ """The checksum value."""
745
+
746
+ signature: Optional[str] = None
747
+ """Optional cryptographic signature."""
748
+
749
+ signed_by: Optional[str] = None
750
+ """Identifier of who signed this file."""
751
+
752
+ @staticmethod
753
+ def from_dict(obj: Any) -> 'Integrity':
754
+ assert isinstance(obj, dict)
755
+ algorithm = from_union([HashAlgorithm, from_none], obj.get("algorithm"))
756
+ checksum = from_union([from_str, from_none], obj.get("checksum"))
757
+ signature = from_union([from_str, from_none], obj.get("signature"))
758
+ signed_by = from_union([from_str, from_none], obj.get("signedBy"))
759
+ return Integrity(algorithm, checksum, signature, signed_by)
760
+
761
+ def to_dict(self) -> dict:
762
+ result: dict = {}
763
+ if self.algorithm is not None:
764
+ result["algorithm"] = from_union([lambda x: to_enum(HashAlgorithm, x), from_none], self.algorithm)
765
+ if self.checksum is not None:
766
+ result["checksum"] = from_union([from_str, from_none], self.checksum)
767
+ if self.signature is not None:
768
+ result["signature"] = from_union([from_str, from_none], self.signature)
769
+ if self.signed_by is not None:
770
+ result["signedBy"] = from_union([from_str, from_none], self.signed_by)
771
+ return result
772
+
773
+
774
+ @dataclass
775
+ class HdfSystem:
776
+ """Describes a system's authorization boundary, components, and interconnections. Maps to
777
+ OSCAL SSP system-characteristics and FedRAMP system inventory.
778
+ """
779
+ components: List[Component]
780
+ """System components within the authorization boundary. Uses the full polymorphic Component
781
+ type with stable identity (componentId), external references, and SBOM support.
782
+ """
783
+ name: str
784
+ """Human-readable system name. Example: 'Enterprise Portal Production'."""
785
+
786
+ authorization_date: Optional[datetime] = None
787
+ """Date the current authorization status was granted. ISO 8601 format."""
788
+
789
+ authorization_status: Optional[AuthorizationStatus] = None
790
+ """Current Authorization to Operate (ATO) status."""
791
+
792
+ boundary_description: Optional[str] = None
793
+ """Description of the system's authorization boundary. Example: network CIDR blocks, cloud
794
+ VPC IDs, physical locations.
795
+ """
796
+ categorization_level: Optional[CategorizationLevel] = None
797
+ """FIPS 199 security categorization (impact level)."""
798
+
799
+ control_designations: Optional[List[ControlDesignation]] = None
800
+ """Declares which controls are common, hybrid, or system-specific, and which component
801
+ provides them. Maps to NIST SP 800-53 control designations and OSCAL
802
+ leveraged-authorizations.
803
+ """
804
+ data_flows: Optional[List[DataFlow]] = None
805
+ """Inter-component data flows describing how components communicate. Supports local,
806
+ cross-system, and external flows. Replaces the interconnections[] field.
807
+ """
808
+ description: Optional[str] = None
809
+ """Description of the system's purpose and mission."""
810
+
811
+ generator: Optional[Generator] = None
812
+ """Information about the tool that generated this system document."""
813
+
814
+ identifier: Optional[str] = None
815
+ """System identifier from an authoritative source. Example: eMASS system ID, FedRAMP package
816
+ ID.
817
+ """
818
+ identifier_scheme: Optional[str] = None
819
+ """URI identifying the scheme of the system identifier. Example: 'https://emass.mil',
820
+ 'https://fedramp.gov'.
821
+ """
822
+ integrity: Optional[Integrity] = None
823
+ """Cryptographic integrity information for verifying this system document has not been
824
+ tampered with.
825
+ """
826
+ labels: Optional[Dict[str, str]] = None
827
+ """Optional key-value labels for grouping and querying systems."""
828
+
829
+ owner: Optional[Identity] = None
830
+ """Team or individual responsible for this system's authorization and compliance. Maps to
831
+ OSCAL responsible-party with role 'system-owner'.
832
+ """
833
+ system_id: Optional[UUID] = None
834
+ """Stable UUID (RFC 4122) for this system. Enables cross-document correlation independent of
835
+ file location. Optional in casual use, expected in production documents.
836
+ """
837
+ version: Optional[str] = None
838
+ """Version of this system document."""
839
+
840
+ @staticmethod
841
+ def from_dict(obj: Any) -> 'HdfSystem':
842
+ assert isinstance(obj, dict)
843
+ components = from_list(Component.from_dict, obj.get("components"))
844
+ name = from_str(obj.get("name"))
845
+ authorization_date = from_union([from_datetime, from_none], obj.get("authorizationDate"))
846
+ authorization_status = from_union([AuthorizationStatus, from_none], obj.get("authorizationStatus"))
847
+ boundary_description = from_union([from_str, from_none], obj.get("boundaryDescription"))
848
+ categorization_level = from_union([CategorizationLevel, from_none], obj.get("categorizationLevel"))
849
+ control_designations = from_union([lambda x: from_list(ControlDesignation.from_dict, x), from_none], obj.get("controlDesignations"))
850
+ data_flows = from_union([lambda x: from_list(DataFlow.from_dict, x), from_none], obj.get("dataFlows"))
851
+ description = from_union([from_str, from_none], obj.get("description"))
852
+ generator = from_union([Generator.from_dict, from_none], obj.get("generator"))
853
+ identifier = from_union([from_str, from_none], obj.get("identifier"))
854
+ identifier_scheme = from_union([from_str, from_none], obj.get("identifierScheme"))
855
+ integrity = from_union([Integrity.from_dict, from_none], obj.get("integrity"))
856
+ labels = from_union([lambda x: from_dict(from_str, x), from_none], obj.get("labels"))
857
+ owner = from_union([Identity.from_dict, from_none], obj.get("owner"))
858
+ system_id = from_union([lambda x: UUID(x), from_none], obj.get("systemId"))
859
+ version = from_union([from_str, from_none], obj.get("version"))
860
+ return HdfSystem(components, name, authorization_date, authorization_status, boundary_description, categorization_level, control_designations, data_flows, description, generator, identifier, identifier_scheme, integrity, labels, owner, system_id, version)
861
+
862
+ def to_dict(self) -> dict:
863
+ result: dict = {}
864
+ result["components"] = from_list(lambda x: to_class(Component, x), self.components)
865
+ result["name"] = from_str(self.name)
866
+ if self.authorization_date is not None:
867
+ result["authorizationDate"] = from_union([lambda x: x.isoformat(), from_none], self.authorization_date)
868
+ if self.authorization_status is not None:
869
+ result["authorizationStatus"] = from_union([lambda x: to_enum(AuthorizationStatus, x), from_none], self.authorization_status)
870
+ if self.boundary_description is not None:
871
+ result["boundaryDescription"] = from_union([from_str, from_none], self.boundary_description)
872
+ if self.categorization_level is not None:
873
+ result["categorizationLevel"] = from_union([lambda x: to_enum(CategorizationLevel, x), from_none], self.categorization_level)
874
+ if self.control_designations is not None:
875
+ result["controlDesignations"] = from_union([lambda x: from_list(lambda x: to_class(ControlDesignation, x), x), from_none], self.control_designations)
876
+ if self.data_flows is not None:
877
+ result["dataFlows"] = from_union([lambda x: from_list(lambda x: to_class(DataFlow, x), x), from_none], self.data_flows)
878
+ if self.description is not None:
879
+ result["description"] = from_union([from_str, from_none], self.description)
880
+ if self.generator is not None:
881
+ result["generator"] = from_union([lambda x: to_class(Generator, x), from_none], self.generator)
882
+ if self.identifier is not None:
883
+ result["identifier"] = from_union([from_str, from_none], self.identifier)
884
+ if self.identifier_scheme is not None:
885
+ result["identifierScheme"] = from_union([from_str, from_none], self.identifier_scheme)
886
+ if self.integrity is not None:
887
+ result["integrity"] = from_union([lambda x: to_class(Integrity, x), from_none], self.integrity)
888
+ if self.labels is not None:
889
+ result["labels"] = from_union([lambda x: from_dict(from_str, x), from_none], self.labels)
890
+ if self.owner is not None:
891
+ result["owner"] = from_union([lambda x: to_class(Identity, x), from_none], self.owner)
892
+ if self.system_id is not None:
893
+ result["systemId"] = from_union([lambda x: str(x), from_none], self.system_id)
894
+ if self.version is not None:
895
+ result["version"] = from_union([from_str, from_none], self.version)
896
+ return result
897
+
898
+
899
+ def hdf_system_from_dict(s: Any) -> HdfSystem:
900
+ return HdfSystem.from_dict(s)
901
+
902
+
903
+ def hdf_system_to_dict(x: HdfSystem) -> Any:
904
+ return to_class(HdfSystem, x)