Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Preload hooks for delete case and task #619

Open
wants to merge 31 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
c2313a3
Fix ref POSTGRES_USER
tvandenabbeel-spotit Oct 4, 2024
5904a30
[MRG] Merge pull request #606 from tvandenabbeel-spotit/patch-1
whikernel Oct 4, 2024
88eb441
[FIX] realization on_preload_task_delete
Vladimir-A Oct 12, 2024
d02fe07
[FIX] realization on_preload_case_delete
Vladimir-A Oct 12, 2024
e7d7c5f
[FIX] Memory issue in alerts
whikernel Oct 23, 2024
be4d6cb
[ADD] Added source reference as filter in alerts
whikernel Oct 23, 2024
966d01d
[ADD] Alerts indexes
whikernel Oct 28, 2024
bb5c8cc
[ADD] Added index for alert source ref
whikernel Oct 29, 2024
4e6e4d5
[ADD] Possibility to unassigne alert from API
whikernel Oct 29, 2024
72a7f31
[UPD] Updated IRIS webhook module
whikernel Oct 29, 2024
0933a3c
[ADD] Alerts graph zoom
whikernel Oct 29, 2024
178df5f
[ADD] Possibility to check alerts
whikernel Oct 29, 2024
0fba01d
Bump version: 2.4.14 → 2.4.15-beta
whikernel Oct 29, 2024
7895516
[FIX] Deletion of alerts with new relations
whikernel Oct 31, 2024
e2e0266
Bump version: 2.4.15-beta → 2.4.15-rc1
whikernel Oct 31, 2024
fd6f8fe
[FIX] report template language
Vladimir-A Nov 2, 2024
45bd397
Merge pull request #632 from Vladimir-A/fix_report_template_language
whikernel Nov 4, 2024
1e5e805
[ADD] Better visibility of commented elements
whikernel Nov 9, 2024
d31acf1
[ADD] Possibility to unset owner from alerts from API
whikernel Nov 9, 2024
8d03428
[ADD] Possibility to copy values from alerts table
whikernel Nov 9, 2024
70767b6
Bump version: 2.4.15-rc1 → 2.4.15-rc2
whikernel Nov 9, 2024
5299bfb
Bump version: 2.4.15-rc2 → 2.4.15
whikernel Nov 9, 2024
2c7f55e
[MRG] Merge pull request #630 from dfir-iris/hotfix_2.4.15-beta
whikernel Nov 9, 2024
50cf190
[FIX] Issue with hybrid OIDC / Local auth
whikernel Nov 14, 2024
6046f85
Bump version: 2.4.15 → 2.4.15
whikernel Nov 14, 2024
e379781
[FIX] Bump issue
whikernel Nov 14, 2024
9f3a725
Bump version: 2.4.15 → 2.4.16
whikernel Nov 14, 2024
a533e61
[MRG] Merge pull request #635 from dfir-iris/hotfix_2.4.16
whikernel Nov 14, 2024
de67335
[FIX] Docker compose issue due to bump
whikernel Nov 14, 2024
37f2eeb
[FIX] Fixed readme
whikernel Nov 14, 2024
6e735ce
Merge branch 'dfir-iris:master' into preload_for_case_and_task
Vladimir-A Nov 15, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .bumpversion.cfg
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[bumpversion]
current_version = 2.4.14
current_version = 2.4.16
commit = True
tag = True
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)(-(?P<release>.*)-(?P<build>\d+))?
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<p align="center">
Incident Response Investigation System
<br>
<i>Current Version v2.4.14</i>
<i>Current Version v2.4.16</i>
<br>
<a href="https://v200.beta.dfir-iris.org">Online Demonstration</a>
</p>
Expand Down Expand Up @@ -52,7 +52,7 @@ git clone https://github.com/dfir-iris/iris-web.git
cd iris-web

# Checkout to the last tagged version
git checkout v2.4.14
git checkout v2.4.16

# Copy the environment file
cp .env.model .env
Expand Down
4 changes: 2 additions & 2 deletions deploy/kubernetes/charts/templates/postgres.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ spec:
value: {{ .Values.postgres.POSTGRES_DB | quote }}

- name: POSTGRES_USER # Setting Database username
value: {{ .Values.postgres.POSTGRES_ADMIN_USER | quote }}
value: {{ .Values.postgres.POSTGRES_USER | quote }}

