Skip to content

Security patches from vanilla DSpace 7.6.7 (CVE-2026-49830, CVE-2026-49831)#1340

Merged
milanmajchrak merged 18 commits into
dtq-devfrom
dtq-dev-security-patches-7.6.7
Jun 18, 2026
Merged

Security patches from vanilla DSpace 7.6.7 (CVE-2026-49830, CVE-2026-49831)#1340
milanmajchrak merged 18 commits into
dtq-devfrom
dtq-dev-security-patches-7.6.7

Conversation

@milanmajchrak

@milanmajchrak milanmajchrak commented Jun 18, 2026

Copy link
Copy Markdown
Collaborator

Security patches for dtq-dev (DSpace 7.6.5)

Backports the vanilla DSpace 7.6.7 security set onto dtq-dev

Ref: dspace-community security announcement

Why this is needed

Without it, the base keeps re-seeding the vulnerable code into future rebases. Of the 4 advisories in the announcement, two apply to 7.x (≤7.6.6, fixed in 7.6.7); the Velocity RCE and LDN path-traversal advisories are 8.x/9.x-only but their hardening is included here as defense-in-depth (same scope as the customer 7.6.7 PRs).

Fixes included

  • CVE-2026-49830 (GHSA-c827-pw3m-67w7) — ORE aggregated resource URI validation
  • CVE-2026-49831 (GHSA-v66x-68f2-pxf5) — Curation tasks path traversal (SecureFileAccess, taskfile/reporter base-path containment, -r/-T moved to CLI-only)
  • Velocity/Email template hardening (Utils.getSecureVelocityProperties, allowed-config map, removal of UnmodifiableConfigurationService)
  • GlobalRequestSecurityFilter (JSP/traversal request filtering, backported for javax)

Conflict resolution (vs. the clean customer cherry-picks)

Two files needed manual resolution because dtq-dev (UFAL) diverges from vanilla:

  • Email.java — dropped the now-unused java.util.Properties import (UFAL kept it), retained Collectors (still used).
  • Curation.java — kept UFAL's reporter abstraction (DoNothingReporter/SystemOutReporter/FilePrinterReporter) instead of vanilla's raw OutputStream. The reporter path is now validated via SecureFileAccess.validatePathForWrite(...) against curate.reporter.base before opening — same security guarantee, preserves UFAL output formatting.

Verification

  • mvn -pl dspace-api -am compile passes locally.
  • New security config keys added: message.templates.allowed-config, curate.taskfile.base, curate.reporter.base, oai.harvester.ore.file.*.
  • Full Unit + IT + docker builds run via PR CI.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Curation CLI now supports -r/--reporter (for output destination) and -T/--taskfile (for task specifications) options.
  • Security Improvements

    • Added protection against path traversal and JSP injection attacks.
    • Implemented secure file path validation with configurable allowed base directories.
    • Added URI validation for OAI ORE resource ingestion.
    • Restricted configuration data exposed to email templates.

kshepherd added 17 commits June 18, 2026 13:30
* Safer Velocity configuration
* New "message.templates.allowed-config" config
* Remove "UnmodifiableConfiguration" in favour of a
  simple Map of whitelisted Config keys/values
* Centralise Velocity config in core Utils
* Small javadoc changes

(cherry picked from commit b2d6141)
(cherry picked from commit 5b31db5)
(cherry picked from commit 6b66531)
(cherry picked from commit 46a0dfb)
(cherry picked from commit 5803819)
(cherry picked from commit 4be430f)
(cherry picked from commit cf9be85)
(cherry picked from commit dc3e455)
(cherry picked from commit 295a046)
(cherry picked from commit 0b1deae)
(cherry picked from commit e2e6a79)
(cherry picked from commit 2e40077)
(cherry picked from commit 56ae287)
(cherry picked from commit 1a3dfd7)
(cherry picked from commit 22bec44)
Removes some JDK >= 16 usage

(cherry picked from commit 55905a2)
(cherry picked from commit a757221)
(cherry picked from commit 6437472)
(cherry picked from commit 37cd6eb)
(cherry picked from commit 00e4979)
(cherry picked from commit 27708ea)
@coderabbitai

