first sync
Some checks failed
Deployment Verification / deploy-and-test (push) Failing after 29s

This commit is contained in:
2025-03-04 07:59:21 +01:00
parent 9cdcf486b6
commit 506716e703
1450 changed files with 577316 additions and 62 deletions

View File

@@ -0,0 +1,455 @@
#!/usr/bin/env python3
#
#
# IRIS Source Code
# Copyright (C) 2022 - DFIR IRIS Team
# contact@dfir-iris.org
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 3 of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
import base64
import datetime
import json
import marshmallow.exceptions
import urllib.parse
from flask import Blueprint
from flask import render_template
from flask import request
from flask import send_file
from flask import url_for
from flask_login import current_user
from pathlib import Path
from werkzeug.utils import redirect
import app
from app import db
from app.datamgmt.datastore.datastore_db import datastore_add_child_node
from app.datamgmt.datastore.datastore_db import datastore_add_file_as_evidence
from app.datamgmt.datastore.datastore_db import datastore_add_file_as_ioc
from app.datamgmt.datastore.datastore_db import datastore_delete_file
from app.datamgmt.datastore.datastore_db import datastore_delete_node
from app.datamgmt.datastore.datastore_db import datastore_filter_tree
from app.datamgmt.datastore.datastore_db import datastore_get_file
from app.datamgmt.datastore.datastore_db import datastore_get_interactive_path_node
from app.datamgmt.datastore.datastore_db import datastore_get_local_file_path
from app.datamgmt.datastore.datastore_db import datastore_get_path_node
from app.datamgmt.datastore.datastore_db import datastore_get_standard_path
from app.datamgmt.datastore.datastore_db import datastore_rename_node
from app.datamgmt.datastore.datastore_db import ds_list_tree
from app.forms import ModalDSFileForm
from app.iris_engine.utils.tracker import track_activity
from app.models.authorization import CaseAccessLevel
from app.schema.marshables import DSFileSchema, DSPathSchema
from app.util import ac_api_case_requires
from app.util import ac_case_requires
from app.util import add_obj_history_entry
from app.util import response_error
from app.util import response_success
datastore_blueprint = Blueprint(
'datastore',
__name__,
template_folder='templates'
)
logger = app.logger
@datastore_blueprint.route('/datastore/list/tree', methods=['GET'])
@ac_api_case_requires(CaseAccessLevel.read_only, CaseAccessLevel.full_access)
def datastore_list_tree(caseid):
data = ds_list_tree(caseid)
return response_success("", data=data)
@datastore_blueprint.route('/datastore/list/filter', methods=['GET'])
@ac_api_case_requires(CaseAccessLevel.read_only, CaseAccessLevel.full_access)
def datastore_list_filter(caseid):
args = request.args.to_dict()
query_filter = args.get('q')
try:
filter_d = dict(json.loads(urllib.parse.unquote_plus(query_filter)))
except Exception as e:
logger.error('Error parsing filter: {}'.format(query_filter))
logger.error(e)
return response_error('Invalid query')
data, log = datastore_filter_tree(filter_d, caseid)
if data is None:
logger.error('Error parsing filter: {}'.format(query_filter))
logger.error(log)
return response_error('Invalid query')
return response_success("", data=data)
@datastore_blueprint.route('/datastore/file/add/<int:cur_id>/modal', methods=['GET'])
@ac_case_requires(CaseAccessLevel.full_access)
def datastore_add_file_modal(cur_id: int, caseid: int, url_redir: bool):
if url_redir:
return redirect(url_for('index.index', cid=caseid, redirect=True))
dsp = datastore_get_path_node(cur_id, caseid)
if not dsp:
return response_error('Invalid path node for this case')
form = ModalDSFileForm()
return render_template("modal_ds_file.html", form=form, file=None, dsp=dsp)
@datastore_blueprint.route('/datastore/filter-help/modal', methods=['GET'])
@ac_case_requires(CaseAccessLevel.read_only, CaseAccessLevel.full_access)
def datastore_filter_help_modal(caseid, url_redir):
if url_redir:
return redirect(url_for('index.index', cid=caseid, redirect=True))
return render_template("modal_help_filter_ds.html")
@datastore_blueprint.route('/datastore/file/update/<int:cur_id>/modal', methods=['GET'])
@ac_case_requires(CaseAccessLevel.read_only, CaseAccessLevel.full_access)
def datastore_update_file_modal(cur_id: int, caseid: int, url_redir: bool):
if url_redir:
return redirect(url_for('index.index', cid=caseid, redirect=True))
file = datastore_get_file(cur_id, caseid)
if not file:
return response_error('Invalid file ID for this case')
dsp = datastore_get_path_node(file.file_parent_id, caseid)
form = ModalDSFileForm()
form.file_is_ioc.data = file.file_is_ioc
form.file_original_name.data = file.file_original_name
form.file_password.data = file.file_password
form.file_password.render_kw = {'disabled': 'disabled'}
form.file_description.data = file.file_description
form.file_is_evidence.data = file.file_is_evidence
return render_template("modal_ds_file.html", form=form, file=file, dsp=dsp)
@datastore_blueprint.route('/datastore/file/info/<int:cur_id>/modal', methods=['GET'])
@ac_case_requires(CaseAccessLevel.read_only, CaseAccessLevel.full_access)
def datastore_info_file_modal(cur_id: int, caseid: int, url_redir: bool):
if url_redir:
return redirect(url_for('index.index', cid=caseid, redirect=True))
file = datastore_get_file(cur_id, caseid)
if not file:
return response_error('Invalid file ID for this case')
dsp = datastore_get_path_node(file.file_parent_id, caseid)
return render_template("modal_ds_file_info.html", file=file, dsp=dsp)
@datastore_blueprint.route('/datastore/file/info/<int:cur_id>', methods=['GET'])
@ac_api_case_requires(CaseAccessLevel.read_only, CaseAccessLevel.full_access)
def datastore_info_file(cur_id: int, caseid: int):
file = datastore_get_file(cur_id, caseid)
if not file:
return response_error('Invalid file ID for this case')
file_schema = DSFileSchema()
file = file_schema.dump(file)
del file['file_local_name']
return response_success("", data=file)
@datastore_blueprint.route('/datastore/file/update/<int:cur_id>', methods=['POST'])
@ac_api_case_requires(CaseAccessLevel.full_access)
def datastore_update_file(cur_id: int, caseid: int):
dsf = datastore_get_file(cur_id, caseid)
if not dsf:
return response_error('Invalid file ID for this case')
dsf_schema = DSFileSchema()
try:
dsf_sc = dsf_schema.load(request.form, instance=dsf, partial=True)
add_obj_history_entry(dsf_sc, 'updated')
dsf.file_is_ioc = request.form.get('file_is_ioc') is not None or request.form.get('file_is_ioc') is True
dsf.file_is_evidence = request.form.get('file_is_evidence') is not None or request.form.get('file_is_evidence') is True
db.session.commit()
if request.files.get('file_content'):
ds_location = datastore_get_standard_path(dsf_sc, caseid)
dsf_sc.file_local_name, dsf_sc.file_size, dsf_sc.file_sha256 = dsf_schema.ds_store_file(
request.files.get('file_content'),
ds_location,
dsf_sc.file_is_ioc,
dsf_sc.file_password)
if dsf_sc.file_is_ioc and not dsf_sc.file_password:
dsf_sc.file_password = 'infected'
db.session.commit()
msg_added_as = ''
if dsf.file_is_ioc:
datastore_add_file_as_ioc(dsf, caseid)
msg_added_as += 'and added in IOC'
if dsf.file_is_evidence:
datastore_add_file_as_evidence(dsf, caseid)
msg_added_as += ' and evidence' if len(msg_added_as) > 0 else 'and added in evidence'
track_activity(f'File \"{dsf.file_original_name}\" updated in DS', caseid=caseid)
return response_success('File updated in datastore', data=dsf_schema.dump(dsf_sc))
except marshmallow.exceptions.ValidationError as e:
return response_error(msg="Data error", data=e.messages)
@datastore_blueprint.route('/datastore/file/move/<int:cur_id>', methods=['POST'])
@ac_api_case_requires(CaseAccessLevel.full_access)
def datastore_move_file(cur_id: int, caseid: int):
if not request.json:
return response_error("Invalid data")
dsf = datastore_get_file(cur_id, caseid)
if not dsf:
return response_error('Invalid file ID for this case')
dsp = datastore_get_path_node(request.json.get('destination-node'), caseid)
if not dsp:
return response_error('Invalid destination node ID for this case')
dsf.file_parent_id = dsp.path_id
db.session.commit()
track_activity(f'File \"{dsf.file_original_name}\" moved to \"{dsp.path_name}\" in DS', caseid=caseid)
return response_success(f"File successfully moved to {dsp.path_name}")
@datastore_blueprint.route('/datastore/folder/move/<int:cur_id>', methods=['POST'])
@ac_api_case_requires(CaseAccessLevel.full_access)
def datastore_move_folder(cur_id: int, caseid: int):
if not request.json:
return response_error("Invalid data")
dsp = datastore_get_path_node(cur_id, caseid)
if not dsp:
return response_error('Invalid file ID for this case')
dsp_dst = datastore_get_path_node(request.json.get('destination-node'), caseid)
if not dsp_dst:
return response_error('Invalid destination node ID for this case')
if dsp.path_id == dsp_dst.path_id:
return response_error("If that's true, then I've made a mistake, and you should kill me now.")
dsp.path_parent_id = dsp_dst.path_id
db.session.commit()
dsf_folder_schema = DSPathSchema()
msg = f"Folder \"{dsp.path_name}\" successfully moved to \"{dsp_dst.path_name}\""
track_activity(msg, caseid=caseid)
return response_success(msg, data=dsf_folder_schema.dump(dsp))
@datastore_blueprint.route('/datastore/file/view/<int:cur_id>', methods=['GET'])
@ac_api_case_requires(CaseAccessLevel.read_only, CaseAccessLevel.full_access)
def datastore_view_file(cur_id: int, caseid: int):
has_error, dsf = datastore_get_local_file_path(cur_id, caseid)
if has_error:
return response_error('Unable to get requested file ID', data=dsf)
if dsf.file_is_ioc or dsf.file_password:
dsf.file_original_name += ".zip"
if not Path(dsf.file_local_name).is_file():
return response_error(f'File {dsf.file_local_name} does not exists on the server. '
f'Update or delete virtual entry')
resp = send_file(dsf.file_local_name, as_attachment=True,
download_name=dsf.file_original_name)
track_activity(f"File \"{dsf.file_original_name}\" downloaded", caseid=caseid, display_in_ui=False)
return resp
@datastore_blueprint.route('/datastore/file/add/<int:cur_id>', methods=['POST'])
@ac_api_case_requires(CaseAccessLevel.full_access)
def datastore_add_file(cur_id: int, caseid: int):
dsp = datastore_get_path_node(cur_id, caseid)
if not dsp:
return response_error('Invalid path node for this case')
dsf_schema = DSFileSchema()
try:
dsf_sc = dsf_schema.load(request.form, partial=True)
dsf_sc.file_parent_id = dsp.path_id
dsf_sc.added_by_user_id = current_user.id
dsf_sc.file_date_added = datetime.datetime.now()
dsf_sc.file_local_name = 'tmp_xc'
dsf_sc.file_case_id = caseid
add_obj_history_entry(dsf_sc, 'created')
if dsf_sc.file_is_ioc and not dsf_sc.file_password:
dsf_sc.file_password = 'infected'
db.session.add(dsf_sc)
db.session.commit()
ds_location = datastore_get_standard_path(dsf_sc, caseid)
dsf_sc.file_local_name, dsf_sc.file_size, dsf_sc.file_sha256 = dsf_schema.ds_store_file(
request.files.get('file_content'),
ds_location,
dsf_sc.file_is_ioc,
dsf_sc.file_password)
db.session.commit()
msg_added_as = ''
if dsf_sc.file_is_ioc:
datastore_add_file_as_ioc(dsf_sc, caseid)
msg_added_as += 'and added in IOC'
if dsf_sc.file_is_evidence:
datastore_add_file_as_evidence(dsf_sc, caseid)
msg_added_as += ' and evidence' if len(msg_added_as) > 0 else 'and added in evidence'
track_activity(f"File \"{dsf_sc.file_original_name}\" added to DS", caseid=caseid)
return response_success(f'File saved in datastore {msg_added_as}', data=dsf_schema.dump(dsf_sc))
except marshmallow.exceptions.ValidationError as e:
return response_error(msg="Data error", data=e.messages)
@datastore_blueprint.route('/datastore/file/add-interactive', methods=['POST'])
@ac_api_case_requires(CaseAccessLevel.full_access)
def datastore_add_interactive_file(caseid: int):
dsp = datastore_get_interactive_path_node(caseid)
if not dsp:
return response_error('Invalid path node for this case')
dsf_schema = DSFileSchema()
try:
js_data = request.json
try:
file_content = base64.b64decode(js_data.get('file_content'))
filename = js_data.get('file_original_name')
except Exception as e:
return response_error(msg=str(e))
dsf_sc, existed = dsf_schema.ds_store_file_b64(filename, file_content, dsp, caseid)
if not existed:
msg = "File saved in datastore"
else:
msg = "File already existing in datastore. Using it."
track_activity(f"File \"{dsf_sc.file_original_name}\" added to DS", caseid=caseid)
return response_success(msg, data={"file_url": f"/datastore/file/view/{dsf_sc.file_id}"})
except marshmallow.exceptions.ValidationError as e:
return response_error(msg="Data error", data=e.messages)
@datastore_blueprint.route('/datastore/folder/add', methods=['POST'])
@ac_api_case_requires(CaseAccessLevel.full_access)
def datastore_add_folder(caseid: int):
data = request.json
if not data:
return response_error('Invalid data')
parent_node = data.get('parent_node')
folder_name = data.get('folder_name')
if not parent_node or not folder_name:
return response_error('Invalid data')
has_error, logs, node = datastore_add_child_node(parent_node, folder_name, caseid)
dsf_folder_schema = DSPathSchema()
if not has_error:
track_activity(f"Folder \"{folder_name}\" added to DS", caseid=caseid)
return response_success(msg=logs, data=dsf_folder_schema.dump(node))
return response_error(msg=logs)
@datastore_blueprint.route('/datastore/folder/rename/<int:cur_id>', methods=['POST'])
@ac_api_case_requires(CaseAccessLevel.full_access)
def datastore_rename_folder(cur_id: int, caseid: int):
data = request.json
if not data:
return response_error('Invalid data')
parent_node = data.get('parent_node')
folder_name = data.get('folder_name')
if not parent_node or not folder_name:
return response_error('Invalid data')
if int(parent_node) != cur_id:
return response_error('Invalid data')
has_error, logs, dsp_base = datastore_rename_node(parent_node, folder_name, caseid)
dsf_folder_schema = DSPathSchema()
if has_error:
return response_error(logs)
track_activity(f"Folder \"{parent_node}\" renamed to \"{folder_name}\" in DS", caseid=caseid)
return response_success(logs, data=dsf_folder_schema.dump(dsp_base))
@datastore_blueprint.route('/datastore/folder/delete/<int:cur_id>', methods=['POST'])
@ac_api_case_requires(CaseAccessLevel.full_access)
def datastore_delete_folder_route(cur_id: int, caseid: int):
has_error, logs = datastore_delete_node(cur_id, caseid)
if has_error:
return response_error(logs)
track_activity(f"Folder \"{cur_id}\" deleted from DS", caseid=caseid)
return response_success(logs)
@datastore_blueprint.route('/datastore/file/delete/<int:cur_id>', methods=['POST'])
@ac_api_case_requires(CaseAccessLevel.full_access)
def datastore_delete_file_route(cur_id: int, caseid: int):
has_error, logs = datastore_delete_file(cur_id, caseid)
if has_error:
return response_error(logs)
track_activity(f"File \"{cur_id}\" deleted from DS", caseid=caseid)
return response_success(logs)

