99# https://github.com/oasis-open/cti-python-stix2/issues/183
1010# https://stackoverflow.com/a/4406521
1111
12- from stix2 import TAXIICollectionSource , Filter , CompositeDataSource , FileSystemSource
12+ from stix2 import TAXIICollectionSource , Filter , CompositeDataSource
1313from stix2 .datastore .filters import apply_common_filters
1414from stix2 .utils import get_type_from_id
1515from stix2 .v20 .sdo import (
2525import json
2626import os
2727
28- from .models import *
29- from pydantic import TypeAdapter
28+ from pydantic import TypeAdapter , ValidationError
3029from typing import List , Type , Dict , Any , Union
30+ from attackcti .models import *
31+ from attackcti .utils .storage import STIXStore
3132
3233# os.environ['http_proxy'] = "http://xxxxxxx"
3334# os.environ['https_proxy'] = "https://xxxxxxx"
3435
3536ATTACK_STIX_COLLECTIONS = "https://cti-taxii.mitre.org/stix/collections/"
3637ENTERPRISE_ATTACK = "95ecc380-afe9-11e4-9b6c-751b66dd541e"
37- PRE_ATTACK = "062767bd-02d2-4b72-84ba-56caef0f8658"
3838MOBILE_ATTACK = "2f669986-b40b-4423-b720-4396ca6a462b"
3939ICS_ATTACK = "02c3ef24-9cd4-48f3-a99f-b74ce24f1d34"
4040
41- ENTERPRISE_ATTACK_LOCAL_DIR = "enterprise-attack"
42- PRE_ATTACK_LOCAL_DIR = "pre-attack"
43- MOBILE_ATTACK_LOCAL_DIR = "mobile-attack"
44- ICS_ATTACK_LOCAL_DIR = "ics-attack"
45-
46- class attack_client (object ):
47- """A Python Module for ATT&CK"""
48- TC_ENTERPRISE_SOURCE = None
49- TC_PRE_SOURCE = None
50- TC_MOBILE_SOURCE = None
51- TC_ICS_SOURCE = None
52- COMPOSITE_DS = None
53-
41+ class attack_client :
42+ """A Python Module for accessing ATT&CK data locally or remotely."""
43+
5444 pydantic_model_mapping = {
5545 "techniques" : Technique ,
5646 "data-component" : DataComponent ,
@@ -74,37 +64,79 @@ class attack_client(object):
7464 "x-mitre-data-component" : DataComponent
7565 }
7666
77- def __init__ (self , local_path = None , include_pre_attack = False , proxies = None , verify = True ):
67+ def __init__ (self , local_paths = None , proxies = None , verify = True ):
7868 """
69+ Initializes the ATT&CK client, setting up local or remote data sources.
70+
7971 Args:
80- proxies - See https://requests.readthedocs.io/en/latest/user/advanced/#proxies
81- verify - See https://requests.readthedocs.io/en/latest/user/advanced/#ssl-cert-verification
72+ local_paths (dict, optional): Dictionary with paths to local directories or JSON files for each domain.
73+ Keys should be 'enterprise', 'mobile', and 'ics'.
74+ proxies (dict, optional): Dictionary mapping protocol or protocol and hostname to the URL of the proxy.
75+ verify (bool, optional): Whether to verify SSL certificates. Defaults to True.
8276 """
77+ self .COMPOSITE_DS = CompositeDataSource ()
8378
84- if local_path is not None and os .path .isdir (os .path .join (local_path , ENTERPRISE_ATTACK_LOCAL_DIR )) \
85- and os .path .isdir (os .path .join (local_path , PRE_ATTACK_LOCAL_DIR )) \
86- and os .path .isdir (os .path .join (local_path , MOBILE_ATTACK_LOCAL_DIR )) \
87- and os .path .isdir (os .path .join (local_path , ICS_ATTACK_LOCAL_DIR )):
88- self .TC_ENTERPRISE_SOURCE = FileSystemSource (os .path .join (local_path , ENTERPRISE_ATTACK_LOCAL_DIR ))
89- self .TC_PRE_SOURCE = FileSystemSource (os .path .join (local_path , PRE_ATTACK_LOCAL_DIR ))
90- self .TC_MOBILE_SOURCE = FileSystemSource (os .path .join (local_path , MOBILE_ATTACK_LOCAL_DIR ))
91- self .TC_ICS_SOURCE = FileSystemSource (os .path .join (local_path , ICS_ATTACK_LOCAL_DIR ))
92- else :
93- ENTERPRISE_COLLECTION = Collection (ATTACK_STIX_COLLECTIONS + ENTERPRISE_ATTACK + "/" , verify = verify , proxies = proxies )
94- PRE_COLLECTION = Collection (ATTACK_STIX_COLLECTIONS + PRE_ATTACK + "/" , verify = verify , proxies = proxies )
95- MOBILE_COLLECTION = Collection (ATTACK_STIX_COLLECTIONS + MOBILE_ATTACK + "/" , verify = verify , proxies = proxies )
96- ICS_COLLECTION = Collection (ATTACK_STIX_COLLECTIONS + ICS_ATTACK + "/" , verify = verify , proxies = proxies )
79+ # Validate local_paths with Pydantic
80+ if local_paths :
81+ try :
82+ self .local_paths = STIXLocalPaths (** local_paths )
83+ except ValidationError as e :
84+ raise ValueError (f"Invalid local_paths: { e } " )
9785
98- self .TC_ENTERPRISE_SOURCE = TAXIICollectionSource (ENTERPRISE_COLLECTION )
99- self .TC_PRE_SOURCE = TAXIICollectionSource (PRE_COLLECTION )
100- self .TC_MOBILE_SOURCE = TAXIICollectionSource (MOBILE_COLLECTION )
101- self .TC_ICS_SOURCE = TAXIICollectionSource (ICS_COLLECTION )
86+ # Initialize data sources
87+ self .init_data_sources (self .local_paths if local_paths else None , proxies , verify )
88+
89+ def init_data_sources (self , local_paths , proxies , verify ):
90+ """
91+ Initializes data sources, either local or remote.
92+
93+ Args:
94+ local_paths (LocalPathsModel, optional): Validated dictionary with paths to local directories or JSON files for each domain.
95+ proxies (dict, optional): Dictionary mapping protocol or protocol and hostname to the URL of the proxy.
96+ verify (bool, optional): Whether to verify SSL certificates. Defaults to True.
97+ """
98+ if local_paths :
99+ self .TC_ENTERPRISE_SOURCE = self .load_stix_store (local_paths .enterprise )
100+ self .TC_MOBILE_SOURCE = self .load_stix_store (local_paths .mobile )
101+ self .TC_ICS_SOURCE = self .load_stix_store (local_paths .ics )
102+
103+ if not (self .TC_ENTERPRISE_SOURCE and self .TC_MOBILE_SOURCE and self .TC_ICS_SOURCE ):
104+ self .initialize_taxii_sources (proxies , verify )
105+ else :
106+ self .initialize_taxii_sources (proxies , verify )
102107
103- self .COMPOSITE_DS = CompositeDataSource ()
104108 self .COMPOSITE_DS .add_data_sources ([self .TC_ENTERPRISE_SOURCE , self .TC_MOBILE_SOURCE , self .TC_ICS_SOURCE ])
105109
106- if include_pre_attack :
107- self .COMPOSITE_DS .add_data_sources ([self .TC_PRE_SOURCE ])
110+ def load_stix_store (self , path ):
111+ """
112+ Loads a STIXStore from the given path.
113+
114+ Args:
115+ path (str): Path to the source directory or JSON file.
116+
117+ Returns:
118+ The loaded STIXStore or None if the path is invalid.
119+ """
120+ if path and os .path .exists (path ):
121+ store = STIXStore (path )
122+ return store .get_store ()
123+ return None
124+
125+ def initialize_taxii_sources (self , proxies , verify ):
126+ """
127+ Initializes data sources from the ATT&CK TAXII server.
128+
129+ Args:
130+ proxies (dict, optional): Dictionary mapping protocol or protocol and hostname to the URL of the proxy.
131+ verify (bool, optional): Whether to verify SSL certificates. Defaults to True.
132+ """
133+ ENTERPRISE_COLLECTION = Collection (ATTACK_STIX_COLLECTIONS + ENTERPRISE_ATTACK + "/" , verify = verify , proxies = proxies )
134+ MOBILE_COLLECTION = Collection (ATTACK_STIX_COLLECTIONS + MOBILE_ATTACK + "/" , verify = verify , proxies = proxies )
135+ ICS_COLLECTION = Collection (ATTACK_STIX_COLLECTIONS + ICS_ATTACK + "/" , verify = verify , proxies = proxies )
136+
137+ self .TC_ENTERPRISE_SOURCE = TAXIICollectionSource (ENTERPRISE_COLLECTION )
138+ self .TC_MOBILE_SOURCE = TAXIICollectionSource (MOBILE_COLLECTION )
139+ self .TC_ICS_SOURCE = TAXIICollectionSource (ICS_COLLECTION )
108140
109141 def get_stix_objects (
110142 self ,
0 commit comments