coderabbitai Bot commented Jun 18, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@milanmajchrak, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 5 minutes and 14 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits.

🚦 How do rate limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan refill rate.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, the refill rate gradually slows as usage increases. The highest same-day bursts are limited more strictly.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 64ff979f-9567-44f6-be48-97b0e2cae642

📥 Commits

Reviewing files that changed from the base of the PR and between 5d27a78 and f39fac0.

📒 Files selected for processing (1)
  • dspace/config/dspace.cfg
📝 Walkthrough

Walkthrough

Adds four independent security controls: a GlobalRequestSecurityFilter blocking path traversal and JSP execution with HTTP 403; a new SecureFileAccess utility enforcing filesystem path boundaries, integrated into the curation CLI; a Velocity template config allowlist via Utils.getAllowedTemplateConfig() replacing full config exposure in Email.send(); and URI scheme/host validation in OREIngestionCrosswalk before downloading aggregated resources.

Changes

HTTP Request Security Filter

Layer / File(s) Summary
GlobalRequestSecurityFilter: normalization and blocking
dspace-server-webapp/src/main/java/org/dspace/app/rest/security/GlobalRequestSecurityFilter.java
New highest-precedence OncePerRequestFilter decodes and normalizes request URIs, then returns HTTP 403 for path traversal or JSP execution patterns; otherwise delegates to the filter chain.
SitemapRestControllerIT: traversal tests updated to 403
dspace-server-webapp/src/test/java/org/dspace/app/rest/SitemapRestControllerIT.java
Traversal integration tests replace expected = ServletException.class with andExpect(status().isForbidden()), reflecting the new filter interception.

Filesystem Access Security

Layer / File(s) Summary
SecureFileAccess: path validation and stream helpers
dspace-api/src/main/java/org/dspace/storage/secure/SecureFileAccess.java
New utility class with validatePathForWrite (normalize-only) and validatePathForRead (symlink-resolving), plus getBufferedReader/getInputStream/getOutputStream wrappers and CWD/base-dir path helpers.
Curation CLI: secure task-file and reporter-file paths
dspace-api/src/main/java/org/dspace/curate/Curation.java, CurationCliScriptConfiguration.java, CurationClientOptions.java, dspace/config/modules/curate.cfg
handleCurationTask and initCurator use SecureFileAccess to restrict file reads/writes to configured base paths (curate.taskfile.base, curate.reporter.base); CLI options -T/-r are registered in CurationCliScriptConfiguration.
CurationScriptIT: task-file tests disabled
dspace-server-webapp/src/test/java/org/dspace/curate/CurationScriptIT.java
curateScript_InvalidTaskFile and curateScript_validRequest_TaskFile are annotated @Ignore to disable execution.

Velocity Template Config Restriction

Layer / File(s) Summary
Utils: getAllowedTemplateConfig and secure Velocity properties
dspace-api/src/main/java/org/dspace/core/Utils.java
Adds DEFAULT_ALLOWED_TEMPLATE_CONFIGS constant and getAllowedTemplateConfig() returning a Map<String, String> of only allowlisted config keys; getSecureVelocityProperties uses SecureUberspector and optional strict mode.
Email: allowlisted config context, remove UnmodifiableConfigurationService
dspace-api/src/main/java/org/dspace/core/Email.java
send() replaces full-config UnmodifiableConfigurationService injection with Utils.getAllowedTemplateConfig() and uses Utils.getSecureVelocityProperties; the nested UnmodifiableConfigurationService class is removed.
dspace.cfg: message.templates.allowed-config
dspace/config/dspace.cfg
Adds message.templates.allowed-config entries whitelisting dspace.name, dspace.ui.url, mail.admin, and related keys; includes commented message.templates.strict_mode.

OREIngestionCrosswalk URI Validation

