From ba1bb077234bc574c7d9e87b5b89d9f7e2336403 Mon Sep 17 00:00:00 2001 From: c8y3 <25362953+c8y3@users.noreply.github.com> Date: Tue, 6 Feb 2024 16:12:46 +0100 Subject: [PATCH 01/51] [ADD] First API test --- tests/README.md | 24 ++++++++++++ tests/data/basic.env | 61 ++++++++++++++++++++++++++++++ tests/docker_compose.py | 34 +++++++++++++++++ tests/iris.py | 70 +++++++++++++++++++++++++++++++++++ tests/requirements.txt | 1 + tests/rest_api.py | 50 +++++++++++++++++++++++++ tests/server_timeout_error.py | 20 ++++++++++ tests/tests.py | 34 +++++++++++++++++ 8 files changed, 294 insertions(+) create mode 100644 tests/README.md create mode 100644 tests/data/basic.env create mode 100644 tests/docker_compose.py create mode 100644 tests/iris.py create mode 100644 tests/requirements.txt create mode 100644 tests/rest_api.py create mode 100644 tests/server_timeout_error.py create mode 100644 tests/tests.py diff --git a/tests/README.md b/tests/README.md new file mode 100644 index 000000000..9b68a81d0 --- /dev/null +++ b/tests/README.md @@ -0,0 +1,24 @@ + +# Setup test environment +``` +python -m venv venv +source venv/bin/activate +pip install -r requirements.txt +``` + +# Run tests + +First activate the virtual environment: +``` +source ./venv/bin/activate +``` + +Then run: +``` +python -m unittest --verbose +``` + +To execute only one test, suffix with the fully qualified test name. Example: +``` +python -m unittest tests.Tests.test_create_asset_should_not_fail +``` diff --git a/tests/data/basic.env b/tests/data/basic.env new file mode 100644 index 000000000..76a6e035d --- /dev/null +++ b/tests/data/basic.env @@ -0,0 +1,61 @@ +# -- NGINX +SERVER_NAME=iris.app.dev +KEY_FILENAME=iris_dev_key.pem +CERT_FILENAME=iris_dev_cert.pem + +# -- DATABASE +POSTGRES_USER=postgres +POSTGRES_PASSWORD=__MUST_BE_CHANGED__ +POSTGRES_ADMIN_USER=raptor +POSTGRES_ADMIN_PASSWORD=__MUST_BE_CHANGED__ +POSTGRES_DB=iris_db + +POSTGRES_SERVER=db +POSTGRES_PORT=5432 + +# -- IRIS +DOCKERIZED=1 +IRIS_SECRET_KEY=AVerySuperSecretKey-SoNotThisOne +IRIS_SECURITY_PASSWORD_SALT=ARandomSalt-NotThisOneEither +IRIS_UPSTREAM_SERVER=app +IRIS_UPSTREAM_PORT=8000 + +# -- WORKER +CELERY_BROKER=amqp://rabbitmq + +# -- AUTH +IRIS_AUTHENTICATION_TYPE=local +## optional +IRIS_ADM_PASSWORD=MySuperAdminPassword! +IRIS_ADM_API_KEY=B8BA5D730210B50F41C06941582D7965D57319D5685440587F98DFDC45A01594 +#IRIS_ADM_EMAIL=admin@localhost +#IRIS_ADM_USERNAME=administrator +# requests the just-in-time creation of users with ldap authentification (see https://github.com/dfir-iris/iris-web/issues/203) +#IRIS_AUTHENTICATION_CREATE_USER_IF_NOT_EXIST=True +# the group to which newly created users are initially added, default value is Analysts +#IRIS_NEW_USERS_DEFAULT_GROUP= + +# -- FOR LDAP AUTHENTICATION +#IRIS_AUTHENTICATION_TYPE=ldap +#LDAP_SERVER=127.0.0.1 +#LDAP_AUTHENTICATION_TYPE=SIMPLE +#LDAP_PORT=3890 +#LDAP_USER_PREFIX=uid= +#LDAP_USER_SUFFIX=ou=people,dc=example,dc=com +#LDAP_USE_SSL=False +# base DN in which to search for users +#LDAP_SEARCH_DN=ou=users,dc=example,dc=org +# unique identifier to search the user +#LDAP_ATTRIBUTE_IDENTIFIER=cn +# name of the attribute to retrieve the user's display name +#LDAP_ATTRIBUTE_DISPLAY_NAME=displayName +# name of the attribute to retrieve the user's email address +#LDAP_ATTRIBUTE_MAIL=mail +#LDAP_VALIDATE_CERTIFICATE=True +#LDAP_TLS_VERSION=1.2 +#LDAP_SERVER_CERTIFICATE= +#LDAP_PRIVATE_KEY= +#LDAP_PRIVATE_KEY_PASSWORD= + +# -- LISTENING PORT +INTERFACE_HTTPS_PORT=443 diff --git a/tests/docker_compose.py b/tests/docker_compose.py new file mode 100644 index 000000000..d12e52375 --- /dev/null +++ b/tests/docker_compose.py @@ -0,0 +1,34 @@ +# IRIS Source Code +# Copyright (C) 2021 - Airbus CyberSecurity (SAS) +# ir@cyberactionlab.net +# +# 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 subprocess + + +class DockerCompose: + + def __init__(self, docker_compose_path): + self._docker_compose_path = docker_compose_path + + def start(self): + subprocess.run(['docker-compose', 'up', '--detach'], cwd=self._docker_compose_path) + + def extract_all_logs(self): + return subprocess.check_output(['docker-compose', 'logs', '--no-color'], cwd=self._docker_compose_path, universal_newlines=True) + def stop(self): + subprocess.run(['docker-compose', 'down'], cwd=self._docker_compose_path) + subprocess.run(['docker', 'volume', 'prune', '--force']) diff --git a/tests/iris.py b/tests/iris.py new file mode 100644 index 000000000..afb47adb2 --- /dev/null +++ b/tests/iris.py @@ -0,0 +1,70 @@ +# IRIS Source Code +# Copyright (C) 2021 - Airbus CyberSecurity (SAS) +# ir@cyberactionlab.net +# +# 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. + +from pathlib import Path +import shutil +from tempfile import TemporaryDirectory +import time +from docker_compose import DockerCompose +from rest_api import RestApi +from server_timeout_error import ServerTimeoutError + +_API_URL = 'http://127.0.0.1:8000' +_API_KEY = 'B8BA5D730210B50F41C06941582D7965D57319D5685440587F98DFDC45A01594' +_IRIS_PATH = Path('..') +_TEST_DATA_PATH = Path('./data') + +class Iris: + + def __init__(self): + self._docker_compose = DockerCompose(_IRIS_PATH) + self._api = RestApi(_API_URL, _API_KEY) + + def _wait(self, condition, attempts, sleep_duration=1): + count = 0 + while not condition(): + time.sleep(sleep_duration) + count += 1 + if count > attempts: + print('Docker compose logs: ', self._docker_compose.extract_all_logs()) + raise ServerTimeoutError() + + def _wait_until_api_is_ready(self): + self._wait(self._api.is_ready, 60) + + def start(self): + # TODO it would be preferable to have a dedicated directory with the docker-compose.yml file, + # because for now, it will overwrite the .env file and development/tests contexts are mixed up + # to do that, we should the building phase of dockers from the execution phase of the docker-compose + # we should minimize the docker-compose so that as few files as possible need to be copied + # also, we should try to use standard dockers as much as possible instead of having iris specific builds (for instance for the database) + shutil.copy2(_TEST_DATA_PATH.joinpath('basic.env'), _IRIS_PATH.joinpath('.env')) + self._docker_compose.start() + print('Waiting for DFIR-IRIS to start...') + self._wait_until_api_is_ready() + + def stop(self): + self._docker_compose.stop() + + def create_asset(self): + body = { + 'asset_type_id': '9', + 'asset_name': 'admin_laptop', + } + response = self._api.post('/case/assets/add', body) + return response.json() diff --git a/tests/requirements.txt b/tests/requirements.txt new file mode 100644 index 000000000..4d6d5ef1a --- /dev/null +++ b/tests/requirements.txt @@ -0,0 +1 @@ +requests >= 2.31.0, < 3.0.0 diff --git a/tests/rest_api.py b/tests/rest_api.py new file mode 100644 index 000000000..38f3f9d82 --- /dev/null +++ b/tests/rest_api.py @@ -0,0 +1,50 @@ +# IRIS Source Code +# Copyright (C) 2021 - Airbus CyberSecurity (SAS) +# ir@cyberactionlab.net +# +# 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 requests +from requests.exceptions import ConnectionError +from urllib import parse + + +class RestApi: + + def __init__(self, url, api_key): + self._url = url + self._headers = {'Authorization': f'Bearer {api_key}', 'Content-Type': 'application/json'} + + def _build_url(self, path): + return parse.urljoin(self._url, path) + + def get(self, path, query_parameters=None): + url = self._build_url(path) + response = requests.get(url, headers=self._headers, params=query_parameters) + print(f'GET {url} => {response.status_code}') + return response + + def post(self, path, payload): + url = self._build_url(path) + response = requests.post(url, headers=self._headers, json=payload) + print(f'POST {url} {payload} => {response.status_code}') + return response + + def is_ready(self): + try: + requests.head(self._url) + return True + except ConnectionError: + return False \ No newline at end of file diff --git a/tests/server_timeout_error.py b/tests/server_timeout_error.py new file mode 100644 index 000000000..53369faef --- /dev/null +++ b/tests/server_timeout_error.py @@ -0,0 +1,20 @@ +# IRIS Source Code +# Copyright (C) 2021 - Airbus CyberSecurity (SAS) +# ir@cyberactionlab.net +# +# 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. + +class ServerTimeoutError(Exception): + pass \ No newline at end of file diff --git a/tests/tests.py b/tests/tests.py new file mode 100644 index 000000000..f2501aac8 --- /dev/null +++ b/tests/tests.py @@ -0,0 +1,34 @@ +# IRIS Source Code +# Copyright (C) 2021 - Airbus CyberSecurity (SAS) +# ir@cyberactionlab.net +# +# 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. + +from unittest import TestCase +from iris import Iris + + +class Tests(TestCase): + + def setUp(self) -> None: + self._subject = Iris() + self._subject.start() + + def tearDown(self) -> None: + self._subject.stop() + + def test_create_asset_should_not_fail(self): + response = self._subject.create_asset() + self.assertEqual('success', response['status']) From a07d444bca87d4c804a9d0b726542f6b5ceffac2 Mon Sep 17 00:00:00 2001 From: c8y3 <25362953+c8y3@users.noreply.github.com> Date: Wed, 7 Feb 2024 08:20:04 +0100 Subject: [PATCH 02/51] [ADD] Github actions continuous integration file, with just the code checkout first --- .github/workflows/ci.yml | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 000000000..0835967f2 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,30 @@ +# IRIS Source Code +# Copyright (C) 2021 - Airbus CyberSecurity (SAS) +# ir@cyberactionlab.net +# +# 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. + +name: Continuous Integration +on: push + +jobs: + + tests: + name: Build iris dockers and run API tests + runs-on: ubuntu-22.04 + steps: + - name: Check out iris + uses: actions/checkout@v4 + From 4aaca9fd664cf217192094ff2f8689fa710a62c1 Mon Sep 17 00:00:00 2001 From: c8y3 <25362953+c8y3@users.noreply.github.com> Date: Wed, 7 Feb 2024 08:34:28 +0100 Subject: [PATCH 03/51] [ADD] Next step in continuous integration consists of building the dockers --- .github/workflows/ci.yml | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0835967f2..003776f8c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -27,4 +27,13 @@ jobs: steps: - name: Check out iris uses: actions/checkout@v4 - + - name: Build dockers + run: | + # TODO using the environment file from tests to build here. + # I am a bit uneasy with this choice. + # For now this works, but if we come to have different .env files for different tests, it won't anymore. + # Maybe the .env should be split to differentiate the variables used during the build from the variables used at runtime, + # or maybe the docker building phase should also be part of the tests + # and we should build different dockers according to the scenarios? This sounds like an issue to me... + cp tests/data/basic.env .env + docker-compose build From b29081c09d19db80035e257eb9facd82c111f7c0 Mon Sep 17 00:00:00 2001 From: c8y3 <25362953+c8y3@users.noreply.github.com> Date: Wed, 7 Feb 2024 08:49:53 +0100 Subject: [PATCH 04/51] [ADD] Last step of CI to execute the backend tests --- .github/workflows/ci.yml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 003776f8c..fcff27d41 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,7 +22,7 @@ on: push jobs: tests: - name: Build iris dockers and run API tests + name: API tests runs-on: ubuntu-22.04 steps: - name: Check out iris @@ -37,3 +37,11 @@ jobs: # and we should build different dockers according to the scenarios? This sounds like an issue to me... cp tests/data/basic.env .env docker-compose build + - name: Run tests + working-directory: tests + run: | + python -m venv venv + source venv/bin/activate + pip install -r requirements.txt + PYTHONUNBUFFERED=true python -m unittest --verbose + From c24e6a1fa2b1c95e718113530ec07257bcfcd204 Mon Sep 17 00:00:00 2001 From: c8y3 <25362953+c8y3@users.noreply.github.com> Date: Wed, 7 Feb 2024 09:29:13 +0100 Subject: [PATCH 05/51] [ADD] Test which retrieves the API version --- tests/iris.py | 4 ++++ tests/tests.py | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/tests/iris.py b/tests/iris.py index afb47adb2..7a81f82a0 100644 --- a/tests/iris.py +++ b/tests/iris.py @@ -61,6 +61,10 @@ def start(self): def stop(self): self._docker_compose.stop() + def get_api_version(self): + response = self._api.get('api/versions') + return response.json() + def create_asset(self): body = { 'asset_type_id': '9', diff --git a/tests/tests.py b/tests/tests.py index f2501aac8..6f07e83ba 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -32,3 +32,7 @@ def tearDown(self) -> None: def test_create_asset_should_not_fail(self): response = self._subject.create_asset() self.assertEqual('success', response['status']) + + def test_get_api_version_should_not_fail(self): + response = self._subject.get_api_version() + self.assertEqual('success', response['status']) From 78443dfa07e5ee3a54f05f56729f30daeda76ab9 Mon Sep 17 00:00:00 2001 From: c8y3 <25362953+c8y3@users.noreply.github.com> Date: Wed, 7 Feb 2024 09:51:50 +0100 Subject: [PATCH 06/51] [ADD] Test about the creation of a case --- tests/docker_compose.py | 2 +- tests/iris.py | 18 ++++++++++++++---- tests/rest_api.py | 10 ++++++---- tests/tests.py | 6 ++++++ 4 files changed, 27 insertions(+), 9 deletions(-) diff --git a/tests/docker_compose.py b/tests/docker_compose.py index d12e52375..fe12ea040 100644 --- a/tests/docker_compose.py +++ b/tests/docker_compose.py @@ -31,4 +31,4 @@ def extract_all_logs(self): return subprocess.check_output(['docker-compose', 'logs', '--no-color'], cwd=self._docker_compose_path, universal_newlines=True) def stop(self): subprocess.run(['docker-compose', 'down'], cwd=self._docker_compose_path) - subprocess.run(['docker', 'volume', 'prune', '--force']) + subprocess.run(['docker', 'volume', 'prune', '--all', '--force']) diff --git a/tests/iris.py b/tests/iris.py index 7a81f82a0..0f7c342bf 100644 --- a/tests/iris.py +++ b/tests/iris.py @@ -62,13 +62,23 @@ def stop(self): self._docker_compose.stop() def get_api_version(self): - response = self._api.get('api/versions') - return response.json() + return self._api.get('api/versions') def create_asset(self): body = { 'asset_type_id': '9', 'asset_name': 'admin_laptop', } - response = self._api.post('/case/assets/add', body) - return response.json() + return self._api.post('/case/assets/add', body) + + def create_case(self): + body = { + 'case_name': 'case name', + 'case_description': 'description', + 'case_customer': 1, + 'case_soc_id': '' + } + return self._api.post('/manage/cases/add', body) + + def get_cases(self): + return self._api.get('/manage/cases/list') diff --git a/tests/rest_api.py b/tests/rest_api.py index 38f3f9d82..e971b3b54 100644 --- a/tests/rest_api.py +++ b/tests/rest_api.py @@ -33,14 +33,16 @@ def _build_url(self, path): def get(self, path, query_parameters=None): url = self._build_url(path) response = requests.get(url, headers=self._headers, params=query_parameters) - print(f'GET {url} => {response.status_code}') - return response + body = response.json() + print(f'GET {url} => {response.status_code} {body}') + return body def post(self, path, payload): url = self._build_url(path) response = requests.post(url, headers=self._headers, json=payload) - print(f'POST {url} {payload} => {response.status_code}') - return response + body = response.json() + print(f'POST {url} {payload} => {response.status_code} {body}') + return body def is_ready(self): try: diff --git a/tests/tests.py b/tests/tests.py index 6f07e83ba..3083744f4 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -36,3 +36,9 @@ def test_create_asset_should_not_fail(self): def test_get_api_version_should_not_fail(self): response = self._subject.get_api_version() self.assertEqual('success', response['status']) + + def test_create_case_should_add_a_new_case(self): + self._subject.create_case() + response = self._subject.get_cases() + case_count = len(response['data']) + self.assertEqual(2, case_count) From b59b7d5ca6e035cd1717ff751c89a88838bd08fa Mon Sep 17 00:00:00 2001 From: c8y3 <25362953+c8y3@users.noreply.github.com> Date: Wed, 7 Feb 2024 13:34:10 +0100 Subject: [PATCH 07/51] [FIX] Missing line --- tests/docker_compose.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/docker_compose.py b/tests/docker_compose.py index fe12ea040..7726debef 100644 --- a/tests/docker_compose.py +++ b/tests/docker_compose.py @@ -29,6 +29,7 @@ def start(self): def extract_all_logs(self): return subprocess.check_output(['docker-compose', 'logs', '--no-color'], cwd=self._docker_compose_path, universal_newlines=True) + def stop(self): subprocess.run(['docker-compose', 'down'], cwd=self._docker_compose_path) subprocess.run(['docker', 'volume', 'prune', '--all', '--force']) From 8034b86c465d1d4e51d3fa1baa06e446ba4e5b22 Mon Sep 17 00:00:00 2001 From: c8y3 <25362953+c8y3@users.noreply.github.com> Date: Fri, 9 Feb 2024 10:44:32 +0100 Subject: [PATCH 08/51] [FIX] some deepsource feedback --- tests/docker_compose.py | 6 +++--- tests/iris.py | 16 ++++++++++------ tests/rest_api.py | 2 +- tests/server_timeout_error.py | 2 +- 4 files changed, 15 insertions(+), 11 deletions(-) diff --git a/tests/docker_compose.py b/tests/docker_compose.py index 7726debef..922f3e852 100644 --- a/tests/docker_compose.py +++ b/tests/docker_compose.py @@ -25,11 +25,11 @@ def __init__(self, docker_compose_path): self._docker_compose_path = docker_compose_path def start(self): - subprocess.run(['docker-compose', 'up', '--detach'], cwd=self._docker_compose_path) + subprocess.check_call(['docker-compose', 'up', '--detach'], cwd=self._docker_compose_path) def extract_all_logs(self): return subprocess.check_output(['docker-compose', 'logs', '--no-color'], cwd=self._docker_compose_path, universal_newlines=True) def stop(self): - subprocess.run(['docker-compose', 'down'], cwd=self._docker_compose_path) - subprocess.run(['docker', 'volume', 'prune', '--all', '--force']) + subprocess.check_call(['docker-compose', 'down'], cwd=self._docker_compose_path) + subprocess.check_call(['docker', 'volume', 'prune', '--all', '--force']) diff --git a/tests/iris.py b/tests/iris.py index 0f7c342bf..13f1c8f6e 100644 --- a/tests/iris.py +++ b/tests/iris.py @@ -18,7 +18,6 @@ from pathlib import Path import shutil -from tempfile import TemporaryDirectory import time from docker_compose import DockerCompose from rest_api import RestApi @@ -29,6 +28,7 @@ _IRIS_PATH = Path('..') _TEST_DATA_PATH = Path('./data') + class Iris: def __init__(self): @@ -48,11 +48,15 @@ def _wait_until_api_is_ready(self): self._wait(self._api.is_ready, 60) def start(self): - # TODO it would be preferable to have a dedicated directory with the docker-compose.yml file, - # because for now, it will overwrite the .env file and development/tests contexts are mixed up - # to do that, we should the building phase of dockers from the execution phase of the docker-compose - # we should minimize the docker-compose so that as few files as possible need to be copied - # also, we should try to use standard dockers as much as possible instead of having iris specific builds (for instance for the database) + # TODO it would be preferable to have a dedicated directory with the + # docker-compose.yml file, because for now, it will overwrite the + # .env file and development/tests contexts are mixed up. To do + # that, we should split the building phase of dockers from the + # execution phase of the docker-compose. We should minimize the + # docker-compose so that as few files as possible need to be + # copied. Also, we should try to use standard dockers as much as + # possible instead of having iris specific builds (for instance + # for the database) shutil.copy2(_TEST_DATA_PATH.joinpath('basic.env'), _IRIS_PATH.joinpath('.env')) self._docker_compose.start() print('Waiting for DFIR-IRIS to start...') diff --git a/tests/rest_api.py b/tests/rest_api.py index e971b3b54..80dbf7e69 100644 --- a/tests/rest_api.py +++ b/tests/rest_api.py @@ -49,4 +49,4 @@ def is_ready(self): requests.head(self._url) return True except ConnectionError: - return False \ No newline at end of file + return False diff --git a/tests/server_timeout_error.py b/tests/server_timeout_error.py index 53369faef..504a2cb45 100644 --- a/tests/server_timeout_error.py +++ b/tests/server_timeout_error.py @@ -17,4 +17,4 @@ # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. class ServerTimeoutError(Exception): - pass \ No newline at end of file + pass From edc9ba8530ac89e11554bea349387b5fe33116e5 Mon Sep 17 00:00:00 2001 From: c8y3 <25362953+c8y3@users.noreply.github.com> Date: Fri, 9 Feb 2024 10:46:31 +0100 Subject: [PATCH 09/51] [FIX] using correct license header --- tests/docker_compose.py | 4 ++-- tests/iris.py | 4 ++-- tests/rest_api.py | 4 ++-- tests/server_timeout_error.py | 4 ++-- tests/tests.py | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/docker_compose.py b/tests/docker_compose.py index 922f3e852..ae03f70ef 100644 --- a/tests/docker_compose.py +++ b/tests/docker_compose.py @@ -1,6 +1,6 @@ # IRIS Source Code -# Copyright (C) 2021 - Airbus CyberSecurity (SAS) -# ir@cyberactionlab.net +# Copyright (C) 2023 - DFIR-IRIS +# 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 diff --git a/tests/iris.py b/tests/iris.py index 13f1c8f6e..a960ac3ae 100644 --- a/tests/iris.py +++ b/tests/iris.py @@ -1,6 +1,6 @@ # IRIS Source Code -# Copyright (C) 2021 - Airbus CyberSecurity (SAS) -# ir@cyberactionlab.net +# Copyright (C) 2023 - DFIR-IRIS +# 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 diff --git a/tests/rest_api.py b/tests/rest_api.py index 80dbf7e69..2a991c2d8 100644 --- a/tests/rest_api.py +++ b/tests/rest_api.py @@ -1,6 +1,6 @@ # IRIS Source Code -# Copyright (C) 2021 - Airbus CyberSecurity (SAS) -# ir@cyberactionlab.net +# Copyright (C) 2023 - DFIR-IRIS +# 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 diff --git a/tests/server_timeout_error.py b/tests/server_timeout_error.py index 504a2cb45..faa932fd1 100644 --- a/tests/server_timeout_error.py +++ b/tests/server_timeout_error.py @@ -1,6 +1,6 @@ # IRIS Source Code -# Copyright (C) 2021 - Airbus CyberSecurity (SAS) -# ir@cyberactionlab.net +# Copyright (C) 2023 - DFIR-IRIS +# 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 diff --git a/tests/tests.py b/tests/tests.py index 3083744f4..241607811 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -1,6 +1,6 @@ # IRIS Source Code -# Copyright (C) 2021 - Airbus CyberSecurity (SAS) -# ir@cyberactionlab.net +# Copyright (C) 2023 - DFIR-IRIS +# 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 From 73658615e77984c11c7dbf35287dc26b8cb9727a Mon Sep 17 00:00:00 2001 From: c8y3 <25362953+c8y3@users.noreply.github.com> Date: Fri, 9 Feb 2024 10:48:28 +0100 Subject: [PATCH 10/51] [FIX] using correct license header for github actions file --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fcff27d41..241493f71 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,6 +1,6 @@ # IRIS Source Code -# Copyright (C) 2021 - Airbus CyberSecurity (SAS) -# ir@cyberactionlab.net +# Copyright (C) 2023 - DFIR-IRIS +# 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 From 30979a4ec67101f72b8ee9fe5745865e3b3a772e Mon Sep 17 00:00:00 2001 From: c8y3 <25362953+c8y3@users.noreply.github.com> Date: Fri, 9 Feb 2024 15:53:10 +0100 Subject: [PATCH 11/51] [#358][FIX] case_name should not be necessary when updating case --- source/app/blueprints/manage/manage_cases_routes.py | 4 +++- tests/iris.py | 3 +++ tests/tests.py | 6 ++++++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/source/app/blueprints/manage/manage_cases_routes.py b/source/app/blueprints/manage/manage_cases_routes.py index 87a7485fc..52ffc3365 100644 --- a/source/app/blueprints/manage/manage_cases_routes.py +++ b/source/app/blueprints/manage/manage_cases_routes.py @@ -454,7 +454,9 @@ def update_case_info(cur_id, caseid): if not user_has_client_access(current_user.id, request_data.get('case_customer')): return response_error("Invalid customer ID. Permission denied.", status=403) - request_data['case_name'] = f"#{case_i.case_id} - {request_data.get('case_name').replace(f'#{case_i.case_id} - ', '')}" + if 'case_name' in request_data: + short_case_name = request_data.get('case_name').replace(f'#{case_i.case_id} - ', '') + request_data['case_name'] = f'#{case_i.case_id} - {short_case_name}' request_data['case_customer'] = case_i.client_id if not request_data.get('case_customer') else request_data.get('case_customer') request_data['reviewer_id'] = None if request_data.get('reviewer_id') == "" else request_data.get('reviewer_id') diff --git a/tests/iris.py b/tests/iris.py index a960ac3ae..2bc2f9da6 100644 --- a/tests/iris.py +++ b/tests/iris.py @@ -84,5 +84,8 @@ def create_case(self): } return self._api.post('/manage/cases/add', body) + def update_case(self, case_identifier, data): + return self._api.post(f'/manage/cases/update/{case_identifier}', data) + def get_cases(self): return self._api.get('/manage/cases/list') diff --git a/tests/tests.py b/tests/tests.py index 241607811..90f93820c 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -42,3 +42,9 @@ def test_create_case_should_add_a_new_case(self): response = self._subject.get_cases() case_count = len(response['data']) self.assertEqual(2, case_count) + + def test_update_case_should_not_require_case_name_issue_358(self): + response = self._subject.create_case() + case_identifier = response['data']['case_id'] + response = self._subject.update_case(case_identifier, {'case_tags': 'test,example'}) + self.assertEqual('success', response['status']) From 1fbce0091f34d1ac0a25dac09bc96199c4093da8 Mon Sep 17 00:00:00 2001 From: c8y3 <25362953+c8y3@users.noreply.github.com> Date: Wed, 28 Feb 2024 09:14:27 +0100 Subject: [PATCH 12/51] [DOC] Started documentation of the git workflow --- CODESTYLE.md | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/CODESTYLE.md b/CODESTYLE.md index 9d341fa32..89784466d 100644 --- a/CODESTYLE.md +++ b/CODESTYLE.md @@ -2,6 +2,26 @@ If you wish to develop in DFIR-IRIS, please make sure to read the following tips. +## Workflow + +- development is done on branch `develop` + ``` + git switch develop + ``` +- safe and small modifications may be directly performed on branch `develop` +- modifications which either imply more work or are risky, must be performed on a branch of their own + ``` + git switch -c + git push --set-upstream origin + ``` +- when work on the branch is ready to be published, then a pull request (PR) is created from the github interface + +### Commits +Try to follow the repository convention: + +- If it's not linked to an issue, use the format `[action] Commit message`, with `action` being a 3 letters action related to the commit, eg `ADD`for additions, `DEL` for deletions, `IMP` for improvements, etc. +- If it's linked to an issue, prepend with the issue ID, i.e `[#issue_id][action] Commit message` + ## License header New files should be prefixed by the following license header, where `${current_year}` is replaced by the current year @@ -33,12 +53,6 @@ New files should be prefixed by the following license header, where `${current_y #!/usr/bin/env python3 ``` -## Commits -Try to follow the repository convention : - -- If it's not linked to an issue, use the format `[action] Commit message`, with `action` being a 3 letters action related to the commit, eg `ADD`for additions, `DEL` for deletions, `IMP` for improvements, etc. -- If it's linked to an issue, prepend with the issue ID, i.e `[#issue_id][action] Commit message` - ## Code The code should be pretty easy to apprehend. It's not perfect but it will improve over time. Some documentation about development is available [here](https://docs.dfir-iris.org/development/). From 6879cea89b1a308dcf64246c0f9b3d124672767f Mon Sep 17 00:00:00 2001 From: c8y3 <25362953+c8y3@users.noreply.github.com> Date: Wed, 28 Feb 2024 09:31:35 +0100 Subject: [PATCH 13/51] [DOC] Note about setting the base branch correctly for pull-requests --- CODESTYLE.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CODESTYLE.md b/CODESTYLE.md index 89784466d..1cc5b25f7 100644 --- a/CODESTYLE.md +++ b/CODESTYLE.md @@ -14,7 +14,10 @@ If you wish to develop in DFIR-IRIS, please make sure to read the following tips git switch -c git push --set-upstream origin ``` -- when work on the branch is ready to be published, then a pull request (PR) is created from the github interface +- when work on the branch is ready to be published, then a pull request (PR) is created from the github interface. + Do not forget to choose `develop` as the base branch (by default it is set to `master`, + more information [here](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request#changing-the-branch-range-and-destination-repository)). + ### Commits Try to follow the repository convention: From 120df6730c03cf06c18b1c12116c082c21412e0b Mon Sep 17 00:00:00 2001 From: c8y3 <25362953+c8y3@users.noreply.github.com> Date: Fri, 1 Mar 2024 08:37:27 +0100 Subject: [PATCH 14/51] [#395][OPT] Start/stop the dockers only once for the whole test suite, instead of for each test --- tests/tests.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/tests/tests.py b/tests/tests.py index 90f93820c..f88b1f7a0 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -22,12 +22,16 @@ class Tests(TestCase): - def setUp(self) -> None: - self._subject = Iris() - self._subject.start() + _subject = None - def tearDown(self) -> None: - self._subject.stop() + @classmethod + def setUpClass(cls) -> None: + cls._subject = Iris() + cls._subject.start() + + @classmethod + def tearDownClass(cls) -> None: + cls._subject.stop() def test_create_asset_should_not_fail(self): response = self._subject.create_asset() @@ -38,10 +42,12 @@ def test_get_api_version_should_not_fail(self): self.assertEqual('success', response['status']) def test_create_case_should_add_a_new_case(self): + response = self._subject.get_cases() + initial_case_count = len(response['data']) self._subject.create_case() response = self._subject.get_cases() case_count = len(response['data']) - self.assertEqual(2, case_count) + self.assertEqual(initial_case_count + 1, case_count) def test_update_case_should_not_require_case_name_issue_358(self): response = self._subject.create_case() From d95adf73324429521542202e0c5cb23c97aa85dc Mon Sep 17 00:00:00 2001 From: c8y3 <25362953+c8y3@users.noreply.github.com> Date: Fri, 1 Mar 2024 09:26:00 +0100 Subject: [PATCH 15/51] [CLEAN] using docker compose rather than docker-compose --- tests/docker_compose.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/docker_compose.py b/tests/docker_compose.py index ae03f70ef..5f3fb22e9 100644 --- a/tests/docker_compose.py +++ b/tests/docker_compose.py @@ -25,11 +25,11 @@ def __init__(self, docker_compose_path): self._docker_compose_path = docker_compose_path def start(self): - subprocess.check_call(['docker-compose', 'up', '--detach'], cwd=self._docker_compose_path) + subprocess.check_call(['docker', 'compose', 'up', '--detach'], cwd=self._docker_compose_path) def extract_all_logs(self): - return subprocess.check_output(['docker-compose', 'logs', '--no-color'], cwd=self._docker_compose_path, universal_newlines=True) + return subprocess.check_output(['docker', 'compose', 'logs', '--no-color'], cwd=self._docker_compose_path, universal_newlines=True) def stop(self): - subprocess.check_call(['docker-compose', 'down'], cwd=self._docker_compose_path) + subprocess.check_call(['docker', 'compose', 'down'], cwd=self._docker_compose_path) subprocess.check_call(['docker', 'volume', 'prune', '--all', '--force']) From 1abc62ad63ab9f77c73e86e9f08f9f09983f3004 Mon Sep 17 00:00:00 2001 From: c8y3 <25362953+c8y3@users.noreply.github.com> Date: Fri, 1 Mar 2024 09:32:22 +0100 Subject: [PATCH 16/51] [CLEAN] using option --volumes of docker compose down rather than calling additional command: docker volume prune --all --- tests/docker_compose.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/docker_compose.py b/tests/docker_compose.py index 5f3fb22e9..dd6223f07 100644 --- a/tests/docker_compose.py +++ b/tests/docker_compose.py @@ -31,5 +31,4 @@ def extract_all_logs(self): return subprocess.check_output(['docker', 'compose', 'logs', '--no-color'], cwd=self._docker_compose_path, universal_newlines=True) def stop(self): - subprocess.check_call(['docker', 'compose', 'down'], cwd=self._docker_compose_path) - subprocess.check_call(['docker', 'volume', 'prune', '--all', '--force']) + subprocess.check_call(['docker', 'compose', 'down', '--volumes'], cwd=self._docker_compose_path) From 302ed98343005dd5beacb00be9e634dceb6b4090 Mon Sep 17 00:00:00 2001 From: c8y3 <25362953+c8y3@users.noreply.github.com> Date: Fri, 1 Mar 2024 09:38:37 +0100 Subject: [PATCH 17/51] [#395][ADD] test suite which stops all containers between each tests --- tests/tests_which_require_a_clean_slate.py | 48 ++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 tests/tests_which_require_a_clean_slate.py diff --git a/tests/tests_which_require_a_clean_slate.py b/tests/tests_which_require_a_clean_slate.py new file mode 100644 index 000000000..4accd1dc3 --- /dev/null +++ b/tests/tests_which_require_a_clean_slate.py @@ -0,0 +1,48 @@ +# IRIS Source Code +# Copyright (C) 2023 - DFIR-IRIS +# 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. + +from unittest import TestCase +from iris import Iris + + +class TestsWhichRequireACleanSlate(TestCase): + """ + In this test suite, each test is started from a clean slate: the state of IRIS after a fresh install. + This is achieved by stopping and removing containers, networks and modules (docker compose down) after each test. + In consequence tests run in this context will be costlier. + So, whenever possible try to avoid adding tests to this suite. + """ + + def setUp(self) -> None: + self._subject = Iris() + self._subject.start() + + def tearDown(self) -> None: + self._subject.stop() + + def test_create_case_should_add_a_new_case(self): + """ + This test is also present in the main test suite tests.py (although in a slightly more complex form + to be independent of the initial the database state) + This was just to give an example of a test which requires starting from an empty database. + It may thus be removed when we have more interesting tests to add to this suite. + """ + self._subject.create_case() + response = self._subject.get_cases() + case_count = len(response['data']) + self.assertEqual(2, case_count) From b33b38902cde36c98fe20452d721e7c3a396dee2 Mon Sep 17 00:00:00 2001 From: c8y3 <25362953+c8y3@users.noreply.github.com> Date: Mon, 4 Mar 2024 15:56:35 +0100 Subject: [PATCH 18/51] [OPT] Skipped test in test suite tests_which_require_a_clean_slate. It is here just as an example --- tests/tests_which_require_a_clean_slate.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/tests_which_require_a_clean_slate.py b/tests/tests_which_require_a_clean_slate.py index 4accd1dc3..0a24e95c3 100644 --- a/tests/tests_which_require_a_clean_slate.py +++ b/tests/tests_which_require_a_clean_slate.py @@ -17,6 +17,7 @@ # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. from unittest import TestCase +from unittest import skip from iris import Iris @@ -35,6 +36,7 @@ def setUp(self) -> None: def tearDown(self) -> None: self._subject.stop() + @skip def test_create_case_should_add_a_new_case(self): """ This test is also present in the main test suite tests.py (although in a slightly more complex form From 45cb3b878c55166db92e1bde213be617611fb6e5 Mon Sep 17 00:00:00 2001 From: whikernel Date: Sun, 4 Feb 2024 16:48:00 +0100 Subject: [PATCH 19/51] [FIX] Secure filename while handling filename pipelines --- source/app/blueprints/manage/manage_cases_routes.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/app/blueprints/manage/manage_cases_routes.py b/source/app/blueprints/manage/manage_cases_routes.py index 7186b4f93..e14dd9a1e 100644 --- a/source/app/blueprints/manage/manage_cases_routes.py +++ b/source/app/blueprints/manage/manage_cases_routes.py @@ -31,6 +31,7 @@ from flask_login import current_user from flask_wtf import FlaskForm from werkzeug import Response +from werkzeug.utils import secure_filename import app from app import db @@ -633,6 +634,7 @@ def manage_cases_uploadfiles(caseid): create=is_update ) + f.filename = secure_filename(f.filename) status = mod.pipeline_files_upload(fpath, f, case_customer, case_name, is_update) if status.is_success(): From 8752b1d79fe6304c00e484915ce696f1c4ef474f Mon Sep 17 00:00:00 2001 From: whikernel Date: Sun, 4 Feb 2024 17:06:33 +0100 Subject: [PATCH 20/51] [ADD] Added autoescape by default --- source/app/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/source/app/__init__.py b/source/app/__init__.py index 2f0fb4606..a57e425c2 100644 --- a/source/app/__init__.py +++ b/source/app/__init__.py @@ -90,6 +90,7 @@ def ac_current_user_has_manage_perms(): app.jinja_env.filters['escape_dots'] = lambda u: u.replace('.', '[.]') app.jinja_env.globals.update(user_has_perm=ac_current_user_has_permission) app.jinja_env.globals.update(user_has_manage_perms=ac_current_user_has_manage_perms) +app.jinja_options["autoescape"] = lambda _: True app.config.from_object('app.configuration.Config') From 0e3bfd77a2cad06b865c58a7152cb731912ed8d6 Mon Sep 17 00:00:00 2001 From: whikernel Date: Sun, 4 Feb 2024 17:21:46 +0100 Subject: [PATCH 21/51] [UPD] Updated dependencies and added always restart to all dockers --- docker-compose.yml | 4 +++- source/app/iris_engine/reporter/reporter.py | 2 +- .../docx_generator-0.8.0-py3-none-any.whl | Bin 23362 -> 23376 bytes .../evtx2splunk-2.0.1-py3-none-any.whl | Bin 12592 -> 12592 bytes .../iris_check_module-1.0.1-py3-none-any.whl | Bin 7163 -> 7163 bytes .../iris_evtx-1.2.0-py3-none-any.whl | Bin 11072 -> 11081 bytes ...ris_intelowl_module-0.1.0-py3-none-any.whl | Bin 21709 -> 21708 bytes .../iris_interface-1.2.0-py3-none-any.whl | Bin 11809 -> 11703 bytes .../iris_misp_module-1.2.0-py3-none-any.whl | Bin 0 -> 11698 bytes .../iris_vt_module-1.2.0-py3-none-any.whl | Bin 0 -> 13767 bytes ...ris_webhooks_module-1.0.2-py3-none-any.whl | Bin 0 -> 10259 bytes 11 files changed, 4 insertions(+), 2 deletions(-) create mode 100644 source/dependencies/iris_misp_module-1.2.0-py3-none-any.whl create mode 100644 source/dependencies/iris_vt_module-1.2.0-py3-none-any.whl create mode 100644 source/dependencies/iris_webhooks_module-1.0.2-py3-none-any.whl diff --git a/docker-compose.yml b/docker-compose.yml index eb90f3ccb..a385f50df 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -20,6 +20,7 @@ services: rabbitmq: image: rabbitmq:3-management-alpine container_name: iriswebapp_rabbitmq + restart: always networks: - iris_backend @@ -87,6 +88,7 @@ services: dockerfile: docker/webApp/Dockerfile image: iriswebapp_app:v2.4.5 container_name: iriswebapp_worker + restart: always command: ['./wait-for-iriswebapp.sh', 'app:8000', './iris-entrypoint.sh', 'iris-worker'] volumes: - ./certificates/rootCA/irisRootCACert.pem:/etc/irisRootCACert.pem:ro @@ -137,7 +139,7 @@ services: - "${INTERFACE_HTTPS_PORT:-443}:${INTERFACE_HTTPS_PORT:-443}" volumes: - "./certificates/web_certificates/:/www/certs/:ro" - restart: on-failure:5 + restart: always depends_on: - "app" diff --git a/source/app/iris_engine/reporter/reporter.py b/source/app/iris_engine/reporter/reporter.py index 89454bf68..03c9b6cee 100644 --- a/source/app/iris_engine/reporter/reporter.py +++ b/source/app/iris_engine/reporter/reporter.py @@ -563,7 +563,7 @@ def generate_md_report(self, doc_type): try: # Load the template - template_loader = jinja2.FileSystemLoader(searchpath="/") + template_loader = jinja2.FileSystemLoader(searchpath=".") template_env = jinja2.Environment(loader=template_loader, autoescape=True) template_env.filters = app.jinja_env.filters template = template_env.get_template(os.path.join( diff --git a/source/dependencies/docx_generator-0.8.0-py3-none-any.whl b/source/dependencies/docx_generator-0.8.0-py3-none-any.whl index d995c00b958c9034c411f9a3ce9fba4c8c561ad7..d470f80a95d0b85662855e9f4b4c5c27b69f6e2c 100644 GIT binary patch delta 3882 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 delta 3867 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 jzM{L85t1}|^#dV{NBVw%Or`$OQ delta 658 zcmdmxv>|DOrW(udO1rSlx@sGl1VOA2uP(6(j0_APnHU&UCLh$2-^{1Eh>-&%^)f@M zVe$^GNEWl}##bhbYMX(zPWIOh1WRv|Uw>=z3T=aW%gKuZMA{zisXu)*ZQ{`cCKi?u z2Qxt-ub14@|7v+F@2^{yeoD*tjGEb;8TI^@r6>#a{xV>3RZ3VmUSDJqeTlaIz%fdYJMa+s4y1 zLbg4TKZ~M0Z`Ow{S#x^MyFV`0u^)@x341@5Wjb3Eo|fQw@lf~o{CRBJf0Ca4yppZm z!rVFW_Jb7$>2{~1g9G@JSsDzTzGvf+S~bT z>Fvee?uwS)EfKNb#xmV+&Jy=k>loUW#QpQUz+L31yYJIF^-pTK_TP6cnX%4EV%PT% z*Z;jJ6u6YU#;e5o+{>l=Z~e}B9lc>af6*eh*`*7fPJ7?ppj7#9ZNA1ljqZ6r4m9Tm z&VN$D@#W0i7Ef+(Bfig@BaiuL)t{`eo3PDrL+su9F;#EXg9 z*LzF3`%g2vy*j5G(C0tdK;6lBjaindlv2dS{JZ;(7P7iW=>A$+|6}6v7s=vpzwDKN zQF(pWiG9Ac9d0UH7u{Le@_tHNs*7MyyPJv&r}^^EKZp2N@D7)!epB&B}|Dz)89VEWV` z-lJS{Z@zH#cd^QV+-)yAoP<(uO1)}Z?^GSV>8Hcoex_;Jl@C5C)W6<+=%UtT?hE%O zUixxtj?a_Jb6Wd#qrYgW78}jb{(fvv-e*?(Z}pQT*{y5CSz|jl{y+YUIl!BlMTCKYfr9}QL<^T1zMGsO z7SFQng7L-4r^U>`K{%OJJQf^+@3?CI&YqktZd!jbZ?S`j>-V2x3ITcsTTd)HlVf~y zg2X1rw4zXBsSghIwacS7rD$fJJY6pSJSNuG>Vn}(kBOn4m!Cf0Rk3xS(9`?{CApsc zG4{sFmJESapYszEYVD0rM+ovPwOIW#>~HVd%*!Vii@NS{P!8Y@ z;&Qz&-f?)m>~_g?hn1D=T@kF|>(}~S6?Ki?v+m?6w(JwGYTMOiCuP%RINYs$S6_a3!XfixOJaW3 ztlHzv*%z&yq8&{eTJIe{9LI5Z^8C`a=vgfloTAb*Dz|uAv)Nv*fAwpLo}TO99l;wn zu?D}pw!O~8O!wuZ)kPjnb7Xp=O?VO~RQ8tsNOj#Xd*46zx>zF5QXOAyp#K-c_4~GX@Ze~;TRJGg9c0`klw-=B@QSr`~%xfvK#7$$F&5}v5fwKJN5C zY}mU= z9&{8oo%`*b^!}a_|EjZQvVk6pbfTT_ehAIT_ueM6;&b4EDsBImc`3E^;TBP;RgAZ+ zgJ#9e-rQL;Z}W$ZsjK6brkrCderov3Oe?!LdtuNz*-hddYy5Zi&(7E9uSqr8RpQLI zwI!9~V*iaZ?_a%*=d%5e5bZ4hB#Fz08nmn4BdR z&ti7n_{!w7VrJj~oXjpB3l6_b);(9}PR11Bfx~=8v%Up$cjje1+jvS^OK!`V%ljr=T`#?K^P-N&o+>9N#tC*mY~A_8=^y|7 zKX>>GQhavSE6sG%nt6_|O!D0}Ig#brJpycPn+o?X=CDZ7Q=iB~spDR~IQg~TwvH=5VxxaGoXg4bJ{1~M#JZ++r?k<8$#u@@Hm5h*%$~SzzThnL z_1l)ddf#~NTXOyMjv`s6P_5?K&Zp0RVpHZ{yDBNMS@hJ;lz@8ErMH$|-15=?Ow6$Hjc4=yrUcbyw`u!2cdWqK$VkUT-XvME|V&7r1>NT6;hnfFsi#@e!j$VE2 zSCRO6*`+ndywlfYUOZy{b%GpA?!|b zYsokkP|lb3u|U@C$17RCs1u&MW@WdMC*sOta# diff --git a/source/dependencies/iris_evtx-1.2.0-py3-none-any.whl b/source/dependencies/iris_evtx-1.2.0-py3-none-any.whl index 09a21c5be53ce4a618942b6e3041b83760e0bd70..88c5d0addab93c388c4a22f50759a9701933fe38 100644 GIT binary patch delta 2573 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 delta 718 zcmX@JlJV?H#tkyEEZ-~b!X_)oR)ZOv=gXSuL0C7vs~I^!k}or)8Yaj1Cb5`ZH@-6Y zkgpj;%OBrlPLTBF)J4)0HQI;F*Ljf^fe@O)im8l3v7e%5nK{-t|l&wPAQmi#1iw$iS#7G~vSwFs+PyI{RawqRA( z9Y(w9y{G5A`F!Qz-bI%$L|6S=_GOXbNAp)V+Zo;|x7x>R`aOvM{x^DgVW~#JXD%TX z-}2X+bNv3!aBB?B%e*-6;kl!6aa{$Ww{OoqxV*03JU;l$>@)ih3O~=fZJm;1m>T}` zgm?qzGZRiru|DPK3y_J~udxEmN*fJGV;t=fZW&mp}gT zyuF=2GdJ!)%);59j=3K)mK;|;Kr81l|s{ZKmM^TL^Doc=k@Gg%hr5xxO&B* z)?jtj3CDzW{I}bLRhQ;0p zP{}R4ea#%TTO6|YB)R|3Nh`f^I$0@vu911yZLg(Mt}p+@o8j5ZRdd+j=;xqqKTNi7 z|K{5KVafZyuhwl;Y~*|?e#)VzMj~_S*eve)nAHS^e>aqCd(*c(mA&F^G mP!yOkDL4!w5EUGbNPIhk10bTjAu&j4t|%mg?Mwgz5C8xkgJe+v diff --git a/source/dependencies/iris_interface-1.2.0-py3-none-any.whl b/source/dependencies/iris_interface-1.2.0-py3-none-any.whl index 47290f78ca7a5e68549173cbc116f03967facdce..1bab03e1ad7e532eb6345324fc7f98ff124abe2e 100644 GIT binary patch literal 11703 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 literal 11809 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 diff --git a/source/dependencies/iris_misp_module-1.2.0-py3-none-any.whl b/source/dependencies/iris_misp_module-1.2.0-py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..8a5d1345e38b98ccbc0038eac33d6ab6f7b28694 GIT binary patch literal 11698 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 SUnj!9eW2eYC;pe+0r`IhEF{YS literal 0 HcmV?d00001 diff --git a/source/dependencies/iris_vt_module-1.2.0-py3-none-any.whl b/source/dependencies/iris_vt_module-1.2.0-py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..8b659c898be2c3597c62f555aea3b58cf6a8e029 GIT binary patch literal 13767 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 literal 0 HcmV?d00001 diff --git a/source/dependencies/iris_webhooks_module-1.0.2-py3-none-any.whl b/source/dependencies/iris_webhooks_module-1.0.2-py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..3e28799d3c2e81fe621e307b9f39fdea97f927a4 GIT binary patch literal 10259 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 Pzt73v@ro7j$KC$`qo2EN literal 0 HcmV?d00001 From 1cc0599cddd47d0f0fadd001bc7600d44bfb4b99 Mon Sep 17 00:00:00 2001 From: whikernel Date: Sun, 4 Feb 2024 20:33:47 +0100 Subject: [PATCH 22/51] [FIX] Setup of Jinja environment --- source/app/__init__.py | 1 + source/app/iris_engine/reporter/reporter.py | 14 ++++++------- source/app/iris_engine/utils/common.py | 22 +++++++++++++++++++++ 3 files changed, 29 insertions(+), 8 deletions(-) diff --git a/source/app/__init__.py b/source/app/__init__.py index a57e425c2..cbc4e8233 100644 --- a/source/app/__init__.py +++ b/source/app/__init__.py @@ -91,6 +91,7 @@ def ac_current_user_has_manage_perms(): app.jinja_env.globals.update(user_has_perm=ac_current_user_has_permission) app.jinja_env.globals.update(user_has_manage_perms=ac_current_user_has_manage_perms) app.jinja_options["autoescape"] = lambda _: True +app.jinja_env.autoescape = True app.config.from_object('app.configuration.Config') diff --git a/source/app/iris_engine/reporter/reporter.py b/source/app/iris_engine/reporter/reporter.py index 03c9b6cee..c10176d70 100644 --- a/source/app/iris_engine/reporter/reporter.py +++ b/source/app/iris_engine/reporter/reporter.py @@ -28,8 +28,10 @@ from datetime import datetime import jinja2 +from jinja2.sandbox import SandboxedEnvironment from app.datamgmt.reporter.report_db import export_case_json_for_report +from app.iris_engine.utils.common import IrisJinjaEnv from docx_generator.docx_generator import DocxGenerator from docx_generator.exceptions import rendering_error from flask_login import current_user @@ -562,14 +564,10 @@ def generate_md_report(self, doc_type): output_file_path = os.path.join(self._tmp, name) try: - # Load the template - template_loader = jinja2.FileSystemLoader(searchpath=".") - template_env = jinja2.Environment(loader=template_loader, autoescape=True) - template_env.filters = app.jinja_env.filters - template = template_env.get_template(os.path.join( - app.config['TEMPLATES_PATH'], report.internal_reference)) - - # Render with a mapping between JSON (from db) and template tags + env = IrisJinjaEnv() + env.filters = app.jinja_env.filters + template = env.from_string( + open(os.path.join(app.config['TEMPLATES_PATH'], report.internal_reference)).read()) output_text = template.render(case_info) # Write the result in the output file diff --git a/source/app/iris_engine/utils/common.py b/source/app/iris_engine/utils/common.py index 44cda66ae..31c2dbfbd 100644 --- a/source/app/iris_engine/utils/common.py +++ b/source/app/iris_engine/utils/common.py @@ -17,6 +17,7 @@ # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. import os from datetime import datetime +from jinja2.sandbox import SandboxedEnvironment from app import app @@ -109,3 +110,24 @@ def parse_bf_date_format(input_str): pass return None + + +class IrisJinjaEnv(SandboxedEnvironment): + + def is_safe_attribute(self, obj, attr, value): + # Extend the list of blocked attributes with magic methods and other potential unsafe attributes + unsafe_attributes = [ + 'os', 'subprocess', 'eval', 'exec', 'open', 'input', '__import__', + '__class__', '__bases__', '__mro__', '__subclasses__', '__globals__' + ] + # Block access to all attributes starting and ending with double underscores + if attr in unsafe_attributes or attr.startswith('__') and attr.endswith('__'): + return False + return super().is_safe_attribute(obj, attr, value) + + def call(self, obj, *args, **kwargs): + # Block calling of functions if necessary + # For example, block if obj is a built-in function or method + if isinstance(obj, (type,)): + raise Exception("Calling of built-in types is not allowed.") + return super().call(obj, *args, **kwargs) \ No newline at end of file From b5ba8f56d489bf63edb12b626bb572fa914bc6de Mon Sep 17 00:00:00 2001 From: whikernel Date: Fri, 16 Feb 2024 14:34:59 +0100 Subject: [PATCH 23/51] [ADD] Possibility to upload multiple files to the datastore --- .../blueprints/datastore/datastore_routes.py | 16 ++++ .../templates/modal_ds_multi_files.html | 92 +++++++++++++++++++ source/app/static/assets/js/iris/datastore.js | 50 +++++++++- 3 files changed, 155 insertions(+), 3 deletions(-) create mode 100644 source/app/blueprints/datastore/templates/modal_ds_multi_files.html diff --git a/source/app/blueprints/datastore/datastore_routes.py b/source/app/blueprints/datastore/datastore_routes.py index 655572aec..90a49056d 100644 --- a/source/app/blueprints/datastore/datastore_routes.py +++ b/source/app/blueprints/datastore/datastore_routes.py @@ -112,6 +112,22 @@ def datastore_add_file_modal(cur_id: int, caseid: int, url_redir: bool): return render_template("modal_ds_file.html", form=form, file=None, dsp=dsp) +@datastore_blueprint.route('/datastore/file/add//multi-modal', methods=['GET']) +@ac_case_requires(CaseAccessLevel.full_access) +def datastore_add_multi_files_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_multi_files.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): diff --git a/source/app/blueprints/datastore/templates/modal_ds_multi_files.html b/source/app/blueprints/datastore/templates/modal_ds_multi_files.html new file mode 100644 index 000000000..434010956 --- /dev/null +++ b/source/app/blueprints/datastore/templates/modal_ds_multi_files.html @@ -0,0 +1,92 @@ + + + \ No newline at end of file diff --git a/source/app/static/assets/js/iris/datastore.js b/source/app/static/assets/js/iris/datastore.js index 39b659d7b..ad8d5b9c9 100644 --- a/source/app/static/assets/js/iris/datastore.js +++ b/source/app/static/assets/js/iris/datastore.js @@ -70,6 +70,7 @@ function build_ds_tree(data, tree_node) {