stidantic 0.1.0__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/__init__.py +23 -0
- stidantic/__init__.pyi +14 -0
- stidantic/bundle.py +29 -0
- stidantic/extension.py +115 -0
- stidantic/language.py +35 -0
- stidantic/marking.py +118 -0
- stidantic/sco.py +91 -0
- stidantic/sdo.py +87 -0
- stidantic/sro.py +159 -0
- stidantic/types.py +409 -0
- stidantic/validators.py +20 -0
- stidantic/vocab.py +512 -0
- stidantic-0.1.0.dist-info/METADATA +173 -0
- stidantic-0.1.0.dist-info/RECORD +16 -0
- stidantic-0.1.0.dist-info/WHEEL +4 -0
- stidantic-0.1.0.dist-info/licenses/LICENSE +21 -0
stidantic/__init__.py
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""stidantic - A Pydantic-based STIX 2.1 library."""
|
|
2
|
+
|
|
3
|
+
__version__ = "0.1.0"
|
|
4
|
+
__author__ = "nicocti"
|
|
5
|
+
__all__ = [
|
|
6
|
+
"StixBundle",
|
|
7
|
+
"StixCore",
|
|
8
|
+
"StixCommon",
|
|
9
|
+
"StixDomain",
|
|
10
|
+
"StixRelationship",
|
|
11
|
+
"StixMeta",
|
|
12
|
+
"Identifier",
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
from stidantic.bundle import StixBundle
|
|
16
|
+
from stidantic.types import (
|
|
17
|
+
Identifier,
|
|
18
|
+
StixCommon,
|
|
19
|
+
StixCore,
|
|
20
|
+
StixDomain,
|
|
21
|
+
StixMeta,
|
|
22
|
+
StixRelationship,
|
|
23
|
+
)
|
stidantic/__init__.pyi
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""Type stubs for stidantic."""
|
|
2
|
+
|
|
3
|
+
from stidantic.bundle import StixBundle as StixBundle
|
|
4
|
+
from stidantic.types import (
|
|
5
|
+
Identifier as Identifier,
|
|
6
|
+
StixCommon as StixCommon,
|
|
7
|
+
StixCore as StixCore,
|
|
8
|
+
StixDomain as StixDomain,
|
|
9
|
+
StixMeta as StixMeta,
|
|
10
|
+
StixRelationship as StixRelationship,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
__version__: str
|
|
14
|
+
__author__: str
|
stidantic/bundle.py
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
from typing import Annotated
|
|
2
|
+
from pydantic import Field
|
|
3
|
+
from stidantic.types import StixCore, Identifier, StixCommon
|
|
4
|
+
from stidantic.sdo import SDOs
|
|
5
|
+
from stidantic.sco import SCOs
|
|
6
|
+
from stidantic.sro import SROs
|
|
7
|
+
from stidantic.language import LanguageContent
|
|
8
|
+
from stidantic.marking import MarkingDefinition
|
|
9
|
+
from stidantic.extension import ExtensionDefinition
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
# 8. Stix Bundle
|
|
13
|
+
class StixBundle(StixCore):
|
|
14
|
+
id: Identifier
|
|
15
|
+
type: str = "bundle"
|
|
16
|
+
objects: list[
|
|
17
|
+
Annotated[
|
|
18
|
+
(
|
|
19
|
+
SROs
|
|
20
|
+
| SDOs
|
|
21
|
+
| SCOs
|
|
22
|
+
| MarkingDefinition
|
|
23
|
+
| LanguageContent
|
|
24
|
+
| ExtensionDefinition
|
|
25
|
+
),
|
|
26
|
+
Field(discriminator="type"),
|
|
27
|
+
]
|
|
28
|
+
| StixCommon
|
|
29
|
+
]
|
stidantic/extension.py
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
from typing import Literal, Annotated, Self
|
|
2
|
+
from pydantic import Field
|
|
3
|
+
from pydantic.functional_validators import model_validator
|
|
4
|
+
from stidantic.types import StixExtension, ExtensionType, StixProp
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
# 7.3 Extension Definition
|
|
8
|
+
class ExtensionDefinition(StixExtension):
|
|
9
|
+
"""
|
|
10
|
+
The STIX Extension Definition object allows producers of threat intelligence to extend existing STIX objects or
|
|
11
|
+
to create entirely new STIX objects in a standardized way. This object contains detailed information about the
|
|
12
|
+
extension and any additional properties and or objects that it defines. This extension mechanism MUST NOT be used
|
|
13
|
+
to redefine existing standardized objects or properties.
|
|
14
|
+
|
|
15
|
+
If a producer does not include the STIX Extension Definition object with the STIX objects that use it,
|
|
16
|
+
consumers should refer to section 3.3 for information in resolving references.
|
|
17
|
+
|
|
18
|
+
There are three ways to extend STIX using STIX Extensions.
|
|
19
|
+
- Define one or more new STIX Object types.
|
|
20
|
+
- Define additional properties for an existing STIX Object type as a nested property extension. This is
|
|
21
|
+
typically done to represent a sub-component or module of one or more STIX Object types.
|
|
22
|
+
- Define additional properties for an existing STIX Object type at the object's top-level. This can be done to
|
|
23
|
+
represent properties that form an inherent part of the definition of an object type.
|
|
24
|
+
|
|
25
|
+
When defining a new STIX Object (e.g., SDO, SCO, or SRO) all common properties associated with that type of
|
|
26
|
+
object (SDO, SCO, SRO) MUST be included in the schema or definition of that new STIX Object type.
|
|
27
|
+
Extensions that create new STIX objects MUST follow all conformance requirements for that object type
|
|
28
|
+
(SDO, SCO, SRO) including all of the requirements for the common properties associated with that object type.
|
|
29
|
+
|
|
30
|
+
When defining a STIX extension using the nested property extension mechanism the extensions property MUST include
|
|
31
|
+
the extension definition's UUID that defines the extension definition object and the extension_type property
|
|
32
|
+
as defined in section 3.2.
|
|
33
|
+
|
|
34
|
+
IMPORTANT NOTE: Producers using top-level property extensions should be mindful that another producer could also
|
|
35
|
+
define a top-level property extension using the same property names but for different purposes causing name
|
|
36
|
+
conflicts when both extensions are used in the same environment. This standard does not define any name conflict
|
|
37
|
+
resolution for new STIX Objects or for top-level properties created by this extension mechanism. However,
|
|
38
|
+
producers SHOULD follow industry best practices such as using unique property names that are guaranteed to avoid
|
|
39
|
+
duplicates across all organizations to avoid naming conflicts.
|
|
40
|
+
IMPORTANT NOTE: Producers using STIX extensions should be mindful that future versions of the STIX specification
|
|
41
|
+
MAY define objects and or properties that conflict with existing non-standardized extensions. In these cases the
|
|
42
|
+
meaning as defined in the STIX specification will override any and all conflicting extensions.
|
|
43
|
+
|
|
44
|
+
Specific extensions, as with specific Custom Properties, MAY NOT be supported across implementations.
|
|
45
|
+
A consumer that receives STIX content containing a STIX extension that it does not understand MAY refuse to
|
|
46
|
+
process the content or MAY ignore that extension and continue processing the content.
|
|
47
|
+
|
|
48
|
+
The 3 uses of this extension facility MAY be combined into a single Extension Definition object when appropriate.
|
|
49
|
+
|
|
50
|
+
The following example highlights where this may be useful.
|
|
51
|
+
|
|
52
|
+
Hybrid Extension Example
|
|
53
|
+
An intelligence producer has a monitoring network of sensors that collect a variety of cybersecurity telemetry
|
|
54
|
+
from each sensor where those sensors have unique data not currently defined in STIX 2.1.
|
|
55
|
+
The producer wishes to create an extension that other downstream consumers can receive both the high-level
|
|
56
|
+
summarization object but also the individual categorized telemetry from each sensor.
|
|
57
|
+
a) A new SDO representing the statistical summarization object.
|
|
58
|
+
b) A list of new properties to be added to the standard Observed Data object representing additional meta-data
|
|
59
|
+
information associated with the telemetry.
|
|
60
|
+
c) A new SCO representing a new cyber observable data type.
|
|
61
|
+
|
|
62
|
+
In this case, the producer creates a single extension that contains the following extension types:
|
|
63
|
+
"extension_types": [ "new-sdo", "new-sco", "property-extension" ]
|
|
64
|
+
|
|
65
|
+
Therefore, producers MAY use the hybrid extension mechanism when they wish to define a single extension that
|
|
66
|
+
encompasses new SDO and/or sub-component or top-level property extension properties in a related extension.
|
|
67
|
+
|
|
68
|
+
Producers SHOULD NOT use the hybrid extension mechanism if the extensions are not related to each other.
|
|
69
|
+
If the extensions are independent features then a producer SHOULD consider creating separate extension definitions.
|
|
70
|
+
"""
|
|
71
|
+
|
|
72
|
+
type: Literal["extension-definition"] = "extension-definition" # pyright: ignore[reportIncompatibleVariableOverride]
|
|
73
|
+
# A name used for display purposes during execution, development, or debugging.
|
|
74
|
+
name: str
|
|
75
|
+
# A detailed explanation of what data the extension conveys and how it is intended to be used.
|
|
76
|
+
# While the description property is optional this property SHOULD be populated.
|
|
77
|
+
# Note that the schema property is the normative definition of the extension, and this property, if present,
|
|
78
|
+
# is for documentation purposes only.
|
|
79
|
+
description: str | None = None
|
|
80
|
+
# The normative definition of the extension, either as a URL or as plain text explaining the definition.
|
|
81
|
+
# A URL SHOULD point to a JSON schema or a location that contains information about the schema
|
|
82
|
+
json_schema: Annotated[str, Field(alias="schema")]
|
|
83
|
+
# The version of this extension. Producers of STIX extensions are encouraged to follow standard semantic
|
|
84
|
+
# versioning procedures where the version number follows the pattern, MAJOR.MINOR.PATCH. This will allow
|
|
85
|
+
# consumers to distinguish between the three different levels of compatibility typically identified by
|
|
86
|
+
# such versioning strings.
|
|
87
|
+
version: str
|
|
88
|
+
# This property specifies one or more extension types contained within this extension.
|
|
89
|
+
# When this property includes toplevel-property-extension then the extension_properties property
|
|
90
|
+
# SHOULD include one or more property names.
|
|
91
|
+
extension_types: list[ExtensionType]
|
|
92
|
+
# This property contains the list of new property names that are added to an object by an extension.
|
|
93
|
+
# This property MUST only be used when the extension_types property includes a value of toplevel-property-extension.
|
|
94
|
+
# In other words, when new properties are being added at the top-level of an existing object.
|
|
95
|
+
# The property names used in Extension STIX Object MUST be in ASCII and MUST only contain the characters a–z
|
|
96
|
+
# (lowercase ASCII), 0–9, and underscore (_).
|
|
97
|
+
# The name of a property of a Extension STIX Object MUST have a minimum length of 3 ASCII characters.
|
|
98
|
+
# The name of a property of a Extension STIX Object MUST be no longer than 250 ASCII characters in length.
|
|
99
|
+
extension_properties: list[StixProp] | None = None
|
|
100
|
+
|
|
101
|
+
@model_validator(mode="after")
|
|
102
|
+
def validate_extension_properties(self) -> Self:
|
|
103
|
+
"""
|
|
104
|
+
extension_properties MUST only be used when the extension_types property includes a value of
|
|
105
|
+
toplevel-property-extension. In other words, when new properties are being added at the
|
|
106
|
+
top-level of an existing object.
|
|
107
|
+
"""
|
|
108
|
+
if (
|
|
109
|
+
self.extension_properties
|
|
110
|
+
and ExtensionType.toplevel_property_extension not in self.extension_types
|
|
111
|
+
):
|
|
112
|
+
raise ValueError(
|
|
113
|
+
"extension_types property can't be used without toplevel-property-extension in extension_types."
|
|
114
|
+
)
|
|
115
|
+
return self
|
stidantic/language.py
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
from typing import Literal
|
|
3
|
+
from stidantic.types import Identifier, StixLanguage
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
# 7.1 Language Content
|
|
7
|
+
class LanguageContent(StixLanguage):
|
|
8
|
+
type: Literal["language-content"] = "language-content" # pyright: ignore[reportIncompatibleVariableOverride]
|
|
9
|
+
# The object_ref property identifies the id of the object that this Language Content applies to.
|
|
10
|
+
# It MUST be the identifier for a STIX Object.
|
|
11
|
+
object_ref: Identifier
|
|
12
|
+
# The object_modified property identifies the modified time of the object that this Language Content applies to.
|
|
13
|
+
# It MUST be an exact match for the modified time of the STIX Object being referenced.
|
|
14
|
+
object_modified: datetime | None = None
|
|
15
|
+
# The contents property contains the actual Language Content (translation).
|
|
16
|
+
# The keys in the dictionary MUST be RFC 5646 language codes for which language content is being provided [RFC5646].
|
|
17
|
+
# The values each consist of a dictionary that mirrors the properties in the target object
|
|
18
|
+
# (identified by object_ref and object_modified). For example, to provide a translation of the name property
|
|
19
|
+
# on the target object the key in the dictionary would be name.
|
|
20
|
+
# For each key in the nested dictionary:
|
|
21
|
+
# ● If the original property is a string, the corresponding property in the language content object
|
|
22
|
+
# MUST contain a string with the content for that property in the language of the top-level key.
|
|
23
|
+
# ● If the original property is a list, the corresponding property in the translation object must also be
|
|
24
|
+
# a list. Each item in this list recursively maps to the item at the same position in the list contained in the
|
|
25
|
+
# target object. The lists MUST have the same length.
|
|
26
|
+
# ● In the event that translations are only provided for some list items, the untranslated list items MUST
|
|
27
|
+
# be represented by an empty string (""). This indicates to a consumer of the Language Content object that they
|
|
28
|
+
# should interpolate the translated list items in the Language Content object with the corresponding (untranslated)
|
|
29
|
+
# list items from the original object as indicated by the object_ref property.
|
|
30
|
+
# ● If the original property is an object (including dictionaries), the corresponding location in the
|
|
31
|
+
# translation object must also be an object. Each key/value field in this object recursively maps to the object
|
|
32
|
+
# with the same key in the original.
|
|
33
|
+
# The translation object MAY contain only a subset of the translatable fields of the original. Keys that point to
|
|
34
|
+
# non-translatable properties in the target or to properties that do not exist in the target object MUST be ignored.
|
|
35
|
+
contents: dict[str, dict[str, str]]
|
stidantic/marking.py
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
from typing import Literal, Self
|
|
2
|
+
from enum import Enum
|
|
3
|
+
from datetime import datetime, timezone
|
|
4
|
+
|
|
5
|
+
from pydantic.functional_validators import model_validator
|
|
6
|
+
from stidantic.types import (
|
|
7
|
+
StixCore,
|
|
8
|
+
StixMarking,
|
|
9
|
+
Identifier,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
# 7.2.1.3 Statement Marking
|
|
14
|
+
class StatementMarking(StixCore):
|
|
15
|
+
"""
|
|
16
|
+
The Statement marking type defines the representation of a textual marking statement (e.g., copyright, terms of use,
|
|
17
|
+
etc.) in a definition. The value of the definition_type property MUST be statement when using this marking type.
|
|
18
|
+
Statement markings are generally not machine-readable, and this specification does not define any behavior or
|
|
19
|
+
actions based on their values.
|
|
20
|
+
|
|
21
|
+
Content may be marked with multiple statements of use. In other words, the same content can be marked both with a
|
|
22
|
+
statement saying "Copyright 2019" and a statement saying, "Terms of use are ..." and both statements apply.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
# A Statement (e.g., copyright, terms of use) applied to the content marked by this marking definition.
|
|
26
|
+
statement: str
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
# 7.2.1.4 TLP Marking
|
|
30
|
+
class TLPMarking(StixCore):
|
|
31
|
+
"""
|
|
32
|
+
The TLP marking type defines how you would represent a Traffic Light Protocol (TLP) marking in a definition
|
|
33
|
+
property. The value of the definition_type property MUST be tlp when using this marking type.
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
# The TLP level [TLP] of the content marked by this marking definition, as defined in this section.
|
|
37
|
+
tlp: str
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
# 7.2.1 Marking Definition
|
|
41
|
+
class MarkingDefinition(StixMarking):
|
|
42
|
+
"""
|
|
43
|
+
The marking-definition object represents a specific marking. Data markings typically represent handling or
|
|
44
|
+
sharing requirements for data and are applied in the object_marking_refs and granular_markings properties on
|
|
45
|
+
STIX Objects, which reference a list of IDs for marking-definition objects.
|
|
46
|
+
|
|
47
|
+
Two marking definition types are defined in this specification: TLP, to capture TLP markings, and Statement,
|
|
48
|
+
to capture text marking statements. In addition, it is expected that the FIRST Information Exchange Policy (IEP)
|
|
49
|
+
will be included in a future version once a machine-usable specification for it has been defined.
|
|
50
|
+
|
|
51
|
+
Unlike other STIX Objects, Marking Definition objects cannot be versioned because it would allow for indirect
|
|
52
|
+
changes to the markings on a STIX Object. For example, if a Statement marking is changed from "Reuse Allowed" to
|
|
53
|
+
"Reuse Prohibited", all STIX Objects marked with that Statement marking would effectively have an updated marking
|
|
54
|
+
without being updated themselves. Instead, a new Statement marking with the new text should be created and the
|
|
55
|
+
marked objects updated to point to the new marking.
|
|
56
|
+
"""
|
|
57
|
+
|
|
58
|
+
type: Literal["marking-definition"] = "marking-definition" # pyright: ignore[reportIncompatibleVariableOverride]
|
|
59
|
+
# A name used to identify the Marking Definition.
|
|
60
|
+
name: str | None = None
|
|
61
|
+
# The definition_type property identifies the type of Marking Definition. The value of the definition_type property
|
|
62
|
+
# SHOULD be one of the types defined in the subsections below: statement or tlp (see sections 7.2.1.3 and 7.2.1.4).
|
|
63
|
+
# Any new marking definitions SHOULD be specified using the extension facility described in section 7.3.
|
|
64
|
+
# If the extensions property is not present, this property MUST be present.
|
|
65
|
+
definition_type: str | None = None
|
|
66
|
+
# The definition property contains the marking object itself (e.g., the TLP marking as defined in section 7.2.1.4,
|
|
67
|
+
# the Statement marking as defined in section 7.2.1.3).
|
|
68
|
+
# Any new marking definitions SHOULD be specified using the extension facility described in section 7.3.
|
|
69
|
+
# If the extensions property is not present, this property MUST be present.
|
|
70
|
+
definition: TLPMarking | StatementMarking | None = None
|
|
71
|
+
|
|
72
|
+
@model_validator(mode="after")
|
|
73
|
+
def definition_if_no_extensions(self) -> Self:
|
|
74
|
+
if not self.extensions and (not self.definition and not self.definition_type):
|
|
75
|
+
raise ValueError(
|
|
76
|
+
"If the extensions property is not present, definition_type and definition properties MUST be present."
|
|
77
|
+
)
|
|
78
|
+
return self
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
STIX_ZERO_DATE = datetime(2017, 1, 20, 00, 00, 00, 00, tzinfo=timezone.utc)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
class TLP(Enum):
|
|
85
|
+
"""
|
|
86
|
+
The following standard marking definitions MUST be used to reference or represent TLP markings.
|
|
87
|
+
Other instances of tlp-marking MUST NOT be used or created (the only instances of TLP marking definitions
|
|
88
|
+
permitted are those defined here).
|
|
89
|
+
"""
|
|
90
|
+
|
|
91
|
+
white = MarkingDefinition(
|
|
92
|
+
id=Identifier("marking-definition--613f2e26-407d-48c7-9eca-b8e91df99dc9"),
|
|
93
|
+
definition_type="tlp",
|
|
94
|
+
definition=TLPMarking(tlp="white"),
|
|
95
|
+
name="TLP:WHITE",
|
|
96
|
+
created=STIX_ZERO_DATE,
|
|
97
|
+
)
|
|
98
|
+
green = MarkingDefinition(
|
|
99
|
+
id=Identifier("marking-definition--34098fce-860f-48ae-8e50-ebd3cc5e41da"),
|
|
100
|
+
definition_type="tlp",
|
|
101
|
+
definition=TLPMarking(tlp="green"),
|
|
102
|
+
name="TLP:GREEN",
|
|
103
|
+
created=STIX_ZERO_DATE,
|
|
104
|
+
)
|
|
105
|
+
amber = MarkingDefinition(
|
|
106
|
+
id=Identifier("marking-definition--f88d31f6-486f-44da-b317-01333bde0b82"),
|
|
107
|
+
definition_type="tlp",
|
|
108
|
+
definition=TLPMarking(tlp="amber"),
|
|
109
|
+
name="TLP:AMBER",
|
|
110
|
+
created=STIX_ZERO_DATE,
|
|
111
|
+
)
|
|
112
|
+
red = MarkingDefinition(
|
|
113
|
+
id=Identifier("marking-definition--5e57c739-391a-4eb3-b6be-7d15ca92d5ed"),
|
|
114
|
+
definition_type="tlp",
|
|
115
|
+
definition=TLPMarking(tlp="red"),
|
|
116
|
+
name="TLP:RED",
|
|
117
|
+
created=STIX_ZERO_DATE,
|
|
118
|
+
)
|
stidantic/sco.py
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
from typing import Literal, Self
|
|
2
|
+
from typing_extensions import Annotated
|
|
3
|
+
from pydantic.functional_validators import model_validator
|
|
4
|
+
from pydantic import Field
|
|
5
|
+
from stidantic.types import StixObservable, StixBinary, StixUrl, Hashes
|
|
6
|
+
from stidantic.vocab import EncryptionAlgorithm
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
# 6.1 Artifact Object
|
|
10
|
+
class Artifact(StixObservable):
|
|
11
|
+
"""
|
|
12
|
+
The Artifact Object permits capturing an array of bytes (8-bits),
|
|
13
|
+
as a base64-encoded string string, or linking to a file-like payload.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
type: Literal["artifact"] = "artifact" # pyright: ignore[reportIncompatibleVariableOverride]
|
|
17
|
+
mime_type: str | None = None
|
|
18
|
+
payload_bin: StixBinary | None = None
|
|
19
|
+
url: StixUrl | None = None
|
|
20
|
+
hashes: Hashes | None = None
|
|
21
|
+
encryption_algorithm: EncryptionAlgorithm | None = None
|
|
22
|
+
decryption_key: str | None = None
|
|
23
|
+
|
|
24
|
+
@model_validator(mode="after")
|
|
25
|
+
def one_of(self) -> Self:
|
|
26
|
+
"""
|
|
27
|
+
One of payload_bin or url MUST be provided.
|
|
28
|
+
"""
|
|
29
|
+
if self.payload_bin or self.hashes:
|
|
30
|
+
return self
|
|
31
|
+
raise ValueError("Missing at least hashes or payload_bin property.")
|
|
32
|
+
|
|
33
|
+
@model_validator(mode="after")
|
|
34
|
+
def url_must_not_be_present_if_payload_bin_provided(self) -> Self:
|
|
35
|
+
"""
|
|
36
|
+
URL property MUST NOT be present if payload_bin is provided.
|
|
37
|
+
"""
|
|
38
|
+
if self.payload_bin and self.url:
|
|
39
|
+
raise ValueError(
|
|
40
|
+
"url property MUST NOT be present if payload_bin is provided."
|
|
41
|
+
)
|
|
42
|
+
return self
|
|
43
|
+
|
|
44
|
+
@model_validator(mode="after")
|
|
45
|
+
def hashes_must_be_present_if_url_provided(self) -> Self:
|
|
46
|
+
"""
|
|
47
|
+
Hashes property MUST be present when the url property is present.
|
|
48
|
+
"""
|
|
49
|
+
if self.url and not self.hashes:
|
|
50
|
+
raise ValueError("hashes MUST be present if url is provided.")
|
|
51
|
+
return self
|
|
52
|
+
|
|
53
|
+
@model_validator(mode="after")
|
|
54
|
+
def decryption_key_must_not_be_present_if_encryption_algorithm_absent(self) -> Self:
|
|
55
|
+
"""
|
|
56
|
+
decryption_key property MUST NOT be present when the encryption_algorithm property is absent.
|
|
57
|
+
"""
|
|
58
|
+
if not self.encryption_algorithm and self.decryption_key:
|
|
59
|
+
raise ValueError(
|
|
60
|
+
"decryption_key MUST NOT be present when the encryption_algorithm property is absent."
|
|
61
|
+
)
|
|
62
|
+
return self
|
|
63
|
+
|
|
64
|
+
class Config:
|
|
65
|
+
json_schema_extra: dict[str, list[str]] = {
|
|
66
|
+
"id_contributing_properties": ["hashes", "payload_bin"]
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
# 6.2 Autonomous System
|
|
71
|
+
class AutonomousSystem(StixObservable):
|
|
72
|
+
"""
|
|
73
|
+
The AS object represents the properties of an Autonomous Systems (AS).
|
|
74
|
+
"""
|
|
75
|
+
|
|
76
|
+
type: Literal["autonomous-system"] = "autonomous-system" # pyright: ignore[reportIncompatibleVariableOverride]
|
|
77
|
+
# Specifies the number assigned to the AS. Such assignments a
|
|
78
|
+
# re typically performed by a Regional Internet Registry (RIR).
|
|
79
|
+
number: int
|
|
80
|
+
# Specifies the name of the AS.
|
|
81
|
+
name: str | None = None
|
|
82
|
+
# Specifies the name of the Regional Internet Registry (RIR) that assigned the number to the AS.
|
|
83
|
+
rir: str | None = None
|
|
84
|
+
|
|
85
|
+
class Config:
|
|
86
|
+
json_schema_extra: dict[str, list[str]] = {
|
|
87
|
+
"id_contributing_properties": ["number"]
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
SCOs = Annotated[(Artifact | AutonomousSystem), Field(discriminator="type")]
|
stidantic/sdo.py
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
from typing import Literal, Annotated, Self
|
|
2
|
+
from datetime import datetime
|
|
3
|
+
from pydantic import Field
|
|
4
|
+
from pydantic.functional_validators import model_validator
|
|
5
|
+
from stidantic.types import StixDomain, KillChainPhase
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
# 4.1 Attack Pattern
|
|
9
|
+
class AttackPattern(StixDomain):
|
|
10
|
+
"""
|
|
11
|
+
Attack Patterns are a type of TTP that describe ways that adversaries attempt to compromise targets.
|
|
12
|
+
|
|
13
|
+
Attack Patterns are used to help categorize attacks, generalize specific attacks to the patterns that they follow,
|
|
14
|
+
and provide detailed information about how attacks are performed. An example of an attack pattern is
|
|
15
|
+
"spear phishing": a common type of attack where an attacker sends a carefully crafted e-mail message
|
|
16
|
+
to a party with the intent of getting them to click a link or open an attachment to deliver malware.
|
|
17
|
+
|
|
18
|
+
Attack Patterns can also be more specific; spear phishing as practiced by a particular threat actor
|
|
19
|
+
(e.g., they might generally say that the target won a contest) can also be an Attack Pattern.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
type: Literal["attack-pattern"] = "attack-pattern" # pyright: ignore[reportIncompatibleVariableOverride]
|
|
23
|
+
# The name used to identify the Attack Pattern.
|
|
24
|
+
name: str
|
|
25
|
+
# A description that provides more details and context about the Attack Pattern,
|
|
26
|
+
# potentially including its purpose and its key characteristics.
|
|
27
|
+
description: str | None = None
|
|
28
|
+
# Alternative names used to identify this Attack Pattern.
|
|
29
|
+
aliases: list[str] | None = None
|
|
30
|
+
# The list of kill chain phases for which this attack pattern is used.
|
|
31
|
+
kill_chain_phases: list[KillChainPhase] | None = None
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
# 4.2 Campaign
|
|
35
|
+
class Campaign(StixDomain):
|
|
36
|
+
"""
|
|
37
|
+
A Campaign is a grouping of adversarial behaviors that describes a set of malicious activities or attacks
|
|
38
|
+
(sometimes called waves) that occur over a period of time against a specific set of targets.
|
|
39
|
+
Campaigns usually have well defined objectives and may be part of an Intrusion Set.
|
|
40
|
+
|
|
41
|
+
Campaigns are often attributed to an intrusion set and threat actors. The threat actors may reuse known
|
|
42
|
+
infrastructure from the intrusion set or may set up new infrastructure specific for conducting that campaign.
|
|
43
|
+
|
|
44
|
+
Campaigns can be characterized by their objectives and the incidents they cause, people or resources they target,
|
|
45
|
+
and the resources (infrastructure, intelligence, Malware, Tools, etc.) they use.
|
|
46
|
+
|
|
47
|
+
For example, a Campaign could be used to describe a crime syndicate's attack using a specific variant of
|
|
48
|
+
malware and new C2 servers against the executives of ACME Bank during the summer of 2016 in order
|
|
49
|
+
to gain secret information about an upcoming merger with another bank.
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
type: Literal["campaign"] = "campaign" # pyright: ignore[reportIncompatibleVariableOverride]
|
|
53
|
+
# A name used to identify the Campaign.
|
|
54
|
+
name: str
|
|
55
|
+
# A description that provides more details and context about the Campaign,
|
|
56
|
+
# potentially including its purpose and its key characteristics.
|
|
57
|
+
description: str | None = None
|
|
58
|
+
# Alternative names used to identify this Campaign.
|
|
59
|
+
aliases: list[str] | None = None
|
|
60
|
+
# The time that this Campaign was first seen.
|
|
61
|
+
# A summary property of data from sightings and other data that may or may not be available in STIX.
|
|
62
|
+
# If new sightings are received that are earlier than the first seen timestamp,
|
|
63
|
+
# the object may be updated to account for the new data.
|
|
64
|
+
first_seen: datetime | None = None
|
|
65
|
+
# The time that this Campaign was last 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 later than the last seen timestamp,
|
|
68
|
+
# the object may be updated to account for the new data.
|
|
69
|
+
last_seen: datetime | None = None
|
|
70
|
+
# The Campaign’s primary goal, objective, desired outcome, or intended effect
|
|
71
|
+
# — what the Threat Actor or Intrusion Set hopes to accomplish with this Campaign.
|
|
72
|
+
objective: str | None = None
|
|
73
|
+
|
|
74
|
+
@model_validator(mode="after")
|
|
75
|
+
def validate_first_last_interval(self) -> Self:
|
|
76
|
+
"""
|
|
77
|
+
If this property and the first_seen property are both defined, then this property
|
|
78
|
+
MUST be greater than or equal to the timestamp in the first_seen property.
|
|
79
|
+
"""
|
|
80
|
+
if self.first_seen and self.last_seen and self.first_seen > self.last_seen:
|
|
81
|
+
raise ValueError(
|
|
82
|
+
"the last_seen property MUST be greater than or equal to the timestamp in the first_seen property"
|
|
83
|
+
)
|
|
84
|
+
return self
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
SDOs = Annotated[(AttackPattern | Campaign), Field(discriminator="type")]
|