Layer / File(s) Summary
OREIngestionCrosswalk: validResourceUri and download guard
dspace-api/src/main/java/org/dspace/content/crosswalk/OREIngestionCrosswalk.java, dspace/config/modules/oai.cfg
Adds validResourceUri enforcing http/https and optionally restricting host to oai.harvester.ore.file.allowedUrlPrefix values or the parent entry host; the download path throws FileNotFoundException when validation fails.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant GlobalRequestSecurityFilter
  participant FilterChain
  Client->>GlobalRequestSecurityFilter: HTTP request with raw URI
  GlobalRequestSecurityFilter->>GlobalRequestSecurityFilter: normalizeAndDecodeUri
  alt traversal pattern detected
    GlobalRequestSecurityFilter-->>Client: HTTP 403 Forbidden
  else JSP execution pattern detected
    GlobalRequestSecurityFilter-->>Client: HTTP 403 Forbidden
  else URI is clean
    GlobalRequestSecurityFilter->>FilterChain: doFilter(request, response)
  end
Loading
sequenceDiagram
  participant CurationCLI as Curation CLI
  participant SecureFileAccess
  participant ConfigurationService
  participant Filesystem
  CurationCLI->>SecureFileAccess: calculateAbsolutePathUsingCwd(taskFile)
  CurationCLI->>ConfigurationService: getPropertyValue("curate.taskfile.base")
  CurationCLI->>SecureFileAccess: getBufferedReader(absolutePath, allowedBasePaths, UTF-8)
  SecureFileAccess->>SecureFileAccess: validatePathForRead(file, allowedBasePaths)
  SecureFileAccess->>Filesystem: toRealPath() + normalize() boundary check
  alt path within allowed base
    SecureFileAccess-->>CurationCLI: BufferedReader
  else path outside allowed base
    SecureFileAccess-->>CurationCLI: IOException
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Suggested labels

Vanilla-upstream, track_to_dtq-dev

Suggested reviewers

  • vidiecan
🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 55.17% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ⚠️ Warning The PR description does not follow the required template structure. It lacks the 'Analysis', 'Problems', 'Manual Testing', and 'Copilot review' sections specified in the template. Add the required template sections: include an 'Analysis' section explaining the technical details, a 'Problems' section if applicable, a 'Manual Testing' checkbox section, and a 'Copilot review' section with appropriate checkboxes.
✅ Passed checks (3 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title directly and specifically identifies the two CVEs being addressed and their DSpace version origin, accurately reflecting the security patches and backport objectives of the changeset.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (1)
dspace-server-webapp/src/main/java/org/dspace/app/rest/security/GlobalRequestSecurityFilter.java (1)

121-126: ⚡ Quick win

Consider adding mixed literal/encoded dot pattern detection.

The %2e%2e check catches double-encoded traversal (%252e%252e), but mixed patterns like .%252e decode to .%2e after a single pass—this evades the current checks since it's neither literal .. nor %2e%2e.

While downstream controllers provide defense-in-depth (e.g., SitemapRestController matches exact filenames from directory listing), strengthening the filter would add another protective layer.

🛡️ Proposed enhancement
 private boolean isTraversalAttempt(String url) {
     return url.contains("../")
             || url.contains("/..")
             || url.contains("%2e%2e")
+            || url.contains(".%2e")
+            || url.contains("%2e.")
             || url.contains("..");
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@dspace-server-webapp/src/main/java/org/dspace/app/rest/security/GlobalRequestSecurityFilter.java`
around lines 121 - 126, The isTraversalAttempt method is missing detection for
mixed literal and encoded dot patterns that can evade the current checks.
Specifically, patterns like .%2e or %2e. (mixing literal dots with
percent-encoded dots) can decode to .. after a single decoding pass, bypassing
the existing checks for literal .. and encoded %2e%2e. Add additional checks to
the return statement in isTraversalAttempt to detect these mixed encoding
patterns by including checks for .%2e, %2e., and case-insensitive variants
(.%2E, %2E., etc.) alongside the existing pattern checks.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@dspace-api/src/main/java/org/dspace/content/crosswalk/OREIngestionCrosswalk.java`:
- Around line 289-311: Add null checks before dereferencing the result of
getHost() calls to prevent NullPointerException when URIs lack authority. In the
allowed prefix validation loop, check if both allowedUri.getHost() and
resourceUri.getHost() are non-null before calling toLowerCase() on them.
Similarly, in the fallback validation checks, verify that entryUri.getHost() and
resourceUri.getHost() are non-null before calling toLowerCase() on them. If
either host is null, handle it appropriately by either skipping that comparison
or returning false as needed for the validation logic.

In `@dspace-api/src/main/java/org/dspace/core/Email.java`:
- Around line 367-371: The email templates are unable to access the lr.help.mail
configuration value because it is not included in the allowlist for template
configuration. In the Email.java file where vctx.put("config",
Utils.getAllowedTemplateConfig()) is called, the getAllowedTemplateConfig method
only returns allowlisted keys. Add lr.help.mail to the allowlist by updating
either the DEFAULT_ALLOWED_TEMPLATE_CONFIGS constant in Utils.java or the
message.templates.allowed-config setting in dspace.cfg, so that templates in
clarin_download_link_admin, clarin_token, matomo_report, and share_submission
can properly resolve the help email address instead of receiving null values.

In `@dspace-api/src/main/java/org/dspace/core/Utils.java`:
- Around line 648-654: The Javadoc comment for the method creating default,
secure Velocity configuration properties contains two syntax errors. First, the
`@see` tag on line 650 incorrectly uses {`@link` Email} syntax within it; replace
`@see {`@link` Email}` with the correct format `@see Email` for a direct
reference, or move the link inline into the description text. Second, line 653
uses the invalid Javadoc tag `@returns`; replace it with the correct tag
`@return` which is the standard Javadoc syntax for describing return values.

In `@dspace-api/src/main/java/org/dspace/storage/secure/SecureFileAccess.java`:
- Around line 41-53: The validatePathForWrite method in SecureFileAccess.java
has a security vulnerability where it does not canonicalize the target path
itself before validation, unlike validatePathForRead. Currently, the method
normalizes the path lexically and checks if resolvedPath starts with basePath,
but this check can be bypassed if a parent directory is a symlink pointing
outside the allowed base. To fix this, apply toRealPath() to the resolvedPath
variable (similar to how basePath is converted to its real path) before
performing the startsWith validation check. This will ensure that any symlinks
in parent directories are resolved to their actual filesystem locations and
verified to be within the allowed base path before the write operation is
permitted.

In `@dspace/config/modules/oai.cfg`:
- Around line 163-165: The example values for
oai.harvester.ore.file.allowedUrlPrefix configuration property are missing the
URI scheme component. Update the two commented-out example lines (lines 164 and
165) to include the full URI with scheme prefix (such as https:// or http://)
before the domain names, so they read as complete URIs (e.g.,
https://dspace.myinstitution.edu and https://files.myinstitution.edu) rather
than just host names, to match what the validator expects at runtime.

---

Nitpick comments:
In
`@dspace-server-webapp/src/main/java/org/dspace/app/rest/security/GlobalRequestSecurityFilter.java`:
- Around line 121-126: The isTraversalAttempt method is missing detection for
mixed literal and encoded dot patterns that can evade the current checks.
Specifically, patterns like .%2e or %2e. (mixing literal dots with
percent-encoded dots) can decode to .. after a single decoding pass, bypassing
the existing checks for literal .. and encoded %2e%2e. Add additional checks to
the return statement in isTraversalAttempt to detect these mixed encoding
patterns by including checks for .%2e, %2e., and case-insensitive variants
(.%2E, %2E., etc.) alongside the existing pattern checks.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 52b48de3-ccaf-46f1-bd56-7aa0cc422530

📥 Commits

Reviewing files that changed from the base of the PR and between 8f4b80d and 5d27a78.

📒 Files selected for processing (13)
  • dspace-api/src/main/java/org/dspace/content/crosswalk/OREIngestionCrosswalk.java
  • dspace-api/src/main/java/org/dspace/core/Email.java
  • dspace-api/src/main/java/org/dspace/core/Utils.java
  • dspace-api/src/main/java/org/dspace/curate/Curation.java
  • dspace-api/src/main/java/org/dspace/curate/CurationCliScriptConfiguration.java
  • dspace-api/src/main/java/org/dspace/curate/CurationClientOptions.java
  • dspace-api/src/main/java/org/dspace/storage/secure/SecureFileAccess.java
  • dspace-server-webapp/src/main/java/org/dspace/app/rest/security/GlobalRequestSecurityFilter.java
  • dspace-server-webapp/src/test/java/org/dspace/app/rest/SitemapRestControllerIT.java
  • dspace-server-webapp/src/test/java/org/dspace/curate/CurationScriptIT.java
  • dspace/config/dspace.cfg
  • dspace/config/modules/curate.cfg
  • dspace/config/modules/oai.cfg

Comment thread dspace-api/src/main/java/org/dspace/core/Email.java
Comment thread dspace-api/src/main/java/org/dspace/core/Utils.java
Comment thread dspace/config/modules/oai.cfg
@milanmajchrak milanmajchrak changed the title DEV/Security patches from vanilla DSpace 7.6.7 (CVE-2026-49830, CVE-2026-49831) Security patches from vanilla DSpace 7.6.7 (CVE-2026-49830, CVE-2026-49831) Jun 18, 2026
@milanmajchrak milanmajchrak requested a review from Copilot June 18, 2026 12:10

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Backports vanilla DSpace 7.6.7 security hardening into this fork (DSpace 7.6.5 base), focusing on ORE aggregated-resource URI validation, curation task/report file path traversal defenses, and tighter Velocity template configuration exposure / execution restrictions.

Changes:

  • Added ORE aggregated-resource URI validation controls/config and implemented validation in OREIngestionCrosswalk.
  • Introduced SecureFileAccess and integrated it into curation taskfile/reporter handling; moved -r/-T to CLI-only options.
  • Hardened Velocity usage for Email/templates via restricted config exposure and secure Velocity properties; added a global request hardening filter and updated related ITs.

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
dspace/config/modules/oai.cfg Adds new (commented) ORE URL validation/allowlist config keys.
dspace/config/modules/curate.cfg Documents new base-path containment config for taskfiles and reporter outputs.
dspace/config/dspace.cfg Adds allowlist config for template-exposed configuration and optional strict Velocity mode.
dspace-server-webapp/src/test/java/org/dspace/curate/CurationScriptIT.java Disables two -T taskfile-related integration tests via @Ignore.
dspace-server-webapp/src/test/java/org/dspace/app/rest/SitemapRestControllerIT.java Updates traversal tests to expect 403 Forbidden instead of servlet exception.
dspace-server-webapp/src/main/java/org/dspace/app/rest/security/GlobalRequestSecurityFilter.java New global servlet filter to block traversal and JSP execution attempts.
dspace-api/src/main/java/org/dspace/storage/secure/SecureFileAccess.java New utility for base-path validation and safe stream/reader creation.
dspace-api/src/main/java/org/dspace/curate/CurationCliScriptConfiguration.java Adds CLI-only -r/--reporter and -T/--taskfile options.
dspace-api/src/main/java/org/dspace/curate/CurationClientOptions.java Removes -r/-T from options available via REST/scripts endpoints; clarifies behavior in Javadoc.
dspace-api/src/main/java/org/dspace/curate/Curation.java Validates taskfile and reporter paths against configured base dirs using SecureFileAccess.
dspace-api/src/main/java/org/dspace/core/Utils.java Adds secure Velocity properties builder and a restricted template config map builder.
dspace-api/src/main/java/org/dspace/core/Email.java Switches to secure Velocity properties and restricts template-accessible configuration.
dspace-api/src/main/java/org/dspace/content/crosswalk/OREIngestionCrosswalk.java Adds ORE aggregated resource URI validation and related configuration usage.

Comment thread dspace-api/src/main/java/org/dspace/core/Utils.java
Comment thread dspace-api/src/main/java/org/dspace/core/Utils.java
The 7.6.7 Velocity hardening restricts templates to an allowlisted
"config" map (Utils.getAllowedTemplateConfig). UFAL/CLARIN templates
(clarin_download_link_admin, clarin_token, matomo_report,
share_submission) reference config.get('lr.help.mail'), which vanilla
DSpace does not ship, so it was missing from the allowlist and those
emails would render a null help address. Add it to dspace.cfg only;
Utils.java stays identical to upstream.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@milanmajchrak milanmajchrak merged commit 22cfef5 into dtq-dev Jun 18, 2026
27 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants