262 lines
6.8 KiB
Bash
262 lines
6.8 KiB
Bash
#!/bin/bash
|
|
# scripts/validate-vex.sh
|
|
# Sprint: SPRINT_8200_0001_0003 - SBOM Schema Validation in CI
|
|
# Task: SCHEMA-8200-006 - Create validate-vex.sh wrapper for OpenVEX validation
|
|
#
|
|
# Validates OpenVEX files against the OpenVEX 0.2.0 JSON schema.
|
|
# Uses ajv-cli for JSON schema validation.
|
|
#
|
|
# Usage:
|
|
# ./scripts/validate-vex.sh <vex-file>
|
|
# ./scripts/validate-vex.sh bench/golden-corpus/sample.vex.json
|
|
# ./scripts/validate-vex.sh --all # Validate all VEX fixtures
|
|
#
|
|
# Exit codes:
|
|
# 0 - All validations passed
|
|
# 1 - Validation failed or error
|
|
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
|
SCHEMA_DIR="${REPO_ROOT}/docs/schemas"
|
|
DEFAULT_SCHEMA="${SCHEMA_DIR}/openvex-0.2.0.schema.json"
|
|
|
|
# Colors for output
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
NC='\033[0m' # No Color
|
|
|
|
log_info() {
|
|
echo -e "${GREEN}[INFO]${NC} $*"
|
|
}
|
|
|
|
log_warn() {
|
|
echo -e "${YELLOW}[WARN]${NC} $*"
|
|
}
|
|
|
|
log_error() {
|
|
echo -e "${RED}[ERROR]${NC} $*"
|
|
}
|
|
|
|
check_ajv() {
|
|
if ! command -v ajv &> /dev/null; then
|
|
log_warn "ajv-cli not found in PATH"
|
|
log_info "Installing ajv-cli via npm..."
|
|
|
|
if command -v npm &> /dev/null; then
|
|
npm install -g ajv-cli ajv-formats
|
|
elif command -v npx &> /dev/null; then
|
|
log_info "Using npx for ajv (no global install)"
|
|
return 0
|
|
else
|
|
log_error "npm/npx not found. Please install Node.js first."
|
|
exit 1
|
|
fi
|
|
|
|
log_info "ajv-cli installed successfully"
|
|
fi
|
|
}
|
|
|
|
run_ajv() {
|
|
local schema="$1"
|
|
local data="$2"
|
|
|
|
if command -v ajv &> /dev/null; then
|
|
ajv validate -s "$schema" -d "$data" --spec=draft2020 2>&1
|
|
elif command -v npx &> /dev/null; then
|
|
npx ajv-cli validate -s "$schema" -d "$data" --spec=draft2020 2>&1
|
|
else
|
|
log_error "No ajv available"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
validate_openvex() {
|
|
local vex_file="$1"
|
|
local schema="${2:-$DEFAULT_SCHEMA}"
|
|
|
|
if [[ ! -f "$vex_file" ]]; then
|
|
log_error "File not found: $vex_file"
|
|
return 1
|
|
fi
|
|
|
|
if [[ ! -f "$schema" ]]; then
|
|
log_error "Schema not found: $schema"
|
|
log_info "Expected schema at: ${DEFAULT_SCHEMA}"
|
|
log_info "Download from: https://raw.githubusercontent.com/openvex/spec/main/openvex_json_schema.json"
|
|
return 1
|
|
fi
|
|
|
|
# Detect if it's an OpenVEX file
|
|
if ! grep -qE '"@context".*"https://openvex.dev/ns"|"openvex"' "$vex_file" 2>/dev/null; then
|
|
log_warn "File does not appear to be OpenVEX: $vex_file"
|
|
log_info "Skipping (use validate-sbom.sh for CycloneDX files)"
|
|
return 0
|
|
fi
|
|
|
|
log_info "Validating: $vex_file"
|
|
|
|
# Run ajv validation
|
|
if run_ajv "$schema" "$vex_file"; then
|
|
log_info "✓ Validation passed: $vex_file"
|
|
return 0
|
|
else
|
|
log_error "✗ Validation failed: $vex_file"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
validate_all() {
|
|
local failed=0
|
|
local passed=0
|
|
local skipped=0
|
|
|
|
# Search multiple directories for VEX files
|
|
local search_dirs=(
|
|
"${REPO_ROOT}/bench/golden-corpus"
|
|
"${REPO_ROOT}/bench/vex-lattice"
|
|
"${REPO_ROOT}/datasets"
|
|
)
|
|
|
|
log_info "Validating all OpenVEX fixtures..."
|
|
|
|
for fixture_dir in "${search_dirs[@]}"; do
|
|
if [[ ! -d "$fixture_dir" ]]; then
|
|
log_warn "Directory not found, skipping: $fixture_dir"
|
|
continue
|
|
fi
|
|
|
|
log_info "Searching in: $fixture_dir"
|
|
|
|
while IFS= read -r -d '' file; do
|
|
# Check if it's an OpenVEX file
|
|
if grep -qE '"@context".*"https://openvex.dev/ns"|"openvex"' "$file" 2>/dev/null; then
|
|
if validate_openvex "$file"; then
|
|
((passed++))
|
|
else
|
|
((failed++))
|
|
fi
|
|
elif grep -q '"vex"' "$file" 2>/dev/null || [[ "$file" == *vex* ]]; then
|
|
# Might be VEX-related but not OpenVEX format
|
|
log_info "Checking potential VEX file: $file"
|
|
if grep -qE '"@context"' "$file" 2>/dev/null; then
|
|
if validate_openvex "$file"; then
|
|
((passed++))
|
|
else
|
|
((failed++))
|
|
fi
|
|
else
|
|
log_info "Skipping non-OpenVEX file: $file"
|
|
((skipped++))
|
|
fi
|
|
else
|
|
((skipped++))
|
|
fi
|
|
done < <(find "$fixture_dir" -type f \( -name '*vex*.json' -o -name '*.vex.json' -o -name '*openvex*.json' \) -print0 2>/dev/null || true)
|
|
done
|
|
|
|
echo ""
|
|
log_info "Validation Summary:"
|
|
log_info " Passed: ${passed}"
|
|
log_info " Failed: ${failed}"
|
|
log_info " Skipped: ${skipped}"
|
|
|
|
if [[ $failed -gt 0 ]]; then
|
|
log_error "Some validations failed!"
|
|
return 1
|
|
fi
|
|
|
|
if [[ $passed -eq 0 ]] && [[ $skipped -eq 0 ]]; then
|
|
log_warn "No OpenVEX files found to validate"
|
|
else
|
|
log_info "All OpenVEX validations passed!"
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
usage() {
|
|
cat << EOF
|
|
Usage: $(basename "$0") [OPTIONS] <vex-file>
|
|
|
|
Validates OpenVEX files against the OpenVEX 0.2.0 JSON schema.
|
|
|
|
Options:
|
|
--all Validate all OpenVEX fixtures in bench/ and datasets/
|
|
--schema <path> Use custom schema file (default: docs/schemas/openvex-0.2.0.schema.json)
|
|
--help, -h Show this help message
|
|
|
|
Examples:
|
|
$(basename "$0") sample.vex.json
|
|
$(basename "$0") --schema custom-schema.json sample.json
|
|
$(basename "$0") --all
|
|
|
|
Exit codes:
|
|
0 All validations passed
|
|
1 Validation failed or error
|
|
EOF
|
|
}
|
|
|
|
main() {
|
|
local schema="$DEFAULT_SCHEMA"
|
|
local validate_all_flag=false
|
|
local files=()
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--all)
|
|
validate_all_flag=true
|
|
shift
|
|
;;
|
|
--schema)
|
|
schema="$2"
|
|
shift 2
|
|
;;
|
|
--help|-h)
|
|
usage
|
|
exit 0
|
|
;;
|
|
-*)
|
|
log_error "Unknown option: $1"
|
|
usage
|
|
exit 1
|
|
;;
|
|
*)
|
|
files+=("$1")
|
|
shift
|
|
;;
|
|
esac
|
|
done
|
|
|
|
# Ensure ajv is available
|
|
check_ajv
|
|
|
|
if [[ "$validate_all_flag" == "true" ]]; then
|
|
validate_all
|
|
exit $?
|
|
fi
|
|
|
|
if [[ ${#files[@]} -eq 0 ]]; then
|
|
log_error "No VEX file specified"
|
|
usage
|
|
exit 1
|
|
fi
|
|
|
|
local failed=0
|
|
for file in "${files[@]}"; do
|
|
if ! validate_openvex "$file" "$schema"; then
|
|
((failed++))
|
|
fi
|
|
done
|
|
|
|
if [[ $failed -gt 0 ]]; then
|
|
exit 1
|
|
fi
|
|
|
|
exit 0
|
|
}
|
|
|
|
main "$@"
|