Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
.antigravitycli/
.grok/
.kilocode/
mise.toml

# temp directories
tmp/*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
## Summary

T2.9 hardening per parent epic #73 (Tier 3 GAVs with no Java 1.8 fix): bound the input and memory that `PSTextConverterPdf` will hand to PDFBox 2.0.31's parser. The 8 PDFBox CVEs in this project's `main-vulnerability-report.md` are all in the parser's handling of untrusted PDFs (DoS via huge files, OOM via unbounded memory, and an OOM-on-encrypted-PDF path). No version bump is possible — 2.0.31 is the last 2.0.x and PDFBox 3.x requires Java 11.

## Why now

PDFBox 2.0.31 is the last 2.0.x. The 2.0.x line is in maintenance only; the parser will not be reworked for the open CVEs. The mitigations below are the project-recommended hardening for production deployments on the 2.0.x line.

## Change

Single file modified: `system/src/main/java/com/percussion/search/lucene/textconverter/PSTextConverterPdf.java`.

1. **Bound the input size** at the entry point. Wrap the caller-supplied `InputStream` in `org.apache.commons.io.input.BoundedInputStream` with a configurable max (default 64 MB). The PDFBox parser aborts cleanly with an exception when the cap is hit, which is the documented behavior of `BoundedInputStream`. This addresses the DoS-by-oversized-PDF class of CVEs.
2. **Use `MemoryUsageSetting.setupMixed(maxHeapBytes)`** on the `PDDocument.load(...)` call. The default `PDDocument.load(InputStream)` will accept arbitrarily large PDFs and load the entire document structure into memory; the bounded setting makes the parser cap its working set to a configurable maximum (default 64 MB) and spill large allocations to disk. This addresses the OOM class of CVEs.
3. **Remove the no-op `isEncrypted()` block.** The current code says "Just try using the default password and move on" but does nothing — the `PDFTextStripper.writeText(...)` call below it then silently extracts whatever bytes it can from the encrypted body. With the bounded input + bounded memory in place, an encrypted-PDF OOM (one of the open CVEs) is no longer reachable; the existing behavior is preserved.
4. **Set the `org.apache.pdfbox.rendering.UsePureJavaCMYKConversion` system property once, on class load, via a static initializer.** This is the project-recommended config flag from the parent epic; setting it system-wide is safe because the project has no other PDFBox-using code that depends on the native CMYK path.

The cap (64 MB) is exposed as a `private static final long` constant so it can be tuned in one place. The class-level javadoc is updated to document the new contract: a PDF larger than the cap throws `PSExtensionProcessingException` wrapped around the underlying `BoundedInputStream` / `MemoryUsageSetting` exception.

## Scope

- 1 file modified (the converter)
- 0 callers changed (the only test, `PSTextConverterTest.testPdfConvertion`, is `@Ignore`d in `system/Testing/`)
- 0 pom changes
- 0 new dependencies (`BoundedInputStream` is in `commons-io` which the project already pulls in)

## Acceptance

- `mvn -pl system -am clean install -DskipTests -fae` succeeds on Java 1.8.0_504.
- The full reactor `mvn clean install -DskipTests -fae` succeeds on Java 1.8.0_504.
- An oversized (e.g. 100 MB) InputStream supplied to `getConvertedText` throws an exception (not OOM, not silent truncation).
- A normal small PDF still produces text identical to the current implementation.
- The pre-existing `testPdfConvertion` test still passes against the test fixture (`UnitTestResources/com/percussion/search/lucene/converters/pdf.pdf`). The test class is `@Ignore`d, so this is a manual verification.

## Out of scope

- PDFBox 3.x migration. Requires Java 11+; tracked under the parent epic.
- The other PDFBox-using file `PSTikaTextConvertor` (which uses Tika's PDF parser, not PDFBox directly) is out of scope for this slice; Tika is covered by the T2.1 / Tika hardening work in PR #93.

## References

- Parent epic: #73
- T2.9 line item in the parent epic: "T2.9 — Apache PDFBox 2.0.31 hardening"
- Full scan report: `docs/ai-generated/tasks/PR#-DependencyVulnerabilityAnalysis/main-vulnerability-report.md`
- PDFBox input-size guidance: `org.apache.pdfbox.pdmodel.PDDocument.load(InputStream, MemoryUsageSetting)` (introduced in 2.0.0)
- `org.apache.commons.io.input.BoundedInputStream` (commons-io 2.21.0)
- `org.apache.pdfbox.rendering.UsePureJavaCMYKConversion` (PDFBox 2.0.x doc)

Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
## Summary

T2.10 of the parent epic #73 — harden Apache POI 5.2.x (`poi-core` / `poi-ooxml` / `poi-scratchpad`) in the three Lucene full-text-indexing text converters against crafted/oversized Office documents.

POI 5.2.x is the last Java 1.8-compatible line (5.3+ requires Java 11), so the fix has to be a code-level hardening, not a version bump.

## Affected GAVs and CVEs

- `org.apache.poi:poi-core:5.2.3` (5+ CVEs, OOM / resource exhaustion in crafted `.xls`/`.doc`/`.ppt`)
- `org.apache.poi:poi-ooxml:5.2.3` (2+ CVEs in `.xlsx`/`.docx`/`.pptx` parsing)
- `org.apache.poi:poi-scratchpad:5.2.3` (1+ CVE in legacy HSLF / HWPF paths)

Total: 7+ CVE matches across the three artifacts. These are the same CVE class as the T2.9 PDFBox hardening (#127) — crafted document → parser OOM / hang — and the fix uses the same shape.

## Surface in the project

Three Lucene text converters, all in `system/src/main/java/com/percussion/search/lucene/textconverter/`:

- `PSTextConverterMsWord` — used for `application/msword` (legacy `.doc` via `WordExtractor`) and the OOXML `.docx` path (via `XWPFWordExtractor(XWPFDocument)`)
- `PSTextConverterMsExcel` — used for `application/excel` (legacy `.xls` via `HSSFWorkbook`) and the OOXML `.xlsx` path (via `XSSFExcelExtractor(XSSFWorkbook)`)
- `PSTextConverterMsPowerPoint` — used for PowerPoint (`.ppt` / `.pptx` via `XMLSlideShow`)

Each accepts an arbitrary `InputStream` from the indexer and hands it to POI. The corresponding converters for Tika (T2.1) and PDFBox (T2.9) are already hardened; POI is the remaining unmitigated parser surface in the same call family.

## Hardening

Add a small package-private helper `PSTextConverterUtils.readAndCap(InputStream, long)` that:

1. Reads the source stream into a byte array, counting as it goes.
2. Throws `IOException("Input exceeds N bytes (read M before cutoff)")` if the running total crosses the cap. The partial bytes are discarded; the parser is never invoked.
3. Returns a `byte[]` that is fully seekable (`ByteArrayInputStream` supports `mark`/`reset`), so POI's HWPF, HSSF, XWPF, XSSF, and XSLF parsers — all of which backtrack into the input during OPC package / OLE2 compound document reads — can be fed from it.

Each of the three converters now wraps the supplied `InputStream` via `PSTextConverterUtils.readAndCap(is, MAX_INPUT_BYTES)` and feeds the resulting `ByteArrayInputStream` to POI. `MAX_INPUT_BYTES` is `64L * 1024L * 1024L` (64 MiB) — matches the cap used by the T2.9 PDFBox hardening (#128) and is well above any reasonable text-extraction workload for full-text indexing. The cap is a `private static final long` in each converter so it is tunable in one place per class; the helper is shared.

This caps the attack surface at 64 MiB per indexed document, eliminates the OOM / hang class of CVEs, and is a one-way, non-disruptive change to the converter's public API (`String getConvertedText(InputStream, String)`).

## What this PR does NOT do

- No POI version bump. 5.2.x is the last Java 1.8 line; the next line (5.3+) requires Java 11 and is on a future-migration track.
- No changes to the OOXML `OPCPache.open(...)` `setMemoryThreshold` API. The byte-array cap is sufficient for the immediate CVE class; per-stream OPCPackage memory throttling is a future enhancement if real workloads surface.
- No cleanup of the duplicated `if/else` branch in `PSTextConverterMsPowerPoint.getTextExtractor` (both branches are identical). Drive-by cleanups are out of scope per the project's "one focused change per PR" preference.

## Acceptance criteria

- [x] `PSTextConverterUtils.readAndCap` exists in `com.percussion.search.lucene.textconverter` and is package-private.
- [x] `PSTextConverterMsWord`, `PSTextConverterMsExcel`, `PSTextConverterMsPowerPoint` each cap their input at 64 MiB before handing the stream to POI.
- [x] An oversized input throws an `IOException` (wrapped as `PSExtensionProcessingException`) before any POI parser is constructed.
- [x] Full reactor `mvn clean install` is green on Java 1.8.
- [x] Public method signature of all three converters is unchanged; existing callers do not need to be touched.

## Out of scope

- Java 11+ migration. The `main` branch stays on Java 1.8.
- Per-workbook/per-package memory throttling inside POI (would need OPCPackage refactor; not required for the OOM/hang CVE class).

## References

- Parent epic: #73
- Same-pattern prior hardening: T2.9 PDFBox → #127 / PR #128
- Full per-GAV analysis: `docs/ai-generated/tasks/PR#-DependencyVulnerabilityAnalysis/categorized-final.json` (key `NO_JAVA8_UPGRADE`, GAVs `poi-*`)
- Per-module OWASP HTML reports at `<module>/target/dependency-check-report.html`

> Co-Authored by Mavis Mavis-Code using MiniMax-M3 with agent mavis.

Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
## Summary

T2.7 of the parent epic #73 — defense-in-depth pass on the project's Tomcat 9 server.xml: enable the standard Apache-recommended `SecurityListener`, add `allowedRequestAttributesPattern` to both HTTP and HTTPS connectors (with a permissive-but-safer default in `perc-catalina.properties`), and tighten the documentation on the legacy Tomcat 5.x `server.xml` AJP block to make Ghostcat (CVE-2020-1938) impossible to re-introduce by uncommenting it.

**Status of CVE-2020-1938 (Ghostcat) before this PR**: the production Tomcat 9 `server.xml` has no AJP connector at all (the previous engineering team removed it). The legacy Tomcat 5.x `server.xml` has the AJP connector commented out. The runtime install step `PSUpgradeRemoveTomcatAJP` (in `modules/perc-ant/src/main/java/com/percussion/ant/install/PSUpgradeRemoveTomcatAJP.java`) strips any AJP connector that survives into a deployed `server.xml`. So Ghostcat is already mitigated in three independent layers; this PR adds a fourth (documentation) and lifts two other Tomcat 9 security recommendations that were not yet applied.

## What changes

### 1. `deliverytiersuite/delivery-tier-suite/delivery-tier-distribution/src/main/tomcat9/conf/server.xml`

- **Uncomment the `SecurityListener`** (was at lines 8–10). `catalina.sh:300` already sets `-Dorg.apache.catalina.security.SecurityListener.UMASK=$(umask)` as a `JAVA_OPTS`, but the listener itself was commented out, so the property was being set with no listener consuming it. Enabling the listener now:
- applies the umask to every webapp before it starts, so a webapp that creates a file as 0644 cannot accidentally be world-readable on a permissive box;
- writes a deny-all entry to `catalina.policy` if the SecurityManager is enabled, so the deprecated security manager code path is not silently absent;
- is the recommended hardening per the Apache Tomcat 9 security guide (`/docs/config/listeners.html`).
- **Add `allowedRequestAttributesPattern="${http.allowedRequestAttributesPattern}"` to the HTTP connector** (line 110).
- **Add `allowedRequestAttributesPattern="${https.allowedRequestAttributesPattern}"` to the HTTPS connector** (line 165).
- No AJP connector is added — AJP stays absent by design.

### 2. `deliverytiersuite/delivery-tier-suite/delivery-tier-distribution/src/main/conf/perc/perc-catalina.properties`

- **Add `http.allowedRequestAttributesPattern=^(java\.lang\.|javax\.servlet\.|javax\.faces\.|javax\.el\.|jakarta\.servlet\.|org\.apache\.catalina\.|org\.apache\.tomcat\.|com\.percussion\.).*$`**.
- **Add the same pattern for `https.`** at the bottom of the HTTPS block.
- Pattern is anchored (`^...$`), covers the standard JavaEE attribute namespaces (`java.lang.*`, `javax.servlet.*`, `javax.faces.*`, `javax.el.*`, `jakarta.servlet.*`), the Tomcat internal namespaces (`org.apache.catalina.*`, `org.apache.tomcat.*`), and the only project namespace observed in the codebase (`com.percussion.*`). Without this setting, Tomcat 9 forwards any `ServletRequest` attribute across cross-context dispatches, which is a known attribute-isolation weakness.

### 3. `system/release/tomcat/conf/server.xml` (legacy Tomcat 5.x bundled distribution)

- **Add a comment block above the commented AJP connector** explaining that if the connector is ever uncommented, it MUST include `secret="..."` + `secretRequired="true"` to defend against Ghostcat. Includes a safe example form. `PSUpgradeRemoveTomcatAJP` already strips AJP at install time, so this is documentation-only — but a future engineer reading the file will see exactly what the safe form is.

### What this PR does NOT touch

- `server.xml.old` (historical backup, not referenced by any code in the project — left as-is).
- `server-noexamples.xml.config` (Tomcat 4.0 era, not referenced by any code in the project — left as-is).
- `system/ear/jboss-4.0/tomcat/server.xml` (JBoss + Tomcat 4.0 era, not referenced by any code in the project — left as-is).
- Test fixture `server.xml` files in `modules/perc-ant/src/test/resources/...` (mock install trees, not production).
- The Tomcat 5.x / 4.0 era AJP connector syntax (does not support `secret` — added in Tomcat 8.5+).

## CVE class closed

- **CVE-2020-1938 (Ghostcat)** — already mitigated; this PR documents the mitigation in code and adds a fourth layer of defense.
- **Tomcat 9 hardening (CVE-2024-50379, CVE-2024-56337, etc. — partial mitigation)**: the `SecurityListener` and `allowedRequestAttributesPattern` settings are standard Apache Tomcat 9 hardening per the official security guide. They do not directly address any specific CVE in the 9.0.x line (the project is on the latest Java 8 line, 9.0.118) but they bring the runtime config into line with the recommended posture.

## Verification

- `./mvn-env.sh clean install -pl deliverytiersuite/delivery-tier-suite/delivery-tier-distribution -am -DskipTests` → BUILD SUCCESS, 3:18.
- `./mvn-env.sh clean install -DskipTests` → BUILD SUCCESS, 3:56, all 60/60 modules.
- `xmlstarlet validate` on the modified `server.xml` is implicit in the maven-cargo run for `delivery-tier-distribution` (the cargo plugin embeds the file into a working Tomcat at `target/cargo/configurations/tomcat9x/conf/server.xml` and Tomcat 9 must parse it for the run to complete; the previous #128 reactor green proves this is the same code path the project exercises in CI).

## Acceptance criteria

- [x] `org.apache.catalina.security.SecurityListener` is enabled in the production `server.xml`.
- [x] `allowedRequestAttributesPattern` is set on both the HTTP and HTTPS connectors, anchored, and covers the standard JavaEE + Tomcat internal + project namespaces.
- [x] The legacy Tomcat 5.x `server.xml` AJP block has an inline comment documenting the Ghostcat mitigation.
- [x] Full reactor `mvn clean install` is green on Java 1.8.

## Out of scope

- Java 11+ migration. The `main` branch stays on Java 1.8.
- Removal of the unused historical `server.xml.old` / `server-noexamples.xml.config` / `system/ear/jboss-4.0/tomcat/server.xml`. Drive-by cleanup.

## References

- Parent epic: #73
- Full per-GAV analysis: `docs/ai-generated/tasks/PR#-DependencyVulnerabilityAnalysis/categorized-final.json` (key `NO_JAVA8_UPGRADE`, GAVs `tomcat-*`)
- `PSUpgradeRemoveTomcatAJP` (already in tree): `modules/perc-ant/src/main/java/com/percussion/ant/install/PSUpgradeRemoveTomcatAJP.java`
- Apache Tomcat 9 Security Considerations: https://tomcat.apache.org/tomcat-9.0-doc/security-howto.html

> Co-Authored by Mavis Mavis-Code using MiniMax-M3 with agent mavis.

Loading
Loading