Summary
T2.6 hardening sub-task of parent epic #73. This is a defense-in-depth follow-up to PR #83, which added per-entry / per-archive / per-archive-count size caps to PSArchiveFiles.extractFilesFromArchive(). This issue covers the other zip/tar sites in the project, which currently have ZipSlipGuard + canonical-path protection but lack the resource-exhaustion caps.
Scope
13 files use zip/tar APIs (excluding PSArchiveFiles.java already hardened in #83 and test files). All 13 already have ZipSlipGuard + canonical-path checks via the existing // codeql[java/zipslip] codeql suppressions. The change here is to add the same three caps that #83 added to PSArchiveFiles:
MAX_ENTRIES (10 000) — reject any archive with more entries
MAX_ENTRY_SIZE (100 MB) — reject any individual entry whose declared uncompressed size exceeds the cap
MAX_TOTAL_SIZE (500 MB) — sum the declared uncompressed sizes as entries are processed; abort if the running total exceeds the cap
These three limits cover the zip-bomb half of the 11 CVEs in commons-compress 1.28.0. The path-traversal half is already covered by the existing ZipSlipGuard.
Files affected
The 13 production files with zip/tar handling (no test files; tests are out of scope):
| # |
File |
What it does |
| 1 |
deployer/.../PSArchive.java |
Archive API for the deployer (ZipFile read path; uses PSArchiveFiles.getFile for extraction) |
| 2 |
modules/perc-packages/.../PSPackageBuilder.java |
Package builder (ZipEntry write — defensive limits not strictly needed for writes, but symmetry helps) |
| 3 |
projects/sitemanage/.../PSWidgetPackageBuilder.java |
Widget package builder (ZipInputStream read) |
| 4 |
deliverytiersuite/.../MainDTSPreInstall.java |
DTS pre-install (ZipInputStream read) |
| 5 |
modules/perc-distribution-tree/.../Main.java |
Distribution tree builder (ZipInputStream read) |
| 6 |
system/.../PSInstallRxApp.java |
Install RxApp (ZipInputStream read) |
| 7 |
system/.../Utils.java |
Install utilities (ZipInputStream read) |
| 8 |
system/.../InstallRxApp.java |
Install RxApp alternate (ZipInputStream read) |
| 9 |
modules/perc-ant/.../PSRxBuildInput.java |
Ant GUI (ZipInputStream read) |
| 10 |
deployer/.../PSPackageLockManager.java |
Package lock manager (ZipFile read) |
| 11 |
modules/Simple/.../PSDirectoryAnalyzer.java |
Directory analyzer (ZipFile read) |
| 12 |
deployer/.../objectstore/PSArchive.java |
Archive wrapper (already covered by entry 1) — exclude |
| 13 |
deployer/.../server/PSPackageLockManager.java |
Same as 10, exclude |
After deduplication: 10 unique files to harden.
Approach
The cleanest pattern is a small helper class — PSZipBombGuard (or similar) — that wraps the size-check logic into a single utility. Then each call site does:
ZipInputStream zin = ...;
ZipBombGuard guard = new ZipBombGuard();
ZipEntry entry;
while ((entry = zin.getNextEntry()) != null) {
guard.check(entry); // throws SecurityException if cap exceeded
... process entry ...
}
The guard throws SecurityException with the offending entry name + cap that was hit (same fail-closed pattern as the ZipSlip check at lines 354-358, 374-379, 398-403 of PSArchiveFiles).
All three caps are overridable per JVM via the system properties PSARCHIVE_MAX_ENTRIES / PSARCHIVE_MAX_ENTRY_SIZE / PSARCHIVE_MAX_TOTAL_SIZE (matching the override keys used in #83).
Verification
Out of scope
- Test files (PSPackageLockManagerTest, PSPackageBuilderTest) are intentionally excluded. Test fixtures are controlled, not untrusted input.
- The commons-compress 1.28.0 library upgrade itself (which would close the CVEs in the library code) — 1.28.0 is the last Java 1.8 line and 1.29+ requires Java 9+. The 11 CVEs cannot be closed by a version bump.
- The
PSPackageBuilder write path is technically not a zip-bomb attack surface (it's building archives, not reading them), but I'll add the same checks for symmetry / future-proofing.
References
Co-Authored by Mavis v1.0.0 using minimax-m3 with agent mavis.
Summary
T2.6 hardening sub-task of parent epic #73. This is a defense-in-depth follow-up to PR #83, which added per-entry / per-archive / per-archive-count size caps to
PSArchiveFiles.extractFilesFromArchive(). This issue covers the other zip/tar sites in the project, which currently have ZipSlipGuard + canonical-path protection but lack the resource-exhaustion caps.Scope
13 files use zip/tar APIs (excluding
PSArchiveFiles.javaalready hardened in #83 and test files). All 13 already haveZipSlipGuard+ canonical-path checks via the existing// codeql[java/zipslip]codeql suppressions. The change here is to add the same three caps that #83 added to PSArchiveFiles:MAX_ENTRIES(10 000) — reject any archive with more entriesMAX_ENTRY_SIZE(100 MB) — reject any individual entry whose declared uncompressed size exceeds the capMAX_TOTAL_SIZE(500 MB) — sum the declared uncompressed sizes as entries are processed; abort if the running total exceeds the capThese three limits cover the zip-bomb half of the 11 CVEs in commons-compress 1.28.0. The path-traversal half is already covered by the existing ZipSlipGuard.
Files affected
The 13 production files with zip/tar handling (no test files; tests are out of scope):
deployer/.../PSArchive.javamodules/perc-packages/.../PSPackageBuilder.javaprojects/sitemanage/.../PSWidgetPackageBuilder.javadeliverytiersuite/.../MainDTSPreInstall.javamodules/perc-distribution-tree/.../Main.javasystem/.../PSInstallRxApp.javasystem/.../Utils.javasystem/.../InstallRxApp.javamodules/perc-ant/.../PSRxBuildInput.javadeployer/.../PSPackageLockManager.javamodules/Simple/.../PSDirectoryAnalyzer.javadeployer/.../objectstore/PSArchive.javadeployer/.../server/PSPackageLockManager.javaAfter deduplication: 10 unique files to harden.
Approach
The cleanest pattern is a small helper class —
PSZipBombGuard(or similar) — that wraps the size-check logic into a single utility. Then each call site does:The guard throws
SecurityExceptionwith the offending entry name + cap that was hit (same fail-closed pattern as the ZipSlip check at lines 354-358, 374-379, 398-403 of PSArchiveFiles).All three caps are overridable per JVM via the system properties
PSARCHIVE_MAX_ENTRIES/PSARCHIVE_MAX_ENTRY_SIZE/PSARCHIVE_MAX_TOTAL_SIZE(matching the override keys used in #83).Verification
./mvn-env.sh clean install -DskipTestssucceeds on Java 1.8./mvn-env.sh spotless:checkpassesSecurityExceptionfrom each hardened call siteUnsupportedClassVersionErrorin the build logOut of scope
PSPackageBuilderwrite path is technically not a zip-bomb attack surface (it's building archives, not reading them), but I'll add the same checks for symmetry / future-proofing.References
docs/ai-generated/tasks/PR#-DependencyVulnerabilityAnalysis/issues/02-epic-non-upgradeable.md#t26--apache-commons-hardening