stoobly-agent 0.21.2__py3-none-any.whl → 0.22.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- stoobly_agent/__init__.py +2 -1
- stoobly_agent/app/api/bodies_controller.py +2 -2
- stoobly_agent/app/api/requests_controller.py +28 -10
- stoobly_agent/app/api/simple_http_request_handler.py +1 -1
- stoobly_agent/app/cli/ca_cert_installer.py +16 -5
- stoobly_agent/app/cli/config_cli.py +32 -20
- stoobly_agent/app/cli/helpers/context.py +7 -0
- stoobly_agent/app/cli/helpers/handle_mock_service.py +14 -0
- stoobly_agent/app/cli/helpers/print_service.py +1 -1
- stoobly_agent/app/cli/intercept.py +46 -2
- stoobly_agent/app/cli/main_group.py +3 -3
- stoobly_agent/app/cli/project_cli.py +3 -1
- stoobly_agent/app/cli/request_cli.py +6 -10
- stoobly_agent/app/models/adapters/mitmproxy/__init__.py +2 -0
- stoobly_agent/app/models/adapters/mitmproxy/request/__init__.py +12 -0
- stoobly_agent/app/models/adapters/mitmproxy/request/python_adapter.py +16 -0
- stoobly_agent/app/models/adapters/mitmproxy/response/__init__.py +12 -0
- stoobly_agent/app/models/adapters/mitmproxy/response/python_adapter.py +24 -0
- stoobly_agent/app/models/adapters/python/__init__.py +2 -0
- stoobly_agent/app/models/adapters/python/request/__init__.py +18 -0
- stoobly_agent/app/models/adapters/python/request/mitmproxy_adapter.py +53 -0
- stoobly_agent/app/models/adapters/python/request/stoobly_adapter.py +49 -0
- stoobly_agent/app/models/adapters/python/response/__init__.py +13 -0
- stoobly_agent/app/models/adapters/{mitmproxy_response_adapter.py → python/response/mitmproxy_adapter.py} +15 -7
- stoobly_agent/app/models/adapters/raw_http_response_adapter.py +10 -3
- stoobly_agent/app/models/adapters/stoobly/request/__init__.py +11 -0
- stoobly_agent/app/models/adapters/stoobly/request/mitmproxy_adapter.py +32 -0
- stoobly_agent/app/models/body_model.py +2 -2
- stoobly_agent/app/models/factories/__init__.py +0 -0
- stoobly_agent/app/models/factories/resource/__init__.py +0 -0
- stoobly_agent/app/models/{adapters/body_adapter_factory.py → factories/resource/body.py} +1 -1
- stoobly_agent/app/models/{adapters/header_adapter_factory.py → factories/resource/header.py} +1 -1
- stoobly_agent/app/models/factories/resource/local_db/__init__.py +0 -0
- stoobly_agent/app/models/{adapters → factories/resource}/local_db/replayed_response_adapter.py +1 -2
- stoobly_agent/app/models/{adapters → factories/resource}/local_db/request_adapter.py +15 -8
- stoobly_agent/app/models/{adapters → factories/resource}/local_db/scenario_adapter.py +13 -7
- stoobly_agent/app/models/{adapters/query_param_adapter_factory.py → factories/resource/query_param.py} +1 -1
- stoobly_agent/app/models/{adapters/replayed_response_adapter_factory.py → factories/resource/replayed_response.py} +1 -1
- stoobly_agent/app/models/{adapters/request_adapter_factory.py → factories/resource/request.py} +2 -2
- stoobly_agent/app/models/{adapters/response_adapter_factory.py → factories/resource/response.py} +1 -1
- stoobly_agent/app/models/{adapters/response_header_adapter_factory.py → factories/resource/response_header.py} +1 -1
- stoobly_agent/app/models/{adapters/scenario_adapter_factory.py → factories/resource/scenario.py} +2 -2
- stoobly_agent/app/models/factories/resource/stoobly/__init__.py +0 -0
- stoobly_agent/app/models/{adapters/stoobly_request_adapter.py → factories/resource/stoobly/request_adapter.py} +1 -2
- stoobly_agent/app/models/{adapters/stoobly_scenario_adapter.py → factories/resource/stoobly/scenario_adapter.py} +1 -2
- stoobly_agent/app/models/header_model.py +2 -2
- stoobly_agent/app/models/query_param_model.py +2 -2
- stoobly_agent/app/models/replayed_response_model.py +2 -2
- stoobly_agent/app/models/request_model.py +4 -5
- stoobly_agent/app/models/response_header_model.py +2 -2
- stoobly_agent/app/models/response_model.py +2 -2
- stoobly_agent/app/models/scenario_model.py +4 -4
- stoobly_agent/app/models/schemas/request.py +8 -1
- stoobly_agent/app/models/types/__init__.py +6 -0
- stoobly_agent/app/proxy/__init__.py +0 -5
- stoobly_agent/app/proxy/handle_mock_service.py +0 -3
- stoobly_agent/app/proxy/handle_test_service.py +1 -1
- stoobly_agent/app/proxy/intercept_handler.py +0 -1
- stoobly_agent/app/proxy/mitmproxy/flow_mock.py +35 -0
- stoobly_agent/app/proxy/mitmproxy/request_facade.py +11 -2
- stoobly_agent/app/proxy/replay/replay_request_service.py +57 -40
- stoobly_agent/app/proxy/simulate_intercept_service.py +30 -0
- stoobly_agent/app/proxy/test/__init__.py +1 -0
- stoobly_agent/app/proxy/test/context.py +2 -1
- stoobly_agent/app/proxy/test/context_abc.py +155 -0
- stoobly_agent/app/proxy/test/matchers/context.py +1 -1
- stoobly_agent/app/proxy/test/matchers/contract.py +1 -1
- stoobly_agent/app/proxy/test/matchers/custom.py +1 -1
- stoobly_agent/app/proxy/test/matchers/diff.py +1 -1
- stoobly_agent/app/proxy/test/matchers/fuzzy.py +1 -1
- stoobly_agent/app/proxy/test/test_service.py +1 -1
- stoobly_agent/app/proxy/upload/joined_request.py +0 -1
- stoobly_agent/app/proxy/upload/proxy_request.py +4 -1
- stoobly_agent/app/proxy/upload/request_string.py +1 -1
- stoobly_agent/app/settings/constants/mode.py +3 -0
- stoobly_agent/cli.py +76 -7
- stoobly_agent/config/constants/env_vars.py +2 -1
- stoobly_agent/config/data_dir.py +11 -8
- stoobly_agent/lib/api/api.py +4 -4
- stoobly_agent/lib/api/keys/organization_key.py +2 -0
- stoobly_agent/lib/api/keys/project_key.py +9 -0
- stoobly_agent/lib/api/scenarios_resource.py +1 -1
- stoobly_agent/lib/api/stoobly_api.py +9 -5
- stoobly_agent/lib/orm/__init__.py +1 -1
- stoobly_agent/lib/orm/replayed_response.py +3 -3
- stoobly_agent/lib/orm/request.py +25 -5
- stoobly_agent/lib/orm/transformers/orm_to_stoobly_request_transformer.py +1 -1
- stoobly_agent/mock.py +94 -0
- stoobly_agent/public/{2-es2015.3d54569af612a07a2e06.js → 1-es2015.37917aa26708d8f35d36.js} +1 -1
- stoobly_agent/public/{2-es5.3d54569af612a07a2e06.js → 1-es5.37917aa26708d8f35d36.js} +1 -1
- stoobly_agent/public/10-es2015.e9556b0d0f0e92fb548b.js +1 -0
- stoobly_agent/public/10-es5.e9556b0d0f0e92fb548b.js +1 -0
- stoobly_agent/public/11-es2015.bc6212fccbe72a623f81.js +1 -0
- stoobly_agent/public/11-es5.bc6212fccbe72a623f81.js +1 -0
- stoobly_agent/public/{13-es2015.76b6c147b0c46f995cd7.js → 12-es2015.d0768894ddffd6efa5e5.js} +1 -1
- stoobly_agent/public/{13-es5.76b6c147b0c46f995cd7.js → 12-es5.d0768894ddffd6efa5e5.js} +1 -1
- stoobly_agent/public/13-es2015.8a044490a76fd298162d.js +1 -0
- stoobly_agent/public/13-es5.8a044490a76fd298162d.js +1 -0
- stoobly_agent/public/{15-es2015.60c3b41c385f5bdedb7b.js → 14-es2015.1cd1a021e51ca0e62e1c.js} +1 -1
- stoobly_agent/public/{15-es5.60c3b41c385f5bdedb7b.js → 14-es5.1cd1a021e51ca0e62e1c.js} +1 -1
- stoobly_agent/public/{16-es2015.5d395009a77978db4405.js → 15-es2015.587781d19864ff0eb4f5.js} +1 -1
- stoobly_agent/public/{16-es5.5d395009a77978db4405.js → 15-es5.587781d19864ff0eb4f5.js} +1 -1
- stoobly_agent/public/16-es2015.ec6a175b1f9578203cd8.js +1 -0
- stoobly_agent/public/16-es5.ec6a175b1f9578203cd8.js +1 -0
- stoobly_agent/public/17-es2015.ad9c4756c96a15bd29d7.js +1 -0
- stoobly_agent/public/17-es5.ad9c4756c96a15bd29d7.js +1 -0
- stoobly_agent/public/{19-es2015.517f68e08f4c582dae66.js → 18-es2015.8583df0f8eccb3e47c0b.js} +1 -1
- stoobly_agent/public/{19-es5.517f68e08f4c582dae66.js → 18-es5.8583df0f8eccb3e47c0b.js} +1 -1
- stoobly_agent/public/{20-es2015.473486aabfa4d4a6431b.js → 19-es2015.d0225852a844dc03a09f.js} +1 -1
- stoobly_agent/public/{20-es5.473486aabfa4d4a6431b.js → 19-es5.d0225852a844dc03a09f.js} +1 -1
- stoobly_agent/public/{3-es5.1dad290844ea619e4c16.js → 2-es2015.8f184ac63348ba447b1f.js} +1 -1
- stoobly_agent/public/{3-es2015.1dad290844ea619e4c16.js → 2-es5.8f184ac63348ba447b1f.js} +1 -1
- stoobly_agent/public/{21-es2015.56aa10803cc1348a55a3.js → 20-es2015.f7c107847935264d58aa.js} +1 -1
- stoobly_agent/public/{21-es5.56aa10803cc1348a55a3.js → 20-es5.f7c107847935264d58aa.js} +1 -1
- stoobly_agent/public/{22-es2015.46d81010003b2a50eeab.js → 21-es2015.dd358e1edaf3d32dd2c0.js} +1 -1
- stoobly_agent/public/{22-es5.46d81010003b2a50eeab.js → 21-es5.dd358e1edaf3d32dd2c0.js} +1 -1
- stoobly_agent/public/26-es2015.6332c32f1b7c8c288f2f.js +1 -0
- stoobly_agent/public/26-es5.6332c32f1b7c8c288f2f.js +1 -0
- stoobly_agent/public/27-es2015.af505e744b0c869a93d1.js +1 -0
- stoobly_agent/public/27-es5.af505e744b0c869a93d1.js +1 -0
- stoobly_agent/public/28-es2015.7c7c0f64e4af29d2e4d4.js +1 -0
- stoobly_agent/public/28-es5.7c7c0f64e4af29d2e4d4.js +1 -0
- stoobly_agent/public/32-es2015.2ab8267be7275dee9059.js +1 -0
- stoobly_agent/public/32-es5.2ab8267be7275dee9059.js +1 -0
- stoobly_agent/public/{34-es2015.ef24f6f7630620a38b19.js → 33-es2015.5b575f3a87c6c2c6b93b.js} +1 -1
- stoobly_agent/public/{34-es5.ef24f6f7630620a38b19.js → 33-es5.5b575f3a87c6c2c6b93b.js} +1 -1
- stoobly_agent/public/{35-es2015.0667e742725cc828f59f.js → 34-es2015.6a1160649c718cdb9338.js} +1 -1
- stoobly_agent/public/{35-es5.0667e742725cc828f59f.js → 34-es5.6a1160649c718cdb9338.js} +1 -1
- stoobly_agent/public/35-es2015.1b9dc7a46a6d5296c3ae.js +1 -0
- stoobly_agent/public/35-es5.1b9dc7a46a6d5296c3ae.js +1 -0
- stoobly_agent/public/{37-es2015.50d0c2d3fe4d0a74fc8f.js → 36-es2015.2fc9151fe6a5ff2bff31.js} +1 -1
- stoobly_agent/public/{37-es5.50d0c2d3fe4d0a74fc8f.js → 36-es5.2fc9151fe6a5ff2bff31.js} +1 -1
- stoobly_agent/public/{38-es2015.c14bde0b8d0cc14e915e.js → 37-es2015.ac7108c3625fd6e1d981.js} +1 -1
- stoobly_agent/public/{38-es5.c14bde0b8d0cc14e915e.js → 37-es5.ac7108c3625fd6e1d981.js} +1 -1
- stoobly_agent/public/38-es2015.9c183b14373c0e449932.js +1 -0
- stoobly_agent/public/38-es5.9c183b14373c0e449932.js +1 -0
- stoobly_agent/public/39-es2015.4624cdeb29fe9850b216.js +1 -0
- stoobly_agent/public/39-es5.4624cdeb29fe9850b216.js +1 -0
- stoobly_agent/public/40-es2015.24d981230c0c8f369cde.js +1 -0
- stoobly_agent/public/40-es5.24d981230c0c8f369cde.js +1 -0
- stoobly_agent/public/{46-es2015.abc7e4fd207d54277fcb.js → 45-es2015.c76937ed45d460bcd36f.js} +1 -1
- stoobly_agent/public/{46-es5.abc7e4fd207d54277fcb.js → 45-es5.c76937ed45d460bcd36f.js} +1 -1
- stoobly_agent/public/6-es2015.53acc5d2ca7f48ef857f.js +1 -0
- stoobly_agent/public/6-es5.53acc5d2ca7f48ef857f.js +1 -0
- stoobly_agent/public/7-es2015.1c6b3d315d50ccd228cb.js +1 -0
- stoobly_agent/public/7-es5.1c6b3d315d50ccd228cb.js +1 -0
- stoobly_agent/public/{9-es2015.ef0f7cb32f5fadb085d0.js → 8-es2015.0fe7492f7b61eb4699b6.js} +1 -1
- stoobly_agent/public/{9-es5.ef0f7cb32f5fadb085d0.js → 8-es5.0fe7492f7b61eb4699b6.js} +1 -1
- stoobly_agent/public/common-es2015.86f70de6df2c705a87f6.js +1 -0
- stoobly_agent/public/common-es5.86f70de6df2c705a87f6.js +1 -0
- stoobly_agent/public/dashboard.agent-alpha-0.22.3.tar.gz +0 -0
- stoobly_agent/public/index.html +1 -1
- stoobly_agent/public/main-es2015.aceb967cb4cccc0bc521.js +1 -0
- stoobly_agent/public/main-es5.aceb967cb4cccc0bc521.js +1 -0
- stoobly_agent/public/{polyfills-es2015.2b40b2ecdf98a9210572.js → polyfills-es2015.580f7a6e775c2c348c9d.js} +1 -1
- stoobly_agent/public/{polyfills-es5.d9fb2eee68607c3f7f64.js → polyfills-es5.4c3461a071d35be3dd81.js} +1 -1
- stoobly_agent/public/runtime-es2015.49eaebd2913fa7fe2b97.js +1 -0
- stoobly_agent/public/runtime-es5.49eaebd2913fa7fe2b97.js +1 -0
- stoobly_agent/test/test_helper.py +4 -5
- {stoobly_agent-0.21.2.dist-info → stoobly_agent-0.22.3.dist-info}/METADATA +1 -1
- {stoobly_agent-0.21.2.dist-info → stoobly_agent-0.22.3.dist-info}/RECORD +175 -157
- {stoobly_agent-0.21.2.dist-info → stoobly_agent-0.22.3.dist-info}/WHEEL +1 -1
- stoobly_agent/app/models/adapters/mitmproxy_request_adapter.py +0 -60
- stoobly_agent/app/models/adapters/types/__init__.py +0 -3
- stoobly_agent/public/11-es2015.b85bdc528bab0ee542fe.js +0 -1
- stoobly_agent/public/11-es5.b85bdc528bab0ee542fe.js +0 -1
- stoobly_agent/public/12-es2015.72399d40488de533bb97.js +0 -1
- stoobly_agent/public/12-es5.72399d40488de533bb97.js +0 -1
- stoobly_agent/public/14-es2015.7cedfd0829bd7a64677a.js +0 -1
- stoobly_agent/public/14-es5.7cedfd0829bd7a64677a.js +0 -1
- stoobly_agent/public/17-es2015.f6f28ba9f681063632a8.js +0 -1
- stoobly_agent/public/17-es5.f6f28ba9f681063632a8.js +0 -1
- stoobly_agent/public/18-es2015.b0cd6822ebd1090b0d60.js +0 -1
- stoobly_agent/public/18-es5.b0cd6822ebd1090b0d60.js +0 -1
- stoobly_agent/public/27-es2015.4635450ff709f7868796.js +0 -1
- stoobly_agent/public/27-es5.4635450ff709f7868796.js +0 -1
- stoobly_agent/public/28-es2015.1b29b35529772ab108f1.js +0 -1
- stoobly_agent/public/28-es5.1b29b35529772ab108f1.js +0 -1
- stoobly_agent/public/32-es2015.862b67803e6242451976.js +0 -1
- stoobly_agent/public/32-es5.862b67803e6242451976.js +0 -1
- stoobly_agent/public/33-es2015.4ff4325d1aec37e1b43c.js +0 -1
- stoobly_agent/public/33-es5.4ff4325d1aec37e1b43c.js +0 -1
- stoobly_agent/public/36-es2015.cd370fdf8990019d0c8e.js +0 -1
- stoobly_agent/public/36-es5.cd370fdf8990019d0c8e.js +0 -1
- stoobly_agent/public/39-es2015.4ec5fc16202c4759eac4.js +0 -1
- stoobly_agent/public/39-es5.4ec5fc16202c4759eac4.js +0 -1
- stoobly_agent/public/40-es2015.25287ce77b40050f3471.js +0 -1
- stoobly_agent/public/40-es5.25287ce77b40050f3471.js +0 -1
- stoobly_agent/public/45-es2015.fd110741ac0fbaada177.js +0 -1
- stoobly_agent/public/45-es5.fd110741ac0fbaada177.js +0 -1
- stoobly_agent/public/6-es2015.7219d596e3545ebaed3a.js +0 -1
- stoobly_agent/public/6-es5.7219d596e3545ebaed3a.js +0 -1
- stoobly_agent/public/7-es2015.98b085349ebd9d246060.js +0 -1
- stoobly_agent/public/7-es5.98b085349ebd9d246060.js +0 -1
- stoobly_agent/public/8-es2015.335fd5c122ad083aec65.js +0 -1
- stoobly_agent/public/8-es5.335fd5c122ad083aec65.js +0 -1
- stoobly_agent/public/common-es2015.81f870bf87411a04446d.js +0 -1
- stoobly_agent/public/common-es5.81f870bf87411a04446d.js +0 -1
- stoobly_agent/public/dashboard.agent-alpha-0.21.0.tar.gz +0 -0
- stoobly_agent/public/main-es2015.575c1d17fc866d3a6649.js +0 -1
- stoobly_agent/public/main-es5.575c1d17fc866d3a6649.js +0 -1
- stoobly_agent/public/runtime-es2015.8915d042dc9b55368999.js +0 -1
- stoobly_agent/public/runtime-es5.8915d042dc9b55368999.js +0 -1
- /stoobly_agent/app/models/adapters/{local_db → stoobly}/__init__.py +0 -0
- /stoobly_agent/app/models/{adapters → factories/resource}/local_db/body_adapter.py +0 -0
- /stoobly_agent/app/models/{adapters → factories/resource}/local_db/header_adapter.py +0 -0
- /stoobly_agent/app/models/{adapters → factories/resource}/local_db/query_param_adapter.py +0 -0
- /stoobly_agent/app/models/{adapters → factories/resource}/local_db/response_adapter.py +0 -0
- /stoobly_agent/app/models/{adapters → factories/resource}/local_db/response_header_adapter.py +0 -0
- /stoobly_agent/app/models/{adapters/types → types}/replayed_response.py +0 -0
- /stoobly_agent/app/models/{adapters/types → types}/request_create_params.py +0 -0
- /stoobly_agent/app/models/{adapters/types → types}/request_show_params.py +0 -0
- /stoobly_agent/app/models/{adapters/types → types}/scenario_create_params.py +0 -0
- {stoobly_agent-0.21.2.dist-info → stoobly_agent-0.22.3.dist-info}/LICENSE +0 -0
- {stoobly_agent-0.21.2.dist-info → stoobly_agent-0.22.3.dist-info}/entry_points.txt +0 -0
- {stoobly_agent-0.21.2.dist-info → stoobly_agent-0.22.3.dist-info}/top_level.txt +0 -0
stoobly_agent/__init__.py
CHANGED
@@ -1 +1,2 @@
|
|
1
|
-
|
1
|
+
COMMAND = 'stoobly-agent'
|
2
|
+
VERSION = '0.22.3'
|
@@ -41,12 +41,12 @@ class BodiesController:
|
|
41
41
|
|
42
42
|
accepted_headers = ['content-encoding', 'content-length', 'content-type']
|
43
43
|
for header, val in request.headers.items():
|
44
|
-
decoded_header = header.
|
44
|
+
decoded_header = header.lower()
|
45
45
|
|
46
46
|
if decoded_header not in accepted_headers:
|
47
47
|
continue
|
48
48
|
|
49
|
-
headers[decoded_header] = val
|
49
|
+
headers[decoded_header] = val
|
50
50
|
|
51
51
|
context.render(
|
52
52
|
data = request.data,
|
@@ -1,3 +1,4 @@
|
|
1
|
+
import json
|
1
2
|
import pdb
|
2
3
|
import requests
|
3
4
|
|
@@ -8,15 +9,15 @@ from urllib.parse import urlparse
|
|
8
9
|
from stoobly_agent.app.api.simple_http_request_handler import SimpleHTTPRequestHandler
|
9
10
|
from stoobly_agent.app.cli.helpers.context import ReplayContext
|
10
11
|
from stoobly_agent.app.models.adapters.joined_request_adapter import JoinedRequestAdapter
|
11
|
-
from stoobly_agent.app.models.adapters.
|
12
|
-
from stoobly_agent.app.models.adapters.mitmproxy_response_adapter import MitmproxyResponseAdapter
|
12
|
+
from stoobly_agent.app.models.adapters.python import PythonRequestAdapterFactory, PythonResponseAdapterFactory
|
13
13
|
from stoobly_agent.app.models.adapters.raw_http_request_adapter import RawHttpRequestAdapter
|
14
14
|
from stoobly_agent.app.models.adapters.raw_http_response_adapter import RawHttpResponseAdapter
|
15
15
|
from stoobly_agent.app.models.request_model import RequestModel
|
16
16
|
from stoobly_agent.app.models.schemas.request import Request
|
17
|
-
from stoobly_agent.app.proxy.replay.replay_request_service import
|
17
|
+
from stoobly_agent.app.proxy.replay.replay_request_service import replay
|
18
18
|
from stoobly_agent.app.proxy.upload.upload_request_service import upload_staged_request
|
19
19
|
from stoobly_agent.app.settings import Settings
|
20
|
+
from stoobly_agent.config.constants import mode
|
20
21
|
from stoobly_agent.lib.orm.replayed_response import ReplayedResponse
|
21
22
|
from stoobly_agent.lib.orm.request import Request as OrmRequest
|
22
23
|
|
@@ -53,8 +54,8 @@ class RequestsController:
|
|
53
54
|
request_adapter = RawHttpRequestAdapter(joined_request.request_string.get())
|
54
55
|
response_adapter = RawHttpResponseAdapter(joined_request.response_string.get())
|
55
56
|
|
56
|
-
mitmproxy_request =
|
57
|
-
mitmproxy_response =
|
57
|
+
mitmproxy_request = PythonRequestAdapterFactory(request_adapter.to_request()).mitmproxy_request(request_adapter.protocol)
|
58
|
+
mitmproxy_response = PythonResponseAdapterFactory(response_adapter.to_response()).mitmproxy_response()
|
58
59
|
|
59
60
|
class MitmproxyFlowMock():
|
60
61
|
def __init__(self, request, response):
|
@@ -67,6 +68,7 @@ class RequestsController:
|
|
67
68
|
request = request_model.create(**{
|
68
69
|
'flow': mitmproxy_flow_mock,
|
69
70
|
'joined_request': joined_request,
|
71
|
+
'scenario_id': body_params.get('scenario_id'),
|
70
72
|
})
|
71
73
|
|
72
74
|
if not request:
|
@@ -206,10 +208,17 @@ class RequestsController:
|
|
206
208
|
|
207
209
|
# PUT /requests/send
|
208
210
|
def send(self, context: SimpleHTTPRequestHandler):
|
211
|
+
headers = []
|
212
|
+
|
213
|
+
try:
|
214
|
+
headers = json.load(context.params.get('headers'))
|
215
|
+
except Exception as e:
|
216
|
+
pass
|
217
|
+
|
209
218
|
url = urlparse(context.params.get('url'))
|
210
219
|
request_response = {
|
211
220
|
'body': context.params.get('body'),
|
212
|
-
'headers':
|
221
|
+
'headers': headers,
|
213
222
|
'method': context.params.get('method'),
|
214
223
|
'path': url.path,
|
215
224
|
'password': url.password,
|
@@ -220,7 +229,13 @@ class RequestsController:
|
|
220
229
|
}
|
221
230
|
|
222
231
|
replay_context = ReplayContext(Request(request_response))
|
223
|
-
self.__send(context, replay_context)
|
232
|
+
self.__send(context, replay_context)
|
233
|
+
|
234
|
+
def export(self, context: SimpleHTTPRequestHandler):
|
235
|
+
context.parse_path_params({
|
236
|
+
'id': 1
|
237
|
+
})
|
238
|
+
request_id = int(context.params.get('id'))
|
224
239
|
|
225
240
|
def __request_model(self, context: SimpleHTTPRequestHandler):
|
226
241
|
request_model = RequestModel(Settings.instance())
|
@@ -228,18 +243,21 @@ class RequestsController:
|
|
228
243
|
return request_model
|
229
244
|
|
230
245
|
def __replay(self, context: SimpleHTTPRequestHandler, replay_context: ReplayContext):
|
231
|
-
|
246
|
+
save = bool(context.params.get('save'))
|
247
|
+
callback = self.__create_replayed_response if save else None
|
248
|
+
|
249
|
+
self.__send(context, replay_context, callback)
|
232
250
|
|
233
251
|
def __send(self, context: SimpleHTTPRequestHandler, replay_context: ReplayContext, callback = None):
|
234
252
|
now = time()
|
235
|
-
res =
|
253
|
+
res = replay(replay_context, { 'mode': mode.REPLAY })
|
236
254
|
received_at = time()
|
237
255
|
|
238
256
|
if callback:
|
239
257
|
callback(context, res, int((received_at - now) * 1000))
|
240
258
|
|
241
259
|
context.render(
|
242
|
-
data = res.raw.data,
|
260
|
+
data = res.raw.data if hasattr(res, 'raw') else res.content,
|
243
261
|
headers = res.headers,
|
244
262
|
status = res.status_code
|
245
263
|
)
|
@@ -62,7 +62,7 @@ class SimpleHTTPRequestHandler(BaseHTTPRequestHandler):
|
|
62
62
|
headers = self.filter_headers(kwargs.get('headers'), {
|
63
63
|
'TRANSFER-ENCODING': 'CHUNKED',
|
64
64
|
})
|
65
|
-
headers['Content-Length'] = len(body)
|
65
|
+
headers['Content-Length'] = str(len(body))
|
66
66
|
|
67
67
|
self.enable_cors(headers)
|
68
68
|
self.render_headers(headers)
|
@@ -7,19 +7,22 @@ class CACertInstaller():
|
|
7
7
|
home_dir = os.path.expanduser('~')
|
8
8
|
self.mitmproxy_certs_dir = os.path.join(home_dir, '.mitmproxy')
|
9
9
|
self.pem_file_name = 'mitmproxy-ca-cert.pem'
|
10
|
+
self.cer_file_name = 'mitmproxy-ca-cert.cer'
|
10
11
|
self.crt_file_name = 'mitmproxy-ca-cert.crt'
|
11
12
|
|
13
|
+
@property
|
14
|
+
def mitm_crt_absolute_path(self):
|
15
|
+
return os.path.join(self.mitmproxy_certs_dir, self.crt_file_name)
|
16
|
+
|
12
17
|
# https://askubuntu.com/a/94861
|
13
18
|
def handle_debian(self):
|
14
19
|
extra_ca_certs_dir = '/usr/local/share/ca-certificates/extra'
|
15
|
-
|
16
|
-
mitm_crt_absolute_path = os.path.join(self.mitmproxy_certs_dir, self.crt_file_name)
|
20
|
+
mitm_cer_absolute_path = os.path.join(self.mitmproxy_certs_dir, self.cer_file_name)
|
17
21
|
extra_crt_absolute_path = os.path.join(extra_ca_certs_dir, self.crt_file_name)
|
18
22
|
|
19
23
|
subprocess.run(f"sudo mkdir -p {extra_ca_certs_dir}".split(), check=True)
|
20
|
-
subprocess.run(f"
|
21
|
-
subprocess.run(
|
22
|
-
subprocess.run('sudo update-ca-certificates'.split(), check=True)
|
24
|
+
subprocess.run(f"sudo cp {mitm_cer_absolute_path} {extra_crt_absolute_path}".split(), check=True)
|
25
|
+
subprocess.run("sudo update-ca-certificates".split(), check=True)
|
23
26
|
|
24
27
|
# https://www.dssw.co.uk/reference/security.html
|
25
28
|
def handle_darwin(self):
|
@@ -27,3 +30,11 @@ class CACertInstaller():
|
|
27
30
|
mitm_pem_absolute_path = os.path.join(self.mitmproxy_certs_dir, self.pem_file_name)
|
28
31
|
|
29
32
|
subprocess.run(f"sudo security add-trusted-cert -d -p ssl -p basic -k {system_keychain_path} {mitm_pem_absolute_path}".split(), check=True)
|
33
|
+
|
34
|
+
def handle_rhel(self):
|
35
|
+
ca_trust_dir = '/etc/pki/ca-trust/source/anchors'
|
36
|
+
mitm_cer_absolute_path = os.path.join(self.mitmproxy_certs_dir, self.cer_file_name)
|
37
|
+
ca_trust_crt_absolute_path = os.path.join(ca_trust_dir, self.crt_file_name)
|
38
|
+
|
39
|
+
subprocess.run(f"sudo cp {mitm_cer_absolute_path} {ca_trust_crt_absolute_path}".split(), check=True)
|
40
|
+
subprocess.run("sudo update-ca-trust extract".split(), check=True)
|
@@ -223,7 +223,16 @@ if is_remote:
|
|
223
223
|
pass
|
224
224
|
|
225
225
|
@project.command(
|
226
|
-
help="
|
226
|
+
help="Use local project."
|
227
|
+
)
|
228
|
+
def local(**kwargs):
|
229
|
+
project_key = ProjectKey.local_key
|
230
|
+
__set_project_key(project_key)
|
231
|
+
|
232
|
+
print("Using local project!")
|
233
|
+
|
234
|
+
@project.command(
|
235
|
+
help="Describe project."
|
227
236
|
)
|
228
237
|
@click.option('--select', multiple=True, help='Select column(s) to display.')
|
229
238
|
@click.option('--without-headers', is_flag=True, default=False, help='Disable printing column headers.')
|
@@ -233,7 +242,7 @@ if is_remote:
|
|
233
242
|
|
234
243
|
project_key = __project_key(settings)
|
235
244
|
if project_key.is_local:
|
236
|
-
return
|
245
|
+
return print('Using local project')
|
237
246
|
|
238
247
|
kwargs['project_key'] = project_key.raw
|
239
248
|
|
@@ -252,25 +261,9 @@ if is_remote:
|
|
252
261
|
)
|
253
262
|
@click.argument('project_key')
|
254
263
|
def set(**kwargs):
|
255
|
-
settings = Settings.instance()
|
256
|
-
|
257
264
|
project_key = kwargs['project_key']
|
258
|
-
validate_project_key(project_key)
|
259
|
-
_project_key = ProjectKey(project_key)
|
260
|
-
|
261
|
-
data_rule = settings.proxy.data.data_rules(_project_key.id)
|
262
|
-
scenario_key = data_rule.scenario_key
|
263
|
-
|
264
|
-
if scenario_key:
|
265
|
-
validate_scenario_key(scenario_key)
|
266
|
-
scenario_key = ScenarioKey(scenario_key)
|
267
265
|
|
268
|
-
|
269
|
-
data_rule.scenario_key = None
|
270
|
-
print("Current scenario does not belong to current project, unsetting current scenario.\n")
|
271
|
-
|
272
|
-
settings.proxy.intercept.project_key = project_key
|
273
|
-
settings.commit()
|
266
|
+
__set_project_key(project_key)
|
274
267
|
|
275
268
|
print("Project updated!")
|
276
269
|
|
@@ -291,4 +284,23 @@ def __select_parameter_rule(kwargs):
|
|
291
284
|
def __project_key(settings):
|
292
285
|
project_key = settings.proxy.intercept.project_key
|
293
286
|
validate_project_key(project_key)
|
294
|
-
return ProjectKey(project_key)
|
287
|
+
return ProjectKey(project_key)
|
288
|
+
|
289
|
+
def __set_project_key(project_key: str):
|
290
|
+
settings = Settings.instance()
|
291
|
+
validate_project_key(project_key)
|
292
|
+
_project_key = ProjectKey(project_key)
|
293
|
+
|
294
|
+
data_rule = settings.proxy.data.data_rules(_project_key.id)
|
295
|
+
scenario_key = data_rule.scenario_key
|
296
|
+
|
297
|
+
if scenario_key:
|
298
|
+
validate_scenario_key(scenario_key)
|
299
|
+
scenario_key = ScenarioKey(scenario_key)
|
300
|
+
|
301
|
+
if project_key.id != scenario_key.project_id:
|
302
|
+
data_rule.scenario_key = None
|
303
|
+
print("Current scenario does not belong to current project, unsetting current scenario.\n")
|
304
|
+
|
305
|
+
settings.proxy.intercept.project_key = project_key
|
306
|
+
settings.commit()
|
@@ -1,6 +1,8 @@
|
|
1
|
+
import pdb
|
1
2
|
import requests
|
2
3
|
import time
|
3
4
|
|
5
|
+
from stoobly_agent.app.models.adapters.python import PythonRequestAdapterFactory
|
4
6
|
from stoobly_agent.app.models.schemas.request import Request
|
5
7
|
|
6
8
|
class ReplayContext():
|
@@ -14,6 +16,11 @@ class ReplayContext():
|
|
14
16
|
|
15
17
|
self.__sequence = None
|
16
18
|
|
19
|
+
@classmethod
|
20
|
+
def from_python_request(cls, request: requests.Request):
|
21
|
+
stoobly_request = PythonRequestAdapterFactory(request).stoobly_request()
|
22
|
+
return cls(Request(stoobly_request))
|
23
|
+
|
17
24
|
@property
|
18
25
|
def end_time(self):
|
19
26
|
return self.__end_time
|
@@ -0,0 +1,14 @@
|
|
1
|
+
import requests
|
2
|
+
|
3
|
+
from stoobly_agent.app.models.adapters.python import PythonResponseAdapterFactory
|
4
|
+
from stoobly_agent.app.proxy.mitmproxy.response_facade import MitmproxyResponseFacade
|
5
|
+
from stoobly_agent.app.proxy.upload.response_string import ResponseString
|
6
|
+
|
7
|
+
RAW_FORMAT = 'raw'
|
8
|
+
|
9
|
+
def print_raw_response(response: requests.Response):
|
10
|
+
mitmproxy_response = PythonResponseAdapterFactory(response).mitmproxy_response()
|
11
|
+
facade = MitmproxyResponseFacade(mitmproxy_response)
|
12
|
+
response_string = ResponseString(facade, None)
|
13
|
+
|
14
|
+
print(response_string.get().decode())
|
@@ -14,7 +14,7 @@ def select_print_options(kwargs):
|
|
14
14
|
def print_projects(projects, **kwargs):
|
15
15
|
tabulate_print(
|
16
16
|
projects,
|
17
|
-
filter=['created_at', 'organization_id', 'project_id', 'starred', 'updated_at'],
|
17
|
+
filter=['created_at', 'is_deleted', 'organization_id', 'project_id', 'starred', 'updated_at'],
|
18
18
|
headers=not kwargs.get('without_headers'),
|
19
19
|
select=kwargs.get('select') or []
|
20
20
|
)
|
@@ -3,15 +3,59 @@ import pdb
|
|
3
3
|
|
4
4
|
from stoobly_agent.app.cli.decorators.config import ConfigDecorator
|
5
5
|
from stoobly_agent.app.settings import Settings
|
6
|
+
from stoobly_agent.config.constants import mode
|
6
7
|
|
7
8
|
settings = Settings.instance()
|
9
|
+
mode_options = [mode.MOCK, mode.RECORD, mode.REPLAY]
|
10
|
+
|
11
|
+
if settings.cli.features.remote:
|
12
|
+
mode_options.append(mode.TEST)
|
8
13
|
|
9
14
|
@click.group(
|
10
15
|
epilog="Run 'stoobly-agent intercept COMMAND --help' for more information on a command.",
|
11
|
-
help="
|
16
|
+
help="Manage request intercept"
|
12
17
|
)
|
13
18
|
@click.pass_context
|
14
19
|
def intercept(ctx):
|
15
20
|
pass
|
16
21
|
|
17
|
-
|
22
|
+
@intercept.command(
|
23
|
+
help="Enable intercept"
|
24
|
+
)
|
25
|
+
@click.option('--mode', type=click.Choice(mode_options))
|
26
|
+
def enable(**kwargs):
|
27
|
+
settings = Settings.instance()
|
28
|
+
|
29
|
+
if kwargs['mode']:
|
30
|
+
settings.proxy.intercept.mode = kwargs['mode']
|
31
|
+
|
32
|
+
settings.proxy.intercept.active = True
|
33
|
+
|
34
|
+
settings.commit()
|
35
|
+
|
36
|
+
print("Intercept enabled!")
|
37
|
+
|
38
|
+
@intercept.command(
|
39
|
+
help="Disable intercept"
|
40
|
+
)
|
41
|
+
def disable(**kwargs):
|
42
|
+
settings = Settings.instance()
|
43
|
+
|
44
|
+
settings.proxy.intercept.active = False
|
45
|
+
|
46
|
+
settings.commit()
|
47
|
+
|
48
|
+
print("Intercept disabled!")
|
49
|
+
|
50
|
+
@intercept.command(
|
51
|
+
help="Show intercept"
|
52
|
+
)
|
53
|
+
def show(**kwargs):
|
54
|
+
settings = Settings.instance()
|
55
|
+
|
56
|
+
mode = settings.proxy.intercept.mode
|
57
|
+
|
58
|
+
if not mode:
|
59
|
+
print('No intercept mode set')
|
60
|
+
else:
|
61
|
+
print(f"{mode.capitalize()} {'enabled' if settings.proxy.intercept.active else 'disabled'}")
|
@@ -26,7 +26,7 @@ class MainGroup(click.Group):
|
|
26
26
|
command_groups: List[CommandGroup] = [
|
27
27
|
{
|
28
28
|
'name': 'Commands',
|
29
|
-
'commands': ['dev-tools', 'exec', 'feature', 'init'],
|
29
|
+
'commands': ['dev-tools', 'exec', 'feature', 'init', 'mock', 'record'],
|
30
30
|
},
|
31
31
|
{
|
32
32
|
'name': 'Proxy Commands',
|
@@ -36,12 +36,12 @@ class MainGroup(click.Group):
|
|
36
36
|
|
37
37
|
if self.__settings.cli.features.remote:
|
38
38
|
command_groups.append({
|
39
|
-
'name': 'Remote Commands',
|
39
|
+
'name': 'Remote Resource Commands',
|
40
40
|
'commands': ['project', 'report', 'request', 'scenario', 'trace'],
|
41
41
|
})
|
42
42
|
else:
|
43
43
|
command_groups.append({
|
44
|
-
'name': 'Local Commands',
|
44
|
+
'name': 'Local Resource Commands',
|
45
45
|
'commands': ['request', 'scenario'],
|
46
46
|
})
|
47
47
|
|
@@ -57,7 +57,9 @@ def list(**kwargs):
|
|
57
57
|
if not organization_key:
|
58
58
|
try:
|
59
59
|
project_key = ProjectKey(settings.proxy.intercept.project_key)
|
60
|
-
|
60
|
+
|
61
|
+
if not project_key.is_local:
|
62
|
+
organization_key = OrganizationKey.encode(project_key.organization_id)
|
61
63
|
except InvalidProjectKey as e:
|
62
64
|
pass
|
63
65
|
|
@@ -38,7 +38,7 @@ def request(ctx):
|
|
38
38
|
)
|
39
39
|
@click.option('--page', default=0)
|
40
40
|
@ConditionalDecorator(lambda f: click.option('--project-key')(f), is_remote)
|
41
|
-
@
|
41
|
+
@click.option('--scenario-key')
|
42
42
|
@click.option('--select', multiple=True, help='Select column(s) to display.')
|
43
43
|
@click.option('--sort-by', default='created_at', help='created_at|path')
|
44
44
|
@click.option('--sort-order', default='desc', help='asc | desc')
|
@@ -86,7 +86,7 @@ def list(**kwargs):
|
|
86
86
|
'''
|
87
87
|
)
|
88
88
|
@click.option('--record', is_flag=True, default=False, help='Replay and record request.')
|
89
|
-
@
|
89
|
+
@click.option('--scenario-key', help='Record to scenario.')
|
90
90
|
@click.option('--scheme', help='Rewrite request scheme.')
|
91
91
|
@ConditionalDecorator(lambda f: click.option('--trace-id', help='Use existing trace.')(f), is_remote)
|
92
92
|
@ConditionalDecorator(lambda f: click.option('--validate', multiple=True, help='Validate one or more aliases. Format: <NAME>=?<TYPE>')(f), is_remote)
|
@@ -203,9 +203,11 @@ def get(**kwargs):
|
|
203
203
|
|
204
204
|
print(res.content)
|
205
205
|
|
206
|
-
@response.command(
|
206
|
+
@response.command(
|
207
|
+
help="Query properties in response"
|
208
|
+
)
|
207
209
|
@click.option('--query', required=True)
|
208
|
-
@click.argument('
|
210
|
+
@click.argument('request_key')
|
209
211
|
def query(**kwargs):
|
210
212
|
validate_request_key(kwargs['request_key'])
|
211
213
|
|
@@ -218,12 +220,6 @@ def query(**kwargs):
|
|
218
220
|
|
219
221
|
print(jmespath.search(kwargs['query'], decoded_response))
|
220
222
|
|
221
|
-
@response.command(
|
222
|
-
help="Set new mocked response"
|
223
|
-
)
|
224
|
-
def set(**kwargs):
|
225
|
-
print("Not yet implemented. Stay tuned!")
|
226
|
-
|
227
223
|
request.add_command(response)
|
228
224
|
|
229
225
|
def __replay(handler, kwargs) -> requests.Response:
|
@@ -0,0 +1,12 @@
|
|
1
|
+
import pdb
|
2
|
+
import requests
|
3
|
+
|
4
|
+
from .python_adapter import PythonRequestAdapter
|
5
|
+
|
6
|
+
class MitmproxyRequestAdapterFactory():
|
7
|
+
|
8
|
+
def __init__(self, request: requests.Request):
|
9
|
+
self.__request = request
|
10
|
+
|
11
|
+
def python_request(self):
|
12
|
+
return PythonRequestAdapter(self.__request).adapt()
|
@@ -0,0 +1,16 @@
|
|
1
|
+
import requests
|
2
|
+
|
3
|
+
from mitmproxy.http import Request as MitmproxyRequest
|
4
|
+
|
5
|
+
class PythonRequestAdapter():
|
6
|
+
|
7
|
+
def __init__(self, request: MitmproxyRequest):
|
8
|
+
self.__request = request
|
9
|
+
|
10
|
+
def adapt(self):
|
11
|
+
return requests.Request(
|
12
|
+
method=self.__request.method,
|
13
|
+
url=self.__request.url,
|
14
|
+
headers=self.__request.headers,
|
15
|
+
data=self.__request.content
|
16
|
+
)
|
@@ -0,0 +1,12 @@
|
|
1
|
+
import pdb
|
2
|
+
import requests
|
3
|
+
|
4
|
+
from .python_adapter import PythonResponseAdapter
|
5
|
+
|
6
|
+
class MitmproxyResponseAdapterFactory():
|
7
|
+
|
8
|
+
def __init__(self, response: requests.Response):
|
9
|
+
self.__response = response
|
10
|
+
|
11
|
+
def python_response(self):
|
12
|
+
return PythonResponseAdapter(self.__response).adapt()
|
@@ -0,0 +1,24 @@
|
|
1
|
+
from io import BytesIO
|
2
|
+
import requests
|
3
|
+
|
4
|
+
from mitmproxy.http import Response as MitmproxyResponse
|
5
|
+
from urllib3 import HTTPResponse
|
6
|
+
|
7
|
+
class PythonResponseAdapter():
|
8
|
+
|
9
|
+
def __init__(self, response: MitmproxyResponse):
|
10
|
+
self.__response = response
|
11
|
+
|
12
|
+
def adapt(self) -> requests.Response:
|
13
|
+
response = requests.Response()
|
14
|
+
response.status_code = self.__response.status_code
|
15
|
+
response.headers = self.__response.headers
|
16
|
+
|
17
|
+
response.raw = HTTPResponse(
|
18
|
+
body=BytesIO(self.__response.raw_content),
|
19
|
+
decode_content=False,
|
20
|
+
headers=self.__response.headers,
|
21
|
+
preload_content=False
|
22
|
+
)
|
23
|
+
|
24
|
+
return response
|
@@ -0,0 +1,18 @@
|
|
1
|
+
import pdb
|
2
|
+
import requests
|
3
|
+
|
4
|
+
from urllib.parse import parse_qs, urlparse
|
5
|
+
|
6
|
+
from .mitmproxy_adapter import MitmproxyRequestAdapter
|
7
|
+
from .stoobly_adapter import StooblyRequestAdapter
|
8
|
+
|
9
|
+
class PythonRequestAdapterFactory():
|
10
|
+
|
11
|
+
def __init__(self, request: requests.Request):
|
12
|
+
self.__request = request
|
13
|
+
|
14
|
+
def mitmproxy_request(self, http_version: str = 'HTTP/1.1'):
|
15
|
+
return MitmproxyRequestAdapter(http_version, self.__request).adapt()
|
16
|
+
|
17
|
+
def stoobly_request(self):
|
18
|
+
return StooblyRequestAdapter(self.__request).adapt()
|
@@ -0,0 +1,53 @@
|
|
1
|
+
import pdb
|
2
|
+
import requests
|
3
|
+
|
4
|
+
from mitmproxy.http import Headers, Request as MitmproxyRequest
|
5
|
+
from urllib.parse import urlparse
|
6
|
+
|
7
|
+
class MitmproxyRequestAdapter():
|
8
|
+
|
9
|
+
def __init__(self, http_version, request: requests.Request):
|
10
|
+
self.__http_version = http_version
|
11
|
+
self.__request = request
|
12
|
+
|
13
|
+
@property
|
14
|
+
def url(self):
|
15
|
+
parsed_url = urlparse(self.__request.url)
|
16
|
+
|
17
|
+
if not parsed_url.netloc:
|
18
|
+
parsed_url = parsed_url._replace(netloc=parsed_url.path, path='')
|
19
|
+
|
20
|
+
if not parsed_url.scheme:
|
21
|
+
parsed_url = parsed_url._replace(scheme='https')
|
22
|
+
|
23
|
+
return parsed_url.geturl()
|
24
|
+
|
25
|
+
@property
|
26
|
+
def headers(self):
|
27
|
+
return Headers(**self.__decode_dict(self.__request.headers))
|
28
|
+
|
29
|
+
@property
|
30
|
+
def data(self):
|
31
|
+
_data = self.__request.data
|
32
|
+
|
33
|
+
# If no data is provided to python requests.Request, it is sent to []
|
34
|
+
if _data == []:
|
35
|
+
return b''
|
36
|
+
|
37
|
+
return _data
|
38
|
+
|
39
|
+
def adapt(self):
|
40
|
+
request = MitmproxyRequest.make(
|
41
|
+
self.__request.method,
|
42
|
+
self.url,
|
43
|
+
self.data,
|
44
|
+
self.headers,
|
45
|
+
)
|
46
|
+
request.http_version = self.__http_version
|
47
|
+
return request
|
48
|
+
|
49
|
+
def __decode_dict(self, d):
|
50
|
+
new_d = {}
|
51
|
+
for k, v in d.items():
|
52
|
+
new_d[k.decode() if isinstance(k, bytes) else k] = v.decode() if isinstance(v, bytes) else v
|
53
|
+
return new_d
|
@@ -0,0 +1,49 @@
|
|
1
|
+
import pdb
|
2
|
+
import requests
|
3
|
+
|
4
|
+
from urllib.parse import parse_qs, urlparse
|
5
|
+
|
6
|
+
class StooblyRequestAdapter():
|
7
|
+
|
8
|
+
def __init__(self, request: requests.Request):
|
9
|
+
self.__request = request
|
10
|
+
|
11
|
+
def adapt(self):
|
12
|
+
parsed_url = urlparse(self.__request.url)
|
13
|
+
|
14
|
+
return {
|
15
|
+
'body': self.__request.data,
|
16
|
+
'headers': self.adapt_headers(),
|
17
|
+
'method': self.__request.method,
|
18
|
+
'path': parsed_url.path,
|
19
|
+
'query_params': self.adapt_query_params(parsed_url.query),
|
20
|
+
'url': self.__request.url,
|
21
|
+
}
|
22
|
+
|
23
|
+
def adapt_query_params(self, query = None):
|
24
|
+
if not query:
|
25
|
+
parsed_url = urlparse(self.__request.url)
|
26
|
+
query = parsed_url.query
|
27
|
+
|
28
|
+
_query_params = parse_qs(query)
|
29
|
+
|
30
|
+
query_params = []
|
31
|
+
for k, v in _query_params.items():
|
32
|
+
for param in v:
|
33
|
+
query_params.append({
|
34
|
+
'name': k,
|
35
|
+
'value': param,
|
36
|
+
})
|
37
|
+
|
38
|
+
return query_params
|
39
|
+
|
40
|
+
def adapt_headers(self):
|
41
|
+
headers = []
|
42
|
+
|
43
|
+
for k, v in self.__request.headers.items():
|
44
|
+
headers.append({
|
45
|
+
'name': k,
|
46
|
+
'value': v
|
47
|
+
})
|
48
|
+
|
49
|
+
return headers
|
@@ -0,0 +1,13 @@
|
|
1
|
+
import http
|
2
|
+
import pdb
|
3
|
+
import requests
|
4
|
+
|
5
|
+
from .mitmproxy_adapter import MitmproxyResponseAdapter
|
6
|
+
|
7
|
+
class PythonResponseAdapterFactory():
|
8
|
+
|
9
|
+
def __init__(self, response: requests.Response):
|
10
|
+
self.__response = response
|
11
|
+
|
12
|
+
def mitmproxy_response(self):
|
13
|
+
return MitmproxyResponseAdapter(self.__response).adapt()
|