bbot 2.2.0.5242rc0__py3-none-any.whl → 2.2.0.5263rc0__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.

bbot/__init__.py CHANGED
@@ -1,4 +1,4 @@
1
1
  # version placeholder (replaced by poetry-dynamic-versioning)
2
- __version__ = "v2.2.0.5242rc"
2
+ __version__ = "v2.2.0.5263rc"
3
3
 
4
4
  from .scanner import Scanner, Preset
@@ -1,3 +1,4 @@
1
+ import os
1
2
  import sys
2
3
  import atexit
3
4
  import logging
@@ -9,6 +10,7 @@ from contextlib import suppress
9
10
 
10
11
  from ..helpers.misc import mkdir, error_and_exit
11
12
  from ...logger import colorize, loglevel_mapping
13
+ from ..multiprocess import SHARED_INTERPRETER_STATE
12
14
 
13
15
 
14
16
  debug_format = logging.Formatter("%(asctime)s [%(levelname)s] %(name)s %(filename)s:%(lineno)s %(message)s")
@@ -65,8 +67,9 @@ class BBOTLogger:
65
67
 
66
68
  self.listener = None
67
69
 
68
- self.process_name = multiprocessing.current_process().name
69
- if self.process_name == "MainProcess":
70
+ # if we haven't set up logging yet, do it now
71
+ if not "_BBOT_LOGGING_SETUP" in os.environ:
72
+ os.environ["_BBOT_LOGGING_SETUP"] = "1"
70
73
  self.queue = multiprocessing.Queue()
71
74
  self.setup_queue_handler()
72
75
  # Start the QueueListener
@@ -113,7 +116,7 @@ class BBOTLogger:
113
116
 
114
117
  self.core_logger.setLevel(log_level)
115
118
  # disable asyncio logging for child processes
116
- if self.process_name != "MainProcess":
119
+ if not SHARED_INTERPRETER_STATE.is_main_process:
117
120
  logging.getLogger("asyncio").setLevel(logging.ERROR)
118
121
 
119
122
  def addLoggingLevel(self, levelName, levelNum, methodName=None):
bbot/core/core.py CHANGED
@@ -6,6 +6,7 @@ from contextlib import suppress
6
6
  from omegaconf import OmegaConf
7
7
 
8
8
  from bbot.errors import BBOTError
9
+ from .multiprocess import SHARED_INTERPRETER_STATE
9
10
 
10
11
 
11
12
  DEFAULT_CONFIG = None
@@ -41,9 +42,23 @@ class BBOTCore:
41
42
  self.logger
42
43
  self.log = logging.getLogger("bbot.core")
43
44
 
45
+ self._prep_multiprocessing()
46
+
47
+ def _prep_multiprocessing(self):
44
48
  import multiprocessing
49
+ from .helpers.process import BBOTProcess
50
+
51
+ if SHARED_INTERPRETER_STATE.is_main_process:
52
+ # if this is the main bbot process, set the logger and queue for the first time
53
+ from functools import partialmethod
45
54
 
46
- self.process_name = multiprocessing.current_process().name
55
+ BBOTProcess.__init__ = partialmethod(
56
+ BBOTProcess.__init__, log_level=self.logger.log_level, log_queue=self.logger.queue
57
+ )
58
+
59
+ # this makes our process class the default for process pools, etc.
60
+ mp_context = multiprocessing.get_context("spawn")
61
+ mp_context.Process = BBOTProcess
47
62
 
48
63
  @property
49
64
  def home(self):
@@ -187,12 +202,14 @@ class BBOTCore:
187
202
  if os.environ.get("BBOT_TESTING", "") == "True":
188
203
  process = self.create_thread(*args, **kwargs)
189
204
  else:
190
- if self.process_name == "MainProcess":
205
+ if SHARED_INTERPRETER_STATE.is_scan_process:
191
206
  from .helpers.process import BBOTProcess
192
207
 
193
208
  process = BBOTProcess(*args, **kwargs)
194
209
  else:
195
- raise BBOTError(f"Tried to start server from process {self.process_name}")
210
+ import multiprocessing
211
+
212
+ raise BBOTError(f"Tried to start server from process {multiprocessing.current_process().name}")
196
213
  process.daemon = True
197
214
  return process
198
215
 
bbot/core/engine.py CHANGED
@@ -10,6 +10,7 @@ import traceback
10
10
  import contextlib
11
11
  import contextvars
12
12
  import zmq.asyncio
13
+ import multiprocessing
13
14
  from pathlib import Path
14
15
  from concurrent.futures import CancelledError
15
16
  from contextlib import asynccontextmanager, suppress
@@ -17,6 +18,7 @@ from contextlib import asynccontextmanager, suppress
17
18
  from bbot.core import CORE
18
19
  from bbot.errors import BBOTEngineError
19
20
  from bbot.core.helpers.async_helpers import get_event_loop
21
+ from bbot.core.multiprocess import SHARED_INTERPRETER_STATE
20
22
  from bbot.core.helpers.misc import rand_string, in_exception_chain
21
23
 
22
24
 
@@ -264,10 +266,8 @@ class EngineClient(EngineBase):
264
266
  return [s for s in self.CMDS if isinstance(s, str)]
265
267
 
266
268
  def start_server(self):
267
- import multiprocessing
268
-
269
269
  process_name = multiprocessing.current_process().name
270
- if process_name == "MainProcess":
270
+ if SHARED_INTERPRETER_STATE.is_scan_process:
271
271
  kwargs = dict(self.server_kwargs)
272
272
  # if we're in tests, we use a single event loop to avoid weird race conditions
273
273
  # this allows us to more easily mock http, etc.
bbot/core/event/base.py CHANGED
@@ -1003,13 +1003,15 @@ class ClosestHostEvent(DictHostEvent):
1003
1003
  if parent_url is not None:
1004
1004
  self.data["url"] = parent_url.geturl()
1005
1005
  # inherit closest path
1006
- if not "path" in self.data and isinstance(parent.data, dict):
1006
+ if not "path" in self.data and isinstance(parent.data, dict) and not parent.type == "HTTP_RESPONSE":
1007
1007
  parent_path = parent.data.get("path", None)
1008
1008
  if parent_path is not None:
1009
1009
  self.data["path"] = parent_path
1010
1010
  # inherit closest host
1011
1011
  if parent.host:
1012
1012
  self.data["host"] = str(parent.host)
1013
+ # we do this to refresh the hash
1014
+ self.data = self.data
1013
1015
  break
1014
1016
  # die if we still haven't found a host
1015
1017
  if not self.host:
@@ -1559,6 +1561,8 @@ class FILESYSTEM(DictPathEvent):
1559
1561
  self.add_tag("compressed")
1560
1562
  self.add_tag(f"{compression}-archive")
1561
1563
  self.data["compression"] = compression
1564
+ # refresh hash
1565
+ self.data = self.data
1562
1566
 
1563
1567
 
