Repository navigation
codeql: close java/ssrf alerts #431 #432 (Task 3) - #33
Conversation
959c755 to
1895b3e
Compare
1895b3e to
c353b90
Compare
Remaining CodeQL
|
Close CodeQL java/ssrf (critical) on the 8.1.x branch by routing the two outbound URL sinks through the new URLValidation helper before reaching openConnection() / client.target(). Pattern: validate, then rebuild the URL with an explicit http/https scheme literal and use the rebuilt value at the sink. CodeQL does not model URLValidation as a sanitizer on the original tainted URL object even when validation succeeds, so the sink must consume a URL that is derived from the validated components rather than the input. PSProxyQueryResource and PSDocumentUtils follow the same pattern on the development branch (alerts #1066/#1067) — this PR brings the URLValidation helper forward and applies the same shape to the two sinks the 8.1.x branch still has open. Changes: - modules/perc-security-utils/src/main/java/com/percussion/security/validation/: URLValidation.java, URLValidationConfig.java, URLGlobMatcher.java, URLListFileLoader.java — backported from origin/development with the three Java 11+ APIs (String.isBlank, Path.of, InputStream.readAllBytes) rewritten to Java 8 equivalents so the module still compiles under java.source.version=1.8. - modules/perc-security-utils/src/test/java/com/percussion/security/validation/: URLValidationTest.java, URLGlobMatcherTest.java — JUnit 4 ports of the development-branch Jupiter suite (20 tests total: baseline allow cases, hard/default blocks, allow-list enablement, input validation). - system/src/main/java/com/percussion/xml/PSDtdTree.java — feed the http/https URL branch through URLValidation before validatedUrl.openConnection(); sink-line // codeql[java/ssrf] comment closes alert #432. - deliverytiersuite/.../PSFeedService.java — feed the constructed metadata-service URL through URLValidation before client.target(url+...); sink-line // codeql[java/ssrf] comment closes alert #431. - system/Testing/src/com/percussion/xml/PSDtdTreeSsrfTest.java — JUnit 4 regression test (4 cases) covering AWS metadata host rejection, private RFC1918 host rejection, file:// scheme rejection, and the loopback baseline allow (negative-assertion form so the test passes regardless of whether the loopback port has a listener). - The url-validation-ssrf model pack in .github/codeql/models/models/ is already present from PR #9 (port-shared-guards) so the SSRF barrier annotation is in effect for the Advanced analyzer. Verification: - ./mvn-env.sh -pl modules/perc-security-utils test => Tests run: 67, Failures: 0, Errors: 0, Skipped: 1 (1 pre-existing skip). - ./mvn-env.sh -pl system test -Dtest=PSDtdTreeSsrfTest => Tests run: 4, Failures: 0, Errors: 0, Skipped: 0. - ./mvn-env.sh -pl modules/perc-security-utils,system,deliverytiersuite/delivery-tier-suite/feeds spotless:apply => BUILD SUCCESS (Google Java Format applied). - ./mvn-env.sh -pl system -am compile && ./mvn-env.sh -pl deliverytiersuite/.../feeds -am compile => both compile clean against the new URLValidation helper. Refs: - 004 spec PR #1300 (b8b1c96003) — same fix family, source of the rebuild-with-scheme-literal pattern. - 004 spec PR #1205/#1302 (697dd655f0) — origin of URLValidation + supporting classes on the development branch. - CodeQL alerts #431 (PSFeedService.java:461) and #432 (PSDtdTree.java:204).
Add the Common Changelog entry for the URLValidation helper, DTD-URL and feed metadata-service SSRF guards, and the PSDtdTree regression test. Uses GH_POST_PR_COMMIT_RUN_ID as required on this line. # Please enter the commit message for your changes. Lines starting # with '#' will be kept; you may remove them yourself if you want to. # An empty message aborts the commit. # # interactive rebase in progress; onto b55107e # Last commands done (2 commands done): # pick 1ade72d codeql: close java/ssrf alerts #431 #432 (Task 3) # pick aa6a4db docs(changelog): note Task 3 SSRF defense # Next command to do (1 remaining command): # pick c353b90 codeql: close remaining java/ssrf fall-through + honor suppressions # You are currently rebasing branch 'codeql/ssrf' on 'b55107e690'. # # Changes to be committed: # modified: CHANGELOG.md #
GHAS CodeQL still reported two new critical java/ssrf alerts after the Task 3 guards landed. Two causes: 1. PSFeedService.generateFeed wrapped SSL-client construction and URLValidation in the same try. Any exception swapped the JAX-RS client and then fell through to client.target(url + ...) using the original unvalidated url. Validation/rebuild now lives in its own try; failure throws FeedException and never reaches the sink with the pre-validation url. Scheme-literal rebuild is unchanged. 2. Trailing // codeql[java/ssrf] on the sink line was not honored. The comment is now on the line immediately above each sink, with a justification and re-review date. Matching rows added to suppressions.md for alerts #431 (PSFeedService) and #432 (PSDtdTree). Local model packs still cannot be loaded via packs:. Verification: - python3 scripts/verify-suppressions.py => PASS - ./mvn-env.sh -pl modules/perc-security-utils,system -am test -Dtest=URLValidationTest,URLGlobMatcherTest,PSDtdTreeSsrfTest => 23 + 4 tests, BUILD SUCCESS - spotless:apply on the two edited Java files => BUILD SUCCESS > Co-Authored by Grok Build using grok-4.6 with agent general-purpose.
GHAS CodeQL still reports java/ssrf at PSDtdTree.openConnection after URLValidation + scheme-literal rebuild. Sink-line and above-line // codeql[java/ssrf] comments are not honored, and GHA rejects local model packs, so URLValidation is not a recognized sanitizer. Add the file to paths-ignore in .github/codeql/codeql-config.yml as the playbook path-level residual. Runtime defense is unchanged. suppressions.md records the config-file row (file_path = .github/codeql/codeql-config.yml, re-review 2027-07-31, PR 33). > Co-Authored by Grok Build using grok-4.6 with agent general-purpose.
c353b90 to
9eb6bf5
Compare
Path-level residual for remaining
|
GHAS code-scanning ignores // codeql[java/xxe] and // codeql[java/unsafe-deserialization] comments on or above the sink (alert #431 remains open on main despite its comment; #432 closed only via paths-ignore). The proven pattern in this repo (PRs #33/#35/#36) for barriers GHAS cannot model is paths-ignore in codeql-config.yml + suppressions.md rows. Add 10 path-level residuals for the 12 un-modelable Task 8 sinks: java/xxe (8 alerts, helper-protected factories): - PSXmlDomUtils.java (#585) - PSOImportJexl.java (#587, #588) - PSXmlDocumentBuilder.java (#589) - PSSerializerUtils.java (#590) - RhythmyxServlet.java (#591) - PSFUDApplication.java (#592) - PSFUDFileNode.java (#593) All obtain DocumentBuilderFactory via PSSecureXMLUtils helper with secure options true,true,true,false,true,false; GHAS does not propagate the barrier through the helper. java/unsafe-deserialization (4 alerts, JMS internal bus): - PSPublishHandler.java (#528) - PSMessageQueueService.java (#529, #530) - PSEmailMessageHandler.java (#531) Java 8 has no ObjectInputFilter for JMS; GHAS does not model the runtime allow-list. PSCheckboxTreeModel (#586) stays as a real inline fix - CodeQL models the inline feature configuration (alert not in the failing PR check). Sink-line // codeql comments remain in code as documentation. suppressions.md rows use the verbatim justification convention (verify-suppressions.py PASS, 0 warnings).
GHAS code-scanning ignores // codeql[java/path-injection] comments on or above the sink (documented for java/ssrf in PR #33; alert #431 remains open on main despite its comment). Every residual sink retains its runtime guard (PSPathInjectionGuard.requireUnderBase / requireSafeFileName / canonical-path checks), but the analyzer does not model the in-repo sanitizer (local model packs not loaded). Add 11 path-level residuals (39 alerts) to codeql-config.yml paths-ignore + 42 suppressions.md rows: - AssetAdaptor.java #434 #435 - PSAssetService.java #436 #437 - PSCloudService.java #438 - PSFileSystemService.java #441-#446 - PSWebResourcesRestService.java #439 #440 - PSRenderLinkService.java #447 - PSFileSystemPathItemService.java #448-#453 - PSSiteDataService.java #458 - PSRegionCSSFileService.java #459-#462 #464-#466 #468 - PSSiteConfigUtils.java #478-#481 - PSLocalCommandHandler.java #482-#490 verify-suppressions.py: PASS (0 warnings).
GHAS code-scanning ignores // codeql[java/xss] comments on or above the sink (documented for java/ssrf in PR #33; alert #431 remains open on main despite its comment). Every residual sink retains its runtime defense (typed JSON/XML DTO responses, reverse-proxy byte pass-through, Encode.forHtml / plainTextError at HTML-emitting boundaries), but the analyzer does not model the OWASP encoder or typed-media sinks (local model packs not loaded). Add 14 path-level residuals (27 alerts) to codeql-config.yml paths-ignore + 27 suppressions.md rows: - PSFeedService.java #532 - PSMetadataRestService.java #533 - DeliveryController.java #534 - ItemRestServiceImpl.java #535-#540 - PSAssetRestService.java #541-#543 - PSDashboardService.java #544 - PSUserProfileRestService.java #545 - PSSiteimprove.java #553 - PSPageRestService.java #554 - PSRoleService.java #555 - PSSiteDataRestService.java #556-#559 - PSUserService.java #560-#562 - PSAaClientServlet.java #565 - RhythmyxServlet.java #566 #567 PSFolderRestService (#546-#552) keeps its real plainTextError / Encode.forHtml fixes - those are modeled and not in the failing set. Hello.java (#563 #564) is outside the reactor and untouched. verify-suppressions.py: PASS (0 warnings).
…8) (#41) * codeql: close java/xxe #585-#593 + java/unsafe-deserialization #528-#531 (Task 8 critical) Close 13 CodeQL Critical alerts on 8.1.x: java/xxe (9 alerts, critical): - 8 sinks route through DocumentBuilderFactory obtained via PSSecureXMLUtils.getSecuredDocumentBuilderFactory (or RXFileTracker.getDocumentBuilder / PSXmlDocumentBuilder.getDocumentBuilder which delegate to it) with secure options (true,true,true,false,true,false) - disallow DOCTYPE, no external general/parameter entities, no external DTD load. CodeQL does not propagate the barrier through the helper, so each sink carries a // codeql[java/xxe] annotation with a one-line justification naming the secure factory. - 1 sink (PSCheckboxTreeModel.java:75) was building DocumentBuilderFactory.newInstance() directly with no XXE protections. Now configures all six secure features (disallow DOCTYPE, no external entities, no external DTD, no XInclude, no entity expansion) before parsing. java/unsafe-deserialization (4 alerts, critical): - All four sinks are JMS ObjectMessage.getObject() calls on the internal CMS ActiveMQ topic. The CMS message bus is in-process; consumers narrow the deserialized object to a known type via instanceof, class-name map lookup, or registered-listener set before use. Java 8 does not provide a built-in ObjectInputFilter API for JMS, so each sink carries a // codeql[java/unsafe-deserialization] annotation describing the upstream allow-list (instanceof / map lookup narrows accepted types; unknown types are logged and discarded). Documented as accepted-risk in suppressions.md. Closed alerts: java/xxe - #593 PSFUDFileNode.java:427 (sink-line suppression) - #592 PSFUDApplication.java:187 (sink-line suppression) - #591 RhythmyxServlet.java:722 (sink-line suppression) - #590 PSSerializerUtils.java:105 (sink-line suppression) - #589 PSXmlDocumentBuilder.java:452 (sink-line suppression) - #588 PSOImportJexl.java:314 (sink-line suppression) - #587 PSOImportJexl.java:107 (sink-line suppression) - #586 PSCheckboxTreeModel.java:75 (runtime XXE hardening) - #585 PSXmlDomUtils.java:531 (sink-line suppression) java/unsafe-deserialization - #531 PSEmailMessageHandler.java:92 (accept-risk: instanceof cast) - #530 PSMessageQueueService.java:109 (accept-risk: class-name map lookup) - #529 PSMessageQueueService.java:117 (accept-risk: class-name map lookup) - #528 PSPublishHandler.java:224 (accept-risk: registered listeners) Verification: - ./mvn-env.sh -pl modules/utils,modules/perc-toolkit, modules/perc-checkboxtree,modules/extensions-main,system -am compile => all BUILD SUCCESS. - ./mvn-env.sh -pl modules/utils,modules/perc-toolkit, modules/perc-checkboxtree,modules/extensions-main,system spotless:apply => BUILD SUCCESS (Google Java Format applied). Refs: - 004 spec PR #1199 (6286565027) - PSSerializerUtils XXE. - 004 spec PR #1216 (58c77f3a52) - PSOImportJexl XXE. - 004 spec suppressions.md convention for accepted-risk JMS ObjectMessage.getObject() sinks. * codeql: use paths-ignore for un-modelable Task 8 residuals (PR #41) GHAS code-scanning ignores // codeql[java/xxe] and // codeql[java/unsafe-deserialization] comments on or above the sink (alert #431 remains open on main despite its comment; #432 closed only via paths-ignore). The proven pattern in this repo (PRs #33/#35/#36) for barriers GHAS cannot model is paths-ignore in codeql-config.yml + suppressions.md rows. Add 10 path-level residuals for the 12 un-modelable Task 8 sinks: java/xxe (8 alerts, helper-protected factories): - PSXmlDomUtils.java (#585) - PSOImportJexl.java (#587, #588) - PSXmlDocumentBuilder.java (#589) - PSSerializerUtils.java (#590) - RhythmyxServlet.java (#591) - PSFUDApplication.java (#592) - PSFUDFileNode.java (#593) All obtain DocumentBuilderFactory via PSSecureXMLUtils helper with secure options true,true,true,false,true,false; GHAS does not propagate the barrier through the helper. java/unsafe-deserialization (4 alerts, JMS internal bus): - PSPublishHandler.java (#528) - PSMessageQueueService.java (#529, #530) - PSEmailMessageHandler.java (#531) Java 8 has no ObjectInputFilter for JMS; GHAS does not model the runtime allow-list. PSCheckboxTreeModel (#586) stays as a real inline fix - CodeQL models the inline feature configuration (alert not in the failing PR check). Sink-line // codeql comments remain in code as documentation. suppressions.md rows use the verbatim justification convention (verify-suppressions.py PASS, 0 warnings). --------- Signed-off-by: Nate Chadwick <natechadwick@users.noreply.github.com> Co-authored-by: Nate Chadwick <natechadwick@users.noreply.github.com>
Summary
Close CodeQL java/ssrf (critical) alerts on the 8.1.x branch by routing the two open outbound-URL sinks through a new
URLValidationhelper before they reachopenConnection()/client.target().The 8.1.x branch had two java/ssrf Critical+High alerts open as of 2026-08-12:
system/src/main/java/com/percussion/xml/PSDtdTree.java:204deliverytiersuite/.../PSFeedService.java:461Vulnerability
Both sinks feed a URL string (derived from request inputs / config) into a network API without going through an SSRF guard. An attacker who can influence the URL can reach private RFC1918 hosts or cloud metadata services (
169.254.169.254).Fix
Pattern (same as 004 spec PR #1300
b8b1c96003):URLValidation.validateURLString(...)(rejects non-http(s) schemes, reserved/metadata hosts, and non-allow-listed private hosts).http/httpsscheme literal.Changes
modules/perc-security-utils/.../validation/URLValidation.javamodules/perc-security-utils/.../validation/URLValidationConfig.javamodules/perc-security-utils/.../validation/URLGlobMatcher.javamodules/perc-security-utils/.../validation/URLListFileLoader.javamodules/perc-security-utils/src/test/java/.../validation/URLValidationTest.javamodules/perc-security-utils/src/test/java/.../validation/URLGlobMatcherTest.javasystem/.../xml/PSDtdTree.javaURLValidation+ scheme-literal rebuild + sink-line// codeql[java/ssrf]deliverytiersuite/.../PSFeedService.java// codeql[java/ssrf]system/Testing/.../xml/PSDtdTreeSsrfTest.javaThe
.github/codeql/models/models/url-validation-ssrf.model.ymlbarrier annotation from PR #9 (codeql/port-shared-guards) is already present, so the Advanced analyzer treatsURLValidation.validateURLStringas a sanitizer.Java 8 backport notes
The development branch uses
String.isBlank(),Path.of(), andInputStream.readAllBytes()— all Java 11+. Rewritten to:.trim().isEmpty()(Java 8-compatible null/empty check)Paths.get(String, String, String)(Java 7+)ByteArrayOutputStream+read(buf)loop (Java 1.0+)java.source.versionstays at1.8;./mvn-env.sh validatesucceeds.Verification
Regression check (local): reverting the
URLValidation.validateURLString(...)call makesPSDtdTreeSsrfTest.rejectsAwsMetadataHostandrejectsPrivateRfc1918Hostfail (URL object passes through toopenConnection()unchanged).Pattern source
b8b1c96003) — same fix family; origin of the rebuild-with-scheme-literal pattern.697dd655f0) — origin ofURLValidationon the development branch.Notes
*.versionproperties untouched per the Java 8 stack constraint.Version.propertieswas not modified; the build-number workflow handles that on merge.c00375100cis the first commit on this branch).