Coverage for turvallisuusneuvonta/csaf/definitions.py: 73.68%

119 statements  

« prev     ^ index     » next       coverage.py v7.6.9, created at 2024-12-18 20:29:38 +00:00

1"""CSAF Document general definitions.""" 

2 

3from __future__ import annotations 

4 

5from collections.abc import Sequence 

6from datetime import datetime 

7from enum import Enum 

8from typing import Annotated, List, Optional, no_type_check 

9 

10from pydantic import AnyUrl, BaseModel, Field, RootModel, field_validator, model_validator 

11 

12 

13class FlagCategory(Enum): 

14 """ 

15 Defines the category of the machine readable label for flags. 

16 """ 

17 

18 component_not_present = 'component_not_present' 

19 inline_mitigations_already_exist = 'inline_mitigations_already_exist' 

20 vulnerable_code_cannot_be_controlled_by_adversary = 'vulnerable_code_cannot_be_controlled_by_adversary' 

21 vulnerable_code_not_in_execute_path = 'vulnerable_code_not_in_execute_path' 

22 vulnerable_code_not_present = 'vulnerable_code_not_present' 

23 

24 

25class Flag(BaseModel): 

26 """Contains product specific information in regard to this vulnerability as a single machine readable flag.""" 

27 

28 date: Annotated[ 

29 Optional[datetime], 

30 Field( 

31 description='Contains the date when assessment was done or the flag was assigned.', 

32 title='Date of the flag', 

33 ), 

34 ] = None 

35 

36 group_ids: Annotated[ 

37 Optional[ProductGroupIds], 

38 Field( 

39 description='Specifies a list of product_group_ids to give context to the parent item.', 

40 title='List of product_group_ids', 

41 ), 

42 ] = None 

43 

44 label: Annotated[ 

45 FlagCategory, 

46 Field( 

47 description='Specifies the machine readable label.', 

48 title='Label of the flag', 

49 ), 

50 ] 

51 product_ids: Annotated[ 

52 Optional[ListOfProductIds], 

53 Field( 

54 description='Specifies a list of product_ids to give context to the parent item.', 

55 title='List of product_ids', 

56 ), 

57 ] = None 

58 

59 

60class Flags( 

61 RootModel[ 

62 Annotated[ 

63 List[Flag], 

64 Field( 

65 description='Contains a list of machine readable flags.', 

66 min_length=1, 

67 # unique_items=True, 

68 title='List of flags', 

69 ), 

70 ] 

71 ] 

72): 

73 """Represents a list of unique labels or tracking IDs for the vulnerability (if such information exists).""" 

74 

75 @classmethod 

76 @no_type_check 

77 @model_validator(mode='before') 

78 def check_len(cls, v): 

79 if not v: 

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

81 return v 

82 

83 

84class Id(BaseModel): 

85 """Contains a single unique label or tracking ID for the vulnerability.""" 

86 

87 system_name: Annotated[ 

88 str, 

89 Field( 

90 description='Indicates the name of the vulnerability tracking or numbering system.', 

91 examples=['Cisco Bug ID', 'GitHub Issue'], 

92 min_length=1, 

93 title='System name', 

94 ), 

95 ] 

96 text: Annotated[ 

97 str, 

98 Field( 

99 description='Is unique label or tracking ID for the vulnerability (if such information exists).', 

100 examples=['CSCso66472', 'oasis-tcs/csaf#210'], 

101 min_length=1, 

102 title='Text', 

103 ), 

104 ] 

105 

106 

107class Ids( 

108 RootModel[ 

109 Annotated[ 

110 List[Id], 

111 Field( 

112 description=( 

113 'Represents a list of unique labels or tracking IDs for the vulnerability' 

114 ' (if such information exists).' 

115 ), 

116 min_length=1, 

117 title='List of IDs', 

118 ), 

119 ] 

120 ] 

121): 

122 """Represents a list of unique labels or tracking IDs for the vulnerability (if such information exists).""" 

123 

124 @classmethod 

125 @no_type_check 

126 @model_validator(mode='before') 

127 def check_len(cls, v): 

