#!/usr/bin/env bash # License validation script for StellaOps CI # Usage: validate-licenses.sh # type: nuget | npm # input-file: Path to package list or license-checker output set -euo pipefail # SPDX identifiers for licenses compatible with AGPL-3.0-or-later ALLOWED_LICENSES=( "MIT" "Apache-2.0" "Apache 2.0" "BSD-2-Clause" "BSD-3-Clause" "BSD" "ISC" "0BSD" "CC0-1.0" "CC0" "Unlicense" "PostgreSQL" "MPL-2.0" "MPL 2.0" "LGPL-2.1-or-later" "LGPL-3.0-or-later" "GPL-3.0-or-later" "AGPL-3.0-or-later" "Zlib" "WTFPL" "BlueOak-1.0.0" "Python-2.0" "(MIT OR Apache-2.0)" "(Apache-2.0 OR MIT)" "MIT OR Apache-2.0" "Apache-2.0 OR MIT" ) # Licenses that are OK but should be noted CONDITIONAL_LICENSES=( "MPL-2.0" "LGPL-2.1-or-later" "LGPL-3.0-or-later" "CC-BY-4.0" ) # Licenses that are NOT compatible with AGPL-3.0-or-later BLOCKED_LICENSES=( "GPL-2.0-only" "SSPL-1.0" "SSPL" "BUSL-1.1" "BSL-1.0" "Commons Clause" "Proprietary" "Commercial" "UNLICENSED" ) TYPE="${1:-}" INPUT="${2:-}" if [[ -z "$TYPE" || -z "$INPUT" ]]; then echo "Usage: $0 " exit 1 fi if [[ ! -f "$INPUT" ]]; then echo "ERROR: Input file not found: $INPUT" exit 1 fi echo "=== StellaOps License Validation ===" echo "Type: $TYPE" echo "Input: $INPUT" echo "" found_blocked=0 found_conditional=0 found_unknown=0 validate_npm() { local input="$1" echo "Validating npm licenses..." # Extract licenses from license-checker JSON output if command -v jq &> /dev/null; then jq -r 'to_entries[] | "\(.key): \(.value.licenses)"' "$input" 2>/dev/null | while read -r line; do pkg=$(echo "$line" | cut -d: -f1) license=$(echo "$line" | cut -d: -f2- | xargs) # Check if license is blocked for blocked in "${BLOCKED_LICENSES[@]}"; do if [[ "$license" == *"$blocked"* ]]; then echo "BLOCKED: $pkg uses '$license'" found_blocked=$((found_blocked + 1)) fi done # Check if license is allowed allowed=0 for ok_license in "${ALLOWED_LICENSES[@]}"; do if [[ "$license" == *"$ok_license"* ]]; then allowed=1 break fi done if [[ $allowed -eq 0 ]]; then echo "UNKNOWN: $pkg uses '$license'" found_unknown=$((found_unknown + 1)) fi done else echo "WARNING: jq not available, performing basic grep check" for blocked in "${BLOCKED_LICENSES[@]}"; do if grep -qi "$blocked" "$input"; then echo "BLOCKED: Found potentially blocked license: $blocked" found_blocked=$((found_blocked + 1)) fi done fi } validate_nuget() { local input="$1" echo "Validating NuGet licenses..." # NuGet package list doesn't include licenses directly # We check for known problematic packages # Known packages with compatible licenses (allowlist approach for critical packages) known_good_patterns=( "Microsoft." "System." "Newtonsoft.Json" "Serilog" "BouncyCastle" "Npgsql" "Dapper" "Polly" "xunit" "Moq" "FluentAssertions" "CycloneDX" "YamlDotNet" "StackExchange.Redis" "Google." "AWSSDK." "Grpc." ) # Check if any packages don't match known patterns echo "Checking for unknown packages..." # This is informational - we trust the allowlist in THIRD-PARTY-DEPENDENCIES.md echo "OK: NuGet validation relies on documented license allowlist" echo "See: docs/legal/THIRD-PARTY-DEPENDENCIES.md" } case "$TYPE" in npm) validate_npm "$INPUT" ;; nuget) validate_nuget "$INPUT" ;; *) echo "ERROR: Unknown type: $TYPE" echo "Supported types: nuget, npm" exit 1 ;; esac echo "" echo "=== Validation Summary ===" echo "Blocked licenses found: $found_blocked" echo "Conditional licenses found: $found_conditional" echo "Unknown licenses found: $found_unknown" if [[ $found_blocked -gt 0 ]]; then echo "" echo "ERROR: Blocked licenses detected!" echo "These licenses are NOT compatible with AGPL-3.0-or-later" echo "Please remove or replace the affected packages" exit 1 fi if [[ $found_unknown -gt 0 ]]; then echo "" echo "WARNING: Unknown licenses detected" echo "Please review and add to allowlist if compatible" echo "See: docs/legal/LICENSE-COMPATIBILITY.md" # Don't fail on unknown - just warn fi echo "" echo "License validation: PASSED" exit 0