feat(scripts): verify.sh — .brain-version + sha256 + strict-secrets check
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,30 @@
|
||||
#!/usr/bin/env bash
|
||||
set -u
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
source "$SCRIPT_DIR/lib/common.sh"
|
||||
|
||||
FAILURES=0
|
||||
assert_eq() {
|
||||
if [ "$1" = "$2" ]; then echo "PASS: $3"; else echo "FAIL: $3 (expected '$1', got '$2')"; FAILURES=$((FAILURES + 1)); fi
|
||||
}
|
||||
|
||||
# Test 1: missing .brain-version → exit 1
|
||||
tmpdir=$(mktemp -d)
|
||||
bash "$SCRIPT_DIR/verify.sh" --target="$tmpdir" >/dev/null 2>&1
|
||||
assert_eq "1" "$?" "missing .brain-version returns exit 1"
|
||||
rm -rf "$tmpdir"
|
||||
|
||||
# Test 2: valid .brain-version → exit 0 (минимальный manifest пустой)
|
||||
tmpdir=$(mktemp -d)
|
||||
echo "brain-v1.0" > "$tmpdir/.brain-version"
|
||||
echo "sha: 0000000000000000000000000000000000000000" >> "$tmpdir/.brain-version"
|
||||
# Создаём минимальный manifest в brain
|
||||
brain_dir=$(mktemp -d)
|
||||
echo '{"version": "brain-v1.0", "files": {}}' > "$brain_dir/manifest.json"
|
||||
BRAIN_ROOT="$brain_dir" bash "$SCRIPT_DIR/verify.sh" --target="$tmpdir" >/dev/null 2>&1
|
||||
assert_eq "0" "$?" "valid .brain-version returns exit 0"
|
||||
rm -rf "$tmpdir" "$brain_dir"
|
||||
|
||||
echo "---"
|
||||
echo "Failures: $FAILURES"
|
||||
exit $FAILURES
|
||||
@@ -0,0 +1,79 @@
|
||||
#!/usr/bin/env bash
|
||||
# Verify brain installation integrity
|
||||
# Checks: .brain-version, sha256 from manifest.json, settings.json validity, hooks syntax
|
||||
#
|
||||
# Usage: verify.sh --target=<path> [--strict-secrets]
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
source "$SCRIPT_DIR/lib/common.sh"
|
||||
|
||||
# Determine brain root: env BRAIN_ROOT or script's parent
|
||||
BRAIN_ROOT="${BRAIN_ROOT:-$(cd "$SCRIPT_DIR/.." && pwd)}"
|
||||
|
||||
target=""
|
||||
strict_secrets=0
|
||||
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--target=*) target="${1#--target=}" ;;
|
||||
--strict-secrets) strict_secrets=1 ;;
|
||||
*) log_error "Unknown: $1"; exit 1 ;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
[ -n "$target" ] || { log_error "--target required"; exit 1; }
|
||||
|
||||
# Check 1: .brain-version exists
|
||||
bv="$target/.brain-version"
|
||||
[ -f "$bv" ] || { log_error ".brain-version not found in $target"; exit 1; }
|
||||
|
||||
# Parse version + sha
|
||||
version=$(head -1 "$bv")
|
||||
sha_line=$(grep '^sha:' "$bv" || echo "")
|
||||
[ -n "$version" ] || { log_error ".brain-version: missing version line"; exit 1; }
|
||||
|
||||
log_info "Found .brain-version: $version"
|
||||
|
||||
# Check 2: manifest.json exists in brain root
|
||||
manifest="$BRAIN_ROOT/manifest.json"
|
||||
[ -f "$manifest" ] || { log_error "manifest.json missing in BRAIN_ROOT=$BRAIN_ROOT"; exit 1; }
|
||||
|
||||
# Check 3: sha256 per file in manifest (если manifest содержит files)
|
||||
files_count=$(jq -r '(.files // {}) | length' "$manifest")
|
||||
if [ "$files_count" -gt 0 ]; then
|
||||
log_info "Verifying $files_count file hashes..."
|
||||
while IFS= read -r entry; do
|
||||
rel_path=$(echo "$entry" | jq -r '.key')
|
||||
expected_sha=$(echo "$entry" | jq -r '.value')
|
||||
actual_path="$target/$rel_path"
|
||||
|
||||
if [ ! -f "$actual_path" ]; then
|
||||
log_error "Missing: $rel_path"
|
||||
exit 2
|
||||
fi
|
||||
|
||||
if command -v sha256sum >/dev/null 2>&1; then
|
||||
actual_sha=$(sha256sum "$actual_path" | cut -d' ' -f1)
|
||||
else
|
||||
actual_sha=$(certutil -hashfile "$actual_path" SHA256 | sed -n '2p' | tr -d ' \r')
|
||||
fi
|
||||
|
||||
if [ "$actual_sha" != "$expected_sha" ]; then
|
||||
log_error "SHA mismatch: $rel_path (expected $expected_sha, got $actual_sha)"
|
||||
exit 2
|
||||
fi
|
||||
done < <(jq -c '(.files // {}) | to_entries[]' "$manifest")
|
||||
fi
|
||||
|
||||
# Check 4: strict secrets — no unresolved <<*>> placeholders
|
||||
if [ "$strict_secrets" -eq 1 ]; then
|
||||
if grep -rE '<<[A-Z_]+>>' "$target" 2>/dev/null | grep -v '.brain-backup' | head -1; then
|
||||
log_error "Unresolved placeholders found"
|
||||
exit 7
|
||||
fi
|
||||
fi
|
||||
|
||||
log_info "Verification passed"
|
||||
exit 0
|
||||
Reference in New Issue
Block a user