bbot 2.0.1.4720rc0__py3-none-any.whl → 2.3.0.5401rc0__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/cli.py +3 -7
- bbot/core/config/files.py +0 -1
- bbot/core/config/logger.py +34 -4
- bbot/core/core.py +21 -4
- bbot/core/engine.py +9 -8
- bbot/core/event/base.py +131 -52
- bbot/core/helpers/bloom.py +10 -3
- bbot/core/helpers/command.py +8 -7
- bbot/core/helpers/depsinstaller/installer.py +31 -13
- bbot/core/helpers/diff.py +10 -10
- bbot/core/helpers/dns/brute.py +7 -4
- bbot/core/helpers/dns/dns.py +1 -2
- bbot/core/helpers/dns/engine.py +4 -6
- bbot/core/helpers/dns/helpers.py +2 -2
- bbot/core/helpers/dns/mock.py +0 -1
- bbot/core/helpers/files.py +1 -1
- bbot/core/helpers/helper.py +7 -4
- bbot/core/helpers/interactsh.py +3 -3
- bbot/core/helpers/libmagic.py +65 -0
- bbot/core/helpers/misc.py +65 -22
- bbot/core/helpers/names_generator.py +17 -3
- bbot/core/helpers/process.py +0 -20
- bbot/core/helpers/regex.py +1 -1
- bbot/core/helpers/regexes.py +12 -6
- bbot/core/helpers/validators.py +1 -2
- bbot/core/helpers/web/client.py +1 -1
- bbot/core/helpers/web/engine.py +1 -2
- bbot/core/helpers/web/web.py +4 -114
- bbot/core/helpers/wordcloud.py +5 -5
- bbot/core/modules.py +36 -27
- bbot/core/multiprocess.py +58 -0
- bbot/core/shared_deps.py +46 -3
- bbot/db/sql/models.py +147 -0
- bbot/defaults.yml +12 -10
- bbot/modules/anubisdb.py +2 -2
- bbot/modules/apkpure.py +63 -0
- bbot/modules/azure_tenant.py +2 -2
- bbot/modules/baddns.py +35 -19
- bbot/modules/baddns_direct.py +92 -0
- bbot/modules/baddns_zone.py +3 -8
- bbot/modules/badsecrets.py +4 -3
- bbot/modules/base.py +195 -51
- bbot/modules/bevigil.py +7 -7
- bbot/modules/binaryedge.py +7 -4
- bbot/modules/bufferoverrun.py +47 -0
- bbot/modules/builtwith.py +6 -10
- bbot/modules/bypass403.py +5 -5
- bbot/modules/c99.py +10 -7
- bbot/modules/censys.py +9 -13
- bbot/modules/certspotter.py +5 -3
- bbot/modules/chaos.py +9 -7
- bbot/modules/code_repository.py +1 -0
- bbot/modules/columbus.py +3 -3
- bbot/modules/crt.py +5 -3
- bbot/modules/deadly/dastardly.py +1 -1
- bbot/modules/deadly/ffuf.py +9 -9
- bbot/modules/deadly/nuclei.py +3 -3
- bbot/modules/deadly/vhost.py +4 -3
- bbot/modules/dehashed.py +1 -1
- bbot/modules/digitorus.py +1 -1
- bbot/modules/dnsbimi.py +145 -0
- bbot/modules/dnscaa.py +3 -3
- bbot/modules/dnsdumpster.py +4 -4
- bbot/modules/dnstlsrpt.py +144 -0
- bbot/modules/docker_pull.py +7 -5
- bbot/modules/dockerhub.py +2 -2
- bbot/modules/dotnetnuke.py +20 -21
- bbot/modules/emailformat.py +1 -1
- bbot/modules/extractous.py +122 -0
- bbot/modules/filedownload.py +9 -7
- bbot/modules/fullhunt.py +7 -4
- bbot/modules/generic_ssrf.py +5 -5
- bbot/modules/github_codesearch.py +3 -2
- bbot/modules/github_org.py +4 -4
- bbot/modules/github_workflows.py +4 -4
- bbot/modules/gitlab.py +2 -5
- bbot/modules/google_playstore.py +93 -0
- bbot/modules/gowitness.py +48 -50
- bbot/modules/hackertarget.py +5 -3
- bbot/modules/host_header.py +5 -5
- bbot/modules/httpx.py +1 -4
- bbot/modules/hunterio.py +3 -9
- bbot/modules/iis_shortnames.py +19 -30
- bbot/modules/internal/cloudcheck.py +29 -12
- bbot/modules/internal/dnsresolve.py +22 -22
- bbot/modules/internal/excavate.py +97 -59
- bbot/modules/internal/speculate.py +41 -32
- bbot/modules/internetdb.py +4 -2
- bbot/modules/ip2location.py +3 -5
- bbot/modules/ipneighbor.py +1 -1
- bbot/modules/ipstack.py +3 -8
- bbot/modules/jadx.py +87 -0
- bbot/modules/leakix.py +11 -10
- bbot/modules/myssl.py +2 -2
- bbot/modules/newsletters.py +2 -2
- bbot/modules/otx.py +5 -3
- bbot/modules/output/asset_inventory.py +7 -7
- bbot/modules/output/base.py +1 -1
- bbot/modules/output/csv.py +1 -1
- bbot/modules/output/http.py +20 -14
- bbot/modules/output/mysql.py +51 -0
- bbot/modules/output/neo4j.py +7 -2
- bbot/modules/output/postgres.py +49 -0
- bbot/modules/output/slack.py +0 -1
- bbot/modules/output/sqlite.py +29 -0
- bbot/modules/output/stdout.py +2 -2
- bbot/modules/output/teams.py +107 -6
- bbot/modules/paramminer_headers.py +8 -11
- bbot/modules/passivetotal.py +13 -13
- bbot/modules/portscan.py +32 -6
- bbot/modules/postman.py +50 -126
- bbot/modules/postman_download.py +220 -0
- bbot/modules/rapiddns.py +3 -8
- bbot/modules/report/asn.py +18 -11
- bbot/modules/robots.py +3 -3
- bbot/modules/securitytrails.py +7 -10
- bbot/modules/securitytxt.py +1 -1
- bbot/modules/shodan_dns.py +7 -9
- bbot/modules/sitedossier.py +1 -1
- bbot/modules/skymem.py +2 -2
- bbot/modules/social.py +2 -1
- bbot/modules/subdomaincenter.py +1 -1
- bbot/modules/subdomainradar.py +160 -0
- bbot/modules/telerik.py +8 -8
- bbot/modules/templates/bucket.py +1 -1
- bbot/modules/templates/github.py +22 -14
- bbot/modules/templates/postman.py +21 -0
- bbot/modules/templates/shodan.py +14 -13
- bbot/modules/templates/sql.py +95 -0
- bbot/modules/templates/subdomain_enum.py +51 -16
- bbot/modules/templates/webhook.py +2 -4
- bbot/modules/trickest.py +8 -37
- bbot/modules/trufflehog.py +10 -12
- bbot/modules/url_manipulation.py +3 -3
- bbot/modules/urlscan.py +1 -1
- bbot/modules/viewdns.py +1 -1
- bbot/modules/virustotal.py +8 -30
- bbot/modules/wafw00f.py +1 -1
- bbot/modules/wayback.py +1 -1
- bbot/modules/wpscan.py +17 -11
- bbot/modules/zoomeye.py +11 -6
- bbot/presets/baddns-thorough.yml +12 -0
- bbot/presets/fast.yml +16 -0
- bbot/presets/kitchen-sink.yml +1 -2
- bbot/presets/spider.yml +4 -0
- bbot/presets/subdomain-enum.yml +7 -7
- bbot/presets/web/dotnet-audit.yml +0 -1
- bbot/scanner/manager.py +5 -16
- bbot/scanner/preset/args.py +46 -26
- bbot/scanner/preset/environ.py +7 -2
- bbot/scanner/preset/path.py +7 -4
- bbot/scanner/preset/preset.py +36 -23
- bbot/scanner/scanner.py +172 -62
- bbot/scanner/target.py +236 -434
- bbot/scripts/docs.py +1 -1
- bbot/test/bbot_fixtures.py +13 -3
- bbot/test/conftest.py +132 -100
- bbot/test/fastapi_test.py +17 -0
- bbot/test/owasp_mastg.apk +0 -0
- bbot/test/run_tests.sh +4 -4
- bbot/test/test.conf +2 -0
- bbot/test/test_step_1/test__module__tests.py +0 -1
- bbot/test/test_step_1/test_bbot_fastapi.py +79 -0
- bbot/test/test_step_1/test_bloom_filter.py +2 -1
- bbot/test/test_step_1/test_cli.py +138 -64
- bbot/test/test_step_1/test_dns.py +61 -27
- bbot/test/test_step_1/test_engine.py +17 -19
- bbot/test/test_step_1/test_events.py +183 -30
- bbot/test/test_step_1/test_helpers.py +64 -29
- bbot/test/test_step_1/test_manager_deduplication.py +1 -1
- bbot/test/test_step_1/test_manager_scope_accuracy.py +333 -330
- bbot/test/test_step_1/test_modules_basic.py +68 -70
- bbot/test/test_step_1/test_presets.py +183 -100
- bbot/test/test_step_1/test_python_api.py +7 -2
- bbot/test/test_step_1/test_regexes.py +35 -5
- bbot/test/test_step_1/test_scan.py +39 -5
- bbot/test/test_step_1/test_scope.py +4 -3
- bbot/test/test_step_1/test_target.py +242 -145
- bbot/test/test_step_1/test_web.py +14 -10
- bbot/test/test_step_2/module_tests/base.py +15 -7
- bbot/test/test_step_2/module_tests/test_module_anubisdb.py +1 -1
- bbot/test/test_step_2/module_tests/test_module_apkpure.py +71 -0
- bbot/test/test_step_2/module_tests/test_module_asset_inventory.py +0 -1
- bbot/test/test_step_2/module_tests/test_module_azure_realm.py +1 -1
- bbot/test/test_step_2/module_tests/test_module_baddns.py +6 -6
- bbot/test/test_step_2/module_tests/test_module_baddns_direct.py +62 -0
- bbot/test/test_step_2/module_tests/test_module_bevigil.py +29 -2
- bbot/test/test_step_2/module_tests/test_module_binaryedge.py +4 -2
- bbot/test/test_step_2/module_tests/test_module_bucket_amazon.py +2 -2
- bbot/test/test_step_2/module_tests/test_module_bucket_azure.py +1 -1
- bbot/test/test_step_2/module_tests/test_module_bufferoverrun.py +35 -0
- bbot/test/test_step_2/module_tests/test_module_builtwith.py +2 -2
- bbot/test/test_step_2/module_tests/test_module_bypass403.py +1 -1
- bbot/test/test_step_2/module_tests/test_module_c99.py +126 -0
- bbot/test/test_step_2/module_tests/test_module_censys.py +4 -1
- bbot/test/test_step_2/module_tests/test_module_cloudcheck.py +4 -0
- bbot/test/test_step_2/module_tests/test_module_code_repository.py +11 -1
- bbot/test/test_step_2/module_tests/test_module_columbus.py +1 -1
- bbot/test/test_step_2/module_tests/test_module_credshed.py +3 -3
- bbot/test/test_step_2/module_tests/test_module_dastardly.py +2 -1
- bbot/test/test_step_2/module_tests/test_module_dehashed.py +2 -2
- bbot/test/test_step_2/module_tests/test_module_digitorus.py +1 -1
- bbot/test/test_step_2/module_tests/test_module_discord.py +1 -1
- bbot/test/test_step_2/module_tests/test_module_dnsbimi.py +103 -0
- bbot/test/test_step_2/module_tests/test_module_dnsbrute.py +9 -10
- bbot/test/test_step_2/module_tests/test_module_dnsbrute_mutations.py +1 -2
- bbot/test/test_step_2/module_tests/test_module_dnscommonsrv.py +1 -2
- bbot/test/test_step_2/module_tests/test_module_dnsdumpster.py +4 -4
- bbot/test/test_step_2/module_tests/test_module_dnstlsrpt.py +64 -0
- bbot/test/test_step_2/module_tests/test_module_dotnetnuke.py +0 -8
- bbot/test/test_step_2/module_tests/test_module_excavate.py +28 -48
- bbot/test/test_step_2/module_tests/test_module_extractous.py +54 -0
- bbot/test/test_step_2/module_tests/test_module_ffuf_shortnames.py +1 -1
- bbot/test/test_step_2/module_tests/test_module_filedownload.py +14 -14
- bbot/test/test_step_2/module_tests/test_module_git_clone.py +2 -2
- bbot/test/test_step_2/module_tests/test_module_github_org.py +19 -8
- bbot/test/test_step_2/module_tests/test_module_github_workflows.py +1 -1
- bbot/test/test_step_2/module_tests/test_module_gitlab.py +9 -4
- bbot/test/test_step_2/module_tests/test_module_google_playstore.py +83 -0
- bbot/test/test_step_2/module_tests/test_module_gowitness.py +4 -6
- bbot/test/test_step_2/module_tests/test_module_host_header.py +1 -1
- bbot/test/test_step_2/module_tests/test_module_http.py +4 -4
- bbot/test/test_step_2/module_tests/test_module_httpx.py +10 -8
- bbot/test/test_step_2/module_tests/test_module_hunterio.py +68 -4
- bbot/test/test_step_2/module_tests/test_module_jadx.py +55 -0
- bbot/test/test_step_2/module_tests/test_module_json.py +22 -9
- bbot/test/test_step_2/module_tests/test_module_leakix.py +7 -3
- bbot/test/test_step_2/module_tests/test_module_mysql.py +76 -0
- bbot/test/test_step_2/module_tests/test_module_myssl.py +1 -1
- bbot/test/test_step_2/module_tests/test_module_neo4j.py +1 -1
- bbot/test/test_step_2/module_tests/test_module_newsletters.py +16 -16
- bbot/test/test_step_2/module_tests/test_module_ntlm.py +8 -7
- bbot/test/test_step_2/module_tests/test_module_oauth.py +1 -1
- bbot/test/test_step_2/module_tests/test_module_otx.py +1 -1
- bbot/test/test_step_2/module_tests/test_module_paramminer_cookies.py +1 -2
- bbot/test/test_step_2/module_tests/test_module_paramminer_getparams.py +0 -6
- bbot/test/test_step_2/module_tests/test_module_paramminer_headers.py +2 -9
- bbot/test/test_step_2/module_tests/test_module_passivetotal.py +3 -1
- bbot/test/test_step_2/module_tests/test_module_pgp.py +2 -2
- bbot/test/test_step_2/module_tests/test_module_portscan.py +9 -8
- bbot/test/test_step_2/module_tests/test_module_postgres.py +74 -0
- bbot/test/test_step_2/module_tests/test_module_postman.py +84 -253
- bbot/test/test_step_2/module_tests/test_module_postman_download.py +439 -0
- bbot/test/test_step_2/module_tests/test_module_rapiddns.py +93 -1
- bbot/test/test_step_2/module_tests/test_module_shodan_dns.py +20 -1
- bbot/test/test_step_2/module_tests/test_module_sitedossier.py +2 -2
- bbot/test/test_step_2/module_tests/test_module_smuggler.py +14 -14
- bbot/test/test_step_2/module_tests/test_module_social.py +11 -1
- bbot/test/test_step_2/module_tests/test_module_speculate.py +4 -8
- bbot/test/test_step_2/module_tests/test_module_splunk.py +4 -4
- bbot/test/test_step_2/module_tests/test_module_sqlite.py +18 -0
- bbot/test/test_step_2/module_tests/test_module_sslcert.py +1 -1
- bbot/test/test_step_2/module_tests/test_module_stdout.py +5 -3
- bbot/test/test_step_2/module_tests/test_module_subdomaincenter.py +1 -1
- bbot/test/test_step_2/module_tests/test_module_subdomainradar.py +208 -0
- bbot/test/test_step_2/module_tests/test_module_subdomains.py +1 -1
- bbot/test/test_step_2/module_tests/test_module_teams.py +8 -6
- bbot/test/test_step_2/module_tests/test_module_telerik.py +1 -1
- bbot/test/test_step_2/module_tests/test_module_trufflehog.py +317 -14
- bbot/test/test_step_2/module_tests/test_module_viewdns.py +1 -1
- bbot/test/test_step_2/module_tests/test_module_wayback.py +1 -1
- bbot/test/test_step_2/template_tests/test_template_subdomain_enum.py +2 -2
- bbot/wordlists/devops_mutations.txt +1 -1
- bbot/wordlists/ffuf_shortname_candidates.txt +1 -1
- bbot/wordlists/nameservers.txt +1 -1
- bbot/wordlists/paramminer_headers.txt +1 -1
- bbot/wordlists/paramminer_parameters.txt +1 -1
- bbot/wordlists/raft-small-extensions-lowercase_CLEANED.txt +1 -1
- bbot/wordlists/valid_url_schemes.txt +1 -1
- {bbot-2.0.1.4720rc0.dist-info → bbot-2.3.0.5401rc0.dist-info}/METADATA +48 -18
- bbot-2.3.0.5401rc0.dist-info/RECORD +421 -0
- {bbot-2.0.1.4720rc0.dist-info → bbot-2.3.0.5401rc0.dist-info}/WHEEL +1 -1
- bbot/modules/unstructured.py +0 -163
- bbot/test/test_step_2/module_tests/test_module_unstructured.py +0 -102
- bbot-2.0.1.4720rc0.dist-info/RECORD +0 -387
- {bbot-2.0.1.4720rc0.dist-info → bbot-2.3.0.5401rc0.dist-info}/LICENSE +0 -0
- {bbot-2.0.1.4720rc0.dist-info → bbot-2.3.0.5401rc0.dist-info}/entry_points.txt +0 -0
bbot/scripts/docs.py
CHANGED
|
@@ -124,7 +124,7 @@ def find_replace_file(file, keyword, replace):
|
|
|
124
124
|
content = f.read()
|
|
125
125
|
new_content = find_replace_markdown(content, keyword, replace)
|
|
126
126
|
if new_content != content:
|
|
127
|
-
if
|
|
127
|
+
if "BBOT_TESTING" not in os.environ:
|
|
128
128
|
with open(file, "w") as f:
|
|
129
129
|
f.write(new_content)
|
|
130
130
|
|
bbot/test/bbot_fixtures.py
CHANGED
|
@@ -15,11 +15,11 @@ from werkzeug.wrappers import Request
|
|
|
15
15
|
from bbot.errors import * # noqa: F401
|
|
16
16
|
from bbot.core import CORE
|
|
17
17
|
from bbot.scanner import Preset
|
|
18
|
-
from bbot.core.helpers.misc import mkdir, rand_string
|
|
19
18
|
from bbot.core.helpers.async_helpers import get_event_loop
|
|
19
|
+
from bbot.core.helpers.misc import mkdir, rand_string, get_python_constraints
|
|
20
20
|
|
|
21
21
|
|
|
22
|
-
log = logging.getLogger(
|
|
22
|
+
log = logging.getLogger("bbot.test.fixtures")
|
|
23
23
|
|
|
24
24
|
|
|
25
25
|
bbot_test_dir = Path("/tmp/.bbot_test")
|
|
@@ -42,6 +42,13 @@ def tempwordlist(content):
|
|
|
42
42
|
return filename
|
|
43
43
|
|
|
44
44
|
|
|
45
|
+
def tempapkfile():
|
|
46
|
+
current_dir = Path(__file__).parent
|
|
47
|
+
with open(current_dir / "owasp_mastg.apk", "rb") as f:
|
|
48
|
+
apk_file = f.read()
|
|
49
|
+
return apk_file
|
|
50
|
+
|
|
51
|
+
|
|
45
52
|
@pytest.fixture
|
|
46
53
|
def clean_default_config(monkeypatch):
|
|
47
54
|
clean_config = OmegaConf.merge(
|
|
@@ -222,4 +229,7 @@ def install_all_python_deps():
|
|
|
222
229
|
deps_pip = set()
|
|
223
230
|
for module in DEFAULT_PRESET.module_loader.preloaded().values():
|
|
224
231
|
deps_pip.update(set(module.get("deps", {}).get("pip", [])))
|
|
225
|
-
|
|
232
|
+
|
|
233
|
+
constraint_file = tempwordlist(get_python_constraints())
|
|
234
|
+
|
|
235
|
+
subprocess.run([sys.executable, "-m", "pip", "install", "--constraint", constraint_file] + list(deps_pip))
|
bbot/test/conftest.py
CHANGED
|
@@ -13,17 +13,30 @@ from bbot.core import CORE
|
|
|
13
13
|
from bbot.core.helpers.misc import execute_sync_or_async
|
|
14
14
|
from bbot.core.helpers.interactsh import server_list as interactsh_servers
|
|
15
15
|
|
|
16
|
+
# silence stdout + trace
|
|
17
|
+
root_logger = logging.getLogger()
|
|
18
|
+
pytest_debug_file = Path(__file__).parent.parent.parent / "pytest_debug.log"
|
|
19
|
+
debug_handler = logging.FileHandler(pytest_debug_file)
|
|
20
|
+
debug_handler.setLevel(logging.DEBUG)
|
|
21
|
+
debug_format = logging.Formatter("%(asctime)s [%(levelname)s] %(name)s %(filename)s:%(lineno)s %(message)s")
|
|
22
|
+
debug_handler.setFormatter(debug_format)
|
|
23
|
+
root_logger.addHandler(debug_handler)
|
|
16
24
|
|
|
17
25
|
test_config = OmegaConf.load(Path(__file__).parent / "test.conf")
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
26
|
+
|
|
27
|
+
os.environ["BBOT_DEBUG"] = "True"
|
|
28
|
+
CORE.logger.log_level = logging.DEBUG
|
|
29
|
+
|
|
30
|
+
# silence all stderr output:
|
|
31
|
+
stderr_handler = CORE.logger.log_handlers["stderr"]
|
|
32
|
+
stderr_handler.setLevel(logging.CRITICAL)
|
|
33
|
+
handlers = list(CORE.logger.listener.handlers)
|
|
34
|
+
handlers.remove(stderr_handler)
|
|
35
|
+
CORE.logger.listener.handlers = tuple(handlers)
|
|
36
|
+
|
|
37
|
+
for h in root_logger.handlers:
|
|
38
|
+
h.addFilter(lambda x: x.levelname not in ("STDOUT", "TRACE"))
|
|
39
|
+
|
|
27
40
|
|
|
28
41
|
CORE.merge_default(test_config)
|
|
29
42
|
|
|
@@ -33,6 +46,13 @@ def assert_all_responses_were_requested() -> bool:
|
|
|
33
46
|
return False
|
|
34
47
|
|
|
35
48
|
|
|
49
|
+
@pytest.fixture(autouse=True)
|
|
50
|
+
def silence_live_logging():
|
|
51
|
+
for handler in logging.getLogger().handlers:
|
|
52
|
+
if type(handler).__name__ == "_LiveLoggingStreamHandler":
|
|
53
|
+
handler.setLevel(logging.CRITICAL)
|
|
54
|
+
|
|
55
|
+
|
|
36
56
|
@pytest.fixture
|
|
37
57
|
def bbot_httpserver():
|
|
38
58
|
server = HTTPServer(host="127.0.0.1", port=8888, threaded=True)
|
|
@@ -74,9 +94,21 @@ def bbot_httpserver_ssl():
|
|
|
74
94
|
server.clear()
|
|
75
95
|
|
|
76
96
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
97
|
+
def should_mock(request):
|
|
98
|
+
return request.url.host not in ["127.0.0.1", "localhost", "raw.githubusercontent.com"] + interactsh_servers
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def pytest_collection_modifyitems(config, items):
|
|
102
|
+
# make sure all tests have the httpx_mock marker
|
|
103
|
+
for item in items:
|
|
104
|
+
item.add_marker(
|
|
105
|
+
pytest.mark.httpx_mock(
|
|
106
|
+
should_mock=should_mock,
|
|
107
|
+
assert_all_requests_were_expected=False,
|
|
108
|
+
assert_all_responses_were_requested=False,
|
|
109
|
+
can_send_already_matched_responses=True,
|
|
110
|
+
)
|
|
111
|
+
)
|
|
80
112
|
|
|
81
113
|
|
|
82
114
|
@pytest.fixture
|
|
@@ -202,97 +234,97 @@ def pytest_terminal_summary(terminalreporter, exitstatus, config): # pragma: no
|
|
|
202
234
|
errors = len(stats.get("error", []))
|
|
203
235
|
failed = stats.get("failed", [])
|
|
204
236
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
f"{GREEN}Passed: {passed}{RESET}, {RED}Failed: {len(failed)}{RESET}, {YELLOW}Skipped: {skipped}{RESET}, Errors: {errors}"
|
|
237
|
+
terminalreporter.write("\nTest Session Summary:")
|
|
238
|
+
terminalreporter.write(f"\nTotal tests run: {total_tests}")
|
|
239
|
+
terminalreporter.write(
|
|
240
|
+
f"\n{GREEN}Passed: {passed}{RESET}, {RED}Failed: {len(failed)}{RESET}, {YELLOW}Skipped: {skipped}{RESET}, Errors: {errors}"
|
|
209
241
|
)
|
|
210
242
|
|
|
211
243
|
if failed:
|
|
212
|
-
|
|
244
|
+
terminalreporter.write(f"\n{RED}Detailed failed test report:{RESET}")
|
|
213
245
|
for item in failed:
|
|
214
246
|
test_name = item.nodeid.split("::")[-1] if "::" in item.nodeid else item.nodeid
|
|
215
247
|
file_and_line = f"{item.location[0]}:{item.location[1]}" # File path and line number
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
248
|
+
terminalreporter.write(f"\n{BLUE}Test Name: {test_name}{RESET} {CYAN}({file_and_line}){RESET}")
|
|
249
|
+
terminalreporter.write(f"\n{RED}Location: {item.nodeid} at {item.location[0]}:{item.location[1]}{RESET}")
|
|
250
|
+
terminalreporter.write(f"\n{RED}Failure details:\n{item.longreprtext}{RESET}")
|
|
219
251
|
|
|
220
252
|
|
|
221
253
|
# BELOW: debugging for frozen/hung tests
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
254
|
+
import psutil
|
|
255
|
+
import traceback
|
|
256
|
+
import inspect
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
def _print_detailed_info(): # pragma: no cover
|
|
260
|
+
"""
|
|
261
|
+
Debugging pytests hanging
|
|
262
|
+
"""
|
|
263
|
+
print("=== Detailed Thread and Process Information ===\n")
|
|
264
|
+
try:
|
|
265
|
+
print("=== Threads ===")
|
|
266
|
+
for thread in threading.enumerate():
|
|
267
|
+
print(f"Thread Name: {thread.name}")
|
|
268
|
+
print(f"Thread ID: {thread.ident}")
|
|
269
|
+
print(f"Is Alive: {thread.is_alive()}")
|
|
270
|
+
print(f"Daemon: {thread.daemon}")
|
|
271
|
+
|
|
272
|
+
if hasattr(thread, "_target"):
|
|
273
|
+
target = thread._target
|
|
274
|
+
if target:
|
|
275
|
+
qualname = (
|
|
276
|
+
f"{target.__module__}.{target.__qualname__}"
|
|
277
|
+
if hasattr(target, "__qualname__")
|
|
278
|
+
else str(target)
|
|
279
|
+
)
|
|
280
|
+
print(f"Target Function: {qualname}")
|
|
281
|
+
|
|
282
|
+
if hasattr(thread, "_args"):
|
|
283
|
+
args = thread._args
|
|
284
|
+
kwargs = thread._kwargs if hasattr(thread, "_kwargs") else {}
|
|
285
|
+
arg_spec = inspect.getfullargspec(target)
|
|
286
|
+
|
|
287
|
+
all_args = list(args) + [f"{k}={v}" for k, v in kwargs.items()]
|
|
288
|
+
|
|
289
|
+
if inspect.ismethod(target) and arg_spec.args[0] == "self":
|
|
290
|
+
arg_spec.args.pop(0)
|
|
291
|
+
|
|
292
|
+
named_args = list(zip(arg_spec.args, all_args))
|
|
293
|
+
if arg_spec.varargs:
|
|
294
|
+
named_args.extend((f"*{arg_spec.varargs}", arg) for arg in all_args[len(arg_spec.args) :])
|
|
295
|
+
|
|
296
|
+
print("Arguments:")
|
|
297
|
+
for name, value in named_args:
|
|
298
|
+
print(f" {name}: {value}")
|
|
299
|
+
else:
|
|
300
|
+
print("Target Function: None")
|
|
301
|
+
else:
|
|
302
|
+
print("Target Function: Unknown")
|
|
303
|
+
|
|
304
|
+
print()
|
|
305
|
+
|
|
306
|
+
print("=== Processes ===")
|
|
307
|
+
current_process = psutil.Process()
|
|
308
|
+
for child in current_process.children(recursive=True):
|
|
309
|
+
print(f"Process ID: {child.pid}")
|
|
310
|
+
print(f"Name: {child.name()}")
|
|
311
|
+
print(f"Status: {child.status()}")
|
|
312
|
+
print(f"CPU Times: {child.cpu_times()}")
|
|
313
|
+
print(f"Memory Info: {child.memory_info()}")
|
|
314
|
+
print()
|
|
315
|
+
|
|
316
|
+
print("=== Current Process ===")
|
|
317
|
+
print(f"Process ID: {current_process.pid}")
|
|
318
|
+
print(f"Name: {current_process.name()}")
|
|
319
|
+
print(f"Status: {current_process.status()}")
|
|
320
|
+
print(f"CPU Times: {current_process.cpu_times()}")
|
|
321
|
+
print(f"Memory Info: {current_process.memory_info()}")
|
|
322
|
+
print()
|
|
323
|
+
|
|
324
|
+
except Exception as e:
|
|
325
|
+
print(f"An error occurred: {str(e)}")
|
|
326
|
+
print("Traceback:")
|
|
327
|
+
traceback.print_exc()
|
|
296
328
|
|
|
297
329
|
|
|
298
330
|
@pytest.hookimpl(tryfirst=True, hookwrapper=True)
|
|
@@ -310,11 +342,11 @@ def pytest_sessionfinish(session, exitstatus):
|
|
|
310
342
|
yield
|
|
311
343
|
|
|
312
344
|
# temporarily suspend stdout capture and print detailed thread info
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
345
|
+
capmanager = session.config.pluginmanager.get_plugin("capturemanager")
|
|
346
|
+
if capmanager:
|
|
347
|
+
capmanager.suspend_global_capture(in_=True)
|
|
316
348
|
|
|
317
|
-
|
|
349
|
+
_print_detailed_info()
|
|
318
350
|
|
|
319
|
-
|
|
320
|
-
|
|
351
|
+
if capmanager:
|
|
352
|
+
capmanager.resume_global_capture()
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from typing import List
|
|
2
|
+
from bbot import Scanner
|
|
3
|
+
from fastapi import FastAPI, Query
|
|
4
|
+
|
|
5
|
+
app = FastAPI()
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@app.get("/start")
|
|
9
|
+
async def start(targets: List[str] = Query(...)):
|
|
10
|
+
scanner = Scanner(*targets, modules=["httpx"])
|
|
11
|
+
events = [e async for e in scanner.async_start()]
|
|
12
|
+
return [e.json() for e in events]
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@app.get("/ping")
|
|
16
|
+
async def ping():
|
|
17
|
+
return {"status": "ok"}
|
|
Binary file
|
bbot/test/run_tests.sh
CHANGED
|
@@ -3,14 +3,14 @@
|
|
|
3
3
|
bbot_dir="$( realpath "$(dirname "$(dirname "${BASH_SOURCE[0]}")")")"
|
|
4
4
|
echo -e "[+] BBOT dir: $bbot_dir\n"
|
|
5
5
|
|
|
6
|
-
echo "[+] Checking code formatting with
|
|
6
|
+
echo "[+] Checking code formatting with ruff"
|
|
7
7
|
echo "======================================="
|
|
8
|
-
|
|
8
|
+
ruff format "$bbot_dir" || exit 1
|
|
9
9
|
echo
|
|
10
10
|
|
|
11
|
-
echo "[+] Linting with
|
|
11
|
+
echo "[+] Linting with ruff"
|
|
12
12
|
echo "======================="
|
|
13
|
-
|
|
13
|
+
ruff check "$bbot_dir" || exit 1
|
|
14
14
|
echo
|
|
15
15
|
|
|
16
16
|
if [ "${1}x" != "x" ] ; then
|
bbot/test/test.conf
CHANGED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import time
|
|
2
|
+
import httpx
|
|
3
|
+
import multiprocessing
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from subprocess import Popen
|
|
6
|
+
from contextlib import suppress
|
|
7
|
+
|
|
8
|
+
cwd = Path(__file__).parent.parent.parent
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def run_bbot_multiprocess(queue):
|
|
12
|
+
from bbot import Scanner
|
|
13
|
+
|
|
14
|
+
scan = Scanner("http://127.0.0.1:8888", "blacklanternsecurity.com", modules=["httpx"])
|
|
15
|
+
events = [e.json() for e in scan.start()]
|
|
16
|
+
queue.put(events)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def test_bbot_multiprocess(bbot_httpserver):
|
|
20
|
+
bbot_httpserver.expect_request("/").respond_with_data("test@blacklanternsecurity.com")
|
|
21
|
+
|
|
22
|
+
queue = multiprocessing.Queue()
|
|
23
|
+
events_process = multiprocessing.Process(target=run_bbot_multiprocess, args=(queue,))
|
|
24
|
+
events_process.start()
|
|
25
|
+
events_process.join()
|
|
26
|
+
events = queue.get()
|
|
27
|
+
assert len(events) >= 3
|
|
28
|
+
scan_events = [e for e in events if e["type"] == "SCAN"]
|
|
29
|
+
assert len(scan_events) == 2
|
|
30
|
+
assert any(e["data"] == "test@blacklanternsecurity.com" for e in events)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def test_bbot_fastapi(bbot_httpserver):
|
|
34
|
+
bbot_httpserver.expect_request("/").respond_with_data("test@blacklanternsecurity.com")
|
|
35
|
+
fastapi_process = start_fastapi_server()
|
|
36
|
+
|
|
37
|
+
try:
|
|
38
|
+
# wait for the server to start with a timeout of 60 seconds
|
|
39
|
+
start_time = time.time()
|
|
40
|
+
while True:
|
|
41
|
+
try:
|
|
42
|
+
response = httpx.get("http://127.0.0.1:8978/ping")
|
|
43
|
+
response.raise_for_status()
|
|
44
|
+
break
|
|
45
|
+
except httpx.HTTPError:
|
|
46
|
+
if time.time() - start_time > 60:
|
|
47
|
+
raise TimeoutError("Server did not start within 60 seconds.")
|
|
48
|
+
time.sleep(0.1)
|
|
49
|
+
continue
|
|
50
|
+
|
|
51
|
+
# run a scan
|
|
52
|
+
response = httpx.get(
|
|
53
|
+
"http://127.0.0.1:8978/start",
|
|
54
|
+
params={"targets": ["http://127.0.0.1:8888", "blacklanternsecurity.com"]},
|
|
55
|
+
timeout=100,
|
|
56
|
+
)
|
|
57
|
+
events = response.json()
|
|
58
|
+
assert len(events) >= 3
|
|
59
|
+
scan_events = [e for e in events if e["type"] == "SCAN"]
|
|
60
|
+
assert len(scan_events) == 2
|
|
61
|
+
assert any(e["data"] == "test@blacklanternsecurity.com" for e in events)
|
|
62
|
+
|
|
63
|
+
finally:
|
|
64
|
+
with suppress(Exception):
|
|
65
|
+
fastapi_process.terminate()
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def start_fastapi_server():
|
|
69
|
+
import os
|
|
70
|
+
import sys
|
|
71
|
+
|
|
72
|
+
env = os.environ.copy()
|
|
73
|
+
with suppress(KeyError):
|
|
74
|
+
del env["BBOT_TESTING"]
|
|
75
|
+
python_executable = str(sys.executable)
|
|
76
|
+
process = Popen(
|
|
77
|
+
[python_executable, "-m", "uvicorn", "bbot.test.fastapi_test:app", "--port", "8978"], cwd=cwd, env=env
|
|
78
|
+
)
|
|
79
|
+
return process
|
|
@@ -6,7 +6,6 @@ import random
|
|
|
6
6
|
|
|
7
7
|
@pytest.mark.asyncio
|
|
8
8
|
async def test_bloom_filter():
|
|
9
|
-
|
|
10
9
|
def generate_random_strings(n, length=10):
|
|
11
10
|
"""Generate a list of n random strings."""
|
|
12
11
|
return ["".join(random.choices(string.ascii_letters + string.digits, k=length)) for _ in range(n)]
|
|
@@ -66,4 +65,6 @@ async def test_bloom_filter():
|
|
|
66
65
|
# ensure false positives are less than .02 percent
|
|
67
66
|
assert false_positive_percent < 0.02
|
|
68
67
|
|
|
68
|
+
bloom_filter.close()
|
|
69
|
+
|
|
69
70
|
await scan._cleanup()
|