#!/usr/bin/env node
/**
 * Emit dist/eval/attestation-trust-root.js.
 *
 * F2 / prompt-qwvu.2 fix: pin the sha256 of the checked-in
 * `docs/security/trusted-signers.json` and `docs/security/revoked-signers.json`
 * into a generated file under `dist/eval/` at build time. Because `dist/` is
 * gitignored and regenerated from source by `npm run build`, this binds the
 * verifier's notion of "the trust roots I was compiled against" to whatever
 * the source tree contained at the moment of the build. The verifier
 * (`scripts/eval/verify-trace.mjs`) imports this pin and refuses to proceed
 * unless the on-disk registry files hash to the pinned values.
 *
 * An attacker who only has workspace write at run time cannot bypass this
 * because modifying the on-disk file shifts its sha256 and the verifier
 * rejects at load. An attacker who has workspace write at BUILD time could
 * of course repin, but that is out of scope: the trust-root pin is meant to
 * stop the meta-flow (which runs *after* build) from appending its own
 * signer to the registry between CI/install and verification.
 */

import { createHash } from 'node:crypto';
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
import { dirname, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';

const __dirname = dirname(fileURLToPath(import.meta.url));
const REPO_ROOT = resolve(__dirname, '..', '..');
const TRUSTED_PATH = resolve(REPO_ROOT, 'docs', 'security', 'trusted-signers.json');
const REVOKED_PATH = resolve(REPO_ROOT, 'docs', 'security', 'revoked-signers.json');
const OUT_DIR = resolve(REPO_ROOT, 'dist', 'eval');
const OUT_PATH = resolve(OUT_DIR, 'attestation-trust-root.js');

function sha256Hex(buf) {
  return createHash('sha256').update(buf).digest('hex');
}

function requireFile(path, label) {
  if (!existsSync(path)) {
    throw new Error(`${label} is missing at build time: ${path}`);
  }
  return readFileSync(path);
}

function main() {
  const trustedBytes = requireFile(TRUSTED_PATH, 'docs/security/trusted-signers.json');
  const revokedBytes = requireFile(REVOKED_PATH, 'docs/security/revoked-signers.json');

  const trustedSha256 = sha256Hex(trustedBytes);
  const revokedSha256 = sha256Hex(revokedBytes);
  const builtAt = new Date().toISOString();

  mkdirSync(OUT_DIR, { recursive: true });
  const body = `// GENERATED by scripts/build/emit-attestation-trust-root.mjs.
// Do not edit. Rebuild with \`npm run build\`.
//
// Source files at build time:
//   ${TRUSTED_PATH.replace(REPO_ROOT + '\\', '').replace(REPO_ROOT + '/', '')}
//   ${REVOKED_PATH.replace(REPO_ROOT + '\\', '').replace(REPO_ROOT + '/', '')}

export const ATTESTATION_TRUST_ROOT = Object.freeze({
  trustedSignersSha256: ${JSON.stringify(trustedSha256)},
  revokedSignersSha256: ${JSON.stringify(revokedSha256)},
  builtAt: ${JSON.stringify(builtAt)},
});
`;
  writeFileSync(OUT_PATH, body, 'utf8');
  process.stdout.write(
    `emit-attestation-trust-root: wrote ${OUT_PATH}\n` +
      `  trustedSignersSha256=${trustedSha256}\n` +
      `  revokedSignersSha256=${revokedSha256}\n`,
  );
}

try {
  main();
} catch (err) {
  process.stderr.write(`emit-attestation-trust-root: ${err.stack || err.message}\n`);
  process.exit(1);
}
