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
bbot/core/helpers/misc.py CHANGED
@@ -227,13 +227,13 @@ def split_host_port(d):
227
227
 
228
228
  match = bbot_regexes.extract_open_port_regex.match(netloc)
229
229
  if match is None:
230
- raise ValueError(f'split_port() failed to parse netloc "{netloc}"')
230
+ raise ValueError(f'split_port() failed to parse netloc "{netloc}" (original value: {d})')
231
231
 
232
232
  host = match.group(2)
233
233
  if host is None:
234
234
  host = match.group(1)
235
235
  if host is None:
236
- raise ValueError(f'split_port() failed to locate host in netloc "{netloc}"')
236
+ raise ValueError(f'split_port() failed to locate host in netloc "{netloc}" (original value: {d})')
237
237
 
238
238
  port = match.group(3)
239
239
  if port is None and scheme is not None:
@@ -365,7 +365,7 @@ def parent_url(u):
365
365
  if path.parent == path:
366
366
  return None
367
367
  else:
368
- return urlunparse(parsed._replace(path=str(path.parent)))
368
+ return urlunparse(parsed._replace(path=str(path.parent), query=""))
369
369
 
370
370
 
371
371
  def url_parents(u):
@@ -391,7 +391,7 @@ def url_parents(u):
391
391
  parent_list = []
392
392
  while 1:
393
393
  parent = parent_url(u)
394
- if parent == None:
394
+ if parent is None:
395
395
  return parent_list
396
396
  elif parent not in parent_list:
397
397
  parent_list.append(parent)
@@ -512,7 +512,7 @@ def domain_stem(domain):
512
512
  - Utilizes the `tldextract` function for domain parsing.
513
513
  """
514
514
  parsed = tldextract(str(domain))
515
- return f".".join(parsed.subdomain.split(".") + parsed.domain.split(".")).strip(".")
515
+ return ".".join(parsed.subdomain.split(".") + parsed.domain.split(".")).strip(".")
516
516
 
517
517
 
518
518
  def ip_network_parents(i, include_self=False):
@@ -586,17 +586,18 @@ def is_dns_name(d, include_local=True):
586
586
  if include_local:
587
587
  if bbot_regexes.hostname_regex.match(d):
588
588
  return True
589
- if bbot_regexes.dns_name_regex.match(d):
589
+ if bbot_regexes.dns_name_validation_regex.match(d):
590
590
  return True
591
591
  return False
592
592
 
593
593
 
594
- def is_ip(d, version=None):
594
+ def is_ip(d, version=None, include_network=False):
595
595
  """
596
596
  Checks if the given string or object represents a valid IP address.
597
597
 
598
598
  Args:
599
599
  d (str or ipaddress.IPvXAddress): The IP address to check.
600
+ include_network (bool, optional): Whether to include network types (IPv4Network or IPv6Network). Defaults to False.
600
601
  version (int, optional): The IP version to validate (4 or 6). Default is None.
601
602
 
602
603
  Returns:
@@ -612,21 +613,27 @@ def is_ip(d, version=None):
612
613
  >>> is_ip('evilcorp.com')
613
614
  False
614
615
  """
616
+ ip = None
615
617
  try:
616
618
  ip = ipaddress.ip_address(d)
617
- if version is None or ip.version == version:
618
- return True
619
619
  except Exception:
620
- pass
620
+ if include_network:
621
+ try:
622
+ ip = ipaddress.ip_network(d, strict=False)
623
+ except Exception:
624
+ pass
625
+ if ip is not None and (version is None or ip.version == version):
626
+ return True
621
627
  return False
622
628
 
623
629
 
624
- def is_ip_type(i):
630
+ def is_ip_type(i, network=None):
625
631
  """
626
632
  Checks if the given object is an instance of an IPv4 or IPv6 type from the ipaddress module.
627
633
 
628
634
  Args:
629
635
  i (ipaddress._BaseV4 or ipaddress._BaseV6): The IP object to check.
636
+ network (bool, optional): Whether to restrict the check to network types (IPv4Network or IPv6Network). Defaults to False.
630
637
 
631
638
  Returns:
632
639
  bool: True if the object is an instance of ipaddress._BaseV4 or ipaddress._BaseV6, False otherwise.