1564
1568
  class RAW_DNS_RECORD(DictHostEvent, DnsEvent):
@@ -1639,23 +1643,23 @@ def make_event(
1639
1643
  tags = set(tags)
1640
1644
 
1641
1645
  if is_event(data):
1642
- data = copy(data)
1643
- if scan is not None and not data.scan:
1644
- data.scan = scan
1645
- if scans is not None and not data.scans:
1646
- data.scans = scans
1646
+ event = copy(data)
1647
+ if scan is not None and not event.scan:
1648
+ event.scan = scan
1649
+ if scans is not None and not event.scans:
1650
+ event.scans = scans
1647
1651
  if module is not None:
1648
- data.module = module
1652
+ event.module = module
1649
1653
  if parent is not None:
1650
- data.parent = parent
1654
+ event.parent = parent
1651
1655
  if context is not None:
1652
- data.discovery_context = context
1656
+ event.discovery_context = context
1653
1657
  if internal == True:
1654
- data.internal = True
1658
+ event.internal = True
1655
1659
  if tags:
1656
- data.tags = tags.union(data.tags)
1660
+ event.tags = tags.union(event.tags)
1657
1661
  event_type = data.type
1658
- return data
1662
+ return event
1659
1663
  else:
1660
1664
  if event_type is None:
1661
1665
  event_type, data = get_event_type(data)
@@ -210,9 +210,10 @@ async def _write_proc_line(proc, chunk):
210
210
  return True
211
211
  except Exception as e:
212
212
  proc_args = [str(s) for s in getattr(proc, "args", [])]
213
- command = " ".join(proc_args)
214
- log.warning(f"Error writing line to stdin for command: {command}: {e}")
215
- log.trace(traceback.format_exc())
213
+ command = " ".join(proc_args).strip()
214
+ if command:
215
+ log.warning(f"Error writing line to stdin for command: {command}: {e}")
216
+ log.trace(traceback.format_exc())
216
217
  return False
217
218
 
218
219
 
@@ -44,6 +44,11 @@ class DepsInstaller:
44
44
  self.parent_helper.mkdir(self.command_status)
45
45
  self.setup_status = self.read_setup_status()
46
46
 
47
+ # make sure we're using a minimal git config
48
+ self.minimal_git_config = self.data_dir / "minimal_git.config"
49
+ self.minimal_git_config.touch()
50
+ os.environ["GIT_CONFIG_GLOBAL"] = str(self.minimal_git_config)
51
+
47
52
  self.deps_behavior = self.parent_helper.config.get("deps_behavior", "abort_on_failure").lower()
48
53
  self.ansible_debug = self.core.logger.log_level <= logging.DEBUG
49
54
  self.venv = ""
@@ -1,15 +1,11 @@
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
10
 
15
11
  default_name = "default bbot thread"
@@ -57,17 +53,3 @@ class BBOTProcess(SpawnProcess):
57
53
  if not in_exception_chain(e, (KeyboardInterrupt,)):
58
54
  log.warning(f"Error in {self.name}: {e}")
59
55
  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
@@ -0,0 +1,58 @@
1
+ import os
2
+ import atexit
3
+ from contextlib import suppress
4
+
5
+
6
+ class SharedInterpreterState:
7
+ """
8
+ A class to track the primary BBOT process.
9
+
10
+ Used to prevent spawning multiple unwanted processes with multiprocessing.
11
+ """
12
+
13
+ def __init__(self):
14
+ self.main_process_var_name = "_BBOT_MAIN_PID"
15
+ self.scan_process_var_name = "_BBOT_SCAN_PID"
16
+ atexit.register(self.cleanup)
17
+
18
+ @property
19
+ def is_main_process(self):
20
+ is_main_process = self.main_pid == os.getpid()
21
+ return is_main_process
22
+
23
+ @property
24
+ def is_scan_process(self):
25
+ is_scan_process = os.getpid() == self.scan_pid
26
+ return is_scan_process
27
+
28
+ @property
29
+ def main_pid(self):
30
+ main_pid = int(os.environ.get(self.main_process_var_name, 0))
31
+ if main_pid == 0:
32
+ main_pid = os.getpid()
33
+ # if main PID is not set, set it to the current PID
34
+ os.environ[self.main_process_var_name] = str(main_pid)
35
+ return main_pid
36
+
37
+ @property
38
+ def scan_pid(self):
39
+ scan_pid = int(os.environ.get(self.scan_process_var_name, 0))
40
+ if scan_pid == 0:
41
+ scan_pid = os.getpid()
42
+ # if scan PID is not set, set it to the current PID
43
+ os.environ[self.scan_process_var_name] = str(scan_pid)
44
+ return scan_pid
45
+
46
+ def update_scan_pid(self):
47
+ os.environ[self.scan_process_var_name] = str(os.getpid())
48
+
49
+ def cleanup(self):
50
+ with suppress(Exception):
51
+ if self.is_main_process:
52
+ with suppress(KeyError):
53
+ del os.environ[self.main_process_var_name]
54
+ with suppress(KeyError):
55
+ del os.environ[self.scan_process_var_name]
56
+
57
+
58
+ SHARED_INTERPRETER_STATE = SharedInterpreterState()
bbot/modules/censys.py CHANGED
@@ -15,25 +15,21 @@ class censys(subdomain_enum_apikey):
15
15
  "author": "@TheTechromancer",
16
16
  "auth_required": True,
17
17
  }
18
- options = {"api_id": "", "api_secret": "", "max_pages": 5}
18
+ options = {"api_key": "", "max_pages": 5}
19
19
  options_desc = {
20
- "api_id": "Censys.io API ID",
21
- "api_secret": "Censys.io API Secret",
20
+ "api_key": "Censys.io API Key in the format of 'key:secret'",
22
21
  "max_pages": "Maximum number of pages to fetch (100 results per page)",
23
22
  }
24
23
 
25
24
  base_url = "https://search.censys.io/api"
26
25
 
27
26
  async def setup(self):
28
- self.api_id = self.config.get("api_id", "")
29
- self.api_secret = self.config.get("api_secret", "")
30
- self.auth = (self.api_id, self.api_secret)
31
27
  self.max_pages = self.config.get("max_pages", 5)
32
28
  return await super().setup()
33
29
 
34
30
  async def ping(self):
35
31
  url = f"{self.base_url}/v1/account"
36
- resp = await self.helpers.request(url, auth=self.auth)
32
+ resp = await self.api_request(url)
37
33
  d = resp.json()
38
34
  assert isinstance(d, dict), f"Invalid response from {url}: {resp}"
39
35
  quota = d.get("quota", {})
@@ -41,6 +37,11 @@ class censys(subdomain_enum_apikey):
41
37
  allowance = int(quota.get("allowance", 0))
42
38
  assert used < allowance, "No quota remaining"
43
39
 
