Skip to content

Upgrade vanilla base 7.6.5 → 7.6.7 (diff-apply two-parent merge) - #1383

Open
milanmajchrak wants to merge 1450 commits into
dtq-dev-767-basefrom
upgrade/vanilla-7.6.7
Open

Upgrade vanilla base 7.6.5 → 7.6.7 (diff-apply two-parent merge)#1383
milanmajchrak wants to merge 1450 commits into
dtq-dev-767-basefrom
upgrade/vanilla-7.6.7

Conversation

@milanmajchrak

@milanmajchrak milanmajchrak commented Jul 21, 2026

Copy link
Copy Markdown
Collaborator

Upgrades the fork's vanilla base from 7.6.5 to 7.6.7 (7.6.6 + 7.6.7, mostly security/bugfix maintenance).

Problem

The fork sat on vanilla 7.6.5 and was missing two maintenance releases, including the CVE-2026-49830/49831 cluster and the content-disposition XSS hardening.

Root cause of the awkward merge

git merge-base dtq-dev vanilla is dspace-7.6.1, not 7.6.5 — the fork carries 7.6.2–7.6.5 textually (via CLARIN ports), not as ancestry. A plain git merge dspace-7.6.7 re-litigates the whole 7.6.1→7.6.7 range: 111 conflicted files.

Change set

Applied only the true 7.6.5→7.6.7 delta (git merge-tree --merge-base=dspace-7.6.5) = 26 conflicts, committed via git commit-tree as a real two-parent merge so merge-base is permanently repaired — 7.6.8 will be an ordinary small merge.

Commit What
81dac88c07 the merge itself (^1 = fork 00501a2db0, ^2 = dspace-7.6.7), 26 conflicts resolved per recipe + 6 out-of-marker fixups
51f4b9e012 CLARIN content-disposition allowlist; D6 Shib ITs moved to the reload-safe helper; test-config pin
e4978e2b95 comment-only correction (8 MB threshold)
c6a1101410 OAI double-escaping fix; allowlist completed
b28457142a OAI sanitizer also strips unpaired surrogates (see below)
1bfc45b888 CLARIN S3 stack ported to AWS SDK v2 (decision D1 reversed by the user)
75848b2639 S3 defects found by the 10-reviewer audit (retryable multipart, abort-on-error, fd leak, doesBucketExist 403, ContentDispositionUtils, presign region + disposition)
4b5390f9d4 OAIPMHSanitizeIT — the OAI sanitizer's first test, mutation-verified

Test evidence

CI green on the head being merged (4b5390f9d4) — 9 success + 1 skipped (dspace-redeploy, skipped on PRs by design), 0 failures. Maven prints no reactor-wide total, so these are the per-module summaries (run 30882484223):

  • Integration -DskipIntegrationTests=false — 540 (dspace-api) + 1 (Additions) + 3362 (server-webapp) + 1 (Local Customizations) = 3904 tests, 0 failures, 0 errors, 60 skipped. dspace-api went 532 -> 540: the eight new ClarinS3BitStoreServiceIT cases, confirmed by name in the CI log alongside S3BitStoreServiceIT at 15; server-webapp gained the 4 OAIPMHSanitizeIT cases
  • Unit -DskipUnitTests=false — 78 (services) + 1328 (dspace-api) + 1 (deprecated REST) + 8 (dspace-oai) + 145 (server-webapp) = 1560 tests, 0 failures, 0 errors, 12 skipped
  • no flaky reruns despite rerunFailingTestsCount=2; all docker-builds pass; checkstyle 0 violations - from a local mvn verify, NOT from this CI run: the Build workflow passes -Dcheckstyle.skip=true

Note for future reviews: PR CI tests the merge of this head with the moving base, so the server-webapp figure moved 3357→3358 when dtq-dev-767-base advanced — not nondeterminism.

Live rehearsal on a local stack built from this branch:

  • Flyway out-of-order trap confirmed, not just predicted. On a DB migrated to the fork's pre-upgrade state both new migrations show Ignored; plain dspace database migrate (what the compose entrypoint runs) does not apply them; dspace database migrate ignored does. entity_type_label_upper_idx created.
  • V7.6_2025.10.29 blast radius measured: of 3 requestitem rows it deleted exactly 1 — the one bound to bitstream.deleted IS TRUE. Rows with NULL bitstream_id and rows on live bitstreams survived.
  • content-disposition matrix: text/plain, text/csv, image/tiff, application/pdfinline; text/html, text/xml/TEI → attachment.
  • Solr core from this branch keeps both vanilla (*_year, HtmlEncoder highlight) and fork (ASCIIFolding, items_owning_community, dspaceAutoComplete) changes; index-discovery -b completes and search returns results.

Feature parity vs dtq-dev: the merge touched none of the 198 CLARIN-named files; 7 files disappeared: 6 are vanilla deletes that were byte-identical to 7.6.5, the 7th is CmdiDownloadLinkIT.java, a CLARIN-only IT that arrived on the base branch in #1384 after this branch was cut - the merge restores it in the fork. Every class="org.dspace..." in Spring XML resolves to a source file — re-measured at HEAD: 588 distinct candidates, 0 unresolved. (Six were flagged by a naive scan and all six are artefacts: three Constants.* static fields, one nested enum Role.Scope, one bean name rather than a class, and CiniiMetadataContributor, which sits inside an XML comment and is equally absent from vanilla 7.6.7.) Flyway migration scripts 149→152 (the 183→186 figure counted everything under storage/rdbms, Java sources included), 0 lost.