@@ -639,6 +646,12 @@ def is_ip_type(i):
639
646
  >>> is_ip_type("192.168.1.0/24")
640
647
  False
641
648
  """
649
+ if network is not None:
650
+ is_network = ipaddress._BaseNetwork in i.__class__.__mro__
651
+ if network:
652
+ return is_network
653
+ else:
654
+ return not is_network
642
655
  return ipaddress._IPAddressBase in i.__class__.__mro__
643
656
 
644
657
 
@@ -908,12 +921,12 @@ def extract_params_xml(xml_data, compare_mode="getparam"):
908
921
 
909
922
  # Define valid characters for each mode based on RFCs
910
923
  valid_chars_dict = {
911
- "header": set(
924
+ "header": {
912
925
  chr(c) for c in range(33, 127) if chr(c) in "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"
913
- ),
914
- "getparam": set(chr(c) for c in range(33, 127) if chr(c) not in ":/?#[]@!$&'()*+,;="),
915
- "postparam": set(chr(c) for c in range(33, 127) if chr(c) not in ":/?#[]@!$&'()*+,;="),
916
- "cookie": set(chr(c) for c in range(33, 127) if chr(c) not in '()<>@,;:"/[]?={} \t'),
926
+ },
927
+ "getparam": {chr(c) for c in range(33, 127) if chr(c) not in ":/?#[]@!$&'()*+,;="},
928
+ "postparam": {chr(c) for c in range(33, 127) if chr(c) not in ":/?#[]@!$&'()*+,;="},
929
+ "cookie": {chr(c) for c in range(33, 127) if chr(c) not in '()<>@,;:"/[]?={} \t'},
917
930
  }
918
931
 
919
932
 
@@ -1135,7 +1148,7 @@ def chain_lists(
1135
1148
  """
1136
1149
  if isinstance(l, str):
1137
1150
  l = [l]
1138
- final_list = dict()
1151
+ final_list = {}
1139
1152
  for entry in l:
1140
1153
  for s in split_regex.split(entry):
1141
1154
  f = s.strip()
@@ -1260,7 +1273,7 @@ def gen_numbers(n, padding=2):
1260
1273
  return results
1261
1274
 
1262
1275
 
1263
- def make_netloc(host, port):
1276
+ def make_netloc(host, port=None):
1264
1277
  """Constructs a network location string from a given host and port.
1265
1278
 
1266
1279
  Args:
@@ -1289,7 +1302,7 @@ def make_netloc(host, port):
1289
1302
  if is_ip(host, version=6):
1290
1303
  host = f"[{host}]"
1291
1304
  if port is None:
1292
- return host
1305
+ return str(host)
1293
1306
  return f"{host}:{port}"
1294
1307
 
1295
1308
 
@@ -1332,7 +1345,7 @@ def search_dict_by_key(key, d):
1332
1345
  if isinstance(d, dict):
1333
1346
  if key in d:
1334
1347
  yield d[key]
1335
- for k, v in d.items():
1348
+ for v in d.values():
1336
1349
  yield from search_dict_by_key(key, v)
1337
1350
  elif isinstance(d, list):
1338
1351
  for v in d:
@@ -1399,7 +1412,7 @@ def search_dict_values(d, *regexes):
1399
1412
  results.add(h)
1400
1413
  yield result
1401
1414
  elif isinstance(d, dict):
1402
- for _, v in d.items():
1415
+ for v in d.values():
1403
1416
  yield from search_dict_values(v, *regexes)
1404
1417
  elif isinstance(d, list):
1405
1418
  for v in d:
@@ -2384,7 +2397,7 @@ def in_exception_chain(e, exc_types):
2384
2397
  ... if not in_exception_chain(e, (KeyboardInterrupt, asyncio.CancelledError)):
2385
2398
  ... raise
2386
2399
  """
2387
- return any([isinstance(_, exc_types) for _ in get_exception_chain(e)])
2400
+ return any(isinstance(_, exc_types) for _ in get_exception_chain(e))
2388
2401
 
2389
2402
 
2390
2403
  def get_traceback_details(e):
@@ -2788,3 +2801,33 @@ def top_tcp_ports(n, as_string=False):
2788
2801
  if as_string:
2789
2802
  return ",".join([str(s) for s in top_ports])
