From 83a18644168289b13a9cbcb00b52631c7fc30658 Mon Sep 17 00:00:00 2001 From: savacano28 Date: Fri, 21 Nov 2025 17:59:53 +0100 Subject: [PATCH 1/6] [http-query] fix: add method to parse headers and key/value data (#92) --- http-query/src/helpers/__init__.py | 0 http-query/src/helpers/helpers.py | 42 +++++++++++++++++++++++++ http-query/src/openaev_http.py | 37 +++++++--------------- http-query/test/helpers/__init__.py | 0 http-query/test/helpers/test_helpers.py | 33 +++++++++++++++++++ 5 files changed, 86 insertions(+), 26 deletions(-) create mode 100644 http-query/src/helpers/__init__.py create mode 100644 http-query/src/helpers/helpers.py create mode 100644 http-query/test/helpers/__init__.py create mode 100644 http-query/test/helpers/test_helpers.py diff --git a/http-query/src/helpers/__init__.py b/http-query/src/helpers/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/http-query/src/helpers/helpers.py b/http-query/src/helpers/helpers.py new file mode 100644 index 00000000..55bfa0f0 --- /dev/null +++ b/http-query/src/helpers/helpers.py @@ -0,0 +1,42 @@ +class HTTPHelpers: + @staticmethod + def parse_headers(headers_str): + if isinstance(headers_str, list): + return headers_str + headers_list = [] + for kv in headers_str.split(","): + if "=" in kv: + k, v = kv.split("=", 1) + headers_list.append({"key": k.strip(), "value": v.strip()}) + return headers_list + + @staticmethod + def parse_parts(parts_str): + if isinstance(parts_str, list): + return parts_str + parts_list = [] + for kv in parts_str.split("&"): + if "=" in kv: + k, v = kv.split("=", 1) + parts_list.append({"key": k.strip(), "value": v.strip()}) + return parts_list + + @staticmethod + def request_data_parts_body(request_data): + parts = HTTPHelpers.parse_parts( + request_data["injection"]["inject_content"]["parts"] + ) + keys = list(map(lambda p: p["key"], parts)) + values = list(map(lambda p: p["value"], parts)) + return dict(zip(keys, values)) + + @staticmethod + def response_parsing(response): + success = 200 <= response.status_code < 300 + success_status = "SUCCESS" if success else "ERROR" + return { + "url": response.url, + "code": response.status_code, + "status": success_status, + "message": response.text, + } diff --git a/http-query/src/openaev_http.py b/http-query/src/openaev_http.py index 483e5273..7a682506 100644 --- a/http-query/src/openaev_http.py +++ b/http-query/src/openaev_http.py @@ -11,6 +11,7 @@ HTTP_RAW_PUT_CONTRACT, HttpContracts, ) +from helpers.helpers import HTTPHelpers from pyoaev.helpers import OpenAEVConfigHelper, OpenAEVInjectorHelper @@ -43,24 +44,6 @@ def __init__(self): self.config, open("img/icon-http.png", "rb") ) - @staticmethod - def _request_data_parts_body(request_data): - parts = request_data["injection"]["inject_content"]["parts"] - keys = list(map(lambda p: p["key"], parts)) - values = list(map(lambda p: p["value"], parts)) - return dict(zip(keys, values)) - - @staticmethod - def _response_parsing(response): - success = 200 <= response.status_code < 300 - success_status = "SUCCESS" if success else "ERROR" - return { - "url": response.url, - "code": response.status_code, - "status": success_status, - "message": response.text, - } - def attachments_to_files(self, request_data): documents = request_data["injection"].get("inject_documents", []) attachments = list(filter(lambda d: d["document_attached"] is True, documents)) @@ -73,7 +56,9 @@ def attachments_to_files(self, request_data): def http_execution(self, data: Dict): # Build headers - inject_headers = data["injection"]["inject_content"].get("headers", []) + inject_headers = HTTPHelpers.parse_headers( + data["injection"]["inject_content"].get("headers", []) + ) headers = {} for header_definition in inject_headers: headers[header_definition["key"]] = header_definition["value"] @@ -93,35 +78,35 @@ def http_execution(self, data: Dict): # Get if inject_contract == HTTP_GET_CONTRACT: response = session.get(url=url, headers=headers) - return self._response_parsing(response) + return HTTPHelpers.response_parsing(response) # Post if inject_contract == HTTP_RAW_POST_CONTRACT: body = data["injection"]["inject_content"]["body"] response = session.post( url=url, headers=headers, data=body, files=http_files ) - return self._response_parsing(response) + return HTTPHelpers.response_parsing(response) # Put if inject_contract == HTTP_RAW_PUT_CONTRACT: body = data["injection"]["inject_content"]["body"] response = session.put( url=url, headers=headers, data=body, files=http_files ) - return self._response_parsing(response) + return HTTPHelpers.response_parsing(response) # Form Post if inject_contract == HTTP_FORM_POST_CONTRACT: - body = self._request_data_parts_body(data) + body = HTTPHelpers.request_data_parts_body(data) response = session.post( url=url, headers=headers, data=body, files=http_files ) - return self._response_parsing(response) + return HTTPHelpers.response_parsing(response) # Form Put if inject_contract == HTTP_FORM_PUT_CONTRACT: - body = self._request_data_parts_body(data) + body = HTTPHelpers.request_data_parts_body(data) response = session.put( url=url, headers=headers, data=body, files=http_files ) - return self._response_parsing(response) + return HTTPHelpers.response_parsing(response) # Nothing supported return { "code": 400, diff --git a/http-query/test/helpers/__init__.py b/http-query/test/helpers/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/http-query/test/helpers/test_helpers.py b/http-query/test/helpers/test_helpers.py new file mode 100644 index 00000000..038ce996 --- /dev/null +++ b/http-query/test/helpers/test_helpers.py @@ -0,0 +1,33 @@ +from unittest import TestCase + +from src.helpers.helpers import HTTPHelpers + + +class HTTPHelpersTest(TestCase): + def test_parse_headers_with_string(self): + input_str = ( + "Content-Type=application/x-www-form-urlencoded,Accept=application/json" + ) + expected = [ + {"key": "Content-Type", "value": "application/x-www-form-urlencoded"}, + {"key": "Accept", "value": "application/json"}, + ] + result = HTTPHelpers.parse_headers(input_str) + self.assertEqual(result, expected) + + +def test_parse_parts_with_string(self): + input_str = "msg=test&user=alice" + expected = [{"key": "msg", "value": "test"}, {"key": "user", "value": "savacano"}] + result = HTTPHelpers.parse_parts(input_str) + self.assertEqual(result, expected) + + +def test_parse_headers_empty_string(self): + result = HTTPHelpers.parse_headers("") + self.assertEqual(result, []) + + +def test_parse_parts_empty_string(self): + result = HTTPHelpers.parse_parts("") + self.assertEqual(result, []) From 686817d38e590f52a1d41f4af2fb75396dadbb8a Mon Sep 17 00:00:00 2001 From: savacano28 Date: Thu, 27 Nov 2025 13:08:32 +0100 Subject: [PATCH 2/6] [http-query] chore: clean code --- http-query/test/helpers/test_helpers.py | 28 ++++++++++++------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/http-query/test/helpers/test_helpers.py b/http-query/test/helpers/test_helpers.py index 038ce996..6ec026b8 100644 --- a/http-query/test/helpers/test_helpers.py +++ b/http-query/test/helpers/test_helpers.py @@ -15,19 +15,19 @@ def test_parse_headers_with_string(self): result = HTTPHelpers.parse_headers(input_str) self.assertEqual(result, expected) + def test_parse_parts_with_string(self): + input_str = "msg=test&user=alice" + expected = [ + {"key": "msg", "value": "test"}, + {"key": "user", "value": "savacano"}, + ] + result = HTTPHelpers.parse_parts(input_str) + self.assertEqual(result, expected) -def test_parse_parts_with_string(self): - input_str = "msg=test&user=alice" - expected = [{"key": "msg", "value": "test"}, {"key": "user", "value": "savacano"}] - result = HTTPHelpers.parse_parts(input_str) - self.assertEqual(result, expected) - - -def test_parse_headers_empty_string(self): - result = HTTPHelpers.parse_headers("") - self.assertEqual(result, []) - + def test_parse_headers_empty_string(self): + result = HTTPHelpers.parse_headers("") + self.assertEqual(result, []) -def test_parse_parts_empty_string(self): - result = HTTPHelpers.parse_parts("") - self.assertEqual(result, []) + def test_parse_parts_empty_string(self): + result = HTTPHelpers.parse_parts("") + self.assertEqual(result, []) From b681e1aca013d13d8e4b7424a8435f629b9e92fd Mon Sep 17 00:00:00 2001 From: Antoine MAZEAS Date: Fri, 28 Nov 2025 17:08:22 +0100 Subject: [PATCH 3/6] make tests a package; add docs for headers format Signed-off-by: Antoine MAZEAS --- http-query/README.md | 11 +++++++++++ http-query/test/__init__.py | 0 2 files changed, 11 insertions(+) create mode 100644 http-query/test/__init__.py diff --git a/http-query/README.md b/http-query/README.md index 5e4e3a64..3ba2f234 100644 --- a/http-query/README.md +++ b/http-query/README.md @@ -84,3 +84,14 @@ python3 openaev_http.py ## Behavior This injector enables new inject contracts, allowing for API calls of the Get, Post, and Put types. + +### Passing headers with the request +The contracts created in OpenAEV include an optional "Headers" parameter. While this field is technically a free-form +text input, the contents passed to it are expected to be in a certain format: `key=value` and together separated with a comma `,`. + +Note the pattern supports whitespace within the keys and values. + +Example: +```plaintext +content-type=application/json,x-custom-header=value for the header +``` \ No newline at end of file diff --git a/http-query/test/__init__.py b/http-query/test/__init__.py new file mode 100644 index 00000000..e69de29b From 66af6cd901a3d31dcd29a08ac0c4da7dba5ea11d Mon Sep 17 00:00:00 2001 From: Antoine MAZEAS Date: Fri, 28 Nov 2025 17:10:58 +0100 Subject: [PATCH 4/6] add http-query tests to ci Signed-off-by: Antoine MAZEAS --- .circleci/config.yml | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index da8e680c..c615234c 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -86,6 +86,30 @@ jobs: working_directory: ~/openaev/nmap name: Tests for nmap injector command: python -m unittest + test-http-query: + working_directory: ~/openaev + docker: + - image: cimg/python:3.13 + steps: + - checkout + - setup_remote_docker + - run: + working_directory: ~/openaev/http-query + name: Install dependencies for http-query injector + command: pip install -r requirements.txt + - run: + working_directory: ~/openaev/http-query + name: Overwrite pyoaev with correct version from CI + command: | + if [ "${CIRCLE_BRANCH}" = "main" ]; then + pip install --force-reinstall git+https://github.com/OpenAEV-Platform/client-python.git@main + else + pip install --force-reinstall git+https://github.com/OpenAEV-Platform/client-python.git@release/current + fi + - run: + working_directory: ~/openaev/http-query + name: Tests for http-query injector + command: python -m unittest build_1: working_directory: ~/openaev docker: @@ -271,6 +295,7 @@ workflows: - linter - test-nmap - test-nuclei + - test-http-query - build_1: filters: tags: @@ -283,6 +308,7 @@ workflows: - linter - test-nmap - test-nuclei + - test-http-query filters: branches: only: @@ -293,6 +319,7 @@ workflows: - linter - test-nmap - test-nuclei + - test-http-query filters: branches: only: From 7c1c80f3e23ab450154192552d86d28ee3b9e1aa Mon Sep 17 00:00:00 2001 From: Antoine MAZEAS Date: Fri, 28 Nov 2025 17:19:33 +0100 Subject: [PATCH 5/6] change location for requirements.txt Signed-off-by: Antoine MAZEAS --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index c615234c..c0ae7a06 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -96,7 +96,7 @@ jobs: - run: working_directory: ~/openaev/http-query name: Install dependencies for http-query injector - command: pip install -r requirements.txt + command: pip install -r src/requirements.txt - run: working_directory: ~/openaev/http-query name: Overwrite pyoaev with correct version from CI From b04e887373b16c8c839e1e2467bacd372ec189b6 Mon Sep 17 00:00:00 2001 From: Antoine MAZEAS Date: Fri, 28 Nov 2025 17:21:55 +0100 Subject: [PATCH 6/6] fix test Signed-off-by: Antoine MAZEAS --- http-query/test/helpers/test_helpers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/http-query/test/helpers/test_helpers.py b/http-query/test/helpers/test_helpers.py index 6ec026b8..d243532c 100644 --- a/http-query/test/helpers/test_helpers.py +++ b/http-query/test/helpers/test_helpers.py @@ -19,7 +19,7 @@ def test_parse_parts_with_string(self): input_str = "msg=test&user=alice" expected = [ {"key": "msg", "value": "test"}, - {"key": "user", "value": "savacano"}, + {"key": "user", "value": "alice"}, ] result = HTTPHelpers.parse_parts(input_str) self.assertEqual(result, expected)