View File

@@ -0,0 +1,119 @@
<div class="modal-header">
<h4 class="modal-title mt-2 mr-4">Datastore File</h4>
<div class="row text-right">
<button type="button" class="pull-right btn bg-transparent" data-dismiss="modal" aria-label="Close"><span
aria-hidden="true"><i class="fa fa-times"></i></span></button>
</div>
</div>
<div class="modal-body">
<div class="container col-md-12">
<form method="post" action="" id="form_new_ds_file">
<div class="col-md-12 col-lg-12 col-sm-12">
{{ form.hidden_tag() }}
{% if file.file_id %}
<div class="row ml-2">
<p>The file is currently saved in virtual folder <code>{{ dsp.path_name }}</code>.</p>
</div>
{% else %}
<div class="row ml-2">
<p>The file will be saved in virtual folder <code>{{ dsp.path_name }}</code>.</p>
</div>
{% endif %}
<div class="form-row ml-2">
<div class="form-group col-12">
<label for="input_upload_ds_file" class="form-label">Choose file to upload : </label>
<input id="input_upload_ds_file" class="form-control" type="file">
</div>
</div>
<div class="form-row ml-2">
<div class="form-group col-12">
<label for="file_original_name" class="placeholder">Filename *</label>
{{ form.file_original_name(class='form-control', autocomplete="off") }}
</div>
</div>
<div class="form-row ml-2">
<div class="form-group col-12">
<label for="file_description" class="placeholder">Description</label>
{{ form.file_description(class='form-control col-md-12 col-sm-12 sizable-textarea', autocomplete="off") }}
</div>
</div>
<div class="form-row ml-2">
<div class="form-group col-6 col-xs-12">
<label for="file_password" class="placeholder">Password<i class="ml-1 mt-1 fa-regular text-dark fa-circle-question"
title="Help" data-toggle="popover" data-html="true"
data-trigger="hover" style="cursor: pointer;"
data-content="If set, the file is locally encrypted with this password.<br/><b class='text-danger'>Passwords are stored in clear text server side. Do not put sensitive password here.</b><br/>Encrypted files cannot be used in notes.<br/>IOC are automatically encrypted with password <code>infected</code> unless specified otherwise here.">
</i></label>
<div class="input-group mb-2 mr-sm-2">
{{ form.file_password(class='form-control', autocomplete="off", type="password") }}
<div class="input-group-append">
<div class="input-group-text" id="toggle_file_password"><i class="fa-solid fa-eye"></i></div>
</div>
</div>
</div>
<div class="form-group col-6 col-xs-12">
<label for="file_tags">File tags</label>
<input type="text" id="file_tags" name="file_tags"
class="form-control col-md-12" {% if file.file_tags %} value="{{ file.file_tags }}" {% endif %}/>
</div>
</div>
<div class="form-row ml-2">
<div class="form-group col-6 col-xs-12">
<div class="form-check">
<label class="form-check-label">
{{ form.file_is_ioc(class="form-check-input", type="checkbox") }}
<span class="form-check-sign"> File is IOC <i class="ml-1 mt-1 fa-regular text-dark fa-circle-question"
title="Help" data-toggle="popover" data-html="true"
data-trigger="hover" style="cursor: pointer;"
data-content="If set, the file is stored in a dedicated IOC folder on the server and is encrypted with password <code>infected</code> unless specified otherwise in the password field.<br/> The file is also added to the case IOC.">
</i></span>
</label>
</div>
</div>
<div class="form-group col-6 col-xs-12">
<div class="form-check">
<label class="form-check-label">
{{ form.file_is_evidence(class="form-check-input", type="checkbox") }}
<span class="form-check-sign"> File is Evidence <i class="ml-1 mt-1 fa-regular text-dark fa-circle-question"
title="Help" data-toggle="popover" data-html="true"
data-trigger="hover" style="cursor: pointer;"
data-content="If set, the file is stored in a dedicated Evidence folder on the server and added to the case Evidences.">
</i></span>
</label>
</div>
</div>
</div>
</div>
{% if file.file_id %}
<button type="button" class="btn btn-outline-danger ml-4 mt-5"
onclick="delete_ds_file({{ file.file_id }});">Delete</button>
{% endif %}
<button type="button" class="btn btn-outline-success ml-4 mt-5 float-right" onclick="save_ds_file({{dsp.path_id}}, {{ file.file_id }});return false;">Save</button>
</form>
</div>
</div>
<script>
$('[data-toggle="popover"]').popover();
$('#toggle_file_password').on('click', function (e) {
const type = $('#file_password').attr('type') === 'password' ? 'text' : 'password';
$('#file_password').attr('type', type);
$('#toggle_file_password > i').attr('class', type === 'password' ? 'fa-solid fa-eye' : 'fa-solid fa-eye-slash');
});
$('#file_tags').amsifySuggestags({
printValues: true,
{% if file.file_tags %}
suggestions: [ {% for tag in file.file_tags %} '{{ tag }}', {% endfor %} ],
{% endif %}
whiteList: false,
selectOnHover: false,
});
$("#input_upload_ds_file").on("change", function(e) {
var file = e.target.files[0].name;
$('#file_original_name').val(file);
});
</script>

