From ba10a5f40da97735ddfaa5cf1b863eca3aa251dd Mon Sep 17 00:00:00 2001 From: pedrooot Date: Thu, 8 Oct 2026 00:14:54 +0200 Subject: [PATCH] fix(network): correct what the outbound guard logs --- prowler/lib/network/ssrf.py | 7 ++++++- tests/lib/network/ssrf_test.py | 17 +++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/prowler/lib/network/ssrf.py b/prowler/lib/network/ssrf.py index 2c5575fcb6..691af46764 100644 --- a/prowler/lib/network/ssrf.py +++ b/prowler/lib/network/ssrf.py @@ -117,7 +117,9 @@ def extract_host(url: str) -> str: return scp_like.group("host") host = urlparse(url).hostname if not host: - raise OutboundURLNotAllowedError(f"Could not read a host from URL {url!r}") + # The URL itself stays out of the message: a configured repository or auth + # URL can carry credentials in its userinfo, and this message is logged + raise OutboundURLNotAllowedError("Could not read a host from the supplied URL") return host @@ -128,6 +130,9 @@ def validate_outbound_host(host: str) -> None: hostile DNS server can still answer differently the second time. """ if outbound_check_skipped(): + logger.warning( + f"{SKIP_OUTBOUND_CHECK_ENV} is set — destination check disabled for host {host!r}" + ) return networks = allowed_private_networks() diff --git a/tests/lib/network/ssrf_test.py b/tests/lib/network/ssrf_test.py index 451aa3ff1b..6d3633b2bf 100644 --- a/tests/lib/network/ssrf_test.py +++ b/tests/lib/network/ssrf_test.py @@ -109,6 +109,15 @@ class TestOutboundCheckOptOut: with pytest.raises(OutboundURLNotAllowedError): validate_outbound_host("169.254.169.254") + def test_logs_the_skip_so_an_operator_sees_the_disabled_control(self, monkeypatch): + monkeypatch.setenv(SKIP_OUTBOUND_CHECK_ENV, "true") + + with mock.patch("prowler.lib.network.ssrf.logger") as logged: + validate_outbound_host("169.254.169.254") + + assert logged.warning.call_count == 1 + assert SKIP_OUTBOUND_CHECK_ENV in logged.warning.call_args.args[0] + def test_the_cli_entrypoint_opts_out(self, monkeypatch): """prowler() must set the opt-out before it does anything else.""" # a throwaway mapping, so the variable prowler() sets cannot leak into @@ -218,6 +227,14 @@ class TestExtractHost: with pytest.raises(OutboundURLNotAllowedError, match="Could not read a host"): extract_host("not a url") + def test_keeps_the_url_out_of_the_rejection_message(self): + # the message is logged, and a configured URL can carry credentials + with pytest.raises(OutboundURLNotAllowedError) as rejection: + extract_host("https://user:s3cr3t@/org/repo.git") + + assert "s3cr3t" not in str(rejection.value) + assert "user" not in str(rejection.value) + class TestValidateOutboundURL: def test_rejects_a_disallowed_scheme(self):