40
+ def prepare_api_request(self, url, kwargs):
41
+ api_id, api_secret = self.api_key.split(":", 1)
42
+ kwargs["auth"] = (api_id, api_secret)
43
+ return url, kwargs
44
+
44
45
  async def query(self, query):
45
46
  results = set()
46
47
  cursor = ""
@@ -52,11 +53,10 @@ class censys(subdomain_enum_apikey):
52
53
  }
53
54
  if cursor:
54
55
  json_data.update({"cursor": cursor})
55
- resp = await self.helpers.request(
56
+ resp = await self.api_request(
56
57
  url,
57
58
  method="POST",
58
59
  json=json_data,
59
- auth=self.auth,
60
60
  )
61
61
 
62
62
  if resp is None:
@@ -96,7 +96,3 @@ class censys(subdomain_enum_apikey):
96
96
  break
97
97
 
98
98
  return results
99
-
100
- @property
101
- def auth_secret(self):
102
- return self.api_id and self.api_secret
@@ -79,7 +79,16 @@ class DNSResolve(BaseInterceptModule):
79
79
  await self.resolve_event(main_host_event, types=non_minimal_rdtypes)
80
80
  # check for wildcards if the event is within the scan's search distance
81
81
  if new_event and main_host_event.scope_distance <= self.scan.scope_search_distance:
82
- await self.handle_wildcard_event(main_host_event)
82
+ event_data_changed = await self.handle_wildcard_event(main_host_event)
83
+ if event_data_changed:
84
+ # since data has changed, we check again whether it's a duplicate
85
+ if self.scan.ingress_module.is_incoming_duplicate(event, add=True):
86
+ if not event._graph_important:
87
+ return False, "event was already emitted by its module"
88
+ else:
89
+ self.debug(
90
+ f"Event {event} was already emitted by its module, but it's graph-important so it gets a pass"
91
+ )
83
92
 
84
93
  # if there weren't any DNS children and it's not an IP address, tag as unresolved
85
94
  if not main_host_event.raw_dns_records and not event_is_ip:
@@ -152,6 +161,8 @@ class DNSResolve(BaseInterceptModule):
152
161
  if wildcard_data != event.data:
153
162
  self.debug(f'Wildcard detected, changing event.data "{event.data}" --> "{wildcard_data}"')
154
163
  event.data = wildcard_data
164
+ return True
165
+ return False
155
166
 
156
167
  async def emit_dns_children(self, event):
157
168
  for rdtype, children in event.dns_children.items():
@@ -11,36 +11,34 @@ class passivetotal(subdomain_enum_apikey):
11
11
  "author": "@TheTechromancer",
12
12
  "auth_required": True,
13
13
  }
14
- options = {"username": "", "api_key": ""}
15
- options_desc = {"username": "RiskIQ Username", "api_key": "RiskIQ API Key"}
14
+ options = {"api_key": ""}
15
+ options_desc = {"api_key": "PassiveTotal API Key in the format of 'username:api_key'"}
16
16
 
17
17
  base_url = "https://api.passivetotal.org/v2"
18
18
 
19
19
  async def setup(self):
20
- self.username = self.config.get("username", "")
21
- self.api_key = self.config.get("api_key", "")
22
- self.auth = (self.username, self.api_key)
23
20
  return await super().setup()
24
21
 
25
22
  async def ping(self):
26
23
  url = f"{self.base_url}/account/quota"
27
- j = (await self.api_request(url, auth=self.auth)).json()
24
+ j = (await self.api_request(url)).json()
28
25
  limit = j["user"]["limits"]["search_api"]
29
26
  used = j["user"]["counts"]["search_api"]
30
27
  assert used < limit, "No quota remaining"
31
28
 
29
+ def prepare_api_request(self, url, kwargs):
30
+ api_username, api_key = self.api_key.split(":", 1)
31
+ kwargs["auth"] = (api_username, api_key)
32
+ return url, kwargs
33
+
32
34
  async def abort_if(self, event):
33
35
  # RiskIQ is famous for their junk data
34
36
  return await super().abort_if(event) or "unresolved" in event.tags
35
37
 
36
38
  async def request_url(self, query):
37
39
  url = f"{self.base_url}/enrichment/subdomains?query={self.helpers.quote(query)}"
38
- return await self.api_request(url, auth=self.auth)
40
+ return await self.api_request(url)
39
41
 
40
42
  def parse_results(self, r, query):
41
43
  for subdomain in r.json().get("subdomains", []):
42
44
  yield f"{subdomain}.{query}"
43
-
44
- @property
45
- def auth_secret(self):
46
- return self.username and self.api_key
bbot/scanner/scanner.py CHANGED
@@ -10,11 +10,11 @@ from datetime import datetime
10
10
  from collections import OrderedDict
11
11
 
12
12
  from bbot import __version__
13
-
14
13
  from bbot.core.event import make_event
15
14
  from .manager import ScanIngress, ScanEgress
16
15
  from bbot.core.helpers.misc import sha1, rand_string
17
16
  from bbot.core.helpers.names_generator import random_name
17
+ from bbot.core.multiprocess import SHARED_INTERPRETER_STATE
18
18
  from bbot.core.helpers.async_helpers import async_to_sync_gen
19
19
  from bbot.errors import BBOTError, ScanError, ValidationError
20
20
 
@@ -259,6 +259,9 @@ class Scanner:
259
259
  Creates the scan's output folder, loads its modules, and calls their .setup() methods.
