bbot 2.0.1.4654rc0__py3-none-any.whl → 2.3.0.5397rc0__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 (270) 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 -6
  6. bbot/core/engine.py +9 -8
  7. bbot/core/event/base.py +162 -63
  8. bbot/core/helpers/bloom.py +10 -3
  9. bbot/core/helpers/command.py +9 -8
  10. bbot/core/helpers/depsinstaller/installer.py +89 -32
  11. bbot/core/helpers/depsinstaller/sudo_askpass.py +38 -2
  12. bbot/core/helpers/diff.py +10 -10
  13. bbot/core/helpers/dns/brute.py +18 -14
  14. bbot/core/helpers/dns/dns.py +16 -15
  15. bbot/core/helpers/dns/engine.py +159 -132
  16. bbot/core/helpers/dns/helpers.py +2 -2
  17. bbot/core/helpers/dns/mock.py +26 -8
  18. bbot/core/helpers/files.py +1 -1
  19. bbot/core/helpers/helper.py +7 -4
  20. bbot/core/helpers/interactsh.py +3 -3
  21. bbot/core/helpers/libmagic.py +65 -0
  22. bbot/core/helpers/misc.py +65 -22
  23. bbot/core/helpers/names_generator.py +17 -3
  24. bbot/core/helpers/process.py +0 -20
  25. bbot/core/helpers/regex.py +1 -1
  26. bbot/core/helpers/regexes.py +12 -6
  27. bbot/core/helpers/validators.py +1 -2
  28. bbot/core/helpers/web/client.py +1 -1
  29. bbot/core/helpers/web/engine.py +18 -13
  30. bbot/core/helpers/web/web.py +25 -116
  31. bbot/core/helpers/wordcloud.py +5 -5
  32. bbot/core/modules.py +36 -27
  33. bbot/core/multiprocess.py +58 -0
  34. bbot/core/shared_deps.py +46 -3
  35. bbot/db/sql/models.py +147 -0
  36. bbot/defaults.yml +15 -10
  37. bbot/errors.py +0 -8
  38. bbot/modules/anubisdb.py +2 -2
  39. bbot/modules/apkpure.py +63 -0
  40. bbot/modules/azure_tenant.py +2 -2
  41. bbot/modules/baddns.py +35 -19
  42. bbot/modules/baddns_direct.py +92 -0
  43. bbot/modules/baddns_zone.py +3 -8
  44. bbot/modules/badsecrets.py +4 -3
  45. bbot/modules/base.py +195 -51
  46. bbot/modules/bevigil.py +7 -7
  47. bbot/modules/binaryedge.py +7 -4
  48. bbot/modules/bufferoverrun.py +47 -0
  49. bbot/modules/builtwith.py +6 -10
  50. bbot/modules/bypass403.py +5 -5
  51. bbot/modules/c99.py +10 -7
  52. bbot/modules/censys.py +9 -13
  53. bbot/modules/certspotter.py +5 -3
  54. bbot/modules/chaos.py +9 -7
  55. bbot/modules/code_repository.py +1 -0
  56. bbot/modules/columbus.py +3 -3
  57. bbot/modules/crt.py +5 -3
  58. bbot/modules/deadly/dastardly.py +1 -1
  59. bbot/modules/deadly/ffuf.py +9 -9
  60. bbot/modules/deadly/nuclei.py +3 -3
  61. bbot/modules/deadly/vhost.py +4 -3
  62. bbot/modules/dehashed.py +1 -1
  63. bbot/modules/digitorus.py +1 -1
  64. bbot/modules/dnsbimi.py +145 -0
  65. bbot/modules/dnscaa.py +3 -3
  66. bbot/modules/dnsdumpster.py +4 -4
  67. bbot/modules/dnstlsrpt.py +144 -0
  68. bbot/modules/docker_pull.py +7 -5
  69. bbot/modules/dockerhub.py +2 -2
  70. bbot/modules/dotnetnuke.py +18 -19
  71. bbot/modules/emailformat.py +1 -1
  72. bbot/modules/extractous.py +122 -0
  73. bbot/modules/filedownload.py +9 -7
  74. bbot/modules/fullhunt.py +7 -4
  75. bbot/modules/generic_ssrf.py +5 -5
  76. bbot/modules/github_codesearch.py +3 -2
  77. bbot/modules/github_org.py +4 -4
  78. bbot/modules/github_workflows.py +4 -4
  79. bbot/modules/gitlab.py +2 -5
  80. bbot/modules/google_playstore.py +93 -0
  81. bbot/modules/gowitness.py +48 -50
  82. bbot/modules/hackertarget.py +5 -3
  83. bbot/modules/host_header.py +5 -5
  84. bbot/modules/httpx.py +1 -4
  85. bbot/modules/hunterio.py +3 -9
  86. bbot/modules/iis_shortnames.py +19 -30
  87. bbot/modules/internal/cloudcheck.py +27 -12
  88. bbot/modules/internal/dnsresolve.py +250 -276
  89. bbot/modules/internal/excavate.py +100 -64
  90. bbot/modules/internal/speculate.py +42 -33
  91. bbot/modules/internetdb.py +4 -2
  92. bbot/modules/ip2location.py +3 -5
  93. bbot/modules/ipneighbor.py +1 -1
  94. bbot/modules/ipstack.py +3 -8
  95. bbot/modules/jadx.py +87 -0
  96. bbot/modules/leakix.py +11 -10
  97. bbot/modules/myssl.py +2 -2
  98. bbot/modules/newsletters.py +2 -2
  99. bbot/modules/otx.py +5 -3
  100. bbot/modules/output/asset_inventory.py +7 -7
  101. bbot/modules/output/base.py +1 -1
  102. bbot/modules/output/csv.py +1 -2
  103. bbot/modules/output/http.py +20 -14
  104. bbot/modules/output/mysql.py +51 -0
  105. bbot/modules/output/neo4j.py +7 -2
  106. bbot/modules/output/postgres.py +49 -0
  107. bbot/modules/output/slack.py +0 -1
  108. bbot/modules/output/sqlite.py +29 -0
  109. bbot/modules/output/stdout.py +2 -2
  110. bbot/modules/output/teams.py +107 -6
  111. bbot/modules/paramminer_headers.py +5 -8
  112. bbot/modules/passivetotal.py +13 -13
  113. bbot/modules/portscan.py +32 -6
  114. bbot/modules/postman.py +50 -126
  115. bbot/modules/postman_download.py +220 -0
  116. bbot/modules/rapiddns.py +3 -8
  117. bbot/modules/report/asn.py +11 -11
  118. bbot/modules/robots.py +3 -3
  119. bbot/modules/securitytrails.py +7 -10
  120. bbot/modules/securitytxt.py +128 -0
  121. bbot/modules/shodan_dns.py +7 -9
  122. bbot/modules/sitedossier.py +1 -1
  123. bbot/modules/skymem.py +2 -2
  124. bbot/modules/social.py +2 -1
  125. bbot/modules/subdomaincenter.py +1 -1
  126. bbot/modules/subdomainradar.py +160 -0
  127. bbot/modules/telerik.py +8 -8
  128. bbot/modules/templates/bucket.py +1 -1
  129. bbot/modules/templates/github.py +22 -14
  130. bbot/modules/templates/postman.py +21 -0
  131. bbot/modules/templates/shodan.py +14 -13
  132. bbot/modules/templates/sql.py +95 -0
  133. bbot/modules/templates/subdomain_enum.py +53 -17
  134. bbot/modules/templates/webhook.py +2 -4
  135. bbot/modules/trickest.py +8 -37
  136. bbot/modules/trufflehog.py +18 -3
  137. bbot/modules/url_manipulation.py +3 -3
  138. bbot/modules/urlscan.py +1 -1
  139. bbot/modules/viewdns.py +1 -1
  140. bbot/modules/virustotal.py +8 -30
  141. bbot/modules/wafw00f.py +1 -1
  142. bbot/modules/wayback.py +1 -1
  143. bbot/modules/wpscan.py +17 -11
  144. bbot/modules/zoomeye.py +11 -6
  145. bbot/presets/baddns-thorough.yml +12 -0
  146. bbot/presets/fast.yml +16 -0
  147. bbot/presets/kitchen-sink.yml +1 -0
  148. bbot/presets/spider.yml +4 -0
  149. bbot/presets/subdomain-enum.yml +7 -7
  150. bbot/scanner/manager.py +5 -16
  151. bbot/scanner/preset/args.py +44 -26
  152. bbot/scanner/preset/environ.py +7 -2
  153. bbot/scanner/preset/path.py +7 -4
  154. bbot/scanner/preset/preset.py +36 -23
  155. bbot/scanner/scanner.py +176 -63
  156. bbot/scanner/target.py +236 -434
  157. bbot/scripts/docs.py +1 -1
  158. bbot/test/bbot_fixtures.py +22 -3
  159. bbot/test/conftest.py +132 -100
  160. bbot/test/fastapi_test.py +17 -0
  161. bbot/test/owasp_mastg.apk +0 -0
  162. bbot/test/run_tests.sh +4 -4
  163. bbot/test/test.conf +2 -0
  164. bbot/test/test_step_1/test_bbot_fastapi.py +82 -0
  165. bbot/test/test_step_1/test_bloom_filter.py +2 -0
  166. bbot/test/test_step_1/test_cli.py +138 -64
  167. bbot/test/test_step_1/test_dns.py +392 -70
  168. bbot/test/test_step_1/test_engine.py +17 -17
  169. bbot/test/test_step_1/test_events.py +203 -37
  170. bbot/test/test_step_1/test_helpers.py +64 -28
  171. bbot/test/test_step_1/test_manager_deduplication.py +1 -1
  172. bbot/test/test_step_1/test_manager_scope_accuracy.py +336 -338
  173. bbot/test/test_step_1/test_modules_basic.py +69 -71
  174. bbot/test/test_step_1/test_presets.py +184 -96
  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 +5 -4
  179. bbot/test/test_step_1/test_target.py +243 -145
  180. bbot/test/test_step_1/test_web.py +48 -10
  181. bbot/test/test_step_2/module_tests/base.py +17 -20
  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 +17 -37
  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 -4
  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 +24 -11
  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 +6 -6
  233. bbot/test/test_step_2/module_tests/test_module_ntlm.py +7 -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_portscan.py +9 -8
  241. bbot/test/test_step_2/module_tests/test_module_postgres.py +74 -0
  242. bbot/test/test_step_2/module_tests/test_module_postman.py +84 -253
  243. bbot/test/test_step_2/module_tests/test_module_postman_download.py +439 -0
  244. bbot/test/test_step_2/module_tests/test_module_rapiddns.py +93 -1
  245. bbot/test/test_step_2/module_tests/test_module_securitytxt.py +50 -0
  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 +1 -1
  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 +2 -6
  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 -11
  261. bbot/test/test_step_2/module_tests/test_module_wayback.py +1 -1
  262. bbot/test/test_step_2/template_tests/test_template_subdomain_enum.py +135 -0
  263. {bbot-2.0.1.4654rc0.dist-info → bbot-2.3.0.5397rc0.dist-info}/METADATA +48 -18
  264. bbot-2.3.0.5397rc0.dist-info/RECORD +421 -0
  265. {bbot-2.0.1.4654rc0.dist-info → bbot-2.3.0.5397rc0.dist-info}/WHEEL +1 -1
  266. bbot/modules/unstructured.py +0 -163
  267. bbot/test/test_step_2/module_tests/test_module_unstructured.py +0 -102
  268. bbot-2.0.1.4654rc0.dist-info/RECORD +0 -385
  269. {bbot-2.0.1.4654rc0.dist-info → bbot-2.3.0.5397rc0.dist-info}/LICENSE +0 -0
  270. {bbot-2.0.1.4654rc0.dist-info → bbot-2.3.0.5397rc0.dist-info}/entry_points.txt +0 -0
