← Files VeraARCHIVED FILE

modules/browser-automation/scripts/agenzia_artifacts.mjs

8.86 KB · Oct 2, 2026 · 00:29 UTC

↓ Download file

/** Byte-level Agenzia artifact checks; no invoice or category meaning is inferred. */
import { createHash } from "node:crypto";
import { constants } from "node:fs";
import { mkdir, open, realpath, writeFile } from "node:fs/promises";
import { isAbsolute, join, relative } from "node:path";

const CMS_SIGNED_DATA_OID = "2a864886f70d010702";
const CMS_DATA_OID = "2a864886f70d010701";
const MAX_INVOICE_BYTES = 64 * 1024 * 1024;

export class AgenziaArtifactError extends Error {
  constructor(code, cause) {
    super(code, cause ? { cause } : undefined);
    this.name = "AgenziaArtifactError";
    this.code = code;
  }
}

const sha256 = (value) => createHash("sha256").update(value).digest("hex");

function readTlv(bytes, offset) {
  if (offset >= bytes.length) throw new AgenziaArtifactError("p7m-truncated");
  const tag = bytes[offset];
  let cursor = offset + 1;
  if (cursor >= bytes.length) throw new AgenziaArtifactError("p7m-truncated");
  const firstLength = bytes[cursor];
  cursor += 1;
  let length;
  if ((firstLength & 0x80) === 0) {
    length = firstLength;
  } else {
    const lengthBytes = firstLength & 0x7f;
    if (lengthBytes === 0 || lengthBytes > 4 || cursor + lengthBytes > bytes.length) {
      throw new AgenziaArtifactError("p7m-unsupported-encoding");
    }
    length = 0;
    for (let index = 0; index < lengthBytes; index += 1) {
      length = length * 256 + bytes[cursor + index];
    }
    cursor += lengthBytes;
  }
  const end = cursor + length;
  if (end > bytes.length) throw new AgenziaArtifactError("p7m-truncated");
  return { tag, contentStart: cursor, end, next: end };
}

function childNodes(bytes, node) {
  const children = [];
  let cursor = node.contentStart;
  while (cursor < node.end) {
    const child = readTlv(bytes, cursor);
    children.push(child);
    cursor = child.next;
  }
  if (cursor !== node.end) throw new AgenziaArtifactError("p7m-invalid-structure");
  return children;
}

function expectTag(node, tag, code = "p7m-invalid-structure") {
  if (!node || node.tag !== tag) throw new AgenziaArtifactError(code);
  return node;
}

function oidHex(bytes, node) {
  expectTag(node, 0x06);
  return bytes.subarray(node.contentStart, node.end).toString("hex");
}

function collectOctets(bytes, node) {
  if (node.tag === 0x04) return bytes.subarray(node.contentStart, node.end);
  if (node.tag !== 0x24 && node.tag !== 0xa0) {
    throw new AgenziaArtifactError("p7m-content-missing");
  }
  return Buffer.concat(childNodes(bytes, node).map((child) => collectOctets(bytes, child)));
}

function decodeCmsBytes(input) {
  const marker = input.subarray(0, Math.min(input.length, 32)).toString("ascii");
  if (!marker.startsWith("-----BEGIN")) return input;
  const text = input.toString("ascii");
  const match = text.match(/-----BEGIN (PKCS7|CMS)-----([A-Za-z0-9+/=\s]+)-----END \1-----/);
  if (!match) throw new AgenziaArtifactError("p7m-invalid-pem");
  return Buffer.from(match[2].replace(/\s+/g, ""), "base64");
}

/** Extract the exact encapsulated CMS payload. Signature validity is not asserted. */
export function extractXmlFromP7m(input) {
  const bytes = decodeCmsBytes(Buffer.from(input));
  const contentInfo = expectTag(readTlv(bytes, 0), 0x30);
  if (contentInfo.next !== bytes.length) throw new AgenziaArtifactError("p7m-trailing-bytes");
  const contentChildren = childNodes(bytes, contentInfo);
  if (oidHex(bytes, contentChildren[0]) !== CMS_SIGNED_DATA_OID) {
    throw new AgenziaArtifactError("p7m-not-signed-data");
  }
  const explicitSignedData = expectTag(contentChildren[1], 0xa0);
  const signedData = expectTag(childNodes(bytes, explicitSignedData)[0], 0x30);
  const signedChildren = childNodes(bytes, signedData);
  const encapsulated = expectTag(signedChildren[2], 0x30);
  const encapsulatedChildren = childNodes(bytes, encapsulated);
  if (oidHex(bytes, encapsulatedChildren[0]) !== CMS_DATA_OID) {
    throw new AgenziaArtifactError("p7m-content-type-unsupported");
  }
  const payload = collectOctets(bytes, expectTag(encapsulatedChildren[1], 0xa0));
  if (!isFatturaXml(payload)) throw new AgenziaArtifactError("p7m-payload-not-fatturapa-xml");
  return payload;
}

