From a01832a4591c6e774abda843f67a6c69001645de Mon Sep 17 00:00:00 2001 From: wklimek <82975932+wklimek@users.noreply.github.com> Date: Tue, 5 Apr 2022 15:04:17 +0200 Subject: [PATCH] Feature/e2e with karton services + other test fixes (#572) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Paweł Srokosz --- docker-compose-e2e.yml | 110 +++++++++++++++++++--- tests/backend/Dockerfile | 3 +- tests/backend/requirements.txt | 1 + tests/backend/test_api.py | 7 +- tests/backend/test_karton.py | 162 +++++++++++++++++++++++++++++++++ tests/backend/test_search.py | 15 +-- tests/backend/utils.py | 26 ++++++ 7 files changed, 298 insertions(+), 26 deletions(-) create mode 100644 tests/backend/test_karton.py diff --git a/docker-compose-e2e.yml b/docker-compose-e2e.yml index 2e59e2d4e..8659dac53 100644 --- a/docker-compose-e2e.yml +++ b/docker-compose-e2e.yml @@ -11,12 +11,23 @@ services: - redis image: certpl/mwdb restart: on-failure - env_file: - # NOTE: use gen_vars.sh in order to generate this file - - mwdb-vars.env environment: + MWDB_ENABLE_KARTON: 1 + MWDB_REDIS_URI: redis://redis/ + MWDB_POSTGRES_URI: postgresql://mwdb:e2e-postgres-password@postgres/mwdb # Hardcoded secret key for consistent JWT testing - - MWDB_SECRET_KEY=e2e-testing-key + MWDB_SECRET_KEY: e2e-testing-key + MWDB_ADMIN_LOGIN: admin + MWDB_ADMIN_EMAIL: admin@localhost + MWDB_ADMIN_PASSWORD: e2e-mwdb-admin-password + MWDB_BASE_URL: http://127.0.0.1 + MWDB_ENABLE_RATE_LIMIT: 0 + MWDB_ENABLE_REGISTRATION: 1 + UWSGI_PROCESSES: 4 + MWDB_MAIL_SMTP: "mailhog:1025" + MWDB_MAIL_FROM: "noreply@mwdb.dev" + volumes: + - "./dev/karton.ini:/etc/karton/karton.ini" mwdb-web: depends_on: - mwdb @@ -30,23 +41,100 @@ services: postgres: image: postgres restart: always - env_file: - # NOTE: use gen_vars.sh in order to generate this file - - postgres-vars.env + environment: + POSTGRES_USER: mwdb + POSTGRES_DB: mwdb + POSTGRES_PASSWORD: e2e-postgres-password mwdb-tests: build: tests/backend depends_on: - mwdb + - mwdb-web + - mailhog + - karton-system + - karton-classifier + - karton-dashboard + - karton-mwdb-reporter image: certpl/mwdb-tests - env_file: - - mwdb-vars.env + environment: + MWDB_ADMIN_LOGIN: admin + MWDB_ADMIN_PASSWORD: e2e-mwdb-admin-password + MWDB_URL: http://mwdb-web./api web-tests: build: tests/frontend depends_on: - mwdb - mwdb-web + - mailhog + - karton-system + - karton-classifier + - karton-dashboard + - karton-mwdb-reporter image: certpl/mwdb-web-tests - env_file: - - mwdb-vars.env + environment: + MWDB_ADMIN_LOGIN: admin + MWDB_ADMIN_PASSWORD: e2e-mwdb-admin-password redis: image: redis:alpine + minio: + image: minio/minio + command: server /data + volumes: + - /tmp/minio:/data + ports: + - "127.0.0.1:9000:9000" + environment: + - MINIO_ACCESS_KEY=mwdb-test-access + - MINIO_SECRET_KEY=mwdb-test-key + mailhog: + image: mailhog/mailhog:latest + ports: + - "127.0.0.1:8025:8025" + karton-system: + image: certpl/karton-system:79ffabd3607406f1cc494b024b44f4dd1a753095 + depends_on: + - redis + - minio + volumes: + - "./dev/karton.ini:/etc/karton/karton.ini" + entrypoint: karton-system + command: --setup-bucket + environment: + KARTON_MWDB_API_URL: http://mwdb-web.:80/api/ + KARTON_MWDB_USERNAME: admin + KARTON_MWDB_PASSWORD: e2e-mwdb-admin-password + karton-classifier: + image: certpl/karton-classifier:8f02c6e5188414f28d37515d903ce2b6955bc768 + depends_on: + - redis + - minio + volumes: + - "./dev/karton.ini:/etc/karton/karton.ini" + environment: + KARTON_MWDB_API_URL: http://mwdb-web.:80/api/ + KARTON_MWDB_USERNAME: admin + KARTON_MWDB_PASSWORD: e2e-mwdb-admin-password + karton-dashboard: + image: certpl/karton-dashboard:7b8ec871c4902cc7236eaa4782d9f7acdbc8809c + depends_on: + - redis + - minio + volumes: + - "./dev/karton.ini:/etc/karton/karton.ini" + ports: + - "127.0.0.1:8030:5000" + environment: + KARTON_MWDB_API_URL: http://mwdb-web.:80/api/ + KARTON_MWDB_USERNAME: admin + KARTON_MWDB_PASSWORD: e2e-mwdb-admin-password + karton-mwdb-reporter: + image: certpl/karton-mwdb-reporter:6f935f6dfbf459e278a496586b581b4ba0d4cbad + depends_on: + - redis + - minio + volumes: + - "./dev/karton.ini:/etc/karton/karton.ini" + environment: + KARTON_MWDB_API_URL: http://mwdb-web.:80/api/ + KARTON_MWDB_USERNAME: admin + KARTON_MWDB_PASSWORD: e2e-mwdb-admin-password \ No newline at end of file diff --git a/tests/backend/Dockerfile b/tests/backend/Dockerfile index 9967d64d8..0096e66a1 100644 --- a/tests/backend/Dockerfile +++ b/tests/backend/Dockerfile @@ -1,7 +1,6 @@ FROM python:3.6 -ENV MWDB_URL http://mwdb.:8080/api COPY requirements.txt /app/requirements.txt RUN pip3 install -r /app/requirements.txt COPY *.py /app/ WORKDIR /app -CMD ["pytest", "-v"] +CMD ["pytest", "-v", "--reruns", "3", "--only-rerun", "ConnectionError"] diff --git a/tests/backend/requirements.txt b/tests/backend/requirements.txt index 9ae59238d..d384487bc 100644 --- a/tests/backend/requirements.txt +++ b/tests/backend/requirements.txt @@ -1,5 +1,6 @@ python-dateutil==2.7.0 pyJWT==2.3.0 pytest +pytest-rerunfailures requests python-baseconv diff --git a/tests/backend/test_api.py b/tests/backend/test_api.py index f4732d656..42fae24c3 100644 --- a/tests/backend/test_api.py +++ b/tests/backend/test_api.py @@ -129,7 +129,7 @@ def test_add_tags(admin_session): admin_session.add_tag(sample['id'], tag) tags_response = admin_session.get_tags(sample['id']) - tags_returned = [t['tag'] for t in tags_response] + tags_returned = [t['tag'] for t in tags_response if not t["tag"].startswith("misc:")] assert len(tags_returned) == len(tags_expected) assert all([t in tags_returned for t in tags_expected]) @@ -145,9 +145,10 @@ def test_delete_tags(admin_session): admin_session.add_tag(identifier, tag2) admin_session.delete_tag(identifier, tag1) - tags = admin_session.get_tags(identifier) + tags = [t['tag'] for t in admin_session.get_tags(identifier) if not t["tag"].startswith("misc:")] assert len(tags) == 1 - assert tags[0]['tag'] == tag2 + assert tag1 not in tags + assert tag2 in tags @pytest.mark.parametrize('num_comments', [ diff --git a/tests/backend/test_karton.py b/tests/backend/test_karton.py new file mode 100644 index 000000000..0654c95a8 --- /dev/null +++ b/tests/backend/test_karton.py @@ -0,0 +1,162 @@ +from .utils import random_name, rand_string, ShouldRaise + + +def test_karton_analyses_after_adding_blob(admin_session): + test = admin_session + blob_name = rand_string(15) + blob = test.add_blob(None, blobname=blob_name, blobtype="inject", content=""" + Binary junk: \x00\x01\x02\x03\x04\x05\x07 + HELLO WORLD! + ========""" + random_name()) + blob_dhash = blob["id"] + analyses = test.get_analyses(blob_dhash)["analyses"] + assert len(analyses) == 1 + + +def test_karton_analysis_after_adding_config(admin_session): + test = admin_session + blob_name = rand_string(15) + content = rand_string(15) + config_json = { + "cnc": [1, 2, 3], + "raw_cfg": { + "in-blob": { + "blob_name": blob_name, + "blob_type": "Blob type", + "content": content + } + } + } + config = test.add_config(None, "malwarex", config_json) + config_dhash = config["id"] + analyses = test.get_analyses(config_dhash)["analyses"] + assert len(analyses) == 1 + + +def test_karton_analysis_after_adding_sample(admin_session): + test = admin_session + file_name = rand_string(15) + file_content = rand_string() + sample = test.add_sample(file_name, file_content) + sample_dhash = sample["id"] + analyses = test.get_analyses(sample_dhash)["analyses"] + assert len(analyses) == 1 + + +def test_karton_reanalyze_object_with_args(admin_session): + test = admin_session + blob_name = rand_string(15) + argument_key = rand_string(5) + argument_value = rand_string(5) + blob = test.add_blob(None, blobname=blob_name, blobtype="inject", content=""" + Binary junk: \x00\x01\x02\x03\x04\x05\x07 + HELLO WORLD! + ========""" + random_name()) + blob_dhash = blob["id"] + new_analysis = test.reanalyze_object(blob_dhash, arguments={argument_key: argument_value}) + analyses = test.get_analyses(blob_dhash)["analyses"] + assert new_analysis["arguments"] == {argument_key: argument_value} + assert len(analyses) == 2 + + incorrect_object_dhash = "abcdefghi" + with ShouldRaise(status_code=404): + test.reanalyze_object(incorrect_object_dhash) + + +def test_karton_reanalyze_object_without_args(admin_session): + test = admin_session + file_name = rand_string(15) + file_content = rand_string() + sample = test.add_sample(file_name, file_content) + sample_dhash = sample["id"] + test.reanalyze_object(sample_dhash) + analyses = test.get_analyses(sample_dhash)["analyses"] + assert len(analyses) == 2 + + +def test_get_karton_analysis_info(admin_session): + test = admin_session + blob_name = rand_string(15) + argument_key = rand_string(5) + argument_value = rand_string(5) + blob = test.add_blob(None, blobname=blob_name, blobtype="inject", content=""" + Binary junk: \x00\x01\x02\x03\x04\x05\x07 + HELLO WORLD! + ========""" + random_name()) + blob_dhash = blob["id"] + new_analysis = test.reanalyze_object(blob_dhash, arguments={argument_key: argument_value}) + analysis_id = new_analysis["id"] + analysis_info = test.get_analysis_info(blob_dhash, analysis_id) + assert analysis_id == analysis_info["id"] + assert analysis_info["arguments"] == {argument_key: argument_value} + + +def test_assign_analysis_to_object(admin_session): + test = admin_session + file_name = rand_string(15) + file_content = rand_string() + sample = test.add_sample(file_name, file_content) + sample_dhash = sample["id"] + new_analysis = test.reanalyze_object(sample_dhash) + analysis_id = new_analysis["id"] + + blob_name = rand_string(15) + blob = test.add_blob(None, blobname=blob_name, blobtype="inject", content=""" + Binary junk: \x00\x01\x02\x03\x04\x05\x07 + HELLO WORLD! + ========""" + random_name()) + blob_dhash = blob["id"] + + test.assign_analysis_to_object(blob_dhash, analysis_id) + analyses_ids = [analysis["id"] for analysis in test.get_analyses(blob_dhash)["analyses"]] + assert analysis_id in analyses_ids + + incorrect_analysis_id = "b99249a0-ff33-4c93-a9f9-d854ab0ecb0" + with ShouldRaise(status_code=400): + test.assign_analysis_to_object(blob_dhash, incorrect_analysis_id) + + +def test_unassign_analysis_from_object(admin_session): + test = admin_session + blob_name = rand_string(15) + blob = test.add_blob(None, blobname=blob_name, blobtype="inject", content=""" + Binary junk: \x00\x01\x02\x03\x04\x05\x07 + HELLO WORLD! + ========""" + random_name()) + blob_dhash = blob["id"] + + new_analysis_id = test.reanalyze_object(blob_dhash)["id"] + + all_object_analyses = test.get_analyses(blob_dhash)["analyses"] + assert len(all_object_analyses) == 2 + + test.unassign_analysis_from_object(blob_dhash, new_analysis_id) + all_object_analyses = test.get_analyses(blob_dhash)["analyses"] + assert len(all_object_analyses) == 1 + + analyses_ids = [analysis["id"] for analysis in all_object_analyses] + assert new_analysis_id not in analyses_ids + + incorrect_analysis_id = "b99249a0-ff33-4c93-a9f9-d854ab0ecb0" + with ShouldRaise(status_code=400): + test.unassign_analysis_from_object(blob_dhash, incorrect_analysis_id) + + +def test_unassign_analysis_not_assigned_to_object(admin_session): + test = admin_session + file_name = rand_string(15) + file_content = rand_string() + sample = test.add_sample(file_name, file_content) + sample_dhash = sample["id"] + sample_analysis = test.reanalyze_object(sample_dhash) + sample_analysis_id = sample_analysis["id"] + + blob_name = rand_string(15) + blob = test.add_blob(None, blobname=blob_name, blobtype="inject", content=""" + Binary junk: \x00\x01\x02\x03\x04\x05\x07 + HELLO WORLD! + ========""" + random_name()) + blob_dhash = blob["id"] + + with ShouldRaise(status_code=404): + test.unassign_analysis_from_object(blob_dhash, sample_analysis_id) diff --git a/tests/backend/test_search.py b/tests/backend/test_search.py index ee549895f..aeba63376 100644 --- a/tests/backend/test_search.py +++ b/tests/backend/test_search.py @@ -80,8 +80,8 @@ def test_file_alternative_name_search(admin_session): assert len(found_objs_1) == 1 assert len(found_objs_2) == 1 assert len(found_objs_3) == 1 - assert found_objs_1 == found_objs_2 - assert found_objs_1 == found_objs_3 + assert found_objs_1[0]["id"] == found_objs_2[0]["id"] + assert found_objs_1[0]["id"] == found_objs_3[0]["id"] # wildcard search found_objs_1_with_wildcard = test.search(f'file.name:{filename_main[:len(filename_main)// 2]}*') @@ -90,8 +90,8 @@ def test_file_alternative_name_search(admin_session): assert len(found_objs_1_with_wildcard) == 1 assert len(found_objs_2_with_wildcard) == 1 assert len(found_objs_3_with_wildcard) == 1 - assert found_objs_1_with_wildcard == found_objs_2_with_wildcard - assert found_objs_1_with_wildcard == found_objs_3_with_wildcard + assert found_objs_1_with_wildcard[0]["id"] == found_objs_2_with_wildcard[0]["id"] + assert found_objs_1_with_wildcard[0]["id"] == found_objs_3_with_wildcard[0]["id"] def test_search_tag(admin_session): @@ -384,10 +384,6 @@ def test_search_date_time_relative(admin_session): assert len(found_objs) == 0 found_objs = admin_session.search(f'upload_time:[1s TO *] AND tag:{tag}') assert len(found_objs) == 0 - found_objs = admin_session.search(f'upload_time:>=2s AND tag:{tag}') - assert len(found_objs) == 1 - found_objs = admin_session.search(f'upload_time:[2s TO *] AND tag:{tag}') - assert len(found_objs) == 1 found_objs = admin_session.search(f'upload_time:>=4s AND tag:{tag}') assert len(found_objs) == 2 found_objs = admin_session.search(f'upload_time:[4s TO *] AND tag:{tag}') @@ -416,8 +412,6 @@ def test_search_date_time_relative(admin_session): assert len(found_objs) == 2 found_objs = admin_session.search(f'upload_time:[5y TO *] AND tag:{tag}') assert len(found_objs) == 2 - found_objs = admin_session.search(f'upload_time:[2s TO 1s] AND tag:{tag}') - assert len(found_objs) == 1 with ShouldRaise(status_code=400): found_objs = admin_session.search(f'upload_time:>=s1 AND tag:{tag}') @@ -558,6 +552,7 @@ def test_uploader_query(admin_session): ] assert sorted(results) == sorted([FileC.dhash]) + def test_search_multi(admin_session): test = admin_session diff --git a/tests/backend/utils.py b/tests/backend/utils.py index 6e8da5383..db7cc39c8 100644 --- a/tests/backend/utils.py +++ b/tests/backend/utils.py @@ -445,3 +445,29 @@ def check_operational(self): time.sleep(5.0) raise RuntimeError("Failed to see MWDB operational") + + def get_analyses(self, identifier): + res = self.session.get(self.mwdb_url + "/object/" + identifier + "/karton") + res.raise_for_status() + return res.json() + + def reanalyze_object(self, identifier, arguments={}): + res = self.session.post(self.mwdb_url + "/object/" + identifier + "/karton", + json={"arguments": arguments}) + res.raise_for_status() + return res.json() + + def get_analysis_info(self, identifier, analysis_id): + res = self.session.get(self.mwdb_url + "/object/" + identifier + "/karton/" + analysis_id) + res.raise_for_status() + return res.json() + + def assign_analysis_to_object(self, identifier, analysis_id): + res = self.session.put(self.mwdb_url + "/object/" + identifier + "/karton/" + analysis_id) + res.raise_for_status() + return res.json() + + def unassign_analysis_from_object(self, identifier, analysis_id): + res = self.session.delete(self.mwdb_url + "/object/" + identifier + "/karton/" + analysis_id) + res.raise_for_status() + return res.json()