Coverage for turvallisuusneuvonta/csaf/vulnerability.py: 95.54%

220 statements  

« prev     ^ index     » next       coverage.py v7.0.3, created at 2023-01-07 19:14 +0100

1"""CSAF Vulnerability model.""" 

2from __future__ import annotations 

3 

4from datetime import datetime 

5from enum import Enum 

6from typing import Annotated, List, Optional, Union, no_type_check 

7 

8from pydantic import AnyUrl, BaseModel, Field, validator 

9 

10from turvallisuusneuvonta.csaf.cvss.cvss import CVSS2, CVSS30, CVSS31 

11from turvallisuusneuvonta.csaf.definitions import Acknowledgments, Id, Notes, ProductGroupIds, Products, References 

12from turvallisuusneuvonta.csaf.product import ProductStatus 

13 

14 

15class Cwe(BaseModel): 

16 """ 

17 Holds the MITRE standard Common Weakness Enumeration (CWE) for the weakness associated. 

18 """ 

19 

20 id: Annotated[ 

21 str, 

22 Field( 

23 description='Holds the ID for the weakness associated.', 

24 examples=['CWE-22', 'CWE-352', 'CWE-79'], 

25 regex='^CWE-[1-9]\\d{0,5}$', 

26 title='Weakness ID', 

27 ), 

28 ] 

29 name: Annotated[ 

30 str, 

31 Field( 

32 description='Holds the full name of the weakness as given in the CWE specification.', 

33 examples=[ 

34 'Cross-Site Request Forgery (CSRF)', 

35 "Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')", 

36 "Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')", 

37 ], 

38 min_length=1, 

39 title='Weakness name', 

40 ), 

41 ] 

42 

43 

44class PartyCategory(Enum): 

45 """ 

46 Defines the category of the involved party. 

47 """ 

48 

49 coordinator = 'coordinator' 

50 discoverer = 'discoverer' 

51 other = 'other' 

52 user = 'user' 

53 vendor = 'vendor' 

54 

55 

56class PartyStatus(Enum): 

57 """ 

58 Defines contact status of the involved party. 

59 """ 

60 

61 completed = 'completed' 

62 contact_attempted = 'contact_attempted' 

63 disputed = 'disputed' 

64 in_progress = 'in_progress' 

65 not_contacted = 'not_contacted' 

66 open = 'open' 

67 

68 

69class Involvement(BaseModel): 

70 """ 

71 Is a container, that allows the document producers to comment on the level of involvement (or engagement) 

72 of themselves or third parties in the vulnerability identification, scoping, and remediation process. 

73 """ 

74 

75 date: Annotated[ 

76 Optional[datetime], 

77 Field( 

78 description='Holds the date and time of the involvement entry.', 

79 title='Date of involvement', 

80 ), 

81 ] = None 

82 party: Annotated[ 

83 PartyCategory, 

84 Field( 

85 description='Defines the category of the involved party.', 

86 title='Party category', 

87 ), 

88 ] 

89 status: Annotated[ 

90 PartyStatus, 

91 Field( 

92 description='Defines contact status of the involved party.', 

93 title='Party status', 

94 ), 

95 ] 

96 summary: Annotated[ 

97 Optional[str], 

98 Field( 

99 description='Contains additional context regarding what is going on.', 

100 min_length=1, 

101 title='Summary of the involvement', 

102 ), 

103 ] = None 

104 

105 

106class RemediationCategory(Enum): 

107 """ 

108 Specifies the category which this remediation belongs to. 

109 """ 

110 

111 mitigation = 'mitigation' 

112 no_fix_planned = 'no_fix_planned' 

113 none_available = 'none_available' 

114 vendor_fix = 'vendor_fix' 

115 workaround = 'workaround' 

116 

117 

118class Entitlement(BaseModel): 

119 __root__: Annotated[ 

120 str, 

121 Field( 

122 description='Contains any possible vendor-defined constraints for obtaining fixed software or hardware' 

123 'that fully resolves the vulnerability.', 

124 min_length=1, 

125 title='Entitlement of the remediation', 

126 ), 

127 ] 

128 

129 

130class RestartRequiredCategory(Enum): 

131 """ 

132 Specifies what category of restart is required by this remediation to become effective. 

133 """ 

134 

135 connected = 'connected' 

136 dependencies = 'dependencies' 

137 machine = 'machine' 

138 none = 'none' 

139 parent = 'parent' 

