up
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
Signals CI & Image / signals-ci (push) Has been cancelled
Signals Reachability Scoring & Events / reachability-smoke (push) Has been cancelled
Signals Reachability Scoring & Events / sign-and-upload (push) Has been cancelled
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Reachability Corpus Validation / validate-corpus (push) Has been cancelled
Reachability Corpus Validation / validate-ground-truths (push) Has been cancelled
Scanner Analyzers / Discover Analyzers (push) Has been cancelled
Scanner Analyzers / Validate Test Fixtures (push) Has been cancelled
Reachability Corpus Validation / determinism-check (push) Has been cancelled
Scanner Analyzers / Build Analyzers (push) Has been cancelled
Scanner Analyzers / Test Language Analyzers (push) Has been cancelled
Scanner Analyzers / Verify Deterministic Output (push) Has been cancelled
Notify Smoke Test / Notify Unit Tests (push) Has been cancelled
Notify Smoke Test / Notifier Service Tests (push) Has been cancelled
Notify Smoke Test / Notification Smoke Test (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
Signals CI & Image / signals-ci (push) Has been cancelled
Signals Reachability Scoring & Events / reachability-smoke (push) Has been cancelled
Signals Reachability Scoring & Events / sign-and-upload (push) Has been cancelled
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Reachability Corpus Validation / validate-corpus (push) Has been cancelled
Reachability Corpus Validation / validate-ground-truths (push) Has been cancelled
Scanner Analyzers / Discover Analyzers (push) Has been cancelled
Scanner Analyzers / Validate Test Fixtures (push) Has been cancelled
Reachability Corpus Validation / determinism-check (push) Has been cancelled
Scanner Analyzers / Build Analyzers (push) Has been cancelled
Scanner Analyzers / Test Language Analyzers (push) Has been cancelled
Scanner Analyzers / Verify Deterministic Output (push) Has been cancelled
Notify Smoke Test / Notify Unit Tests (push) Has been cancelled
Notify Smoke Test / Notifier Service Tests (push) Has been cancelled
Notify Smoke Test / Notification Smoke Test (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
This commit is contained in:
198
bench/tools/verify.sh
Normal file
198
bench/tools/verify.sh
Normal file
@@ -0,0 +1,198 @@
|
||||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
# BENCH-AUTO-401-019: Online DSSE + Rekor verification script
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
REPO_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
|
||||
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m'
|
||||
|
||||
log_pass() { echo -e "${GREEN}✓${NC} $*"; }
|
||||
log_fail() { echo -e "${RED}✗${NC} $*"; }
|
||||
log_warn() { echo -e "${YELLOW}!${NC} $*"; }
|
||||
|
||||
usage() {
|
||||
echo "Usage: $0 <dsse-file> [--catalog PATH] [--rekor-url URL]"
|
||||
echo ""
|
||||
echo "Verify a VEX proof bundle with DSSE signature and Rekor inclusion."
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " --catalog PATH Path to justification catalog (default: docs/benchmarks/vex-justifications.catalog.json)"
|
||||
echo " --rekor-url URL Rekor URL (default: https://rekor.sigstore.dev)"
|
||||
echo " --offline Skip Rekor verification"
|
||||
echo " --help, -h Show this help"
|
||||
exit 1
|
||||
}
|
||||
|
||||
DSSE_FILE=""
|
||||
CATALOG="${REPO_ROOT}/docs/benchmarks/vex-justifications.catalog.json"
|
||||
REKOR_URL="https://rekor.sigstore.dev"
|
||||
OFFLINE=false
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
--catalog)
|
||||
CATALOG="$2"
|
||||
shift 2
|
||||
;;
|
||||
--rekor-url)
|
||||
REKOR_URL="$2"
|
||||
shift 2
|
||||
;;
|
||||
--offline)
|
||||
OFFLINE=true
|
||||
shift
|
||||
;;
|
||||
--help|-h)
|
||||
usage
|
||||
;;
|
||||
*)
|
||||
if [[ -z "$DSSE_FILE" ]]; then
|
||||
DSSE_FILE="$1"
|
||||
else
|
||||
echo "Unknown option: $1"
|
||||
usage
|
||||
fi
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ -z "$DSSE_FILE" ]]; then
|
||||
echo "Error: DSSE file required"
|
||||
usage
|
||||
fi
|
||||
|
||||
if [[ ! -f "$DSSE_FILE" ]]; then
|
||||
echo "Error: DSSE file not found: $DSSE_FILE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Verifying: $DSSE_FILE"
|
||||
echo ""
|
||||
|
||||
# Step 1: Validate JSON structure
|
||||
if ! python3 -c "import json; json.load(open('$DSSE_FILE'))" 2>/dev/null; then
|
||||
log_fail "Invalid JSON"
|
||||
exit 1
|
||||
fi
|
||||
log_pass "Valid JSON structure"
|
||||
|
||||
# Step 2: Check DSSE envelope structure
|
||||
PAYLOAD_TYPE=$(python3 -c "import json; print(json.load(open('$DSSE_FILE')).get('payloadType', ''))")
|
||||
if [[ -z "$PAYLOAD_TYPE" ]]; then
|
||||
log_fail "Missing payloadType"
|
||||
exit 1
|
||||
fi
|
||||
log_pass "DSSE payloadType: $PAYLOAD_TYPE"
|
||||
|
||||
# Step 3: Decode and validate payload
|
||||
PAYLOAD_B64=$(python3 -c "import json; print(json.load(open('$DSSE_FILE')).get('payload', ''))")
|
||||
if [[ -z "$PAYLOAD_B64" ]]; then
|
||||
log_fail "Missing payload"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Decode payload
|
||||
PAYLOAD_JSON=$(echo "$PAYLOAD_B64" | base64 -d 2>/dev/null || echo "")
|
||||
if [[ -z "$PAYLOAD_JSON" ]]; then
|
||||
log_fail "Failed to decode payload"
|
||||
exit 1
|
||||
fi
|
||||
log_pass "Payload decoded successfully"
|
||||
|
||||
# Step 4: Validate OpenVEX structure (if applicable)
|
||||
if [[ "$PAYLOAD_TYPE" == *"openvex"* ]]; then
|
||||
STATEMENTS_COUNT=$(echo "$PAYLOAD_JSON" | python3 -c "import json,sys; d=json.load(sys.stdin); print(len(d.get('statements', [])))")
|
||||
if [[ "$STATEMENTS_COUNT" -eq 0 ]]; then
|
||||
log_warn "OpenVEX has no statements"
|
||||
else
|
||||
log_pass "OpenVEX contains $STATEMENTS_COUNT statement(s)"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Step 5: Check signature presence
|
||||
SIG_COUNT=$(python3 -c "import json; print(len(json.load(open('$DSSE_FILE')).get('signatures', [])))")
|
||||
if [[ "$SIG_COUNT" -eq 0 ]]; then
|
||||
log_fail "No signatures found"
|
||||
exit 1
|
||||
fi
|
||||
log_pass "Found $SIG_COUNT signature(s)"
|
||||
|
||||
# Step 6: Check for placeholder signatures
|
||||
SIG_VALUE=$(python3 -c "import json; sigs=json.load(open('$DSSE_FILE')).get('signatures', []); print(sigs[0].get('sig', '') if sigs else '')")
|
||||
if [[ "$SIG_VALUE" == "PLACEHOLDER"* ]]; then
|
||||
log_warn "Signature is a placeholder (not yet signed)"
|
||||
else
|
||||
log_pass "Signature present (verification requires public key)"
|
||||
fi
|
||||
|
||||
# Step 7: Rekor verification (if online)
|
||||
if [[ "$OFFLINE" == false ]]; then
|
||||
# Check for rekor.txt in same directory
|
||||
DSSE_DIR=$(dirname "$DSSE_FILE")
|
||||
REKOR_FILE="${DSSE_DIR}/rekor.txt"
|
||||
|
||||
if [[ -f "$REKOR_FILE" ]]; then
|
||||
LOG_INDEX=$(grep -E "^log_index:" "$REKOR_FILE" | cut -d: -f2 | tr -d ' ')
|
||||
if [[ "$LOG_INDEX" != "PENDING" && -n "$LOG_INDEX" ]]; then
|
||||
log_pass "Rekor log index: $LOG_INDEX"
|
||||
|
||||
# Verify with Rekor API
|
||||
if command -v curl &>/dev/null; then
|
||||
REKOR_RESP=$(curl -s "${REKOR_URL}/api/v1/log/entries?logIndex=${LOG_INDEX}" 2>/dev/null || echo "")
|
||||
if [[ -n "$REKOR_RESP" && "$REKOR_RESP" != "null" ]]; then
|
||||
log_pass "Rekor inclusion verified"
|
||||
else
|
||||
log_warn "Could not verify Rekor inclusion (may be offline or index invalid)"
|
||||
fi
|
||||
else
|
||||
log_warn "curl not available for Rekor verification"
|
||||
fi
|
||||
else
|
||||
log_warn "Rekor entry pending submission"
|
||||
fi
|
||||
else
|
||||
log_warn "No rekor.txt found - Rekor verification skipped"
|
||||
fi
|
||||
else
|
||||
log_warn "Offline mode - Rekor verification skipped"
|
||||
fi
|
||||
|
||||
# Step 8: Check justification catalog membership
|
||||
if [[ -f "$CATALOG" ]]; then
|
||||
# Extract justification from payload if present
|
||||
JUSTIFICATION=$(echo "$PAYLOAD_JSON" | python3 -c "
|
||||
import json, sys
|
||||
d = json.load(sys.stdin)
|
||||
stmts = d.get('statements', [])
|
||||
if stmts:
|
||||
print(stmts[0].get('justification', ''))
|
||||
" 2>/dev/null || echo "")
|
||||
|
||||
if [[ -n "$JUSTIFICATION" ]]; then
|
||||
CATALOG_MATCH=$(python3 -c "
|
||||
import json
|
||||
catalog = json.load(open('$CATALOG'))
|
||||
entries = catalog if isinstance(catalog, list) else catalog.get('entries', [])
|
||||
ids = [e.get('id', '') for e in entries]
|
||||
print('yes' if '$JUSTIFICATION' in ids else 'no')
|
||||
" 2>/dev/null || echo "no")
|
||||
|
||||
if [[ "$CATALOG_MATCH" == "yes" ]]; then
|
||||
log_pass "Justification '$JUSTIFICATION' found in catalog"
|
||||
else
|
||||
log_warn "Justification '$JUSTIFICATION' not in catalog"
|
||||
fi
|
||||
fi
|
||||
else
|
||||
log_warn "Justification catalog not found at $CATALOG"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Verification complete."
|
||||
Reference in New Issue
Block a user