Coverage for csaf/vulnerability.py: 94.51%
178 statements
« prev ^ index » next coverage.py v7.6.9, created at 2024-12-18 20:12:48 +00:00
« prev ^ index » next coverage.py v7.6.9, created at 2024-12-18 20:12:48 +00:00
1"""CSAF Vulnerability model."""
3from __future__ import annotations
5from datetime import datetime
6from enum import Enum
7from typing import Annotated, List, Optional, Union, no_type_check
9from pydantic import AnyUrl, BaseModel, Field, RootModel, field_validator
11from csaf.cvss import CVSS2, CVSS30, CVSS31
12from csaf.definitions import Acknowledgments, Flags, Ids, Notes, ProductGroupIds, Products, References
13from csaf.product import ProductStatus
16class Cwe(BaseModel):
17 """
18 Holds the MITRE standard Common Weakness Enumeration (CWE) for the weakness associated.
19 """
21 id: Annotated[
22 str,
23 Field(
24 description='Holds the ID for the weakness associated.',
25 examples=['CWE-22', 'CWE-352', 'CWE-79'],
26 pattern='^CWE-[1-9]\\d{0,5}$',
27 title='Weakness ID',
28 ),
29 ]
30 name: Annotated[
31 str,
32 Field(
33 description='Holds the full name of the weakness as given in the CWE specification.',
34 examples=[
35 'Cross-Site Request Forgery (CSRF)',
36 "Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')",
37 "Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')",
38 ],
39 min_length=1,
40 title='Weakness name',
41 ),
42 ]
45class PartyCategory(Enum):
46 """
47 Defines the category of the involved party.
48 """
50 coordinator = 'coordinator'
51 discoverer = 'discoverer'
52 other = 'other'
53 user = 'user'
54 vendor = 'vendor'
57class PartyStatus(Enum):
58 """
59 Defines contact status of the involved party.
60 """
62 completed = 'completed'
63 contact_attempted = 'contact_attempted'
64 disputed = 'disputed'
65 in_progress = 'in_progress'
66 not_contacted = 'not_contacted'
67 open = 'open'
70class Involvement(BaseModel):
71 """
72 Is a container, that allows the document producers to comment on the level of involvement (or engagement)
73 of themselves or third parties in the vulnerability identification, scoping, and remediation process.
74 """
76 date: Annotated[
77 Optional[datetime],
78 Field(
79 description='Holds the date and time of the involvement entry.',
80 title='Date of involvement',
81 ),
82 ] = None
83 party: Annotated[
84 PartyCategory,
85 Field(
86 description='Defines the category of the involved party.',
87 title='Party category',
88 ),
89 ]
90 status: Annotated[
91 PartyStatus,
92 Field(
93 description='Defines contact status of the involved party.',
94 title='Party status',
95 ),
96 ]
97 summary: Annotated[
98 Optional[str],
99 Field(
100 description='Contains additional context regarding what is going on.',
101 min_length=1,
102 title='Summary of the involvement',
103 ),
104 ] = None
107class RemediationCategory(Enum):
108 """
109 Specifies the category which this remediation belongs to.
110 """
112 mitigation = 'mitigation'
113 no_fix_planned = 'no_fix_planned'
114 none_available = 'none_available'
115 vendor_fix = 'vendor_fix'
116 workaround = 'workaround'
119class Entitlement(
120 RootModel[
121 Annotated[
122 str,
123 Field(
124 description='Contains any possible vendor-defined constraints for obtaining fixed software or hardware'
125 'that fully resolves the vulnerability.',
126 min_length=1,
127 title='Entitlement of the remediation',
128 ),
129 ]
130 ]
131):
132 pass
135class RestartRequiredCategory(Enum):
136 """
137 Specifies what category of restart is required by this remediation to become effective.
138 """
140 connected = 'connected'
141 dependencies = 'dependencies'
142 machine = 'machine'
143 none = 'none'
144 parent = 'parent'
145 service = 'service'
146 system = 'system'
147 vulnerable_component = 'vulnerable_component'
148 zone = 'zone'
151class RestartRequired(BaseModel):
152 """
153 Provides information on category of restart is required by this remediation to become effective.
154 """
156 category: Annotated[
157 RestartRequiredCategory,
158 Field(
159 description='Specifies what category of restart is required by this remediation to become effective.',
160 title='Category of restart',
161 ),
162 ]
163 details: Annotated[
164 Optional[str],
165 Field(
166 description='Provides additional information for the restart. This can include details on procedures,'
167 'scope or impact.',
168 min_length=1,
169 title='Additional restart information',
170 ),
171 ] = None
174class ThreatCategory(Enum):
175 """
176 Categorizes the threat according to the rules of the specification.
177 """
179 exploit_status = 'exploit_status'
180 impact = 'impact'
181 target_set = 'target_set'
184class ScoreType(BaseModel):
185 value: Annotated[float, Field(ge=0.0, le=10.0)]
188class AttackVectorType(Enum):
189 network = 'NETWORK'
190 adjacent_network = 'ADJACENT_NETWORK'
191 local = 'LOCAL'
192 physical = 'PHYSICAL'
195class ModifiedAttackVectorType(Enum):
196 network = 'NETWORK'
197 adjacent_network = 'ADJACENT_NETWORK'
198 local = 'LOCAL'
199 physical = 'PHYSICAL'
200 not_defined = 'NOT_DEFINED'
203class AttackComplexityType(Enum):
204 high = 'HIGH'
205 low = 'LOW'
208class ModifiedAttackComplexityType(Enum):
209 high = 'HIGH'
210 low = 'LOW'
211 not_defined = 'NOT_DEFINED'
214class PrivilegesRequiredType(Enum):
215 high = 'HIGH'
216 low = 'LOW'
217 none = 'NONE'
220class ModifiedPrivilegesRequiredType(Enum):
221 high = 'HIGH'
222 low = 'LOW'
223 none = 'NONE'
224 not_defined = 'NOT_DEFINED'
227class UserInteractionType(Enum):
228 none = 'NONE'
229 required = 'REQUIRED'
232class ModifiedUserInteractionType(Enum):
233 none = 'NONE'
234 required = 'REQUIRED'
235 not_defined = 'NOT_DEFINED'
238class ScopeType(Enum):
239 unchanged = 'UNCHANGED'
240 changed = 'CHANGED'
243class ModifiedScopeType(Enum):
244 unchanged = 'UNCHANGED'
245 changed = 'CHANGED'
246 not_defined = 'NOT_DEFINED'
249class CiaTypeModel(Enum):
250 none = 'NONE'
251 low = 'LOW'
252 high = 'HIGH'
255class ModifiedCiaType(Enum):
256 none = 'NONE'
257 low = 'LOW'
258 high = 'HIGH'
259 not_defined = 'NOT_DEFINED'
262class ConfidenceType(Enum):
263 unknown = 'UNKNOWN'
264 reasonable = 'REASONABLE'
265 confirmed = 'CONFIRMED'
266 not_defined = 'NOT_DEFINED'
269class ScoreTypeModel(ScoreType):
270 pass
273class SeverityType(Enum):
274 none = 'NONE'
275 low = 'LOW'
276 medium = 'MEDIUM'
277 high = 'HIGH'
278 critical = 'CRITICAL'
281class ScoreTypeModel1(ScoreType):
282 pass
285class Remediation(BaseModel):
286 """
287 Specifies details on how to handle (and presumably, fix) a vulnerability.
288 """
290 category: Annotated[
291 RemediationCategory,
292 Field(
293 description='Specifies the category which this remediation belongs to.',
294 title='Category of the remediation',
295 ),
296 ]
297 date: Annotated[
298 Optional[datetime],
299 Field(
300 description='Contains the date from which the remediation is available.',
301 title='Date of the remediation',
302 ),
303 ] = None
304 details: Annotated[
305 str,
306 Field(
307 description='Contains a thorough human-readable discussion of the remediation.',
308 min_length=1,
309 title='Details of the remediation',
310 ),
311 ]
312 entitlements: Annotated[
313 Optional[List[Entitlement]],
314 Field(
315 description='Contains a list of entitlements.',
316 min_length=1,
317 title='List of entitlements',
318 ),
319 ] = None
320 group_ids: Optional[ProductGroupIds] = None
321 product_ids: Optional[Products] = None
322 restart_required: Annotated[
323 Optional[RestartRequired],
324 Field(
325 description='Provides information on category of restart is required by this remediation to'
326 ' become effective.',
327 title='Restart required by remediation',
328 ),
329 ] = None
330 url: Annotated[
331 Optional[AnyUrl],
332 Field(
333 description='Contains the URL where to obtain the remediation.',
334 title='URL to the remediation',
335 ),
336 ] = None
338 @classmethod
339 @no_type_check
340 @field_validator('entitlements')
341 def check_len(cls, v):
342 if not v:
343 raise ValueError('optional element present but empty')
344 return v
347class Threat(BaseModel):
348 """
349 Contains the vulnerability kinetic information. This information can change as the vulnerability ages and new
350 information becomes available.
351 """
353 category: Annotated[
354 ThreatCategory,
355 Field(
356 description='Categorizes the threat according to the rules of the specification.',
357 title='Category of the threat',
358 ),
359 ]
360 date: Annotated[
361 Optional[datetime],
362 Field(
363 description='Contains the date when the assessment was done or the threat appeared.',
364 title='Date of the threat',
365 ),
366 ] = None
367 details: Annotated[
368 str,
369 Field(
370 description='Represents a thorough human-readable discussion of the threat.',
371 min_length=1,
372 title='Details of the threat',
373 ),
374 ]
375 group_ids: Optional[ProductGroupIds] = None
376 product_ids: Optional[Products] = None
379class Score(BaseModel):
380 """
381 Specifies information about (at least one) score of the vulnerability and for
382 which products the given value applies.
383 """
385 cvss_v2: Optional[CVSS2] = None
386 cvss_v3: Optional[Union[CVSS30, CVSS31]] = None
387 products: Products
389 @no_type_check
390 def model_dump_json(self, *args, **kwargs):
391 kwargs.setdefault('by_alias', True)
392 return super().model_dump_json(*args, **kwargs)
395class Vulnerability(BaseModel):
396 """
397 Is a container for the aggregation of all fields that are related to a single vulnerability in the document.
398 """
400 acknowledgments: Annotated[
401 Optional[Acknowledgments],
402 Field(
403 description='Contains a list of acknowledgment elements associated with this vulnerability item.',
404 title='Vulnerability acknowledgments',
405 ),
406 ] = None
407 cve: Annotated[
408 Optional[str],
409 Field(
410 description='Holds the MITRE standard Common Vulnerabilities and Exposures (CVE) tracking number for'
411 ' the vulnerability.',
412 pattern='^CVE-[0-9]{4}-[0-9]{4,}$',
413 title='CVE',
414 ),
415 ] = None
416 cwe: Annotated[
417 Optional[Cwe],
418 Field(
419 description='Holds the MITRE standard Common Weakness Enumeration (CWE) for the weakness associated.',
420 title='CWE',
421 ),
422 ] = None
423 discovery_date: Annotated[
424 Optional[datetime],
425 Field(
426 description='Holds the date and time the vulnerability was originally discovered.',
427 title='Discovery date',
428 ),
429 ] = None
430 flags: Annotated[
431 Optional[Flags],
432 Field(
433 description=(
434 'Contains product specific information in regard to this vulnerability as'
435 ' a single machine readable flag.'
436 ),
437 title='List of flags',
438 ),
439 ] = None
440 ids: Annotated[
441 Optional[Ids],
442 Field(
443 description=(
444 'Represents a list of unique labels or tracking IDs for the vulnerability'
445 ' (if such information exists).'
446 ),
447 title='List of IDs',
448 ),
449 ] = None
450 involvements: Annotated[
451 Optional[List[Involvement]],
452 Field(
453 description='Contains a list of involvements.',
454 min_length=1,
455 title='List of involvements',
456 ),
457 ] = None
458 notes: Annotated[
459 Optional[Notes],
460 Field(
461 description='Holds notes associated with this vulnerability item.',
462 title='Vulnerability notes',
463 ),
464 ] = None
465 product_status: Annotated[
466 Optional[ProductStatus],
467 Field(
468 description='Contains different lists of product_ids which provide details on the status of the'
469 ' referenced product related to the current vulnerability. ',
470 title='Product status',
471 ),
472 ] = None
473 references: Annotated[
474 Optional[References],
475 Field(
476 description='Holds a list of references associated with this vulnerability item.',
477 title='Vulnerability references',
478 ),
479 ] = None
480 release_date: Annotated[
481 Optional[datetime],
482 Field(
483 description='Holds the date and time the vulnerability was originally released into the wild.',
484 title='Release date',
485 ),
486 ] = None
487 remediations: Annotated[
488 Optional[List[Remediation]],
489 Field(
490 description='Contains a list of remediations.',
491 min_length=1,
492 title='List of remediations',
493 ),
494 ] = None
495 scores: Annotated[
496 Optional[List[Score]],
497 Field(
498 description='Contains score objects for the current vulnerability.',
499 min_length=1,
500 title='List of scores',
501 ),
502 ] = None
503 threats: Annotated[
504 Optional[List[Threat]],
505 Field(
506 description='Contains information about a vulnerability that can change with time.',
507 min_length=1,
508 title='List of threats',
509 ),
510 ] = None
511 title: Annotated[
512 Optional[str],
513 Field(
514 description='Gives the document producer the ability to apply a canonical name or title to'
515 ' the vulnerability.',
516 min_length=1,
517 title='Title',
518 ),
519 ] = None
521 @no_type_check
522 def model_dump_json(self, *args, **kwargs):
523 kwargs.setdefault('by_alias', True)
524 return super().model_dump_json(*args, **kwargs)
526 @classmethod
527 @no_type_check
528 @field_validator('involvements', 'remediations', 'scores', 'threats')
529 def check_len(cls, v):
530 if not v:
531 raise ValueError('optional element present but empty')
532 return v