Files
tech-log-frontend/scripts/lib/provider-raw-guardian.ts
T

642 lines
20 KiB
TypeScript

import { createHash } from "node:crypto";
import {
closeSync,
fstatSync,
fsyncSync,
lstatSync,
readlinkSync,
readSync,
type Stats,
writeSync,
createReadStream,
} from "node:fs";
import { link, lstat, unlink } from "node:fs/promises";
import path from "node:path";
import {
decodeProviderGuardianCommit,
decodeProviderGuardianGuard,
decodeProviderGuardianPublish,
encodeProviderGuardianPublished,
encodeProviderGuardianReady,
MAX_PROVIDER_GUARDIAN_FRAME_PAYLOAD_BYTES,
MAX_PROVIDER_GUARDIAN_LEASE_MS,
providerGuardianRawStagingLeaf,
providerGuardianSealedTempLeaf,
type ProviderGuardianGuard,
type ProviderGuardianKind,
} from "./provider-guardian-protocol.ts";
import { cleanupOwnedProviderReport } from "./provider-raw-cleanup.ts";
type OwnedIdentity = Readonly<{ dev: number; ino: number }>;
type BootstrapAuthority = Readonly<{
kind: ProviderGuardianKind;
noncePrefix: string;
rawStagingLeaf: string;
rawStagingPath: string;
rawPath: string;
rawIdentity: OwnedIdentity;
sealedTempLeaf: string;
sealedTempPath: string;
sealedPath: string;
sealedIdentity: OwnedIdentity;
}>;
type BoundPrivateAuthority = Readonly<{
identity: OwnedIdentity;
leaf: string;
noncePrefix: string;
path: string;
stem: string;
}>;
type GuardianTransaction = Readonly<{ guard: ProviderGuardianGuard }>;
const RAW_DIRECTORY_FD = 3;
const EVIDENCE_DIRECTORY_FD = 4;
const RAW_STAGING_FD = 5;
const SEALED_TEMP_FD = 6;
const RAW_DIRECTORY_PATH = `/proc/self/fd/${RAW_DIRECTORY_FD}`;
const EVIDENCE_DIRECTORY_PATH = `/proc/self/fd/${EVIDENCE_DIRECTORY_FD}`;
const RAW_STAGING_FD_PATH = `/proc/self/fd/${RAW_STAGING_FD}`;
const SEALED_TEMP_FD_PATH = `/proc/self/fd/${SEALED_TEMP_FD}`;
const RAW_STAGING_PATTERN =
/^\.(vulnerability-report|provenance-attestation)\.json\.guardian-([0-9a-f]{32})\.raw\.tmp$/u;
const SEALED_TEMP_PATTERN =
/^\.(vulnerability-report|provenance-attestation)\.json\.guardian-([0-9a-f]{32})\.tmp$/u;
let privateFdsClosed = false;
const bootstrap = await initializeBootstrap();
let pending = Buffer.alloc(0);
let expectedBytes: number | undefined;
let state: "starting" | "guarding" | "published" | "commitPending" = "starting";
let transaction: GuardianTransaction | undefined;
let terminal = false;
let deadline: NodeJS.Timeout | undefined;
let operations = Promise.resolve();
const liveness = createReadStream("", { fd: 0, autoClose: false });
liveness.on("data", consumeChunk);
liveness.once("end", () => {
enqueue(async () => {
if (state === "commitPending" && pending.byteLength === 0 && expectedBytes === undefined) {
await succeedOnCommittedEof();
return;
}
if (state === "starting" && pending.byteLength > 0) {
await failClosed(126, "provider guardian frame is truncated");
return;
}
await failClosed(125, "provider guardian liveness EOF");
});
});
liveness.once("error", (error) => {
enqueue(async () => failClosed(125, `provider guardian liveness error: ${error.message}`));
});
async function initializeBootstrap(): Promise<BootstrapAuthority> {
const rawDirectory = path.resolve(process.cwd(), "provider-evidence/untrusted");
const evidenceDirectory = path.resolve(process.cwd(), "provider-evidence");
const failures: Error[] = [];
const rawDirectoryValid = captureInheritedDirectory(
RAW_DIRECTORY_FD,
rawDirectory,
"raw",
failures,
);
const evidenceDirectoryValid = captureInheritedDirectory(
EVIDENCE_DIRECTORY_FD,
evidenceDirectory,
"evidence",
failures,
);
const rawDescriptor = capturePrivateDescriptor(RAW_STAGING_FD, "raw staging", failures);
const sealedDescriptor = capturePrivateDescriptor(SEALED_TEMP_FD, "sealed temp", failures);
const rawAuthority = rawDescriptor && rawDirectoryValid
? capturePrivateAlias({
descriptorMetadata: rawDescriptor,
descriptorTarget: RAW_STAGING_FD_PATH,
expectedDirectory: rawDirectory,
descriptorDirectory: RAW_DIRECTORY_PATH,
grammar: RAW_STAGING_PATTERN,
label: "raw staging",
}, failures)
: undefined;
const sealedAuthority = sealedDescriptor && evidenceDirectoryValid
? capturePrivateAlias({
descriptorMetadata: sealedDescriptor,
descriptorTarget: SEALED_TEMP_FD_PATH,
expectedDirectory: evidenceDirectory,
descriptorDirectory: EVIDENCE_DIRECTORY_PATH,
grammar: SEALED_TEMP_PATTERN,
label: "sealed temp",
}, failures)
: undefined;
if (!rawAuthority || !sealedAuthority) {
return await failBootstrap(rawAuthority, sealedAuthority, failures);
}
try {
if (
rawAuthority.stem !== sealedAuthority.stem ||
rawAuthority.noncePrefix !== sealedAuthority.noncePrefix
) {
throw new TypeError("provider guardian inherited private aliases disagree");
}
const kind = providerKindFromStem(rawAuthority.stem);
const rawLeaf = kind === "vulnerability"
? "vulnerability-report.json"
: "provenance-attestation.json";
return Object.freeze({
kind,
noncePrefix: rawAuthority.noncePrefix,
rawStagingLeaf: rawAuthority.leaf,
rawStagingPath: rawAuthority.path,
rawPath: `${RAW_DIRECTORY_PATH}/${rawLeaf}`,
rawIdentity: rawAuthority.identity,
sealedTempLeaf: sealedAuthority.leaf,
sealedTempPath: sealedAuthority.path,
sealedPath: `${EVIDENCE_DIRECTORY_PATH}/${rawLeaf}`,
sealedIdentity: sealedAuthority.identity,
});
} catch (error) {
failures.push(toError(error));
return await failBootstrap(rawAuthority, sealedAuthority, failures);
}
}
function captureInheritedDirectory(
fd: number,
canonicalPath: string,
label: string,
failures: Error[],
): boolean {
try {
assertInheritedDirectory(fd, canonicalPath, label);
return true;
} catch (error) {
failures.push(toError(error));
return false;
}
}
function capturePrivateDescriptor(
fd: number,
label: string,
failures: Error[],
): Stats | undefined {
try {
return fstatSync(fd);
} catch (error) {
failures.push(new Error(`provider guardian inherited ${label} fd is invalid`, {
cause: error,
}));
return undefined;
}
}
function capturePrivateAlias(
input: Readonly<{
descriptorMetadata: Stats;
descriptorTarget: string;
expectedDirectory: string;
descriptorDirectory: string;
grammar: RegExp;
label: string;
}>,
failures: Error[],
): BoundPrivateAuthority | undefined {
try {
return bindPrivateAlias(input);
} catch (error) {
failures.push(toError(error));
return undefined;
}
}
function bindPrivateAlias(input: Readonly<{
descriptorMetadata: Stats;
descriptorTarget: string;
expectedDirectory: string;
descriptorDirectory: string;
grammar: RegExp;
label: string;
}>): BoundPrivateAuthority {
const descriptorTarget = readlinkSync(input.descriptorTarget);
if (path.dirname(descriptorTarget) !== input.expectedDirectory) {
throw new TypeError(
`provider guardian inherited ${input.label} alias is outside its directory`,
);
}
const leaf = path.basename(descriptorTarget);
const match = input.grammar.exec(leaf);
if (!match) {
throw new TypeError(`provider guardian inherited ${input.label} alias is invalid`);
}
const boundPath = `${input.descriptorDirectory}/${leaf}`;
const pathnameMetadata = lstatSync(boundPath);
assertPrivateMetadata(input.descriptorMetadata, pathnameMetadata, input.label);
return Object.freeze({
identity: Object.freeze({
dev: input.descriptorMetadata.dev,
ino: input.descriptorMetadata.ino,
}),
leaf,
noncePrefix: match[2]!,
path: boundPath,
stem: match[1]!,
});
}
async function failBootstrap(
rawAuthority: BoundPrivateAuthority | undefined,
sealedAuthority: BoundPrivateAuthority | undefined,
failures: Error[],
): Promise<never> {
for (const authority of [rawAuthority, sealedAuthority]) {
if (!authority) continue;
await cleanupOwnedPath(authority.path, authority.identity, failures);
}
closePrivateFds(failures);
writeAggregateDiagnostic("provider guardian bootstrap failed", failures);
process.exit(126);
}
function assertPrivateMetadata(
descriptorMetadata: Stats,
pathnameMetadata: Stats,
label: string,
): void {
if (
!descriptorMetadata.isFile() || !pathnameMetadata.isFile() ||
pathnameMetadata.isSymbolicLink() || descriptorMetadata.dev !== pathnameMetadata.dev ||
descriptorMetadata.ino !== pathnameMetadata.ino || descriptorMetadata.nlink !== 1 ||
pathnameMetadata.nlink !== 1 || (descriptorMetadata.mode & 0o777) !== 0o600 ||
(pathnameMetadata.mode & 0o777) !== 0o600 || descriptorMetadata.size !== 0 ||
pathnameMetadata.size !== 0
) {
throw new TypeError(`provider guardian inherited ${label} identity is invalid`);
}
}
function providerKindFromStem(stem: string): ProviderGuardianKind {
if (stem === "vulnerability-report") return "vulnerability";
if (stem === "provenance-attestation") return "provenance";
throw new TypeError("provider guardian inherited private kind is invalid");
}
function consumeChunk(chunk: Buffer | string): void {
if (terminal) return;
pending = Buffer.concat([pending, Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)]);
if (expectedBytes === undefined && pending.byteLength >= 4) {
expectedBytes = pending.readUInt32BE(0);
if (expectedBytes <= 0 || expectedBytes > MAX_PROVIDER_GUARDIAN_FRAME_PAYLOAD_BYTES) {
enqueue(async () => failClosed(126, "provider guardian frame length is invalid"));
return;
}
}
if (expectedBytes !== undefined && pending.byteLength === expectedBytes + 4) {
const payload = pending.subarray(4);
pending = Buffer.alloc(0);
expectedBytes = undefined;
enqueue(async () => handleFrame(payload));
} else if (expectedBytes !== undefined && pending.byteLength > expectedBytes + 4) {
enqueue(async () => failClosed(126, "provider guardian frame has trailing bytes"));
}
}
function enqueue(operation: () => Promise<void>): void {
operations = operations.then(operation).catch(async (error) => {
await failClosed(126, error instanceof Error ? error.message : String(error));
});
}
async function handleFrame(payload: Buffer): Promise<void> {
if (state === "starting") {
await establishTransaction(payload);
} else if (state === "guarding") {
await publishSealedArtifact(payload);
} else if (state === "published") {
await prepareCommit(payload);
} else {
await failClosed(126, "provider guardian received data after commit");
}
}
async function establishTransaction(payload: Buffer): Promise<void> {
const nowEpochMs = Date.now();
const guard = decodeProviderGuardianGuard(payload, {
nowEpochMs,
maxLeaseMs: MAX_PROVIDER_GUARDIAN_LEASE_MS,
});
if (
guard.kind !== bootstrap.kind ||
guard.nonce.subarray(0, 16).toString("hex") !== bootstrap.noncePrefix ||
providerGuardianRawStagingLeaf(guard.kind, guard.nonce) !== bootstrap.rawStagingLeaf ||
providerGuardianSealedTempLeaf(guard.kind, guard.nonce) !== bootstrap.sealedTempLeaf
) {
throw new TypeError("provider guardian guard does not match inherited private aliases");
}
assertBoundPrivateLeaf(
RAW_STAGING_FD,
bootstrap.rawStagingPath,
bootstrap.rawIdentity,
"raw staging",
);
assertBoundPrivateLeaf(
SEALED_TEMP_FD,
bootstrap.sealedTempPath,
bootstrap.sealedIdentity,
"sealed temp",
);
transaction = Object.freeze({ guard });
await link(bootstrap.rawStagingPath, bootstrap.rawPath);
assertOwnedPathMetadata(bootstrap.rawStagingPath, bootstrap.rawIdentity, 2, 0o600, 0,
"raw staging link");
assertOwnedPathMetadata(bootstrap.rawPath, bootstrap.rawIdentity, 2, 0o600, 0,
"canonical raw link");
await unlink(bootstrap.rawStagingPath);
fsyncSync(RAW_DIRECTORY_FD);
assertOwnedPathMetadata(bootstrap.rawPath, bootstrap.rawIdentity, 1, 0o600, 0,
"canonical raw");
assertBoundPrivateLeaf(
SEALED_TEMP_FD,
bootstrap.sealedTempPath,
bootstrap.sealedIdentity,
"sealed temp",
);
const remainingLeaseMs = guard.deadlineEpochMs - Date.now();
if (remainingLeaseMs <= 0) {
throw new TypeError("provider guardian guard deadline expired during startup");
}
deadline = setTimeout(() => {
enqueue(async () => failClosed(124, "provider guardian lease deadline expired", true));
}, remainingLeaseMs);
writeSync(1, encodeProviderGuardianReady({
nonce: guard.nonce,
rawDev: bootstrap.rawIdentity.dev,
rawIno: bootstrap.rawIdentity.ino,
sealedTempLeaf: bootstrap.sealedTempLeaf,
sealedDev: bootstrap.sealedIdentity.dev,
sealedIno: bootstrap.sealedIdentity.ino,
}));
state = "guarding";
}
function assertBoundPrivateLeaf(
fd: number,
target: string,
identity: OwnedIdentity,
label: string,
): void {
const descriptorMetadata = fstatSync(fd);
const pathnameMetadata = lstatSync(target);
assertOwnedMetadata(descriptorMetadata, identity, 1, 0o600, 0, label);
assertOwnedMetadata(pathnameMetadata, identity, 1, 0o600, 0, label);
if (pathnameMetadata.isSymbolicLink()) {
throw new TypeError(`provider guardian ${label} alias became symbolic`);
}
}
async function publishSealedArtifact(payload: Buffer): Promise<void> {
if (!transaction) {
throw new Error("provider guardian transaction identity is unavailable");
}
const publication = decodeProviderGuardianPublish(payload, transaction.guard.nonce);
if (
publication.sealedDev !== bootstrap.sealedIdentity.dev ||
publication.sealedIno !== bootstrap.sealedIdentity.ino
) {
throw new TypeError("provider guardian publish identity is invalid");
}
assertOwnedMetadata(
fstatSync(SEALED_TEMP_FD),
bootstrap.sealedIdentity,
1,
0o400,
publication.size,
"sealed publish descriptor",
);
const pathnameMetadata = await lstat(bootstrap.sealedTempPath);
assertOwnedMetadata(
pathnameMetadata,
bootstrap.sealedIdentity,
1,
0o400,
publication.size,
"sealed publish pathname",
);
if (pathnameMetadata.isSymbolicLink()) {
throw new TypeError("provider guardian sealed publish pathname became symbolic");
}
const actualSha256 = hashInheritedFile(SEALED_TEMP_FD, publication.size);
if (actualSha256 !== publication.sha256) {
throw new TypeError("provider guardian publish hash is invalid");
}
try {
await lstat(bootstrap.sealedPath);
throw new Error("provider guardian sealed output already exists");
} catch (error) {
if (!hasErrorCode(error, "ENOENT")) throw error;
}
await link(bootstrap.sealedTempPath, bootstrap.sealedPath);
await unlink(bootstrap.sealedTempPath);
fsyncSync(EVIDENCE_DIRECTORY_FD);
const finalMetadata = await lstat(bootstrap.sealedPath);
assertOwnedMetadata(
finalMetadata,
bootstrap.sealedIdentity,
1,
0o400,
publication.size,
"sealed final",
);
if (finalMetadata.isSymbolicLink()) {
throw new TypeError("provider guardian sealed final became symbolic");
}
state = "published";
writeSync(1, encodeProviderGuardianPublished({
nonce: transaction.guard.nonce,
sealedDev: bootstrap.sealedIdentity.dev,
sealedIno: bootstrap.sealedIdentity.ino,
}));
}
function assertOwnedPathMetadata(
target: string,
identity: OwnedIdentity,
expectedLinks: number,
expectedMode: number,
expectedSize: number,
label: string,
): void {
const metadata = lstatSync(target);
assertOwnedMetadata(metadata, identity, expectedLinks, expectedMode, expectedSize, label);
if (metadata.isSymbolicLink()) {
throw new TypeError(`provider guardian ${label} became symbolic`);
}
}
function assertOwnedMetadata(
metadata: Stats,
identity: OwnedIdentity,
expectedLinks: number,
expectedMode: number,
expectedSize: number,
label: string,
): void {
if (
!metadata.isFile() || metadata.dev !== identity.dev || metadata.ino !== identity.ino ||
metadata.nlink !== expectedLinks || (metadata.mode & 0o777) !== expectedMode ||
metadata.size !== expectedSize
) {
throw new TypeError(`provider guardian ${label} metadata is invalid`);
}
}
function hashInheritedFile(fd: number, size: number): string {
const digest = createHash("sha256");
const buffer = Buffer.allocUnsafe(Math.min(65_536, size));
let position = 0;
while (position < size) {
const requested = Math.min(buffer.byteLength, size - position);
const bytesRead = readSync(fd, buffer, 0, requested, position);
if (bytesRead <= 0) throw new Error("provider guardian sealed publish read was truncated");
digest.update(buffer.subarray(0, bytesRead));
position += bytesRead;
}
return digest.digest("hex");
}
async function prepareCommit(payload: Buffer): Promise<void> {
if (!transaction) throw new Error("provider guardian transaction identity is unavailable");
decodeProviderGuardianCommit(payload, transaction.guard.nonce);
const removedRaw = await cleanupOwnedProviderReport({
reportPath: bootstrap.rawPath,
reportDev: bootstrap.rawIdentity.dev,
reportIno: bootstrap.rawIdentity.ino,
});
if (!removedRaw) throw new Error("provider guardian raw output disappeared before commit");
state = "commitPending";
}
async function succeedOnCommittedEof(): Promise<void> {
const closeErrors: Error[] = [];
closePrivateFds(closeErrors);
if (closeErrors.length > 0) {
await failClosed(
126,
"provider guardian private descriptor close failed",
false,
closeErrors,
);
return;
}
terminal = true;
if (deadline) clearTimeout(deadline);
liveness.removeAllListeners();
liveness.destroy();
closeControlInputBestEffort();
process.exit(0);
}
async function failClosed(
exitCode: number,
message: string,
forceSignal = false,
priorErrors: readonly Error[] = [],
): Promise<void> {
if (terminal) return;
terminal = true;
if (deadline) clearTimeout(deadline);
liveness.removeAllListeners();
liveness.destroy();
const failures = [new Error(message), ...priorErrors];
await cleanupOwnedPath(bootstrap.rawStagingPath, bootstrap.rawIdentity, failures);
await cleanupOwnedPath(bootstrap.rawPath, bootstrap.rawIdentity, failures);
await cleanupOwnedPath(bootstrap.sealedTempPath, bootstrap.sealedIdentity, failures);
await cleanupOwnedPath(bootstrap.sealedPath, bootstrap.sealedIdentity, failures);
closePrivateFds(failures);
closeControlInputBestEffort();
writeAggregateDiagnostic("provider guardian failed", failures);
if (forceSignal) {
try {
process.kill(process.pid, "SIGKILL");
} finally {
process.exit(exitCode);
}
}
process.exit(exitCode);
}
async function cleanupOwnedPath(
target: string,
identity: OwnedIdentity,
errors: Error[],
): Promise<void> {
try {
await cleanupOwnedProviderReport({
reportPath: target,
reportDev: identity.dev,
reportIno: identity.ino,
});
} catch (error) {
errors.push(toError(error));
}
}
function closePrivateFds(errors: Error[]): void {
if (privateFdsClosed) return;
privateFdsClosed = true;
for (const fd of [RAW_STAGING_FD, SEALED_TEMP_FD]) {
try {
closeSync(fd);
} catch (error) {
errors.push(toError(error));
}
}
}
function closeControlInputBestEffort(): void {
try {
closeSync(0);
} catch {
// Terminal cleanup and the exit status must not depend on a diagnostic fd.
}
}
function writeAggregateDiagnostic(label: string, failures: readonly Error[]): void {
const aggregate = failures.length > 1
? new AggregateError(failures, label, { cause: failures[0] })
: failures[0];
const detail = aggregate instanceof AggregateError
? aggregate.errors.map((error) => toError(error).message).join("; ")
: aggregate?.message ?? label;
try {
writeSync(2, `${label}: ${detail}\n`);
} catch {
// A closed parent-side pipe must not convert fail-closed termination to exit 0.
}
}
function assertInheritedDirectory(fd: number, canonicalPath: string, label: string): void {
const descriptorMetadata = fstatSync(fd);
const pathMetadata = lstatSync(canonicalPath);
if (
!descriptorMetadata.isDirectory() || !pathMetadata.isDirectory() ||
pathMetadata.isSymbolicLink() || descriptorMetadata.dev !== pathMetadata.dev ||
descriptorMetadata.ino !== pathMetadata.ino
) {
throw new TypeError(`provider guardian inherited ${label} fd is not a directory`);
}
}
function toError(error: unknown): Error {
return error instanceof Error ? error : new Error(String(error));
}
function hasErrorCode(error: unknown, code: string): boolean {
return Boolean(error && typeof error === "object" && "code" in error && error.code === code);
}