Browse Source

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
pull/15254/head
Viacheslav Klimov 7 months ago
parent
commit
3a765209ff
Failed to extract signature
  1. 5
      application/src/main/java/org/thingsboard/server/config/HttpSecurityHeadersCustomizer.java
  2. 4
      application/src/main/java/org/thingsboard/server/config/HttpSecurityHeadersProperties.java
  3. 2
      msa/web-ui/config/custom-environment-variables.yml
  4. 2
      msa/web-ui/config/default.yml
  5. 9
      msa/web-ui/server.ts

5
application/src/main/java/org/thingsboard/server/config/HttpSecurityHeadersCustomizer.java

@ -16,11 +16,13 @@
package org.thingsboard.server.config; package org.thingsboard.server.config;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer; import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer;
import org.springframework.security.web.header.writers.StaticHeadersWriter; import org.springframework.security.web.header.writers.StaticHeadersWriter;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
@Slf4j
@Component @Component
@RequiredArgsConstructor @RequiredArgsConstructor
public class HttpSecurityHeadersCustomizer { public class HttpSecurityHeadersCustomizer {
@ -41,6 +43,9 @@ public class HttpSecurityHeadersCustomizer {
if ("DENY".equalsIgnoreCase(value)) { if ("DENY".equalsIgnoreCase(value)) {
headers.frameOptions(HeadersConfigurer.FrameOptionsConfig::deny); headers.frameOptions(HeadersConfigurer.FrameOptionsConfig::deny);
} else { } 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); headers.frameOptions(HeadersConfigurer.FrameOptionsConfig::sameOrigin);
} }
} }

4
application/src/main/java/org/thingsboard/server/config/HttpSecurityHeadersProperties.java

@ -17,9 +17,9 @@ package org.thingsboard.server.config;
import lombok.Data; import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration; import org.springframework.stereotype.Component;
@Configuration @Component
@ConfigurationProperties(prefix = "security.headers") @ConfigurationProperties(prefix = "security.headers")
@Data @Data
public class HttpSecurityHeadersProperties { public class HttpSecurityHeadersProperties {

2
msa/web-ui/config/custom-environment-variables.yml

@ -38,7 +38,7 @@ security:
content-security-policy: content-security-policy:
enabled: "SECURITY_HEADERS_CONTENT_SECURITY_POLICY_ENABLED" enabled: "SECURITY_HEADERS_CONTENT_SECURITY_POLICY_ENABLED"
value: "SECURITY_HEADERS_CONTENT_SECURITY_POLICY_VALUE" 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: logger:
level: "LOGGER_LEVEL" level: "LOGGER_LEVEL"
path: "LOG_FOLDER" path: "LOG_FOLDER"

2
msa/web-ui/config/default.yml

@ -38,7 +38,7 @@ security:
content-security-policy: content-security-policy:
enabled: false enabled: false
value: "" value: ""
reportOnly: false report-only: false
logger: logger:
level: "info" level: "info"
path: "logs" path: "logs"

9
msa/web-ui/server.ts

@ -60,7 +60,9 @@ let connections: Socket[] = [];
const app = express(); const app = express();
server = http.createServer(app); 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<string, string> = {}; const securityHeaders: Record<string, string> = {};
if (config.has('security.headers')) { if (config.has('security.headers')) {
const hc: any = config.get('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'; securityHeaders['X-Frame-Options'] = hc['x-frame-options']?.value || 'SAMEORIGIN';
} }
if (hc['content-security-policy']?.enabled && hc['content-security-policy']?.value) { 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'; ? 'Content-Security-Policy-Report-Only' : 'Content-Security-Policy';
securityHeaders[name] = hc['content-security-policy'].value; securityHeaders[name] = csp.value;
} }
} else { } else {
// Defaults when no security.headers config block exists // Defaults when no security.headers config block exists

Loading…
Cancel
Save