Scope beyond the 26 conflicts (each load-bearing, none incidental)

  1. AbstractCurationTask.java — re-added dereference(Context,String) that 7.6.7 deleted; CLARIN-only ItemMetadataQAChecker:409 calls it. Compile fix.
  2. ItemServiceTest.java — 5 call sites updated to vanilla's new String q parameter.
  3. RequestItemRepositoryIT.javacontext.reloadEntity before bitstreamService.delete; CLARIN updateItemFilesMetadata needs an attached item. Test-scoped, not a production change.
  4. dspace/config/clarin-dspace.cfg — CLARIN inline allowlist. 7.6.7 flipped this key from blocklist to allowlist, so without it text/plain corpora, audio, TIFF scans and CSV silently became force-download. HTML/XML/TEI/SVG/JS deliberately excluded.
  5. dspace-api/src/test/data/dspaceFolder/config/local.cfg — pins the vanilla value for tests. testEnvironment.zip ships clarin-dspace.cfg into dspace.dir, so item 4 alone turned BitstreamRestControllerIT.checkContentDispositionOfFormats red. Keeps the upstream test file byte-identical to vanilla.
  6. AuthenticationRestControllerIT.java — the 2 new vanilla Shib tests were the only 2 of 22 sites still using plain setProperty() for the auth plugin sequence, which this file's own javadoc documents as silently discarded under CLARIN config auto-reload. Switched to the fork's setAuthenticationMethodSequence helper.
  7. dspace-oai/.../ItemUtils.java — see below.

The one vanilla defect we had to work around

7.6.6 added ItemUtils.sanitize() = StringEscapeUtils.escapeXml10(value) and applies it to every metadata value before the XOAI serializer escapes it again. Everything containing & < > " ' reaches harvesters double-escaped. Measured live:

REST:  u767xssprobe <script>alert(1)</script> end
cmdi:  u767xssprobe &amp;lt;script&amp;gt;alert(1)&amp;lt;/script&amp;gt; end

That corrupts cmdi and olac — CLARIN/LINDAT's primary machine interface (VLO, OLAC). sanitize() now removes characters XML 1.0 forbids instead of escaping them, which is what its own javadoc says it does. Escaping is not lost: Element$Field.write() hands every value to XMLStreamWriter.writeCharacters(), which escapes on its own — it was simply being done twice.

b28457142a completes that fix. escapeXml10 was an AggregateTranslator that also ran UnicodeUnpairedSurrogateRemover, and the first version reproduced only the control-character part. A lone surrogate (truncated 4-byte character, mojibake corpus) then makes the StAX writer throw Broken surrogate pair inside XOAI.index(), and since that is caught per item the record is silently dropped from the OAI index — 2048 code units affected, against 0 for vanilla. Verified across the whole BMP: 0 illegal code units leak, 0 legal ones are removed, valid surrogate pairs survive, unpaired ones do not, metacharacters still pass unescaped. Deliberate remaining difference: C0/DEL U+007F–U+009F are left literal instead of becoming &#NNN;; they are legal XML 1.0 characters, so well-formedness is unaffected.

This is an upstream defect and should be reported to DSpace/DSpace.

Risk

  • dspace database migrate ignored is irreversible and V7.6_2025.10.29 deletes rows. Snapshot requestitem first (blast radius above).
  • Deploy via sha tag, not dspace-7_x.
  • Decision D1 was reversed on 2026-08-03: the S3 stack is now on AWS SDK v2, see the section below. (v1 is past AWS end-of-support.)
  • Decision D3: the allowlist is only half the story — webui.content_disposition_threshold (code default 8 MB) still sends larger files as attachment. Left unchanged deliberately; raising it widens the inline-render surface.
  • audio/flac, audio/ogg and audio/wav are inert until those formats are added to the registry (backlog).

Rollback

Revert the 7 CLARIN commits above plus the merge 81dac88c07, or simply reset the branch to 00501a2db0. Nothing is deployed by merging. Already-run migrate ignored is not revertible — restore from the pre-migration snapshot.

Non-goals / still open

Playwright suite, CLARIN license-gated download / preview+tgz / Shibboleth / request-a-copy happy path, staged rollout via sha tag, v9-ledger closure, customer branches. ui.baseUrl must be set in the deployment overlay (see the FE PR).

Sign-off status: D1, D5 and D7 were answered by the maintainer and are implemented. D2, D3, D4 and D6
were queried, explained in UPGRADE_767_QUESTIONS.md, and still have no decision — the branch ships the
documented default for each. Merging accepts those defaults. What the audit deliberately did not fix,
and why, is listed in UPGRADE_767_AUDIT.md §2.

🤖 Generated with Claude Code


⚠️ Deployment prerequisites found by the final review — read before deploying

No compose path applies the two new migrations. Verified in DatabaseUtils.java: migrate ignored passes outOfOrder=true, but migrate force passes outOfOrder=false and plain migrate defaults to false. This repo's docker-compose.yml:67 runs plain migrate; the FE composes run migrate force. So both new migrations stay Ignored forever — the entity_type_label_upper_idx index is never created and the requestitem cleanup never runs, with no error and no log line.

dspace database migrate ignored therefore has to be run manually per instance, after snapshotting requestitem. That is exactly what the G4 runbook says; this note records why it cannot be skipped. Switching compose to migrate ignored would make the destructive migration run automatically on every stack start — deliberately not done here.