128 if not v: 

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

130 return v 

131 

132 

133class Name( 

134 RootModel[ 

135 Annotated[ 

136 str, 

137 Field( 

138 description='Contains the name of a single person.', 

139 examples=['Albert Einstein', 'Johann Sebastian Bach'], 

140 min_length=1, 

141 title='Name of entity being recognized', 

142 ), 

143 ] 

144 ] 

145): 

146 pass 

147 

148 

149class Acknowledgment(BaseModel): 

150 """ 

151 Acknowledges contributions by describing those that contributed. 

152 """ 

153 

154 names: Annotated[ 

155 Optional[List[Name]], 

156 Field( 

157 description='Contains the names of entities being recognized.', 

158 min_length=1, 

159 title='List of acknowledged names', 

160 ), 

161 ] = None 

162 organization: Annotated[ 

163 Optional[str], 

164 Field( 

165 description='Contains the name of a contributing organization being recognized.', 

166 examples=['CISA', 'Google Project Zero', 'Talos'], 

167 min_length=1, 

168 title='Contributing organization', 

169 ), 

170 ] = None 

171 summary: Annotated[ 

172 Optional[str], 

173 Field( 

174 description='SHOULD represent any contextual details the document producers wish to make known about the' 

175 ' acknowledgment or acknowledged parties.', 

176 examples=['First analysis of Coordinated Multi-Stream Attack (CMSA)'], 

177 min_length=1, 

178 title='Summary of the acknowledgment', 

179 ), 

180 ] = None 

181 urls: Annotated[ 

182 Optional[List[AnyUrl]], 

183 Field( 

184 description='Specifies a list of URLs or location of the reference to be acknowledged.', 

185 min_length=1, 

186 title='List of URLs', 

187 ), 

188 ] 

189 

190 @classmethod 

191 @no_type_check 

192 @field_validator('names', 'organization', 'summary', 'urls') 

193 def check_len(cls, v): 

194 if not v: 

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

196 return v 

197 

198 

199class Acknowledgments( 

200 RootModel[ 

201 Annotated[ 

202 List[Acknowledgment], 

203 Field( 

204 description='Contains a list of acknowledgment elements.', 

205 min_length=1, 

206 title='List of acknowledgments', 

207 ), 

208 ] 

209 ] 

210): 

211 """Contains a list of acknowledgment elements.""" 

212 

213 @classmethod 

214 @no_type_check 

215 @model_validator(mode='before') 

216 def check_len(cls, v): 

217 if not v: 

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

219 return v 

220 

221 

222class ReferenceTokenForProductGroupInstance( 

223 RootModel[ 

224 Annotated[ 

225 str, 

226 Field( 

227 description=( 

228 'Token required to identify a group of products so that it can be referred to from' 

229 ' other parts in the document.' 

230 ' There is no predefined or required format for the product_group_id' 

231 ' as long as it uniquely identifies a group in the context of the current document.' 

232 ), 

233 examples=['CSAFGID-0001', 'CSAFGID-0002', 'CSAFGID-0020'], 

234 min_length=1, 

235 title='Reference token for product group instance', 

236 ), 

237 ] 

238 ] 

239): 

240 pass 

241 

242 

243class ProductGroupId( 

244 RootModel[ 

245 Annotated[ 

246 str, 

247 Field( 

248 description='Token required to identify a group of products so that it can be referred to from other' 

249 ' parts in the document. There is no predefined or required format for the product_group_id' 

250 ' as long as it uniquely identifies a group in the context of the current document.', 

251 examples=['CSAFGID-0001', 'CSAFGID-0002', 'CSAFGID-0020'], 

252 min_length=1, 

253 title='Reference token for product group instance', 

254 ), 

255 ] 

256 ] 

257): 

258 pass 

259 

260 

261class ProductGroupIds( 

262 RootModel[ 

263 Annotated[ 

264 List[ProductGroupId], 

265 Field( 

266 description='Specifies a list of product_group_ids to give context to the parent item.', 

267 min_length=1, 

268 title='List of product_group_ids', 

269 ), 

270 ] 

271 ] 

272): 

