#!/usr/bin/env bash # SPDX-License-Identifier: AGPL-3.0-or-later # Copyright (c) StellaOps # # bundle.sh - Bundle SBOM validators for air-gap deployment # Sprint: SPRINT_20260107_005_003 Task VG-008 # # Usage: # ./bundle.sh [--output-dir DIR] [--platform PLATFORM] # # Options: # --output-dir DIR Output directory for bundle (default: ./bundle) # --platform PLATFORM Target platform (linux-amd64, linux-arm64, darwin-amd64, darwin-arm64, windows-amd64) # If not specified, bundles for current platform # --all-platforms Bundle for all supported platforms # --help Show this help message # # Examples: # ./bundle.sh # Bundle for current platform # ./bundle.sh --platform linux-amd64 # Bundle for specific platform # ./bundle.sh --all-platforms # Bundle for all platforms set -euo pipefail # Validator versions - pin for reproducibility SBOM_UTILITY_VERSION="0.17.0" SPDX_TOOLS_VERSION="1.1.9" # Download URLs SBOM_UTILITY_BASE="https://github.com/CycloneDX/sbom-utility/releases/download/v${SBOM_UTILITY_VERSION}" SPDX_TOOLS_BASE="https://github.com/spdx/tools-java/releases/download/v${SPDX_TOOLS_VERSION}" # Script directory SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # Defaults OUTPUT_DIR="${SCRIPT_DIR}/bundle" TARGET_PLATFORM="" ALL_PLATFORMS=false # Supported platforms PLATFORMS=("linux-amd64" "linux-arm64" "darwin-amd64" "darwin-arm64" "windows-amd64") # 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} $1" } log_warn() { echo -e "${YELLOW}[WARN]${NC} $1" } log_error() { echo -e "${RED}[ERROR]${NC} $1" >&2 } detect_platform() { 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 echo "${os}-${arch}" } show_help() { head -n 24 "$0" | tail -n +2 | sed 's/^# //' | sed 's/^#//' exit 0 } parse_args() { while [[ $# -gt 0 ]]; do case "$1" in --output-dir) OUTPUT_DIR="$2" shift 2 ;; --platform) TARGET_PLATFORM="$2" shift 2 ;; --all-platforms) ALL_PLATFORMS=true shift ;; --help|-h) show_help ;; *) log_error "Unknown option: $1" exit 1 ;; esac done if [[ -z "$TARGET_PLATFORM" && "$ALL_PLATFORMS" == "false" ]]; then TARGET_PLATFORM=$(detect_platform) fi } download_file() { local url="$1" local output="$2" log_info "Downloading: ${url}" if command -v curl &> /dev/null; then curl -fsSL -o "$output" "$url" elif command -v wget &> /dev/null; then wget -q -O "$output" "$url" else log_error "Neither curl nor wget found" exit 1 fi } verify_checksum() { local file="$1" local expected="$2" local actual actual=$(sha256sum "$file" | cut -d' ' -f1) if [[ "$actual" != "$expected" ]]; then log_error "Checksum mismatch for ${file}" log_error "Expected: ${expected}" log_error "Actual: ${actual}" return 1 fi log_info "Checksum verified: ${file}" return 0 } bundle_sbom_utility() { local platform="$1" local bundle_dir="$2" local os arch ext os="${platform%-*}" arch="${platform#*-}" ext="" [[ "$os" == "windows" ]] && ext=".exe" local filename="sbom-utility-v${SBOM_UTILITY_VERSION}-${os}-${arch}.tar.gz" local url="${SBOM_UTILITY_BASE}/${filename}" local temp_dir temp_dir=$(mktemp -d) log_info "Bundling sbom-utility for ${platform}..." download_file "$url" "${temp_dir}/${filename}" # Extract tar -xzf "${temp_dir}/${filename}" -C "${temp_dir}" # Copy binary local binary_name="sbom-utility${ext}" local src_binary="${temp_dir}/${binary_name}" local dest_binary="${bundle_dir}/sbom-utility/${SBOM_UTILITY_VERSION}/${binary_name}" mkdir -p "$(dirname "$dest_binary")" cp "$src_binary" "$dest_binary" chmod +x "$dest_binary" 2>/dev/null || true # Compute and record hash local hash hash=$(sha256sum "$dest_binary" | cut -d' ' -f1) echo "sbom-utility/${SBOM_UTILITY_VERSION}/${binary_name}:${hash}" >> "${bundle_dir}/SHA256SUMS" # Cleanup rm -rf "$temp_dir" log_info "sbom-utility ${SBOM_UTILITY_VERSION} bundled for ${platform}" } bundle_spdx_tools() { local bundle_dir="$1" local filename="tools-java-${SPDX_TOOLS_VERSION}-jar-with-dependencies.jar" local url="${SPDX_TOOLS_BASE}/${filename}" local temp_dir temp_dir=$(mktemp -d) log_info "Bundling spdx-tools (platform-independent JAR)..." download_file "$url" "${temp_dir}/${filename}" # Copy JAR local dest_jar="${bundle_dir}/spdx-tools/${SPDX_TOOLS_VERSION}/${filename}" mkdir -p "$(dirname "$dest_jar")" cp "${temp_dir}/${filename}" "$dest_jar" # Compute and record hash local hash hash=$(sha256sum "$dest_jar" | cut -d' ' -f1) echo "spdx-tools/${SPDX_TOOLS_VERSION}/${filename}:${hash}" >> "${bundle_dir}/SHA256SUMS" # Cleanup rm -rf "$temp_dir" log_info "spdx-tools ${SPDX_TOOLS_VERSION} bundled" } create_manifest() { local bundle_dir="$1" cat > "${bundle_dir}/manifest.json" << EOF { "schema": "stellaops.validator-bundle@1", "generatedAt": "$(date -u +%Y-%m-%dT%H:%M:%SZ)", "validators": { "sbom-utility": { "version": "${SBOM_UTILITY_VERSION}", "source": "https://github.com/CycloneDX/sbom-utility", "formats": ["cyclonedx-json", "cyclonedx-xml"] }, "spdx-tools": { "version": "${SPDX_TOOLS_VERSION}", "source": "https://github.com/spdx/tools-java", "formats": ["spdx-json", "spdx-rdf", "spdx-tag-value"], "requires": "java >= 11" } } } EOF log_info "Created manifest.json" } create_readme() { local bundle_dir="$1" cat > "${bundle_dir}/README.md" << 'EOF' # SBOM Validator Bundle This bundle contains pre-downloaded SBOM validators for air-gap deployments. ## Contents - **sbom-utility**: CycloneDX validator (Go binary) - **spdx-tools**: SPDX validator (Java JAR, requires JRE 11+) ## Installation 1. Copy this bundle to your air-gapped environment 2. Set `STELLAOPS_VALIDATOR_DIR` environment variable to point to this directory 3. Or configure in `appsettings.yaml`: ```yaml Scanner: Validation: BinaryDirectory: /path/to/validator-bundle OfflineMode: true ``` ## Verification Verify file integrity using the SHA256SUMS file: ```bash cd /path/to/validator-bundle sha256sum -c SHA256SUMS ``` ## Version Information See `manifest.json` for exact versions and source URLs. ## License - sbom-utility: Apache-2.0 - spdx-tools: Apache-2.0 EOF log_info "Created README.md" } bundle_platform() { local platform="$1" local bundle_dir="${OUTPUT_DIR}/${platform}" log_info "Creating bundle for platform: ${platform}" mkdir -p "$bundle_dir" # Initialize checksum file : > "${bundle_dir}/SHA256SUMS" # Bundle validators bundle_sbom_utility "$platform" "$bundle_dir" bundle_spdx_tools "$bundle_dir" # Create metadata files create_manifest "$bundle_dir" create_readme "$bundle_dir" # Sort checksums for determinism sort -o "${bundle_dir}/SHA256SUMS" "${bundle_dir}/SHA256SUMS" log_info "Bundle created: ${bundle_dir}" } main() { parse_args "$@" log_info "SBOM Validator Bundle Generator" log_info "sbom-utility version: ${SBOM_UTILITY_VERSION}" log_info "spdx-tools version: ${SPDX_TOOLS_VERSION}" log_info "Output directory: ${OUTPUT_DIR}" mkdir -p "$OUTPUT_DIR" if [[ "$ALL_PLATFORMS" == "true" ]]; then for platform in "${PLATFORMS[@]}"; do bundle_platform "$platform" done else bundle_platform "$TARGET_PLATFORM" fi log_info "Bundle generation complete!" log_info "To install, copy the bundle to your target system and set STELLAOPS_VALIDATOR_DIR" } main "$@"