Pair with the FE PR's ui.baseUrl prerequisite (see dataquest-dev/dspace-angular#1398).


AWS SDK v2 port (1bfc45b888) — added 2026-08-03

7.6.7 rewrote S3BitStoreService from com.amazonaws (v1) to software.amazon.awssdk (v2 CRT client). This PR
originally rejected that and kept v1; the user reversed the decision — v1 is past AWS end-of-support anyway.

The fork owns three S3 classes vanilla does not know, so this was not a file takeover:

File Treatment
S3BitStoreService vanilla 7.6.7 + exactly three CLARIN items (+58/-8 vs dspace-7.6.7 at HEAD; was +47/-5 before the audit fixes): getFile(Bitstream), a protected client so the subclasses can reuse it, and a configurable pathStyleAccessEnabled. Vanilla hardcodes forcePathStyle(true) on an endpoint override; keeping the property preserves the fork's v1 behaviour and keeps bitstore.xml valid.
SyncS3BitStoreService (339 lines, 199 non-comment) CLARIN-only, hand-ported including the explicit multipart path
S3DirectDownloadServiceImpl (204 lines, 142 non-comment) CLARIN-only; presigning moved to S3Presigner, built from the same credentials/region/endpoint as the bitstore client
S3BitStoreServiceIT taken from 7.6.7 wholesale — the fork's copy was byte-identical to 7.6.5, so no CLARIN test was lost. LocalStack replaces s3mock, which only ever spoke v1.
ClarinS3BitStoreServiceIT new — the CLARIN-only paths had no test at all
bitstore.xml, assetstore.cfg the merge had dropped vanilla's four new tuning keys (targetThroughputGbps, minPartSizeBytes, maxConcurrency, s3ChecksumAlgorithm)

getFile is not a new invention: it is byte-identical to the fork's own v9 implementation (5e930d1f2a), so the
7.x and v9 branches do not diverge on it.

Two defects the new tests caught — neither visible by reading the code

  1. ETag quoting. v1 returned part ETags unquoted, v2 returns them quoted. The multipart checksum comparison
    would have failed on every part, and because that path logs and swallows, completeMultipartUpload would
    never run — a silently failed upload with a bitstream row pointing at nothing.
  2. Leaked FileOutputStream. SyncS3BitStoreService.put() never closed the output stream on the local
    assetstore copy. Pre-existing, not a port regression, but it leaks a handle per stored bitstream and made
    remove() silently fail to delete the local file.

Evidence

  • mvn test-compile -DskipTests — Reactor Summary 13/13 modules SUCCESS
  • S3BitStoreServiceIT (vanilla, LocalStack) — Tests run: 15, Failures: 0, Errors: 0
  • ClarinS3BitStoreServiceIT (new) — Tests run: 8, Failures: 0, Errors: 0
  • S3DirectDownloadServiceTestTests run: 11, Failures: 0, Errors: 0
  • ContentDispositionUtilsTest (new) — Tests run: 9, Failures: 0, Errors: 0
  • OAIPMHSanitizeIT (new) — Tests run: 4, Failures: 0, Errors: 0
  • checkstyle 0 violations; git grep com.amazonaws finds nothing anywhere in the tree

s3Store is lazy-init and assetstore.index.primary defaults to the local store, so nothing otherwise
instantiates the bean — springWiringBindsStoreProperties pulls it out of the service manager on purpose. It is the
only check that the blank maxConcurrency binds to a null Integer and that s3ChecksumAlgorithm binds to the enum.

Not covered by tests

No test talks to a real non-AWS endpoint, so the CESNET/MinIO path (endpoint + pathStyleAccessEnabled) and the
presigned-URL path are verified against LocalStack and mocks only. Worth one manual download check on a dev instance
with S3 enabled before this reaches production.


Independent audit round (2026-08-04) — 10 reviewers, and what they found

Ten independent reviewers audited this PR, each on a separate area, all told to disprove rather than
confirm. They found real defects; the fixes are the two commits above 1bfc45b888 (75848b2639, 4b5390f9d4) plus ff96c2cada in the FE PR.

Fixed here

What Why it mattered
Multipart upload lost retryability uploadPart got a non-resettable stream, so any transient S3 error became permanent; the failure was then logged and swallowed and put() still recorded the bitstream — a DB row pointing at an object S3 does not have. Now uses AsyncRequestBody.fromFile(FileRequestBodyConfiguration), aborts the upload, and throws.
The multipart loop never ran twice 50 MB part size vs a 23-byte fixture. The offset arithmetic this PR rewrote had zero coverage. syncStoreUploadsInMultipleParts now crosses a real part boundary and verifies the object byte for byte.
A tripwire in amazonClientBuilderBy left all 21 S3 tests green Every test injected a ready-made client, so endpointOverride, forcePathStyle, maxConcurrency and the part sizes never executed. The fork's headline delta — configurable pathStyleAccessEnabled — was verified only at its getter. Now covered by a test proven to go red when endpointOverride is removed.
Two Content-Disposition implementations, the newer one wrong It escaped " but not \, and used URLEncoder so a space arrived as +. Extracted to ContentDispositionUtils, used by both call sites.
Direct downloads silently killed inline preview The redirect hardcoded attachment, discarding the disposition the allowlist had just computed.
Presigner region could diverge from the client's Hardcoded us-east-1; now mirrors init() branch for branch.
A 403 read as "bucket absent" init() would try to create an existing bucket; a least-privilege policy denies that and the assetstore comes up dead.
s3ChecksumAlgorithm was dead config bitstore.xml wires SyncS3BitStoreService, which overrides put(), so the only consumer never ran.
ItemUtils.sanitize() had no test Now OAIPMHSanitizeIT, verified non-vacuous: disabling the sanitiser turns 3 of 4 red with WritingXmlException and SAXParseException.

