183 lines
5.7 KiB
Bash
183 lines
5.7 KiB
Bash
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
# Proves that the release artifact—not the source checkout—contains a complete,
|
|
# offline, read-only Local Assurance Viewer.
|
|
|
|
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." && pwd)"
|
|
WORK="$(mktemp -d)"
|
|
DIST="$WORK/dist"
|
|
PROJECT="$WORK/clean-project"
|
|
VIEWER_STARTED=0
|
|
|
|
cleanup() {
|
|
if [[ "$VIEWER_STARTED" == "1" && -n "${CASAN_BIN:-}" ]]; then
|
|
CASAN_APP_ROOT="$PROJECT" "$CASAN_BIN" dashboard stop >/dev/null 2>&1 || true
|
|
fi
|
|
rm -rf "$WORK"
|
|
}
|
|
trap cleanup EXIT
|
|
|
|
CASAN_DIST_DIR="$DIST" bash "$ROOT/scripts/package-release.sh" core >/dev/null
|
|
ARTIFACT="$DIST/casan-core-v$(cat "$ROOT/VERSION").tar.gz"
|
|
tar -xzf "$ARTIFACT" -C "$WORK"
|
|
BUNDLE="$WORK/casan-core-v$(cat "$ROOT/VERSION")"
|
|
CASAN_BIN="$BUNDLE/bin/casan"
|
|
|
|
test -f "$BUNDLE/packages/casan-harness/assets/local-viewer/index.html"
|
|
test -f "$BUNDLE/packages/casan-harness/scripts/python/local_report.py"
|
|
test -f "$BUNDLE/packages/casan-harness/scripts/python/local_viewer.py"
|
|
test ! -e "$BUNDLE/packages/casan-control-panel"
|
|
test ! -e "$BUNDLE/package.json"
|
|
|
|
mkdir -p \
|
|
"$PROJECT/.casan" \
|
|
"$PROJECT/.specify/state" \
|
|
"$PROJECT/.specify/logs/trace" \
|
|
"$PROJECT/.specify/logs/trace-events" \
|
|
"$PROJECT/.specify/logs/cost"
|
|
|
|
python3 - "$PROJECT" <<'PY'
|
|
import json
|
|
from pathlib import Path
|
|
import sys
|
|
|
|
root = Path(sys.argv[1])
|
|
(root / ".casan/config.json").write_text(json.dumps({
|
|
"schema_version": "21.3",
|
|
"project_id": "artifact-project",
|
|
"project_name": "Artifact Project",
|
|
"edition": "core",
|
|
"enforcement_mode": "enforce",
|
|
}), encoding="utf-8")
|
|
trace_id = "artifact-run-1"
|
|
trace = {
|
|
"schema_version": "20.1",
|
|
"trace_id": trace_id,
|
|
"project_id": "artifact-project",
|
|
"certified": True,
|
|
"certification_strength": "project_hook",
|
|
"finalized_at": "2026-07-28T10:00:07Z",
|
|
"tool_calls": 1,
|
|
"failures": 0,
|
|
"evidence": [
|
|
{"h": f"H{i}", "at": f"2026-07-28T10:00:0{i}Z", "decision": "pass", "kind": "gate", "detail": f"H{i} passed"}
|
|
for i in range(1, 8)
|
|
],
|
|
}
|
|
(root / f".specify/logs/trace/agentic-{trace_id}.json").write_text(
|
|
json.dumps(trace), encoding="utf-8")
|
|
metric = {
|
|
"timestamp": "2026-07-28T10:00:06Z",
|
|
"trace_id": trace_id,
|
|
"project_id": "artifact-project",
|
|
"harness": "H6-agentic",
|
|
"step": "agentic-turn",
|
|
"status": "success",
|
|
"latency_ms": 812,
|
|
"retry_count": 0,
|
|
"input_tokens": None,
|
|
"output_tokens": None,
|
|
"total_tokens": None,
|
|
"cost_estimate": None,
|
|
"telemetry_quality": "insufficient",
|
|
"alerts": [],
|
|
}
|
|
(root / ".specify/logs/cost/metrics.jsonl").write_text(
|
|
json.dumps(metric) + "\n", encoding="utf-8")
|
|
(root / ".specify/state/latest-run.json").write_text(json.dumps({
|
|
"schema_version": 1,
|
|
"trace_id": trace_id,
|
|
"project_id": "artifact-project",
|
|
"certified": True,
|
|
"telemetry_quality": "insufficient",
|
|
"duration_ms": 812,
|
|
"tool_calls": 1,
|
|
"failures": 0,
|
|
"finalized_at": "2026-07-28T10:00:07Z",
|
|
}), encoding="utf-8")
|
|
PY
|
|
|
|
(
|
|
cd "$PROJECT"
|
|
CASAN_APP_ROOT="$PROJECT" "$CASAN_BIN" report latest | grep -q "view_command=casan view artifact-run-1"
|
|
CASAN_APP_ROOT="$PROJECT" "$CASAN_BIN" report export artifact-run-1 \
|
|
--format html --output "$WORK/run.html" >/dev/null
|
|
CASAN_APP_ROOT="$PROJECT" "$CASAN_BIN" report export --h6 \
|
|
--format json --output "$WORK/h6.json" >/dev/null
|
|
CASAN_APP_ROOT="$PROJECT" "$CASAN_BIN" report export --h6 \
|
|
--format html --output "$WORK/h6.html" >/dev/null
|
|
)
|
|
grep -q "Independent assurance dossier" "$WORK/run.html"
|
|
grep -q "H6 · AgentOps Report" "$WORK/h6.html"
|
|
python3 - "$WORK/h6.json" <<'PY'
|
|
import json, sys
|
|
d = json.load(open(sys.argv[1], encoding="utf-8"))
|
|
assert d["harness"] == "H6"
|
|
assert d["summary"]["runs"] == 1
|
|
assert d["summary"]["tokens"]["total"] is None
|
|
assert d["summary"]["cost_usd"]["estimated"] is None
|
|
PY
|
|
|
|
URL="$(CASAN_APP_ROOT="$PROJECT" "$CASAN_BIN" view artifact-run-1 --no-open)"
|
|
VIEWER_STARTED=1
|
|
python3 - "$URL" <<'PY'
|
|
import json
|
|
import sys
|
|
from urllib.error import HTTPError
|
|
from urllib.parse import parse_qs, urlparse
|
|
from urllib.request import Request, urlopen
|
|
|
|
url = urlparse(sys.argv[1])
|
|
token = parse_qs(url.query)["token"][0]
|
|
base = f"{url.scheme}://{url.netloc}"
|
|
|
|
def get(path):
|
|
request = Request(base + path, headers={"X-CASAN-Viewer-Token": token})
|
|
return json.loads(urlopen(request, timeout=3).read())
|
|
|
|
project = get("/api/v1/project")
|
|
assert project["project_id"] == "artifact-project"
|
|
assert project["viewer"] == {
|
|
"mode": "local_core",
|
|
"scope": "single_project",
|
|
"read_only": True,
|
|
"network": "loopback_only",
|
|
}
|
|
run = get("/api/v1/reports/run/artifact-run-1")
|
|
assert run["verdict"] == "certified"
|
|
assert run["summary"]["gates_observed"] == 7
|
|
h6 = get("/api/v1/reports/h6?run=artifact-run-1")
|
|
assert h6["summary"]["runs"] == 1
|
|
|
|
try:
|
|
urlopen(Request(base + "/api/v1/project"), timeout=3)
|
|
raise AssertionError("API accepted a request without the session token")
|
|
except HTTPError as error:
|
|
assert error.code == 401
|
|
|
|
try:
|
|
urlopen(Request(
|
|
base + "/api/v1/project",
|
|
method="POST",
|
|
headers={"X-CASAN-Viewer-Token": token},
|
|
), timeout=3)
|
|
raise AssertionError("read-only viewer accepted POST")
|
|
except HTTPError as error:
|
|
assert error.code == 405
|
|
|
|
try:
|
|
urlopen(Request(
|
|
base + "/api/v1/project",
|
|
headers={"X-CASAN-Viewer-Token": token, "Host": "attacker.example"},
|
|
), timeout=3)
|
|
raise AssertionError("viewer accepted a non-loopback Host header")
|
|
except HTTPError as error:
|
|
assert error.code == 403
|
|
PY
|
|
|
|
CASAN_APP_ROOT="$PROJECT" "$CASAN_BIN" dashboard status | grep -q '"mode": "local_core"'
|
|
CASAN_APP_ROOT="$PROJECT" "$CASAN_BIN" dashboard stop | grep -q "CASAN_VIEW_STOPPED"
|
|
VIEWER_STARTED=0
|
|
echo "PASS: Core release artifact provides offline visual reports without Platform or Node"
|