#!/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 # ./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] Validates OpenVEX files against the OpenVEX 0.2.0 JSON schema. Options: --all Validate all OpenVEX fixtures in bench/ and datasets/ --schema 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 "$@"