Coverage for csaf/document.py: 94.85%

87 statements  

« prev     ^ index     » next       coverage.py v7.4.1, created at 2024-02-04 16:28:45 +00:00

1"""CSAF Document meta information model.""" 

2 

3from __future__ import annotations 

4 

5from datetime import datetime 

6from enum import Enum 

7from typing import Annotated, List, Optional, no_type_check 

8 

9from pydantic import AnyUrl, BaseModel, Field, RootModel, field_validator 

10 

11from csaf.definitions import Acknowledgments, Lang, Notes, References, Version 

12 

13 

14class Revision(BaseModel): 

15 """ 

16 Contains all the information elements required to track the evolution of a CSAF document. 

17 """ 

18 

19 date: Annotated[ 

20 datetime, 

21 Field(description='The date of the revision entry', title='Date of the revision'), 

22 ] 

23 number: Version 

24 summary: Annotated[ 

25 str, 

26 Field( 

27 description='Holds a single non-empty string representing a short description of the changes.', 

28 examples=['Initial version.'], 

29 min_length=1, 

30 title='Summary of the revision', 

31 ), 

32 ] 

33 

34 

35class Tracking(BaseModel): 

36 """ 

37 Is a container designated to hold all management attributes necessary to track a CSAF document as a whole. 

38 """ 

39 

40 aliases: Annotated[ 

41 Optional[List[Alias]], 

42 Field( 

43 description='Contains a list of alternate names for the same document.', 

44 title='Aliases', 

45 ), 

46 ] = None 

47 current_release_date: Annotated[ 

48 datetime, 

49 Field( 

50 description='The date when the current revision of this document was released', 

51 title='Current release date', 

52 ), 

53 ] 

54 generator: Annotated[ 

55 Optional[Generator], 

56 Field( 

57 description='Is a container to hold all elements related to the generation of the document.' 

58 ' These items will reference when the document was actually created,' 

59 ' including the date it was generated and the entity that generated it.', 

60 title='Document generator', 

61 ), 

62 ] = None 

63 id: Annotated[ 

64 str, 

65 Field( 

66 description='The ID is a simple label that provides for a wide range of numbering values, types,' 

67 ' and schemes. Its value SHOULD be assigned and maintained by the original document' 

68 ' issuing authority.', 

69 examples=[ 

70 'Example Company - 2019-YH3234', 

71 'RHBA-2019:0024', 

72 'cisco-sa-20190513-secureboot', 

73 ], 

74 min_length=1, 

75 title='Unique identifier for the document', 

76 ), 

77 ] 

78 initial_release_date: Annotated[ 

79 datetime, 

80 Field( 

81 description='The date when this document was first published.', 

82 title='Initial release date', 

83 ), 

84 ] 

85 revision_history: Annotated[ 

86 List[Revision], 

87 Field( 

88 description='Holds one revision item for each version of the CSAF document, including the initial one.', 

89 min_length=1, 

90 title='Revision history', 

91 ), 

92 ] 

93 status: Annotated[ 

94 DocumentStatus, 

95 Field( 

96 description='Defines the draft status of the document.', 

97 title='Document status', 

98 ), 

99 ] 

100 version: Version 

101 

102 @classmethod 

103 @no_type_check 

104 @field_validator('aliases', 'revision_history') 

105 def check_len(cls, v): 

106 if not v: 

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

108 return v 

109 

110 

111class AggregateSeverity(BaseModel): 

112 """ 

113 Is a vehicle that is provided by the document producer to convey the urgency and criticality with which the one or 

114 more vulnerabilities reported should be addressed. It is a document-level metric and applied to the document as a 

115 whole — not any specific vulnerability. The range of values in this field is defined according to the document 

116 producer's policies and procedures. 

117 """ 

118 

119 namespace: Annotated[ 

120 Optional[AnyUrl], 

121 Field( 

122 description='Points to the namespace so referenced.', 

123 title='Namespace of aggregate severity', 

124 ), 

125 ] = None 

126 text: Annotated[ 

127 str, 

128 Field( 

129 description='Provides a severity which is independent of - and in addition to - any other standard metric' 

130 ' for determining the impact or severity of a given vulnerability (such as CVSS).', 

131 examples=['Critical', 'Important', 'Moderate'], 

132 min_length=1, 

133 title='Text of aggregate severity', 

134 ), 

135 ] 

136 

137 

138class CsafVersion(Enum): 

139 """ 

140 Gives the version of the CSAF specification which the document was generated for. 

141 """ 

142 

143 version_2_0 = '2.0' 

144 

145 

146class Label(Enum): 

147 """ 

148 Provides the TLP label of the document. 

149 """ 

150 

151 AMBER = 'AMBER' 

152 GREEN = 'GREEN' 

153 RED = 'RED' 

154 WHITE = 'WHITE' 

155 

156 

157class TrafficLightProtocol(BaseModel): 

158 """ 

159 Provides details about the TLP classification of the document. 

160 """ 

