fix: logic-audit P0/P1 wave — CORS PUT, chunked cap, presign checksums, logout await, refresh rotation race

From the logic audit (HEAD 04f5f7e):
- P0 CORS: enableCors methods lacked PUT — browsers could not chunk-upload
  at all (cross-origin preflight refused)
- P0 chunked: validateUpload enforced the 2 GiB whole-body cap on chunked
  completes, so every >2 GiB chunked upload failed after full upload
- P1 presignPut: the SDK's flexible-checksum default added an
  x-amz-checksum-crc32 header browsers cannot reproduce; S3Client now uses
  requestChecksumCalculation WHEN_REQUIRED
- P1 refresh rotation race: concurrent refreshes both succeeded (consume
  ignored rowCount) forking the token family; consume now returns whether
  it won and the loser revokes the chain
- P2 logout: awaits the revocation and ignores missing/forged bearers
honestly
This commit is contained in:
glass beadian 2026-10-18 10:18:02 +00:00
parent e475402b4d
commit b24ebc5862
19 changed files with 1636 additions and 101 deletions

View file

@ -0,0 +1,37 @@
import { HttpException, HttpStatus } from "@nestjs/common";
/** Carries the plaintext size for the controller’s unsatisfied Content-Range header. */
export class FileRangeNotSatisfiableException extends HttpException {
constructor(readonly total: number) {
super("requested range is not satisfiable", HttpStatus.REQUESTED_RANGE_NOT_SATISFIABLE);
}
}
/** Inclusive HTTP byte bounds, clipped to the plaintext EOF. */
export function playbackRange(total: number, start: number, end: number): { start: number; end: number } {
if (!Number.isSafeInteger(start) || !Number.isSafeInteger(end) || start < 0 || start >= total || end < start) {
throw new FileRangeNotSatisfiableException(total);
}
return { start, end: Math.min(end, total - 1) };
}
/** Query bounds take precedence; also accept the single Range browsers send. */
export function requestedPlaybackRange(total: number, start: unknown, end: unknown, range?: string): { start: number; end: number } {
const fail = (): never => { throw new FileRangeNotSatisfiableException(total); };
const integer = (value: unknown): number => {
if (typeof value !== "string" || !/^\d+$/.test(value) || !Number.isSafeInteger(Number(value))) return fail();
return Number(value);
};
if (start !== undefined || end !== undefined) {
return playbackRange(total, start === undefined ? 0 : integer(start), end === undefined ? total - 1 : integer(end));
}
if (range === undefined) return playbackRange(total, 0, total - 1);
const match = /^bytes=(\d*)-(\d*)$/.exec(range);
if (match === null || (match[1] === "" && match[2] === "")) return fail();
if (match[1] === "") {
const suffix = integer(match[2]);
if (suffix === 0) return fail();
return playbackRange(total, Math.max(0, total - suffix), total - 1);
}
return playbackRange(total, integer(match[1]), match[2] === "" ? total - 1 : integer(match[2]));
}