|
| 1 | += VCL-ut Verification Stance (proof-backed) |
| 2 | +:toc: |
| 3 | +:sectnums: |
| 4 | + |
| 5 | +// SPDX-License-Identifier: PMPL-1.0-or-later |
| 6 | +// Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) <j.d.a.jewell@open.ac.uk> |
| 7 | + |
| 8 | +WARNING: vcl-ut is the interface between *hypatia* and *verisim*; both sides |
| 9 | +trust safety claims made here. This document states *precisely* what is and is |
| 10 | +not machine-verified, and takes precedence over any "verified" / "type-safe" / |
| 11 | +"formally proven" phrasing in `README.adoc`, `PROOF-NEEDS.md`, or module |
| 12 | +doc-comments. It is the proof-backed companion to (and supersedes) the |
| 13 | +doc-only `docs/VERIFICATION-STANCE.adoc` (PR #19). |
| 14 | + |
| 15 | +Tracked under hyperpolymath/standards#124. |
| 16 | + |
| 17 | +== Headline finding (verified, not sampled) |
| 18 | + |
| 19 | +The shipped Idris2 "proof corpus" of vcl-ut *does not compile at all* on |
| 20 | +`origin/main`, at every layer, and has *never been machine-checked* (there is |
| 21 | +no `.ipkg`/Makefile/CI for `src/core/**` — only the unrelated trivial |
| 22 | +`src/abi/vclut.ipkg`, itself absent from CI). The previously-circulated stance |
| 23 | +("the dependent-type apparatus exists but is not connected") was *too |
| 24 | +generous*: the apparatus does not even typecheck. Concretely, reproduced with |
| 25 | +idris2 0.8.0: |
| 26 | + |
| 27 | +* `src/interface/abi/Types.idr` |
| 28 | +** missing `import Decidable.Equality` (every `DecEq` use is an undefined name); |
| 29 | +** the `DecEq SafetyLevel/VclTotalError/QueryMode` instances are *malformed* |
| 30 | + (`decEq _ _ = No absurd` with no `Uninhabited (x = y)` — un-fixable by hand |
| 31 | + for 11/12-constructor enums without a derivation mechanism); |
| 32 | +** `CPtr p _ = Bits (ptrSize p)` is ill-typed (`Data.Bits.Bits` is an |
| 33 | + interface, not a `Nat -> Type` family); |
| 34 | +** `createQueryHandle ptr = Just (MkQueryHandle ptr)` omits the required |
| 35 | + `So (ptr /= 0)` witness; |
| 36 | +** `thisPlatform` uses `%runElab` without `%language ElabReflection`. |
| 37 | +* `src/core/Grammar.idr` — `Expr` (`ESubquery Statement`) and the epistemic |
| 38 | + clause constructors forward-reference `Statement` / `EpistemicRequirement`, |
| 39 | + which are defined ~100 lines later, with *no `mutual` block or forward |
| 40 | + declarations*. The file does not pass the type checker. |
| 41 | + |
| 42 | +Because Grammar fails, every downstream module (`Schema`, `Levels`, `Checker`, |
| 43 | +`Composition`) is unverifiable in situ regardless of its own correctness. |
| 44 | + |
| 45 | +== What IS genuinely machine-verified by this PR |
| 46 | + |
| 47 | +`verification/proofs/SafetyL4Model.idr` — checked by **idris2 0.8.0, |
| 48 | +`idris2 --check`, exit 0, with `%default total` and ZERO |
| 49 | +`believe_me`/`really_believe_me`/`postulate`/`assert_total`/`idris_crash`/ |
| 50 | +`sorry`**. It reproduces the *exact mathematical content* of the in-repo |
| 51 | +Level-4 (SQL-injection) remediation against a minimal, self-contained, |
| 52 | +byte-faithful model of the relevant AST fragment, proving: |
| 53 | + |
| 54 | +. `NoRawUserInput` is no longer *vacuous*. It previously had the single |
| 55 | + catch-all constructor `AllParameterised : NoRawUserInput stmt`, inhabiting |
| 56 | + it for *every* statement — including a WHERE clause that is pure string |
| 57 | + interpolation. It now carries real evidence: `whereHasStringLit w = False`. |
| 58 | +. `checkLevel4Sound` — *soundness of the decision procedure*: if |
| 59 | + `checkLevel4` accepts, the statement genuinely carries the (now real) |
| 60 | + `NoRawUserInput` witness. This is the certificate↔checker connection for |
| 61 | + L4; it was previously unstateable (predicate vacuous, checker returned a |
| 62 | + bare `Bool`). |
| 63 | +. `injectionRejected` — negative test: a WHERE bearing a string literal |
| 64 | + (`… = '1=1'`) is *rejected*. Under the old vacuous predicate the |
| 65 | + corresponding query "type-checked" at Level 4. |
| 66 | +. `noRawUserInputCompose` — Level-4 injection-freedom is *genuinely* closed |
| 67 | + under join composition, because `joinWhere` introduces no string literal |
| 68 | + not already present. This replaces the two |
| 69 | + `MkL4 _ AllParameterised` uses in `Composition.compositionPreservation`, |
| 70 | + whose "preservation theorem" was therefore *vacuously true*. |
| 71 | + |
| 72 | +The corresponding in-repo source fix is committed on branch |
| 73 | +`fix/vclut-l4-devacuize-honest-stance` (`src/core/{Grammar,Levels,Checker, |
| 74 | +Composition}.idr`). It is logically correct and reviewable, and is |
| 75 | +*identical in content* to the verified model, but cannot be |
| 76 | +`idris2 --check`-ed in situ until the corpus is resurrected (see roadmap). |
| 77 | + |
| 78 | +== What is NOT proven (honest, precisely-scoped gaps — OWED) |
| 79 | + |
| 80 | +. *Corpus does not compile* (above). Until fixed, NO in-repo Idris claim is |
| 81 | + machine-checked. Highest priority. |
| 82 | +. *L2/L3/L5 predicates remain vacuous.* `AllComparisonsTypeSafe` via |
| 83 | + `WhereTypeSafe LogicSafe` (holds for any logic expr); `L3` |
| 84 | + `AllNullableFieldsGuarded`'s `GuardedNull` (accepts any expr); `L5` |
| 85 | + `AllSelectItemsTyped`'s `ConsTyped` (no per-item typing evidence). As |
| 86 | + written, Levels 2, 3 and 5 prove nothing about the property they name. |
| 87 | + Only L4 has been de-vacuized. |
| 88 | +. *The certificate is disconnected from the checker for every level except |
| 89 | + the L4 evidence.* `Checker.checkQuery` returns a plain `CheckResult` |
| 90 | + record and never constructs a `SafetyCertificate`. The dependent types |
| 91 | + are decorative w.r.t. the decision procedure (L4 excepted, via |
| 92 | + `checkLevel4Sound`). |
| 93 | +. *`Composition.compositionPreservation`*: the L4 case is now genuine; the |
| 94 | + L2/L3/L5 cases remain vacuous (and self-document as such). |
| 95 | +. *The FFI boundary is a stub.* `ffi/zig/src/lib.zig` returns `2` |
| 96 | + (`L2`) for any query containing the substring `SELECT`; the Rust |
| 97 | + `extern "C"` counterpart does not exist. `src/abi/Foreign.idr` |
| 98 | + fabricates `Verified Ln` from a bare untrusted `Int`. The level tag |
| 99 | + hypatia/verisim receive is an *asserted integer from a substring check*, |
| 100 | + not a transported proof. |
| 101 | + |
| 102 | +== Consumer guidance (hypatia / verisim) |
| 103 | + |
| 104 | +* Treat *every* vcl-ut safety-level tag as an *advisory claim*, not a proof |
| 105 | + carrier. Do not weaken your own checks on the strength of a vcl-ut level. |
| 106 | +* The *only* claim with a genuine machine-checked basis is L4 |
| 107 | + injection-freedom — and even that is, today, not wired through the |
| 108 | + (non-compiling) corpus or the (stubbed) FFI. Do not cite vcl-ut as |
| 109 | + "formally verified query safety" downstream. |
| 110 | + |
| 111 | +== Remediation roadmap (phased, tracked standards#124) |
| 112 | + |
| 113 | +. *Phase 0 (this PR):* de-vacuize L4 + checker soundness + real |
| 114 | + composition; machine-verify the content; document the true state. |
| 115 | +. *Phase 1:* make the corpus compile — `mutual` block in `Grammar.idr`; |
| 116 | + fix/replace `ABI.Types` (`Decidable.Equality`, a real `DecEq` |
| 117 | + derivation, `CPtr`, `createQueryHandle`, `thisPlatform`); add a |
| 118 | + `verification/proofs/vclut-core.ipkg` + CI step so it stays compiled. |
| 119 | +. *Phase 2:* de-vacuize L2/L3/L5 with evidence-carrying constructors and |
| 120 | + prove `checkLevelN` sound for each (as done for L4). |
| 121 | +. *Phase 3:* have `checkQuery` emit a real `SafetyCertificate`; transport |
| 122 | + it as an opaque proof token through a non-stub Zig/Rust FFI. |
| 123 | + |
| 124 | +== Provenance |
| 125 | + |
| 126 | +Audit + remediation 2026-05-18 (hyperpolymath/standards#124). Findings |
| 127 | +reproduced by reading the corpus and by `idris2 0.8.0 --build` / |
| 128 | +`--check` (compile failures above are reproducible verbatim). The verified |
| 129 | +model carries no proof-escape symbols. The risk here is *absent and |
| 130 | +non-compiling* proof masquerading as present proof at a doubly-trusted |
| 131 | +interface. |
0 commit comments