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

94 statements  

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

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

2from __future__ import annotations 

3 

4from collections.abc import Sequence 

5from enum import Enum 

6from typing import Annotated, List, Optional, no_type_check 

7 

8from pydantic import AnyUrl, BaseModel, Field, validator 

9 

10 

11class Id(BaseModel): 

12 """ 

13 Gives the document producer a place to publish a unique label or tracking ID for the vulnerability 

14 (if such information exists). 

15 """ 

16 

17 system_name: Annotated[ 

18 str, 

19 Field( 

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

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

22 min_length=1, 

23 title='System name', 

24 ), 

25 ] 

26 text: Annotated[ 

27 str, 

28 Field( 

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

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

31 min_length=1, 

32 title='Text', 

33 ), 

34 ] 

35 

36 

37class Name(BaseModel): 

38 __root__: Annotated[ 

39 str, 

40 Field( 

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

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

43 min_length=1, 

44 title='Name of entity being recognized', 

45 ), 

46 ] 

47 

48 

49class Acknowledgment(BaseModel): 

50 """ 

51 Acknowledges contributions by describing those that contributed. 

52 """ 

53 

54 names: Annotated[ 

55 Optional[List[Name]], 

56 Field( 

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

58 min_items=1, 

59 title='List of acknowledged names', 

60 ), 

61 ] 

62 organization: Annotated[ 

63 Optional[str], 

64 Field( 

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

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

67 min_length=1, 

68 title='Contributing organization', 

69 ), 

70 ] 

71 summary: Annotated[ 

72 Optional[str], 

73 Field( 

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

75 ' acknowledgment or acknowledged parties.', 

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

77 min_length=1, 

78 title='Summary of the acknowledgment', 

79 ), 

80 ] 

81 urls: Annotated[ 

82 Optional[List[AnyUrl]], 

83 Field( 

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

85 min_items=1, 

86 title='List of URLs', 

87 ), 

88 ] 

89 

90 @no_type_check 

91 @validator('names', 'organization', 'summary', 'urls') 

92 @classmethod 

93 def check_len(cls, v): 

94 if not v: 

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

96 return v 

97 

98 

99class Acknowledgments(BaseModel): 

100 """ 

101 Contains a list of acknowledgment elements. 

102 """ 

103 

104 __root__: Annotated[ 

105 List[Acknowledgment], 

106 Field( 

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

108 min_items=1, 

109 title='List of acknowledgments', 

110 ), 

111 ] 

112 

113 @no_type_check 

114 @validator('__root__') 

115 @classmethod 

116 def check_len(cls, v): 

117 if not v: 

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

119 return v 

120 

121 

122class ReferenceTokenForProductGroupInstance(BaseModel): 

123 __root__: Annotated[ 

124 str, 

125 Field( 

126 description=( 

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

128 ' other parts in the document.' 

129 ' There is no predefined or required format for the product_group_id as long as it uniquely identifies' 

130 ' a group in the context of the current document.' 

131 ), 

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

133 min_length=1, 

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

135 ), 

136 ] 

137 

138 

139class ProductGroupId(BaseModel): 

140 __root__: Annotated[ 

141 str, 

142 Field( 

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

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

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

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

147 min_length=1, 

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

149 ), 

150 ] 

151 

152 

153class ProductGroupIds(BaseModel): 

154 """ 

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

156 """ 

157 

158 __root__: Annotated[ 

159 List[ProductGroupId], 

160 Field( 

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

162 min_items=1, 

163 title='List of product_group_ids', 

164 ), 

165 ] 

166 

167 @no_type_check 

168 @validator('__root__') 

169 @classmethod 

170 def check_len(cls, v): 

171 if not v: 

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

173 return v 

174 

175 

176class ProductId(BaseModel): 

177 __root__: Annotated[ 

178 str, 

179 Field( 

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

181 ' in the document. There is no predefined or required format for the product_id as long as it' 

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

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

184 min_length=1, 

185 title='Reference token for product instance', 

186 ), 

187 ] 

188 

189 

190class Products(BaseModel): 

191 """ 

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

193 """ 

194 