export function isFatturaXml(input) {
  let text;
  try {
    text = new TextDecoder("utf-8", { fatal: true }).decode(input);
  } catch {
    return false;
  }
  return /^\uFEFF?\s*(?:<\?xml[^>]*>\s*)?(?:<!--[^]*?-->\s*)*<(?:[A-Za-z_][\w.-]*:)?FatturaElettronica(?:\s|>)/.test(text);
}

async function readVerifiedFile(evidence, { allowedRoot = null } = {}) {
  if (!evidence?.path || !Number.isInteger(evidence.byte_length) || !/^[a-f0-9]{64}$/.test(evidence.sha256 ?? "")) {
    throw new AgenziaArtifactError("download-evidence-invalid");
  }
  if (evidence.byte_length < 1 || evidence.byte_length > MAX_INVOICE_BYTES) {
    throw new AgenziaArtifactError("download-size-unsupported");
  }
  let verifiedPath = evidence.path;
  if (allowedRoot) {
    const [actualRoot, actualPath] = await Promise.all([realpath(allowedRoot), realpath(evidence.path)]);
    const child = relative(actualRoot, actualPath);
    if (!child || child.startsWith("..") || isAbsolute(child)) {
      throw new AgenziaArtifactError("archive-path-outside-run");
    }
    verifiedPath = actualPath;
  }
  const handle = await open(verifiedPath, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0));
  try {
    const before = await handle.stat();
    if (!before.isFile() || before.size !== evidence.byte_length) {
      throw new AgenziaArtifactError("download-file-changed");
    }
    const bytes = await handle.readFile();
    const after = await handle.stat();
    if (after.size !== before.size || after.mtimeMs !== before.mtimeMs || sha256(bytes) !== evidence.sha256) {
      throw new AgenziaArtifactError("download-file-changed");
    }
    return bytes;
  } finally {
    await handle.close();
  }
}

async function writeExact(path, bytes) {
  const result = { path, byte_length: bytes.length, sha256: sha256(bytes) };
  try {
    await writeFile(path, bytes, { flag: "wx", mode: 0o600 });
    return result;
  } catch (error) {
    if (error?.code !== "EEXIST") throw error;
    await readVerifiedFile(result);
    return { ...result, recovered_existing_exact_copy: true };
  }
}

function validateArchiveIdentity({ year, category, documentKey }) {
  if (!/^\d{4}$/.test(String(year))) throw new AgenziaArtifactError("archive-year-invalid");
  if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(category ?? "")) {
    throw new AgenziaArtifactError("archive-category-invalid");
  }
  if (!/^[a-f0-9]{64}$/.test(documentKey ?? "")) {
    throw new AgenziaArtifactError("archive-document-key-invalid");
  }
}

/** Preserve an XML or P7M original and hash-link any extracted signed XML. */
export async function archiveInvoiceOriginal({ evidence, archiveRoot, year, category, documentKey }) {
  validateArchiveIdentity({ year, category, documentKey });
  const bytes = await readVerifiedFile(evidence);
  const directory = join(archiveRoot, String(year), category, "originals");
  await mkdir(directory, { recursive: true, mode: 0o700 });

  if (isFatturaXml(bytes)) {
    const original = await writeExact(join(directory, `${documentKey}.xml`), bytes);
    return {
      kind: "xml",
      original: { ...original, preservation: "byte_exact_copy" },
      extracted_xml: null,
      signature_validation: "not_applicable"
    };
  }

  const extracted = extractXmlFromP7m(bytes);
  const original = await writeExact(join(directory, `${documentKey}.p7m`), bytes);
  const extractedDirectory = join(archiveRoot, String(year), category, "extracted");
  await mkdir(extractedDirectory, { recursive: true, mode: 0o700 });
  const extractedXml = await writeExact(join(extractedDirectory, `${documentKey}.xml`), extracted);
  return {
    kind: "p7m",
    original: { ...original, preservation: "byte_exact_copy" },
    extracted_xml: {
      ...extractedXml,
      source_p7m_sha256: original.sha256,
      binding: "cms_encapsulated_content"
    },
    signature_validation: "not_performed"
  };
}

/** Verify and archive a PDF saved by the operator through the native print dialog. */
export async function archiveNativePdf({ evidence, archiveRoot, year, category, documentKey }) {
  validateArchiveIdentity({ year, category, documentKey });
  const bytes = await readVerifiedFile(evidence);
  if (bytes.subarray(0, 5).toString("ascii") !== "%PDF-") {
    throw new AgenziaArtifactError("native-output-not-pdf");
  }
  const directory = join(archiveRoot, String(year), category, "pdf");
  await mkdir(directory, { recursive: true, mode: 0o700 });
  const pdf = await writeExact(join(directory, `${documentKey}.pdf`), bytes);
  return {
    ...pdf,
    preservation: "byte_exact_copy",
    evidence_basis: "operator_native_gap_directory_bytes_verified"
  };
}

export async function verifyArchivedArtifact(artifact, { allowedRoot = null } = {}) {
  const evidence = {
    path: artifact?.path,
    byte_length: artifact?.byte_length,
    sha256: artifact?.sha256
  };
  await readVerifiedFile(evidence, { allowedRoot });
  return true;
}

SHA-256: dbe4ee98cfce1071b12d8184eda4833405a783e466dc5a669b0630623019dcc3