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:
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user