Expose bounded post-build Source Check failures to authors (#641)

This commit is contained in:
Quixos Development Agent 01f26965-84fa-4a79-8069-725682c13065
2026-10-31 14:48:20 +00:00
parent 6f5b5265b6
commit 94ddc8bedf
+15 -5
View File
@@ -1,6 +1,13 @@
import { constants } from "node:fs"; import { constants } from "node:fs";
import { open } from "node:fs/promises"; import { open } from "node:fs/promises";
const admissionFailure = (
code: string,
kind: "file-bytes" | "total-bytes" | "owner-bytes" | "candidates",
measured: number,
limit: number,
) => Object.assign(Error(code), { metadataAdmission: { kind, measured, limit } });
/** Admission estimates, not a hard V8 heap limit. A process/cgroup ceiling is /** Admission estimates, not a hard V8 heap limit. A process/cgroup ceiling is
* still required. Charge input, parse trees and derived indexes before reading * still required. Charge input, parse trees and derived indexes before reading
* or parsing. Each live candidate retains its reservation until explicit close. */ * or parsing. Each live candidate retains its reservation until explicit close. */
@@ -27,7 +34,8 @@ export class CandidateMetadataBudget {
} }
acquire(owner: string) { acquire(owner: string) {
if (!owner || owner.length > 4096) throw Error("INVALID_CANDIDATE_METADATA_OWNER"); if (!owner || owner.length > 4096) throw Error("INVALID_CANDIDATE_METADATA_OWNER");
if (this.scopes >= this.limits.candidates) throw Error("CANDIDATE_METADATA_LIMIT"); if (this.scopes >= this.limits.candidates)
throw admissionFailure("CANDIDATE_METADATA_LIMIT", "candidates", this.scopes + 1, this.limits.candidates);
this.scopes++; this.scopes++;
let bytes = 0, let bytes = 0,
closed = false; closed = false;
@@ -35,8 +43,10 @@ export class CandidateMetadataBudget {
if (closed) throw Error("CANDIDATE_METADATA_CLOSED"); if (closed) throw Error("CANDIDATE_METADATA_CLOSED");
if (!Number.isSafeInteger(amount) || amount < 0) throw Error("INVALID_CANDIDATE_METADATA_CHARGE"); if (!Number.isSafeInteger(amount) || amount < 0) throw Error("INVALID_CANDIDATE_METADATA_CHARGE");
const owned = this.owners.get(owner) ?? 0; const owned = this.owners.get(owner) ?? 0;
if (this.bytes + amount > this.limits.bytes || owned + amount > this.limits.ownerBytes) if (this.bytes + amount > this.limits.bytes)
throw Error("CANDIDATE_METADATA_LIMIT"); throw admissionFailure("CANDIDATE_METADATA_LIMIT", "total-bytes", this.bytes + amount, this.limits.bytes);
if (owned + amount > this.limits.ownerBytes)
throw admissionFailure("CANDIDATE_METADATA_LIMIT", "owner-bytes", owned + amount, this.limits.ownerBytes);
bytes += amount; bytes += amount;
this.bytes += amount; this.bytes += amount;
this.owners.set(owner, owned + amount); this.owners.set(owner, owned + amount);
@@ -116,8 +126,8 @@ export async function readCandidateMetadata(
let releaseInput: (() => void) | undefined; let releaseInput: (() => void) | undefined;
try { try {
const stat = await handle.stat(); const stat = await handle.stat();
if (!stat.isFile() || !Number.isSafeInteger(stat.size) || stat.size > limit) if (!stat.isFile() || !Number.isSafeInteger(stat.size)) throw Error("CANDIDATE_METADATA_FILE_LIMIT");
throw Error("CANDIDATE_METADATA_FILE_LIMIT"); if (stat.size > limit) throw admissionFailure("CANDIDATE_METADATA_FILE_LIMIT", "file-bytes", stat.size, limit);
// Reserve the input buffer and UTF-16 decoding before allocating either. // Reserve the input buffer and UTF-16 decoding before allocating either.
releaseInput = scope.reserve((stat.size + 1) * 3); releaseInput = scope.reserve((stat.size + 1) * 3);
const bytes = Buffer.alloc(stat.size + 1); const bytes = Buffer.alloc(stat.size + 1);