This kit turns privately maintained Spring security backports into repeatable, reviewable inputs. A project definition pins the upstream repository and base tag, declares the private version and tests, and stores one ordered Git patch per atomic change.
The registry contains:
| Project definition | Base | Private version | CVEs |
|---|---|---|---|
spring-framework-6.2.19 |
v6.2.19 |
6.2.19-cve1 |
10 cumulative fixes |
spring-data-jpa-3.5.13 |
3.5.13 |
3.5.13-cve1 |
CVE-2026-47834 |
spring-integration-6.5.10 |
v6.5.10 |
6.5.10-cve1 |
13 cumulative fixes |
spring-security-6.5.11 |
6.5.11 |
6.5.11-cve1 |
4 cumulative fixes |
These are private, source-maintained builds and are not vendor-supported Spring releases.
This registry is still preparing the first Artifactory release, so every
artifact suffix remains cve1 as requested. Earlier public source tags for
Integration and Security are retained unchanged; the expanded source sets use
dated branch/tag names ending in 20260907. If any cve1 Maven artifact was
already uploaded or consumed, stop and change that project to cve2 instead
of overwriting it.
See CVE coverage and artifact mapping for the complete list, upstream fix provenance, and scanner-to-artifact caveats. The latest clean-replay, build, staging, and dependency-resolution results are recorded in the 2026-09-07 release-candidate evidence.
The kit is self-contained and requires no Python, system Maven, system Gradle,
jq, or patch utility. It uses Bash and Git to clone and apply patches. The
Framework, Integration, and Security repositories provide Gradle wrappers;
Spring Data JPA provides a Maven wrapper. IntelliJ can import each generated
repository with its matching wrapper.
Transfer either this Git repository or the generated
spring-patch-kit.bundle file to the empty directory. With the bundle:
git clone spring-patch-kit.bundle spring-patch-kit
cd spring-patch-kit
./patchctl doctor
./patchctl apply all ../patched-spring
./patchctl verify all ../patched-spring
./patchctl test all ../patched-springThe resulting layout is:
empty-directory/
├── spring-patch-kit/
└── patched-spring/
├── spring-framework-upstream/
├── spring-data-jpa-upstream/
├── spring-integration-upstream/
└── spring-security-upstream/
Open a directory under patched-spring directly in IntelliJ. Use the Gradle
wrapper for Framework, Integration, and Security. Import the root pom.xml as
a Maven project for Spring Data JPA; its Maven wrapper is checked in.
The target machine needs network access to GitHub and Gradle/Maven artifact repositories for the initial clone and dependency download. An actually offline installation would also need pinned upstream source bundles and a pre-populated Gradle dependency cache; those large binary inputs are not stored in this source-only kit.
Git for Windows supplies Bash, Git, and the standard command-line utilities
used by patchctl. The script is part of this repository rather than a global
command, so invoke it explicitly with Bash:
cd spring-patch-kit
export PATCH_JAVA_HOME="/c/Program Files/Eclipse Adoptium/jdk-21"
bash ./patchctl doctor
bash ./patchctl apply all ../patched-spring
bash ./patchctl verify all ../patched-spring
bash ./patchctl test all ../patched-springAdjust the Java directory to the installed JDK 21 location. Forward-slash Git
Bash paths such as /c/Program Files/... avoid ambiguity with Windows
backslashes. The repository pins scripts, configurations, documentation, and
mail patches to LF line endings so core.autocrlf does not make them invalid.
If the build machine cannot access public artifact repositories, obtain the URL of an Artifactory virtual Maven repository from the repository administrator. The virtual repository must aggregate or proxy all of:
- Maven Central;
- Spring milestone and snapshot repositories (
repo.spring.io); - the Gradle Plugin Portal Maven repository (
plugins.gradle.org/m2).
A browser URL ending only in /artifactory/ is not sufficient; use the full
repository URL ending in its virtual repository key. A Central-only virtual
repository cannot supply milestone dependencies such as Reactor Netty 5 M3.
Configure the URL in the current Git Bash session, without adding it to this repository:
export PATCH_ARTIFACTORY_MAVEN_URL="https://artifactory.example/artifactory/virtual-repository-key"
export PATCH_JAVA_HOME="/c/JAVA/JDK/OpenJDK21"
bash ./patchctl doctor
bash ./patchctl test all ../patched-springIf authentication is required, prompt for the values so the password or token is not written into shell history:
read -r -p "Artifactory user: " PATCH_ARTIFACTORY_USERNAME
read -r -s -p "Artifactory password/token: " PATCH_ARTIFACTORY_PASSWORD
echo
export PATCH_ARTIFACTORY_USERNAME PATCH_ARTIFACTORY_PASSWORDThe same environment variables can configure Maven on the other host. Use the credential-free template without copying it over an existing user settings file:
export PATCH_ARTIFACTORY_MAVEN_URL="https://artifactory.example/artifactory/virtual-repository-key"
read -r -p "Artifactory user: " PATCH_ARTIFACTORY_USERNAME
read -r -s -p "Artifactory password/token: " PATCH_ARTIFACTORY_PASSWORD
echo
export PATCH_ARTIFACTORY_USERNAME PATCH_ARTIFACTORY_PASSWORD
mvn --settings ./examples/maven-settings-artifactory.xml \
-Dmaven.repo.local=/c/TEMP/empty-maven-repository \
dependency:treeexamples/maven-settings-artifactory.xml mirrors every Maven repository to the
one approved virtual repository. Its <server> id matches the mirror id so
Maven supplies the credentials only to that mirror. If authentication is not
required, leave both credential variables unset and remove the <servers>
block from a local copy. The template configures dependency resolution only.
patchctl uses it for the Spring Data JPA Maven build and uses the separate
Gradle init script for the other Spring builds. Manual release upload remains
manual.
patchctl injects gradle/artifactory-mirror.init.gradle only when the URL is
set. That init script removes public plugin, buildscript, and dependency
repositories for the build invocation and uses only the configured mirror.
Credentials are inherited from the process environment and are never written
to a source repository, patch, generated POM, or log by the kit. Clear them
afterward with:
unset PATCH_ARTIFACTORY_PASSWORD PATCH_ARTIFACTORY_USERNAMEThe Gradle or Maven wrapper distribution ZIP is downloaded before repository
mirroring can take effect. If
services.gradle.org is also blocked and the wrapper is not already cached,
ask the Artifactory administrator for mirrored Gradle 8.14.3 and 8.14.5
distribution ZIPs or have those wrapper distributions pre-populated in the
build account's Gradle cache. Spring Data JPA's wrapper similarly needs the
Maven 3.9.16 distribution from Maven Central once; on a sealed network, have
that distribution pre-populated in the build account's Maven wrapper cache.
Do not put wrapper credentials in this repository.
If Artifactory uses an internal certificate authority, import that CA into the
JDK 21 trust store approved for the build machine. Do not disable TLS certificate
or revocation checks. An HTML file downloaded with a .jar name usually means
the requested repository path returned a login, proxy, or error page rather
than the artifact.
Create and verify the patched repositories from Git Bash before opening IntelliJ:
cd spring-patch-kit
export PATCH_JAVA_HOME="/c/Program Files/Eclipse Adoptium/jdk-21"
bash ./patchctl apply all ../patched-spring
bash ./patchctl verify all ../patched-springThe four source repositories are independent builds. Open each one separately with File > Open (using separate IntelliJ windows):
patched-spring/spring-framework-upstream
patched-spring/spring-data-jpa-upstream
patched-spring/spring-integration-upstream
patched-spring/spring-security-upstream
For Framework, Integration, and Security, configure Settings > Build, Execution, Deployment > Build Tools > Gradle as follows:
- Build and run using:
Gradle - Run tests using:
Gradle - Gradle distribution:
gradle-wrapper.properties/ Wrapper - Gradle JVM: the installed JDK 21
Reload the Gradle project after changing these settings. IntelliJ obtains the
Gradle version from the repository's wrapper; no system Gradle installation or
Gradle PATH entry is needed.
For Spring Data JPA, open its root pom.xml, choose Load Maven Project, and
set Build Tools > Maven > Runner > JRE to the same JDK 21. Use the detected
Maven wrapper when IntelliJ offers that option. No system Maven installation is
needed.
You may navigate, edit, and debug focused tests from IntelliJ. The authoritative security and release build remains the clean Git Bash workflow:
bash ./patchctl test all ../patched-spring
bash ./patchctl build all ../patched-spring
bash ./patchctl publish-local all ../patched-springDo not use IntelliJ's generic Build Project output as the release artifact
source and do not upload to Artifactory from IntelliJ. Use the Gradle-wrapper
artifacts produced by patchctl, followed by the documented manual Artifactory
upload and clean-consumer validation.
Choose a new workspace. Existing target repositories are never overwritten:
./patchctl apply all /path/to/replayed-spring
./patchctl verify all /path/to/replayed-springRun all focused regression tests. On macOS, Java 21 is selected for these commands only; the system Java default is unchanged:
./patchctl test all /path/to/replayed-springOn other systems, or to select a specific JDK explicitly:
PATCH_JAVA_HOME=/path/to/jdk-21 \
./patchctl test all /path/to/replayed-springBuild or publish all complete custom artifact sets to Maven Local:
./patchctl build all /path/to/replayed-spring
./patchctl publish-local all /path/to/replayed-springThe complete-family publication above remains the lowest-risk option. When the environment requires uploading only affected modules, prepare a separate Maven-only repository containing exactly the registered patched artifacts:
bash ./patchctl stage-selective all /path/to/replayed-spring \
/path/to/new/selective-maven-uploadThe output directory must not exist. The command generates and validates:
org.springframework:{spring-beans,spring-context-support,spring-expression,spring-web,spring-webflux,spring-webmvc}:6.2.19-cve1
org.springframework.data:{spring-data-jpa-parent,spring-data-jpa,spring-data-envers}:3.5.13-cve1
org.springframework.integration:{spring-integration-core,spring-integration-file,spring-integration-http,spring-integration-ip,spring-integration-jdbc,spring-integration-scripting,spring-integration-smb,spring-integration-syslog,spring-integration-zip}:6.5.10-cve1
org.springframework.security:{spring-security-config,spring-security-core,spring-security-crypto,spring-security-ldap,spring-security-web}:6.5.11-cve1
Each coordinate includes its main, source, and Javadoc JAR plus a POM. The
selective POM transformer preserves dependencies between patched artifacts
(spring-webmvc still requires patched spring-web) and rewrites unaffected
Spring Framework dependencies to 6.2.19 and unaffected Spring Integration
dependencies to 6.5.10. It then rejects any remaining custom-version
dependency on an artifact that is not staged.
The staging repository deliberately omits Gradle module metadata so Maven and
Gradle consumers use the validated POM dependency graph. SHA256SUMS covers
every staged artifact. Retain that manifest as release evidence rather than
uploading it as a Maven coordinate.
Selective consumers must manage every staged module directly; do not import a custom Framework, Integration, or Security BOM because the remaining custom-version modules are intentionally absent. Start from the checked-in selective Maven consumer example and confirm the resolved dependency tree from an empty local repository.
This mode creates a deliberately mixed release: patched artifacts plus official base-version dependencies. Run application smoke and integration tests before approval. Do not combine the selective and complete publication modes under the same immutable private version after either one has been released.
Keep the patch kit and the four patched Spring source repositories separate. Create these public forks once through GitHub's web interface:
spring-projects/spring-frameworktowoozer/spring-frameworkspring-projects/spring-data-jpatowoozer/spring-data-jpaspring-projects/spring-integrationtowoozer/spring-integrationspring-projects/spring-securitytowoozer/spring-security
Do not enter a GitHub token in this repository or in a command URL. Preview the publication from a verified workspace first:
bash ./patchctl publish-source all /path/to/replayed-springThe preview shows the exact commit, branch, immutable private-version tag, and public fork URL for each project. After reviewing it, run the push yourself in Git Bash so GitHub authentication remains in Git Credential Manager or the browser:
bash ./patchctl publish-source all /path/to/replayed-spring --pushFor each repository, --push adds a dedicated patch-publish remote if needed
and atomically pushes only the registered backport branch and private-version
tag. It does not push the default branch, other branches, build output, Gradle
caches, or the patch-kit repository. It never force-pushes. The existing
official Spring origin remote is left unchanged.
Fork creation remains manual because automating it would require GitHub account
credentials. The public HTTPS fork URLs in project.conf contain no secrets.
If one project in an all operation fails, investigate and rerun it; Git cannot
make a single atomic transaction across independent repositories.
The patch-kit repository shows the portable mail patches; each source fork
shows the same changes as normal commits on its cumulative backport branch.
Inspect a CVE locally with git log BASE_REF..HEAD and git show COMMIT, or use
the corresponding GitHub branch after publication. patchctl does not create
GitHub pull requests or merge requests. Those are optional review objects, not
part of the reproducible release, and creating them would require authenticated
GitHub tooling. The immutable source tag, signed CVE commits, checked-in mail
patches, and release evidence remain available even without a pull request.
This environment requires a manual Artifactory upload. patchctl deliberately
does not connect or authenticate to Artifactory. Choose one immutable release
mode before uploading:
publish-localprepares the complete Maven publication families under the current user's local Maven repository;stage-selectiveprepares only the registered affected Maven modules in a new, checksummed upload directory.
The complete publications are stored under:
~/.m2/repository/org/springframework/
For complete mode, upload all private version sets to the approved Artifactory Maven repository while preserving their Maven coordinates and directory layout:
org.springframework:*:6.2.19-cve1
org.springframework.data:*:3.5.13-cve1
org.springframework.integration:*:6.5.10-cve1
org.springframework.security:*:6.5.11-cve1
Upload the generated POMs and required JARs (including BOM POMs and any source
or Javadoc artifacts required by local policy). Do not upload Maven-local
bookkeeping files such as _remote.repositories or maven-metadata-local.xml;
Artifactory manages its own repository metadata.
For selective mode, upload only the org/ repository paths inside the new
staging directory. Preserve every POM and JAR path exactly. Retain
SHA256SUMS with the release record and verify Artifactory's stored checksums
against it. Do not upload a custom BOM: consumers manage the staged affected
coordinates directly using the example POM.
Enter Artifactory credentials only in the approved Artifactory interface. Do not place its URL, username, password, access token, or API key in this repository, shell history, patch files, or build logs.
After the manual upload, validate from a clean application environment that has no matching artifacts in Maven Local. Resolve all private versions from Artifactory, inspect the dependency graph, and run the application smoke and integration tests. Record the uploader, date, target repository, artifact checksums, and validation result in the CVE release evidence.
Commands can target one project instead of all:
./patchctl list
./patchctl test spring-framework-6.2.19 /path/to/replayed-spring
./patchctl stage-selective spring-framework-6.2.19 \
/path/to/replayed-spring /path/to/new/framework-selective-uploadFollow the complete new CVE backport runbook. It includes the release checklist, immutable versioning rules, Windows Git Bash commands, clean-room replay, and evidence requirements. Start an audit record from the CVE record template.
- Start from the pinned base tag in a clean clone.
- Create or reuse the project's backport branch.
- Add one production change and its tests in one commit per CVE. Include the CVE identifier in each commit subject.
- Keep private version and documentation changes in separate commits.
- Run the narrow module tests and then the project build.
- Capture the linear commit series into a new directory:
./patchctl capture spring-framework-6.2.19 \
/path/to/clean/source-repository \
/path/to/new-patch-exportThe capture command refuses to replace an existing directory. Review the
exported patches, then replace the corresponding projects/<id>/patches
directory through your normal code-review process.
For a new Spring line, copy an existing project.conf, change its pinned base,
private version, branch, CVE list, build tool, and test arguments, and add its
patch series. If its Gradle project names differ from their source directory names,
also define a positional SELECTIVE_DIRECTORIES array alongside
SELECTIVE_MODULES.
A production pipeline should run these stages:
apply -> verify -> test -> build -> publish-local -> manual Artifactory upload
-> clean dependency resolution -> application smoke test
Use immutable versions such as 6.2.19-cve1; never publish private artifacts
under Spring's official version numbers. Publish to an internal Nexus or
Artifactory repository, generate checksums and an SBOM, and retain:
- upstream advisory and fix references;
- the atomic patch commits;
- test and build logs;
- artifact checksums and provenance;
- VEX statements or scanner exceptions for version-only findings.
Credentials and internal repository URLs intentionally do not live in this kit. In this environment, a human supplies them only through the approved manual Artifactory upload process.