195 __root__: Annotated[ 

196 List[ProductId], 

197 Field( 

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

199 min_items=1, 

200 title='List of product_ids', 

201 ), 

202 ] 

203 

204 

205class ReferenceTokenForProductInstance(BaseModel): 

206 value: Annotated[ 

207 str, 

208 Field( 

209 description=( 

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

211 ' parts in the document.' 

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

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

214 ), 

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

216 min_length=1, 

217 title='Reference token for product instance', 

218 ), 

219 ] 

220 

221 

222class ListOfProductIds(BaseModel): 

223 """ 

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

225 """ 

226 

227 product_ids: Annotated[ 

228 Sequence[ReferenceTokenForProductInstance], 

229 Field( 

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

231 # min_items=1, 

232 title='List of product_ids', 

233 ), 

234 ] 

235 

236 @no_type_check 

237 @validator('product_ids') 

238 @classmethod 

239 def check_len(cls, v): 

240 if not v: 

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

242 return v 

243 

244 

245class Lang(BaseModel): 

246 __root__: Annotated[ 

247 str, 

248 Field( 

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

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

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

252 regex='^(([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}|' 

253 '[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})+)*' 

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

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

256 title='Language type', 

257 ), 

258 ] 

259 

260 

261class NoteCategory(Enum): 

262 """ 

263 Choice of what kind of note this is. 

264 """ 

265 

266 description = 'description' 

267 details = 'details' 

268 faq = 'faq' 

269 general = 'general' 

270 legal_disclaimer = 'legal_disclaimer' 

271 other = 'other' 

272 summary = 'summary' 

273 

274 

275class Note(BaseModel): 

276 """ 

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

278 """ 

279 

280 audience: Annotated[ 

281 Optional[str], 

282 Field( 

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

284 examples=[ 

285 'all', 

286 'executives', 

287 'operational management and system administrators', 

288 'safety engineers', 

289 ], 

290 min_length=1, 

291 title='Audience of note', 

292 ), 

293 ] 

294 category: Annotated[ 

295 NoteCategory, 

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

297 ] 

298 text: Annotated[ 

299 str, 

300 Field( 

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

302 min_length=1, 

303 title='Note contents', 

304 ), 

305 ] 

306 title: Annotated[ 

307 Optional[str], 

308 Field( 

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

310 examples=[ 

311 'Details', 

312 'Executive summary', 

313 'Technical summary', 

314 'Impact on safety systems', 

315 ], 

316 min_length=1, 

317 title='Title of note', 

318 ), 

319 ] 

320 

321 

322class Notes(BaseModel): 

323 """ 

324 Contains notes which are specific to the current context. 

325 """ 

326 

327 __root__: Annotated[ 

328 List[Note], 

329 Field( 

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

331 min_items=1, 

332 title='List of notes', 

333 ), 

334 ] 

335 

336 @no_type_check 

337 @validator('__root__') 

338 @classmethod 

339 def check_len(cls, v): 

340 if not v: 

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

342 return v 

343 

344 

345class ReferenceCategory(Enum): 

346 """ 

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

348 to an external resource. 

349 """ 

350 

351 external = 'external' 

352 self = 'self' 

353 

354 

355class Reference(BaseModel): 

356 """ 

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

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

359 """ 

360 

361 category: Annotated[ 

362 Optional[ReferenceCategory], 

363 Field( 

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

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

366 title='Category of reference', 

367 ), 

368 ] = ReferenceCategory.external 

369 summary: Annotated[ 

370 str, 

371 Field( 

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

373 min_length=1, 

374 title='Summary of the reference', 

375 ), 

376 ] 

377 url: Annotated[ 

378 AnyUrl, 

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

380 ] 

381 

382 

383class References(BaseModel): 

384 """ 

385 Holds a list of references. 

386 """ 

387 

388 __root__: Annotated[ 

389 List[Reference], 

390 Field( 

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

392 min_items=1, 

393 title='List of references', 

394 ), 

395 ] 

396 

397 

398class Version(BaseModel): 

399 __root__: Annotated[ 

400 str, 

401 Field( 

402 description=( 

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

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

405 ), 

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

407 regex=( 

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

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

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

411 ), 

412 title='Version', 

413 ), 

414 ]