#!/usr/bin/env node
/**
 * Verify the seal on synovaindustries.com.
 *
 *   node verify.mjs
 *   node verify.mjs https://www.synovaindustries.com
 *
 * Zero dependencies. Nothing to install. It fetches every page, recomputes the
 * chain from scratch, and compares it against the published seal.
 *
 * We do not participate in this. You do not need our keys, our cooperation, or
 * our permission, and if we tampered with a page after sealing it, this script
 * would tell you so and we could not stop it. That property is the entire
 * argument of the site, and a claim like that is worth exactly nothing unless
 * you can run it yourself.
 *
 * The reduction below is identical to scripts/seal.mjs. It is the published
 * procedure, not a secret one.
 */

import { createHash } from "node:crypto";

const BASE = (process.argv[2] ?? "https://www.synovaindustries.com").replace(/\/+$/, "");

const sha256 = (s) => createHash("sha256").update(s, "utf8").digest("hex");


/**
 * Remove every element carrying `data-seal`, INCLUDING its children.
 *
 * A regex cannot do this. `<div data-seal>...</div>` with nested divs inside it
 * needs balanced matching, and the lazy regex I used first stopped at the FIRST
 * `</...>` it found, which left the injected hashes inside the very text the
 * hash is taken over. The chain then failed to verify against its own output,
 * which the published verifier duly reported. It was right to.
 *
 * The rule this enforces: a page cannot contain the hash of itself. Anything
 * that displays a seal value is excluded from what the seal is computed over.
 */
function stripSealed(html) {
  let out = "";
  let i = 0;
  while (i < html.length) {
    const open = html.indexOf("<", i);
    if (open === -1) { out += html.slice(i); break; }
    const close = html.indexOf(">", open);
    if (close === -1) { out += html.slice(i); break; }

    const tag = html.slice(open, close + 1);
    const m = /^<([a-zA-Z][\w-]*)\b/.exec(tag);

    if (!m || !/\bdata-seal\b/.test(tag) || tag.endsWith("/>")) {
      out += html.slice(i, close + 1);
      i = close + 1;
      continue;
    }

    // Balanced scan to the matching close tag.
    const name = m[1];
    const openRe = new RegExp(`<${name}\\b`, "gi");
    const closeRe = new RegExp(`</${name}\\s*>`, "gi");
    let depth = 1;
    let cursor = close + 1;
    while (depth > 0 && cursor < html.length) {
      openRe.lastIndex = cursor;
      closeRe.lastIndex = cursor;
      const no = openRe.exec(html);
      const nc = closeRe.exec(html);
      if (!nc) { cursor = html.length; break; }
      if (no && no.index < nc.index) { depth++; cursor = no.index + 1; }
      else { depth--; cursor = nc.index + nc[0].length; }
    }
    out += " ";
    i = cursor;
  }
  return out;
}

function reduce(html) {
  return stripSealed(html)
    .replace(/<(script|style|svg)\b[^>]*>[\s\S]*?<\/\1>/gi, " ")
    .replace(/<!--[\s\S]*?-->/g, " ")
    .replace(/<[^>]+>/g, " ")
    .replace(/&[a-z]+;|&#\d+;/gi, " ")
    .replace(/\s+/g, " ")
    .trim();
}

const seal = await (await fetch(`${BASE}/seal.json`)).json();

console.log(`\n  seal published ${seal.sealedAt}`);
console.log(`  algorithm      ${seal.algorithm}`);
console.log(`  chain          ${seal.chain}\n`);

let prev = "0".repeat(64);
let ok = true;

for (const link of seal.chain_ ?? seal.links) {
  const html = await (await fetch(`${BASE}${link.route}`)).text();
  const content = sha256(reduce(html));
  const computed = sha256(prev + content);

  const contentOk = content === link.content;
  const linkOk = computed === link.link && prev === link.prev;
  if (!contentOk || !linkOk) ok = false;

  const mark = contentOk && linkOk ? "ok  " : "BAD ";
  console.log(`  ${mark} ${link.route.padEnd(10)} ${computed.slice(0, 24)}…`);
  if (!contentOk) {
    console.log(`       content hash differs: page has changed since sealing`);
    console.log(`       published ${link.content.slice(0, 32)}…`);
    console.log(`       computed  ${content.slice(0, 32)}…`);
  }
  prev = computed;
}

const headOk = prev === seal.head;
console.log(`\n  head           ${prev.slice(0, 32)}…`);
console.log(`  published      ${seal.head.slice(0, 32)}…`);

if (ok && headOk) {
  console.log(`\n  CHAIN INTACT. Every page matches the seal.\n`);
  process.exit(0);
}
console.log(`\n  CHAIN BROKEN. At least one page has changed since it was sealed.\n`);
process.exit(1);
