diff --git a/tests/discovery/test_bitbucket.py b/tests/discovery/test_bitbucket.py new file mode 100644 index 00000000..f569d558 --- /dev/null +++ b/tests/discovery/test_bitbucket.py @@ -0,0 +1,23 @@ +import logging +from unittest.mock import AsyncMock + +import pytest + +from theHarvester.discovery import bitbucket + + +@pytest.mark.asyncio +async def test_process_does_not_log_error_body(monkeypatch, caplog) -> None: + monkeypatch.setattr(bitbucket.Core, 'bitbucket_key', lambda: 'test-key') + search = bitbucket.SearchBitBucket('owner/repository', limit=10) + monkeypatch.setattr( + search, + 'do_search', + AsyncMock(return_value=('', {'secret': 'provider-secret-payload'}, 500, {})), + ) + caplog.set_level(logging.INFO, logger=bitbucket.__name__) + + await search.process() + + assert 'provider-secret-payload' not in caplog.text + assert '500' in caplog.text diff --git a/tests/discovery/test_criminalip.py b/tests/discovery/test_criminalip.py index 6799e44f..973f9921 100644 --- a/tests/discovery/test_criminalip.py +++ b/tests/discovery/test_criminalip.py @@ -1,10 +1,29 @@ #!/usr/bin/env python3 # coding=utf-8 +import logging + import pytest from theHarvester.discovery import criminalip +@pytest.mark.asyncio +async def test_failed_response_body_is_not_logged(monkeypatch, caplog) -> None: + monkeypatch.setattr(criminalip.Core, 'criminalip_key', lambda: 'test-key') + monkeypatch.setattr(criminalip.Core, 'get_user_agent', lambda: 'test-agent') + + async def fake_post_fetch(*args, **kwargs): + return {'status': 500, 'secret': 'provider-secret-payload'} + + monkeypatch.setattr(criminalip.AsyncFetcher, 'post_fetch', fake_post_fetch) + caplog.set_level(logging.INFO, logger=criminalip.__name__) + + await criminalip.SearchCriminalIP('example.com').process() + + assert 'provider-secret-payload' not in caplog.text + assert '500' in caplog.text + + @pytest.mark.asyncio async def test_parser_handles_missing_legacy_fields(monkeypatch) -> None: monkeypatch.setattr(criminalip.Core, 'criminalip_key', lambda: 'test-key') diff --git a/tests/discovery/test_githubcode_logging.py b/tests/discovery/test_githubcode_logging.py new file mode 100644 index 00000000..34cb8f71 --- /dev/null +++ b/tests/discovery/test_githubcode_logging.py @@ -0,0 +1,23 @@ +import logging +from unittest.mock import AsyncMock + +import pytest + +from theHarvester.discovery import githubcode + + +@pytest.mark.asyncio +async def test_process_does_not_log_error_body(monkeypatch, caplog) -> None: + monkeypatch.setattr(githubcode.Core, 'github_key', lambda: 'test-key') + search = githubcode.SearchGithubCode(word='test', limit=10) + monkeypatch.setattr( + search, + 'do_search', + AsyncMock(return_value=('', {'secret': 'provider-secret-payload'}, 500, {})), + ) + caplog.set_level(logging.INFO, logger=githubcode.__name__) + + await search.process() + + assert 'provider-secret-payload' not in caplog.text + assert '500' in caplog.text diff --git a/tests/discovery/test_leakix.py b/tests/discovery/test_leakix.py new file mode 100644 index 00000000..f63020e7 --- /dev/null +++ b/tests/discovery/test_leakix.py @@ -0,0 +1,22 @@ +import logging + +import pytest + +from theHarvester.discovery import leakix + + +@pytest.mark.asyncio +async def test_authentication_response_body_is_not_logged(monkeypatch, caplog) -> None: + monkeypatch.setattr(leakix.Core, 'leakix_key', lambda: None) + monkeypatch.setattr(leakix.Core, 'get_user_agent', lambda: 'test-agent') + + async def fake_fetch_all(*args, **kwargs): + return ['Incorrect API Key: provider-secret-payload'] + + monkeypatch.setattr(leakix.AsyncFetcher, 'fetch_all', fake_fetch_all) + caplog.set_level(logging.INFO, logger=leakix.__name__) + + await leakix.SearchLeakix('example.com').process() + + assert 'provider-secret-payload' not in caplog.text + assert 'requires authentication' in caplog.text diff --git a/tests/discovery/test_onyphe.py b/tests/discovery/test_onyphe.py new file mode 100644 index 00000000..2179bdca --- /dev/null +++ b/tests/discovery/test_onyphe.py @@ -0,0 +1,39 @@ +import logging + +import pytest + +from theHarvester.discovery import onyphe + + +@pytest.mark.asyncio +async def test_failed_response_body_is_not_logged(monkeypatch, caplog) -> None: + monkeypatch.setattr(onyphe.Core, 'onyphe_key', lambda: 'test-key') + monkeypatch.setattr(onyphe.Core, 'get_user_agent', lambda: 'test-agent') + + async def fake_fetch_all(*args, **kwargs): + return [{'text': 'Failed', 'secret': 'provider-secret-payload'}] + + monkeypatch.setattr(onyphe.AsyncFetcher, 'fetch_all', fake_fetch_all) + caplog.set_level(logging.INFO, logger=onyphe.__name__) + + search = onyphe.SearchOnyphe('example.com') + await search.process() + + assert 'provider-secret-payload' not in caplog.text + assert 'did not succeed' in caplog.text + + +@pytest.mark.asyncio +async def test_unexpected_response_body_is_not_in_error(monkeypatch) -> None: + monkeypatch.setattr(onyphe.Core, 'onyphe_key', lambda: 'test-key') + monkeypatch.setattr(onyphe.Core, 'get_user_agent', lambda: 'test-agent') + + async def fake_fetch_all(*args, **kwargs): + return ['provider-secret-payload'] + + monkeypatch.setattr(onyphe.AsyncFetcher, 'fetch_all', fake_fetch_all) + + with pytest.raises(TypeError) as error: + await onyphe.SearchOnyphe('example.com').process() + + assert 'provider-secret-payload' not in str(error.value) diff --git a/tests/discovery/test_pentesttools.py b/tests/discovery/test_pentesttools.py new file mode 100644 index 00000000..e1b7fea4 --- /dev/null +++ b/tests/discovery/test_pentesttools.py @@ -0,0 +1,33 @@ +import logging + +import pytest + +from theHarvester.discovery import pentesttools + + +@pytest.mark.asyncio +async def test_status_error_payload_is_not_logged(monkeypatch, caplog) -> None: + monkeypatch.setattr(pentesttools.Core, 'pentest_tools_key', lambda: 'test-key') + + responses = iter( + [ + '{"op_status":"success","scan_id":"scan-1"}', + '{"op_status":"error","error":"provider-secret-payload","details":"private target data"}', + ] + ) + + async def fake_post_fetch(*args, **kwargs): + return next(responses) + + async def no_sleep(*args, **kwargs): + return None + + monkeypatch.setattr(pentesttools.AsyncFetcher, 'post_fetch', fake_post_fetch) + monkeypatch.setattr(pentesttools.asyncio, 'sleep', no_sleep) + caplog.set_level(logging.INFO, logger=pentesttools.__name__) + + await pentesttools.SearchPentestTools('example.com').process() + + assert 'provider-secret-payload' not in caplog.text + assert 'private target data' not in caplog.text + assert 'get_scan_status failed' in caplog.text diff --git a/tests/discovery/test_search_dehashed.py b/tests/discovery/test_search_dehashed.py index 787f40e1..f58ea03f 100644 --- a/tests/discovery/test_search_dehashed.py +++ b/tests/discovery/test_search_dehashed.py @@ -1,5 +1,8 @@ +import logging + import pytest +from theHarvester.discovery import search_dehashed from theHarvester.discovery.search_dehashed import SearchDehashed @@ -16,3 +19,47 @@ async def test_process_does_not_output_credentials(monkeypatch, capsys) -> None: assert 'secret-password' not in capsys.readouterr().out assert await search.get_emails() == {'user@example.com'} + + +@pytest.mark.asyncio +async def test_non_json_response_body_is_not_logged(monkeypatch, caplog) -> None: + class Response: + status = 200 + + async def __aenter__(self): + return self + + async def __aexit__(self, *args): + return None + + async def json(self): + raise ValueError + + async def text(self): + return 'provider-secret-payload' + + class Session: + def __init__(self, **kwargs): + pass + + async def __aenter__(self): + return self + + async def __aexit__(self, *args): + return None + + def post(self, *args, **kwargs): + return Response() + + monkeypatch.setattr(search_dehashed.aiohttp, 'ClientSession', Session) + caplog.set_level(logging.INFO, logger=search_dehashed.__name__) + search = SearchDehashed.__new__(SearchDehashed) + search.word = 'example.com' + search.api = 'https://provider.example' + search.headers = {} + search.proxy = False + search.data = [] + + await search.do_search() + + assert 'provider-secret-payload' not in caplog.text diff --git a/tests/discovery/test_sherlockeye.py b/tests/discovery/test_sherlockeye.py index 2d3a1816..830d4416 100644 --- a/tests/discovery/test_sherlockeye.py +++ b/tests/discovery/test_sherlockeye.py @@ -1,3 +1,4 @@ +import logging import sys import types @@ -99,14 +100,14 @@ async def test_process_extracts_domain_intelligence(monkeypatch) -> None: @pytest.mark.asyncio -async def test_process_handles_api_error(monkeypatch) -> None: +async def test_process_handles_api_error(monkeypatch, caplog) -> None: monkeypatch.setattr(sherlockeye.Core, 'sherlockeye_key', lambda: 'dummy-key') class _FakeResponse: status = 401 async def text(self): - return '{"success":false,"errorCode":"UNAUTHORIZED","message":"Invalid bearer token"}' + return 'provider-secret-payload' async def __aenter__(self): return self @@ -128,6 +129,7 @@ async def test_process_handles_api_error(monkeypatch) -> None: pass monkeypatch.setattr(sherlockeye.aiohttp, 'ClientSession', _FakeSession) + caplog.set_level(logging.INFO, logger=sherlockeye.__name__) search = sherlockeye.SearchSherlockeye('example.com') await search.process() @@ -135,3 +137,43 @@ async def test_process_handles_api_error(monkeypatch) -> None: assert await search.get_hostnames() == set() assert await search.get_emails() == set() assert await search.get_ips() == set() + assert 'provider-secret-payload' not in caplog.text + assert '401' in caplog.text + + +@pytest.mark.asyncio +async def test_process_does_not_log_provider_error_message(monkeypatch, caplog) -> None: + monkeypatch.setattr(sherlockeye.Core, 'sherlockeye_key', lambda: 'dummy-key') + + class _FakeResponse: + status = 200 + + async def json(self): + return {'success': False, 'message': 'provider-secret-payload'} + + async def __aenter__(self): + return self + + async def __aexit__(self, *_args): + pass + + class _FakeSession: + def __init__(self, **_kwargs): + pass + + def post(self, *_args, **_kwargs): + return _FakeResponse() + + async def __aenter__(self): + return self + + async def __aexit__(self, *_args): + pass + + monkeypatch.setattr(sherlockeye.aiohttp, 'ClientSession', _FakeSession) + caplog.set_level(logging.INFO, logger=sherlockeye.__name__) + + await sherlockeye.SearchSherlockeye('example.com').process() + + assert 'provider-secret-payload' not in caplog.text + assert 'API error' in caplog.text diff --git a/tests/discovery/test_whoisxml.py b/tests/discovery/test_whoisxml.py new file mode 100644 index 00000000..30891b2a --- /dev/null +++ b/tests/discovery/test_whoisxml.py @@ -0,0 +1,28 @@ +import logging + +import pytest + +from theHarvester.discovery import whoisxml + + +@pytest.mark.asyncio +async def test_response_body_is_not_logged_and_records_are_returned(monkeypatch, caplog) -> None: + monkeypatch.setattr(whoisxml.Core, 'whoisxml_key', lambda: 'test-key') + monkeypatch.setattr(whoisxml.Core, 'get_user_agent', lambda: 'test-agent') + + async def fake_fetch_all(*args, **kwargs): + return [ + { + 'secret': 'provider-secret-payload', + 'result': {'records': [{'domain': 'www.example.com'}]}, + } + ] + + monkeypatch.setattr(whoisxml.AsyncFetcher, 'fetch_all', fake_fetch_all) + caplog.set_level(logging.INFO, logger=whoisxml.__name__) + + search = whoisxml.SearchWhoisXML('example.com') + await search.process() + + assert await search.get_hostnames() == ['www.example.com'] + assert 'provider-secret-payload' not in caplog.text diff --git a/theHarvester/discovery/bitbucket.py b/theHarvester/discovery/bitbucket.py index 0811e04a..b97a5156 100644 --- a/theHarvester/discovery/bitbucket.py +++ b/theHarvester/discovery/bitbucket.py @@ -142,7 +142,7 @@ class SearchBitBucket: await asyncio.sleep(sleepy_time) else: # On error, stop to avoid endless retries on a bad state - logger.info(f'\tException occurred: status_code: {result.status_code} reason: {result.body}') + logger.info('\tBitbucket API request failed with status %s', result.status_code) self.page = 0 break except (aiohttp.ClientError, TimeoutError, ValueError, TypeError, AttributeError) as e: diff --git a/theHarvester/discovery/bravesearch.py b/theHarvester/discovery/bravesearch.py index 31124ce9..82afc7bc 100644 --- a/theHarvester/discovery/bravesearch.py +++ b/theHarvester/discovery/bravesearch.py @@ -67,7 +67,7 @@ class SearchBrave: await asyncio.sleep(self.rate_limit_delay) continue elif 'quota' in error_msg.lower() or error_code == 'quota_exceeded': - logger.info(f'API quota exceeded: {error_msg}') + logger.info('Brave Search API quota exceeded') break else: break diff --git a/theHarvester/discovery/chaos.py b/theHarvester/discovery/chaos.py index c2ca3aa9..3bdf5a76 100644 --- a/theHarvester/discovery/chaos.py +++ b/theHarvester/discovery/chaos.py @@ -67,7 +67,7 @@ class SearchChaos: # Check for error messages if 'error' in data: error_msg = data.get('message', data.get('error', 'Unknown error')) - logger.info(f'Chaos API error: {error_msg}') + logger.info('Chaos API returned an error') if 'unauthorized' in error_msg.lower(): raise MissingKey('Chaos (ProjectDiscovery)') return diff --git a/theHarvester/discovery/criminalip.py b/theHarvester/discovery/criminalip.py index dd3318a6..64893f38 100644 --- a/theHarvester/discovery/criminalip.py +++ b/theHarvester/discovery/criminalip.py @@ -102,15 +102,15 @@ class SearchCriminalIP: # Expected response format: # {'data': {'scan_id': scan_id}, 'message': 'api success', 'status': 200} if not isinstance(response, dict): - logger.info(f'An error has occurred searching criminalip dumping response: {response}') + logger.info('CriminalIP scan response has unexpected type: %s', type(response).__name__) return if response.get('status') != 200: - logger.info(f'An error has occurred searching criminalip dumping response: {response}') + logger.info('CriminalIP scan request failed with status %s', response.get('status')) return scan_id = response.get('data', {}).get('scan_id') if scan_id is None: - logger.info(f'CriminalIP did not return a scan_id, dumping response: {response}') + logger.info('CriminalIP scan response did not include a scan_id') return scan_percentage = 0 @@ -126,26 +126,25 @@ class SearchCriminalIP: ) status = status_response[0] if isinstance(status_response, list) and len(status_response) > 0 else {} if not isinstance(status, dict): - logger.info(f'CriminalIP status response is malformed dumping data: {status_response}') + logger.info('CriminalIP status response has unexpected type: %s', type(status).__name__) return if status.get('status') != 200: - logger.info(f'CriminalIP status check failed dumping data: status_response: {status}') + logger.info('CriminalIP status request failed with status %s', status.get('status')) return # Expected format: # {"data": {"scan_percentage": 100}, "message": "api success", "status": 200} scan_percentage = status.get('data', {}).get('scan_percentage') if scan_percentage is None: - logger.info(f'CriminalIP status did not include scan_percentage dumping data: {status}') + logger.info('CriminalIP status response did not include scan_percentage') return if scan_percentage == 100: break if scan_percentage == -2: logger.info(f'CriminalIP failed to scan: {self.word} does not exist, verify manually') - logger.info(f'Dumping data: scan_response: {response} status_response: {status}') return if scan_percentage == -1: - logger.info(f'CriminalIP scan failed dumping data: scan_response: {response} status_response: {status}') + logger.info('CriminalIP scan failed with scan_percentage -1') return # Wait for scan to finish if counter >= 5: @@ -157,9 +156,6 @@ class SearchCriminalIP: logger.info( 'Ten iterations have occurred in CriminalIP waiting for scan to finish, returning to prevent infinite loop.' ) - logger.info( - f'Verify results manually on CriminalIP dumping data: scan_response: {response} status_response: {status}' - ) return report_url = f'https://api.criminalip.io/v2/domain/report/{scan_id}' @@ -171,25 +167,23 @@ class SearchCriminalIP: ) scan = scan_response[0] if isinstance(scan_response, list) and len(scan_response) > 0 else {} if not isinstance(scan, dict): - logger.info(f'CriminalIP report response is malformed dumping data: {scan_response}') + logger.info('CriminalIP report response has unexpected type: %s', type(scan).__name__) return if scan.get('status') != 200: - logger.info(f'CriminalIP report request failed dumping data: {scan}') + logger.info('CriminalIP report request failed with status %s', scan.get('status')) return try: await self.parser(scan) except Exception as e: - logger.info(f'An exception occurred while parsing criminalip result: {e}') - logger.info('Dumping json: ') - logger.info(scan) + logger.info('CriminalIP report parsing failed with %s', type(e).__name__) async def parser(self, jlines): # TODO when new scope field is added to parse lines for potential new scope! # TODO map as_name to asn for asn data # TODO determine if worth storing interesting urls if not isinstance(jlines, dict) or 'data' not in jlines.keys() or not isinstance(jlines['data'], dict): - logger.info(f'Error with criminalip data, dumping: {jlines}') + logger.info('CriminalIP report has an unexpected structure') return data = jlines['data'] @@ -208,8 +202,7 @@ class SearchCriminalIP: for sub in subdomains: self._add_host(sub) except Exception as e: - logger.info(f'An exception has occurred: {e}') - logger.info(f'Main line: {connected_domain}') + logger.info('CriminalIP connected-domain parsing failed with %s', type(e).__name__) for ip_info in data.get('connected_ip_info', []): if not isinstance(ip_info, dict): diff --git a/theHarvester/discovery/fofa.py b/theHarvester/discovery/fofa.py index bb98c1b8..0eb7640a 100644 --- a/theHarvester/discovery/fofa.py +++ b/theHarvester/discovery/fofa.py @@ -85,7 +85,7 @@ class SearchFofa: # Check for errors if data.get('error', False): error_msg = data.get('errmsg', 'Unknown error') - logger.info(f'Fofa API error: {error_msg}') + logger.info('Fofa API returned an error') if '账号无效' in error_msg or 'invalid' in error_msg.lower(): raise MissingKey('Fofa API (Invalid credentials)') return diff --git a/theHarvester/discovery/githubcode.py b/theHarvester/discovery/githubcode.py index 94b19081..41832c4d 100644 --- a/theHarvester/discovery/githubcode.py +++ b/theHarvester/discovery/githubcode.py @@ -146,7 +146,7 @@ class SearchGithubCode: await asyncio.sleep(sleepy_time) else: # On error, stop to avoid endless retries on a bad state - logger.info(f'\tException occurred: status_code: {result.status_code} reason: {result.body}') + logger.info('\tGitHub code API request failed with status %s', result.status_code) self.page = 0 break except Exception as e: diff --git a/theHarvester/discovery/intelxsearch.py b/theHarvester/discovery/intelxsearch.py index 740517fd..1126e2c3 100644 --- a/theHarvester/discovery/intelxsearch.py +++ b/theHarvester/discovery/intelxsearch.py @@ -49,7 +49,7 @@ class SearchIntelx: async with session.post(f'{self.database}/phonebook/search', headers=headers, json=data) as total_resp: search_data = await total_resp.json() if not search_data['success']: - logger.info(f'Error: {search_data["message"]}') + logger.info('IntelX search request failed') return phonebook_id = search_data['id'] diff --git a/theHarvester/discovery/leakix.py b/theHarvester/discovery/leakix.py index 8db98c64..136a03a0 100644 --- a/theHarvester/discovery/leakix.py +++ b/theHarvester/discovery/leakix.py @@ -72,7 +72,7 @@ class SearchLeakix: or 'unauthorized' in response[0].lower() or 'error' in response[0].lower() ): - logger.info(f'LeakIX API requires authentication: {response[0][:100]}') + logger.info('LeakIX API requires authentication') continue try: diff --git a/theHarvester/discovery/onyphe.py b/theHarvester/discovery/onyphe.py index 29ef3601..40bf1ab3 100644 --- a/theHarvester/discovery/onyphe.py +++ b/theHarvester/discovery/onyphe.py @@ -39,7 +39,7 @@ class SearchOnyphe: if isinstance(self.response, list): self.response = self.response[0] if not isinstance(self.response, dict): - raise Exception(f'An exception has occurred {self.response} is not a dict') + raise TypeError(f'Onyphe response has unexpected type: {type(self.response).__name__}') if self.response['text'] == 'Success': if 'results' in self.response.keys(): for result in self.response['results']: @@ -83,7 +83,7 @@ class SearchOnyphe: except Exception: continue else: - logger.info(f'Onhyphe API query did not succeed dumping current response: {self.response}') + logger.info('Onyphe API query did not succeed') async def get_asns(self) -> set: return self.asns diff --git a/theHarvester/discovery/pentesttools.py b/theHarvester/discovery/pentesttools.py index 9e26b0d2..a02d82f1 100644 --- a/theHarvester/discovery/pentesttools.py +++ b/theHarvester/discovery/pentesttools.py @@ -40,7 +40,7 @@ class SearchPentestTools: self.total_results = await self.parse_json(res_json) break else: - logger.info(f'Operation get_scan_status failed because: {res_json["error"]}. {res_json["details"]}') + logger.info('Pentest-Tools operation get_scan_status failed') break @staticmethod diff --git a/theHarvester/discovery/search_dehashed.py b/theHarvester/discovery/search_dehashed.py index 43ab5d1d..17da8461 100644 --- a/theHarvester/discovery/search_dehashed.py +++ b/theHarvester/discovery/search_dehashed.py @@ -56,8 +56,7 @@ class SearchDehashed: try: data = await response.json() except Exception: - text = await response.text() - raise Exception(f'Unexpected response format: {text[:200]}') + raise ValueError('Unexpected response format') entries = data.get('entries', []) if not entries: diff --git a/theHarvester/discovery/searchhunterhow.py b/theHarvester/discovery/searchhunterhow.py index f8e4222f..99eae2a0 100644 --- a/theHarvester/discovery/searchhunterhow.py +++ b/theHarvester/discovery/searchhunterhow.py @@ -45,7 +45,7 @@ class SearchHunterHow: dct = response[0] if 'code' in dct.keys(): if dct['code'] == 40001: - logger.info(f'Code 40001 indicates for searchhunterhow: {dct["message"]}') + logger.info('SearchHunterHow API returned code 40001') return # total = dct['data']['total'] # TODO determine if total is ever 100 how to get more subdomains? diff --git a/theHarvester/discovery/sherlockeye.py b/theHarvester/discovery/sherlockeye.py index 1fc5c346..1c30bdbe 100644 --- a/theHarvester/discovery/sherlockeye.py +++ b/theHarvester/discovery/sherlockeye.py @@ -97,8 +97,7 @@ class SearchSherlockeye: def _extract_response(self, response: dict[str, Any]) -> None: if response.get('success') is False: - message = response.get('message', 'Unknown Sherlockeye API error') - logger.info(f'Sherlockeye API error: {message}') + logger.info('Sherlockeye API error') return data = response.get('data') @@ -130,8 +129,7 @@ class SearchSherlockeye: proxy=self._proxy_url(), ) as response: if response.status != 200: - error_body = await response.text() - logger.info(f'Sherlockeye API error ({response.status}): {error_body[:200]}') + logger.info('Sherlockeye API request failed with status %s', response.status) return response_data = await response.json() diff --git a/theHarvester/discovery/whoisxml.py b/theHarvester/discovery/whoisxml.py index 09f3843a..1da2a0be 100644 --- a/theHarvester/discovery/whoisxml.py +++ b/theHarvester/discovery/whoisxml.py @@ -1,10 +1,6 @@ -import logging - from theHarvester.discovery.constants import MissingKey from theHarvester.lib.core import AsyncFetcher, Core -logger = logging.getLogger(__name__) - class SearchWhoisXML: def __init__(self, word) -> None: @@ -29,7 +25,6 @@ class SearchWhoisXML: # Parse the response according to the example JSON structure: # {"search":"example.com.com","result":{"count":10000,"records":[{"domain":"test.example.com","firstSeen":1678169400,"lastSeen":1678169400}]}} self.total_results = [] - logger.info(response[0]) if response and response[0]: # Extract domains from the records array if 'result' in response[0] and 'records' in response[0]['result']: diff --git a/theHarvester/discovery/windvane.py b/theHarvester/discovery/windvane.py index 026bc7dc..e51dec97 100644 --- a/theHarvester/discovery/windvane.py +++ b/theHarvester/discovery/windvane.py @@ -115,7 +115,7 @@ class SearchWindvane: else: # API error - stop pagination if response_data.get('code') != 0: - logger.info(f'Windvane subdomain API error: {response_data.get("msg", "Unknown error")}') + logger.info('Windvane subdomain API returned code %s', response_data.get('code')) break except Exception as e: diff --git a/theHarvester/discovery/zoomeyesearch.py b/theHarvester/discovery/zoomeyesearch.py index 2d14f8cd..ca6de5ed 100644 --- a/theHarvester/discovery/zoomeyesearch.py +++ b/theHarvester/discovery/zoomeyesearch.py @@ -79,7 +79,7 @@ class SearchZoomEye: # some responses put HTTP-like code here return resp.get('status') in (0, 200) except Exception as e: - logger.info(f'An error occurred while trying to parse {resp} : {e}') + logger.info('ZoomEye response status parsing failed with %s', type(e).__name__) return False # If no explicit status, assume success and let parsing validate