View File

@@ -0,0 +1,74 @@
<div class="modal-header">
<h4 class="modal-title mt-2 mr-4">Datastore File {{ file.file_original_name }}</h4>
<div class="row text-right">
<button type="button" class="pull-right btn bg-transparent" data-dismiss="modal" aria-label="Close"><span
aria-hidden="true"><i class="fa fa-times"></i></span></button>
</div>
</div>
<div class="modal-body">
<div class="container col-md-12">
<div class="col-md-12 col-lg-12 col-sm-12">
<h3>File information</h3>
<dl class="row mt-2">
<dt class="col-sm-3">Virtual location: </dt>
<dd class="col-sm-8">{{ dsp.path_name }}</dd>
<dt class="col-sm-3">Original file name: </dt>
<dd class="col-sm-8">{{ file.file_original_name }}</dd>
<dt class="col-sm-3">File description: </dt>
<dd class="col-sm-8">{{ file.file_description }}</dd>
<dt class="col-sm-3">Storage UUID: </dt>
<dd class="col-sm-8">dsf-{{ file.file_uuid }}</dd>
<dt class="col-sm-3">Storage ID: </dt>
<dd class="col-sm-8">dsf-{{ file.file_id }}</dd>
<dt class="col-sm-3">Tags: </dt>
<dd class="col-sm-8">{% for tag in file.file_tags.split(',') %} <div class="badge badge-light">{{ tag }}</div> {% endfor %}</dd>
<dt class="col-sm-3">SHA256: </dt>
<dd class="col-sm-8">{{ file.file_sha256 }}</dd>
<dt class="col-sm-3">Size (bytes): </dt>
<dd class="col-sm-8">{{ file.file_size }}</dd>
<dt class="col-sm-3">Is evidence: </dt>
<dd class="col-sm-8">{{ file.file_is_evidence }}</dd>
<dt class="col-sm-3">Is IOC: </dt>
<dd class="col-sm-8">{{ file.file_is_ioc }}</dd>
<dt class="col-sm-3">Is password protected: </dt>
<dd class="col-sm-8">{% if file.file_password %} True {% else %} False {% endif %}</dd>
<dt class="col-sm-3">Password: </dt>
<dd class="col-sm-8">
<div class="row">
<input class="form_control ml-3" style="border:none;" type="password" value="{{ file.file_password }}" id="ds_file_password" disabled>
<div class="file_show_password" id="toggle_file_password"><i class="fa-solid fa-eye"></i></div>
</div>
</dd>
<dt class="col-sm-3 mt-4">Modification history: </dt>
<dd class="mt-4">
<ul>
{% if file.modification_history %}
{% for mod in file.modification_history %}
<li>{{ mod|format_datetime('%Y-%m-%d %H:%M') }} - {{ file.modification_history[mod].action }} by {{ file.modification_history[mod].user }} </li>
{% endfor %}
{% endif %}
</ul>
</dd>
</dl>
</div>
</div>
</div>
<script>
$('#toggle_file_password').on('click', function (e) {
const type = $('#ds_file_password').attr('type') === 'password' ? 'text' : 'password';
$('#ds_file_password').attr('type', type);
$('#toggle_file_password > i').attr('class', type === 'password' ? 'fa-solid fa-eye' : 'fa-solid fa-eye-slash');
});
</script>

