secubox-openwrt/scripts/sbom-audit-feed.sh
CyberMind-FR 8769a60275 feat(sbom): Add CRA Annex I compliant SBOM pipeline
Implements comprehensive Software Bill of Materials generation for
EU Cyber Resilience Act compliance with ANSSI CSPN certification path.

SBOM Pipeline:
- scripts/check-sbom-prereqs.sh: Prerequisites validation (OpenWrt, tools, Kconfig)
- scripts/sbom-generate.sh: Multi-source SBOM generation (native, feed, rootfs, firmware)
- scripts/sbom-audit-feed.sh: PKG_HASH/PKG_LICENSE feed audit with MANIFEST.md
- Makefile: SBOM targets (sbom, sbom-quick, sbom-validate, sbom-scan, sbom-audit)
- .github/workflows/sbom-release.yml: CI with CVE gating and auto-security issues

Documentation:
- SECURITY.md: CRA Art. 13 §6 compliant vulnerability disclosure policy
- docs/sbom-pipeline.md: Architecture, CRA mapping, ANSSI CSPN guidance

AI Gateway (bonus feed):
- secubox-ai-gateway: 3-tier data classification (LOCAL_ONLY/SANITIZED/CLOUD_DIRECT)
- luci-app-ai-gateway: LuCI dashboard with provider management and audit logging

Output formats: CycloneDX 1.6 (primary) + SPDX 2.3 (secondary)
Tools: syft, grype, cyclonedx-cli (auto-installed if missing)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-03-04 08:01:00 +01:00

220 lines
5.8 KiB
Bash
Executable File