161 

162 label: Annotated[ 

163 Label, 

164 Field(description='Provides the TLP label of the document.', title='Label of TLP'), 

165 ] 

166 url: Annotated[ 

167 Optional[AnyUrl], 

168 Field( 

169 description='Provides a URL where to find the textual description of the TLP version which is used in this' 

170 ' document. Default is the URL to the definition by FIRST.', 

171 examples=[ 

172 'https://www.us-cert.gov/tlp', 

173 'https://www.bsi.bund.de/SharedDocs/Downloads/DE/BSI/Kritis/Merkblatt_TLP.pdf', 

174 ], 

175 title='URL of TLP version', 

176 ), 

177 ] = AnyUrl(url='https://www.first.org/tlp/') 

178 

179 

180class Distribution(BaseModel): 

181 """ 

182 Describe any constraints on how this document might be shared. 

183 """ 

184 

185 text: Annotated[ 

186 Optional[str], 

187 Field( 

188 description='Provides a textual description of additional constraints.', 

189 examples=[ 

190 'Copyright 2021, Example Company, All Rights Reserved.', 

191 'Distribute freely.', 

192 'Share only on a need-to-know-basis only.', 

193 ], 

194 min_length=1, 

195 title='Textual description', 

196 ), 

197 ] = None 

198 tlp: Annotated[ 

199 Optional[TrafficLightProtocol], 

200 Field( 

201 description='Provides details about the TLP classification of the document.', 

202 title='Traffic Light Protocol (TLP)', 

203 ), 

204 ] = None 

205 

206 

207class PublisherCategory(Enum): 

208 """ 

209 Provides information about the category of publisher releasing the document. 

210 """ 

211 

212 coordinator = 'coordinator' 

213 discoverer = 'discoverer' 

214 other = 'other' 

215 translator = 'translator' 

216 user = 'user' 

217 vendor = 'vendor' 

218 

219 

220class Publisher(BaseModel): 

221 """ 

222 Provides information about the publisher of the document. 

223 """ 

224 

225 category: Annotated[ 

226 PublisherCategory, 

227 Field( 

228 description='Provides information about the category of publisher releasing the document.', 

229 title='Category of publisher', 

230 ), 

231 ] 

232 contact_details: Annotated[ 

233 Optional[str], 

234 Field( 

235 description='Information on how to contact the publisher, possibly including details such as web sites,' 

236 ' email addresses, phone numbers, and postal mail addresses.', 

237 examples=[ 

238 'Example Company can be reached at [email protected], or via our website' 

239 ' at https://www.example.com/contact.' 

240 ], 

241 min_length=1, 

242 title='Contact details', 

243 ), 

244 ] = None 

245 issuing_authority: Annotated[ 

246 Optional[str], 

247 Field( 

248 description='Provides information about the authority of the issuing party to release the document,' 

249 " in particular, the party's constituency and responsibilities or other obligations.", 

250 min_length=1, 

251 title='Issuing authority', 

252 ), 

253 ] = None 

254 name: Annotated[ 

255 str, 

256 Field( 

257 description='Contains the name of the issuing party.', 

258 examples=['BSI', 'Cisco PSIRT', 'Siemens ProductCERT'], 

259 min_length=1, 

260 title='Name of publisher', 

261 ), 

262 ] 

263 namespace: Annotated[ 

264 AnyUrl, 

265 Field( 

266 description='Contains a URL which is under control of the issuing party and can be used as a globally' 

267 ' unique identifier for that issuing party.', 

268 examples=['https://csaf.io', 'https://www.example.com'], 

269 title='Namespace of publisher', 

270 ), 

271 ] 

272 

273 

274class Alias( 

275 RootModel[ 

276 Annotated[ 

277 str, 

278 Field( 

279 description='Specifies a non-empty string that represents a distinct optional alternative ID used to' 

280 ' refer to the document.', 

281 examples=['CVE-2019-12345'], 

282 min_length=1, 

283 title='Alternate name', 

284 ), 

285 ] 

286 ] 

287): 

288 pass 

289 

290 

291class Engine(BaseModel): 

292 """ 

293 Contains information about the engine that generated the CSAF document. 

294 """ 

295 

296 name: Annotated[ 

297 str, 

298 Field( 

299 description='Represents the name of the engine that generated the CSAF document.', 

300 examples=['Red Hat rhsa-to-cvrf', 'Secvisogram', 'TVCE'], 

301 min_length=1, 

302 title='Engine name', 

303 ), 

304 ] 

305 version: Annotated[ 

306 Optional[str], 

307 Field( 

308 description='Contains the version of the engine that generated the CSAF document.', 

309 examples=['0.6.0', '1.0.0-beta+exp.sha.a1c44f85', '2'], 

310 min_length=1, 

311 title='Engine version', 

312 ), 

313 ] = None 

314 

315 

316class Generator(BaseModel): 

