fix: harden provider and promotion evidence
This commit is contained in:
@@ -36,6 +36,62 @@ const MAX_MEMBER_PATH_BYTES = 1_024;
|
||||
const TAR_EXECUTABLE = "/usr/bin/tar";
|
||||
const TAR_ENVIRONMENT = Object.freeze({ PATH: "/usr/bin:/bin", LC_ALL: "C", LANG: "C" });
|
||||
|
||||
export type CapturedCandidateArchive = Readonly<{
|
||||
bytes: Buffer;
|
||||
archiveSha256: string;
|
||||
}>;
|
||||
|
||||
export async function captureCiCandidateArchive(input: Readonly<{
|
||||
archivePath: string;
|
||||
expectedSha256: string;
|
||||
}>): Promise<CapturedCandidateArchive> {
|
||||
if (!/^[a-f0-9]{64}$/u.test(input.expectedSha256)) {
|
||||
throw new TypeError("expected candidate archive SHA-256 is invalid");
|
||||
}
|
||||
const absolute = path.resolve(input.archivePath);
|
||||
const before = await lstat(absolute);
|
||||
if (!before.isFile() || before.isSymbolicLink()) {
|
||||
throw new TypeError("candidate archive must be a regular non-symlink file");
|
||||
}
|
||||
if (before.size <= 0 || before.size > MAX_ARCHIVE_BYTES) {
|
||||
throw new RangeError(`candidate archive size is outside 1..${MAX_ARCHIVE_BYTES}`);
|
||||
}
|
||||
const handle = await open(absolute, constants.O_RDONLY | constants.O_NOFOLLOW);
|
||||
let bytes: Buffer;
|
||||
try {
|
||||
assertSameIdentity(before, await handle.stat());
|
||||
bytes = await readCapturedArchive(handle, before.size);
|
||||
assertSameIdentity(before, await handle.stat());
|
||||
} finally {
|
||||
await handle.close();
|
||||
}
|
||||
const archiveSha256 = createHash("sha256").update(bytes).digest("hex");
|
||||
if (archiveSha256 !== input.expectedSha256) {
|
||||
throw new Error("candidate archive SHA-256 mismatch");
|
||||
}
|
||||
return Object.freeze({ bytes, archiveSha256 });
|
||||
}
|
||||
|
||||
export async function withVerifiedCapturedCandidate<T>(input: Readonly<{
|
||||
captured: CapturedCandidateArchive;
|
||||
verify: (view: Readonly<{
|
||||
extractionRoot: string;
|
||||
manifest: ReleaseCandidateManifest;
|
||||
}>) => Promise<T>;
|
||||
}>): Promise<T> {
|
||||
let result: T | undefined;
|
||||
await verifyCapturedCiCandidateArchive(
|
||||
input.captured.bytes,
|
||||
input.captured.archiveSha256,
|
||||
{
|
||||
verifyExtracted: async (extractionRoot, manifest) => {
|
||||
result = await input.verify({ extractionRoot, manifest });
|
||||
},
|
||||
},
|
||||
);
|
||||
return result as T;
|
||||
}
|
||||
|
||||
export async function verifyCiCandidateArchive(
|
||||
input: Readonly<{
|
||||
archivePath: string;
|
||||
|
||||
Reference in New Issue
Block a user