260
260
  """
261
261
 
262
+ # update the master PID
263
+ SHARED_INTERPRETER_STATE.update_scan_pid()
264
+
262
265
  self.helpers.mkdir(self.home)
263
266
  if not self._prepped:
264
267
  # save scan preset
bbot/test/conftest.py CHANGED
@@ -16,7 +16,6 @@ from bbot.core.helpers.interactsh import server_list as interactsh_servers
16
16
  # silence stdout + trace
17
17
  root_logger = logging.getLogger()
18
18
  pytest_debug_file = Path(__file__).parent.parent.parent / "pytest_debug.log"
19
- print(f"pytest_debug_file: {pytest_debug_file}")
20
19
  debug_handler = logging.FileHandler(pytest_debug_file)
21
20
  debug_handler.setLevel(logging.DEBUG)
22
21
  debug_format = logging.Formatter("%(asctime)s [%(levelname)s] %(name)s %(filename)s:%(lineno)s %(message)s")
@@ -0,0 +1,17 @@
1
+ from typing import List
2
+ from bbot import Scanner
3
+ from fastapi import FastAPI, Query
4
+
5
+ app = FastAPI()
6
+
7
+
8
+ @app.get("/start")
9
+ async def start(targets: List[str] = Query(...)):
10
+ scanner = Scanner(*targets, modules=["httpx"])
11
+ events = [e async for e in scanner.async_start()]
12
+ return [e.json() for e in events]
13
+
14
+
15
+ @app.get("/ping")
16
+ async def ping():
17
+ return {"status": "ok"}
@@ -0,0 +1,82 @@
1
+ import time
2
+ import httpx
3
+ import multiprocessing
4
+ from pathlib import Path
5
+ from subprocess import Popen
6
+ from contextlib import suppress
7
+
8
+ cwd = Path(__file__).parent.parent.parent
9
+
10
+
11
+ def run_bbot_multiprocess(queue):
12
+ from bbot import Scanner
13
+
14
+ scan = Scanner("http://127.0.0.1:8888", "blacklanternsecurity.com", modules=["httpx"])
15
+ events = [e.json() for e in scan.start()]
16
+ queue.put(events)
17
+
18
+
19
+ def test_bbot_multiprocess(bbot_httpserver):
20
+
21
+ bbot_httpserver.expect_request("/").respond_with_data("test@blacklanternsecurity.com")
22
+
23
+ queue = multiprocessing.Queue()
24
+ events_process = multiprocessing.Process(target=run_bbot_multiprocess, args=(queue,))
25
+ events_process.start()
26
+ events_process.join()
27
+ events = queue.get()
28
+ assert len(events) >= 3
29
+ scan_events = [e for e in events if e["type"] == "SCAN"]
30
+ assert len(scan_events) == 2
31
+ assert any([e["data"] == "test@blacklanternsecurity.com" for e in events])
32
+
33
+
34
+ def test_bbot_fastapi(bbot_httpserver):
35
+
36
+ bbot_httpserver.expect_request("/").respond_with_data("test@blacklanternsecurity.com")
37
+ fastapi_process = start_fastapi_server()
38
+
39
+ try:
40
+
41
+ # wait for the server to start with a timeout of 60 seconds
42
+ start_time = time.time()
43
+ while True:
44
+ try:
45
+ response = httpx.get("http://127.0.0.1:8978/ping")
46
+ response.raise_for_status()
47
+ break
48
+ except httpx.HTTPError:
49
+ if time.time() - start_time > 60:
50
+ raise TimeoutError("Server did not start within 60 seconds.")
51
+ time.sleep(0.1)
52
+ continue
53
+
54
+ # run a scan
55
+ response = httpx.get(
56
+ "http://127.0.0.1:8978/start",
57
+ params={"targets": ["http://127.0.0.1:8888", "blacklanternsecurity.com"]},
58
+ timeout=100,
59
+ )
60
+ events = response.json()
61
+ assert len(events) >= 3
62
+ scan_events = [e for e in events if e["type"] == "SCAN"]
63
+ assert len(scan_events) == 2
64
+ assert any([e["data"] == "test@blacklanternsecurity.com" for e in events])
65
+
66
+ finally:
67
+ with suppress(Exception):
68
+ fastapi_process.terminate()
69
+
70
+
71
+ def start_fastapi_server():
72
+ import os
73
+ import sys
74
+
75
+ env = os.environ.copy()
76
+ with suppress(KeyError):
77
+ del env["BBOT_TESTING"]
78
+ python_executable = str(sys.executable)
79
+ process = Popen(
80
+ [python_executable, "-m", "uvicorn", "bbot.test.fastapi_test:app", "--port", "8978"], cwd=cwd, env=env
81
+ )
82
+ return process
@@ -631,6 +631,42 @@ def custom_lookup(query, rdtype):
631
631
  assert len(modified_wildcard_events) == 0
632
632
 
633
633
 
634
+ @pytest.mark.asyncio
635
+ async def test_wildcard_deduplication(bbot_scanner):
636
+
637
+ custom_lookup = """
638
+ def custom_lookup(query, rdtype):
639
+ if rdtype == "TXT" and query.strip(".").endswith("evilcorp.com"):
640
+ return {""}
641
+ """
642
+
643
+ mock_data = {
644
+ "evilcorp.com": {"A": ["127.0.0.1"]},
645
+ }
646
+
647
+ from bbot.modules.base import BaseModule
648
+
649
+ class DummyModule(BaseModule):
650
+ watched_events = ["DNS_NAME"]
651
+ per_domain_only = True
652
+
653
+ async def handle_event(self, event):
654
+ for i in range(30):
655
+ await self.emit_event(f"www{i}.evilcorp.com", "DNS_NAME", parent=event)
656
+
657
+ # scan without omitted event type
658
+ scan = bbot_scanner(
659
+ "evilcorp.com", config={"dns": {"minimal": False, "wildcard_ignore": []}, "omit_event_types": []}
660
+ )
661
+ await scan.helpers.dns._mock_dns(mock_data, custom_lookup_fn=custom_lookup)
662
+ dummy_module = DummyModule(scan)
663
+ scan.modules["dummy_module"] = dummy_module
664
+ events = [e async for e in scan.async_start()]
665
+ dns_name_events = [e for e in events if e.type == "DNS_NAME"]
666
+ assert len(dns_name_events) == 2
667
+ assert 1 == len([e for e in dns_name_events if e.data == "_wildcard.evilcorp.com"])
668
+
669
+
634
670
  @pytest.mark.asyncio
635
671
  async def test_dns_raw_records(bbot_scanner):
636
672
 
@@ -966,3 +966,35 @@ def test_event_magic():
966
966
  assert event.tags == {"folder"}
967
967
 
968
968
  zip_file.unlink()
969
+
970
+
971
+ def test_event_hashing():
972
+ scan = Scanner("example.com")
973
+ url_event = scan.make_event("https://api.example.com/", "URL_UNVERIFIED", parent=scan.root_event)
974
+ host_event_1 = scan.make_event("www.example.com", "DNS_NAME", parent=url_event)
975
+ host_event_2 = scan.make_event("test.example.com", "DNS_NAME", parent=url_event)
976
+ finding_data = {"description": "Custom Yara Rule [find_string] Matched via identifier [str1]"}
977
+ finding1 = scan.make_event(finding_data, "FINDING", parent=host_event_1)
978
+ finding2 = scan.make_event(finding_data, "FINDING", parent=host_event_2)
979
+ finding3 = scan.make_event(finding_data, "FINDING", parent=host_event_2)
980
+
981
+ assert finding1.data == {
982
+ "description": "Custom Yara Rule [find_string] Matched via identifier [str1]",
983
+ "host": "www.example.com",
984
+ }
985
+ assert finding2.data == {
986
+ "description": "Custom Yara Rule [find_string] Matched via identifier [str1]",
987
+ "host": "test.example.com",
988
+ }
989
+ assert finding3.data == {
990
+ "description": "Custom Yara Rule [find_string] Matched via identifier [str1]",
991
+ "host": "test.example.com",
992
+ }
993
+ assert finding1.id != finding2.id
994
+ assert finding2.id == finding3.id
995
+ assert finding1.data_id != finding2.data_id
996
+ assert finding2.data_id == finding3.data_id
997
+ assert finding1.data_hash != finding2.data_hash
998
+ assert finding2.data_hash == finding3.data_hash
999
+ assert hash(finding1) != hash(finding2)
1000
+ assert hash(finding2) == hash(finding3)
@@ -2,11 +2,12 @@ from .base import ModuleTestBase
2
2
 
3
3
 
4
4
  class TestCensys(ModuleTestBase):
5
- config_overrides = {"modules": {"censys": {"api_id": "api_id", "api_secret": "api_secret"}}}
5
+ config_overrides = {"modules": {"censys": {"api_key": "api_id:api_secret"}}}
6
6
 
7
7
  async def setup_before_prep(self, module_test):
8
8
  module_test.httpx_mock.add_response(
9
9
  url="https://search.censys.io/api/v1/account",
10
+ match_headers={"Authorization": "Basic YXBpX2lkOmFwaV9zZWNyZXQ="},
10
11
  json={
11
12
  "email": "info@blacklanternsecurity.com",
12
13
  "login": "nope",
@@ -17,6 +18,7 @@ class TestCensys(ModuleTestBase):
17
18
  )
18
19
  module_test.httpx_mock.add_response(
19
20
  url="https://search.censys.io/api/v2/certificates/search",
21
+ match_headers={"Authorization": "Basic YXBpX2lkOmFwaV9zZWNyZXQ="},
20
22
  match_content=b'{"q": "names: blacklanternsecurity.com", "per_page": 100}',
21
23
  json={
22
24
  "code": 200,
@@ -45,6 +47,7 @@ class TestCensys(ModuleTestBase):
45
47
  )
46
48
  module_test.httpx_mock.add_response(
47
49
  url="https://search.censys.io/api/v2/certificates/search",
50
+ match_headers={"Authorization": "Basic YXBpX2lkOmFwaV9zZWNyZXQ="},
48
51
  match_content=b'{"q": "names: blacklanternsecurity.com", "per_page": 100, "cursor": "NextToken"}',
49
52
  json={
50
53
  "code": 200,
@@ -2,15 +2,17 @@ from .base import ModuleTestBase
2
2
 
3
3
 
4
4
  class TestPassiveTotal(ModuleTestBase):
5
- config_overrides = {"modules": {"passivetotal": {"username": "jon@bls.fakedomain", "api_key": "asdf"}}}
5
+ config_overrides = {"modules": {"passivetotal": {"api_key": "jon@bls.fakedomain:asdf"}}}
6
6
 
7
7
  async def setup_before_prep(self, module_test):
8
8
  module_test.httpx_mock.add_response(
9
9
  url="https://api.passivetotal.org/v2/account/quota",
10
+ match_headers={"Authorization": "Basic am9uQGJscy5mYWtlZG9tYWluOmFzZGY="},
10
11
  json={"user": {"counts": {"search_api": 10}, "limits": {"search_api": 20}}},
11
12
  )
12
13
  module_test.httpx_mock.add_response(
13
14
  url="https://api.passivetotal.org/v2/enrichment/subdomains?query=blacklanternsecurity.com",
15
+ match_headers={"Authorization": "Basic am9uQGJscy5mYWtlZG9tYWluOmFzZGY="},
14
16
  json={"subdomains": ["asdf"]},
15
17
  )
16
18
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: bbot
3
- Version: 2.2.0.5242rc0
3
+ Version: 2.2.0.5263rc0
4
4
  Summary: OSINT automation for hackers.
5
5
  Home-page: https://github.com/blacklanternsecurity/bbot
6
6
  License: GPL-3.0
@@ -1,22 +1,22 @@
1
- bbot/__init__.py,sha256=L_3qf4Pz8yPBVEKrDwVNm_E7vXA7pqI-bECuajUR-oQ,130
1
+ bbot/__init__.py,sha256=zHXaGwoPshL7cKbzVuXyPNKEb-cMwH90drmKbblMwKE,130
2
2
  bbot/cli.py,sha256=7S3a4eB-Dl8yodc5WC-927Z30CNlLl9EXimGvIVypJo,10434
3
3
  bbot/core/__init__.py,sha256=l255GJE_DvUnWvrRb0J5lG-iMztJ8zVvoweDOfegGtI,46
4
4
  bbot/core/config/__init__.py,sha256=zYNw2Me6tsEr8hOOkLb4BQ97GB7Kis2k--G81S8vofU,342
5
5
  bbot/core/config/files.py,sha256=pNrcw61UKKZeMt0rp9Ac5mUK7LdIRmcpojMxI-LwjeA,1413
6
- bbot/core/config/logger.py,sha256=zkD08_KNiIa8LTZkI4wiAeA4g0zVCiA7d7P5MmocXsk,10467
7
- bbot/core/core.py,sha256=twd7-fiaaxzgcWTPwT1zbSWfAa_gHHfl7gAFvLYvFYg,6358
8
- bbot/core/engine.py,sha256=wGopKa2GNs61r16Pr_xtp6Si9AT6I-lE83iWhEgtxwA,29290
6
+ bbot/core/config/logger.py,sha256=YBWLg3x0bEaHKQR8Fn9e1Y0OeyFTED0fgAMtUZT2gEw,10583
7
+ bbot/core/core.py,sha256=zFSbPd0YcjpeI1uO9U6lYY5gKmzNSTGhduvXvB6qZ0M,7065
8
+ bbot/core/engine.py,sha256=XulYpIPtFej1fYB0pjJB1z1hYcI9aHIUuE0fpMcLSc8,29352
9
9
  bbot/core/event/__init__.py,sha256=8ut88ZUg0kbtWkOx2j3XzNr_3kTfgoM-3UdiWHFA_ag,56
10
- bbot/core/event/base.py,sha256=vb4rPOEGILaWsaKG_DB7aZQqAuNsCHYFblK5gThZT1U,60459
10
+ bbot/core/event/base.py,sha256=y6uvXJ2Z832d0v7lvkbuP8BJraqaiGM35BcvPkPT1Ig,60666
11
11
  bbot/core/event/helpers.py,sha256=PUN4Trq5_wpKVuhmwUQWAr40apgMXhJ9Gz-VfZ0j3lA,1554
12
12
  bbot/core/flags.py,sha256=Ltvm8Bc4D65I55HuU5bzyjO1R3yMDNpVmreGU83ZBXE,1266
13
13
  bbot/core/helpers/__init__.py,sha256=0UNwcZjNsX41hbHdo3yZPuARkYWch-okI68DScexve4,86
14
14
  bbot/core/helpers/async_helpers.py,sha256=KF-Dl4zI_VjXhQdOmwGub3sIGavlXlitZkzJAn_l6Tw,3621
15
15
  bbot/core/helpers/bloom.py,sha256=z7gttz-ugvwj7s2L14feJhEx2rzECdqcB255A0hjvNI,2579
16
16
  bbot/core/helpers/cache.py,sha256=1aMr3HVD45cDtHEG5xlznDUCywRgO9oRFidscrs_5sA,1537
17
- bbot/core/helpers/command.py,sha256=kORIRaDdbJF7yGOd5BNJH-UDLKi6rHfUoVUaJMF662M,12774
17
+ bbot/core/helpers/command.py,sha256=2i0hz-uo6zUV9oGtXf-9erh_g3SazDtYeh6pETlfPPA,12810
18
18
  bbot/core/helpers/depsinstaller/__init__.py,sha256=2mx1nYylSyvwl0GCM9YDHqrFEt2_5dSWAjP1RmhmbQg,37
19
- bbot/core/helpers/depsinstaller/installer.py,sha256=p1fU0uMh6hFKMbGyg6w40z-KCBNycQw9Fx_-4Y2zisI,16701
19
+ bbot/core/helpers/depsinstaller/installer.py,sha256=5lanuF26n1DD0IV1uNn1zBfZFE3f4Qeh_fXnmTv-gic,16937
20
20
  bbot/core/helpers/depsinstaller/sudo_askpass.py,sha256=yGa2OQv30RO75QkMuG1iruKqb7amQxRVRRcHmvIeGhk,1276
21
21
  bbot/core/helpers/diff.py,sha256=7waBeHFGnAKn-R-sBd-wc3yjwxT_umwy4YxfE7JFd6w,10599
22
22
  bbot/core/helpers/dns/__init__.py,sha256=2JK8P0BUfPlh4CTuuOWQCOacwL7NEtGFYPJsxbA0Zwo,27
@@ -32,7 +32,7 @@ bbot/core/helpers/libmagic.py,sha256=a9tmL558cM5lzN69YahBc7JNHmPnBJgu9Wa8Q5bH1S0
32
32
  bbot/core/helpers/misc.py,sha256=rvfZmm8UHCChmbMorjPMybaCZTkERrKZhxvY9S4dVPc,86873
33
33
  bbot/core/helpers/names_generator.py,sha256=Sj_Q-7KQyElEpalzlUadSwaniESqrIVVEle9ycPIiho,10322
34
34
  bbot/core/helpers/ntlm.py,sha256=P2Xj4-GPos2iAzw4dfk0FJp6oGyycGhu2x6sLDVjYjs,2573
35
- bbot/core/helpers/process.py,sha256=6D9_LYZrhQ0Jb7Rn58rWMafmAZn7rVVA2LqMKwpR_xg,2271
35
+ bbot/core/helpers/process.py,sha256=2bbveUuLIrr6wjixZRdFX-gqNE-b7rxVpzBPHYNcmzM,1704
36
36
  bbot/core/helpers/ratelimiter.py,sha256=K8qFIyJPJtfdb9kSW6_lL6ahWqxR2uWyCBkDlg6uJgo,1990
37
37
  bbot/core/helpers/regex.py,sha256=XURaY6ijpOYYU9lzWMAKg12G1VFtGJjlJl07_eN1xxk,4170
38
38
  bbot/core/helpers/regexes.py,sha256=kz3y3hyEJvsnF7-4NrkrHTwwhkbRqEYi_wqA_rmZlgQ,5859
@@ -45,6 +45,7 @@ bbot/core/helpers/web/ssl_context.py,sha256=aWVgl-d0HoE8B4EBKNxaa5UAzQmx79DjDByf
45
45
  bbot/core/helpers/web/web.py,sha256=K7BOts1c1bRjU5rpluD94jClwchmBMZQk8FZI1ljS94,22661
46
46
  bbot/core/helpers/wordcloud.py,sha256=WdQwboCNcCxcUdLuB6MMMDQBL4ZshFM_f6GW7nUZEBQ,19819
47
47
  bbot/core/modules.py,sha256=OOUSncr-EM6bJBrI3iH5wvfnpTXKQ-A8OL8UMvkL0CU,31432
48
+ bbot/core/multiprocess.py,sha256=ocQHanskJ09gHwe7RZmwNdZyCOQyeyUoIHCtLbtvXUk,1771
48
49
  bbot/core/shared_deps.py,sha256=A3vrI62uPTTayNIHhWAj6xz43cj--oXWC4prmDlgJnw,6958
49
50
  bbot/db/sql/models.py,sha256=AXefz4nEtpV2p19d6rLaEcCQNnqXODpldA-P5uSUntg,4729
50
51
  bbot/defaults.yml,sha256=_3sNH-2TWPaQHZ6ozBA1UKWLB7HuHK8vjZ534mb8cO4,6042
@@ -73,7 +74,7 @@ bbot/modules/bufferoverrun.py,sha256=FcHzj04iSyBqyfUY6lBPLlkD-hT-hBgICKJqlgIGkHY
73
74
  bbot/modules/builtwith.py,sha256=A2Q70GtHtMHQgLYgX-UhUNzBwIZ7n0p5IOEI5lPSBk4,5369
74
75
  bbot/modules/bypass403.py,sha256=Qrszg-vVfjKA0IosM88CekpW6BcazJThpl5moXIdeiQ,6843
75
76
  bbot/modules/c99.py,sha256=cvyLZ7oHk6kxq6UWu0oISbXLKwnZW9yNBmsvT1RTGwI,1388
76
- bbot/modules/censys.py,sha256=4b8Mev00zPSMawkGE1lkk-_cKKf1FF8Msk2Jf9JRS6c,3470
77
+ bbot/modules/censys.py,sha256=J9ntCZGpS-HPf4EfoS6NpkOv7RqGk7AZmYIpLI8uYIE,3309
77
78
  bbot/modules/certspotter.py,sha256=dENaH4gUGV-ainYOupBO7GkOW9KQutTzC1NdWfpMhnw,877
78
79
  bbot/modules/chaos.py,sha256=axnGkINmr-3Hu4x_x3t7eQ8n7wpCsmUp2HykWhcRDGU,1513
79
80
  bbot/modules/code_repository.py,sha256=x70Z45VnNNMF8BPkHfGWZXsZXw_fStGB3y0-8jbP1Ns,2078
@@ -120,7 +121,7 @@ bbot/modules/internal/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3h
120
121
  bbot/modules/internal/aggregate.py,sha256=csWYIt2fUp9K_CRxP3bndUMIjpNIh8rmBubp5Fr1-nc,395
121
122
  bbot/modules/internal/base.py,sha256=BXO4Hc7XKaAOaLzolF3krJX1KibPxtek2GTQUgnCHk0,387
122
123
  bbot/modules/internal/cloudcheck.py,sha256=kkD5DC5Y7EPCiVTdEspZktkflArU3unx--9ciBikxHo,4099
123
- bbot/modules/internal/dnsresolve.py,sha256=rmXBb6r8MNYrHlnTbES1ok5M9q3JD3Oj6AOx6d0Bawc,14558
124
+ bbot/modules/internal/dnsresolve.py,sha256=UW88BlpJ7gOjPARrjVgtwpDIDPNQZRpuIRpL2yVP6T4,15251
124
125
  bbot/modules/internal/excavate.py,sha256=1kPiaUlpJVT_xgPgmzqhpf04uanIe9ZayQsWANbzhd4,51238
125
126
  bbot/modules/internal/speculate.py,sha256=qT9tQWiSZ1bhAt-CeehRztVo_DP5eOqdbpPVVxGktTg,8765
126
127
  bbot/modules/internetdb.py,sha256=Edg0Z84dH8dPTZMd7RlzvYBYNq8JHs_ns_ldnFxwRKo,5415
@@ -156,7 +157,7 @@ bbot/modules/output/websocket.py,sha256=sDTtHU-Ey_tvS0gMi6PVPV9L4qAmGyWeccxAKfEW
156
157
  bbot/modules/paramminer_cookies.py,sha256=q1PzftHQpCHLz81_VgLZsO6moia7ZtnU32igfcySi2w,1816
157
158
  bbot/modules/paramminer_getparams.py,sha256=_j6rgaqV5wGJoa8p5-KKbe2YsVGUtmWIanCVtFiF97Y,1893
158
159
  bbot/modules/paramminer_headers.py,sha256=QEnWxveQVuS91MEYKYtfquqdNzimN7sQHuxpcqiTHpo,10305
159
- bbot/modules/passivetotal.py,sha256=daVREcQdxALQmUuI7rTY4MBaqaV8mFKHiDHK6kUtJ5s,1694
160
+ bbot/modules/passivetotal.py,sha256=sq6xN8k3Dfbpa4hEleR8d9u45_lA5kOCTTaHtkjgVAg,1593
160
161
  bbot/modules/pgp.py,sha256=Xu2M9WEIlwTm5-Lv29g7BblI05tD9Dl0XsYSeY6UURs,2065
161
162
  bbot/modules/portscan.py,sha256=eb1sNb8ffe5gcjOsOOvX48oNtecJ1Tqq6exoz_G_jZ4,13327
162
163
  bbot/modules/postman.py,sha256=tbLrxi5pOycLABvphORxyK9duTSBXZLgpf1vAZvOIa0,3512
@@ -220,29 +221,31 @@ bbot/scanner/preset/conditions.py,sha256=hFL9cSIWGEsv2TfM5UGurf0c91cyaM8egb5IngB
220
221
  bbot/scanner/preset/environ.py,sha256=-wbFk1YHpU8IJLKVw23Q3btQTICeX0iulURo7D673L0,4732
221
222
  bbot/scanner/preset/path.py,sha256=p9tZC7XcgZv2jXpbEJAg1lU2b4ZLX5COFnCxEUOXz2g,2234
222
223
  bbot/scanner/preset/preset.py,sha256=-HH_nlr4VaXmKCooXMG5av39gOUdCVOO_y9Bhgbt_u4,40180
223
- bbot/scanner/scanner.py,sha256=n0jpHQ9tXqrPJTwg8DndMgNPas0NbNzV0FeoQDbQgJE,53692
224
+ bbot/scanner/scanner.py,sha256=pcnJJyDJiHLSuWfeM22_5Zv3S0HhFUq1oEDZDngX704,53835
224
225
  bbot/scanner/stats.py,sha256=re93sArKXZSiD0Owgqk2J3Kdvfm3RL4Y9Qy_VOcaVk8,3623
225
226
  bbot/scanner/target.py,sha256=X25gpgRv5HmqQjGADiSe6b8744yOkRhAGAvKKYbXnSI,19886
226
227
  bbot/scripts/docs.py,sha256=kg2CzovmUVGJx9hBZjAjUdE1hXeIwC7Ry3CyrnE8GL8,10782
227
228
  bbot/test/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
228
229
  bbot/test/bbot_fixtures.py,sha256=J1_MfpCMXftfGHZc-dgn42ODpTmSJidoBibOltfthac,9862
229
- bbot/test/conftest.py,sha256=js4NAX3iiIc2CCTpWKEclkcSH4egmjMNnqBiHzK_7BA,11242
230
+ bbot/test/conftest.py,sha256=El74QSpuE4jOxvQOUGqoUcXP7j3UJiIDlThErxZj1lE,11193
230
231
  bbot/test/coverage.cfg,sha256=ko9RacAYsJxWJCL8aEuNtkAOtP9lexYiDbeFWe8Tp8Y,31
232
+ bbot/test/fastapi_test.py,sha256=9OhOFRyagXTshMsnuzuKIcR4uzS6VW65m7h9KgB4jSA,381
231
233
  bbot/test/owasp_mastg.apk,sha256=Hai_V9JmEJ-aB8Ab9xEaGXXOAfGQudkUvNOuPb75byE,66651
232
234
  bbot/test/run_tests.sh,sha256=0oprBl970NAqXS4YQa8nRUtKljPeS_WNSvd-QmO5FNY,945
233
235
  bbot/test/test.conf,sha256=HrE7z3O91wg2MeauqIP-rwq10FAB5K6WrHJrX1eN7oc,963
234
236
  bbot/test/test_output.ndjson,sha256=Jfor8nUJ3QTEwXxD6UULrFXM4zhP5wflWo_UNekM3V8,323
235
237
  bbot/test/test_step_1/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
236
238
  bbot/test/test_step_1/test__module__tests.py,sha256=RpD4yuVuQRgbbUkfuasxUlyoVxhTm6TeDyi87y_AaK0,1461
239
+ bbot/test/test_step_1/test_bbot_fastapi.py,sha256=Ph1oTgwOz-oz5H_5Scjv0ItGwenS93lyF4eOkOOlfZ0,2538
237
240
  bbot/test/test_step_1/test_bloom_filter.py,sha256=OpiZXsBX-I8QdTK0LqSYkGMDLA6vL_6t0wco9ypxxtQ,2114
238
241
  bbot/test/test_step_1/test_cli.py,sha256=m4LyIiiedR41f5SpnlkxgE-f42fF7qoGcuthpYxQ_m8,24688
239
242
  bbot/test/test_step_1/test_command.py,sha256=5IeGV6TKB0xtFEsfsU_0mNrOmEdIQiQ3FHkUmsBNoOI,6485
240
243
  bbot/test/test_step_1/test_config.py,sha256=Q38hygpke2GDcv8OguVZuiSOnfDJxEMrRy20dN5Qsn0,887
241
244
  bbot/test/test_step_1/test_depsinstaller.py,sha256=zr9f-wJDotD1ZvKXGEuDRWzFYMAYBI6209mI_PWPtTQ,703
242
- bbot/test/test_step_1/test_dns.py,sha256=YZtSbja-Z76KC9MWBieRExolVWHm0WqssL0WHUpUiC8,30932
245
+ bbot/test/test_step_1/test_dns.py,sha256=uFNQCaHLmmVz8KLIHTtZXSLXYLKZkRd7NiIQC5TSGXY,32132
243
246
  bbot/test/test_step_1/test_docs.py,sha256=YWVGNRfzcrvDmFekX0Cq9gutQplsqvhKTpZ0XK4tWvo,82
244
247
  bbot/test/test_step_1/test_engine.py,sha256=Bfid3-D9ziN93w4vym97tFEn_l2Iof08wjITTv_lAZw,4269
245
- bbot/test/test_step_1/test_events.py,sha256=_rMAxbyuSReZxNwwghL37p7HA9YNpptVcBmcuz74nKw,46669
248
+ bbot/test/test_step_1/test_events.py,sha256=8vuskVWNF73AoiQf-UHpFouRUTtRjRzTw1Xt6H0UjkI,48173
246
249
  bbot/test/test_step_1/test_files.py,sha256=5Q_3jPpMXULxDHsanSDUaj8zF8bXzKdiJZHOmoYpLhQ,699
247
250
  bbot/test/test_step_1/test_helpers.py,sha256=oY2hWhgL-TCB67ve1bAyIwZO3wNRWpx4SjCHNUxHep8,38676
248
251
  bbot/test/test_step_1/test_manager_deduplication.py,sha256=hZQpDXzg6zvzxFolVOcJuY-ME8NXjZUsqS70BRNXp8A,15594
@@ -283,7 +286,7 @@ bbot/test/test_step_2/module_tests/test_module_bufferoverrun.py,sha256=os7A6vdwl
283
286
  bbot/test/test_step_2/module_tests/test_module_builtwith.py,sha256=c_Ta6OXWYdUdcwuE-AbaT-tzj8SUuglMecQX6mDavuE,5051
284
287
  bbot/test/test_step_2/module_tests/test_module_bypass403.py,sha256=-MV06l6Q7d_sM0L2OIw1ReXJc2dj30xC3suOl2HhZTY,3551
285
288
  bbot/test/test_step_2/module_tests/test_module_c99.py,sha256=-xyL1y3eX_rGuBR-U0N1HDZuAw_A_UysN5PupWe0iDI,7427
286
- bbot/test/test_step_2/module_tests/test_module_censys.py,sha256=CIOZGs29fCJZBUhHaFFaO3tfl1CNakoWnr8i4FCi-7w,3956
289
+ bbot/test/test_step_2/module_tests/test_module_censys.py,sha256=RoFfLS0hgASdSoctJEzaKrHVqqRkuPRKPTYVCX2rZLo,4177
287
290
  bbot/test/test_step_2/module_tests/test_module_certspotter.py,sha256=60jCOeK1yaUEgtTxYW-T47kZgKt9XxP2qBH9w-0MDBk,636
288
291
  bbot/test/test_step_2/module_tests/test_module_chaos.py,sha256=9JRgtDEnnJgmEMCTB2bqRJRkBavLys-6ypHPxrM_hXk,956
289
292
  bbot/test/test_step_2/module_tests/test_module_cloudcheck.py,sha256=sRWgotul2I4qY5SjNBDuo2iOceHs-3x0cqJ-K29GitY,3927
@@ -348,7 +351,7 @@ bbot/test/test_step_2/module_tests/test_module_otx.py,sha256=tWkJlrdSCP2IUPGkB2R
348
351
  bbot/test/test_step_2/module_tests/test_module_paramminer_cookies.py,sha256=BHbyFTh37DX7RdDQChqtbs8emCHXUTZGGq3e4UlklEA,2377
349
352
  bbot/test/test_step_2/module_tests/test_module_paramminer_getparams.py,sha256=mWHeWQ_di3q6cMt164xaQ2hjqYkuIil8grs7-Um87PY,10083
350
353
  bbot/test/test_step_2/module_tests/test_module_paramminer_headers.py,sha256=qTT-5fyqw69LaZBxak3FvPfH7lh9W0KVBtJgslD-3eQ,5545
351
- bbot/test/test_step_2/module_tests/test_module_passivetotal.py,sha256=CZLjOMuYCITelT2w2Oo5z06c_wJf1_lw0dxCklmJUkI,961
354
+ bbot/test/test_step_2/module_tests/test_module_passivetotal.py,sha256=fTGQECQ0OzcwiH64-0igFRKO-rs3kXScivZord_oWWU,1120
352
355
  bbot/test/test_step_2/module_tests/test_module_pgp.py,sha256=-m-nPq6WR5UzPDuxeZbuzBQfFi1QfrZQ8RZH4g11ocE,1609
353
356
  bbot/test/test_step_2/module_tests/test_module_portscan.py,sha256=StCm93P4q3o-NhPtUDOA6g_LTH2cwzEw0l2V5ZN5eeI,7306
354
357
  bbot/test/test_step_2/module_tests/test_module_postman.py,sha256=XvgfMgUhJuVgGkgT-JzxJyevNSVv7YvX1yLKJHmD3dw,5026
@@ -404,8 +407,8 @@ bbot/wordlists/raft-small-extensions-lowercase_CLEANED.txt,sha256=ruUQwVfia1_m2u
404
407
  bbot/wordlists/top_open_ports_nmap.txt,sha256=LmdFYkfapSxn1pVuQC2LkOIY2hMLgG-Xts7DVtYzweM,42727
405
408
  bbot/wordlists/valid_url_schemes.txt,sha256=VciB-ww0y-O8Ii1wpTR6rJzGDiC2r-dhVsIJApS1ZYU,3309
406
409
  bbot/wordlists/wordninja_dns.txt.gz,sha256=DYHvvfW0TvzrVwyprqODAk4tGOxv5ezNmCPSdPuDUnQ,570241
407
- bbot-2.2.0.5242rc0.dist-info/LICENSE,sha256=GzeCzK17hhQQDNow0_r0L8OfLpeTKQjFQwBQU7ZUymg,32473
408
- bbot-2.2.0.5242rc0.dist-info/METADATA,sha256=tIqdDdgT-c_QMrxqEs6IM6Rlluzyt3rh4hsTMFz0D0w,17109
409
- bbot-2.2.0.5242rc0.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
410
- bbot-2.2.0.5242rc0.dist-info/entry_points.txt,sha256=cWjvcU_lLrzzJgjcjF7yeGuRA_eDS8pQ-kmPUAyOBfo,38
411
- bbot-2.2.0.5242rc0.dist-info/RECORD,,
410
+ bbot-2.2.0.5263rc0.dist-info/LICENSE,sha256=GzeCzK17hhQQDNow0_r0L8OfLpeTKQjFQwBQU7ZUymg,32473
411
+ bbot-2.2.0.5263rc0.dist-info/METADATA,sha256=w_Verhck5yE-bvYz7e08XFMYQSLCh0bWxLxq5VSqcfw,17109
412
+ bbot-2.2.0.5263rc0.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
413
+ bbot-2.2.0.5263rc0.dist-info/entry_points.txt,sha256=cWjvcU_lLrzzJgjcjF7yeGuRA_eDS8pQ-kmPUAyOBfo,38
414
+ bbot-2.2.0.5263rc0.dist-info/RECORD,,