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
@@ -16,7 +16,7 @@ from bbot.scanner import Scanner, Preset
16
16
  def test_preset_descriptions():
17
17
  # ensure very preset has a description
18
18
  preset = Preset()
19
- for yaml_file, (loaded_preset, category, preset_path, original_filename) in preset.all_presets.items():
19
+ for loaded_preset, category, preset_path, original_filename in preset.all_presets.values():
20
20
  assert (
21
21
  loaded_preset.description
22
22
  ), f'Preset "{loaded_preset.name}" at {original_filename} does not have a description.'
@@ -68,7 +68,6 @@ def test_core():
68
68
 
69
69
 
70
70
  def test_preset_yaml(clean_default_config):
71
-
72
71
  import yaml
73
72
 
74
73
  preset1 = Preset(
@@ -86,12 +85,15 @@ def test_preset_yaml(clean_default_config):
86
85
  debug=False,
87
86
  silent=True,
88
87
  config={"preset_test_asdf": 1},
89
- strict_scope=False,
90
88
  )
91
89
  preset1 = preset1.bake()
92
- assert "evilcorp.com" in preset1.target
90
+ assert "evilcorp.com" in preset1.target.seeds
91
+ assert "evilcorp.ce" not in preset1.target.seeds
92
+ assert "asdf.www.evilcorp.ce" in preset1.target.seeds
93
93
  assert "evilcorp.ce" in preset1.whitelist
94
+ assert "asdf.evilcorp.ce" in preset1.whitelist
94
95
  assert "test.www.evilcorp.ce" in preset1.blacklist
96
+ assert "asdf.test.www.evilcorp.ce" in preset1.blacklist
95
97
  assert "sslcert" in preset1.scan_modules
96
98
  assert preset1.whitelisted("evilcorp.ce")
97
99
  assert preset1.whitelisted("www.evilcorp.ce")
@@ -168,11 +170,17 @@ exclude_flags:
168
170
 
169
171
 
170
172
  def test_preset_scope():
173
+ # test target merging
174
+ scan = Scanner("1.2.3.4", preset=Preset.from_dict({"target": ["evilcorp.com"]}))
175
+ assert {str(h) for h in scan.preset.target.seeds.hosts} == {"1.2.3.4/32", "evilcorp.com"}
176
+ assert {e.data for e in scan.target.seeds} == {"1.2.3.4", "evilcorp.com"}
177
+ assert {e.data for e in scan.target.whitelist} == {"1.2.3.4", "evilcorp.com"}
171
178
 
172
179
  blank_preset = Preset()
173
180
  blank_preset = blank_preset.bake()
174
- assert not blank_preset.target
175
- assert blank_preset.strict_scope == False
181
+ assert not blank_preset.target.seeds
182
+ assert not blank_preset.target.whitelist
183
+ assert blank_preset.strict_scope is False
176
184
 
177
185
  preset1 = Preset(
178
186
  "evilcorp.com",
@@ -183,13 +191,14 @@ def test_preset_scope():
183
191
  preset1_baked = preset1.bake()
184
192
 
185
193
  # make sure target logic works as expected
186
- assert "evilcorp.com" in preset1_baked.target
187
- assert "asdf.evilcorp.com" in preset1_baked.target
188
- assert "asdf.www.evilcorp.ce" in preset1_baked.target
189
- assert not "evilcorp.ce" in preset1_baked.target
194
+ assert "evilcorp.com" in preset1_baked.target.seeds
195
+ assert "evilcorp.com" not in preset1_baked.target.whitelist
196
+ assert "asdf.evilcorp.com" in preset1_baked.target.seeds
197
+ assert "asdf.evilcorp.com" not in preset1_baked.target.whitelist
198
+ assert "asdf.evilcorp.ce" in preset1_baked.whitelist
190
199
  assert "evilcorp.ce" in preset1_baked.whitelist
191
200
  assert "test.www.evilcorp.ce" in preset1_baked.blacklist
192
- assert not "evilcorp.ce" in preset1_baked.blacklist
201
+ assert "evilcorp.ce" not in preset1_baked.blacklist
193
202
  assert preset1_baked.in_scope("www.evilcorp.ce")
194
203
  assert not preset1_baked.in_scope("evilcorp.com")
195
204
  assert not preset1_baked.in_scope("asdf.test.www.evilcorp.ce")
@@ -205,7 +214,7 @@ def test_preset_scope():
205
214
  "evilcorp.org",
206
215
  whitelist=["evilcorp.de"],
207
216
  blacklist=["test.www.evilcorp.de"],
208
- strict_scope=True,
217
+ config={"scope": {"strict": True}},
209
218
  )
210
219
 
