stidantic 0.1.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of stidantic might be problematic. Click here for more details.

stidantic/sdo.py ADDED
@@ -0,0 +1,952 @@
1
+ from datetime import datetime
2
+ from typing import Any, Literal, Annotated, Self
3
+ from typing_extensions import deprecated
4
+ from annotated_types import Ge, Gt, Le
5
+
6
+ from pydantic import Field
7
+ from pydantic.functional_validators import model_validator
8
+
9
+ from stidantic.types import Identifier, StixDomain, KillChainPhase, ExternalReference
10
+ from stidantic.vocab import OpinionEnum
11
+
12
+
13
+ # 4.1 Attack Pattern
14
+ class AttackPattern(StixDomain):
15
+ """
16
+ Attack Patterns are a type of TTP that describe ways that adversaries attempt to compromise targets.
17
+
18
+ Attack Patterns are used to help categorize attacks, generalize specific attacks to the patterns that they follow,
19
+ and provide detailed information about how attacks are performed. An example of an attack pattern is
20
+ "spear phishing": a common type of attack where an attacker sends a carefully crafted e-mail message
21
+ to a party with the intent of getting them to click a link or open an attachment to deliver malware.
22
+
23
+ Attack Patterns can also be more specific; spear phishing as practiced by a particular threat actor
24
+ (e.g., they might generally say that the target won a contest) can also be an Attack Pattern.
25
+ """
26
+
27
+ type: Literal["attack-pattern"] = "attack-pattern" # pyright: ignore[reportIncompatibleVariableOverride]
28
+ # The name used to identify the Attack Pattern.
29
+ name: str
30
+ # A description that provides more details and context about the Attack Pattern,
31
+ # potentially including its purpose and its key characteristics.
32
+ description: str | None = None
33
+ # Alternative names used to identify this Attack Pattern.
34
+ aliases: list[str] | None = None
35
+ # The list of kill chain phases for which this attack pattern is used.
36
+ kill_chain_phases: list[KillChainPhase] | None = None
37
+
38
+
39
+ # 4.2 Campaign
40
+ class Campaign(StixDomain):
41
+ """
42
+ A Campaign is a grouping of adversarial behaviors that describes a set of malicious activities or attacks
43
+ (sometimes called waves) that occur over a period of time against a specific set of targets.
44
+ Campaigns usually have well defined objectives and may be part of an Intrusion Set.
45
+
46
+ Campaigns are often attributed to an intrusion set and threat actors. The threat actors may reuse known
47
+ infrastructure from the intrusion set or may set up new infrastructure specific for conducting that campaign.
48
+
49
+ Campaigns can be characterized by their objectives and the incidents they cause, people or resources they target,
50
+ and the resources (infrastructure, intelligence, Malware, Tools, etc.) they use.
51
+
52
+ For example, a Campaign could be used to describe a crime syndicate's attack using a specific variant of
53
+ malware and new C2 servers against the executives of ACME Bank during the summer of 2016 in order
54
+ to gain secret information about an upcoming merger with another bank.
55
+ """
56
+
57
+ type: Literal["campaign"] = "campaign" # pyright: ignore[reportIncompatibleVariableOverride]
58
+ # A name used to identify the Campaign.
59
+ name: str
60
+ # A description that provides more details and context about the Campaign,
61
+ # potentially including its purpose and its key characteristics.
62
+ description: str | None = None
63
+ # Alternative names used to identify this Campaign.
64
+ aliases: list[str] | None = None
65
+ # The time that this Campaign was first seen.
66
+ # A summary property of data from sightings and other data that may or may not be available in STIX.
67
+ # If new sightings are received that are earlier than the first seen timestamp,
68
+ # the object may be updated to account for the new data.
69
+ first_seen: datetime | None = None
70
+ # The time that this Campaign was last seen.
71
+ # A summary property of data from sightings and other data that may or may not be available in STIX.
72
+ # If new sightings are received that are later than the last seen timestamp,
73
+ # the object may be updated to account for the new data.
74
+ last_seen: datetime | None = None
75
+ # The Campaign’s primary goal, objective, desired outcome, or intended effect
76
+ # — what the Threat Actor or Intrusion Set hopes to accomplish with this Campaign.
77
+ objective: str | None = None
78
+
79
+ @model_validator(mode="after")
80
+ def validate_last_seen_after_first_seen(self) -> Self:
81
+ """
82
+ If the last_seen property and the first_seen property are both defined, then the last_seen property
83
+ MUST be greater than or equal to the timestamp in the first_seen property.
84
+ """
85
+ if self.first_seen and self.last_seen and self.first_seen > self.last_seen:
86
+ raise ValueError(
87
+ "The last_seen property MUST be greater than or equal to the timestamp in the first_seen property"
88
+ )
89
+ return self
90
+
91
+
92
+ # 4.3 Course of Action
93
+ class CourseOfAction(StixDomain):
94
+ """
95
+ Note: The Course of Action object in STIX 2.1 is a stub. It is included to support basic use cases
96
+ (such as sharing prose courses of action) but does not support the ability to represent automated courses of
97
+ action or contain properties to represent metadata about courses of action. Future STIX 2 releases will expand it
98
+ to include these capabilities.
99
+
100
+ A Course of Action is an action taken either to prevent an attack or to respond to an attack that is in progress.
101
+ It may describe technical, automatable responses (applying patches, reconfiguring firewalls) but can also describe
102
+ higher level actions like employee training or policy changes. For example, a course of action to mitigate a
103
+ vulnerability could describe applying the patch that fixes it.
104
+
105
+ The Course of Action SDO contains a textual description of the action; a reserved action property also serves as a
106
+ placeholder for future inclusion of machine automatable courses of action.
107
+ """
108
+
109
+ type: Literal["course-of-action"] = "course-of-action" # pyright: ignore[reportIncompatibleVariableOverride]
110
+ # A name used to identify the Course of Action.
111
+ name: str
112
+ # A description that provides more details and context about the Course of Action,
113
+ # potentially including its purpose and its key characteristics.
114
+ description: str | None = None
115
+ # A reserved property that serves as a placeholder for future inclusion of machine automatable courses of action.
116
+ # action: str | None = None
117
+
118
+
119
+ # 4.4 Grouping
120
+ class Grouping(StixDomain):
121
+ """
122
+ A Grouping object explicitly asserts that the referenced STIX Objects have a shared context, unlike a STIX Bundle
123
+ (which explicitly conveys no context). A Grouping object should not be confused with an intelligence product,
124
+ which should be conveyed via a STIX Report.
125
+
126
+ A STIX Grouping object might represent a set of data that, in time, given sufficient analysis, would mature to
127
+ convey an incident or threat report as a STIX Report object. For example, a Grouping could be used to characterize
128
+ an ongoing investigation into a security event or incident. A Grouping object could also be used to assert that the
129
+ referenced STIX Objects are related to an ongoing analysis process, such as when a threat analyst is collaborating
130
+ with others in their trust community to examine a series of Campaigns and Indicators. The Grouping SDO contains a
131
+ list of references to SDOs, SCOs, SROs, and SMOs, along with an explicit statement of the context shared by the
132
+ content, a textual description, and the name of the grouping.
133
+ """
134
+
135
+ type: Literal["grouping"] = "grouping" # pyright: ignore[reportIncompatibleVariableOverride]
136
+ # A name used to identify the Grouping.
137
+ name: str | None = None
138
+ # A description that provides more details and context about the Grouping, potentially including its purpose and
139
+ # its key characteristics.
140
+ description: str | None = None
141
+ # A short descriptor of the particular context shared by the content referenced by the Grouping.
142
+ # The value for this property SHOULD come from the grouping-context-ov open vocabulary.
143
+ context: str
144
+ # Specifies the STIX Objects that are referred to by this Grouping.
145
+ object_refs: list[Identifier]
146
+
147
+
148
+ # 4.5 Identity
149
+ class Identity(StixDomain):
150
+ """
151
+ Identities can represent actual individuals, organizations, or groups (e.g., ACME, Inc.) as well as classes of
152
+ individuals, organizations, systems or groups (e.g., the finance sector).
153
+
154
+ The Identity SDO can capture basic identifying information, contact information, and the sectors that the Identity
155
+ belongs to. Identity is used in STIX to represent, among other things, targets of attacks, information sources,
156
+ object creators, and threat actor identities.
157
+ """
158
+
159
+ type: Literal["identity"] = "identity" # pyright: ignore[reportIncompatibleVariableOverride]
160
+ # A name used to identify the Identity. When referring to a specific entity (e.g., an individual or organization),
161
+ # this property SHOULD contain the canonical name of the specific entity.
162
+ name: str
163
+ # A description that provides more details and context about the Identity,
164
+ # potentially including its purpose and its key characteristics.
165
+ description: str | None = None
166
+ # The list of roles that this Identity performs (e.g., CEO, Domain Administrators, Doctors, Hospital, or Retailer).
167
+ # No open vocabulary is yet defined for this property.
168
+ roles: list[str] | None = None
169
+ # The type of entity that this Identity describes, e.g., an individual or organization.
170
+ # The value for this property SHOULD come from the identity-class-ov open vocabulary.
171
+ identity_class: str | None = None
172
+ # The list of industry sectors that this Identity belongs to.
173
+ # The value for this property SHOULD come from the industry-sector-ov open vocabulary.
174
+ sectors: list[str] | None = None
175
+ # The contact information (e-mail, phone number, etc.) for this Identity.
176
+ # No format for this information is currently defined by this specification.
177
+ contact_information: str | None = None
178
+
179
+
180
+ # 4.6 Incident
181
+ class Incident(StixDomain):
182
+ """
183
+ NOTE: The Incident object in STIX 2.1 is a stub. It is included to support basic use cases but does not contain
184
+ properties to represent metadata about incidents. Future STIX 2 releases will expand it to include these
185
+ capabilities. It is suggested that it is used as an extension point for an Incident object defined using the
186
+ extension facility described in section 7.3.
187
+ """
188
+
189
+ type: Literal["incident"] = "incident" # pyright: ignore[reportIncompatibleVariableOverride]
190
+ # A name used to identify the Incident.
191
+ name: str
192
+ # A description that provides more details and context about the Incident,
193
+ # potentially including its purpose and its key characteristics.
194
+ description: str | None = None
195
+
196
+
197
+ class Indicator(StixDomain):
198
+ """
199
+ Indicators contain a pattern that can be used to detect suspicious or malicious cyber activity. For example, an
200
+ Indicator may be used to represent a set of malicious domains and use the STIX Patterning Language
201
+ (see section 9) to specify these domains.
202
+
203
+ The Indicator SDO contains a simple textual description, the Kill Chain Phases that it detects behavior in, a time
204
+ window for when the Indicator is valid or useful, and a required pattern property to capture a structured detection
205
+ pattern. Conforming STIX implementations MUST support the STIX Patterning Language as defined in section 9.
206
+
207
+ Relationships from the Indicator can describe the malicious or suspicious behavior that it directly detects
208
+ (Malware, Tool, and Attack Pattern). In addition, it may also imply the presence of a Campaigns, Intrusion Sets,
209
+ and Threat Actors, etc.
210
+ """
211
+
212
+ type: Literal["indicator"] = "indicator" # pyright: ignore[reportIncompatibleVariableOverride]
213
+ # A name used to identify the Indicator.
214
+ # Producers SHOULD provide this property to help products and analysts understand what this Indicator actually does.
215
+ name: str | None = None
216
+ # A description that provides more details and context about the Indicator,
217
+ # potentially including its purpose and its key characteristics.
218
+ # Producers SHOULD provide this property to help products and analysts understand what this Indicator actually does.
219
+ description: str | None = None
220
+ # A set of categorizations for this indicator.
221
+ # The values for this property SHOULD come from the indicator-type-ov open vocabulary.
222
+ indicator_types: list[str] | None = None
223
+ # The detection pattern for this Indicator MAY be expressed as a STIX Pattern as specified in section 9 or another
224
+ # appropriate language such as SNORT, YARA, etc.
225
+ pattern: str
226
+ # The pattern language used in this indicator.
227
+ # The value for this property SHOULD come from the pattern-type-ov open vocabulary.
228
+ # The value of this property MUST match the type of pattern data included in the pattern property.
229
+ pattern_type: str
230
+ # The version of the pattern language that is used for the data in the pattern property which MUST match the type
231
+ # of pattern data included in the pattern property.
232
+ # For patterns that do not have a formal specification, the build or code version that the pattern is known to
233
+ # work with SHOULD be used.
234
+ # For the STIX Pattern language, the default value is determined by the specification version of the object.
235
+ # For other languages, the default value SHOULD be the latest version of the patterning language at the time of
236
+ # this object’s creation.
237
+ pattern_version: str | None = None
238
+ # The time from which this Indicator is considered a valid indicator of the behaviors it is related or represents.
239
+ valid_from: datetime
240
+ # The time at which this Indicator should no longer be considered a valid indicator of the behaviors it is
241
+ # related to or represents.
242
+ # If the valid_until property is omitted, then there is no constraint on the latest time for which the
243
+ # Indicator is valid.
244
+ valid_until: datetime | None = None
245
+ # The kill chain phase(s) to which this Indicator corresponds.
246
+ kill_chain_phases: list[KillChainPhase] | None = None
247
+
248
+ @model_validator(mode="after")
249
+ def validate_valid_until_after_valid_from(self) -> Self:
250
+ """
251
+ The valid_until property MUST be greater than the timestamp in the valid_from property.
252
+ """
253
+ if self.valid_from and self.valid_until and self.valid_from > self.valid_until:
254
+ raise ValueError(
255
+ "The valid_until property MUST be greater than the timestamp in the valid_from property"
256
+ )
257
+ return self
258
+
259
+
260
+ # 4.8 Infrastructure
261
+ class Infrastructure(StixDomain):
262
+ """
263
+ The Infrastructure SDO represents a type of TTP and describes any systems, software services and any associated
264
+ physical or virtual resources intended to support some purpose (e.g., C2 servers used as part of an attack,
265
+ device or server that are part of defense, database servers targeted by an attack, etc.). While elements of an
266
+ attack can be represented by other SDOs or SCOs, the Infrastructure SDO represents a named group of related data
267
+ that constitutes the infrastructure.
268
+ """
269
+
270
+ type: Literal["infrastructure"] = "infrastructure" # pyright: ignore[reportIncompatibleVariableOverride]
271
+ # A name or characterizing text used to identify the Infrastructure.
272
+ name: str
273
+ # A description that provides more details and context about the Infrastructure,
274
+ # potentially including its purpose, how it is being used, how it relates to other intelligence activities
275
+ # captured in related objects, and its key characteristics.
276
+ description: str | None = None
277
+ # The type of infrastructure being described.
278
+ # The values for this property SHOULD come from the infrastructure-type-ov open vocabulary.
279
+ infrastructure_types: list[str] | None = None
280
+ # Alternative names used to identify this Infrastructure.
281
+ aliases: list[str] | None = None
282
+ # The list of Kill Chain Phases for which this Infrastructure is used.
283
+ kill_chain_phases: list[KillChainPhase] | None = None
284
+ # The time that this Infrastructure was first seen performing malicious activities.
285
+ first_seen: datetime | None = None
286
+ # The time that this Infrastructure was last seen performing malicious activities.
287
+ last_seen: datetime | None = None
288
+
289
+ @model_validator(mode="after")
290
+ def validate_last_seen_after_first_seen(self) -> Self:
291
+ """
292
+ If the last_seen and the first_seen properties are both defined, then the last_seen property
293
+ MUST be greater than or equal to the timestamp in the first_seen property.
294
+ """
295
+ if self.first_seen and self.last_seen and self.first_seen > self.last_seen:
296
+ raise ValueError(
297
+ "The last_seen property MUST be greater than or equal to the timestamp in the first_seen property"
298
+ )
299
+ return self
300
+
301
+
302
+ # 4.9 Intrusion Set
303
+ class IntrusionSet(StixDomain):
304
+ """
305
+ An Intrusion Set is a grouped set of adversarial behaviors and resources with common properties that is believed to
306
+ be orchestrated by a single organization. An Intrusion Set may capture multiple Campaigns or other activities that
307
+ are all tied together by shared attributes indicating a commonly known or unknown Threat Actor. New activity can be
308
+ attributed to an Intrusion Set even if the Threat Actors behind the attack are not known. Threat Actors can move
309
+ from supporting one Intrusion Set to supporting another, or they may support multiple Intrusion Sets.
310
+
311
+ Where a Campaign is a set of attacks over a period of time against a specific set of targets to achieve some
312
+ objective, an Intrusion Set is the entire attack package and may be used over a very long period of time in
313
+ multiple Campaigns to achieve potentially multiple purposes.
314
+
315
+ While sometimes an Intrusion Set is not active, or changes focus, it is usually difficult to know if it has
316
+ truly disappeared or ended. Analysts may have varying level of fidelity on attributing an Intrusion Set back to
317
+ Threat Actors.
318
+ """
319
+
320
+ type: Literal["intrusion-set"] = "intrusion-set" # pyright: ignore[reportIncompatibleVariableOverride]
321
+ # A name or characterizing text used to identify the Intrusion Set.
322
+ name: str
323
+ # A description that provides more details and context about the Intrusion Set,
324
+ # potentially including its purpose and its key characteristics.
325
+ description: str | None = None
326
+ # Alternative names used to identify this Intrusion Set.
327
+ aliases: list[str] | None = None
328
+ # The time that this Intrusion Set was first seen.
329
+ # A summary property of data from sightings and other data that may or may not be available in STIX.
330
+ # If new sightings are received that are earlier than the first seen timestamp, the object may be updated to
331
+ # account for the new data.
332
+ first_seen: datetime | None = None
333
+ # The time that this Intrusion Set was last seen.
334
+ # This property is a summary property of data from sightings and other data that may or may not be available.
335
+ # If new sightings are received that are later than the last seen timestamp, the object may be updated to
336
+ # account for the new data.
337
+ last_seen: datetime | None = None
338
+ # The high-level goals of this Intrusion Set, namely, what are they trying to do.
339
+ # For example, they may be motivated by personal gain, but their goal is to steal credit card numbers.
340
+ # To do this, they may execute specific Campaigns that have detailed objectives like compromising point of sale
341
+ # systems at a large retailer.
342
+ # Another example: to gain information about latest merger and IPO information from ACME Bank.
343
+ goals: list[str] | None = None
344
+ # This property specifies the organizational level at which this Intrusion Set typically works, which in turn
345
+ # determines the resources available to this Intrusion Set for use in an attack.
346
+ # The value for this property SHOULD come from the attack-resource-level-ov open vocabulary.
347
+ resource_level: str | None = None
348
+ # The primary reason, motivation, or purpose behind this Intrusion Set.
349
+ # The value for this property SHOULD come from the attack-motivation-ov open vocabulary.
350
+ primary_motivation: str | None = None
351
+ # The secondary reasons, motivations, or purposes behind this Intrusion Set.
352
+ # The values for this property SHOULD come from the attack-motivation-ov open vocabulary.
353
+ secondary_motivations: list[str] | None = None
354
+
355
+ @model_validator(mode="after")
356
+ def validate_last_seen_after_first_seen(self) -> Self:
357
+ """
358
+ If the last_seen and the first_seen properties are both defined, then the last_seen property
359
+ MUST be greater than or equal to the timestamp in the first_seen property.
360
+ """
361
+ if self.first_seen and self.last_seen and self.first_seen > self.last_seen:
362
+ raise ValueError(
363
+ "The last_seen property MUST be greater than or equal to the timestamp in the first_seen property"
364
+ )
365
+ return self
366
+
367
+
368
+ # 4.10 Location
369
+ class Location(StixDomain):
370
+ """
371
+ A Location represents a geographic location. The location may be described as any, some or all of the following:
372
+ region (e.g., North America), civic address (e.g. New York, US), latitude and longitude.
373
+
374
+ Locations are primarily used to give context to other SDOs. For example, a Location could be used in a
375
+ relationship to describe that the Bourgeois Swallow intrusion set originates from Eastern Europe.
376
+
377
+ The Location SDO can be related to an Identity or Intrusion Set to indicate that the identity or intrusion set is
378
+ located in that location. It can also be related from a malware or attack pattern to indicate that they target
379
+ victims in that location. The Location object describes geographic areas, not governments, even in cases where that
380
+ area might have a government. For example, a Location representing the United States describes the United States
381
+ as a geographic area, not the federal government of the United States.
382
+ """
383
+
384
+ type: Literal["location"] = "location" # pyright: ignore[reportIncompatibleVariableOverride]
385
+ # A name used to identify the Location.
386
+ name: str | None = None
387
+ # A textual description of the Location.
388
+ description: str | None = None
389
+ # The latitude of the Location in decimal degrees.
390
+ # Positive numbers describe latitudes north of the equator,
391
+ # and negative numbers describe latitudes south of the equator
392
+ latitude: Annotated[float, Ge(-90.0), Le(90.0)] | None = None
393
+ # The longitude of the Location in decimal degrees.
394
+ # Positive numbers describe longitudes east of the prime meridian
395
+ # and negative numbers describe longitudes west of the prime meridian.
396
+ longitude: Annotated[float, Ge(-180.0), Le(180.0)] | None = None
397
+ # Defines the precision of the coordinates specified by the latitude and longitude properties.
398
+ # This is measured in meters. The actual Location may be anywhere up to precision meters from the defined point.
399
+ precision: float | None = None
400
+ # The region that this Location describes.
401
+ # The value for this property SHOULD come from the region-ov open vocabulary.
402
+ region: str | None = None
403
+ # The country that this Location describes.
404
+ # This property SHOULD contain a valid ISO 3166-1 ALPHA-2 Code [ISO3166-1].
405
+ country: str | None = None
406
+ # The state, province, or other sub-national administrative area that this Location describes.
407
+ # This property SHOULD contain a valid ISO 3166-2 Code [ISO3166-2].
408
+ administrative_area: str | None = None
409
+ # The city that this Location describes.
410
+ city: str | None = None
411
+ # The street address that this Location describes.
412
+ # This property includes all aspects or parts of the street address.
413
+ # For example, some addresses may have multiple lines including a mailstop or apartment number.
414
+ street_address: str | None = None
415
+ # The postal code for the Location.
416
+ postal_code: str | None = None
417
+
418
+ @model_validator(mode="after")
419
+ def validate_location_properties(self) -> Self:
420
+ """
421
+ At least one of the following properties/sets of properties MUST be provided:
422
+ - region
423
+ - country
424
+ - latitude and longitude
425
+ """
426
+ if (
427
+ not self.region
428
+ and not self.country
429
+ and not (self.latitude and self.longitude)
430
+ ):
431
+ raise ValueError(
432
+ "At least one of region, country, or both latitude and longitude MUST be provided"
433
+ )
434
+
435
+ if self.latitude and not self.longitude:
436
+ raise ValueError("If latitude is present, longitude MUST be present")
437
+
438
+ if self.longitude and not self.latitude:
439
+ raise ValueError("If longitude is present, latitude MUST be present")
440
+
441
+ if self.precision and (not self.latitude or not self.longitude):
442
+ raise ValueError(
443
+ "If precision is present, latitude and longitude MUST be present"
444
+ )
445
+
446
+ return self
447
+
448
+
449
+ # 4.11 Malware
450
+ class Malware(StixDomain):
451
+ """
452
+ Malware is a type of TTP that represents malicious code. It generally refers to a program that is inserted into a
453
+ system, usually covertly. The intent is to compromise the confidentiality, integrity, or availability of the
454
+ victim's data, applications, or operating system (OS) or otherwise annoy or disrupt the victim.
455
+
456
+ The Malware SDO characterizes, identifies, and categorizes malware instances and families from data that may be
457
+ derived from analysis. This SDO captures detailed information about how the malware works and what it does. This
458
+ SDO captures contextual data relevant to sharing Malware data without requiring the full analysis provided by the
459
+ Malware Analysis SDO.
460
+ """
461
+
462
+ type: Literal["malware"] = "malware" # pyright: ignore[reportIncompatibleVariableOverride]
463
+ # A name used to identify the Malware instance or family, as specified by the producer of the SDO.
464
+ # If a name for a malware instance is not available, the SHA-256 hash value or the filename MAY be used instead.
465
+ name: str | None = None
466
+ # A description that provides more details and context about the Malware instance or family,
467
+ # potentially including its purpose and its key characteristics.
468
+ description: str | None = None
469
+ # A set of categorizations for the malware being described.
470
+ # The values for this property SHOULD come from the malware-type-ov open vocabulary.
471
+ malware_types: list[str] | None = None
472
+ # Whether the object represents a malware family (if true) or a malware instance (if false).
473
+ is_family: bool | None = None
474
+ # Alternative names used to identify this malware or malware family.
475
+ aliases: list[str] | None = None
476
+ # The list of Kill Chain Phases for which this malware can be used.
477
+ kill_chain_phases: list[KillChainPhase] | None = None
478
+ # The time that the malware instance or family was first seen.
479
+ # This property is a summary property of data from sightings and other data that may or may not be available in
480
+ # STIX. If new sightings are received that are earlier than the first seen timestamp,
481
+ # the object may be updated to account for the new data.
482
+ first_seen: datetime | None = None
483
+ # The time that the malware family or malware instance was last seen.
484
+ # This property is a summary property of data from sightings and other data that may or may not be available in
485
+ # STIX. If new sightings are received that are later than the last_seen timestamp,
486
+ # the object may be updated to account for the new data.
487
+ last_seen: datetime | None = None
488
+ # The operating systems that the malware family or malware instance is executable on.
489
+ # This applies to virtualized operating systems as well as those running on bare metal.
490
+ operating_system_refs: list[Identifier] | None = None
491
+ # The processor architectures (e.g., x86, ARM, etc.) that the malware instance or family is executable on.
492
+ # The values for this property SHOULD come from the processor-architecture-ov open vocabulary.
493
+ architecture_execution_envs: list[str] | None = None
494
+ # The programming language(s) used to implement the malware instance or family.
495
+ # The values for this property SHOULD come from the implementation-language-ov open vocabulary.
496
+ implementation_languages: list[str] | None = None
497
+ # Any of the capabilities identified for the malware instance or family.
498
+ # The values for this property SHOULD come from the malware-capabilities-ov open vocabulary.
499
+ capabilities: list[str] | None = None
500
+ # The sample_refs property specifies a list of identifiers of the SCO file or artifact objects associated with
501
+ # this malware instance(s) or family.
502
+ sample_refs: list[Identifier] | None = None
503
+
504
+ @model_validator(mode="after")
505
+ def validate_last_seen_after_first_seen(self) -> Self:
506
+ """
507
+ If the last_seen and the first_seen properties are both defined, then last_seen property
508
+ MUST be greater than or equal to the timestamp in the first_seen property.
509
+ """
510
+ if self.first_seen and self.last_seen and self.first_seen > self.last_seen:
511
+ raise ValueError(
512
+ "The last_seen property MUST be greater than or equal to the timestamp in the first_seen property"
513
+ )
514
+ return self
515
+
516
+ @model_validator(mode="after")
517
+ def validate_name_if_is_family(self) -> Self:
518
+ """
519
+ For a malware family the name MUST be defined.
520
+ """
521
+ if self.is_family and not self.name:
522
+ raise ValueError("For a malware family the name MUST be defined")
523
+ return self
524
+
525
+
526
+ # 4.12 Malware Analysis
527
+ class MalwareAnalysis(StixDomain):
528
+ """
529
+ Malware Analysis captures the metadata and results of a particular static or dynamic analysis performed on a malware
530
+ instance or family.
531
+ """
532
+
533
+ type: Literal["malware-analysis"] = "malware-analysis" # pyright: ignore[reportIncompatibleVariableOverride]
534
+ # The name of the analysis engine or product that was used.
535
+ # Product names SHOULD be all lowercase with words separated by a dash "-".
536
+ # For cases where the name of a product cannot be specified, a value of "anonymized" MUST be used.
537
+ product: str
538
+ # The version of the analysis product that was used to perform the analysis.
539
+ version: str | None = None
540
+ # A description of the virtual machine environment used to host the guest operating system (if applicable) that was
541
+ # used for the dynamic analysis of the malware instance or family. If this value is not included in conjunction
542
+ # with the operating_system_ref property, this means that the dynamic analysis may have been performed on bare
543
+ # metal (i.e. without virtualization) or the information was redacted.
544
+ # The value of this property MUST be the identifier for a SCO software object.
545
+ host_vm_ref: Identifier | None = None
546
+ # The operating system used for the dynamic analysis of the malware instance or family. This applies to
547
+ # virtualized operating systems as well as those running on bare metal.
548
+ # The value of this property MUST be the identifier for a SCO software object.
549
+ operating_system_ref: Identifier | None = None
550
+ # Any non-standard software installed on the operating system (specified through the operating-system value)
551
+ # used for the dynamic analysis of the malware instance or family.
552
+ # The value of this property MUST be the identifier for a SCO software object.
553
+ installed_software_refs: list[Identifier] | None = None
554
+ # The named configuration of additional product configuration parameters for this analysis run. For example, when
555
+ # a product is configured to do full depth analysis of Window™ PE files. This configuration may have a named
556
+ # version and that named version can be captured in this property. This will ensure additional runs can be
557
+ # configured in the same way.
558
+ configuration_version: str | None = None
559
+ # The specific analysis modules that were used and configured in the product during this analysis run.
560
+ # For example, configuring a product to support analysis of Dridex.
561
+ modules: list[str] | None = None
562
+ # The version of the analysis engine or product (including AV engines) that was used to perform the analysis.
563
+ analysis_engine_version: str | None = None
564
+ # The version of the analysis definitions used by the analysis tool (including AV tools).
565
+ analysis_definition_version: str | None = None
566
+ # The date and time that the malware was first submitted for scanning or analysis. This value will stay constant
567
+ # while the scanned date can change. For example, when Malware was submitted to a virus analysis tool.
568
+ submitted: datetime | None = None
569
+ # The date and time that the malware analysis was initiated.
570
+ analysis_started: datetime | None = None
571
+ # The date and time that the malware analysis ended.
572
+ analysis_ended: datetime | None = None
573
+ # The classification result or name assigned to the malware instance by the scanner tool.
574
+ result_name: str | None = None
575
+ # The classification result as determined by the scanner or tool analysis process.
576
+ # The value for this property SHOULD come from the malware-result-ov open vocabulary.
577
+ result: str | None = None
578
+ # This property contains the references to the STIX Cyber-observable Objects that were captured during the
579
+ # analysis process.
580
+ analysis_sco_refs: list[Identifier] | None = None
581
+ # This property contains the reference to the SCO file, network traffic or artifact object that this malware
582
+ # analysis was performed against. Caution should be observed when creating an SRO between Malware and Malware
583
+ # Analysis objects when the Malware sample_refs property does not contain the SCO that is included in the
584
+ # Malware Analysis sample_ref property. Note, this property can also contain a reference to an SCO which is not
585
+ # associated with Malware (i.e., some SCO which was scanned and found to be benign.)
586
+ sample_ref: Identifier | None = None
587
+
588
+ @model_validator(mode="after")
589
+ def at_least_one_of(self) -> Self:
590
+ """
591
+ One of result or analysis_sco_refs properties MUST be provided.
592
+ """
593
+ if self.result is None and self.analysis_sco_refs is None:
594
+ raise ValueError("One of result or analysis_sco_refs must be provided")
595
+ return self
596
+
597
+
598
+ # 4.13 Note
599
+ class Note(StixDomain):
600
+ """
601
+ A Note is intended to convey informative text to provide further context and/or to provide additional analysis
602
+ not contained in the STIX Objects, Marking Definition objects, or Language Content objects which the Note relates
603
+ to. Notes can be created by anyone (not just the original object creator).
604
+
605
+ For example, an analyst may add a Note to a Campaign object created by another organization indicating that they've
606
+ seen posts related to that Campaign on a hacker forum.
607
+
608
+ Because Notes are typically (though not always) created by human analysts and are comprised of human-oriented text,
609
+ they contain an additional property to capture the analyst(s) that created the Note. This is distinct from the
610
+ created_by_ref property, which is meant to capture the organization that created the object.
611
+ """
612
+
613
+ type: Literal["note"] = "note" # pyright: ignore[reportIncompatibleVariableOverride]
614
+ # A brief summary of the note content.
615
+ abstract: str | None = None
616
+ # The content of the note.
617
+ content: str
618
+ # The name of the author(s) of this note (e.g., the analyst(s) that created it).
619
+ authors: list[str] | None = None
620
+ # The STIX Objects that the note is being applied to.
621
+ object_refs: list[Identifier]
622
+
623
+
624
+ # 4.14 Observed Data
625
+ class ObservedData(StixDomain):
626
+ """
627
+ Observed Data conveys information about cyber security related entities such as files, systems, and networks using
628
+ the STIX Cyber-observable Objects (SCOs). For example, Observed Data can capture information about an IP address,
629
+ a network connection, a file, or a registry key. Observed Data is not an intelligence assertion, it is simply the
630
+ raw information without any context for what it means.
631
+
632
+ Observed Data can capture that a piece of information was seen one or more times. Meaning, it can capture both a
633
+ single observation of a single entity (file, network connection) as well as the aggregation of multiple
634
+ observations of an entity. When the number_observed property is 1 the Observed Data represents a single entity.
635
+ When the number_observed property is greater than 1, the Observed Data represents several instances of an entity
636
+ potentially collected over a period of time. If a time window is known, that can be captured using the
637
+ first_observed and last_observed properties. When used to collect aggregate data, it is likely that some properties
638
+ in the SCO (e.g., timestamp properties) will be omitted because they would differ for each of the individual
639
+ observations.
640
+
641
+ Observed Data may be used by itself (without relationships) to convey raw data collected from any source including
642
+ analyst reports, sandboxes, and network and host-based detection tools. An intelligence producer conveying
643
+ Observed Data SHOULD include as much context (e.g. SCOs) as possible that supports the use of the observed data set
644
+ in systems expecting to utilize the Observed Data for improved security. This includes all SCOs that matched on an
645
+ Indicator pattern and are represented in the collected observed event (or events) being conveyed in the
646
+ Observed Data object. For example, a firewall could emit a single Observed Data instance containing a single
647
+ Network Traffic object for each connection it sees. The firewall could also aggregate data and instead send out an
648
+ Observed Data instance every ten minutes with an IP address and an appropriate number_observed value to indicate
649
+ the number of times that IP address was observed in that window. A sandbox could emit an Observed Data instance
650
+ containing a file hash that it discovered.
651
+
652
+ Observed Data may also be related to other SDOs to represent raw data that is relevant to those objects.
653
+ For example, the Sighting Relationship object, can relate an Indicator, Malware, or other SDO to a specific
654
+ Observed Data to represent the raw information that led to the creation of the Sighting (e.g., what was actually
655
+ seen that suggested that a particular instance of malware was active).
656
+ """
657
+
658
+ type: Literal["observed-data"] = "observed-data" # pyright: ignore[reportIncompatibleVariableOverride]
659
+ # The beginning of the time window during which the data was seen.
660
+ first_observed: datetime
661
+ # The end of the time window during which the data was seen.
662
+ last_observed: datetime
663
+ # The number of times that each Cyber-observable object represented in the objects or object_refs property was
664
+ # seen. If present, this MUST be an integer between 1 and 999,999,999 inclusive.
665
+ # If the number_observed property is greater than 1, the data contained in the objects or object_refs property was
666
+ # seen multiple times. In these cases, object creators MAY omit properties of the SCO (such as timestamps) that are
667
+ # specific to a single instance of that observed data.
668
+ number_observed: Annotated[int, Gt(1), Gt(999999999)]
669
+ # A dictionary of SCO representing the observation. The dictionary MUST contain at least one object.
670
+ # The cyber observable content MAY include multiple objects if those objects are related as part of a single
671
+ # observation. Multiple objects not related to each other via cyber observable Relationships MUST NOT be contained
672
+ # within the same Observed Data instance.
673
+ # For example, a Network Traffic object and two IPv4 Address objects related via the src_ref and dst_ref properties
674
+ # can be contained in the same Observed Data because they are all related and characterize that single entity.
675
+ # NOTE: this property is now deprecated in favor of object_refs and will be removed in a future version.
676
+ objects: Annotated[dict[str, Any], deprecated] | None = None # pyright: ignore[reportExplicitAny]
677
+ # A list of SCOs and SROs representing the observation. The object_refs MUST contain at least one SCO reference
678
+ # if defined.
679
+ object_refs: list[Identifier] | None = None
680
+
681
+ @model_validator(mode="after")
682
+ def validate_last_observed_after_first_observed(self) -> Self:
683
+ """
684
+ The last_observed property MUST be greater than or equal to the timestamp in the first_observed property.
685
+ """
686
+ if (
687
+ self.first_observed
688
+ and self.last_observed
689
+ and self.first_observed > self.last_observed
690
+ ):
691
+ raise ValueError(
692
+ "The last_observed property MUST be greater than or equal to the the first_observed property"
693
+ )
694
+ return self
695
+
696
+ @model_validator(mode="after")
697
+ def validate_objects_or_object_refs(self) -> Self:
698
+ """
699
+ The objects property or the object_refs property MUST be provided,
700
+ but both MUST NOT be present at the same time.
701
+ """
702
+ if self.objects is not None and self.object_refs is not None:
703
+ raise ValueError(
704
+ "The objects and object_refs properties MUST NOT be present at the same time"
705
+ )
706
+ if self.objects is None and self.object_refs is None:
707
+ raise ValueError("Either objects or object_refs MUST be provided")
708
+ return self
709
+
710
+
711
+ # 4.15 Opinion
712
+ class Opinion(StixDomain):
713
+ """
714
+ An Opinion is an assessment of the correctness of the information in a STIX Object produced by a different entity.
715
+ The primary property is the opinion property, which captures the level of agreement or disagreement using a fixed
716
+ scale. That fixed scale also supports a numeric mapping to allow for consistent statistical operations across
717
+ opinions.
718
+
719
+ For example, an analyst from a consuming organization might say that they "strongly disagree" with a Campaign
720
+ object and provide an explanation about why. In a more automated workflow, a SOC operator might give an Indicator
721
+ "one star" in their TIP (expressing "strongly disagree") because it is considered to be a false positive within
722
+ their environment. Opinions are subjective, and the specification does not address how best to interpret them.
723
+ Sharing communities are encouraged to provide clear guidelines to their constituents regarding best practice for
724
+ the use of Opinion objects within the community.
725
+
726
+ Because Opinions are typically (though not always) created by human analysts and are comprised of human-oriented
727
+ text, they contain an additional property to capture the analyst(s) that created the Opinion. This is distinct
728
+ from the created_by_ref property, which is meant to capture the organization that created the object.
729
+ """
730
+
731
+ type: Literal["opinion"] = "opinion" # pyright: ignore[reportIncompatibleVariableOverride]
732
+ # An explanation of why the producer has this Opinion. For example, if an Opinion of strongly-disagree is given,
733
+ # the explanation can contain an explanation of why the Opinion producer disagrees and what evidence they have
734
+ # for their disagreement.
735
+ explanation: str | None = None
736
+ # The name of the author(s) of this Opinion (e.g., the analyst(s) that created it).
737
+ authors: list[str] | None = None
738
+ # The opinion that the producer has about all of the STIX Object(s) listed in the object_refs property.
739
+ # The values of this property MUST come from the opinion-enum enumeration.
740
+ opinion: OpinionEnum
741
+ # The STIX Objects that the Opinion is being applied to.
742
+ object_refs: list[Identifier]
743
+
744
+
745
+ # 4.16 Report
746
+ class Report(StixDomain):
747
+ """
748
+ Reports are collections of threat intelligence focused on one or more topics, such as a description of a threat
749
+ actor, malware, or attack technique, including context and related details. They are used to group related threat
750
+ intelligence together so that it can be published as a comprehensive cyber threat story.
751
+
752
+ The Report SDO contains a list of references to STIX Objects (the CTI objects included in the report) along with a
753
+ textual description and the name of the report.
754
+
755
+ For example, a threat report produced by ACME Defense Corp. discussing the Glass Gazelle campaign should be
756
+ represented using Report. The Report itself would contain the narrative of the report while the Campaign SDO and
757
+ any related SDOs (e.g., Indicators for the Campaign, Malware it uses, and the associated Relationships) would be
758
+ referenced in the report contents.
759
+ """
760
+
761
+ type: Literal["report"] = "report" # pyright: ignore[reportIncompatibleVariableOverride]
762
+ # A name or characterizing text used to identify the Report.
763
+ name: str
764
+ # A description that provides more details and context about the Report, potentially including its purpose and
765
+ # its key characteristics.
766
+ description: str | None = None
767
+ # The primary type(s) of content found in this report.
768
+ # The values for this property SHOULD come from the report-type-ov open vocabulary.
769
+ report_types: list[str] | None = None
770
+ # The date that this Report object was officially published by the creator of this report.
771
+ # The publication date (public release, legal release, etc.) may be different than the date the report was
772
+ # created or shared internally (the date in the created property).
773
+ published: datetime
774
+ # Specifies the STIX Objects that are referred to by this Report.
775
+ object_refs: list[Identifier]
776
+
777
+
778
+ # 4.17 Threat Actor
779
+ class ThreatActor(StixDomain):
780
+ """
781
+ Threat Actors are actual individuals, groups, or organizations believed to be operating with malicious intent.
782
+ A Threat Actor is not an Intrusion Set but may support or be affiliated with various Intrusion Sets, groups, or
783
+ organizations over time.
784
+
785
+ Threat Actors leverage their resources, and possibly the resources of an Intrusion Set, to conduct attacks and
786
+ run Campaigns against targets.
787
+
788
+ Threat Actors can be characterized by their motives, capabilities, goals, sophistication level, past activities,
789
+ resources they have access to, and their role in the organization.
790
+ """
791
+
792
+ type: Literal["threat-actor"] = "threat-actor" # pyright: ignore[reportIncompatibleVariableOverride]
793
+ # A name or characterizing text used to identify the Threat Actor or Threat Actor group.
794
+ name: str
795
+ # A description that provides more details and context about the Threat Actor, potentially including its purpose
796
+ # and its key characteristics.
797
+ description: str | None = None
798
+ # The type(s) of this threat actor.
799
+ # The values for this property SHOULD come from the threat-actor-type-ov open vocabulary.
800
+ threat_actor_types: list[str] | None = None
801
+ # A list of other names that this Threat Actor is believed to use.
802
+ aliases: list[str] | None = None
803
+ # The time that this Threat Actor was first seen.
804
+ # This property is a summary property of data from sightings and other data that may or may not be available in
805
+ # STIX. If new sightings are received that are earlier than the first seen timestamp, the object may be updated
806
+ # to account for the new data.
807
+ first_seen: datetime | None = None
808
+ # The time that this Threat Actor was last seen.
809
+ # This property is a summary property of data from sightings and other data that may or may not be available in
810
+ # STIX. If new sightings are received that are later than the last seen timestamp, the object may be updated to
811
+ # account for the new data
812
+ last_seen: datetime | None = None
813
+ # A list of roles the Threat Actor plays.
814
+ # The values for this property SHOULD come from the threat-actor-role-ov open vocabulary.
815
+ roles: list[str] | None = None
816
+ # The high-level goals of this Threat Actor, namely, *what* are they trying to do.
817
+ # For example, they may be motivated by personal gain, but their goal is to steal credit card numbers.
818
+ # To do this, they may execute specific Campaigns that have detailed objectives like compromising point of sale
819
+ # systems at a large retailer.
820
+ goals: list[str] | None = None
821
+ # The skill, specific knowledge, special training, or expertise a Threat Actor must have to perform the attack.
822
+ # The value for this property SHOULD come from the threat-actor-sophistication-ov open vocabulary.
823
+ sophistication: str | None = None
824
+ # The organizational level at which this Threat Actor typically works, which in turn determines the resources
825
+ # available to this Threat Actor for use in an attack.
826
+ # This attribute is linked to the sophistication property — a specific resource level implies that the
827
+ # Threat Actor has access to at least a specific sophistication level.
828
+ # The value for this property SHOULD come from the attack-resource-level-ov open vocabulary.
829
+ resource_level: str | None = None
830
+ # The primary reason, motivation, or purpose behind this Threat Actor.
831
+ # The motivation is *why* the Threat Actor wishes to achieve the goal (what they are trying to achieve).
832
+ # The value for this property SHOULD come from the attack-motivation-ov open vocabulary.
833
+ primary_motivation: str | None = None
834
+ # This property specifies the secondary reasons, motivations, or purposes behind this Threat Actor.
835
+ # These motivations can exist as an equal or near-equal cause to the primary motivation. However, it does not
836
+ # replace or necessarily magnify the primary motivation, but it might indicate additional context.
837
+ # The position in the list has no significance.
838
+ # The value for this property SHOULD come from the attack-motivation-ov open vocabulary.
839
+ secondary_motivations: list[str] | None = None
840
+ # The personal reasons, motivations, or purposes of the Threat Actor regardless of organizational goals.
841
+ # Personal motivation, which is independent of the organization's goals, describes what impels an individual to
842
+ # carry out an attack. Personal motivation may align with the organization's motivation
843
+ # — as is common with activists — but more often it supports personal goals. For example, an individual analyst may
844
+ # join a Data Miner corporation because his or her skills may align with the corporation's objectives.
845
+ # But the analyst most likely performs his or her daily work toward those objectives for personal reward in the
846
+ # form of a paycheck. The motivation of personal reward may be even stronger for Threat Actors who commit illegal
847
+ # acts, as it is more difficult for someone to cross that line purely for altruistic reasons.
848
+ # The values for this property SHOULD come from the attack-motivation-ov open vocabulary.
849
+ # The position in the list has no significance.
850
+ personal_motivations: list[str] | None = None
851
+
852
+ @model_validator(mode="after")
853
+ def validate_last_seen_after_first_seen(self) -> Self:
854
+ """
855
+ If the last_seen property and the first_seen property are both defined, then the last_seen property
856
+ MUST be greater than or equal to the timestamp in the first_seen property.
857
+ """
858
+ if self.first_seen and self.last_seen and self.first_seen > self.last_seen:
859
+ raise ValueError(
860
+ "The last_seen property MUST be greater than or equal to the timestamp in the first_seen property"
861
+ )
862
+ return self
863
+
864
+
865
+ # 4.18 Tool
866
+ class Tool(StixDomain):
867
+ """
868
+ Tools are legitimate software that can be used by threat actors to perform attacks. Knowing how and when threat
869
+ actors use such tools can be important for understanding how campaigns are executed. Unlike malware, these tools
870
+ or software packages are often found on a system and have legitimate purposes for power users, system
871
+ administrators, network administrators, or even normal users. Remote access tools (e.g., RDP) and network
872
+ scanning tools (e.g., Nmap) are examples of Tools that may be used by a Threat Actor during an attack.
873
+
874
+ The Tool SDO characterizes the properties of these software tools and can be used as a basis for making an
875
+ assertion about how a Threat Actor uses them during an attack. It contains properties to name and describe the
876
+ tool, a list of Kill Chain Phases the tool can be used to carry out, and the version of the tool.
877
+
878
+ This SDO MUST NOT be used to characterize malware. Further, Tool MUST NOT be used to characterize tools used as
879
+ part of a course of action in response to an attack.
880
+ """
881
+
882
+ type: Literal["tool"] = "tool" # pyright: ignore[reportIncompatibleVariableOverride]
883
+ # A name or characterizing text used to identify the Tool.
884
+ name: str
885
+ # A description that provides more details and context about the Tool, potentially including its purpose and
886
+ # its key characteristics.
887
+ description: str | None = None
888
+ # The kind(s) of tool(s) being described.
889
+ # The values for this property SHOULD come from the tool-type-ov open vocabulary.
890
+ tool_types: list[str] | None = None
891
+ # Alternative names used to identify this Tool.
892
+ aliases: list[str] | None = None
893
+ # The list of kill chain phases for which this Tool can be used.
894
+ kill_chain_phases: list[KillChainPhase] | None = None
895
+ # The version identifier associated with the Tool.
896
+ tool_version: str | None = None
897
+
898
+
899
+ # 4.19 Vulnerability
900
+ class Vulnerability(StixDomain):
901
+ """
902
+ A Vulnerability is a weakness or defect in the requirements, designs, or implementations of the computational
903
+ logic (e.g., code) found in software and some hardware components (e.g., firmware) that can be directly
904
+ exploited to negatively impact the confidentiality, integrity, or availability of that system.
905
+
906
+ CVE is a list of information security vulnerabilities and exposures that provides common names for publicly known
907
+ problems [CVE]. For example, if a piece of malware exploits CVE-2015-12345, a Malware object could be linked to a
908
+ Vulnerability object that references CVE-2015-12345.
909
+
910
+ The Vulnerability SDO is primarily used to link to external definitions of vulnerabilities or to describe 0-day
911
+ vulnerabilities that do not yet have an external definition. Typically, other SDOs assert relationships to
912
+ Vulnerability objects when a specific vulnerability is targeted and exploited as part of malicious cyber activity.
913
+ As such, Vulnerability objects can be used as a linkage to the asset management and compliance process.
914
+ """
915
+
916
+ type: Literal["vulnerability"] = "vulnerability" # pyright: ignore[reportIncompatibleVariableOverride]
917
+ # A list of external references which refer to non-STIX information.
918
+ # This property MAY be used to provide one or more Vulnerability identifiers, such as a CVE ID [CVE].
919
+ # When specifying a CVE ID, the source_name property of the external reference MUST be set to cve and the
920
+ # external_id property MUST be the exact CVE identifier.
921
+ external_references: list[ExternalReference] | None = None
922
+ # A name or characterizing text used to identify the Vulnerability.
923
+ name: str
924
+ # A description that provides more details and context about the Vulnerability, potentially including its
925
+ # purpose and its key characteristics.
926
+ description: str | None = None
927
+
928
+
929
+ SDOs = Annotated[
930
+ (
931
+ AttackPattern
932
+ | Campaign
933
+ | CourseOfAction
934
+ | Grouping
935
+ | Identity
936
+ | Incident
937
+ | Indicator
938
+ | Infrastructure
939
+ | IntrusionSet
940
+ | Location
941
+ | Malware
942
+ | MalwareAnalysis
943
+ | Note
944
+ | ObservedData
945
+ | Opinion
946
+ | Report
947
+ | ThreatActor
948
+ | Tool
949
+ | Vulnerability
950
+ ),
951
+ Field(discriminator="type"),
952
+ ]