Repository navigation
fix: create the XXE levels' solution files on startup (#654) - #1000
ismisepaul wants to merge 3 commits into
Conversation
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>
There was a problem hiding this comment.
🟢 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
There was a problem hiding this comment.
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.
| try { | ||
| XxeLesson.createXxeLessonSolutionFile(); | ||
| XxeChallenge1.createXxeChallenge1SolutionFile(); | ||
| } catch (RuntimeException e) { | ||
| log.error("Could not create the XXE solution files: " + e.getMessage(), e); | ||
| } |

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-filesSet up. Use a machine where the Shepherd Docker volumes can be thrown away:
down -vdeletes 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 --buildadmin/passwordand set a new password when asked.Test
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, fromfileSystemKeys.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.DatabaseLifecycleListenernow 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.testContextInitializedCreatesXxeSolutionFilesdeletes both files, runscontextInitialized, and checks that each file exists with the solution fromfileSystemKeys.properties. It fails ondev(briefcase.pulp was not created) and passes with the fix. An@AfterEachremoves the files, because every test in the class that callscontextInitializednow writes them into the working directory.Every step of
test.ymlpassed locally (lint, build, PGP check, compose build, Tomcat config check, unit tests, integration tests).🤖 Generated with Claude Code