bbot 2.4.2.6109rc0__py3-none-any.whl → 2.4.2.6596rc0__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.
Potentially problematic release.
This version of bbot might be problematic. Click here for more details.
- bbot/__init__.py +1 -1
- bbot/core/event/base.py +64 -4
- bbot/core/helpers/diff.py +10 -7
- bbot/core/helpers/helper.py +5 -1
- bbot/core/helpers/misc.py +48 -11
- bbot/core/helpers/regex.py +4 -0
- bbot/core/helpers/regexes.py +45 -8
- bbot/core/helpers/url.py +21 -5
- bbot/core/helpers/web/client.py +25 -5
- bbot/core/helpers/web/engine.py +9 -1
- bbot/core/helpers/web/envelopes.py +352 -0
- bbot/core/helpers/web/web.py +10 -2
- bbot/core/helpers/yara_helper.py +50 -0
- bbot/core/modules.py +23 -7
- bbot/defaults.yml +26 -1
- bbot/modules/base.py +4 -2
- bbot/modules/{deadly/dastardly.py → dastardly.py} +1 -1
- bbot/modules/{deadly/ffuf.py → ffuf.py} +1 -1
- bbot/modules/ffuf_shortnames.py +1 -1
- bbot/modules/httpx.py +14 -0
- bbot/modules/hunt.py +24 -6
- bbot/modules/internal/aggregate.py +1 -0
- bbot/modules/internal/excavate.py +356 -197
- bbot/modules/lightfuzz/lightfuzz.py +203 -0
- bbot/modules/lightfuzz/submodules/__init__.py +0 -0
- bbot/modules/lightfuzz/submodules/base.py +312 -0
- bbot/modules/lightfuzz/submodules/cmdi.py +106 -0
- bbot/modules/lightfuzz/submodules/crypto.py +474 -0
- bbot/modules/lightfuzz/submodules/nosqli.py +183 -0
- bbot/modules/lightfuzz/submodules/path.py +154 -0
- bbot/modules/lightfuzz/submodules/serial.py +179 -0
- bbot/modules/lightfuzz/submodules/sqli.py +187 -0
- bbot/modules/lightfuzz/submodules/ssti.py +39 -0
- bbot/modules/lightfuzz/submodules/xss.py +191 -0
- bbot/modules/{deadly/nuclei.py → nuclei.py} +1 -1
- bbot/modules/paramminer_headers.py +2 -0
- bbot/modules/reflected_parameters.py +80 -0
- bbot/modules/{deadly/vhost.py → vhost.py} +2 -2
- bbot/presets/web/lightfuzz-heavy.yml +16 -0
- bbot/presets/web/lightfuzz-light.yml +20 -0
- bbot/presets/web/lightfuzz-medium.yml +14 -0
- bbot/presets/web/lightfuzz-superheavy.yml +13 -0
- bbot/presets/web/lightfuzz-xss.yml +22 -0
- bbot/presets/web/paramminer.yml +8 -5
- bbot/scanner/preset/args.py +26 -0
- bbot/scanner/preset/path.py +12 -10
- bbot/scanner/preset/preset.py +42 -37
- bbot/scanner/scanner.py +6 -0
- bbot/scripts/docs.py +5 -5
- bbot/test/test_step_1/test__module__tests.py +1 -1
- bbot/test/test_step_1/test_helpers.py +7 -0
- bbot/test/test_step_1/test_presets.py +2 -2
- bbot/test/test_step_1/test_web.py +20 -0
- bbot/test/test_step_1/test_web_envelopes.py +343 -0
- bbot/test/test_step_2/module_tests/test_module_excavate.py +404 -29
- bbot/test/test_step_2/module_tests/test_module_httpx.py +29 -0
- bbot/test/test_step_2/module_tests/test_module_hunt.py +18 -1
- bbot/test/test_step_2/module_tests/test_module_lightfuzz.py +1947 -0
- bbot/test/test_step_2/module_tests/test_module_paramminer_getparams.py +4 -1
- bbot/test/test_step_2/module_tests/test_module_paramminer_headers.py +46 -2
- bbot/test/test_step_2/module_tests/test_module_reflected_parameters.py +226 -0
- bbot/wordlists/paramminer_parameters.txt +0 -8
- {bbot-2.4.2.6109rc0.dist-info → bbot-2.4.2.6596rc0.dist-info}/METADATA +2 -1
- {bbot-2.4.2.6109rc0.dist-info → bbot-2.4.2.6596rc0.dist-info}/RECORD +67 -45
- {bbot-2.4.2.6109rc0.dist-info → bbot-2.4.2.6596rc0.dist-info}/LICENSE +0 -0
- {bbot-2.4.2.6109rc0.dist-info → bbot-2.4.2.6596rc0.dist-info}/WHEEL +0 -0
- {bbot-2.4.2.6109rc0.dist-info → bbot-2.4.2.6596rc0.dist-info}/entry_points.txt +0 -0
bbot/scanner/preset/preset.py
CHANGED
|
@@ -308,7 +308,7 @@ class Preset(metaclass=BasePreset):
|
|
|
308
308
|
|
|
309
309
|
@property
|
|
310
310
|
def preset_dir(self):
|
|
311
|
-
return self.bbot_home / "presets"
|
|
311
|
+
return (self.bbot_home / "presets").expanduser().resolve()
|
|
312
312
|
|
|
313
313
|
@property
|
|
314
314
|
def default_output_modules(self):
|
|
@@ -413,30 +413,32 @@ class Preset(metaclass=BasePreset):
|
|
|
413
413
|
self.log_debug("Getting baked")
|
|
414
414
|
# create a copy of self
|
|
415
415
|
baked_preset = copy(self)
|
|
416
|
-
|
|
416
|
+
|
|
417
417
|
# copy core
|
|
418
418
|
baked_preset.core = self.core.copy()
|
|
419
|
-
# copy module loader
|
|
420
|
-
baked_preset._module_loader = self.module_loader.copy()
|
|
421
|
-
# prepare os environment
|
|
422
|
-
os_environ = baked_preset.environ.prepare()
|
|
423
|
-
# find and replace preloaded modules with os environ
|
|
424
|
-
# this is different from the config variable substitution because it modifies
|
|
425
|
-
# the preloaded modules, i.e. their ansible playbooks
|
|
426
|
-
baked_preset.module_loader.find_and_replace(**os_environ)
|
|
427
|
-
# update os environ
|
|
428
|
-
os.environ.clear()
|
|
429
|
-
os.environ.update(os_environ)
|
|
430
419
|
|
|
431
|
-
|
|
432
|
-
|
|
420
|
+
if scan is not None:
|
|
421
|
+
baked_preset.scan = scan
|
|
422
|
+
# copy module loader
|
|
423
|
+
baked_preset._module_loader = self.module_loader.copy()
|
|
424
|
+
# prepare os environment
|
|
425
|
+
os_environ = baked_preset.environ.prepare()
|
|
426
|
+
# find and replace preloaded modules with os environ
|
|
427
|
+
# this is different from the config variable substitution because it modifies
|
|
428
|
+
# the preloaded modules, i.e. their ansible playbooks
|
|
429
|
+
baked_preset.module_loader.find_and_replace(**os_environ)
|
|
430
|
+
# update os environ
|
|
431
|
+
os.environ.clear()
|
|
432
|
+
os.environ.update(os_environ)
|
|
433
|
+
|
|
434
|
+
# assign baked preset to our scan
|
|
435
|
+
scan.preset = baked_preset
|
|
433
436
|
|
|
434
437
|
# validate log level options
|
|
435
438
|
baked_preset.apply_log_level(apply_core=scan is not None)
|
|
436
439
|
|
|
437
|
-
#
|
|
438
|
-
|
|
439
|
-
scan.preset = baked_preset
|
|
440
|
+
# validate flags, config options
|
|
441
|
+
baked_preset.validate()
|
|
440
442
|
|
|
441
443
|
# now that our requirements / exclusions are validated, we can start enabling modules
|
|
442
444
|
# enable scan modules
|
|
@@ -483,15 +485,19 @@ class Preset(metaclass=BasePreset):
|
|
|
483
485
|
from bbot.scanner.target import BBOTTarget
|
|
484
486
|
|
|
485
487
|
baked_preset._target = BBOTTarget(
|
|
486
|
-
*list(self._seeds),
|
|
488
|
+
*list(self._seeds),
|
|
489
|
+
whitelist=self._whitelist,
|
|
490
|
+
blacklist=self._blacklist,
|
|
491
|
+
strict_scope=self.strict_scope,
|
|
487
492
|
)
|
|
488
493
|
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
494
|
+
if scan is not None:
|
|
495
|
+
# evaluate conditions
|
|
496
|
+
if baked_preset.conditions:
|
|
497
|
+
from .conditions import ConditionEvaluator
|
|
492
498
|
|
|
493
|
-
|
|
494
|
-
|
|
499
|
+
evaluator = ConditionEvaluator(baked_preset)
|
|
500
|
+
evaluator.evaluate()
|
|
495
501
|
|
|
496
502
|
self._baked = True
|
|
497
503
|
return baked_preset
|
|
@@ -562,6 +568,12 @@ class Preset(metaclass=BasePreset):
|
|
|
562
568
|
return self.scope_config.get("strict", False)
|
|
563
569
|
|
|
564
570
|
def apply_log_level(self, apply_core=False):
|
|
571
|
+
"""
|
|
572
|
+
Apply the log level to the preset.
|
|
573
|
+
|
|
574
|
+
Args:
|
|
575
|
+
apply_core (bool, optional): If True, apply the log level to the core logger.
|
|
576
|
+
"""
|
|
565
577
|
# silent takes precedence
|
|
566
578
|
if self.silent:
|
|
567
579
|
self.verbose = False
|
|
@@ -920,20 +932,17 @@ class Preset(metaclass=BasePreset):
|
|
|
920
932
|
"""
|
|
921
933
|
Recursively find all the presets and return them as a dictionary
|
|
922
934
|
"""
|
|
923
|
-
preset_dir = self.preset_dir
|
|
924
|
-
home_dir = Path.home()
|
|
925
|
-
|
|
926
935
|
# first, add local preset dir to PRESET_PATH
|
|
927
936
|
PRESET_PATH.add_path(self.preset_dir)
|
|
928
937
|
|
|
929
938
|
# ensure local preset directory exists
|
|
930
|
-
mkdir(preset_dir)
|
|
939
|
+
mkdir(self.preset_dir)
|
|
931
940
|
|
|
932
941
|
global DEFAULT_PRESETS
|
|
933
942
|
if DEFAULT_PRESETS is None:
|
|
934
943
|
presets = {}
|
|
935
|
-
for
|
|
936
|
-
for
|
|
944
|
+
for preset_path in PRESET_PATH:
|
|
945
|
+
for ext in ("yml", "yaml"):
|
|
937
946
|
# for every yaml file
|
|
938
947
|
for original_filename in preset_path.rglob(f"**/*.{ext}"):
|
|
939
948
|
# not including symlinks
|
|
@@ -957,18 +966,14 @@ class Preset(metaclass=BasePreset):
|
|
|
957
966
|
|
|
958
967
|
local_preset = original_filename
|
|
959
968
|
# populate symlinks in local preset dir
|
|
960
|
-
if not original_filename.is_relative_to(preset_dir):
|
|
969
|
+
if not original_filename.is_relative_to(self.preset_dir):
|
|
961
970
|
relative_preset = original_filename.relative_to(preset_path)
|
|
962
|
-
local_preset = preset_dir / relative_preset
|
|
971
|
+
local_preset = self.preset_dir / relative_preset
|
|
963
972
|
mkdir(local_preset.parent, check_writable=False)
|
|
964
973
|
if not local_preset.exists():
|
|
965
974
|
local_preset.symlink_to(original_filename)
|
|
966
975
|
|
|
967
|
-
|
|
968
|
-
if local_preset.is_relative_to(home_dir):
|
|
969
|
-
local_preset = Path("~") / local_preset.relative_to(home_dir)
|
|
970
|
-
|
|
971
|
-
presets[local_preset] = (loaded_preset, category, preset_path, original_filename)
|
|
976
|
+
presets[local_preset.stem] = (loaded_preset, category, preset_path, original_filename)
|
|
972
977
|
|
|
973
978
|
# sort by name
|
|
974
979
|
DEFAULT_PRESETS = dict(sorted(presets.items(), key=lambda x: x[-1][0].name))
|
bbot/scanner/scanner.py
CHANGED
|
@@ -214,6 +214,12 @@ class Scanner:
|
|
|
214
214
|
self.warning(
|
|
215
215
|
"You have enabled custom HTTP headers. These will be attached to all in-scope requests and all requests made by httpx."
|
|
216
216
|
)
|
|
217
|
+
# custom HTTP cookies warning
|
|
218
|
+
self.custom_http_cookies = self.web_config.get("http_cookies", {})
|
|
219
|
+
if self.custom_http_cookies:
|
|
220
|
+
self.warning(
|
|
221
|
+
"You have enabled custom HTTP cookies. These will be attached to all in-scope requests and all requests made by httpx."
|
|
222
|
+
)
|
|
217
223
|
|
|
218
224
|
# url file extensions
|
|
219
225
|
self.url_extension_blacklist = {e.lower() for e in self.config.get("url_extension_blacklist", [])}
|
bbot/scripts/docs.py
CHANGED
|
@@ -198,15 +198,15 @@ def update_docs():
|
|
|
198
198
|
update_md_files("BBOT PRESETS", bbot_presets_table)
|
|
199
199
|
|
|
200
200
|
# BBOT presets
|
|
201
|
-
for
|
|
201
|
+
for _, (loaded_preset, category, preset_path, original_filename) in DEFAULT_PRESET.all_presets.items():
|
|
202
202
|
preset_yaml = f"""
|
|
203
|
-
```yaml title={
|
|
203
|
+
```yaml title={preset_path.name}
|
|
204
204
|
{loaded_preset._yaml_str}
|
|
205
205
|
```
|
|
206
206
|
"""
|
|
207
207
|
preset_yaml_expandable = f"""
|
|
208
208
|
<details>
|
|
209
|
-
<summary><b><code>{
|
|
209
|
+
<summary><b><code>{preset_path.name}</code></b></summary>
|
|
210
210
|
|
|
211
211
|
```yaml
|
|
212
212
|
{loaded_preset._yaml_str}
|
|
@@ -218,11 +218,11 @@ def update_docs():
|
|
|
218
218
|
update_md_files(f"BBOT {loaded_preset.name.upper()} PRESET EXPANDABLE", preset_yaml_expandable)
|
|
219
219
|
|
|
220
220
|
content = []
|
|
221
|
-
for
|
|
221
|
+
for _, (loaded_preset, category, preset_path, original_filename) in DEFAULT_PRESET.all_presets.items():
|
|
222
222
|
yaml_str = loaded_preset._yaml_str
|
|
223
223
|
indent = " " * 4
|
|
224
224
|
yaml_str = f"\n{indent}".join(yaml_str.splitlines())
|
|
225
|
-
filename = homedir_collapseuser(
|
|
225
|
+
filename = homedir_collapseuser(preset_path)
|
|
226
226
|
|
|
227
227
|
num_modules = len(loaded_preset.scan_modules)
|
|
228
228
|
modules = ", ".join(sorted([f"`{m}`" for m in loaded_preset.scan_modules]))
|
|
@@ -18,7 +18,7 @@ def test__module__tests():
|
|
|
18
18
|
preset = Preset()
|
|
19
19
|
|
|
20
20
|
# make sure each module has a .py file
|
|
21
|
-
for module_name in preset.module_loader.preloaded():
|
|
21
|
+
for module_name, preloaded in preset.module_loader.preloaded().items():
|
|
22
22
|
module_name = module_name.lower()
|
|
23
23
|
assert module_name in module_test_files, f'No test file found for module "{module_name}"'
|
|
24
24
|
|
|
@@ -460,6 +460,13 @@ async def test_helpers_misc(helpers, scan, bbot_scanner, bbot_httpserver):
|
|
|
460
460
|
s = "asdf {unused} {used}"
|
|
461
461
|
assert helpers.safe_format(s, used="fdsa") == "asdf {unused} fdsa"
|
|
462
462
|
|
|
463
|
+
# is_printable
|
|
464
|
+
assert helpers.is_printable("asdf") is True
|
|
465
|
+
assert helpers.is_printable(r"""~!@#$^&*()_+=-<>:"?,./;'[]\{}|""") is True
|
|
466
|
+
assert helpers.is_printable("ドメイン.テスト") is True
|
|
467
|
+
assert helpers.is_printable("4") is True
|
|
468
|
+
assert helpers.is_printable("asdf\x00") is False
|
|
469
|
+
|
|
463
470
|
# punycode
|
|
464
471
|
assert helpers.smart_encode_punycode("ドメイン.テスト") == "xn--eckwd4c7c.xn--zckzah"
|
|
465
472
|
assert helpers.smart_decode_punycode("xn--eckwd4c7c.xn--zckzah") == "ドメイン.テスト"
|
|
@@ -596,7 +596,7 @@ class TestModule1(BaseModule):
|
|
|
596
596
|
from bbot.modules.output.base import BaseOutputModule
|
|
597
597
|
|
|
598
598
|
class TestModule2(BaseOutputModule):
|
|
599
|
-
|
|
599
|
+
watched_events = []
|
|
600
600
|
"""
|
|
601
601
|
)
|
|
602
602
|
|
|
@@ -607,7 +607,7 @@ class TestModule2(BaseOutputModule):
|
|
|
607
607
|
from bbot.modules.internal.base import BaseInternalModule
|
|
608
608
|
|
|
609
609
|
class TestModule3(BaseInternalModule):
|
|
610
|
-
|
|
610
|
+
watched_events = []
|
|
611
611
|
"""
|
|
612
612
|
)
|
|
613
613
|
|
|
@@ -478,3 +478,23 @@ async def test_web_cookies(bbot_scanner, httpx_mock):
|
|
|
478
478
|
assert not client2.cookies
|
|
479
479
|
|
|
480
480
|
await scan._cleanup()
|
|
481
|
+
|
|
482
|
+
|
|
483
|
+
@pytest.mark.asyncio
|
|
484
|
+
async def test_http_sendcookies(bbot_scanner, bbot_httpserver):
|
|
485
|
+
endpoint = "/"
|
|
486
|
+
url = bbot_httpserver.url_for(endpoint)
|
|
487
|
+
from werkzeug.wrappers import Response
|
|
488
|
+
|
|
489
|
+
def echo_cookies_handler(request):
|
|
490
|
+
cookies = request.cookies
|
|
491
|
+
cookie_str = "; ".join([f"{key}={value}" for key, value in cookies.items()])
|
|
492
|
+
return Response(f"Echoed Cookies: {cookie_str}\nEchoed Headers: {request.headers}")
|
|
493
|
+
|
|
494
|
+
bbot_httpserver.expect_request(uri=endpoint).respond_with_handler(echo_cookies_handler)
|
|
495
|
+
scan1 = bbot_scanner("127.0.0.1", config={"web": {"debug": True}})
|
|
496
|
+
r1 = await scan1.helpers.request(url, cookies={"foo": "bar"})
|
|
497
|
+
|
|
498
|
+
assert r1 is not None, "Request to self-signed SSL server went through even with ssl_verify=True"
|
|
499
|
+
assert "bar" in r1.text
|
|
500
|
+
await scan1._cleanup()
|
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
async def test_web_envelopes():
|
|
5
|
+
from bbot.core.helpers.web.envelopes import (
|
|
6
|
+
BaseEnvelope,
|
|
7
|
+
TextEnvelope,
|
|
8
|
+
HexEnvelope,
|
|
9
|
+
B64Envelope,
|
|
10
|
+
JSONEnvelope,
|
|
11
|
+
XMLEnvelope,
|
|
12
|
+
URLEnvelope,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
# simple text
|
|
16
|
+
text_envelope = BaseEnvelope.detect("foo")
|
|
17
|
+
assert isinstance(text_envelope, TextEnvelope)
|
|
18
|
+
assert text_envelope.unpacked_data() == "foo"
|
|
19
|
+
assert text_envelope.subparams == {"__default__": "foo"}
|
|
20
|
+
expected_subparams = [([], "foo")]
|
|
21
|
+
assert list(text_envelope.get_subparams()) == expected_subparams
|
|
22
|
+
for subparam, value in expected_subparams:
|
|
23
|
+
assert text_envelope.get_subparam(subparam) == value
|
|
24
|
+
assert text_envelope.pack() == "foo"
|
|
25
|
+
assert text_envelope.num_envelopes == 0
|
|
26
|
+
assert text_envelope.get_subparam() == "foo"
|
|
27
|
+
text_envelope.set_subparam(value="bar")
|
|
28
|
+
assert text_envelope.get_subparam() == "bar"
|
|
29
|
+
assert text_envelope.unpacked_data() == "bar"
|
|
30
|
+
|
|
31
|
+
# simple binary
|
|
32
|
+
# binary_envelope = BaseEnvelope.detect("foo\x00")
|
|
33
|
+
# assert isinstance(binary_envelope, BinaryEnvelope)
|
|
34
|
+
# assert binary_envelope.unpacked_data == "foo\x00"
|
|
35
|
+
# assert binary_envelope.packed_data == "foo\x00"
|
|
36
|
+
# assert binary_envelope.subparams == {"__default__": "foo\x00"}
|
|
37
|
+
|
|
38
|
+
# text encoded as hex
|
|
39
|
+
hex_envelope = BaseEnvelope.detect("706172616d")
|
|
40
|
+
assert isinstance(hex_envelope, HexEnvelope)
|
|
41
|
+
assert hex_envelope.unpacked_data(recursive=True) == "param"
|
|
42
|
+
hex_inner_envelope = hex_envelope.unpacked_data(recursive=False)
|
|
43
|
+
assert isinstance(hex_inner_envelope, TextEnvelope)
|
|
44
|
+
assert hex_inner_envelope.unpacked_data(recursive=False) == "param"
|
|
45
|
+
assert hex_inner_envelope.unpacked_data(recursive=True) == "param"
|
|
46
|
+
assert list(hex_envelope.get_subparams(recursive=False)) == [([], hex_inner_envelope)]
|
|
47
|
+
assert list(hex_envelope.get_subparams(recursive=True)) == [([], "param")]
|
|
48
|
+
assert hex_inner_envelope.unpacked_data() == "param"
|
|
49
|
+
assert hex_inner_envelope.subparams == {"__default__": "param"}
|
|
50
|
+
expected_subparams = [([], "param")]
|
|
51
|
+
assert list(hex_inner_envelope.get_subparams()) == expected_subparams
|
|
52
|
+
for subparam, value in expected_subparams:
|
|
53
|
+
assert hex_inner_envelope.get_subparam(subparam) == value
|
|
54
|
+
assert hex_envelope.pack() == "706172616d"
|
|
55
|
+
assert hex_envelope.num_envelopes == 1
|
|
56
|
+
assert hex_envelope.get_subparam() == "param"
|
|
57
|
+
hex_envelope.set_subparam(value="asdf")
|
|
58
|
+
assert hex_envelope.get_subparam() == "asdf"
|
|
59
|
+
assert hex_envelope.unpacked_data() == "asdf"
|
|
60
|
+
assert hex_envelope.pack() == "61736466"
|
|
61
|
+
|
|
62
|
+
# text encoded as base64
|
|
63
|
+
base64_envelope = BaseEnvelope.detect("cGFyYW0=")
|
|
64
|
+
assert isinstance(base64_envelope, B64Envelope)
|
|
65
|
+
assert base64_envelope.unpacked_data() == "param"
|
|
66
|
+
base64_inner_envelope = base64_envelope.unpacked_data(recursive=False)
|
|
67
|
+
assert isinstance(base64_inner_envelope, TextEnvelope)
|
|
68
|
+
assert list(base64_envelope.get_subparams(recursive=False)) == [([], base64_inner_envelope)]
|
|
69
|
+
assert list(base64_envelope.get_subparams()) == [([], "param")]
|
|
70
|
+
assert base64_inner_envelope.pack() == "param"
|
|
71
|
+
assert base64_inner_envelope.unpacked_data() == "param"
|
|
72
|
+
assert base64_inner_envelope.subparams == {"__default__": "param"}
|
|
73
|
+
expected_subparams = [([], "param")]
|
|
74
|
+
assert list(base64_inner_envelope.get_subparams()) == expected_subparams
|
|
75
|
+
for subparam, value in expected_subparams:
|
|
76
|
+
assert base64_inner_envelope.get_subparam(subparam) == value
|
|
77
|
+
assert base64_envelope.num_envelopes == 1
|
|
78
|
+
base64_envelope.set_subparam(value="asdf")
|
|
79
|
+
assert base64_envelope.get_subparam() == "asdf"
|
|
80
|
+
assert base64_envelope.unpacked_data() == "asdf"
|
|
81
|
+
assert base64_envelope.pack() == "YXNkZg=="
|
|
82
|
+
|
|
83
|
+
# test inside hex inside base64
|
|
84
|
+
hex_envelope = BaseEnvelope.detect("634746795957303d")
|
|
85
|
+
assert isinstance(hex_envelope, HexEnvelope)
|
|
86
|
+
assert hex_envelope.get_subparam() == "param"
|
|
87
|
+
assert hex_envelope.unpacked_data() == "param"
|
|
88
|
+
base64_envelope = hex_envelope.unpacked_data(recursive=False)
|
|
89
|
+
assert isinstance(base64_envelope, B64Envelope)
|
|
90
|
+
assert base64_envelope.get_subparam() == "param"
|
|
91
|
+
assert base64_envelope.unpacked_data() == "param"
|
|
92
|
+
text_envelope = base64_envelope.unpacked_data(recursive=False)
|
|
93
|
+
assert isinstance(text_envelope, TextEnvelope)
|
|
94
|
+
assert text_envelope.get_subparam() == "param"
|
|
95
|
+
assert text_envelope.unpacked_data() == "param"
|
|
96
|
+
hex_envelope.set_subparam(value="asdf")
|
|
97
|
+
assert hex_envelope.get_subparam() == "asdf"
|
|
98
|
+
assert hex_envelope.unpacked_data() == "asdf"
|
|
99
|
+
assert text_envelope.get_subparam() == "asdf"
|
|
100
|
+
assert text_envelope.unpacked_data() == "asdf"
|
|
101
|
+
assert base64_envelope.get_subparam() == "asdf"
|
|
102
|
+
assert base64_envelope.unpacked_data() == "asdf"
|
|
103
|
+
|
|
104
|
+
# URL-encoded text
|
|
105
|
+
url_encoded_envelope = BaseEnvelope.detect("a%20b%20c")
|
|
106
|
+
assert isinstance(url_encoded_envelope, URLEnvelope)
|
|
107
|
+
assert url_encoded_envelope.pack() == "a%20b%20c"
|
|
108
|
+
assert url_encoded_envelope.unpacked_data() == "a b c"
|
|
109
|
+
url_inner_envelope = url_encoded_envelope.unpacked_data(recursive=False)
|
|
110
|
+
assert isinstance(url_inner_envelope, TextEnvelope)
|
|
111
|
+
assert url_inner_envelope.unpacked_data(recursive=False) == "a b c"
|
|
112
|
+
assert url_inner_envelope.unpacked_data(recursive=True) == "a b c"
|
|
113
|
+
assert list(url_encoded_envelope.get_subparams(recursive=False)) == [([], url_inner_envelope)]
|
|
114
|
+
assert list(url_encoded_envelope.get_subparams(recursive=True)) == [([], "a b c")]
|
|
115
|
+
assert url_inner_envelope.pack() == "a b c"
|
|
116
|
+
assert url_inner_envelope.unpacked_data() == "a b c"
|
|
117
|
+
assert url_inner_envelope.subparams == {"__default__": "a b c"}
|
|
118
|
+
expected_subparams = [([], "a b c")]
|
|
119
|
+
assert list(url_inner_envelope.get_subparams()) == expected_subparams
|
|
120
|
+
for subparam, value in expected_subparams:
|
|
121
|
+
assert url_inner_envelope.get_subparam(subparam) == value
|
|
122
|
+
assert url_encoded_envelope.num_envelopes == 1
|
|
123
|
+
url_encoded_envelope.set_subparam(value="a s d f")
|
|
124
|
+
assert url_encoded_envelope.get_subparam() == "a s d f"
|
|
125
|
+
assert url_encoded_envelope.unpacked_data() == "a s d f"
|
|
126
|
+
assert url_encoded_envelope.pack() == "a%20s%20d%20f"
|
|
127
|
+
|
|
128
|
+
# json
|
|
129
|
+
json_envelope = BaseEnvelope.detect('{"param1": "val1", "param2": {"param3": "val3"}}')
|
|
130
|
+
assert isinstance(json_envelope, JSONEnvelope)
|
|
131
|
+
assert json_envelope.pack() == '{"param1": "val1", "param2": {"param3": "val3"}}'
|
|
132
|
+
assert json_envelope.unpacked_data() == {"param1": "val1", "param2": {"param3": "val3"}}
|
|
133
|
+
assert json_envelope.unpacked_data(recursive=False) == {"param1": "val1", "param2": {"param3": "val3"}}
|
|
134
|
+
assert json_envelope.unpacked_data(recursive=True) == {"param1": "val1", "param2": {"param3": "val3"}}
|
|
135
|
+
assert json_envelope.subparams == {"param1": "val1", "param2": {"param3": "val3"}}
|
|
136
|
+
expected_subparams = [
|
|
137
|
+
(["param1"], "val1"),
|
|
138
|
+
(["param2", "param3"], "val3"),
|
|
139
|
+
]
|
|
140
|
+
assert list(json_envelope.get_subparams()) == expected_subparams
|
|
141
|
+
for subparam, value in expected_subparams:
|
|
142
|
+
assert json_envelope.get_subparam(subparam) == value
|
|
143
|
+
json_envelope.selected_subparam = ["param2", "param3"]
|
|
144
|
+
assert json_envelope.get_subparam() == "val3"
|
|
145
|
+
assert json_envelope.num_envelopes == 1
|
|
146
|
+
|
|
147
|
+
# prevent json over-detection
|
|
148
|
+
just_a_string = BaseEnvelope.detect("10")
|
|
149
|
+
assert not isinstance(just_a_string, JSONEnvelope)
|
|
150
|
+
|
|
151
|
+
# xml
|
|
152
|
+
xml_envelope = BaseEnvelope.detect(
|
|
153
|
+
'<root><param1 attr="attr1">val1</param1><param2><param3>val3</param3></param2></root>'
|
|
154
|
+
)
|
|
155
|
+
assert isinstance(xml_envelope, XMLEnvelope)
|
|
156
|
+
assert (
|
|
157
|
+
xml_envelope.pack()
|
|
158
|
+
== '<?xml version="1.0" encoding="utf-8"?>\n<root><param1 attr="attr1">val1</param1><param2><param3>val3</param3></param2></root>'
|
|
159
|
+
)
|
|
160
|
+
assert xml_envelope.unpacked_data() == {
|
|
161
|
+
"root": {"param1": {"@attr": "attr1", "#text": "val1"}, "param2": {"param3": "val3"}}
|
|
162
|
+
}
|
|
163
|
+
assert xml_envelope.unpacked_data(recursive=False) == {
|
|
164
|
+
"root": {"param1": {"@attr": "attr1", "#text": "val1"}, "param2": {"param3": "val3"}}
|
|
165
|
+
}
|
|
166
|
+
assert xml_envelope.unpacked_data(recursive=True) == {
|
|
167
|
+
"root": {"param1": {"@attr": "attr1", "#text": "val1"}, "param2": {"param3": "val3"}}
|
|
168
|
+
}
|
|
169
|
+
assert xml_envelope.subparams == {
|
|
170
|
+
"root": {"param1": {"@attr": "attr1", "#text": "val1"}, "param2": {"param3": "val3"}}
|
|
171
|
+
}
|
|
172
|
+
expected_subparams = [
|
|
173
|
+
(["root", "param1", "@attr"], "attr1"),
|
|
174
|
+
(["root", "param1", "#text"], "val1"),
|
|
175
|
+
(["root", "param2", "param3"], "val3"),
|
|
176
|
+
]
|
|
177
|
+
assert list(xml_envelope.get_subparams()) == expected_subparams
|
|
178
|
+
for subparam, value in expected_subparams:
|
|
179
|
+
assert xml_envelope.get_subparam(subparam) == value
|
|
180
|
+
assert xml_envelope.num_envelopes == 1
|
|
181
|
+
|
|
182
|
+
# json inside base64
|
|
183
|
+
base64_json_envelope = BaseEnvelope.detect("eyJwYXJhbTEiOiAidmFsMSIsICJwYXJhbTIiOiB7InBhcmFtMyI6ICJ2YWwzIn19")
|
|
184
|
+
assert isinstance(base64_json_envelope, B64Envelope)
|
|
185
|
+
assert base64_json_envelope.pack() == "eyJwYXJhbTEiOiAidmFsMSIsICJwYXJhbTIiOiB7InBhcmFtMyI6ICJ2YWwzIn19"
|
|
186
|
+
assert base64_json_envelope.unpacked_data() == {"param1": "val1", "param2": {"param3": "val3"}}
|
|
187
|
+
base64_inner_envelope = base64_json_envelope.unpacked_data(recursive=False)
|
|
188
|
+
assert isinstance(base64_inner_envelope, JSONEnvelope)
|
|
189
|
+
assert base64_inner_envelope.pack() == '{"param1": "val1", "param2": {"param3": "val3"}}'
|
|
190
|
+
assert base64_inner_envelope.unpacked_data() == {"param1": "val1", "param2": {"param3": "val3"}}
|
|
191
|
+
assert base64_inner_envelope.subparams == {"param1": "val1", "param2": {"param3": "val3"}}
|
|
192
|
+
expected_subparams = [
|
|
193
|
+
(["param1"], "val1"),
|
|
194
|
+
(["param2", "param3"], "val3"),
|
|
195
|
+
]
|
|
196
|
+
assert list(base64_json_envelope.get_subparams()) == expected_subparams
|
|
197
|
+
for subparam, value in expected_subparams:
|
|
198
|
+
assert base64_json_envelope.get_subparam(subparam) == value
|
|
199
|
+
assert base64_json_envelope.num_envelopes == 2
|
|
200
|
+
with pytest.raises(ValueError):
|
|
201
|
+
assert base64_json_envelope.get_subparam()
|
|
202
|
+
base64_json_envelope.selected_subparam = ["param2", "param3"]
|
|
203
|
+
assert base64_json_envelope.get_subparam() == "val3"
|
|
204
|
+
|
|
205
|
+
# xml inside url inside hex inside base64
|
|
206
|
+
nested_xml_envelope = BaseEnvelope.detect(
|
|
207
|
+
"MjUzMzYzMjUzNzMyMjUzNjY2MjUzNjY2MjUzNzM0MjUzMzY1MjUzMzYzMjUzNzMwMjUzNjMxMjUzNzMyMjUzNjMxMjUzNjY0MjUzMzMxMjUzMjMwMjUzNjMxMjUzNzM0MjUzNzM0MjUzNzMyMjUzMzY0MjUzMjMyMjUzNzM2MjUzNjMxMjUzNjYzMjUzMzMxMjUzMjMyMjUzMzY1MjUzNzM2MjUzNjMxMjUzNjYzMjUzMzMxMjUzMzYzMjUzMjY2MjUzNzMwMjUzNjMxMjUzNzMyMjUzNjMxMjUzNjY0MjUzMzMxMjUzMzY1MjUzMzYzMjUzNzMwMjUzNjMxMjUzNzMyMjUzNjMxMjUzNjY0MjUzMzMyMjUzMzY1MjUzMzYzMjUzNzMwMjUzNjMxMjUzNzMyMjUzNjMxMjUzNjY0MjUzMzMzMjUzMzY1MjUzNzM2MjUzNjMxMjUzNjYzMjUzMzMzMjUzMzYzMjUzMjY2MjUzNzMwMjUzNjMxMjUzNzMyMjUzNjMxMjUzNjY0MjUzMzMzMjUzMzY1MjUzMzYzMjUzMjY2MjUzNzMwMjUzNjMxMjUzNzMyMjUzNjMxMjUzNjY0MjUzMzMyMjUzMzY1MjUzMzYzMjUzMjY2MjUzNzMyMjUzNjY2MjUzNjY2MjUzNzM0MjUzMzY1"
|
|
208
|
+
)
|
|
209
|
+
assert isinstance(nested_xml_envelope, B64Envelope)
|
|
210
|
+
assert nested_xml_envelope.unpacked_data() == {
|
|
211
|
+
"root": {"param1": {"@attr": "val1", "#text": "val1"}, "param2": {"param3": "val3"}}
|
|
212
|
+
}
|
|
213
|
+
assert (
|
|
214
|
+
nested_xml_envelope.pack()
|
|
215
|
+
== "MjUzMzQzMjUzMzQ2Nzg2ZDZjMjUzMjMwNzY2NTcyNzM2OTZmNmUyNTMzNDQyNTMyMzIzMTJlMzAyNTMyMzIyNTMyMzA2NTZlNjM2ZjY0Njk2ZTY3MjUzMzQ0MjUzMjMyNzU3NDY2MmQzODI1MzIzMjI1MzM0NjI1MzM0NTI1MzA0MTI1MzM0MzcyNmY2Zjc0MjUzMzQ1MjUzMzQzNzA2MTcyNjE2ZDMxMjUzMjMwNjE3NDc0NzIyNTMzNDQyNTMyMzI3NjYxNmMzMTI1MzIzMjI1MzM0NTc2NjE2YzMxMjUzMzQzMmY3MDYxNzI2MTZkMzEyNTMzNDUyNTMzNDM3MDYxNzI2MTZkMzIyNTMzNDUyNTMzNDM3MDYxNzI2MTZkMzMyNTMzNDU3NjYxNmMzMzI1MzM0MzJmNzA2MTcyNjE2ZDMzMjUzMzQ1MjUzMzQzMmY3MDYxNzI2MTZkMzIyNTMzNDUyNTMzNDMyZjcyNmY2Zjc0MjUzMzQ1"
|
|
216
|
+
)
|
|
217
|
+
inner_hex_envelope = nested_xml_envelope.unpacked_data(recursive=False)
|
|
218
|
+
assert isinstance(inner_hex_envelope, HexEnvelope)
|
|
219
|
+
assert (
|
|
220
|
+
inner_hex_envelope.pack()
|
|
221
|
+
== "253343253346786d6c25323076657273696f6e253344253232312e30253232253230656e636f64696e672533442532327574662d38253232253346253345253041253343726f6f74253345253343706172616d312532306174747225334425323276616c3125323225334576616c312533432f706172616d31253345253343706172616d32253345253343706172616d3325334576616c332533432f706172616d332533452533432f706172616d322533452533432f726f6f74253345"
|
|
222
|
+
)
|
|
223
|
+
inner_url_envelope = inner_hex_envelope.unpacked_data(recursive=False)
|
|
224
|
+
assert isinstance(inner_url_envelope, URLEnvelope)
|
|
225
|
+
assert (
|
|
226
|
+
inner_url_envelope.pack()
|
|
227
|
+
== r"%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22utf-8%22%3F%3E%0A%3Croot%3E%3Cparam1%20attr%3D%22val1%22%3Eval1%3C/param1%3E%3Cparam2%3E%3Cparam3%3Eval3%3C/param3%3E%3C/param2%3E%3C/root%3E"
|
|
228
|
+
)
|
|
229
|
+
inner_xml_envelope = inner_url_envelope.unpacked_data(recursive=False)
|
|
230
|
+
assert isinstance(inner_xml_envelope, XMLEnvelope)
|
|
231
|
+
assert (
|
|
232
|
+
inner_xml_envelope.pack()
|
|
233
|
+
== '<?xml version="1.0" encoding="utf-8"?>\n<root><param1 attr="val1">val1</param1><param2><param3>val3</param3></param2></root>'
|
|
234
|
+
)
|
|
235
|
+
assert inner_xml_envelope.unpacked_data() == {
|
|
236
|
+
"root": {"param1": {"@attr": "val1", "#text": "val1"}, "param2": {"param3": "val3"}}
|
|
237
|
+
}
|
|
238
|
+
assert inner_xml_envelope.subparams == {
|
|
239
|
+
"root": {"param1": {"@attr": "val1", "#text": "val1"}, "param2": {"param3": "val3"}}
|
|
240
|
+
}
|
|
241
|
+
expected_subparams = [
|
|
242
|
+
(["root", "param1", "@attr"], "val1"),
|
|
243
|
+
(["root", "param1", "#text"], "val1"),
|
|
244
|
+
(["root", "param2", "param3"], "val3"),
|
|
245
|
+
]
|
|
246
|
+
assert list(nested_xml_envelope.get_subparams()) == expected_subparams
|
|
247
|
+
for subparam, value in expected_subparams:
|
|
248
|
+
assert nested_xml_envelope.get_subparam(subparam) == value
|
|
249
|
+
assert nested_xml_envelope.num_envelopes == 4
|
|
250
|
+
|
|
251
|
+
# manipulating text inside hex
|
|
252
|
+
hex_envelope = BaseEnvelope.detect("706172616d")
|
|
253
|
+
expected_subparams = [([], "param")]
|
|
254
|
+
assert list(hex_envelope.get_subparams()) == expected_subparams
|
|
255
|
+
for subparam, value in expected_subparams:
|
|
256
|
+
assert hex_envelope.get_subparam(subparam) == value
|
|
257
|
+
hex_envelope.set_subparam([], "asdf")
|
|
258
|
+
expected_subparams = [([], "asdf")]
|
|
259
|
+
assert list(hex_envelope.get_subparams()) == expected_subparams
|
|
260
|
+
for subparam, value in expected_subparams:
|
|
261
|
+
assert hex_envelope.get_subparam(subparam) == value
|
|
262
|
+
assert hex_envelope.unpacked_data() == "asdf"
|
|
263
|
+
|
|
264
|
+
# manipulating json inside base64
|
|
265
|
+
base64_json_envelope = BaseEnvelope.detect("eyJwYXJhbTEiOiAidmFsMSIsICJwYXJhbTIiOiB7InBhcmFtMyI6ICJ2YWwzIn19")
|
|
266
|
+
expected_subparams = [
|
|
267
|
+
(["param1"], "val1"),
|
|
268
|
+
(["param2", "param3"], "val3"),
|
|
269
|
+
]
|
|
270
|
+
assert list(base64_json_envelope.get_subparams()) == expected_subparams
|
|
271
|
+
for subparam, value in expected_subparams:
|
|
272
|
+
assert base64_json_envelope.get_subparam(subparam) == value
|
|
273
|
+
base64_json_envelope.set_subparam(["param1"], {"asdf": [None], "fdsa": 1.0})
|
|
274
|
+
expected_subparams = [
|
|
275
|
+
(["param1", "asdf"], [None]),
|
|
276
|
+
(["param1", "fdsa"], 1.0),
|
|
277
|
+
(["param2", "param3"], "val3"),
|
|
278
|
+
]
|
|
279
|
+
assert list(base64_json_envelope.get_subparams()) == expected_subparams
|
|
280
|
+
for subparam, value in expected_subparams:
|
|
281
|
+
assert base64_json_envelope.get_subparam(subparam) == value
|
|
282
|
+
base64_json_envelope.set_subparam(["param2", "param3"], {"1234": [None], "4321": 1.0})
|
|
283
|
+
expected_subparams = [
|
|
284
|
+
(["param1", "asdf"], [None]),
|
|
285
|
+
(["param1", "fdsa"], 1.0),
|
|
286
|
+
(["param2", "param3", "1234"], [None]),
|
|
287
|
+
(["param2", "param3", "4321"], 1.0),
|
|
288
|
+
]
|
|
289
|
+
assert list(base64_json_envelope.get_subparams()) == expected_subparams
|
|
290
|
+
base64_json_envelope.set_subparam(["param2"], None)
|
|
291
|
+
expected_subparams = [
|
|
292
|
+
(["param1", "asdf"], [None]),
|
|
293
|
+
(["param1", "fdsa"], 1.0),
|
|
294
|
+
(["param2"], None),
|
|
295
|
+
]
|
|
296
|
+
assert list(base64_json_envelope.get_subparams()) == expected_subparams
|
|
297
|
+
|
|
298
|
+
# xml inside url inside base64
|
|
299
|
+
xml_envelope = BaseEnvelope.detect(
|
|
300
|
+
"JTNDP3htbCUyMHZlcnNpb249JTIyMS4wJTIyJTIwZW5jb2Rpbmc9JTIydXRmLTglMjI/JTNFJTBBJTNDcm9vdCUzRSUzQ3BhcmFtMSUyMGF0dHI9JTIydmFsMSUyMiUzRXZhbDElM0MvcGFyYW0xJTNFJTNDcGFyYW0yJTNFJTNDcGFyYW0zJTNFdmFsMyUzQy9wYXJhbTMlM0UlM0MvcGFyYW0yJTNFJTNDL3Jvb3QlM0U="
|
|
301
|
+
)
|
|
302
|
+
assert (
|
|
303
|
+
xml_envelope.pack()
|
|
304
|
+
== "JTNDJTNGeG1sJTIwdmVyc2lvbiUzRCUyMjEuMCUyMiUyMGVuY29kaW5nJTNEJTIydXRmLTglMjIlM0YlM0UlMEElM0Nyb290JTNFJTNDcGFyYW0xJTIwYXR0ciUzRCUyMnZhbDElMjIlM0V2YWwxJTNDL3BhcmFtMSUzRSUzQ3BhcmFtMiUzRSUzQ3BhcmFtMyUzRXZhbDMlM0MvcGFyYW0zJTNFJTNDL3BhcmFtMiUzRSUzQy9yb290JTNF"
|
|
305
|
+
)
|
|
306
|
+
expected_subparams = [
|
|
307
|
+
(["root", "param1", "@attr"], "val1"),
|
|
308
|
+
(["root", "param1", "#text"], "val1"),
|
|
309
|
+
(["root", "param2", "param3"], "val3"),
|
|
310
|
+
]
|
|
311
|
+
assert list(xml_envelope.get_subparams()) == expected_subparams
|
|
312
|
+
xml_envelope.set_subparam(["root", "param1", "@attr"], "asdf")
|
|
313
|
+
expected_subparams = [
|
|
314
|
+
(["root", "param1", "@attr"], "asdf"),
|
|
315
|
+
(["root", "param1", "#text"], "val1"),
|
|
316
|
+
(["root", "param2", "param3"], "val3"),
|
|
317
|
+
]
|
|
318
|
+
assert list(xml_envelope.get_subparams()) == expected_subparams
|
|
319
|
+
assert (
|
|
320
|
+
xml_envelope.pack()
|
|
321
|
+
== "JTNDJTNGeG1sJTIwdmVyc2lvbiUzRCUyMjEuMCUyMiUyMGVuY29kaW5nJTNEJTIydXRmLTglMjIlM0YlM0UlMEElM0Nyb290JTNFJTNDcGFyYW0xJTIwYXR0ciUzRCUyMmFzZGYlMjIlM0V2YWwxJTNDL3BhcmFtMSUzRSUzQ3BhcmFtMiUzRSUzQ3BhcmFtMyUzRXZhbDMlM0MvcGFyYW0zJTNFJTNDL3BhcmFtMiUzRSUzQy9yb290JTNF"
|
|
322
|
+
)
|
|
323
|
+
xml_envelope.set_subparam(["root", "param2", "param3"], {"1234": [None], "4321": 1.0})
|
|
324
|
+
expected_subparams = [
|
|
325
|
+
(["root", "param1", "@attr"], "asdf"),
|
|
326
|
+
(["root", "param1", "#text"], "val1"),
|
|
327
|
+
(["root", "param2", "param3", "1234"], [None]),
|
|
328
|
+
(["root", "param2", "param3", "4321"], 1.0),
|
|
329
|
+
]
|
|
330
|
+
assert list(xml_envelope.get_subparams()) == expected_subparams
|
|
331
|
+
|
|
332
|
+
# null
|
|
333
|
+
null_envelope = BaseEnvelope.detect("null")
|
|
334
|
+
assert isinstance(null_envelope, JSONEnvelope)
|
|
335
|
+
assert null_envelope.unpacked_data() is None
|
|
336
|
+
assert null_envelope.pack() == "null"
|
|
337
|
+
expected_subparams = [([], None)]
|
|
338
|
+
assert list(null_envelope.get_subparams()) == expected_subparams
|
|
339
|
+
for subparam, value in expected_subparams:
|
|
340
|
+
assert null_envelope.get_subparam(subparam) == value
|
|
341
|
+
|
|
342
|
+
tiny_base64 = BaseEnvelope.detect("YWJi")
|
|
343
|
+
assert isinstance(tiny_base64, TextEnvelope)
|