@@ -1,4 +1,5 @@
1
1
  import re
2
+ import httpx
2
3
 
3
4
  from ..bbot_fixtures import *
4
5
 
@@ -13,6 +14,7 @@ async def test_web_engine(bbot_scanner, bbot_httpserver, httpx_mock):
13
14
 
14
15
  base_url = bbot_httpserver.url_for("/test/")
15
16
  bbot_httpserver.expect_request(uri=re.compile(r"/test/\d+")).respond_with_handler(server_handler)
17
+ bbot_httpserver.expect_request(uri=re.compile(r"/nope")).respond_with_data("nope", status=500)
16
18
 
17
19
  scan = bbot_scanner()
18
20
 
@@ -27,7 +29,7 @@ async def test_web_engine(bbot_scanner, bbot_httpserver, httpx_mock):
27
29
  urls = [f"{base_url}{i}" for i in range(num_urls)]
28
30
  responses = [r async for r in scan.helpers.request_batch(urls)]
29
31
  assert len(responses) == 100
30
- assert all([r[1].status_code == 200 and r[1].text.startswith(f"{r[0]}: ") for r in responses])
32
+ assert all(r[1].status_code == 200 and r[1].text.startswith(f"{r[0]}: ") for r in responses)
31
33
 
32
34
  # request_batch w/ cancellation
