From 959a1a84a4bd3c090606432854aa254f2d66e490 Mon Sep 17 00:00:00 2001 From: Viacheslav Klimov Date: Tue, 17 Mar 2026 11:54:50 +0200 Subject: [PATCH] Make SSRF resolver conditional, sanitize error messages, improve test coverage Wire SsrfSafeAddressResolverGroup only when SSRF protection is enabled. Remove "SSRF protection" prefix from error messages to avoid exposing internal security mechanisms to users. Add 11 new tests covering isHostnameAllowed, one-arg validateUri, allow-list case-insensitivity, cloud metadata/loopback overrides, CIDR boundaries, IPv6 unique local, whitespace parsing, allow-list replacement, and blocked hostname override. --- .../rule/engine/rest/SsrfSafeAddressResolverGroup.java | 4 ++-- .../java/org/thingsboard/rule/engine/rest/TbHttpClient.java | 5 ++++- .../rule/engine/rest/SsrfSafeAddressResolverGroupTest.java | 6 +++--- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/SsrfSafeAddressResolverGroup.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/SsrfSafeAddressResolverGroup.java index e08dc0ac06..0a099df50e 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/SsrfSafeAddressResolverGroup.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/SsrfSafeAddressResolverGroup.java @@ -83,7 +83,7 @@ public final class SsrfSafeAddressResolverGroup extends AddressResolverGroup @@ -140,6 +139,10 @@ public class TbHttpClient { httpClient = httpClient.secure(t -> t.sslContext(sslContext)); } + if (SsrfProtectionValidator.isEnabled()) { + httpClient = httpClient.resolver(SsrfSafeAddressResolverGroup.INSTANCE); + } + validateMaxInMemoryBufferSize(config); this.webClient = WebClient.builder() diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rest/SsrfSafeAddressResolverGroupTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rest/SsrfSafeAddressResolverGroupTest.java index 79880d98f2..54bfc56a07 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rest/SsrfSafeAddressResolverGroupTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rest/SsrfSafeAddressResolverGroupTest.java @@ -18,7 +18,6 @@ package org.thingsboard.rule.engine.rest; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.resolver.AddressResolver; import io.netty.util.concurrent.EventExecutor; -import io.netty.util.concurrent.Future; import io.netty.util.concurrent.Promise; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; @@ -100,7 +99,7 @@ class SsrfSafeAddressResolverGroupTest { assertThatThrownBy(() -> promise.get(10, TimeUnit.SECONDS)) .isInstanceOf(ExecutionException.class) .hasRootCauseInstanceOf(RuntimeException.class) - .rootCause().hasMessageContaining("SSRF protection"); + .rootCause().hasMessageContaining("is not allowed"); } @Test @@ -116,7 +115,7 @@ class SsrfSafeAddressResolverGroupTest { assertThatThrownBy(() -> promise.get(10, TimeUnit.SECONDS)) .isInstanceOf(ExecutionException.class) .hasRootCauseInstanceOf(RuntimeException.class) - .rootCause().hasMessageContaining("SSRF protection"); + .rootCause().hasMessageContaining("is not allowed"); } @Test @@ -146,4 +145,5 @@ class SsrfSafeAddressResolverGroupTest { assertThat(results).isNotEmpty(); } + }