Нет описания

assets.py 6.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  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_login import current_user
  19. from marshmallow.exceptions import ValidationError
  20. from flask_sqlalchemy.pagination import Pagination
  21. from app import db
  22. from app.business.errors import BusinessProcessingError
  23. from app.business.errors import ObjectNotFoundError
  24. from app.business.cases import cases_exists
  25. from app.datamgmt.case.case_db import get_case_client_id
  26. from app.datamgmt.manage.manage_users_db import get_user_cases_fast
  27. from app.datamgmt.states import get_assets_state
  28. from app.datamgmt.states import update_assets_state
  29. from app.models.models import CaseAssets
  30. from app.models.pagination_parameters import PaginationParameters
  31. from app.datamgmt.case.case_assets_db import get_asset
  32. from app.datamgmt.case.case_assets_db import get_assets
  33. from app.datamgmt.case.case_assets_db import filter_assets
  34. from app.datamgmt.case.case_assets_db import get_assets_ioc_links
  35. from app.datamgmt.case.case_assets_db import get_similar_assets
  36. from app.datamgmt.case.case_assets_db import case_assets_db_exists
  37. from app.datamgmt.case.case_assets_db import create_asset
  38. from app.datamgmt.case.case_assets_db import set_ioc_links
  39. from app.datamgmt.case.case_assets_db import get_linked_iocs_finfo_from_asset
  40. from app.datamgmt.case.case_assets_db import delete_asset
  41. from app.iris_engine.module_handler.module_handler import call_modules_hook
  42. from app.iris_engine.utils.tracker import track_activity
  43. from app.schema.marshables import CaseAssetsSchema
  44. def _load(request_data, **kwargs):
  45. try:
  46. add_assets_schema = CaseAssetsSchema()
  47. return add_assets_schema.load(request_data, **kwargs)
  48. except ValidationError as e:
  49. raise BusinessProcessingError('Data error', data=e.messages)
  50. def assets_create(case_identifier, request_json):
  51. request_data = call_modules_hook('on_preload_asset_create', data=request_json, caseid=case_identifier)
  52. asset = _load(request_data)
  53. asset.case_id = case_identifier
  54. if case_assets_db_exists(asset):
  55. raise BusinessProcessingError('Asset with same value and type already exists')
  56. asset = create_asset(asset=asset, caseid=case_identifier, user_id=current_user.id)
  57. # TODO should the custom attributes be set?
  58. if request_data.get('ioc_links'):
  59. errors, _ = set_ioc_links(request_data.get('ioc_links'), asset.asset_id)
  60. if errors:
  61. raise BusinessProcessingError('Encountered errors while linking IOC. Asset has still been created.')
  62. asset = call_modules_hook('on_postload_asset_create', data=asset, caseid=case_identifier)
  63. if asset:
  64. track_activity(f'added asset "{asset.asset_name}"', caseid=case_identifier)
  65. return 'Asset added', asset
  66. raise BusinessProcessingError('Unable to create asset for internal reasons')
  67. def assets_delete(asset: CaseAssets):
  68. call_modules_hook('on_preload_asset_delete', data=asset.asset_id)
  69. # Deletes an asset and the potential links with the IoCs from the database
  70. delete_asset(asset)
  71. call_modules_hook('on_postload_asset_delete', data=asset.asset_id, caseid=asset.case_id)
  72. track_activity(f'removed asset ID {asset.asset_name}', caseid=asset.case_id)
  73. def assets_get(identifier) -> CaseAssets:
  74. asset = get_asset(identifier)
  75. if not asset:
  76. raise ObjectNotFoundError()
  77. return asset
  78. def assets_get_detailed(identifier):
  79. asset = assets_get(identifier)
  80. # TODO this is a code smell: shouldn't have schemas in the business layer + the CaseAssetsSchema is instantiated twice
  81. case_assets_schema = CaseAssetsSchema()
  82. data = case_assets_schema.dump(asset)
  83. asset_iocs = get_linked_iocs_finfo_from_asset(identifier)
  84. data['linked_ioc'] = [row._asdict() for row in asset_iocs]
  85. return data
  86. def get_assets_case(case_identifier):
  87. assets = get_assets(case_identifier)
  88. customer_id = get_case_client_id(case_identifier)
  89. ret = {'assets': []}
  90. ioc_links_req = get_assets_ioc_links(case_identifier)
  91. cache_ioc_link = {}
  92. for ioc in ioc_links_req:
  93. if ioc.asset_id not in cache_ioc_link:
  94. cache_ioc_link[ioc.asset_id] = [ioc._asdict()]
  95. else:
  96. cache_ioc_link[ioc.asset_id].append(ioc._asdict())
  97. cases_access = get_user_cases_fast(current_user.id)
  98. for asset in assets:
  99. asset = asset._asdict()
  100. if len(assets) < 300:
  101. # Find similar assets from other cases with the same customer
  102. asset['link'] = list(get_similar_assets(
  103. asset['asset_name'], asset['asset_type_id'], case_identifier, customer_id, cases_access))
  104. else:
  105. asset['link'] = []
  106. asset['ioc_links'] = cache_ioc_link.get(asset['asset_id'])
  107. ret['assets'].append(asset)
  108. ret['state'] = get_assets_state(case_identifier)
  109. return ret
  110. def assets_filter(case_identifier, pagination_parameters: PaginationParameters) -> Pagination:
  111. if not cases_exists(case_identifier):
  112. raise ObjectNotFoundError()
  113. return filter_assets(case_identifier, pagination_parameters)
  114. def assets_update(asset: CaseAssets, request_json):
  115. caseid = asset.case_id
  116. request_data = call_modules_hook('on_preload_asset_update', data=request_json, caseid=caseid)
  117. request_data['asset_id'] = asset.asset_id
  118. asset_schema = _load(request_data, instance=asset)
  119. if case_assets_db_exists(asset_schema):
  120. raise BusinessProcessingError('Data error', data='Asset with same value and type already exists')
  121. update_assets_state(caseid=caseid)
  122. db.session.commit()
  123. if hasattr(asset_schema, 'ioc_links'):
  124. errors, _ = set_ioc_links(asset_schema.ioc_links, asset.asset_id)
  125. if errors:
  126. raise BusinessProcessingError('Encountered errors while linking IOC. Asset has still been updated.')
  127. asset_schema = call_modules_hook('on_postload_asset_update', data=asset_schema, caseid=caseid)
  128. if asset_schema:
  129. track_activity(f'updated asset "{asset_schema.asset_name}"', caseid=caseid)
  130. return asset_schema
  131. raise BusinessProcessingError('Unable to update asset for internal reasons')