33
35
  agen = scan.helpers.request_batch(urls)
@@ -49,15 +51,45 @@ async def test_web_engine(bbot_scanner, bbot_httpserver, httpx_mock):
49
51
  assert response.text.startswith(f"{url}: ")
50
52
  assert f"H{custom_tracker}: v{custom_tracker}" in response.text
51
53
 
54
+ # request with raise_error=True
55
+ with pytest.raises(WebError):
56
+ await scan.helpers.request("http://www.example.com/", raise_error=True)
57
+ try:
58
+ await scan.helpers.request("http://www.example.com/", raise_error=True)
59
+ except WebError as e:
60
+ assert hasattr(e, "response")
61
+ assert e.response is None
62
+ with pytest.raises(httpx.HTTPStatusError):
63
+ response = await scan.helpers.request(bbot_httpserver.url_for("/nope"), raise_error=True)
64
+ response.raise_for_status()
65
+ try:
66
+ response = await scan.helpers.request(bbot_httpserver.url_for("/nope"), raise_error=True)
67
+ response.raise_for_status()
68
+ except httpx.HTTPStatusError as e:
69
+ assert hasattr(e, "response")
70
+ assert e.response.status_code == 500
71
+
52
72
  # download
53
73
  url = f"{base_url}999"
54
74
  filename = await scan.helpers.download(url)
55
75
  file_content = open(filename).read()
56
76
  assert file_content.startswith(f"{url}: ")
57
77
 
58
- # raise_error=True
78
+ # download with raise_error=True
59
79
  with pytest.raises(WebError):
60
- await scan.helpers.request("http://www.example.com/", raise_error=True)
80
+ await scan.helpers.download("http://www.example.com/", raise_error=True)
81
+ try:
82
+ await scan.helpers.download("http://www.example.com/", raise_error=True)
83
+ except WebError as e:
84
+ assert hasattr(e, "response")
85
+ assert e.response is None
86
+ with pytest.raises(WebError):
87
+ await scan.helpers.download(bbot_httpserver.url_for("/nope"), raise_error=True)
88
+ try:
89
+ await scan.helpers.download(bbot_httpserver.url_for("/nope"), raise_error=True)
90
+ except WebError as e:
91
+ assert hasattr(e, "response")
92
+ assert e.response.status_code == 500
61
93
 
62
94
  await scan._cleanup()
63
95
 
@@ -121,9 +153,12 @@ async def test_web_helpers(bbot_scanner, bbot_httpserver, httpx_mock):
121
153
 
122
154
  await scan._cleanup()
123
155
 
124
- scan1 = bbot_scanner("8.8.8.8")
156
+ scan1 = bbot_scanner("8.8.8.8", modules=["ipneighbor"])
125
157
  scan2 = bbot_scanner("127.0.0.1")