Corrections to earlier claims in this description

  • The content-disposition allowlist flip was 7.6.7, not 7.6.6; ItemUtils.sanitize() arrived in 7.6.6, not 7.6.7.
  • Of the 7 disappeared files, the 7th is a CLARIN-only IT (CmdiDownloadLinkIT), not a vanilla delete. Risk is nil — the base branch restores it — but the original sentence implied no CLARIN file was affected.
  • Retracted twice. "523 of 525 Spring FQCNs resolve; the 2 that do not are Solr plugins" was wrong, and so was the follow-up claim that "2 were lost". Neither figure reproduces under any scope. Re-measured: 588 distinct candidates at HEAD, 0 unresolved — see the parity paragraph above.
  • "Flyway 183→186" counted everything under storage/rdbms; the migration-script count is 149→152.
  • "No compose path applies the two new migrations" is a false absolute: db.entities.yml in both repos runs migrate ignored. The conclusion about the deploy path stands — deploy.yml uses only docker-compose.yml and -rest.yml.
  • "checkstyle clean" was quoted next to CI evidence, but the Build workflow passes -Dcheckstyle.skip=true; that claim comes from local runs.

Known and deliberately not fixed

  • The CLARIN allowlist is not exercised by any test. dspace-api/src/test/data/dspaceFolder/config/local.cfg pins the vanilla list and beats clarin-dspace.cfg, which is what keeps the upstream IT green. Fixing it means giving the CLARIN list its own IT.
  • Relative to dtq-dev, 68 of 97 registry formats flip inline → attachment (office, OpenDocument, archives, TeX). That is the allowlist semantics change working as intended, but users will notice.
  • dspace checker -a <handle> was renamed to -i upstream, and checker-emailer's long options were recased. Any cron using the old spellings breaks silently after deploy.
  • get() no longer reads in 5 MB ranges (vanilla dropped S3LazyInputStream); a single stream is held open for the whole object, which exposes proxy idle timeouts on multi-GB corpora that chunking used to hide.
  • Preview temp files rely on deleteOnExit, which only fires on a clean JVM shutdown. Pre-existing, but it scales with corpus size.
  • The presigned-URL redirect IT never runs@IfProfileValue(name="s3.download.direct.enabled", value="true") and nothing sets that property.
  • No test talks to a real non-AWS endpoint, so the CESNET/MinIO path is verified against LocalStack only.

April Herron and others added 30 commits January 15, 2026 22:26
…of o-o operations [DS-3975]

Includes toniprieto's suggested change in DSpace#2169 discussion and additional logging (e.g. how many bitstreams are missing).

(cherry picked from commit fc1bd87)
Error found when running checker ITs in DSpace#11144.

