import type { TableOwnership } from "../types.js"; import { type StaticFinding } from "./absolute.js"; import { type Drift, type ModelFingerprint } from "./fingerprint.js"; /** * The shape of the assurance report, which is not the version of the software. * * These were the same constant, and `report.version` carried this — so * `crossline verify` printed "Crossline 0.1.0" while every other command * printed the real one, and, worse, every record sent to the evidence store * claimed it was produced by client 0.1.0 no matter what was installed. History * cannot be backfilled, so that would have been wrong permanently, and "which * version of the control produced this" is exactly what an auditor asks. */ export declare const ASSURANCE_FORMAT = 1; export interface AssureOptions { connectionString: string; schemas?: string[]; roles?: { anonymous?: string; authenticated?: string; }; userTable?: string; overrides?: Record>; /** The fingerprint a passing run recorded, or null if there is none. */ recorded?: ModelFingerprint | null; } export type Comparison = { kind: "matched"; recordedAt: string; } | { kind: "drifted"; recordedAt: string; drift: Drift[]; } | { kind: "not_recorded"; }; export type AssuranceVerdict = "clean" | "drifted" | "open" | "unproven"; export interface AssuranceReport { brand: string; version: string; checkedAt: string; /** Connection string with the password removed. Never anything from a row. */ target: string; schemas: string[]; /** Always true, and stated in the document so a reader of the JSON sees it. */ readOnly: true; live: ModelFingerprint; comparison: Comparison; findings: StaticFinding[]; /** * Reachable surface this check will not judge, in words. * * Not findings and not passes. A `SECURITY DEFINER` function runs with its * owner's privileges, so row-level security does not apply inside it, and no * amount of catalogue reading settles whether a given one checks its caller. * The dynamic run settles it by calling it; this one says so and stops. */ unsettled: string[]; tablesExamined: number; verdict: AssuranceVerdict; /** * True when this run has something a build should stop for, and the only * thing the exit code is derived from. * * Any drift counts, not only drift that widens. The claim this command makes * is "the model that was proved is the model running", and a narrowing change * falsifies it just as completely as a widening one. Letting the harmless * half through quietly would recreate exactly the situation the feature * exists for — a green build sitting on top of a database nobody proved * anything about. The urgency of the difference is carried in the output, in * which lines are red; whether to stop is not a judgement about urgency. * * Having no recorded proof does not count. That is a fact about the * repository rather than the database, it is said loudly, and a gate that * fires on it fires on every project that has not run the suite yet. */ actionable: boolean; } /** * Read the authorization model of a running database and say two things about * it: whether anything in it is wrong on its face, and whether it is still the * model a dynamic run proved. * * Nothing is seeded, nothing is probed, no row is read, and no statement but a * `select` is sent — see {@link CatalogReader}, which enforces all three. That * is what makes this safe to point at production, and it is also the limit of * what it can claim: it confirms the model is intact and nothing is open on its * face. It does not prove isolation. Only a run that plants rows for two * unrelated users and fails to cross between them proves that. */ export declare function assure(opts: AssureOptions): Promise; export { compareFingerprints, fingerprint } from "./fingerprint.js"; export type { Drift, DriftKind, ModelFingerprint } from "./fingerprint.js"; export { LOCK_FILE, lockPath, readLock, writeLock } from "./lock.js"; export { CatalogReader } from "./reader.js"; export { absoluteFindings } from "./absolute.js"; export type { StaticFinding } from "./absolute.js";