- name: POSTGRES_PASSWORDD # Setting Database password
value: {{ .Values.postgres.POSTGRES_PASSWORD | quote }}
Expand Down Expand Up @@ -101,4 +101,4 @@ spec:
- port: {{ .Values.postgres.service.port }}
selector:
app: {{ .Values.postgres.app }}
---
---
8 changes: 4 additions & 4 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,25 +25,25 @@ services:
extends:
file: docker-compose.base.yml
service: db
image: ${DB_IMAGE_NAME:-ghcr.io/dfir-iris/iriswebapp_db}:${DB_IMAGE_TAG:-v2.4.14}
image: ${DB_IMAGE_NAME:-ghcr.io/dfir-iris/iriswebapp_db}:${DB_IMAGE_TAG:-v2.4.16}

app:
extends:
file: docker-compose.base.yml
service: app
image: ${APP_IMAGE_NAME:-ghcr.io/dfir-iris/iriswebapp_app}:${APP_IMAGE_TAG:-v2.4.14}
image: ${APP_IMAGE_NAME:-ghcr.io/dfir-iris/iriswebapp_app}:${APP_IMAGE_TAG:-v2.4.16}

worker:
extends:
file: docker-compose.base.yml
service: worker
image: ${APP_IMAGE_NAME:-ghcr.io/dfir-iris/iriswebapp_app}:${APP_IMAGE_TAG:-v2.4.14}
image: ${APP_IMAGE_NAME:-ghcr.io/dfir-iris/iriswebapp_app}:${APP_IMAGE_TAG:-v2.4.16}

nginx:
extends:
file: docker-compose.base.yml
service: nginx
image: ${NGINX_IMAGE_NAME:-ghcr.io/dfir-iris/iriswebapp_nginx}:${NGINX_IMAGE_TAG:-v2.4.14}
image: ${NGINX_IMAGE_NAME:-ghcr.io/dfir-iris/iriswebapp_nginx}:${NGINX_IMAGE_TAG:-v2.4.16}

volumes:
iris-downloads:
Expand Down
12 changes: 11 additions & 1 deletion source/app/alembic/alembic_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,14 @@ def _has_table(table_name):
)
inspector = reflection.Inspector.from_engine(engine)
tables = inspector.get_table_names()
return table_name in tables
return table_name in tables


def index_exists(table_name, index_name):
config = op.get_context().config
engine = engine_from_config(
config.get_section(config.config_ini_section), prefix="sqlalchemy."
)
inspector = reflection.Inspector.from_engine(engine)
indexes = inspector.get_indexes(table_name)
return any(index['name'] == index_name for index in indexes)
66 changes: 66 additions & 0 deletions source/app/alembic/versions/d5a720d1b99b_add_alerts_indexes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
"""Add alerts indexes

Revision ID: d5a720d1b99b
Revises: 11aa5b725b8e
Create Date: 2024-10-28 12:54:22.782313

"""
import sqlalchemy as sa
from alembic import op
from sqlalchemy import and_, or_, text
from sqlalchemy.orm import Session

from app.alembic.alembic_utils import _has_table, index_exists

# revision identifiers, used by Alembic.
revision = 'd5a720d1b99b'
down_revision = '11aa5b725b8e'
branch_labels = None
depends_on = None


def upgrade():
# Adding indexes to the Alerts table
if _has_table('alerts'):
if not index_exists('alerts', 'idx_alerts_title'):
op.create_index('idx_alerts_title', 'alerts', ['alert_title'])
if not index_exists('alerts', 'idx_alerts_creation_time'):
op.create_index('idx_alerts_creation_time', 'alerts', ['alert_creation_time'])
if not index_exists('alerts', 'idx_alerts_source_event_time'):
op.create_index('idx_alerts_source_event_time', 'alerts', ['alert_source_event_time'])
if not index_exists('alerts', 'idx_alerts_customer_id'):
op.create_index('idx_alerts_customer_id', 'alerts', ['alert_customer_id'])
if not index_exists('alerts', 'alert_source_ref'):
op.create_index('idx_alert_source_ref', 'alerts', ['alert_source_ref'])

# Adding indexes to the Ioc table
if _has_table('ioc'):
if not index_exists('ioc', 'idx_ioc_value_hash'):
# Create an index on the MD5 hash of ioc_value to handle large values
op.execute(text("CREATE INDEX idx_ioc_value_hash ON ioc (md5(ioc_value::text))"))
if not index_exists('ioc', 'idx_ioc_tags'):
op.create_index('idx_ioc_tags', 'ioc', ['ioc_tags'])

# Adding indexes to the CaseAssets table
if _has_table('case_assets'):
if not index_exists('case_assets', 'idx_case_assets_name'):
op.create_index('idx_case_assets_name', 'case_assets', ['asset_name'])
if not index_exists('case_assets', 'idx_case_assets_case_id'):
op.create_index('idx_case_assets_case_id', 'case_assets', ['case_id'])
if not index_exists('case_assets', 'idx_case_assets_date_added'):
op.create_index('idx_case_assets_date_added', 'case_assets', ['date_added'])
if not index_exists('case_assets', 'idx_case_assets_date_update'):
op.create_index('idx_case_assets_date_update', 'case_assets', ['date_update'])


def downgrade():
# Drop indexes
op.drop_index('ix_alert_similarity_alert_id', table_name='alert_similarity')
op.drop_index('ix_alert_similarity_similar_alert_id', table_name='alert_similarity')
op.drop_index('ix_alert_similarity_matching_asset_id', table_name='alert_similarity')
op.drop_index('ix_alert_similarity_matching_ioc_id', table_name='alert_similarity')
op.drop_index('ix_alert_similarity_similarity_type', table_name='alert_similarity')

# Drop AlertSimilarity table
op.drop_table('alert_similarity')

29 changes: 16 additions & 13 deletions source/app/blueprints/alerts/alerts_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@
import app
from app import db
from app.blueprints.case.case_comments import case_comment_update
from app.datamgmt.alerts.alerts_db import get_filtered_alerts, get_alert_by_id, create_case_from_alert
from app.datamgmt.alerts.alerts_db import get_filtered_alerts, get_alert_by_id, create_case_from_alert, \
register_related_alerts, delete_related_alerts_cache
from app.datamgmt.alerts.alerts_db import merge_alert_in_case, unmerge_alert_from_case, cache_similar_alert
from app.datamgmt.alerts.alerts_db import get_related_alerts, get_related_alerts_details
from app.datamgmt.alerts.alerts_db import get_alert_comments, delete_alert_comment, get_alert_comment
Expand All @@ -38,7 +39,7 @@
from app.iris_engine.access_control.utils import ac_set_new_case_access
from app.iris_engine.module_handler.module_handler import call_modules_hook
from app.iris_engine.utils.tracker import track_activity
from app.models.alerts import AlertStatus
from app.models.alerts import AlertStatus, AlertSimilarity, Alert
from app.models.authorization import Permissions
from app.schema.marshables import AlertSchema, CaseSchema, CommentSchema, CaseAssetsSchema, IocSchema
from app.util import ac_api_requires
Expand Down Expand Up @@ -109,13 +110,12 @@ def alerts_list_route() -> Response:
except ValueError:
return response_error('Invalid alert ioc')

alert_schema = AlertSchema()

