stix2-utils 0.0.2__tar.gz

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.
@@ -0,0 +1,33 @@
1
+ Metadata-Version: 2.3
2
+ Name: stix2-utils
3
+ Version: 0.0.2
4
+ Summary: Utility library for validating stix2 entities and patterns
5
+ Author: Daan Willems
6
+ Author-email: Daan Willems <daan@daan.nl>
7
+ Requires-Dist: pydantic
8
+ Requires-Dist: pytest ; extra == 'dev'
9
+ Requires-Python: >=3.13
10
+ Project-URL: Homepage, https://github.com/DaanWillems/stix2-utils
11
+ Provides-Extra: dev
12
+ Description-Content-Type: text/markdown
13
+
14
+ # stix2-utils
15
+ A utility library for validating STIX 2.1 objects. This library used pydantic to validate STIX 2.1 objects. Later on it will also support creating objects and validating indicator patterns.
16
+
17
+ ## Installation
18
+ The stix2-utils library is hosted on PyPi. The most recent version can be installed with pip:
19
+
20
+ ```bash
21
+ pip install stix2-utils
22
+ ```
23
+
24
+ ## Usage
25
+ You can use the library to validate STIX 2.1 objects and receive human readable errors
26
+
27
+ ```python
28
+ validator = STIX2Validator()
29
+ validation_result = validator.validate_entity(stix_obj)
30
+
31
+ print(validation_result.success)
32
+ print(validation_result.errors)
33
+ ```
@@ -0,0 +1,20 @@
1
+ # stix2-utils
2
+ A utility library for validating STIX 2.1 objects. This library used pydantic to validate STIX 2.1 objects. Later on it will also support creating objects and validating indicator patterns.
3
+
4
+ ## Installation
5
+ The stix2-utils library is hosted on PyPi. The most recent version can be installed with pip:
6
+
7
+ ```bash
8
+ pip install stix2-utils
9
+ ```
10
+
11
+ ## Usage
12
+ You can use the library to validate STIX 2.1 objects and receive human readable errors
13
+
14
+ ```python
15
+ validator = STIX2Validator()
16
+ validation_result = validator.validate_entity(stix_obj)
17
+
18
+ print(validation_result.success)
19
+ print(validation_result.errors)
20
+ ```
@@ -0,0 +1,76 @@
1
+ [build-system]
2
+ requires = ["uv_build >= 0.11.23, <0.12.0"]
3
+ build-backend = "uv_build"
4
+
5
+ [project]
6
+ name = "stix2-utils"
7
+ version = "0.0.2"
8
+ requires-python = ">= 3.13"
9
+ description = "Utility library for validating stix2 entities and patterns"
10
+ readme = "README.md"
11
+ authors = [
12
+ { name = "Daan Willems", email = "daan@daan.nl" }
13
+ ]
14
+ dependencies = [
15
+ "pydantic"
16
+ ]
17
+ [project.optional-dependencies]
18
+ dev = [
19
+ "pytest"
20
+ ]
21
+
22
+ [project.urls]
23
+ Homepage = "https://github.com/DaanWillems/stix2-utils"
24
+
25
+ [tool.uv.build-backend]
26
+ module-root = ""
27
+ module-name = "stix2utils"
28
+
29
+ [tool.coverage.run]
30
+ omit = [
31
+ ]
32
+
33
+ [tool.coverage.report]
34
+ skip_empty = true
35
+
36
+ [tool.semantic_release.branches.main]
37
+ match = "(main|master)"
38
+ prerelease = "false"
39
+
40
+ [tool.setuptools.packages]
41
+ find = { }
42
+
43
+ [tool.pytest.ini_options]
44
+ pythonpath = "."
45
+ env = [
46
+ "ENV = test",
47
+ ]
48
+
49
+ [[tool.mypy.overrides]]
50
+ module = "tests.*"
51
+ ignore_errors = true
52
+
53
+ [tool.ruff]
54
+ exclude = [
55
+ ]
56
+ line-length = 140
57
+ indent-width = 4
58
+
59
+ [tool.ruff.format]
60
+ line-ending = "lf"
61
+
62
+ [tool.ruff.lint.per-file-ignores]
63
+ "tests/fixtures/**.py" = ["PT022"] # Inversed teardown, ignore for this specific case only
64
+ "tests/*" = [
65
+ "ANN", # Non annotations are fine in tests.
66
+ "S101", # We actually want 'assert' in tests.
67
+ "S106", # Dummy strings as passswords are fine in tests.
68
+ ]
69
+
70
+
71
+ [tool.ruff.lint]
72
+ ignore = ["ANN002", "ANN003", "FIX002", "COM812", "UP040"]
73
+ select = ["E", "F", "W", "ANN", "I", "N", "UP", "YTT", "S", "BLE", "FBT", "B", "A", "COM", "C4", "T10", "EM", "EXE", "ISC", "ICN", "LOG", "G", "INP", "PIE", "T20", "PYI", "PT", "RSE", "RET", "SLF", "SIM", "TID", "TCH", "INT", "ARG", "PTH", "TD", "FIX", "ERA", "R", "TRY", "FLY", "PERF", "FURB", "RUF"]
74
+
75
+ [tool.ruff.lint.flake8-type-checking]
76
+ runtime-evaluated-base-classes = ["pydantic.BaseModel"]
@@ -0,0 +1,85 @@
1
+ from stix2utils.sco_models import (
2
+ URL,
3
+ Artifact,
4
+ AutonomousSystem,
5
+ Directory,
6
+ DomainName,
7
+ EmailAddress,
8
+ EmailMessage,
9
+ File,
10
+ IPv4Address,
11
+ IPv6Address,
12
+ MACAddress,
13
+ Mutex,
14
+ NetworkTraffic,
15
+ Process,
16
+ Software,
17
+ UserAccount,
18
+ WindowsRegistryKey,
19
+ X509Certificate,
20
+ )
21
+ from stix2utils.sdo_models import (
22
+ AttackPattern,
23
+ Campaign,
24
+ CourseOfAction,
25
+ Grouping,
26
+ Identity,
27
+ Indicator,
28
+ Infrastructure,
29
+ IntrusionSet,
30
+ Location,
31
+ Malware,
32
+ MalwareAnalysis,
33
+ Note,
34
+ ObservedData,
35
+ Opinion,
36
+ Report,
37
+ ThreatActor,
38
+ Tool,
39
+ Vulnerability,
40
+ )
41
+ from stix2utils.sro_models import Relationship, Sighting
42
+ from stix2utils.validator import STIX2ValidationResult, STIX2Validator
43
+
44
+ __all__ = [
45
+ STIX2Validator,
46
+ STIX2ValidationResult,
47
+ AttackPattern,
48
+ Campaign,
49
+ CourseOfAction,
50
+ Grouping,
51
+ Identity,
52
+ Indicator,
53
+ Infrastructure,
54
+ IntrusionSet,
55
+ Location,
56
+ Malware,
57
+ MalwareAnalysis,
58
+ Note,
59
+ ObservedData,
60
+ Opinion,
61
+ Report,
62
+ ThreatActor,
63
+ Tool,
64
+ Vulnerability,
65
+ Artifact,
66
+ AutonomousSystem,
67
+ Directory,
68
+ DomainName,
69
+ EmailAddress,
70
+ EmailMessage,
71
+ File,
72
+ IPv4Address,
73
+ IPv6Address,
74
+ MACAddress,
75
+ Mutex,
76
+ NetworkTraffic,
77
+ Process,
78
+ Software,
79
+ URL,
80
+ UserAccount,
81
+ WindowsRegistryKey,
82
+ X509Certificate,
83
+ Relationship,
84
+ Sighting,
85
+ ]
@@ -0,0 +1,89 @@
1
+ from typing import Annotated
2
+
3
+ from pydantic import Field, TypeAdapter
4
+
5
+ from stix2utils.sco_models import (
6
+ URL,
7
+ Artifact,
8
+ AutonomousSystem,
9
+ Directory,
10
+ DomainName,
11
+ EmailAddress,
12
+ EmailMessage,
13
+ File,
14
+ IPv4Address,
15
+ IPv6Address,
16
+ MACAddress,
17
+ Mutex,
18
+ NetworkTraffic,
19
+ Process,
20
+ Software,
21
+ UserAccount,
22
+ WindowsRegistryKey,
23
+ X509Certificate,
24
+ )
25
+ from stix2utils.sdo_models import (
26
+ AttackPattern,
27
+ Campaign,
28
+ CourseOfAction,
29
+ Grouping,
30
+ Identity,
31
+ Indicator,
32
+ Infrastructure,
33
+ IntrusionSet,
34
+ Location,
35
+ Malware,
36
+ MalwareAnalysis,
37
+ Note,
38
+ ObservedData,
39
+ Opinion,
40
+ Report,
41
+ ThreatActor,
42
+ Tool,
43
+ Vulnerability,
44
+ )
45
+ from stix2utils.sro_models import Relationship, Sighting
46
+
47
+ SDO_SCO_SRO = Annotated[
48
+ AttackPattern
49
+ | Campaign
50
+ | CourseOfAction
51
+ | Grouping
52
+ | Identity
53
+ | Indicator
54
+ | Infrastructure
55
+ | IntrusionSet
56
+ | Location
57
+ | Malware
58
+ | MalwareAnalysis
59
+ | Note
60
+ | ObservedData
61
+ | Opinion
62
+ | Report
63
+ | ThreatActor
64
+ | Tool
65
+ | Vulnerability
66
+ | Artifact
67
+ | AutonomousSystem
68
+ | Directory
69
+ | DomainName
70
+ | EmailAddress
71
+ | EmailMessage
72
+ | File
73
+ | IPv4Address
74
+ | IPv6Address
75
+ | MACAddress
76
+ | Mutex
77
+ | NetworkTraffic
78
+ | Process
79
+ | Software
80
+ | URL
81
+ | UserAccount
82
+ | WindowsRegistryKey
83
+ | X509Certificate
84
+ | Relationship
85
+ | Sighting,
86
+ Field(discriminator="type"),
87
+ ]
88
+
89
+ stix_adapter = TypeAdapter(SDO_SCO_SRO)
@@ -0,0 +1,87 @@
1
+ from enum import Enum
2
+ from typing import Annotated
3
+
4
+ from pydantic import Field
5
+
6
+ # ---------------------------------------------------------------------------
7
+ # Common base
8
+ # ---------------------------------------------------------------------------
9
+
10
+ _TIMESTAMP_RE = (
11
+ r"^\d{4}-\d{2}-\d{2}" # YYYY-MM-DD
12
+ r"T\d{2}:\d{2}:\d{2}" # THH:mm:ss
13
+ r"(?:\.\d+)?" # optional .s+ (dot only if digits follow)
14
+ r"Z$" # mandatory UTC designator
15
+ )
16
+
17
+ Timestamp = Annotated[str, Field(pattern=_TIMESTAMP_RE)]
18
+
19
+ # hex type: even number of lowercase hex characters
20
+ Hex = Annotated[str, Field(pattern=r"^(?:[0-9a-f]{2})+$")]
21
+
22
+ # A hashes dictionary: keys are algorithm names, values are hash strings.
23
+ # Keys should come from hash-algorithm-ov, dictionary keys 3-250 ASCII chars.
24
+ Hashes = Annotated[dict[str, str], Field(min_length=1)]
25
+
26
+
27
+ class HashAlgorithm(str, Enum):
28
+ MD5 = "MD5"
29
+ SHA_1 = "SHA-1"
30
+ SHA_256 = "SHA-256"
31
+ SHA_512 = "SHA-512"
32
+ SHA3_256 = "SHA3-256"
33
+ SHA3_512 = "SHA3-512"
34
+ SSDEEP = "SSDEEP"
35
+ TLSH = "TLSH"
36
+
37
+
38
+ # ---------------------------------------------------------------------------
39
+ # STIX object type universe
40
+ # ---------------------------------------------------------------------------
41
+
42
+ SDO_TYPES: frozenset[str] = frozenset(
43
+ {
44
+ "attack-pattern",
45
+ "campaign",
46
+ "course-of-action",
47
+ "grouping",
48
+ "identity",
49
+ "incident",
50
+ "indicator",
51
+ "infrastructure",
52
+ "intrusion-set",
53
+ "location",
54
+ "malware",
55
+ "malware-analysis",
56
+ "note",
57
+ "observed-data",
58
+ "opinion",
59
+ "report",
60
+ "threat-actor",
61
+ "tool",
62
+ "vulnerability",
63
+ }
64
+ )
65
+
66
+ SCO_TYPES: frozenset[str] = frozenset(
67
+ {
68
+ "artifact",
69
+ "autonomous-system",
70
+ "directory",
71
+ "domain-name",
72
+ "email-addr",
73
+ "email-message",
74
+ "file",
75
+ "ipv4-addr",
76
+ "ipv6-addr",
77
+ "mac-addr",
78
+ "mutex",
79
+ "network-traffic",
80
+ "process",
81
+ "software",
82
+ "url",
83
+ "user-account",
84
+ "windows-registry-key",
85
+ "x509-certificate",
86
+ }
87
+ )
@@ -0,0 +1,47 @@
1
+ from typing import Annotated, TypeAlias
2
+
3
+ from pydantic import Field
4
+
5
+ _UUID = r"[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"
6
+
7
+
8
+ def _pattern(*types: str) -> str:
9
+ """Build the id-regex for one or more STIX id prefixes."""
10
+ prefix = types[0] if len(types) == 1 else "(?:" + "|".join(types) + ")"
11
+ return rf"^{prefix}--{_UUID}$"
12
+
13
+
14
+ # Single-type references
15
+ IdentityRef: TypeAlias = Annotated[str, Field(pattern=_pattern("identity"))]
16
+ MarkingRef: TypeAlias = Annotated[str, Field(pattern=_pattern("marking-definition"))]
17
+ SoftwareRef: TypeAlias = Annotated[str, Field(pattern=_pattern("software"))]
18
+ ObservedDataRef: TypeAlias = Annotated[str, Field(pattern=_pattern("observed-data"))]
19
+
20
+ # Multi-type references
21
+ SampleRef: TypeAlias = Annotated[str, Field(pattern=_pattern("file", "artifact"))] # malware / malware-analysis samples
22
+ IdentityOrLocationRef: TypeAlias = Annotated[str, Field(pattern=_pattern("identity", "location"))] # malware / malware-analysis samples
23
+ InstalledSoftwareRef: TypeAlias = Annotated[str, Field(pattern=_pattern("software"))]
24
+
25
+ # Open reference: any valid STIX object id (SDO / SCO / SRO / SMO)
26
+ AnyRef: TypeAlias = Annotated[str, Field(pattern=_pattern("[a-z0-9-]+"))]
27
+
28
+
29
+ # Single-type references
30
+ ArtifactRef: TypeAlias = Annotated[str, Field(pattern=_pattern("artifact"))]
31
+ AutonomousSystemRef: TypeAlias = Annotated[str, Field(pattern=_pattern("autonomous-system"))]
32
+ DirectoryRef: TypeAlias = Annotated[str, Field(pattern=_pattern("directory"))]
33
+ FileRef: TypeAlias = Annotated[str, Field(pattern=_pattern("file"))]
34
+ MACAddrRef: TypeAlias = Annotated[str, Field(pattern=_pattern("mac-addr"))]
35
+ NetworkTrafficRef: TypeAlias = Annotated[str, Field(pattern=_pattern("network-traffic"))]
36
+ ProcessRef: TypeAlias = Annotated[str, Field(pattern=_pattern("process"))]
37
+ UserAccountRef: TypeAlias = Annotated[str, Field(pattern=_pattern("user-account"))]
38
+
39
+ # Multi-type references
40
+ DirectoryContainsRef: TypeAlias = Annotated[str, Field(pattern=_pattern("file", "directory"))]
41
+ DomainResolvesToRef: TypeAlias = Annotated[str, Field(pattern=_pattern("ipv4-addr", "ipv6-addr", "domain-name"))]
42
+ IPResolvesToRef: TypeAlias = Annotated[str, Field(pattern=_pattern("mac-addr"))]
43
+ IPBelongsToRef: TypeAlias = Annotated[str, Field(pattern=_pattern("autonomous-system"))]
44
+ EmailAddrRef: TypeAlias = Annotated[str, Field(pattern=_pattern("email-addr"))]
45
+ EmailFromRef: TypeAlias = Annotated[str, Field(pattern=_pattern("email-addr"))]
46
+ MimeBodyRawRef: TypeAlias = Annotated[str, Field(pattern=_pattern("artifact", "file"))]
47
+ NetworkSrcDstRef: TypeAlias = Annotated[str, Field(pattern=_pattern("ipv4-addr", "ipv6-addr", "mac-addr", "domain-name"))]