Skip to content

Commit

Permalink
Improve build command
Browse files Browse the repository at this point in the history
  • Loading branch information
Patrick committed Sep 8, 2023
1 parent b92d758 commit 5008a5c
Show file tree
Hide file tree
Showing 39 changed files with 811 additions and 174 deletions.
Binary file added .DS_Store
Binary file not shown.
2 changes: 1 addition & 1 deletion contentctl/actions/generate.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ def execute(self, input_dto: GenerateInputDto) -> DirectorOutputDto:
conf_output.writeObjects(director_output_dto.macros, SecurityContentType.macros)
conf_output.writeAppConf()
conf_output.packageApp()
conf_output.inspectApp()
#conf_output.inspectApp()

elif input_dto.director_input_dto.product == SecurityContentProduct.SSA:
shutil.rmtree(input_dto.output_path + '/srs/', ignore_errors=True)
Expand Down
2 changes: 1 addition & 1 deletion contentctl/actions/initialize.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ def execute(self, input_dto: InitializeInputDto) -> None:
YmlWriter.writeYmlFile(os.path.join(input_dto.path, 'contentctl_test.yml'), dict(obj))


folders = ['detections', 'stories', 'lookups', 'macros', 'baselines', 'dist', 'docs', 'reporting']
folders = ['detections', 'stories', 'lookups', 'macros', 'baselines', 'dist', 'docs', 'reporting', 'deployments', 'investigations']
for folder in folders:
os.makedirs(os.path.join(input_dto.path, folder))

Expand Down
27 changes: 11 additions & 16 deletions contentctl/actions/validate.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,6 @@ def execute(self, input_dto: ValidateInputDto) -> None:
)
self.validate_duplicate_uuids(security_content_objects)

# validate tests
self.validate_detection_exist_for_test(
director_output_dto.tests, director_output_dto.detections
)

except ValueError as e:
print(e)
sys.exit(1)
Expand Down Expand Up @@ -73,14 +68,14 @@ def validate_duplicate_uuids(self, security_content_objects):
+ "\n".join([obj.name for obj in content_with_duplicate_uuid])
)

def validate_detection_exist_for_test(self, tests: list, detections: list):
for test in tests:
found_detection = False
for detection in detections:
if test.tests[0].file in detection.file_path:
found_detection = True

if not found_detection:
raise ValueError(
"ERROR: detection doesn't exist for test file: " + test.name
)
# def validate_detection_exist_for_test(self, tests: list, detections: list):
# for test in tests:
# found_detection = False
# for detection in detections:
# if test.tests[0].file in detection.file_path:
# found_detection = True

# if not found_detection:
# raise ValueError(
# "ERROR: detection doesn't exist for test file: " + test.name
# )
134 changes: 134 additions & 0 deletions contentctl/helper/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
ATTACK_TACTICS_KILLCHAIN_MAPPING = {
"Reconnaissance": "Reconnaissance",
"Resource Development": "Weaponization",
"Initial Access": "Delivery",
"Execution": "Installation",
"Persistence": "Installation",
"Privilege Escalation": "Exploitation",
"Defense Evasion": "Exploitation",
"Credential Access": "Exploitation",
"Discovery": "Exploitation",
"Lateral Movement": "Exploitation",
"Collection": "Exploitation",
"Command And Control": "Command And Control",
"Command And Control": "Command And Control",
"Exfiltration": "Actions on Objectives",
"Impact": "Actions on Objectives"
}

SES_CONTEXT_MAPPING = {
"Unknown": 0,
"Source:Endpoint": 10,
"Source:AD": 11,
"Source:Firewall": 12,
"Source:Application Log": 13,
"Source:IPS": 14,
"Source:Cloud Data": 15,
"Source:Correlation": 16,
"Source:Printer": 17,
"Source:Badge": 18,
"Scope:Internal": 20,
"Scope:External": 21,
"Scope:Inbound": 22,
"Scope:Outbound": 23,
"Scope:Local": 24,
"Scope:Network": 25,
"Outcome:Blocked": 30,
"Outcome:Allowed": 31,
"Stage:Recon": 40,
"Stage:Initial Access": 41,
"Stage:Execution": 42,
"Stage:Persistence": 43,
"Stage:Privilege Escalation": 44,
"Stage:Defense Evasion": 45,
"Stage:Credential Access": 46,
"Stage:Discovery": 47,
"Stage:Lateral Movement": 48,
"Stage:Collection": 49,
"Stage:Exfiltration": 50,
"Stage:Command And Control": 51,
"Consequence:Infection": 60,
"Consequence:Reduced Visibility": 61,
"Consequence:Data Destruction": 62,
"Consequence:Denial Of Service": 63,
"Consequence:Loss Of Control": 64,
"Rares:Rare User": 70,
"Rares:Rare Process": 71,
"Rares:Rare Device": 72,
"Rares:Rare Domain": 73,
"Rares:Rare Network": 74,
"Rares:Rare Location": 75,
"Other:Peer Group": 80,
"Other:Brute Force": 81,
"Other:Policy Violation": 82,
"Other:Threat Intelligence": 83,
"Other:Flight Risk": 84,
"Other:Removable Storage": 85
}

