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;
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);
}
}

4
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 {

2
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"

2
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"

9
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<string, string> = {};
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

Loading…
Cancel
Save