Commit Graph
18 Commits
Author SHA1 Message Date
Keith Solomon 3185bc3602 fix: detect stale CSRF cookie pair and auto-rotate
CI / php (push) Failing after 1m17s
When a browser session predates a server restart with a fresh
secret.key, the old __csrf HMAC cookie is no longer verifiable
under the new secret. Every action POST returns 403 until the user
manually clears cookies, because the existing-pair check on the
front controller was 'is the cookie present?' rather than 'does
the cookie verify under the current secret?'.

The new check at the top of the front controller computes the
expected HMAC of the raw-token cookie under the current secret and
constant-time-compares it to the __csrf cookie. If they don't match,
the controller re-mints a fresh pair on the response, replacing the
browser's stale cookies with a working set. The next click of 'Play
bundled' then succeeds without user intervention.

Test coverage in tests/Integration/StaleCookieRotationTest.php:
  - testStaleCookiesAreReplacedOnNextRequest
  - testValidCookiesAreNotReIssuedOnNextRequest
  - testMissingTokenCookieIsFilledInOnNextRequest
2026-07-26 16:07:51 -05:00
Keith Solomon 2ea8cfd83d fix: CSRF flow ships the raw token, not the HMAC cookie 2026-07-26 11:01:36 -05:00
Keith Solomon 4c2586908f fix: use server-minted matchId in smoke test actions 2026-07-26 03:32:54 -05:00
Keith Solomon d118920739 test: add end-to-end smoke test for the battle interface 2026-07-26 03:25:57 -05:00
Keith Solomon df8d4023ba feat: add four per-verb match action handlers 2026-07-26 01:05:14 -05:00
Keith Solomon b444f98aa0 feat: add match view server-rendered shell 2026-07-26 00:13:40 -05:00
Keith Solomon 51f57d309a feat: render the bundled-scenarios list on the home page 2026-07-26 00:01:00 -05:00
Keith Solomon b8451112d3 feat: add GetBundledScenario handler with fixture validation 2026-07-25 23:47:09 -05:00
Keith Solomon 1f9e76afef feat: add GetBundledScenarios handler with manifest-driven list 2026-07-25 23:37:27 -05:00
Keith Solomon 9314fc5870 feat: mint matchId and turnToken in PostStartMatch 2026-07-25 22:52:34 -05:00
Keith Solomon d15dfc6835 fix: escape scenario id when serialising saved page
PostTeamEditor interpolated the scenario id into a <script> block as
'localStorage.setItem("scenario:<id>", ...)', so an id containing
'</script><script>...</script>' would break out of the JS-string context
and execute in the browser. Use json_encode with JSON_HEX_TAG / HEX_AMP /
HEX_APOS / HEX_QUOT so the id is always a safe JS string literal.

Adds a PostTeamEditorTest case that submits '</script><script>alert(1)</script>'
and asserts the literal '<script>alert(1)</script>' substring is absent
from the response. Updates the existing happy-path and FullFlowTest
assertions to match the new 'scenario:"demo"' (quoted) form.
2026-07-06 23:22:42 -05:00
Keith Solomon 61abb93e59 fix: align upload-asset URL contract end-to-end
- ImageUploadService::store() now returns /assets/uploads/<token>/<file>
  so the URL the upload handler emits matches the new GET route.
- public/index.php registers a dedicated GET /assets/uploads/{userToken}/{filename}
  route; the legacy /assets/{kind}/{filename} is kept as a fallback.
- GetAssets reads the upload token from $request->server['__uploads_token']
  (the front controller threads it that way) instead of the cookies bag.
- Adds GetAssets unit tests for the upload-token happy path and a
  token-mismatch 404, plus a FullFlowTest step that uploads and re-fetches
  the asset through the front controller.
2026-07-06 23:21:33 -05:00
Keith Solomon 5d9af7a4fd test: cover full create-and-save flow end-to-end
Add FullFlowTest, a single end-to-end test that exercises the home page, the team editor POST, the battlefield editor POST, and the start-match POST through the front controller (no php -S).

Wiring fixes in public/index.php (called out in the test brief):

- Honor $_SERVER['__csrf_secret'] when set so the test can supply its own secret; fall back to BATTLEFORGE_SECRET / var/secret.key otherwise.
- Read the raw body from $_SERVER['__raw_body'] when set; fall back to php://input otherwise. PHP CLI's php://input is empty, so an explicit hook is needed for JSON bodies.
- Return the response status from the front controller so the test can assert the HTTP status it set via http_response_code().

Test fixes:

- Drop the static keyword on the runFrontController closure; static forbids $this access and the closure must capture $this to record lastStatus.
- Drop the redundant '?? ''' on the ob_get_clean() result; (string) ob_get_clean() is always a string.
- Set $_SERVER['__raw_body'] alongside $this->rawBody for the JSON requests so the front controller can read the body.
2026-07-06 22:59:18 -05:00
Keith Solomon 2dc7bc1130 feat: add image upload and start-match handlers 2026-07-06 22:24:07 -05:00
Keith Solomon 7cb670b512 feat: add battlefield editor GET and POST handlers 2026-07-06 22:05:47 -05:00
Keith Solomon 12b1547fac feat: add team editor GET and POST handlers 2026-07-06 21:54:59 -05:00
Keith Solomon 3e4184f08d feat: add home page and asset-serving handlers 2026-07-06 19:02:06 -05:00
Keith Solomon a05baeb4f6 chore: track tests/Integration directory with placeholder
The phpunit.xml split into unit + integration testsuites requires the
integration directory to exist on disk, even when empty. The .gitkeep
keeps the directory present in fresh checkouts and on CI; the first real
integration test in a later task will replace the placeholder.
2026-07-06 17:24:41 -05:00