SES_KILL_CHAIN_MAPPINGS = {
"Unknown": 0,
"Reconnaissance": 1,
"Weaponization": 2,
"Delivery": 3,
"Exploitation": 4,
"Installation": 5,
"Command And Control": 6,
"Actions on Objectives": 7
}

SES_OBSERVABLE_ROLE_MAPPING = {
"Other": -1,
"Unknown": 0,
"Actor": 1,
"Target": 2,
"Attacker": 3,
"Victim": 4,
"Parent Process": 5,
"Child Process": 6,
"Known Bad": 7,
"Data Loss": 8,
"Observer": 9
}

SES_OBSERVABLE_TYPE_MAPPING = {
"Unknown": 0,
"Hostname": 1,
"IP Address": 2,
"MAC Address": 3,
"User Name": 4,
"Email Address": 5,
"URL String": 6,
"File Name": 7,
"File Hash": 8,
"Process Name": 9,
"Ressource UID": 10,
"Endpoint": 20,
"User": 21,
"Email": 22,
"Uniform Resource Locator": 23,
"File": 24,
"Process": 25,
"Geo Location": 26,
"Container": 27,
"Registry Key": 28,
"Registry Value": 29,
"Other": 99
}

SES_ATTACK_TACTICS_ID_MAPPING = {
"Reconnaissance": "TA0043",
"Resource_Development": "TA0042",
"Initial_Access": "TA0001",
"Execution": "TA0002",
"Persistence": "TA0003",
"Privilege_Escalation": "TA0004",
"Defense_Evasion": "TA0005",
"Credential_Access": "TA0006",
"Discovery": "TA0007",
"Lateral_Movement": "TA0008",
"Collection": "TA0009",
"Command_and_Control": "TA0011",
"Exfiltration": "TA0010",
"Impact": "TA0040"
}
44 changes: 27 additions & 17 deletions contentctl/input/baseline_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,34 +18,44 @@ def setObject(self, path: pathlib.Path) -> None:
try:
self.baseline = Baseline.parse_obj(yml_dict)


except ValidationError as e:
print('Validation Error for file ' + str(path))
print(e)
sys.exit(1)


def addDeployment(self, deployments: list) -> None:
matched_deployments = []
if not self.baseline.deployment:

matched_deployments = []

for d in deployments:
d_tags = dict(d.tags)
baseline_dict = self.baseline.dict()
baseline_tags_dict = self.baseline.tags.dict()
for d_tag in d_tags.keys():
for attr in baseline_dict.keys():
if attr == d_tag:
if isinstance(baseline_dict[attr], str):
if baseline_dict[attr] == d_tags[d_tag]:
matched_deployments.append(d)
elif isinstance(baseline_dict[attr], list):
if d_tags[d_tag] in baseline_dict[attr]:
matched_deployments.append(d)

for d in deployments:
d_tags = dict(d.tags)
for d_tag in d_tags.keys():
for attr in dir(self.baseline):
if not (attr.startswith('__') or attr.startswith('_')):
for attr in baseline_tags_dict.keys():
if attr == d_tag:
if type(self.baseline.__getattribute__(attr)) is str:
attr_values = [self.baseline.__getattribute__(attr)]
else:
attr_values = self.baseline.__getattribute__(attr)

for attr_value in attr_values:
if attr_value == d_tags[d_tag]:
if isinstance(baseline_tags_dict[attr], str):
if baseline_tags_dict[attr] == d_tags[d_tag]:
matched_deployments.append(d)
elif isinstance(baseline_tags_dict[attr], list):
if d_tags[d_tag] in baseline_tags_dict[attr]:
matched_deployments.append(d)

