-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathreporter.py
executable file
·253 lines (184 loc) · 43 KB
/
reporter.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
#!/usr/bin/env python3
# encoding: utf-8
#https://github.com/didix21/mdutils#writing-and-reading-files
import time
import json
import os
#import markdown
from cortexutils.responder import Responder
from mdutils import MdUtils
from thehive4py.api import TheHiveApi
from thehive4py.models import Case, CaseTask, CaseTaskLog, CustomFieldHelper
class Reporter(Responder):
def __init__(self):
Responder.__init__(self)
self.thehive_instance = self.get_param('config.thehive_instance', 'localhost:9000')
self.thehive_api = self.get_param('config.thehive_api', 'YOUR_KEY_HERE')
self.api = TheHiveApi(self.thehive_instance,self.thehive_api)
self.tmpPath = self.get_param('config.tmp_file_location')
def getSummary(self,severity):
# Summary Fields - Severity
if(severity == 1):
severity = "Low"
elif(severity == 2):
severity = "Medium"
elif(severity == 3):
severity = "High"
else:
severity = "unknown"
return severity
def getTLP(self,tlp):
# Summary Fields - TLP
if(tlp == 0):
tlp = ['', '**TLP:WHITE - Disclosure is not limited.**']
elif(tlp == 1):
tlp = ['', '**TLP:GREEN - Limited disclosure, restricted to the community.**']
elif(tlp == 2):
tlp = ['','**TLP:AMBER - Limited disclosure, restricted to participants’ organizations.**']
elif(tlp == 3):
tlp = ['', '**TLP:RED - Not for disclosure, restricted to participants only.**']
else:
tlp = "unknown"
return tlp
def getCaseSummary(self,data):
startDate = time.strftime('%Y-%m-%dT%H:%M:%SZ', time.localtime(data['startDate'] / 1000)) #Convert epoch ms to sec then human readable
severity = self.getSummary(data['severity'])
if(data['tags'].__len__() == 0):
tags = ["No tags found"]
else:
tags = (data['tags'])
caseSummary = [' ',' ','**Severity** ', str(severity), '**Created By** ', str(data['createdBy']), '**Assignee** ', str(data['owner']), '**Tags** ', str(', '.join(tags))]
if data['status'] == 'Resolved':
closeDate = time.strftime('%Y-%m-%dT%H:%M:%SZ', time.localtime(data['endDate'] / 1000)) #Convert epoch ms to sec then human readable
caseSummary.extend(['**Case status:** ', 'Closed', '**Start Date**', startDate, '**Close Date:** ', closeDate, '**Resolution:** ', data['resolutionStatus'], '**Summary:** <br>', data['summary']])
else:
caseSummary.extend(['**Case status:** ', 'Open', '**Start Date**', startDate])
return caseSummary
def getCaseObservables(self,case_observables):
case_observables_sorted = sorted(case_observables, key=lambda k: k['createdAt'])
caseObservables = ['Created At', 'Data Type', 'Data', 'Sighted', 'IOC', 'Tags']
for observable in case_observables_sorted:
createdAt = time.strftime('%Y-%m-%dT%H:%M:%SZ', time.localtime(observable['createdAt'] / 1000)) #Convert epoch ms to sec then human readable
caseObservables.append(createdAt)
caseObservables.append(str(observable['dataType']))
if(observable['dataType'] == 'file'):
caseObservables.append(str(observable['attachment']['name']))
else:
caseObservables.append(str(observable['data'].replace('\n','<br>').replace('.','[.]').replace('http','hxxp')))
caseObservables.append(str(observable['sighted']))
caseObservables.append(str(observable['ioc']))
caseObservables.append(str(', '.join(observable['tags'])))
return caseObservables
def getCaseTasks(self,caseId):
response = self.api.get_case_tasks(caseId)
caseTasks = (json.dumps(response.json(), indent=4, sort_keys=True))
allTaskIds = {}
# Build a list of tasks that we want to get the details for
for task in json.loads(caseTasks):
if (task['title'] == 'Autogenerated Report') or (task['status'] == 'Cancel'):
continue
else:
taskId=task['id']
try:
if (task['description']):
allTaskIds[taskId] = {'taskGroup' : task['group'], 'taskTitle' : task['title'], 'createdAt' : task['createdAt'], 'createdBy' : task['createdBy'], 'owner' : task['owner'], 'status' : task['status'], 'description' : task['description']}
except KeyError:
allTaskIds[taskId] = {'taskGroup' : task['group'], 'taskTitle' : task['title'], 'createdAt' : task['createdAt'], 'createdBy' : task['createdBy'], 'owner' : task['owner'], 'status' : task['status'], 'description' : 'No description specified'}
return allTaskIds
def getCaseTaskLog(self,taskLogId):
response = self.api.get_task_logs(taskLogId)
caseTaskLog = (json.dumps(response.json(), indent=4, sort_keys=True))
return caseTaskLog
def getTlpFooter(self):
tlpFooter = ['Color','When should it be used?','How may it be shared',
'TLP:RED <br> ', "Sources may use TLP\:RED when information cannot be effectively acted upon by additional parties, and could lead to impacts on a party's privacy, reputation, or operations if misused.", "Recipients may not share TLP\:RED information with any parties outside of the specific exchange, meeting, or conversation in which it was originally disclosed. In the context of a meeting, for example, TLP\:RED information is limited to those present at the meeting. In most circumstances, TLP\:RED should be exchanged verbally or in person.",
'TLP:AMBER <br> ', "Sources may use TLP\:AMBER when information requires support to be effectively acted upon, yet carries risks to privacy, reputation, or operations if shared outside of the organizations involved.", "Recipients may only share TLP\:AMBER information with members of their own organization, and with clients or customers who need to know the information to protect themselves or prevent further harm. **Sources are at liberty to specify additional intended limits of the sharing\: these must be adhered to.**",
'TLP:GREEN <br> ' , "Sources may use TLP\:GREEN when information is useful for the awareness of all participating organizations as well as with peers within the broader community or sector.", "Recipients may share TLP\:GREEN information with peers and partner organizations within their sector or community, but not via publicly accessible channels. Information in this category can be circulated widely within a particular community. TLP\:GREEN information may not be released outside of the community.",
'TLP:WHITE <br> ', "Sources may use TLP\:WHITE when information carries minimal or no foreseeable risk of misuse, in accordance with applicable rules and procedures for public release.", "Subject to standard copyright rules, TLP\:WHITE information may be distributed without restriction."]
return tlpFooter
def addTask(self,caseId):
response = self.api.create_case_task(caseId, CaseTask(
title='Autogenerated Report',
startDate=int(time.time())*1000))
if response.status_code == 201:
return(json.dumps(response.json(), indent=4, sort_keys=True))
else:
self.error('ko: {}/{}'.format(response.status_code, response.text))
def addTaskLog(self,taskId,filename):
response = self.api.create_task_log(taskId, CaseTaskLog(
message='Autogenerated report',
file=filename))
if response.status_code == 201:
return(json.dumps(response.json(), indent=4, sort_keys=True))
else:
self.error('ko: {}/{}'.format(response.status_code, response.text))
def run(self):
Responder.run(self)
caseNumber = self.get_param('data.caseId') #Friendly case number
caseId = self.get_param('data.id') #Raw case number
case_observables = self.api.get_case_observables(caseId).json()
title = self.get_param('data.title', None, 'title is missing')
description = self.get_param('data.description', None, 'description is missing')
tags = self.get_param('data.tags')
data = self.get_param('data')
tlp = self.getTLP(data['tlp'])
# Title
#mdFile = MdUtils(file_name=str(caseNumber),title=tlp[0] + ' Case #' + str(caseNumber) + ': ' + title)
mdFile = MdUtils(file_name=str(self.tmpPath) + str(caseNumber),title=tlp[0] + ' Case #' + str(caseNumber) + ': ' + title)
# Case Summary
caseSummary = self.getCaseSummary(data)
mdFile.new_header(level=1, title='Case Summary')
mdFile.new_line(str(tlp[1]))
mdFile.new_table(columns=2, rows=int(caseSummary.__len__()/2), text=caseSummary, text_align='left')
# Case Description
mdFile.new_line('<div style="page-break-after: always;"></div>')
mdFile.new_line(' ')
mdFile.new_header(level=1, title='Case Description')
mdFile.new_line(str(data['description']))
mdFile.new_line(' ')
# Task Log
allTaskIds = self.getCaseTasks(caseId)
allTaskIds_sorted = sorted(allTaskIds.items(), key=lambda x: x[1]['createdAt'])
mdFile.new_header(level=1, title='Task Log Entries')
for task in allTaskIds_sorted:
title=str(task[1]['taskGroup'] + ' \: ' + task[1]['taskTitle'])
createdAt = time.strftime('%Y-%m-%dT%H:%M:%SZ', time.localtime(task[1]['createdAt'] / 1000)) #Convert epoch ms to sec then human readable
mdFile.new_header(level=2, title=title)
mdFile.new_line(str('**Created At:** ') + str(createdAt))
mdFile.new_line(str('**Created By:** ') + str(task[1]['createdBy']))
mdFile.new_line(str('**Assigned To:** ') + str(task[1]['owner']))
mdFile.new_line(str('**Case Status:** ') + str(task[1]['status']))
mdFile.new_line(' ')
mdFile.new_line(str('**Description:** '))
mdFile.new_line(str(task[1]['description']))
mdFile.new_line(' ')
caseTaskLog = self.getCaseTaskLog(task[0])
caseTaskLogEntries = (json.loads(caseTaskLog))
caseTaskLogEntries_sorted = sorted(caseTaskLogEntries, key=lambda k: k['createdAt'])
for caseTaskLogEntry in caseTaskLogEntries_sorted:
createdAt = time.strftime('%Y-%m-%dT%H:%M:%SZ', time.localtime(caseTaskLogEntry['createdAt'] / 1000)) #Convert epoch ms to sec then human readable
mdFile.new_line(str(createdAt) + ' : ' + str(caseTaskLogEntry['message']))
# Case Observables
mdFile.new_header(level=1, title='Case Observables')
caseObservables = self.getCaseObservables(case_observables)
mdFile.new_table(columns=6, rows=int(caseObservables.__len__()/6), text=caseObservables, text_align='left')
# TLP Protocol description
mdFile.new_line('<div style="page-break-after: always;"></div>')
mdFile.new_line(' ')
mdFile.new_header(level=1, title='Traffic Light Protocol (TLP) Definitions and Usage')
tlpFooter = self.getTlpFooter()
mdFile.new_table(columns=3, rows=5, text=tlpFooter, text_align='left')
# Build TOC
mdFile.new_table_of_contents(table_title='Table of Contents', depth=2)
# Compile the report
mdFile.create_md_file()
# Add the report to the case
addTask = json.loads(self.addTask(caseId))
taskId = addTask['_id']
# Add the MD file to the task
addTaskLog = json.loads(self.addTaskLog(taskId,str(self.tmpPath) + str(caseNumber)+'.md'))
# Cleanup the MD file
os.remove(str(self.tmpPath) + str(caseNumber)+'.md')
self.report({'report': 'created'})
if __name__ == '__main__':
Reporter().run()