diff --git a/bbot/modules/base.py b/bbot/modules/base.py
index c24fd9251a..f3ba6eb211 100644
--- a/bbot/modules/base.py
+++ b/bbot/modules/base.py
@@ -1176,10 +1176,17 @@ async def api_request(self, *args, **kwargs):
return r
+ def _prepare_api_iter_req(self, url, page, page_size, offset, **requests_kwargs):
+ """
+ Default function for preparing an API request for iterating through paginated data.
+ """
+ url = self.helpers.safe_format(url, page=page, page_size=page_size, offset=offset)
+ return url, requests_kwargs
+
def _api_response_is_success(self, r):
return r.is_success
- async def api_page_iter(self, url, page_size=100, json=True, next_key=None, **requests_kwargs):
+ async def api_page_iter(self, url, page_size=100, _json=True, next_key=None, iter_key=None, **requests_kwargs):
"""
An asynchronous generator function for iterating through paginated API data.
@@ -1192,6 +1199,7 @@ async def api_page_iter(self, url, page_size=100, json=True, next_key=None, **re
page_size (int, optional): The number of items per page. Defaults to 100.
json (bool, optional): If True, attempts to deserialize the response content to a JSON object. Defaults to True.
next_key (callable, optional): A function that takes the last page's data and returns the URL for the next page. Defaults to None.
+ iter_key (callable, optional): A function that builds each new request based on the page number, page size, and offset. Defaults to a simple implementation that autoreplaces {page} and {page_size} in the url.
**requests_kwargs: Arbitrary keyword arguments that will be forwarded to the HTTP request function.
Yields:
@@ -1214,6 +1222,8 @@ async def api_page_iter(self, url, page_size=100, json=True, next_key=None, **re
page = 1
offset = 0
result = None
+ if iter_key is None:
+ iter_key = self._prepare_api_iter_req
while 1:
if result and callable(next_key):
try:
@@ -1222,13 +1232,13 @@ async def api_page_iter(self, url, page_size=100, json=True, next_key=None, **re
self.debug(f"Failed to extract next page of results from {url}: {e}")
self.debug(traceback.format_exc())
else:
- new_url = self.helpers.safe_format(url, page=page, page_size=page_size, offset=offset)
- result = await self.api_request(new_url, **requests_kwargs)
+ new_url, new_kwargs = iter_key(url, page, page_size, offset, **requests_kwargs)
+ result = await self.api_request(new_url, **new_kwargs)
if result is None:
self.verbose(f"api_page_iter() got no response for {url}")
break
try:
- if json:
+ if _json:
result = result.json()
yield result
except Exception:
diff --git a/bbot/modules/dehashed.py b/bbot/modules/dehashed.py
index d8ef7d003e..9fbefa9bb8 100644
--- a/bbot/modules/dehashed.py
+++ b/bbot/modules/dehashed.py
@@ -90,7 +90,7 @@ async def query(self, domain):
url = f"{self.base_url}?query={query}&size=10000&page=" + "{page}"
page = 0
num_entries = 0
- agen = self.api_page_iter(url=url, auth=self.auth, headers=self.headers, json=False)
+ agen = self.api_page_iter(url=url, auth=self.auth, headers=self.headers, _json=False)
async for result in agen:
result_json = {}
with suppress(Exception):
diff --git a/bbot/modules/dockerhub.py b/bbot/modules/dockerhub.py
index 23f45ab756..2b8efafb5d 100644
--- a/bbot/modules/dockerhub.py
+++ b/bbot/modules/dockerhub.py
@@ -64,7 +64,7 @@ async def handle_social(self, event):
async def get_repos(self, username):
repos = []
url = f"{self.api_url}/repositories/{username}?page_size=25&page=" + "{page}"
- agen = self.api_page_iter(url, json=False)
+ agen = self.api_page_iter(url, _json=False)
try:
async for r in agen:
if r is None:
diff --git a/bbot/modules/github_codesearch.py b/bbot/modules/github_codesearch.py
index 4c838a1c5b..9cb8fb5fef 100644
--- a/bbot/modules/github_codesearch.py
+++ b/bbot/modules/github_codesearch.py
@@ -42,7 +42,7 @@ async def handle_event(self, event):
async def query(self, query):
repos = {}
url = f"{self.base_url}/search/code?per_page=100&type=Code&q={self.helpers.quote(query)}&page=" + "{page}"
- agen = self.api_page_iter(url, headers=self.headers, json=False)
+ agen = self.api_page_iter(url, headers=self.headers, _json=False)
num_results = 0
try:
async for r in agen:
diff --git a/bbot/modules/github_org.py b/bbot/modules/github_org.py
index 229b4671bd..604b9feb27 100644
--- a/bbot/modules/github_org.py
+++ b/bbot/modules/github_org.py
@@ -108,7 +108,7 @@ async def handle_event(self, event):
async def query_org_repos(self, query):
repos = []
url = f"{self.base_url}/orgs/{self.helpers.quote(query)}/repos?per_page=100&page=" + "{page}"
- agen = self.api_page_iter(url, json=False)
+ agen = self.api_page_iter(url, _json=False)
try:
async for r in agen:
if r is None:
@@ -136,7 +136,7 @@ async def query_org_repos(self, query):
async def query_org_members(self, query):
members = []
url = f"{self.base_url}/orgs/{self.helpers.quote(query)}/members?per_page=100&page=" + "{page}"
- agen = self.api_page_iter(url, json=False)
+ agen = self.api_page_iter(url, _json=False)
try:
async for r in agen:
if r is None:
@@ -164,7 +164,7 @@ async def query_org_members(self, query):
async def query_user_repos(self, query):
repos = []
url = f"{self.base_url}/users/{self.helpers.quote(query)}/repos?per_page=100&page=" + "{page}"
- agen = self.api_page_iter(url, json=False)
+ agen = self.api_page_iter(url, _json=False)
try:
async for r in agen:
if r is None:
diff --git a/bbot/modules/github_workflows.py b/bbot/modules/github_workflows.py
index 53712c32e1..d77e2c03c8 100644
--- a/bbot/modules/github_workflows.py
+++ b/bbot/modules/github_workflows.py
@@ -92,7 +92,7 @@ async def handle_event(self, event):
async def get_workflows(self, owner, repo):
workflows = []
url = f"{self.base_url}/repos/{owner}/{repo}/actions/workflows?per_page=100&page=" + "{page}"
- agen = self.api_page_iter(url, json=False)
+ agen = self.api_page_iter(url, _json=False)
try:
async for r in agen:
if r is None:
diff --git a/bbot/modules/postman.py b/bbot/modules/postman.py
index a7374a5715..f6eafc6ff9 100644
--- a/bbot/modules/postman.py
+++ b/bbot/modules/postman.py
@@ -10,52 +10,64 @@ class postman(postman):
"created_date": "2024-09-07",
"author": "@domwhewell-sage",
}
-
+ options = {"api_key": ""}
+ options_desc = {"api_key": "Postman API Key"}
reject_wildcards = False
async def handle_event(self, event):
# Handle postman profile
if event.type == "SOCIAL":
- await self.handle_profile(event)
+ owner = event.data.get("profile_name", "")
+ in_scope_workspaces = await self.process_workspaces(user=owner)
elif event.type == "ORG_STUB":
- await self.handle_org_stub(event)
-
- async def handle_profile(self, event):
- profile_name = event.data.get("profile_name", "")
- self.verbose(f"Searching for postman workspaces, collections, requests belonging to {profile_name}")
- for item in await self.query(profile_name):
- workspace = item["document"]
- name = workspace["slug"]
- profile = workspace["publisherHandle"]
- if profile_name.lower() == profile.lower():
- self.verbose(f"Got {name}")
- workspace_url = f"{self.html_url}/{profile}/{name}"
+ owner = event.data
+ in_scope_workspaces = await self.process_workspaces(org=owner)
+ if in_scope_workspaces:
+ for workspace in in_scope_workspaces:
+ repo_url = workspace["url"]
+ repo_name = workspace["repo_name"]
+ if event.type == "SOCIAL":
+ context = f'{{module}} searched postman.com for workspaces belonging to "{owner}" and found "{repo_name}" at {{event.type}}: {repo_url}'
+ elif event.type == "ORG_STUB":
+ context = f'{{module}} searched postman.com for "{owner}" and found matching workspace "{repo_name}" at {{event.type}}: {repo_url}'
await self.emit_event(
- {"url": workspace_url},
+ {"url": repo_url},
"CODE_REPOSITORY",
tags="postman",
parent=event,
- context=f'{{module}} searched postman.com for workspaces belonging to "{profile_name}" and found "{name}" at {{event.type}}: {workspace_url}',
+ context=context,
)
- async def handle_org_stub(self, event):
- org_name = event.data
- self.verbose(f"Searching for any postman workspaces, collections, requests for {org_name}")
- for item in await self.query(org_name):
- workspace = item["document"]
- name = workspace["slug"]
- profile = workspace["publisherHandle"]
- self.verbose(f"Got {name}")
- workspace_url = f"{self.html_url}/{profile}/{name}"
- await self.emit_event(
- {"url": workspace_url},
- "CODE_REPOSITORY",
- tags="postman",
- parent=event,
- context=f'{{module}} searched postman.com for "{org_name}" and found matching workspace "{name}" at {{event.type}}: {workspace_url}',
- )
+ async def process_workspaces(self, user=None, org=None):
+ in_scope_workspaces = []
+ owner = user or org
+ if owner:
+ self.verbose(f"Searching for postman workspaces, collections, requests for {owner}")
+ for item in await self.query(owner):
+ workspace = item["document"]
+ slug = workspace["slug"]
+ profile = workspace["publisherHandle"]
+ repo_url = f"{self.html_url}/{profile}/{slug}"
+ workspace_id = await self.get_workspace_id(repo_url)
+ if (org and workspace_id) or (user and owner.lower() == profile.lower()):
+ self.verbose(f"Found workspace ID {workspace_id} for {repo_url}")
+ data = await self.request_workspace(workspace_id)
+ in_scope = await self.validate_workspace(
+ data["workspace"], data["environments"], data["collections"]
+ )
+ if in_scope:
+ in_scope_workspaces.append({"url": repo_url, "repo_name": slug})
+ else:
+ self.verbose(
+ f"Failed to validate {repo_url} is in our scope as it does not contain any in-scope dns_names / emails"
+ )
+ return in_scope_workspaces
async def query(self, query):
+ def api_page_iter(url, page, page_size, offset, **kwargs):
+ kwargs["json"]["body"]["from"] = offset
+ return url, kwargs
+
data = []
url = f"{self.base_url}/ws/proxy"
json = {
@@ -67,7 +79,7 @@ async def query(self, query):
"collaboration.workspace",
],
"queryText": self.helpers.quote(query),
- "size": 100,
+ "size": 25,
"from": 0,
"clientTraceId": "",
"requestOrigin": "srp",
@@ -76,13 +88,19 @@ async def query(self, query):
"domain": "public",
},
}
- r = await self.helpers.request(url, method="POST", json=json, headers=self.headers)
- if r is None:
- return data
- status_code = getattr(r, "status_code", 0)
- try:
- json = r.json()
- except Exception as e:
- self.warning(f"Failed to decode JSON for {r.url} (HTTP status: {status_code}): {e}")
- return None
- return json.get("data", [])
+
+ agen = self.api_page_iter(
+ url, page_size=25, method="POST", iter_key=api_page_iter, json=json, _json=False, headers=self.headers
+ )
+ async for r in agen:
+ status_code = getattr(r, "status_code", 0)
+ if status_code != 200:
+ self.debug(f"Reached end of postman search results (url: {r.url}) with status code {status_code}")
+ break
+ try:
+ data.extend(r.json().get("data", []))
+ except Exception as e:
+ self.warning(f"Failed to decode JSON for {r.url} (HTTP status: {status_code}): {e}")
+ return None
+
+ return data
diff --git a/bbot/modules/postman_download.py b/bbot/modules/postman_download.py
index 104f4261b8..727c655772 100644
--- a/bbot/modules/postman_download.py
+++ b/bbot/modules/postman_download.py
@@ -24,11 +24,7 @@ async def setup(self):
else:
self.output_dir = self.scan.home / "postman_workspaces"
self.helpers.mkdir(self.output_dir)
- return await self.require_api_key()
-
- def prepare_api_request(self, url, kwargs):
- kwargs["headers"]["X-Api-Key"] = self.api_key
- return url, kwargs
+ return await super().setup()
async def filter_event(self, event):
if event.type == "CODE_REPOSITORY":
@@ -45,149 +41,16 @@ async def handle_event(self, event):
workspace = data["workspace"]
environments = data["environments"]
collections = data["collections"]
- in_scope = await self.validate_workspace(workspace, environments, collections)
- if in_scope:
- workspace_path = self.save_workspace(workspace, environments, collections)
- if workspace_path:
- self.verbose(f"Downloaded workspace from {repo_url} to {workspace_path}")
- codebase_event = self.make_event(
- {"path": str(workspace_path)}, "FILESYSTEM", tags=["postman", "workspace"], parent=event
- )
- await self.emit_event(
- codebase_event,
- context=f"{{module}} downloaded postman workspace at {repo_url} to {{event.type}}: {workspace_path}",
- )
- else:
- self.verbose(
- f"Failed to validate {repo_url} is in our scope as it does not contain any in-scope dns_names / emails, skipping download"
+ workspace_path = self.save_workspace(workspace, environments, collections)
+ if workspace_path:
+ self.verbose(f"Downloaded workspace from {repo_url} to {workspace_path}")
+ codebase_event = self.make_event(
+ {"path": str(workspace_path)}, "FILESYSTEM", tags=["postman", "workspace"], parent=event
+ )
+ await self.emit_event(
+ codebase_event,
+ context=f"{{module}} downloaded postman workspace at {repo_url} to {{event.type}}: {workspace_path}",
)
-
- async def get_workspace_id(self, repo_url):
- workspace_id = ""
- profile = repo_url.split("/")[-2]
- name = repo_url.split("/")[-1]
- url = f"{self.base_url}/ws/proxy"
- json = {
- "service": "workspaces",
- "method": "GET",
- "path": f"/workspaces?handle={profile}&slug={name}",
- }
- r = await self.helpers.request(url, method="POST", json=json, headers=self.headers)
- if r is None:
- return workspace_id
- status_code = getattr(r, "status_code", 0)
- try:
- json = r.json()
- except Exception as e:
- self.warning(f"Failed to decode JSON for {r.url} (HTTP status: {status_code}): {e}")
- return workspace_id
- data = json.get("data", [])
- if len(data) == 1:
- workspace_id = data[0]["id"]
- return workspace_id
-
- async def request_workspace(self, id):
- data = {"workspace": {}, "environments": [], "collections": []}
- workspace = await self.get_workspace(id)
- if workspace:
- # Main Workspace
- name = workspace["name"]
- data["workspace"] = workspace
-
- # Workspace global variables
- self.verbose(f"Downloading globals for workspace {name}")
- globals = await self.get_globals(id)
- data["environments"].append(globals)
-
- # Workspace Environments
- workspace_environments = workspace.get("environments", [])
- if workspace_environments:
- self.verbose(f"Downloading environments for workspace {name}")
- for _ in workspace_environments:
- environment_id = _["uid"]
- environment = await self.get_environment(environment_id)
- data["environments"].append(environment)
-
- # Workspace Collections
- workspace_collections = workspace.get("collections", [])
- if workspace_collections:
- self.verbose(f"Downloading collections for workspace {name}")
- for _ in workspace_collections:
- collection_id = _["uid"]
- collection = await self.get_collection(collection_id)
- data["collections"].append(collection)
- return data
-
- async def get_workspace(self, workspace_id):
- workspace = {}
- workspace_url = f"{self.api_url}/workspaces/{workspace_id}"
- r = await self.api_request(workspace_url)
- if r is None:
- return workspace
- status_code = getattr(r, "status_code", 0)
- try:
- json = r.json()
- except Exception as e:
- self.warning(f"Failed to decode JSON for {r.url} (HTTP status: {status_code}): {e}")
- return workspace
- workspace = json.get("workspace", {})
- return workspace
-
- async def get_globals(self, workspace_id):
- globals = {}
- globals_url = f"{self.base_url}/workspace/{workspace_id}/globals"
- r = await self.helpers.request(globals_url, headers=self.headers)
- if r is None:
- return globals
- status_code = getattr(r, "status_code", 0)
- try:
- json = r.json()
- except Exception as e:
- self.warning(f"Failed to decode JSON for {r.url} (HTTP status: {status_code}): {e}")
- return globals
- globals = json.get("data", {})
- return globals
-
- async def get_environment(self, environment_id):
- environment = {}
- environment_url = f"{self.api_url}/environments/{environment_id}"
- r = await self.api_request(environment_url)
- if r is None:
- return environment
- status_code = getattr(r, "status_code", 0)
- try:
- json = r.json()
- except Exception as e:
- self.warning(f"Failed to decode JSON for {r.url} (HTTP status: {status_code}): {e}")
- return environment
- environment = json.get("environment", {})
- return environment
-
- async def get_collection(self, collection_id):
- collection = {}
- collection_url = f"{self.api_url}/collections/{collection_id}"
- r = await self.api_request(collection_url)
- if r is None:
- return collection
- status_code = getattr(r, "status_code", 0)
- try:
- json = r.json()
- except Exception as e:
- self.warning(f"Failed to decode JSON for {r.url} (HTTP status: {status_code}): {e}")
- return collection
- collection = json.get("collection", {})
- return collection
-
- async def validate_workspace(self, workspace, environments, collections):
- name = workspace.get("name", "")
- full_wks = str([workspace, environments, collections])
- in_scope_hosts = await self.scan.extract_in_scope_hostnames(full_wks)
- if in_scope_hosts:
- self.verbose(
- f'Found in-scope hostname(s): "{in_scope_hosts}" in workspace {name}, it appears to be in-scope'
- )
- return True
- return False
def save_workspace(self, workspace, environments, collections):
zip_path = None
diff --git a/bbot/modules/templates/postman.py b/bbot/modules/templates/postman.py
index 2048abc32d..490c6e62eb 100644
--- a/bbot/modules/templates/postman.py
+++ b/bbot/modules/templates/postman.py
@@ -14,8 +14,170 @@ class postman(BaseModule):
headers = {
"Content-Type": "application/json",
- "X-App-Version": "10.18.8-230926-0808",
+ "X-App-Version": "11.27.4-250109-2338",
"X-Entity-Team-Id": "0",
"Origin": "https://www.postman.com",
"Referer": "https://www.postman.com/search?q=&scope=public&type=all",
}
+ auth_required = True
+
+ async def setup(self):
+ await super().setup()
+ self.headers = {}
+ api_keys = set()
+ modules_config = self.scan.config.get("modules", {})
+ postman_modules = [m for m in modules_config if str(m).startswith("postman")]
+ for module_name in postman_modules:
+ module_config = modules_config.get(module_name, {})
+ api_key = module_config.get("api_key", "")
+ if isinstance(api_key, str):
+ api_key = [api_key]
+ for key in api_key:
+ key = key.strip()
+ if key:
+ api_keys.add(key)
+ if not api_keys:
+ if self.auth_required:
+ return None, "No API key set"
+ self.api_key = api_keys
+ if self.api_key:
+ try:
+ await self.ping()
+ self.hugesuccess("API is ready")
+ return True
+ except Exception as e:
+ self.trace()
+ return None, f"Error with API ({str(e).strip()})"
+ return True
+
+ def prepare_api_request(self, url, kwargs):
+ if self.api_key:
+ kwargs["headers"]["X-Api-Key"] = self.api_key
+ return url, kwargs
+
+ async def get_workspace_id(self, repo_url):
+ workspace_id = ""
+ profile = repo_url.split("/")[-2]
+ name = repo_url.split("/")[-1]
+ url = f"{self.base_url}/ws/proxy"
+ json = {
+ "service": "workspaces",
+ "method": "GET",
+ "path": f"/workspaces?handle={profile}&slug={name}",
+ }
+ r = await self.helpers.request(url, method="POST", json=json, headers=self.headers)
+ if r is None:
+ return workspace_id
+ status_code = getattr(r, "status_code", 0)
+ try:
+ json = r.json()
+ except Exception as e:
+ self.warning(f"Failed to decode JSON for {r.url} (HTTP status: {status_code}): {e}")
+ return workspace_id
+ data = json.get("data", [])
+ if len(data) == 1:
+ workspace_id = data[0]["id"]
+ return workspace_id
+
+ async def request_workspace(self, id):
+ data = {"workspace": {}, "environments": [], "collections": []}
+ workspace = await self.get_workspace(id)
+ if workspace:
+ # Main Workspace
+ name = workspace["name"]
+ data["workspace"] = workspace
+
+ # Workspace global variables
+ self.verbose(f"Searching globals for workspace {name}")
+ globals = await self.get_globals(id)
+ data["environments"].append(globals)
+
+ # Workspace Environments
+ workspace_environments = workspace.get("environments", [])
+ if workspace_environments:
+ self.verbose(f"Searching environments for workspace {name}")
+ for _ in workspace_environments:
+ environment_id = _["uid"]
+ environment = await self.get_environment(environment_id)
+ data["environments"].append(environment)
+
+ # Workspace Collections
+ workspace_collections = workspace.get("collections", [])
+ if workspace_collections:
+ self.verbose(f"Searching collections for workspace {name}")
+ for _ in workspace_collections:
+ collection_id = _["uid"]
+ collection = await self.get_collection(collection_id)
+ data["collections"].append(collection)
+ return data
+
+ async def get_workspace(self, workspace_id):
+ workspace = {}
+ workspace_url = f"{self.api_url}/workspaces/{workspace_id}"
+ r = await self.api_request(workspace_url)
+ if r is None:
+ return workspace
+ status_code = getattr(r, "status_code", 0)
+ try:
+ json = r.json()
+ except Exception as e:
+ self.warning(f"Failed to decode JSON for {r.url} (HTTP status: {status_code}): {e}")
+ return workspace
+ workspace = json.get("workspace", {})
+ return workspace
+
+ async def get_globals(self, workspace_id):
+ globals = {}
+ globals_url = f"{self.base_url}/workspace/{workspace_id}/globals"
+ r = await self.helpers.request(globals_url, headers=self.headers)
+ if r is None:
+ return globals
+ status_code = getattr(r, "status_code", 0)
+ try:
+ json = r.json()
+ except Exception as e:
+ self.warning(f"Failed to decode JSON for {r.url} (HTTP status: {status_code}): {e}")
+ return globals
+ globals = json.get("data", {})
+ return globals
+
+ async def get_environment(self, environment_id):
+ environment = {}
+ environment_url = f"{self.api_url}/environments/{environment_id}"
+ r = await self.api_request(environment_url)
+ if r is None:
+ return environment
+ status_code = getattr(r, "status_code", 0)
+ try:
+ json = r.json()
+ except Exception as e:
+ self.warning(f"Failed to decode JSON for {r.url} (HTTP status: {status_code}): {e}")
+ return environment
+ environment = json.get("environment", {})
+ return environment
+
+ async def get_collection(self, collection_id):
+ collection = {}
+ collection_url = f"{self.api_url}/collections/{collection_id}"
+ r = await self.api_request(collection_url)
+ if r is None:
+ return collection
+ status_code = getattr(r, "status_code", 0)
+ try:
+ json = r.json()
+ except Exception as e:
+ self.warning(f"Failed to decode JSON for {r.url} (HTTP status: {status_code}): {e}")
+ return collection
+ collection = json.get("collection", {})
+ return collection
+
+ async def validate_workspace(self, workspace, environments, collections):
+ name = workspace.get("name", "")
+ full_wks = str([workspace, environments, collections])
+ in_scope_hosts = await self.scan.extract_in_scope_hostnames(full_wks)
+ if in_scope_hosts:
+ self.verbose(
+ f'Found in-scope hostname(s): "{in_scope_hosts}" in workspace {name}, it appears to be in-scope'
+ )
+ return True
+ return False
diff --git a/bbot/test/test_step_1/test_web.py b/bbot/test/test_step_1/test_web.py
index e07ed3d7d4..b88e4eaa8c 100644
--- a/bbot/test/test_step_1/test_web.py
+++ b/bbot/test/test_step_1/test_web.py
@@ -263,7 +263,7 @@ async def test_web_helpers(bbot_scanner, bbot_httpserver, httpx_mock):
finally:
await agen.aclose()
assert not results
- agen = module.api_page_iter(template_url, json=False)
+ agen = module.api_page_iter(template_url, _json=False)
try:
async for result in agen:
if result and result.text.startswith("page"):
diff --git a/bbot/test/test_step_2/module_tests/test_module_postman.py b/bbot/test/test_step_2/module_tests/test_module_postman.py
index 1066c5d731..d5b9cb3f2c 100644
--- a/bbot/test/test_step_2/module_tests/test_module_postman.py
+++ b/bbot/test/test_step_2/module_tests/test_module_postman.py
@@ -2,14 +2,50 @@
class TestPostman(ModuleTestBase):
+ config_overrides = {"modules": {"postman": {"api_key": "asdf"}}}
modules_overrides = ["postman", "speculate"]
+ async def setup_before_prep(self, module_test):
+ module_test.httpx_mock.add_response(
+ url="https://api.getpostman.com/me",
+ match_headers={"X-Api-Key": "asdf"},
+ json={
+ "user": {
+ "id": 000000,
+ "username": "test_key",
+ "email": "blacklanternsecurity@test.com",
+ "fullName": "Test Key",
+ "avatar": "",
+ "isPublic": True,
+ "teamId": 0,
+ "teamDomain": "",
+ "roles": ["user"],
+ },
+ "operations": [
+ {"name": "api_object_usage", "limit": 3, "usage": 0, "overage": 0},
+ {"name": "collection_run_limit", "limit": 25, "usage": 0, "overage": 0},
+ {"name": "file_storage_limit", "limit": 20, "usage": 0, "overage": 0},
+ {"name": "flow_count", "limit": 5, "usage": 0, "overage": 0},
+ {"name": "flow_requests", "limit": 5000, "usage": 0, "overage": 0},
+ {"name": "performance_test_limit", "limit": 25, "usage": 0, "overage": 0},
+ {"name": "postbot_calls", "limit": 50, "usage": 0, "overage": 0},
+ {"name": "reusable_packages", "limit": 3, "usage": 0, "overage": 0},
+ {"name": "test_data_retrieval", "limit": 1000, "usage": 0, "overage": 0},
+ {"name": "test_data_storage", "limit": 10, "usage": 0, "overage": 0},
+ {"name": "mock_usage", "limit": 1000, "usage": 0, "overage": 0},
+ {"name": "monitor_request_runs", "limit": 1000, "usage": 0, "overage": 0},
+ {"name": "api_usage", "limit": 1000, "usage": 0, "overage": 0},
+ ],
+ },
+ )
+
async def setup_after_prep(self, module_test):
await module_test.mock_dns(
{"blacklanternsecurity.com": {"A": ["127.0.0.99"]}, "github.com": {"A": ["127.0.0.99"]}}
)
module_test.httpx_mock.add_response(
url="https://www.postman.com/_api/ws/proxy",
+ match_content=b'{"service": "search", "method": "POST", "path": "/search-all", "body": {"queryIndices": ["collaboration.workspace"], "queryText": "blacklanternsecurity", "size": 25, "from": 0, "clientTraceId": "", "requestOrigin": "srp", "mergeEntities": "true", "nonNestedRequests": "true", "domain": "public"}}',
json={
"data": [
{
@@ -62,14 +98,65 @@ async def setup_after_prep(self, module_test):
"documentType": "workspace",
},
"highlight": {"summary": "BLS BBOT api test."},
- }
+ },
+ {
+ "score": 611.41156,
+ "normalizedScore": 23,
+ "document": {
+ "watcherCount": 6,
+ "apiCount": 0,
+ "forkCount": 0,
+ "isblacklisted": "false",
+ "createdAt": "2021-06-15T14:03:51",
+ "publishertype": "team",
+ "publisherHandle": "testteam",
+ "id": "11498add-357d-4bc5-a008-0a2d44fb8829",
+ "slug": "testing-bbot-api",
+ "updatedAt": "2024-07-30T11:00:35",
+ "entityType": "workspace",
+ "visibilityStatus": "public",
+ "forkcount": "0",
+ "tags": [],
+ "createdat": "2021-06-15T14:03:51",
+ "forkLabel": "",
+ "publisherName": "testteam",
+ "name": "Test BlackLanternSecurity API Team Workspace",
+ "dependencyCount": 7,
+ "collectionCount": 6,
+ "warehouse__updated_at": "2024-07-30 11:00:00",
+ "privateNetworkFolders": [],
+ "isPublisherVerified": False,
+ "publisherType": "team",
+ "curatedInList": [],
+ "creatorId": "6900157",
+ "description": "",
+ "forklabel": "",
+ "publisherId": "299401",
+ "publisherLogo": "",
+ "popularity": 5,
+ "isPublic": True,
+ "categories": [],
+ "universaltags": "",
+ "views": 5788,
+ "summary": "Private test of BBOTs public API",
+ "memberCount": 2,
+ "isBlacklisted": False,
+ "publisherid": "299401",
+ "isPrivateNetworkEntity": False,
+ "isDomainNonTrivial": True,
+ "privateNetworkMeta": "",
+ "updatedat": "2021-10-20T16:19:29",
+ "documentType": "workspace",
+ },
+ "highlight": {"summary": "Private test of BBOTs Public API"},
+ },
],
"meta": {
"queryText": "blacklanternsecurity",
"total": {
"collection": 0,
"request": 0,
- "workspace": 1,
+ "workspace": 2,
"api": 0,
"team": 0,
"user": 0,
@@ -78,7 +165,7 @@ async def setup_after_prep(self, module_test):
"privateNetworkFolder": 0,
},
"state": "AQ4",
- "spellCorrection": {"count": {"all": 1, "workspace": 1}, "correctedQueryText": None},
+ "spellCorrection": {"count": {"all": 2, "workspace": 2}, "correctedQueryText": None},
"featureFlags": {
"enabledPublicResultCuration": True,
"boostByPopularity": True,
@@ -88,6 +175,253 @@ async def setup_after_prep(self, module_test):
},
},
)
+ module_test.httpx_mock.add_response(
+ url="https://www.postman.com/_api/ws/proxy",
+ match_content=b'{"service": "workspaces", "method": "GET", "path": "/workspaces?handle=blacklanternsecurity&slug=bbot-public"}',
+ json={
+ "meta": {"model": "workspace", "action": "find", "nextCursor": ""},
+ "data": [
+ {
+ "id": "3a7e4bdc-7ff7-4dd4-8eaa-61ddce1c3d1b",
+ "name": "BlackLanternSecurity BBOT [Public]",
+ "description": None,
+ "summary": "BLS public workspaces.",
+ "createdBy": "299401",
+ "updatedBy": "299401",
+ "team": None,
+ "createdAt": "2021-10-20T16:19:29",
+ "updatedAt": "2021-10-20T16:19:29",
+ "visibilityStatus": "public",
+ "profileInfo": {
+ "slug": "bbot-public",
+ "profileType": "team",
+ "profileId": "000000",
+ "publicHandle": "https://www.postman.com/blacklanternsecurity",
+ "publicImageURL": "",
+ "publicName": "BlackLanternSecurity",
+ "isVerified": False,
+ },
+ }
+ ],
+ },
+ )
+ module_test.httpx_mock.add_response(
+ url="https://www.postman.com/_api/ws/proxy",
+ match_content=b'{"service": "workspaces", "method": "GET", "path": "/workspaces?handle=testteam&slug=testing-bbot-api"}',
+ json={
+ "meta": {"model": "workspace", "action": "find", "nextCursor": ""},
+ "data": [
+ {
+ "id": "a4dfe981-2593-4f0b-b4c3-5145e8640f7d",
+ "name": "Test BlackLanternSecurity API Team Workspace",
+ "description": None,
+ "summary": "Private test of BBOTs public API",
+ "createdBy": "299401",
+ "updatedBy": "299401",
+ "team": None,
+ "createdAt": "2021-10-20T16:19:29",
+ "updatedAt": "2021-10-20T16:19:29",
+ "visibilityStatus": "public",
+ "profileInfo": {
+ "slug": "bbot-public",
+ "profileType": "team",
+ "profileId": "000000",
+ "publicHandle": "https://www.postman.com/testteam",
+ "publicImageURL": "",
+ "publicName": "testteam",
+ "isVerified": False,
+ },
+ }
+ ],
+ },
+ )
+ module_test.httpx_mock.add_response(
+ url="https://api.getpostman.com/workspaces/3a7e4bdc-7ff7-4dd4-8eaa-61ddce1c3d1b",
+ match_headers={"X-Api-Key": "asdf"},
+ json={
+ "workspace": {
+ "id": "3a7e4bdc-7ff7-4dd4-8eaa-61ddce1c3d1b",
+ "name": "BlackLanternSecurity BBOT [Public]",
+ "type": "personal",
+ "description": None,
+ "visibility": "public",
+ "createdBy": "00000000",
+ "updatedBy": "00000000",
+ "createdAt": "2021-11-17T06:09:01.000Z",
+ "updatedAt": "2021-11-17T08:57:16.000Z",
+ "collections": [
+ {
+ "id": "2aab9fd0-3715-4abe-8bb0-8cb0264d023f",
+ "name": "BBOT Public",
+ "uid": "10197090-2aab9fd0-3715-4abe-8bb0-8cb0264d023f",
+ },
+ ],
+ "environments": [
+ {
+ "id": "f770f816-9c6a-40f7-bde3-c0855d2a1089",
+ "name": "BBOT Test",
+ "uid": "10197090-f770f816-9c6a-40f7-bde3-c0855d2a1089",
+ }
+ ],
+ "apis": [],
+ }
+ },
+ )
+ module_test.httpx_mock.add_response(
+ url="https://api.getpostman.com/workspaces/a4dfe981-2593-4f0b-b4c3-5145e8640f7d",
+ json={
+ "workspace": {
+ "id": "a4dfe981-2593-4f0b-b4c3-5145e8640f7d",
+ "name": "Test BlackLanternSecurity API Team Workspace",
+ "type": "personal",
+ "description": None,
+ "visibility": "public",
+ "createdBy": "00000000",
+ "updatedBy": "00000000",
+ "createdAt": "2021-11-17T06:09:01.000Z",
+ "updatedAt": "2021-11-17T08:57:16.000Z",
+ "collections": [
+ {
+ "id": "f46bebfd-420a-4adf-97d1-6fb5a02cf7fc",
+ "name": "BBOT Public",
+ "uid": "10197090-f46bebfd-420a-4adf-97d1-6fb5a02cf7fc",
+ },
+ ],
+ "environments": [],
+ "apis": [],
+ }
+ },
+ )
+ module_test.httpx_mock.add_response(
+ url="https://www.postman.com/_api/workspace/3a7e4bdc-7ff7-4dd4-8eaa-61ddce1c3d1b/globals",
+ json={
+ "model_id": "8be7574b-219f-49e0-8d25-da447a882e4e",
+ "meta": {"model": "globals", "action": "find"},
+ "data": {
+ "workspace": "3a7e4bdc-7ff7-4dd4-8eaa-61ddce1c3d1b",
+ "lastUpdatedBy": "00000000",
+ "lastRevision": 1637239113000,
+ "id": "8be7574b-219f-49e0-8d25-da447a882e4e",
+ "values": [
+ {
+ "key": "endpoint_url",
+ "value": "https://api.blacklanternsecurity.com/",
+ "enabled": True,
+ },
+ ],
+ "createdAt": "2021-11-17T06:09:01.000Z",
+ "updatedAt": "2021-11-18T12:38:33.000Z",
+ },
+ },
+ )
+ module_test.httpx_mock.add_response(
+ url="https://www.postman.com/_api/workspace/a4dfe981-2593-4f0b-b4c3-5145e8640f7d/globals",
+ json={
+ "model_id": "8be7574b-219f-49e0-8d25-da447a882e4e",
+ "meta": {"model": "globals", "action": "find"},
+ "data": {
+ "workspace": "a4dfe981-2593-4f0b-b4c3-5145e8640f7d",
+ "lastUpdatedBy": "00000000",
+ "lastRevision": 1637239113000,
+ "id": "8be7574b-219f-49e0-8d25-da447a882e4e",
+ "values": [],
+ "createdAt": "2021-11-17T06:09:01.000Z",
+ "updatedAt": "2021-11-18T12:38:33.000Z",
+ },
+ },
+ )
+ module_test.httpx_mock.add_response(
+ url="https://api.getpostman.com/environments/10197090-f770f816-9c6a-40f7-bde3-c0855d2a1089",
+ match_headers={"X-Api-Key": "asdf"},
+ json={
+ "environment": {
+ "id": "f770f816-9c6a-40f7-bde3-c0855d2a1089",
+ "name": "BBOT Test",
+ "owner": "00000000",
+ "createdAt": "2021-11-17T06:29:54.000Z",
+ "updatedAt": "2021-11-23T07:06:53.000Z",
+ "values": [
+ {
+ "key": "temp_session_endpoint",
+ "value": "https://api.blacklanternsecurity.com/",
+ "enabled": True,
+ },
+ ],
+ "isPublic": True,
+ }
+ },
+ )
+ module_test.httpx_mock.add_response(
+ url="https://api.getpostman.com/collections/10197090-2aab9fd0-3715-4abe-8bb0-8cb0264d023f",
+ match_headers={"X-Api-Key": "asdf"},
+ json={
+ "collection": {
+ "info": {
+ "_postman_id": "62b91565-d2e2-4bcd-8248-4dba2e3452f0",
+ "name": "BBOT Public",
+ "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
+ "updatedAt": "2021-11-17T07:13:16.000Z",
+ "createdAt": "2021-11-17T07:13:15.000Z",
+ "lastUpdatedBy": "00000000",
+ "uid": "00000000-62b91565-d2e2-4bcd-8248-4dba2e3452f0",
+ },
+ "item": [
+ {
+ "name": "Generate API Session",
+ "id": "c1bac38c-dfc9-4cc0-9c19-828cbc8543b1",
+ "protocolProfileBehavior": {"disableBodyPruning": True},
+ "request": {
+ "method": "POST",
+ "header": [{"key": "Content-Type", "value": "application/json"}],
+ "body": {
+ "mode": "raw",
+ "raw": '{"username": "test", "password": "Test"}',
+ },
+ "url": {"raw": "{{endpoint_url}}", "host": ["{{endpoint_url}}"]},
+ "description": "",
+ },
+ "response": [],
+ "uid": "10197090-c1bac38c-dfc9-4cc0-9c19-828cbc8543b1",
+ },
+ ],
+ }
+ },
+ )
+ module_test.httpx_mock.add_response(
+ url="https://api.getpostman.com/collections/10197090-f46bebfd-420a-4adf-97d1-6fb5a02cf7fc",
+ json={
+ "collection": {
+ "info": {
+ "_postman_id": "f46bebfd-420a-4adf-97d1-6fb5a02cf7fc",
+ "name": "BBOT Public",
+ "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
+ "updatedAt": "2021-11-17T07:13:16.000Z",
+ "createdAt": "2021-11-17T07:13:15.000Z",
+ "lastUpdatedBy": "00000000",
+ "uid": "00000000-f46bebfd-420a-4adf-97d1-6fb5a02cf7fc",
+ },
+ "item": [
+ {
+ "name": "Out of Scope API request",
+ "id": "c1bac38c-dfc9-4cc0-9c19-828cbc8543b1",
+ "protocolProfileBehavior": {"disableBodyPruning": True},
+ "request": {
+ "method": "POST",
+ "header": [{"key": "Content-Type", "value": "application/json"}],
+ "body": {
+ "mode": "raw",
+ "raw": '{"username": "test", "password": "Test"}',
+ },
+ "url": {"raw": "https://www.outofscope.com", "host": ["www.outofscope.com"]},
+ "description": "",
+ },
+ "response": [],
+ "uid": "10197090-c1bac38c-dfc9-4cc0-9c19-828cbc8543b1",
+ },
+ ],
+ }
+ },
+ )
def check(self, module_test, events):
assert len(events) == 5
@@ -101,6 +435,7 @@ def check(self, module_test, events):
assert 1 == len(
[e for e in events if e.type == "ORG_STUB" and e.data == "blacklanternsecurity" and e.scope_distance == 0]
), "Failed to find ORG_STUB"
+ # Find only 1 in-scope workspace the other will be out of scope
assert 1 == len(
[
e
diff --git a/bbot/test/test_step_2/module_tests/test_module_postman_download.py b/bbot/test/test_step_2/module_tests/test_module_postman_download.py
index f937ebc20f..4a893601a7 100644
--- a/bbot/test/test_step_2/module_tests/test_module_postman_download.py
+++ b/bbot/test/test_step_2/module_tests/test_module_postman_download.py
@@ -45,7 +45,7 @@ async def setup_after_prep(self, module_test):
)
module_test.httpx_mock.add_response(
url="https://www.postman.com/_api/ws/proxy",
- match_content=b'{"service": "search", "method": "POST", "path": "/search-all", "body": {"queryIndices": ["collaboration.workspace"], "queryText": "blacklanternsecurity", "size": 100, "from": 0, "clientTraceId": "", "requestOrigin": "srp", "mergeEntities": "true", "nonNestedRequests": "true", "domain": "public"}}',
+ match_content=b'{"service": "search", "method": "POST", "path": "/search-all", "body": {"queryIndices": ["collaboration.workspace"], "queryText": "blacklanternsecurity", "size": 25, "from": 0, "clientTraceId": "", "requestOrigin": "srp", "mergeEntities": "true", "nonNestedRequests": "true", "domain": "public"}}',
json={
"data": [
{
@@ -99,64 +99,13 @@ async def setup_after_prep(self, module_test):
},
"highlight": {"summary": "BLS BBOT api test."},
},
- {
- "score": 611.41156,
- "normalizedScore": 23,
- "document": {
- "watcherCount": 6,
- "apiCount": 0,
- "forkCount": 0,
- "isblacklisted": "false",
- "createdAt": "2021-06-15T14:03:51",
- "publishertype": "team",
- "publisherHandle": "testteam",
- "id": "11498add-357d-4bc5-a008-0a2d44fb8829",
- "slug": "testing-bbot-api",
- "updatedAt": "2024-07-30T11:00:35",
- "entityType": "workspace",
- "visibilityStatus": "public",
- "forkcount": "0",
- "tags": [],
- "createdat": "2021-06-15T14:03:51",
- "forkLabel": "",
- "publisherName": "testteam",
- "name": "Test BlackLanternSecurity API Team Workspace",
- "dependencyCount": 7,
- "collectionCount": 6,
- "warehouse__updated_at": "2024-07-30 11:00:00",
- "privateNetworkFolders": [],
- "isPublisherVerified": False,
- "publisherType": "team",
- "curatedInList": [],
- "creatorId": "6900157",
- "description": "",
- "forklabel": "",
- "publisherId": "299401",
- "publisherLogo": "",
- "popularity": 5,
- "isPublic": True,
- "categories": [],
- "universaltags": "",
- "views": 5788,
- "summary": "Private test of BBOTs public API",
- "memberCount": 2,
- "isBlacklisted": False,
- "publisherid": "299401",
- "isPrivateNetworkEntity": False,
- "isDomainNonTrivial": True,
- "privateNetworkMeta": "",
- "updatedat": "2021-10-20T16:19:29",
- "documentType": "workspace",
- },
- "highlight": {"summary": "Private test of BBOTs Public API"},
- },
],
"meta": {
"queryText": "blacklanternsecurity",
"total": {
"collection": 0,
"request": 0,
- "workspace": 2,
+ "workspace": 1,
"api": 0,
"team": 0,
"user": 0,
@@ -165,7 +114,7 @@ async def setup_after_prep(self, module_test):
"privateNetworkFolder": 0,
},
"state": "AQ4",
- "spellCorrection": {"count": {"all": 2, "workspace": 2}, "correctedQueryText": None},
+ "spellCorrection": {"count": {"all": 1, "workspace": 1}, "correctedQueryText": None},
"featureFlags": {
"enabledPublicResultCuration": True,
"boostByPopularity": True,
@@ -205,36 +154,6 @@ async def setup_after_prep(self, module_test):
],
},
)
- module_test.httpx_mock.add_response(
- url="https://www.postman.com/_api/ws/proxy",
- match_content=b'{"service": "workspaces", "method": "GET", "path": "/workspaces?handle=testteam&slug=testing-bbot-api"}',
- json={
- "meta": {"model": "workspace", "action": "find", "nextCursor": ""},
- "data": [
- {
- "id": "a4dfe981-2593-4f0b-b4c3-5145e8640f7d",
- "name": "Test BlackLanternSecurity API Team Workspace",
- "description": None,
- "summary": "Private test of BBOTs public API",
- "createdBy": "299401",
- "updatedBy": "299401",
- "team": None,
- "createdAt": "2021-10-20T16:19:29",
- "updatedAt": "2021-10-20T16:19:29",
- "visibilityStatus": "public",
- "profileInfo": {
- "slug": "bbot-public",
- "profileType": "team",
- "profileId": "000000",
- "publicHandle": "https://www.postman.com/testteam",
- "publicImageURL": "",
- "publicName": "testteam",
- "isVerified": False,
- },
- }
- ],
- },
- )
module_test.httpx_mock.add_response(
url="https://api.getpostman.com/workspaces/3a7e4bdc-7ff7-4dd4-8eaa-61ddce1c3d1b",
match_headers={"X-Api-Key": "asdf"},
@@ -267,31 +186,6 @@ async def setup_after_prep(self, module_test):
}
},
)
- module_test.httpx_mock.add_response(
- url="https://api.getpostman.com/workspaces/a4dfe981-2593-4f0b-b4c3-5145e8640f7d",
- json={
- "workspace": {
- "id": "a4dfe981-2593-4f0b-b4c3-5145e8640f7d",
- "name": "Test BlackLanternSecurity API Team Workspace",
- "type": "personal",
- "description": None,
- "visibility": "public",
- "createdBy": "00000000",
- "updatedBy": "00000000",
- "createdAt": "2021-11-17T06:09:01.000Z",
- "updatedAt": "2021-11-17T08:57:16.000Z",
- "collections": [
- {
- "id": "f46bebfd-420a-4adf-97d1-6fb5a02cf7fc",
- "name": "BBOT Public",
- "uid": "10197090-f46bebfd-420a-4adf-97d1-6fb5a02cf7fc",
- },
- ],
- "environments": [],
- "apis": [],
- }
- },
- )
module_test.httpx_mock.add_response(
url="https://www.postman.com/_api/workspace/3a7e4bdc-7ff7-4dd4-8eaa-61ddce1c3d1b/globals",
json={
@@ -314,22 +208,6 @@ async def setup_after_prep(self, module_test):
},
},
)
- module_test.httpx_mock.add_response(
- url="https://www.postman.com/_api/workspace/a4dfe981-2593-4f0b-b4c3-5145e8640f7d/globals",
- json={
- "model_id": "8be7574b-219f-49e0-8d25-da447a882e4e",
- "meta": {"model": "globals", "action": "find"},
- "data": {
- "workspace": "a4dfe981-2593-4f0b-b4c3-5145e8640f7d",
- "lastUpdatedBy": "00000000",
- "lastRevision": 1637239113000,
- "id": "8be7574b-219f-49e0-8d25-da447a882e4e",
- "values": [],
- "createdAt": "2021-11-17T06:09:01.000Z",
- "updatedAt": "2021-11-18T12:38:33.000Z",
- },
- },
- )
module_test.httpx_mock.add_response(
url="https://api.getpostman.com/environments/10197090-f770f816-9c6a-40f7-bde3-c0855d2a1089",
match_headers={"X-Api-Key": "asdf"},
@@ -387,44 +265,9 @@ async def setup_after_prep(self, module_test):
}
},
)
- module_test.httpx_mock.add_response(
- url="https://api.getpostman.com/collections/10197090-f46bebfd-420a-4adf-97d1-6fb5a02cf7fc",
- json={
- "collection": {
- "info": {
- "_postman_id": "f46bebfd-420a-4adf-97d1-6fb5a02cf7fc",
- "name": "BBOT Public",
- "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
- "updatedAt": "2021-11-17T07:13:16.000Z",
- "createdAt": "2021-11-17T07:13:15.000Z",
- "lastUpdatedBy": "00000000",
- "uid": "00000000-f46bebfd-420a-4adf-97d1-6fb5a02cf7fc",
- },
- "item": [
- {
- "name": "Out of Scope API request",
- "id": "c1bac38c-dfc9-4cc0-9c19-828cbc8543b1",
- "protocolProfileBehavior": {"disableBodyPruning": True},
- "request": {
- "method": "POST",
- "header": [{"key": "Content-Type", "value": "application/json"}],
- "body": {
- "mode": "raw",
- "raw": '{"username": "test", "password": "Test"}',
- },
- "url": {"raw": "https://www.outofscope.com", "host": ["www.outofscope.com"]},
- "description": "",
- },
- "response": [],
- "uid": "10197090-c1bac38c-dfc9-4cc0-9c19-828cbc8543b1",
- },
- ],
- }
- },
- )
def check(self, module_test, events):
- assert 2 == len(
+ assert 1 == len(
[e for e in events if e.type == "CODE_REPOSITORY" and "postman" in e.tags and e.scope_distance == 1]
), "Failed to find blacklanternsecurity postman workspace"
assert 1 == len(
diff --git a/bbot/test/test_step_2/module_tests/test_module_trufflehog.py b/bbot/test/test_step_2/module_tests/test_module_trufflehog.py
index 7f78977f1e..d8594cd34c 100644
--- a/bbot/test/test_step_2/module_tests/test_module_trufflehog.py
+++ b/bbot/test/test_step_2/module_tests/test_module_trufflehog.py
@@ -848,7 +848,7 @@ async def setup_before_prep(self, module_test):
async def setup_after_prep(self, module_test):
module_test.httpx_mock.add_response(
url="https://www.postman.com/_api/ws/proxy",
- match_content=b'{"service": "search", "method": "POST", "path": "/search-all", "body": {"queryIndices": ["collaboration.workspace"], "queryText": "blacklanternsecurity", "size": 100, "from": 0, "clientTraceId": "", "requestOrigin": "srp", "mergeEntities": "true", "nonNestedRequests": "true", "domain": "public"}}',
+ match_content=b'{"service": "search", "method": "POST", "path": "/search-all", "body": {"queryIndices": ["collaboration.workspace"], "queryText": "blacklanternsecurity", "size": 25, "from": 0, "clientTraceId": "", "requestOrigin": "srp", "mergeEntities": "true", "nonNestedRequests": "true", "domain": "public"}}',
json={
"data": [
{