126
158
 
159
+ await scan1._prep()
160
+ module = scan1.modules["ipneighbor"]
161
+
127
162
  web_config = CORE.config.get("web", {})
128
163
  user_agent = web_config.get("user_agent", "")
129
164
  headers = {"User-Agent": user_agent}
@@ -176,14 +211,14 @@ async def test_web_helpers(bbot_scanner, bbot_httpserver, httpx_mock):
176
211
  url = bbot_httpserver.url_for(path)
177
212
  bbot_httpserver.expect_request(uri=path).respond_with_data(download_content, status=200)
178
213
  webpage = await scan1.helpers.request(url)
179
- assert webpage, f"Webpage is False"
214
+ assert webpage, "Webpage is False"
180
215
  soup = scan1.helpers.beautifulsoup(webpage, "html.parser")
181
- assert soup, f"Soup is False"
216
+ assert soup, "Soup is False"
182
217
  # pretty_print = soup.prettify()
183
218
  # assert pretty_print, f"PrettyPrint is False"
184
219
  # scan1.helpers.log.info(f"{pretty_print}")
185
220
  html_text = soup.find(text="Example Domain")
186
- assert html_text, f"Find HTML Text is False"
221
+ assert html_text, "Find HTML Text is False"
187
222
 
188
223
  # 404
189
224
  path = "/test_http_helpers_download_404"
@@ -220,7 +255,7 @@ async def test_web_helpers(bbot_scanner, bbot_httpserver, httpx_mock):
220
255
  uri=f"{base_path}/3", query_string={"page_size": "100", "offset": "200"}
221
256
  ).respond_with_data("page3")
222
257
  results = []
223
- agen = scan1.helpers.api_page_iter(template_url)
258
+ agen = module.api_page_iter(template_url)
224
259
  try:
225
260
  async for result in agen:
226
261
  if result and result.text.startswith("page"):
@@ -230,7 +265,7 @@ async def test_web_helpers(bbot_scanner, bbot_httpserver, httpx_mock):
230
265
  finally:
231
266
  await agen.aclose()
232
267
  assert not results
233
- agen = scan1.helpers.api_page_iter(template_url, json=False)
268
+ agen = module.api_page_iter(template_url, json=False)
234
269
  try:
235
270
  async for result in agen:
236
271
  if result and result.text.startswith("page"):
@@ -354,7 +389,7 @@ async def test_web_http_compare(httpx_mock, bbot_scanner):
354
389
  await compare_helper.compare("http://www.example.com", check_reflection=True)
355
390
  compare_helper.compare_body({"asdf": "fdsa"}, {"fdsa": "asdf"})
356
391
  for mode in ("getparam", "header", "cookie"):
357
- assert await compare_helper.canary_check("http://www.example.com", mode=mode) == True
392
+ assert await compare_helper.canary_check("http://www.example.com", mode=mode) is True
358
393
 
359
394
  await scan._cleanup()
360
395
 
@@ -436,6 +471,9 @@ async def test_web_cookies(bbot_scanner, httpx_mock):
436
471
  # but that they're not sent in the response
437
472
  with pytest.raises(httpx.TimeoutException):
438
473
  r = await client2.get(url="http://www2.evilcorp.com/cookies/test")
474
+ # make sure cookies are sent
475
+ r = await client2.get(url="http://www2.evilcorp.com/cookies/test", cookies={"wats": "fdsa"})
476
+ assert r.status_code == 200
439
477
  # make sure we can manually send cookies
440
478
  httpx_mock.add_response(url="http://www2.evilcorp.com/cookies/test2", match_headers={"Cookie": "fdsa=wats"})
441
479
  r = await client2.get(url="http://www2.evilcorp.com/cookies/test2", cookies={"fdsa": "wats"})
@@ -11,17 +11,6 @@ from bbot.core.helpers.misc import rand_string
11
11
  log = logging.getLogger("bbot.test.modules")
12
12
 
13
13
 
14
- def tempwordlist(content):
15
- from bbot.core.helpers.misc import rand_string
16
-
17
- filename = bbot_test_dir / f"{rand_string(8)}"
18
- with open(filename, "w", errors="ignore") as f:
19
- for c in content:
20
- line = f"{c}\n"
21
- f.write(line)
22
- return filename
23
-
24
-
25
14
  class ModuleTestBase:
26
15
  targets = ["blacklanternsecurity.com"]
27
16
  scan_name = None
@@ -31,6 +20,8 @@ class ModuleTestBase:
31
20
  config_overrides = {}
32
21
  modules_overrides = None
33
22
  log = logging.getLogger("bbot")
23
+ # if True, the test will be skipped (useful for tests that require docker)
24
+ skip_distro_tests = False
34
25
 
35
26
  class ModuleTest:
36
27
  def __init__(
@@ -82,10 +73,10 @@ class ModuleTestBase:
82
73
  def set_expect_requests_handler(self, expect_args=None, request_handler=None):
83
74
  self.httpserver.expect_request(expect_args).respond_with_handler(request_handler)
84
75
 
85
- async def mock_dns(self, mock_data, scan=None):
76
+ async def mock_dns(self, mock_data, custom_lookup_fn=None, scan=None):
86
77
  if scan is None:
87
78
  scan = self.scan
88
- await scan.helpers.dns._mock_dns(mock_data)
79
+ await scan.helpers.dns._mock_dns(mock_data, custom_lookup_fn=custom_lookup_fn)
89
80
 
90
81
  def mock_interactsh(self, name):
91
82
  from ...conftest import Interactsh_mock
@@ -100,33 +91,39 @@ class ModuleTestBase:
100
91
  async def module_test(
101
92
  self, httpx_mock, bbot_httpserver, bbot_httpserver_ssl, monkeypatch, request, caplog, capsys
102
93
  ):
94
+ # Skip dastardly test if we're in the distro tests (because dastardly uses docker)
95
+ if os.getenv("BBOT_DISTRO_TESTS") and self.skip_distro_tests:
96
+ pytest.skip("Skipping module_test for dastardly module due to BBOT_DISTRO_TESTS environment variable")
97
+
103
98
  self.log.info(f"Starting {self.name} module test")
104
99
  module_test = self.ModuleTest(
105
100
  self, httpx_mock, bbot_httpserver, bbot_httpserver_ssl, monkeypatch, request, caplog, capsys
106
101
  )
107
- self.log.debug(f"Mocking DNS")
102
+ self.log.debug("Mocking DNS")
108
103
  await module_test.mock_dns({"blacklanternsecurity.com": {"A": ["127.0.0.88"]}})
109
- self.log.debug(f"Executing setup_before_prep()")
104
+ self.log.debug("Executing setup_before_prep()")
110
105
  await self.setup_before_prep(module_test)
111
- self.log.debug(f"Executing scan._prep()")
106
+ self.log.debug("Executing scan._prep()")
112
107
  await module_test.scan._prep()
113
- self.log.debug(f"Executing setup_after_prep()")
108
+ self.log.debug("Executing setup_after_prep()")
114
109
  await self.setup_after_prep(module_test)
115
- self.log.debug(f"Starting scan")
110
+ self.log.debug("Starting scan")
116
111
  module_test.events = [e async for e in module_test.scan.async_start()]
117
112
  self.log.debug(f"Finished {module_test.name} module test")
118
113
  yield module_test
119
114
 
120
115
  @pytest.mark.asyncio
121
116
  async def test_module_run(self, module_test):
122
- self.check(module_test, module_test.events)
117
+ from bbot.core.helpers.misc import execute_sync_or_async
118
+
119
+ await execute_sync_or_async(self.check, module_test, module_test.events)
123
120
  module_test.log.info(f"Finished {self.name} module test")
124
121
  current_task = asyncio.current_task()
125
122
  tasks = [t for t in asyncio.all_tasks() if t != current_task]
126
123
  if len(tasks):
127
124
  module_test.log.info(f"Unfinished tasks detected: {tasks}")
128
125
  else:
129
- module_test.log.info(f"No unfinished tasks detected")
126
+ module_test.log.info("No unfinished tasks detected")
130
127
 
131
128
  def check(self, module_test, events):
132
129
  assert False, f"Must override {self.name}.check()"
@@ -5,7 +5,7 @@ class TestAnubisdb(ModuleTestBase):
5
5
  async def setup_after_prep(self, module_test):
6
6
  module_test.module.abort_if = lambda e: False
7
7
  module_test.httpx_mock.add_response(
8
- url=f"https://jldc.me/anubis/subdomains/blacklanternsecurity.com",
8
+ url="https://jldc.me/anubis/subdomains/blacklanternsecurity.com",
9
9
  json=["asdf.blacklanternsecurity.com", "zzzz.blacklanternsecurity.com"],
10
10
  )
11
11
 
@@ -0,0 +1,71 @@
1
+ from pathlib import Path
2
+ from .base import ModuleTestBase, tempapkfile
3
+
4
+
5
+ class TestAPKPure(ModuleTestBase):
6
+ modules_overrides = ["apkpure", "google_playstore", "speculate"]
7
+ apk_file = tempapkfile()
8
+
9
+ async def setup_after_prep(self, module_test):
10
+ await module_test.mock_dns({"blacklanternsecurity.com": {"A": ["127.0.0.99"]}})
11
+ module_test.httpx_mock.add_response(
12
+ url="https://play.google.com/store/search?q=blacklanternsecurity&c=apps",
13
+ text="""<!DOCTYPE html>
14
+ <html>
15
+ <head>
16
+ <title>"blacklanternsecurity" - Android Apps on Google Play</title>
17
+ </head>
18
+ <body>
19
+ <a href="/store/apps/details?id=com.bbot.test&pcampaignid=dontmatchme&pli=1"/>
20
+ </body>
21
+ </html>""",
22
+ )
23
+ module_test.httpx_mock.add_response(
24
+ url="https://play.google.com/store/apps/details?id=com.bbot.test",
25
+ text="""<!DOCTYPE html>
26
+ <html>
27
+ <head>
28
+ <title>BBOT</title>
29
+ </head>
30
+ <body>
31
+ <meta name="appstore:developer_url" content="https://www.blacklanternsecurity.com">
32
+ </div>
33
+ </div>
34
+ </body>
35
+ </html>""",
36
+ )
37
+ module_test.httpx_mock.add_response(
38
+ url="https://d.apkpure.com/b/XAPK/com.bbot.test?version=latest",
39
+ content=self.apk_file,
40
+ headers={
41
+ "Content-Type": "application/vnd.android.package-archive",
42
+ "Content-Disposition": "attachment; filename=com.bbot.test.apk",
43
+ },
44
+ )
45
+
46
+ def check(self, module_test, events):
47
+ assert len(events) == 6
48
+ assert 1 == len(
49
+ [
50
+ e
51
+ for e in events
52
+ if e.type == "DNS_NAME" and e.data == "blacklanternsecurity.com" and e.scope_distance == 0
53
+ ]
54
+ ), "Failed to emit target DNS_NAME"
55
+ assert 1 == len(
56
+ [e for e in events if e.type == "ORG_STUB" and e.data == "blacklanternsecurity" and e.scope_distance == 0]
57
+ ), "Failed to find ORG_STUB"
58
+ assert 1 == len(
59
+ [
60
+ e
61
+ for e in events
62
+ if e.type == "MOBILE_APP"
63
+ and "android" in e.tags
64
+ and e.data["id"] == "com.bbot.test"
65
+ and e.data["url"] == "https://play.google.com/store/apps/details?id=com.bbot.test"
66
+ ]
67
+ ), "Failed to find bbot android app"
68
+ filesystem_event = [e for e in events if e.type == "FILESYSTEM" and "com.bbot.test.apk" in e.data["path"]]
69
+ assert 1 == len(filesystem_event), "Failed to download apk"
70
+ file = Path(filesystem_event[0].data["path"])
71
+ assert file.is_file(), "Destination apk doesn't exist"
@@ -10,7 +10,6 @@ class TestAsset_Inventory(ModuleTestBase):
10
10
  masscan_output = """{ "ip": "127.0.0.1", "timestamp": "1680197558", "ports": [ {"port": 9999, "proto": "tcp", "status": "open", "reason": "syn-ack", "ttl": 54} ] }"""
11
11
 
12
12
  async def setup_before_prep(self, module_test):
13
-
14
13
  async def run_masscan(command, *args, **kwargs):
15
14
  if "masscan" in command[:2]:
16
15
  targets = open(command[11]).read().splitlines()
@@ -22,7 +22,7 @@ class TestAzure_Realm(ModuleTestBase):
22
22
  async def setup_after_prep(self, module_test):
23
23
  await module_test.mock_dns({"evilcorp.com": {"A": ["127.0.0.5"]}})
24
24
  module_test.httpx_mock.add_response(
25
- url=f"https://login.microsoftonline.com/getuserrealm.srf?login=test@evilcorp.com",
25
+ url="https://login.microsoftonline.com/getuserrealm.srf?login=test@evilcorp.com",
26
26
  json=self.response_json,
27
27
  )
28
28
 
@@ -31,9 +31,9 @@ class TestBaddns_cname_nxdomain(BaseTestBaddns):
31
31
  module_test.monkeypatch.setattr(WhoisManager, "dispatchWHOIS", self.dispatchWHOIS)
32
32
 
33
33
  def check(self, module_test, events):
34
- assert any([e.data == "baddns.azurewebsites.net" for e in events]), "CNAME detection failed"
35
- assert any([e.type == "VULNERABILITY" for e in events]), "Failed to emit VULNERABILITY"
36
- assert any(["baddns-cname" in e.tags for e in events]), "Failed to add baddns tag"
34
+ assert any(e.data == "baddns.azurewebsites.net" for e in events), "CNAME detection failed"
35
+ assert any(e.type == "VULNERABILITY" for e in events), "Failed to emit VULNERABILITY"
36
+ assert any("baddns-cname" in e.tags for e in events), "Failed to add baddns tag"
37
37
 
38
38
 
39
39
  class TestBaddns_cname_signature(BaseTestBaddns):
@@ -60,8 +60,8 @@ class TestBaddns_cname_signature(BaseTestBaddns):
60
60
  module_test.monkeypatch.setattr(WhoisManager, "dispatchWHOIS", self.dispatchWHOIS)
61
61
 
62
62
  def check(self, module_test, events):
63
- assert any([e for e in events])
63
+ assert any(e for e in events)
64
64
  assert any(
65
- [e.type == "VULNERABILITY" and "bigcartel.com" in e.data["description"] for e in events]
65
+ e.type == "VULNERABILITY" and "bigcartel.com" in e.data["description"] for e in events
66
66
  ), "Failed to emit VULNERABILITY"
67
- assert any(["baddns-cname" in e.tags for e in events]), "Failed to add baddns tag"
67
+ assert any("baddns-cname" in e.tags for e in events), "Failed to add baddns tag"
@@ -0,0 +1,62 @@
1
+ from .base import ModuleTestBase
2
+ from bbot.modules.base import BaseModule
3
+
4
+
5
+ class BaseTestBaddns(ModuleTestBase):
6
+ modules_overrides = ["baddns_direct"]
7
+ targets = ["bad.dns"]
8
+ config_overrides = {"dns": {"minimal": False}, "cloudcheck": True}
9
+
10
+
11
+ class TestBaddns_direct_cloudflare(BaseTestBaddns):
12
+ targets = ["bad.dns:8888"]
13
+ modules_overrides = ["baddns_direct"]
14
+
15
+ async def dispatchWHOIS(self):
16
+ return None
17
+
18
+ class DummyModule(BaseModule):
19
+ watched_events = ["DNS_NAME"]
20
+ _name = "dummy_module"
21
+ events_seen = []
22
+
23
+ async def handle_event(self, event):
24
+ if event.data == "bad.dns":
25
+ await self.helpers.sleep(0.5)
26
+ self.events_seen.append(event.data)
27
+ url = "http://bad.dns:8888/"
28
+ url_event = self.scan.make_event(
29
+ url, "URL", parent=self.scan.root_event, tags=["cdn-cloudflare", "in-scope", "status-401"]
30
+ )
31
+ if url_event is not None:
32
+ await self.emit_event(url_event)
33
+
34
+ async def setup_after_prep(self, module_test):
35
+ from baddns.base import BadDNS_base
36
+ from baddns.lib.whoismanager import WhoisManager
37
+
38
+ def set_target(self, target):
39
+ return "127.0.0.1:8888"
40
+
41
+ self.module_test = module_test
42
+
43
+ self.dummy_module = self.DummyModule(module_test.scan)
44
+ module_test.scan.modules["dummy_module"] = self.dummy_module
45
+
46
+ expect_args = {"method": "GET", "uri": "/"}
47
+ respond_args = {"response_data": "The specified bucket does not exist", "status": 401}
48
+ module_test.set_expect_requests(expect_args=expect_args, respond_args=respond_args)
49
+
50
+ await module_test.mock_dns({"bad.dns": {"A": ["127.0.0.1"]}})
51
+
52
+ module_test.monkeypatch.setattr(BadDNS_base, "set_target", set_target)
53
+ module_test.monkeypatch.setattr(WhoisManager, "dispatchWHOIS", self.dispatchWHOIS)
54
+
55
+ def check(self, module_test, events):
56
+ assert any(
57
+ e.type == "FINDING"
58
+ and "Possible [AWS Bucket Takeover Detection] via direct BadDNS analysis. Indicator: [[Words: The specified bucket does not exist | Condition: and | Part: body] Matchers-Condition: and] Trigger: [self] baddns Module: [CNAME]"
59
+ in e.data["description"]
60
+ for e in events
61
+ ), "Failed to emit FINDING"
62
+ assert any("baddns-cname" in e.tags for e in events), "Failed to add baddns tag"
@@ -1,12 +1,16 @@
1
+ import random
2
+
1
3
  from .base import ModuleTestBase
2
4
 
3
5
 
4
6
  class TestBeVigil(ModuleTestBase):
7
+ module_name = "bevigil"
5
8
  config_overrides = {"modules": {"bevigil": {"api_key": "asdf", "urls": True}}}
6
9
 
7
10
  async def setup_after_prep(self, module_test):
8
11
  module_test.httpx_mock.add_response(
9
- url=f"https://osint.bevigil.com/api/blacklanternsecurity.com/subdomains/",
12
+ url="https://osint.bevigil.com/api/blacklanternsecurity.com/subdomains/",
13
+ match_headers={"X-Access-Token": "asdf"},
10
14
  json={
11
15
  "domain": "blacklanternsecurity.com",
12
16
  "subdomains": [
@@ -15,10 +19,33 @@ class TestBeVigil(ModuleTestBase):
15
19
  },
16
20
  )
17
21
  module_test.httpx_mock.add_response(
18
- url=f"https://osint.bevigil.com/api/blacklanternsecurity.com/urls/",
22
+ url="https://osint.bevigil.com/api/blacklanternsecurity.com/urls/",
19
23
  json={"domain": "blacklanternsecurity.com", "urls": ["https://asdf.blacklanternsecurity.com"]},
20
24
  )
21
25
 
22
26
  def check(self, module_test, events):
23
27
  assert any(e.data == "asdf.blacklanternsecurity.com" for e in events), "Failed to detect subdomain"
24
28
  assert any(e.data == "https://asdf.blacklanternsecurity.com/" for e in events), "Failed to detect url"
29
+
30
+
31
+ class TestBeVigilMultiKey(TestBeVigil):
32
+ api_keys = ["1234", "4321", "asdf", "fdsa"]
33
+ random.shuffle(api_keys)
34
+ config_overrides = {"modules": {"bevigil": {"api_key": api_keys, "urls": True}}}
35
+
36
+ async def setup_after_prep(self, module_test):
37
+ module_test.httpx_mock.add_response(
38
+ url="https://osint.bevigil.com/api/blacklanternsecurity.com/subdomains/",
39
+ match_headers={"X-Access-Token": "fdsa"},
40
+ json={
41
+ "domain": "blacklanternsecurity.com",
42
+ "subdomains": [
43
+ "asdf.blacklanternsecurity.com",
44
+ ],
45
+ },
46
+ )
47
+ module_test.httpx_mock.add_response(
48
+ match_headers={"X-Access-Token": "asdf"},
49
+ url="https://osint.bevigil.com/api/blacklanternsecurity.com/urls/",
50
+ json={"domain": "blacklanternsecurity.com", "urls": ["https://asdf.blacklanternsecurity.com"]},
51
+ )
@@ -6,7 +6,8 @@ class TestBinaryEdge(ModuleTestBase):
6
6
 
7
7
  async def setup_before_prep(self, module_test):
8
8
  module_test.httpx_mock.add_response(
9
- url=f"https://api.binaryedge.io/v2/query/domains/subdomain/blacklanternsecurity.com",
9
+ url="https://api.binaryedge.io/v2/query/domains/subdomain/blacklanternsecurity.com",
10
+ match_headers={"X-Key": "asdf"},
10
11
  json={
11
12
  "query": "blacklanternsecurity.com",
12
13
  "page": 1,
@@ -18,7 +19,8 @@ class TestBinaryEdge(ModuleTestBase):
18
19
  },
19
20
  )
20
21
  module_test.httpx_mock.add_response(
21
- url=f"https://api.binaryedge.io/v2/user/subscription",
22
+ url="https://api.binaryedge.io/v2/user/subscription",
23
+ match_headers={"X-Key": "asdf"},
22
24
  json={
23
25
  "subscription": {"name": "Free"},
24
26
  "end_date": "2023-06-17",
@@ -82,8 +82,8 @@ class Bucket_Amazon_Base(ModuleTestBase):
82
82
  if e.type == "FINDING" and str(e.module) == self.module_name:
83
83
  url = e.data.get("url", "")
84
84
  assert self.random_bucket_2 in url
85
- assert not self.random_bucket_1 in url
86
- assert not self.random_bucket_3 in url
85
+ assert self.random_bucket_1 not in url
86
+ assert self.random_bucket_3 not in url
87
87
  # make sure bucket mutations were found
88
88
  assert any(
89
89
  e.type == "STORAGE_BUCKET"
@@ -21,7 +21,7 @@ class TestBucket_Azure_NoDup(ModuleTestBase):
21
21
 
22
22
  async def setup_before_prep(self, module_test):
23
23
  module_test.httpx_mock.add_response(
24
- url=f"https://tesla.blob.core.windows.net/tesla?restype=container",
24
+ url="https://tesla.blob.core.windows.net/tesla?restype=container",
25
25
  text="",
26
26
  )
27
27
  await module_test.mock_dns(
@@ -0,0 +1,35 @@
1
+ from .base import ModuleTestBase
2
+
3
+
4
+ class TestBufferOverrun(ModuleTestBase):
5
+ config_overrides = {"modules": {"bufferoverrun": {"api_key": "asdf", "commercial": False}}}
6
+
7
+ async def setup_before_prep(self, module_test):
8
+ # Mock response for non-commercial API
9
+ module_test.httpx_mock.add_response(
10
+ url="https://tls.bufferover.run/dns?q=.blacklanternsecurity.com",
11
+ match_headers={"x-api-key": "asdf"},
12
+ json={"Results": ["1.2.3.4,example.com,*,*,sub.blacklanternsecurity.com"]},
13
+ )
14
+
15
+ def check(self, module_test, events):
16
+ assert any(e.data == "sub.blacklanternsecurity.com" for e in events), "Failed to detect subdomain for free API"
17
+
18
+
19
+ class TestBufferOverrunCommercial(ModuleTestBase):
20
+ modules_overrides = ["bufferoverrun"]
21
+ module_name = "bufferoverrun"
22
+ config_overrides = {"modules": {"bufferoverrun": {"api_key": "asdf", "commercial": True}}}
23
+
24
+ async def setup_before_prep(self, module_test):
25
+ # Mock response for commercial API
26
+ module_test.httpx_mock.add_response(
27
+ url="https://bufferover-run-tls.p.rapidapi.com/ipv4/dns?q=.blacklanternsecurity.com",
28
+ match_headers={"x-rapidapi-host": "bufferover-run-tls.p.rapidapi.com", "x-rapidapi-key": "asdf"},
29
+ json={"Results": ["5.6.7.8,blacklanternsecurity.com,*,*,sub.blacklanternsecurity.com"]},
30
+ )
31
+
32
+ def check(self, module_test, events):
33
+ assert any(
34
+ e.data == "sub.blacklanternsecurity.com" for e in events
35
+ ), "Failed to detect subdomain for commercial API"
@@ -6,7 +6,7 @@ class TestBuiltWith(ModuleTestBase):
6
6
 
7
7
  async def setup_after_prep(self, module_test):
8
8
  module_test.httpx_mock.add_response(
9
- url=f"https://api.builtwith.com/v20/api.json?KEY=asdf&LOOKUP=blacklanternsecurity.com&NOMETA=yes&NOATTR=yes&HIDETEXT=yes&HIDEDL=yes",
9
+ url="https://api.builtwith.com/v20/api.json?KEY=asdf&LOOKUP=blacklanternsecurity.com&NOMETA=yes&NOATTR=yes&HIDETEXT=yes&HIDEDL=yes",
10
10
  json={
11
11
  "Results": [
12
12
  {
@@ -91,7 +91,7 @@ class TestBuiltWith(ModuleTestBase):
91
91
  },
92
92
  )
93
93
  module_test.httpx_mock.add_response(
94
- url=f"https://api.builtwith.com/redirect1/api.json?KEY=asdf&LOOKUP=blacklanternsecurity.com",
94
+ url="https://api.builtwith.com/redirect1/api.json?KEY=asdf&LOOKUP=blacklanternsecurity.com",
95
95
  json={
96
96
  "Lookup": "blacklanternsecurity.com",
97
97
  "Inbound": [
@@ -26,7 +26,7 @@ class TestBypass403_collapsethreshold(ModuleTestBase):
26
26
  async def setup_after_prep(self, module_test):
27
27
  respond_args = {"response_data": "alive"}
28
28
 
29
- # some of these wont work outside of the module because of the complex logic. This doesn't matter, we just need to get more alerts than the threshold.
29
+ # some of these won't work outside of the module because of the complex logic. This doesn't matter, we just need to get more alerts than the threshold.
30
30
 
31
31
  query_payloads = [
32
32
  "%09",