bbot 2.4.2.6677rc0__py3-none-any.whl → 2.4.2.6706rc0__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 (33) hide show
  1. bbot/__init__.py +1 -1
  2. bbot/core/helpers/async_helpers.py +29 -8
  3. bbot/defaults.yml +6 -0
  4. bbot/modules/apkpure.py +5 -3
  5. bbot/modules/base.py +86 -32
  6. bbot/modules/docker_pull.py +3 -3
  7. bbot/modules/filedownload.py +8 -4
  8. bbot/modules/git_clone.py +5 -2
  9. bbot/modules/gitdumper.py +14 -5
  10. bbot/modules/github_workflows.py +12 -5
  11. bbot/modules/nuclei.py +4 -2
  12. bbot/modules/portscan.py +2 -0
  13. bbot/modules/postman_download.py +6 -3
  14. bbot/modules/trufflehog.py +1 -2
  15. bbot/scanner/preset/args.py +10 -1
  16. bbot/scanner/preset/preset.py +0 -2
  17. bbot/scanner/scanner.py +1 -4
  18. bbot/scripts/docs.py +8 -0
  19. bbot/test/test_step_1/test_scan.py +57 -0
  20. bbot/test/test_step_2/module_tests/test_module_apkpure.py +2 -0
  21. bbot/test/test_step_2/module_tests/test_module_bucket_file_enum.py +8 -3
  22. bbot/test/test_step_2/module_tests/test_module_docker_pull.py +2 -0
  23. bbot/test/test_step_2/module_tests/test_module_filedownload.py +5 -1
  24. bbot/test/test_step_2/module_tests/test_module_git_clone.py +4 -1
  25. bbot/test/test_step_2/module_tests/test_module_gitdumper.py +2 -0
  26. bbot/test/test_step_2/module_tests/test_module_portscan.py +3 -3
  27. bbot/test/test_step_2/module_tests/test_module_postman_download.py +6 -1
  28. bbot/test/test_step_2/module_tests/test_module_trufflehog.py +38 -12
  29. {bbot-2.4.2.6677rc0.dist-info → bbot-2.4.2.6706rc0.dist-info}/METADATA +1 -1
  30. {bbot-2.4.2.6677rc0.dist-info → bbot-2.4.2.6706rc0.dist-info}/RECORD +33 -33
  31. {bbot-2.4.2.6677rc0.dist-info → bbot-2.4.2.6706rc0.dist-info}/LICENSE +0 -0
  32. {bbot-2.4.2.6677rc0.dist-info → bbot-2.4.2.6706rc0.dist-info}/WHEEL +0 -0
  33. {bbot-2.4.2.6677rc0.dist-info → bbot-2.4.2.6706rc0.dist-info}/entry_points.txt +0 -0
bbot/__init__.py CHANGED
@@ -1,5 +1,5 @@
1
1
  # version placeholder (replaced by poetry-dynamic-versioning)
2
- __version__ = "v2.4.2.6677rc"
2
+ __version__ = "v2.4.2.6706rc"
3
3
 
4
4
  from .scanner import Scanner, Preset
5
5
 
@@ -1,10 +1,10 @@
1
+ import time
1
2
  import uuid
2
3
  import random
3
4
  import asyncio
4
5
  import logging
5
6
  import functools
6
- from datetime import datetime
7
- from .misc import human_timedelta
7
+ from contextlib import suppress
8
8
  from cachetools import keys, LRUCache
9
9
  from contextlib import asynccontextmanager
10
10
 
@@ -63,26 +63,27 @@ class TaskCounter:
63
63
  self._lock = asyncio.Lock()
64
64
  return self._lock
65
65
 
66
- def count(self, task_name, n=1, _log=True):
66
+ def count(self, task_name, n=1, asyncio_task=None, _log=True):
67
67
  if callable(task_name):
68
68
  task_name = f"{task_name.__qualname__}()"
69
- return self.Task(self, task_name, n=n, _log=_log)
69
+ return self.Task(self, task_name, n=n, _log=_log, asyncio_task=asyncio_task)
70
70
 
71
71
  class Task:
72
- def __init__(self, manager, task_name, n=1, _log=True):
72
+ def __init__(self, manager, task_name, n=1, _log=True, asyncio_task=None):
73
73
  self.manager = manager
74
74
  self.task_name = task_name
75
75
  self.task_id = None
76
76
  self.start_time = None
77
77
  self.log = _log
78
78
  self.n = n
79
+ self._asyncio_task = asyncio_task
79
80
 
80
81
  async def __aenter__(self):
81
82
  self.task_id = uuid.uuid4()
82
83
  # if self.log:
83
84
  # log.trace(f"Starting task {self.task_name} ({self.task_id})")
84
85
  async with self.manager.lock:
85
- self.start_time = datetime.now()
86
+ self.start_time = time.time()
86
87
  self.manager.tasks[self.task_id] = self
87
88
  return self
88
89
 
@@ -92,9 +93,29 @@ class TaskCounter:
92
93
  # if self.log:
93
94
  # log.trace(f"Finished task {self.task_name} ({self.task_id})")
94
95
 
96
+ @property
97
+ def asyncio_task(self):
98
+ if self._asyncio_task is None:
99
+ raise AttributeError("No asyncio task associated with this task")
100
+ return self._asyncio_task
101
+
102
+ @property
103
+ def function_name(self):
104
+ with suppress(AttributeError):
105
+ return self.asyncio_task.get_coro().__name__
106
+ return ""
107
+
108
+ async def cancel(self):
109
+ self.asyncio_task.cancel()
110
+ with suppress(asyncio.CancelledError):
111
+ await self.asyncio_task
112
+
113
+ @property
114
+ def running_for(self):
115
+ return time.time() - self.start_time
116
+
95
117
  def __str__(self):
96
- running_for = human_timedelta(datetime.now() - self.start_time)
97
- return f"{self.task_name} running for {running_for}"
118
+ return f"{self.task_name} running for {self.running_for:.1f}s"
98
119
 
99
120
 
100
121
  def get_event_loop():
bbot/defaults.yml CHANGED
@@ -133,6 +133,12 @@ deps:
133
133
  # Load BBOT modules from these custom paths
134
134
  module_dirs: []
135
135
 
136
+ # maximum runtime in seconds for each module's handle_event() is 60 minutes
137
+ # when the timeout is reached, the offending handle_event() will be cancelled and the module will move on to the next event
138
+ module_handle_event_timeout: 3600
139
+ # handle_batch() default timeout is 2 hours
140
+ module_handle_batch_timeout: 7200
141
+
136
142
  # Infer certain events from others, e.g. IPs from IP ranges, DNS_NAMEs from URLs, etc.
137
143
  speculate: True
138
144
  # Passively search event data for URLs, hostnames, emails, etc.
bbot/modules/apkpure.py CHANGED
@@ -13,14 +13,16 @@ class apkpure(BaseModule):
13
13
  "author": "@domwhewell-sage",
14
14
  }
15
15
  options = {"output_folder": ""}
16
- options_desc = {"output_folder": "Folder to download apk's to"}
16
+ options_desc = {
17
+ "output_folder": "Folder to download APKs to. If not specified, downloaded APKs will be deleted when the scan completes, to minimize disk usage."
18
+ }
17
19
 
18
20
  async def setup(self):
19
- output_folder = self.config.get("output_folder")
21
+ output_folder = self.config.get("output_folder", "")
20
22
  if output_folder:
21
23
  self.output_dir = Path(output_folder) / "apk_files"
22
24
  else:
23
- self.output_dir = self.scan.home / "apk_files"
25
+ self.output_dir = self.helpers.temp_dir / "apk_files"
24
26
  self.helpers.mkdir(self.output_dir)
25
27
  return await super().setup()
26
28
 
bbot/modules/base.py CHANGED
@@ -61,8 +61,6 @@ class BaseModule:
61
61
 
62
62
  batch_size (int): Size of batches processed by handle_batch(). Default is 1.
63
63
 
64
- batch_wait (int): Seconds to wait before force-submitting a batch. Default is 10.
65
-
66
64
  api_failure_abort_threshold (int): Threshold for setting error state after failed HTTP requests (only takes effect when `api_request()` is used. Default is 5.
67
65
 
68
66
  _preserve_graph (bool): When set to True, accept events that may be duplicates but are necessary for construction of complete graph. Typically only enabled for output modules that need to maintain full chains of events, e.g. `neo4j` and `json`. Default is False.
@@ -102,7 +100,6 @@ class BaseModule:
102
100
 
103
101
  _module_threads = 1
104
102
  _batch_size = 1
105
- batch_wait = 10
106
103
 
107
104
  # disable the module after this many failed attempts in a row
108
105
  _api_failure_abort_threshold = 3
@@ -159,6 +156,13 @@ class BaseModule:
159
156
 
160
157
  self._tasks = []
161
158
  self._event_received = None
159
+ # maximum runtime for each module's handle_event()
160
+ self._default_handle_event_timeout = self.scan.config.get("module_handle_event_timeout", 60 * 60) # 1 hour
161
+ self._default_handle_batch_timeout = self.scan.config.get(
162
+ "module_handle_batch_timeout", 60 * 60 * 2
163
+ ) # 2 hours
164
+ self._event_handler_watchdog_task = None
165
+ self._event_handler_watchdog_interval = self.event_handler_timeout / 10
162
166
 
163
167
  # used for optional "per host" tracking
164
168
  self._per_host_tracker = set()
@@ -399,6 +403,13 @@ class BaseModule:
399
403
  module_threads = self._module_threads
400
404
  return module_threads
401
405
 
406
+ @property
407
+ def event_handler_timeout(self):
408
+ module_timeout = self.config.get("module_timeout", None)
409
+ if module_timeout is not None:
410
+ return float(module_timeout)
411
+ return self._default_handle_event_timeout if self.batch_size <= 1 else self._default_handle_batch_timeout
412
+
402
413
  @property
403
414
  def auth_secret(self):
404
415
  """Indicates if the module is properly configured for authentication.
@@ -446,23 +457,28 @@ class BaseModule:
446
457
  - If a "FINISHED" event is found, invokes 'finish()' method of the module.
447
458
  """
448
459
  finish = False
449
- async with self._task_counter.count(f"{self.name}.handle_batch()") as counter:
450
- submitted = False
451
- if self.batch_size <= 1:
452
- return
453
- if self.num_incoming_events > 0:
454
- events, finish = await self._events_waiting()
455
- if events and not self.errored:
456
- counter.n = len(events)
457
- self.verbose(f"Handling batch of {len(events):,} events")
458
- submitted = True
459
- async with self.scan._acatch(f"{self.name}.handle_batch()"):
460
- await self.handle_batch(*events)
461
- self.verbose(f"Finished handling batch of {len(events):,} events")
460
+ submitted = False
461
+ if self.batch_size <= 1:
462
+ return
463
+ if self.num_incoming_events > 0:
464
+ events, finish = await self._events_waiting()
465
+ if events and not self.errored:
466
+ self.verbose(f"Handling batch of {len(events):,} events")
467
+ event_types = {}
468
+ for e in events:
469
+ event_types[e.type] = event_types.get(e.type, 0) + 1
470
+ event_types_sorted = sorted(event_types.items(), key=lambda x: x[1], reverse=True)
471
+ event_types_str = ", ".join(f"{k}: {v}" for k, v in event_types_sorted)
472
+ submitted = True
473
+ context = f"{self.name}.handle_batch({event_types_str})"
474
+ try:
475
+ await self.run_task(self.handle_batch(*events), context, n=len(events))
476
+ except asyncio.CancelledError:
477
+ self.debug(f"{context} was cancelled")
478
+ self.verbose(f"Finished handling batch of {len(events):,} events")
462
479
  if finish:
463
480
  context = f"{self.name}.finish()"
464
- async with self.scan._acatch(context), self._task_counter.count(context):
465
- await self.finish()
481
+ await self.run_task(self.finish(), context)
466
482
  return submitted
467
483
 
468
484
  def make_event(self, *args, **kwargs):
@@ -594,6 +610,10 @@ class BaseModule:
594
610
  asyncio.create_task(self._worker(), name=f"{self.scan.name}.{self.name}._worker()")
595
611
  for _ in range(self.module_threads)
596
612
  ]
