- 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
- Extract shared parseHostEntries() to deduplicate setAllowedHosts/setAdditionalBlockedHosts
- Add isHostnameAllowed() and propagate hostname allow-list check in resolver
- Move OAuth2 custom mapper URL SSRF validation to save-time (Oauth2ClientDataValidator)
- Remove runtime SSRF checks from CustomOAuth2ClientMapper and GithubOAuth2ClientMapper
(custom URL now validated at save; GitHub emailUrl is server config, not user input)
- Replace example.com with 8.8.8.8 in resolver test to avoid DNS dependency
Fix security issues from penetration test report:
- M2: Add configurable X-Frame-Options and CSP headers (disabled by default)
- L2: Add X-Content-Type-Options and Referrer-Policy headers (enabled by default)
- L3: Make CORS allowed-origin-patterns configurable via TB_CORS_* env vars
Root cause: ThingsboardSecurityConfiguration called .disable() on the entire
HeadersConfigurer, which removed ALL security headers including Cache-Control.
Fix uses defaultsDisabled() + selective header enablement via a new
HttpSecurityHeadersCustomizer component.
Both Spring Boot (tb-node) and Express.js (web-ui) share the same
SECURITY_HEADERS_* environment variables for consistent configuration
across monolith and microservice deployments.
Add SsrfSafeAddressResolverGroup that validates resolved IPs at Netty
connection time, eliminating the TOCTOU gap where DNS rebinding domains
resolve to safe IPs during validation but to private/metadata IPs at
connection time. Disable HTTP redirects in TbHttpClient to prevent
redirect-based SSRF bypass.
Add allow-list support (SSRF_ALLOWED_HOSTS) to SsrfProtectionValidator
so customers with IoT devices on private networks can whitelist specific
addresses or CIDR ranges while keeping SSRF protection enabled.
Add SSRF validation to MS Teams webhook, custom OAuth2 mapper, and
GitHub OAuth2 mapper endpoints. Log a warning when SSRF protection is
disabled.
When Maven runs with -T N, all modules using the packaging profile invoke
gradle-maven-plugin against the same gradleProjectDirectory
(packaging/java or packaging/js), causing them to share and contend on
the same .gradle/ project cache directory simultaneously.
Two fixes:
- Pass --project-cache-dir pointing to each module's own target/.gradle,
fully isolating parallel Gradle invocations from each other.
- Add maven-clean-plugin filesets to remove packaging/java/.gradle and
packaging/js/.gradle on mvn clean. Gradle always writes project-level
metadata to the project directory regardless of --project-cache-dir,
so these would otherwise accumulate on CI agents with persistent home
directories.
Rename `openConnections`/`connectionsCounter` to `statsName`/`number`
to match DefaultTransportService and avoid misleading future readers.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Root pluginManagement and netty-mqtt hardcoded version 3.1.1 while
the jar-plugin.version property (used by dao and others) was set to
3.4.0, causing build failures due to incompatible commons-io dependency.
Replace both hardcoded 3.1.1 occurrences with ${jar-plugin.version}
so all modules consistently use the version defined in one place.
Fixes#15037
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Introduce exponential backoff (2s → 60s cap) for WebSocket reconnect
attempts and suppress duplicate close-event error notifications during
reconnect cycles. Improve UX by showing session limit and data size
errors as user-friendly warnings instead of raw error codes.
Static fields RESOURCE_ID_3303_12_5700_TS_0/TS_1 in Lwm2mTestHelper are never
reset between test runs. On CI retries the await() at the start of the test
passes immediately (both timestamps are still > 0 from the previous run), so
the telemetry query uses stale timestamps and the second await() times out.
Fix: add @Before that resets both timestamps to 0 before each test method,
which was already correctly implied by the unused @Before import.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Leshan's NotificationDataStore.toKey() can throw NPE when the server reference
is null during CoAP observe-relation cleanup on client shutdown (race condition).
This NPE was caught by the outer try-catch in startUpdating(), which prevented
leshanClient.start() from being called, leaving the simulated device stuck in
UPDATING state and causing the awaitility timeout in the OTA integration test.
Fix: wrap leshanClient.stop(false) in its own try-catch so that a Leshan
internal exception during stop does not abort the subsequent client restart.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>