273 """Specifies a list of product_group_ids to give context to the parent item.""" 

274 

275 @classmethod 

276 @no_type_check 

277 @model_validator(mode='before') 

278 def check_len(cls, v): 

279 if not v: 

280 raise ValueError('mandatory element present but empty') 

281 return v 

282 

283 

284class ProductId( 

285 RootModel[ 

286 Annotated[ 

287 str, 

288 Field( 

289 description='Token required to identify a full_product_name so that it can be referred to from' 

290 ' other parts in the document.' 

291 ' There is no predefined or required format for the product_id as long as it' 

292 ' uniquely identifies a product in the context of the current document.', 

293 examples=['CSAFPID-0004', 'CSAFPID-0008'], 

294 min_length=1, 

295 title='Reference token for product instance', 

296 ), 

297 ] 

298 ] 

299): 

300 pass 

301 

302 

303class Products( 

304 RootModel[ 

305 Annotated[ 

306 List[ProductId], 

307 Field( 

308 description='Specifies a list of product_ids to give context to the parent item.', 

309 min_length=1, 

310 title='List of product_ids', 

311 ), 

312 ] 

313 ] 

314): 

315 """Specifies a list of product_ids to give context to the parent item.""" 

316 

317 pass 

318 

319 

320class ReferenceTokenForProductInstance( 

321 RootModel[ 

322 Annotated[ 

323 str, 

324 Field( 

325 description=( 

326 'Token required to identify a full_product_name so that it can be referred to from other' 

327 ' parts in the document.' 

328 ' There is no predefined or required format for the product_id as long as it uniquely' 

329 ' identifies a product in the context of the current document.' 

330 ), 

331 examples=['CSAFPID-0004', 'CSAFPID-0008'], 

332 min_length=1, 

333 title='Reference token for product instance', 

334 ), 

335 ] 

336 ] 

337): 

338 pass 

339 

340 

341class ListOfProductIds(BaseModel): 

342 """ 

343 Specifies a list of product_ids to give context to the parent item. 

344 """ 

345 

346 product_ids: Annotated[ 

347 Sequence[ReferenceTokenForProductInstance], 

348 Field( 

349 description='Specifies a list of product_ids to give context to the parent item.', 

350 # min_length=1, 

351 title='List of product_ids', 

352 ), 

353 ] 

354 

355 @classmethod 

356 @no_type_check 

357 @field_validator('product_ids') 

358 def check_len(cls, v): 

359 if not v: 

360 raise ValueError('mandatory element present but empty') 

361 return v 

362 

363 

364class Lang( 

365 RootModel[ 

366 Annotated[ 

367 str, 

368 Field( 

369 description='Identifies a language, corresponding to IETF BCP 47 / RFC 5646. See IETF language' 

370 ' registry: https://www.iana.org/assignments/language-subtag-registry/language-subtag-registry', 

371 examples=['de', 'en', 'fr', 'frc', 'jp'], 

372 pattern='^' 

373 '(([A-Za-z]{2,3}(-[A-Za-z]{3}(-[A-Za-z]{3}){0,2})?|[A-Za-z]{4,8})(-[A-Za-z]{4})?(-([A-Za-z]{2}|' 

374 '[0-9]{3}))?(-([A-Za-z0-9]{5,8}|[0-9][A-Za-z0-9]{3}))*(-[A-WY-Za-wy-z0-9](-[A-Za-z0-9]{2,8})+)*' 

375 '(-[Xx](-[A-Za-z0-9]{1,8})+)?|[Xx](-[A-Za-z0-9]{1,8})+|[Ii]-[Dd][Ee][Ff][Aa][Uu][Ll][Tt]|' 

376 '[Ii]-[Mm][Ii][Nn][Gg][Oo])$', 

377 title='Language type', 

378 ), 

379 ] 

380 ] 

381): 

382 pass 

383 

384 

385class NoteCategory(Enum): 

386 """ 

387 Choice of what kind of note this is. 

388 """ 

389 

390 description = 'description' 

391 details = 'details' 

392 faq = 'faq' 

