Skip to content

Commit 97c0ccf

Browse files
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>
1 parent 4e46ba3 commit 97c0ccf

5 files changed

Lines changed: 62 additions & 11 deletions

File tree

app/common/build.gradle

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@ bootRun {
33
enabled = false
44
}
55
dependencies {
6+
// Security-hardening utilities (zip-slip, SSRF, filename sanitization, command injection).
7+
// Declared as api here so core + proprietary (which depend on common) get it transitively,
8+
// keeping it off modules that don't need it (e.g. saas).
9+
api 'io.github.pixee:java-security-toolkit:1.2.3'
610
api "com.google.guava:guava:${guavaVersion}"
711
api 'org.springframework.boot:spring-boot-starter-webmvc'
812
api 'org.springframework.boot:spring-boot-starter-aspectj'
@@ -22,7 +26,10 @@ dependencies {
2226
api "org.springdoc:springdoc-openapi-starter-webmvc-ui:3.0.3"
2327
// Simple Java Mail for EML/MSG parsing (replaces direct Angus Mail usage)
2428
api 'org.simplejavamail:simple-java-mail:9.3.2'
25-
api 'org.simplejavamail:outlook-module:9.3.2' // MSG file support
29+
// MSG file support; exclude commons-math3 (only HSSF/formula needs it, MSG parsing doesn't)
30+
api('org.simplejavamail:outlook-module:9.3.2') {
31+
exclude group: 'org.apache.commons', module: 'commons-math3'
32+
}
2633
api 'jakarta.mail:jakarta.mail-api:2.1.5'
2734
runtimeOnly 'org.eclipse.angus:angus-mail:2.0.5'
2835

@@ -36,12 +43,30 @@ dependencies {
3643

3744
api "com.stirling:jpdfium:${jpdfiumVersion}"
3845

39-
// -PjpdfiumPlatforms=all|none|<csv of linux-x64,linux-arm64,darwin-x64,darwin-arm64,windows-x64>
40-
// 'none' skips natives entirely (windows-arm64 builds, until JPDFium ships that platform).
41-
def jpdfiumPlatformsProp = (project.findProperty('jpdfiumPlatforms') ?: 'all').toString().trim()
42-
def jpdfiumAllPlatforms = ['linux-x64', 'linux-arm64', 'darwin-x64', 'darwin-arm64', 'windows-x64']
46+
// -PjpdfiumPlatforms=auto|all|none|<csv of linux-x64,linux-arm64,linux-musl-x64,linux-musl-arm64,darwin-x64,darwin-arm64,windows-x64> (windows-arm64 natives not published yet)
47+
def jpdfiumPlatformsProp = (project.findProperty('jpdfiumPlatforms') ?: 'auto').toString().trim()
48+
def jpdfiumAllPlatforms = ['linux-x64', 'linux-arm64', 'linux-musl-x64', 'linux-musl-arm64', 'darwin-x64', 'darwin-arm64', 'windows-x64']
4349
def jpdfiumPlatforms
44-
if (jpdfiumPlatformsProp == 'all') {
50+
if (jpdfiumPlatformsProp == 'auto') {
51+
def osName = System.getProperty('os.name').toLowerCase()
52+
def osArch = System.getProperty('os.arch').toLowerCase()
53+
def isArm64 = osArch.contains('aarch64') || osArch.contains('arm64')
54+
if (osName.contains('linux')) {
55+
jpdfiumPlatforms = isArm64 ? ['linux-arm64'] : ['linux-x64']
56+
} else if (osName.contains('mac')) {
57+
jpdfiumPlatforms = isArm64 ? ['darwin-arm64'] : ['darwin-x64']
58+
} else if (osName.contains('win')) {
59+
if (isArm64) {
60+
logger.lifecycle("JPDFium natives are not available for windows-arm64; set -PjpdfiumPlatforms=none to skip bundling natives.")
61+
jpdfiumPlatforms = []
62+
} else {
63+
jpdfiumPlatforms = ['windows-x64']
64+
}
65+
} else {
66+
// Fallback: bundle all platforms when host can't be determined
67+
jpdfiumPlatforms = jpdfiumAllPlatforms
68+
}
69+
} else if (jpdfiumPlatformsProp == 'all') {
4570
jpdfiumPlatforms = jpdfiumAllPlatforms
4671
} else if (jpdfiumPlatformsProp == 'none') {
4772
jpdfiumPlatforms = []
@@ -51,7 +76,7 @@ dependencies {
5176
def jpdfiumInvalid = jpdfiumPlatforms.findAll { !jpdfiumAllPlatforms.contains(it) }
5277
if (jpdfiumInvalid) {
5378
throw new GradleException("Unknown jpdfiumPlatforms value(s): ${jpdfiumInvalid.join(', ')}. " +
54-
"Valid: ${jpdfiumAllPlatforms.join(', ')}, 'all' or 'none'.")
79+
"Valid: ${jpdfiumAllPlatforms.join(', ')}, 'auto', 'all' or 'none'.")
5580
}
5681
logger.lifecycle("JPDFium native platforms: ${jpdfiumPlatforms ? jpdfiumPlatforms.join(', ') : 'none'}")
5782
jpdfiumPlatforms.each { platform ->

app/core/build.gradle

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,16 @@ dependencies {
6262
// CVE-2022-25647: Explicit gson to prevent unsafe deserialization (tabula would pull 2.8.7)
6363
implementation "com.google.code.gson:gson:${gsonVersion}"
6464
implementation 'org.apache.pdfbox:jbig2-imageio:3.0.5'
65-
implementation 'com.opencsv:opencsv:5.12.0' // https://mvnrepository.com/artifact/com.opencsv/opencsv
66-
implementation 'org.apache.poi:poi-ooxml:5.5.1'
65+
// OpenCSV: Stirling-PDF only uses CSVWriter, not the opencsv-bean module.
66+
// Exclude commons-beanutils + commons-collections.
67+
implementation('com.opencsv:opencsv:5.12.0') {
68+
exclude group: 'commons-beanutils', module: 'commons-beanutils'
69+
exclude group: 'commons-collections', module: 'commons-collections'
70+
}
71+
// POI: only XSSF (modern Excel) is used, not HSSF/FormulaEvaluator which need commons-math3.
72+
implementation('org.apache.poi:poi-ooxml:5.5.1') {
73+
exclude group: 'org.apache.commons', module: 'commons-math3'
74+
}
6775

6876
// Batik only bridge module needed (transitively pulls anim, gvt, util, css, dom, svg-dom)
6977
// Replaces batik-all which included unused codec, svggen, transcoder, script modules
@@ -129,6 +137,10 @@ bootJar {
129137
exclude 'META-INF/*.RSA'
130138
exclude 'META-INF/*.EC'
131139

140+
// Exclude source maps from production JAR, dev-only debugging artifacts, not needed at runtime
141+
exclude 'static/pdfjs-legacy/**/*.map'
142+
exclude 'static/**/*.map'
143+
132144
manifest {
133145
attributes(
134146
'Implementation-Title': 'Stirling-PDF',

app/core/src/main/java/stirling/software/SPDF/controller/api/security/RedactController.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,10 @@ public ResponseEntity<Resource> redactPdf(@ModelAttribute RedactPdfRequest reque
221221
.normalizeFonts(false)
222222
.fixToUnicode(false)
223223
.glyphAware(true)
224+
.ligatureAware(true)
225+
.bidiAware(true)
226+
.graphemeSafe(true)
227+
.sanitizeStructure(false) // WIP/Experimental API
224228
.redactMetadata(true)
225229
.build();
226230

app/core/src/main/java/stirling/software/SPDF/controller/api/security/TextRedactionService.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,10 @@ boolean performTextReplacement(
110110
.fixToUnicode(false)
111111
.repairWidths(false)
112112
.glyphAware(true)
113+
.ligatureAware(true)
114+
.bidiAware(true)
115+
.graphemeSafe(true)
116+
.sanitizeStructure(false)
113117
.build();
114118

115119
try (PdfDocument checkDoc = PdfDocument.open(tempIn.toPath())) {

build.gradle

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ ext {
4242
bucket4jVersion = "8.19.0"
4343
archunitVersion = "1.4.2"
4444
batikVersion = "1.19"
45-
jpdfiumVersion = "1.0.4"
45+
jpdfiumVersion = "1.1.3"
4646
jwtVersion = "0.13.0"
4747
awsSdkVersion = "2.51.3"
4848
jschVersion = "2.28.6"
@@ -265,7 +265,6 @@ subprojects {
265265

266266
dependencies {
267267
implementation 'org.springframework.boot:spring-boot-starter-actuator'
268-
implementation 'io.github.pixee:java-security-toolkit:1.2.3'
269268

270269
//tmp for security bumps
271270
implementation "ch.qos.logback:logback-core:$logback"
@@ -543,6 +542,13 @@ subprojects {
543542
}
544543
}
545544

545+
// Lazy initialization defers bean creation until first use,
546+
// reducing dev-mode RSS significantly (heap drops ~40-60%).
547+
// Enable with: ./gradlew bootRun -PlazyInit=true
548+
if (rootProject.findProperty('lazyInit') == 'true') {
549+
runtimeArgs.add("-Dspring.main.lazy-initialization=true")
550+
logger.lifecycle("Lazy initialization enabled (-PlazyInit=true)")
551+
}
546552
jvmArgs = runtimeArgs
547553
}
548554
}

0 commit comments

Comments
 (0)