From 3a765209ff4c621a239d825e43bfcc7e78ff7c69 Mon Sep 17 00:00:00 2001 From: Viacheslav Klimov Date: Mon, 16 Mar 2026 16:48:38 +0200 Subject: [PATCH] Address PR review comments - Use kebab-case 'report-only' in web-ui configs to match thingsboard.yml - Add log.warn for unrecognized X-Frame-Options values in customizer - Replace @Configuration with @Component on HttpSecurityHeadersProperties - Add comment explaining '!== false' vs truthiness pattern in server.ts --- .../server/config/HttpSecurityHeadersCustomizer.java | 5 +++++ .../server/config/HttpSecurityHeadersProperties.java | 4 ++-- msa/web-ui/config/custom-environment-variables.yml | 2 +- msa/web-ui/config/default.yml | 2 +- msa/web-ui/server.ts | 9 ++++++--- 5 files changed, 15 insertions(+), 7 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/config/HttpSecurityHeadersCustomizer.java b/application/src/main/java/org/thingsboard/server/config/HttpSecurityHeadersCustomizer.java index 922bf39afa..318c435353 100644 --- a/application/src/main/java/org/thingsboard/server/config/HttpSecurityHeadersCustomizer.java +++ b/application/src/main/java/org/thingsboard/server/config/HttpSecurityHeadersCustomizer.java @@ -16,11 +16,13 @@ package org.thingsboard.server.config; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer; import org.springframework.security.web.header.writers.StaticHeadersWriter; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; +@Slf4j @Component @RequiredArgsConstructor public class HttpSecurityHeadersCustomizer { @@ -41,6 +43,9 @@ public class HttpSecurityHeadersCustomizer { if ("DENY".equalsIgnoreCase(value)) { headers.frameOptions(HeadersConfigurer.FrameOptionsConfig::deny); } else { + if (!"SAMEORIGIN".equalsIgnoreCase(value)) { + log.warn("Unrecognized X-Frame-Options value '{}', falling back to SAMEORIGIN. Valid values: DENY, SAMEORIGIN", value); + } headers.frameOptions(HeadersConfigurer.FrameOptionsConfig::sameOrigin); } } diff --git a/application/src/main/java/org/thingsboard/server/config/HttpSecurityHeadersProperties.java b/application/src/main/java/org/thingsboard/server/config/HttpSecurityHeadersProperties.java index 80a070f35c..224e2aeea0 100644 --- a/application/src/main/java/org/thingsboard/server/config/HttpSecurityHeadersProperties.java +++ b/application/src/main/java/org/thingsboard/server/config/HttpSecurityHeadersProperties.java @@ -17,9 +17,9 @@ package org.thingsboard.server.config; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.context.annotation.Configuration; +import org.springframework.stereotype.Component; -@Configuration +@Component @ConfigurationProperties(prefix = "security.headers") @Data public class HttpSecurityHeadersProperties { diff --git a/msa/web-ui/config/custom-environment-variables.yml b/msa/web-ui/config/custom-environment-variables.yml index 4185e8f5ed..6ba9147555 100644 --- a/msa/web-ui/config/custom-environment-variables.yml +++ b/msa/web-ui/config/custom-environment-variables.yml @@ -38,7 +38,7 @@ security: content-security-policy: enabled: "SECURITY_HEADERS_CONTENT_SECURITY_POLICY_ENABLED" value: "SECURITY_HEADERS_CONTENT_SECURITY_POLICY_VALUE" - reportOnly: "SECURITY_HEADERS_CONTENT_SECURITY_POLICY_REPORT_ONLY" + report-only: "SECURITY_HEADERS_CONTENT_SECURITY_POLICY_REPORT_ONLY" logger: level: "LOGGER_LEVEL" path: "LOG_FOLDER" diff --git a/msa/web-ui/config/default.yml b/msa/web-ui/config/default.yml index 23b0162130..6ffa85b3bc 100644 --- a/msa/web-ui/config/default.yml +++ b/msa/web-ui/config/default.yml @@ -38,7 +38,7 @@ security: content-security-policy: enabled: false value: "" - reportOnly: false + report-only: false logger: level: "info" path: "logs" diff --git a/msa/web-ui/server.ts b/msa/web-ui/server.ts index 7fa35ea9c5..c6b4870b28 100644 --- a/msa/web-ui/server.ts +++ b/msa/web-ui/server.ts @@ -60,7 +60,9 @@ let connections: Socket[] = []; const app = express(); server = http.createServer(app); - // Build security headers map once at startup + // Build security headers map once at startup. + // Headers enabled by default use '!== false' so they stay on unless explicitly disabled. + // Headers disabled by default use simple truthiness checks. const securityHeaders: Record = {}; if (config.has('security.headers')) { const hc: any = config.get('security.headers'); @@ -74,9 +76,10 @@ let connections: Socket[] = []; securityHeaders['X-Frame-Options'] = hc['x-frame-options']?.value || 'SAMEORIGIN'; } if (hc['content-security-policy']?.enabled && hc['content-security-policy']?.value) { - const name = hc['content-security-policy']?.reportOnly + const csp = hc['content-security-policy']; + const name = csp['report-only'] ? 'Content-Security-Policy-Report-Only' : 'Content-Security-Policy'; - securityHeaders[name] = hc['content-security-policy'].value; + securityHeaders[name] = csp.value; } } else { // Defaults when no security.headers config block exists