From 853161e891b6d968ab5d5826cd226aba3e187526 Mon Sep 17 00:00:00 2001 From: Anthony Stirling <77850077+Frooodle@users.noreply.github.com> Date: Thu, 22 Jan 2026 16:14:29 +0000 Subject: [PATCH] Fix: Whitelist health endpoints in Security and Enterprise Filter + fix keep original commit (#5494) Community PR that had a bug, kept original Commit for history with bug fixed, Merge dont squash merge # Description of Changes This PR fixes #5159 by allowing standard health check endpoints to be accessed without authentication, which is required for container orchestration (Kubernetes/Docker) liveness and readiness probes. Changes: 1. **Security Whitelist (RequestUriUtils):** Added `/actuator/health`, `/healthz`, `/liveness`, and `/readiness` to the public whitelist to prevent 401 Unauthorized. 2. **Enterprise Filter (EnterpriseEndpointFilter):** Added an exception for health endpoints. Previously, this filter was aggressively blocking all /actuator/ requests for non-Pro users, returning 404 Not Found even if the security config was correct. **Testing:** Verified locally. **Before:** `curl http://localhost:8080/actuator/health` returned `401 Unauthorized`. **After:** curl `http://localhost:8080/actuator/health` returns `200 OK` and `{"status":"UP"}`. A sample response from cURL: After the fix ```bash $ curl -v http://localhost:8080/actuator/health ``` Now returns: ```bash * Host localhost:8080 was resolved. * IPv6: ::1 * IPv4: 127.0.0.1 * Trying [::1]:8080... * Connected to localhost (::1) port 8080 > GET /actuator/health HTTP/1.1 > Host: localhost:8080 > User-Agent: curl/8.5.0 > Accept: */* > < HTTP/1.1 200 OK < Date: Mon, 15 Dec 2025 18:39:18 GMT < Vary: Origin < Vary: Access-Control-Request-Method < Vary: Access-Control-Request-Headers < X-Request-Id: 775fb1ee-35de-400d-9e8b-bd1805e3c61a < Content-Type: application/vnd.spring-boot.actuator.v3+json < X-Content-Type-Options: nosniff < X-XSS-Protection: 0 < Cache-Control: no-cache, no-store, max-age=0, must-revalidate < Pragma: no-cache < Expires: 0 < X-Frame-Options: DENY < Transfer-Encoding: chunked < * Connection #0 to host localhost left intact {"status":"UP"} ``` --- ## Checklist ### General - [x] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md) - [x] I have read the [Stirling-PDF Developer Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/DeveloperGuide.md) (if applicable) - [x] I have read the [How to add new languages to Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md) (if applicable) - [x] I have performed a self-review of my own code - [x] My changes generate no new warnings ### Documentation **NOT APPLICABLE** - [ ] I have updated relevant docs on [Stirling-PDF's doc repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/) (if functionality has heavily changed) - [ ] I have read the section [Add New Translation Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### UI Changes (if applicable) **NOT APPLICABLE** - [ ] Screenshots or videos demonstrating the UI changes are attached (e.g., as comments or direct attachments in the PR) ### Testing (if applicable) - [x] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/DeveloperGuide.md#6-testing) for more details. --------- # Description of Changes --- ## Checklist ### General - [ ] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md) - [ ] I have read the [Stirling-PDF Developer Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/DeveloperGuide.md) (if applicable) - [ ] I have read the [How to add new languages to Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md) (if applicable) - [ ] I have performed a self-review of my own code - [ ] My changes generate no new warnings ### Documentation - [ ] I have updated relevant docs on [Stirling-PDF's doc repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/) (if functionality has heavily changed) - [ ] I have read the section [Add New Translation Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### Translations (if applicable) - [ ] I ran [`scripts/counter_translation.py`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/docs/counter_translation.md) ### UI Changes (if applicable) - [ ] Screenshots or videos demonstrating the UI changes are attached (e.g., as comments or direct attachments in the PR) ### Testing (if applicable) - [ ] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/DeveloperGuide.md#6-testing) for more details. --------- Co-authored-by: Saksham Mittal Co-authored-by: Dario Ghunney Ware --- .../software/common/util/RequestUriUtils.java | 18 ++++++++++----- .../filter/EnterpriseEndpointFilter.java | 23 +++++++++++++++++-- 2 files changed, 33 insertions(+), 8 deletions(-) diff --git a/app/common/src/main/java/stirling/software/common/util/RequestUriUtils.java b/app/common/src/main/java/stirling/software/common/util/RequestUriUtils.java index 31321e49d..6310154a8 100644 --- a/app/common/src/main/java/stirling/software/common/util/RequestUriUtils.java +++ b/app/common/src/main/java/stirling/software/common/util/RequestUriUtils.java @@ -38,12 +38,12 @@ public class RequestUriUtils { } // Specific static files bundled with the frontend - if (normalizedUri.equals("/robots.txt") - || normalizedUri.equals("/favicon.ico") - || normalizedUri.equals("/manifest.json") - || normalizedUri.equals("/site.webmanifest") - || normalizedUri.equals("/manifest-classic.json") - || normalizedUri.equals("/index.html")) { + if ("/robots.txt".equals(normalizedUri) + || "/favicon.ico".equals(normalizedUri) + || "/manifest.json".equals(normalizedUri) + || "/site.webmanifest".equals(normalizedUri) + || "/manifest-classic.json".equals(normalizedUri) + || "/index.html".equals(normalizedUri)) { return true; } @@ -173,6 +173,12 @@ public class RequestUriUtils { "/api/v1/ui-data/footer-info") // Public footer configuration || trimmedUri.startsWith("/api/v1/invite/validate") || trimmedUri.startsWith("/api/v1/invite/accept") + // Health Endoints + || trimmedUri.startsWith("/actuator/health") + || trimmedUri.startsWith("/health") + || trimmedUri.startsWith("/healthz") + || trimmedUri.startsWith("/liveness") + || trimmedUri.startsWith("/readiness") || trimmedUri.startsWith( "/api/v1/mobile-scanner/") // Mobile scanner endpoints (no auth) || trimmedUri.startsWith("/v1/api-docs"); diff --git a/app/proprietary/src/main/java/stirling/software/proprietary/security/filter/EnterpriseEndpointFilter.java b/app/proprietary/src/main/java/stirling/software/proprietary/security/filter/EnterpriseEndpointFilter.java index 5ee61f8ff..2d34032cf 100644 --- a/app/proprietary/src/main/java/stirling/software/proprietary/security/filter/EnterpriseEndpointFilter.java +++ b/app/proprietary/src/main/java/stirling/software/proprietary/security/filter/EnterpriseEndpointFilter.java @@ -26,8 +26,27 @@ public class EnterpriseEndpointFilter extends OncePerRequestFilter { throws ServletException, IOException { if (!runningProOrHigher && isPrometheusEndpointRequest(request)) { - response.setStatus(HttpStatus.NOT_FOUND.value()); - return; + // Allow only health checks to pass through for non-pro users + String uri = request.getRequestURI(); + + // Strip the context path + String contextPath = request.getContextPath(); + String trimmedUri = + (contextPath != null && uri.startsWith(contextPath)) + ? uri.substring(contextPath.length()) + : uri; + + boolean isHealthCheck = + trimmedUri.startsWith("/actuator/health") + || "/health".equals(trimmedUri) + || "/healthz".equals(trimmedUri) + || "/liveness".equals(trimmedUri) + || "/readiness".equals(trimmedUri); + + if (!isHealthCheck) { + response.setStatus(HttpStatus.NOT_FOUND.value()); + return; + } } filterChain.doFilter(request, response); }