2790
2803
  return top_ports
2804
+
2805
+
2806
+ class SafeDict(dict):
2807
+ def __missing__(self, key):
2808
+ return "{" + key + "}"
2809
+
2810
+
2811
+ def safe_format(s, **kwargs):
2812
+ """
2813
+ Format string while ignoring unused keys (prevents KeyError)
2814
+ """
2815
+ return s.format_map(SafeDict(kwargs))
2816
+
2817
+
2818
+ def get_python_constraints():
2819
+ req_regex = re.compile(r"([^(]+)\s*\((.*)\)", re.IGNORECASE)
2820
+
2821
+ def clean_requirement(req_string):
2822
+ # Extract package name and version constraints from format like "package (>=1.0,<2.0)"
2823
+ match = req_regex.match(req_string)
2824
+ if match:
2825
+ name, constraints = match.groups()
2826
+ return f"{name.strip()}{constraints}"
2827
+
2828
+ return req_string
2829
+
2830
+ from importlib.metadata import distribution
2831
+
2832
+ dist = distribution("bbot")
2833
+ return [clean_requirement(r) for r in dist.requires]
@@ -10,9 +10,11 @@ adjectives = [
10
10
  "affectionate",
11
11
  "aggravated",
12
12
  "aggrieved",
13
+ "agoraphobic",
13
14
  "almighty",
14
15
  "anal",
15
16
  "atrocious",
17
+ "autistic",
16
18
  "awkward",
17
19
  "baby",
18
20
  "begrudged",
@@ -65,6 +67,7 @@ adjectives = [
65
67
  "dramatic",
66
68
  "drunk",
67
69
  "effeminate",
70
+ "effervescent",
68
71
  "elden",
69
72
  "eldritch",
70
73
  "embarrassed",
@@ -75,6 +78,7 @@ adjectives = [
75
78
  "ethereal",
76
79
  "euphoric",
77
80
  "evil",
81
+ "expired",
78
82
  "exquisite",
79
83
  "extreme",
80
84
  "ferocious",
@@ -87,10 +91,8 @@ adjectives = [
87
91
  "foreboding",
88
92
  "frenetic",
89
93
  "frolicking",
90
- "frothy",
91
94
  "furry",
92
95
  "fuzzy",
93
- "gay",
94
96
  "gentle",
95
97
  "giddy",
96
98
  "glowering",
@@ -112,6 +114,7 @@ adjectives = [
112
114
  "imaginary",
113
115
  "immense",
114
116
  "immoral",
117
+ "impulsive",
115
118
  "incomprehensible",
116
119
  "inebriated",
117
120
  "inexplicable",
@@ -149,6 +152,7 @@ adjectives = [
149
152
  "muscular",
150
153
  "mushy",
151
154
  "mysterious",
155
+ "nascent",
152
156
  "naughty",
153
157
  "nefarious",
154
158
  "negligent",
@@ -163,6 +167,7 @@ adjectives = [
163
167
  "overzealous",
164
168
  "paranoid",
165
169
  "pasty",
170
+ "peckish",
166
171
  "pedantic",
167
172
  "pernicious",
168
173
  "perturbed",
@@ -183,7 +188,6 @@ adjectives = [
183
188
  "psychic",
184
189
  "puffy",
185
190
  "pure",
186
- "queer",
187
191
  "questionable",
188
192
  "rabid",
189
193
  "raging",
@@ -210,6 +214,7 @@ adjectives = [
210
214
  "sneaky",
211
215
  "soft",
212
216
  "sophisticated",
217
+ "spicy",
213
218
  "spiteful",
214
219
  "squishy",
215
220
  "steamy",
@@ -269,12 +274,14 @@ adjectives = [
269
274
  "wispy",
270
275
  "witty",
271
276
  "woolly",
277
+ "zesty",
272
278
  ]
273
279
 
274
280
  names = [
275
281
  "aaron",
276
282
  "abigail",
277
283
  "adam",
284
+ "adeem",
278
285
  "alan",
279
286
  "albert",
280
287
  "alex",
@@ -412,6 +419,7 @@ names = [
412
419
  "evelyn",
413
420
  "faramir",
414
421
  "florence",
422
+ "fox",
415
423
  "frances",
416
424
  "francis",
417
425
  "frank",
@@ -437,6 +445,7 @@ names = [
437
445
  "gregory",
438
446
  "gus",
439
447
  "hagrid",
448
+ "hank",
440
449
  "hannah",
441
450
  "harold",
442
451
  "harry",
@@ -447,6 +456,7 @@ names = [
447
456
  "hermione",
448
457
  "homer",
449
458
  "howard",
459
+ "hunter",
450
460
  "irene",
451
461
  "isaac",
452
462
  "isabella",
@@ -505,6 +515,7 @@ names = [
505
515
  "kevin",
506
516
  "kimberly",
507
517
  "kyle",
518
+ "kylie",
508
519
  "lantern",
509
520
  "larry",
510
521
  "laura",
@@ -528,6 +539,7 @@ names = [
528
539
  "lupin",
529
540
  "madison",
530
541
  "magnus",
542
+ "marcus",
531
543
  "margaret",
532
544
  "maria",
533
545
  "marie",
@@ -626,11 +638,13 @@ names = [
626
638
  "stephen",
627
639
  "steven",
628
640
  "susan",
641
+ "syrina",
629
642
  "tammy",
630
643
  "taylor",
631
644
  "teresa",
632
645
  "terry",
633
646
  "theoden",
647
+ "theon",
634
648
  "theresa",
635
649
  "thomas",
636
650
  "tiffany",
@@ -1,17 +1,12 @@
1
1
  import logging
2
2
  import traceback
3
3
  import threading
4
- import multiprocessing
5
4
  from multiprocessing.context import SpawnProcess
6
5
 
7
6
  from .misc import in_exception_chain
8
7
 
9
8
 
10
- current_process = multiprocessing.current_process()
11
-
12
-
13
9
  class BBOTThread(threading.Thread):
14
-
15
10
  default_name = "default bbot thread"
16
11
 
17
12
  def __init__(self, *args, **kwargs):
@@ -28,7 +23,6 @@ class BBOTThread(threading.Thread):
28
23
 
29
24
 
30
25
  class BBOTProcess(SpawnProcess):
31
-
32
26
  default_name = "bbot process pool"
33
27
 
34
28
  def __init__(self, *args, **kwargs):
@@ -57,17 +51,3 @@ class BBOTProcess(SpawnProcess):
57
51
  if not in_exception_chain(e, (KeyboardInterrupt,)):
58
52
  log.warning(f"Error in {self.name}: {e}")
59
53
  log.trace(traceback.format_exc())
60
-
61
-
62
- if current_process.name == "MainProcess":
63
- # if this is the main bbot process, set the logger and queue for the first time
64
- from bbot.core import CORE
65
- from functools import partialmethod
66
-
67
- BBOTProcess.__init__ = partialmethod(
68
- BBOTProcess.__init__, log_level=CORE.logger.log_level, log_queue=CORE.logger.queue
69
- )
70
-
71
- # this makes our process class the default for process pools, etc.
72
- mp_context = multiprocessing.get_context("spawn")
73
- mp_context.Process = BBOTProcess
@@ -41,7 +41,7 @@ class RegexHelper:
41
41
  """
42
42
  if not isinstance(compiled_regexes, dict):
43
43
  raise ValueError('compiled_regexes must be a dictionary like this: {"regex_name": <compiled_regex>}')
44
- for k, v in compiled_regexes.items():
44
+ for v in compiled_regexes.values():
45
45
  self.ensure_compiled_regex(v)
46
46
 
47
47
  tasks = {}
@@ -23,7 +23,7 @@ num_regex = re.compile(r"\d+")
23
23
  _ipv4_regex = r"(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(?:\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}"
24
24
  ipv4_regex = re.compile(_ipv4_regex, re.I)
25
25
 
26
- # IPv6 is complicated, so we have accomodate alternative patterns,
26
+ # IPv6 is complicated, so we have accommodate alternative patterns,
27
27
  # :(:[A-F0-9]{1,4}){1,7} == ::1, ::ffff:1
28
28
  # ([A-F0-9]{1,4}:){1,7}: == 2001::, 2001:db8::, 2001:db8:0:1:2:3::
29
29
  # ([A-F0-9]{1,4}:){1,6}:([A-F0-9]{1,4}) == 2001::1, 2001:db8::1, 2001:db8:0:1:2:3::1
@@ -36,11 +36,12 @@ _ip_range_regexes = (
36
36
  _ipv4_regex + r"\/[0-9]{1,2}",
37
37
  _ipv6_regex + r"\/[0-9]{1,3}",
38
38
  )
39
- ip_range_regexes = list(re.compile(r, re.I) for r in _ip_range_regexes)
39
+ ip_range_regexes = [re.compile(r, re.I) for r in _ip_range_regexes]
40
40
 
41
41
  # dns names with periods
42
42
  _dns_name_regex = r"(?:\w(?:[\w-]{0,100}\w)?\.)+(?:[xX][nN]--)?[^\W_]{1,63}\.?"
43
- dns_name_regex = re.compile(_dns_name_regex, re.I)
43
+ dns_name_extraction_regex = re.compile(_dns_name_regex, re.I)
44
+ dns_name_validation_regex = re.compile(r"^" + _dns_name_regex + r"$", re.I)
44
45
 
45
46
  # dns names without periods
46
47
  _hostname_regex = r"(?!\w*\.\w+)\w(?:[\w-]{0,100}\w)?"
@@ -54,20 +55,23 @@ ptr_regex = re.compile(_ptr_regex)
54
55
  # uuid regex
55
56
  _uuid_regex = r"[0-9a-f]{8}\b-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-\b[0-9a-f]{12}"
56
57
  uuid_regex = re.compile(_uuid_regex, re.I)
58
+ # event uuid regex
59
+ _event_uuid_regex = r"[0-9A-Z_]+:[0-9a-f]{8}\b-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-\b[0-9a-f]{12}"
60
+ event_uuid_regex = re.compile(_event_uuid_regex, re.I)
57
61
 
58
62
  _open_port_regexes = (
59
63
  _dns_name_regex + r":[0-9]{1,5}",
60
64
  _hostname_regex + r":[0-9]{1,5}",
61
65
  r"\[" + _ipv6_regex + r"\]:[0-9]{1,5}",
62
66
  )
63
- open_port_regexes = list(re.compile(r, re.I) for r in _open_port_regexes)
67
+ open_port_regexes = [re.compile(r, re.I) for r in _open_port_regexes]
64
68
 
65
69
  _url_regexes = (
66
70
  r"https?://" + _dns_name_regex + r"(?::[0-9]{1,5})?(?:(?:/|\?).*)?",
67
71
  r"https?://" + _hostname_regex + r"(?::[0-9]{1,5})?(?:(?:/|\?).*)?",
68
72
  r"https?://\[" + _ipv6_regex + r"\](?::[0-9]{1,5})?(?:(?:/|\?).*)?",
69
73
  )
70
- url_regexes = list(re.compile(r, re.I) for r in _url_regexes)
74
+ url_regexes = [re.compile(r, re.I) for r in _url_regexes]
71
75
 
72
76
  _double_slash_regex = r"/{2,}"
73
77
  double_slash_regex = re.compile(_double_slash_regex)
@@ -114,7 +118,7 @@ event_type_regexes = OrderedDict(
114
118
  scan_name_regex = re.compile(r"[a-z]{3,20}_[a-z]{3,20}")
115
119
 
116
120
 
117
- # For use with excavate paramaters extractor
121
+ # For use with excavate parameters extractor
118
122
  input_tag_regex = re.compile(
119
123
  r"<input[^>]+?name=[\"\']?([\.$\w]+)[\"\']?(?:[^>]*?value=[\"\']([=+\/\w]*)[\"\'])?[^>]*>"
120
124
  )
@@ -152,3 +156,5 @@ extract_host_regex = re.compile(_extract_host_regex, re.I)
152
156
  # for use in recursive_decode()
153
157
  encoded_regex = re.compile(r"%[0-9a-fA-F]{2}|\\u[0-9a-fA-F]{4}|\\U[0-9a-fA-F]{8}|\\[ntrbv]")
154
158
  backslash_regex = re.compile(r"(?P<slashes>\\+)(?P<char>[ntrvb])")
159
+
160
+ uuid_regex = re.compile(r"[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}")
@@ -132,7 +132,7 @@ def validate_host(host: Union[str, ipaddress.IPv4Address, ipaddress.IPv6Address]
132
132
  @validator
133
133
  def validate_severity(severity: str):
134
134
  severity = str(severity).strip().upper()
135
- if not severity in ("UNKNOWN", "INFO", "LOW", "MEDIUM", "HIGH", "CRITICAL"):
135
+ if severity not in ("UNKNOWN", "INFO", "LOW", "MEDIUM", "HIGH", "CRITICAL"):
136
136
  raise ValueError(f"Invalid severity: {severity}")
137
137
  return severity
138
138
 
@@ -299,7 +299,6 @@ def is_email(email):
299
299
 
300
300
 
301
301
  class Validators:
302
-
303
302
  def __init__(self, parent_helper):
304
303
  self.parent_helper = parent_helper
305
304
 
@@ -56,7 +56,7 @@ class BBOTAsyncClient(httpx.AsyncClient):
56
56
 
57
57
  # timeout
58
58
  http_timeout = self._web_config.get("http_timeout", 20)
59
- if not "timeout" in kwargs:
59
+ if "timeout" not in kwargs:
60
60
  kwargs["timeout"] = http_timeout
61
61
 
62
62
  # headers
@@ -14,7 +14,6 @@ log = logging.getLogger("bbot.core.helpers.web.engine")
14
14
 
15
15
 
16
16
  class HTTPEngine(EngineServer):
17
-
18
17
  CMDS = {
19
18
  0: "request",
20
19
  1: "request_batch",
@@ -81,15 +80,20 @@ class HTTPEngine(EngineServer):
81
80
  if client_kwargs:
82
81
  client = self.AsyncClient(**client_kwargs)
83
82
 
84
- async with self._acatch(url, raise_error):
85
- if self.http_debug:
86
- log.trace(f"Web request: {str(args)}, {str(kwargs)}")
87
- response = await client.request(*args, **kwargs)
88
- if self.http_debug:
89
- log.trace(
90
- f"Web response from {url}: {response} (Length: {len(response.content)}) headers: {response.headers}"
91
- )
92
- return response
83
+ try:
84
+ async with self._acatch(url, raise_error):
85
+ if self.http_debug:
86
+ log.trace(f"Web request: {str(args)}, {str(kwargs)}")
87
+ response = await client.request(*args, **kwargs)
88
+ if self.http_debug:
89
+ log.trace(
90
+ f"Web response from {url}: {response} (Length: {len(response.content)}) headers: {response.headers}"
91
+ )
92
+ return response
93
+ except httpx.HTTPError as e:
94
+ if raise_error:
95
+ _response = getattr(e, "response", None)
96
+ return {"_request_error": str(e), "_response": _response}
93
97
 
94
98
  async def request_batch(self, urls, threads=10, **kwargs):
95
99
  async for (args, _, _), response in self.task_pool(
@@ -105,8 +109,8 @@ class HTTPEngine(EngineServer):
105
109
 
106
110
  async def download(self, url, **kwargs):
107
111
  warn = kwargs.pop("warn", True)
112
+ raise_error = kwargs.pop("raise_error", False)
108
113
  filename = kwargs.pop("filename")
109
- raise_error = kwargs.get("raise_error", False)
110
114
  try:
111
115
  result = await self.stream_request(url, **kwargs)
112
116
  if result is None:
@@ -123,7 +127,8 @@ class HTTPEngine(EngineServer):
123
127
  log_fn = log.warning
124
128
  log_fn(f"Failed to download {url}: {e}")
125
129
  if raise_error:
126
- raise
130
+ _response = getattr(e, "response", None)
131
+ return {"_download_error": str(e), "_response": _response}
127
132
 
128
133
  async def stream_request(self, url, **kwargs):
129
134
  follow_redirects = kwargs.pop("follow_redirects", True)
@@ -132,7 +137,7 @@ class HTTPEngine(EngineServer):
132
137
  if max_size is not None:
133
138
  max_size = human_to_bytes(max_size)
134
139
  kwargs["follow_redirects"] = follow_redirects
135
- if not "method" in kwargs:
140
+ if "method" not in kwargs:
136
141
  kwargs["method"] = "GET"
137
142
  try:
138
143
  total_size = 0