暂无描述

report_db.py 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415
  1. # IRIS Source Code
  2. # Copyright (C) 2021 - Airbus CyberSecurity (SAS)
  3. # ir@cyberactionlab.net
  4. #
  5. # This program is free software; you can redistribute it and/or
  6. # modify it under the terms of the GNU Lesser General Public
  7. # License as published by the Free Software Foundation; either
  8. # version 3 of the License, or (at your option) any later version.
  9. #
  10. # This program is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  13. # Lesser General Public License for more details.
  14. #
  15. # You should have received a copy of the GNU Lesser General Public License
  16. # along with this program; if not, write to the Free Software Foundation,
  17. # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  18. import datetime
  19. import re
  20. from sqlalchemy import desc
  21. from app.datamgmt.case.case_notes_db import get_notes_from_group
  22. from app.datamgmt.case.case_notes_db import get_case_note_comments
  23. from app.models.models import AnalysisStatus
  24. from app.models.models import CompromiseStatus
  25. from app.models.models import TaskAssignee
  26. from app.models.models import AssetsType
  27. from app.models.models import CaseAssets
  28. from app.models.models import CaseEventsAssets
  29. from app.models.models import CaseEventsIoc
  30. from app.models.models import CaseReceivedFile
  31. from app.models.models import CaseTasks
  32. from app.models.cases import Cases
  33. from app.models.cases import CasesEvent
  34. from app.models.models import Comments
  35. from app.models.models import EventCategory
  36. from app.models.models import Ioc
  37. from app.models.models import IocAssetLink
  38. from app.models.models import IocType
  39. from app.models.models import Notes
  40. from app.models.models import NotesGroup
  41. from app.models.models import TaskStatus
  42. from app.models.models import Tlp
  43. from app.models.authorization import User
  44. from app.schema.marshables import CaseDetailsSchema
  45. from app.schema.marshables import CommentSchema
  46. from app.schema.marshables import CaseNoteSchema
  47. def export_case_json_extended(case_id):
  48. """
  49. Export a case a JSON
  50. """
  51. export = {}
  52. case = export_caseinfo_json_extended(case_id)
  53. if not case:
  54. export['errors'] = ["Invalid case number"]
  55. return export
  56. export['case'] = case
  57. export['evidences'] = export_case_evidences_json_extended(case_id)
  58. export['timeline'] = export_case_tm_json_extended(case_id)
  59. export['iocs'] = export_case_iocs_json_extended(case_id)
  60. export['assets'] = export_case_assets_json_extended(case_id)
  61. export['tasks'] = export_case_tasks_json_extended(case_id)
  62. export['notes'] = export_case_notes_json_extended(case_id)
  63. export['export_date'] = datetime.datetime.utcnow()
  64. return export
  65. def process_md_images_links_for_report(markdown_text):
  66. """Process images links in markdown for better processing on the generator side
  67. Creates proper links with FQDN and removal of scale
  68. """
  69. markdown = re.sub(r'(/datastore\/file\/view\/\d+\?cid=\d+)( =[\dA-z%]*)\)',
  70. r"http://127.0.0.1:8000:/\1)", markdown_text)
  71. return markdown
  72. def export_caseinfo_json_extended(case_id):
  73. case = Cases.query.filter(
  74. Cases.case_id == case_id
  75. ).first()
  76. return case
  77. def export_case_evidences_json_extended(case_id):
  78. evidences = CaseReceivedFile.query.filter(
  79. CaseReceivedFile.case_id == case_id
  80. ).join(
  81. CaseReceivedFile.case
  82. ).join(
  83. CaseReceivedFile.user).all()
  84. return evidences
  85. def export_case_tm_json_extended(case_id):
  86. events = CasesEvent.query.filter(
  87. CasesEvent.case_id == case_id
  88. ).all()
  89. return events
  90. def export_case_iocs_json_extended(case_id):
  91. iocs = Ioc.query.filter(
  92. Ioc.case_id == case_id
  93. ).all()
  94. return iocs
  95. def export_case_assets_json_extended(case_id):
  96. assets = CaseAssets.query.filter(
  97. CaseAssets.case_id == case_id
  98. ).all()
  99. return assets
  100. def export_case_tasks_json_extended(case_id):
  101. tasks = CaseTasks.query.filter(
  102. CaseTasks.task_case_id == case_id
  103. ).all()
  104. return tasks
  105. def export_case_notes_json_extended(case_id):
  106. notes_groups = NotesGroup.query.filter(
  107. NotesGroup.group_case_id == case_id
  108. ).all()
  109. for notes_group in notes_groups:
  110. notes_group = notes_group.__dict__
  111. notes_group['notes'] = get_notes_from_group(notes_group['group_id'], case_id)
  112. return notes_groups
  113. def export_caseinfo_json(case_id):
  114. case = Cases.query.filter(
  115. Cases.case_id == case_id
  116. ).first()
  117. if not case:
  118. return None
  119. case = CaseDetailsSchema().dump(case)
  120. return case
  121. def export_case_evidences_json(case_id):
  122. evidences = CaseReceivedFile.query.filter(
  123. CaseReceivedFile.case_id == case_id
  124. ).with_entities(
  125. CaseReceivedFile.filename,
  126. CaseReceivedFile.date_added,
  127. CaseReceivedFile.file_hash,
  128. User.name.label('added_by'),
  129. CaseReceivedFile.custom_attributes,
  130. CaseReceivedFile.file_uuid,
  131. CaseReceivedFile.id,
  132. CaseReceivedFile.file_size,
  133. ).order_by(
  134. CaseReceivedFile.date_added
  135. ).join(
  136. CaseReceivedFile.user
  137. ).all()
  138. if evidences:
  139. return [row._asdict() for row in evidences]
  140. else:
  141. return []
  142. def export_case_notes_json(case_id):
  143. # Fetch all notes associated with the case
  144. notes = Notes.query.filter(
  145. Notes.note_case_id == case_id
  146. ).all()
  147. # Initialize the schemas
  148. note_schema = CaseNoteSchema()
  149. comments_schema = CommentSchema(many=True)
  150. # Serialize the notes and their comments
  151. serialized_notes = []
  152. for note in notes:
  153. note_comments = get_case_note_comments(note.note_id)
  154. serialized_note = note_schema.dump(note)
  155. serialized_note['comments'] = comments_schema.dump(note_comments)
  156. serialized_note["note_content"] = process_md_images_links_for_report(serialized_note["note_content"])
  157. serialized_notes.append(serialized_note)
  158. return serialized_notes
  159. def export_case_tm_json(case_id):
  160. timeline = CasesEvent.query.with_entities(
  161. CasesEvent.event_id,
  162. CasesEvent.event_title,
  163. CasesEvent.event_in_summary,
  164. CasesEvent.event_date,
  165. CasesEvent.event_tz,
  166. CasesEvent.event_date_wtz,
  167. CasesEvent.event_content,
  168. CasesEvent.event_tags,
  169. CasesEvent.event_source,
  170. CasesEvent.event_raw,
  171. CasesEvent.custom_attributes,
  172. EventCategory.name.label('category'),
  173. User.name.label('last_edited_by'),
  174. CasesEvent.event_uuid,
  175. CasesEvent.event_in_graph,
  176. CasesEvent.event_in_summary,
  177. CasesEvent.event_color,
  178. CasesEvent.event_is_flagged
  179. ).filter(
  180. CasesEvent.case_id == case_id
  181. ).order_by(
  182. CasesEvent.event_date
  183. ).join(
  184. CasesEvent.user
  185. ).outerjoin(
  186. CasesEvent.category
  187. ).all()
  188. tim = []
  189. for row in timeline:
  190. ras = row._asdict()
  191. ras['assets'] = None
  192. as_list = CaseEventsAssets.query.with_entities(
  193. CaseAssets.asset_id,
  194. CaseAssets.asset_name,
  195. AssetsType.asset_name.label('type')
  196. ).filter(
  197. CaseEventsAssets.event_id == row.event_id
  198. ).join(
  199. CaseEventsAssets.asset
  200. ).join(
  201. CaseAssets.asset_type
  202. ).all()
  203. alki = []
  204. for asset in as_list:
  205. alki.append("{} ({})".format(asset.asset_name, asset.type))
  206. ras['assets'] = alki
  207. iocs_list = CaseEventsIoc.query.with_entities(
  208. CaseEventsIoc.ioc_id,
  209. Ioc.ioc_value,
  210. Ioc.ioc_description,
  211. Tlp.tlp_name,
  212. IocType.type_name.label('type')
  213. ).filter(
  214. CaseEventsIoc.event_id == row.event_id
  215. ).join(
  216. CaseEventsIoc.ioc
  217. ).join(
  218. Ioc.ioc_type
  219. ).join(
  220. Ioc.tlp
  221. ).all()
  222. ras['iocs'] = [ioc._asdict() for ioc in iocs_list]
  223. tim.append(ras)
  224. return tim
  225. def export_case_tasks_json(case_id):
  226. res = CaseTasks.query.with_entities(
  227. CaseTasks.task_title,
  228. TaskStatus.status_name.label('task_status'),
  229. CaseTasks.task_tags,
  230. CaseTasks.task_open_date,
  231. CaseTasks.task_close_date,
  232. CaseTasks.task_last_update,
  233. CaseTasks.task_description,
  234. CaseTasks.custom_attributes,
  235. CaseTasks.task_uuid,
  236. CaseTasks.id
  237. ).filter(
  238. CaseTasks.task_case_id == case_id
  239. ).join(
  240. CaseTasks.status
  241. ).all()
  242. tasks = [c._asdict() for c in res]
  243. task_with_assignees = []
  244. for task in tasks:
  245. task_id = task['id']
  246. get_assignee_list = TaskAssignee.query.with_entities(
  247. TaskAssignee.task_id,
  248. User.user,
  249. User.id,
  250. User.name
  251. ).join(
  252. TaskAssignee.user
  253. ).filter(
  254. TaskAssignee.task_id == task_id
  255. ).all()
  256. assignee_list = {}
  257. for member in get_assignee_list:
  258. if member.task_id not in assignee_list:
  259. assignee_list[member.task_id] = [{
  260. 'user': member.user,
  261. 'name': member.name,
  262. 'id': member.id
  263. }]
  264. else:
  265. assignee_list[member.task_id].append({
  266. 'user': member.user,
  267. 'name': member.name,
  268. 'id': member.id
  269. })
  270. task['task_assignees'] = assignee_list.get(task['id'], [])
  271. task_with_assignees.append(task)
  272. return task_with_assignees
  273. def export_case_assets_json(case_id):
  274. ret = []
  275. res = CaseAssets.query.with_entities(
  276. CaseAssets.asset_id,
  277. CaseAssets.asset_uuid,
  278. CaseAssets.asset_name,
  279. CaseAssets.asset_description,
  280. CaseAssets.asset_compromise_status_id,
  281. AssetsType.asset_name.label("type"),
  282. AnalysisStatus.name.label('analysis_status'),
  283. CaseAssets.date_added,
  284. CaseAssets.asset_domain,
  285. CaseAssets.asset_ip,
  286. CaseAssets.asset_info,
  287. CaseAssets.asset_tags,
  288. CaseAssets.custom_attributes
  289. ).filter(
  290. CaseAssets.case_id == case_id
  291. ).join(
  292. CaseAssets.asset_type
  293. ).join(
  294. CaseAssets.analysis_status
  295. ).order_by(desc(CaseAssets.asset_compromise_status_id)).all()
  296. for row in res:
  297. row = row._asdict()
  298. row['light_asset_description'] = row['asset_description']
  299. ial = IocAssetLink.query.with_entities(
  300. Ioc.ioc_value,
  301. IocType.type_name,
  302. Ioc.ioc_description
  303. ).filter(
  304. IocAssetLink.asset_id == row['asset_id']
  305. ).join(
  306. IocAssetLink.ioc
  307. ).join(
  308. Ioc.ioc_type
  309. ).all()
  310. if ial:
  311. row['asset_ioc'] = [row._asdict() for row in ial]
  312. else:
  313. row['asset_ioc'] = []
  314. if row['asset_compromise_status_id'] is None:
  315. row['asset_compromise_status_id'] = CompromiseStatus.unknown.value
  316. status_text = CompromiseStatus.unknown.name.replace('_', ' ').title()
  317. else:
  318. status_text = CompromiseStatus(row['asset_compromise_status_id']).name.replace('_', ' ').title()
  319. row['asset_compromise_status'] = status_text
  320. ret.append(row)
  321. return ret
  322. def export_case_comments_json(case_id):
  323. comments = Comments.query.with_entities(
  324. Comments.comment_id,
  325. Comments.comment_uuid,
  326. Comments.comment_text,
  327. User.name.label('comment_by'),
  328. Comments.comment_date,
  329. ).filter(
  330. Comments.comment_case_id == case_id
  331. ).join(
  332. Comments.user
  333. ).order_by(
  334. Comments.comment_date
  335. ).all()
  336. return [row._asdict() for row in comments]