(cherry picked from commit c358e88)
[Port dspace-7_x] Fix updateMissingBitstreams to use single database operation instead of o-o operations [DS-3975] with logging
[Port dspace-7_x] Add DataCite sandbox url to crosswalk
… test special characters in the URL (#930)

(cherry picked from commit 29199d5)
[Port dspace-7_x] SWORDv2 Added the bitstream encoding to the URL
[Port dspace-7_x] Make sure that bitstream downloads have a content-disposition header set
Bumps `log4j.version` from 2.25.2 to 2.25.3.

Updates `org.apache.logging.log4j:log4j-api` from 2.25.2 to 2.25.3

Updates `org.apache.logging.log4j:log4j-core` from 2.25.2 to 2.25.3

Updates `org.apache.logging.log4j:log4j-1.2-api` from 2.25.2 to 2.25.3

---
updated-dependencies:
- dependency-name: org.apache.logging.log4j:log4j-api
  dependency-version: 2.25.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
- dependency-name: org.apache.logging.log4j:log4j-core
  dependency-version: 2.25.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
- dependency-name: org.apache.logging.log4j:log4j-1.2-api
  dependency-version: 2.25.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
(cherry picked from commit 1cb8827)
[Port dspace-7_x] DSpace 8.x bug fix link checker
Bumps the build-tools group with 4 updates: [org.sonatype.central:central-publishing-maven-plugin](https://github.com/sonatype/central-publishing-maven-plugin), [org.codehaus.mojo:xml-maven-plugin](https://github.com/mojohaus/xml-maven-plugin), [org.codehaus.mojo:license-maven-plugin](https://github.com/mojohaus/license-maven-plugin) and [org.codehaus.mojo:buildnumber-maven-plugin](https://github.com/mojohaus/buildnumber-maven-plugin).


Updates `org.sonatype.central:central-publishing-maven-plugin` from 0.9.0 to 0.10.0
- [Commits](https://github.com/sonatype/central-publishing-maven-plugin/commits)

Updates `org.codehaus.mojo:xml-maven-plugin` from 1.2.0 to 1.2.1
- [Release notes](https://github.com/mojohaus/xml-maven-plugin/releases)
- [Commits](mojohaus/xml-maven-plugin@1.2.0...xml-maven-plugin-1.2.1)

Updates `org.codehaus.mojo:license-maven-plugin` from 2.7.0 to 2.7.1
- [Release notes](https://github.com/mojohaus/license-maven-plugin/releases)
- [Commits](mojohaus/license-maven-plugin@2.7.0...2.7.1)

Updates `org.codehaus.mojo:buildnumber-maven-plugin` from 3.2.1 to 3.3.0
- [Release notes](https://github.com/mojohaus/buildnumber-maven-plugin/releases)
- [Commits](mojohaus/buildnumber-maven-plugin@3.2.1...buildnumber-maven-plugin-3.3.0)

---
updated-dependencies:
- dependency-name: org.sonatype.central:central-publishing-maven-plugin
  dependency-version: 0.10.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: build-tools
- dependency-name: org.codehaus.mojo:xml-maven-plugin
  dependency-version: 1.2.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: build-tools
- dependency-name: org.codehaus.mojo:license-maven-plugin
  dependency-version: 2.7.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: build-tools
- dependency-name: org.codehaus.mojo:buildnumber-maven-plugin
  dependency-version: 3.3.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: build-tools
...

Signed-off-by: dependabot[bot] <support@github.com>
Bumps the fasterxml group with 4 updates: [com.fasterxml:classmate](https://github.com/FasterXML/java-classmate), [com.fasterxml.jackson.core:jackson-annotations](https://github.com/FasterXML/jackson), [com.fasterxml.jackson.core:jackson-core](https://github.com/FasterXML/jackson-core) and [com.fasterxml.jackson.core:jackson-databind](https://github.com/FasterXML/jackson).


Updates `com.fasterxml:classmate` from 1.7.1 to 1.7.3
- [Commits](FasterXML/java-classmate@classmate-1.7.1...classmate-1.7.3)

Updates `com.fasterxml.jackson.core:jackson-annotations` from 2.20 to 2.21
- [Commits](https://github.com/FasterXML/jackson/commits)

Updates `com.fasterxml.jackson.core:jackson-core` from 2.20.1 to 2.21.0
- [Commits](FasterXML/jackson-core@jackson-core-2.20.1...jackson-core-2.21.0)

Updates `com.fasterxml.jackson.core:jackson-databind` from 2.20.1 to 2.21.0
- [Commits](https://github.com/FasterXML/jackson/commits)

Updates `com.fasterxml.jackson.core:jackson-databind` from 2.20.1 to 2.21.0
- [Commits](https://github.com/FasterXML/jackson/commits)

---
updated-dependencies:
- dependency-name: com.fasterxml:classmate
  dependency-version: 1.7.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: fasterxml
- dependency-name: com.fasterxml.jackson.core:jackson-annotations
  dependency-version: '2.21'
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: fasterxml
- dependency-name: com.fasterxml.jackson.core:jackson-core
  dependency-version: 2.21.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: fasterxml
- dependency-name: com.fasterxml.jackson.core:jackson-databind
  dependency-version: 2.21.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: fasterxml
- dependency-name: com.fasterxml.jackson.core:jackson-databind
  dependency-version: 2.21.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: fasterxml
...

Signed-off-by: dependabot[bot] <support@github.com>
Bumps [org.apache.bcel:bcel](https://github.com/apache/commons-bcel) from 6.11.0 to 6.12.0.
- [Changelog](https://github.com/apache/commons-bcel/blob/master/RELEASE-NOTES.txt)
- [Commits](apache/commons-bcel@rel/commons-bcel-6.11.0...rel/commons-bcel-6.12.0)

---
updated-dependencies:
- dependency-name: org.apache.bcel:bcel
  dependency-version: 6.12.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Bumps [dnsjava:dnsjava](https://github.com/dnsjava/dnsjava) from 3.6.3 to 3.6.4.
- [Release notes](https://github.com/dnsjava/dnsjava/releases)
- [Changelog](https://github.com/dnsjava/dnsjava/blob/master/Changelog)
- [Commits](dnsjava/dnsjava@v3.6.3...v3.6.4)

---
updated-dependencies:
- dependency-name: dnsjava:dnsjava
  dependency-version: 3.6.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Bumps [org.postgresql:postgresql](https://github.com/pgjdbc/pgjdbc) from 42.7.8 to 42.7.9.
- [Release notes](https://github.com/pgjdbc/pgjdbc/releases)
- [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md)
- [Commits](pgjdbc/pgjdbc@REL42.7.8...REL42.7.9)

---
updated-dependencies:
- dependency-name: org.postgresql:postgresql
  dependency-version: 42.7.9
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Bumps the amazon-s3 group with 2 updates: software.amazon.awssdk:s3 and [software.amazon.awssdk.crt:aws-crt](https://github.com/awslabs/aws-crt-java).


Updates `software.amazon.awssdk:s3` from 2.38.8 to 2.39.6

Updates `software.amazon.awssdk.crt:aws-crt` from 0.39.4 to 0.40.1
- [Release notes](https://github.com/awslabs/aws-crt-java/releases)
- [Commits](awslabs/aws-crt-java@v0.39.4...v0.40.1)

---
updated-dependencies:
- dependency-name: software.amazon.awssdk:s3
  dependency-version: 2.39.6
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: amazon-s3
- dependency-name: software.amazon.awssdk.crt:aws-crt
  dependency-version: 0.40.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: amazon-s3
...

Signed-off-by: dependabot[bot] <support@github.com>
Bumps the apache-commons group with 4 updates in the / directory: [commons-codec:commons-codec](https://github.com/apache/commons-codec), org.apache.commons:commons-dbcp2, org.apache.commons:commons-pool2 and [org.apache.commons:commons-text](https://github.com/apache/commons-text).


Updates `commons-codec:commons-codec` from 1.20.0 to 1.21.0
- [Changelog](https://github.com/apache/commons-codec/blob/master/RELEASE-NOTES.txt)
- [Commits](apache/commons-codec@rel/commons-codec-1.20.0...rel/commons-codec-1.21.0)

Updates `org.apache.commons:commons-dbcp2` from 2.13.0 to 2.14.0

Updates `org.apache.commons:commons-pool2` from 2.12.1 to 2.13.1

Updates `org.apache.commons:commons-text` from 1.14.0 to 1.15.0
- [Changelog](https://github.com/apache/commons-text/blob/master/RELEASE-NOTES.txt)
- [Commits](apache/commons-text@rel/commons-text-1.14.0...rel/commons-text-1.15.0)

---
updated-dependencies:
- dependency-name: commons-codec:commons-codec
  dependency-version: 1.21.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: apache-commons
- dependency-name: org.apache.commons:commons-dbcp2
  dependency-version: 2.14.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: apache-commons
- dependency-name: org.apache.commons:commons-pool2
  dependency-version: 2.13.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: apache-commons
- dependency-name: org.apache.commons:commons-text
  dependency-version: 1.15.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: apache-commons
...

Signed-off-by: dependabot[bot] <support@github.com>
[Port dspace-7_x] Move same-collection check to inheritDefaultPolicies move method (manual port)
…_x/org.apache.bcel-bcel-6.12.0

Bump org.apache.bcel:bcel from 6.11.0 to 6.12.0
…_x/dnsjava-dnsjava-3.6.4

Bump dnsjava:dnsjava from 3.6.3 to 3.6.4
…_x/fasterxml-8fce214327

Bump the fasterxml group with 4 updates
…_x/io.grpc-grpc-context-1.78.0

build(deps): bump io.grpc:grpc-context from 1.77.0 to 1.78.0
…_x/org.postgresql-postgresql-42.7.9

Bump org.postgresql:postgresql from 42.7.8 to 42.7.9
…_x/build-tools-57bb4f0b4d

Bump the build-tools group with 4 updates
kshepherd and others added 20 commits May 27, 2026 16:30
…_config-7_x

[Port dspace-7_x] Velocity template engine configuration improvements
[Port dspace-7_x] Application hardening: filter requests for JSPs and other unwanted patterns
…ent-7_x

[Port dspace-7_x] Curation I/O improvements
Applies the DSpace 7.6.5->7.6.7 delta onto the CLARIN fork dtq-dev as a
two-parent merge with merge-base dspace-7.6.5. Resolves the 26 conflicting
files per the section-4 recipes plus out-of-marker fixups: keep AWS SDK v1
S3 stack (aws-java-sdk-s3 @ 1.12.792 + s3mock), dedupe Swordv2IT
depositItemWithEmbargo, bump json-smart pin to 2.6.0.

Build-caught fixups (vanilla API changes / new tests vs CLARIN code):
- AbstractCurationTask: re-added dereference(Context,String) helper that vanilla
  removed (inlined into perform()); CLARIN ItemMetadataQAChecker still calls it.
- ItemServiceTest: updated findItemsWithEdit/countItemsWithEdit to the new
  String-query signature from PR DSpace#11953 (empty query preserves old behavior).
- RequestItemRepositoryIT.testDeleteBitstreamRemovesRequestItem: reattach the
  bitstream/item to the session after the committed REST POST, so CLARIN
  updateItemFilesMetadata (run inside bitstream delete) sees an attached item.

Validation: mvn install -P-assembly BUILD SUCCESS (all modules); VersioningTest
6/6, RequestItemRepositoryIT#testDeleteBitstreamRemovesRequestItem green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@milanmajchrak
milanmajchrak force-pushed the upgrade/vanilla-7.6.7 branch from c257741 to 81dac88 Compare July 21, 2026 13:54
@milanmajchrak
milanmajchrak changed the base branch from dtq-dev to dtq-dev-767-base July 30, 2026 08:20
milanmajchrak and others added 3 commits July 30, 2026 11:59
…hib ITs

- clarin-dspace.cfg: webui.content_disposition_inline allowlist (vanilla flipped
  blocklist->allowlist; text/plain, audio, tiff, csv would force-download).
  video/ogg is listed alongside audio/ogg because bitstream-formats.xml maps the
  .ogg extension to video/ogg, so audio/ogg alone would never match.
- AuthenticationRestControllerIT: 2 new vanilla tests now use
  setAuthenticationMethodSequence() instead of unreliable setProperty()
- test local.cfg: pin the vanilla allowlist for the test environment, otherwise the
  new CLARIN override reaches the ITs and breaks the upstream
  BitstreamRestControllerIT#checkContentDispositionOfFormats assertion on text/plain
Live rehearsal showed the previous wording ("must stay previewable inline") is
untrue for real file sizes: BitstreamRestController also enforces
webui.content_disposition_threshold, code default 8 MB, so most real audio/video
and large TIFF scans are sent as attachment despite being allowlisted.

Comment only - no value change. Raising the threshold widens the inline-render
surface and is left as a deliberate deployment decision.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
OAI (regression introduced by taking 7.6.7, breaks CLARIN's VLO/OLAC harvesting):
vanilla 7.6.7 added ItemUtils.sanitize() and applies it to every metadata value
before the XOAI serializer escapes it again, so any value containing & < > " '
reaches harvesters double-escaped. Measured live on cmdi and olac:
  REST:  u767xssprobe <script>alert(1)</script> end
  cmdi:  u767xssprobe &amp;lt;script&amp;gt;alert(1)&amp;lt;/script&amp;gt; end
sanitize() now removes characters XML 1.0 forbids instead of escaping, which is
what its own javadoc says it does. Upstream defect - should be reported upstream.

content-disposition: the previous allowlist restored inline for only 14 of the 96
registered formats; 7.6.5 allowed 90 (every image/* was inline unconditionally).
Added the omitted formats LINDAT actually serves - WebVTT subtitles, AIFF/AU/MPEG
audio, JPEG2000/WebP/AVIF/BMP images, Quicktime. Also added the parameterised
"text/plain; charset=utf-8": the allowlist is matched by exact string equality, so
the registry's extension-less "License" format could never match a bare
"text/plain" token and license bitstreams always downloaded.
Still deliberately absent: HTML/XML/TEI/SVG/JS. Threshold left at the 8 MB default.

Verified: dspace-oai compiles, checkstyle clean, sanitize() behaviour unit-checked
(metachars pass through, illegal C0/noncharacters removed, tab/LF/CR and CJK kept).
Test config pin keeps the vanilla value, so upstream BitstreamRestControllerIT is
unaffected.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
milanmajchrak and others added 4 commits July 30, 2026 15:26
The previous commit replaced escapeXml10 with a regex to stop the double
escaping, but escapeXml10 was an AggregateTranslator that ALSO ran
UnicodeUnpairedSurrogateRemover, and the regex reproduced only the control-char
part. Consequence: a lone surrogate in metadata (truncated 4-byte character,
mojibake corpus) makes the StAX writer throw "Broken surrogate pair" inside
XOAI.index(), and because that is caught per item the record is silently dropped
from the OAI index. Measured before this fix: 2048 code units broke indexing,
against 0 for vanilla 7.6.7.

Verified over the whole BMP: 0 XML-1.0-illegal code units leak, 0 legal ones are
removed, valid surrogate pairs survive, unpaired ones do not, and XML metacharacters
still pass through unescaped.

Known and intentional difference from escapeXml10: C0/DEL controls U+007F-U+009F are
left as-is instead of being turned into &#NNN; entities. They are legal XML 1.0
characters (only XML 1.1 requires escaping them), so well-formedness is unaffected.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
7.6.7 rewrote S3BitStoreService from com.amazonaws (v1) to software.amazon.awssdk
(v2 CRT client). The original plan rejected that and kept v1; the user reversed it:
"if 7.6.7 is on AWS2, go to AWS2". v1 is past AWS end-of-support anyway.

The fork owns three S3 classes vanilla does not know, so this was not a file takeover:

- S3BitStoreService  = vanilla 7.6.7 + exactly three CLARIN items (47 added lines):
  getFile(Bitstream), `protected` client so the subclasses can reuse it, and a
  configurable pathStyleAccessEnabled (vanilla hardcodes forcePathStyle(true) on an
  endpoint override; keeping the property preserves the fork's v1 behaviour and keeps
  bitstore.xml valid). getFile is byte-identical to the fork's own v9 implementation
  (5e930d1) so the branches do not diverge.
- SyncS3BitStoreService     - hand-ported, including the explicit multipart path.
- S3DirectDownloadServiceImpl - presigning moved to S3Presigner, built from the same
  credentials/region/endpoint as the bitstore client.
- S3BitStoreServiceIT       - taken from 7.6.7 wholesale; the fork's copy was
  byte-identical to 7.6.5, so no CLARIN test was lost. LocalStack replaces s3mock,
  which only ever spoke v1.
- ClarinS3BitStoreServiceIT - new. The CLARIN-only paths had no test at all.
- bitstore.xml / assetstore.cfg - the merge had dropped vanilla's four new tuning keys
  (targetThroughputGbps, minPartSizeBytes, maxConcurrency, s3ChecksumAlgorithm).

Two defects the new tests caught, neither visible by reading the code:

1. v1 returned part ETags unquoted, v2 returns them quoted. The multipart checksum
   comparison would have failed on every part, and since that path logs and swallows,
   completeMultipartUpload would never run - a silent upload failure.
2. SyncS3BitStoreService.put() never closed the FileOutputStream on the local
   assetstore copy. Pre-existing, not a port regression, but it leaks a handle per
   stored bitstream and made remove() silently fail to delete the local file.

Evidence (local, this head):
- mvn test-compile -DskipTests: 13/13 modules SUCCESS
- S3BitStoreServiceIT       Tests run: 15, Failures: 0, Errors: 0
- ClarinS3BitStoreServiceIT Tests run: 6,  Failures: 0, Errors: 0
- S3DirectDownloadServiceTest Tests run: 8, Failures: 0, Errors: 0
- checkstyle 0 violations; `git grep com.amazonaws` finds nothing

s3Store is lazy-init and nothing else instantiates it, so springWiringBindsStoreProperties
loads the bean from bitstore.xml on purpose - it is the only check that the blank
maxConcurrency binds to a null Integer and that s3ChecksumAlgorithm binds to the enum.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
**Silent data loss on multipart upload.** `uploadPart` was handed a `BoundedInputStream`
over a `FileInputStream`, which cannot be reset, so the SDK refused to retry a part after
any transient error ("Request cannot be retried, because the request stream could not be
reset"). The v1 SDK passed the file plus an offset and retried freely. The failure then
landed in a catch that logged and swallowed, `completeMultipartUpload` never ran, and
`put()` carried on setting size and checksum - DSpace recorded a bitstream that S3 does
not hold, with no error anywhere.

Now uses the public `AsyncRequestBody.fromFile(FileRequestBodyConfiguration)` with
`position`/`numBytesToRead`, which is file-backed and therefore retryable; this also
removes the hand-rolled `openPart` helper and its executor. A failed upload now aborts the
multipart upload (parts were previously leaked and billed) and throws instead of returning
normally.

**The multipart loop was never executed more than once.** 50 MB part size against a 23-byte
fixture meant one iteration with offset 0, so the offset arithmetic and last-part handling
this port rewrote had no coverage at all. `uploadPartSizeBytes` is now settable and
`syncStoreUploadsInMultipleParts` uploads 5 MB + 4 KB across a real part boundary, verifying
the object byte for byte.

**A tripwire in `amazonClientBuilderBy` left all 21 S3 tests green.** Every test injects a
ready-made client, so `FunctionalUtils.getDefaultOrBuild` never called the supplier and
`endpointOverride`, `forcePathStyle`, `maxConcurrency` and the part sizes never ran - the
fork's headline delta, configurable `pathStyleAccessEnabled`, was verified only at its
getter. `clientBuilderAppliesEndpointAndPathStyle` builds a client through that method and
uses it against LocalStack. Verified it detects breakage: removing `endpointOverride` turns
it red, restoring it turns it green.

**Content-Disposition had two implementations and the newer one was wrong.** It escaped `"`
but not `\`, so a bitstream named `evil\` terminated the quoted string and swallowed
`filename*`; and it used `URLEncoder` directly, so a space arrived as `+`, which RFC 8187
reads literally - re-introducing the bug vanilla had just fixed. Extracted the correct
implementation to `org.dspace.util.ContentDispositionUtils`, now used by both call sites.

**Enabling direct downloads silently disabled inline preview.** The redirect hardcoded
`attachment`, discarding the disposition the allowlist and the 8 MB threshold had just
computed. `generatePresignedUrl` takes the caller's disposition; `BitstreamRestController`
passes the exact header the non-redirect path would have sent.

**Presigner region could diverge from the client's.** It hardcoded `us-east-1` while the
client leaves the region to the default provider chain when no credentials are configured,
so presigned URLs would be signed for the wrong region on an IAM role outside us-east-1. It
now mirrors `init()` branch for branch.

**A 403 was reported as "bucket absent".** `doesBucketExist` treated everything except
`NoSuchBucketException` as absent, so `init()` would try to create a bucket that already
exists; a least-privilege policy denies that and the assetstore comes up dead. Only 404 and
`NoSuchBucketException` now answer false. `doesObjectExist` likewise stops reporting a 403
or a timeout as "object does not exist" with the cause hidden at DEBUG.

**`s3ChecksumAlgorithm` was dead configuration.** `bitstore.xml` wires
`SyncS3BitStoreService`, which overrides `put()`, so the parent's `putObject` - the only
consumer - never ran. `uploadFluently` now applies it. Also `${assetstore.s3.endpoint:}` is
self-defaulting, matching vanilla.

Tests: S3BitStoreServiceIT 15, ClarinS3BitStoreServiceIT 8, S3DirectDownloadServiceTest 11,
ContentDispositionUtilsTest 9 - all 0 failures, checkstyle 0 violations. Maven does not
recompile edited sources reliably here, so every run used
`-Dmaven.compiler.useIncrementalCompilation=false`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`ItemUtils.sanitize()` decides what VLO and OLAC receive, was changed twice in this PR, and
had no test at all - a reviewer called it the highest-risk file in the upgrade for exactly
that reason. `OAIPMHBundleExposureIT` already drove `ItemUtils.retrieveMetadata()`, so the
harness existed; this uses the same serialisation path as
`DSpaceXOAIItemCacheService.put()` and re-parses the result, because writing alone proves
nothing - `U+FFFE` serialises happily and only fails on the harvester's parser.

Four cases: metacharacters round-trip exactly once (the double-escaping 7.6.6 introduced);
control characters and unpaired surrogates are removed rather than throwing inside
`XOAI.index()`, which catches per item and would silently drop the record; supplementary
characters and `U+1FFFE` survive while `U+FFFE` does not; and the output always re-parses.

Verified non-vacuous: making `sanitize()` return its input unchanged turns three of the four
red, with `WritingXmlException` and `SAXParseException` - the exact failure modes the fix
exists to prevent.

Two things the test itself established, both recorded in comments so nobody re-derives them:
`U+0000` never reaches the sanitiser because PostgreSQL cannot store a NUL in a text column,
and DSpace replaces C0 controls with a space before persisting, so the spacing around a
removed control character is the persistence layer's doing and not something this test can
assert on.

Also fixes the import order `ContentDispositionUtils` was inserted at in
`BitstreamByHandleRestController` (checkstyle).

Tests: OAIPMHSanitizeIT 4, OAIPMHBundleExposureIT 3, 0 failures, checkstyle 0 violations.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.