From 906f89db988b72b9d5294383223fba87c0b0d055 Mon Sep 17 00:00:00 2001 From: Light1YT Date: Sun, 28 Jun 2026 17:42:33 +0500 Subject: [PATCH] fix(scrapers): don't leak proxy creds in parse_proxy_url ValueError (#1945) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit parse_proxy_url raised ValueError(f"...{proxy_url!r}") embedding the FULL proxy URL incl. password. Это исключение всплывает в run_region_sweep try → пишется в kn_scrape_runs.error (str(e)[:2000]) + log_progress + logger.exception→Sentry — малформный proxy URL утёк бы пароль. Теперь сообщение содержит только scheme, без сырого URL/creds. Happy-path лог уже server-only (host:port), creds не пишет. test: ValueError-сообщение НЕ содержит пароль/юзера/сырой URL. --- backend/app/services/scrapers/stealth.py | 4 +++- .../services/scrapers/test_stealth_throttle_proxy.py | 11 +++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/backend/app/services/scrapers/stealth.py b/backend/app/services/scrapers/stealth.py index a1408f64..db02ad0c 100644 --- a/backend/app/services/scrapers/stealth.py +++ b/backend/app/services/scrapers/stealth.py @@ -57,7 +57,9 @@ def parse_proxy_url(proxy_url: str | None) -> dict[str, str] | None: return None parts = urlsplit(proxy_url) if not parts.hostname: - raise ValueError(f"proxy URL без host: {proxy_url!r}") + # НЕ эхо-им сырой proxy_url — он содержит пароль, а это исключение + # всплывает в kn_scrape_runs.error / log_progress / Sentry (#1945 sec-review). + raise ValueError(f"proxy URL без host (scheme={parts.scheme!r})") scheme = parts.scheme or "http" server = f"{scheme}://{parts.hostname}" if parts.port: diff --git a/backend/tests/services/scrapers/test_stealth_throttle_proxy.py b/backend/tests/services/scrapers/test_stealth_throttle_proxy.py index ac14a973..a1582e76 100644 --- a/backend/tests/services/scrapers/test_stealth_throttle_proxy.py +++ b/backend/tests/services/scrapers/test_stealth_throttle_proxy.py @@ -69,6 +69,17 @@ class TestParseProxyUrl: with pytest.raises(ValueError): parse_proxy_url("http://:8080") + def test_error_does_not_leak_credentials(self) -> None: + # ValueError всплывает в kn_scrape_runs.error / log_progress / Sentry — + # НЕ должен содержать пароль (#1945 sec-review). + bad = "http://secretuser:secretpass@:8080" # есть creds, но нет host + with pytest.raises(ValueError) as exc: + parse_proxy_url(bad) + msg = str(exc.value) + assert "secretpass" not in msg + assert "secretuser" not in msg + assert bad not in msg + class TestBrowserSessionThrottleThreading: """concurrency/jitter/proxy прокидываются в инстанс-поля BrowserSession."""