#!/usr/bin/env bash # Package advisory feeds (SBOM pointers + provenance) for release/offline kit # Usage: ./package-advisory-feeds.sh # Dev mode: COSIGN_ALLOW_DEV_KEY=1 COSIGN_PASSWORD=stellaops-dev ./package-advisory-feeds.sh set -euo pipefail ROOT=$(cd "$(dirname "$0")/../../.." && pwd) OUT_DIR="${OUT_DIR:-$ROOT/out/advisory-ai/feeds}" CREATED="${CREATED:-$(date -u +%Y-%m-%dT%H:%M:%SZ)}" mkdir -p "$OUT_DIR" # Key resolution (same pattern as tools/cosign/sign-signals.sh) resolve_key() { if [[ -n "${COSIGN_KEY_FILE:-}" && -f "$COSIGN_KEY_FILE" ]]; then echo "$COSIGN_KEY_FILE" elif [[ -n "${COSIGN_PRIVATE_KEY_B64:-}" ]]; then local tmp_key="$OUT_DIR/.cosign.key" echo "$COSIGN_PRIVATE_KEY_B64" | base64 -d > "$tmp_key" chmod 600 "$tmp_key" echo "$tmp_key" elif [[ -f "$ROOT/tools/cosign/cosign.key" ]]; then echo "$ROOT/tools/cosign/cosign.key" elif [[ "${COSIGN_ALLOW_DEV_KEY:-0}" == "1" && -f "$ROOT/tools/cosign/cosign.dev.key" ]]; then echo "[info] Using development key (non-production)" >&2 echo "$ROOT/tools/cosign/cosign.dev.key" else echo "[error] No signing key available. Set COSIGN_PRIVATE_KEY_B64 or COSIGN_ALLOW_DEV_KEY=1" >&2 return 1 fi } KEY_FILE=$(resolve_key) # Collect advisory feed sources FEED_SOURCES=( "$ROOT/docs/samples/advisory-feeds" "$ROOT/src/AdvisoryAI/feeds" "$ROOT/out/feeds" ) echo "==> Collecting advisory feeds..." STAGE_DIR="$OUT_DIR/stage" mkdir -p "$STAGE_DIR" for src in "${FEED_SOURCES[@]}"; do if [[ -d "$src" ]]; then echo " Adding feeds from $src" cp -r "$src"/* "$STAGE_DIR/" 2>/dev/null || true fi done # Create placeholder if no feeds found (dev mode) if [[ -z "$(ls -A "$STAGE_DIR" 2>/dev/null)" ]]; then echo "[info] No feed sources found; creating placeholder for dev mode" cat > "$STAGE_DIR/placeholder.json" < Creating feed bundle..." BUNDLE_TAR="$OUT_DIR/advisory-feeds.tar.gz" tar -czf "$BUNDLE_TAR" -C "$STAGE_DIR" . # Compute hashes sha256() { sha256sum "$1" | awk '{print $1}' } BUNDLE_HASH=$(sha256 "$BUNDLE_TAR") # Generate manifest with SBOM pointers echo "==> Generating manifest..." MANIFEST="$OUT_DIR/advisory-feeds.manifest.json" cat > "$MANIFEST" </dev/null || stat -f%z "$BUNDLE_TAR") }, "sbom": { "format": "spdx-json", "path": "advisory-feeds.sbom.json", "note": "SBOM generated during CI; pointer only in manifest" }, "provenance": { "path": "provenance.json", "builder": "stellaops-advisory-ai-release" } } EOF # Sign manifest with DSSE echo "==> Signing manifest..." DSSE_OUT="$OUT_DIR/advisory-feeds.manifest.dsse.json" # Check for cosign COSIGN="${COSIGN:-$ROOT/tools/cosign/cosign}" if ! command -v cosign &>/dev/null && [[ ! -x "$COSIGN" ]]; then echo "[warn] cosign not found; skipping DSSE signing" >&2 else COSIGN_CMD="${COSIGN:-cosign}" if command -v cosign &>/dev/null; then COSIGN_CMD="cosign" fi COSIGN_PASSWORD="${COSIGN_PASSWORD:-}" "$COSIGN_CMD" sign-blob \ --key "$KEY_FILE" \ --bundle "$DSSE_OUT" \ --tlog-upload=false \ --yes \ "$MANIFEST" 2>/dev/null || echo "[warn] DSSE signing skipped (cosign error)" fi # Generate provenance echo "==> Generating provenance..." PROVENANCE="$OUT_DIR/provenance.json" cat > "$PROVENANCE" </dev/null || echo "dev-$(date +%s)")", "startedOn": "$CREATED" } } } } EOF # Cleanup temp key [[ -f "$OUT_DIR/.cosign.key" ]] && rm -f "$OUT_DIR/.cosign.key" echo "==> Advisory feed packaging complete" echo " Bundle: $BUNDLE_TAR" echo " Manifest: $MANIFEST" echo " DSSE: $DSSE_OUT" echo " Provenance: $PROVENANCE"