fix: 2026-06-09 full-codebase audit hardening

Validation: shared NaN-guarded Zod coercion helpers in schemas/common.ts replace
the raw number|string transform idiom across every schema (the root-cause NaN bug
class); emailOrEmpty + lenient isoDateString/timeString.

Security: roles privilege-escalation closed; refresh-token family revocation on
reuse; TOTP uses config params; read endpoints permission-guarded; received-invoices
gross VAT on all paths; orders-pdf custom-items authz.

Concurrency: $queryRaw SELECT...FOR UPDATE locks in ascending-id order (warehouse
confirm/cancel, attendance lockUserRow); uniqueness checks moved into create
transactions (TOCTOU -> 409); deterministic id tiebreak on second-precision
timestamp ordering (plan resolveCell/resolveGrid, warehouse FIFO).

Frontend: Rules-of-Hooks fixed across ~14 pages + PlanCellModal; UTC-date persisted
fields; dashboard invalidation gaps; stale-closure confirm bugs.

Tooling/tests: ESLint flat config (react-hooks/rules-of-hooks = error) + Prettier;
tsconfig.test.json so tsc -b type-checks the tests; removed 3 dead deps; npm audit
fix (8 -> 3). Suite 195 -> 247 (happy-path auth, FIFO oldest-first, flakiness fixes),
isolated on app_test via .env.test with a hard-throw setup guard.

Gates: tsc 0 | build 0 | vitest 247/247 | eslint 0 errors.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
BOHA
2026-06-09 06:45:26 +02:00
parent c454d1a3fc
commit 519edce373
179 changed files with 7179 additions and 2844 deletions

View File

@@ -37,9 +37,12 @@ export default async function totpRoutes(
issuer: companyName,
label: request.authData!.email,
secret,
algorithm: "SHA1",
digits: 6,
period: 30,
// Keep the QR/URI params in lockstep with /enable verification and
// login (all read config.totp.*) so the scanned authenticator produces
// codes that verify under the same algorithm/digits/period.
algorithm: config.totp.algorithm,
digits: config.totp.digits,
period: config.totp.period,
});
return success(reply, {
@@ -84,11 +87,15 @@ export default async function totpRoutes(
}
}
// Verify the new secret with the SAME parameters login verification uses
// (src/utils/totp.ts → OTPAuth.verify reads config.totp.*). Hardcoding
// SHA1/6/30 here would let an enrolled secret verify under different
// params than login → lockout if the config ever diverges.
const totp = new OTPAuthLib.TOTP({
secret: OTPAuthLib.Secret.fromBase32(secret),
algorithm: "SHA1",
digits: 6,
period: 30,
algorithm: config.totp.algorithm,
digits: config.totp.digits,
period: config.totp.period,
});
const delta = totp.validate({ token: code, window: 1 });
@@ -308,6 +315,7 @@ export default async function totpRoutes(
const isMatch = await bcrypt.compare(String(code), backupCodes[i]);
if (isMatch) {
matchIndex = i;
break; // early-exit: stop running bcrypt once a code matches
}
}