From fbee4b99e42adabb854f43116a1bdb7580c93b31 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 20 Aug 2025 15:33:56 +0100 Subject: [PATCH 1/7] build(deps): bump actions/dependency-review-action from 4.7.1 to 4.7.2 (#4230) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [actions/dependency-review-action](https://github.com/actions/dependency-review-action) from 4.7.1 to 4.7.2.
Release notes

Sourced from actions/dependency-review-action's releases.

4.7.2

What's Changed

New Contributors

Full Changelog: https://github.com/actions/dependency-review-action/compare/v4...v4.7.2

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=actions/dependency-review-action&package-manager=github_actions&previous-version=4.7.1&new-version=4.7.2)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/dependency-review.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml index 8d938011d..d55dbd783 100644 --- a/.github/workflows/dependency-review.yml +++ b/.github/workflows/dependency-review.yml @@ -24,4 +24,4 @@ jobs: - name: "Checkout Repository" uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 - name: "Dependency Review" - uses: actions/dependency-review-action@da24556b548a50705dd671f47852072ea4c105d9 # v4.7.1 + uses: actions/dependency-review-action@bc41886e18ea39df68b1b1245f4184881938e050 # v4.7.2 From 12d4e26aa35116920d3b31b046532ef7afce181b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 20 Aug 2025 15:34:11 +0100 Subject: [PATCH 2/7] build(deps): bump jwtVersion from 0.12.6 to 0.12.7 (#4229) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps `jwtVersion` from 0.12.6 to 0.12.7. Updates `io.jsonwebtoken:jjwt-api` from 0.12.6 to 0.12.7
Release notes

Sourced from io.jsonwebtoken:jjwt-api's releases.

0.12.7

This patch release:

  • Adds a new Maven BOM! This is useful for multi-module projects. See Issue 967.

  • Allows the JwtParserBuilder to have empty nested algorithm collections, effectively disabling the parser's associated feature:

    • Emptying the zip() nested collection disables JWT decompression.
    • Emptying the sig() nested collection disables JWS mac/signature verification (i.e. all JWSs will be unsupported/rejected).
    • Emptying either the enc() or key() nested collections disables JWE decryption (i.e. all JWEs will be unsupported/rejected)

    See Issue 996.

  • Fixes bug 961 where JwtParserBuilder nested collection builders were not correctly replacing algorithms with the same id.

  • Ensures a JwkSet's keys collection is no longer entirely secret/redacted by default. This was an overzealous default that was unnecessarily restrictive; the keys collection itself should always be public, and each individual key within should determine which fields should be redacted when printed. See Issue 976.

  • Improves performance slightly by ensuring all jjwt-api utility methods that create *Builder instances (Jwts.builder(), Jwts.parserBuilder(), Jwks.builder(), etc) no longer use reflection.

    Instead,static factories are created via reflection only once during initial jjwt-api classloading, and then *Builders are created via standard instantiation using the new operator thereafter. This also benefits certain environments that may not have ideal ClassLoader implementations (e.g. Tomcat in some cases).

    NOTE: because this changes which classes are loaded via reflection, any environments that must explicitly reference reflective class names (e.g. GraalVM applications) will need to be updated to reflect the new factory class names.

    See Issue 988.

  • Upgrades the Gson dependency to 2.11.0

  • Upgrades the BouncyCastle dependency to 1.78.1

New Contributors

Full Changelog: https://github.com/jwtk/jjwt/compare/0.12.6...0.12.7

Changelog

Sourced from io.jsonwebtoken:jjwt-api's changelog.

0.12.7

This patch release:

  • Adds a new Maven BOM, useful for multi-module projects. See Issue 967.

  • Allows the JwtParserBuilder to have empty nested algorithm collections, effectively disabling the parser's associated feature:

    • Emptying the zip() nested collection disables JWT decompression.
    • Emptying the sig() nested collection disables JWS mac/signature verification (i.e. all JWSs will be unsupported/rejected).
    • Emptying either the enc() or key() nested collections disables JWE decryption (i.e. all JWEs will be unsupported/rejected)

    See Issue 996.

  • Fixes bug 961 where JwtParserBuilder nested collection builders were not correctly replacing algorithms with the same id.

  • Ensures a JwkSet's keys collection is no longer entirely secret/redacted by default. This was an overzealous default that was unnecessarily restrictive; the keys collection itself should always be public, and each individual key within should determine which fields should be redacted when printed. See Issue 976.

  • Improves performance slightly by ensuring all jjwt-api utility methods that create *Builder instances (Jwts.builder(), Jwts.parserBuilder(), Jwks.builder(), etc) no longer use reflection.

    Instead,static factories are created via reflection only once during initial jjwt-api classloading, and then *Builders are created via standard instantiation using the new operator thereafter. This also benefits certain environments that may not have ideal ClassLoader implementations (e.g. Tomcat in some cases).

    NOTE: because this changes which classes are loaded via reflection, any environments that must explicitly reference reflective class names (e.g. GraalVM applications) will need to be updated to reflect the new factory class names.

    See Issue 988.

  • Upgrades the Gson dependency to 2.11.0

  • Upgrades the BouncyCastle dependency to 1.78.1

Commits

Updates `io.jsonwebtoken:jjwt-impl` from 0.12.6 to 0.12.7
Release notes

Sourced from io.jsonwebtoken:jjwt-impl's releases.

0.12.7

This patch release:

  • Adds a new Maven BOM! This is useful for multi-module projects. See Issue 967.

  • Allows the JwtParserBuilder to have empty nested algorithm collections, effectively disabling the parser's associated feature:

    • Emptying the zip() nested collection disables JWT decompression.
    • Emptying the sig() nested collection disables JWS mac/signature verification (i.e. all JWSs will be unsupported/rejected).
    • Emptying either the enc() or key() nested collections disables JWE decryption (i.e. all JWEs will be unsupported/rejected)

    See Issue 996.

  • Fixes bug 961 where JwtParserBuilder nested collection builders were not correctly replacing algorithms with the same id.

  • Ensures a JwkSet's keys collection is no longer entirely secret/redacted by default. This was an overzealous default that was unnecessarily restrictive; the keys collection itself should always be public, and each individual key within should determine which fields should be redacted when printed. See Issue 976.

  • Improves performance slightly by ensuring all jjwt-api utility methods that create *Builder instances (Jwts.builder(), Jwts.parserBuilder(), Jwks.builder(), etc) no longer use reflection.

    Instead,static factories are created via reflection only once during initial jjwt-api classloading, and then *Builders are created via standard instantiation using the new operator thereafter. This also benefits certain environments that may not have ideal ClassLoader implementations (e.g. Tomcat in some cases).

    NOTE: because this changes which classes are loaded via reflection, any environments that must explicitly reference reflective class names (e.g. GraalVM applications) will need to be updated to reflect the new factory class names.

    See Issue 988.

  • Upgrades the Gson dependency to 2.11.0

  • Upgrades the BouncyCastle dependency to 1.78.1

New Contributors

Full Changelog: https://github.com/jwtk/jjwt/compare/0.12.6...0.12.7

Changelog

Sourced from io.jsonwebtoken:jjwt-impl's changelog.

0.12.7

This patch release:

  • Adds a new Maven BOM, useful for multi-module projects. See Issue 967.

  • Allows the JwtParserBuilder to have empty nested algorithm collections, effectively disabling the parser's associated feature:

    • Emptying the zip() nested collection disables JWT decompression.
    • Emptying the sig() nested collection disables JWS mac/signature verification (i.e. all JWSs will be unsupported/rejected).
    • Emptying either the enc() or key() nested collections disables JWE decryption (i.e. all JWEs will be unsupported/rejected)

    See Issue 996.

  • Fixes bug 961 where JwtParserBuilder nested collection builders were not correctly replacing algorithms with the same id.

  • Ensures a JwkSet's keys collection is no longer entirely secret/redacted by default. This was an overzealous default that was unnecessarily restrictive; the keys collection itself should always be public, and each individual key within should determine which fields should be redacted when printed. See Issue 976.

  • Improves performance slightly by ensuring all jjwt-api utility methods that create *Builder instances (Jwts.builder(), Jwts.parserBuilder(), Jwks.builder(), etc) no longer use reflection.

    Instead,static factories are created via reflection only once during initial jjwt-api classloading, and then *Builders are created via standard instantiation using the new operator thereafter. This also benefits certain environments that may not have ideal ClassLoader implementations (e.g. Tomcat in some cases).

    NOTE: because this changes which classes are loaded via reflection, any environments that must explicitly reference reflective class names (e.g. GraalVM applications) will need to be updated to reflect the new factory class names.

    See Issue 988.

  • Upgrades the Gson dependency to 2.11.0

  • Upgrades the BouncyCastle dependency to 1.78.1

Commits