140 service = 'service' 

141 system = 'system' 

142 vulnerable_component = 'vulnerable_component' 

143 zone = 'zone' 

144 

145 

146class RestartRequired(BaseModel): 

147 """ 

148 Provides information on category of restart is required by this remediation to become effective. 

149 """ 

150 

151 category: Annotated[ 

152 RestartRequiredCategory, 

153 Field( 

154 description='Specifies what category of restart is required by this remediation to become effective.', 

155 title='Category of restart', 

156 ), 

157 ] 

158 details: Annotated[ 

159 Optional[str], 

160 Field( 

161 description='Provides additional information for the restart. This can include details on procedures,' 

162 'scope or impact.', 

163 min_length=1, 

164 title='Additional restart information', 

165 ), 

166 ] 

167 

168 

169class ThreatCategory(Enum): 

170 """ 

171 Categorizes the threat according to the rules of the specification. 

172 """ 

173 

174 exploit_status = 'exploit_status' 

175 impact = 'impact' 

176 target_set = 'target_set' 

177 

178 

179class AccessVectorType(Enum): 

180 network = 'NETWORK' 

181 adjacent_network = 'ADJACENT_NETWORK' 

182 local = 'LOCAL' 

183 

184 

185class AccessComplexityType(Enum): 

186 high = 'HIGH' 

187 medium = 'MEDIUM' 

188 low = 'LOW' 

189 

190 

191class AuthenticationType(Enum): 

192 multiple = 'MULTIPLE' 

193 single = 'SINGLE' 

194 none = 'NONE' 

195 

196 

197class CiaType(Enum): 

198 none = 'NONE' 

199 partial = 'PARTIAL' 

200 complete = 'COMPLETE' 

201 

202 

203class ExploitabilityType(Enum): 

204 unproven = 'UNPROVEN' 

205 proof_of_concept = 'PROOF_OF_CONCEPT' 

206 functional = 'FUNCTIONAL' 

207 high = 'HIGH' 

208 not_defined = 'NOT_DEFINED' 

209 

210 

211class RemediationLevelType(Enum): 

212 official_fix = 'OFFICIAL_FIX' 

213 temporary_fix = 'TEMPORARY_FIX' 

214 workaround = 'WORKAROUND' 

215 unavailable = 'UNAVAILABLE' 

216 not_defined = 'NOT_DEFINED' 

217 

218 

219class ReportConfidenceType(Enum): 

220 unconfirmed = 'UNCONFIRMED' 

221 uncorroborated = 'UNCORROBORATED' 

222 confirmed = 'CONFIRMED' 

223 not_defined = 'NOT_DEFINED' 

224 

225 

226class CollateralDamagePotentialType(Enum): 

227 none = 'NONE' 

228 low = 'LOW' 

229 low_medium = 'LOW_MEDIUM' 

230 medium_high = 'MEDIUM_HIGH' 

231 high = 'HIGH' 

232 not_defined = 'NOT_DEFINED' 

233 

234 

235class TargetDistributionType(Enum): 

236 none = 'NONE' 

237 low = 'LOW' 

238 medium = 'MEDIUM' 

239 high = 'HIGH' 

240 not_defined = 'NOT_DEFINED' 

241 

242 

243class CiaRequirementType(Enum): 

244 low = 'LOW' 

245 medium = 'MEDIUM' 

246 high = 'HIGH' 

247 not_defined = 'NOT_DEFINED' 

248 

249 

250class ScoreType(BaseModel): 

251 value: Annotated[float, Field(ge=0.0, le=10.0)] 

252 

253 

254class AttackVectorType(Enum): 

255 network = 'NETWORK' 

256 adjacent_network = 'ADJACENT_NETWORK' 

257 local = 'LOCAL' 

258 physical = 'PHYSICAL' 

259 

260 

261class ModifiedAttackVectorType(Enum): 

262 network = 'NETWORK' 

263 adjacent_network = 'ADJACENT_NETWORK' 

264 local = 'LOCAL' 

265 physical = 'PHYSICAL' 

266 not_defined = 'NOT_DEFINED' 

267 

268 

269class AttackComplexityType(Enum): 

270 high = 'HIGH' 

271 low = 'LOW' 

272 

273 

274class ModifiedAttackComplexityType(Enum): 

275 high = 'HIGH' 

276 low = 'LOW' 

277 not_defined = 'NOT_DEFINED' 

278 

279 