211
220
  preset1.merge(preset3)
@@ -213,20 +222,24 @@ def test_preset_scope():
213
222
  preset1_baked = preset1.bake()
214
223
 
215
224
  # targets should be merged
216
- assert "evilcorp.com" in preset1_baked.target
217
- assert "www.evilcorp.ce" in preset1_baked.target
218
- assert "evilcorp.org" in preset1_baked.target
225
+ assert "evilcorp.com" in preset1_baked.target.seeds
226
+ assert "www.evilcorp.ce" in preset1_baked.target.seeds
227
+ assert "evilcorp.org" in preset1_baked.target.seeds
219
228
  # strict scope is enabled
220
- assert not "asdf.evilcorp.com" in preset1_baked.target
221
- assert not "asdf.www.evilcorp.ce" in preset1_baked.target
229
+ assert "asdf.www.evilcorp.ce" not in preset1_baked.target.seeds
230
+ assert "asdf.evilcorp.org" not in preset1_baked.target.seeds
231
+ assert "asdf.evilcorp.com" not in preset1_baked.target.seeds
232
+ assert "asdf.www.evilcorp.ce" not in preset1_baked.target.seeds
222
233
  assert "evilcorp.ce" in preset1_baked.whitelist
223
234
  assert "evilcorp.de" in preset1_baked.whitelist
224
- assert not "asdf.evilcorp.de" in preset1_baked.whitelist
225
- assert not "asdf.evilcorp.ce" in preset1_baked.whitelist
235
+ assert "asdf.evilcorp.de" not in preset1_baked.whitelist
236
+ assert "asdf.evilcorp.ce" not in preset1_baked.whitelist
226
237
  # blacklist should be merged, strict scope does not apply
238
+ assert "test.www.evilcorp.ce" in preset1_baked.blacklist
239
+ assert "test.www.evilcorp.de" in preset1_baked.blacklist
227
240
  assert "asdf.test.www.evilcorp.ce" in preset1_baked.blacklist
228
241
  assert "asdf.test.www.evilcorp.de" in preset1_baked.blacklist
229
- assert not "asdf.test.www.evilcorp.org" in preset1_baked.blacklist
242
+ assert "asdf.test.www.evilcorp.org" not in preset1_baked.blacklist
230
243
  # only the base domain of evilcorp.de should be in scope
231
244
  assert not preset1_baked.in_scope("evilcorp.com")
232
245
  assert not preset1_baked.in_scope("evilcorp.org")
@@ -259,14 +272,14 @@ def test_preset_scope():
259
272
  }
260
273
  assert preset_whitelist_baked.to_dict(include_target=True) == {
261
274
  "target": ["evilcorp.org"],
262
- "whitelist": ["1.2.3.0/24", "evilcorp.net"],
263
- "blacklist": ["evilcorp.co.uk"],
275
+ "whitelist": ["1.2.3.0/24", "http://evilcorp.net/"],
276
+ "blacklist": ["bob@evilcorp.co.uk", "evilcorp.co.uk:443"],
264
277
  "config": {"modules": {"secretsdb": {"api_key": "deadbeef", "otherthing": "asdf"}}},
265
278
  }
266
279
  assert preset_whitelist_baked.to_dict(include_target=True, redact_secrets=True) == {
267
280
  "target": ["evilcorp.org"],
268
- "whitelist": ["1.2.3.0/24", "evilcorp.net"],
269
- "blacklist": ["evilcorp.co.uk"],
281
+ "whitelist": ["1.2.3.0/24", "http://evilcorp.net/"],
282
+ "blacklist": ["bob@evilcorp.co.uk", "evilcorp.co.uk:443"],
270
283
  "config": {"modules": {"secretsdb": {"otherthing": "asdf"}}},
271
284
  }
272
285
 
@@ -274,7 +287,8 @@ def test_preset_scope():
274
287
  assert not preset_nowhitelist_baked.in_scope("www.evilcorp.de")
275
288
  assert not preset_nowhitelist_baked.in_scope("1.2.3.4/24")
276
289
 
277
- assert "www.evilcorp.org" in preset_whitelist_baked.target
290
+ assert "www.evilcorp.org" in preset_whitelist_baked.target.seeds
291
+ assert "www.evilcorp.org" not in preset_whitelist_baked.target.whitelist
278
292
  assert "1.2.3.4" in preset_whitelist_baked.whitelist
279
293
  assert not preset_whitelist_baked.in_scope("www.evilcorp.org")
280
294
  assert not preset_whitelist_baked.in_scope("www.evilcorp.de")
@@ -287,17 +301,17 @@ def test_preset_scope():
287
301
  assert preset_whitelist_baked.whitelisted("1.2.3.4/28")
288
302
  assert preset_whitelist_baked.whitelisted("1.2.3.4/24")
289
303
 
290
- assert set([e.data for e in preset_nowhitelist_baked.target]) == {"evilcorp.com"}
291
- assert set([e.data for e in preset_whitelist_baked.target]) == {"evilcorp.org"}
292
- assert set([e.data for e in preset_nowhitelist_baked.whitelist]) == {"evilcorp.com"}
293
- assert set([e.data for e in preset_whitelist_baked.whitelist]) == {"1.2.3.0/24", "evilcorp.net"}
304
+ assert {e.data for e in preset_nowhitelist_baked.seeds} == {"evilcorp.com"}
305
+ assert {e.data for e in preset_nowhitelist_baked.whitelist} == {"evilcorp.com"}
306
+ assert {e.data for e in preset_whitelist_baked.seeds} == {"evilcorp.org"}
307
+ assert {e.data for e in preset_whitelist_baked.whitelist} == {"1.2.3.0/24", "http://evilcorp.net/"}
294
308
 
295
309
  preset_nowhitelist.merge(preset_whitelist)
296
310
  preset_nowhitelist_baked = preset_nowhitelist.bake()
297
- assert set([e.data for e in preset_nowhitelist_baked.target]) == {"evilcorp.com", "evilcorp.org"}
298
- assert set([e.data for e in preset_nowhitelist_baked.whitelist]) == {"1.2.3.0/24", "evilcorp.net"}
299
- assert "www.evilcorp.org" in preset_nowhitelist_baked.target
300
- assert "www.evilcorp.com" in preset_nowhitelist_baked.target
311
+ assert {e.data for e in preset_nowhitelist_baked.seeds} == {"evilcorp.com", "evilcorp.org"}
312
+ assert {e.data for e in preset_nowhitelist_baked.whitelist} == {"1.2.3.0/24", "http://evilcorp.net/"}
313
+ assert "www.evilcorp.org" in preset_nowhitelist_baked.seeds
314
+ assert "www.evilcorp.com" in preset_nowhitelist_baked.seeds
301
315
  assert "1.2.3.4" in preset_nowhitelist_baked.whitelist
302
316
  assert not preset_nowhitelist_baked.in_scope("www.evilcorp.org")
303
317
  assert not preset_nowhitelist_baked.in_scope("www.evilcorp.com")
@@ -309,10 +323,12 @@ def test_preset_scope():
309
323
  preset_whitelist = Preset("evilcorp.org", whitelist=["1.2.3.4/24"])
310
324
  preset_whitelist.merge(preset_nowhitelist)
311
325
  preset_whitelist_baked = preset_whitelist.bake()
312
- assert set([e.data for e in preset_whitelist_baked.target]) == {"evilcorp.com", "evilcorp.org"}
313
- assert set([e.data for e in preset_whitelist_baked.whitelist]) == {"1.2.3.0/24"}
314
- assert "www.evilcorp.org" in preset_whitelist_baked.target
315
- assert "www.evilcorp.com" in preset_whitelist_baked.target
326
+ assert {e.data for e in preset_whitelist_baked.seeds} == {"evilcorp.com", "evilcorp.org"}
327
+ assert {e.data for e in preset_whitelist_baked.whitelist} == {"1.2.3.0/24"}
328
+ assert "www.evilcorp.org" in preset_whitelist_baked.seeds
329
+ assert "www.evilcorp.com" in preset_whitelist_baked.seeds
330
+ assert "www.evilcorp.org" not in preset_whitelist_baked.target.whitelist
331
+ assert "www.evilcorp.com" not in preset_whitelist_baked.target.whitelist
316
332
  assert "1.2.3.4" in preset_whitelist_baked.whitelist
317
333
  assert not preset_whitelist_baked.in_scope("www.evilcorp.org")
318
334
  assert not preset_whitelist_baked.in_scope("www.evilcorp.com")
@@ -324,18 +340,18 @@ def test_preset_scope():
324
340
  preset_nowhitelist2 = Preset("evilcorp.de")
325
341
  preset_nowhitelist1_baked = preset_nowhitelist1.bake()
326
342
  preset_nowhitelist2_baked = preset_nowhitelist2.bake()
