@mitre/hdf-schema 3.0.0 → 3.1.0-rc.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/LICENSE.md +55 -0
- package/README.md +96 -41
- package/dist/go/hdf.go +148 -104
- package/dist/helpers.js +4 -44
- package/dist/index.d.ts +26 -1
- package/dist/index.js +26 -1
- package/dist/schemas/hdf-amendments.schema.json +178 -53
- package/dist/schemas/hdf-baseline.schema.json +181 -56
- package/dist/schemas/hdf-comparison.schema.json +523 -108
- package/dist/schemas/hdf-evidence-package.schema.json +175 -50
- package/dist/schemas/hdf-plan.schema.json +181 -56
- package/dist/schemas/hdf-results.schema.json +502 -87
- package/dist/schemas/hdf-system.schema.json +190 -65
- package/dist/ts/hdf-amendments.d.ts +43 -15
- package/dist/ts/hdf-amendments.js +18 -7
- package/dist/ts/hdf-amendments.ts +44 -15
- package/dist/ts/hdf-results.d.ts +91 -37
- package/dist/ts/hdf-results.js +40 -20
- package/dist/ts/hdf-results.ts +91 -36
- package/package.json +44 -44
- package/dist/python/hdf_amendments.py +0 -695
- package/dist/python/hdf_baseline.py +0 -782
- package/dist/python/hdf_comparison.py +0 -1771
- package/dist/python/hdf_evidence_package.py +0 -593
- package/dist/python/hdf_plan.py +0 -363
- package/dist/python/hdf_results.py +0 -2163
- package/dist/python/hdf_system.py +0 -904
- package/src/schemas/hdf-amendments.schema.json +0 -97
- package/src/schemas/hdf-baseline.schema.json +0 -190
- package/src/schemas/hdf-comparison.schema.json +0 -107
- package/src/schemas/hdf-evidence-package.schema.json +0 -227
- package/src/schemas/hdf-plan.schema.json +0 -92
- package/src/schemas/hdf-results.schema.json +0 -304
- package/src/schemas/hdf-system.schema.json +0 -136
- package/src/schemas/primitives/amendments.schema.json +0 -155
- package/src/schemas/primitives/common.schema.json +0 -814
- package/src/schemas/primitives/comparison.schema.json +0 -809
- package/src/schemas/primitives/component.schema.json +0 -518
- package/src/schemas/primitives/data-flow.schema.json +0 -158
- package/src/schemas/primitives/extensions.schema.json +0 -342
- package/src/schemas/primitives/parameter.schema.json +0 -128
- package/src/schemas/primitives/plan.schema.json +0 -128
- package/src/schemas/primitives/platform.schema.json +0 -32
- package/src/schemas/primitives/result.schema.json +0 -133
- package/src/schemas/primitives/runner.schema.json +0 -83
- package/src/schemas/primitives/statistics.schema.json +0 -71
- package/src/schemas/primitives/system.schema.json +0 -132
- package/src/schemas/primitives/target.schema.json +0 -523
|
@@ -1,1771 +0,0 @@
|
|
|
1
|
-
from enum import Enum
|
|
2
|
-
from dataclasses import dataclass
|
|
3
|
-
from typing import Optional, Any, List, Dict, TypeVar, Type, Callable, cast
|
|
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 from_bool(x: Any) -> bool:
|
|
33
|
-
assert isinstance(x, bool)
|
|
34
|
-
return x
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
def to_enum(c: Type[EnumT], x: Any) -> EnumT:
|
|
38
|
-
assert isinstance(x, c)
|
|
39
|
-
return x.value
|
|
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 to_class(c: Type[T], x: Any) -> dict:
|
|
48
|
-
assert isinstance(x, c)
|
|
49
|
-
return cast(Any, x).to_dict()
|
|
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_dict(f: Callable[[Any], T], x: Any) -> Dict[str, T]:
|
|
58
|
-
assert isinstance(x, dict)
|
|
59
|
-
return { k: f(v) for (k, v) in x.items() }
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
def from_float(x: Any) -> float:
|
|
63
|
-
assert isinstance(x, (float, int)) and not isinstance(x, bool)
|
|
64
|
-
return float(x)
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
def to_float(x: Any) -> float:
|
|
68
|
-
assert isinstance(x, (int, float))
|
|
69
|
-
return x
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
def from_datetime(x: Any) -> datetime:
|
|
73
|
-
return dateutil.parser.parse(x)
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
class AnnotationCategory(Enum):
|
|
77
|
-
"""The category of this annotation.
|
|
78
|
-
|
|
79
|
-
The category of an annotation attached to a comparison.
|
|
80
|
-
"""
|
|
81
|
-
BASELINE_CHANGE = "baselineChange"
|
|
82
|
-
DRIFT = "drift"
|
|
83
|
-
REMEDIATION = "remediation"
|
|
84
|
-
SCANNER_NOTE = "scannerNote"
|
|
85
|
-
WAIVER = "waiver"
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
@dataclass
|
|
89
|
-
class Annotation:
|
|
90
|
-
"""An annotation attached to a comparison, providing context or action items."""
|
|
91
|
-
|
|
92
|
-
label: str
|
|
93
|
-
"""Human-readable label for this annotation."""
|
|
94
|
-
|
|
95
|
-
category: Optional[AnnotationCategory] = None
|
|
96
|
-
"""The category of this annotation."""
|
|
97
|
-
|
|
98
|
-
description: Optional[str] = None
|
|
99
|
-
"""Detailed description of the annotation."""
|
|
100
|
-
|
|
101
|
-
needs_confirmation: Optional[bool] = None
|
|
102
|
-
"""Whether this annotation requires human confirmation before acting on it."""
|
|
103
|
-
|
|
104
|
-
@staticmethod
|
|
105
|
-
def from_dict(obj: Any) -> 'Annotation':
|
|
106
|
-
assert isinstance(obj, dict)
|
|
107
|
-
label = from_str(obj.get("label"))
|
|
108
|
-
category = from_union([AnnotationCategory, from_none], obj.get("category"))
|
|
109
|
-
description = from_union([from_str, from_none], obj.get("description"))
|
|
110
|
-
needs_confirmation = from_union([from_bool, from_none], obj.get("needsConfirmation"))
|
|
111
|
-
return Annotation(label, category, description, needs_confirmation)
|
|
112
|
-
|
|
113
|
-
def to_dict(self) -> dict:
|
|
114
|
-
result: dict = {}
|
|
115
|
-
result["label"] = from_str(self.label)
|
|
116
|
-
if self.category is not None:
|
|
117
|
-
result["category"] = from_union([lambda x: to_enum(AnnotationCategory, x), from_none], self.category)
|
|
118
|
-
if self.description is not None:
|
|
119
|
-
result["description"] = from_union([from_str, from_none], self.description)
|
|
120
|
-
if self.needs_confirmation is not None:
|
|
121
|
-
result["needsConfirmation"] = from_union([from_bool, from_none], self.needs_confirmation)
|
|
122
|
-
return result
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
class BaselineDiffState(Enum):
|
|
126
|
-
"""The state of this baseline in the comparison.
|
|
127
|
-
|
|
128
|
-
The state of this component in the comparison.
|
|
129
|
-
"""
|
|
130
|
-
ABSENT = "absent"
|
|
131
|
-
NEW = "new"
|
|
132
|
-
UNCHANGED = "unchanged"
|
|
133
|
-
UPDATED = "updated"
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
@dataclass
|
|
137
|
-
class BaselineDiff:
|
|
138
|
-
"""Comparison of a baseline between sources."""
|
|
139
|
-
|
|
140
|
-
name: str
|
|
141
|
-
"""Name of the baseline being compared."""
|
|
142
|
-
|
|
143
|
-
state: BaselineDiffState
|
|
144
|
-
"""The state of this baseline in the comparison."""
|
|
145
|
-
|
|
146
|
-
mapping_source: Optional[str] = None
|
|
147
|
-
"""The source of any ID mapping used to correlate requirements across baseline versions."""
|
|
148
|
-
|
|
149
|
-
new_version: Optional[str] = None
|
|
150
|
-
"""Version of the baseline in the new source."""
|
|
151
|
-
|
|
152
|
-
old_version: Optional[str] = None
|
|
153
|
-
"""Version of the baseline in the old source."""
|
|
154
|
-
|
|
155
|
-
@staticmethod
|
|
156
|
-
def from_dict(obj: Any) -> 'BaselineDiff':
|
|
157
|
-
assert isinstance(obj, dict)
|
|
158
|
-
name = from_str(obj.get("name"))
|
|
159
|
-
state = BaselineDiffState(obj.get("state"))
|
|
160
|
-
mapping_source = from_union([from_str, from_none], obj.get("mappingSource"))
|
|
161
|
-
new_version = from_union([from_str, from_none], obj.get("newVersion"))
|
|
162
|
-
old_version = from_union([from_str, from_none], obj.get("oldVersion"))
|
|
163
|
-
return BaselineDiff(name, state, mapping_source, new_version, old_version)
|
|
164
|
-
|
|
165
|
-
def to_dict(self) -> dict:
|
|
166
|
-
result: dict = {}
|
|
167
|
-
result["name"] = from_str(self.name)
|
|
168
|
-
result["state"] = to_enum(BaselineDiffState, self.state)
|
|
169
|
-
if self.mapping_source is not None:
|
|
170
|
-
result["mappingSource"] = from_union([from_str, from_none], self.mapping_source)
|
|
171
|
-
if self.new_version is not None:
|
|
172
|
-
result["newVersion"] = from_union([from_str, from_none], self.new_version)
|
|
173
|
-
if self.old_version is not None:
|
|
174
|
-
result["oldVersion"] = from_union([from_str, from_none], self.old_version)
|
|
175
|
-
return result
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
class ComparisonMode(Enum):
|
|
179
|
-
"""The mode of comparison being performed.
|
|
180
|
-
|
|
181
|
-
The mode of comparison. 'temporal' compares the same target over time. 'baseline'
|
|
182
|
-
compares against a golden reference. 'fleet' compares across multiple systems.
|
|
183
|
-
'multiSource' compares outputs from different scanners. 'baselineEvolution' compares two
|
|
184
|
-
baseline documents to detect requirement changes between versions. 'systemDrift' compares
|
|
185
|
-
two system documents to detect component-level changes.
|
|
186
|
-
"""
|
|
187
|
-
BASELINE = "baseline"
|
|
188
|
-
BASELINE_EVOLUTION = "baselineEvolution"
|
|
189
|
-
FLEET = "fleet"
|
|
190
|
-
MULTI_SOURCE = "multiSource"
|
|
191
|
-
SYSTEM_DRIFT = "systemDrift"
|
|
192
|
-
TEMPORAL = "temporal"
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
class Op(Enum):
|
|
196
|
-
"""The type of change operation."""
|
|
197
|
-
|
|
198
|
-
ADD = "add"
|
|
199
|
-
REMOVE = "remove"
|
|
200
|
-
REPLACE = "replace"
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
@dataclass
|
|
204
|
-
class FieldChange:
|
|
205
|
-
"""A single field-level change between two versions of a requirement."""
|
|
206
|
-
|
|
207
|
-
op: Op
|
|
208
|
-
"""The type of change operation."""
|
|
209
|
-
|
|
210
|
-
path: str
|
|
211
|
-
"""JSON Pointer path to the changed field."""
|
|
212
|
-
|
|
213
|
-
new_value: Any
|
|
214
|
-
"""The new value of the field (for 'add' and 'replace' operations)."""
|
|
215
|
-
|
|
216
|
-
old_value: Any
|
|
217
|
-
"""The previous value of the field (for 'remove' and 'replace' operations)."""
|
|
218
|
-
|
|
219
|
-
@staticmethod
|
|
220
|
-
def from_dict(obj: Any) -> 'FieldChange':
|
|
221
|
-
assert isinstance(obj, dict)
|
|
222
|
-
op = Op(obj.get("op"))
|
|
223
|
-
path = from_str(obj.get("path"))
|
|
224
|
-
new_value = obj.get("newValue")
|
|
225
|
-
old_value = obj.get("oldValue")
|
|
226
|
-
return FieldChange(op, path, new_value, old_value)
|
|
227
|
-
|
|
228
|
-
def to_dict(self) -> dict:
|
|
229
|
-
result: dict = {}
|
|
230
|
-
result["op"] = to_enum(Op, self.op)
|
|
231
|
-
result["path"] = from_str(self.path)
|
|
232
|
-
if self.new_value is not None:
|
|
233
|
-
result["newValue"] = self.new_value
|
|
234
|
-
if self.old_value is not None:
|
|
235
|
-
result["oldValue"] = self.old_value
|
|
236
|
-
return result
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
@dataclass
|
|
240
|
-
class ComponentDiff:
|
|
241
|
-
"""Comparison of a single component between two system document versions."""
|
|
242
|
-
|
|
243
|
-
name: str
|
|
244
|
-
"""Component name used for matching across system versions."""
|
|
245
|
-
|
|
246
|
-
state: BaselineDiffState
|
|
247
|
-
"""The state of this component in the comparison."""
|
|
248
|
-
|
|
249
|
-
after: Any
|
|
250
|
-
"""Component snapshot from the new system document."""
|
|
251
|
-
|
|
252
|
-
before: Any
|
|
253
|
-
"""Component snapshot from the old system document."""
|
|
254
|
-
|
|
255
|
-
field_changes: Optional[List[FieldChange]] = None
|
|
256
|
-
"""Detailed field-level changes between the before and after component snapshots."""
|
|
257
|
-
|
|
258
|
-
@staticmethod
|
|
259
|
-
def from_dict(obj: Any) -> 'ComponentDiff':
|
|
260
|
-
assert isinstance(obj, dict)
|
|
261
|
-
name = from_str(obj.get("name"))
|
|
262
|
-
state = BaselineDiffState(obj.get("state"))
|
|
263
|
-
after = obj.get("after")
|
|
264
|
-
before = obj.get("before")
|
|
265
|
-
field_changes = from_union([lambda x: from_list(FieldChange.from_dict, x), from_none], obj.get("fieldChanges"))
|
|
266
|
-
return ComponentDiff(name, state, after, before, field_changes)
|
|
267
|
-
|
|
268
|
-
def to_dict(self) -> dict:
|
|
269
|
-
result: dict = {}
|
|
270
|
-
result["name"] = from_str(self.name)
|
|
271
|
-
result["state"] = to_enum(BaselineDiffState, self.state)
|
|
272
|
-
if self.after is not None:
|
|
273
|
-
result["after"] = self.after
|
|
274
|
-
if self.before is not None:
|
|
275
|
-
result["before"] = self.before
|
|
276
|
-
if self.field_changes is not None:
|
|
277
|
-
result["fieldChanges"] = from_union([lambda x: from_list(lambda x: to_class(FieldChange, x), x), from_none], self.field_changes)
|
|
278
|
-
return result
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
class ChangeReason(Enum):
|
|
282
|
-
"""The reason a requirement's state changed between sources."""
|
|
283
|
-
|
|
284
|
-
BASELINE_UPGRADED = "baselineUpgraded"
|
|
285
|
-
CONFIG_CHANGED = "configChanged"
|
|
286
|
-
CONTROL_MAPPED = "controlMapped"
|
|
287
|
-
IMPACT_CHANGED = "impactChanged"
|
|
288
|
-
METADATA_CHANGED = "metadataChanged"
|
|
289
|
-
OVERRIDE_ADDED = "overrideAdded"
|
|
290
|
-
OVERRIDE_EXPIRED = "overrideExpired"
|
|
291
|
-
OVERRIDE_MODIFIED = "overrideModified"
|
|
292
|
-
OVERRIDE_REMOVED = "overrideRemoved"
|
|
293
|
-
RESULT_CHANGED = "resultChanged"
|
|
294
|
-
SCANNER_CHANGED = "scannerChanged"
|
|
295
|
-
TARGET_CHANGED = "targetChanged"
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
class ConflictResolution(Enum):
|
|
299
|
-
"""How the conflict was resolved.
|
|
300
|
-
|
|
301
|
-
How a conflict between multiple scanner results was resolved.
|
|
302
|
-
"""
|
|
303
|
-
MANUAL = "manual"
|
|
304
|
-
MOST_RECENT = "mostRecent"
|
|
305
|
-
MOST_SEVERE = "mostSevere"
|
|
306
|
-
UNRESOLVED = "unresolved"
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
@dataclass
|
|
310
|
-
class Value:
|
|
311
|
-
source_index: int
|
|
312
|
-
"""Zero-based index into the sources array."""
|
|
313
|
-
|
|
314
|
-
source_label: str
|
|
315
|
-
"""Human-readable label for the source."""
|
|
316
|
-
|
|
317
|
-
value: Any
|
|
318
|
-
"""The value reported by this source for the conflicting field."""
|
|
319
|
-
|
|
320
|
-
@staticmethod
|
|
321
|
-
def from_dict(obj: Any) -> 'Value':
|
|
322
|
-
assert isinstance(obj, dict)
|
|
323
|
-
source_index = from_int(obj.get("sourceIndex"))
|
|
324
|
-
source_label = from_str(obj.get("sourceLabel"))
|
|
325
|
-
value = obj.get("value")
|
|
326
|
-
return Value(source_index, source_label, value)
|
|
327
|
-
|
|
328
|
-
def to_dict(self) -> dict:
|
|
329
|
-
result: dict = {}
|
|
330
|
-
result["sourceIndex"] = from_int(self.source_index)
|
|
331
|
-
result["sourceLabel"] = from_str(self.source_label)
|
|
332
|
-
result["value"] = self.value
|
|
333
|
-
return result
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
@dataclass
|
|
337
|
-
class ScannerConflict:
|
|
338
|
-
"""A conflict between scanner results for the same requirement."""
|
|
339
|
-
|
|
340
|
-
field: str
|
|
341
|
-
"""The field where the conflict occurs."""
|
|
342
|
-
|
|
343
|
-
values: List[Value]
|
|
344
|
-
"""The conflicting values from each source."""
|
|
345
|
-
|
|
346
|
-
resolution: Optional[ConflictResolution] = None
|
|
347
|
-
"""How the conflict was resolved."""
|
|
348
|
-
|
|
349
|
-
resolved_index: Optional[int] = None
|
|
350
|
-
"""Index of the source whose value was chosen as the resolution."""
|
|
351
|
-
|
|
352
|
-
@staticmethod
|
|
353
|
-
def from_dict(obj: Any) -> 'ScannerConflict':
|
|
354
|
-
assert isinstance(obj, dict)
|
|
355
|
-
field = from_str(obj.get("field"))
|
|
356
|
-
values = from_list(Value.from_dict, obj.get("values"))
|
|
357
|
-
resolution = from_union([ConflictResolution, from_none], obj.get("resolution"))
|
|
358
|
-
resolved_index = from_union([from_int, from_none], obj.get("resolvedIndex"))
|
|
359
|
-
return ScannerConflict(field, values, resolution, resolved_index)
|
|
360
|
-
|
|
361
|
-
def to_dict(self) -> dict:
|
|
362
|
-
result: dict = {}
|
|
363
|
-
result["field"] = from_str(self.field)
|
|
364
|
-
result["values"] = from_list(lambda x: to_class(Value, x), self.values)
|
|
365
|
-
if self.resolution is not None:
|
|
366
|
-
result["resolution"] = from_union([lambda x: to_enum(ConflictResolution, x), from_none], self.resolution)
|
|
367
|
-
if self.resolved_index is not None:
|
|
368
|
-
result["resolvedIndex"] = from_union([from_int, from_none], self.resolved_index)
|
|
369
|
-
return result
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
class MatchStrategy(Enum):
|
|
373
|
-
"""The strategy that was used to match this requirement across sources.
|
|
374
|
-
|
|
375
|
-
The strategy used to match requirements across sources. 'exactId' matches by identical
|
|
376
|
-
IDs. 'mappedId' uses an ID mapping table. 'cciMatch'/'nistMatch' match by framework
|
|
377
|
-
identifiers. 'fuzzyTitle'/'fuzzyContent' use text similarity.
|
|
378
|
-
|
|
379
|
-
The primary strategy used to match requirements across sources.
|
|
380
|
-
"""
|
|
381
|
-
CCI_MATCH = "cciMatch"
|
|
382
|
-
EXACT_ID = "exactId"
|
|
383
|
-
FUZZY_CONTENT = "fuzzyContent"
|
|
384
|
-
FUZZY_TITLE = "fuzzyTitle"
|
|
385
|
-
MAPPED_ID = "mappedId"
|
|
386
|
-
NIST_MATCH = "nistMatch"
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
class RequirementState(Enum):
|
|
390
|
-
"""The state of this requirement in the comparison.
|
|
391
|
-
|
|
392
|
-
SARIF-compatible vocabulary extended for security. 'new' = present only in new source,
|
|
393
|
-
'absent' = present only in old, 'unchanged' = same effective status, 'updated' = status
|
|
394
|
-
changed (generic), 'fixed' = was failing now passing, 'regressed' = was passing now
|
|
395
|
-
failing, 'moved' = reorganized same content, 'split'/'merged' = reserved for v1.1.
|
|
396
|
-
"""
|
|
397
|
-
ABSENT = "absent"
|
|
398
|
-
FIXED = "fixed"
|
|
399
|
-
MERGED = "merged"
|
|
400
|
-
MOVED = "moved"
|
|
401
|
-
NEW = "new"
|
|
402
|
-
REGRESSED = "regressed"
|
|
403
|
-
SPLIT = "split"
|
|
404
|
-
UNCHANGED = "unchanged"
|
|
405
|
-
UPDATED = "updated"
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
@dataclass
|
|
409
|
-
class RequirementDiff:
|
|
410
|
-
"""A comparison of a single requirement between sources, including state, changes, and full
|
|
411
|
-
before/after snapshots.
|
|
412
|
-
"""
|
|
413
|
-
after: Any
|
|
414
|
-
"""The requirement as it appeared in the new source. Null when state is 'absent'."""
|
|
415
|
-
|
|
416
|
-
before: Any
|
|
417
|
-
"""The requirement as it appeared in the old/reference source. Null when state is 'new'."""
|
|
418
|
-
|
|
419
|
-
change_reasons: List[ChangeReason]
|
|
420
|
-
"""The reasons for the state change."""
|
|
421
|
-
|
|
422
|
-
field_changes: List[FieldChange]
|
|
423
|
-
"""Detailed field-level changes between the before and after versions."""
|
|
424
|
-
|
|
425
|
-
id: str
|
|
426
|
-
"""The canonical requirement identifier used for this diff."""
|
|
427
|
-
|
|
428
|
-
state: RequirementState
|
|
429
|
-
"""The state of this requirement in the comparison."""
|
|
430
|
-
|
|
431
|
-
after_sensitive: Optional[Dict[str, Any]] = None
|
|
432
|
-
"""Sensitive data from the new source that should not be included in the main after snapshot."""
|
|
433
|
-
|
|
434
|
-
annotation_ids: Optional[List[str]] = None
|
|
435
|
-
"""IDs of annotations attached to this requirement diff."""
|
|
436
|
-
|
|
437
|
-
before_sensitive: Optional[Dict[str, Any]] = None
|
|
438
|
-
"""Sensitive data from the old source that should not be included in the main before
|
|
439
|
-
snapshot.
|
|
440
|
-
"""
|
|
441
|
-
conflicts: Optional[List[ScannerConflict]] = None
|
|
442
|
-
"""Conflicts between multiple scanner results for this requirement."""
|
|
443
|
-
|
|
444
|
-
match_confidence: Optional[float] = None
|
|
445
|
-
"""Confidence score for the match (0-1)."""
|
|
446
|
-
|
|
447
|
-
match_manual: Optional[bool] = None
|
|
448
|
-
"""Whether the match was manually confirmed by a human."""
|
|
449
|
-
|
|
450
|
-
match_strategy: Optional[MatchStrategy] = None
|
|
451
|
-
"""The strategy that was used to match this requirement across sources."""
|
|
452
|
-
|
|
453
|
-
new_effective_status: Optional[str] = None
|
|
454
|
-
"""The effective status of the requirement in the new source."""
|
|
455
|
-
|
|
456
|
-
new_id: Optional[str] = None
|
|
457
|
-
"""The requirement ID in the new source, if different from the canonical id."""
|
|
458
|
-
|
|
459
|
-
new_impact: Optional[float] = None
|
|
460
|
-
"""The impact score of the requirement in the new source (0-1)."""
|
|
461
|
-
|
|
462
|
-
old_effective_status: Optional[str] = None
|
|
463
|
-
"""The effective status of the requirement in the old source."""
|
|
464
|
-
|
|
465
|
-
old_id: Optional[str] = None
|
|
466
|
-
"""The requirement ID in the old source, if different from the canonical id."""
|
|
467
|
-
|
|
468
|
-
old_impact: Optional[float] = None
|
|
469
|
-
"""The impact score of the requirement in the old source (0-1)."""
|
|
470
|
-
|
|
471
|
-
source_index: Optional[int] = None
|
|
472
|
-
"""Index into the sources array for multi-source comparisons."""
|
|
473
|
-
|
|
474
|
-
title: Optional[str] = None
|
|
475
|
-
"""The requirement title for human readability."""
|
|
476
|
-
|
|
477
|
-
@staticmethod
|
|
478
|
-
def from_dict(obj: Any) -> 'RequirementDiff':
|
|
479
|
-
assert isinstance(obj, dict)
|
|
480
|
-
after = obj.get("after")
|
|
481
|
-
before = obj.get("before")
|
|
482
|
-
change_reasons = from_list(ChangeReason, obj.get("changeReasons"))
|
|
483
|
-
field_changes = from_list(FieldChange.from_dict, obj.get("fieldChanges"))
|
|
484
|
-
id = from_str(obj.get("id"))
|
|
485
|
-
state = RequirementState(obj.get("state"))
|
|
486
|
-
after_sensitive = from_union([lambda x: from_dict(lambda x: x, x), from_none], obj.get("afterSensitive"))
|
|
487
|
-
annotation_ids = from_union([lambda x: from_list(from_str, x), from_none], obj.get("annotationIds"))
|
|
488
|
-
before_sensitive = from_union([lambda x: from_dict(lambda x: x, x), from_none], obj.get("beforeSensitive"))
|
|
489
|
-
conflicts = from_union([lambda x: from_list(ScannerConflict.from_dict, x), from_none], obj.get("conflicts"))
|
|
490
|
-
match_confidence = from_union([from_float, from_none], obj.get("matchConfidence"))
|
|
491
|
-
match_manual = from_union([from_bool, from_none], obj.get("matchManual"))
|
|
492
|
-
match_strategy = from_union([MatchStrategy, from_none], obj.get("matchStrategy"))
|
|
493
|
-
new_effective_status = from_union([from_str, from_none], obj.get("newEffectiveStatus"))
|
|
494
|
-
new_id = from_union([from_str, from_none], obj.get("newId"))
|
|
495
|
-
new_impact = from_union([from_float, from_none], obj.get("newImpact"))
|
|
496
|
-
old_effective_status = from_union([from_str, from_none], obj.get("oldEffectiveStatus"))
|
|
497
|
-
old_id = from_union([from_str, from_none], obj.get("oldId"))
|
|
498
|
-
old_impact = from_union([from_float, from_none], obj.get("oldImpact"))
|
|
499
|
-
source_index = from_union([from_int, from_none], obj.get("sourceIndex"))
|
|
500
|
-
title = from_union([from_str, from_none], obj.get("title"))
|
|
501
|
-
return RequirementDiff(after, before, change_reasons, field_changes, id, state, after_sensitive, annotation_ids, before_sensitive, conflicts, match_confidence, match_manual, match_strategy, new_effective_status, new_id, new_impact, old_effective_status, old_id, old_impact, source_index, title)
|
|
502
|
-
|
|
503
|
-
def to_dict(self) -> dict:
|
|
504
|
-
result: dict = {}
|
|
505
|
-
result["after"] = self.after
|
|
506
|
-
result["before"] = self.before
|
|
507
|
-
result["changeReasons"] = from_list(lambda x: to_enum(ChangeReason, x), self.change_reasons)
|
|
508
|
-
result["fieldChanges"] = from_list(lambda x: to_class(FieldChange, x), self.field_changes)
|
|
509
|
-
result["id"] = from_str(self.id)
|
|
510
|
-
result["state"] = to_enum(RequirementState, self.state)
|
|
511
|
-
if self.after_sensitive is not None:
|
|
512
|
-
result["afterSensitive"] = from_union([lambda x: from_dict(lambda x: x, x), from_none], self.after_sensitive)
|
|
513
|
-
if self.annotation_ids is not None:
|
|
514
|
-
result["annotationIds"] = from_union([lambda x: from_list(from_str, x), from_none], self.annotation_ids)
|
|
515
|
-
if self.before_sensitive is not None:
|
|
516
|
-
result["beforeSensitive"] = from_union([lambda x: from_dict(lambda x: x, x), from_none], self.before_sensitive)
|
|
517
|
-
if self.conflicts is not None:
|
|
518
|
-
result["conflicts"] = from_union([lambda x: from_list(lambda x: to_class(ScannerConflict, x), x), from_none], self.conflicts)
|
|
519
|
-
if self.match_confidence is not None:
|
|
520
|
-
result["matchConfidence"] = from_union([to_float, from_none], self.match_confidence)
|
|
521
|
-
if self.match_manual is not None:
|
|
522
|
-
result["matchManual"] = from_union([from_bool, from_none], self.match_manual)
|
|
523
|
-
if self.match_strategy is not None:
|
|
524
|
-
result["matchStrategy"] = from_union([lambda x: to_enum(MatchStrategy, x), from_none], self.match_strategy)
|
|
525
|
-
if self.new_effective_status is not None:
|
|
526
|
-
result["newEffectiveStatus"] = from_union([from_str, from_none], self.new_effective_status)
|
|
527
|
-
if self.new_id is not None:
|
|
528
|
-
result["newId"] = from_union([from_str, from_none], self.new_id)
|
|
529
|
-
if self.new_impact is not None:
|
|
530
|
-
result["newImpact"] = from_union([to_float, from_none], self.new_impact)
|
|
531
|
-
if self.old_effective_status is not None:
|
|
532
|
-
result["oldEffectiveStatus"] = from_union([from_str, from_none], self.old_effective_status)
|
|
533
|
-
if self.old_id is not None:
|
|
534
|
-
result["oldId"] = from_union([from_str, from_none], self.old_id)
|
|
535
|
-
if self.old_impact is not None:
|
|
536
|
-
result["oldImpact"] = from_union([to_float, from_none], self.old_impact)
|
|
537
|
-
if self.source_index is not None:
|
|
538
|
-
result["sourceIndex"] = from_union([from_int, from_none], self.source_index)
|
|
539
|
-
if self.title is not None:
|
|
540
|
-
result["title"] = from_union([from_str, from_none], self.title)
|
|
541
|
-
return result
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
class FormatVersion(Enum):
|
|
545
|
-
THE_100 = "1.0.0"
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
@dataclass
|
|
549
|
-
class Generator:
|
|
550
|
-
"""Information about the tool that generated this comparison.
|
|
551
|
-
|
|
552
|
-
Information about the tool that generated this HDF file.
|
|
553
|
-
"""
|
|
554
|
-
name: str
|
|
555
|
-
"""The name of the software that produced this HDF file. Example: 'gosec-to-hdf'."""
|
|
556
|
-
|
|
557
|
-
version: str
|
|
558
|
-
"""The version of the tool. Example: '5.22.3'."""
|
|
559
|
-
|
|
560
|
-
@staticmethod
|
|
561
|
-
def from_dict(obj: Any) -> 'Generator':
|
|
562
|
-
assert isinstance(obj, dict)
|
|
563
|
-
name = from_str(obj.get("name"))
|
|
564
|
-
version = from_str(obj.get("version"))
|
|
565
|
-
return Generator(name, version)
|
|
566
|
-
|
|
567
|
-
def to_dict(self) -> dict:
|
|
568
|
-
result: dict = {}
|
|
569
|
-
result["name"] = from_str(self.name)
|
|
570
|
-
result["version"] = from_str(self.version)
|
|
571
|
-
return result
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
class HashAlgorithm(Enum):
|
|
575
|
-
"""The hash algorithm used for the checksum.
|
|
576
|
-
|
|
577
|
-
Supported cryptographic hash algorithms for checksums and integrity verification.
|
|
578
|
-
"""
|
|
579
|
-
SHA256 = "sha256"
|
|
580
|
-
SHA384 = "sha384"
|
|
581
|
-
SHA512 = "sha512"
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
@dataclass
|
|
585
|
-
class Integrity:
|
|
586
|
-
"""Cryptographic integrity information for verifying this comparison document.
|
|
587
|
-
|
|
588
|
-
Cryptographic integrity information for verifying the HDF file has not been tampered
|
|
589
|
-
with. If algorithm is provided, checksum must also be provided, and vice versa.
|
|
590
|
-
"""
|
|
591
|
-
algorithm: Optional[HashAlgorithm] = None
|
|
592
|
-
"""The hash algorithm used for the checksum."""
|
|
593
|
-
|
|
594
|
-
checksum: Optional[str] = None
|
|
595
|
-
"""The checksum value."""
|
|
596
|
-
|
|
597
|
-
signature: Optional[str] = None
|
|
598
|
-
"""Optional cryptographic signature."""
|
|
599
|
-
|
|
600
|
-
signed_by: Optional[str] = None
|
|
601
|
-
"""Identifier of who signed this file."""
|
|
602
|
-
|
|
603
|
-
@staticmethod
|
|
604
|
-
def from_dict(obj: Any) -> 'Integrity':
|
|
605
|
-
assert isinstance(obj, dict)
|
|
606
|
-
algorithm = from_union([HashAlgorithm, from_none], obj.get("algorithm"))
|
|
607
|
-
checksum = from_union([from_str, from_none], obj.get("checksum"))
|
|
608
|
-
signature = from_union([from_str, from_none], obj.get("signature"))
|
|
609
|
-
signed_by = from_union([from_str, from_none], obj.get("signedBy"))
|
|
610
|
-
return Integrity(algorithm, checksum, signature, signed_by)
|
|
611
|
-
|
|
612
|
-
def to_dict(self) -> dict:
|
|
613
|
-
result: dict = {}
|
|
614
|
-
if self.algorithm is not None:
|
|
615
|
-
result["algorithm"] = from_union([lambda x: to_enum(HashAlgorithm, x), from_none], self.algorithm)
|
|
616
|
-
if self.checksum is not None:
|
|
617
|
-
result["checksum"] = from_union([from_str, from_none], self.checksum)
|
|
618
|
-
if self.signature is not None:
|
|
619
|
-
result["signature"] = from_union([from_str, from_none], self.signature)
|
|
620
|
-
if self.signed_by is not None:
|
|
621
|
-
result["signedBy"] = from_union([from_str, from_none], self.signed_by)
|
|
622
|
-
return result
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
@dataclass
|
|
626
|
-
class MatchingConfig:
|
|
627
|
-
"""Configuration for how requirements were matched across sources.
|
|
628
|
-
|
|
629
|
-
Configuration for how requirements are matched across sources.
|
|
630
|
-
"""
|
|
631
|
-
primary_strategy: MatchStrategy
|
|
632
|
-
"""The primary strategy used to match requirements across sources."""
|
|
633
|
-
|
|
634
|
-
fallback_strategies: Optional[List[MatchStrategy]] = None
|
|
635
|
-
"""Ordered list of fallback strategies tried when the primary strategy fails to find a match."""
|
|
636
|
-
|
|
637
|
-
fingerprint_fields: Optional[List[str]] = None
|
|
638
|
-
"""Fields used to compute a fingerprint for fuzzy matching."""
|
|
639
|
-
|
|
640
|
-
mapping_table_uri: Optional[str] = None
|
|
641
|
-
"""URI pointing to an external mapping table used for ID translation."""
|
|
642
|
-
|
|
643
|
-
minimum_confidence: Optional[float] = None
|
|
644
|
-
"""Minimum confidence score (0-1) required to accept a match."""
|
|
645
|
-
|
|
646
|
-
@staticmethod
|
|
647
|
-
def from_dict(obj: Any) -> 'MatchingConfig':
|
|
648
|
-
assert isinstance(obj, dict)
|
|
649
|
-
primary_strategy = MatchStrategy(obj.get("primaryStrategy"))
|
|
650
|
-
fallback_strategies = from_union([lambda x: from_list(MatchStrategy, x), from_none], obj.get("fallbackStrategies"))
|
|
651
|
-
fingerprint_fields = from_union([lambda x: from_list(from_str, x), from_none], obj.get("fingerprintFields"))
|
|
652
|
-
mapping_table_uri = from_union([from_str, from_none], obj.get("mappingTableUri"))
|
|
653
|
-
minimum_confidence = from_union([from_float, from_none], obj.get("minimumConfidence"))
|
|
654
|
-
return MatchingConfig(primary_strategy, fallback_strategies, fingerprint_fields, mapping_table_uri, minimum_confidence)
|
|
655
|
-
|
|
656
|
-
def to_dict(self) -> dict:
|
|
657
|
-
result: dict = {}
|
|
658
|
-
result["primaryStrategy"] = to_enum(MatchStrategy, self.primary_strategy)
|
|
659
|
-
if self.fallback_strategies is not None:
|
|
660
|
-
result["fallbackStrategies"] = from_union([lambda x: from_list(lambda x: to_enum(MatchStrategy, x), x), from_none], self.fallback_strategies)
|
|
661
|
-
if self.fingerprint_fields is not None:
|
|
662
|
-
result["fingerprintFields"] = from_union([lambda x: from_list(from_str, x), from_none], self.fingerprint_fields)
|
|
663
|
-
if self.mapping_table_uri is not None:
|
|
664
|
-
result["mappingTableUri"] = from_union([from_str, from_none], self.mapping_table_uri)
|
|
665
|
-
if self.minimum_confidence is not None:
|
|
666
|
-
result["minimumConfidence"] = from_union([to_float, from_none], self.minimum_confidence)
|
|
667
|
-
return result
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
class PackageDiffState(Enum):
|
|
671
|
-
"""The state of this package: added (new in new SBOM), removed (absent from new SBOM),
|
|
672
|
-
updated (version changed), unchanged.
|
|
673
|
-
"""
|
|
674
|
-
ADDED = "added"
|
|
675
|
-
REMOVED = "removed"
|
|
676
|
-
UNCHANGED = "unchanged"
|
|
677
|
-
UPDATED = "updated"
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
@dataclass
|
|
681
|
-
class PackageDiff:
|
|
682
|
-
"""Comparison of a single package between two SBOM versions, matched by purl."""
|
|
683
|
-
|
|
684
|
-
purl: str
|
|
685
|
-
"""Package URL (purl) used as the identity key for matching across SBOMs."""
|
|
686
|
-
|
|
687
|
-
state: PackageDiffState
|
|
688
|
-
"""The state of this package: added (new in new SBOM), removed (absent from new SBOM),
|
|
689
|
-
updated (version changed), unchanged.
|
|
690
|
-
"""
|
|
691
|
-
licenses: Optional[List[str]] = None
|
|
692
|
-
"""License identifiers for this package."""
|
|
693
|
-
|
|
694
|
-
name: Optional[str] = None
|
|
695
|
-
"""Human-readable package name."""
|
|
696
|
-
|
|
697
|
-
new_version: Optional[str] = None
|
|
698
|
-
"""Package version in the new SBOM."""
|
|
699
|
-
|
|
700
|
-
old_version: Optional[str] = None
|
|
701
|
-
"""Package version in the old SBOM."""
|
|
702
|
-
|
|
703
|
-
@staticmethod
|
|
704
|
-
def from_dict(obj: Any) -> 'PackageDiff':
|
|
705
|
-
assert isinstance(obj, dict)
|
|
706
|
-
purl = from_str(obj.get("purl"))
|
|
707
|
-
state = PackageDiffState(obj.get("state"))
|
|
708
|
-
licenses = from_union([lambda x: from_list(from_str, x), from_none], obj.get("licenses"))
|
|
709
|
-
name = from_union([from_str, from_none], obj.get("name"))
|
|
710
|
-
new_version = from_union([from_str, from_none], obj.get("newVersion"))
|
|
711
|
-
old_version = from_union([from_str, from_none], obj.get("oldVersion"))
|
|
712
|
-
return PackageDiff(purl, state, licenses, name, new_version, old_version)
|
|
713
|
-
|
|
714
|
-
def to_dict(self) -> dict:
|
|
715
|
-
result: dict = {}
|
|
716
|
-
result["purl"] = from_str(self.purl)
|
|
717
|
-
result["state"] = to_enum(PackageDiffState, self.state)
|
|
718
|
-
if self.licenses is not None:
|
|
719
|
-
result["licenses"] = from_union([lambda x: from_list(from_str, x), from_none], self.licenses)
|
|
720
|
-
if self.name is not None:
|
|
721
|
-
result["name"] = from_union([from_str, from_none], self.name)
|
|
722
|
-
if self.new_version is not None:
|
|
723
|
-
result["newVersion"] = from_union([from_str, from_none], self.new_version)
|
|
724
|
-
if self.old_version is not None:
|
|
725
|
-
result["oldVersion"] = from_union([from_str, from_none], self.old_version)
|
|
726
|
-
return result
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
@dataclass
|
|
730
|
-
class BaselineRef:
|
|
731
|
-
"""Reference to the baseline used in this source assessment."""
|
|
732
|
-
|
|
733
|
-
name: str
|
|
734
|
-
"""Name of the baseline used in this source."""
|
|
735
|
-
|
|
736
|
-
version: Optional[str] = None
|
|
737
|
-
"""Version of the baseline used in this source."""
|
|
738
|
-
|
|
739
|
-
@staticmethod
|
|
740
|
-
def from_dict(obj: Any) -> 'BaselineRef':
|
|
741
|
-
assert isinstance(obj, dict)
|
|
742
|
-
name = from_str(obj.get("name"))
|
|
743
|
-
version = from_union([from_str, from_none], obj.get("version"))
|
|
744
|
-
return BaselineRef(name, version)
|
|
745
|
-
|
|
746
|
-
def to_dict(self) -> dict:
|
|
747
|
-
result: dict = {}
|
|
748
|
-
result["name"] = from_str(self.name)
|
|
749
|
-
if self.version is not None:
|
|
750
|
-
result["version"] = from_union([from_str, from_none], self.version)
|
|
751
|
-
return result
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
@dataclass
|
|
755
|
-
class Checksum:
|
|
756
|
-
"""Cryptographic checksum of the source document for integrity verification.
|
|
757
|
-
|
|
758
|
-
Cryptographic checksum for baseline integrity verification.
|
|
759
|
-
"""
|
|
760
|
-
algorithm: HashAlgorithm
|
|
761
|
-
"""The hash algorithm used for the checksum."""
|
|
762
|
-
|
|
763
|
-
value: str
|
|
764
|
-
"""The checksum value."""
|
|
765
|
-
|
|
766
|
-
@staticmethod
|
|
767
|
-
def from_dict(obj: Any) -> 'Checksum':
|
|
768
|
-
assert isinstance(obj, dict)
|
|
769
|
-
algorithm = HashAlgorithm(obj.get("algorithm"))
|
|
770
|
-
value = from_str(obj.get("value"))
|
|
771
|
-
return Checksum(algorithm, value)
|
|
772
|
-
|
|
773
|
-
def to_dict(self) -> dict:
|
|
774
|
-
result: dict = {}
|
|
775
|
-
result["algorithm"] = to_enum(HashAlgorithm, self.algorithm)
|
|
776
|
-
result["value"] = from_str(self.value)
|
|
777
|
-
return result
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
class TypeEnum(Enum):
|
|
781
|
-
"""The type of identifier. Use 'email' for email addresses, 'username' for user accounts,
|
|
782
|
-
'system' for automated systems, 'simple' for basic string identifiers without additional
|
|
783
|
-
classification, or 'other' for custom identity systems.
|
|
784
|
-
"""
|
|
785
|
-
EMAIL = "email"
|
|
786
|
-
OTHER = "other"
|
|
787
|
-
SIMPLE = "simple"
|
|
788
|
-
SYSTEM = "system"
|
|
789
|
-
USERNAME = "username"
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
@dataclass
|
|
793
|
-
class Identity:
|
|
794
|
-
"""Identity of the person or system that approved this override.
|
|
795
|
-
|
|
796
|
-
Represents an identity that performed an action, such as capturing evidence or applying
|
|
797
|
-
an override.
|
|
798
|
-
|
|
799
|
-
Team or individual responsible for this component. Enables per-component ownership when
|
|
800
|
-
different teams manage different parts of a system.
|
|
801
|
-
"""
|
|
802
|
-
identifier: str
|
|
803
|
-
"""The identifier value. Example: 'user@example.com', 'jdoe', 'automated-scanner-01'."""
|
|
804
|
-
|
|
805
|
-
type: TypeEnum
|
|
806
|
-
"""The type of identifier. Use 'email' for email addresses, 'username' for user accounts,
|
|
807
|
-
'system' for automated systems, 'simple' for basic string identifiers without additional
|
|
808
|
-
classification, or 'other' for custom identity systems.
|
|
809
|
-
"""
|
|
810
|
-
description: Optional[str] = None
|
|
811
|
-
"""Optional description of the identity or identity system, particularly useful when type is
|
|
812
|
-
'other'.
|
|
813
|
-
"""
|
|
814
|
-
|
|
815
|
-
@staticmethod
|
|
816
|
-
def from_dict(obj: Any) -> 'Identity':
|
|
817
|
-
assert isinstance(obj, dict)
|
|
818
|
-
identifier = from_str(obj.get("identifier"))
|
|
819
|
-
type = TypeEnum(obj.get("type"))
|
|
820
|
-
description = from_union([from_str, from_none], obj.get("description"))
|
|
821
|
-
return Identity(identifier, type, description)
|
|
822
|
-
|
|
823
|
-
def to_dict(self) -> dict:
|
|
824
|
-
result: dict = {}
|
|
825
|
-
result["identifier"] = from_str(self.identifier)
|
|
826
|
-
result["type"] = to_enum(TypeEnum, self.type)
|
|
827
|
-
if self.description is not None:
|
|
828
|
-
result["description"] = from_union([from_str, from_none], self.description)
|
|
829
|
-
return result
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
@dataclass
|
|
833
|
-
class InputOverride:
|
|
834
|
-
"""An override of a baseline input value for a specific component. Enables system-specific
|
|
835
|
-
tailoring of baseline parameters.
|
|
836
|
-
"""
|
|
837
|
-
input_name: str
|
|
838
|
-
"""Name of the input being overridden. Must match an Input.name in the referenced baseline."""
|
|
839
|
-
|
|
840
|
-
value: Any
|
|
841
|
-
"""The overridden value. Should match the type of the original input."""
|
|
842
|
-
|
|
843
|
-
approved_by: Optional[Identity] = None
|
|
844
|
-
"""Identity of the person or system that approved this override."""
|
|
845
|
-
|
|
846
|
-
baseline_ref: Optional[str] = None
|
|
847
|
-
"""Name of the baseline this override applies to. If omitted, applies to all baselines that
|
|
848
|
-
define this input.
|
|
849
|
-
"""
|
|
850
|
-
justification: Optional[str] = None
|
|
851
|
-
"""Rationale for why this override is needed."""
|
|
852
|
-
|
|
853
|
-
@staticmethod
|
|
854
|
-
def from_dict(obj: Any) -> 'InputOverride':
|
|
855
|
-
assert isinstance(obj, dict)
|
|
856
|
-
input_name = from_str(obj.get("inputName"))
|
|
857
|
-
value = obj.get("value")
|
|
858
|
-
approved_by = from_union([Identity.from_dict, from_none], obj.get("approvedBy"))
|
|
859
|
-
baseline_ref = from_union([from_str, from_none], obj.get("baselineRef"))
|
|
860
|
-
justification = from_union([from_str, from_none], obj.get("justification"))
|
|
861
|
-
return InputOverride(input_name, value, approved_by, baseline_ref, justification)
|
|
862
|
-
|
|
863
|
-
def to_dict(self) -> dict:
|
|
864
|
-
result: dict = {}
|
|
865
|
-
result["inputName"] = from_str(self.input_name)
|
|
866
|
-
result["value"] = self.value
|
|
867
|
-
if self.approved_by is not None:
|
|
868
|
-
result["approvedBy"] = from_union([lambda x: to_class(Identity, x), from_none], self.approved_by)
|
|
869
|
-
if self.baseline_ref is not None:
|
|
870
|
-
result["baselineRef"] = from_union([from_str, from_none], self.baseline_ref)
|
|
871
|
-
if self.justification is not None:
|
|
872
|
-
result["justification"] = from_union([from_str, from_none], self.justification)
|
|
873
|
-
return result
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
class CloudProvider(Enum):
|
|
877
|
-
AWS = "aws"
|
|
878
|
-
AZURE = "azure"
|
|
879
|
-
GCP = "gcp"
|
|
880
|
-
OCI = "oci"
|
|
881
|
-
OTHER = "other"
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
class SbomFormat(Enum):
|
|
885
|
-
"""Format of the SBOM (embedded or referenced). Required when sbom or sbomRef is present."""
|
|
886
|
-
|
|
887
|
-
CYCLONEDX = "cyclonedx"
|
|
888
|
-
SPDX = "spdx"
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
class Description(Enum):
|
|
892
|
-
"""IP address of the host."""
|
|
893
|
-
|
|
894
|
-
APPLICATION = "application"
|
|
895
|
-
ARTIFACT = "artifact"
|
|
896
|
-
CLOUD_ACCOUNT = "cloudAccount"
|
|
897
|
-
CLOUD_RESOURCE = "cloudResource"
|
|
898
|
-
CONTAINER_IMAGE = "containerImage"
|
|
899
|
-
CONTAINER_INSTANCE = "containerInstance"
|
|
900
|
-
CONTAINER_PLATFORM = "containerPlatform"
|
|
901
|
-
DATABASE = "database"
|
|
902
|
-
HOST = "host"
|
|
903
|
-
NETWORK = "network"
|
|
904
|
-
REPOSITORY = "repository"
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
@dataclass
|
|
908
|
-
class Component:
|
|
909
|
-
"""A system component. Uses discriminated union pattern with 'type' field as discriminator.
|
|
910
|
-
Superset of Target with identity, external IDs, and SBOM support.
|
|
911
|
-
|
|
912
|
-
A physical or virtual server, workstation, or network device.
|
|
913
|
-
|
|
914
|
-
Base properties shared by all component types. Extends the Target concept with stable
|
|
915
|
-
identity, external references, and SBOM embedding.
|
|
916
|
-
|
|
917
|
-
A static container image (not running).
|
|
918
|
-
|
|
919
|
-
A running container instance.
|
|
920
|
-
|
|
921
|
-
A container orchestration platform (Kubernetes, OpenShift, ECS, etc.).
|
|
922
|
-
|
|
923
|
-
A cloud provider account (AWS account, Azure subscription, GCP project).
|
|
924
|
-
|
|
925
|
-
A specific cloud resource (EC2 instance, S3 bucket, Azure VM, etc.).
|
|
926
|
-
|
|
927
|
-
A code repository (for SAST tools).
|
|
928
|
-
|
|
929
|
-
A running application or API (for DAST tools).
|
|
930
|
-
|
|
931
|
-
A software artifact or dependency (for SCA tools).
|
|
932
|
-
|
|
933
|
-
A network segment or network device.
|
|
934
|
-
|
|
935
|
-
A database instance.
|
|
936
|
-
"""
|
|
937
|
-
name: str
|
|
938
|
-
"""Human-readable name for this component."""
|
|
939
|
-
|
|
940
|
-
type: Description
|
|
941
|
-
"""Component type discriminator. Same values as Target types."""
|
|
942
|
-
|
|
943
|
-
baseline_refs: Optional[List[str]] = None
|
|
944
|
-
"""Names of baselines that apply to this component."""
|
|
945
|
-
|
|
946
|
-
component_id: Optional[UUID] = None
|
|
947
|
-
"""Stable UUID (RFC 4122) for this component. Required in hdf-system documents, optional in
|
|
948
|
-
hdf-results. Enables cross-document correlation, diffing, and data flow references.
|
|
949
|
-
"""
|
|
950
|
-
description: Optional[str] = None
|
|
951
|
-
"""Description of this component's role or purpose."""
|
|
952
|
-
|
|
953
|
-
external_ids: Optional[Dict[str, str]] = None
|
|
954
|
-
"""Map of external identifier scheme to value. Well-known schemes: aws (instance ID), azure
|
|
955
|
-
(resource ID), cmdb (asset ID), emass (system ID), cve (CVE ID). Custom schemes are
|
|
956
|
-
allowed.
|
|
957
|
-
"""
|
|
958
|
-
input_overrides: Optional[List[InputOverride]] = None
|
|
959
|
-
"""System-specific overrides for baseline input values."""
|
|
960
|
-
|
|
961
|
-
labels: Optional[Dict[str, str]] = None
|
|
962
|
-
"""Optional key-value labels for flexible grouping. Well-known keys: system, component,
|
|
963
|
-
environment, region, team. Values must be strings.
|
|
964
|
-
"""
|
|
965
|
-
owner: Optional[Identity] = None
|
|
966
|
-
"""Team or individual responsible for this component. Enables per-component ownership when
|
|
967
|
-
different teams manage different parts of a system.
|
|
968
|
-
"""
|
|
969
|
-
sbom: Any
|
|
970
|
-
"""Embedded CycloneDX or SPDX SBOM document representing this component's software
|
|
971
|
-
inventory. The sbomFormat field determines which format constraints apply.
|
|
972
|
-
"""
|
|
973
|
-
sbom_format: Optional[SbomFormat] = None
|
|
974
|
-
"""Format of the SBOM (embedded or referenced). Required when sbom or sbomRef is present."""
|
|
975
|
-
|
|
976
|
-
sbom_ref: Optional[str] = None
|
|
977
|
-
"""URI reference to an external CycloneDX or SPDX SBOM document for this component. May be a
|
|
978
|
-
relative path, absolute URI, or fragment identifier.
|
|
979
|
-
"""
|
|
980
|
-
target_selector: Optional[Dict[str, str]] = None
|
|
981
|
-
"""Label selector to match targets belonging to this component during migration. Targets
|
|
982
|
-
with matching labels are automatically included.
|
|
983
|
-
"""
|
|
984
|
-
fqdn: Optional[str] = None
|
|
985
|
-
"""Fully qualified domain name."""
|
|
986
|
-
|
|
987
|
-
ip_address: Optional[str] = None
|
|
988
|
-
"""IP address of the host."""
|
|
989
|
-
|
|
990
|
-
mac_address: Optional[str] = None
|
|
991
|
-
"""MAC address in colon-separated hexadecimal format."""
|
|
992
|
-
|
|
993
|
-
os_name: Optional[str] = None
|
|
994
|
-
"""Operating system name."""
|
|
995
|
-
|
|
996
|
-
os_version: Optional[str] = None
|
|
997
|
-
"""Operating system version."""
|
|
998
|
-
|
|
999
|
-
digest: Optional[str] = None
|
|
1000
|
-
"""Image digest for immutable reference."""
|
|
1001
|
-
|
|
1002
|
-
image_id: Optional[str] = None
|
|
1003
|
-
"""Container image ID."""
|
|
1004
|
-
|
|
1005
|
-
registry: Optional[str] = None
|
|
1006
|
-
"""Container registry. Example: 'docker.io'."""
|
|
1007
|
-
|
|
1008
|
-
repository: Optional[str] = None
|
|
1009
|
-
"""Repository name. Example: 'library/nginx'."""
|
|
1010
|
-
|
|
1011
|
-
tag: Optional[str] = None
|
|
1012
|
-
"""Image tag. Example: '1.25'."""
|
|
1013
|
-
|
|
1014
|
-
container_id: Optional[str] = None
|
|
1015
|
-
"""Running container ID."""
|
|
1016
|
-
|
|
1017
|
-
image: Optional[str] = None
|
|
1018
|
-
"""Image the container was started from."""
|
|
1019
|
-
|
|
1020
|
-
runtime: Optional[str] = None
|
|
1021
|
-
"""Container runtime. Example: 'docker', 'containerd', 'cri-o'."""
|
|
1022
|
-
|
|
1023
|
-
cluster_name: Optional[str] = None
|
|
1024
|
-
"""Cluster name."""
|
|
1025
|
-
|
|
1026
|
-
namespace: Optional[str] = None
|
|
1027
|
-
"""Namespace within the cluster, if applicable."""
|
|
1028
|
-
|
|
1029
|
-
platform_type: Optional[str] = None
|
|
1030
|
-
"""Platform type. Example: 'kubernetes', 'openshift', 'ecs', 'docker-swarm'."""
|
|
1031
|
-
|
|
1032
|
-
version: Optional[str] = None
|
|
1033
|
-
"""Platform version.
|
|
1034
|
-
|
|
1035
|
-
Application version.
|
|
1036
|
-
|
|
1037
|
-
Package version.
|
|
1038
|
-
|
|
1039
|
-
Database version.
|
|
1040
|
-
"""
|
|
1041
|
-
account_id: Optional[str] = None
|
|
1042
|
-
"""Cloud account identifier."""
|
|
1043
|
-
|
|
1044
|
-
provider: Optional[CloudProvider] = None
|
|
1045
|
-
"""Cloud provider."""
|
|
1046
|
-
|
|
1047
|
-
region: Optional[str] = None
|
|
1048
|
-
"""Cloud region, if applicable.
|
|
1049
|
-
|
|
1050
|
-
Cloud region where the resource resides.
|
|
1051
|
-
"""
|
|
1052
|
-
arn: Optional[str] = None
|
|
1053
|
-
"""Amazon Resource Name (AWS only)."""
|
|
1054
|
-
|
|
1055
|
-
resource_id: Optional[str] = None
|
|
1056
|
-
"""Provider-specific resource identifier."""
|
|
1057
|
-
|
|
1058
|
-
resource_type: Optional[str] = None
|
|
1059
|
-
"""Type of cloud resource. Example: 'ec2:instance', 's3:bucket'."""
|
|
1060
|
-
|
|
1061
|
-
branch: Optional[str] = None
|
|
1062
|
-
"""Branch that was scanned."""
|
|
1063
|
-
|
|
1064
|
-
commit: Optional[str] = None
|
|
1065
|
-
"""Commit SHA that was scanned."""
|
|
1066
|
-
|
|
1067
|
-
url: Optional[str] = None
|
|
1068
|
-
"""Repository URL.
|
|
1069
|
-
|
|
1070
|
-
Application URL (for DAST tools).
|
|
1071
|
-
"""
|
|
1072
|
-
environment: Optional[str] = None
|
|
1073
|
-
"""Environment. Example: 'production', 'staging', 'development'."""
|
|
1074
|
-
|
|
1075
|
-
checksum: Optional[str] = None
|
|
1076
|
-
"""Package checksum for verification."""
|
|
1077
|
-
|
|
1078
|
-
package_manager: Optional[str] = None
|
|
1079
|
-
"""Package manager. Example: 'npm', 'maven', 'pip', 'nuget'."""
|
|
1080
|
-
|
|
1081
|
-
package_name: Optional[str] = None
|
|
1082
|
-
"""Package name."""
|
|
1083
|
-
|
|
1084
|
-
cidr: Optional[str] = None
|
|
1085
|
-
"""Network CIDR block."""
|
|
1086
|
-
|
|
1087
|
-
gateway: Optional[str] = None
|
|
1088
|
-
"""Network gateway address."""
|
|
1089
|
-
|
|
1090
|
-
engine: Optional[str] = None
|
|
1091
|
-
"""Database engine. Example: 'postgresql', 'mysql', 'oracle', 'mssql'."""
|
|
1092
|
-
|
|
1093
|
-
host: Optional[str] = None
|
|
1094
|
-
"""Database host."""
|
|
1095
|
-
|
|
1096
|
-
port: Optional[int] = None
|
|
1097
|
-
"""Database port."""
|
|
1098
|
-
|
|
1099
|
-
@staticmethod
|
|
1100
|
-
def from_dict(obj: Any) -> 'Component':
|
|
1101
|
-
assert isinstance(obj, dict)
|
|
1102
|
-
name = from_str(obj.get("name"))
|
|
1103
|
-
type = Description(obj.get("type"))
|
|
1104
|
-
baseline_refs = from_union([lambda x: from_list(from_str, x), from_none], obj.get("baselineRefs"))
|
|
1105
|
-
component_id = from_union([lambda x: UUID(x), from_none], obj.get("componentId"))
|
|
1106
|
-
description = from_union([from_str, from_none], obj.get("description"))
|
|
1107
|
-
external_ids = from_union([lambda x: from_dict(from_str, x), from_none], obj.get("externalIds"))
|
|
1108
|
-
input_overrides = from_union([lambda x: from_list(InputOverride.from_dict, x), from_none], obj.get("inputOverrides"))
|
|
1109
|
-
labels = from_union([lambda x: from_dict(from_str, x), from_none], obj.get("labels"))
|
|
1110
|
-
owner = from_union([Identity.from_dict, from_none], obj.get("owner"))
|
|
1111
|
-
sbom = obj.get("sbom")
|
|
1112
|
-
sbom_format = from_union([SbomFormat, from_none], obj.get("sbomFormat"))
|
|
1113
|
-
sbom_ref = from_union([from_str, from_none], obj.get("sbomRef"))
|
|
1114
|
-
target_selector = from_union([lambda x: from_dict(from_str, x), from_none], obj.get("targetSelector"))
|
|
1115
|
-
fqdn = from_union([from_str, from_none], obj.get("fqdn"))
|
|
1116
|
-
ip_address = from_union([from_str, from_none], obj.get("ipAddress"))
|
|
1117
|
-
mac_address = from_union([from_str, from_none], obj.get("macAddress"))
|
|
1118
|
-
os_name = from_union([from_str, from_none], obj.get("osName"))
|
|
1119
|
-
os_version = from_union([from_str, from_none], obj.get("osVersion"))
|
|
1120
|
-
digest = from_union([from_str, from_none], obj.get("digest"))
|
|
1121
|
-
image_id = from_union([from_str, from_none], obj.get("imageId"))
|
|
1122
|
-
registry = from_union([from_str, from_none], obj.get("registry"))
|
|
1123
|
-
repository = from_union([from_str, from_none], obj.get("repository"))
|
|
1124
|
-
tag = from_union([from_str, from_none], obj.get("tag"))
|
|
1125
|
-
container_id = from_union([from_str, from_none], obj.get("containerId"))
|
|
1126
|
-
image = from_union([from_str, from_none], obj.get("image"))
|
|
1127
|
-
runtime = from_union([from_str, from_none], obj.get("runtime"))
|
|
1128
|
-
cluster_name = from_union([from_str, from_none], obj.get("clusterName"))
|
|
1129
|
-
namespace = from_union([from_str, from_none], obj.get("namespace"))
|
|
1130
|
-
platform_type = from_union([from_str, from_none], obj.get("platformType"))
|
|
1131
|
-
version = from_union([from_str, from_none], obj.get("version"))
|
|
1132
|
-
account_id = from_union([from_str, from_none], obj.get("accountId"))
|
|
1133
|
-
provider = from_union([from_none, CloudProvider], obj.get("provider"))
|
|
1134
|
-
region = from_union([from_str, from_none], obj.get("region"))
|
|
1135
|
-
arn = from_union([from_str, from_none], obj.get("arn"))
|
|
1136
|
-
resource_id = from_union([from_str, from_none], obj.get("resourceId"))
|
|
1137
|
-
resource_type = from_union([from_str, from_none], obj.get("resourceType"))
|
|
1138
|
-
branch = from_union([from_str, from_none], obj.get("branch"))
|
|
1139
|
-
commit = from_union([from_str, from_none], obj.get("commit"))
|
|
1140
|
-
url = from_union([from_str, from_none], obj.get("url"))
|
|
1141
|
-
environment = from_union([from_str, from_none], obj.get("environment"))
|
|
1142
|
-
checksum = from_union([from_str, from_none], obj.get("checksum"))
|
|
1143
|
-
package_manager = from_union([from_str, from_none], obj.get("packageManager"))
|
|
1144
|
-
package_name = from_union([from_str, from_none], obj.get("packageName"))
|
|
1145
|
-
cidr = from_union([from_str, from_none], obj.get("cidr"))
|
|
1146
|
-
gateway = from_union([from_str, from_none], obj.get("gateway"))
|
|
1147
|
-
engine = from_union([from_str, from_none], obj.get("engine"))
|
|
1148
|
-
host = from_union([from_str, from_none], obj.get("host"))
|
|
1149
|
-
port = from_union([from_int, from_none], obj.get("port"))
|
|
1150
|
-
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)
|
|
1151
|
-
|
|
1152
|
-
def to_dict(self) -> dict:
|
|
1153
|
-
result: dict = {}
|
|
1154
|
-
result["name"] = from_str(self.name)
|
|
1155
|
-
result["type"] = to_enum(Description, self.type)
|
|
1156
|
-
if self.baseline_refs is not None:
|
|
1157
|
-
result["baselineRefs"] = from_union([lambda x: from_list(from_str, x), from_none], self.baseline_refs)
|
|
1158
|
-
if self.component_id is not None:
|
|
1159
|
-
result["componentId"] = from_union([lambda x: str(x), from_none], self.component_id)
|
|
1160
|
-
if self.description is not None:
|
|
1161
|
-
result["description"] = from_union([from_str, from_none], self.description)
|
|
1162
|
-
if self.external_ids is not None:
|
|
1163
|
-
result["externalIds"] = from_union([lambda x: from_dict(from_str, x), from_none], self.external_ids)
|
|
1164
|
-
if self.input_overrides is not None:
|
|
1165
|
-
result["inputOverrides"] = from_union([lambda x: from_list(lambda x: to_class(InputOverride, x), x), from_none], self.input_overrides)
|
|
1166
|
-
if self.labels is not None:
|
|
1167
|
-
result["labels"] = from_union([lambda x: from_dict(from_str, x), from_none], self.labels)
|
|
1168
|
-
if self.owner is not None:
|
|
1169
|
-
result["owner"] = from_union([lambda x: to_class(Identity, x), from_none], self.owner)
|
|
1170
|
-
if self.sbom is not None:
|
|
1171
|
-
result["sbom"] = self.sbom
|
|
1172
|
-
if self.sbom_format is not None:
|
|
1173
|
-
result["sbomFormat"] = from_union([lambda x: to_enum(SbomFormat, x), from_none], self.sbom_format)
|
|
1174
|
-
if self.sbom_ref is not None:
|
|
1175
|
-
result["sbomRef"] = from_union([from_str, from_none], self.sbom_ref)
|
|
1176
|
-
if self.target_selector is not None:
|
|
1177
|
-
result["targetSelector"] = from_union([lambda x: from_dict(from_str, x), from_none], self.target_selector)
|
|
1178
|
-
if self.fqdn is not None:
|
|
1179
|
-
result["fqdn"] = from_union([from_str, from_none], self.fqdn)
|
|
1180
|
-
if self.ip_address is not None:
|
|
1181
|
-
result["ipAddress"] = from_union([from_str, from_none], self.ip_address)
|
|
1182
|
-
if self.mac_address is not None:
|
|
1183
|
-
result["macAddress"] = from_union([from_str, from_none], self.mac_address)
|
|
1184
|
-
if self.os_name is not None:
|
|
1185
|
-
result["osName"] = from_union([from_str, from_none], self.os_name)
|
|
1186
|
-
if self.os_version is not None:
|
|
1187
|
-
result["osVersion"] = from_union([from_str, from_none], self.os_version)
|
|
1188
|
-
if self.digest is not None:
|
|
1189
|
-
result["digest"] = from_union([from_str, from_none], self.digest)
|
|
1190
|
-
if self.image_id is not None:
|
|
1191
|
-
result["imageId"] = from_union([from_str, from_none], self.image_id)
|
|
1192
|
-
if self.registry is not None:
|
|
1193
|
-
result["registry"] = from_union([from_str, from_none], self.registry)
|
|
1194
|
-
if self.repository is not None:
|
|
1195
|
-
result["repository"] = from_union([from_str, from_none], self.repository)
|
|
1196
|
-
if self.tag is not None:
|
|
1197
|
-
result["tag"] = from_union([from_str, from_none], self.tag)
|
|
1198
|
-
if self.container_id is not None:
|
|
1199
|
-
result["containerId"] = from_union([from_str, from_none], self.container_id)
|
|
1200
|
-
if self.image is not None:
|
|
1201
|
-
result["image"] = from_union([from_str, from_none], self.image)
|
|
1202
|
-
if self.runtime is not None:
|
|
1203
|
-
result["runtime"] = from_union([from_str, from_none], self.runtime)
|
|
1204
|
-
if self.cluster_name is not None:
|
|
1205
|
-
result["clusterName"] = from_union([from_str, from_none], self.cluster_name)
|
|
1206
|
-
if self.namespace is not None:
|
|
1207
|
-
result["namespace"] = from_union([from_str, from_none], self.namespace)
|
|
1208
|
-
if self.platform_type is not None:
|
|
1209
|
-
result["platformType"] = from_union([from_str, from_none], self.platform_type)
|
|
1210
|
-
if self.version is not None:
|
|
1211
|
-
result["version"] = from_union([from_str, from_none], self.version)
|
|
1212
|
-
if self.account_id is not None:
|
|
1213
|
-
result["accountId"] = from_union([from_str, from_none], self.account_id)
|
|
1214
|
-
if self.provider is not None:
|
|
1215
|
-
result["provider"] = from_union([from_none, lambda x: to_enum(CloudProvider, x)], self.provider)
|
|
1216
|
-
if self.region is not None:
|
|
1217
|
-
result["region"] = from_union([from_str, from_none], self.region)
|
|
1218
|
-
if self.arn is not None:
|
|
1219
|
-
result["arn"] = from_union([from_str, from_none], self.arn)
|
|
1220
|
-
if self.resource_id is not None:
|
|
1221
|
-
result["resourceId"] = from_union([from_str, from_none], self.resource_id)
|
|
1222
|
-
if self.resource_type is not None:
|
|
1223
|
-
result["resourceType"] = from_union([from_str, from_none], self.resource_type)
|
|
1224
|
-
if self.branch is not None:
|
|
1225
|
-
result["branch"] = from_union([from_str, from_none], self.branch)
|
|
1226
|
-
if self.commit is not None:
|
|
1227
|
-
result["commit"] = from_union([from_str, from_none], self.commit)
|
|
1228
|
-
if self.url is not None:
|
|
1229
|
-
result["url"] = from_union([from_str, from_none], self.url)
|
|
1230
|
-
if self.environment is not None:
|
|
1231
|
-
result["environment"] = from_union([from_str, from_none], self.environment)
|
|
1232
|
-
if self.checksum is not None:
|
|
1233
|
-
result["checksum"] = from_union([from_str, from_none], self.checksum)
|
|
1234
|
-
if self.package_manager is not None:
|
|
1235
|
-
result["packageManager"] = from_union([from_str, from_none], self.package_manager)
|
|
1236
|
-
if self.package_name is not None:
|
|
1237
|
-
result["packageName"] = from_union([from_str, from_none], self.package_name)
|
|
1238
|
-
if self.cidr is not None:
|
|
1239
|
-
result["cidr"] = from_union([from_str, from_none], self.cidr)
|
|
1240
|
-
if self.gateway is not None:
|
|
1241
|
-
result["gateway"] = from_union([from_str, from_none], self.gateway)
|
|
1242
|
-
if self.engine is not None:
|
|
1243
|
-
result["engine"] = from_union([from_str, from_none], self.engine)
|
|
1244
|
-
if self.host is not None:
|
|
1245
|
-
result["host"] = from_union([from_str, from_none], self.host)
|
|
1246
|
-
if self.port is not None:
|
|
1247
|
-
result["port"] = from_union([from_int, from_none], self.port)
|
|
1248
|
-
return result
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
class OriginalFormat(Enum):
|
|
1252
|
-
"""The original format of the source document before conversion to HDF."""
|
|
1253
|
-
|
|
1254
|
-
HDF_V2 = "hdf-v2"
|
|
1255
|
-
INSPEC_V1 = "inspec-v1"
|
|
1256
|
-
OSCAL_AR = "oscal-ar"
|
|
1257
|
-
SARIF = "sarif"
|
|
1258
|
-
XCCDF = "xccdf"
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
class SourceRole(Enum):
|
|
1262
|
-
"""The role of this source in the comparison.
|
|
1263
|
-
|
|
1264
|
-
The role of a source document in the comparison.
|
|
1265
|
-
"""
|
|
1266
|
-
GOLDEN = "golden"
|
|
1267
|
-
NEW = "new"
|
|
1268
|
-
OLD = "old"
|
|
1269
|
-
REFERENCE = "reference"
|
|
1270
|
-
SYSTEM = "system"
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
@dataclass
|
|
1274
|
-
class Tool:
|
|
1275
|
-
"""The security tool that produced the assessment data in this source.
|
|
1276
|
-
|
|
1277
|
-
The security tool that produced the assessment data represented in this HDF file. Aligns
|
|
1278
|
-
with SARIF, OSCAL, and CycloneDX terminology.
|
|
1279
|
-
"""
|
|
1280
|
-
format: Optional[str] = None
|
|
1281
|
-
"""The file format, if it is a recognized named format shared by multiple tools. Examples:
|
|
1282
|
-
'SARIF', 'XCCDF'. Omit for tool-specific formats where the tool name already implies the
|
|
1283
|
-
format (Nessus XML, gosec JSON).
|
|
1284
|
-
"""
|
|
1285
|
-
name: Optional[str] = None
|
|
1286
|
-
"""The name of the security tool that produced the data. Examples: 'gosec', 'Semgrep',
|
|
1287
|
-
'OpenSCAP', 'AWS Config', 'Nessus'. Omit if the tool cannot be identified.
|
|
1288
|
-
"""
|
|
1289
|
-
version: Optional[str] = None
|
|
1290
|
-
"""Version of the source tool, if available in the tool's output. Example: '5.22.3'."""
|
|
1291
|
-
|
|
1292
|
-
@staticmethod
|
|
1293
|
-
def from_dict(obj: Any) -> 'Tool':
|
|
1294
|
-
assert isinstance(obj, dict)
|
|
1295
|
-
format = from_union([from_str, from_none], obj.get("format"))
|
|
1296
|
-
name = from_union([from_str, from_none], obj.get("name"))
|
|
1297
|
-
version = from_union([from_str, from_none], obj.get("version"))
|
|
1298
|
-
return Tool(format, name, version)
|
|
1299
|
-
|
|
1300
|
-
def to_dict(self) -> dict:
|
|
1301
|
-
result: dict = {}
|
|
1302
|
-
if self.format is not None:
|
|
1303
|
-
result["format"] = from_union([from_str, from_none], self.format)
|
|
1304
|
-
if self.name is not None:
|
|
1305
|
-
result["name"] = from_union([from_str, from_none], self.name)
|
|
1306
|
-
if self.version is not None:
|
|
1307
|
-
result["version"] = from_union([from_str, from_none], self.version)
|
|
1308
|
-
return result
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
@dataclass
|
|
1312
|
-
class Source:
|
|
1313
|
-
"""A source document participating in the comparison."""
|
|
1314
|
-
|
|
1315
|
-
label: str
|
|
1316
|
-
"""Human-readable label for this source. Example: 'Before remediation scan'."""
|
|
1317
|
-
|
|
1318
|
-
role: SourceRole
|
|
1319
|
-
"""The role of this source in the comparison."""
|
|
1320
|
-
|
|
1321
|
-
assessment_timestamp: Optional[datetime] = None
|
|
1322
|
-
"""When the source assessment was performed. ISO 8601 format."""
|
|
1323
|
-
|
|
1324
|
-
baseline_ref: Optional[BaselineRef] = None
|
|
1325
|
-
"""Reference to the baseline used in this source assessment."""
|
|
1326
|
-
|
|
1327
|
-
checksum: Optional[Checksum] = None
|
|
1328
|
-
"""Cryptographic checksum of the source document for integrity verification."""
|
|
1329
|
-
|
|
1330
|
-
components: Optional[List[Component]] = None
|
|
1331
|
-
"""The components assessed in this source."""
|
|
1332
|
-
|
|
1333
|
-
original_format: Optional[OriginalFormat] = None
|
|
1334
|
-
"""The original format of the source document before conversion to HDF."""
|
|
1335
|
-
|
|
1336
|
-
tool: Optional[Tool] = None
|
|
1337
|
-
"""The security tool that produced the assessment data in this source."""
|
|
1338
|
-
|
|
1339
|
-
uri: Optional[str] = None
|
|
1340
|
-
"""URI pointing to the source document."""
|
|
1341
|
-
|
|
1342
|
-
@staticmethod
|
|
1343
|
-
def from_dict(obj: Any) -> 'Source':
|
|
1344
|
-
assert isinstance(obj, dict)
|
|
1345
|
-
label = from_str(obj.get("label"))
|
|
1346
|
-
role = SourceRole(obj.get("role"))
|
|
1347
|
-
assessment_timestamp = from_union([from_datetime, from_none], obj.get("assessmentTimestamp"))
|
|
1348
|
-
baseline_ref = from_union([BaselineRef.from_dict, from_none], obj.get("baselineRef"))
|
|
1349
|
-
checksum = from_union([Checksum.from_dict, from_none], obj.get("checksum"))
|
|
1350
|
-
components = from_union([lambda x: from_list(Component.from_dict, x), from_none], obj.get("components"))
|
|
1351
|
-
original_format = from_union([OriginalFormat, from_none], obj.get("originalFormat"))
|
|
1352
|
-
tool = from_union([Tool.from_dict, from_none], obj.get("tool"))
|
|
1353
|
-
uri = from_union([from_str, from_none], obj.get("uri"))
|
|
1354
|
-
return Source(label, role, assessment_timestamp, baseline_ref, checksum, components, original_format, tool, uri)
|
|
1355
|
-
|
|
1356
|
-
def to_dict(self) -> dict:
|
|
1357
|
-
result: dict = {}
|
|
1358
|
-
result["label"] = from_str(self.label)
|
|
1359
|
-
result["role"] = to_enum(SourceRole, self.role)
|
|
1360
|
-
if self.assessment_timestamp is not None:
|
|
1361
|
-
result["assessmentTimestamp"] = from_union([lambda x: x.isoformat(), from_none], self.assessment_timestamp)
|
|
1362
|
-
if self.baseline_ref is not None:
|
|
1363
|
-
result["baselineRef"] = from_union([lambda x: to_class(BaselineRef, x), from_none], self.baseline_ref)
|
|
1364
|
-
if self.checksum is not None:
|
|
1365
|
-
result["checksum"] = from_union([lambda x: to_class(Checksum, x), from_none], self.checksum)
|
|
1366
|
-
if self.components is not None:
|
|
1367
|
-
result["components"] = from_union([lambda x: from_list(lambda x: to_class(Component, x), x), from_none], self.components)
|
|
1368
|
-
if self.original_format is not None:
|
|
1369
|
-
result["originalFormat"] = from_union([lambda x: to_enum(OriginalFormat, x), from_none], self.original_format)
|
|
1370
|
-
if self.tool is not None:
|
|
1371
|
-
result["tool"] = from_union([lambda x: to_class(Tool, x), from_none], self.tool)
|
|
1372
|
-
if self.uri is not None:
|
|
1373
|
-
result["uri"] = from_union([from_str, from_none], self.uri)
|
|
1374
|
-
return result
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
@dataclass
|
|
1378
|
-
class StateCounts:
|
|
1379
|
-
"""State counts for critical severity requirements.
|
|
1380
|
-
|
|
1381
|
-
Counts of requirements in each state.
|
|
1382
|
-
|
|
1383
|
-
State counts for high severity requirements.
|
|
1384
|
-
|
|
1385
|
-
State counts for low severity requirements.
|
|
1386
|
-
|
|
1387
|
-
State counts for medium severity requirements.
|
|
1388
|
-
"""
|
|
1389
|
-
absent: Optional[int] = None
|
|
1390
|
-
"""Number of requirements present only in the old source."""
|
|
1391
|
-
|
|
1392
|
-
fixed: Optional[int] = None
|
|
1393
|
-
"""Number of requirements that changed from failing to passing."""
|
|
1394
|
-
|
|
1395
|
-
moved: Optional[int] = None
|
|
1396
|
-
"""Number of requirements that were reorganized without content change."""
|
|
1397
|
-
|
|
1398
|
-
new: Optional[int] = None
|
|
1399
|
-
"""Number of requirements present only in the new source."""
|
|
1400
|
-
|
|
1401
|
-
regressed: Optional[int] = None
|
|
1402
|
-
"""Number of requirements that changed from passing to failing."""
|
|
1403
|
-
|
|
1404
|
-
unchanged: Optional[int] = None
|
|
1405
|
-
"""Number of requirements with the same effective status."""
|
|
1406
|
-
|
|
1407
|
-
updated: Optional[int] = None
|
|
1408
|
-
"""Number of requirements with a generic status change."""
|
|
1409
|
-
|
|
1410
|
-
@staticmethod
|
|
1411
|
-
def from_dict(obj: Any) -> 'StateCounts':
|
|
1412
|
-
assert isinstance(obj, dict)
|
|
1413
|
-
absent = from_union([from_int, from_none], obj.get("absent"))
|
|
1414
|
-
fixed = from_union([from_int, from_none], obj.get("fixed"))
|
|
1415
|
-
moved = from_union([from_int, from_none], obj.get("moved"))
|
|
1416
|
-
new = from_union([from_int, from_none], obj.get("new"))
|
|
1417
|
-
regressed = from_union([from_int, from_none], obj.get("regressed"))
|
|
1418
|
-
unchanged = from_union([from_int, from_none], obj.get("unchanged"))
|
|
1419
|
-
updated = from_union([from_int, from_none], obj.get("updated"))
|
|
1420
|
-
return StateCounts(absent, fixed, moved, new, regressed, unchanged, updated)
|
|
1421
|
-
|
|
1422
|
-
def to_dict(self) -> dict:
|
|
1423
|
-
result: dict = {}
|
|
1424
|
-
if self.absent is not None:
|
|
1425
|
-
result["absent"] = from_union([from_int, from_none], self.absent)
|
|
1426
|
-
if self.fixed is not None:
|
|
1427
|
-
result["fixed"] = from_union([from_int, from_none], self.fixed)
|
|
1428
|
-
if self.moved is not None:
|
|
1429
|
-
result["moved"] = from_union([from_int, from_none], self.moved)
|
|
1430
|
-
if self.new is not None:
|
|
1431
|
-
result["new"] = from_union([from_int, from_none], self.new)
|
|
1432
|
-
if self.regressed is not None:
|
|
1433
|
-
result["regressed"] = from_union([from_int, from_none], self.regressed)
|
|
1434
|
-
if self.unchanged is not None:
|
|
1435
|
-
result["unchanged"] = from_union([from_int, from_none], self.unchanged)
|
|
1436
|
-
if self.updated is not None:
|
|
1437
|
-
result["updated"] = from_union([from_int, from_none], self.updated)
|
|
1438
|
-
return result
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
@dataclass
|
|
1442
|
-
class SeverityBreakdown:
|
|
1443
|
-
"""State counts broken down by severity level.
|
|
1444
|
-
|
|
1445
|
-
Breakdown of state counts by severity level.
|
|
1446
|
-
"""
|
|
1447
|
-
critical: Optional[StateCounts] = None
|
|
1448
|
-
"""State counts for critical severity requirements."""
|
|
1449
|
-
|
|
1450
|
-
high: Optional[StateCounts] = None
|
|
1451
|
-
"""State counts for high severity requirements."""
|
|
1452
|
-
|
|
1453
|
-
low: Optional[StateCounts] = None
|
|
1454
|
-
"""State counts for low severity requirements."""
|
|
1455
|
-
|
|
1456
|
-
medium: Optional[StateCounts] = None
|
|
1457
|
-
"""State counts for medium severity requirements."""
|
|
1458
|
-
|
|
1459
|
-
@staticmethod
|
|
1460
|
-
def from_dict(obj: Any) -> 'SeverityBreakdown':
|
|
1461
|
-
assert isinstance(obj, dict)
|
|
1462
|
-
critical = from_union([StateCounts.from_dict, from_none], obj.get("critical"))
|
|
1463
|
-
high = from_union([StateCounts.from_dict, from_none], obj.get("high"))
|
|
1464
|
-
low = from_union([StateCounts.from_dict, from_none], obj.get("low"))
|
|
1465
|
-
medium = from_union([StateCounts.from_dict, from_none], obj.get("medium"))
|
|
1466
|
-
return SeverityBreakdown(critical, high, low, medium)
|
|
1467
|
-
|
|
1468
|
-
def to_dict(self) -> dict:
|
|
1469
|
-
result: dict = {}
|
|
1470
|
-
if self.critical is not None:
|
|
1471
|
-
result["critical"] = from_union([lambda x: to_class(StateCounts, x), from_none], self.critical)
|
|
1472
|
-
if self.high is not None:
|
|
1473
|
-
result["high"] = from_union([lambda x: to_class(StateCounts, x), from_none], self.high)
|
|
1474
|
-
if self.low is not None:
|
|
1475
|
-
result["low"] = from_union([lambda x: to_class(StateCounts, x), from_none], self.low)
|
|
1476
|
-
if self.medium is not None:
|
|
1477
|
-
result["medium"] = from_union([lambda x: to_class(StateCounts, x), from_none], self.medium)
|
|
1478
|
-
return result
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
@dataclass
|
|
1482
|
-
class PerSourceSummary:
|
|
1483
|
-
"""Summary statistics for a single source in a multi-source comparison."""
|
|
1484
|
-
|
|
1485
|
-
label: str
|
|
1486
|
-
"""Human-readable label for this source."""
|
|
1487
|
-
|
|
1488
|
-
source_index: int
|
|
1489
|
-
"""Zero-based index into the sources array identifying which source this summary is for."""
|
|
1490
|
-
|
|
1491
|
-
absent: Optional[int] = None
|
|
1492
|
-
"""Number of requirements present only in the old source."""
|
|
1493
|
-
|
|
1494
|
-
fixed: Optional[int] = None
|
|
1495
|
-
"""Number of requirements that changed from failing to passing."""
|
|
1496
|
-
|
|
1497
|
-
moved: Optional[int] = None
|
|
1498
|
-
"""Number of requirements that were reorganized without content change."""
|
|
1499
|
-
|
|
1500
|
-
new: Optional[int] = None
|
|
1501
|
-
"""Number of requirements present only in the new source."""
|
|
1502
|
-
|
|
1503
|
-
regressed: Optional[int] = None
|
|
1504
|
-
"""Number of requirements that changed from passing to failing."""
|
|
1505
|
-
|
|
1506
|
-
unchanged: Optional[int] = None
|
|
1507
|
-
"""Number of requirements with the same effective status."""
|
|
1508
|
-
|
|
1509
|
-
updated: Optional[int] = None
|
|
1510
|
-
"""Number of requirements with a generic status change."""
|
|
1511
|
-
|
|
1512
|
-
@staticmethod
|
|
1513
|
-
def from_dict(obj: Any) -> 'PerSourceSummary':
|
|
1514
|
-
assert isinstance(obj, dict)
|
|
1515
|
-
label = from_str(obj.get("label"))
|
|
1516
|
-
source_index = from_int(obj.get("sourceIndex"))
|
|
1517
|
-
absent = from_union([from_int, from_none], obj.get("absent"))
|
|
1518
|
-
fixed = from_union([from_int, from_none], obj.get("fixed"))
|
|
1519
|
-
moved = from_union([from_int, from_none], obj.get("moved"))
|
|
1520
|
-
new = from_union([from_int, from_none], obj.get("new"))
|
|
1521
|
-
regressed = from_union([from_int, from_none], obj.get("regressed"))
|
|
1522
|
-
unchanged = from_union([from_int, from_none], obj.get("unchanged"))
|
|
1523
|
-
updated = from_union([from_int, from_none], obj.get("updated"))
|
|
1524
|
-
return PerSourceSummary(label, source_index, absent, fixed, moved, new, regressed, unchanged, updated)
|
|
1525
|
-
|
|
1526
|
-
def to_dict(self) -> dict:
|
|
1527
|
-
result: dict = {}
|
|
1528
|
-
result["label"] = from_str(self.label)
|
|
1529
|
-
result["sourceIndex"] = from_int(self.source_index)
|
|
1530
|
-
if self.absent is not None:
|
|
1531
|
-
result["absent"] = from_union([from_int, from_none], self.absent)
|
|
1532
|
-
if self.fixed is not None:
|
|
1533
|
-
result["fixed"] = from_union([from_int, from_none], self.fixed)
|
|
1534
|
-
if self.moved is not None:
|
|
1535
|
-
result["moved"] = from_union([from_int, from_none], self.moved)
|
|
1536
|
-
if self.new is not None:
|
|
1537
|
-
result["new"] = from_union([from_int, from_none], self.new)
|
|
1538
|
-
if self.regressed is not None:
|
|
1539
|
-
result["regressed"] = from_union([from_int, from_none], self.regressed)
|
|
1540
|
-
if self.unchanged is not None:
|
|
1541
|
-
result["unchanged"] = from_union([from_int, from_none], self.unchanged)
|
|
1542
|
-
if self.updated is not None:
|
|
1543
|
-
result["updated"] = from_union([from_int, from_none], self.updated)
|
|
1544
|
-
return result
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
@dataclass
|
|
1548
|
-
class ComparisonSummary:
|
|
1549
|
-
"""Summary statistics for the overall comparison."""
|
|
1550
|
-
|
|
1551
|
-
matched_count: int
|
|
1552
|
-
"""Number of requirements successfully matched between sources."""
|
|
1553
|
-
|
|
1554
|
-
total: int
|
|
1555
|
-
"""Total number of unique requirements across all sources."""
|
|
1556
|
-
|
|
1557
|
-
unmatched_new_count: int
|
|
1558
|
-
"""Number of requirements in the new source with no match in the old source."""
|
|
1559
|
-
|
|
1560
|
-
unmatched_old_count: int
|
|
1561
|
-
"""Number of requirements in the old source with no match in the new source."""
|
|
1562
|
-
|
|
1563
|
-
absent: Optional[int] = None
|
|
1564
|
-
"""Number of requirements present only in the old source."""
|
|
1565
|
-
|
|
1566
|
-
average_match_confidence: Optional[float] = None
|
|
1567
|
-
"""Average confidence score across all requirement matches (0-1)."""
|
|
1568
|
-
|
|
1569
|
-
by_severity: Optional[SeverityBreakdown] = None
|
|
1570
|
-
"""State counts broken down by severity level."""
|
|
1571
|
-
|
|
1572
|
-
compliance_delta: Optional[float] = None
|
|
1573
|
-
"""Change in compliance percentage (new - old)."""
|
|
1574
|
-
|
|
1575
|
-
fixed: Optional[int] = None
|
|
1576
|
-
"""Number of requirements that changed from failing to passing."""
|
|
1577
|
-
|
|
1578
|
-
moved: Optional[int] = None
|
|
1579
|
-
"""Number of requirements that were reorganized without content change."""
|
|
1580
|
-
|
|
1581
|
-
new: Optional[int] = None
|
|
1582
|
-
"""Number of requirements present only in the new source."""
|
|
1583
|
-
|
|
1584
|
-
new_compliance_percent: Optional[float] = None
|
|
1585
|
-
"""Compliance percentage of the new source (0-100)."""
|
|
1586
|
-
|
|
1587
|
-
old_compliance_percent: Optional[float] = None
|
|
1588
|
-
"""Compliance percentage of the old source (0-100)."""
|
|
1589
|
-
|
|
1590
|
-
per_source: Optional[List[PerSourceSummary]] = None
|
|
1591
|
-
"""Summary statistics for each individual source in a multi-source comparison."""
|
|
1592
|
-
|
|
1593
|
-
regressed: Optional[int] = None
|
|
1594
|
-
"""Number of requirements that changed from passing to failing."""
|
|
1595
|
-
|
|
1596
|
-
unchanged: Optional[int] = None
|
|
1597
|
-
"""Number of requirements with the same effective status."""
|
|
1598
|
-
|
|
1599
|
-
updated: Optional[int] = None
|
|
1600
|
-
"""Number of requirements with a generic status change."""
|
|
1601
|
-
|
|
1602
|
-
@staticmethod
|
|
1603
|
-
def from_dict(obj: Any) -> 'ComparisonSummary':
|
|
1604
|
-
assert isinstance(obj, dict)
|
|
1605
|
-
matched_count = from_int(obj.get("matchedCount"))
|
|
1606
|
-
total = from_int(obj.get("total"))
|
|
1607
|
-
unmatched_new_count = from_int(obj.get("unmatchedNewCount"))
|
|
1608
|
-
unmatched_old_count = from_int(obj.get("unmatchedOldCount"))
|
|
1609
|
-
absent = from_union([from_int, from_none], obj.get("absent"))
|
|
1610
|
-
average_match_confidence = from_union([from_float, from_none], obj.get("averageMatchConfidence"))
|
|
1611
|
-
by_severity = from_union([SeverityBreakdown.from_dict, from_none], obj.get("bySeverity"))
|
|
1612
|
-
compliance_delta = from_union([from_float, from_none], obj.get("complianceDelta"))
|
|
1613
|
-
fixed = from_union([from_int, from_none], obj.get("fixed"))
|
|
1614
|
-
moved = from_union([from_int, from_none], obj.get("moved"))
|
|
1615
|
-
new = from_union([from_int, from_none], obj.get("new"))
|
|
1616
|
-
new_compliance_percent = from_union([from_float, from_none], obj.get("newCompliancePercent"))
|
|
1617
|
-
old_compliance_percent = from_union([from_float, from_none], obj.get("oldCompliancePercent"))
|
|
1618
|
-
per_source = from_union([lambda x: from_list(PerSourceSummary.from_dict, x), from_none], obj.get("perSource"))
|
|
1619
|
-
regressed = from_union([from_int, from_none], obj.get("regressed"))
|
|
1620
|
-
unchanged = from_union([from_int, from_none], obj.get("unchanged"))
|
|
1621
|
-
updated = from_union([from_int, from_none], obj.get("updated"))
|
|
1622
|
-
return ComparisonSummary(matched_count, total, unmatched_new_count, unmatched_old_count, absent, average_match_confidence, by_severity, compliance_delta, fixed, moved, new, new_compliance_percent, old_compliance_percent, per_source, regressed, unchanged, updated)
|
|
1623
|
-
|
|
1624
|
-
def to_dict(self) -> dict:
|
|
1625
|
-
result: dict = {}
|
|
1626
|
-
result["matchedCount"] = from_int(self.matched_count)
|
|
1627
|
-
result["total"] = from_int(self.total)
|
|
1628
|
-
result["unmatchedNewCount"] = from_int(self.unmatched_new_count)
|
|
1629
|
-
result["unmatchedOldCount"] = from_int(self.unmatched_old_count)
|
|
1630
|
-
if self.absent is not None:
|
|
1631
|
-
result["absent"] = from_union([from_int, from_none], self.absent)
|
|
1632
|
-
if self.average_match_confidence is not None:
|
|
1633
|
-
result["averageMatchConfidence"] = from_union([to_float, from_none], self.average_match_confidence)
|
|
1634
|
-
if self.by_severity is not None:
|
|
1635
|
-
result["bySeverity"] = from_union([lambda x: to_class(SeverityBreakdown, x), from_none], self.by_severity)
|
|
1636
|
-
if self.compliance_delta is not None:
|
|
1637
|
-
result["complianceDelta"] = from_union([to_float, from_none], self.compliance_delta)
|
|
1638
|
-
if self.fixed is not None:
|
|
1639
|
-
result["fixed"] = from_union([from_int, from_none], self.fixed)
|
|
1640
|
-
if self.moved is not None:
|
|
1641
|
-
result["moved"] = from_union([from_int, from_none], self.moved)
|
|
1642
|
-
if self.new is not None:
|
|
1643
|
-
result["new"] = from_union([from_int, from_none], self.new)
|
|
1644
|
-
if self.new_compliance_percent is not None:
|
|
1645
|
-
result["newCompliancePercent"] = from_union([to_float, from_none], self.new_compliance_percent)
|
|
1646
|
-
if self.old_compliance_percent is not None:
|
|
1647
|
-
result["oldCompliancePercent"] = from_union([to_float, from_none], self.old_compliance_percent)
|
|
1648
|
-
if self.per_source is not None:
|
|
1649
|
-
result["perSource"] = from_union([lambda x: from_list(lambda x: to_class(PerSourceSummary, x), x), from_none], self.per_source)
|
|
1650
|
-
if self.regressed is not None:
|
|
1651
|
-
result["regressed"] = from_union([from_int, from_none], self.regressed)
|
|
1652
|
-
if self.unchanged is not None:
|
|
1653
|
-
result["unchanged"] = from_union([from_int, from_none], self.unchanged)
|
|
1654
|
-
if self.updated is not None:
|
|
1655
|
-
result["updated"] = from_union([from_int, from_none], self.updated)
|
|
1656
|
-
return result
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
@dataclass
|
|
1660
|
-
class HdfComparison:
|
|
1661
|
-
"""Structured comparison between two or more HDF security assessment documents. Supports
|
|
1662
|
-
temporal, baseline, fleet, and multi-source comparison modes.
|
|
1663
|
-
"""
|
|
1664
|
-
comparison_mode: ComparisonMode
|
|
1665
|
-
"""The mode of comparison being performed."""
|
|
1666
|
-
|
|
1667
|
-
format_version: FormatVersion
|
|
1668
|
-
"""Schema version for this comparison format."""
|
|
1669
|
-
|
|
1670
|
-
requirement_diffs: List[RequirementDiff]
|
|
1671
|
-
"""Detailed comparison of individual requirements between sources."""
|
|
1672
|
-
|
|
1673
|
-
sources: List[Source]
|
|
1674
|
-
"""The source documents being compared. At least two sources are required."""
|
|
1675
|
-
|
|
1676
|
-
summary: ComparisonSummary
|
|
1677
|
-
"""Summary statistics for the overall comparison."""
|
|
1678
|
-
|
|
1679
|
-
annotations: Optional[Dict[str, Annotation]] = None
|
|
1680
|
-
"""Map of annotation IDs to annotation objects, providing context or action items for
|
|
1681
|
-
requirement diffs.
|
|
1682
|
-
"""
|
|
1683
|
-
baseline_diffs: Optional[List[BaselineDiff]] = None
|
|
1684
|
-
"""Comparison of baselines between sources."""
|
|
1685
|
-
|
|
1686
|
-
component_diffs: Optional[List[ComponentDiff]] = None
|
|
1687
|
-
"""Comparison of components between two system documents. Used in systemDrift mode."""
|
|
1688
|
-
|
|
1689
|
-
drift: Optional[List[RequirementDiff]] = None
|
|
1690
|
-
"""External/metadata changes separate from status changes (Terraform pattern)."""
|
|
1691
|
-
|
|
1692
|
-
extensions: Optional[Dict[str, Any]] = None
|
|
1693
|
-
"""Reserved for tool-specific data not defined in the HDF standard."""
|
|
1694
|
-
|
|
1695
|
-
generator: Optional[Generator] = None
|
|
1696
|
-
"""Information about the tool that generated this comparison."""
|
|
1697
|
-
|
|
1698
|
-
integrity: Optional[Integrity] = None
|
|
1699
|
-
"""Cryptographic integrity information for verifying this comparison document."""
|
|
1700
|
-
|
|
1701
|
-
matching: Optional[MatchingConfig] = None
|
|
1702
|
-
"""Configuration for how requirements were matched across sources."""
|
|
1703
|
-
|
|
1704
|
-
package_diffs: Optional[List[PackageDiff]] = None
|
|
1705
|
-
"""Comparison of packages between two SBOMs. Used in systemDrift mode for SBOM comparison."""
|
|
1706
|
-
|
|
1707
|
-
system_ref: Optional[str] = None
|
|
1708
|
-
"""URI identifying the system being compared in systemDrift mode."""
|
|
1709
|
-
|
|
1710
|
-
timestamp: Optional[datetime] = None
|
|
1711
|
-
"""When this comparison was performed."""
|
|
1712
|
-
|
|
1713
|
-
@staticmethod
|
|
1714
|
-
def from_dict(obj: Any) -> 'HdfComparison':
|
|
1715
|
-
assert isinstance(obj, dict)
|
|
1716
|
-
comparison_mode = ComparisonMode(obj.get("comparisonMode"))
|
|
1717
|
-
format_version = FormatVersion(obj.get("formatVersion"))
|
|
1718
|
-
requirement_diffs = from_list(RequirementDiff.from_dict, obj.get("requirementDiffs"))
|
|
1719
|
-
sources = from_list(Source.from_dict, obj.get("sources"))
|
|
1720
|
-
summary = ComparisonSummary.from_dict(obj.get("summary"))
|
|
1721
|
-
annotations = from_union([lambda x: from_dict(Annotation.from_dict, x), from_none], obj.get("annotations"))
|
|
1722
|
-
baseline_diffs = from_union([lambda x: from_list(BaselineDiff.from_dict, x), from_none], obj.get("baselineDiffs"))
|
|
1723
|
-
component_diffs = from_union([lambda x: from_list(ComponentDiff.from_dict, x), from_none], obj.get("componentDiffs"))
|
|
1724
|
-
drift = from_union([lambda x: from_list(RequirementDiff.from_dict, x), from_none], obj.get("drift"))
|
|
1725
|
-
extensions = from_union([lambda x: from_dict(lambda x: x, x), from_none], obj.get("extensions"))
|
|
1726
|
-
generator = from_union([Generator.from_dict, from_none], obj.get("generator"))
|
|
1727
|
-
integrity = from_union([Integrity.from_dict, from_none], obj.get("integrity"))
|
|
1728
|
-
matching = from_union([MatchingConfig.from_dict, from_none], obj.get("matching"))
|
|
1729
|
-
package_diffs = from_union([lambda x: from_list(PackageDiff.from_dict, x), from_none], obj.get("packageDiffs"))
|
|
1730
|
-
system_ref = from_union([from_str, from_none], obj.get("systemRef"))
|
|
1731
|
-
timestamp = from_union([from_datetime, from_none], obj.get("timestamp"))
|
|
1732
|
-
return HdfComparison(comparison_mode, format_version, requirement_diffs, sources, summary, annotations, baseline_diffs, component_diffs, drift, extensions, generator, integrity, matching, package_diffs, system_ref, timestamp)
|
|
1733
|
-
|
|
1734
|
-
def to_dict(self) -> dict:
|
|
1735
|
-
result: dict = {}
|
|
1736
|
-
result["comparisonMode"] = to_enum(ComparisonMode, self.comparison_mode)
|
|
1737
|
-
result["formatVersion"] = to_enum(FormatVersion, self.format_version)
|
|
1738
|
-
result["requirementDiffs"] = from_list(lambda x: to_class(RequirementDiff, x), self.requirement_diffs)
|
|
1739
|
-
result["sources"] = from_list(lambda x: to_class(Source, x), self.sources)
|
|
1740
|
-
result["summary"] = to_class(ComparisonSummary, self.summary)
|
|
1741
|
-
if self.annotations is not None:
|
|
1742
|
-
result["annotations"] = from_union([lambda x: from_dict(lambda x: to_class(Annotation, x), x), from_none], self.annotations)
|
|
1743
|
-
if self.baseline_diffs is not None:
|
|
1744
|
-
result["baselineDiffs"] = from_union([lambda x: from_list(lambda x: to_class(BaselineDiff, x), x), from_none], self.baseline_diffs)
|
|
1745
|
-
if self.component_diffs is not None:
|
|
1746
|
-
result["componentDiffs"] = from_union([lambda x: from_list(lambda x: to_class(ComponentDiff, x), x), from_none], self.component_diffs)
|
|
1747
|
-
if self.drift is not None:
|
|
1748
|
-
result["drift"] = from_union([lambda x: from_list(lambda x: to_class(RequirementDiff, x), x), from_none], self.drift)
|
|
1749
|
-
if self.extensions is not None:
|
|
1750
|
-
result["extensions"] = from_union([lambda x: from_dict(lambda x: x, x), from_none], self.extensions)
|
|
1751
|
-
if self.generator is not None:
|
|
1752
|
-
result["generator"] = from_union([lambda x: to_class(Generator, x), from_none], self.generator)
|
|
1753
|
-
if self.integrity is not None:
|
|
1754
|
-
result["integrity"] = from_union([lambda x: to_class(Integrity, x), from_none], self.integrity)
|
|
1755
|
-
if self.matching is not None:
|
|
1756
|
-
result["matching"] = from_union([lambda x: to_class(MatchingConfig, x), from_none], self.matching)
|
|
1757
|
-
if self.package_diffs is not None:
|
|
1758
|
-
result["packageDiffs"] = from_union([lambda x: from_list(lambda x: to_class(PackageDiff, x), x), from_none], self.package_diffs)
|
|
1759
|
-
if self.system_ref is not None:
|
|
1760
|
-
result["systemRef"] = from_union([from_str, from_none], self.system_ref)
|
|
1761
|
-
if self.timestamp is not None:
|
|
1762
|
-
result["timestamp"] = from_union([lambda x: x.isoformat(), from_none], self.timestamp)
|
|
1763
|
-
return result
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
def hdf_comparison_from_dict(s: Any) -> HdfComparison:
|
|
1767
|
-
return HdfComparison.from_dict(s)
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
def hdf_comparison_to_dict(x: HdfComparison) -> Any:
|
|
1771
|
-
return to_class(HdfComparison, x)
|