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.

Files changed (278) hide show
  1. bbot/__init__.py +1 -1
  2. bbot/cli.py +3 -7
  3. bbot/core/config/files.py +0 -1
  4. bbot/core/config/logger.py +34 -4
  5. bbot/core/core.py +21 -4
  6. bbot/core/engine.py +9 -8
  7. bbot/core/event/base.py +131 -52
  8. bbot/core/helpers/bloom.py +10 -3
  9. bbot/core/helpers/command.py +8 -7
  10. bbot/core/helpers/depsinstaller/installer.py +31 -13
  11. bbot/core/helpers/diff.py +10 -10
  12. bbot/core/helpers/dns/brute.py +7 -4
  13. bbot/core/helpers/dns/dns.py +1 -2
  14. bbot/core/helpers/dns/engine.py +4 -6
  15. bbot/core/helpers/dns/helpers.py +2 -2
  16. bbot/core/helpers/dns/mock.py +0 -1
  17. bbot/core/helpers/files.py +1 -1
  18. bbot/core/helpers/helper.py +7 -4
  19. bbot/core/helpers/interactsh.py +3 -3
  20. bbot/core/helpers/libmagic.py +65 -0
  21. bbot/core/helpers/misc.py +65 -22
  22. bbot/core/helpers/names_generator.py +17 -3
  23. bbot/core/helpers/process.py +0 -20
  24. bbot/core/helpers/regex.py +1 -1
  25. bbot/core/helpers/regexes.py +12 -6
  26. bbot/core/helpers/validators.py +1 -2
  27. bbot/core/helpers/web/client.py +1 -1
  28. bbot/core/helpers/web/engine.py +1 -2
  29. bbot/core/helpers/web/web.py +4 -114
  30. bbot/core/helpers/wordcloud.py +5 -5
  31. bbot/core/modules.py +36 -27
  32. bbot/core/multiprocess.py +58 -0
  33. bbot/core/shared_deps.py +46 -3
  34. bbot/db/sql/models.py +147 -0
  35. bbot/defaults.yml +12 -10
  36. bbot/modules/anubisdb.py +2 -2
  37. bbot/modules/apkpure.py +63 -0
  38. bbot/modules/azure_tenant.py +2 -2
  39. bbot/modules/baddns.py +35 -19
  40. bbot/modules/baddns_direct.py +92 -0
  41. bbot/modules/baddns_zone.py +3 -8
  42. bbot/modules/badsecrets.py +4 -3
  43. bbot/modules/base.py +195 -51
  44. bbot/modules/bevigil.py +7 -7
  45. bbot/modules/binaryedge.py +7 -4
  46. bbot/modules/bufferoverrun.py +47 -0
  47. bbot/modules/builtwith.py +6 -10
  48. bbot/modules/bypass403.py +5 -5
  49. bbot/modules/c99.py +10 -7
  50. bbot/modules/censys.py +9 -13
  51. bbot/modules/certspotter.py +5 -3
  52. bbot/modules/chaos.py +9 -7
  53. bbot/modules/code_repository.py +1 -0
  54. bbot/modules/columbus.py +3 -3
  55. bbot/modules/crt.py +5 -3
  56. bbot/modules/deadly/dastardly.py +1 -1
  57. bbot/modules/deadly/ffuf.py +9 -9
  58. bbot/modules/deadly/nuclei.py +3 -3
  59. bbot/modules/deadly/vhost.py +4 -3
  60. bbot/modules/dehashed.py +1 -1
  61. bbot/modules/digitorus.py +1 -1
  62. bbot/modules/dnsbimi.py +145 -0
  63. bbot/modules/dnscaa.py +3 -3
  64. bbot/modules/dnsdumpster.py +4 -4
  65. bbot/modules/dnstlsrpt.py +144 -0
  66. bbot/modules/docker_pull.py +7 -5
  67. bbot/modules/dockerhub.py +2 -2
  68. bbot/modules/dotnetnuke.py +20 -21
  69. bbot/modules/emailformat.py +1 -1
  70. bbot/modules/extractous.py +122 -0
  71. bbot/modules/filedownload.py +9 -7
  72. bbot/modules/fullhunt.py +7 -4
  73. bbot/modules/generic_ssrf.py +5 -5
  74. bbot/modules/github_codesearch.py +3 -2
  75. bbot/modules/github_org.py +4 -4
  76. bbot/modules/github_workflows.py +4 -4
  77. bbot/modules/gitlab.py +2 -5
  78. bbot/modules/google_playstore.py +93 -0
  79. bbot/modules/gowitness.py +48 -50
  80. bbot/modules/hackertarget.py +5 -3
  81. bbot/modules/host_header.py +5 -5
  82. bbot/modules/httpx.py +1 -4
  83. bbot/modules/hunterio.py +3 -9
  84. bbot/modules/iis_shortnames.py +19 -30
  85. bbot/modules/internal/cloudcheck.py +29 -12
  86. bbot/modules/internal/dnsresolve.py +22 -22
  87. bbot/modules/internal/excavate.py +97 -59
  88. bbot/modules/internal/speculate.py +41 -32
  89. bbot/modules/internetdb.py +4 -2
  90. bbot/modules/ip2location.py +3 -5
  91. bbot/modules/ipneighbor.py +1 -1
  92. bbot/modules/ipstack.py +3 -8
  93. bbot/modules/jadx.py +87 -0
  94. bbot/modules/leakix.py +11 -10
  95. bbot/modules/myssl.py +2 -2
  96. bbot/modules/newsletters.py +2 -2
  97. bbot/modules/otx.py +5 -3
  98. bbot/modules/output/asset_inventory.py +7 -7
  99. bbot/modules/output/base.py +1 -1
  100. bbot/modules/output/csv.py +1 -1
  101. bbot/modules/output/http.py +20 -14
  102. bbot/modules/output/mysql.py +51 -0
  103. bbot/modules/output/neo4j.py +7 -2
  104. bbot/modules/output/postgres.py +49 -0
  105. bbot/modules/output/slack.py +0 -1
  106. bbot/modules/output/sqlite.py +29 -0
  107. bbot/modules/output/stdout.py +2 -2
  108. bbot/modules/output/teams.py +107 -6
  109. bbot/modules/paramminer_headers.py +8 -11
  110. bbot/modules/passivetotal.py +13 -13
  111. bbot/modules/portscan.py +32 -6
  112. bbot/modules/postman.py +50 -126
  113. bbot/modules/postman_download.py +220 -0
  114. bbot/modules/rapiddns.py +3 -8
  115. bbot/modules/report/asn.py +18 -11
  116. bbot/modules/robots.py +3 -3
  117. bbot/modules/securitytrails.py +7 -10
  118. bbot/modules/securitytxt.py +1 -1
  119. bbot/modules/shodan_dns.py +7 -9
  120. bbot/modules/sitedossier.py +1 -1
  121. bbot/modules/skymem.py +2 -2
  122. bbot/modules/social.py +2 -1
  123. bbot/modules/subdomaincenter.py +1 -1
  124. bbot/modules/subdomainradar.py +160 -0
  125. bbot/modules/telerik.py +8 -8
  126. bbot/modules/templates/bucket.py +1 -1
  127. bbot/modules/templates/github.py +22 -14
  128. bbot/modules/templates/postman.py +21 -0
  129. bbot/modules/templates/shodan.py +14 -13
  130. bbot/modules/templates/sql.py +95 -0
  131. bbot/modules/templates/subdomain_enum.py +51 -16
  132. bbot/modules/templates/webhook.py +2 -4
  133. bbot/modules/trickest.py +8 -37
  134. bbot/modules/trufflehog.py +10 -12
  135. bbot/modules/url_manipulation.py +3 -3
  136. bbot/modules/urlscan.py +1 -1
  137. bbot/modules/viewdns.py +1 -1
  138. bbot/modules/virustotal.py +8 -30
  139. bbot/modules/wafw00f.py +1 -1
  140. bbot/modules/wayback.py +1 -1
  141. bbot/modules/wpscan.py +17 -11
  142. bbot/modules/zoomeye.py +11 -6
  143. bbot/presets/baddns-thorough.yml +12 -0
  144. bbot/presets/fast.yml +16 -0
  145. bbot/presets/kitchen-sink.yml +1 -2
  146. bbot/presets/spider.yml +4 -0
  147. bbot/presets/subdomain-enum.yml +7 -7
  148. bbot/presets/web/dotnet-audit.yml +0 -1
  149. bbot/scanner/manager.py +5 -16
  150. bbot/scanner/preset/args.py +46 -26
  151. bbot/scanner/preset/environ.py +7 -2
  152. bbot/scanner/preset/path.py +7 -4
  153. bbot/scanner/preset/preset.py +36 -23
  154. bbot/scanner/scanner.py +172 -62
  155. bbot/scanner/target.py +236 -434
  156. bbot/scripts/docs.py +1 -1
  157. bbot/test/bbot_fixtures.py +13 -3
  158. bbot/test/conftest.py +132 -100
  159. bbot/test/fastapi_test.py +17 -0
  160. bbot/test/owasp_mastg.apk +0 -0
  161. bbot/test/run_tests.sh +4 -4
  162. bbot/test/test.conf +2 -0
  163. bbot/test/test_step_1/test__module__tests.py +0 -1
  164. bbot/test/test_step_1/test_bbot_fastapi.py +79 -0
  165. bbot/test/test_step_1/test_bloom_filter.py +2 -1
  166. bbot/test/test_step_1/test_cli.py +138 -64
  167. bbot/test/test_step_1/test_dns.py +61 -27
  168. bbot/test/test_step_1/test_engine.py +17 -19
  169. bbot/test/test_step_1/test_events.py +183 -30
  170. bbot/test/test_step_1/test_helpers.py +64 -29
  171. bbot/test/test_step_1/test_manager_deduplication.py +1 -1
  172. bbot/test/test_step_1/test_manager_scope_accuracy.py +333 -330
  173. bbot/test/test_step_1/test_modules_basic.py +68 -70
  174. bbot/test/test_step_1/test_presets.py +183 -100
  175. bbot/test/test_step_1/test_python_api.py +7 -2
  176. bbot/test/test_step_1/test_regexes.py +35 -5
  177. bbot/test/test_step_1/test_scan.py +39 -5
  178. bbot/test/test_step_1/test_scope.py +4 -3
  179. bbot/test/test_step_1/test_target.py +242 -145
  180. bbot/test/test_step_1/test_web.py +14 -10
  181. bbot/test/test_step_2/module_tests/base.py +15 -7
  182. bbot/test/test_step_2/module_tests/test_module_anubisdb.py +1 -1
  183. bbot/test/test_step_2/module_tests/test_module_apkpure.py +71 -0
  184. bbot/test/test_step_2/module_tests/test_module_asset_inventory.py +0 -1
  185. bbot/test/test_step_2/module_tests/test_module_azure_realm.py +1 -1
  186. bbot/test/test_step_2/module_tests/test_module_baddns.py +6 -6
  187. bbot/test/test_step_2/module_tests/test_module_baddns_direct.py +62 -0
  188. bbot/test/test_step_2/module_tests/test_module_bevigil.py +29 -2
  189. bbot/test/test_step_2/module_tests/test_module_binaryedge.py +4 -2
  190. bbot/test/test_step_2/module_tests/test_module_bucket_amazon.py +2 -2
  191. bbot/test/test_step_2/module_tests/test_module_bucket_azure.py +1 -1
  192. bbot/test/test_step_2/module_tests/test_module_bufferoverrun.py +35 -0
  193. bbot/test/test_step_2/module_tests/test_module_builtwith.py +2 -2
  194. bbot/test/test_step_2/module_tests/test_module_bypass403.py +1 -1
  195. bbot/test/test_step_2/module_tests/test_module_c99.py +126 -0
  196. bbot/test/test_step_2/module_tests/test_module_censys.py +4 -1
  197. bbot/test/test_step_2/module_tests/test_module_cloudcheck.py +4 -0
  198. bbot/test/test_step_2/module_tests/test_module_code_repository.py +11 -1
  199. bbot/test/test_step_2/module_tests/test_module_columbus.py +1 -1
  200. bbot/test/test_step_2/module_tests/test_module_credshed.py +3 -3
  201. bbot/test/test_step_2/module_tests/test_module_dastardly.py +2 -1
  202. bbot/test/test_step_2/module_tests/test_module_dehashed.py +2 -2
  203. bbot/test/test_step_2/module_tests/test_module_digitorus.py +1 -1
  204. bbot/test/test_step_2/module_tests/test_module_discord.py +1 -1
  205. bbot/test/test_step_2/module_tests/test_module_dnsbimi.py +103 -0
  206. bbot/test/test_step_2/module_tests/test_module_dnsbrute.py +9 -10
  207. bbot/test/test_step_2/module_tests/test_module_dnsbrute_mutations.py +1 -2
  208. bbot/test/test_step_2/module_tests/test_module_dnscommonsrv.py +1 -2
  209. bbot/test/test_step_2/module_tests/test_module_dnsdumpster.py +4 -4
  210. bbot/test/test_step_2/module_tests/test_module_dnstlsrpt.py +64 -0
  211. bbot/test/test_step_2/module_tests/test_module_dotnetnuke.py +0 -8
  212. bbot/test/test_step_2/module_tests/test_module_excavate.py +28 -48
  213. bbot/test/test_step_2/module_tests/test_module_extractous.py +54 -0
  214. bbot/test/test_step_2/module_tests/test_module_ffuf_shortnames.py +1 -1
  215. bbot/test/test_step_2/module_tests/test_module_filedownload.py +14 -14
  216. bbot/test/test_step_2/module_tests/test_module_git_clone.py +2 -2
  217. bbot/test/test_step_2/module_tests/test_module_github_org.py +19 -8
  218. bbot/test/test_step_2/module_tests/test_module_github_workflows.py +1 -1
  219. bbot/test/test_step_2/module_tests/test_module_gitlab.py +9 -4
  220. bbot/test/test_step_2/module_tests/test_module_google_playstore.py +83 -0
  221. bbot/test/test_step_2/module_tests/test_module_gowitness.py +4 -6
  222. bbot/test/test_step_2/module_tests/test_module_host_header.py +1 -1
  223. bbot/test/test_step_2/module_tests/test_module_http.py +4 -4
  224. bbot/test/test_step_2/module_tests/test_module_httpx.py +10 -8
  225. bbot/test/test_step_2/module_tests/test_module_hunterio.py +68 -4
  226. bbot/test/test_step_2/module_tests/test_module_jadx.py +55 -0
  227. bbot/test/test_step_2/module_tests/test_module_json.py +22 -9
  228. bbot/test/test_step_2/module_tests/test_module_leakix.py +7 -3
  229. bbot/test/test_step_2/module_tests/test_module_mysql.py +76 -0
  230. bbot/test/test_step_2/module_tests/test_module_myssl.py +1 -1
  231. bbot/test/test_step_2/module_tests/test_module_neo4j.py +1 -1
  232. bbot/test/test_step_2/module_tests/test_module_newsletters.py +16 -16
  233. bbot/test/test_step_2/module_tests/test_module_ntlm.py +8 -7
  234. bbot/test/test_step_2/module_tests/test_module_oauth.py +1 -1
  235. bbot/test/test_step_2/module_tests/test_module_otx.py +1 -1
  236. bbot/test/test_step_2/module_tests/test_module_paramminer_cookies.py +1 -2
  237. bbot/test/test_step_2/module_tests/test_module_paramminer_getparams.py +0 -6
  238. bbot/test/test_step_2/module_tests/test_module_paramminer_headers.py +2 -9
  239. bbot/test/test_step_2/module_tests/test_module_passivetotal.py +3 -1
  240. bbot/test/test_step_2/module_tests/test_module_pgp.py +2 -2
  241. bbot/test/test_step_2/module_tests/test_module_portscan.py +9 -8
  242. bbot/test/test_step_2/module_tests/test_module_postgres.py +74 -0
  243. bbot/test/test_step_2/module_tests/test_module_postman.py +84 -253
  244. bbot/test/test_step_2/module_tests/test_module_postman_download.py +439 -0
  245. bbot/test/test_step_2/module_tests/test_module_rapiddns.py +93 -1
  246. bbot/test/test_step_2/module_tests/test_module_shodan_dns.py +20 -1
  247. bbot/test/test_step_2/module_tests/test_module_sitedossier.py +2 -2
  248. bbot/test/test_step_2/module_tests/test_module_smuggler.py +14 -14
  249. bbot/test/test_step_2/module_tests/test_module_social.py +11 -1
  250. bbot/test/test_step_2/module_tests/test_module_speculate.py +4 -8
  251. bbot/test/test_step_2/module_tests/test_module_splunk.py +4 -4
  252. bbot/test/test_step_2/module_tests/test_module_sqlite.py +18 -0
  253. bbot/test/test_step_2/module_tests/test_module_sslcert.py +1 -1
  254. bbot/test/test_step_2/module_tests/test_module_stdout.py +5 -3
  255. bbot/test/test_step_2/module_tests/test_module_subdomaincenter.py +1 -1
  256. bbot/test/test_step_2/module_tests/test_module_subdomainradar.py +208 -0
  257. bbot/test/test_step_2/module_tests/test_module_subdomains.py +1 -1
  258. bbot/test/test_step_2/module_tests/test_module_teams.py +8 -6
  259. bbot/test/test_step_2/module_tests/test_module_telerik.py +1 -1
  260. bbot/test/test_step_2/module_tests/test_module_trufflehog.py +317 -14
  261. bbot/test/test_step_2/module_tests/test_module_viewdns.py +1 -1
  262. bbot/test/test_step_2/module_tests/test_module_wayback.py +1 -1
  263. bbot/test/test_step_2/template_tests/test_template_subdomain_enum.py +2 -2
  264. bbot/wordlists/devops_mutations.txt +1 -1
  265. bbot/wordlists/ffuf_shortname_candidates.txt +1 -1
  266. bbot/wordlists/nameservers.txt +1 -1
  267. bbot/wordlists/paramminer_headers.txt +1 -1
  268. bbot/wordlists/paramminer_parameters.txt +1 -1
  269. bbot/wordlists/raft-small-extensions-lowercase_CLEANED.txt +1 -1
  270. bbot/wordlists/valid_url_schemes.txt +1 -1
  271. {bbot-2.0.1.4720rc0.dist-info → bbot-2.3.0.5401rc0.dist-info}/METADATA +48 -18
  272. bbot-2.3.0.5401rc0.dist-info/RECORD +421 -0
  273. {bbot-2.0.1.4720rc0.dist-info → bbot-2.3.0.5401rc0.dist-info}/WHEEL +1 -1
  274. bbot/modules/unstructured.py +0 -163
  275. bbot/test/test_step_2/module_tests/test_module_unstructured.py +0 -102
  276. bbot-2.0.1.4720rc0.dist-info/RECORD +0 -387
  277. {bbot-2.0.1.4720rc0.dist-info → bbot-2.3.0.5401rc0.dist-info}/LICENSE +0 -0
  278. {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 not "BBOT_TESTING" in os.environ:
127
+ if "BBOT_TESTING" not in os.environ:
128
128
  with open(file, "w") as f:
129
129
  f.write(new_content)
130
130
 
@@ -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(f"bbot.test.fixtures")
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
- subprocess.run([sys.executable, "-m", "pip", "install"] + list(deps_pip))
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
- if test_config.get("debug", False):
19
- os.environ["BBOT_DEBUG"] = "True"
20
- logging.getLogger("bbot").setLevel(logging.DEBUG)
21
- CORE.logger.log_level = logging.DEBUG
22
- else:
23
- # silence stdout + trace
24
- root_logger = logging.getLogger()
25
- for h in root_logger.handlers:
26
- h.addFilter(lambda x: x.levelname not in ("STDOUT", "TRACE"))
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
- @pytest.fixture
78
- def non_mocked_hosts() -> list:
79
- return ["127.0.0.1", "localhost", "raw.githubusercontent.com"] + interactsh_servers
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
- print("\nTest Session Summary:")
206
- print(f"Total tests run: {total_tests}")
207
- print(
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
- print(f"\n{RED}Detailed failed test report:{RESET}")
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
- print(f"{BLUE}Test Name: {test_name}{RESET} {CYAN}({file_and_line}){RESET}")
217
- print(f"{RED}Location: {item.nodeid} at {item.location[0]}:{item.location[1]}{RESET}")
218
- print(f"{RED}Failure details:\n{item.longreprtext}{RESET}")
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
- # import psutil
223
- # import traceback
224
- # import inspect
225
-
226
-
227
- # def _print_detailed_info(): # pragma: no cover
228
- # """
229
- # Debugging pytests hanging
230
- # """
231
- # print("=== Detailed Thread and Process Information ===\n")
232
- # try:
233
- # print("=== Threads ===")
234
- # for thread in threading.enumerate():
235
- # print(f"Thread Name: {thread.name}")
236
- # print(f"Thread ID: {thread.ident}")
237
- # print(f"Is Alive: {thread.is_alive()}")
238
- # print(f"Daemon: {thread.daemon}")
239
-
240
- # if hasattr(thread, "_target"):
241
- # target = thread._target
242
- # if target:
243
- # qualname = (
244
- # f"{target.__module__}.{target.__qualname__}"
245
- # if hasattr(target, "__qualname__")
246
- # else str(target)
247
- # )
248
- # print(f"Target Function: {qualname}")
249
-
250
- # if hasattr(thread, "_args"):
251
- # args = thread._args
252
- # kwargs = thread._kwargs if hasattr(thread, "_kwargs") else {}
253
- # arg_spec = inspect.getfullargspec(target)
254
-
255
- # all_args = list(args) + [f"{k}={v}" for k, v in kwargs.items()]
256
-
257
- # if inspect.ismethod(target) and arg_spec.args[0] == "self":
258
- # arg_spec.args.pop(0)
259
-
260
- # named_args = list(zip(arg_spec.args, all_args))
261
- # if arg_spec.varargs:
262
- # named_args.extend((f"*{arg_spec.varargs}", arg) for arg in all_args[len(arg_spec.args) :])
263
-
264
- # print("Arguments:")
265
- # for name, value in named_args:
266
- # print(f" {name}: {value}")
267
- # else:
268
- # print("Target Function: None")
269
- # else:
270
- # print("Target Function: Unknown")
271
-
272
- # print()
273
-
274
- # print("=== Processes ===")
275
- # current_process = psutil.Process()
276
- # for child in current_process.children(recursive=True):
277
- # print(f"Process ID: {child.pid}")
278
- # print(f"Name: {child.name()}")
279
- # print(f"Status: {child.status()}")
280
- # print(f"CPU Times: {child.cpu_times()}")
281
- # print(f"Memory Info: {child.memory_info()}")
282
- # print()
283
-
284
- # print("=== Current Process ===")
285
- # print(f"Process ID: {current_process.pid}")
286
- # print(f"Name: {current_process.name()}")
287
- # print(f"Status: {current_process.status()}")
288
- # print(f"CPU Times: {current_process.cpu_times()}")
289
- # print(f"Memory Info: {current_process.memory_info()}")
290
- # print()
291
-
292
- # except Exception as e:
293
- # print(f"An error occurred: {str(e)}")
294
- # print("Traceback:")
295
- # traceback.print_exc()
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
- # capmanager = session.config.pluginmanager.get_plugin("capturemanager")
314
- # if capmanager:
315
- # capmanager.suspend_global_capture(in_=True)
345
+ capmanager = session.config.pluginmanager.get_plugin("capturemanager")
346
+ if capmanager:
347
+ capmanager.suspend_global_capture(in_=True)
316
348
 
317
- # _print_detailed_info()
349
+ _print_detailed_info()
318
350
 
319
- # if capmanager:
320
- # capmanager.resume_global_capture()
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 black"
6
+ echo "[+] Checking code formatting with ruff"
7
7
  echo "======================================="
8
- black --check "$bbot_dir" || exit 1
8
+ ruff format "$bbot_dir" || exit 1
9
9
  echo
10
10
 
11
- echo "[+] Linting with flake8"
11
+ echo "[+] Linting with ruff"
12
12
  echo "======================="
13
- flake8 --select F,E722 --ignore F403,F405,F541 --per-file-ignores="*/__init__.py:F401,F403" "$bbot_dir" || exit 1
13
+ ruff check "$bbot_dir" || exit 1
14
14
  echo
15
15
 
16
16
  if [ "${1}x" != "x" ] ; then
bbot/test/test.conf CHANGED
@@ -36,6 +36,8 @@ dns:
36
36
  - example.com
37
37
  - evilcorp.com
38
38
  - one
39
+ deps:
40
+ behavior: retry_failed
39
41
  engine:
40
42
  debug: true
41
43
  agent_url: ws://127.0.0.1:8765
@@ -15,7 +15,6 @@ module_test_files = [m.name.split("test_module_")[-1].split(".")[0] for m in _mo
15
15
 
16
16
 
17
17
  def test__module__tests():
18
-
19
18
  preset = Preset()
20
19
 
21
20
  # make sure each module has a .py file
@@ -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()