Coverage for turvallisuusneuvonta/csaf/definitions.py: 82.27%
91 statements
« prev ^ index » next coverage.py v7.4.1, created at 2024-02-05 19:27:17 +00:00
« prev ^ index » next coverage.py v7.4.1, created at 2024-02-05 19:27:17 +00:00
1"""CSAF Document general definitions."""
3from __future__ import annotations
5from collections.abc import Sequence
6from enum import Enum
7from typing import Annotated, List, Optional, no_type_check
9from pydantic import AnyUrl, BaseModel, Field, RootModel, field_validator, model_validator
12class Id(BaseModel):
13 """
14 Gives the document producer a place to publish a unique label or tracking ID for the vulnerability
15 (if such information exists).
16 """
18 system_name: Annotated[
19 str,
20 Field(
21 description='Indicates the name of the vulnerability tracking or numbering system.',
22 examples=['Cisco Bug ID', 'GitHub Issue'],
23 min_length=1,
24 title='System name',
25 ),
26 ]
27 text: Annotated[
28 str,
29 Field(
30 description='Is unique label or tracking ID for the vulnerability (if such information exists).',
31 examples=['CSCso66472', 'oasis-tcs/csaf#210'],
32 min_length=1,
33 title='Text',
34 ),
35 ]
38class Name(
39 RootModel[
40 Annotated[
41 str,
42 Field(
43 description='Contains the name of a single person.',
44 examples=['Albert Einstein', 'Johann Sebastian Bach'],
45 min_length=1,
46 title='Name of entity being recognized',
47 ),
48 ]
49 ]
50):
51 pass
54class Acknowledgment(BaseModel):
55 """
56 Acknowledges contributions by describing those that contributed.
57 """
59 names: Annotated[
60 Optional[List[Name]],
61 Field(
62 description='Contains the names of entities being recognized.',
63 min_length=1,
64 title='List of acknowledged names',
65 ),
66 ] = None
67 organization: Annotated[
68 Optional[str],
69 Field(
70 description='Contains the name of a contributing organization being recognized.',
71 examples=['CISA', 'Google Project Zero', 'Talos'],
72 min_length=1,
73 title='Contributing organization',
74 ),
75 ] = None
76 summary: Annotated[
77 Optional[str],
78 Field(
79 description='SHOULD represent any contextual details the document producers wish to make known about the'
80 ' acknowledgment or acknowledged parties.',
81 examples=['First analysis of Coordinated Multi-Stream Attack (CMSA)'],
82 min_length=1,
83 title='Summary of the acknowledgment',
84 ),
85 ] = None
86 urls: Annotated[
87 Optional[List[AnyUrl]],
88 Field(
89 description='Specifies a list of URLs or location of the reference to be acknowledged.',
90 min_length=1,
91 title='List of URLs',
92 ),
93 ]
95 @classmethod
96 @no_type_check
97 @field_validator('names', 'organization', 'summary', 'urls')
98 def check_len(cls, v):
99 if not v:
100 raise ValueError('optional element present but empty')
101 return v
104class Acknowledgments(
105 RootModel[
106 Annotated[
107 List[Acknowledgment],
108 Field(
109 description='Contains a list of acknowledgment elements.',
110 min_length=1,
111 title='List of acknowledgments',
112 ),
113 ]
114 ]
115):
116 """Contains a list of acknowledgment elements."""
118 @classmethod
119 @no_type_check
120 @model_validator(mode='before')
121 def check_len(cls, v):
122 if not v:
123 raise ValueError('optional element present but empty')
124 return v
127class ReferenceTokenForProductGroupInstance(
128 RootModel[
129 Annotated[
130 str,
131 Field(
132 description=(
133 'Token required to identify a group of products so that it can be referred to from'
134 ' other parts in the document.'
135 ' There is no predefined or required format for the product_group_id'
136 ' as long as it uniquely identifies a group in the context of the current document.'
137 ),
138 examples=['CSAFGID-0001', 'CSAFGID-0002', 'CSAFGID-0020'],
139 min_length=1,
140 title='Reference token for product group instance',
141 ),
142 ]
143 ]
144):
145 pass
148class ProductGroupId(
149 RootModel[
150 Annotated[
151 str,
152 Field(
153 description='Token required to identify a group of products so that it can be referred to from other'
154 ' parts in the document. There is no predefined or required format for the product_group_id'
155 ' as long as it uniquely identifies a group in the context of the current document.',
156 examples=['CSAFGID-0001', 'CSAFGID-0002', 'CSAFGID-0020'],
157 min_length=1,
158 title='Reference token for product group instance',
159 ),
160 ]
161 ]
162):
163 pass
166class ProductGroupIds(
167 RootModel[
168 Annotated[
169 List[ProductGroupId],
170 Field(
171 description='Specifies a list of product_group_ids to give context to the parent item.',
172 min_length=1,
173 title='List of product_group_ids',
174 ),
175 ]
176 ]
177):
178 """Specifies a list of product_group_ids to give context to the parent item."""
180 @classmethod
181 @no_type_check
182 @model_validator(mode='before')
183 def check_len(cls, v):
184 if not v:
185 raise ValueError('mandatory element present but empty')
186 return v
189class ProductId(
190 RootModel[
191 Annotated[
192 str,
193 Field(
194 description='Token required to identify a full_product_name so that it can be referred to from'
195 ' other parts in the document.'
196 ' There is no predefined or required format for the product_id as long as it'
197 ' uniquely identifies a product in the context of the current document.',
198 examples=['CSAFPID-0004', 'CSAFPID-0008'],
199 min_length=1,
200 title='Reference token for product instance',
201 ),
202 ]
203 ]
204):
205 pass
208class Products(
209 RootModel[
210 Annotated[
211 List[ProductId],
212 Field(
213 description='Specifies a list of product_ids to give context to the parent item.',
214 min_length=1,
215 title='List of product_ids',
216 ),
217 ]
218 ]
219):
220 """Specifies a list of product_ids to give context to the parent item."""
222 pass
225class ReferenceTokenForProductInstance(
226 RootModel[
227 Annotated[
228 str,
229 Field(
230 description=(
231 'Token required to identify a full_product_name so that it can be referred to from other'
232 ' parts in the document.'
233 ' There is no predefined or required format for the product_id as long as it uniquely'
234 ' identifies a product in the context of the current document.'
235 ),
236 examples=['CSAFPID-0004', 'CSAFPID-0008'],
237 min_length=1,
238 title='Reference token for product instance',
239 ),
240 ]
241 ]
242):
243 pass
246class ListOfProductIds(BaseModel):
247 """
248 Specifies a list of product_ids to give context to the parent item.
249 """
251 product_ids: Annotated[
252 Sequence[ReferenceTokenForProductInstance],
253 Field(
254 description='Specifies a list of product_ids to give context to the parent item.',
255 # min_length=1,
256 title='List of product_ids',
257 ),
258 ]
260 @classmethod
261 @no_type_check
262 @field_validator('product_ids')
263 def check_len(cls, v):
264 if not v:
265 raise ValueError('mandatory element present but empty')
266 return v
269class Lang(
270 RootModel[
271 Annotated[
272 str,
273 Field(
274 description='Identifies a language, corresponding to IETF BCP 47 / RFC 5646. See IETF language'
275 ' registry: https://www.iana.org/assignments/language-subtag-registry/language-subtag-registry',
276 examples=['de', 'en', 'fr', 'frc', 'jp'],
277 pattern='^'
278 '(([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}|'
279 '[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})+)*'
280 '(-[Xx](-[A-Za-z0-9]{1,8})+)?|[Xx](-[A-Za-z0-9]{1,8})+|[Ii]-[Dd][Ee][Ff][Aa][Uu][Ll][Tt]|'
281 '[Ii]-[Mm][Ii][Nn][Gg][Oo])$',
282 title='Language type',
283 ),
284 ]
285 ]
286):
287 pass
290class NoteCategory(Enum):
291 """
292 Choice of what kind of note this is.
293 """
295 description = 'description'
296 details = 'details'
297 faq = 'faq'
298 general = 'general'
299 legal_disclaimer = 'legal_disclaimer'
300 other = 'other'
301 summary = 'summary'
304class Note(BaseModel):
305 """
306 Is a place to put all manner of text blobs related to the current context.
307 """
309 audience: Annotated[
310 Optional[str],
311 Field(
312 description='Indicate who is intended to read it.',
313 examples=[
314 'all',
315 'executives',
316 'operational management and system administrators',
317 'safety engineers',
318 ],
319 min_length=1,
320 title='Audience of note',
321 ),
322 ] = None
323 category: Annotated[
324 NoteCategory,
325 Field(description='Choice of what kind of note this is.', title='Note category'),
326 ]
327 text: Annotated[
328 str,
329 Field(
330 description='The contents of the note. Content varies depending on type.',
331 min_length=1,
332 title='Note contents',
333 ),
334 ]
335 title: Annotated[
336 Optional[str],
337 Field(
338 description='Provides a concise description of what is contained in the text of the note.',
339 examples=[
340 'Details',
341 'Executive summary',
342 'Technical summary',
343 'Impact on safety systems',
344 ],
345 min_length=1,
346 title='Title of note',
347 ),
348 ] = None
351class Notes(
352 RootModel[
353 Annotated[
354 List[Note],
355 Field(
356 description='Contains notes which are specific to the current context.',
357 min_length=1,
358 title='List of notes',
359 ),
360 ]
361 ]
362):
363 """Contains notes which are specific to the current context."""
365 @classmethod
366 @no_type_check
367 @model_validator(mode='before')
368 def check_len(cls, v):
369 if not v:
370 raise ValueError('mandatory element present but empty')
371 return v
374class ReferenceCategory(Enum):
375 """
376 Indicates whether the reference points to the same document or vulnerability in focus (depending on scope) or
377 to an external resource.
378 """
380 external = 'external'
381 self = 'self'
384class Reference(BaseModel):
385 """
386 Holds any reference to conferences, papers, advisories, and other resources that are related and considered
387 related to either a surrounding part of or the entire document and to be of value to the document consumer.
388 """
390 category: Annotated[
391 Optional[ReferenceCategory],
392 Field(
393 description='Indicates whether the reference points to the same document or vulnerability in focus'
394 ' (depending on scope) or to an external resource.',
395 title='Category of reference',
396 ),
397 ] = ReferenceCategory.external
398 summary: Annotated[
399 str,
400 Field(
401 description='Indicates what this reference refers to.',
402 min_length=1,
403 title='Summary of the reference',
404 ),
405 ]
406 url: Annotated[
407 AnyUrl,
408 Field(description='Provides the URL for the reference.', title='URL of reference'),
409 ]
412class References(
413 RootModel[
414 Annotated[
415 List[Reference],
416 Field(
417 description='Holds a list of references.',
418 min_length=1,
419 title='List of references',
420 ),
421 ]
422 ]
423):
424 """Holds a list of references."""
426 pass
429class Version(
430 RootModel[
431 Annotated[
432 str,
433 Field(
434 description=(
435 'Specifies a version string to denote clearly the evolution of the content of the document.'
436 ' Format must be either integer or semantic versioning.'
437 ),
438 examples=['1', '4', '0.9.0', '1.4.3', '2.40.0+21AF26D3'],
439 pattern=(
440 '^(0|[1-9][0-9]*)$|^((0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)'
441 '(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)'
442 '(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?'
443 '(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?)$'
444 ),
445 title='Version',
446 ),
447 ]
448 ]
449):
450 pass