Commit Graph
2 Commits
Author SHA1 Message Date
ckochandClaude Opus 5 130921cf89 Add backend test suite covering auth, ownership and the query surface
The API had no tests. These run against the real application through
WebApplicationFactory — same pipeline, same Identity configuration, same JWT
validation — with only the SQLite file, upload folder and signing key
swapped, so a passing test says something about what ships.

The ownership tests pin the rule the old PHP API got wrong: a second user
sees an empty library, gets 404 (not 403, which would confirm the id exists)
when reading, updating or deleting someone else's game, and cannot reassign
ownership by putting ownerId or userId in the request body.

Also covered: the password policy, that login is indistinguishable between a
wrong password and an absent user, that the availability endpoint leaks no
row data, that a token signed with an untrusted key is refused, that the
sort parameter is allow-listed rather than interpolated, and that uploads
must decode as an image regardless of extension or content type.

36 tests, ~1s. Program is now declared public partial so the test host can
reach it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-04 12:16:33 -04:00
ckochandClaude Opus 5 2a7d90b2d5 Rebuild on Angular 22 + ASP.NET Core 10, containerised
The 2018 stack (Angular 5.2 / CLI 1.7, PHP, MySQL) had not been touched since
July 2018. Rebuilt rather than upgraded in place: the frontend was 17 major
versions behind, and of ~16,700 lines of PHP only ~150 were application logic —
the rest was four near-identical vendored copies of php-crud-api plus
class.upload.php.

Backend — ASP.NET Core 10, EF Core, SQLite
  * ASP.NET Core Identity (PBKDF2) + JWT bearer auth
  * Clean REST API replacing php-crud-api's filter[]/transform query syntax
  * Box art uploads re-encoded to WebP via SkiaSharp
  * Imports the 105 games recovered from the 2018 dump on first run

Frontend — Angular 22, zoneless, signals, Material 22
  * Standalone components, lazy routes, functional guards and interceptor
  * Vitest replaces Karma/Jasmine; fonts and icons bundled, no CDN calls
  * No provideAnimations: @angular/animations is deprecated in v22 and
    Material no longer depends on it (pinned by a test)

Docker
  * Multi-stage builds for both services, non-root at runtime
  * nginx serves the SPA and reverse-proxies the API, so everything is
    same-origin; one volume holds the database, uploads and DP keys

Security issues in the old code, not carried across:
  * Two endpoints exposed unauthenticated CRUD over every table
  * The client chose whose rows to read (filter[]=userId,eq,N); ownership now
    comes from the JWT subject server-side
  * Login was hardcoded to a single username
  * crypt() with one global salt, silently truncating passwords to 8 chars
  * JWT secret was the literal string "testing", tokens never expired
  * Token travelled in the query string rather than a header
  * Uploads were anonymous with the path built from the client filename
  * Access-Control-Allow-Origin: *

The live MySQL password committed in 2018 remains in git history and must be
rotated independently of this change.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-03 18:46:33 -04:00