filtered_data = get_filtered_alerts(
start_date=request.args.get('creation_start_date'),
end_date=request.args.get('creation_end_date'),
source_start_date=request.args.get('source_start_date'),
source_end_date=request.args.get('source_end_date'),
source_reference=request.args.get('source_reference'),
title=request.args.get('alert_title'),
description=request.args.get('alert_description'),
status=request.args.get('alert_status_id', type=int),
Expand All @@ -139,15 +139,7 @@ def alerts_list_route() -> Response:
if filtered_data is None:
return response_error('Filtering error')

alerts = {
'total': filtered_data.total,
'alerts': alert_schema.dump(filtered_data.items, many=True),
'last_page': filtered_data.pages,
'current_page': filtered_data.page,
'next_page': filtered_data.next_num if filtered_data.has_next else None,
}

return response_success(data=alerts)
return response_success(data=filtered_data)


@alerts_blueprint.route('/alerts/add', methods=['POST'])
Expand Down Expand Up @@ -203,6 +195,8 @@ def alerts_add_route() -> Response:
cache_similar_alert(new_alert.alert_customer_id, assets=assets_list,
iocs=iocs_list, alert_id=new_alert.alert_id,
creation_date=new_alert.alert_source_event_time)

register_related_alerts(new_alert, assets_list=assets, iocs_list=iocs)

new_alert = call_modules_hook('on_postload_alert_create', data=new_alert)

Expand Down Expand Up @@ -360,6 +354,9 @@ def alerts_update_route(alert_id) -> Response:
if data.get('alert_owner_id') is None and updated_alert.alert_owner_id is None:
updated_alert.alert_owner_id = current_user.id

if data.get('alert_owner_id') == "-1" or data.get('alert_owner_id') == -1:
updated_alert.alert_owner_id = None

# Save the changes
db.session.commit()

Expand Down Expand Up @@ -437,6 +434,9 @@ def alerts_batch_update_route() -> Response:
if not user_has_client_access(current_user.id, alert.alert_customer_id):
return response_error('User not entitled to update alerts for the client', status=403)

if data.get('alert_owner_id') == "-1" or data.get('alert_owner_id') == -1:
updates['alert_owner_id'] = None

# Deserialize the JSON data into an Alert object
alert_schema.load(updates, instance=alert, partial=True)

Expand Down Expand Up @@ -530,6 +530,9 @@ def alerts_delete_route(alert_id) -> Response:
# Delete the case association
delete_similar_alert_cache(alert_id=alert_id)

# Delete the similarity entries
delete_related_alerts_cache(alert_id=alert_id)

# Delete the alert from the database
db.session.delete(alert)
db.session.commit()
Expand Down
6 changes: 6 additions & 0 deletions source/app/blueprints/alerts/templates/alerts.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
{% block stylesheets %}
<link rel="stylesheet" href="/static/assets/css/suggestags.css">
<link rel="stylesheet" href="/static/assets/css/alerts.css">
<link rel="stylesheet" href="/static/assets/css/vis.graph.css">
<link rel="stylesheet" href="/static/assets/css/vis.min.css">

{% endblock stylesheets %}

Expand Down Expand Up @@ -158,6 +160,10 @@
<label for="alert_ids">Alert(s) ID</label>
<input class="form-control" id="alert_ids" name="alert_ids">
</div>
<div class="col-md-3 form-group">
<label for="alert_ids">Source Reference</label>
<input class="form-control" id="source_reference" name="source_reference">
</div>
<div class="col-md-3 form-group">
<label for="case_id">Case ID</label>
<input type="number" class="form-control" id="case_id" name="case_id">
Expand Down
44 changes: 42 additions & 2 deletions source/app/blueprints/case/case_assets_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
import app
from app import db
from app.blueprints.case.case_comments import case_comment_update
from app.datamgmt.case.case_assets_db import add_comment_to_asset
from app.datamgmt.case.case_assets_db import add_comment_to_asset, get_raw_assets
from app.datamgmt.case.case_assets_db import create_asset
from app.datamgmt.case.case_assets_db import delete_asset
from app.datamgmt.case.case_assets_db import delete_asset_comment
Expand Down Expand Up @@ -94,6 +94,47 @@ def case_assets(caseid, url_redir):
return render_template("case_assets.html", case=case, form=form)


@case_assets_blueprint.route('/case/assets/filter', methods=['GET'])
@ac_api_case_requires(CaseAccessLevel.read_only, CaseAccessLevel.full_access)
def case_filter_assets(caseid):
"""
Returns the list of assets from the case.
:return: A JSON object containing the assets of the case, enhanced with assets seen on other cases.
"""

# Get all assets objects from the case and the customer id
ret = {}
assets = CaseAssetsSchema().dump(get_raw_assets(caseid), many=True)
customer_id = get_case_client_id(caseid)

ioc_links_req = get_assets_ioc_links(caseid)

cache_ioc_link = {}
for ioc in ioc_links_req:

if ioc.asset_id not in cache_ioc_link:
cache_ioc_link[ioc.asset_id] = [ioc._asdict()]
else:
cache_ioc_link[ioc.asset_id].append(ioc._asdict())

cases_access = get_user_cases_fast(current_user.id)

for a in assets:
a['ioc_links'] = cache_ioc_link.get(a['asset_id'])

if len(assets) < 300:
# Find similar assets from other cases with the same customer
a['link'] = list(get_similar_assets(
a['asset_name'], a['asset_type_id'], caseid, customer_id, cases_access))
else:
a['link'] = []

ret['assets'] = assets

ret['state'] = get_assets_state(caseid=caseid)

return response_success("", data=ret)

@case_assets_blueprint.route('/case/assets/list', methods=['GET'])
@ac_api_case_requires(CaseAccessLevel.read_only, CaseAccessLevel.full_access)
def case_list_assets(caseid):
Expand All @@ -102,7 +143,6 @@ def case_list_assets(caseid):
:return: A JSON object containing the assets of the case, enhanced with assets seen on other cases.
"""

# Get all assets objects from the case and the customer id
assets = get_assets(caseid)
customer_id = get_case_client_id(caseid)

Expand Down
2 changes: 1 addition & 1 deletion source/app/blueprints/case/case_tasks_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -256,10 +256,10 @@ def case_edit_task(cur_id, caseid):
@case_tasks_blueprint.route('/case/tasks/delete/<int:cur_id>', methods=['POST'])
@ac_api_case_requires(CaseAccessLevel.full_access)
def case_delete_task(cur_id, caseid):
call_modules_hook('on_preload_task_delete', data=cur_id, caseid=caseid)
task = get_task_with_assignees(task_id=cur_id, case_id=caseid)
if not task:
return response_error("Invalid task ID for this case")
call_modules_hook('on_preload_task_delete', data=task, caseid=caseid)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe then we should update all preload deletions to work with a dump of the object? Normally preload hooks are only working with raw data, nothing from the DB, to avoid any conflicts.
I was acutally thinking of removing all preload in future versions. Do you have any use cases where working with preload instead of postload?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In our case, there is no difference between preload and postload. We need to track the deletion of cases and tasks. Since postload works after the deletion of object and it is impossible to process a non-existent object with a webhook module, so I tried to restore the correct operation of preload, of course we need information about the object in the output to work with it.


delete_task(task.id)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ <h4 class="modal-title mr-4">{{ "Asset #{}".format(asset.asset_id) if asset.asse
</div>
<button type="button" class="btn bg-transparent btn-xs" onclick="comment_element({{ asset.asset_id }}, 'assets')" title="Comments">
<span class="btn-label">
<i class="fa-solid fa-comments"></i><span class="notification" id="object_comments_number">{{ comments_map|length }}</span>
<i class="fa-solid fa-comments"></i><span class="notification" id="object_comments_number">{{ comments_map|length if comments_map|length > 0 else '' }}</span>
</span>
</button>
{% endif %}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ <h4 class="modal-title mr-4">{% if event.event_id %} Event ID #{{ event.event_i
</div>
<button type="button" class="btn bg-transparent btn-xs" onclick="comment_element({{ event.event_id }}, 'timeline/events')" title="Comments">
<span class="btn-label">
<i class="fa-solid fa-comments"></i><span class="notification" id="object_comments_number">{{ comments_map|length }}</span>
<i class="fa-solid fa-comments"></i><span class="notification" id="object_comments_number">{{ comments_map|length if comments_map|length > 0 else '' }}</span>
</span>
</button>
{% endif %}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ <h4 class="modal-title mr-4">{% if ioc.ioc_id %}Edit IOC #{{ ioc.ioc_id }}{% els
</div>
<button type="button" class="btn bg-transparent btn-xs" onclick="comment_element({{ ioc.ioc_id }}, 'ioc')" title="Comments">
<span class="btn-label">
<i class="fa-solid fa-comments"></i><span class="notification" id="object_comments_number">{{ comments_map|length }}</span>
<i class="fa-solid fa-comments"></i><span class="notification" id="object_comments_number">{{ comments_map|length if comments_map|length > 0 else '' }}</span>
</span>
</button>
{% endif %}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ <h4 class="modal-title mr-4">{% if rfile.id %}Edit evidence #{{rfile.id}}{% else
</div>
<button type="button" class="btn bg-transparent btn-xs" onclick="comment_element({{ rfile.id }}, 'evidences')" title="Comments">
<span class="btn-label">
<i class="fa-solid fa-comments"></i><span class="notification" id="object_comments_number">{{ comments_map|length }}</span>
<i class="fa-solid fa-comments"></i><span class="notification" id="object_comments_number">{{ comments_map|length if comments_map|length > 0 else '' }}</span>
</span>
</button>
{% endif %}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ <h4 class="modal-title mr-4">{% if task.id %} Task ID #{{ task.id }}{% else %}
</div>
<button type="button" class="btn bg-transparent btn-xs" onclick="comment_element({{ task.id }}, 'tasks')" title="Comments">
<span class="btn-label">
<i class="fa-solid fa-comments"></i><span class="notification" id="object_comments_number">{{ comments_map|length }}</span>
<i class="fa-solid fa-comments"></i><span class="notification" id="object_comments_number">{{ comments_map|length if comments_map|length > 0 else '' }}</span>
</span>
</button>
{% endif %}
Expand Down
2 changes: 1 addition & 1 deletion source/app/blueprints/case/templates/modal_note_edit.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ <h4 class="modal-title mr-4">Note #{{ note.note_id }}</h4>
</button>
<button type="button" class="btn bg-transparent btn-xs" onclick="comment_element({{ note.note_id }}, 'notes')" title="Comments">
<span class="btn-label">
<i class="fa-solid fa-comments"></i><span class="notification" id="object_comments_number">{{ comments_map|length }}</span>
<i class="fa-solid fa-comments"></i><span class="notification" id="object_comments_number">{{ comments_map|length if comments_map|length > 0 else '' }}</span>
</span>
</button>
<div class="dropdown">
Expand Down
Loading