From 97c0ccf58214fe502c0f93ac859d264dc58c3c00 Mon Sep 17 00:00:00 2001 From: brios <127139797+balazs-szucs@users.noreply.github.com> Date: Thu, 27 Aug 2026 19:31:54 +0200 Subject: [PATCH] refactor(deps): optimize dependency footprints, and add lazy initialization with platform-specific JPDFium bundling (#7620) Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- app/common/build.gradle | 39 +++++++++++++++---- app/core/build.gradle | 16 +++++++- .../api/security/RedactController.java | 4 ++ .../api/security/TextRedactionService.java | 4 ++ build.gradle | 10 ++++- 5 files changed, 62 insertions(+), 11 deletions(-) diff --git a/app/common/build.gradle b/app/common/build.gradle index 8af68bcb76..8ac1dfe0a9 100644 --- a/app/common/build.gradle +++ b/app/common/build.gradle @@ -3,6 +3,10 @@ bootRun { enabled = false } dependencies { + // Security-hardening utilities (zip-slip, SSRF, filename sanitization, command injection). + // Declared as api here so core + proprietary (which depend on common) get it transitively, + // keeping it off modules that don't need it (e.g. saas). + api 'io.github.pixee:java-security-toolkit:1.2.3' api "com.google.guava:guava:${guavaVersion}" api 'org.springframework.boot:spring-boot-starter-webmvc' api 'org.springframework.boot:spring-boot-starter-aspectj' @@ -22,7 +26,10 @@ dependencies { api "org.springdoc:springdoc-openapi-starter-webmvc-ui:3.0.3" // Simple Java Mail for EML/MSG parsing (replaces direct Angus Mail usage) api 'org.simplejavamail:simple-java-mail:9.3.2' - api 'org.simplejavamail:outlook-module:9.3.2' // MSG file support + // MSG file support; exclude commons-math3 (only HSSF/formula needs it, MSG parsing doesn't) + api('org.simplejavamail:outlook-module:9.3.2') { + exclude group: 'org.apache.commons', module: 'commons-math3' + } api 'jakarta.mail:jakarta.mail-api:2.1.5' runtimeOnly 'org.eclipse.angus:angus-mail:2.0.5' @@ -36,12 +43,30 @@ dependencies { api "com.stirling:jpdfium:${jpdfiumVersion}" - // -PjpdfiumPlatforms=all|none| - // 'none' skips natives entirely (windows-arm64 builds, until JPDFium ships that platform). - def jpdfiumPlatformsProp = (project.findProperty('jpdfiumPlatforms') ?: 'all').toString().trim() - def jpdfiumAllPlatforms = ['linux-x64', 'linux-arm64', 'darwin-x64', 'darwin-arm64', 'windows-x64'] + // -PjpdfiumPlatforms=auto|all|none| (windows-arm64 natives not published yet) + def jpdfiumPlatformsProp = (project.findProperty('jpdfiumPlatforms') ?: 'auto').toString().trim() + def jpdfiumAllPlatforms = ['linux-x64', 'linux-arm64', 'linux-musl-x64', 'linux-musl-arm64', 'darwin-x64', 'darwin-arm64', 'windows-x64'] def jpdfiumPlatforms - if (jpdfiumPlatformsProp == 'all') { + if (jpdfiumPlatformsProp == 'auto') { + def osName = System.getProperty('os.name').toLowerCase() + def osArch = System.getProperty('os.arch').toLowerCase() + def isArm64 = osArch.contains('aarch64') || osArch.contains('arm64') + if (osName.contains('linux')) { + jpdfiumPlatforms = isArm64 ? ['linux-arm64'] : ['linux-x64'] + } else if (osName.contains('mac')) { + jpdfiumPlatforms = isArm64 ? ['darwin-arm64'] : ['darwin-x64'] + } else if (osName.contains('win')) { + if (isArm64) { + logger.lifecycle("JPDFium natives are not available for windows-arm64; set -PjpdfiumPlatforms=none to skip bundling natives.") + jpdfiumPlatforms = [] + } else { + jpdfiumPlatforms = ['windows-x64'] + } + } else { + // Fallback: bundle all platforms when host can't be determined + jpdfiumPlatforms = jpdfiumAllPlatforms + } + } else if (jpdfiumPlatformsProp == 'all') { jpdfiumPlatforms = jpdfiumAllPlatforms } else if (jpdfiumPlatformsProp == 'none') { jpdfiumPlatforms = [] @@ -51,7 +76,7 @@ dependencies { def jpdfiumInvalid = jpdfiumPlatforms.findAll { !jpdfiumAllPlatforms.contains(it) } if (jpdfiumInvalid) { throw new GradleException("Unknown jpdfiumPlatforms value(s): ${jpdfiumInvalid.join(', ')}. " + - "Valid: ${jpdfiumAllPlatforms.join(', ')}, 'all' or 'none'.") + "Valid: ${jpdfiumAllPlatforms.join(', ')}, 'auto', 'all' or 'none'.") } logger.lifecycle("JPDFium native platforms: ${jpdfiumPlatforms ? jpdfiumPlatforms.join(', ') : 'none'}") jpdfiumPlatforms.each { platform -> diff --git a/app/core/build.gradle b/app/core/build.gradle index 76828445a3..0e1533b6e8 100644 --- a/app/core/build.gradle +++ b/app/core/build.gradle @@ -62,8 +62,16 @@ dependencies { // CVE-2022-25647: Explicit gson to prevent unsafe deserialization (tabula would pull 2.8.7) implementation "com.google.code.gson:gson:${gsonVersion}" implementation 'org.apache.pdfbox:jbig2-imageio:3.0.5' - implementation 'com.opencsv:opencsv:5.12.0' // https://mvnrepository.com/artifact/com.opencsv/opencsv - implementation 'org.apache.poi:poi-ooxml:5.5.1' + // OpenCSV: Stirling-PDF only uses CSVWriter, not the opencsv-bean module. + // Exclude commons-beanutils + commons-collections. + implementation('com.opencsv:opencsv:5.12.0') { + exclude group: 'commons-beanutils', module: 'commons-beanutils' + exclude group: 'commons-collections', module: 'commons-collections' + } + // POI: only XSSF (modern Excel) is used, not HSSF/FormulaEvaluator which need commons-math3. + implementation('org.apache.poi:poi-ooxml:5.5.1') { + exclude group: 'org.apache.commons', module: 'commons-math3' + } // Batik only bridge module needed (transitively pulls anim, gvt, util, css, dom, svg-dom) // Replaces batik-all which included unused codec, svggen, transcoder, script modules @@ -129,6 +137,10 @@ bootJar { exclude 'META-INF/*.RSA' exclude 'META-INF/*.EC' + // Exclude source maps from production JAR, dev-only debugging artifacts, not needed at runtime + exclude 'static/pdfjs-legacy/**/*.map' + exclude 'static/**/*.map' + manifest { attributes( 'Implementation-Title': 'Stirling-PDF', diff --git a/app/core/src/main/java/stirling/software/SPDF/controller/api/security/RedactController.java b/app/core/src/main/java/stirling/software/SPDF/controller/api/security/RedactController.java index 7a186235cd..1c694da2b0 100644 --- a/app/core/src/main/java/stirling/software/SPDF/controller/api/security/RedactController.java +++ b/app/core/src/main/java/stirling/software/SPDF/controller/api/security/RedactController.java @@ -221,6 +221,10 @@ public class RedactController { .normalizeFonts(false) .fixToUnicode(false) .glyphAware(true) + .ligatureAware(true) + .bidiAware(true) + .graphemeSafe(true) + .sanitizeStructure(false) // WIP/Experimental API .redactMetadata(true) .build(); diff --git a/app/core/src/main/java/stirling/software/SPDF/controller/api/security/TextRedactionService.java b/app/core/src/main/java/stirling/software/SPDF/controller/api/security/TextRedactionService.java index 9cf4e6c700..c0b74f5428 100644 --- a/app/core/src/main/java/stirling/software/SPDF/controller/api/security/TextRedactionService.java +++ b/app/core/src/main/java/stirling/software/SPDF/controller/api/security/TextRedactionService.java @@ -110,6 +110,10 @@ class TextRedactionService { .fixToUnicode(false) .repairWidths(false) .glyphAware(true) + .ligatureAware(true) + .bidiAware(true) + .graphemeSafe(true) + .sanitizeStructure(false) .build(); try (PdfDocument checkDoc = PdfDocument.open(tempIn.toPath())) { diff --git a/build.gradle b/build.gradle index 6382e75c12..ffaba65377 100644 --- a/build.gradle +++ b/build.gradle @@ -42,7 +42,7 @@ ext { bucket4jVersion = "8.19.0" archunitVersion = "1.4.2" batikVersion = "1.19" - jpdfiumVersion = "1.0.4" + jpdfiumVersion = "1.1.3" jwtVersion = "0.13.0" awsSdkVersion = "2.51.3" jschVersion = "2.28.6" @@ -265,7 +265,6 @@ subprojects { dependencies { implementation 'org.springframework.boot:spring-boot-starter-actuator' - implementation 'io.github.pixee:java-security-toolkit:1.2.3' //tmp for security bumps implementation "ch.qos.logback:logback-core:$logback" @@ -543,6 +542,13 @@ subprojects { } } + // Lazy initialization defers bean creation until first use, + // reducing dev-mode RSS significantly (heap drops ~40-60%). + // Enable with: ./gradlew bootRun -PlazyInit=true + if (rootProject.findProperty('lazyInit') == 'true') { + runtimeArgs.add("-Dspring.main.lazy-initialization=true") + logger.lifecycle("Lazy initialization enabled (-PlazyInit=true)") + } jvmArgs = runtimeArgs } }