Skip to content

Commit

Permalink
Development: CLI Validator (#10)
Browse files Browse the repository at this point in the history
* added additional property, code cleanup

* created validator to verify STIX bundles

* updating poetry packages

* Patched unintended behavior that affected creating relationships for data source, data component pairs

* updating .gitignore, CSVs for mappings & mappings

* Addressed various changes

Defined conditions for a STIX Bundle to be considered valid;
Suppressed stack trace to throw more useful errors;
Changed how timestamps are validated so non-valid string templates throw proper errors;
Added validator to Action workflow

* fixed location of validation action

* Removed unneeded package from poetry lock file.

* Added dateutil to poetry

* Changed import statement for dateutil

* Changed validator command
  • Loading branch information
tleef42 authored Nov 7, 2023
1 parent 6e0968a commit b1a1739
Show file tree
Hide file tree
Showing 19 changed files with 9,999 additions and 17,930 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/sphinx.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ jobs:
run: echo "$HOME/.poetry/bin" >> $GITHUB_PATH
- name: Install dependencies
run: poetry install
- name: Validate STIX bundles
run: poetry run python src/util/cli_validator.py
- name: Build HTML docs
run: poetry run make docs-ci
- name: Upload HTML docs
Expand Down
3 changes: 1 addition & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,4 @@ docs/_build/
*.tmp
TODO*
.DS_Store
.vscode/
mappings/input/enterprise/csv/*.csv
.vscode/

Large diffs are not rendered by default.

204 changes: 204 additions & 0 deletions mappings/input/enterprise/csv/OSQuery-sensors-mappings-enterprise.csv

Large diffs are not rendered by default.

227 changes: 227 additions & 0 deletions mappings/input/enterprise/csv/Zeek-sensors-mappings-enterprise.csv

Large diffs are not rendered by default.

2,434 changes: 570 additions & 1,864 deletions mappings/stix/enterprise/Auditd-mappings-enterprise.json

Large diffs are not rendered by default.

3,351 changes: 921 additions & 2,430 deletions mappings/stix/enterprise/CloudTrail-mappings-enterprise.json

Large diffs are not rendered by default.

3,946 changes: 1,027 additions & 2,919 deletions mappings/stix/enterprise/OSQuery-mappings-enterprise.json

Large diffs are not rendered by default.

9,287 changes: 4,672 additions & 4,615 deletions mappings/stix/enterprise/Reference-for-mappings-enterprise.json

Large diffs are not rendered by default.

783 changes: 185 additions & 598 deletions mappings/stix/enterprise/Sysmon-mappings-enterprise.json

Large diffs are not rendered by default.

3,175 changes: 870 additions & 2,305 deletions mappings/stix/enterprise/WinEvtx-mappings-enterprise.json

Large diffs are not rendered by default.

3,875 changes: 736 additions & 3,139 deletions mappings/stix/enterprise/Zeek-mappings-enterprise.json

Large diffs are not rendered by default.

82 changes: 38 additions & 44 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ pandas = "2.0.0"
tqdm = "^4.66.1"
openpyxl = "^3.1.2"
requests = "^2.31.0"
schema = "^0.7.5"
python-dateutil = "^2.8.2"

[tool.poetry.group.dev.dependencies]
sphinx = "^4.5.0"
Expand Down
28 changes: 17 additions & 11 deletions src/parse/generate_stix.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import requests
from tqdm import tqdm
import pandas as pd
from stix2.properties import StringProperty, ReferenceProperty, EnumProperty, ListProperty
from stix2.properties import StringProperty, ReferenceProperty, EnumProperty, ListProperty, BooleanProperty
from stix2.v21 import Bundle, CustomObject, ExternalReference, Relationship


Expand All @@ -23,14 +23,16 @@
("external_references", ListProperty(ExternalReference)),
("x_mitre_version", StringProperty()),
("x_mitre_attack_spec_version", StringProperty()),
("x_mitre_modified_by_ref", ReferenceProperty(valid_types=['identity']))
("x_mitre_modified_by_ref", ReferenceProperty(valid_types=['identity'])),
("x_mitre_deprecated", BooleanProperty())
]
)


class DataSource():
"""Custom MITRE Data Source STIX object."""
def __init__(self, **kwargs):
# Intentionally left blank.
pass


Expand All @@ -44,14 +46,16 @@ def __init__(self, **kwargs):
("x_mitre_domains", ListProperty(EnumProperty(allowed=['enterprise-attack', 'mobile-attack', 'ics-attack']))),
("x_mitre_modified_by_ref", ReferenceProperty(valid_types=['identity'])),
("created_by_ref", ReferenceProperty(valid_types=['identity'])),
("object_marking_refs", ListProperty(ReferenceProperty(valid_types=['marking-definition'])))
("object_marking_refs", ListProperty(ReferenceProperty(valid_types=['marking-definition']))),
("x_mitre_deprecated", BooleanProperty())
]
)


class DataComponent():
"""Custom MITRE Data Component STIX object."""
def __init__(self, **kwargs):
# Intentionally left blank.
pass


Expand All @@ -72,6 +76,7 @@ def __init__(self, **kwargs):
class SensorMapping():
"""Custom MITRE sensor data mapping STIX object."""
def __init__(self, **kwargs):
# Intentionally left blank.
pass


Expand Down Expand Up @@ -150,6 +155,7 @@ def bundle_append(bundle, object_to_add):
"""Checks to see if `object_to_add` has already been added to `bundle`. If it has not,
it is added."""
simple_bundle = [item["id"] for item in bundle]

if object_to_add["id"] not in simple_bundle:
bundle.append(object_to_add)

Expand Down Expand Up @@ -254,7 +260,8 @@ def create_stix_object(reference_dict, created_objects, object_type, object_deta
x_mitre_domains=object_details["domain"],
created_by_ref="identity--c78cb6e5-0c4b-4611-8297-d1b8b55e40b5",
x_mitre_modified_by_ref="identity--c78cb6e5-0c4b-4611-8297-d1b8b55e40b5",
external_references = []
external_references = [],
x_mitre_deprecated=False
)
case "Data Component":
new_sdo = DataComponent(
Expand All @@ -267,7 +274,8 @@ def create_stix_object(reference_dict, created_objects, object_type, object_deta
x_mitre_modified_by_ref="identity--c78cb6e5-0c4b-4611-8297-d1b8b55e40b5",
created_by_ref="identity--c78cb6e5-0c4b-4611-8297-d1b8b55e40b5",
object_marking_refs=["marking-definition--fa42a846-8d90-4e51-bc29-71d5b4802168"],
allow_custom=True
allow_custom=True,
x_mitre_deprecated=False
)
case _:
raise NotImplementedError("Unexpected object type.")
Expand All @@ -282,7 +290,7 @@ def parse_mappings(mappings_location, config_location, attack_domain, data_sdo_i
:param mappings_location the filepath to the mappings CSV file
:param config_location: the filepath to the JSON configuration file.
:param relationship_ids is a dict of format {relationship-source-id---relationship-target-id -> relationship-id} which maps relationships to desired STIX IDs
:param relationship_ids is a dict of format {relationship-source-id---relationship-target-id -> (relationship-id, relationship-type)} which maps relationships to desired STIX IDs
:return List of tuples: (Source Mappings, stix2 Bundle)
"""
print("reading framework config... ", end="", flush=True)
Expand All @@ -294,8 +302,6 @@ def parse_mappings(mappings_location, config_location, attack_domain, data_sdo_i

attack_data_sources, attack_data_components = load_attack_data(version, attack_domain, groups)

# Match case of attack_data for Data Components to the mappings
attack_data_components = {k.replace(" ", "_").lower(): v for k,v in attack_data_components.items()}
tqdm_format = "{desc}: {percentage:3.0f}% |{bar}| {elapsed}<{remaining}{postfix}"

# build STIX objects
Expand Down Expand Up @@ -370,8 +376,7 @@ def parse_mappings(mappings_location, config_location, attack_domain, data_sdo_i

# Create a STIX SRO between the Data Source and Data Component
relation = row["RELATIONSHIP"]

joined_id = f"{data_source_stix_ID}---{data_component_stix_ID}"
joined_id = f"{data_source_stix_ID}---{data_component_stix_ID}-{relation}"
if joined_id in relationship_ids:
relationship_id = relationship_ids[joined_id]
else:
Expand Down Expand Up @@ -476,7 +481,7 @@ def use_reference_file(reference_file):
if sdo["type"] in ["x-mitre-data-component", "x-mitre-data-source"]:
data_sdo_ids[sdo["name"]] = sdo["id"]
elif sdo["type"] == "relationship":
from_id = f"{sdo['source_ref']}---{sdo['target_ref']}"
from_id = f"{sdo['source_ref']}---{sdo['target_ref']}-{sdo['relationship_type']}"
to_id = sdo["id"]
relationship_ids[from_id] = to_id
elif sdo["type"] == "x-mitre-sensor-mapping":
Expand Down Expand Up @@ -529,3 +534,4 @@ def use_reference_file(reference_file):
groups= True if args.groups else False)

to_stix_json(bundles, output_location)

Loading

0 comments on commit b1a1739

Please sign in to comment.