327
- assert set([e.data for e in preset_nowhitelist1_baked.target]) == {"evilcorp.com"}
328
- assert set([e.data for e in preset_nowhitelist2_baked.target]) == {"evilcorp.de"}
329
- assert set([e.data for e in preset_nowhitelist1_baked.whitelist]) == {"evilcorp.com"}
330
- assert set([e.data for e in preset_nowhitelist2_baked.whitelist]) == {"evilcorp.de"}
343
+ assert {e.data for e in preset_nowhitelist1_baked.seeds} == {"evilcorp.com"}
344
+ assert {e.data for e in preset_nowhitelist2_baked.seeds} == {"evilcorp.de"}
345
+ assert {e.data for e in preset_nowhitelist1_baked.whitelist} == {"evilcorp.com"}
346
+ assert {e.data for e in preset_nowhitelist2_baked.whitelist} == {"evilcorp.de"}
331
347
  preset_nowhitelist1.merge(preset_nowhitelist2)
332
348
  preset_nowhitelist1_baked = preset_nowhitelist1.bake()
333
- assert set([e.data for e in preset_nowhitelist1_baked.target]) == {"evilcorp.com", "evilcorp.de"}
334
- assert set([e.data for e in preset_nowhitelist2_baked.target]) == {"evilcorp.de"}
335
- assert set([e.data for e in preset_nowhitelist1_baked.whitelist]) == {"evilcorp.com", "evilcorp.de"}
336
- assert set([e.data for e in preset_nowhitelist2_baked.whitelist]) == {"evilcorp.de"}
337
- assert "www.evilcorp.com" in preset_nowhitelist1_baked.target
338
- assert "www.evilcorp.de" in preset_nowhitelist1_baked.target
349
+ assert {e.data for e in preset_nowhitelist1_baked.seeds} == {"evilcorp.com", "evilcorp.de"}
350
+ assert {e.data for e in preset_nowhitelist2_baked.seeds} == {"evilcorp.de"}
351
+ assert {e.data for e in preset_nowhitelist1_baked.whitelist} == {"evilcorp.com", "evilcorp.de"}
352
+ assert {e.data for e in preset_nowhitelist2_baked.whitelist} == {"evilcorp.de"}
353
+ assert "www.evilcorp.com" in preset_nowhitelist1_baked.seeds
354
+ assert "www.evilcorp.de" in preset_nowhitelist1_baked.seeds
339
355
  assert "www.evilcorp.com" in preset_nowhitelist1_baked.target.seeds
340
356
  assert "www.evilcorp.de" in preset_nowhitelist1_baked.target.seeds
341
357
  assert "www.evilcorp.com" in preset_nowhitelist1_baked.whitelist
@@ -352,15 +368,14 @@ def test_preset_scope():
352
368
  preset_nowhitelist2.merge(preset_nowhitelist1)
353
369
  preset_nowhitelist1_baked = preset_nowhitelist1.bake()
354
370
  preset_nowhitelist2_baked = preset_nowhitelist2.bake()
355
- assert set([e.data for e in preset_nowhitelist1_baked.target]) == {"evilcorp.com"}
356
- assert set([e.data for e in preset_nowhitelist2_baked.target]) == {"evilcorp.com", "evilcorp.de"}
357
- assert set([e.data for e in preset_nowhitelist1_baked.whitelist]) == {"evilcorp.com"}
358
- assert set([e.data for e in preset_nowhitelist2_baked.whitelist]) == {"evilcorp.com", "evilcorp.de"}
371
+ assert {e.data for e in preset_nowhitelist1_baked.seeds} == {"evilcorp.com"}
372
+ assert {e.data for e in preset_nowhitelist2_baked.seeds} == {"evilcorp.com", "evilcorp.de"}
373
+ assert {e.data for e in preset_nowhitelist1_baked.whitelist} == {"evilcorp.com"}
374
+ assert {e.data for e in preset_nowhitelist2_baked.whitelist} == {"evilcorp.com", "evilcorp.de"}
359
375
 
360
376
 
361
377
  @pytest.mark.asyncio
362
378
  async def test_preset_logging():
363
-
364
379
  scan = Scanner()
365
380
 
366
381
  # test individual verbosity levels
@@ -369,30 +384,30 @@ async def test_preset_logging():
369
384
 
370
385
  try:
371
386
  silent_preset = Preset(silent=True)
372
- assert silent_preset.silent == True
373
- assert silent_preset.debug == False
374
- assert silent_preset.verbose == False
387
+ assert silent_preset.silent is True
388
+ assert silent_preset.debug is False
389
+ assert silent_preset.verbose is False
375
390
  assert original_log_level == CORE.logger.log_level
376
391
  debug_preset = Preset(debug=True)
377
- assert debug_preset.silent == False
378
- assert debug_preset.debug == True
379
- assert debug_preset.verbose == False
392
+ assert debug_preset.silent is False
393
+ assert debug_preset.debug is True
394
+ assert debug_preset.verbose is False
380
395
  assert original_log_level == CORE.logger.log_level
381
396
  verbose_preset = Preset(verbose=True)
382
- assert verbose_preset.silent == False
383
- assert verbose_preset.debug == False
384
- assert verbose_preset.verbose == True
397
+ assert verbose_preset.silent is False
398
+ assert verbose_preset.debug is False
399
+ assert verbose_preset.verbose is True
385
400
  assert original_log_level == CORE.logger.log_level
386
401
 
387
402
  # test conflicting verbosity levels
388
403
  silent_and_verbose = Preset(silent=True, verbose=True)
389
- assert silent_and_verbose.silent == True
390
- assert silent_and_verbose.debug == False
391
- assert silent_and_verbose.verbose == True
404
+ assert silent_and_verbose.silent is True
405
+ assert silent_and_verbose.debug is False
406
+ assert silent_and_verbose.verbose is True
392
407
  baked = silent_and_verbose.bake()
393
- assert baked.silent == True
394
- assert baked.debug == False
395
- assert baked.verbose == False
408
+ assert baked.silent is True
409
+ assert baked.debug is False
410
+ assert baked.verbose is False
396
411
  assert baked.core.logger.log_level == original_log_level
397
412
  baked = silent_and_verbose.bake(scan=scan)
398
413
  assert baked.core.logger.log_level == logging.CRITICAL
@@ -402,13 +417,13 @@ async def test_preset_logging():
402
417
  assert CORE.logger.log_level == original_log_level
403
418
 
404
419
  silent_and_debug = Preset(silent=True, debug=True)
405
- assert silent_and_debug.silent == True
406
- assert silent_and_debug.debug == True
407
- assert silent_and_debug.verbose == False
420
+ assert silent_and_debug.silent is True
421
+ assert silent_and_debug.debug is True
422
+ assert silent_and_debug.verbose is False
408
423
  baked = silent_and_debug.bake()
409
- assert baked.silent == True
410
- assert baked.debug == False
411
- assert baked.verbose == False
424
+ assert baked.silent is True
425
+ assert baked.debug is False
426
+ assert baked.verbose is False
412
427
  assert baked.core.logger.log_level == original_log_level
413
428
  baked = silent_and_debug.bake(scan=scan)
414
429
  assert baked.core.logger.log_level == logging.CRITICAL
@@ -418,13 +433,13 @@ async def test_preset_logging():
418
433
  assert CORE.logger.log_level == original_log_level
419
434
 
420
435
  debug_and_verbose = Preset(verbose=True, debug=True)
421
- assert debug_and_verbose.silent == False
422
- assert debug_and_verbose.debug == True
423
- assert debug_and_verbose.verbose == True
436
+ assert debug_and_verbose.silent is False
437
+ assert debug_and_verbose.debug is True
438
+ assert debug_and_verbose.verbose is True
424
439
  baked = debug_and_verbose.bake()
425
- assert baked.silent == False
426
- assert baked.debug == True
427
- assert baked.verbose == False
440
+ assert baked.silent is False
441
+ assert baked.debug is True
442
+ assert baked.verbose is False
428
443
  assert baked.core.logger.log_level == original_log_level
429
444
  baked = debug_and_verbose.bake(scan=scan)
430
445
  assert baked.core.logger.log_level == logging.DEBUG
@@ -434,13 +449,13 @@ async def test_preset_logging():
434
449
  assert CORE.logger.log_level == original_log_level
435
450
 
436
451
  all_preset = Preset(verbose=True, debug=True, silent=True)
437
- assert all_preset.silent == True
438
- assert all_preset.debug == True
439
- assert all_preset.verbose == True
452
+ assert all_preset.silent is True
453
+ assert all_preset.debug is True
454
+ assert all_preset.verbose is True
440
455
  baked = all_preset.bake()
441
- assert baked.silent == True
442
- assert baked.debug == False
443
- assert baked.verbose == False
456
+ assert baked.silent is True
457
+ assert baked.debug is False
458
+ assert baked.verbose is False
444
459
  assert baked.core.logger.log_level == original_log_level
445
460
  baked = all_preset.bake(scan=scan)
446
461
  assert baked.core.logger.log_level == logging.CRITICAL
@@ -527,12 +542,26 @@ def test_preset_module_resolution(clean_default_config):
527
542
  assert set(preset.scan_modules) == {"wayback"}
528
543
 
529
544
  # modules + module exclusions
