#!/bin/bash # scripts/validate-sbom.sh # Sprint: SPRINT_8200_0001_0003 - SBOM Schema Validation in CI # Task: SCHEMA-8200-004 - Create validate-sbom.sh wrapper for sbom-utility # # Validates SBOM files against official CycloneDX JSON schemas. # Uses sbom-utility for CycloneDX validation. # # Usage: # ./scripts/validate-sbom.sh [--schema ] # ./scripts/validate-sbom.sh bench/golden-corpus/sample.cyclonedx.json # ./scripts/validate-sbom.sh --all # Validate all CycloneDX 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}/cyclonedx-bom-1.6.schema.json" SBOM_UTILITY_VERSION="v0.16.0" # 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_sbom_utility() { if ! command -v sbom-utility &> /dev/null; then log_warn "sbom-utility not found in PATH" log_info "Installing sbom-utility ${SBOM_UTILITY_VERSION}..." # Detect OS and architecture local os arch case "$(uname -s)" in Linux*) os="linux";; Darwin*) os="darwin";; MINGW*|MSYS*|CYGWIN*) os="windows";; *) log_error "Unsupported OS: $(uname -s)"; exit 1;; esac case "$(uname -m)" in x86_64|amd64) arch="amd64";; arm64|aarch64) arch="arm64";; *) log_error "Unsupported architecture: $(uname -m)"; exit 1;; esac local url="https://github.com/CycloneDX/sbom-utility/releases/download/${SBOM_UTILITY_VERSION}/sbom-utility-${SBOM_UTILITY_VERSION}-${os}-${arch}.tar.gz" local temp_dir temp_dir=$(mktemp -d) log_info "Downloading from ${url}..." curl -sSfL "${url}" | tar xz -C "${temp_dir}" if [[ "$os" == "windows" ]]; then log_info "Please add ${temp_dir}/sbom-utility.exe to your PATH" export PATH="${temp_dir}:${PATH}" else log_info "Installing to /usr/local/bin (may require sudo)..." if [[ -w /usr/local/bin ]]; then mv "${temp_dir}/sbom-utility" /usr/local/bin/ else sudo mv "${temp_dir}/sbom-utility" /usr/local/bin/ fi fi rm -rf "${temp_dir}" log_info "sbom-utility installed successfully" fi } validate_cyclonedx() { local sbom_file="$1" local schema="${2:-$DEFAULT_SCHEMA}" if [[ ! -f "$sbom_file" ]]; then log_error "File not found: $sbom_file" return 1 fi if [[ ! -f "$schema" ]]; then log_error "Schema not found: $schema" log_info "Expected schema at: ${DEFAULT_SCHEMA}" return 1 fi # Detect if it's a CycloneDX file if ! grep -q '"bomFormat"' "$sbom_file" 2>/dev/null; then log_warn "File does not appear to be CycloneDX: $sbom_file" log_info "Skipping (use validate-spdx.sh for SPDX files)" return 0 fi log_info "Validating: $sbom_file" # Run sbom-utility validation if sbom-utility validate --input-file "$sbom_file" --format json 2>&1; then log_info "✓ Validation passed: $sbom_file" return 0 else log_error "✗ Validation failed: $sbom_file" return 1 fi } validate_all() { local fixture_dir="${REPO_ROOT}/bench/golden-corpus" local failed=0 local passed=0 local skipped=0 log_info "Validating all CycloneDX fixtures in ${fixture_dir}..." if [[ ! -d "$fixture_dir" ]]; then log_error "Fixture directory not found: $fixture_dir" return 1 fi while IFS= read -r -d '' file; do if grep -q '"bomFormat".*"CycloneDX"' "$file" 2>/dev/null; then if validate_cyclonedx "$file"; then ((passed++)) else ((failed++)) fi else log_info "Skipping non-CycloneDX file: $file" ((skipped++)) fi done < <(find "$fixture_dir" -type f -name '*.json' -print0) 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 log_info "All CycloneDX validations passed!" return 0 } usage() { cat << EOF Usage: $(basename "$0") [OPTIONS] Validates CycloneDX SBOM files against official JSON schemas. Options: --all Validate all CycloneDX fixtures in bench/golden-corpus/ --schema Use custom schema file (default: docs/schemas/cyclonedx-bom-1.6.schema.json) --help, -h Show this help message Examples: $(basename "$0") sample.cyclonedx.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 sbom-utility is available check_sbom_utility if [[ "$validate_all_flag" == "true" ]]; then validate_all exit $? fi if [[ ${#files[@]} -eq 0 ]]; then log_error "No SBOM file specified" usage exit 1 fi local failed=0 for file in "${files[@]}"; do if ! validate_cyclonedx "$file" "$schema"; then ((failed++)) fi done if [[ $failed -gt 0 ]]; then exit 1 fi exit 0 } main "$@"