317 """ 

318 Is a container to hold all elements related to the generation of the document. These items will reference when 

319 the document was actually created, including the date it was generated and the entity that generated it. 

320 """ 

321 

322 date: Annotated[ 

323 Optional[datetime], 

324 Field( 

325 description='This SHOULD be the current date that the document was generated. Because documents are' 

326 ' often generated internally by a document producer and exist for a nonzero amount of time' 

327 ' before being released, this field MAY be different from the Initial Release Date and' 

328 ' Current Release Date.', 

329 title='Date of document generation', 

330 ), 

331 ] = None 

332 engine: Annotated[ 

333 Engine, 

334 Field( 

335 description='Contains information about the engine that generated the CSAF document.', 

336 title='Engine of document generation', 

337 ), 

338 ] 

339 

340 

341class DocumentStatus(Enum): 

342 """ 

343 Defines the draft status of the document. 

344 """ 

345 

346 draft = 'draft' 

347 final = 'final' 

348 interim = 'interim' 

349 

350 

351class RelationshipCategory(Enum): 

352 """ 

353 Defines the category of relationship for the referenced component. 

354 """ 

355 

356 default_component_of = 'default_component_of' 

357 external_component_of = 'external_component_of' 

358 installed_on = 'installed_on' 

359 installed_with = 'installed_with' 

360 optional_component_of = 'optional_component_of' 

361 

362 

363class Document(BaseModel): 

364 """ 

365 Captures the meta-data about this document describing a particular set of security advisories. 

366 """ 

367 

368 acknowledgments: Annotated[ 

369 Optional[Acknowledgments], 

370 Field( 

371 description='Contains a list of acknowledgment elements associated with the whole document.', 

372 title='Document acknowledgments', 

373 ), 

374 ] = None 

375 aggregate_severity: Annotated[ 

376 Optional[AggregateSeverity], 

377 Field( 

378 description='Is a vehicle that is provided by the document producer to convey the urgency and' 

379 ' criticality with which the one or more vulnerabilities reported should be addressed.' 

380 ' It is a document-level metric and applied to the document as a whole' 

381 ' — not any specific vulnerability. The range of values in this field is defined according' 

382 " to the document producer's policies and procedures.", 

383 title='Aggregate severity', 

384 ), 

385 ] = None 

386 category: Annotated[ 

387 str, 

388 Field( 

389 description='Defines a short canonical name, chosen by the document producer, which will inform the end' 

390 ' user as to the category of document.', 

391 examples=[ 

392 'Example Company Security Notice', 

393 'generic_csaf', 

394 'security_advisory', 

395 'vex', 

396 ], 

397 min_length=1, 

398 title='Document category', 

399 ), 

400 ] 

401 csaf_version: Annotated[ 

402 CsafVersion, 

403 Field( 

404 description='Gives the version of the CSAF specification which the document was generated for.', 

405 title='CSAF version', 

406 ), 

407 ] 

408 distribution: Annotated[ 

409 Optional[Distribution], 

410 Field( 

411 description='Describe any constraints on how this document might be shared.', 

412 title='Rules for sharing document', 

413 ), 

414 ] = None 

415 lang: Annotated[ 

416 Optional[Lang], 

417 Field( 

418 description='Identifies the language used by this document, corresponding to IETF BCP 47 / RFC 5646.', 

419 title='Document language', 

420 ), 

421 ] = None 

422 notes: Annotated[ 

423 Optional[Notes], 

424 Field( 

425 description='Holds notes associated with the whole document.', 

426 title='Document notes', 

427 ), 

428 ] = None 

429 publisher: Annotated[ 

430 Publisher, 

431 Field( 

432 description='Provides information about the publisher of the document.', 

433 title='Publisher', 

434 ), 

435 ] 

436 references: Annotated[ 

437 Optional[References], 

438 Field( 

439 description='Holds a list of references associated with the whole document.', 

440 title='Document references', 

441 ), 

442 ] = None 

443 source_lang: Annotated[ 

444 Optional[Lang], 

445 Field( 

446 description='If this copy of the document is a translation then the value of this property describes' 

447 ' from which language this document was translated.', 

448 title='Source language', 

449 ), 

450 ] = None 

451 title: Annotated[ 

452 str, 

453 Field( 

454 description='This SHOULD be a canonical name for the document, and sufficiently unique to distinguish' 

455 ' it from similar documents.', 

456 examples=[ 

457 'Cisco IPv6 Crafted Packet Denial of Service Vulnerability', 

458 'Example Company Cross-Site-Scripting Vulnerability in Example Generator', 

459 ], 

460 min_length=1, 

461 title='Title of this document', 

462 ), 

463 ] 

464 tracking: Annotated[ 

465 Tracking, 

466 Field( 

467 description='Is a container designated to hold all management attributes necessary to track a' 

468 ' CSAF document as a whole.', 

469 title='Tracking', 

470 ), 

471 ] 

472 

473 

474Tracking.model_rebuild()