From 15950e01cd0a97e53019cb4f013ba84ec135a176 Mon Sep 17 00:00:00 2001 From: Wout Feys Date: Thu, 25 Jul 2024 10:35:23 +0200 Subject: [PATCH 1/6] Move input_sources to context file --- aikido_firewall/context/__init__.py | 2 ++ .../sql_injection/check_context_for_sql_injection.py | 3 +-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/aikido_firewall/context/__init__.py b/aikido_firewall/context/__init__.py index 53910f4ab..223ba69a4 100644 --- a/aikido_firewall/context/__init__.py +++ b/aikido_firewall/context/__init__.py @@ -5,6 +5,8 @@ import threading SUPPORTED_SOURCES = ["django", "flask"] +UINPUT_SOURCES = ["body", "cookies", "query", "headers"] + local = threading.local() diff --git a/aikido_firewall/vulnerabilities/sql_injection/check_context_for_sql_injection.py b/aikido_firewall/vulnerabilities/sql_injection/check_context_for_sql_injection.py index 83ec77f38..77c205ae2 100644 --- a/aikido_firewall/vulnerabilities/sql_injection/check_context_for_sql_injection.py +++ b/aikido_firewall/vulnerabilities/sql_injection/check_context_for_sql_injection.py @@ -8,8 +8,7 @@ ) from aikido_firewall.vulnerabilities.sql_injection import detect_sql_injection from aikido_firewall.helpers.logging import logger - -SOURCES = ["body", "cookies", "query", "headers"] +from aikido_firewall.context import UINPUT_SOURCES as SOURCES def check_context_for_sql_injection(sql, operation, context, dialect): From 677ce38878daeebeb8693c1c65e4a167f0dcee9d Mon Sep 17 00:00:00 2001 From: Wout Feys Date: Thu, 25 Jul 2024 10:35:38 +0200 Subject: [PATCH 2/6] Pour over code for NoSQL vulns --- .../nosql_injection/__init__.py | 109 ++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 aikido_firewall/vulnerabilities/nosql_injection/__init__.py diff --git a/aikido_firewall/vulnerabilities/nosql_injection/__init__.py b/aikido_firewall/vulnerabilities/nosql_injection/__init__.py new file mode 100644 index 000000000..372d9f4a1 --- /dev/null +++ b/aikido_firewall/vulnerabilities/nosql_injection/__init__.py @@ -0,0 +1,109 @@ +""" +init.py file for the module to detect NoSQL Injections +""" + +import json +from aikido_firewall.helpers.is_plain_object import is_plain_object +from aikido_firewall.helpers.build_path_to_payload import build_path_to_payload +from aikido_firewall.helpers.try_decode_as_jwt import try_decode_as_jwt +from aikido_firewall.context import UINPUT_SOURCES + + +def match_filter_part_in_user(user_input, filter_part, path_to_payload=[]): + if isinstance(user_input, str): + jwt = try_decode_as_jwt(user_input) + if jwt[0]: + return match_filter_part_in_user( + jwt[1], filter_part, path_to_payload + [{"type": "jwt"}] + ) + if user_input == filter_part: + return {"match": True, "pathToPayload": build_path_to_payload(path_to_payload)} + + if is_plain_object(user_input): + for key in user_input: + match = match_filter_part_in_user( + user_input[key], + filter_part, + path_to_payload + [{"type": "object", "key": key}], + ) + if match.get("match"): + return match + if isinstance(user_input, list): + for index, value in enumerate(user_input): + match = match_filter_part_in_user( + value, + filter_part, + path_to_payload + [{"type": "array", "index": index}], + ) + if match.get("match"): + return match + + return {"match": False} + + +def remove_keys_that_dont_start_with_dollar_sign(filter): + return {key: value for key, value in filter.items() if key.startswith("$")} + + +def find_filter_part_with_operators(user_input, part_of_filter): + print("ui", json.dumps(part_of_filter)) + if is_plain_object(part_of_filter): + print("Prev part of filter", part_of_filter) + obj = remove_keys_that_dont_start_with_dollar_sign(part_of_filter) + print("Object", obj) + if len(obj) > 0: + print("len > 0") + result = match_filter_part_in_user(user_input, obj) + + if result.get("match"): + return { + "found": True, + "pathToPayload": result.get("pathToPayload"), + "payload": obj, + } + for key in part_of_filter: + result = find_filter_part_with_operators(user_input, part_of_filter[key]) + + if result.get("found"): + return { + "found": True, + "pathToPayload": result.get("pathToPayload"), + "payload": result.get("payload"), + } + + if isinstance(part_of_filter, list): + for val in part_of_filter: + result = find_filter_part_with_operators(user_input, val) + if result.get("found"): + return { + "found": True, + "pathToPayload": result.get("pathToPayload"), + "payload": result.get("payload"), + } + + return {"found": False} + + +def detect_no_sql_injection(request, _filter): + """ + Give a context object and a nosql filter and this function + checks if there is a NoSQL injection + """ + if not is_plain_object(_filter) and not isinstance(_filter, list): + return {"injection": False} + + for source in UINPUT_SOURCES: + if request.get(source): + print("Getting source %s", source) + print(request[source]) + result = find_filter_part_with_operators(request[source], _filter) + + if result.get("found"): + return { + "injection": True, + "source": source, + "pathToPayload": result.get("pathToPayload"), + "payload": result.get("payload"), + } + + return {"injection": False} From 0e8ea009473e7f47378b55e96e2c02c17112f877 Mon Sep 17 00:00:00 2001 From: Wout Feys Date: Thu, 25 Jul 2024 10:35:58 +0200 Subject: [PATCH 3/6] Pour over tests for nosql code --- .../nosql_injection/init_test.py | 479 ++++++++++++++++++ 1 file changed, 479 insertions(+) create mode 100644 aikido_firewall/vulnerabilities/nosql_injection/init_test.py diff --git a/aikido_firewall/vulnerabilities/nosql_injection/init_test.py b/aikido_firewall/vulnerabilities/nosql_injection/init_test.py new file mode 100644 index 000000000..cd2714660 --- /dev/null +++ b/aikido_firewall/vulnerabilities/nosql_injection/init_test.py @@ -0,0 +1,479 @@ +import pytest +from aikido_firewall.vulnerabilities.nosql_injection import detect_no_sql_injection + + +@pytest.fixture +def create_context(): + def _create_context( + query=None, headers=None, body=None, cookies=None, route_params=None + ): + context = { + "remote_address": "::1", + "method": "GET", + "url": "http://localhost:4000", + "query": query if query else {}, + "headers": headers if headers else {}, + "body": body, + "cookies": cookies if cookies else {}, + "route_params": route_params if route_params else {}, + "source": "express", + "route": "/posts/:id", + } + return context + + return _create_context + + +def test_empty_filter_and_request(create_context): + assert detect_no_sql_injection(create_context(), {}) == {"injection": False} + + +def test_ignore_if_filter_not_object(create_context): + assert detect_no_sql_injection(create_context(), "abc") == {"injection": False} + + +def test_ignore_if_and_not_array(create_context): + assert detect_no_sql_injection(create_context(), {"$and": "abc"}) == { + "injection": False + } + + +def test_ignore_if_or_not_array(create_context): + assert detect_no_sql_injection(create_context(), {"$or": "abc"}) == { + "injection": False + } + + +def test_ignore_if_nor_not_array(create_context): + assert detect_no_sql_injection(create_context(), {"$nor": "abc"}) == { + "injection": False + } + + +def test_ignore_if_nor_empty_array(create_context): + assert detect_no_sql_injection(create_context(), {"$nor": []}) == { + "injection": False + } + + +def test_ignore_if_not_not_object(create_context): + assert detect_no_sql_injection(create_context(), {"$not": "abc"}) == { + "injection": False + } + + +def test_filter_with_string_value_and_empty_request(create_context): + assert detect_no_sql_injection(create_context(), {"title": {"title": "title"}}) == { + "injection": False + } + + +def test_filter_with_ne_and_empty_request(create_context): + assert detect_no_sql_injection(create_context(), {"title": {"$ne": None}}) == { + "injection": False + } + + +def test_using_gt_in_query_parameter(create_context): + assert detect_no_sql_injection( + create_context(query={"title": {"$gt": ""}}), {"title": {"$gt": ""}} + ) == { + "injection": True, + "source": "query", + "pathToPayload": ".title", + "payload": {"$gt": ""}, + } + + +def test_safe_filter(create_context): + assert detect_no_sql_injection( + create_context(query={"title": "title"}), {"$and": [{"title": "title"}]} + ) == {"injection": False} + + +def test_using_ne_in_body(create_context): + assert detect_no_sql_injection( + create_context(body={"title": {"$ne": None}}), {"title": {"$ne": None}} + ) == { + "injection": True, + "source": "body", + "pathToPayload": ".title", + "payload": {"$ne": None}, + } + + +def test_using_ne_in_body_different_name(create_context): + assert detect_no_sql_injection( + create_context(body={"title": {"$ne": None}}), {"myTitle": {"$ne": None}} + ) == { + "injection": True, + "source": "body", + "pathToPayload": ".title", + "payload": {"$ne": None}, + } + + +def test_using_ne_in_headers_with_different_name(create_context): + assert detect_no_sql_injection( + create_context(body={"title": {"$ne": None}}), {"someField": {"$ne": None}} + ) == { + "injection": True, + "source": "body", + "pathToPayload": ".title", + "payload": {"$ne": None}, + } + + +def test_using_ne_inside_and(create_context): + assert detect_no_sql_injection( + create_context(body={"title": {"$ne": None}}), + {"$and": [{"title": {"$ne": None}}, {"published": True}]}, + ) == { + "injection": True, + "source": "body", + "pathToPayload": ".title", + "payload": {"$ne": None}, + } + + +def test_using_ne_inside_or(create_context): + assert detect_no_sql_injection( + create_context(body={"title": {"$ne": None}}), + {"$or": [{"title": {"$ne": None}}, {"published": True}]}, + ) == { + "injection": True, + "source": "body", + "pathToPayload": ".title", + "payload": {"$ne": None}, + } + + +def test_using_ne_inside_nor(create_context): + assert detect_no_sql_injection( + create_context(body={"title": {"$ne": None}}), + {"$nor": [{"title": {"$ne": None}}, {"published": True}]}, + ) == { + "injection": True, + "source": "body", + "pathToPayload": ".title", + "payload": {"$ne": None}, + } + + +def test_using_ne_inside_not(create_context): + assert detect_no_sql_injection( + create_context(body={"title": {"$ne": None}}), + {"$not": {"title": {"$ne": None}}}, + ) == { + "injection": True, + "source": "body", + "pathToPayload": ".title", + "payload": {"$ne": None}, + } + + +def test_using_ne_nested_in_body(create_context): + assert detect_no_sql_injection( + create_context(body={"nested": {"nested": {"$ne": None}}}), + {"$not": {"title": {"$ne": None}}}, + ) == { + "injection": True, + "source": "body", + "pathToPayload": ".nested.nested", + "payload": {"$ne": None}, + } + + +def test_using_ne_in_jwt_in_headers(create_context): + assert detect_no_sql_injection( + create_context( + # JWT token with the following payload: + # { + # "sub": "1234567890", + # "username": { + # "$ne": null + # }, + # "iat": 1516239022 + # } + headers={ + "Authorization": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwidXNlcm5hbWUiOnsiJG5lIjpudWxsfSwiaWF0IjoxNTE2MjM5MDIyfQ._jhGJw9WzB6gHKPSozTFHDo9NOHs3CNOlvJ8rWy6VrQ" + } + ), + {"username": {"$ne": None}}, + ) == { + "injection": True, + "source": "headers", + "pathToPayload": ".Authorization.username", + "payload": {"$ne": None}, + } + + +def test_using_ne_in_jwt_in_bearer_header(create_context): + assert detect_no_sql_injection( + create_context( + # JWT token with the following payload: + # { + # "sub": "1234567890", + # "username": { + # "$ne": null + # }, + # "iat": 1516239022 + # } + headers={ + "Authorization": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwidXNlcm5hbWUiOnsiJG5lIjpudWxsfSwiaWF0IjoxNTE2MjM5MDIyfQ._jhGJw9WzB6gHKPSozTFHDo9NOHs3CNOlvJ8rWy6VrQ" + } + ), + {"username": {"$ne": None}}, + ) == { + "injection": True, + "source": "headers", + "pathToPayload": ".Authorization.username", + "payload": {"$ne": None}, + } + + +def test_using_ne_in_jwt_in_cookies(create_context): + assert detect_no_sql_injection( + create_context( + # JWT token with the following payload: + # { + # "sub": "1234567890", + # "username": { + # "$ne": null + # }, + # "iat": 1516239022 + # } + cookies={ + "session": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwidXNlcm5hbWUiOnsiJG5lIjpudWxsfSwiaWF0IjoxNTE2MjM5MDIyfQ._jhGJw9WzB6gHKPSozTFHDo9NOHs3CNOlvJ8rWy6VrQ" + } + ), + {"username": {"$ne": None}}, + ) == { + "injection": True, + "source": "cookies", + "pathToPayload": ".session.username", + "payload": {"$ne": None}, + } + + +def test_jwt_lookalike(create_context): + assert detect_no_sql_injection( + create_context( + cookies={ + "session": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwidXNlcm5hbW!iOnsiJG5lIjpudWxsfSwiaWF0IjoxNTE2MjM5MDIyfQ._jhGJw9WzB6gHKPSozTFHDo9NOHs3CNOlvJ8rWy6VrQ" + } + ), + {"username": {"$ne": None}}, + ) == {"injection": False} + + +def test_using_gt_in_query_parameter(create_context): + assert detect_no_sql_injection( + create_context(query={"age": {"$gt": "21"}}), {"age": {"$gt": "21"}} + ) == { + "injection": True, + "source": "query", + "pathToPayload": ".age", + "payload": {"$gt": "21"}, + } + + +def test_using_gt_and_lt_in_query_parameter(create_context): + assert detect_no_sql_injection( + create_context(body={"age": {"$gt": "21", "$lt": "100"}}), + {"age": {"$gt": "21", "$lt": "100"}}, + ) == { + "injection": True, + "source": "body", + "pathToPayload": ".age", + "payload": {"$gt": "21", "$lt": "100"}, + } + + +def test_using_gt_and_lt_in_query_parameter_different_name(create_context): + assert detect_no_sql_injection( + create_context(body={"age": {"$gt": "21", "$lt": "100"}}), + {"myAge": {"$gt": "21", "$lt": "100"}}, + ) == { + "injection": True, + "source": "body", + "pathToPayload": ".age", + "payload": {"$gt": "21", "$lt": "100"}, + } + + +def test_using_gt_and_lt_in_query_parameter_nested(create_context): + assert detect_no_sql_injection( + create_context( + body={"nested": {"nested": {"age": {"$gt": "21", "$lt": "100"}}}} + ), + {"$and": [{"someAgeField": {"$gt": "21", "$lt": "100"}}]}, + ) == { + "injection": True, + "source": "body", + "pathToPayload": ".nested.nested.age", + "payload": {"$gt": "21", "$lt": "100"}, + } + + +def test_using_gt_and_lt_in_query_parameter_root(create_context): + assert detect_no_sql_injection( + create_context(body={"$and": [{"someAgeField": {"$gt": "21", "$lt": "100"}}]}), + {"$and": [{"someAgeField": {"$gt": "21", "$lt": "100"}}]}, + ) == { + "injection": True, + "source": "body", + "pathToPayload": ".", + "payload": {"$and": [{"someAgeField": {"$gt": "21", "$lt": "100"}}]}, + } + + +def test_where(create_context): + assert detect_no_sql_injection( + create_context(body={"$and": [{"$where": "sleep(1000)"}]}), + {"$and": [{"$where": "sleep(1000)"}]}, + ) == { + "injection": True, + "source": "body", + "pathToPayload": ".", + "payload": {"$and": [{"$where": "sleep(1000)"}]}, + } + + +def test_array_body(create_context): + assert detect_no_sql_injection( + create_context( + body=[ + { + "$where": "sleep(1000)", + }, + ] + ), + { + "$and": [ + { + "$where": "sleep(1000)", + }, + ], + }, + ) == { + "injection": True, + "source": "body", + "pathToPayload": ".[0]", + "payload": {"$where": "sleep(1000)"}, + } + + +def test_safe_email_password(create_context): + assert detect_no_sql_injection( + create_context( + body={ + "email": "email", + "password": "password", + } + ), + { + "email": "email", + "password": "password", + }, + ) == {"injection": False} + + +def test_flags_pipeline_aggregations(create_context): + assert detect_no_sql_injection( + create_context( + body=[ + { + "$lookup": { + "from": "users", + "localField": "Dummy-IdontExist", + "foreignField": "Dummy-IdontExist", + "as": "user_docs", + }, + }, + { + "$limit": 1, + }, + ] + ), + [ + { + "$lookup": { + "from": "users", + "localField": "Dummy-IdontExist", + "foreignField": "Dummy-IdontExist", + "as": "user_docs", + }, + }, + { + "$limit": 1, + }, + ], + ) == { + "injection": True, + "source": "body", + "pathToPayload": ".[0]", + "payload": { + "$lookup": { + "from": "users", + "localField": "Dummy-IdontExist", + "foreignField": "Dummy-IdontExist", + "as": "user_docs", + }, + }, + } + + assert detect_no_sql_injection( + create_context( + body={ + "username": { + "$gt": "", + }, + } + ), + [ + { + "$match": { + "username": { + "$gt": "", + }, + }, + }, + { + "$group": { + "_id": "$username", + "count": {"$sum": 1}, + }, + }, + ], + ) == { + "injection": True, + "source": "body", + "pathToPayload": ".username", + "payload": { + "$gt": "", + }, + } + + +def test_ignores_safe_pipeline_aggregations(create_context): + assert detect_no_sql_injection( + create_context( + body={ + "username": "admin", + } + ), + [ + { + "$match": { + "username": "admin", + }, + }, + { + "$group": { + "_id": "$username", + "count": {"$sum": 1}, + }, + }, + ], + ) == {"injection": False} From 793eba5db0482c4feb1c786ef0ddb8b51564cf39 Mon Sep 17 00:00:00 2001 From: Wout Feys Date: Thu, 25 Jul 2024 10:36:39 +0200 Subject: [PATCH 4/6] Remove print debug statements --- aikido_firewall/vulnerabilities/nosql_injection/__init__.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/aikido_firewall/vulnerabilities/nosql_injection/__init__.py b/aikido_firewall/vulnerabilities/nosql_injection/__init__.py index 372d9f4a1..bdcfa8397 100644 --- a/aikido_firewall/vulnerabilities/nosql_injection/__init__.py +++ b/aikido_firewall/vulnerabilities/nosql_injection/__init__.py @@ -46,13 +46,9 @@ def remove_keys_that_dont_start_with_dollar_sign(filter): def find_filter_part_with_operators(user_input, part_of_filter): - print("ui", json.dumps(part_of_filter)) if is_plain_object(part_of_filter): - print("Prev part of filter", part_of_filter) obj = remove_keys_that_dont_start_with_dollar_sign(part_of_filter) - print("Object", obj) if len(obj) > 0: - print("len > 0") result = match_filter_part_in_user(user_input, obj) if result.get("match"): @@ -94,8 +90,6 @@ def detect_no_sql_injection(request, _filter): for source in UINPUT_SOURCES: if request.get(source): - print("Getting source %s", source) - print(request[source]) result = find_filter_part_with_operators(request[source], _filter) if result.get("found"): From 722bd19130da7a431341c763d11aec8c9246b778 Mon Sep 17 00:00:00 2001 From: Wout Feys Date: Thu, 25 Jul 2024 10:38:46 +0200 Subject: [PATCH 5/6] Linting and best practices --- .../vulnerabilities/nosql_injection/__init__.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/aikido_firewall/vulnerabilities/nosql_injection/__init__.py b/aikido_firewall/vulnerabilities/nosql_injection/__init__.py index bdcfa8397..4aaed54ed 100644 --- a/aikido_firewall/vulnerabilities/nosql_injection/__init__.py +++ b/aikido_firewall/vulnerabilities/nosql_injection/__init__.py @@ -9,7 +9,12 @@ from aikido_firewall.context import UINPUT_SOURCES -def match_filter_part_in_user(user_input, filter_part, path_to_payload=[]): +def match_filter_part_in_user(user_input, filter_part, path_to_payload=None): + """ + This tries to match a filter part to a part in user input + """ + if not path_to_payload: + path_to_payload = [] if isinstance(user_input, str): jwt = try_decode_as_jwt(user_input) if jwt[0]: @@ -42,10 +47,16 @@ def match_filter_part_in_user(user_input, filter_part, path_to_payload=[]): def remove_keys_that_dont_start_with_dollar_sign(filter): + """ + This removes key that don't start with $, since they are not dangerous + """ return {key: value for key, value in filter.items() if key.startswith("$")} def find_filter_part_with_operators(user_input, part_of_filter): + """ + This looks for parts in the filter that have NSQL operators (e.g. $) + """ if is_plain_object(part_of_filter): obj = remove_keys_that_dont_start_with_dollar_sign(part_of_filter) if len(obj) > 0: From e496ecb75693ce5c10cbadfab33379ee2d4b551e Mon Sep 17 00:00:00 2001 From: Wout Feys Date: Thu, 25 Jul 2024 14:26:49 +0200 Subject: [PATCH 6/6] detect_no_sql_injection --> detect_nosql_injection --- .../nosql_injection/__init__.py | 2 +- .../nosql_injection/init_test.py | 70 +++++++++---------- 2 files changed, 36 insertions(+), 36 deletions(-) diff --git a/aikido_firewall/vulnerabilities/nosql_injection/__init__.py b/aikido_firewall/vulnerabilities/nosql_injection/__init__.py index 4aaed54ed..0637fad11 100644 --- a/aikido_firewall/vulnerabilities/nosql_injection/__init__.py +++ b/aikido_firewall/vulnerabilities/nosql_injection/__init__.py @@ -91,7 +91,7 @@ def find_filter_part_with_operators(user_input, part_of_filter): return {"found": False} -def detect_no_sql_injection(request, _filter): +def detect_nosql_injection(request, _filter): """ Give a context object and a nosql filter and this function checks if there is a NoSQL injection diff --git a/aikido_firewall/vulnerabilities/nosql_injection/init_test.py b/aikido_firewall/vulnerabilities/nosql_injection/init_test.py index cd2714660..d3458e688 100644 --- a/aikido_firewall/vulnerabilities/nosql_injection/init_test.py +++ b/aikido_firewall/vulnerabilities/nosql_injection/init_test.py @@ -1,5 +1,5 @@ import pytest -from aikido_firewall.vulnerabilities.nosql_injection import detect_no_sql_injection +from aikido_firewall.vulnerabilities.nosql_injection import detect_nosql_injection @pytest.fixture @@ -25,57 +25,57 @@ def _create_context( def test_empty_filter_and_request(create_context): - assert detect_no_sql_injection(create_context(), {}) == {"injection": False} + assert detect_nosql_injection(create_context(), {}) == {"injection": False} def test_ignore_if_filter_not_object(create_context): - assert detect_no_sql_injection(create_context(), "abc") == {"injection": False} + assert detect_nosql_injection(create_context(), "abc") == {"injection": False} def test_ignore_if_and_not_array(create_context): - assert detect_no_sql_injection(create_context(), {"$and": "abc"}) == { + assert detect_nosql_injection(create_context(), {"$and": "abc"}) == { "injection": False } def test_ignore_if_or_not_array(create_context): - assert detect_no_sql_injection(create_context(), {"$or": "abc"}) == { + assert detect_nosql_injection(create_context(), {"$or": "abc"}) == { "injection": False } def test_ignore_if_nor_not_array(create_context): - assert detect_no_sql_injection(create_context(), {"$nor": "abc"}) == { + assert detect_nosql_injection(create_context(), {"$nor": "abc"}) == { "injection": False } def test_ignore_if_nor_empty_array(create_context): - assert detect_no_sql_injection(create_context(), {"$nor": []}) == { + assert detect_nosql_injection(create_context(), {"$nor": []}) == { "injection": False } def test_ignore_if_not_not_object(create_context): - assert detect_no_sql_injection(create_context(), {"$not": "abc"}) == { + assert detect_nosql_injection(create_context(), {"$not": "abc"}) == { "injection": False } def test_filter_with_string_value_and_empty_request(create_context): - assert detect_no_sql_injection(create_context(), {"title": {"title": "title"}}) == { + assert detect_nosql_injection(create_context(), {"title": {"title": "title"}}) == { "injection": False } def test_filter_with_ne_and_empty_request(create_context): - assert detect_no_sql_injection(create_context(), {"title": {"$ne": None}}) == { + assert detect_nosql_injection(create_context(), {"title": {"$ne": None}}) == { "injection": False } def test_using_gt_in_query_parameter(create_context): - assert detect_no_sql_injection( + assert detect_nosql_injection( create_context(query={"title": {"$gt": ""}}), {"title": {"$gt": ""}} ) == { "injection": True, @@ -86,13 +86,13 @@ def test_using_gt_in_query_parameter(create_context): def test_safe_filter(create_context): - assert detect_no_sql_injection( + assert detect_nosql_injection( create_context(query={"title": "title"}), {"$and": [{"title": "title"}]} ) == {"injection": False} def test_using_ne_in_body(create_context): - assert detect_no_sql_injection( + assert detect_nosql_injection( create_context(body={"title": {"$ne": None}}), {"title": {"$ne": None}} ) == { "injection": True, @@ -103,7 +103,7 @@ def test_using_ne_in_body(create_context): def test_using_ne_in_body_different_name(create_context): - assert detect_no_sql_injection( + assert detect_nosql_injection( create_context(body={"title": {"$ne": None}}), {"myTitle": {"$ne": None}} ) == { "injection": True, @@ -114,7 +114,7 @@ def test_using_ne_in_body_different_name(create_context): def test_using_ne_in_headers_with_different_name(create_context): - assert detect_no_sql_injection( + assert detect_nosql_injection( create_context(body={"title": {"$ne": None}}), {"someField": {"$ne": None}} ) == { "injection": True, @@ -125,7 +125,7 @@ def test_using_ne_in_headers_with_different_name(create_context): def test_using_ne_inside_and(create_context): - assert detect_no_sql_injection( + assert detect_nosql_injection( create_context(body={"title": {"$ne": None}}), {"$and": [{"title": {"$ne": None}}, {"published": True}]}, ) == { @@ -137,7 +137,7 @@ def test_using_ne_inside_and(create_context): def test_using_ne_inside_or(create_context): - assert detect_no_sql_injection( + assert detect_nosql_injection( create_context(body={"title": {"$ne": None}}), {"$or": [{"title": {"$ne": None}}, {"published": True}]}, ) == { @@ -149,7 +149,7 @@ def test_using_ne_inside_or(create_context): def test_using_ne_inside_nor(create_context): - assert detect_no_sql_injection( + assert detect_nosql_injection( create_context(body={"title": {"$ne": None}}), {"$nor": [{"title": {"$ne": None}}, {"published": True}]}, ) == { @@ -161,7 +161,7 @@ def test_using_ne_inside_nor(create_context): def test_using_ne_inside_not(create_context): - assert detect_no_sql_injection( + assert detect_nosql_injection( create_context(body={"title": {"$ne": None}}), {"$not": {"title": {"$ne": None}}}, ) == { @@ -173,7 +173,7 @@ def test_using_ne_inside_not(create_context): def test_using_ne_nested_in_body(create_context): - assert detect_no_sql_injection( + assert detect_nosql_injection( create_context(body={"nested": {"nested": {"$ne": None}}}), {"$not": {"title": {"$ne": None}}}, ) == { @@ -185,7 +185,7 @@ def test_using_ne_nested_in_body(create_context): def test_using_ne_in_jwt_in_headers(create_context): - assert detect_no_sql_injection( + assert detect_nosql_injection( create_context( # JWT token with the following payload: # { @@ -209,7 +209,7 @@ def test_using_ne_in_jwt_in_headers(create_context): def test_using_ne_in_jwt_in_bearer_header(create_context): - assert detect_no_sql_injection( + assert detect_nosql_injection( create_context( # JWT token with the following payload: # { @@ -233,7 +233,7 @@ def test_using_ne_in_jwt_in_bearer_header(create_context): def test_using_ne_in_jwt_in_cookies(create_context): - assert detect_no_sql_injection( + assert detect_nosql_injection( create_context( # JWT token with the following payload: # { @@ -257,7 +257,7 @@ def test_using_ne_in_jwt_in_cookies(create_context): def test_jwt_lookalike(create_context): - assert detect_no_sql_injection( + assert detect_nosql_injection( create_context( cookies={ "session": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwidXNlcm5hbW!iOnsiJG5lIjpudWxsfSwiaWF0IjoxNTE2MjM5MDIyfQ._jhGJw9WzB6gHKPSozTFHDo9NOHs3CNOlvJ8rWy6VrQ" @@ -268,7 +268,7 @@ def test_jwt_lookalike(create_context): def test_using_gt_in_query_parameter(create_context): - assert detect_no_sql_injection( + assert detect_nosql_injection( create_context(query={"age": {"$gt": "21"}}), {"age": {"$gt": "21"}} ) == { "injection": True, @@ -279,7 +279,7 @@ def test_using_gt_in_query_parameter(create_context): def test_using_gt_and_lt_in_query_parameter(create_context): - assert detect_no_sql_injection( + assert detect_nosql_injection( create_context(body={"age": {"$gt": "21", "$lt": "100"}}), {"age": {"$gt": "21", "$lt": "100"}}, ) == { @@ -291,7 +291,7 @@ def test_using_gt_and_lt_in_query_parameter(create_context): def test_using_gt_and_lt_in_query_parameter_different_name(create_context): - assert detect_no_sql_injection( + assert detect_nosql_injection( create_context(body={"age": {"$gt": "21", "$lt": "100"}}), {"myAge": {"$gt": "21", "$lt": "100"}}, ) == { @@ -303,7 +303,7 @@ def test_using_gt_and_lt_in_query_parameter_different_name(create_context): def test_using_gt_and_lt_in_query_parameter_nested(create_context): - assert detect_no_sql_injection( + assert detect_nosql_injection( create_context( body={"nested": {"nested": {"age": {"$gt": "21", "$lt": "100"}}}} ), @@ -317,7 +317,7 @@ def test_using_gt_and_lt_in_query_parameter_nested(create_context): def test_using_gt_and_lt_in_query_parameter_root(create_context): - assert detect_no_sql_injection( + assert detect_nosql_injection( create_context(body={"$and": [{"someAgeField": {"$gt": "21", "$lt": "100"}}]}), {"$and": [{"someAgeField": {"$gt": "21", "$lt": "100"}}]}, ) == { @@ -329,7 +329,7 @@ def test_using_gt_and_lt_in_query_parameter_root(create_context): def test_where(create_context): - assert detect_no_sql_injection( + assert detect_nosql_injection( create_context(body={"$and": [{"$where": "sleep(1000)"}]}), {"$and": [{"$where": "sleep(1000)"}]}, ) == { @@ -341,7 +341,7 @@ def test_where(create_context): def test_array_body(create_context): - assert detect_no_sql_injection( + assert detect_nosql_injection( create_context( body=[ { @@ -365,7 +365,7 @@ def test_array_body(create_context): def test_safe_email_password(create_context): - assert detect_no_sql_injection( + assert detect_nosql_injection( create_context( body={ "email": "email", @@ -380,7 +380,7 @@ def test_safe_email_password(create_context): def test_flags_pipeline_aggregations(create_context): - assert detect_no_sql_injection( + assert detect_nosql_injection( create_context( body=[ { @@ -423,7 +423,7 @@ def test_flags_pipeline_aggregations(create_context): }, } - assert detect_no_sql_injection( + assert detect_nosql_injection( create_context( body={ "username": { @@ -457,7 +457,7 @@ def test_flags_pipeline_aggregations(create_context): def test_ignores_safe_pipeline_aggregations(create_context): - assert detect_no_sql_injection( + assert detect_nosql_injection( create_context( body={ "username": "admin",