613
+ self._event_handler_watchdog_task = asyncio.create_task(
614
+ self._event_handler_watchdog(),
615
+ name=f"{self.scan.name}.{self.name}._event_handler_watchdog()",
616
+ )
597
617
 
598
618
  async def _setup(self):
599
619
  """
@@ -662,11 +682,6 @@ class BaseModule:
662
682
  async with self.scan._acatch(context=self._worker, unhandled_is_critical=True):
663
683
  try:
664
684
  while not self.scan.stopping and not self.errored:
665
- # hold the reigns if our outgoing queue is full
666
- if self._qsize > 0 and self.outgoing_event_queue.qsize() >= self._qsize:
667
- await asyncio.sleep(0.1)
668
- continue
669
-
670
685
  # if batch wasn't big enough, we wait for the next event before continuing
671
686
  if self.batch_size > 1:
672
687
  submitted = await self._handle_batch()
@@ -689,14 +704,20 @@ class BaseModule:
689
704
  if acceptable:
690
705
  if event.type == "FINISHED":
691
706
  context = f"{self.name}.finish()"
692
- async with self.scan._acatch(context), self._task_counter.count(context):
693
- await self.finish()
707
+ try:
708
+ await self.run_task(self.finish(), context)
709
+ except asyncio.CancelledError:
710
+ self.debug(f"{context} was cancelled")
711
+ continue
694
712
  else:
695
713
  context = f"{self.name}.handle_event({event})"
696
714
  self.scan.stats.event_consumed(event, self)
697
715
  self.debug(f"Handling {event}")
698
- async with self.scan._acatch(context), self._task_counter.count(context):
699
- await self.handle_event(event)
716
+ try:
717
+ await self.run_task(self.handle_event(event), context)
718
+ except asyncio.CancelledError:
719
+ self.debug(f"{context} was cancelled")
720
+ continue
700
721
  self.debug(f"Finished handling {event}")
701
722
  else:
702
723
  self.debug(f"Not accepting {event} because {reason}")
@@ -852,6 +873,36 @@ class BaseModule:
852
873
  async with self.scan._acatch(context), self._task_counter.count(context):
853
874
  await self.helpers.execute_sync_or_async(callback)
854
875
 
876
+ async def run_task(self, coro, name, n=1):
877
+ """
878
+ Start a task while tracking it in the module's task counter.
879
+
880
+ This lets us keep a detailed module status and selectively cancel tasks when needed, like when handle_event exceeds its max runtime.
881
+ """
882
+ task = asyncio.create_task(coro)
883
+ async with self.scan._acatch(context=name), self._task_counter.count(task_name=name, asyncio_task=task, n=n):
884
+ return await task
885
+
886
+ async def _event_handler_watchdog(self):
887
+ """
888
+ Watches handle_event and handle_batch tasks and cancels them if they exceed their max runtime.
889
+ """
890
+ while not self.scan.stopping and not self.errored:
891
+ # if there are events in the outgoing queue, we leave the tasks alone
892
+ if self.outgoing_event_queue.qsize() > 0:
893
+ await self.helpers.sleep(self._event_handler_watchdog_interval)
894
+ continue
895
+ event_handler_tasks = [
896
+ t for t in self._task_counter.tasks.values() if t.function_name in ("handle_event", "handle_batch")
897
+ ]
898
+ for task in event_handler_tasks:
899
+ if task.running_for > self.event_handler_timeout:
900
+ self.warning(
901
+ f"{self.name} Cancelling event handler task {task.task_name} because it's been running for {task.running_for:.1f}s (max timeout is {self.event_handler_timeout})"
902
+ )
903
+ await task.cancel()
904
+ await asyncio.sleep(self._event_handler_watchdog_interval)
905
+
855
906
  async def queue_event(self, event):
856
907
  """
857
908
  Asynchronously queues an incoming event to the module's event queue for further processing.
@@ -1266,7 +1317,7 @@ class BaseModule:
1266
1317
  new_url, new_kwargs = iter_key(url, page, page_size, offset, **requests_kwargs)
1267
1318
  result = await self.api_request(new_url, **new_kwargs)
1268
1319
  if result is None:
1269
- self.verbose(f"api_page_iter() got no response for {url}")
1320
+ self.verbose(f"api_page_iter() got no response for {new_url}")
1270
1321
  break
1271
1322
  try:
1272
1323
  if _json:
@@ -1708,10 +1759,13 @@ class BaseInterceptModule(BaseModule):
1708
1759
  context = f"{self.name}.handle_event({event, kwargs})"
1709
1760
  self.scan.stats.event_consumed(event, self)
1710
1761
  self.debug(f"Intercepting {event}")
1711
- async with self.scan._acatch(context), self._task_counter.count(context):
1712
- forward_event = await self.handle_event(event, **kwargs)
1713
- with suppress(ValueError, TypeError):
1714
- forward_event, forward_event_reason = forward_event
1762
+ try:
1763
+ forward_event = await self.run_task(self.handle_event(event, **kwargs), context)
1764
+ except asyncio.CancelledError:
1765
+ self.debug(f"{context} was cancelled")
1766
+ continue
1767
+ with suppress(ValueError, TypeError):
1768
+ forward_event, forward_event_reason = forward_event
1715
1769
 
1716
1770
  if forward_event is False:
1717
1771
  self.debug(f"Not forwarding {event} because {forward_event_reason}")
@@ -17,7 +17,7 @@ class docker_pull(BaseModule):
17
17
  options = {"all_tags": False, "output_folder": ""}
18
18
  options_desc = {
19
19
  "all_tags": "Download all tags from each registry (Default False)",
20
- "output_folder": "Folder to download docker repositories to",
20
+ "output_folder": "Folder to download docker repositories to. If not specified, downloaded docker images will be deleted when the scan completes, to minimize disk usage.",
21
21
  }
22
22
 
23
23
  scope_distance_modifier = 2
@@ -34,11 +34,11 @@ class docker_pull(BaseModule):
34
34
  )
35
35
  }
36
36
  self.all_tags = self.config.get("all_tags", True)
37
- output_folder = self.config.get("output_folder")
37
+ output_folder = self.config.get("output_folder", "")
38
38
  if output_folder:
39
39
  self.output_dir = Path(output_folder) / "docker_images"
40
40
  else:
41
- self.output_dir = self.scan.home / "docker_images"
41
+ self.output_dir = self.helpers.temp_dir / "docker_images"
42
42
  self.helpers.mkdir(self.output_dir)
43
43
  return await super().setup()
44
44
 
@@ -84,12 +84,12 @@ class filedownload(BaseModule):
84
84
  "bz2", # Bzip2 Compressed File
85
85
  ],
86
86
  "max_filesize": "10MB",
87
- "base_64_encoded_file": "false",
87
+ "output_folder": "",
88
88
  }
89
89
  options_desc = {
90
90
  "extensions": "File extensions to download",
91
91
  "max_filesize": "Cancel download if filesize is greater than this size",
92
- "base_64_encoded_file": "Stream the bytes of a file and encode them in base 64 for event data.",
92
+ "output_folder": "Folder to download files to. If not specified, downloaded files will be deleted when the scan completes, to minimize disk usage.",
93
93
  }
94
94
 
95
95
  scope_distance_modifier = 3
@@ -97,10 +97,14 @@ class filedownload(BaseModule):
97
97
  async def setup(self):
98
98
  self.extensions = list({e.lower().strip(".") for e in self.config.get("extensions", [])})
99
99
  self.max_filesize = self.config.get("max_filesize", "10MB")
100
- self.download_dir = self.scan.home / "filedownload"
101
- self.helpers.mkdir(self.download_dir)
102
100
  self.urls_downloaded = set()
103
101
  self.files_downloaded = 0
102
+ output_dir = self.config.get("output_folder", "")
103
+ if output_dir:
104
+ self.download_dir = Path(output_dir) / "filedownload"
105
+ else:
106
+ self.download_dir = self.helpers.temp_dir / "filedownload"
107
+ self.helpers.mkdir(self.download_dir)
104
108
  self.mime_db_file = await self.helpers.wordlist(
105
109
  "https://raw.githubusercontent.com/jshttp/mime-db/master/db.json"
106
110
  )
bbot/modules/git_clone.py CHANGED
@@ -13,7 +13,10 @@ class git_clone(github):
13
13
  "author": "@domwhewell-sage",
14
14
  }
15
15
  options = {"api_key": "", "output_folder": ""}
16
- options_desc = {"api_key": "Github token", "output_folder": "Folder to clone repositories to"}
16
+ options_desc = {
17
+ "api_key": "Github token",
18
+ "output_folder": "Folder to clone repositories to. If not specified, cloned repositories will be deleted when the scan completes, to minimize disk usage.",
19
+ }
17
20
 
18
21
  deps_apt = ["git"]
19
22
 
@@ -24,7 +27,7 @@ class git_clone(github):
24
27
  if output_folder:
25
28
  self.output_dir = Path(output_folder) / "git_repos"
26
29
  else:
27
- self.output_dir = self.scan.home / "git_repos"
30
+ self.output_dir = self.helpers.temp_dir / "git_repos"
28
31
  self.helpers.mkdir(self.output_dir)
29
32
  return await super().setup()
30
33
 
bbot/modules/gitdumper.py CHANGED
@@ -1,3 +1,4 @@
1
+ import asyncio
1
2
  import regex as re
2
3
  from pathlib import Path
3
4
  from subprocess import CalledProcessError
