@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,363 @@
1
+ from dataclasses import dataclass
2
+ from typing import Optional, Any, Dict, List, TypeVar, Callable, Type, cast
3
+ from uuid import UUID
4
+ from enum import Enum
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 from_dict(f: Callable[[Any], T], x: Any) -> Dict[str, T]:
33
+ assert isinstance(x, dict)
34
+ return { k: f(v) for (k, v) in x.items() }
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 to_enum(c: Type[EnumT], x: Any) -> EnumT:
43
+ assert isinstance(x, c)
44
+ return x.value
45
+
46
+
47
+ def from_datetime(x: Any) -> datetime:
48
+ return dateutil.parser.parse(x)
49
+
50
+
51
+ def from_list(f: Callable[[Any], T], x: Any) -> List[T]:
52
+ assert isinstance(x, list)
53
+ return [f(y) for y in x]
54
+
55
+
56
+ @dataclass
57
+ class RunnerConfig:
58
+ """Runner/scanner configuration for this assessment.
59
+
60
+ Configuration for the assessment runner/scanner.
61
+ """
62
+ name: Optional[str] = None
63
+ """Name of the assessment runner. Example: 'cinc-auditor', 'inspec', 'openscap'."""
64
+
65
+ version: Optional[str] = None
66
+ """Version of the runner."""
67
+
68
+ @staticmethod
69
+ def from_dict(obj: Any) -> 'RunnerConfig':
70
+ assert isinstance(obj, dict)
71
+ name = from_union([from_str, from_none], obj.get("name"))
72
+ version = from_union([from_str, from_none], obj.get("version"))
73
+ return RunnerConfig(name, version)
74
+
75
+ def to_dict(self) -> dict:
76
+ result: dict = {}
77
+ if self.name is not None:
78
+ result["name"] = from_union([from_str, from_none], self.name)
79
+ if self.version is not None:
80
+ result["version"] = from_union([from_str, from_none], self.version)
81
+ return result
82
+
83
+
84
+ @dataclass
85
+ class Assessment:
86
+ """A single assessment within a plan — defines which baseline to run against which targets
87
+ with what configuration.
88
+ """
89
+ baseline_ref: str
90
+ """Reference to the baseline to evaluate. May be a baseline name (e.g. 'RHEL9-STIG'), a
91
+ relative path to an HDF Baseline document (e.g. 'rhel9-stig.hdf-baseline.json'), or an
92
+ absolute URI.
93
+ """
94
+ component_ref: Optional[UUID] = None
95
+ """componentId of the system component this assessment targets. Use for direct component
96
+ binding. Alternative to targetSelector.
97
+ """
98
+ description: Optional[str] = None
99
+ """Description of this assessment's purpose."""
100
+
101
+ inputs: Optional[Dict[str, Any]] = None
102
+ """Resolved input values for this assessment. Keys are input names, values are the final
103
+ resolved values (after baseline defaults + system overrides).
104
+ """
105
+ runner: Optional[RunnerConfig] = None
106
+ """Runner/scanner configuration for this assessment."""
107
+
108
+ target_selector: Optional[Dict[str, str]] = None
109
+ """Label selector to match targets for this assessment. Overrides the system component's
110
+ targetSelector if provided.
111
+ """
112
+
113
+ @staticmethod
114
+ def from_dict(obj: Any) -> 'Assessment':
115
+ assert isinstance(obj, dict)
116
+ baseline_ref = from_str(obj.get("baselineRef"))
117
+ component_ref = from_union([lambda x: UUID(x), from_none], obj.get("componentRef"))
118
+ description = from_union([from_str, from_none], obj.get("description"))
119
+ inputs = from_union([lambda x: from_dict(lambda x: x, x), from_none], obj.get("inputs"))
120
+ runner = from_union([RunnerConfig.from_dict, from_none], obj.get("runner"))
121
+ target_selector = from_union([lambda x: from_dict(from_str, x), from_none], obj.get("targetSelector"))
122
+ return Assessment(baseline_ref, component_ref, description, inputs, runner, target_selector)
123
+
124
+ def to_dict(self) -> dict:
125
+ result: dict = {}
126
+ result["baselineRef"] = from_str(self.baseline_ref)
127
+ if self.component_ref is not None:
128
+ result["componentRef"] = from_union([lambda x: str(x), from_none], self.component_ref)
129
+ if self.description is not None:
130
+ result["description"] = from_union([from_str, from_none], self.description)
131
+ if self.inputs is not None:
132
+ result["inputs"] = from_union([lambda x: from_dict(lambda x: x, x), from_none], self.inputs)
133
+ if self.runner is not None:
134
+ result["runner"] = from_union([lambda x: to_class(RunnerConfig, x), from_none], self.runner)
135
+ if self.target_selector is not None:
136
+ result["targetSelector"] = from_union([lambda x: from_dict(from_str, x), from_none], self.target_selector)
137
+ return result
138
+
139
+
140
+ @dataclass
141
+ class Generator:
142
+ """Information about the tool that generated this plan.
143
+
144
+ Information about the tool that generated this HDF file.
145
+ """
146
+ name: str
147
+ """The name of the software that produced this HDF file. Example: 'gosec-to-hdf'."""
148
+
149
+ version: str
150
+ """The version of the tool. Example: '5.22.3'."""
151
+
152
+ @staticmethod
153
+ def from_dict(obj: Any) -> 'Generator':
154
+ assert isinstance(obj, dict)
155
+ name = from_str(obj.get("name"))
156
+ version = from_str(obj.get("version"))
157
+ return Generator(name, version)
158
+
159
+ def to_dict(self) -> dict:
160
+ result: dict = {}
161
+ result["name"] = from_str(self.name)
162
+ result["version"] = from_str(self.version)
163
+ return result
164
+
165
+
166
+ class HashAlgorithm(Enum):
167
+ """The hash algorithm used for the checksum.
168
+
169
+ Supported cryptographic hash algorithms for checksums and integrity verification.
170
+ """
171
+ SHA256 = "sha256"
172
+ SHA384 = "sha384"
173
+ SHA512 = "sha512"
174
+
175
+
176
+ @dataclass
177
+ class Integrity:
178
+ """Cryptographic integrity information for verifying this plan document has not been
179
+ tampered with.
180
+
181
+ Cryptographic integrity information for verifying the HDF file has not been tampered
182
+ with. If algorithm is provided, checksum must also be provided, and vice versa.
183
+ """
184
+ algorithm: Optional[HashAlgorithm] = None
185
+ """The hash algorithm used for the checksum."""
186
+
187
+ checksum: Optional[str] = None
188
+ """The checksum value."""
189
+
190
+ signature: Optional[str] = None
191
+ """Optional cryptographic signature."""
192
+
193
+ signed_by: Optional[str] = None
194
+ """Identifier of who signed this file."""
195
+
196
+ @staticmethod
197
+ def from_dict(obj: Any) -> 'Integrity':
198
+ assert isinstance(obj, dict)
199
+ algorithm = from_union([HashAlgorithm, from_none], obj.get("algorithm"))
200
+ checksum = from_union([from_str, from_none], obj.get("checksum"))
201
+ signature = from_union([from_str, from_none], obj.get("signature"))
202
+ signed_by = from_union([from_str, from_none], obj.get("signedBy"))
203
+ return Integrity(algorithm, checksum, signature, signed_by)
204
+
205
+ def to_dict(self) -> dict:
206
+ result: dict = {}
207
+ if self.algorithm is not None:
208
+ result["algorithm"] = from_union([lambda x: to_enum(HashAlgorithm, x), from_none], self.algorithm)
209
+ if self.checksum is not None:
210
+ result["checksum"] = from_union([from_str, from_none], self.checksum)
211
+ if self.signature is not None:
212
+ result["signature"] = from_union([from_str, from_none], self.signature)
213
+ if self.signed_by is not None:
214
+ result["signedBy"] = from_union([from_str, from_none], self.signed_by)
215
+ return result
216
+
217
+
218
+ @dataclass
219
+ class Schedule:
220
+ """Optional scheduling configuration for recurring assessments.
221
+
222
+ Scheduling configuration for recurring assessments.
223
+ """
224
+ cron: Optional[str] = None
225
+ """Cron expression for recurring assessments. Example: '0 2 1 * *' (2 AM on the 1st of each
226
+ month).
227
+ """
228
+ end_date: Optional[datetime] = None
229
+ """Date after which assessments should no longer run. ISO 8601 format."""
230
+
231
+ notify_on_completion: Optional[List[str]] = None
232
+ """Email addresses or notification endpoints to alert when assessments complete."""
233
+
234
+ notify_on_regression: Optional[List[str]] = None
235
+ """Email addresses or notification endpoints to alert when regressions are detected."""
236
+
237
+ start_date: Optional[datetime] = None
238
+ """Earliest date to begin assessments. ISO 8601 format."""
239
+
240
+ @staticmethod
241
+ def from_dict(obj: Any) -> 'Schedule':
242
+ assert isinstance(obj, dict)
243
+ cron = from_union([from_str, from_none], obj.get("cron"))
244
+ end_date = from_union([from_datetime, from_none], obj.get("endDate"))
245
+ notify_on_completion = from_union([lambda x: from_list(from_str, x), from_none], obj.get("notifyOnCompletion"))
246
+ notify_on_regression = from_union([lambda x: from_list(from_str, x), from_none], obj.get("notifyOnRegression"))
247
+ start_date = from_union([from_datetime, from_none], obj.get("startDate"))
248
+ return Schedule(cron, end_date, notify_on_completion, notify_on_regression, start_date)
249
+
250
+ def to_dict(self) -> dict:
251
+ result: dict = {}
252
+ if self.cron is not None:
253
+ result["cron"] = from_union([from_str, from_none], self.cron)
254
+ if self.end_date is not None:
255
+ result["endDate"] = from_union([lambda x: x.isoformat(), from_none], self.end_date)
256
+ if self.notify_on_completion is not None:
257
+ result["notifyOnCompletion"] = from_union([lambda x: from_list(from_str, x), from_none], self.notify_on_completion)
258
+ if self.notify_on_regression is not None:
259
+ result["notifyOnRegression"] = from_union([lambda x: from_list(from_str, x), from_none], self.notify_on_regression)
260
+ if self.start_date is not None:
261
+ result["startDate"] = from_union([lambda x: x.isoformat(), from_none], self.start_date)
262
+ return result
263
+
264
+
265
+ class PlanType(Enum):
266
+ """The type of assessment plan.
267
+
268
+ The type of assessment. 'automated' for scanner-driven, 'manual' for human-performed,
269
+ 'hybrid' for both.
270
+ """
271
+ AUTOMATED = "automated"
272
+ HYBRID = "hybrid"
273
+ MANUAL = "manual"
274
+
275
+
276
+ @dataclass
277
+ class HdfPlan:
278
+ """Defines an assessment plan — what baselines to run against which targets, with resolved
279
+ inputs and scheduling. Maps to OSCAL Assessment Plan.
280
+ """
281
+ assessments: List[Assessment]
282
+ """The assessments to perform. Each assessment pairs a baseline with targets and resolved
283
+ inputs.
284
+ """
285
+ name: str
286
+ """Human-readable plan name. Example: 'Portal Monthly Assessment'."""
287
+
288
+ description: Optional[str] = None
289
+ """Description of the plan's purpose and scope."""
290
+
291
+ generator: Optional[Generator] = None
292
+ """Information about the tool that generated this plan."""
293
+
294
+ integrity: Optional[Integrity] = None
295
+ """Cryptographic integrity information for verifying this plan document has not been
296
+ tampered with.
297
+ """
298
+ labels: Optional[Dict[str, str]] = None
299
+ """Optional key-value labels for grouping and querying plans."""
300
+
301
+ plan_id: Optional[UUID] = None
302
+ """Unique identifier for this plan. Optional in casual use, expected in production
303
+ documents. Auto-generated if omitted during creation.
304
+ """
305
+ schedule: Optional[Schedule] = None
306
+ """Optional scheduling configuration for recurring assessments."""
307
+
308
+ system_ref: Optional[str] = None
309
+ """URI to the hdf-system document this plan targets. Example: 'portal-prod.hdf-system.json'."""
310
+
311
+ type: Optional[PlanType] = None
312
+ """The type of assessment plan."""
313
+
314
+ version: Optional[str] = None
315
+ """Version of this plan document."""
316
+
317
+ @staticmethod
318
+ def from_dict(obj: Any) -> 'HdfPlan':
319
+ assert isinstance(obj, dict)
320
+ assessments = from_list(Assessment.from_dict, obj.get("assessments"))
321
+ name = from_str(obj.get("name"))
322
+ description = from_union([from_str, from_none], obj.get("description"))
323
+ generator = from_union([Generator.from_dict, from_none], obj.get("generator"))
324
+ integrity = from_union([Integrity.from_dict, from_none], obj.get("integrity"))
325
+ labels = from_union([lambda x: from_dict(from_str, x), from_none], obj.get("labels"))
326
+ plan_id = from_union([lambda x: UUID(x), from_none], obj.get("planId"))
327
+ schedule = from_union([Schedule.from_dict, from_none], obj.get("schedule"))
328
+ system_ref = from_union([from_str, from_none], obj.get("systemRef"))
329
+ type = from_union([PlanType, from_none], obj.get("type"))
330
+ version = from_union([from_str, from_none], obj.get("version"))
331
+ return HdfPlan(assessments, name, description, generator, integrity, labels, plan_id, schedule, system_ref, type, version)
332
+
333
+ def to_dict(self) -> dict:
334
+ result: dict = {}
335
+ result["assessments"] = from_list(lambda x: to_class(Assessment, x), self.assessments)
336
+ result["name"] = from_str(self.name)
337
+ if self.description is not None:
338
+ result["description"] = from_union([from_str, from_none], self.description)
339
+ if self.generator is not None:
340
+ result["generator"] = from_union([lambda x: to_class(Generator, x), from_none], self.generator)
341
+ if self.integrity is not None:
342
+ result["integrity"] = from_union([lambda x: to_class(Integrity, x), from_none], self.integrity)
343
+ if self.labels is not None:
344
+ result["labels"] = from_union([lambda x: from_dict(from_str, x), from_none], self.labels)
345
+ if self.plan_id is not None:
346
+ result["planId"] = from_union([lambda x: str(x), from_none], self.plan_id)
347
+ if self.schedule is not None:
348
+ result["schedule"] = from_union([lambda x: to_class(Schedule, x), from_none], self.schedule)
349
+ if self.system_ref is not None:
350
+ result["systemRef"] = from_union([from_str, from_none], self.system_ref)
351
+ if self.type is not None:
352
+ result["type"] = from_union([lambda x: to_enum(PlanType, x), from_none], self.type)
353
+ if self.version is not None:
354
+ result["version"] = from_union([from_str, from_none], self.version)
355
+ return result
356
+
357
+
358
+ def hdf_plan_from_dict(s: Any) -> HdfPlan:
359
+ return HdfPlan.from_dict(s)
360
+
361
+
362
+ def hdf_plan_to_dict(x: HdfPlan) -> Any:
363
+ return to_class(HdfPlan, x)