|
|
@@ -766,12 +766,35 @@ note. Do not delete entries — they're history.
|
|
|
|
|
|
### R01-N22 — Migrator runs at request time, swallows stale cache risk
|
|
|
- **Severity**: MEDIUM (operational hazard).
|
|
|
-- **Status**: open.
|
|
|
-- **Where**: `public/index.php` lines 73-85; `src/Db/Migrator.php`.
|
|
|
-- **What**: Migrations run on every request as `pdo->exec($sql)` (multi-
|
|
|
+- **Status**: fixed-in-`43b2fc9` — went with both bullet points from
|
|
|
+ the audit's Suggested-fix list. Migrations are now applied by the
|
|
|
+ new `bin/migrate.php` CLI, called from `bin/docker-entrypoint.sh`
|
|
|
+ before `exec apache2-foreground`, so a failed migration aborts
|
|
|
+ container start (`docker logs` carries the stderr) instead of
|
|
|
+ leaking into the request path. The web request path only calls
|
|
|
+ the new pure-read `Migrator::pendingFiles()` (which diffs files on
|
|
|
+ disk against `schema_version` without applying SQL) and emits
|
|
|
+ `503 Service Unavailable` + `Retry-After: 30` + a structured
|
|
|
+ `error_log` line when anything is pending. Bare-metal deploys that
|
|
|
+ skip Docker run the CLI manually as part of the release procedure;
|
|
|
+ `doc/admin-manual.md` §5.5 documents both. The 503 path is
|
|
|
+ intentionally loud — the alternative (auto-migrating in a request
|
|
|
+ that may then crash mid-DDL) is exactly the partial-schema failure
|
|
|
+ mode the finding flagged. New `tests/Db/MigratorTest.php` (6
|
|
|
+ cases) pins `pendingFiles()` against the real `migrations/`
|
|
|
+ folder, virgin-DB / post-migrate / drift-after-deploy paths, the
|
|
|
+ ignore-non-`NNN_*.sql` rule, and the `schema_version`-creation
|
|
|
+ side-effect. Tests: 311 / 823 (was 305 / 814).
|
|
|
+- **Where**:
|
|
|
+ - `public/index.php` lines 75-104 (check + 503).
|
|
|
+ - `src/Db/Migrator.php` — `pendingFiles()` added; `migrate()`
|
|
|
+ unchanged.
|
|
|
+ - `bin/migrate.php` (new), `bin/docker-entrypoint.sh` (new).
|
|
|
+ - `Dockerfile` — `ENTRYPOINT` + `CMD`.
|
|
|
+- **What**: Migrations ran on every request as `pdo->exec($sql)` (multi-
|
|
|
statement). On a fresh deploy, the *first* request after the new code
|
|
|
- ships does the migration; if that request crashes mid-migration (timeout,
|
|
|
- OOM), partial state may persist. SQLite + the migrator's `BEGIN..COMMIT`
|
|
|
+ ships did the migration; if that request crashed mid-migration (timeout,
|
|
|
+ OOM), partial state could persist. SQLite + the migrator's `BEGIN..COMMIT`
|
|
|
protects most cases, but `ALTER TABLE … ADD COLUMN` is a DDL that's
|
|
|
conditionally inside a tx — see SQLite caveats.
|
|
|
- **Why it matters**: A partially-applied migration leaves the schema in a
|