280class PrivilegesRequiredType(Enum): 

281 high = 'HIGH' 

282 low = 'LOW' 

283 none = 'NONE' 

284 

285 

286class ModifiedPrivilegesRequiredType(Enum): 

287 high = 'HIGH' 

288 low = 'LOW' 

289 none = 'NONE' 

290 not_defined = 'NOT_DEFINED' 

291 

292 

293class UserInteractionType(Enum): 

294 none = 'NONE' 

295 required = 'REQUIRED' 

296 

297 

298class ModifiedUserInteractionType(Enum): 

299 none = 'NONE' 

300 required = 'REQUIRED' 

301 not_defined = 'NOT_DEFINED' 

302 

303 

304class ScopeType(Enum): 

305 unchanged = 'UNCHANGED' 

306 changed = 'CHANGED' 

307 

308 

309class ModifiedScopeType(Enum): 

310 unchanged = 'UNCHANGED' 

311 changed = 'CHANGED' 

312 not_defined = 'NOT_DEFINED' 

313 

314 

315class CiaTypeModel(Enum): 

316 none = 'NONE' 

317 low = 'LOW' 

318 high = 'HIGH' 

319 

320 

321class ModifiedCiaType(Enum): 

322 none = 'NONE' 

323 low = 'LOW' 

324 high = 'HIGH' 

325 not_defined = 'NOT_DEFINED' 

326 

327 

328class ConfidenceType(Enum): 

329 unknown = 'UNKNOWN' 

330 reasonable = 'REASONABLE' 

331 confirmed = 'CONFIRMED' 

332 not_defined = 'NOT_DEFINED' 

333 

334 

335class ScoreTypeModel(ScoreType): 

336 pass 

337 

338 

339class SeverityType(Enum): 

340 none = 'NONE' 

341 low = 'LOW' 

342 medium = 'MEDIUM' 

343 high = 'HIGH' 

344 critical = 'CRITICAL' 

345 

346 

347class ScoreTypeModel1(ScoreType): 

348 pass 

349 

350 

351class Remediation(BaseModel): 

352 """ 

353 Specifies details on how to handle (and presumably, fix) a vulnerability. 

354 """ 

355 

356 category: Annotated[ 

357 RemediationCategory, 

358 Field( 

359 description='Specifies the category which this remediation belongs to.', 

360 title='Category of the remediation', 

361 ), 

362 ] 

363 date: Annotated[ 

364 Optional[datetime], 

365 Field( 

366 description='Contains the date from which the remediation is available.', 

367 title='Date of the remediation', 

368 ), 

369 ] 

370 details: Annotated[ 

371 str, 

372 Field( 

373 description='Contains a thorough human-readable discussion of the remediation.', 

374 min_length=1, 

375 title='Details of the remediation', 

376 ), 

377 ] 

378 entitlements: Annotated[ 

379 Optional[List[Entitlement]], 

380 Field( 

381 description='Contains a list of entitlements.', 

382 min_items=1, 

383 title='List of entitlements', 

384 ), 

385 ] 

386 group_ids: Optional[ProductGroupIds] 

387 product_ids: Optional[Products] 

388 restart_required: Annotated[ 

389 Optional[RestartRequired], 

390 Field( 

391 description='Provides information on category of restart is required by this remediation to' 

392 ' become effective.', 

393 title='Restart required by remediation', 

394 ), 

395 ] 

396 url: Annotated[ 

397 Optional[AnyUrl], 

398 Field( 

399 description='Contains the URL where to obtain the remediation.', 

400 title='URL to the remediation', 

401 ), 

402 ] 

403 

404 @no_type_check 

405 @validator('entitlements') 

406 @classmethod 

407 def check_len(cls, v): 

408 if not v: 

409 raise ValueError('optional element present but empty') 

410 return v 

411 

412 

413class Threat(BaseModel): 

414 """ 

415 Contains the vulnerability kinetic information. This information can change as the vulnerability ages and new 

416 information becomes available. 

417 """ 

418 

419 category: Annotated[ 

420 ThreatCategory, 

421 Field( 

422 description='Categorizes the threat according to the rules of the specification.', 

423 title='Category of the threat', 

424 ), 

425 ] 

426 date: Annotated[ 

427 Optional[datetime], 

428 Field( 

429 description='Contains the date when the assessment was done or the threat appeared.', 

430 title='Date of the threat', 

431 ), 

432 ] 