530
- with pytest.raises(ValidationError) as error:
531
- preset = Preset(exclude_modules=["sslcert"], modules=["sslcert", "wappalyzer", "wayback"]).bake()
532
- assert str(error.value) == 'Unable to add scan module "sslcert" because the module has been excluded'
545
+ preset = Preset(exclude_modules=["sslcert"], modules=["sslcert", "wappalyzer", "wayback"]).bake()
546
+ baked_preset = preset.bake()
547
+ assert baked_preset.modules == {
548
+ "wayback",
549
+ "cloudcheck",
550
+ "python",
551
+ "json",
552
+ "speculate",
553
+ "dnsresolve",
554
+ "aggregate",
555
+ "excavate",
556
+ "txt",
557
+ "httpx",
558
+ "csv",
559
+ "wappalyzer",
560
+ }
533
561
 
534
562
 
535
- def test_preset_module_loader():
563
+ @pytest.mark.asyncio
564
+ async def test_preset_module_loader():
536
565
  custom_module_dir = bbot_test_dir / "custom_module_dir"
537
566
  custom_module_dir_2 = custom_module_dir / "asdf"
538
567
  custom_output_module_dir = custom_module_dir / "output"
@@ -640,9 +669,45 @@ class TestModule4(BaseModule):
640
669
  # reset module_loader
641
670
  preset2.module_loader.__init__()
642
671
 
672
+ # custom module dir via preset
673
+ custom_module_dir_3 = bbot_test_dir / "custom_module_dir_3"
674
+ custom_module_dir_3.mkdir(exist_ok=True, parents=True)
675
+ custom_module_5 = custom_module_dir_3 / "testmodule5.py"
676
+ with open(custom_module_5, "w") as f:
677
+ f.write(
678
+ """
679
+ from bbot.modules.base import BaseModule
643
680
 
644
- def test_preset_include():
681
+ class TestModule5(BaseModule):
682
+ watched_events = ["TECHNOLOGY"]
683
+ produced_events = ["FINDING"]
684
+ """
685
+ )
645
686
 
687
+ preset = Preset.from_yaml_string(
688
+ """
689
+ modules:
690
+ - testmodule5
691
+ """
692
+ )
693
+ # should fail
694
+ with pytest.raises(ValidationError):
695
+ scan = Scanner(preset=preset)
696
+
697
+ preset = Preset.from_yaml_string(
698
+ f"""
699
+ module_dirs:
700
+ - {custom_module_dir_3}
701
+ modules:
702
+ - testmodule5
703
+ """
704
+ )
705
+ scan = Scanner(preset=preset)
706
+ await scan._prep()
707
+ assert "testmodule5" in scan.modules
708
+
709
+
710
+ def test_preset_include():
646
711
  # test recursive preset inclusion
647
712
 
648
713
  custom_preset_dir_1 = bbot_test_dir / "custom_preset_dir"
@@ -814,7 +879,6 @@ def test_preset_module_disablement(clean_default_config):
814
879
 
815
880
 
816
881
  def test_preset_require_exclude():
817
-
818
882
  def get_module_flags(p):
819
883
  for m in p.scan_modules:
820
884
  preloaded = p.preloaded_module(m)
@@ -827,9 +891,9 @@ def test_preset_require_exclude():
827
891
  dnsbrute_flags = preset.preloaded_module("dnsbrute").get("flags", [])
828
892
  assert "subdomain-enum" in dnsbrute_flags
829
893
  assert "active" in dnsbrute_flags
830
- assert not "passive" in dnsbrute_flags
894
+ assert "passive" not in dnsbrute_flags
831
895
  assert "aggressive" in dnsbrute_flags
832
- assert not "safe" in dnsbrute_flags
896
+ assert "safe" not in dnsbrute_flags
833
897
  assert "dnsbrute" in [x[0] for x in module_flags]
834
898
  assert "certspotter" in [x[0] for x in module_flags]
835
899
  assert "c99" in [x[0] for x in module_flags]
@@ -843,7 +907,7 @@ def test_preset_require_exclude():
843
907
  assert len(preset.modules) > 25
844
908
  module_flags = list(get_module_flags(preset))
845
909
  assert "chaos" in [x[0] for x in module_flags]
846
- assert not "httpx" in [x[0] for x in module_flags]
910
+ assert "httpx" not in [x[0] for x in module_flags]
847
911
  assert all("passive" in flags for module, flags in module_flags)
848
912
  assert not any("active" in flags for module, flags in module_flags)
849
913
  assert any("safe" in flags for module, flags in module_flags)
@@ -854,7 +918,7 @@ def test_preset_require_exclude():
854
918
  assert len(preset.modules) > 25
855
919
  module_flags = list(get_module_flags(preset))
856
920
  assert "chaos" in [x[0] for x in module_flags]
857
- assert not "httpx" in [x[0] for x in module_flags]
921
+ assert "httpx" not in [x[0] for x in module_flags]
858
922
  assert all("passive" in flags for module, flags in module_flags)
