#!/usr/bin/env bash # AUR-Shield Client Installer # Configures pacman to use the AUR-Shield repo set -euo pipefail SHIELD_HOST="${AUR_SHIELD_HOST:-localhost}" SHIELD_PORT="${AUR_SHIELD_PORT:-8443}" echo "╔══════════════════════════════════════════════╗" echo "║ AUR-Shield Client Installer ║" echo "╚══════════════════════════════════════════════╝" echo "" if ! command -v pacman &>/dev/null; then echo "✗ This script requires Arch Linux (pacman)." exit 1 fi echo "Server: http://$SHIELD_HOST:$SHIELD_PORT" echo "" # Add pacman repo PACMAN_CONF="/etc/pacman.conf" REPO_ENTRY="[aur-shield] Server = http://$SHIELD_HOST:$SHIELD_PORT/repo SigLevel = Never" if grep -q "^\[aur-shield\]" "$PACMAN_CONF"; then echo "⚠ [aur-shield] already exists in $PACMAN_CONF — skipping" else echo "Adding [aur-shield] to $PACMAN_CONF..." sudo tee -a "$PACMAN_CONF" > /dev/null << EOF # AUR-Shield — AI-scanned AUR packages $REPO_ENTRY EOF echo "✓ Added repo entry" fi # Install safe-yay wrapper WRAPPER_PATH="/usr/local/bin/safe-yay" echo "Installing safe-yay wrapper to $WRAPPER_PATH..." sudo tee "$WRAPPER_PATH" > /dev/null << 'WRAPPER' #!/usr/bin/env bash # safe-yay — wrapper that routes AUR packages through AUR-Shield set -euo pipefail SHIELD_HOST="${AUR_SHIELD_HOST:-localhost}" SHIELD_PORT="${AUR_SHIELD_PORT:-8443}" # Exit codes (archcanary-compatible) # 0 = clean, 1 = warning, 2 = malicious/blocked if [ $# -eq 0 ]; then echo "Usage: safe-yay [package2 ...]" echo "Scans and builds AUR packages through AUR-Shield" echo "" echo "Options:" echo " --scan-only Scan without building" echo " --doctor Check AUR-Shield setup" exit 0 fi # --doctor health check if [ "$1" = "--doctor" ]; then echo "AUR-Shield Doctor" echo "================" echo "" # Check server STATUS=$(curl -sf "http://$SHIELD_HOST:$SHIELD_PORT/api/status" 2>/dev/null) if [ $? -eq 0 ]; then MODEL=$(echo "$STATUS" | python3 -c "import sys,json; print(json.loads(sys.stdin.read()).get('model','?'))" 2>/dev/null) CACHE=$(echo "$STATUS" | python3 -c "import sys,json; print(json.loads(sys.stdin.read()).get('cached_scans',0))" 2>/dev/null) echo " ✓ Server: http://$SHIELD_HOST:$SHIELD_PORT" echo " Model: $MODEL" echo " Cached scans: $CACHE" else echo " ✗ Server: http://$SHIELD_HOST:$SHIELD_PORT NOT REACHABLE" exit 1 fi # Check ollama OLLAMA=$(curl -sf "http://$SHIELD_HOST:11434/api/tags" 2>/dev/null) if [ $? -eq 0 ]; then echo " ✓ Ollama: running" else echo " ⚠ Ollama: not reachable on $SHIELD_HOST" fi # Check repo if grep -q "^\[aur-shield\]" /etc/pacman.conf 2>/dev/null; then echo " ✓ pacman repo: [aur-shield] configured" else echo " ⚠ pacman repo: [aur-shield] NOT in /etc/pacman.conf" fi echo "" echo "All checks passed." exit 0 fi # --scan-only mode SCAN_ONLY=false if [ "$1" = "--scan-only" ]; then SCAN_ONLY=true shift fi EXIT_CODE=0 for pkg in "$@"; do echo "→ Processing $pkg..." if [ "$SCAN_ONLY" = true ]; then RESPONSE=$(curl -sf "http://$SHIELD_HOST:$SHIELD_PORT/api/scan/$pkg" 2>&1) || { echo " ✗ Failed to scan $pkg" echo " $RESPONSE" EXIT_CODE=1 continue } else RESPONSE=$(curl -sf "http://$SHIELD_HOST:$SHIELD_PORT/api/build/$pkg" 2>&1) || { echo " ✗ Failed to process $pkg" echo " $RESPONSE" EXIT_CODE=1 continue } fi VERDICT=$(echo "$RESPONSE" | python3 -c "import sys,json; print(json.loads(sys.stdin.read()).get('verdict','error'))" 2>/dev/null) case "$VERDICT" in clean) echo " ✓ Clean — package verified" if [ "$SCAN_ONLY" = false ]; then echo " Installing via pacman..." sudo pacman -Sy "aur-shield/$pkg" || { echo " ⚠ Package not in repo yet — may need to wait for build" EXIT_CODE=1 } fi ;; suspicious) echo " ⚠ Suspicious — review recommended" echo " Report: http://$SHIELD_HOST:$SHIELD_PORT/api/report/$pkg" EXIT_CODE=1 if [ "$SCAN_ONLY" = false ]; then read -rp " Install anyway? [y/N] " FORCE if [[ "${FORCE,,}" == "y" ]]; then sudo pacman -Sy "aur-shield/$pkg" fi fi ;; malicious) echo " ✗ MALICIOUS — package blocked!" echo " Report: http://$SHIELD_HOST:$SHIELD_PORT/api/report/$pkg" # Show IOC matches if available echo "$RESPONSE" | python3 -c " import sys,json d = json.loads(sys.stdin.read()) for ioc in d.get('ioc_matches',[]): print(f\" IOC: {ioc['source']} — {ioc['description']}\") " 2>/dev/null EXIT_CODE=2 ;; *) echo " ? Error: $RESPONSE" EXIT_CODE=1 ;; esac done exit $EXIT_CODE WRAPPER sudo chmod +x "$WRAPPER_PATH" echo "✓ Installed safe-yay" echo "" echo "╔══════════════════════════════════════════════╗" echo "║ Client setup complete! ║" echo "╠══════════════════════════════════════════════╣" echo "║ ║" echo "║ Usage: ║" echo "║ safe-yay ║" echo "║ sudo pacman -S aur-shield/ ║" echo "║ ║" echo "║ Set server via env: ║" echo "║ export AUR_SHIELD_HOST=10.90.9.102 ║" echo "║ export AUR_SHIELD_PORT=8443 ║" echo "║ ║" echo "╚══════════════════════════════════════════════╝"