[FIX] Uge session of debugging and code coverage.

This commit is contained in:
NADAL Jean-Baptiste
2026-09-14 17:22:18 +02:00
parent e79c865f8c
commit a38fc0bfa9
35 changed files with 2315 additions and 1092 deletions
+120 -7
View File
@@ -20,7 +20,52 @@ site is archived in `legacy/` and must not be modified.
- Node.js 26 / npm 11
- Svelte 5, SvelteKit 2, `@sveltejs/adapter-static`, Vite 5, Tailwind 3
- PHP 8.5 (CLI built-in server in development)
- Dependencies: `axios`, `pdfjs-dist`, `lucide-svelte` (front); `setasign/fpdi`, `setasign/fpdf`, `tecnickcom/tcpdf` (back)
## Dependencies
### Frontend (`partitions/package.json`)
Runtime:
| Package | Version |
|---|---|
| `axios` | ^1.6.0 |
| `pdfjs-dist` | ^4.0.379 |
Dev:
| Package | Version |
|---|---|
| `svelte` | ^5.0.0 |
| `@sveltejs/kit` | ^2.0.0 |
| `@sveltejs/adapter-static` | ^3.0.0 |
| `@sveltejs/vite-plugin-svelte` | ^4.0.0 |
| `vite` | ^5.0.0 |
| `typescript` | ^5.0.0 |
| `svelte-check` | ^4.0.0 |
| `tailwindcss` | ^3.4.0 |
| `autoprefixer` | ^10.4.16 |
| `postcss` | ^8.4.32 |
Install: `cd partitions && npm install`.
### Backend (`api/composer.json`)
| Package | Version |
|---|---|
| `setasign/fpdi` | ^2.6 (import de pages PDF) |
| `setasign/fpdf` | ^1.8 (base PDF) |
| `tecnickcom/tcpdf` | ^6.10 |
Install: `cd api && php composer.phar install` (le dossier `vendor/` est déjà présent).
PHP extensions requises (via `composer.lock`) : `gd`, `zlib`, `curl`. Déjà fournies
ou activées avec les paquets Arch `php` + `php-gd`.
### Tooling de développement
- `xdebug` (paquet Arch `extra/xdebug`) — optionnel, utilisé pour mesurer la
couverture de code du backend (`xdebug.mode=coverage`).
> PHP 8.5 note: nullable parameters MUST use explicit `?type` syntax
> (e.g. `?string $x = null`). Implicit nullable is deprecated and pollutes API output.
@@ -46,12 +91,14 @@ cd partitions
npm install
npm run dev # http://localhost:5173
npm run build # outputs to partitions/build
npm run check # svelte-check (0 errors / 0 warnings expected)
npm run preview
```
- `frontend` is a symlink to `partitions/build` (the deployed static site).
- `VITE_API_URL` (build/dev env) selects the API base URL; falls back to
`localhost:8000` (dev) or the production URL.
- No ESLint / test framework is configured for the frontend.
- `VITE_API_URL` is defined but currently unused (base URL is chosen from `import.meta.env.DEV`).
### Deploy
@@ -65,6 +112,17 @@ npm run preview
## Testing
### Run everything (single command)
```bash
./test.sh
```
Runs, in order: backend tests (`api/tests.php`), backend code coverage (Xdebug,
if available), frontend `npm run check` (svelte-check), and the frontend E2E smoke
test. It starts the API and dev servers if needed and stops them afterwards.
"Test the project" means running `./test.sh`.
### PHP API tests
```bash
@@ -80,6 +138,49 @@ php tests.php
validation, INI injection, security headers.
- `tests.php` is kept for development only and MUST NOT be shipped in the deploy zip.
### Code coverage (optional, requires Xdebug)
The test suite covers ~96% of `api/` non-vendor code (`Auth.php` and `router.php`
at 100%, `ScoreScanner.php` +99%). The remaining uncovered lines are defensive
I/O-failure branches (rate-limiter `flock`/`fopen` failures, missing
`SCORES_PATH`, `OOM`/partial PHP upload errors, `createScore` rollback) that
need OS-level fault injection to trigger.
```bash
cd api
rm -rf /tmp/ohmj_cov
# Terminal 1: instrumented dev server
php -d zend_extension=xdebug -d xdebug.mode=coverage \
-d auto_prepend_file="$PWD/coverage-prepend.php" \
-c php-upload.ini -S localhost:8000 router.php
# Terminal 2: run tests through the instrumented server
php -d zend_extension=xdebug -d xdebug.mode=coverage \
-d auto_prepend_file="$PWD/coverage-prepend.php" tests.php
# Then, from the repo root:
SHOW_MISSING=1 php api/coverage-report.php \
api/index.php api/router.php api/lib/Auth.php api/lib/ScoreScanner.php
```
`coverage-prepend.php` / `coverage-report.php` are dev-only and excluded from the
deploy archive.
### Frontend E2E smoke test (CDP, no dependency)
Requires the API (`:8000`) and the frontend dev server (`:5173`) to be running.
```bash
cd partitions
npm run test:e2e
```
`tests/e2e-smoke.mjs` drives the system `chromium` through the DevTools Protocol:
it logs in via the API, injects the session, then visits `/scores`, a score
detail, the PDF viewer and `/admin`. It asserts that every API call returns
`< 400`, that the SPA stays on each route, and that the PDF is fetched with an
`Authorization` header (no token in the URL).
Env overrides: `API_URL`, `FRONT_URL`, `CHROME_PATH`, `LOGIN_USER`, `LOGIN_PASS`.
## Authentication
- Login: `POST /login` with `{"username":"admin","password":"password"}` (development).
@@ -137,6 +238,9 @@ Admin only (`role === 'admin'`):
## Data Model (files)
> Full filename/directory convention: see [`NAMING.md`](NAMING.md). It is
> normalized and MUST NOT be changed casually (backend + frontend depend on it).
```
legacy/Scores/<NUM>/<PIECE>/<INSTRUMENT>/<VERSION>/<PARTIE>.pdf
legacy/Scores/<NUM>/score.ini
@@ -218,11 +322,20 @@ curl -s -I http://localhost:8000/login -X POST \
-d '{"username":"admin","password":"password"}'
```
> Known security findings from the latest review (not yet fixed): static file
> passthrough in `api/router.php` exposes `.env`/`users.json`; `deleteScoreFile`
> lacks path containment; `api/.env` is tracked by git; JWT is standard base64
> (not base64url) and expiry is optional; stored XSS via the `ressource` link.
> See the project issue tracker / recent review notes.
> Security fixes applied recently: `api/router.php` no longer serves files
> statically (`.env`/`config/users.json` are not reachable); `deleteScoreFile`
> enforces `realpath()` containment and only deletes `.pdf`; `api/.env` and
> `partitions/.env` are untracked/gitignored (rotate the secret since it remains
> in git history); JWT uses base64url, requires `exp` and validates `alg`;
> credentials errors are generic; the `ressource` link rejects non-http(s) URLs.
>
> Known findings still open: default `admin`/`password` credentials; login rate
> limiter is race-prone (no `flock`) and `/api/login` bypasses it; CORS falls
> back to `*` for unknown origins; `GET /admin/scores/:id` passes extra
> arguments dropped by `ScoreScanner::updateScore` (so `ressource` is not
> persisted); watermarking only keeps page 1 of multi-page PDFs; `createScore`
> and `uploadPdf` do not validate path segments; `updateScore`/`updatePieces`
> do not sanitize INI values.
## Temporary Files