diff --git a/dependencyCheckSuppression.xml b/dependencyCheckSuppression.xml
index 86150a3244..e8926bd673 100644
--- a/dependencyCheckSuppression.xml
+++ b/dependencyCheckSuppression.xml
@@ -469,4 +469,23 @@
^pkg:maven/org\.jetbrains\.kotlin/.*@.*$
CVE-2026-53914
+
+
+
+
+ ^pkg:maven/org\.apache\.tomcat/tomcat-.*@.*$
+ CVE-2026-66299
+
+
+
+ ^pkg:maven/org\.apache\.tomcat\.embed/tomcat-embed-.*@.*$
+ CVE-2026-66299
+
+
diff --git a/gradle.properties b/gradle.properties
index 14e673de02..14e282a14d 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -44,7 +44,7 @@ buildFromSource=true
# The default version for LabKey artifacts that are built or that we depend on.
# override in an individual module's gradle.properties file as necessary
-labkeyVersion=26.7.4
+labkeyVersion=26.7.5
labkeyClientApiVersion=7.3.0
# Uncomment the following line to download proteomics binaries
@@ -103,7 +103,7 @@ apacheTomcatVersion=11.0.24
# (mothership) -> json-path -> json-smart -> accessor-smart
# (core) -> graalvm
# tika
-asmVersion=9.10
+asmVersion=9.10.1
awsSdkVersion=2.29.50
@@ -114,8 +114,8 @@ azureIdentityVersion=1.18.4
batikVersion=1.19
# sync with Tika version (or later)
-bouncycastlePgpVersion=1.84
-bouncycastleVersion=1.84
+bouncycastlePgpVersion=1.85
+bouncycastleVersion=1.85
cglibNodepVersion=2.2.3
@@ -272,7 +272,7 @@ opencsvVersion=2.3
openTracingVersion=0.33.0
# sync with version Tika ships
-pdfboxVersion=3.0.7
+pdfboxVersion=3.0.8
# sync with version Tika ships
poiVersion=5.5.1
@@ -315,7 +315,7 @@ stax2ApiVersion=4.2.2
thumbnailatorVersion=0.4.21
# used for tika-core in API and tika-parsers in search
-tikaVersion=3.3.1
+tikaVersion=3.3.2
# sync with Tika
tukaaniXZVersion=1.12
diff --git a/server/configs/application.properties b/server/configs/application.properties
index 365ce22ce9..74fa7b0a96 100644
--- a/server/configs/application.properties
+++ b/server/configs/application.properties
@@ -81,6 +81,9 @@ mail.smtpUser=@@smtpUser@@
#mail.smtpStartTlsEnable=@@smtpStartTlsEnable@@
#mail.smtpSocketFactoryClass=@@smtpSocketFactoryClass@@
#mail.smtpAuth=@@smtpAuth@@
+#mail.smtpConnectionTimeout=@@smtpConnectionTimeout@@
+#mail.smtpTimeout=@@smtpTimeout@@
+#mail.smtpWriteTimeout=@@smtpWriteTimeout@@
## Microsoft Graph configuration - if using this, comment out SMTP configuration
#mail.graph.tenantId=@@graphTenantId@@
diff --git a/server/configs/webapps/embedded/config/application.properties b/server/configs/webapps/embedded/config/application.properties
index 54258056d6..0c9cb6dc05 100644
--- a/server/configs/webapps/embedded/config/application.properties
+++ b/server/configs/webapps/embedded/config/application.properties
@@ -78,6 +78,9 @@ mail.smtpUser=Anonymous
#mail.smtpStartTlsEnable=@@smtpStartTlsEnable@@
#mail.smtpSocketFactoryClass=@@smtpSocketFactoryClass@@
#mail.smtpAuth=@@smtpAuth@@
+#mail.smtpConnectionTimeout=@@smtpConnectionTimeout@@
+#mail.smtpTimeout=@@smtpTimeout@@
+#mail.smtpWriteTimeout=@@smtpWriteTimeout@@
####################################################################################
## Microsoft Graph Configuration - if using this, comment out SMTP configuration
diff --git a/server/embedded/src/org/labkey/embedded/LabKeyServer.java b/server/embedded/src/org/labkey/embedded/LabKeyServer.java
index 1c49bcd268..fdf05a4520 100644
--- a/server/embedded/src/org/labkey/embedded/LabKeyServer.java
+++ b/server/embedded/src/org/labkey/embedded/LabKeyServer.java
@@ -41,11 +41,7 @@ public class LabKeyServer
private static final String TERMINATE_ON_STARTUP_FAILURE = "terminateOnStartupFailure";
private static final String JARS_TO_SKIP = "tomcat.util.scan.StandardJarScanFilter.jarsToSkip";
private static final String JARS_TO_SCAN = "tomcat.util.scan.StandardJarScanFilter.jarsToScan";
- private static final String SERVER_GUID = "serverGUID";
- public static final String SERVER_GUID_PARAMETER_NAME = "org.labkey.mothership." + SERVER_GUID;
- public static final String SERVER_SSL_KEYSTORE = "org.labkey.serverSslKeystore";
- public static final String CUSTOM_LOG4J_CONFIG = "org.labkey.customLog4JConfig";
- public static final String CORS_PREFIX = "cors.";
+
static final String MAX_TOTAL_CONNECTIONS_DEFAULT = "50";
static final String MAX_IDLE_DEFAULT = "10";
static final String MAX_WAIT_MILLIS_DEFAULT = "120000";
@@ -108,83 +104,89 @@ public static void main(String[] args)
""";
application.setDefaultProperties(new HashMap<>()
- {{
+ {{
// GitHub Issue 796: JSON logging stopped after Tomcat/Spring update
- // Propagate log4j configuration to Spring Boot config, which is necessary with Spring Boot 4.x
- String log4JConfig = System.getProperty("log4j.configurationFile");
- if (log4JConfig != null)
- {
- String[] log4JConfigParts = log4JConfig.split(",");
- if (log4JConfigParts.length > 0)
- {
- if ("log4j2.xml".equals(log4JConfigParts[0]))
- {
- // Assume this is the one packaged with our embedded build and on the classpath
- put("logging.config", "classpath:log4j2.xml");
- }
- else
- {
- put("logging.config", log4JConfigParts[0]);
- }
- if (log4JConfigParts.length > 1)
- {
+ // Propagate log4j configuration to Spring Boot config, which is necessary with Spring Boot 4.x
+ String log4JConfig = System.getProperty("log4j.configurationFile");
+ if (log4JConfig != null)
+ {
+ String[] log4JConfigParts = log4JConfig.split(",");
+ if (log4JConfigParts.length > 0)
+ {
+ if ("log4j2.xml".equals(log4JConfigParts[0]))
+ {
+ // Assume this is the one packaged with our embedded build and on the classpath
+ put("logging.config", "classpath:log4j2.xml");
+ }
+ else
+ {
+ put("logging.config", log4JConfigParts[0]);
+ }
+ if (log4JConfigParts.length > 1)
+ {
put("logging.log4j2.config.override", String.join(",", Arrays.asList(log4JConfigParts).subList(1, log4JConfigParts.length)));
- }
- }
- }
+ }
+ }
+ }
- put("server.tomcat.basedir", ".");
- put("server.tomcat.accesslog.directory", logHome);
+ put("server.tomcat.basedir", ".");
+ put("server.tomcat.accesslog.directory", logHome);
- // Boost limits imposed by Tomcat v10.1.42
- put("server.tomcat.max-part-count", 500);
- put("server.tomcat.max-part-header-size", 1024); // GitHub Issue 161: LKS insert forms can't handle long file field names
- put("server.tomcat.max-connections", 250);
+ // Boost limits imposed by Tomcat v10.1.42
+ put("server.tomcat.max-part-count", 500);
+ put("server.tomcat.max-part-header-size", 1024); // GitHub Issue 161: LKS insert forms can't handle long file field names
+ put("server.tomcat.max-connections", 250);
// Boost limit back to Tomcat 10 level
- put("server.tomcat.max-parameter-count", 10_000);
-
- // Enable HTTP compression for response content
- put("server.compression.enabled", "true");
- // Spring Boot compresses HTML, JSON and other types by default, but not TSV, CSV, or SVG.
- // We have to duplicate the defaults and add those types
- put("server.compression.mime-types", "text/html,text/xml,text/plain,text/css,text/javascript,application/javascript,application/json,application/xml,text/tab-separated-values,text/csv,image/svg+xml");
-
- put("server.tomcat.accesslog.enabled", "true");
- put("server.tomcat.accesslog.pattern", "%h %l %u %t \"%r\" %s %b %D %S %I \"%{Referer}i\" \"%{User-Agent}i\" %{LABKEY.username}s %{X-Forwarded-For}i");
- put("jsonaccesslog.pattern", "%h %t %m %U %s %b %D %S \"%{Referer}i\" \"%{User-Agent}i\" %{LABKEY.username}s %{X-Forwarded-For}i");
-
- // Issue 52415: Omit stack traces from Tomcat error pages by default, but propagate error messages
- put("server.error.include-stacktrace", "never");
- put("server.error.include-message", "always");
-
- put("csp.enforce", enforceCsp);
- put("csp.report", reportCsp);
-
- // GitHub Issue 692: Stop using CBC in HTTPS ciphers
- // These settings configure HTTPS. Admins must opt in with additional settings
- // in application.properties, like the key store. Without those other settings,
- // HTTP-only startup fails unless "server.ssl.enabled" is explicitly set to false here
- put("server.ssl.enabled", "false");
- put("server.ssl.protocol", "TLS");
- put("server.ssl.enabled-protocols", "TLSv1.3,TLSv1.2");
- // Use explicit JSSE cipher suite names to avoid CBC-mode suites
- put("server.ssl.ciphers",
- String.join(",",
- // TLS 1.3
- "TLS_AES_256_GCM_SHA384",
- "TLS_AES_128_GCM_SHA256",
- "TLS_CHACHA20_POLY1305_SHA256",
- // TLS 1.2 (AEAD only, no CBC)
- "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
- "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
- "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
- "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
- "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
- "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256"
- )
- );
- put("server.ssl.use-cipher-suites-order", "true");
- }}
+ put("server.tomcat.max-parameter-count", 10_000);
+
+ // Enable HTTP compression for response content
+ put("server.compression.enabled", "true");
+ // Spring Boot compresses HTML, JSON and other types by default, but not TSV, CSV, or SVG.
+ // We have to duplicate the defaults and add those types
+ put("server.compression.mime-types", "text/html,text/xml,text/plain,text/css,text/javascript,application/javascript,application/json,application/xml,text/tab-separated-values,text/csv,image/svg+xml");
+
+ put("server.tomcat.accesslog.enabled", "true");
+ put("server.tomcat.accesslog.pattern", "%h %l %u %t \"%r\" %s %b %D %S %I \"%{Referer}i\" \"%{User-Agent}i\" %{LABKEY.username}s %{X-Forwarded-For}i");
+ put("jsonaccesslog.pattern", "%h %t %m %U %s %b %D %S \"%{Referer}i\" \"%{User-Agent}i\" %{LABKEY.username}s %{X-Forwarded-For}i");
+
+ // Issue 52415: Omit stack traces from Tomcat error pages by default, but propagate error messages
+ put("server.error.include-stacktrace", "never");
+ put("server.error.include-message", "always");
+
+ put("csp.enforce", enforceCsp);
+ put("csp.report", reportCsp);
+
+ // GitHub Issue 692: Stop using CBC in HTTPS ciphers
+ // These settings configure HTTPS. Admins must opt in with additional settings
+ // in application.properties, like the key store. Without those other settings,
+ // HTTP-only startup fails unless "server.ssl.enabled" is explicitly set to false here
+ put("server.ssl.enabled", "false");
+ put("server.ssl.protocol", "TLS");
+ put("server.ssl.enabled-protocols", "TLSv1.3,TLSv1.2");
+ // Use explicit JSSE cipher suite names to avoid CBC-mode suites
+ put("server.ssl.ciphers",
+ String.join(",",
+ // TLS 1.3
+ "TLS_AES_256_GCM_SHA384",
+ "TLS_AES_128_GCM_SHA256",
+ "TLS_CHACHA20_POLY1305_SHA256",
+ // TLS 1.2 (AEAD only, no CBC)
+ "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
+ "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
+ "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
+ "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
+ "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
+ "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256"
+ )
+ );
+ put("server.ssl.use-cipher-suites-order", "true");
+
+ // GitHub Issue #1416 - default values for SMTP timeouts
+ put("mail.smtpConnectionTimeout", 10 * 1000);
+ put("mail.smtpTimeout", 60 * 1000);
+ // Unlike the socket-level timeouts above, JavaMail implements writetimeout with a ScheduledThreadPool per connection - one per message, since Transport.send() doesn't pool
+ put("mail.smtpWriteTimeout", 60 * 1000);
+ }}
);
application.setBannerMode(Banner.Mode.OFF);
application.run(args);
@@ -818,6 +820,9 @@ public static class MailProperties
private String smtpStartTlsEnable;
private String smtpSocketFactoryClass;
private String smtpAuth;
+ private Integer smtpConnectionTimeout;
+ private Integer smtpTimeout;
+ private Integer smtpWriteTimeout;
public String getSmtpHost()
{
@@ -898,6 +903,36 @@ public void setSmtpAuth(String smtpAuth)
{
this.smtpAuth = smtpAuth;
}
+
+ public Integer getSmtpConnectionTimeout()
+ {
+ return smtpConnectionTimeout;
+ }
+
+ public void setSmtpConnectionTimeout(Integer smtpConnectionTimeout)
+ {
+ this.smtpConnectionTimeout = smtpConnectionTimeout;
+ }
+
+ public Integer getSmtpTimeout()
+ {
+ return smtpTimeout;
+ }
+
+ public void setSmtpTimeout(Integer smtpTimeout)
+ {
+ this.smtpTimeout = smtpTimeout;
+ }
+
+ public Integer getSmtpWriteTimeout()
+ {
+ return smtpWriteTimeout;
+ }
+
+ public void setSmtpWriteTimeout(Integer smtpWriteTimeout)
+ {
+ this.smtpWriteTimeout = smtpWriteTimeout;
+ }
}
@Configuration
diff --git a/server/embedded/src/org/labkey/embedded/LabKeyTomcatServletWebServerFactory.java b/server/embedded/src/org/labkey/embedded/LabKeyTomcatServletWebServerFactory.java
index 8eb8942e16..e85abd874f 100644
--- a/server/embedded/src/org/labkey/embedded/LabKeyTomcatServletWebServerFactory.java
+++ b/server/embedded/src/org/labkey/embedded/LabKeyTomcatServletWebServerFactory.java
@@ -29,6 +29,7 @@
import org.apache.logging.log4j.Logger;
import org.apache.tomcat.util.descriptor.web.ContextResource;
import org.labkey.bootstrap.ConfigException;
+import org.labkey.embedded.LabKeyServer.MailProperties;
import org.springframework.boot.tomcat.TomcatWebServer;
import org.springframework.boot.tomcat.servlet.TomcatServletWebServerFactory;
import org.springframework.boot.web.servlet.ServletContextInitializer;
@@ -39,14 +40,15 @@
import java.util.Map;
import java.util.Objects;
-import static org.labkey.embedded.LabKeyServer.CORS_PREFIX;
-import static org.labkey.embedded.LabKeyServer.CUSTOM_LOG4J_CONFIG;
-import static org.labkey.embedded.LabKeyServer.SERVER_GUID_PARAMETER_NAME;
-import static org.labkey.embedded.LabKeyServer.SERVER_SSL_KEYSTORE;
-
class LabKeyTomcatServletWebServerFactory extends TomcatServletWebServerFactory
{
private static final Logger LOG = LogManager.getLogger(LabKeyTomcatServletWebServerFactory.class);
+ private static final String SERVER_GUID = "serverGUID";
+ private static final String SERVER_GUID_PARAMETER_NAME = "org.labkey.mothership." + SERVER_GUID;
+ private static final String SERVER_SSL_KEYSTORE = "org.labkey.serverSslKeystore";
+ private static final String CUSTOM_LOG4J_CONFIG = "org.labkey.customLog4JConfig";
+ private static final String CORS_PREFIX = "cors.";
+
private final LabKeyServer _server;
public LabKeyTomcatServletWebServerFactory(LabKeyServer server)
@@ -423,42 +425,35 @@ private String getPropValue(Map propValues, Integer resourceKey
private void addSmtpProperties(StandardContext context)
{
// Get session/mail properties
- LabKeyServer.MailProperties mailProps = _server.smtpSource();
+ MailProperties mailProps = _server.smtpSource();
+
+ addSmtpProperty(context, "host", mailProps.getSmtpHost());
+ addSmtpProperty(context, "user", mailProps.getSmtpUser());
+ addSmtpProperty(context, "port", mailProps.getSmtpPort());
+ addSmtpProperty(context, "from", mailProps.getSmtpFrom());
+ addSmtpProperty(context, "password", mailProps.getSmtpPassword());
+ addSmtpProperty(context, "starttls.enable", mailProps.getSmtpStartTlsEnable());
+ addSmtpProperty(context, "socketFactory.class", mailProps.getSmtpSocketFactoryClass());
+ addSmtpProperty(context, "auth", mailProps.getSmtpAuth());
+ addSmtpProperty(context, "connectiontimeout", mailProps.getSmtpConnectionTimeout());
+ addSmtpProperty(context, "timeout", mailProps.getSmtpTimeout());
+ addSmtpProperty(context, "writetimeout", mailProps.getSmtpWriteTimeout());
+ }
- if (mailProps.getSmtpHost() != null)
- {
- context.addParameter("mail.smtp.host", mailProps.getSmtpHost());
- }
- if (mailProps.getSmtpUser() != null)
- {
- context.addParameter("mail.smtp.user", mailProps.getSmtpUser());
- }
- if (mailProps.getSmtpPort() != null)
- {
- context.addParameter("mail.smtp.port", mailProps.getSmtpPort());
- }
- if (mailProps.getSmtpFrom() != null)
- {
- context.addParameter("mail.smtp.from", mailProps.getSmtpFrom());
- }
- if (mailProps.getSmtpPassword() != null)
- {
- context.addParameter("mail.smtp.password", mailProps.getSmtpPassword());
- }
- if (mailProps.getSmtpStartTlsEnable() != null)
- {
- context.addParameter("mail.smtp.starttls.enable", mailProps.getSmtpStartTlsEnable());
- }
- if (mailProps.getSmtpSocketFactoryClass() != null)
- {
- context.addParameter("mail.smtp.socketFactory.class", mailProps.getSmtpSocketFactoryClass());
- }
- if (mailProps.getSmtpAuth() != null)
+ private void addSmtpProperty(StandardContext context, String name, String value)
+ {
+ if (value != null)
{
- context.addParameter("mail.smtp.auth", mailProps.getSmtpAuth());
+ context.addParameter("mail.smtp." + name, value);
}
}
+ private void addSmtpProperty(StandardContext context, String name, Integer value)
+ {
+ if (value != null)
+ addSmtpProperty(context, name, String.valueOf(value));
+ }
+
private void addGraphProperties(StandardContext context)
{
// Get Microsoft Graph mail properties