859
923
  assert not any("active" in flags for module, flags in module_flags)
860
924
  assert any("safe" in flags for module, flags in module_flags)
@@ -864,7 +928,7 @@ def test_preset_require_exclude():
864
928
  preset = Preset(flags=["subdomain-enum"], exclude_modules=["dnsbrute"]).bake()
865
929
  assert len(preset.modules) > 25
866
930
  module_flags = list(get_module_flags(preset))
867
- assert not "dnsbrute" in [x[0] for x in module_flags]
931
+ assert "dnsbrute" not in [x[0] for x in module_flags]
868
932
  assert "httpx" in [x[0] for x in module_flags]
869
933
  assert any("passive" in flags for module, flags in module_flags)
870
934
  assert any("active" in flags for module, flags in module_flags)
@@ -875,7 +939,7 @@ def test_preset_require_exclude():
875
939
  preset = Preset(flags=["subdomain-enum"], require_flags=["safe", "passive"]).bake()
876
940
  assert len(preset.modules) > 25
877
941
  module_flags = list(get_module_flags(preset))
878
- assert not "dnsbrute" in [x[0] for x in module_flags]
942
+ assert "dnsbrute" not in [x[0] for x in module_flags]
879
943
  assert all("passive" in flags and "safe" in flags for module, flags in module_flags)
880
944
  assert all("active" not in flags and "aggressive" not in flags for module, flags in module_flags)
881
945
  assert not any("active" in flags for module, flags in module_flags)
@@ -885,7 +949,7 @@ def test_preset_require_exclude():
885
949
  preset = Preset(flags=["subdomain-enum"], exclude_flags=["aggressive", "active"]).bake()
886
950
  assert len(preset.modules) > 25
887
951
  module_flags = list(get_module_flags(preset))
888
- assert not "dnsbrute" in [x[0] for x in module_flags]
952
+ assert "dnsbrute" not in [x[0] for x in module_flags]
889
953
  assert all("passive" in flags and "safe" in flags for module, flags in module_flags)
890
954
  assert all("active" not in flags and "aggressive" not in flags for module, flags in module_flags)
891
955
  assert not any("active" in flags for module, flags in module_flags)
@@ -895,10 +959,29 @@ def test_preset_require_exclude():
895
959
  preset = Preset(flags=["subdomain-enum"], exclude_modules=["dnsbrute", "c99"]).bake()
896
960
  assert len(preset.modules) > 25
897
961
  module_flags = list(get_module_flags(preset))
898
- assert not "dnsbrute" in [x[0] for x in module_flags]
962
+ assert "dnsbrute" not in [x[0] for x in module_flags]
899
963
  assert "certspotter" in [x[0] for x in module_flags]
900
- assert not "c99" in [x[0] for x in module_flags]
964
+ assert "c99" not in [x[0] for x in module_flags]
901
965
  assert any("passive" in flags for module, flags in module_flags)
902
966
  assert any("active" in flags for module, flags in module_flags)
903
967
  assert any("safe" in flags for module, flags in module_flags)
904
968
  assert any("aggressive" in flags for module, flags in module_flags)
969
+
970
+
971
+ @pytest.mark.asyncio
972
+ async def test_preset_output_dir():
973
+ output_dir = bbot_test_dir / "preset_output_dir"
974
+ preset = Preset.from_yaml_string(
975
+ f"""
976
+ output_dir: {output_dir}
977
+ scan_name: bbot_test
978
+ """
979
+ )
980
+ scan = Scanner(preset=preset)
981
+ await scan.async_start_without_generator()
982
+ scan_dir = output_dir / "bbot_test"
983
+ assert scan_dir.is_dir()
984
+ output_file = scan_dir / "output.txt"
985
+ assert output_file.is_file()
986
+
987
+ shutil.rmtree(output_dir, ignore_errors=True)
@@ -84,6 +84,10 @@ def test_python_api_sync():
84
84
  def test_python_api_validation():
85
85
  from bbot.scanner import Scanner, Preset
86
86
 
87
+ # invalid target
88
+ with pytest.raises(ValidationError) as error:
89
+ Scanner("asdf:::asdf")
90
+ assert str(error.value) == 'Unable to autodetect event type from "asdf:::asdf"'
87
91
  # invalid module
88
92
  with pytest.raises(ValidationError) as error:
89
93
  Scanner(modules=["asdf"])
@@ -122,7 +126,8 @@ def test_python_api_validation():
122
126
  assert str(error.value) == 'Preset must be of type Preset, not "str"'
123
127
  # include nonexistent preset
124
128
  with pytest.raises(ValidationError) as error:
125
- Preset(include=["asdf"])
129
+ Preset(include=["nonexistent"])
126
130
  assert (
127
- str(error.value) == 'Could not find preset at "asdf" - file does not exist. Use -lp to list available presets'
131
+ str(error.value)
132
+ == 'Could not find preset at "nonexistent" - file does not exist. Use -lp to list available presets'
128
133
  )
@@ -91,7 +91,7 @@ def test_ip_regexes():
91
91
  ip == "2001:db8::1/128" and event_type == "IP_RANGE"
92
92
  ), f"Event type for IP_ADDRESS {ip} was not properly detected"
93
93
  else:
94
- matches = list(r.match(ip) for r in ip_address_regexes)
94
+ matches = [r.match(ip) for r in ip_address_regexes]
95
95
  assert any(matches), f"Good IP ADDRESS {ip} did not match regexes"
96
96
 
97
97
 
@@ -138,7 +138,7 @@ def test_ip_range_regexes():
138
138
  pytest.fail(f"BAD IP_RANGE: {bad_ip_range} raised unknown error: {e}: {traceback.format_exc()}")
139
139
 
140
140
  for good_ip_range in good_ip_ranges:
141
- matches = list(r.match(good_ip_range) for r in ip_range_regexes)
141
+ matches = [r.match(good_ip_range) for r in ip_range_regexes]
142
142
  assert any(matches), f"Good IP_RANGE {good_ip_range} did not match regexes"
143
143
 
144
144
 
@@ -191,7 +191,7 @@ def test_dns_name_regexes():
191
191
  pytest.fail(f"BAD DNS NAME: {dns} raised unknown error: {e}")
192
192
 
193
193
  for dns in good_dns:
194
- matches = list(r.match(dns) for r in dns_name_regexes)
194
+ matches = [r.match(dns) for r in dns_name_regexes]
195
195
  assert any(matches), f"Good DNS_NAME {dns} did not match regexes"
196
196
  event_type, _ = get_event_type(dns)
197
197
  if not event_type == "DNS_NAME":
@@ -253,7 +253,7 @@ def test_open_port_regexes():
253
253
  pytest.fail(f"BAD OPEN_TCP_PORT: {open_port} raised unknown error: {e}")
254
254
 
255
255
  for open_port in good_ports:
256
- matches = list(r.match(open_port) for r in open_port_regexes)
256
+ matches = [r.match(open_port) for r in open_port_regexes]
257
257
  assert any(matches), f"Good OPEN_TCP_PORT {open_port} did not match regexes"
258
258
  event_type, _ = get_event_type(open_port)
259
259
  assert event_type == "OPEN_TCP_PORT"
@@ -318,7 +318,7 @@ def test_url_regexes():
318
318
  pytest.fail(f"BAD URL: {bad_url} raised unknown error: {e}: {traceback.format_exc()}")
319
319
 
320
320
  for good_url in good_urls:
321
- matches = list(r.match(good_url) for r in url_regexes)
321
+ matches = [r.match(good_url) for r in url_regexes]
322
322
  assert any(matches), f"Good URL {good_url} did not match regexes"
323
323
  assert (
324
324
  get_event_type(good_url)[0] == "URL_UNVERIFIED"
@@ -372,3 +372,33 @@ async def test_regex_helper():
372
372
  assert matches.count(s) == 2
373
373
 
374
374
  await scan._cleanup()
375
+
376
+ # test yara hostname extractor helper
377
+ scan = Scanner("evilcorp.com", "www.evilcorp.net", "evilcorp.co.uk")
378
+ host_blob = """
379
+ https://evilcorp.com/
380
+ https://asdf.evilcorp.com/
381
+ https://asdf.www.evilcorp.net/
382
+ https://asdf.www.evilcorp.co.uk/
383
+ https://asdf.www.evilcorp.com/
384
+ https://asdf.www.evilcorp.com/
385
+ https://test.api.www.evilcorp.net/
386
+ """
387
+ extracted = await scan.extract_in_scope_hostnames(host_blob)
388
+ assert extracted == {
389
+ "evilcorp.co.uk",
390
+ "evilcorp.com",
391
+ "www.evilcorp.com",
392
+ "asdf.evilcorp.com",
393
+ "asdf.www.evilcorp.com",
394
+ "www.evilcorp.net",
395
+ "api.www.evilcorp.net",
396
+ "asdf.www.evilcorp.net",
397
+ "test.api.www.evilcorp.net",
398
+ "asdf.www.evilcorp.co.uk",
399
+ "www.evilcorp.co.uk",
400
+ }
401
+
402
+ scan = Scanner()
403
+ extracted = await scan.extract_in_scope_hostnames(host_blob)
404
+ assert extracted == set()