From 62c1963507f450ad1430a28eafde5d68b34e90be Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Tue, 16 Jun 2026 12:15:34 +0300 Subject: [PATCH] fix(solutions): clamp solution-template installTimeoutMs to a configurable max MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The post-install Thread.sleep was driven by installTimeoutMs from the uploaded solution.json with no upper bound — a malicious or misconfigured template could pin an HTTP worker thread indefinitely. Cap it at a configurable max (default 60s) exposed as iot-hub.max-install-timeout-ms with an IOT_HUB_MAX_INSTALL_TIMEOUT_MS env override. --- .../server/service/solutions/DefaultSolutionService.java | 5 ++++- application/src/main/resources/thingsboard.yml | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/application/src/main/java/org/thingsboard/server/service/solutions/DefaultSolutionService.java b/application/src/main/java/org/thingsboard/server/service/solutions/DefaultSolutionService.java index 53a9d503b6..71020ae47e 100644 --- a/application/src/main/java/org/thingsboard/server/service/solutions/DefaultSolutionService.java +++ b/application/src/main/java/org/thingsboard/server/service/solutions/DefaultSolutionService.java @@ -189,6 +189,9 @@ public class DefaultSolutionService implements SolutionService { @Value("${iot-hub.max-archive-entry-count:10000}") private int maxArchiveEntryCount; + @Value("${iot-hub.max-install-timeout-ms:60000}") + private long maxInstallTimeoutMs; + private final RuleChainService ruleChainService; private final TbRuleChainService tbRuleChainService; private final DeviceProfileService deviceProfileService; @@ -400,7 +403,7 @@ public class DefaultSolutionService implements SolutionService { List ruleChainDefs = loadListOfEntitiesIfFileExists(ctx.getTempDir(), "rule_chains.json", new TypeReference<>() {}); if (ruleChainDefs.stream().anyMatch(r -> StringUtils.isNotEmpty(r.getUpdate()))) { - long timeout = loadInstallTimeoutMs(ctx.getTempDir()); + long timeout = Math.min(loadInstallTimeoutMs(ctx.getTempDir()), maxInstallTimeoutMs); if (timeout > 0) { Thread.sleep(timeout); } diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index fbc6ada7b8..f678076c57 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -2175,3 +2175,4 @@ iot-hub: max-uncompressed-archive-bytes: "${IOT_HUB_MAX_UNCOMPRESSED_ARCHIVE_BYTES:209715200}" # maximum cumulative uncompressed size in bytes for a solution template archive. Extraction aborts past this threshold to mitigate zip-bomb attacks. max-uncompressed-entry-bytes: "${IOT_HUB_MAX_UNCOMPRESSED_ENTRY_BYTES:52428800}" # maximum uncompressed size in bytes for any single entry inside a solution template archive. max-archive-entry-count: "${IOT_HUB_MAX_ARCHIVE_ENTRY_COUNT:10000}" # maximum number of entries allowed inside a solution template archive. + max-install-timeout-ms: "${IOT_HUB_MAX_INSTALL_TIMEOUT_MS:60000}" # upper clamp in milliseconds for the post-install wait declared in a solution template's solution.json. Prevents a malicious or misconfigured template from pinning an HTTP worker thread.