Нет описания

dim_tasks_routes.py 7.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. # IRIS Source Code
  2. # Copyright (C) 2024 - DFIR-IRIS
  3. # contact@dfir-iris.org
  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. from flask import Blueprint
  19. from flask import request
  20. import json
  21. import pickle
  22. from sqlalchemy import desc
  23. from app.iris_engine.module_handler.module_handler import call_modules_hook
  24. from app.models.models import CeleryTaskMeta
  25. from app.models.models import IrisHook
  26. from app.models.models import IrisModule
  27. from app.models.models import IrisModuleHook
  28. from app.models.models import CaseAssets
  29. from app.models.models import CaseReceivedFile
  30. from app.models.models import CaseTasks
  31. from app.models.cases import Cases
  32. from app.models.cases import CasesEvent
  33. from app.models.models import GlobalTasks
  34. from app.models.models import Ioc
  35. from app.models.models import Notes
  36. from app.models.alerts import Alert
  37. from app.models.authorization import CaseAccessLevel
  38. from app.blueprints.access_controls import ac_requires_case_identifier
  39. from app.blueprints.access_controls import ac_api_requires
  40. from app.blueprints.responses import response_error
  41. from app.blueprints.responses import response_success
  42. from iris_interface.IrisInterfaceStatus import IIStatus
  43. dim_tasks_rest_blueprint = Blueprint('dim_tasks_rest', __name__)
  44. @dim_tasks_rest_blueprint.route('/dim/hooks/call', methods=['POST'])
  45. @ac_requires_case_identifier(CaseAccessLevel.full_access)
  46. @ac_api_requires()
  47. def dim_hooks_call(caseid):
  48. logs = []
  49. js_data = request.json
  50. if not js_data:
  51. return response_error('Invalid data')
  52. hook_name = js_data.get('hook_name')
  53. if not hook_name:
  54. return response_error('Missing hook_name')
  55. hook_ui_name = js_data.get('hook_ui_name')
  56. targets = js_data.get('targets')
  57. if not targets:
  58. return response_error('Missing targets')
  59. data_type = js_data.get('type')
  60. if not data_type:
  61. return response_error('Missing data type')
  62. module_name = js_data.get('module_name')
  63. index = 0
  64. obj_targets = []
  65. for target in js_data.get('targets'):
  66. if type(target) is str:
  67. try:
  68. target = int(target)
  69. except ValueError:
  70. return response_error('Invalid target')
  71. elif type(target) != int:
  72. return response_error('Invalid target')
  73. if data_type == 'ioc':
  74. obj = Ioc.query.filter(Ioc.ioc_id == target).first()
  75. elif data_type == "case":
  76. obj = Cases.query.filter(Cases.case_id == caseid).first()
  77. elif data_type == "asset":
  78. obj = CaseAssets.query.filter(
  79. CaseAssets.asset_id == target,
  80. CaseAssets.case_id == caseid
  81. ).first()
  82. elif data_type == "note":
  83. obj = Notes.query.filter(
  84. Notes.note_id == target,
  85. Notes.note_case_id == caseid
  86. ).first()
  87. elif data_type == "event":
  88. obj = CasesEvent.query.filter(
  89. CasesEvent.event_id == target,
  90. CasesEvent.case_id == caseid
  91. ).first()
  92. elif data_type == "task":
  93. obj = CaseTasks.query.filter(
  94. CaseTasks.id == target,
  95. CaseTasks.task_case_id == caseid
  96. ).first()
  97. elif data_type == "evidence":
  98. obj = CaseReceivedFile.query.filter(
  99. CaseReceivedFile.id == target,
  100. CaseReceivedFile.case_id == caseid
  101. ).first()
  102. elif data_type == "global_task":
  103. obj = GlobalTasks.query.filter(
  104. GlobalTasks.id == target
  105. ).first()
  106. elif data_type == 'alert':
  107. obj = Alert.query.filter(
  108. Alert.alert_id == target
  109. ).first()
  110. else:
  111. logs.append(f'Data type {data_type} not supported')
  112. continue
  113. if not obj:
  114. logs.append(f'Object ID {target} not found')
  115. continue
  116. obj_targets.append(obj)
  117. # Call to queue task
  118. index += 1
  119. if len(obj_targets) > 0:
  120. call_modules_hook(hook_name=hook_name, hook_ui_name=hook_ui_name, data=obj_targets,
  121. caseid=caseid, module_name=module_name)
  122. if len(logs) > 0:
  123. return response_error(f"Errors encountered during processing of data. Queued task with {index} objects",
  124. data=logs)
  125. return response_success(f'Queued task with {index} objects')
  126. @dim_tasks_rest_blueprint.route('/dim/hooks/options/<hook_type>/list', methods=['GET'])
  127. @ac_api_requires()
  128. def list_dim_hook_options_ioc(hook_type):
  129. mods_options = (IrisModuleHook.query.with_entities(
  130. IrisModuleHook.manual_hook_ui_name,
  131. IrisHook.hook_name,
  132. IrisModule.module_name
  133. ).filter(
  134. IrisHook.hook_name == f"on_manual_trigger_{hook_type}",
  135. IrisModule.is_active == True
  136. )
  137. .join(IrisHook, IrisHook.id == IrisModuleHook.hook_id)
  138. .join(IrisModule, IrisModule.id == IrisModuleHook.module_id)
  139. .all())
  140. data = [options._asdict() for options in mods_options]
  141. return response_success("", data=data)
  142. @dim_tasks_rest_blueprint.route('/dim/tasks/list/<int:count>', methods=['GET'])
  143. @ac_api_requires()
  144. def list_dim_tasks(count):
  145. tasks = CeleryTaskMeta.query.filter(
  146. ~ CeleryTaskMeta.name.like('app.iris_engine.updater.updater.%')
  147. ).order_by(desc(CeleryTaskMeta.date_done)).limit(count).all()
  148. data = []
  149. for row in tasks:
  150. tkp = {'state': row.status, 'case': "Unknown", 'module': row.name, 'task_id': row.task_id, 'date_done': row.date_done, 'user': "Unknown"}
  151. try:
  152. _ = row.result
  153. except AttributeError:
  154. # Legacy task
  155. data.append(tkp)
  156. continue
  157. if row.name is not None and 'task_hook_wrapper' in row.name:
  158. task_name = f"{row.kwargs}::{row.kwargs}"
  159. else:
  160. task_name = row.name
  161. user = None
  162. case_name = None
  163. if row.kwargs and row.kwargs != b'{}':
  164. kwargs = json.loads(row.kwargs.decode('utf-8'))
  165. if kwargs:
  166. user = kwargs.get('init_user')
  167. case_name = f"Case #{kwargs.get('caseid')}"
  168. task_name = f"{kwargs.get('module_name')}::{kwargs.get('hook_name')}"
  169. try:
  170. result = pickle.loads(row.result)
  171. except:
  172. result = None
  173. if isinstance(result, IIStatus):
  174. try:
  175. success = result.is_success()
  176. except:
  177. success = None
  178. else:
  179. success = None
  180. tkp['state'] = "success" if success else str(row.result)
  181. tkp['user'] = user if user else "Shadow Iris"
  182. tkp['module'] = task_name
  183. tkp['case'] = case_name if case_name else ""
  184. data.append(tkp)
  185. return response_success("", data=data)