Updates `io.jsonwebtoken:jjwt-jackson` from 0.12.6 to 0.12.7 Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- app/proprietary/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/proprietary/build.gradle b/app/proprietary/build.gradle index b8862bdd8..0254d63ed 100644 --- a/app/proprietary/build.gradle +++ b/app/proprietary/build.gradle @@ -3,7 +3,7 @@ repositories { } ext { - jwtVersion = '0.12.6' + jwtVersion = '0.12.7' } bootRun { From 246a59a79436472f05a2dae6a07257f0cd0339e5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 20 Aug 2025 15:34:26 +0100 Subject: [PATCH 3/7] build(deps): bump github/codeql-action from 3.29.8 to 3.29.10 (#4231) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.29.8 to 3.29.10.
Release notes

Sourced from github/codeql-action's releases.

v3.29.10

CodeQL Action Changelog

See the releases page for the relevant changes to the CodeQL CLI and language packs.

3.29.10 - 18 Aug 2025

No user facing changes.

See the full CHANGELOG.md for more information.

v3.29.9

CodeQL Action Changelog

See the releases page for the relevant changes to the CodeQL CLI and language packs.

3.29.9 - 12 Aug 2025

No user facing changes.

See the full CHANGELOG.md for more information.

Changelog

Sourced from github/codeql-action's changelog.

CodeQL Action Changelog

See the releases page for the relevant changes to the CodeQL CLI and language packs.

[UNRELEASED]

No user facing changes.

3.29.10 - 18 Aug 2025

No user facing changes.

3.29.9 - 12 Aug 2025

No user facing changes.

3.29.8 - 08 Aug 2025

  • Fix an issue where the Action would autodetect unsupported languages such as HTML. #3015

3.29.7 - 07 Aug 2025

This release rolls back 3.29.6 to address issues with language autodetection. It is identical to 3.29.5.

3.29.6 - 07 Aug 2025

  • The cleanup-level input to the analyze Action is now deprecated. The CodeQL Action has written a limited amount of intermediate results to the database since version 2.2.5, and now automatically manages cleanup. #2999
  • Update default CodeQL bundle version to 2.22.3. #3000

3.29.5 - 29 Jul 2025

  • Update default CodeQL bundle version to 2.22.2. #2986

3.29.4 - 23 Jul 2025

No user facing changes.

3.29.3 - 21 Jul 2025

No user facing changes.

3.29.2 - 30 Jun 2025

  • Experimental: When the quality-queries input for the init action is provided with an argument, separate .quality.sarif files are produced and uploaded for each language with the results of the specified queries. Do not use this in production as it is part of an internal experiment and subject to change at any time. #2935

3.29.1 - 27 Jun 2025

  • Fix bug in PR analysis where user-provided include query filter fails to exclude non-included queries. #2938
  • Update default CodeQL bundle version to 2.22.1. #2950

... (truncated)

Commits
  • 96f518a Merge pull request #3042 from github/update-v3.29.10-6ec994ecb
  • 57a1c6b Update changelog for v3.29.10
  • 6ec994e Merge pull request #3039 from github/mbg/remove-cpp-bmn-check
  • 3f00c7c Remove unused C++ BMN FF
  • 141ee4a Remove C++ BMN FF check that is no longer used
  • 2330521 Merge pull request #3037 from github/henrymercer/failed-upload-logs
  • 3966569 Merge pull request #3035 from github/henrymercer/fix-cleanup-info
  • f7bd70c Merge branch 'main' into henrymercer/failed-upload-logs
  • 75151c2 Merge branch 'main' into henrymercer/fix-cleanup-info
  • 4ff91f1 Merge pull request #3036 from github/mbg/ci/gradle9
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=github/codeql-action&package-manager=github_actions&previous-version=3.29.8&new-version=3.29.10)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/scorecards.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml index a3a355845..53ad28c84 100644 --- a/.github/workflows/scorecards.yml +++ b/.github/workflows/scorecards.yml @@ -74,6 +74,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@76621b61decf072c1cee8dd1ce2d2a82d33c17ed # v3.29.5 + uses: github/codeql-action/upload-sarif@96f518a34f7a870018057716cc4d7a5c014bd61c # v3.29.5 with: sarif_file: results.sarif From c10474fd3035fcde5e9d911e6c8a9eb4be015398 Mon Sep 17 00:00:00 2001 From: Ludy Date: Wed, 20 Aug 2025 16:35:24 +0200 Subject: [PATCH 4/7] fix(h2): refine SQL condition check for custom database flag (#4216) # Description of Changes - Refactored `H2SQLCondition.matches` to use `env.getProperty` with proper default values and types. - Adjusted logic to only return `false` when a custom database is enabled and datasource type is not `h2`. - Simplified environment variable handling for better readability and robustness. --- ## 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) - [ ] 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 - [ ] 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) - [ ] 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. --- .../proprietary/security/database/H2SQLCondition.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/app/proprietary/src/main/java/stirling/software/proprietary/security/database/H2SQLCondition.java b/app/proprietary/src/main/java/stirling/software/proprietary/security/database/H2SQLCondition.java index 4e259e49b..6cb5d2bce 100644 --- a/app/proprietary/src/main/java/stirling/software/proprietary/security/database/H2SQLCondition.java +++ b/app/proprietary/src/main/java/stirling/software/proprietary/security/database/H2SQLCondition.java @@ -8,16 +8,15 @@ public class H2SQLCondition implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { + var env = context.getEnvironment(); boolean enableCustomDatabase = - Boolean.parseBoolean( - context.getEnvironment() - .getProperty("system.datasource.enableCustomDatabase")); + env.getProperty("system.datasource.enableCustomDatabase", Boolean.class, false); - if (!enableCustomDatabase) { + if (enableCustomDatabase) { return false; } - String dataSourceType = context.getEnvironment().getProperty("system.datasource.type"); + String dataSourceType = env.getProperty("system.datasource.type", String.class, ""); return "h2".equalsIgnoreCase(dataSourceType); } } From ab7cef5a97446997750271838704b25e73963215 Mon Sep 17 00:00:00 2001 From: Ludy Date: Wed, 20 Aug 2025 16:36:39 +0200 Subject: [PATCH 5/7] feat(common,core,proprietary): remove unused injections, enhance type safety, and improve test mocks (#4213) # Description of Changes This PR introduces several refactorings and minor enhancements across the `common`, `core`, and `proprietary` modules: - **Dependency Injection Cleanup** - Removed unused constructor-injected dependencies (e.g., `FileOrUploadService`, `ApplicationProperties`, redundant `@Autowired` annotations). - Simplified constructors to only require actively used dependencies. - **Model Enhancements** - Added `@NoArgsConstructor` to `FileInfo`, `PdfMetadata`, and `SignatureFile` to improve serialization/deserialization support. - **Service Improvements** - Improved `JobExecutorService` content type retrieval by assigning `MediaType` to a variable before conversion. - Enhanced `KeyPersistenceService` with type-safe `.filter(JwtVerificationKey.class::isInstance)`. - Annotated `decodePublicKey` in `KeyPersistenceService` with `@Override` for clarity. - **Controller & API Changes** - Updated `AdminSettingsController` to use `TypeReference>` for safer conversion. - Improved long log and description strings with consistent formatting. - **Testing Updates** - Replaced `.lenient()` mock settings with `.defaultAnswer(RETURNS_DEFAULTS)` for `FileToPdf` static mocks. - Used `ArgumentMatchers.>>any()` in `EditTableOfContentsControllerTest` for type safety. - Updated `UserServiceTest` default `AuthenticationType` from `SSO` to `OAUTH2`. - **Formatting** - Broke up long log/debug lines for better readability. - Removed redundant `@SuppressWarnings` where type safety was ensured. These changes aim to make the codebase leaner, more type-safe, and maintainable, while improving test reliability. --- ## 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) - [ ] 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 - [ ] 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) - [ ] 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. --- .../software/common/aop/AutoJobAspect.java | 8 ++--- .../software/common/model/FileInfo.java | 4 ++- .../software/common/model/PdfMetadata.java | 4 +++ .../common/service/JobExecutorService.java | 6 ++-- .../common/util/CustomHtmlSanitizer.java | 2 -- .../AutoJobPostMappingIntegrationTest.java | 6 +--- .../software/common/util/EmlToPdfTest.java | 15 +++++++-- .../software/SPDF/SPDFApplication.java | 4 --- .../SPDF/config/EndpointInspector.java | 15 ++++----- .../software/SPDF/model/SignatureFile.java | 2 ++ .../service/MetricsAggregatorService.java | 10 +++--- .../EditTableOfContentsControllerTest.java | 19 ++++++++--- .../api/AdminSettingsController.java | 33 ++++++++++++------- .../filter/UserAuthenticationFilter.java | 18 +++++----- .../security/service/JwtService.java | 5 ++- .../service/KeyPairCleanupService.java | 2 -- .../service/KeyPersistenceService.java | 7 ++-- .../security/service/UserServiceTest.java | 2 +- 18 files changed, 91 insertions(+), 71 deletions(-) diff --git a/app/common/src/main/java/stirling/software/common/aop/AutoJobAspect.java b/app/common/src/main/java/stirling/software/common/aop/AutoJobAspect.java index 2ee10ebcd..ac36cd0d7 100644 --- a/app/common/src/main/java/stirling/software/common/aop/AutoJobAspect.java +++ b/app/common/src/main/java/stirling/software/common/aop/AutoJobAspect.java @@ -19,7 +19,6 @@ import lombok.extern.slf4j.Slf4j; import stirling.software.common.annotations.AutoJobPostMapping; import stirling.software.common.model.api.PDFFile; -import stirling.software.common.service.FileOrUploadService; import stirling.software.common.service.FileStorage; import stirling.software.common.service.JobExecutorService; @@ -34,7 +33,6 @@ public class AutoJobAspect { private final JobExecutorService jobExecutorService; private final HttpServletRequest request; - private final FileOrUploadService fileOrUploadService; private final FileStorage fileStorage; @Around("@annotation(autoJobPostMapping)") @@ -53,7 +51,8 @@ public class AutoJobAspect { boolean trackProgress = autoJobPostMapping.trackProgress(); log.debug( - "AutoJobPostMapping execution with async={}, timeout={}, retryCount={}, trackProgress={}", + "AutoJobPostMapping execution with async={}, timeout={}, retryCount={}," + + " trackProgress={}", async, timeout > 0 ? timeout : "default", retryCount, @@ -148,7 +147,8 @@ public class AutoJobAspect { } catch (Throwable ex) { lastException = ex; log.error( - "AutoJobAspect caught exception during job execution (attempt {}/{}): {}", + "AutoJobAspect caught exception during job execution (attempt" + + " {}/{}): {}", currentAttempt, maxRetries, ex.getMessage(), diff --git a/app/common/src/main/java/stirling/software/common/model/FileInfo.java b/app/common/src/main/java/stirling/software/common/model/FileInfo.java index 41a3a4717..2e3e59e83 100644 --- a/app/common/src/main/java/stirling/software/common/model/FileInfo.java +++ b/app/common/src/main/java/stirling/software/common/model/FileInfo.java @@ -8,9 +8,11 @@ import java.util.Locale; import lombok.AllArgsConstructor; import lombok.Data; +import lombok.NoArgsConstructor; -@AllArgsConstructor @Data +@NoArgsConstructor +@AllArgsConstructor public class FileInfo { private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); diff --git a/app/common/src/main/java/stirling/software/common/model/PdfMetadata.java b/app/common/src/main/java/stirling/software/common/model/PdfMetadata.java index ef8684788..be588ec86 100644 --- a/app/common/src/main/java/stirling/software/common/model/PdfMetadata.java +++ b/app/common/src/main/java/stirling/software/common/model/PdfMetadata.java @@ -2,11 +2,15 @@ package stirling.software.common.model; import java.util.Calendar; +import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; +import lombok.NoArgsConstructor; @Data @Builder +@NoArgsConstructor +@AllArgsConstructor public class PdfMetadata { private String author; private String producer; diff --git a/app/common/src/main/java/stirling/software/common/service/JobExecutorService.java b/app/common/src/main/java/stirling/software/common/service/JobExecutorService.java index 73afa22a0..ac0833995 100644 --- a/app/common/src/main/java/stirling/software/common/service/JobExecutorService.java +++ b/app/common/src/main/java/stirling/software/common/service/JobExecutorService.java @@ -252,8 +252,10 @@ public class JobExecutorService { } } - if (response.getHeaders().getContentType() != null) { - contentType = response.getHeaders().getContentType().toString(); + MediaType mediaType = response.getHeaders().getContentType(); + + if (mediaType != null) { + contentType = mediaType.toString(); } // Store byte array directly to disk diff --git a/app/common/src/main/java/stirling/software/common/util/CustomHtmlSanitizer.java b/app/common/src/main/java/stirling/software/common/util/CustomHtmlSanitizer.java index 05d9b73a6..60bf9ab83 100644 --- a/app/common/src/main/java/stirling/software/common/util/CustomHtmlSanitizer.java +++ b/app/common/src/main/java/stirling/software/common/util/CustomHtmlSanitizer.java @@ -4,7 +4,6 @@ import org.owasp.html.AttributePolicy; import org.owasp.html.HtmlPolicyBuilder; import org.owasp.html.PolicyFactory; import org.owasp.html.Sanitizers; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import stirling.software.common.model.ApplicationProperties; @@ -16,7 +15,6 @@ public class CustomHtmlSanitizer { private final SsrfProtectionService ssrfProtectionService; private final ApplicationProperties applicationProperties; - @Autowired public CustomHtmlSanitizer( SsrfProtectionService ssrfProtectionService, ApplicationProperties applicationProperties) { diff --git a/app/common/src/test/java/stirling/software/common/annotations/AutoJobPostMappingIntegrationTest.java b/app/common/src/test/java/stirling/software/common/annotations/AutoJobPostMappingIntegrationTest.java index 2c4546ac0..dd7a0f79b 100644 --- a/app/common/src/test/java/stirling/software/common/annotations/AutoJobPostMappingIntegrationTest.java +++ b/app/common/src/test/java/stirling/software/common/annotations/AutoJobPostMappingIntegrationTest.java @@ -29,7 +29,6 @@ import jakarta.servlet.http.HttpServletRequest; import stirling.software.common.aop.AutoJobAspect; import stirling.software.common.model.api.PDFFile; -import stirling.software.common.service.FileOrUploadService; import stirling.software.common.service.FileStorage; import stirling.software.common.service.JobExecutorService; import stirling.software.common.service.JobQueue; @@ -44,8 +43,6 @@ class AutoJobPostMappingIntegrationTest { @Mock private HttpServletRequest request; - @Mock private FileOrUploadService fileOrUploadService; - @Mock private FileStorage fileStorage; @Mock private ResourceMonitor resourceMonitor; @@ -54,8 +51,7 @@ class AutoJobPostMappingIntegrationTest { @BeforeEach void setUp() { - autoJobAspect = - new AutoJobAspect(jobExecutorService, request, fileOrUploadService, fileStorage); + autoJobAspect = new AutoJobAspect(jobExecutorService, request, fileStorage); } @Mock private ProceedingJoinPoint joinPoint; diff --git a/app/common/src/test/java/stirling/software/common/util/EmlToPdfTest.java b/app/common/src/test/java/stirling/software/common/util/EmlToPdfTest.java index 952bc692a..9385d260c 100644 --- a/app/common/src/test/java/stirling/software/common/util/EmlToPdfTest.java +++ b/app/common/src/test/java/stirling/software/common/util/EmlToPdfTest.java @@ -586,7 +586,10 @@ class EmlToPdfTest { when(mockPdDocument.getNumberOfPages()).thenReturn(1); try (MockedStatic fileToPdf = - mockStatic(FileToPdf.class, org.mockito.Mockito.withSettings().lenient())) { + mockStatic( + FileToPdf.class, + org.mockito.Mockito.withSettings() + .defaultAnswer(org.mockito.Answers.RETURNS_DEFAULTS))) { fileToPdf .when( () -> @@ -657,7 +660,10 @@ class EmlToPdfTest { when(mockPdDocument.getNumberOfPages()).thenReturn(1); try (MockedStatic fileToPdf = - mockStatic(FileToPdf.class, org.mockito.Mockito.withSettings().lenient())) { + mockStatic( + FileToPdf.class, + org.mockito.Mockito.withSettings() + .defaultAnswer(org.mockito.Answers.RETURNS_DEFAULTS))) { fileToPdf .when( () -> @@ -724,7 +730,10 @@ class EmlToPdfTest { String errorMessage = "Conversion failed"; try (MockedStatic fileToPdf = - mockStatic(FileToPdf.class, org.mockito.Mockito.withSettings().lenient())) { + mockStatic( + FileToPdf.class, + org.mockito.Mockito.withSettings() + .defaultAnswer(org.mockito.Answers.RETURNS_DEFAULTS))) { fileToPdf .when( () -> diff --git a/app/core/src/main/java/stirling/software/SPDF/SPDFApplication.java b/app/core/src/main/java/stirling/software/SPDF/SPDFApplication.java index 2131b4239..9322cea23 100644 --- a/app/core/src/main/java/stirling/software/SPDF/SPDFApplication.java +++ b/app/core/src/main/java/stirling/software/SPDF/SPDFApplication.java @@ -27,7 +27,6 @@ import stirling.software.SPDF.UI.WebBrowser; import stirling.software.common.configuration.AppConfig; import stirling.software.common.configuration.ConfigInitializer; import stirling.software.common.configuration.InstallationPathConfig; -import stirling.software.common.model.ApplicationProperties; import stirling.software.common.util.UrlUtils; @Slf4j @@ -46,17 +45,14 @@ public class SPDFApplication { private final AppConfig appConfig; private final Environment env; - private final ApplicationProperties applicationProperties; private final WebBrowser webBrowser; public SPDFApplication( AppConfig appConfig, Environment env, - ApplicationProperties applicationProperties, @Autowired(required = false) WebBrowser webBrowser) { this.appConfig = appConfig; this.env = env; - this.applicationProperties = applicationProperties; this.webBrowser = webBrowser; } diff --git a/app/core/src/main/java/stirling/software/SPDF/config/EndpointInspector.java b/app/core/src/main/java/stirling/software/SPDF/config/EndpointInspector.java index d9ceb0f9d..457213412 100644 --- a/app/core/src/main/java/stirling/software/SPDF/config/EndpointInspector.java +++ b/app/core/src/main/java/stirling/software/SPDF/config/EndpointInspector.java @@ -6,8 +6,6 @@ import java.util.Map; import java.util.Set; import java.util.TreeSet; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationListener; import org.springframework.context.event.ContextRefreshedEvent; @@ -18,11 +16,12 @@ import org.springframework.web.servlet.mvc.method.RequestMappingInfo; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; @Component @RequiredArgsConstructor +@Slf4j public class EndpointInspector implements ApplicationListener { - private static final Logger logger = LoggerFactory.getLogger(EndpointInspector.class); private final ApplicationContext applicationContext; private final Set validGetEndpoints = new HashSet<>(); @@ -71,13 +70,13 @@ public class EndpointInspector implements ApplicationListener sortedEndpoints = new TreeSet<>(validGetEndpoints); - logger.info("=== BEGIN: All discovered GET endpoints ==="); + log.info("=== BEGIN: All discovered GET endpoints ==="); for (String endpoint : sortedEndpoints) { - logger.info("Endpoint: {}", endpoint); + log.info("Endpoint: {}", endpoint); } - logger.info("=== END: All discovered GET endpoints ==="); + log.info("=== END: All discovered GET endpoints ==="); } } diff --git a/app/core/src/main/java/stirling/software/SPDF/model/SignatureFile.java b/app/core/src/main/java/stirling/software/SPDF/model/SignatureFile.java index 7d82ebf0f..89ea49644 100644 --- a/app/core/src/main/java/stirling/software/SPDF/model/SignatureFile.java +++ b/app/core/src/main/java/stirling/software/SPDF/model/SignatureFile.java @@ -2,8 +2,10 @@ package stirling.software.SPDF.model; import lombok.AllArgsConstructor; import lombok.Data; +import lombok.NoArgsConstructor; @Data +@NoArgsConstructor @AllArgsConstructor public class SignatureFile { private String fileName; diff --git a/app/core/src/main/java/stirling/software/SPDF/service/MetricsAggregatorService.java b/app/core/src/main/java/stirling/software/SPDF/service/MetricsAggregatorService.java index 181757a04..3b12c9d5e 100644 --- a/app/core/src/main/java/stirling/software/SPDF/service/MetricsAggregatorService.java +++ b/app/core/src/main/java/stirling/software/SPDF/service/MetricsAggregatorService.java @@ -4,8 +4,6 @@ import java.util.HashMap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; @@ -13,15 +11,15 @@ import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.search.Search; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import stirling.software.SPDF.config.EndpointInspector; import stirling.software.common.service.PostHogService; @Service @RequiredArgsConstructor +@Slf4j public class MetricsAggregatorService { - private static final Logger logger = LoggerFactory.getLogger(MetricsAggregatorService.class); - private final MeterRegistry meterRegistry; private final PostHogService postHogService; private final EndpointInspector endpointInspector; @@ -66,7 +64,7 @@ public class MetricsAggregatorService { if ("GET".equals(method) && validateGetEndpoints && !endpointInspector.isValidGetEndpoint(uri)) { - logger.debug("Skipping invalid GET endpoint: {}", uri); + log.debug("Skipping invalid GET endpoint: {}", uri); return; } @@ -77,7 +75,7 @@ public class MetricsAggregatorService { double lastCount = lastSentMetrics.getOrDefault(key, 0.0); double difference = currentCount - lastCount; if (difference > 0) { - logger.debug("{}, {}", key, difference); + log.debug("{}, {}", key, difference); metrics.put(key, difference); lastSentMetrics.put(key, currentCount); } diff --git a/app/core/src/test/java/stirling/software/SPDF/controller/api/EditTableOfContentsControllerTest.java b/app/core/src/test/java/stirling/software/SPDF/controller/api/EditTableOfContentsControllerTest.java index fc8783f73..b829531c9 100644 --- a/app/core/src/test/java/stirling/software/SPDF/controller/api/EditTableOfContentsControllerTest.java +++ b/app/core/src/test/java/stirling/software/SPDF/controller/api/EditTableOfContentsControllerTest.java @@ -20,6 +20,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.ArgumentCaptor; +import org.mockito.ArgumentMatchers; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; @@ -202,7 +203,9 @@ class EditTableOfContentsControllerTest { bookmarks.add(bookmark); when(pdfDocumentFactory.load(mockFile)).thenReturn(mockDocument); - when(objectMapper.readValue(eq(request.getBookmarkData()), any(TypeReference.class))) + when(objectMapper.readValue( + eq(request.getBookmarkData()), + ArgumentMatchers.>>any())) .thenReturn(bookmarks); when(mockDocument.getDocumentCatalog()).thenReturn(mockCatalog); when(mockDocument.getNumberOfPages()).thenReturn(5); @@ -242,7 +245,8 @@ class EditTableOfContentsControllerTest { request.setFileInput(mockFile); String bookmarkJson = - "[{\"title\":\"Chapter 1\",\"pageNumber\":1,\"children\":[{\"title\":\"Section 1.1\",\"pageNumber\":2,\"children\":[]}]}]"; + "[{\"title\":\"Chapter 1\",\"pageNumber\":1,\"children\":[{\"title\":\"Section" + + " 1.1\",\"pageNumber\":2,\"children\":[]}]}]"; request.setBookmarkData(bookmarkJson); List bookmarks = new ArrayList<>(); @@ -261,7 +265,9 @@ class EditTableOfContentsControllerTest { bookmarks.add(parentBookmark); when(pdfDocumentFactory.load(mockFile)).thenReturn(mockDocument); - when(objectMapper.readValue(eq(bookmarkJson), any(TypeReference.class))) + when(objectMapper.readValue( + eq(bookmarkJson), + ArgumentMatchers.>>any())) .thenReturn(bookmarks); when(mockDocument.getDocumentCatalog()).thenReturn(mockCatalog); when(mockDocument.getNumberOfPages()).thenReturn(5); @@ -292,7 +298,8 @@ class EditTableOfContentsControllerTest { EditTableOfContentsRequest request = new EditTableOfContentsRequest(); request.setFileInput(mockFile); request.setBookmarkData( - "[{\"title\":\"Chapter 1\",\"pageNumber\":-5,\"children\":[]},{\"title\":\"Chapter 2\",\"pageNumber\":100,\"children\":[]}]"); + "[{\"title\":\"Chapter 1\",\"pageNumber\":-5,\"children\":[]},{\"title\":\"Chapter" + + " 2\",\"pageNumber\":100,\"children\":[]}]"); List bookmarks = new ArrayList<>(); @@ -310,7 +317,9 @@ class EditTableOfContentsControllerTest { bookmarks.add(bookmark2); when(pdfDocumentFactory.load(mockFile)).thenReturn(mockDocument); - when(objectMapper.readValue(eq(request.getBookmarkData()), any(TypeReference.class))) + when(objectMapper.readValue( + eq(request.getBookmarkData()), + ArgumentMatchers.>>any())) .thenReturn(bookmarks); when(mockDocument.getDocumentCatalog()).thenReturn(mockCatalog); when(mockDocument.getNumberOfPages()).thenReturn(5); diff --git a/app/proprietary/src/main/java/stirling/software/proprietary/security/controller/api/AdminSettingsController.java b/app/proprietary/src/main/java/stirling/software/proprietary/security/controller/api/AdminSettingsController.java index ebe856b00..650ccbb1f 100644 --- a/app/proprietary/src/main/java/stirling/software/proprietary/security/controller/api/AdminSettingsController.java +++ b/app/proprietary/src/main/java/stirling/software/proprietary/security/controller/api/AdminSettingsController.java @@ -21,6 +21,7 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.util.HtmlUtils; +import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import io.swagger.v3.oas.annotations.Operation; @@ -81,7 +82,8 @@ public class AdminSettingsController { @Operation( summary = "Get all application settings", description = - "Retrieve all current application settings. Use includePending=true to include settings that will take effect after restart. Admin access required.") + "Retrieve all current application settings. Use includePending=true to include" + + " settings that will take effect after restart. Admin access required.") @ApiResponses( value = { @ApiResponse(responseCode = "200", description = "Settings retrieved successfully"), @@ -95,7 +97,9 @@ public class AdminSettingsController { log.debug("Admin requested all application settings (includePending={})", includePending); // Convert ApplicationProperties to Map - Map settings = objectMapper.convertValue(applicationProperties, Map.class); + Map settings = + objectMapper.convertValue( + applicationProperties, new TypeReference>() {}); if (includePending && !pendingChanges.isEmpty()) { // Merge pending changes into the settings map @@ -112,7 +116,8 @@ public class AdminSettingsController { @Operation( summary = "Get pending settings changes", description = - "Retrieve settings that have been modified but not yet applied (require restart). Admin access required.") + "Retrieve settings that have been modified but not yet applied (require" + + " restart). Admin access required.") @ApiResponses( value = { @ApiResponse( @@ -137,7 +142,8 @@ public class AdminSettingsController { @Operation( summary = "Update application settings (delta updates)", description = - "Update specific application settings using dot notation keys. Only sends changed values. Changes take effect on restart. Admin access required.") + "Update specific application settings using dot notation keys. Only sends" + + " changed values. Changes take effect on restart. Admin access required.") @ApiResponses( value = { @ApiResponse(responseCode = "200", description = "Settings updated successfully"), @@ -178,7 +184,8 @@ public class AdminSettingsController { return ResponseEntity.ok( String.format( - "Successfully updated %d setting(s). Changes will take effect on application restart.", + "Successfully updated %d setting(s). Changes will take effect on" + + " application restart.", updatedCount)); } catch (IOException e) { @@ -199,7 +206,8 @@ public class AdminSettingsController { @Operation( summary = "Get specific settings section", description = - "Retrieve settings for a specific section (e.g., security, system, ui). Admin access required.") + "Retrieve settings for a specific section (e.g., security, system, ui). Admin" + + " access required.") @ApiResponses( value = { @ApiResponse( @@ -288,7 +296,8 @@ public class AdminSettingsController { String escapedSectionName = HtmlUtils.htmlEscape(sectionName); return ResponseEntity.ok( String.format( - "Successfully updated %d setting(s) in section '%s'. Changes will take effect on application restart.", + "Successfully updated %d setting(s) in section '%s'. Changes will take" + + " effect on application restart.", updatedCount, escapedSectionName)); } catch (IOException e) { @@ -308,7 +317,8 @@ public class AdminSettingsController { @Operation( summary = "Get specific setting value", description = - "Retrieve value for a specific setting key using dot notation. Admin access required.") + "Retrieve value for a specific setting key using dot notation. Admin access" + + " required.") @ApiResponses( value = { @ApiResponse( @@ -348,7 +358,8 @@ public class AdminSettingsController { @Operation( summary = "Update specific setting value", description = - "Update value for a specific setting key using dot notation. Admin access required.") + "Update value for a specific setting key using dot notation. Admin access" + + " required.") @ApiResponses( value = { @ApiResponse(responseCode = "200", description = "Setting updated successfully"), @@ -376,7 +387,8 @@ public class AdminSettingsController { String escapedKey = HtmlUtils.htmlEscape(key); return ResponseEntity.ok( String.format( - "Successfully updated setting '%s'. Changes will take effect on application restart.", + "Successfully updated setting '%s'. Changes will take effect on" + + " application restart.", escapedKey)); } catch (IOException e) { @@ -532,7 +544,6 @@ public class AdminSettingsController { * Recursively mask sensitive fields in settings map. Sensitive fields are replaced with a * status indicator showing if they're configured. */ - @SuppressWarnings("unchecked") private Map maskSensitiveFields(Map settings) { return maskSensitiveFieldsWithPath(settings, ""); } diff --git a/app/proprietary/src/main/java/stirling/software/proprietary/security/filter/UserAuthenticationFilter.java b/app/proprietary/src/main/java/stirling/software/proprietary/security/filter/UserAuthenticationFilter.java index f51a9d543..bec6f1d04 100644 --- a/app/proprietary/src/main/java/stirling/software/proprietary/security/filter/UserAuthenticationFilter.java +++ b/app/proprietary/src/main/java/stirling/software/proprietary/security/filter/UserAuthenticationFilter.java @@ -128,7 +128,7 @@ public class UserAuthenticationFilter extends OncePerRequestFilter { // Check if the authenticated user is disabled and invalidate their session if so if (authentication != null && authentication.isAuthenticated()) { - LoginMethod loginMethod = LoginMethod.UNKNOWN; + UserLoginType loginMethod = UserLoginType.UNKNOWN; boolean blockRegistration = false; @@ -137,20 +137,20 @@ public class UserAuthenticationFilter extends OncePerRequestFilter { String username = null; if (principal instanceof UserDetails detailsUser) { username = detailsUser.getUsername(); - loginMethod = LoginMethod.USERDETAILS; + loginMethod = UserLoginType.USERDETAILS; } else if (principal instanceof OAuth2User oAuth2User) { username = oAuth2User.getName(); - loginMethod = LoginMethod.OAUTH2USER; + loginMethod = UserLoginType.OAUTH2USER; OAUTH2 oAuth = securityProp.getOauth2(); blockRegistration = oAuth != null && oAuth.getBlockRegistration(); } else if (principal instanceof CustomSaml2AuthenticatedPrincipal saml2User) { username = saml2User.name(); - loginMethod = LoginMethod.SAML2USER; + loginMethod = UserLoginType.SAML2USER; SAML2 saml2 = securityProp.getSaml2(); blockRegistration = saml2 != null && saml2.getBlockRegistration(); } else if (principal instanceof String stringUser) { username = stringUser; - loginMethod = LoginMethod.STRINGUSER; + loginMethod = UserLoginType.STRINGUSER; } // Retrieve all active sessions for the user @@ -164,8 +164,8 @@ public class UserAuthenticationFilter extends OncePerRequestFilter { boolean isUserDisabled = userService.isUserDisabled(username); boolean notSsoLogin = - !LoginMethod.OAUTH2USER.equals(loginMethod) - && !LoginMethod.SAML2USER.equals(loginMethod); + !UserLoginType.OAUTH2USER.equals(loginMethod) + && !UserLoginType.SAML2USER.equals(loginMethod); // Block user registration if not allowed by configuration if (blockRegistration && !isUserExists) { @@ -200,7 +200,7 @@ public class UserAuthenticationFilter extends OncePerRequestFilter { filterChain.doFilter(request, response); } - private enum LoginMethod { + private enum UserLoginType { USERDETAILS("UserDetails"), OAUTH2USER("OAuth2User"), STRINGUSER("StringUser"), @@ -209,7 +209,7 @@ public class UserAuthenticationFilter extends OncePerRequestFilter { private String method; - LoginMethod(String method) { + UserLoginType(String method) { this.method = method; } diff --git a/app/proprietary/src/main/java/stirling/software/proprietary/security/service/JwtService.java b/app/proprietary/src/main/java/stirling/software/proprietary/security/service/JwtService.java index 8724da9a8..af32a183a 100644 --- a/app/proprietary/src/main/java/stirling/software/proprietary/security/service/JwtService.java +++ b/app/proprietary/src/main/java/stirling/software/proprietary/security/service/JwtService.java @@ -12,7 +12,6 @@ import java.util.Map; import java.util.Optional; import java.util.function.Function; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.ResponseCookie; @@ -53,7 +52,6 @@ public class JwtService implements JwtServiceInterface { private final KeyPersistenceServiceInterface keyPersistenceService; private final boolean v2Enabled; - @Autowired public JwtService( @Qualifier("v2Enabled") boolean v2Enabled, KeyPersistenceServiceInterface keyPersistenceService) { @@ -155,7 +153,8 @@ public class JwtService implements JwtServiceInterface { keyPair = specificKeyPair.get(); } else { log.warn( - "Key ID {} not found in keystore, token may have been signed with an expired key", + "Key ID {} not found in keystore, token may have been signed with an" + + " expired key", keyId); if (keyId.equals(keyPersistenceService.getActiveKey().getKeyId())) { diff --git a/app/proprietary/src/main/java/stirling/software/proprietary/security/service/KeyPairCleanupService.java b/app/proprietary/src/main/java/stirling/software/proprietary/security/service/KeyPairCleanupService.java index b419f78fe..af7c5f7e2 100644 --- a/app/proprietary/src/main/java/stirling/software/proprietary/security/service/KeyPairCleanupService.java +++ b/app/proprietary/src/main/java/stirling/software/proprietary/security/service/KeyPairCleanupService.java @@ -8,7 +8,6 @@ import java.time.LocalDateTime; import java.util.List; import java.util.concurrent.TimeUnit; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; @@ -30,7 +29,6 @@ public class KeyPairCleanupService { private final KeyPersistenceService keyPersistenceService; private final ApplicationProperties.Security.Jwt jwtProperties; - @Autowired public KeyPairCleanupService( KeyPersistenceService keyPersistenceService, ApplicationProperties applicationProperties) { diff --git a/app/proprietary/src/main/java/stirling/software/proprietary/security/service/KeyPersistenceService.java b/app/proprietary/src/main/java/stirling/software/proprietary/security/service/KeyPersistenceService.java index 48bcddac0..cc07fbbc7 100644 --- a/app/proprietary/src/main/java/stirling/software/proprietary/security/service/KeyPersistenceService.java +++ b/app/proprietary/src/main/java/stirling/software/proprietary/security/service/KeyPersistenceService.java @@ -20,7 +20,6 @@ import java.util.List; import java.util.Optional; import java.util.stream.Collectors; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.Cache; import org.springframework.cache.CacheManager; import org.springframework.cache.annotation.CacheEvict; @@ -43,16 +42,13 @@ public class KeyPersistenceService implements KeyPersistenceServiceInterface { public static final String KEY_SUFFIX = ".key"; private final ApplicationProperties.Security.Jwt jwtProperties; - private final CacheManager cacheManager; private final Cache verifyingKeyCache; private volatile JwtVerificationKey activeKey; - @Autowired public KeyPersistenceService( ApplicationProperties applicationProperties, CacheManager cacheManager) { this.jwtProperties = applicationProperties.getSecurity().getJwt(); - this.cacheManager = cacheManager; this.verifyingKeyCache = cacheManager.getCache("verifyingKeys"); } @@ -159,7 +155,7 @@ public class KeyPersistenceService implements KeyPersistenceServiceInterface { nativeCache.asMap().size()); return nativeCache.asMap().values().stream() - .filter(value -> value instanceof JwtVerificationKey) + .filter(JwtVerificationKey.class::isInstance) .map(value -> (JwtVerificationKey) value) .filter( key -> { @@ -233,6 +229,7 @@ public class KeyPersistenceService implements KeyPersistenceServiceInterface { return Base64.getEncoder().encodeToString(publicKey.getEncoded()); } + @Override public PublicKey decodePublicKey(String encodedKey) throws NoSuchAlgorithmException, InvalidKeySpecException { byte[] keyBytes = Base64.getDecoder().decode(encodedKey); diff --git a/app/proprietary/src/test/java/stirling/software/proprietary/security/service/UserServiceTest.java b/app/proprietary/src/test/java/stirling/software/proprietary/security/service/UserServiceTest.java index be087314a..c536cccdb 100644 --- a/app/proprietary/src/test/java/stirling/software/proprietary/security/service/UserServiceTest.java +++ b/app/proprietary/src/test/java/stirling/software/proprietary/security/service/UserServiceTest.java @@ -189,7 +189,7 @@ class UserServiceTest { void testSaveUser_WithValidEmail_Success() throws Exception { // Given String emailUsername = "test@example.com"; - AuthenticationType authType = AuthenticationType.SSO; + AuthenticationType authType = AuthenticationType.OAUTH2; when(teamRepository.findByName("Default")).thenReturn(Optional.of(mockTeam)); when(userRepository.save(any(User.class))).thenReturn(mockUser); From c141a15215e4cb767ffe42ccf9dc27225acd2847 Mon Sep 17 00:00:00 2001 From: Ludy Date: Wed, 20 Aug 2025 16:38:21 +0200 Subject: [PATCH 6/7] refactor(build): centralize security disable condition in shared Gradle property (#4209) # Description of Changes - Introduced `ext.isSecurityDisabled` closure in root `build.gradle` to consolidate logic for determining if security features should be disabled. - Removed duplicated conditional checks from `sourceSets` configurations in both root and `app/core` `build.gradle` files. - Updated dependency inclusion for `:proprietary` module to use the new `isSecurityDisabled()` method for clarity and maintainability. - Simplified build logic by reducing repeated environment and property checks. This change improves maintainability by ensuring that the security disable condition is defined in one place, reducing the risk of inconsistencies across modules. --- ## 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) - [ ] 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 - [ ] 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) - [ ] 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. --- app/core/build.gradle | 5 ----- build.gradle | 43 ++++++++----------------------------------- 2 files changed, 8 insertions(+), 40 deletions(-) diff --git a/app/core/build.gradle b/app/core/build.gradle index c9905a308..409e1711d 100644 --- a/app/core/build.gradle +++ b/app/core/build.gradle @@ -114,11 +114,6 @@ sourceSets { } test { java { - if (System.getenv('DOCKER_ENABLE_SECURITY') == 'false' || System.getenv('DISABLE_ADDITIONAL_FEATURES') == 'true' - || (project.hasProperty('DISABLE_ADDITIONAL_FEATURES') - && System.getProperty('DISABLE_ADDITIONAL_FEATURES') == 'true')) { - exclude 'stirling/software/proprietary/security/**' - } if (System.getenv('STIRLING_PDF_DESKTOP_UI') == 'false') { exclude 'stirling/software/SPDF/UI/impl/**' } diff --git a/build.gradle b/build.gradle index 1cd58b00e..4059d48fe 100644 --- a/build.gradle +++ b/build.gradle @@ -34,6 +34,13 @@ ext { tempJrePath = null } +ext.isSecurityDisabled = { -> + System.getenv('DOCKER_ENABLE_SECURITY') == 'false' || + System.getenv('DISABLE_ADDITIONAL_FEATURES') == 'true' || + (project.hasProperty('DISABLE_ADDITIONAL_FEATURES') && + System.getProperty('DISABLE_ADDITIONAL_FEATURES') == 'true') +} + jar { enabled = false manifest { @@ -222,37 +229,6 @@ licenseReport { outputDir = project.layout.buildDirectory.dir("reports/dependency-license").get().asFile.path } -sourceSets { - main { - java { - if (System.getenv('DOCKER_ENABLE_SECURITY') == 'false' || System.getenv('DISABLE_ADDITIONAL_FEATURES') == 'true' - || (project.hasProperty('DISABLE_ADDITIONAL_FEATURES') - && System.getProperty('DISABLE_ADDITIONAL_FEATURES') == 'true')) { - exclude 'stirling/software/proprietary/security/**' - } - - if (System.getenv('STIRLING_PDF_DESKTOP_UI') == 'false') { - exclude 'stirling/software/SPDF/UI/impl/**' - } - - } - } - - test { - java { - if (System.getenv('DOCKER_ENABLE_SECURITY') == 'false' || System.getenv('DISABLE_ADDITIONAL_FEATURES') == 'true' - || (project.hasProperty('DISABLE_ADDITIONAL_FEATURES') - && System.getProperty('DISABLE_ADDITIONAL_FEATURES') == 'true')) { - exclude 'stirling/software/proprietary/security/**' - } - - if (System.getenv('STIRLING_PDF_DESKTOP_UI') == 'false') { - exclude 'stirling/software/SPDF/UI/impl/**' - } - } - } -} - // Configure the forked spring boot run task to properly delegate to the stirling-pdf module tasks.named('forkedSpringBootRun') { dependsOn ':stirling-pdf:bootRun' @@ -575,9 +551,7 @@ swaggerhubUpload { dependencies { implementation project(':stirling-pdf') implementation project(':common') - if (System.getenv('DISABLE_ADDITIONAL_FEATURES') != 'true' - || (project.hasProperty('DISABLE_ADDITIONAL_FEATURES') - && System.getProperty('DISABLE_ADDITIONAL_FEATURES') != 'true')) { + if (rootProject.ext.isSecurityDisabled()) { implementation project(':proprietary') } @@ -592,7 +566,6 @@ tasks.named("test") { useJUnitPlatform() } - // Make sure all relevant processes depend on writeVersion processResources.dependsOn(writeVersion) From 409cada93a40cfc606f3fab3aff2f91f394e96b5 Mon Sep 17 00:00:00 2001 From: Ludy Date: Thu, 21 Aug 2025 11:31:25 +0200 Subject: [PATCH 7/7] chore(ci): include `testing/**` in file change detection for `docker-compose-tests` workflow (#4206) # Description of Changes - Added `testing/**` to `.github/config/.files.yaml` so that changes in the `testing` directory will trigger the `docker-compose-tests` workflow in `build.yml`. - Updated Python dependencies in `.github/scripts/requirements_pre_commit.txt` and `testing/cucumber/requirements.txt` to newer versions, including `behave`, `pypdf`, `reportlab`, and others. - Introduced new dependencies like `colorama`, `cucumber-expressions`, `cucumber-tag-expressions`, and `tomli` in the testing requirements to support enhanced test execution. - Ensured hash integrity for all dependency updates. This change was made to ensure that modifications in the testing suite automatically trigger relevant CI jobs and that testing dependencies remain up-to-date for compatibility and stability. --- ## 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) - [ ] 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 - [ ] 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) - [ ] 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. --- .github/config/.files.yaml | 1 + .github/scripts/requirements_pre_commit.txt | 28 ++- testing/cucumber/requirements.txt | 249 +++++++++++--------- 3 files changed, 158 insertions(+), 120 deletions(-) diff --git a/.github/config/.files.yaml b/.github/config/.files.yaml index a5d8410f3..eb1097266 100644 --- a/.github/config/.files.yaml +++ b/.github/config/.files.yaml @@ -29,3 +29,4 @@ project: &project - settings.gradle - frontend/** - docker/** + - testing/** diff --git a/.github/scripts/requirements_pre_commit.txt b/.github/scripts/requirements_pre_commit.txt index 4e2d2c2b6..9a3e4c223 100644 --- a/.github/scripts/requirements_pre_commit.txt +++ b/.github/scripts/requirements_pre_commit.txt @@ -8,17 +8,17 @@ cfgv==3.4.0 \ --hash=sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9 \ --hash=sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560 # via pre-commit -distlib==0.3.9 \ - --hash=sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87 \ - --hash=sha256:a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403 +distlib==0.4.0 \ + --hash=sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16 \ + --hash=sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d # via virtualenv filelock==3.18.0 \ --hash=sha256:adbc88eabb99d2fec8c9c1b229b171f18afa655400173ddc653d5d01501fb9f2 \ --hash=sha256:c401f4f8377c4464e6db25fff06205fd89bdd83b65eb0488ed1b160f780e21de # via virtualenv -identify==2.6.12 \ - --hash=sha256:ad9672d5a72e0d2ff7c5c8809b62dfa60458626352fb0eb7b55e69bdc45334a2 \ - --hash=sha256:d8de45749f1efb108badef65ee8386f0f7bb19a7f26185f74de6367bffbaf0e6 +identify==2.6.13 \ + --hash=sha256:60381139b3ae39447482ecc406944190f690d4a2997f2584062089848361b33b \ + --hash=sha256:da8d6c828e773620e13bfa86ea601c5a5310ba4bcd65edf378198b56a1f9fb32 # via pre-commit nodeenv==1.9.1 \ --hash=sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f \ @@ -28,9 +28,9 @@ platformdirs==4.3.8 \ --hash=sha256:3d512d96e16bcb959a814c9f348431070822a6496326a4be0911c40b5a74c2bc \ --hash=sha256:ff7059bb7eb1179e2685604f4aaf157cfd9535242bd23742eadc3c13542139b4 # via virtualenv -pre-commit==4.2.0 \ - --hash=sha256:601283b9757afd87d40c4c4a9b2b5de9637a8ea02eaff7adc2d0fb4e04841146 \ - --hash=sha256:a009ca7205f1eb497d10b845e52c838a98b6cdd2102a6c8e4540e94ee75c58bd +pre-commit==4.3.0 \ + --hash=sha256:2b0747ad7e6e967169136edffee14c16e148a778a54e4f967921aa1ebf2308d8 \ + --hash=sha256:499fe450cc9d42e9d58e606262795ecb64dd05438943c62b66f6a8673da30b16 # via -r .github\scripts\requirements_pre_commit.in pyyaml==6.0.2 \ --hash=sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff \ @@ -87,7 +87,11 @@ pyyaml==6.0.2 \ --hash=sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12 \ --hash=sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4 # via pre-commit -virtualenv==20.31.2 \ - --hash=sha256:36efd0d9650ee985f0cad72065001e66d49a6f24eb44d98980f630686243cf11 \ - --hash=sha256:e10c0a9d02835e592521be48b332b6caee6887f332c111aa79a09b9e79efc2af +typing-extensions==4.14.1 \ + --hash=sha256:38b39f4aeeab64884ce9f74c94263ef78f3c22467c8724005483154c26648d36 \ + --hash=sha256:d1e1e3b58374dc93031d6eda2420a48ea44a36c2b4766a4fdeb3710755731d76 + # via virtualenv +virtualenv==20.34.0 \ + --hash=sha256:341f5afa7eee943e4984a9207c025feedd768baff6753cd660c857ceb3e36026 \ + --hash=sha256:44815b2c9dee7ed86e387b842a84f20b93f7f417f95886ca1996a72a4138eb1a # via pre-commit diff --git a/testing/cucumber/requirements.txt b/testing/cucumber/requirements.txt index 534b337a9..8f8cf8ad3 100644 --- a/testing/cucumber/requirements.txt +++ b/testing/cucumber/requirements.txt @@ -4,110 +4,109 @@ # # pip-compile --generate-hashes --output-file='testing\cucumber\requirements.txt' --strip-extras 'testing\cucumber\requirements.in' # -behave==1.2.6 \ - --hash=sha256:b9662327aa53294c1351b0a9c369093ccec1d21026f050c3bd9b3e5cccf81a86 \ - --hash=sha256:ebda1a6c9e5bfe95c5f9f0a2794e01c7098b3dde86c10a95d8621c5907ff6f1c +behave==1.3.1 \ + --hash=sha256:2a1f3a2490242132c4daf0732d9b65c99be6fef1f787f97fd028ea5a402025ff \ + --hash=sha256:71b2dc00664de83c3aad61c91e5b3051b7b860aa2053e24db4742edecb800d21 # via -r testing\cucumber\requirements.in -certifi==2025.6.15 \ - --hash=sha256:2e0c7ce7cb5d8f8634ca55d2ba7e6ec2689a2fd6537d8dec1296a477a4910057 \ - --hash=sha256:d747aa5a8b9bbbb1bb8c22bb13e22bd1f18e9796defa16bab421f7f7a317323b +certifi==2025.8.3 \ + --hash=sha256:e564105f78ded564e3ae7c923924435e1daa7463faeab5bb932bc53ffae63407 \ + --hash=sha256:f6c12493cfb1b06ba2ff328595af9350c65d6644968e5d3a2ffd78699af217a5 # via requests -charset-normalizer==3.4.2 \ - --hash=sha256:005fa3432484527f9732ebd315da8da8001593e2cf46a3d817669f062c3d9ed4 \ - --hash=sha256:046595208aae0120559a67693ecc65dd75d46f7bf687f159127046628178dc45 \ - --hash=sha256:0c29de6a1a95f24b9a1aa7aefd27d2487263f00dfd55a77719b530788f75cff7 \ - --hash=sha256:0c8c57f84ccfc871a48a47321cfa49ae1df56cd1d965a09abe84066f6853b9c0 \ - --hash=sha256:0f5d9ed7f254402c9e7d35d2f5972c9bbea9040e99cd2861bd77dc68263277c7 \ - --hash=sha256:18dd2e350387c87dabe711b86f83c9c78af772c748904d372ade190b5c7c9d4d \ - --hash=sha256:1b1bde144d98e446b056ef98e59c256e9294f6b74d7af6846bf5ffdafd687a7d \ - --hash=sha256:1c95a1e2902a8b722868587c0e1184ad5c55631de5afc0eb96bc4b0d738092c0 \ - --hash=sha256:1cad5f45b3146325bb38d6855642f6fd609c3f7cad4dbaf75549bf3b904d3184 \ - --hash=sha256:21b2899062867b0e1fde9b724f8aecb1af14f2778d69aacd1a5a1853a597a5db \ - --hash=sha256:24498ba8ed6c2e0b56d4acbf83f2d989720a93b41d712ebd4f4979660db4417b \ - --hash=sha256:25a23ea5c7edc53e0f29bae2c44fcb5a1aa10591aae107f2a2b2583a9c5cbc64 \ - --hash=sha256:289200a18fa698949d2b39c671c2cc7a24d44096784e76614899a7ccf2574b7b \ - --hash=sha256:28a1005facc94196e1fb3e82a3d442a9d9110b8434fc1ded7a24a2983c9888d8 \ - --hash=sha256:32fc0341d72e0f73f80acb0a2c94216bd704f4f0bce10aedea38f30502b271ff \ - --hash=sha256:36b31da18b8890a76ec181c3cf44326bf2c48e36d393ca1b72b3f484113ea344 \ - --hash=sha256:3c21d4fca343c805a52c0c78edc01e3477f6dd1ad7c47653241cf2a206d4fc58 \ - --hash=sha256:3fddb7e2c84ac87ac3a947cb4e66d143ca5863ef48e4a5ecb83bd48619e4634e \ - --hash=sha256:43e0933a0eff183ee85833f341ec567c0980dae57c464d8a508e1b2ceb336471 \ - --hash=sha256:4a476b06fbcf359ad25d34a057b7219281286ae2477cc5ff5e3f70a246971148 \ - --hash=sha256:4e594135de17ab3866138f496755f302b72157d115086d100c3f19370839dd3a \ - --hash=sha256:50bf98d5e563b83cc29471fa114366e6806bc06bc7a25fd59641e41445327836 \ - --hash=sha256:5a9979887252a82fefd3d3ed2a8e3b937a7a809f65dcb1e068b090e165bbe99e \ - --hash=sha256:5baececa9ecba31eff645232d59845c07aa030f0c81ee70184a90d35099a0e63 \ - --hash=sha256:5bf4545e3b962767e5c06fe1738f951f77d27967cb2caa64c28be7c4563e162c \ - --hash=sha256:6333b3aa5a12c26b2a4d4e7335a28f1475e0e5e17d69d55141ee3cab736f66d1 \ - --hash=sha256:65c981bdbd3f57670af8b59777cbfae75364b483fa8a9f420f08094531d54a01 \ - --hash=sha256:68a328e5f55ec37c57f19ebb1fdc56a248db2e3e9ad769919a58672958e8f366 \ - --hash=sha256:6a0289e4589e8bdfef02a80478f1dfcb14f0ab696b5a00e1f4b8a14a307a3c58 \ - --hash=sha256:6b66f92b17849b85cad91259efc341dce9c1af48e2173bf38a85c6329f1033e5 \ - --hash=sha256:6c9379d65defcab82d07b2a9dfbfc2e95bc8fe0ebb1b176a3190230a3ef0e07c \ - --hash=sha256:6fc1f5b51fa4cecaa18f2bd7a003f3dd039dd615cd69a2afd6d3b19aed6775f2 \ - --hash=sha256:70f7172939fdf8790425ba31915bfbe8335030f05b9913d7ae00a87d4395620a \ - --hash=sha256:721c76e84fe669be19c5791da68232ca2e05ba5185575086e384352e2c309597 \ - --hash=sha256:7222ffd5e4de8e57e03ce2cef95a4c43c98fcb72ad86909abdfc2c17d227fc1b \ - --hash=sha256:75d10d37a47afee94919c4fab4c22b9bc2a8bf7d4f46f87363bcf0573f3ff4f5 \ - --hash=sha256:76af085e67e56c8816c3ccf256ebd136def2ed9654525348cfa744b6802b69eb \ - --hash=sha256:770cab594ecf99ae64c236bc9ee3439c3f46be49796e265ce0cc8bc17b10294f \ - --hash=sha256:7a6ab32f7210554a96cd9e33abe3ddd86732beeafc7a28e9955cdf22ffadbab0 \ - --hash=sha256:7c48ed483eb946e6c04ccbe02c6b4d1d48e51944b6db70f697e089c193404941 \ - --hash=sha256:7f56930ab0abd1c45cd15be65cc741c28b1c9a34876ce8c17a2fa107810c0af0 \ - --hash=sha256:8075c35cd58273fee266c58c0c9b670947c19df5fb98e7b66710e04ad4e9ff86 \ - --hash=sha256:8272b73e1c5603666618805fe821edba66892e2870058c94c53147602eab29c7 \ - --hash=sha256:82d8fd25b7f4675d0c47cf95b594d4e7b158aca33b76aa63d07186e13c0e0ab7 \ - --hash=sha256:844da2b5728b5ce0e32d863af26f32b5ce61bc4273a9c720a9f3aa9df73b1455 \ - --hash=sha256:8755483f3c00d6c9a77f490c17e6ab0c8729e39e6390328e42521ef175380ae6 \ - --hash=sha256:915f3849a011c1f593ab99092f3cecfcb4d65d8feb4a64cf1bf2d22074dc0ec4 \ - --hash=sha256:926ca93accd5d36ccdabd803392ddc3e03e6d4cd1cf17deff3b989ab8e9dbcf0 \ - --hash=sha256:982bb1e8b4ffda883b3d0a521e23abcd6fd17418f6d2c4118d257a10199c0ce3 \ - --hash=sha256:98f862da73774290f251b9df8d11161b6cf25b599a66baf087c1ffe340e9bfd1 \ - --hash=sha256:9cbfacf36cb0ec2897ce0ebc5d08ca44213af24265bd56eca54bee7923c48fd6 \ - --hash=sha256:a370b3e078e418187da8c3674eddb9d983ec09445c99a3a263c2011993522981 \ - --hash=sha256:a955b438e62efdf7e0b7b52a64dc5c3396e2634baa62471768a64bc2adb73d5c \ - --hash=sha256:aa6af9e7d59f9c12b33ae4e9450619cf2488e2bbe9b44030905877f0b2324980 \ - --hash=sha256:aa88ca0b1932e93f2d961bf3addbb2db902198dca337d88c89e1559e066e7645 \ - --hash=sha256:aaeeb6a479c7667fbe1099af9617c83aaca22182d6cf8c53966491a0f1b7ffb7 \ - --hash=sha256:aaf27faa992bfee0264dc1f03f4c75e9fcdda66a519db6b957a3f826e285cf12 \ - --hash=sha256:b2680962a4848b3c4f155dc2ee64505a9c57186d0d56b43123b17ca3de18f0fa \ - --hash=sha256:b2d318c11350e10662026ad0eb71bb51c7812fc8590825304ae0bdd4ac283acd \ - --hash=sha256:b33de11b92e9f75a2b545d6e9b6f37e398d86c3e9e9653c4864eb7e89c5773ef \ - --hash=sha256:b3daeac64d5b371dea99714f08ffc2c208522ec6b06fbc7866a450dd446f5c0f \ - --hash=sha256:be1e352acbe3c78727a16a455126d9ff83ea2dfdcbc83148d2982305a04714c2 \ - --hash=sha256:bee093bf902e1d8fc0ac143c88902c3dfc8941f7ea1d6a8dd2bcb786d33db03d \ - --hash=sha256:c72fbbe68c6f32f251bdc08b8611c7b3060612236e960ef848e0a517ddbe76c5 \ - --hash=sha256:c9e36a97bee9b86ef9a1cf7bb96747eb7a15c2f22bdb5b516434b00f2a599f02 \ - --hash=sha256:cddf7bd982eaa998934a91f69d182aec997c6c468898efe6679af88283b498d3 \ - --hash=sha256:cf713fe9a71ef6fd5adf7a79670135081cd4431c2943864757f0fa3a65b1fafd \ - --hash=sha256:d11b54acf878eef558599658b0ffca78138c8c3655cf4f3a4a673c437e67732e \ - --hash=sha256:d41c4d287cfc69060fa91cae9683eacffad989f1a10811995fa309df656ec214 \ - --hash=sha256:d524ba3f1581b35c03cb42beebab4a13e6cdad7b36246bd22541fa585a56cccd \ - --hash=sha256:daac4765328a919a805fa5e2720f3e94767abd632ae410a9062dff5412bae65a \ - --hash=sha256:db4c7bf0e07fc3b7d89ac2a5880a6a8062056801b83ff56d8464b70f65482b6c \ - --hash=sha256:dc7039885fa1baf9be153a0626e337aa7ec8bf96b0128605fb0d77788ddc1681 \ - --hash=sha256:dccab8d5fa1ef9bfba0590ecf4d46df048d18ffe3eec01eeb73a42e0d9e7a8ba \ - --hash=sha256:dedb8adb91d11846ee08bec4c8236c8549ac721c245678282dcb06b221aab59f \ - --hash=sha256:e45ba65510e2647721e35323d6ef54c7974959f6081b58d4ef5d87c60c84919a \ - --hash=sha256:e53efc7c7cee4c1e70661e2e112ca46a575f90ed9ae3fef200f2a25e954f4b28 \ - --hash=sha256:e635b87f01ebc977342e2697d05b56632f5f879a4f15955dfe8cef2448b51691 \ - --hash=sha256:e70e990b2137b29dc5564715de1e12701815dacc1d056308e2b17e9095372a82 \ - --hash=sha256:e8082b26888e2f8b36a042a58307d5b917ef2b1cacab921ad3323ef91901c71a \ - --hash=sha256:e8323a9b031aa0393768b87f04b4164a40037fb2a3c11ac06a03ffecd3618027 \ - --hash=sha256:e92fca20c46e9f5e1bb485887d074918b13543b1c2a1185e69bb8d17ab6236a7 \ - --hash=sha256:eb30abc20df9ab0814b5a2524f23d75dcf83cde762c161917a2b4b7b55b1e518 \ - --hash=sha256:eba9904b0f38a143592d9fc0e19e2df0fa2e41c3c3745554761c5f6447eedabf \ - --hash=sha256:ef8de666d6179b009dce7bcb2ad4c4a779f113f12caf8dc77f0162c29d20490b \ - --hash=sha256:efd387a49825780ff861998cd959767800d54f8308936b21025326de4b5a42b9 \ - --hash=sha256:f0aa37f3c979cf2546b73e8222bbfa3dc07a641585340179d768068e3455e544 \ - --hash=sha256:f4074c5a429281bf056ddd4c5d3b740ebca4d43ffffe2ef4bf4d2d05114299da \ - --hash=sha256:f69a27e45c43520f5487f27627059b64aaf160415589230992cec34c5e18a509 \ - --hash=sha256:fb707f3e15060adf5b7ada797624a6c6e0138e2a26baa089df64c68ee98e040f \ - --hash=sha256:fcbe676a55d7445b22c10967bceaaf0ee69407fbe0ece4d032b6eb8d4565982a \ - --hash=sha256:fdb20a30fe1175ecabed17cbf7812f7b804b8a315a25f24678bcdf120a90077f +charset-normalizer==3.4.3 \ + --hash=sha256:00237675befef519d9af72169d8604a067d92755e84fe76492fef5441db05b91 \ + --hash=sha256:02425242e96bcf29a49711b0ca9f37e451da7c70562bc10e8ed992a5a7a25cc0 \ + --hash=sha256:027b776c26d38b7f15b26a5da1044f376455fb3766df8fc38563b4efbc515154 \ + --hash=sha256:07a0eae9e2787b586e129fdcbe1af6997f8d0e5abaa0bc98c0e20e124d67e601 \ + --hash=sha256:0cacf8f7297b0c4fcb74227692ca46b4a5852f8f4f24b3c766dd94a1075c4884 \ + --hash=sha256:0e78314bdc32fa80696f72fa16dc61168fda4d6a0c014e0380f9d02f0e5d8a07 \ + --hash=sha256:0f2be7e0cf7754b9a30eb01f4295cc3d4358a479843b31f328afd210e2c7598c \ + --hash=sha256:13faeacfe61784e2559e690fc53fa4c5ae97c6fcedb8eb6fb8d0a15b475d2c64 \ + --hash=sha256:14c2a87c65b351109f6abfc424cab3927b3bdece6f706e4d12faaf3d52ee5efe \ + --hash=sha256:1606f4a55c0fd363d754049cdf400175ee96c992b1f8018b993941f221221c5f \ + --hash=sha256:16a8770207946ac75703458e2c743631c79c59c5890c80011d536248f8eaa432 \ + --hash=sha256:18343b2d246dc6761a249ba1fb13f9ee9a2bcd95decc767319506056ea4ad4dc \ + --hash=sha256:18b97b8404387b96cdbd30ad660f6407799126d26a39ca65729162fd810a99aa \ + --hash=sha256:1bb60174149316da1c35fa5233681f7c0f9f514509b8e399ab70fea5f17e45c9 \ + --hash=sha256:1e8ac75d72fa3775e0b7cb7e4629cec13b7514d928d15ef8ea06bca03ef01cae \ + --hash=sha256:1ef99f0456d3d46a50945c98de1774da86f8e992ab5c77865ea8b8195341fc19 \ + --hash=sha256:2001a39612b241dae17b4687898843f254f8748b796a2e16f1051a17078d991d \ + --hash=sha256:23b6b24d74478dc833444cbd927c338349d6ae852ba53a0d02a2de1fce45b96e \ + --hash=sha256:252098c8c7a873e17dd696ed98bbe91dbacd571da4b87df3736768efa7a792e4 \ + --hash=sha256:257f26fed7d7ff59921b78244f3cd93ed2af1800ff048c33f624c87475819dd7 \ + --hash=sha256:2c322db9c8c89009a990ef07c3bcc9f011a3269bc06782f916cd3d9eed7c9312 \ + --hash=sha256:30a96e1e1f865f78b030d65241c1ee850cdf422d869e9028e2fc1d5e4db73b92 \ + --hash=sha256:30d006f98569de3459c2fc1f2acde170b7b2bd265dc1943e87e1a4efe1b67c31 \ + --hash=sha256:31a9a6f775f9bcd865d88ee350f0ffb0e25936a7f930ca98995c05abf1faf21c \ + --hash=sha256:320e8e66157cc4e247d9ddca8e21f427efc7a04bbd0ac8a9faf56583fa543f9f \ + --hash=sha256:34a7f768e3f985abdb42841e20e17b330ad3aaf4bb7e7aeeb73db2e70f077b99 \ + --hash=sha256:3653fad4fe3ed447a596ae8638b437f827234f01a8cd801842e43f3d0a6b281b \ + --hash=sha256:3cd35b7e8aedeb9e34c41385fda4f73ba609e561faedfae0a9e75e44ac558a15 \ + --hash=sha256:3cfb2aad70f2c6debfbcb717f23b7eb55febc0bb23dcffc0f076009da10c6392 \ + --hash=sha256:416175faf02e4b0810f1f38bcb54682878a4af94059a1cd63b8747244420801f \ + --hash=sha256:41d1fc408ff5fdfb910200ec0e74abc40387bccb3252f3f27c0676731df2b2c8 \ + --hash=sha256:42e5088973e56e31e4fa58eb6bd709e42fc03799c11c42929592889a2e54c491 \ + --hash=sha256:4ca4c094de7771a98d7fbd67d9e5dbf1eb73efa4f744a730437d8a3a5cf994f0 \ + --hash=sha256:511729f456829ef86ac41ca78c63a5cb55240ed23b4b737faca0eb1abb1c41bc \ + --hash=sha256:53cd68b185d98dde4ad8990e56a58dea83a4162161b1ea9272e5c9182ce415e0 \ + --hash=sha256:585f3b2a80fbd26b048a0be90c5aae8f06605d3c92615911c3a2b03a8a3b796f \ + --hash=sha256:5b413b0b1bfd94dbf4023ad6945889f374cd24e3f62de58d6bb102c4d9ae534a \ + --hash=sha256:5d8d01eac18c423815ed4f4a2ec3b439d654e55ee4ad610e153cf02faf67ea40 \ + --hash=sha256:6aab0f181c486f973bc7262a97f5aca3ee7e1437011ef0c2ec04b5a11d16c927 \ + --hash=sha256:6cf8fd4c04756b6b60146d98cd8a77d0cdae0e1ca20329da2ac85eed779b6849 \ + --hash=sha256:6fb70de56f1859a3f71261cbe41005f56a7842cc348d3aeb26237560bfa5e0ce \ + --hash=sha256:6fce4b8500244f6fcb71465d4a4930d132ba9ab8e71a7859e6a5d59851068d14 \ + --hash=sha256:70bfc5f2c318afece2f5838ea5e4c3febada0be750fcf4775641052bbba14d05 \ + --hash=sha256:73dc19b562516fc9bcf6e5d6e596df0b4eb98d87e4f79f3ae71840e6ed21361c \ + --hash=sha256:74d77e25adda8581ffc1c720f1c81ca082921329452eba58b16233ab1842141c \ + --hash=sha256:78deba4d8f9590fe4dae384aeff04082510a709957e968753ff3c48399f6f92a \ + --hash=sha256:86df271bf921c2ee3818f0522e9a5b8092ca2ad8b065ece5d7d9d0e9f4849bcc \ + --hash=sha256:88ab34806dea0671532d3f82d82b85e8fc23d7b2dd12fa837978dad9bb392a34 \ + --hash=sha256:8999f965f922ae054125286faf9f11bc6932184b93011d138925a1773830bbe9 \ + --hash=sha256:8dcfc373f888e4fb39a7bc57e93e3b845e7f462dacc008d9749568b1c4ece096 \ + --hash=sha256:939578d9d8fd4299220161fdd76e86c6a251987476f5243e8864a7844476ba14 \ + --hash=sha256:96b2b3d1a83ad55310de8c7b4a2d04d9277d5591f40761274856635acc5fcb30 \ + --hash=sha256:a2d08ac246bb48479170408d6c19f6385fa743e7157d716e144cad849b2dd94b \ + --hash=sha256:b256ee2e749283ef3ddcff51a675ff43798d92d746d1a6e4631bf8c707d22d0b \ + --hash=sha256:b5e3b2d152e74e100a9e9573837aba24aab611d39428ded46f4e4022ea7d1942 \ + --hash=sha256:b89bc04de1d83006373429975f8ef9e7932534b8cc9ca582e4db7d20d91816db \ + --hash=sha256:bd28b817ea8c70215401f657edef3a8aa83c29d447fb0b622c35403780ba11d5 \ + --hash=sha256:c60e092517a73c632ec38e290eba714e9627abe9d301c8c8a12ec32c314a2a4b \ + --hash=sha256:c6dbd0ccdda3a2ba7c2ecd9d77b37f3b5831687d8dc1b6ca5f56a4880cc7b7ce \ + --hash=sha256:c6e490913a46fa054e03699c70019ab869e990270597018cef1d8562132c2669 \ + --hash=sha256:c6f162aabe9a91a309510d74eeb6507fab5fff92337a15acbe77753d88d9dcf0 \ + --hash=sha256:c6fd51128a41297f5409deab284fecbe5305ebd7e5a1f959bee1c054622b7018 \ + --hash=sha256:cc34f233c9e71701040d772aa7490318673aa7164a0efe3172b2981218c26d93 \ + --hash=sha256:cc9370a2da1ac13f0153780040f465839e6cccb4a1e44810124b4e22483c93fe \ + --hash=sha256:ccf600859c183d70eb47e05a44cd80a4ce77394d1ac0f79dbd2dd90a69a3a049 \ + --hash=sha256:ce571ab16d890d23b5c278547ba694193a45011ff86a9162a71307ed9f86759a \ + --hash=sha256:cf1ebb7d78e1ad8ec2a8c4732c7be2e736f6e5123a4146c5b89c9d1f585f8cef \ + --hash=sha256:d0e909868420b7049dafd3a31d45125b31143eec59235311fc4c57ea26a4acd2 \ + --hash=sha256:d22dbedd33326a4a5190dd4fe9e9e693ef12160c77382d9e87919bce54f3d4ca \ + --hash=sha256:d716a916938e03231e86e43782ca7878fb602a125a91e7acb8b5112e2e96ac16 \ + --hash=sha256:d79c198e27580c8e958906f803e63cddb77653731be08851c7df0b1a14a8fc0f \ + --hash=sha256:d95bfb53c211b57198bb91c46dd5a2d8018b3af446583aab40074bf7988401cb \ + --hash=sha256:e28e334d3ff134e88989d90ba04b47d84382a828c061d0d1027b1b12a62b39b1 \ + --hash=sha256:ec557499516fc90fd374bf2e32349a2887a876fbf162c160e3c01b6849eaf557 \ + --hash=sha256:fb6fecfd65564f208cbf0fba07f107fb661bcd1a7c389edbced3f7a493f70e37 \ + --hash=sha256:fb731e5deb0c7ef82d698b0f4c5bb724633ee2a489401594c5c88b02e6cb15f7 \ + --hash=sha256:fb7f67a1bfa6e40b438170ebdc8158b78dc465a5a67b6dde178a46987b244a72 \ + --hash=sha256:fd10de089bcdcd1be95a2f73dbe6254798ec1bda9f450d5828c96f93e2536b9c \ + --hash=sha256:fdabf8315679312cfa71302f9bd509ded4f2f263fb5b765cf1433b39106c3cc9 # via # reportlab # requests +colorama==0.4.6 \ + --hash=sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44 \ + --hash=sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6 + # via behave +cucumber-expressions==18.0.1 \ + --hash=sha256:86230d503cdda7ef35a1f2072a882d7d57c740aa4c163c82b07f039b6bc60c42 \ + --hash=sha256:86ce41bf28ee520408416f38022e5a083d815edf04a0bd1dae46d474ca597c60 + # via behave +cucumber-tag-expressions==6.2.0 \ + --hash=sha256:b60aa2cdbf9ac43e28d9b0e4fd49edf9f09d5d941257d2912f5228f9d166c023 \ + --hash=sha256:f94404b656831c56a3815da5305ac097003884d2ae64fa51f5f4fad82d97e583 + # via behave idna==3.10 \ --hash=sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9 \ --hash=sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3 @@ -118,9 +117,9 @@ parse==1.20.2 \ # via # behave # parse-type -parse-type==0.6.4 \ - --hash=sha256:5e1ec10440b000c3f818006033372939e693a9ec0176f446d9303e4db88489a6 \ - --hash=sha256:83d41144a82d6b8541127bf212dd76c7f01baff680b498ce8a4d052a7a5bce4c +parse-type==0.6.6 \ + --hash=sha256:3ca79bbe71e170dfccc8ec6c341edfd1c2a0fc1e5cfd18330f93af938de2348c \ + --hash=sha256:513a3784104839770d690e04339a8b4d33439fcd5dd99f2e4580f9fc1097bfb2 # via behave pillow==11.3.0 \ --hash=sha256:023f6d2d11784a465f09fd09a34b150ea4672e85fb3d05931d89f373ab14abb2 \ @@ -273,13 +272,13 @@ pycryptodome==3.23.0 \ --hash=sha256:e3f2d0aaf8080bda0587d58fc9fe4766e012441e2eed4269a77de6aea981c8be \ --hash=sha256:eb8f24adb74984aa0e5d07a2368ad95276cf38051fe2dc6605cbcf482e04f2a7 # via -r testing\cucumber\requirements.in -pypdf==5.7.0 \ - --hash=sha256:203379453439f5b68b7a1cd43cdf4c5f7a02b84810cefa7f93a47b350aaaba48 \ - --hash=sha256:68c92f2e1aae878bab1150e74447f31ab3848b1c0a6f8becae9f0b1904460b6f +pypdf==6.0.0 \ + --hash=sha256:282a99d2cc94a84a3a3159f0d9358c0af53f85b4d28d76ea38b96e9e5ac2a08d \ + --hash=sha256:56ea60100ce9f11fc3eec4f359da15e9aec3821b036c1f06d2b660d35683abb8 # via -r testing\cucumber\requirements.in -reportlab==4.4.2 \ - --hash=sha256:58e11be387457928707c12153b7e41e52533a5da3f587b15ba8f8fd0805c6ee2 \ - --hash=sha256:fc6283048ddd0781a9db1d671715990e6aa059c8d40ec9baf34294c4bd583a36 +reportlab==4.4.3 \ + --hash=sha256:073b0975dab69536acd3251858e6b0524ed3e087e71f1d0d1895acb50acf9c7b \ + --hash=sha256:df905dc5ec5ddaae91fc9cb3371af863311271d555236410954961c5ee6ee1b5 # via -r testing\cucumber\requirements.in requests==2.32.4 \ --hash=sha256:27babd3cda2a6d50b30443204ee89830707d396671944c998b5975b031ac2b2c \ @@ -291,6 +290,40 @@ six==1.17.0 \ # via # behave # parse-type +tomli==2.2.1 \ + --hash=sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6 \ + --hash=sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd \ + --hash=sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c \ + --hash=sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b \ + --hash=sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8 \ + --hash=sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6 \ + --hash=sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77 \ + --hash=sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff \ + --hash=sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea \ + --hash=sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192 \ + --hash=sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249 \ + --hash=sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee \ + --hash=sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4 \ + --hash=sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98 \ + --hash=sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8 \ + --hash=sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4 \ + --hash=sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281 \ + --hash=sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744 \ + --hash=sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69 \ + --hash=sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13 \ + --hash=sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140 \ + --hash=sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e \ + --hash=sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e \ + --hash=sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc \ + --hash=sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff \ + --hash=sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec \ + --hash=sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2 \ + --hash=sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222 \ + --hash=sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106 \ + --hash=sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272 \ + --hash=sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a \ + --hash=sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7 + # via behave typing-extensions==4.14.1 \ --hash=sha256:38b39f4aeeab64884ce9f74c94263ef78f3c22467c8724005483154c26648d36 \ --hash=sha256:d1e1e3b58374dc93031d6eda2420a48ea44a36c2b4766a4fdeb3710755731d76