|
| 1 | +#!/usr/bin/env bash |
| 2 | +# SPDX-License-Identifier: MPL-2.0 |
| 3 | +# Copyright (c) Jonathan D.A. Jewell <j.d.a.jewell@open.ac.uk> |
| 4 | +# |
| 5 | +# demo-admissibility.sh — VCLTGate admissibility check, end to end. |
| 6 | +# |
| 7 | +# Shows the gate protocol: statements that parse cleanly are admitted; |
| 8 | +# structurally malformed statements are rejected before touching the store. |
| 9 | +# The gate is a pure function: no network, no filesystem side effects. |
| 10 | +# |
| 11 | +# Usage: |
| 12 | +# scripts/demo-admissibility.sh # finds vclt-gate in PATH or sibling repo |
| 13 | +# VCLT_GATE=/path/to/vclt-gate scripts/demo-admissibility.sh |
| 14 | +# |
| 15 | +# The vclt-gate binary lives in vcl-ut: |
| 16 | +# cd vcl-ut/src/interface/parse && cargo build --bin vclt-gate |
| 17 | + |
| 18 | +set -euo pipefail |
| 19 | + |
| 20 | +GATE="${VCLT_GATE:-}" |
| 21 | + |
| 22 | +say() { printf '\n\033[1m== %s ==\033[0m\n' "$*"; } |
| 23 | +ok() { printf '\033[32m ✓ %s\033[0m\n' "$*"; } |
| 24 | +bad() { printf '\033[31m ✗ %s\033[0m\n' "$*"; } |
| 25 | +show() { printf ' %s\n' "$*"; } |
| 26 | +showjq() { echo "$1" | jq -C . 2>/dev/null || echo "$1"; } |
| 27 | + |
| 28 | +# ── Locate gate binary ──────────────────────────────────────────────────────── |
| 29 | +if [ -z "$GATE" ]; then |
| 30 | + # Try sibling vcl-ut repo first |
| 31 | + SIBLING="$(dirname "$(dirname "$(readlink -f "$0")")")" |
| 32 | + VCLT_BUILD="${SIBLING}/../vcl-ut/src/interface/parse/target/debug/vclt-gate" |
| 33 | + if [ -x "$VCLT_BUILD" ]; then |
| 34 | + GATE="$(realpath "$VCLT_BUILD")" |
| 35 | + elif command -v vclt-gate &>/dev/null; then |
| 36 | + GATE="vclt-gate" |
| 37 | + fi |
| 38 | +fi |
| 39 | + |
| 40 | +if [ -z "$GATE" ] || [ ! -x "$GATE" ]; then |
| 41 | + printf '\033[33mvclt-gate not found.\033[0m\n' |
| 42 | + printf 'Build it with:\n' |
| 43 | + printf ' cd vcl-ut/src/interface/parse && cargo build --bin vclt-gate\n' |
| 44 | + printf 'Then re-run or export VCLT_GATE=/path/to/binary.\n' |
| 45 | + exit 1 |
| 46 | +fi |
| 47 | + |
| 48 | +say "Gate binary" |
| 49 | +show "path: $GATE" |
| 50 | +show "version probe:" |
| 51 | +echo '{"schema_version":1,"statement":"SELECT GRAPH FROM HEXAD probe","schema":{}}' \ |
| 52 | + | "$GATE" | jq -r '" certified_level=\(.certified_level) admissible=\(.admissible)"' |
| 53 | + |
| 54 | +# ── Helper: run one statement through the gate ──────────────────────────────── |
| 55 | +run_gate() { |
| 56 | + local label="$1"; local stmt="$2" |
| 57 | + local payload; payload=$(jq -n --arg s "$stmt" '{"schema_version":1,"statement":$s,"schema":{}}') |
| 58 | + local out code=0 |
| 59 | + out=$(echo "$payload" | "$GATE" 2>/dev/null) || code=$? |
| 60 | + local level; level=$(echo "$out" | jq -r '.certified_level // -1') |
| 61 | + local reasons; reasons=$(echo "$out" | jq -r '.reasons[]? // empty' | head -3) |
| 62 | + if [ "$code" -eq 0 ]; then |
| 63 | + ok "$label → admit (level $level)" |
| 64 | + elif [ "$code" -eq 1 ]; then |
| 65 | + bad "$label → reject (level $level)" |
| 66 | + while IFS= read -r r; do show " $r"; done <<< "$reasons" |
| 67 | + else |
| 68 | + bad "$label → gate_failed (exit $code)" |
| 69 | + fi |
| 70 | +} |
| 71 | + |
| 72 | +# ── Admitted: well-formed VCL statements ──────────────────────────────────── |
| 73 | +say "Admitted statements" |
| 74 | + |
| 75 | +run_gate "SELECT GRAPH" \ |
| 76 | + "SELECT GRAPH FROM HEXAD drone007" |
| 77 | + |
| 78 | +run_gate "SELECT multi-modality" \ |
| 79 | + "SELECT DOCUMENT, GRAPH FROM HEXAD drone007" |
| 80 | + |
| 81 | +run_gate "SELECT with explicit LIMIT" \ |
| 82 | + "SELECT GRAPH FROM HEXAD drone007 LIMIT 100" |
| 83 | + |
| 84 | +run_gate "SELECT VECTOR" \ |
| 85 | + "SELECT VECTOR FROM HEXAD sensor42" |
| 86 | + |
| 87 | +# ── Rejected: structurally malformed or unsafe statements ──────────────────── |
| 88 | +say "Rejected statements (gate exits 1 — never reach the store)" |
| 89 | + |
| 90 | +run_gate "Trailing-token injection" \ |
| 91 | + "SELECT GRAPH FROM HEXAD abc; DROP TABLE hexads" |
| 92 | + |
| 93 | +run_gate "Chained OR injection" \ |
| 94 | + "SELECT GRAPH FROM HEXAD abc WHERE id == '1' OR '1'=='1'" |
| 95 | + |
| 96 | +run_gate "Non-integer LIMIT" \ |
| 97 | + "SELECT GRAPH FROM HEXAD abc LIMIT not_a_number" |
| 98 | + |
| 99 | +run_gate "Missing source identifier" \ |
| 100 | + "SELECT GRAPH FROM HEXAD" |
| 101 | + |
| 102 | +# ── Summary ────────────────────────────────────────────────────────────────── |
| 103 | +say "Protocol summary" |
| 104 | +show "exit 0 → :admit (statement is structurally safe)" |
| 105 | +show "exit 1 → {:reject, reasons} (violation detected before execution)" |
| 106 | +show "exit 2 → {:error, :gate_failed} (gate itself failed)" |
| 107 | +show "" |
| 108 | +show "Set VERISIM_VCLT_GATE=<path> to wire this gate into VeriSimDB's" |
| 109 | +show "VCL executor (queries bypass gate; mutations always pass through)." |
0 commit comments