diff --git a/tavily/async_tavily.py b/tavily/async_tavily.py index 4da6194..95abfe0 100644 --- a/tavily/async_tavily.py +++ b/tavily/async_tavily.py @@ -50,6 +50,34 @@ def __init__(self, api_key: Optional[str] = None, ) self._company_info_tags = company_info_tags + def _raise_for_status_secure(self, response): + """ + Secure version of response.raise_for_status() that prevents API key exposure. + + This method sanitizes the error message to remove sensitive information like + API keys from Authorization headers before raising the exception. + + Security Note: This prevents API key exposure in logs, error tracking systems, + and debug output when HTTP errors occur. + """ + if response.status_code >= 400: + # Create a sanitized error message without exposing sensitive headers + error_msg = f"{response.status_code} {response.reason_phrase} for url: {response.url}" + + # Create the appropriate exception type based on status code + if response.status_code == 400: + raise httpx.HTTPStatusError(error_msg, request=response.request, response=response) + elif response.status_code == 401: + raise httpx.HTTPStatusError(error_msg, request=response.request, response=response) + elif response.status_code == 403: + raise httpx.HTTPStatusError(error_msg, request=response.request, response=response) + elif response.status_code == 404: + raise httpx.HTTPStatusError(error_msg, request=response.request, response=response) + elif response.status_code >= 500: + raise httpx.HTTPStatusError(error_msg, request=response.request, response=response) + else: + raise httpx.HTTPStatusError(error_msg, request=response.request, response=response) + async def _search( self, query: str, @@ -116,7 +144,7 @@ async def _search( elif response.status_code == 400: raise BadRequestError(detail) else: - raise response.raise_for_status() + self._raise_for_status_secure(response) async def search(self, query: str, @@ -211,7 +239,7 @@ async def _extract( elif response.status_code == 400: raise BadRequestError(detail) else: - raise response.raise_for_status() + self._raise_for_status_secure(response) async def extract(self, urls: Union[List[str], str], # Accept a list of URLs or a single URL @@ -310,7 +338,7 @@ async def _crawl(self, elif response.status_code == 400: raise BadRequestError(detail) else: - raise response.raise_for_status() + self._raise_for_status_secure(response) async def crawl(self, url: str, @@ -419,7 +447,7 @@ async def _map(self, elif response.status_code == 400: raise BadRequestError(detail) else: - raise response.raise_for_status() + self._raise_for_status_secure(response) async def map(self, url: str, diff --git a/tavily/tavily.py b/tavily/tavily.py index 7c8d491..577acdb 100644 --- a/tavily/tavily.py +++ b/tavily/tavily.py @@ -36,6 +36,34 @@ def __init__(self, api_key: Optional[str] = None, proxies: Optional[dict[str, st "X-Client-Source": "tavily-python" } + def _raise_for_status_secure(self, response): + """ + Secure version of response.raise_for_status() that prevents API key exposure. + + This method sanitizes the error message to remove sensitive information like + API keys from Authorization headers before raising the exception. + + Security Note: This prevents API key exposure in logs, error tracking systems, + and debug output when HTTP errors occur. + """ + if response.status_code >= 400: + # Create a sanitized error message without exposing sensitive headers + error_msg = f"{response.status_code} {response.reason} for url: {response.url}" + + # Create the appropriate exception type based on status code + if response.status_code == 400: + raise requests.exceptions.HTTPError(error_msg, response=response) + elif response.status_code == 401: + raise requests.exceptions.HTTPError(error_msg, response=response) + elif response.status_code == 403: + raise requests.exceptions.HTTPError(error_msg, response=response) + elif response.status_code == 404: + raise requests.exceptions.HTTPError(error_msg, response=response) + elif response.status_code >= 500: + raise requests.exceptions.HTTPError(error_msg, response=response) + else: + raise requests.exceptions.HTTPError(error_msg, response=response) + def _search(self, query: str, search_depth: Literal["basic", "advanced"] = None, @@ -102,7 +130,7 @@ def _search(self, raise BadRequestError(detail) else: - raise response.raise_for_status() + self._raise_for_status_secure(response) def search(self, @@ -195,7 +223,7 @@ def _extract(self, elif response.status_code == 400: raise BadRequestError(detail) else: - raise response.raise_for_status() + self._raise_for_status_secure(response) def extract(self, urls: Union[List[str], str], # Accept a list of URLs or a single URL @@ -293,7 +321,7 @@ def _crawl(self, elif response.status_code == 400: raise BadRequestError(detail) else: - raise response.raise_for_status() + self._raise_for_status_secure(response) def crawl(self, url: str, @@ -402,7 +430,7 @@ def _map(self, elif response.status_code == 400: raise BadRequestError(detail) else: - raise response.raise_for_status() + self._raise_for_status_secure(response) def map(self, url: str,