From de9625942bbc329fdefcfde161476a633a3b3213 Mon Sep 17 00:00:00 2001 From: Anthony Stirling <77850077+Frooodle@users.noreply.github.com> Date: Thu, 2 Apr 2026 12:52:22 +0100 Subject: [PATCH] Pipeline changes and version bump (#6047) # 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: a --- .../common/service/UserServiceInterface.java | 2 ++ .../api/pipeline/PipelineProcessor.java | 30 +++++++++++++++++-- .../api/pipeline/PipelineProcessorTest.java | 3 +- .../security/service/UserService.java | 9 ++++++ build.gradle | 4 +-- frontend/src-tauri/tauri.conf.json | 2 +- .../testing/serverExperienceSimulations.ts | 2 +- .../testing/serverExperienceSimulations.ts | 2 +- 8 files changed, 46 insertions(+), 8 deletions(-) diff --git a/app/common/src/main/java/stirling/software/common/service/UserServiceInterface.java b/app/common/src/main/java/stirling/software/common/service/UserServiceInterface.java index 074f422000..9649696567 100644 --- a/app/common/src/main/java/stirling/software/common/service/UserServiceInterface.java +++ b/app/common/src/main/java/stirling/software/common/service/UserServiceInterface.java @@ -5,6 +5,8 @@ public interface UserServiceInterface { String getCurrentUsername(); + String getCurrentUserApiKey(); + long getTotalUsersCount(); boolean isCurrentUserAdmin(); diff --git a/app/core/src/main/java/stirling/software/SPDF/controller/api/pipeline/PipelineProcessor.java b/app/core/src/main/java/stirling/software/SPDF/controller/api/pipeline/PipelineProcessor.java index 1eac93b227..4d43faf629 100644 --- a/app/core/src/main/java/stirling/software/SPDF/controller/api/pipeline/PipelineProcessor.java +++ b/app/core/src/main/java/stirling/software/SPDF/controller/api/pipeline/PipelineProcessor.java @@ -37,7 +37,6 @@ import stirling.software.SPDF.model.PipelineConfig; import stirling.software.SPDF.model.PipelineOperation; import stirling.software.SPDF.model.PipelineResult; import stirling.software.SPDF.service.ApiDocService; -import stirling.software.common.model.enumeration.Role; import stirling.software.common.service.UserServiceInterface; import stirling.software.common.util.TempFile; import stirling.software.common.util.TempFileManager; @@ -84,9 +83,35 @@ public class PipelineProcessor { return name.substring(0, underscoreIndex) + extension; } + // Allowlist of URL path prefixes permitted through the pipeline. + private static final List ALLOWED_PIPELINE_PATH_PREFIXES = + List.of( + "/api/v1/general/", + "/api/v1/misc/", + "/api/v1/security/", + "/api/v1/convert/", + "/api/v1/filter/"); + + private void validatePipelineUrl(String url) { + // Strip scheme+host to get the path portion for comparison + String path = url; + int schemeEnd = url.indexOf("://"); + if (schemeEnd != -1) { + int pathStart = url.indexOf('/', schemeEnd + 3); + path = pathStart != -1 ? url.substring(pathStart) : "/"; + } + final String pathToCheck = path; + boolean allowed = ALLOWED_PIPELINE_PATH_PREFIXES.stream().anyMatch(pathToCheck::contains); + if (!allowed) { + log.warn("Blocked pipeline request to disallowed URL: {}", url); + throw new SecurityException( + "Pipeline operation not permitted for endpoint: " + pathToCheck); + } + } + private String getApiKeyForUser() { if (userService == null) return ""; - return userService.getApiKeyForUser(Role.INTERNAL_API_USER.getRoleId()); + return userService.getCurrentUserApiKey(); } private String getBaseUrl() { @@ -283,6 +308,7 @@ public class PipelineProcessor { /* package */ ResponseEntity sendWebRequest( String url, MultiValueMap body) { + validatePipelineUrl(url); RestTemplate restTemplate = new RestTemplate(); // Set up headers, including API key HttpHeaders headers = new HttpHeaders(); diff --git a/app/core/src/test/java/stirling/software/SPDF/controller/api/pipeline/PipelineProcessorTest.java b/app/core/src/test/java/stirling/software/SPDF/controller/api/pipeline/PipelineProcessorTest.java index d58770f45d..6b6ed82976 100644 --- a/app/core/src/test/java/stirling/software/SPDF/controller/api/pipeline/PipelineProcessorTest.java +++ b/app/core/src/test/java/stirling/software/SPDF/controller/api/pipeline/PipelineProcessorTest.java @@ -205,7 +205,8 @@ class PipelineProcessorTest { }); })) { ResponseEntity response = - pipelineProcessor.sendWebRequest("http://localhost/api", body); + pipelineProcessor.sendWebRequest( + "http://localhost/api/v1/general/merge-pdfs", body); assertNotNull(response); assertEquals(HttpStatus.OK, response.getStatusCode()); diff --git a/app/proprietary/src/main/java/stirling/software/proprietary/security/service/UserService.java b/app/proprietary/src/main/java/stirling/software/proprietary/security/service/UserService.java index ea960207dd..e18c101e1e 100644 --- a/app/proprietary/src/main/java/stirling/software/proprietary/security/service/UserService.java +++ b/app/proprietary/src/main/java/stirling/software/proprietary/security/service/UserService.java @@ -198,6 +198,15 @@ public class UserService implements UserServiceInterface { return user.getApiKey(); } + @Override + public String getCurrentUserApiKey() { + String username = getCurrentUsername(); + if (username == null || username.isEmpty()) { + throw new IllegalStateException("Cannot determine calling user for API key lookup"); + } + return getApiKeyForUser(username); + } + public boolean isValidApiKey(String apiKey) { return userRepository.findByApiKey(apiKey).isPresent(); } diff --git a/build.gradle b/build.gradle index b41c54e3b8..5acd760592 100644 --- a/build.gradle +++ b/build.gradle @@ -28,7 +28,7 @@ ext { springSecuritySamlVersion = "7.0.2" openSamlVersion = "5.2.1" commonmarkVersion = "0.27.1" - googleJavaFormatVersion = "1.35.0" + googleJavaFormatVersion = "1.28.0" logback = "1.5.32" junitPlatformVersion = "1.12.2" modernJavaVersion = 21 @@ -78,7 +78,7 @@ springBoot { allprojects { group = 'stirling.software' - version = '2.8.0' + version = '2.9.0' configurations.configureEach { exclude group: "org.springframework.boot", module: "spring-boot-starter-tomcat" diff --git a/frontend/src-tauri/tauri.conf.json b/frontend/src-tauri/tauri.conf.json index 44ef827c3f..84acfeba8a 100644 --- a/frontend/src-tauri/tauri.conf.json +++ b/frontend/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "../node_modules/@tauri-apps/cli/config.schema.json", "productName": "Stirling-PDF", - "version": "2.8.0", + "version": "2.9.0", "identifier": "stirling.pdf.dev", "build": { "frontendDist": "../dist", diff --git a/frontend/src/core/testing/serverExperienceSimulations.ts b/frontend/src/core/testing/serverExperienceSimulations.ts index bbd9b1702a..06bd8fdc76 100644 --- a/frontend/src/core/testing/serverExperienceSimulations.ts +++ b/frontend/src/core/testing/serverExperienceSimulations.ts @@ -38,7 +38,7 @@ const FREE_LICENSE_INFO: LicenseInfo = { const BASE_NO_LOGIN_CONFIG: AppConfig = { enableAnalytics: true, - appVersion: '2.8.0', + appVersion: '2.9.0', serverCertificateEnabled: false, enableAlphaFunctionality: false, serverPort: 8080, diff --git a/frontend/src/proprietary/testing/serverExperienceSimulations.ts b/frontend/src/proprietary/testing/serverExperienceSimulations.ts index 4177f296ee..d7087533f4 100644 --- a/frontend/src/proprietary/testing/serverExperienceSimulations.ts +++ b/frontend/src/proprietary/testing/serverExperienceSimulations.ts @@ -48,7 +48,7 @@ const FREE_LICENSE_INFO: LicenseInfo = { const BASE_NO_LOGIN_CONFIG: AppConfig = { enableAnalytics: true, - appVersion: '2.8.0', + appVersion: '2.9.0', serverCertificateEnabled: false, enableAlphaFunctionality: false, enableDesktopInstallSlide: true,