433 details: Annotated[ 

434 str, 

435 Field( 

436 description='Represents a thorough human-readable discussion of the threat.', 

437 min_length=1, 

438 title='Details of the threat', 

439 ), 

440 ] 

441 group_ids: Optional[ProductGroupIds] 

442 product_ids: Optional[Products] 

443 

444 

445class Score(BaseModel): 

446 """ 

447 specifies information about (at least one) score of the vulnerability and for 

448 which products the given value applies. 

449 """ 

450 

451 cvss_v2: Optional[CVSS2] = None 

452 cvss_v3: Optional[Union[CVSS30, CVSS31]] = None 

453 products: Products 

454 

455 

456class Vulnerability(BaseModel): 

457 """ 

458 Is a container for the aggregation of all fields that are related to a single vulnerability in the document. 

459 """ 

460 

461 acknowledgments: Annotated[ 

462 Optional[Acknowledgments], 

463 Field( 

464 description='Contains a list of acknowledgment elements associated with this vulnerability item.', 

465 title='Vulnerability acknowledgments', 

466 ), 

467 ] 

468 cve: Annotated[ 

469 Optional[str], 

470 Field( 

471 description='Holds the MITRE standard Common Vulnerabilities and Exposures (CVE) tracking number for' 

472 ' the vulnerability.', 

473 regex='^CVE-[0-9]{4}-[0-9]{4,}$', 

474 title='CVE', 

475 ), 

476 ] 

477 cwe: Annotated[ 

478 Optional[Cwe], 

479 Field( 

480 description='Holds the MITRE standard Common Weakness Enumeration (CWE) for the weakness associated.', 

481 title='CWE', 

482 ), 

483 ] 

484 discovery_date: Annotated[ 

485 Optional[datetime], 

486 Field( 

487 description='Holds the date and time the vulnerability was originally discovered.', 

488 title='Discovery date', 

489 ), 

490 ] 

491 id: Annotated[ 

492 Optional[Id], 

493 Field( 

494 description='Gives the document producer a place to publish a unique label or tracking ID for the' 

495 ' vulnerability (if such information exists).', 

496 title='ID', 

497 ), 

498 ] 

499 involvements: Annotated[ 

500 Optional[List[Involvement]], 

501 Field( 

502 description='Contains a list of involvements.', 

503 min_items=1, 

504 title='List of involvements', 

505 ), 

506 ] 

507 notes: Annotated[ 

508 Optional[Notes], 

509 Field( 

510 description='Holds notes associated with this vulnerability item.', 

511 title='Vulnerability notes', 

512 ), 

513 ] 

514 product_status: Annotated[ 

515 Optional[ProductStatus], 

516 Field( 

517 description='Contains different lists of product_ids which provide details on the status of the' 

518 ' referenced product related to the current vulnerability. ', 

519 title='Product status', 

520 ), 

521 ] 

522 references: Annotated[ 

523 Optional[References], 

524 Field( 

525 description='Holds a list of references associated with this vulnerability item.', 

526 title='Vulnerability references', 

527 ), 

528 ] 

529 release_date: Annotated[ 

530 Optional[datetime], 

531 Field( 

532 description='Holds the date and time the vulnerability was originally released into the wild.', 

533 title='Release date', 

534 ), 

535 ] 

536 remediations: Annotated[ 

537 Optional[List[Remediation]], 

538 Field( 

539 description='Contains a list of remediations.', 

540 min_items=1, 

541 title='List of remediations', 

542 ), 

543 ] 

544 scores: Annotated[ 

545 Optional[List[Score]], 

546 Field( 

547 description='contains score objects for the current vulnerability.', 

548 min_items=1, 

549 title='List of scores', 

550 ), 

551 ] 

552 threats: Annotated[ 

553 Optional[List[Threat]], 

554 Field( 

555 description='Contains information about a vulnerability that can change with time.', 

556 min_items=1, 

557 title='List of threats', 

558 ), 

559 ] 

560 title: Annotated[ 

561 Optional[str], 

562 Field( 

563 description='Gives the document producer the ability to apply a canonical name or title to' 

564 ' the vulnerability.', 

565 min_length=1, 

566 title='Title', 

567 ), 

568 ] 

569 

570 @no_type_check 

571 @validator('involvements', 'remediations', 'scores', 'threats') 

572 @classmethod 

573 def check_len(cls, v): 

574 if not v: 

575 raise ValueError('optional element present but empty') 

576 return v