Skip to content

Commit 2c0867d

Browse files
authored
Merge pull request #217 from mitre-attack/ds-an-lg-relationships
detection strategies, analytics and log sources relationships in Excel
2 parents a8dd03b + 0c56988 commit 2c0867d

2 files changed

Lines changed: 100 additions & 10 deletions

File tree

mitreattack/attackToExcel/attackToExcel.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,24 @@ def build_dataframes(src: MemoryStore, domain: str) -> Dict:
147147
return df
148148

149149

150+
def build_ds_an_lg_relationships(dataframes: Dict) -> Dict[str, pd.DataFrame]:
151+
"""Build sheets for ds-an-lg.xlsx using existing relationship tables."""
152+
# Use existing DetectionStrategy -> Analytics relationship table
153+
ds_an = dataframes["detectionstrategies"].get("detectionstrategies-analytic", pd.DataFrame())
154+
155+
# Use existing Analytics -> LogSource relationship table
156+
an_lg = dataframes["analytics"].get("analytic-logsource", pd.DataFrame())
157+
158+
# Use existing Analytics -> Detection Strategy relationship table
159+
an_ds = dataframes["analytics"].get("analytic-detectionstrategy", pd.DataFrame())
160+
161+
return {
162+
"detectionstrategy_to_analytics": ds_an,
163+
"analytics_to_logsources": an_lg,
164+
"analytics_to_detectionstrategy": an_ds,
165+
}
166+
167+
150168
def write_excel(dataframes: Dict, domain: str, version: Optional[str] = None, output_dir: str = ".") -> List:
151169
"""Given a set of dataframes from build_dataframes, write the ATT&CK dataset to output directory.
152170
@@ -292,6 +310,18 @@ def write_excel(dataframes: Dict, domain: str, version: Optional[str] = None, ou
292310
)
293311

294312
written_files.append(master_fp)
313+
314+
# Write Detection strategy - Analytics - Log sources file
315+
ds_an_lg_frames = build_ds_an_lg_relationships(dataframes)
316+
ds_an_lg_fp = os.path.join(output_directory, f"{domain_version_string}-detectionstrategy-analytic-logsources.xlsx")
317+
318+
with pd.ExcelWriter(ds_an_lg_fp) as rel_writer:
319+
for sheet_name, df in ds_an_lg_frames.items():
320+
if not df.empty:
321+
df.to_excel(rel_writer, sheet_name=sheet_name, index=False)
322+
323+
written_files.append(ds_an_lg_fp)
324+
295325
for thefile in written_files:
296326
logger.info(f"Excel file created: {thefile}")
297327
return written_files

mitreattack/attackToExcel/stixToDf.py

Lines changed: 70 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -367,16 +367,60 @@ def analyticsToDf(src):
367367
analytics = src.query([Filter("type", "=", "x-mitre-analytic")])
368368
analytics = remove_revoked_deprecated(analytics)
369369

370+
# Detection strategies (needed for analytics to detection strategies relationship)
371+
detection_strategies = src.query([Filter("type", "=", "x-mitre-detection-strategy")])
372+
detection_strategies = remove_revoked_deprecated(detection_strategies)
373+
370374
dataframes = {}
371375
if analytics:
372376
analytic_rows = []
377+
logsource_rows = []
378+
analytic_to_ds_rows = []
379+
380+
# analytics to detection strategies
381+
analytic_to_ds_map = {}
382+
for ds in detection_strategies:
383+
for analytic_id in ds.get("x_mitre_analytic_refs", []):
384+
analytic_to_ds_map.setdefault(analytic_id, []).append(
385+
{"detection_strategy_id": ds["id"], "detection_strategy_name": ds.get("name", "")}
386+
)
387+
373388
for analytic in tqdm(analytics, desc="parsing analytics"):
374389
analytic_rows.append(parseBaseStix(analytic))
375390

391+
# log-source relationship table rows
392+
for logsrc in analytic.get("x_mitre_log_source_references", []):
393+
data_comp_id = logsrc.get("x_mitre_data_component_ref", "")
394+
data_comp = src.get(data_comp_id)
395+
data_comp_name = data_comp.get("name", "") if data_comp else ""
396+
397+
logsource_rows.append(
398+
{
399+
"analytic_id": analytic["id"],
400+
"analytic_name": analytic["external_references"][0]["external_id"],
401+
"data_component_id": data_comp_id,
402+
"data_component_name": data_comp_name,
403+
"log_source_name": logsrc.get("name", ""),
404+
"channel": logsrc.get("channel", ""),
405+
}
406+
)
407+
408+
# detection strategies relationship table rows
409+
for ds_info in analytic_to_ds_map.get(analytic["id"], []):
410+
analytic_to_ds_rows.append(
411+
{
412+
"analytic_id": analytic["id"],
413+
"analytic_name": analytic["external_references"][0]["external_id"],
414+
"detection_strategy_id": ds_info["detection_strategy_id"],
415+
"detection_strategy_name": ds_info["detection_strategy_name"],
416+
}
417+
)
418+
419+
dataframes["analytics"] = pd.DataFrame(analytic_rows).sort_values("name")
420+
dataframes["analytic-logsource"] = pd.DataFrame(logsource_rows)
421+
dataframes["analytic-detectionstrategy"] = pd.DataFrame(analytic_to_ds_rows)
422+
376423
citations = get_citations(analytics)
377-
dataframes = {
378-
"analytics": pd.DataFrame(analytic_rows).sort_values("name"),
379-
}
380424
if not citations.empty:
381425
dataframes["citations"] = citations.sort_values("reference")
382426

@@ -398,20 +442,36 @@ def detectionstrategiesToDf(src):
398442
dataframes = {}
399443
if detection_strategies:
400444
detection_strategy_rows = []
445+
rel_rows = []
401446
for detection_strategy in tqdm(detection_strategies, desc="parsing detection strategies"):
402-
detection_strategy_rows.append(parseBaseStix(detection_strategy))
447+
row = parseBaseStix(detection_strategy)
448+
row["analytic_refs"] = "; ".join(detection_strategy.get("x_mitre_analytic_refs", []))
449+
detection_strategy_rows.append(row)
450+
451+
# analytics relationship table rows
452+
for analytic_id in detection_strategy.get("x_mitre_analytic_refs", []):
453+
analytic_obj = src.get(analytic_id)
454+
455+
rel_rows.append(
456+
{
457+
"detection_strategy_id": detection_strategy["id"],
458+
"detection_strategy_name": detection_strategy.get("name", ""),
459+
"analytic_id": analytic_id,
460+
"analytic_name": analytic_obj["external_references"][0]["external_id"],
461+
}
462+
)
463+
464+
# Build main dataframes
465+
dataframes["detectionstrategies"] = pd.DataFrame(detection_strategy_rows).sort_values("name")
466+
467+
dataframes["detectionstrategies-analytic"] = pd.DataFrame(rel_rows)
403468

404469
citations = get_citations(detection_strategies)
405-
dataframes = {
406-
"detectionstrategies": pd.DataFrame(detection_strategy_rows).sort_values("name"),
407-
}
408470
if not citations.empty:
409-
if "citations" in dataframes: # append to existing citations from references
410-
dataframes["citations"] = citations.sort_values("reference")
471+
dataframes["citations"] = citations.sort_values("reference")
411472

412473
else:
413474
logger.warning("No detection strategies found - nothing to parse")
414-
415475
return dataframes
416476

417477

0 commit comments

Comments
 (0)