diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 3968d993f..2dbd64e99 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 2.4.14 +current_version = 2.4.16 commit = True tag = True parse = (?P\d+)\.(?P\d+)\.(?P\d+)(-(?P.*)-(?P\d+))? diff --git a/README.md b/README.md index 232abcd46..04514642c 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@

Incident Response Investigation System
- Current Version v2.4.14 + Current Version v2.4.16
Online Demonstration

@@ -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 diff --git a/deploy/kubernetes/charts/templates/postgres.yaml b/deploy/kubernetes/charts/templates/postgres.yaml index fa3e6066d..a0ea62ae1 100644 --- a/deploy/kubernetes/charts/templates/postgres.yaml +++ b/deploy/kubernetes/charts/templates/postgres.yaml @@ -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 }} @@ -101,4 +101,4 @@ spec: - port: {{ .Values.postgres.service.port }} selector: app: {{ .Values.postgres.app }} ---- \ No newline at end of file +--- diff --git a/docker-compose.yml b/docker-compose.yml index 42917bf03..b9b24a3f1 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -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: diff --git a/source/app/alembic/alembic_utils.py b/source/app/alembic/alembic_utils.py index f1f2e9bda..285ae664d 100644 --- a/source/app/alembic/alembic_utils.py +++ b/source/app/alembic/alembic_utils.py @@ -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 \ No newline at end of file + 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) \ No newline at end of file diff --git a/source/app/alembic/versions/d5a720d1b99b_add_alerts_indexes.py b/source/app/alembic/versions/d5a720d1b99b_add_alerts_indexes.py new file mode 100644 index 000000000..6fe3951d4 --- /dev/null +++ b/source/app/alembic/versions/d5a720d1b99b_add_alerts_indexes.py @@ -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') + diff --git a/source/app/blueprints/alerts/alerts_routes.py b/source/app/blueprints/alerts/alerts_routes.py index 5b0d540b0..5876f6e1d 100644 --- a/source/app/blueprints/alerts/alerts_routes.py +++ b/source/app/blueprints/alerts/alerts_routes.py @@ -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 @@ -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 @@ -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), @@ -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']) @@ -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) @@ -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() @@ -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) @@ -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() diff --git a/source/app/blueprints/alerts/templates/alerts.html b/source/app/blueprints/alerts/templates/alerts.html index c2a4d6cbe..a7c28c1ac 100644 --- a/source/app/blueprints/alerts/templates/alerts.html +++ b/source/app/blueprints/alerts/templates/alerts.html @@ -5,6 +5,8 @@ {% block stylesheets %} + + {% endblock stylesheets %} @@ -158,6 +160,10 @@ +
+ + +
diff --git a/source/app/blueprints/case/case_assets_routes.py b/source/app/blueprints/case/case_assets_routes.py index f3761c1ef..e7409e113 100644 --- a/source/app/blueprints/case/case_assets_routes.py +++ b/source/app/blueprints/case/case_assets_routes.py @@ -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 @@ -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): @@ -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) diff --git a/source/app/blueprints/case/case_tasks_routes.py b/source/app/blueprints/case/case_tasks_routes.py index 999500ce3..87456fc93 100644 --- a/source/app/blueprints/case/case_tasks_routes.py +++ b/source/app/blueprints/case/case_tasks_routes.py @@ -256,10 +256,10 @@ def case_edit_task(cur_id, caseid): @case_tasks_blueprint.route('/case/tasks/delete/', 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) delete_task(task.id) diff --git a/source/app/blueprints/case/templates/modal_add_case_asset.html b/source/app/blueprints/case/templates/modal_add_case_asset.html index 7b1283899..30f7b353b 100644 --- a/source/app/blueprints/case/templates/modal_add_case_asset.html +++ b/source/app/blueprints/case/templates/modal_add_case_asset.html @@ -20,7 +20,7 @@
{% endif %} diff --git a/source/app/blueprints/case/templates/modal_add_case_event.html b/source/app/blueprints/case/templates/modal_add_case_event.html index f3ecfb92d..2a6d4fa21 100644 --- a/source/app/blueprints/case/templates/modal_add_case_event.html +++ b/source/app/blueprints/case/templates/modal_add_case_event.html @@ -28,7 +28,7 @@ ` : ''} ${alert.alert_source_link ? `
Source Link:
-
${ +
${ alert.alert_source_link && alert.alert_source_link.startsWith('http') - ? `${alert.alert_source_link}` + ? `${alert.alert_source_link} + ` : 'No valid link provided' }
` : ''} ${alert.alert_source_ref ? `
Source Reference:
-
${alert.alert_source_ref}
+
+ ${alert.alert_source_ref} + +
` : ''} ${alert.alert_source_event_time ? `
Source Event Time:
-
${formatTime(alert.alert_source_event_time)} UTC
+
+ ${formatTime(alert.alert_source_event_time)} UTC + +
` : ''} ${alert.alert_creation_time ? `
IRIS Creation Time:
-
${formatTime(alert.alert_creation_time)} UTC
+
+ ${formatTime(alert.alert_creation_time)} UTC + +
` : ''}
@@ -1089,7 +1112,12 @@ function renderAlert(alert, expanded=false, modulesOptionsAlertReq, .map( (ioc) => ` - ${filterXSS(ioc.ioc_value)} + + ${filterXSS(ioc.ioc_value)} + + ${filterXSS(ioc.ioc_description)} ${ioc.ioc_type ? filterXSS(ioc.ioc_type.type_name) : '-'} ${filterXSS(ioc.ioc_tlp) ? ioc.ioc_tlp : '-'} @@ -1141,6 +1169,12 @@ function renderAlert(alert, expanded=false, modulesOptionsAlertReq, .map( (asset) => ` + + ${asset.asset_name ? filterXSS(asset.asset_name) : '-'} + + ${asset.asset_name ? filterXSS(asset.asset_name) : '-'} ${asset.asset_description ? filterXSS(asset.asset_description) : '-'} ${asset.asset_type ? filterXSS(asset.asset_type.asset_name) : '-'} @@ -1412,6 +1446,11 @@ async function updateAlerts(page, per_page, filters = {}, paging=false){ filterString || queryParams.get('filter_id') ? $('#resetFilters').show() : $('#resetFilters').hide(); alertsContainer.show(); + + $('.copy-btn').off().on('click', function() { + let value = $(this).data('value'); + copy_text_clipboard(value); + }); } $('#alertsPerPage').on('change', (e) => { diff --git a/source/app/static/assets/js/iris/case.asset.js b/source/app/static/assets/js/iris/case.asset.js index e5732cb6b..a455eba15 100644 --- a/source/app/static/assets/js/iris/case.asset.js +++ b/source/app/static/assets/js/iris/case.asset.js @@ -120,7 +120,7 @@ function add_assets() { function get_case_assets() { show_loader(); - get_request_api('/case/assets/list') + get_request_api('/case/assets/filter') .done(function (response) { if (response.status == 'success') { if (response.data != null) { @@ -133,7 +133,17 @@ function get_case_assets() { Table.clear(); Table.rows.add(jsdata.assets); Table.columns.adjust().draw(); - load_menu_mod_options('asset', Table, delete_asset); + load_menu_mod_options('asset', Table, delete_asset, [{ + type: 'option', + title: 'Check Alerts', + multi: false, + iconClass: 'fas fa-bell', + action: function(rows) { + let row = rows[0]; + let asset = get_row_value(row); + window.open(`/alerts?alert_assets=${asset}`, '_blank'); + } + }]); $('[data-toggle="popover"]').popover(); set_last_state(jsdata.state); hide_loader(); @@ -377,32 +387,40 @@ $(document).ready(function(){ if (row.link.length > 0) { let has_compro = false; let datacontent = 'data-content="'; - for (let idx in row.link) { - if (row.link[idx]['asset_compromise_status_id'] === 1) { + + row.link.forEach(link => { + const caseInfo = `Observed `; + const caseLink = `case #${link.case_id} `; + const date = link.case_open_date.replace('00:00:00 GMT', ''); + + if (link.asset_compromise_status_id === 1) { has_compro = true; - datacontent += `Observed as compromised
on case #${row.link[idx]['case_id']} (${row.link[idx]['case_open_date'].replace('00:00:00 GMT', '')}) for the same customer.

`; + datacontent += `${caseInfo} as compromised
on ${caseLink} (${date}) for the same customer.

`; } else { - - datacontent += `Observed as not compromised
on case #${row.link[idx]['case_id']} (${row.link[idx]['case_open_date'].replace('00:00:00 GMT', '')}) for the same customer.

`; + datacontent += `${caseInfo} as not compromised
on ${caseLink} (${date}) for the same customer.

`; } - } - if (has_compro) { - compro += ``; + } + + if (row.alerts.length > 0) { + let alerts_content = ""; + + row.alerts.forEach(alert => { + alerts_content += `#${alert.alert_id} - ${alert.alert_title.replace(/'/g, "'").replace(/"/g, """)}
`; + } ); + alerts_content += `More..`; + - compro += datacontent; - compro += '">'; + compro += ``; } let img = $('') .addClass('mr-2') .css({width: '1.5em', height: '1.5em'}) - .attr('src', '/static/assets/img/graph/' + (row['asset_compromise_status_id'] == 1 ? row['asset_icon_compromised'] : row['asset_icon_not_compromised'])) - .attr('title', row['asset_type']); + .attr('src', '/static/assets/img/graph/' + (row['asset_compromise_status_id'] == 1 ? row['asset_type']['asset_icon_compromised'] : row['asset_type']['asset_icon_not_compromised'])) + .attr('title', row['asset_type']['asset_name']); let link = $('') .attr('href', 'javascript:void(0);') @@ -419,7 +437,7 @@ $(document).ready(function(){ } }, { - "data": "asset_type", + "data": "asset_type.asset_name", "render": function (data, type, row, meta) { if (type === 'display') { data = sanitizeHTML(data);} return data; @@ -483,7 +501,7 @@ $(document).ready(function(){ "data": "analysis_status", "render": function(data, type, row, meta) { if (type === 'display') { - data = sanitizeHTML(data); + data = sanitizeHTML(data['name']); if (data == 'To be done') { flag = 'danger'; } else if (data == 'Started') { diff --git a/source/app/static/assets/js/iris/common.js b/source/app/static/assets/js/iris/common.js index cd51aa1c6..befd2ec56 100644 --- a/source/app/static/assets/js/iris/common.js +++ b/source/app/static/assets/js/iris/common.js @@ -626,7 +626,7 @@ function copy_object_link_md(data_type, node_id){ function copy_text_clipboardb(data){ navigator.clipboard.writeText(fromBinary64(data)).then(function() { - notify_success('Copied!'); + notify_success('Copied'); }, function(err) { notify_error('Can\'t copy link. I printed it in console.'); console.error(err); @@ -635,7 +635,7 @@ function copy_text_clipboardb(data){ function copy_text_clipboard(data){ navigator.clipboard.writeText(data).then(function() { - notify_success('Copied!'); + notify_success('Copied'); }, function(err) { notify_error('Can\'t copy link. I printed it in console.'); console.error(err); @@ -1081,7 +1081,7 @@ function goto_case_number() { } -function load_menu_mod_options(data_type, table, deletion_fn) { +function load_menu_mod_options(data_type, table, deletion_fn, additionalOptions = []) { var actionOptions = { classes: [], contextMenu: { @@ -1105,28 +1105,28 @@ function load_menu_mod_options(data_type, table, deletion_fn) { items: [], }; - datatype_map = { + let datatype_map = { 'task': 'tasks', 'ioc': 'ioc', 'evidence': 'evidences', 'note': 'notes', 'asset': 'assets', 'event': 'timeline/events' - } + }; - get_request_api("/dim/hooks/options/"+ data_type +"/list") + get_request_api("/dim/hooks/options/" + data_type + "/list") .done((data) => { - if(notify_auto_api(data, true)) { + if (notify_auto_api(data, true)) { if (data.data != null) { - jsdata = data.data; + let jsdata = data.data; actionOptions.items.push({ type: 'option', title: 'Share', multi: false, iconClass: 'fas fa-share', - action: function(rows){ - row = rows[0]; + action: function(rows) { + let row = rows[0]; copy_object_link(get_row_id(row)); } }); @@ -1136,8 +1136,8 @@ function load_menu_mod_options(data_type, table, deletion_fn) { title: 'Comment', multi: false, iconClass: 'fas fa-comments', - action: function(rows){ - row = rows[0]; + action: function(rows) { + let row = rows[0]; if (data_type in datatype_map) { comment_element(get_row_id(row), datatype_map[data_type]); } @@ -1149,8 +1149,8 @@ function load_menu_mod_options(data_type, table, deletion_fn) { title: 'Markdown Link', multi: false, iconClass: 'fa-brands fa-markdown', - action: function(rows){ - row = rows[0]; + action: function(rows) { + let row = rows[0]; copy_object_link_md(data_type, get_row_id(row)); } }); @@ -1160,19 +1160,22 @@ function load_menu_mod_options(data_type, table, deletion_fn) { title: 'Copy', multi: false, iconClass: 'fa-regular fa-copy', - action: function(rows){ - row = rows[0]; + action: function(rows) { + let row = rows[0]; copy_text_clipboard(get_row_value(row)); } }); + additionalOptions.forEach(option => { + actionOptions.items.push(option); + }); + actionOptions.items.push({ type: 'divider' }); - jdata_menu_options = jsdata; - for (option in jsdata) { - opt = jsdata[option]; + for (let option in jsdata) { + let opt = jsdata[option]; actionOptions.items.push({ type: 'option', @@ -1181,10 +1184,10 @@ function load_menu_mod_options(data_type, table, deletion_fn) { multiTitle: opt.manual_hook_ui_name, iconClass: 'fas fa-rocket', contextMenuClasses: ['text-dark'], - action: function (rows, de, ke) { + action: function(rows, de, ke) { init_module_processing_wrap(rows, data_type, de[0].outerText); }, - }) + }); } if (deletion_fn !== undefined) { @@ -1198,21 +1201,22 @@ function load_menu_mod_options(data_type, table, deletion_fn) { multi: false, iconClass: 'fas fa-trash', contextMenuClasses: ['text-danger'], - action: function(rows){ - row = rows[0]; + action: function(rows) { + let row = rows[0]; deletion_fn(get_row_id(row)); } }); } - tableActions = table.contextualActions(actionOptions); + let tableActions = table.contextualActions(actionOptions); tableActions.update(); } } - }) + }); } + function get_custom_attributes_fields() { values = Object(); has_error = []; @@ -1696,6 +1700,12 @@ function do_deletion_prompt(message, force_prompt=false) { } } +function escapeHtml(text) { + let parser = new DOMParser(); + let escapedDoc = parser.parseFromString(text, 'text/html'); + return escapedDoc.documentElement.textContent; +} + function toBinary64(string) { const codeUnits = new Uint16Array(string.length); for (let i = 0; i < codeUnits.length; i++) { diff --git a/source/dependencies/iris_webhooks_module-1.0.7-py3-none-any.whl b/source/dependencies/iris_webhooks_module-1.0.7-py3-none-any.whl deleted file mode 100644 index 87914c497..000000000 Binary files a/source/dependencies/iris_webhooks_module-1.0.7-py3-none-any.whl and /dev/null differ diff --git a/source/dependencies/iris_webhooks_module-1.0.8-py3-none-any.whl b/source/dependencies/iris_webhooks_module-1.0.8-py3-none-any.whl new file mode 100644 index 000000000..53886a6df Binary files /dev/null and b/source/dependencies/iris_webhooks_module-1.0.8-py3-none-any.whl differ diff --git a/source/requirements.txt b/source/requirements.txt index a29374dfc..8a70664dd 100644 --- a/source/requirements.txt +++ b/source/requirements.txt @@ -48,6 +48,6 @@ dependencies/iris_evtx-1.2.0-py3-none-any.whl dependencies/iris_check_module-1.0.1-py3-none-any.whl dependencies/iris_vt_module-1.2.1-py3-none-any.whl dependencies/iris_misp_module-1.3.0-py3-none-any.whl -dependencies/iris_webhooks_module-1.0.7-py3-none-any.whl +dependencies/iris_webhooks_module-1.0.8-py3-none-any.whl dependencies/iris_intelowl_module-0.1.0-py3-none-any.whl dependencies/iris_seika_module-1.0.0-py3-none-any.whl \ No newline at end of file