contentctl 4.2.5__py3-none-any.whl → 4.3.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,153 +0,0 @@
1
- import os
2
- import re
3
-
4
- from urllib.parse import urlparse
5
-
6
- from contentctl.output.yml_writer import YmlWriter
7
- from contentctl.objects.enums import SecurityContentType
8
- from contentctl.output.finding_report_writer import FindingReportObject
9
- from contentctl.objects.unit_test_old import UnitTestOld
10
-
11
-
12
- class BAYmlOutput():
13
-
14
-
15
- def writeObjectsInPlace(self, objects: list) -> None:
16
- for object in objects:
17
- file_path = object['file_path']
18
- object.pop('file_path')
19
- object.pop('deprecated')
20
- object.pop('experimental')
21
- YmlWriter.writeYmlFile(file_path, object)
22
-
23
-
24
- def writeObjects(self, objects: list, output_path: str, contentType: SecurityContentType = None) -> None:
25
- for obj in objects:
26
- file_name = "ssa___" + self.convertNameToFileName(obj.name, obj.tags)
27
- if self.isComplexBARule(obj.search):
28
- file_path = os.path.join(output_path, 'complex', file_name)
29
- else:
30
- file_path = os.path.join(output_path, 'srs', file_name)
31
-
32
- # add research object
33
- RESEARCH_SITE_BASE = 'https://research.splunk.com/'
34
- research_site_url = RESEARCH_SITE_BASE + obj.source + "/" + obj.id + "/"
35
- obj.tags.research_site_url = research_site_url
36
-
37
- # add ocsf schema tag
38
- obj.tags.event_schema = 'ocsf'
39
-
40
- body = FindingReportObject.writeFindingReport(obj)
41
-
42
- if obj.test:
43
- test_dict = {
44
- "name": obj.name + " Unit Test",
45
- "tests": [obj.test.dict()]
46
- }
47
- test_dict["tests"][0]["name"] = obj.name
48
- for count in range(len(test_dict["tests"][0]["attack_data"])):
49
- a = urlparse(str(test_dict["tests"][0]["attack_data"][count]["data"]))
50
- test_dict["tests"][0]["attack_data"][count]["file_name"] = os.path.basename(a.path)
51
-
52
- test = UnitTestOld.parse_obj(test_dict)
53
-
54
- obj.test = test
55
-
56
- # create annotations object
57
- obj.tags.annotations = {
58
- "analytic_story": obj.tags.analytic_story,
59
- "cis20": obj.tags.cis20,
60
- "kill_chain_phases": obj.tags.kill_chain_phases,
61
- "mitre_attack_id": obj.tags.mitre_attack_id,
62
- "nist": obj.tags.nist
63
- }
64
-
65
- obj.runtime = "SPL2"
66
- obj.internalVersion = 2
67
-
68
- ### Adding detection_type as top level key for SRS detections
69
- obj.detection_type = "STREAMING"
70
-
71
- # remove unncessary fields
72
- YmlWriter.writeYmlFile(file_path, obj.dict(
73
- exclude_none=True,
74
- include =
75
- {
76
- "name": True,
77
- "id": True,
78
- "version": True,
79
- "status": True,
80
- "detection_type": True,
81
- "description": True,
82
- "search": True,
83
- "how_to_implement": True,
84
- "known_false_positives": True,
85
- "references": True,
86
- "runtime": True,
87
- "internalVersion": True,
88
- "tags":
89
- {
90
- #"analytic_story": True,
91
- #"cis20" : True,
92
- #"nist": True,
93
- #"kill_chain_phases": True,
94
- "annotations": True,
95
- "mappings": True,
96
- #"mitre_attack_id": True,
97
- "risk_severity": True,
98
- "risk_score": True,
99
- "security_domain": True,
100
- "required_fields": True,
101
- "research_site_url": True,
102
- "event_schema": True
103
- },
104
- "test":
105
- {
106
- "name": True,
107
- "tests": {
108
- '__all__':
109
- {
110
- "name": True,
111
- "file": True,
112
- "pass_condition": True,
113
- "attack_data": {
114
- '__all__':
115
- {
116
- "file_name": True,
117
- "data": True,
118
- "source": True
119
- }
120
- }
121
- }
122
- }
123
- }
124
- }
125
- ))
126
-
127
- # Add Finding Report Object
128
- with open(file_path, 'r') as file:
129
- data = file.read().replace('--finding_report--', body)
130
-
131
- f = open(file_path, "w")
132
- f.write(data)
133
- f.close()
134
-
135
-
136
- def convertNameToFileName(self, name: str, product: list):
137
- file_name = name \
138
- .replace(' ', '_') \
139
- .replace('-','_') \
140
- .replace('.','_') \
141
- .replace('/','_') \
142
- .lower()
143
- if 'Splunk Behavioral Analytics' in product:
144
-
145
- file_name = 'ssa___' + file_name + '.yml'
146
- else:
147
- file_name = file_name + '.yml'
148
- return file_name
149
-
150
-
151
- def isComplexBARule(self, search):
152
- return re.findall("stats|first_time_event|adaptive_threshold", search)
153
-
@@ -1,91 +0,0 @@
1
- import os
2
- import re
3
- from jinja2 import Environment, FileSystemLoader
4
-
5
- from contentctl.objects.ssa_detection import SSADetection
6
- from contentctl.objects.constants import *
7
-
8
- class FindingReportObject():
9
-
10
- @staticmethod
11
- def writeFindingReport(detection : SSADetection) -> None:
12
-
13
- if detection.tags.confidence < 33:
14
- detection.tags.confidence_id = 1
15
- elif detection.tags.confidence < 66:
16
- detection.tags.confidence_id = 2
17
- else:
18
- detection.tags.confidence_id = 3
19
-
20
- if detection.tags.impact < 20:
21
- detection.tags.impact_id = 1
22
- elif detection.tags.impact < 40:
23
- detection.tags.impact_id = 2
24
- elif detection.tags.impact < 60:
25
- detection.tags.impact_id = 3
26
- elif detection.tags.impact < 80:
27
- detection.tags.impact_id = 4
28
- else:
29
- detection.tags.impact_id = 5
30
-
31
- detection.tags.kill_chain_phases_id = dict()
32
- for kill_chain_phase in detection.tags.kill_chain_phases:
33
- detection.tags.kill_chain_phases_id[kill_chain_phase] = SES_KILL_CHAIN_MAPPINGS[kill_chain_phase]
34
-
35
- kill_chain_phase_str = "["
36
- i = 0
37
- for kill_chain_phase in detection.tags.kill_chain_phases_id.keys():
38
- kill_chain_phase_str = kill_chain_phase_str + '{"phase": "' + kill_chain_phase + '", "phase_id": ' + str(detection.tags.kill_chain_phases_id[kill_chain_phase]) + "}"
39
- if not i == (len(detection.tags.kill_chain_phases_id.keys()) - 1):
40
- kill_chain_phase_str = kill_chain_phase_str + ', '
41
- i = i + 1
42
- kill_chain_phase_str = kill_chain_phase_str + ']'
43
- detection.tags.kill_chain_phases_str = kill_chain_phase_str
44
-
45
- if detection.tags.risk_score < 20:
46
- detection.tags.risk_level_id = 0
47
- detection.tags.risk_level = "Info"
48
- elif detection.tags.risk_score < 40:
49
- detection.tags.risk_level_id = 1
50
- detection.tags.risk_level = "Low"
51
- elif detection.tags.risk_score < 60:
52
- detection.tags.risk_level_id = 2
53
- detection.tags.risk_level = "Medium"
54
- elif detection.tags.risk_score < 80:
55
- detection.tags.risk_level_id = 3
56
- detection.tags.risk_level = "High"
57
- else:
58
- detection.tags.risk_level_id = 4
59
- detection.tags.risk_level = "Critical"
60
-
61
- evidence_str = "{"
62
- for i in range(len(detection.tags.required_fields)):
63
- evidence_str = evidence_str + '"' + detection.tags.required_fields[i] + '": ' + detection.tags.required_fields[i].replace(".", "_")
64
- if not i == (len(detection.tags.required_fields) - 1):
65
- evidence_str = evidence_str + ', '
66
-
67
- evidence_str = evidence_str + ', "sourceType": metadata.source_type, "source": metadata.source}'
68
-
69
-
70
- detection.tags.evidence_str = evidence_str
71
-
72
- analytics_story_str = "["
73
- for i in range(len(detection.tags.analytic_story)):
74
- analytics_story_str = analytics_story_str + '"' + detection.tags.analytic_story[i] + '"'
75
- if not i == (len(detection.tags.analytic_story) - 1):
76
- analytics_story_str = analytics_story_str + ', '
77
- analytics_story_str = analytics_story_str + ']'
78
- detection.tags.analytics_story_str = analytics_story_str
79
-
80
- if "actor.user.name" in detection.tags.required_fields:
81
- actor_user_name = "actor_user_name"
82
- else:
83
- actor_user_name = "\"Unknown\""
84
-
85
- j2_env = Environment(
86
- loader=FileSystemLoader(os.path.join(os.path.dirname(__file__), 'templates')),
87
- trim_blocks=True)
88
- template = j2_env.get_template('finding_report.j2')
89
- body = template.render(detection=detection, attack_tactics_id_mapping=SES_ATTACK_TACTICS_ID_MAPPING, actor_user_name=actor_user_name)
90
-
91
- return body