refactor(structure): promote app to repo root + remove redundant workspace cruft
Standard production layout: the OKR app (was nested under AINative_OKR_CASAN5/) is now
the repository root. No more wrapper directory.
- Promote AINative_OKR_CASAN5/* -> repo root (backend/ frontend/ packages/ apps/
.specify/ docs/ infra/ nginx/ scripts/ + configs). Merge tool dirs: .gitea (kept the
active deploy ci.yml, added harness-ci.yml + runbooks), .claude (agents/commands +
launch.json), .github moved up.
- Remove redundant: 00_SUBMISSION_PACKAGE, scattered root notes (FPT_CASAN_Full.md,
tu-tuong-casan.md, casan-tu-sinh..., casan_harness_assessment.md, source-review...,
README_CASAN5_REFINED.md), casan-next-plans/ and optimize-docs/ (competition/planning
artifacts — roadmap + design history preserved in git log / commit messages).
- Update all references to the old layout:
- .gitea/workflows/{ci,harness-ci}.yml, .github/workflows/{ci,deploy}.yml:
working-directory .; drop AINative_OKR_CASAN5/ prefix; .specify/{tests,scripts}
-> packages/casan-harness/... (.specify/logs state kept)
- .claude/launch.json, .gitea/*-runbook.md: path prefixes
- CLAUDE.md, README.md: docs/input -> apps/okr/domain/input
- policy-bundle.yaml: 8 policy paths -> packages/casan-harness/...; manifest re-signed
- secrets-scan.sh: fixture excludes -> new package/domain paths.
Full gate from the new root: PASS=64 FAIL=0 SKIP=3.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
7101af9fd4
commit
36a4812ef3
+125
@@ -0,0 +1,125 @@
|
||||
#!/usr/bin/env bash
|
||||
set -uo pipefail
|
||||
|
||||
# CASAN Plan-09 — Evidence Pack (MVP).
|
||||
#
|
||||
# Packages a tamper-evident proof of a CASAN run: per-harness JSON reports, a
|
||||
# red-team / benign-FP result, an artifact manifest (sha256 of every file), a
|
||||
# human decision log, and an RSA signature over the manifest head. Verification
|
||||
# fails if any packed file changes.
|
||||
#
|
||||
# CLI mapping (future `casan` binary):
|
||||
# casan pack <run-id> -> evidence-pack.sh pack <run-id>
|
||||
# casan verify-pack <run-id> -> evidence-pack.sh verify-pack <run-id>
|
||||
#
|
||||
# A "Certified run" is only asserted when the required gates PASS and none was
|
||||
# silently skipped (see run-summary.json.certification_reasons).
|
||||
#
|
||||
# Usage:
|
||||
# evidence-pack.sh pack <run-id> [--out <dir>]
|
||||
# evidence-pack.sh verify-pack <run-id> [--dir <dir>]
|
||||
# Exit: 0 ok, 1 verify failed, 64 usage.
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
source "$SCRIPT_DIR/casan-paths.sh"
|
||||
PROJECT_ROOT="$CASAN_APP_ROOT"
|
||||
# shellcheck source=casan-log.sh
|
||||
source "$SCRIPT_DIR/casan-log.sh"
|
||||
|
||||
CMD="${1:-}"; RUN_ID="${2:-}"
|
||||
shift 2 2>/dev/null || true
|
||||
PACKS_ROOT="$PROJECT_ROOT/docs/output/casan/evidence-packs"
|
||||
PACK_DIR=""
|
||||
while [[ "$#" -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--out|--dir) PACK_DIR="${2:-}"; shift 2 ;;
|
||||
*) shift ;;
|
||||
esac
|
||||
done
|
||||
[[ -z "$CMD" || -z "$RUN_ID" ]] && { echo "Usage: evidence-pack.sh {pack|verify-pack} <run-id> [--out/--dir <dir>]" >&2; exit 64; }
|
||||
[[ -z "$PACK_DIR" ]] && PACK_DIR="$PACKS_ROOT/$RUN_ID"
|
||||
|
||||
AUDIT_PRIV="${CASAN_AUDIT_PRIV:-$CASAN_GOVERNANCE_ROOT/audit-private.pem}"
|
||||
AUDIT_PUB="${CASAN_AUDIT_PUB:-$CASAN_GOVERNANCE_ROOT/audit-public.pem}"
|
||||
|
||||
run_status() { # <command...> -> prints "<first-stdout-line>|<rc>"
|
||||
local out rc
|
||||
out="$("$@" 2>/dev/null | head -1)"; rc="${PIPESTATUS[0]}"
|
||||
printf '%s|%s' "${out:-none}" "$rc"
|
||||
}
|
||||
|
||||
case "$CMD" in
|
||||
pack)
|
||||
mkdir -p "$PACK_DIR"
|
||||
casan_log info evidence-pack "packing run=$RUN_ID dir=$PACK_DIR"
|
||||
|
||||
AUDIT_ST="$(run_status bash "$SCRIPT_DIR/verify-audit-chain.sh")"
|
||||
TOOL_ST="$(run_status bash "$SCRIPT_DIR/verify-tool-audit.sh")"
|
||||
TEL_ST="$(run_status bash "$SCRIPT_DIR/telemetry-integrity.sh" verify)"
|
||||
COST_RC=0; bash "$SCRIPT_DIR/cost-spike-detect.sh" >/dev/null 2>&1 || COST_RC=$?
|
||||
|
||||
# Reuse an existing benign-FP report if present (fast); else leave unset so
|
||||
# the certification records the gate as skipped rather than fabricating it.
|
||||
FP_JSON="$PROJECT_ROOT/docs/output/casan/benign-fp-report.json"
|
||||
[[ -f "$FP_JSON" ]] || FP_JSON=""
|
||||
|
||||
CERT_LINE="$(CASAN_EP_AUDIT="$AUDIT_ST" CASAN_EP_TOOLAUDIT="$TOOL_ST" \
|
||||
CASAN_EP_TELEMETRY="$TEL_ST" CASAN_EP_COST_RC="$COST_RC" CASAN_EP_FP_JSON="$FP_JSON" \
|
||||
python "$SCRIPT_DIR/evidence-pack-build.py" "$PROJECT_ROOT" "$RUN_ID" "$PACK_DIR")"
|
||||
|
||||
# Safety: the human decision log must not leak secrets/PII (fail closed).
|
||||
if ! bash "$SCRIPT_DIR/data-exfil-guard.sh" "$PACK_DIR/decision-log.md" artifact >/dev/null 2>&1; then
|
||||
casan_log error evidence-pack "decision-log failed data-exfil guard — pack aborted"
|
||||
echo "EVIDENCE_PACK_ABORTED reason=decision_log_would_leak" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Sign the manifest head (off-repo key in production; unsigned in keyless dev).
|
||||
HEAD_FILE="$PACK_DIR/manifest-head.txt"
|
||||
SIG_FILE="$PACK_DIR/evidence-pack.sig"
|
||||
if [[ -f "$AUDIT_PRIV" ]] && command -v openssl >/dev/null 2>&1; then
|
||||
openssl dgst -sha256 -sign "$AUDIT_PRIV" -out "$SIG_FILE" "$HEAD_FILE"
|
||||
ANCHOR="signed"
|
||||
else
|
||||
rm -f "$SIG_FILE"; ANCHOR="unsigned"
|
||||
fi
|
||||
|
||||
CERTIFIED="${CERT_LINE#CERTIFIED|}"; CERTIFIED="${CERTIFIED%%|*}"
|
||||
echo "EVIDENCE_PACK_CREATED run=$RUN_ID dir=$PACK_DIR certified=$CERTIFIED anchor=$ANCHOR"
|
||||
[[ "$CERTIFIED" == "true" ]] && echo "CASAN_CERTIFIED_RUN run=$RUN_ID" || echo "CASAN_UNCERTIFIED_RUN run=$RUN_ID reason=${CERT_LINE##*|}"
|
||||
;;
|
||||
|
||||
verify-pack)
|
||||
[[ -d "$PACK_DIR" ]] || { echo "EVIDENCE_PACK_NOT_FOUND dir=$PACK_DIR" >&2; exit 1; }
|
||||
python "$SCRIPT_DIR/evidence-pack-verify.py" "$PACK_DIR"; VRC=$?
|
||||
[[ "$VRC" -ne 0 ]] && exit 1
|
||||
# Signature check over the manifest head (catches a manifest rewrite).
|
||||
HEAD_FILE="$PACK_DIR/manifest-head.txt"
|
||||
SIG_FILE="$PACK_DIR/evidence-pack.sig"
|
||||
if [[ -f "$SIG_FILE" && -f "$AUDIT_PUB" ]] && command -v openssl >/dev/null 2>&1; then
|
||||
if openssl dgst -sha256 -verify "$AUDIT_PUB" -signature "$SIG_FILE" "$HEAD_FILE" >/dev/null 2>&1; then
|
||||
echo "EVIDENCE_PACK_VALID anchor=signed dir=$PACK_DIR"
|
||||
else
|
||||
echo "EVIDENCE_PACK_SIGNATURE_INVALID dir=$PACK_DIR" >&2
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
# SEC-01 (H-01): enforced mode treats a missing/unverifiable pack signature
|
||||
# as FAIL, otherwise deleting evidence-pack.sig after editing packed files
|
||||
# would still verify as a "valid unsigned" pack.
|
||||
if [[ "${CASAN_PROFILE:-}" == "prod" || "${CASAN_VERIFY_STRICT:-}" == "1" ]]; then
|
||||
MISSING=""
|
||||
[[ -f "$SIG_FILE" ]] || MISSING="$MISSING pack-sig"
|
||||
[[ -f "$AUDIT_PUB" ]] || MISSING="$MISSING pubkey"
|
||||
command -v openssl >/dev/null 2>&1 || MISSING="$MISSING openssl"
|
||||
echo "EVIDENCE_PACK_UNSIGNED_STRICT_FAIL dir=$PACK_DIR missing=${MISSING# }" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "EVIDENCE_PACK_VALID anchor=unsigned dir=$PACK_DIR"
|
||||
fi
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "Usage: evidence-pack.sh {pack|verify-pack} <run-id> [--out/--dir <dir>]" >&2
|
||||
exit 64 ;;
|
||||
esac
|
||||
Reference in New Issue
Block a user