Skip to content

fix: create the XXE levels' solution files on startup (#654) - #1000

Open
ismisepaul wants to merge 3 commits into
devfrom
dev#654-xxe-solution-files
Open

ismisepaul wants to merge 3 commits into
devfrom
dev#654-xxe-solution-files

Conversation

@ismisepaul

@ismisepaul ismisepaul commented Oct 25, 2026 •

Copy link
Copy Markdown
Member

⚠️ Manual testing required before merging

The automated test checks that the solution files are written at start-up. Whether the XXE levels can be solved after being opened from the admin pages (the bug in #654) needs a running app.

Please run this branch, follow the steps, and comment on this PR with the result: pass, or the step that failed and what you saw.

Branch: dev#654-xxe-solution-files

Set up. Use a machine where the Shepherd Docker volumes can be thrown away: down -v deletes their data. JDK 17 and Maven 3.9.2+ are needed.

git fetch origin
git checkout 'dev#654-xxe-solution-files'
mvn -Pdocker clean install -DskipTests
docker compose down -v
docker compose up -d --build
  1. Open https://localhost, accept the self-signed certificate warning, log in as admin / password and set a new password when asked.
  2. Admin → Configuration → Cheat Sheet Management → Enable Cheat Sheets for Administrators. With a level open, the Cheat button in the left-hand menu then shows its solution.

Test

  1. Don't use Open All Levels for this test. Go to Admin → Module Management → Open and Close Modules and open only XXE Injection (the lesson) and XXE Injection 1.
  2. Solve both with the solutions from their Cheat buttons. Each must return the key read from the file on the server.
  3. Run docker compose restart web, log in again and solve XXE Injection again. It should still work.

It's broken if: an XXE level says the file wasn't found, or returns no key.


Fixes #654.

What

The XXE lesson and XXE challenge 1 ask players to read a file from the server (briefcase.pulp / json.xml, from fileSystemKeys.properties). Only two paths created those files: setup with "unsafe levels" ticked, and "Open all modules" with unsafe levels enabled. Opening the levels any other way left them unsolvable, because the file wasn't there. That includes Open and Close Modules, Open or Close by Category, and a container recreated after setup.

DatabaseLifecycleListener now creates both files when the application starts. This is done in its own try/catch, so a failure is logged and doesn't affect the database start-up. The existing call sites are unchanged. The levels themselves aren't touched.

Tests

DatabaseLifecycleListenerTest.testContextInitializedCreatesXxeSolutionFiles deletes both files, runs contextInitialized, and checks that each file exists with the solution from fileSystemKeys.properties. It fails on dev (briefcase.pulp was not created) and passes with the fix. An @AfterEach removes the files, because every test in the class that calls contextInitialized now writes them into the working directory.

Every step of test.yml passed locally (lint, build, PGP check, compose build, Tomcat config check, unit tests, integration tests).

🤖 Generated with Claude Code

The XXE lesson and challenge ask players to read a file from the server.
Only setup and "open all modules" with unsafe levels created those
files, so opening the levels from Open/Close Modules or by category left
them unsolvable, and so did a container that was recreated after setup.
Create both files when the application starts, as well as where they
were already created.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ismisepaul ismisepaul added this to the 4.0.0 milestone Oct 25, 2026
Copilot AI balanced review requested due to automatic review settings October 25, 2026 00:10
@ismisepaul ismisepaul moved this from Backlog to Next Release Backlog in Security Shepherd Oct 25, 2026

Copilot AI left a comment

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.

🟢 Approval recommended

The focused change reuses existing helpers, isolates startup error handling, and includes regression coverage with no unresolved findings.

No open issues 🎉

What changed in this PR

Fixes #654 by creating both XXE solution files at application startup, so the levels remain solvable regardless of how they are opened or whether the container is recreated.

Changes:

  • Reuses existing file-creation helpers in the lifecycle listener, with separate error handling from database initialization.
  • Adds regression coverage for both files’ existence and contents, plus cleanup after each test.
File Description
src/​test/​java/​listeners/​DatabaseLifecycleListenerTest.java Verifies both solution files are created correctly and cleans them up after tests.
src/​main/​java/​listeners/​DatabaseLifecycleListener.java Creates both XXE solution files during application startup.

Review effort: Balanced

@ismisepaul ismisepaul added the Needs Manual Testing Must be tested by hand from the PR's branch (steps in the PR) before merging label Oct 25, 2026

@SeanDuggan SeanDuggan left a comment •

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.

1000th PR!

Startup: both XXE solution files (briefcase.pulp, json.xml) are created, and both levels can be solved once opened.

Closed levels: the endpoints refuse with "The Level is not open" and never return the key, even though the files now exist.

Setup, Open All, Open Web, Close All: all behave correctly. Setup and Open All rewrite the files with identical content, Close All re-closes the levels, and Open Web only opens the XXE levels when unsafeLevels=enable.

Failure path: I locked both files and restarted. The error is logged ("Could not create the XXE solution files…") and Tomcat still starts and serves.

Tests: the new unit test passes with the fix and fails without it. The full unit suite ran 295 tests with 0 failures, and Spotless was clean.

Integration tests: 18 of 20 pass. The 2 failures (testLevelValidAnswerXxeInjection in XxeLessonIT and XxeChallenge1IT) are identical on plain upstream dev. They are a Windows path problem in the test code, which builds file://C:... URLs.

Copilot AI balanced review requested due to automatic review settings November 1, 2026 18:51

Copilot AI left a comment

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.

🟡 Changes recommended

A shared try block lets a failure creating briefcase.pulp skip json.xml initialization, leaving XXE Injection 1 unsolvable when opened individually.

1 open issue

Review effort: Balanced

Comment on lines +69 to +74
try {
XxeLesson.createXxeLessonSolutionFile();
XxeChallenge1.createXxeChallenge1SolutionFile();
} catch (RuntimeException e) {
log.error("Could not create the XXE solution files: " + e.getMessage(), e);
}
@ismisepaul ismisepaul mentioned this pull request Nov 1, 2026
15 of 44 tasks

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Bug Levels Needs Manual Testing Must be tested by hand from the PR's branch (steps in the PR) before merging

Projects

Status: Next Release Backlog

Development

Successfully merging this pull request may close these issues.

[BUG] XXE levels don't create solution file when opened

3 participants