if len(matched_deployments) == 0:
raise ValueError('No deployment found for baseline: ' + self.baseline.name)
if len(matched_deployments) == 0:
raise ValueError('No deployment found for baseline: ' + self.baseline.name)

self.baseline.deployment = matched_deployments[-1]
self.baseline.deployment = matched_deployments[-1]


def reset(self) -> None:
Expand Down
1 change: 0 additions & 1 deletion contentctl/input/basic_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ class BasicBuilder():


def setObject(self, path: pathlib.Path, type: SecurityContentType) -> None:
#print(path)
yml_dict = YmlReader.load_file(path)
if type == SecurityContentType.deployments:
if "alert_action" in yml_dict:
Expand Down
43 changes: 27 additions & 16 deletions contentctl/input/detection_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from contentctl.enrichments.cve_enrichment import CveEnrichment
from contentctl.enrichments.splunk_app_enrichment import SplunkAppEnrichment
from contentctl.objects.config import ConfigDetectionConfiguration
from contentctl.helper.constants import *


class DetectionBuilder():
Expand All @@ -25,9 +26,29 @@ def setObject(self, path: str) -> None:
self.security_content_obj.source = os.path.split(os.path.dirname(self.security_content_obj.file_path))[-1]


def addDeployment(self, detection_configuration: ConfigDetectionConfiguration) -> None:
def addDeployment(self, deployments: list) -> None:
if self.security_content_obj:
self.security_content_obj.deployment = detection_configuration
if not self.security_content_obj.deployment:
matched_deployments = []
for d in deployments:
d_tags = dict(d.tags)
for d_tag in d_tags.keys():
for attr in dir(self.security_content_obj):
if not (attr.startswith('__') or attr.startswith('_')):
if attr == d_tag:
if type(self.security_content_obj.__getattribute__(attr)) is str:
attr_values = [self.security_content_obj.__getattribute__(attr)]
else:
attr_values = self.security_content_obj.__getattribute__(attr)

for attr_value in attr_values:
if attr_value == d_tags[d_tag]:
matched_deployments.append(d)

if len(matched_deployments) == 0:
self.security_content_obj.deployment = None
else:
self.security_content_obj.deployment = matched_deployments[-1]


def addRBA(self) -> None:
Expand Down Expand Up @@ -145,20 +166,6 @@ def addBaseline(self, baselines: list) -> None:
self.security_content_obj.baselines = matched_baselines


def addUnitTest(self, tests: list) -> None:
if self.security_content_obj:
if self.security_content_obj.tests and len(self.security_content_obj.tests) > 0:
return
elif self.security_content_obj.type not in ["Correlation"] and \
self.security_content_obj.deprecated == False and \
self.security_content_obj.experimental == False and \
self.security_content_obj.tags.manual_test == None:
raise(Exception(f"No tests found found {self.security_content_obj.file_path}"))
#print(f"No tests found found {self.security_content_obj.file_path}")
return None



def addMitreAttackEnrichment(self, attack_enrichment: dict) -> None:
if self.security_content_obj:
if attack_enrichment:
Expand Down Expand Up @@ -221,13 +228,15 @@ def addCve(self) -> None:
for cve in self.security_content_obj.tags.cve:
self.security_content_obj.cve_enrichment.append(CveEnrichment.enrich_cve(cve))


def addSplunkApp(self) -> None:
if self.security_content_obj:
self.security_content_obj.splunk_app_enrichment = []
if self.security_content_obj.tags.supported_tas:
for splunk_app in self.security_content_obj.tags.supported_tas:
self.security_content_obj.splunk_app_enrichment.append(SplunkAppEnrichment.enrich_splunk_app(splunk_app))


def addCIS(self) -> None:
if self.security_content_obj:
if self.security_content_obj.tags.security_domain == "network":
Expand All @@ -246,13 +255,15 @@ def addKillChainPhase(self) -> None:
kill_chain_phases.append(ATTACK_TACTICS_KILLCHAIN_MAPPING[mitre_attack_tactic])
self.security_content_obj.tags.kill_chain_phases = list(dict.fromkeys(kill_chain_phases))


def addNist(self) -> None:
if self.security_content_obj:
if self.security_content_obj.type == "TTP":
self.security_content_obj.tags.nist = ["DE.CM"]
else:
self.security_content_obj.tags.nist = ["DE.AE"]


def addDatamodel(self) -> None:
if self.security_content_obj:
self.security_content_obj.datamodel = []
Expand Down
Loading

0 comments on commit 5008a5c

Please sign in to comment.