393 general = 'general' 

394 legal_disclaimer = 'legal_disclaimer' 

395 other = 'other' 

396 summary = 'summary' 

397 

398 

399class Note(BaseModel): 

400 """ 

401 Is a place to put all manner of text blobs related to the current context. 

402 """ 

403 

404 audience: Annotated[ 

405 Optional[str], 

406 Field( 

407 description='Indicate who is intended to read it.', 

408 examples=[ 

409 'all', 

410 'executives', 

411 'operational management and system administrators', 

412 'safety engineers', 

413 ], 

414 min_length=1, 

415 title='Audience of note', 

416 ), 

417 ] = None 

418 category: Annotated[ 

419 NoteCategory, 

420 Field(description='Choice of what kind of note this is.', title='Note category'), 

421 ] 

422 text: Annotated[ 

423 str, 

424 Field( 

425 description='The contents of the note. Content varies depending on type.', 

426 min_length=1, 

427 title='Note contents', 

428 ), 

429 ] 

430 title: Annotated[ 

431 Optional[str], 

432 Field( 

433 description='Provides a concise description of what is contained in the text of the note.', 

434 examples=[ 

435 'Details', 

436 'Executive summary', 

437 'Technical summary', 

438 'Impact on safety systems', 

439 ], 

440 min_length=1, 

441 title='Title of note', 

442 ), 

443 ] = None 

444 

445 

446class Notes( 

447 RootModel[ 

448 Annotated[ 

449 List[Note], 

450 Field( 

451 description='Contains notes which are specific to the current context.', 

452 min_length=1, 

453 title='List of notes', 

454 ), 

455 ] 

456 ] 

457): 

458 """Contains notes which are specific to the current context.""" 

459 

460 @classmethod 

461 @no_type_check 

462 @model_validator(mode='before') 

463 def check_len(cls, v): 

464 if not v: 

465 raise ValueError('mandatory element present but empty') 

466 return v 

467 

468 

469class ReferenceCategory(Enum): 

470 """ 

471 Indicates whether the reference points to the same document or vulnerability in focus (depending on scope) or 

472 to an external resource. 

473 """ 

474 

475 external = 'external' 

476 self = 'self' 

477 

478 

479class Reference(BaseModel): 

480 """ 

481 Holds any reference to conferences, papers, advisories, and other resources that are related and considered 

482 related to either a surrounding part of or the entire document and to be of value to the document consumer. 

483 """ 

484 

485 category: Annotated[ 

486 Optional[ReferenceCategory], 

487 Field( 

488 description='Indicates whether the reference points to the same document or vulnerability in focus' 

489 ' (depending on scope) or to an external resource.', 

490 title='Category of reference', 

491 ), 

492 ] = ReferenceCategory.external 

493 summary: Annotated[ 

494 str, 

495 Field( 

496 description='Indicates what this reference refers to.', 

497 min_length=1, 

498 title='Summary of the reference', 

499 ), 

500 ] 

501 url: Annotated[ 

502 AnyUrl, 

503 Field(description='Provides the URL for the reference.', title='URL of reference'), 

504 ] 

505 

506 

507class References( 

508 RootModel[ 

509 Annotated[ 

510 List[Reference], 

511 Field( 

512 description='Holds a list of references.', 

513 min_length=1, 

514 title='List of references', 

515 ), 

516 ] 

517 ] 

518): 

519 """Holds a list of references.""" 

520 

521 pass 

522 

523 

524class Version( 

525 RootModel[ 

526 Annotated[ 

527 str, 

528 Field( 

529 description=( 

530 'Specifies a version string to denote clearly the evolution of the content of the document.' 

531 ' Format must be either integer or semantic versioning.' 

532 ), 

533 examples=['1', '4', '0.9.0', '1.4.3', '2.40.0+21AF26D3'], 

534 pattern=( 

535 '^(0|[1-9][0-9]*)$|^((0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)' 

536 '(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)' 

537 '(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?' 

538 '(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?)$' 

539 ), 

540 title='Version', 

541 ), 

542 ] 

543 ] 

544): 

545 pass