|
|
|
@ -420,3 +420,29 @@ the load-bearing mediums were fixed in seven batches, each committed green with
|
|
|
|
(large shared-package refactor; the event-loop-DoS vector is mitigated by the login rate
|
|
|
|
(large shared-package refactor; the event-loop-DoS vector is mitigated by the login rate
|
|
|
|
limiter now); rendered-PDF caching on `/share`; api.ts file split; broad naming-consistency
|
|
|
|
limiter now); rendered-PDF caching on `/share`; api.ts file split; broad naming-consistency
|
|
|
|
renames (`staff`/`user`/`employee`, `at_wall`, schema.sql drift) — too risky pre-go-live.
|
|
|
|
renames (`staff`/`user`/`employee`, `at_wall`, schema.sql drift) — too risky pre-go-live.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## SQL-injection re-audit ✅ CLEAN (2026-07-18)
|
|
|
|
|
|
|
|
The SQL-injection surface (transiently blocked by a safety classifier during the D24 run)
|
|
|
|
|
|
|
|
was re-run as a focused 4-angle workflow (string-interpolation, dynamic identifiers, the
|
|
|
|
|
|
|
|
`?`→`$n` rewriter, the CSV importer) with adversarial verification. **Result: 0 findings.**
|
|
|
|
|
|
|
|
The auditors confirmed clean, angle by angle: every UPDATE `SET` clause is built from a fixed
|
|
|
|
|
|
|
|
source-literal column allowlist with `?`-bound values; every WHERE fragment is a constant
|
|
|
|
|
|
|
|
with bound args; client/ticket search uses `LOWER(col) LIKE LOWER(?)` with the pattern bound;
|
|
|
|
|
|
|
|
all `LIMIT/OFFSET` are integer-clamped bound params and every `ORDER BY` is a static literal;
|
|
|
|
|
|
|
|
D21 field-spec keys are validated by `KEY_RE` and used only as JSON map keys inside a bound
|
|
|
|
|
|
|
|
`field_values`/`secrets_enc` blob (never as SQL identifiers); the CSV importer binds every
|
|
|
|
|
|
|
|
cell value and no CSV header becomes a column name; and `db-pg.ts` `toDollarParams` fails
|
|
|
|
|
|
|
|
closed (a mis-split breaks the query, never injects) since bound values never pass through it.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## D25 — Installable PWA + phone compatibility ✅ (2026-07-18)
|
|
|
|
|
|
|
|
Founder ask: the console should be a PWA and phone-usable. Delivered without a heavy build
|
|
|
|
|
|
|
|
plugin: a hand-written `manifest.webmanifest` (standalone, teal theme, scope `/`) + an icon
|
|
|
|
|
|
|
|
set generated from the HQ mark (`pwa-192/512`, full-bleed `maskable-512`, `apple-touch-icon`,
|
|
|
|
|
|
|
|
`icon.svg`; `sharp` is a build-only devDep); a conservative service worker (`public/sw.js`)
|
|
|
|
|
|
|
|
that makes the app installable and fast on repeat loads but **never** intercepts `/api` or
|
|
|
|
|
|
|
|
`/share` (always live), does network-first navigations with an app-shell fallback, and
|
|
|
|
|
|
|
|
cache-first for hashed assets — registered from `main.tsx` in production builds only. Phone
|
|
|
|
|
|
|
|
polish on top of the existing `<900px` drawer + scrolling tables: safe-area insets
|
|
|
|
|
|
|
|
(`viewport-fit=cover`), 16px inputs (kills iOS focus-zoom), 40px tap targets, bottom-sheet
|
|
|
|
|
|
|
|
dialogs. Also hardened the deploy: `nginx` now sends `X-Forwarded-For $remote_addr` (replace,
|
|
|
|
|
|
|
|
not append) so the D24 per-IP rate limiters can't be bypassed by a spoofed header.
|
|
|
|
|