#!/bin/bash
# SecuBox Feed Audit Script
# Audits SecuBox feed Makefiles for SBOM metadata completeness
#
# Copyright (C) 2026 CyberMind Produits SASU
# License: GPL-2.0-only
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
TOPDIR="${SCRIPT_DIR}/.."
FEED_DIR="${FEED_DIR:-${TOPDIR}/feeds/secubox}"
OUTPUT_DIR="${OUTPUT_DIR:-${FEED_DIR}}"
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
log_ok() { echo -e "${GREEN}[✓]${NC} $*"; }
log_warn() { echo -e "${YELLOW}[!]${NC} $*"; }
log_fail() { echo -e "${RED}[✗]${NC} $*"; }
MISSING_HASH=()
MISSING_LICENSE=()
PACKAGES=()
# Extract variable from Makefile
extract_var() {
local file="$1"
local var="$2"
grep -E "^${var}\s*[:?]?=" "$file" 2>/dev/null | head -1 | sed 's/.*=\s*//' | tr -d ' ' || echo ""
}
# Try to compute hash from downloaded source
compute_hash() {
local pkg_name="$1"
local pkg_version="$2"
local dl_dir="${TOPDIR}/dl"
local patterns=(
"${dl_dir}/${pkg_name}-${pkg_version}.tar.gz"
"${dl_dir}/${pkg_name}-${pkg_version}.tar.xz"
"${dl_dir}/${pkg_name}-${pkg_version}.tar.bz2"
"${dl_dir}/${pkg_name}_${pkg_version}.orig.tar.gz"
)
for file in "${patterns[@]}"; do
if [[ -f "$file" ]]; then
sha256sum "$file" | cut -d' ' -f1
return 0
fi
done
return 1
}
audit_package() {
local makefile="$1"
local pkg_dir
pkg_dir=$(dirname "$makefile")
local dir_name
dir_name=$(basename "$pkg_dir")
local pkg_name pkg_version pkg_license pkg_hash pkg_source_url
pkg_name=$(extract_var "$makefile" "PKG_NAME")
[[ -z "$pkg_name" ]] && pkg_name="$dir_name"
pkg_version=$(extract_var "$makefile" "PKG_VERSION")
[[ -z "$pkg_version" ]] && pkg_version="unknown"
pkg_license=$(extract_var "$makefile" "PKG_LICENSE")
pkg_hash=$(extract_var "$makefile" "PKG_HASH")
pkg_source_url=$(extract_var "$makefile" "PKG_SOURCE_URL")
# Check for missing hash
local hash_status="❌ MISSING"
local computed_hash=""
if [[ -n "$pkg_hash" && "$pkg_hash" != "skip" ]]; then
hash_status="${pkg_hash:0:12}..."
else
# Try to compute from local download
computed_hash=$(compute_hash "$pkg_name" "$pkg_version" 2>/dev/null || echo "")
if [[ -n "$computed_hash" ]]; then
hash_status="⚠️ COMPUTED: ${computed_hash:0:12}..."
else
MISSING_HASH+=("$pkg_name")
fi
fi
# Check for missing license
local license_status="❌ MISSING"
if [[ -n "$pkg_license" ]]; then
license_status="$pkg_license"
else
MISSING_LICENSE+=("$pkg_name")
fi
# Store package info
PACKAGES+=("$pkg_name|$pkg_version|$license_status|$hash_status|${pkg_source_url:-N/A}")
# Output computed hash suggestion if found
if [[ -n "$computed_hash" && ( -z "$pkg_hash" || "$pkg_hash" == "skip" ) ]]; then
echo " → Suggested: PKG_HASH:=${computed_hash}" >&2
fi
}
generate_manifest() {
local manifest="${OUTPUT_DIR}/MANIFEST.md"
cat > "$manifest" <<EOF
# SecuBox Feed — Package Manifest
_Generated by scripts/sbom-audit-feed.sh — $(date -Iseconds)_
| Package | Version | License | PKG_HASH | Source URL |
|---------|---------|---------|----------|------------|
EOF
for pkg_info in "${PACKAGES[@]}"; do
IFS='|' read -r name version license hash source <<< "$pkg_info"
echo "| $name | $version | $license | $hash | $source |" >> "$manifest"
done
cat >> "$manifest" <<EOF
## Summary
- **Total packages:** ${#PACKAGES[@]}
- **Missing PKG_HASH:** ${#MISSING_HASH[@]}
- **Missing PKG_LICENSE:** ${#MISSING_LICENSE[@]}
EOF
if [[ ${#MISSING_HASH[@]} -gt 0 ]]; then
echo "### Packages Missing PKG_HASH" >> "$manifest"
echo "" >> "$manifest"
for pkg in "${MISSING_HASH[@]}"; do
echo "- \`$pkg\`" >> "$manifest"
done
echo "" >> "$manifest"
fi
if [[ ${#MISSING_LICENSE[@]} -gt 0 ]]; then
echo "### Packages Missing PKG_LICENSE" >> "$manifest"
echo "" >> "$manifest"
for pkg in "${MISSING_LICENSE[@]}"; do
echo "- \`$pkg\`" >> "$manifest"
done
echo "" >> "$manifest"
fi
log_ok "Generated: $manifest"
}
main() {
echo "=========================================="
echo "SecuBox Feed Audit"
echo "=========================================="
echo ""
if [[ ! -d "$FEED_DIR" ]]; then
log_fail "Feed directory not found: $FEED_DIR"
exit 1
fi
echo "Scanning: $FEED_DIR"
echo ""
local count=0
for makefile in "$FEED_DIR"/*/Makefile; do
[[ -f "$makefile" ]] || continue
((count++))
audit_package "$makefile"
done
echo ""
echo "=========================================="
echo "Audit Results"
echo "=========================================="
echo ""
echo "Total packages: $count"
echo "Missing PKG_HASH: ${#MISSING_HASH[@]}"
echo "Missing PKG_LICENSE: ${#MISSING_LICENSE[@]}"
echo ""
generate_manifest
if [[ ${#MISSING_HASH[@]} -gt 0 || ${#MISSING_LICENSE[@]} -gt 0 ]]; then
echo ""
log_warn "Some packages have missing metadata (see MANIFEST.md)"
if [[ ${#MISSING_HASH[@]} -gt 0 ]]; then
echo ""
echo "Packages missing PKG_HASH:"
for pkg in "${MISSING_HASH[@]}"; do
echo " - $pkg"
done
fi
if [[ ${#MISSING_LICENSE[@]} -gt 0 ]]; then
echo ""
echo "Packages missing PKG_LICENSE:"
for pkg in "${MISSING_LICENSE[@]}"; do
echo " - $pkg"
done
fi
echo ""
log_fail "Fix missing metadata for CRA compliance"
exit 1
else
log_ok "All packages have required metadata"
exit 0
fi
}
main "$@"