@@ -40,7 +40,9 @@ def download_stix(stix_version: str, domain: str, download_dir: str, release: st
4040 pooch .retrieve (download_url , known_hash = known_hash , fname = fname , path = str (release_download_dir ))
4141
4242
43- def download_domains (domains : List [str ], download_dir : str , all_versions : bool , stix_version : str ):
43+ def download_domains (
44+ domains : List [str ], download_dir : str , all_versions : bool , stix_version : str , specific_versions : List [str ] = None
45+ ):
4446 """Download ATT&CK domains specified.
4547
4648 Parameters
@@ -53,6 +55,8 @@ def download_domains(domains: List[str], download_dir: str, all_versions: bool,
5355 Whether or not to download all versions of the domains.
5456 stix_version : str
5557 Version of STIX to download. Options are "2.0" or "2.1"
58+ specific_versions : List[str], optional
59+ List of specific versions to download. If provided, overrides all_versions behavior.
5660 """
5761 for domain in domains :
5862 if domain == "pre" and stix_version == "2.1" :
@@ -75,7 +79,22 @@ def download_domains(domains: List[str], download_dir: str, all_versions: bool,
7579 if stix_version == "2.0" :
7680 releases = stix_hash_data ["pre" ]
7781
78- if all_versions :
82+ if specific_versions :
83+ # Download specific versions
84+ logger .info (f"Downloading STIX { stix_version } specific versions for the { domain } domain to { download_dir } " )
85+ for version in specific_versions :
86+ if version in releases :
87+ known_hash = releases [version ]
88+ download_stix (
89+ stix_version = stix_version ,
90+ domain = domain ,
91+ download_dir = download_dir ,
92+ release = version ,
93+ known_hash = known_hash ,
94+ )
95+ else :
96+ logger .warning (f"Version { version } not available for { domain } domain in STIX { stix_version } " )
97+ elif all_versions :
7998 logger .info (f"Downloading STIX { stix_version } bundles for the { domain } domain to { download_dir } " )
8099 for release , known_hash in releases .items ():
81100 download_stix (
@@ -99,31 +118,108 @@ def download_domains(domains: List[str], download_dir: str, all_versions: bool,
99118 )
100119
101120
121+ def _validate_versions (versions : List [str ], stix20 : bool , stix21 : bool ):
122+ """Validate that specified versions exist in the available data.
123+
124+ Parameters
125+ ----------
126+ versions : List[str]
127+ List of versions to validate
128+ stix20 : bool
129+ Whether STIX 2.0 is being downloaded
130+ stix21 : bool
131+ Whether STIX 2.1 is being downloaded
132+ """
133+ domains = ["enterprise" , "mobile" , "ics" , "pre" ]
134+ invalid_versions = []
135+
136+ for version in versions :
137+ version_exists = False
138+
139+ # Check STIX 2.0 if enabled
140+ if stix20 :
141+ for domain in domains :
142+ if domain == "pre" :
143+ domain_releases = release_info .STIX20 .get ("pre" , {})
144+ else :
145+ domain_releases = release_info .STIX20 .get (domain , {})
146+
147+ if version in domain_releases :
148+ version_exists = True
149+ break
150+
151+ # Check STIX 2.1 if enabled
152+ if stix21 and not version_exists :
153+ for domain in domains :
154+ if domain == "pre" :
155+ # PRE domain not available in STIX 2.1
156+ continue
157+
158+ domain_releases = release_info .STIX21 .get (domain , {})
159+ if version in domain_releases :
160+ version_exists = True
161+ break
162+
163+ if not version_exists :
164+ invalid_versions .append (version )
165+
166+ if invalid_versions :
167+ logger .error (f"Invalid version(s): { ', ' .join (invalid_versions )} " )
168+ logger .info (f"Latest available version: { release_info .LATEST_VERSION } " )
169+ raise typer .Exit (code = 1 )
170+
171+
102172@app .command ()
103173def download_attack_stix (
104174 download_dir : str = typer .Option (
105175 "attack-releases" , "--download-dir" , "-d" , help = "Folder to save downloaded STIX data."
106176 ),
107- all_versions : bool = typer .Option (False , "--all" , "-a" , help = "Download all ATT&CK releases." ),
177+ all_versions : bool = typer .Option (
178+ False , "--all" , "-a" , help = "Download all ATT&CK releases. Mutually exclusive with --version."
179+ ),
180+ versions : List [str ] = typer .Option (
181+ None ,
182+ "--version" ,
183+ "-v" ,
184+ help = "Download specific ATT&CK version(s). Can be specified multiple times. Mutually exclusive with --all." ,
185+ ),
108186 stix20 : bool = typer .Option (True , help = "Download STIX 2.0 data." ),
109187 stix21 : bool = typer .Option (False , help = "Download STIX 2.1 data." ),
110188):
111189 """Download the ATT&CK STIX data from GitHub in JSON format.
112190
113191 By default, only the latest ATT&CK release will be downloaded in STIX 2.0 format.
192+ Use --version to specify particular versions, or --all to download all versions.
114193 """
194+ # Validate mutually exclusive options
195+ if all_versions and versions :
196+ logger .error ("Cannot specify both --all and --version. Use one or the other." )
197+ raise typer .Exit (code = 1 )
198+
199+ # Validate specified versions exist
200+ if versions :
201+ _validate_versions (versions , stix20 , stix21 )
202+
115203 domains = ["enterprise" , "mobile" , "ics" , "pre" ]
116204
117205 if stix20 :
118206 stix20_download_dir = f"{ download_dir } /stix-2.0"
119207 pathlib .Path (stix20_download_dir ).mkdir (parents = True , exist_ok = True )
120208 download_domains (
121- domains = domains , download_dir = stix20_download_dir , all_versions = all_versions , stix_version = "2.0"
209+ domains = domains ,
210+ download_dir = stix20_download_dir ,
211+ all_versions = all_versions ,
212+ stix_version = "2.0" ,
213+ specific_versions = versions ,
122214 )
123215
124216 if stix21 :
125217 stix21_download_dir = f"{ download_dir } /stix-2.1"
126218 pathlib .Path (stix21_download_dir ).mkdir (parents = True , exist_ok = True )
127219 download_domains (
128- domains = domains , download_dir = stix21_download_dir , all_versions = all_versions , stix_version = "2.1"
220+ domains = domains ,
221+ download_dir = stix21_download_dir ,
222+ all_versions = all_versions ,
223+ stix_version = "2.1" ,
224+ specific_versions = versions ,
129225 )
0 commit comments