@@ -19,7 +20,7 @@ class gitdumper(BaseModule):
19
20
  "max_semanic_version": 10,
20
21
  }
21
22
  options_desc = {
22
- "output_folder": "Folder to download repositories to",
23
+ "output_folder": "Folder to download repositories to. If not specified, downloaded repositories will be deleted when the scan completes, to minimize disk usage.",
23
24
  "fuzz_tags": "Fuzz for common git tag names (v0.0.1, 0.0.2, etc.) up to the max_semanic_version",
24
25
  "max_semanic_version": "Maximum version number to fuzz for (default < v10.10.10)",
25
26
  }
@@ -28,11 +29,11 @@ class gitdumper(BaseModule):
28
29
 
29
30
  async def setup(self):
30
31
  self.urls_downloaded = set()
31
- output_folder = self.config.get("output_folder")
32
+ output_folder = self.config.get("output_folder", "")
32
33
  if output_folder:
33
34
  self.output_dir = Path(output_folder) / "git_repos"
34
35
  else:
35
- self.output_dir = self.scan.home / "git_repos"
36
+ self.output_dir = self.helpers.temp_dir / "git_repos"
36
37
  self.helpers.mkdir(self.output_dir)
37
38
  self.unsafe_regex = self.helpers.re.compile(r"^\s*fsmonitor|sshcommand|askpass|editor|pager", re.IGNORECASE)
38
39
  self.ref_regex = self.helpers.re.compile(r"ref: refs/heads/([a-zA-Z\d_-]+)")
@@ -79,6 +80,7 @@ class gitdumper(BaseModule):
79
80
  "staging",
80
81
  "test",
81
82
  "testing",
83
+ "trunk",
82
84
  "wip",
83
85
  ]
84
86
  url_patterns = [
@@ -121,7 +123,11 @@ class gitdumper(BaseModule):
121
123
  dir_listing = await self.directory_listing_enabled(repo_url)
122
124
  if dir_listing:
123
125
  urls = await self.recursive_dir_list(dir_listing)
124
- result = await self.download_files(urls, repo_folder)
126
+ try:
127
+ result = await self.download_files(urls, repo_folder)
128
+ except asyncio.CancelledError:
129
+ self.verbose(f"Cancellation requested while downloading files from {repo_url}")
130
+ result = True
125
131
  else:
126
132
  result = await self.git_fuzz(repo_url, repo_folder)
127
133
  if result:
@@ -171,7 +177,10 @@ class gitdumper(BaseModule):
171
177
  result = await self.download_files(url_list, repo_folder)
172
178
  if result:
173
179
  await self.download_current_branch(repo_url, repo_folder)
174
- await self.download_git_objects(repo_url, repo_folder)
180
+ try:
181
+ await self.download_git_objects(repo_url, repo_folder)
182
+ except asyncio.CancelledError:
183
+ self.verbose(f"Cancellation requested while downloading git objects from {repo_url}")
175
184
  await self.download_git_packs(repo_url, repo_folder)
176
185
  return True
177
186
  else:
@@ -1,5 +1,6 @@
1
1
  import zipfile
2
2
  import fnmatch
3
+ from pathlib import Path
3
4
 
4
5
  from bbot.modules.templates.github import github
5
6
 
@@ -13,10 +14,11 @@ class github_workflows(github):
13
14
  "created_date": "2024-04-29",
14
15
  "author": "@domwhewell-sage",
15
16
  }
16
- options = {"api_key": "", "num_logs": 1}
17
+ options = {"api_key": "", "num_logs": 1, "output_folder": ""}
17
18
  options_desc = {
18
19
  "api_key": "Github token",
19
20
  "num_logs": "For each workflow fetch the last N successful runs logs (max 100)",
21
+ "output_folder": "Folder to download workflow logs and artifacts to",
20
22
  }
21
23
 
22
24
  scope_distance_modifier = 2
@@ -26,7 +28,11 @@ class github_workflows(github):
26
28
  if self.num_logs > 100:
27
29
  self.log.error("num_logs option is capped at 100")
28
30
  return False
29
- self.output_dir = self.scan.home / "workflow_logs"
31
+ output_folder = self.config.get("output_folder", "")
32
+ if output_folder:
33
+ self.output_dir = Path(output_folder) / "workflow_logs"
34
+ else:
35
+ self.output_dir = self.scan.home / "workflow_logs"
30
36
  self.helpers.mkdir(self.output_dir)
31
37
  return await super().setup()
32
38
 
@@ -35,9 +41,10 @@ class github_workflows(github):
35
41
  return r.is_success or getattr(r, "status_code", 0) == 404
36
42
 
37
43
  async def filter_event(self, event):
38
- if event.type == "CODE_REPOSITORY":
39
- if "git" not in event.tags and "github" not in event.data.get("url", ""):
40
- return False, "event is not a git repository"
44
+ if "git" not in event.tags:
45
+ return False, "event is not a git repository"
46
+ elif "github.com" not in event.data.get("url", ""):
47
+ return False, "event is not a github repository"
41
48
  return True
42
49
 
43
50
  async def handle_event(self, event):
bbot/modules/nuclei.py CHANGED
@@ -28,6 +28,7 @@ class nuclei(BaseModule):
28
28
  "directory_only": True,
29
29
  "retries": 0,
30
30
  "batch_size": 200,
31
+ "module_timeout": 21600, # 6 hours
31
32
  }
32
33
  options_desc = {
33
34
  "version": "nuclei version",
@@ -38,11 +39,12 @@ class nuclei(BaseModule):
38
39
  "concurrency": "maximum number of templates to be executed in parallel (default 25)",
39
40
  "mode": "manual | technology | severe | budget. Technology: Only activate based on technology events that match nuclei tags (nuclei -as mode). Manual (DEFAULT): Fully manual settings. Severe: Only critical and high severity templates without intrusive. Budget: Limit Nuclei to a specified number of HTTP requests",
40
41
  "etags": "tags to exclude from the scan",
41
- "budget": "Used in budget mode to set the number of requests which will be allotted to the nuclei scan",
42
+ "budget": "Used in budget mode to set the number of allowed requests per host",
42
43
  "silent": "Don't display nuclei's banner or status messages",
43
44
  "directory_only": "Filter out 'file' URL event (default True)",
44
45
  "retries": "number of times to retry a failed request (default 0)",
45
46
  "batch_size": "Number of targets to send to Nuclei per batch (default 200)",
47
+ "module_timeout": "Max time in seconds to spend handling each batch of events",
46
48
  }
47
49
  deps_ansible = [
48
50
  {
@@ -57,7 +59,7 @@ class nuclei(BaseModule):
57
59
  ]
58
60
  deps_pip = ["pyyaml~=6.0"]
59
61
  in_scope_only = True
60
- _batch_size = 25
62
+ _batch_size = 200
61
63
 
62
64
  async def setup(self):
63
65
  # attempt to update nuclei templates
bbot/modules/portscan.py CHANGED
@@ -30,6 +30,7 @@ class portscan(BaseModule):
30
30
  "adapter_ip": "",
31
31
  "adapter_mac": "",
32
32
  "router_mac": "",
33
+ "module_timeout": 259200, # 3 days
33
34
  }
34
35
  options_desc = {
35
36
  "top_ports": "Top ports to scan (default 100) (to override, specify 'ports')",
@@ -42,6 +43,7 @@ class portscan(BaseModule):
42
43
  "adapter_ip": "Send packets using this IP address. Not needed unless masscan's autodetection fails",
43
44
  "adapter_mac": "Send packets using this as the source MAC address. Not needed unless masscan's autodetection fails",
44
45
  "router_mac": "Send packets to this MAC address as the destination. Not needed unless masscan's autodetection fails",
46
+ "module_timeout": "Max time in seconds to spend handling each batch of events",
45
47
  }
46
48
  deps_common = ["masscan"]
47
49
  batch_size = 1000000
@@ -14,15 +14,18 @@ class postman_download(postman):
14
14
  "author": "@domwhewell-sage",
15
15
  }
16
16
  options = {"output_folder": "", "api_key": ""}
17
- options_desc = {"output_folder": "Folder to download postman workspaces to", "api_key": "Postman API Key"}
17
+ options_desc = {
18
+ "output_folder": "Folder to download postman workspaces to. If not specified, downloaded workspaces will be deleted when the scan completes, to minimize disk usage.",
19
+ "api_key": "Postman API Key",
20
+ }
18
21
  scope_distance_modifier = 2
19
22
 
20
23
  async def setup(self):
21
- output_folder = self.config.get("output_folder")
24
+ output_folder = self.config.get("output_folder", "")
22
25
  if output_folder:
23
26
  self.output_dir = Path(output_folder) / "postman_workspaces"
24
27
  else:
25
- self.output_dir = self.scan.home / "postman_workspaces"
28
+ self.output_dir = self.helpers.temp_dir / "postman_workspaces"
26
29
  self.helpers.mkdir(self.output_dir)
27
30
  return await super().setup()
28
31
 
@@ -88,8 +88,7 @@ class trufflehog(BaseModule):
88
88
 
89
89
  if event.type == "CODE_REPOSITORY":
90
90
  path = event.data["url"]
91
- if "git" in event.tags:
92
- module = "github-experimental"
91
+ module = "github-experimental"
93
92
  elif event.type == "FILESYSTEM":
94
93
  path = event.data["path"]
95
94
  if "git" in event.tags:
@@ -9,9 +9,18 @@ from bbot.core.helpers.misc import chain_lists, get_closest_match, get_keys_in_d
9
9
  log = logging.getLogger("bbot.presets.args")
10
10
 
11
11
 
12
+ universal_module_options = {
13
+ "batch_size": "The number of events to process in a single batch (only applies to batch modules)",
14
+ "module_threads": "How many event handlers to run in parallel",
15
+ "module_timeout": "Max time in seconds to spend handling each event or batch of events",
16
+ }
17
+
18
+
12
19
  class BBOTArgs:
13
20
  # module config options to exclude from validation
14
- exclude_from_validation = re.compile(r".*modules\.[a-z0-9_]+\.(?:batch_size|module_threads)$")
21
+ exclude_from_validation = re.compile(
22
+ r".*modules\.[a-z0-9_]+\.(?:" + "|".join(universal_module_options.keys()) + ")$"
23
+ )
15
24
 