View File

@@ -0,0 +1,32 @@
<div class="modal-xl modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title mt-1 mr-4">Datastore filtering help</h4>
<button type="button" class="float-right btn bg-transparent" data-dismiss="modal" aria-label="Close"><span
aria-hidden="true"><i class="fa fa-times"></i></span></button>
</div>
<div class="modal-body mb-2">
<div class="row">
<div class="col-12">
<p>Files can be filtered thanks to simple queries. The query schema is : <code>target_element:search_value AND target_element2:search_value2</code>.<br/>
There is no <code>OR</code> condition and searching without target does not work.
<p>The following target elements can be used to filter :</p>
<ul>
<li><code>name</code>: Name of the file</li>
<li><code>id</code>: ID of the file</li>
<li><code>uuid</code>: UUID of the file</li>
<li><code>storage_name</code>: Name of the file on the FS</li>
<li><code>tag</code>: Tag of the file</li>
<li><code>description</code>: Description of the file</li>
<li><code>is_ioc</code> : Set to any value to filter files which are IOCs</li>
<li><code>is_evidence</code>: Set to any value to filter files which are evidences</li>
<li><code>has_password</code>: Set to any value to filter files which have passwords</li>
<li><code>sha256</code>: SHA256 to filter files with</li>
</ul>
Example of filter :
<code>name: .exe AND is_ioc: true</code>
</div>
</div>
</div>
</div>
</div>