16
25
  scan_examples = [
17
26
  (
@@ -276,8 +276,6 @@ class Preset(metaclass=BasePreset):
276
276
  self.exclude_flags.update(set(exclude_flags))
277
277
  self.require_flags.update(set(require_flags))
278
278
 
279
- # log.critical(f"{self.name}: verbose: {self.verbose}, debug: {self.debug}, silent: {self.silent}")
280
-
281
279
  @property
282
280
  def bbot_home(self):
283
281
  return Path(self.config.get("home", "~/.bbot")).expanduser().resolve()
bbot/scanner/scanner.py CHANGED
@@ -245,8 +245,6 @@ class Scanner:
245
245
  self._cleanedup = False
246
246
  self._omitted_event_types = None
247
247
 
248
- self.__loop = None
249
- self._manager_worker_loop_tasks = []
250
248
  self.init_events_task = None
251
249
  self.ticker_task = None
252
250
  self.dispatcher_tasks = []
@@ -726,6 +724,7 @@ class Scanner:
726
724
  scan_active_status.append(f" - {task}:")
727
725
  # scan_active_status.append(f" incoming_queue_size: {m.num_incoming_events}")
728
726
  # scan_active_status.append(f" outgoing_queue_size: {m.outgoing_event_queue.qsize()}")
727
+
729
728
  for line in scan_active_status:
730
729
  self.debug(line)
731
730
 
@@ -834,8 +833,6 @@ class Scanner:
834
833
  tasks.append(self.ticker_task)
835
834
  # dispatcher
836
835
  tasks += self.dispatcher_tasks
837
- # manager worker loops
838
- tasks += self._manager_worker_loop_tasks
839
836
  self.helpers.cancel_tasks_sync(tasks)
840
837
  # process pool
841
838
  self.helpers.process_pool.shutdown(cancel_futures=True)
bbot/scripts/docs.py CHANGED
@@ -181,6 +181,14 @@ def update_docs():
181
181
  assert len(bbot_output_module_table.splitlines()) > 10
182
182
  update_md_files("BBOT OUTPUT MODULES", bbot_output_module_table)
183
183
 
184
+ # BBOT universal module options
185
+ from bbot.scanner.preset.args import universal_module_options
186
+
187
+ universal_module_options_table = ""
188
+ for option, description in universal_module_options.items():
189
+ universal_module_options_table += f"**{option}**: {description}\n"
190
+ update_md_files("BBOT UNIVERSAL MODULE OPTIONS", universal_module_options_table)
191
+
184
192
  # BBOT module options
185
193
  bbot_module_options_table = DEFAULT_PRESET.module_loader.modules_options_table()
186
194
  assert len(bbot_module_options_table.splitlines()) > 100
@@ -88,6 +88,63 @@ async def test_scan(
88
88
  assert len(scan6.dns_strings) == 1
89
89
 
90
90
 
91
+ @pytest.mark.asyncio
92
+ async def test_task_scan_handle_event_timeout(bbot_scanner):
93
+ from bbot.modules.base import BaseModule
94
+
95
+ # make a module that takes a long time to handle an event
96
+ class LongModule(BaseModule):
97
+ watched_events = ["IP_ADDRESS"]
98
+ handled_event = False
99
+ cancelled = False
100
+ _name = "long"
101
+
102
+ async def handle_event(self, event):
103
+ self.handled_event = True
104
+ try:
105
+ await self.helpers.sleep(99999999)
106
+ except asyncio.CancelledError:
107
+ self.cancelled = True
108
+ raise
109
+
110
+ # same thing but handle_batch
111
+ class LongBatchModule(BaseModule):
112
+ watched_events = ["IP_ADDRESS"]
113
+ handled_event = False
114
+ canceled = False
115
+ _name = "long_batch"
116
+ _batch_size = 2
117
+
118
+ async def handle_batch(self, *events):
119
+ self.handled_event = True
120
+ try:
121
+ await self.helpers.sleep(99999999)
122
+ except asyncio.CancelledError:
123
+ self.cancelled = True
124
+ raise
125
+
126
+ # scan with both modules
127
+ scan = bbot_scanner(
128
+ "127.0.0.1",
129
+ config={
130
+ "module_handle_event_timeout": 5,
131
+ "module_handle_batch_timeout": 5,
132
+ },
133
+ )
134
+ await scan._prep()
135
+ scan.modules["long"] = LongModule(scan=scan)
136
+ scan.modules["long_batch"] = LongBatchModule(scan=scan)
137
+ events = [e async for e in scan.async_start()]
138
+ assert events
139
+ assert any(e.data == "127.0.0.1" for e in events)
140
+ # make sure both modules were called
141
+ assert scan.modules["long"].handled_event
142
+ assert scan.modules["long_batch"].handled_event
143
+ # they should also be cancelled
144
+ assert scan.modules["long"].cancelled
145
+ assert scan.modules["long_batch"].cancelled
146
+
147
+
91
148
  @pytest.mark.asyncio
92
149
  async def test_url_extension_handling(bbot_scanner):
93
150
  scan = bbot_scanner(config={"url_extension_blacklist": ["css"], "url_extension_httpx_only": ["js"]})
@@ -1,9 +1,11 @@
1
1
  from pathlib import Path
2
2
  from .base import ModuleTestBase, tempapkfile
3
+ from bbot.test.bbot_fixtures import bbot_test_dir
3
4
 
4
5
 
5
6
  class TestAPKPure(ModuleTestBase):
6
7
  modules_overrides = ["apkpure", "google_playstore", "speculate"]
8
+ config_overrides = {"modules": {"apkpure": {"output_folder": str(bbot_test_dir / "test_apkpure_files")}}}
7
9
  apk_file = tempapkfile()
8
10
 
9
11
  async def setup_after_prep(self, module_test):
@@ -1,10 +1,16 @@
1
1
  from .base import ModuleTestBase
2
+ from bbot.test.bbot_fixtures import bbot_test_dir
2
3
 
3
4
 
4
5
  class TestBucket_File_Enum(ModuleTestBase):
5
6
  targets = ["http://127.0.0.1:8888"]
6
7
  modules_overrides = ["bucket_file_enum", "filedownload", "httpx", "excavate", "cloudcheck"]
7
- config_overrides = {"scope": {"report_distance": 5}}
8
+
9
+ download_dir = bbot_test_dir / "test_bucket_file_enum"
10
+ config_overrides = {
11
+ "scope": {"report_distance": 5},
12
+ "modules": {"filedownload": {"output_folder": str(download_dir)}},
13
+ }
8
14
 
9
15
  open_bucket_url = "https://testbucket.s3.amazonaws.com/"
10
16
  open_bucket_body = """<ListBucketResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><Name>testbucket</Name><Prefix></Prefix><Marker></Marker><MaxKeys>1000</MaxKeys><IsTruncated>false</IsTruncated><Contents><Key>index.html</Key><LastModified>2023-05-22T23:04:38.000Z</LastModified><ETag>&quot;4a2d2d114f3abf90f8bd127c1f25095a&quot;</ETag><Size>5</Size><StorageClass>STANDARD</StorageClass></Contents><Contents><Key>test.pdf</Key><LastModified>2022-04-30T21:13:40.000Z</LastModified><ETag>&quot;723b0018c2f5a7ef06a34f84f6fa97e4&quot;</ETag><Size>388901</Size><StorageClass>STANDARD</StorageClass></Contents></ListBucketResult>"""
@@ -32,8 +38,7 @@ trailer <</Root 1 0 R>>"""
32
38
  )
33
39
 
34
40
  def check(self, module_test, events):
35
- download_dir = module_test.scan.home / "filedownload"
36
- files = list(download_dir.glob("*.pdf"))
41
+ files = list((self.download_dir / "filedownload").glob("*.pdf"))
37
42
  assert any(e.type == "URL_UNVERIFIED" and e.data.endswith("test.pdf") for e in events)
38
43
  assert not any(e.type == "URL_UNVERIFIED" and e.data.endswith("test.css") for e in events)
39
44
  assert any(f.name.endswith("test.pdf") for f in files), "Failed to download PDF file from open bucket"
@@ -3,10 +3,12 @@ import tarfile
3
3
  from pathlib import Path
4
4
 
5
5
  from .base import ModuleTestBase
6
+ from bbot.test.bbot_fixtures import bbot_test_dir
6
7
 
7
8
 
8
9
  class TestDockerPull(ModuleTestBase):
9
10
  modules_overrides = ["speculate", "dockerhub", "docker_pull"]
11
+ config_overrides = {"modules": {"docker_pull": {"output_folder": str(bbot_test_dir / "test_docker_files")}}}
10
12
 
11
13
  async def setup_before_prep(self, module_test):
12
14
  module_test.httpx_mock.add_response(
@@ -1,11 +1,15 @@
1
1
  from pathlib import Path
2
2
  from .base import ModuleTestBase
3
+ from bbot.test.bbot_fixtures import bbot_test_dir
3
4
 
4
5
 
5
6
  class TestFileDownload(ModuleTestBase):
6
7
  targets = ["http://127.0.0.1:8888"]
7
8
  modules_overrides = ["filedownload", "httpx", "excavate", "speculate"]
8
- config_overrides = {"web": {"spider_distance": 2, "spider_depth": 2}}
9
+ config_overrides = {
10
+ "web": {"spider_distance": 2, "spider_depth": 2},
11
+ "modules": {"filedownload": {"output_folder": str(bbot_test_dir / "test_filedownload_files")}},
12
+ }
9
13
 
10
14
  pdf_data = """%PDF-1.
11
15
  1 0 obj<</Pages 2 0 R>>endobj
@@ -6,10 +6,13 @@ import subprocess
6
6
  from pathlib import Path
7
7
 
8
8
  from .base import ModuleTestBase
9
+ from bbot.test.bbot_fixtures import bbot_test_dir
9
10
 
10
11
 
11
12
  class TestGit_Clone(ModuleTestBase):
12
- config_overrides = {"modules": {"git_clone": {"api_key": "asdf"}}}
13
+ config_overrides = {
14
+ "modules": {"git_clone": {"api_key": "asdf", "output_folder": str(bbot_test_dir / "test_git_files")}}
15
+ }
13
16
  modules_overrides = ["github_org", "speculate", "git_clone"]
14
17
 
15
18
  file_content = "https://admin:admin@the-internet.herokuapp.com/basic_auth"
@@ -1,5 +1,6 @@
1
1
  from pathlib import Path
2
2
  from .base import ModuleTestBase
3
+ from bbot.test.bbot_fixtures import bbot_test_dir
3
4
 
4
5
 
5
6
  class TestGitDumper_Dirlisting(ModuleTestBase):
@@ -8,6 +9,7 @@ class TestGitDumper_Dirlisting(ModuleTestBase):
8
9
  ]
9
10
 
10
11
  modules_overrides = ["git", "gitdumper", "httpx"]
12
+ config_overrides = {"modules": {"gitdumper": {"output_folder": str(bbot_test_dir / "test_output")}}}
11
13
 
12
14
  index_html = """<html>
13
15
  <head>
@@ -76,7 +76,7 @@ class TestPortscan(ModuleTestBase):
76
76
  def check(self, module_test, events):
77
77
  assert set(self.syn_scanned) == {"8.8.8.0/24", "8.8.4.0/24"}
78
78
  assert set(self.ping_scanned) == set()
79
- assert self.syn_runs == 1
79
+ assert self.syn_runs >= 1
80
80
  assert self.ping_runs == 0
81
81
  assert 1 == len(
82
82
  [e for e in events if e.type == "DNS_NAME" and e.data == "evilcorp.com" and str(e.module) == "TARGET"]
@@ -133,7 +133,7 @@ class TestPortscanPingFirst(TestPortscan):
133
133
  assert set(self.syn_scanned) == {"8.8.8.8/32"}
134
134
  assert set(self.ping_scanned) == {"8.8.8.0/24", "8.8.4.0/24"}
135
135
  assert self.syn_runs == 1
136
- assert self.ping_runs == 1
136
+ assert self.ping_runs >= 1
137
137
  open_port_events = [e for e in events if e.type == "OPEN_TCP_PORT"]
138
138
  assert len(open_port_events) == 3
139
139
  assert {e.data for e in open_port_events} == {"8.8.8.8:443", "evilcorp.com:443", "www.evilcorp.com:443"}
@@ -149,7 +149,7 @@ class TestPortscanPingOnly(TestPortscan):
149
149
  assert set(self.syn_scanned) == set()
150
150
  assert set(self.ping_scanned) == {"8.8.8.0/24", "8.8.4.0/24"}
151
151
  assert self.syn_runs == 0
152
- assert self.ping_runs == 1
152
+ assert self.ping_runs >= 1
153
153
  open_port_events = [e for e in events if e.type == "OPEN_TCP_PORT"]
154
154
  assert len(open_port_events) == 0
155
155
  ip_events = [e for e in events if e.type == "IP_ADDRESS"]
@@ -1,8 +1,13 @@
1
1
  from .base import ModuleTestBase
2
+ from bbot.test.bbot_fixtures import bbot_test_dir
2
3
 
3
4
 
4
5
  class TestPostman_Download(ModuleTestBase):
5
- config_overrides = {"modules": {"postman_download": {"api_key": "asdf"}}}
6
+ config_overrides = {
7
+ "modules": {
8
+ "postman_download": {"api_key": "asdf", "output_folder": str(bbot_test_dir / "test_postman_files")}
9
+ }
10
+ }
6
11
  modules_overrides = ["postman", "postman_download", "speculate"]
7
12
 
8
13
  async def setup_before_prep(self, module_test):
@@ -1,15 +1,24 @@
1
- import subprocess
2
- import shutil
3
1
  import io
2
+ import shutil
4
3
  import zipfile
5
4
  import tarfile
5
+ import subprocess
6
+ from copy import copy
6
7
  from pathlib import Path
7
8
 
8
9
  from .base import ModuleTestBase
10
+ from bbot.test.bbot_fixtures import bbot_test_dir
9
11
 
10
12
 
11
13
  class TestTrufflehog(ModuleTestBase):
12
- config_overrides = {"modules": {"postman_download": {"api_key": "asdf"}}}
14
+ download_dir = bbot_test_dir / "test_trufflehog"
15
+ config_overrides = {
16
+ "modules": {
17
+ "postman_download": {"api_key": "asdf", "output_folder": str(download_dir)},
18
+ "docker_pull": {"output_folder": str(download_dir)},
19
+ "git_clone": {"output_folder": str(download_dir)},
20
+ }
21
+ }
13
22
  modules_overrides = [
14
23
  "github_org",
15
24
  "speculate",
@@ -1129,15 +1138,20 @@ class TestTrufflehog(ModuleTestBase):
1129
1138
  cwd=temp_repo_path,
1130
1139
  )
1131
1140
 
1132
- old_filter_event = module_test.scan.modules["git_clone"].filter_event
1141
+ # we need this test to work offline, so we patch git_clone to pull from a local file:// path
1142
+ old_handle_event = module_test.scan.modules["git_clone"].handle_event
1133
1143
 
1134
- def new_filter_event(event):
1135
- event.data["url"] = event.data["url"].replace(
1136
- "https://github.com/blacklanternsecurity", f"file://{temp_path}"
1137
- )
1138
- return old_filter_event(event)
1144
+ async def new_handle_event(event):
1145
+ if event.type == "CODE_REPOSITORY":
1146
+ event = copy(event)
1147
+ data = dict(event.data)
1148
+ data["url"] = event.data["url"].replace(
1149
+ "https://github.com/blacklanternsecurity", f"file://{temp_path}"
1150
+ )
1151
+ event.data = data
1152
+ return await old_handle_event(event)
1139
1153
 
1140
- module_test.monkeypatch.setattr(module_test.scan.modules["git_clone"], "filter_event", new_filter_event)
1154
+ module_test.monkeypatch.setattr(module_test.scan.modules["git_clone"], "handle_event", new_handle_event)
1141
1155
 
1142
1156
  def check(self, module_test, events):
1143
1157
  vuln_events = [
@@ -1201,7 +1215,15 @@ class TestTrufflehog(ModuleTestBase):
1201
1215
 
1202
1216
 
1203
1217
  class TestTrufflehog_NonVerified(TestTrufflehog):
1204
- config_overrides = {"modules": {"trufflehog": {"only_verified": False}, "postman_download": {"api_key": "asdf"}}}
1218
+ download_dir = bbot_test_dir / "test_trufflehog_nonverified"
1219
+ config_overrides = {
1220
+ "modules": {
1221
+ "trufflehog": {"only_verified": False},
1222
+ "docker_pull": {"output_folder": str(download_dir)},
1223
+ "postman_download": {"api_key": "asdf", "output_folder": str(download_dir)},
1224
+ "git_clone": {"output_folder": str(download_dir)},
1225
+ }
1226
+ }
1205
1227
 
1206
1228
  def check(self, module_test, events):
1207
1229
  finding_events = [
@@ -1279,7 +1301,11 @@ class TestTrufflehog_HTTPResponse(ModuleTestBase):
1279
1301
  class TestTrufflehog_RAWText(ModuleTestBase):
1280
1302
  targets = ["http://127.0.0.1:8888/test.pdf"]
1281
1303
  modules_overrides = ["httpx", "trufflehog", "filedownload", "extractous"]
1282
- config_overrides = {"modules": {"trufflehog": {"only_verified": False}}}
1304
+
1305
+ download_dir = bbot_test_dir / "test_trufflehog_rawtext"
1306
+ config_overrides = {
1307
+ "modules": {"trufflehog": {"only_verified": False}, "filedownload": {"output_folder": str(download_dir)}}
1308
+ }
1283
1309
 
1284
1310
  async def setup_before_prep(self, module_test):
1285
1311
  expect_args = {
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: bbot
3
- Version: 2.4.2.6677rc0
3
+ Version: 2.4.2.6706rc0
4
4
  Summary: OSINT automation for hackers.
5
5
  License: GPL-3.0
6
6
  Keywords: python,cli,automation,osint,threat-intel,intelligence,neo4j,scanner,python-library,hacking,recursion,pentesting,recon,command-line-tool,bugbounty,subdomains,security-tools,subdomain-scanner,osint-framework,attack-surface,subdomain-enumeration,osint-tool
@@ -1,4 +1,4 @@
1
- bbot/__init__.py,sha256=bmNeDHa64LphmNVvCdHjCmGtQexQBcOVguArCx7r5Dw,163
1
+ bbot/__init__.py,sha256=s1OTUHWAHT3aJvwRblp7G38RUxmbxe716-VaBNn5r2k,163
2
2
  bbot/cli.py,sha256=1QJbANVw9Q3GFM92H2QRV2ds5756ulm08CDZwzwPpeI,11888
3
3
  bbot/core/__init__.py,sha256=l255GJE_DvUnWvrRb0J5lG-iMztJ8zVvoweDOfegGtI,46
4
4
  bbot/core/config/__init__.py,sha256=zYNw2Me6tsEr8hOOkLb4BQ97GB7Kis2k--G81S8vofU,342
@@ -11,7 +11,7 @@ bbot/core/event/base.py,sha256=1jUgd3I3TDITKoobh92ir_tIm38EN1ZbhoaX1W9fKts,67125
11
11
  bbot/core/event/helpers.py,sha256=MohOCVBjkn_K1p4Ipgx-MKliZtV6l4NJPq3YgagkvSM,6507
12
12
  bbot/core/flags.py,sha256=Ltvm8Bc4D65I55HuU5bzyjO1R3yMDNpVmreGU83ZBXE,1266
13
13
  bbot/core/helpers/__init__.py,sha256=cpOGLKIgA3vdHYqsOtx63BFO_qbtwCmez2amFPu6YTs,111
14
- bbot/core/helpers/async_helpers.py,sha256=3GVvRXEdRe3hAClTOSaIGb8Rn-_gM6l0IBQlaaNIsNA,3723
14
+ bbot/core/helpers/async_helpers.py,sha256=bVHEUIOZo8iCmuovLYb3oNLPdLFUoEyc6wZIIvtELVs,4399
15
15
  bbot/core/helpers/bloom.py,sha256=gk02rO6x3F5MICa7ZUDsinRudwoGAifsbiyiMCwd0Gs,2739
16
16
  bbot/core/helpers/cache.py,sha256=1aMr3HVD45cDtHEG5xlznDUCywRgO9oRFidscrs_5sA,1537
17
17
  bbot/core/helpers/command.py,sha256=UBJa2RInEJtGjZ5e24PUQxPu1aTCIFkcCrrB0ERLdGI,12810
@@ -50,20 +50,20 @@ bbot/core/modules.py,sha256=G4rRVF1bQzp62kwpgxwMa_FTV4-huWwtcd6HpW9jQf0,31970
50
50
  bbot/core/multiprocess.py,sha256=ocQHanskJ09gHwe7RZmwNdZyCOQyeyUoIHCtLbtvXUk,1771
51
51
  bbot/core/shared_deps.py,sha256=mCMZeKSt46trzVqQDPGfXfEWg0Zw5YjiJx4BnsIRgHM,7640
52
52
  bbot/db/sql/models.py,sha256=SrUdDOBCICzXJBY29p0VvILhMQ1JCuh725bqvIYogX0,4884
53
- bbot/defaults.yml,sha256=CQLKUpvLJMAOf0ukc9orvBCdvFm0u3lL-9bei4_UPdU,7517
53
+ bbot/defaults.yml,sha256=TTxtlnyE9vPihXjkGMDbBpNRlGa48GhRXS23iFsKUAg,7830
54
54
  bbot/errors.py,sha256=xwQcD26nU9oc7-o0kv5jmEDTInmi8_W8eKAgQZZxdVM,953
55
55
  bbot/logger.py,sha256=wE-532v5FyKuSSoTdyW1xSfaOnLZB1axAJnB-uW2xrI,2745
56
56
  bbot/modules/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
57
57
  bbot/modules/ajaxpro.py,sha256=daE1yQoCsSI5c4dh3YKwRSggTISNjWgrK7qTPidk7cU,3764
58
58
  bbot/modules/anubisdb.py,sha256=JCy2YCfa0e_VawpzNmcPXAosKUthmYGutireJ0gMDws,1916
59
- bbot/modules/apkpure.py,sha256=V-bGIFV7b91kXo24OitACip8Rx5qaoI1p6iHwbICxwk,2434
59
+ bbot/modules/apkpure.py,sha256=a_VRujUOIk7SVWyI9-N-nJVqfMApciIRdXVc-y_Ebuw,2558
60
60
  bbot/modules/azure_realm.py,sha256=pP2PUlLy0K9KKaE8aNcznWjDW3PKHvnMejdOSc-o4ms,1612
61
61
  bbot/modules/azure_tenant.py,sha256=qBn7CUA_hth2PqW55XZVjYxIw20xLYrMntXc6mYpmKU,5366
62
62
  bbot/modules/baddns.py,sha256=ubO3KDfcIMJnMjyZX5FWZ4GWxLSekV_JQV7QvsPjtD0,6693
63
63
  bbot/modules/baddns_direct.py,sha256=hWThpkXP87nnCRTlUh5qBJ1t4eo4l9kUmKNNxVNJI8A,3819
64
64
  bbot/modules/baddns_zone.py,sha256=y1XaBUfFPnRbR2qaTqRyUsPgEL73722v2B8aS5YoGN4,1035
65
65
  bbot/modules/badsecrets.py,sha256=LG37p48Rlxsfc3BmACMpkypsbuFTVvXqNhlP1IEsx0k,5109
66
- bbot/modules/base.py,sha256=2P-cCiFCBm9ov7z2WE7kWC_fncRiooD5604E_uOUYNE,75356
66
+ bbot/modules/base.py,sha256=qH5nmYRP3Gc9z8ddr0iY1DR7b68tatv4M0IB-SjDobU,78162
67
67
  bbot/modules/bevigil.py,sha256=0VLIxmeXRUI2-EoR6IzuHJMcX8KCHNNta-WYa3gVlDg,2862
68
68
  bbot/modules/binaryedge.py,sha256=5F9LnZwRM_rZnzTv29hLJLI2GEQdzOwSpahPFC1kJC0,1397
69
69
  bbot/modules/bucket_amazon.py,sha256=mwjYeEAcdfOpjbOa1sD8U9KBMMVY_c8FoHjSGR9GQbg,730
@@ -93,24 +93,24 @@ bbot/modules/dnscaa.py,sha256=pyaLqHrdsVhqtd1JBZVjKKcuYT_ywUbFYkrnfXcGD5s,5014
93
93
  bbot/modules/dnscommonsrv.py,sha256=gEErfSur7Odkaif4CbXYx3OZ3FQrQESyiMGPbcDKSIg,1538
94
94
  bbot/modules/dnsdumpster.py,sha256=bqUqyvRJVtoTXbDxTZ-kgPNq4dCE9xv_msBIn_Nj5IM,3251
95
95
  bbot/modules/dnstlsrpt.py,sha256=ntNKVDXDgDVWr1A20ShNT5HFBhXsVEM5aUIEU_0c9HU,6427
96
- bbot/modules/docker_pull.py,sha256=N0wOGPasrqiBt7z1Y89t6E0ldZd8a2pw_kabPEwyzb4,9072
96
+ bbot/modules/docker_pull.py,sha256=3Ui5z3pNfZDgX8q25h-LwKvUM7FDPST2dz1vk_I8gDc,9192
97
97
  bbot/modules/dockerhub.py,sha256=JQkujjqvQRzQuvHjQ7JbFs_VlJj8dLRPRObAkBgUQhc,3493
98
98
  bbot/modules/dotnetnuke.py,sha256=zipcHyNYr2FEecStb1Yrm938ps01RvHV8NnyqAvnGGc,10537
99
99
  bbot/modules/emailformat.py,sha256=RLPJW-xitYB-VT4Lp08qVzFkXx_kMyV_035JT_Yf4fM,1082
100
100
  bbot/modules/extractous.py,sha256=VSGKmHPAA_4r62jaN8Yqi3JcjehjxpI2lhe8i2j786s,4648
101
101
  bbot/modules/ffuf.py,sha256=94TJ5xvqKwH0JaWmC_t1dLTpRsO8HEy4lnbsu8LF_HY,14965
102
102
  bbot/modules/ffuf_shortnames.py,sha256=y5vnypLPN-KrjpmoG5zlqcX8VwfcLBpNg1yQI7bP9Hg,18737
103
- bbot/modules/filedownload.py,sha256=TOxftfxguaRSEKI8oG79XVRQqUGg1_IhYDYl_Jw9eYc,8694
103
+ bbot/modules/filedownload.py,sha256=ZQZQCkXYb0lUP4DnEfL__8vc9KRi0m9hfBhClTKwy2U,8906
104
104
  bbot/modules/fingerprintx.py,sha256=rdlR9d64AntAhbS_eJzh8bZCeLPTJPSKdkdKdhH_qAo,3269
105
105
  bbot/modules/fullhunt.py,sha256=zeehQb9akBSbHW9dF4icH8Vfd8LqoTrpIvnQEEMWes8,1311
106
106
  bbot/modules/generic_ssrf.py,sha256=KFdcHpUV9-Z7oN7emzbirimsNc2xZ_1IFqnsfIkEbcM,9196
107
107
  bbot/modules/git.py,sha256=zmHeI0bn181T1P8C55HSebkdVGLTpzGxPc-LRqiHrbc,1723
108
- bbot/modules/git_clone.py,sha256=XFZXx0k97EMY3E5PZzdNvqQzZddOfRMaVp5ol2gk11s,2468
109
- bbot/modules/gitdumper.py,sha256=XBYt6oSXm09FJVdH37zrn9T1Nhqc0zK4KLugMevedOw,11531
108
+ bbot/modules/git_clone.py,sha256=w-s3O6rZL_I8_BuPKotnAzXKnn7saw159jcQ_R1xtKw,2602
109
+ bbot/modules/gitdumper.py,sha256=d2FnSAraWftkQ9ENmkBk3_kUbuw4G3eKpsi0FXLzBss,12042
110
110
  bbot/modules/github_codesearch.py,sha256=a-r2vE9N9WyBpFUiKCsg0TK4Qn7DaEGyVRTUKzkDLWA,3641
111
111
  bbot/modules/github_org.py,sha256=WM18vJCHuOHJJ5rPzQzQ3Pmp7XPPuaMeVgNfW-FlO0k,8938
112
112
  bbot/modules/github_usersearch.py,sha256=G8knkQBJsn7EKcMhcEaFPiB_Y5S96e2VaseBubsqOyk,3407
113
- bbot/modules/github_workflows.py,sha256=RDtzR0DC2sqiWzMtiqlrCSwtZHWL2MoIJBKd6LVTAdI,9720
113
+ bbot/modules/github_workflows.py,sha256=xKntAFDeGuE4MqbEmhJyYXKbzoSh9tWYlHNlnF37PYA,10040
114
114
  bbot/modules/gitlab.py,sha256=9oWWpBijeHCjuFBfWW4HvNqt7bvJvrBgBjaaz_UPPnE,5964
115
115
  bbot/modules/google_playstore.py,sha256=N4QjzQag_bgDXfX17rytBiiWA-SQtYI2N0J_ZNEOdv0,3701
116
116
  bbot/modules/gowitness.py,sha256=vcyZl87xJVHWTuRY032d2dORg3ykVKPIBwD2HzSxvYA,11784
@@ -147,7 +147,7 @@ bbot/modules/lightfuzz/submodules/xss.py,sha256=VP15TBeRjglIRjLvwmHJaOCNQOWS7R4W
147
147
  bbot/modules/myssl.py,sha256=DoMF7o6MxIrcglCrC-W3nM-GPcyJRM4PlGdKfnOlIvs,942
148
148
  bbot/modules/newsletters.py,sha256=1Q4JjShPsxHJ-by2CbGfCvEt80blUGPX0hxQIzB_a9M,2630
149
149
  bbot/modules/ntlm.py,sha256=EGmb4k3YC_ZuHIU3mGUZ4yaMjE35wVQQSv8HwTsQJzY,4391
150
- bbot/modules/nuclei.py,sha256=m1Y1lp3suDT173dxmMNLGNjwu-o7Ahi0cX6W1LNyZSY,17875
150
+ bbot/modules/nuclei.py,sha256=65-tgre6a738Z2s3Bo0nxhNcvQ1OuYX8zQS5x7fFk7g,17983
151
151
  bbot/modules/oauth.py,sha256=s-Q6PYJl1OLncGgHzCV0QAzbkewT5zzKCRaa8GidBqc,6720
152
152
  bbot/modules/otx.py,sha256=GYi5GFLKlKuRHPYMqtq42bSulerkSpAWHM6ex5eK7ww,913
153
153
  bbot/modules/output/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -179,9 +179,9 @@ bbot/modules/paramminer_headers.py,sha256=GMErLmTO0w7JRIpJE2VFvRTrjmoux_-jTx3Efa
179
179
  bbot/modules/passivetotal.py,sha256=uGT6c_CUxBNInmClsTg8afIYA2ZykKYYCgjkyzujfHg,1653
180
180
  bbot/modules/pgp.py,sha256=Xu2M9WEIlwTm5-Lv29g7BblI05tD9Dl0XsYSeY6UURs,2065
181
181
  bbot/modules/portfilter.py,sha256=3iu4xqCsHafhVMbA32Mw6K_7Yn576Rz6GxXMevZQEpM,1752
182
- bbot/modules/portscan.py,sha256=AhPsPGD7CdVuw01JurpcmhpxR0Tp_M995k3DRP7wuTw,12944
182
+ bbot/modules/portscan.py,sha256=dmR2H3Wag6vLLIXrwyU_7bvYuLIv3zdJZ2japogwFUU,13076
183
183
  bbot/modules/postman.py,sha256=vo761Nzu3kPBzfCY3KJcvsGEsjImaa7iA2z-LyASBDc,4589
184
- bbot/modules/postman_download.py,sha256=LUB9cP-otkB1HaNACGS5YPwsxnwp1uSo28SpGvmQ60A,3467
184
+ bbot/modules/postman_download.py,sha256=4pvF1ePRqxPxXL1jhI0gMf1CasfVFl-xjwU-U22SFXA,3607
185
185
  bbot/modules/rapiddns.py,sha256=uONESr0B5pv9cSAr7lF4WWV31APUhXyHexvI04rUcyk,787
186
186
  bbot/modules/reflected_parameters.py,sha256=RjS-4C-XC9U-jC9J7AYNqwn6I-O2y3LvTRhB68dpgKI,3281
187
187
  bbot/modules/report/affiliates.py,sha256=vvus8LylqOfP-lfGid0z4FS6MwOpNuRTcSJ9aSnybp4,1713
@@ -208,7 +208,7 @@ bbot/modules/templates/sql.py,sha256=o-CdyyoJvHJdJBKkj3CIGXYxUta4w2AB_2Vr-k7cDDU
208
208
  bbot/modules/templates/subdomain_enum.py,sha256=epyKSly08jqaINV_AMMWbNafIeQjJqvd3aj63KD0Mck,8402
209
209
  bbot/modules/templates/webhook.py,sha256=uGFmcJ81GzGN1UI2k2O7nQF_fyh4ehLDEg2NSXaPnhk,3373
210
210
  bbot/modules/trickest.py,sha256=MRgLW0YiDWzlWdAjyqfPPLFb-a51r-Ffn_dphiJI_gA,1550
211
- bbot/modules/trufflehog.py,sha256=bMO30pE9HAYeSgEQ00xH3kvtKbUoiULeTkT33BiCdR8,8743
211
+ bbot/modules/trufflehog.py,sha256=TA8TVj9he12I9TtVvHdH_WJwDqLK97mATwtkuSi3l2g,8703
212
212
  bbot/modules/url_manipulation.py,sha256=4J3oFkqTSJPPmbKEKAHJg2Q2w4QNKtQhiN03ZJq5VtI,4326
213
213
  bbot/modules/urlscan.py,sha256=-w_3Bm6smyG2GLQyIbnMUkKmeQVauo-V6F4_kJDYG7s,3740
214
214
  bbot/modules/vhost.py,sha256=cirOe0HR4M0TEBN8JdXo2l0s2flc8ZSdxggGm79blT8,5459
@@ -250,15 +250,15 @@ bbot/scanner/__init__.py,sha256=sJ7FoLQ1vwLscH8hju2PEUyGTZ_OwMVvW9b11CrCWdI,89
250
250
  bbot/scanner/dispatcher.py,sha256=_hsIegfUDrt8CUdXqgRvp1J0UwwzqVSDxjQmiviO41c,793
251
251
  bbot/scanner/manager.py,sha256=eyd_0IjnPH3e-tJSOwY-rxauVI6L9Ltr3pWmpPSO5Jc,11019
252
252
  bbot/scanner/preset/__init__.py,sha256=If_YqKILIxjlaJvf8lFc5zQTHDkounLdC8x_72N-V10,49
253
- bbot/scanner/preset/args.py,sha256=yADXhmmLWBODsEqw1NbEBh8UWltUEoB--2S7myHTwAQ,19212
253
+ bbot/scanner/preset/args.py,sha256=Oto4sO8E9hKeQn6Fp8ua_WB3xvYI97GgnBFg5f4jh0Y,19547
254
254
  bbot/scanner/preset/conditions.py,sha256=hFL9cSIWGEsv2TfM5UGurf0c91cyaM8egb5IngBmIjA,1569
255
255
  bbot/scanner/preset/environ.py,sha256=9KbEOLWkUdoAf5Ez_2A1NNm6QduQElbnNnrPi6VDhZs,4731
256
256
  bbot/scanner/preset/path.py,sha256=X32-ZUmL7taIv37VKF1KfmeiK9fjuQOE7pWUTEbPK8c,2483
257
- bbot/scanner/preset/preset.py,sha256=t9Aa3n3mpGVZ274z2hXaGsdpRZe0BSGuF3WTM6Dinj8,40817
258
- bbot/scanner/scanner.py,sha256=2slrxEfcBjSZgZ1jmQ34a4X6aMli80K85_eY8ti7zZQ,55471
257
+ bbot/scanner/preset/preset.py,sha256=G_aMMI33d2OlzNUwjfi5ddJdxa8nK0oF5HrYAsuregU,40708
258
+ bbot/scanner/scanner.py,sha256=6TgnGMa_Zl4Wm6fKxujGDHGrNC6ad1pexUDCZ7xRy5I,55320
259
259
  bbot/scanner/stats.py,sha256=re93sArKXZSiD0Owgqk2J3Kdvfm3RL4Y9Qy_VOcaVk8,3623
260
260
  bbot/scanner/target.py,sha256=lI0Tn5prQiPiJE3WW-ZLx_l6EFqzAVabtyL-nfXJ8cE,10636
261
- bbot/scripts/docs.py,sha256=JYdn9jLnnxmHM3hQc1C3tjxGs6hgFhqouUS3WtYN0q0,11004
261
+ bbot/scripts/docs.py,sha256=aYAHlcHtMAhM-XGTDiSpzccnX1dh0Xi_WxmC2bgylQ4,11373
262
262
  bbot/test/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
263
263
  bbot/test/bbot_fixtures.py,sha256=XrCQDLVe80BG3QTUDnXb0y-cWnBpJJoRh2Z3J3xJn_w,9961
264
264
  bbot/test/conftest.py,sha256=OacpJ98g00HqCoHpEnuzzMK47LkbZdJWr25Pm0SbTM0,11783
@@ -289,7 +289,7 @@ bbot/test/test_step_1/test_modules_basic.py,sha256=ELpGlsthSq8HaxB5My8-ESVHqMxqd
289
289
  bbot/test/test_step_1/test_presets.py,sha256=HnJhKwDnVh9Y6adgxqe85677rWpnFil_WS5GjX21ZvM,40959
290
290
  bbot/test/test_step_1/test_python_api.py,sha256=Fk5bxEsPSjsMZ_CcRMTJft8I48EizwHJivG9Fy4jIu0,5502
291
291
  bbot/test/test_step_1/test_regexes.py,sha256=wSx_e6hgHuBh95igL_fauWKK4a1xXujs9TtyLBaMwRM,14636
292
- bbot/test/test_step_1/test_scan.py,sha256=yZs-bZVLd7Ao5uoNzoOI_F8gBEbGdeVXIAjyoGoNhe0,8875
292
+ bbot/test/test_step_1/test_scan.py,sha256=jGySAMGb0gXirJpDniX6SSm4TbpHYrkGHcDPpJRGHQg,10676
293
293
  bbot/test/test_step_1/test_scope.py,sha256=S2nssENKJKCvgXUMyU8MFQmXHeUIz0C_sbWGkdYti2A,3063
294
294
  bbot/test/test_step_1/test_target.py,sha256=4Xz6Fns_6wa2O3AXDBvd7W04LCfZSCiit2lezQJicTI,19472
295
295
  bbot/test/test_step_1/test_web.py,sha256=qzMb5v_1l6fK6SvJZoHpBI3Zb7iaHU_VnenQ8UQIK-4,19637
@@ -301,7 +301,7 @@ bbot/test/test_step_2/module_tests/test_module_affiliates.py,sha256=d6uAzb_MF4oN
301
301
  bbot/test/test_step_2/module_tests/test_module_aggregate.py,sha256=hjxbMxAEFhS7W8RamBrM1t6T-tsLHq95MmQVfrYsock,487
302
302
  bbot/test/test_step_2/module_tests/test_module_ajaxpro.py,sha256=S2pFV0TgOJ01SMHnIxcoBkGZ8SAaQVY9o32DOFoZ1u4,3857
303
303
  bbot/test/test_step_2/module_tests/test_module_anubisdb.py,sha256=y_GMm20Fy4z9L0fN2dYOExaSsi8Z9PwMKnAjSsYhBk8,545
304
- bbot/test/test_step_2/module_tests/test_module_apkpure.py,sha256=8KM-JCir0doUupwN3JwV5OPaPE_Jq_hz2-96-ZKGpRI,2823
304
+ bbot/test/test_step_2/module_tests/test_module_apkpure.py,sha256=-G4hoRPFhNH-rj6MvuhqlwiXHqBqHgfFGYxbzoyKz2s,2983
305
305
  bbot/test/test_step_2/module_tests/test_module_asn.py,sha256=qIbitSAEAmYyxhpxvdFDsQrHaaxfgKsFox9Q3jTmvgI,10616
306
306
  bbot/test/test_step_2/module_tests/test_module_asset_inventory.py,sha256=NEMSPBlczpA5NbQpIcRBpRXpVnfS9lmSS1U7eJmIXAU,3878
307
307
  bbot/test/test_step_2/module_tests/test_module_azure_realm.py,sha256=gPRvGA9RHsAcYlHxQG0lHZOYolyzbe6L-ALGIvW-Mg0,1288
@@ -315,7 +315,7 @@ bbot/test/test_step_2/module_tests/test_module_binaryedge.py,sha256=tK1fv4o2JOxa
315
315
  bbot/test/test_step_2/module_tests/test_module_bucket_amazon.py,sha256=bKTnrR3FfCaAXZTWJh2m4bm-Pa1xiJV9lY2N_waaq2s,4002
316
316
  bbot/test/test_step_2/module_tests/test_module_bucket_azure.py,sha256=CUK1OHGhA-piIzPG15UNiMdaK1oSSCwCv_WPcCvC4VQ,2112
317
317
  bbot/test/test_step_2/module_tests/test_module_bucket_digitalocean.py,sha256=EFzScFloY6DQVD7MbvQOfsSp_8tApV4epKFm9OP1_ZM,907
318
- bbot/test/test_step_2/module_tests/test_module_bucket_file_enum.py,sha256=aOgtrsb32nnTZBvX1tf6Fvfjc-GvuxA8Tu7LGq2oDJo,2301
318
+ bbot/test/test_step_2/module_tests/test_module_bucket_file_enum.py,sha256=7n7eFWQJanZjyammwBDM9Jmt5kJ8AXn9eS4fmKEBqm4,2463
319
319
  bbot/test/test_step_2/module_tests/test_module_bucket_firebase.py,sha256=gM3h1staY3tEHF2l9cYgRhaVwEg7ykfo4E0mvhqTA0g,506
320
320
  bbot/test/test_step_2/module_tests/test_module_bucket_google.py,sha256=wXROpF9TSQVOa8cGTOo8k9uDEj7H5pNAcppj4WR3qnY,1312
321
321
  bbot/test/test_step_2/module_tests/test_module_bufferoverrun.py,sha256=6pyJ0dbx8u0BPVXyiqdDPgECLGfIGkWPHZ35tbBzoPg,1582
@@ -342,7 +342,7 @@ bbot/test/test_step_2/module_tests/test_module_dnscommonsrv.py,sha256=_dqcgVQAc1
342
342
  bbot/test/test_step_2/module_tests/test_module_dnsdumpster.py,sha256=p--1JaDb7PjiAYRQiAQ_qYBQAQgkBzB1iLnDp6B0UCk,59745
343
343
  bbot/test/test_step_2/module_tests/test_module_dnsresolve.py,sha256=15LEcggP_eVYFQdMO1zHTvoGc6n8IaUjsQDmX0sZS4o,2077
344
344
  bbot/test/test_step_2/module_tests/test_module_dnstlsrpt.py,sha256=8xXSFo0vwKfehIqgF41tbEkL1vbp6RIB8kiO8TSH4NU,2648
345
- bbot/test/test_step_2/module_tests/test_module_docker_pull.py,sha256=-JSAo51dS3Ie9RaLBcWK0kfbg8bCPr7mohpFGAwOKPQ,27988
345
+ bbot/test/test_step_2/module_tests/test_module_docker_pull.py,sha256=SKc43IKzwoi74qaY8felJXnrgNVXrXRQlg6BtrSjyAc,28151
346
346
  bbot/test/test_step_2/module_tests/test_module_dockerhub.py,sha256=9T8CFcFP32MOppUmSVNBUSifnk2kMONqzW_7vvvKdpk,3907
347
347
  bbot/test/test_step_2/module_tests/test_module_dotnetnuke.py,sha256=Q7M3hrbEwOuORZXPS-pIGFTRzB2-g4cEvGtsEcTp7t8,8049
348
348
  bbot/test/test_step_2/module_tests/test_module_emailformat.py,sha256=cKxBPnEQ4AiRKV_-hSYEE6756ypst3hi6MN0L5RTukY,461
@@ -351,13 +351,13 @@ bbot/test/test_step_2/module_tests/test_module_excavate.py,sha256=hoVQnZYb_tI1Fl
351
351
  bbot/test/test_step_2/module_tests/test_module_extractous.py,sha256=PuTE5rkEIFPwU9lhCYpTgNSkrVjcXm8PClbfOkfRS84,17973
352
352
  bbot/test/test_step_2/module_tests/test_module_ffuf.py,sha256=z8ihAM1WYss7QGXIjbi67cekg8iOemDjaM8YR9_qSEs,4100
353
353
  bbot/test/test_step_2/module_tests/test_module_ffuf_shortnames.py,sha256=0-a9J-gq8bUtmxl_-QPVidwZ9KkCvgvoG30Ot3a8lqM,8406
354
- bbot/test/test_step_2/module_tests/test_module_filedownload.py,sha256=ZLPlBVs8CMWofLZAl63zdYMryVdYXykoaxE4jBGED8I,4304
354
+ bbot/test/test_step_2/module_tests/test_module_filedownload.py,sha256=Fd_5DKA0VaHyCR6qS5WPS9CoyPKFeBTswhAwM5RsU-c,4473
355
355
  bbot/test/test_step_2/module_tests/test_module_fingerprintx.py,sha256=nU3jxbkGcmPYiSzc6thJhNvjAFb4qVxcR7rkOAvjB18,445
356
356
  bbot/test/test_step_2/module_tests/test_module_fullhunt.py,sha256=NblfNHQrE82j-cESvm66hpN-ooKZwR1kEwJDTk_BXac,1946
357
357
  bbot/test/test_step_2/module_tests/test_module_generic_ssrf.py,sha256=ZhfZpH0QTl6_YftGoZzZk6_2x0ZDnWjZ7vNZMTibBHw,3228
358
358
  bbot/test/test_step_2/module_tests/test_module_git.py,sha256=gyBS3vZUWAyatGlcY26mGOYeqXSqJA5pbhJWgTmLqNo,1656
359
- bbot/test/test_step_2/module_tests/test_module_git_clone.py,sha256=Mo0Q7bCXcrkGWJc3-u5y4sdfC13ei-qj79aKvEbnkk4,13198
360
- bbot/test/test_step_2/module_tests/test_module_gitdumper.py,sha256=ya_eQUQk0344G7iqBYMls2z5H-bYM87rydbz-ACR2Ng,17461
359
+ bbot/test/test_step_2/module_tests/test_module_git_clone.py,sha256=z97IZLTVaa3aJ9O3NDP3MaOnmDocL-kT8kT-1rMAqn0,13318
360
+ bbot/test/test_step_2/module_tests/test_module_gitdumper.py,sha256=bsSly-OOyDuFffVmwJpm7aEafrSHgLAk_VsJ69S3LUo,17616
361
361
  bbot/test/test_step_2/module_tests/test_module_github_codesearch.py,sha256=M50xBiGG2EuPGXDJU6uFsSUE-fhqZl3CzYtNdszW7LM,4735
362
362
  bbot/test/test_step_2/module_tests/test_module_github_org.py,sha256=5tKO6NH4TPBeIdeTf7Bz9PUZ1pcvKsjrG0nFhc3YgT0,25458
363
363
  bbot/test/test_step_2/module_tests/test_module_github_usersearch.py,sha256=IIQ0tYZjQN8_L8u_N4m8Nz3kbB4IyBp95tYCPcQeScg,5264
@@ -395,10 +395,10 @@ bbot/test/test_step_2/module_tests/test_module_paramminer_headers.py,sha256=8hRU
395
395
  bbot/test/test_step_2/module_tests/test_module_passivetotal.py,sha256=fTGQECQ0OzcwiH64-0igFRKO-rs3kXScivZord_oWWU,1120
396
396
  bbot/test/test_step_2/module_tests/test_module_pgp.py,sha256=_T-kmpr5F0cJHl4_mpfhxK3aj0hH3UxDLeV1iByanJk,1607
397
397
  bbot/test/test_step_2/module_tests/test_module_portfilter.py,sha256=gOEy1XYtTJNGvTH6o3NNSOXXPdste462BdQvayOwzVs,2012
398
- bbot/test/test_step_2/module_tests/test_module_portscan.py,sha256=TJtutIORCNu-3Wm1zCqtzPVC0OXUICnY4YL1toJ57yk,7539
398
+ bbot/test/test_step_2/module_tests/test_module_portscan.py,sha256=I42EhBN736JShaek5TvE-NHD8X3hcVxXLHhlJcqquYQ,7539
399
399
  bbot/test/test_step_2/module_tests/test_module_postgres.py,sha256=bNHzDvPs5AkoA_ho7s50bFaF5qzV7KL3DplhOA1ZYa4,2688
400
400
  bbot/test/test_step_2/module_tests/test_module_postman.py,sha256=7SxZi39dJhnwyg4IVf5M8VNdQU_zaPAufTEw60rgCkg,22311
401
- bbot/test/test_step_2/module_tests/test_module_postman_download.py,sha256=0mevxk5AMrBmQ2vvWzokc15fdzxOxMM5Rqw-CDIZLKs,14079
401
+ bbot/test/test_step_2/module_tests/test_module_postman_download.py,sha256=u3lF7Uve8riHNCj9XEFrRBONQHUzccsfq-qEkwQ51wE,14225
402
402
  bbot/test/test_step_2/module_tests/test_module_python.py,sha256=6UQVXGJ1ugfNbt9l_nN0q5FVxNWlpq6j0sZcB0Nh_Pg,184
403
403
  bbot/test/test_step_2/module_tests/test_module_rapiddns.py,sha256=zXHNLnUjLO22yRwrDFCZ40sRTmFVZEj9q_dyK8w1TYM,4441
404
404
  bbot/test/test_step_2/module_tests/test_module_reflected_parameters.py,sha256=4cY8yK9iImB1O68pi1lACcPEtNQ9-sud9Xl16fYB8cU,9003
@@ -423,7 +423,7 @@ bbot/test/test_step_2/module_tests/test_module_subdomains.py,sha256=r1zCmw5ZZ_0w
423
423
  bbot/test/test_step_2/module_tests/test_module_teams.py,sha256=r91ZZxhj3pEhKnjr1jGwhcqOPXTqNJNupC1CDKccfH8,1638
424
424
  bbot/test/test_step_2/module_tests/test_module_telerik.py,sha256=vGORDSRU1S1hkLlHO3KOdoAYLqWcrPpvVGxadTGl5y8,11099
425
425
  bbot/test/test_step_2/module_tests/test_module_trickest.py,sha256=6mTYH6fIah-WbKnFI-_WZBwRdKFi-oeWyVtl1n0nVAU,1630
426
- bbot/test/test_step_2/module_tests/test_module_trufflehog.py,sha256=y4TyPSrsQ9al4RG_F1y8zttX-nUtfGEy5L7zCnX37XU,95785
426
+ bbot/test/test_step_2/module_tests/test_module_trufflehog.py,sha256=lXX2KYh5s1FVpivth43Nj3MLfMkiksrooEne_DAL_qg,96796
427
427
  bbot/test/test_step_2/module_tests/test_module_txt.py,sha256=R-EBfEZM0jwY2yuVyfYhoccDOl0Y2uQZSkXQ1HyinUA,247
428
428
  bbot/test/test_step_2/module_tests/test_module_unarchive.py,sha256=--p2kpnyfbABuJI5qmDbD-K_D5e4Icvq5VzEyp6AWVE,10676
429
429
  bbot/test/test_step_2/module_tests/test_module_url_manipulation.py,sha256=aP3nK2TQQOjk0ZeuHhHYfZm_e37qrrXbnufd7m-QeJU,1144
@@ -452,8 +452,8 @@ bbot/wordlists/raft-small-extensions-lowercase_CLEANED.txt,sha256=ZSIVebs7ptMvHx
452
452
  bbot/wordlists/top_open_ports_nmap.txt,sha256=LmdFYkfapSxn1pVuQC2LkOIY2hMLgG-Xts7DVtYzweM,42727
453
453
  bbot/wordlists/valid_url_schemes.txt,sha256=0B_VAr9Dv7aYhwi6JSBDU-3M76vNtzN0qEC_RNLo7HE,3310
454
454
  bbot/wordlists/wordninja_dns.txt.gz,sha256=DYHvvfW0TvzrVwyprqODAk4tGOxv5ezNmCPSdPuDUnQ,570241
455
- bbot-2.4.2.6677rc0.dist-info/LICENSE,sha256=GzeCzK17hhQQDNow0_r0L8OfLpeTKQjFQwBQU7ZUymg,32473
456
- bbot-2.4.2.6677rc0.dist-info/METADATA,sha256=C1S2EK8cW4u3b8k4fsrQGrMW-12R47Oa4yNzgqUHncg,18308
457
- bbot-2.4.2.6677rc0.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
458
- bbot-2.4.2.6677rc0.dist-info/entry_points.txt,sha256=cWjvcU_lLrzzJgjcjF7yeGuRA_eDS8pQ-kmPUAyOBfo,38
459
- bbot-2.4.2.6677rc0.dist-info/RECORD,,
455
+ bbot-2.4.2.6706rc0.dist-info/LICENSE,sha256=GzeCzK17hhQQDNow0_r0L8OfLpeTKQjFQwBQU7ZUymg,32473
456
+ bbot-2.4.2.6706rc0.dist-info/METADATA,sha256=7rjBEj5pcTefGTU6PY90E3IBhUd5zt-fnQIWvZGoJto,18308
457
+ bbot-2.4.2.6706rc0.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
458
+ bbot-2.4.2.6706rc0.dist-info/entry_points.txt,sha256=cWjvcU_lLrzzJgjcjF7yeGuRA_eDS8pQ-kmPUAyOBfo,38
459
+ bbot-2.4.2.6706rc0.dist-info/RECORD,,