diff --git a/README.md b/README.md index fd26ef2..e30d57a 100644 --- a/README.md +++ b/README.md @@ -136,8 +136,29 @@ Based on [AegisAUR](https://gitea.die-heimatlosen.eu/arch_agent/aegisaur) IOC fe - `GET /api/build/` — Scan + build + add to repo - `GET /api/status` — Server status + cache info - `GET /api/report/` — Get last scan report +- `GET /api/cache` — List all cached scan results +- `DELETE /api/cache/` — Clear cached scan for a package - `GET /repo/` — Pacman repo endpoint (for client pacman.conf) +## Client Usage + +```bash +# Neue Pakete installieren (Scan + Build + Install) +safe-yay + +# System-Update (AUR-Updates werden gescannt vor Installation) +safe-yay -Syu + +# Nur scannen, nicht installieren +safe-yay --scan-only + +# Installierte AUR-Pakete gegen IOC-Listen pruefen +safe-yay --check-installed + +# Setup-Verifikation +safe-yay --doctor +``` + ## Models Tested models (smallest to best): diff --git a/install-client.sh b/install-client.sh index 174343d..2d30eea 100644 --- a/install-client.sh +++ b/install-client.sh @@ -54,11 +54,10 @@ SHIELD_PORT="${AUR_SHIELD_PORT:-8443}" 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" + echo " safe-yay -Syu # System-Update mit AUR-Scan" + echo " safe-yay --scan-only # Scan without building" + echo " safe-yay --check-installed # Installierte AUR-Pakete pruefen" + echo " safe-yay --doctor # Check AUR-Shield setup" exit 0 fi @@ -67,8 +66,6 @@ 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) @@ -80,24 +77,181 @@ if [ "$1" = "--doctor" ]; then 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." + echo "All checks passed." + exit 0 +fi + +# --check-installed: pruefe bereits installierte AUR-Pakete gegen IOC-Listen +if [ "$1" = "--check-installed" ]; then + echo "AUR-Shield: Installierte AUR-Pakete pruefen" + echo "==========================================" + echo "" + + # Foreign packages = AUR + manuell installierte + FOREIGN=$(pacman -Qmq 2>/dev/null) + if [ -z "$FOREIGN" ]; then + echo "Keine AUR-Pakete installiert." + exit 0 + fi + + COUNT=$(echo "$FOREIGN" | wc -l) + echo "Pruefe $COUNT installierte AUR-Pakete gegen IOC-Listen..." + echo "" + + MALICIOUS=0 + SUSPICIOUS=0 + CLEAN=0 + + for pkg in $FOREIGN; do + RESPONSE=$(curl -sf "http://$SHIELD_HOST:$SHIELD_PORT/api/scan/$pkg" 2>/dev/null) + if [ $? -ne 0 ]; then + 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 + malicious) + echo " ✗ MALICIOUS: $pkg" + 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 + MALICIOUS=$((MALICIOUS + 1)) + ;; + suspicious) + echo " ⚠ SUSPICIOUS: $pkg" + SUSPICIOUS=$((SUSPICIOUS + 1)) + ;; + clean) + CLEAN=$((CLEAN + 1)) + ;; + esac + done + + echo "" + echo "==========================================" + echo " Ergebnis:" + echo " Clean: $CLEAN" + echo " Suspicious: $SUSPICIOUS" + echo " Malicious: $MALICIOUS" + echo "==========================================" + + if [ $MALICIOUS -gt 0 ]; then + exit 2 + elif [ $SUSPICIOUS -gt 0 ]; then + exit 1 + fi + exit 0 +fi + +# -Syu: System-Update — scanne AUR-Updates bevor sie installiert werden +if [ "$1" = "-Syu" ] || [ "$1" = "-Sy" ]; then + echo "AUR-Shield: System-Update mit Pre-Scan" + echo "=======================================" + echo "" + + # Erst normales Repo-Update (ohne AUR) + echo "→ Repo-Updates..." + sudo pacman -Sy 2>&1 + + # AUR-Updates finden mit yay + if command -v yay &>/dev/null; then + echo "" + echo "→ Pruefe AUR-Updates..." + AUR_UPDATES=$(yay -Qua 2>/dev/null || true) + + if [ -z "$AUR_UPDATES" ]; then + echo " Keine AUR-Updates." + else + echo " AUR-Updates gefunden:" + echo "$AUR_UPDATES" + echo "" + echo "→ Scanne AUR-Updates durch AUR-Shield..." + + BLOCKED=0 + SAFE_UPDATES="" + + while IFS= read -r line; do + pkg=$(echo "$line" | awk '{print $1}') + pkg=$(echo "$pkg" | sed 's/[+]$//') # strip + suffix from yay output + + RESPONSE=$(curl -sf "http://$SHIELD_HOST:$SHIELD_PORT/api/scan/$pkg" 2>/dev/null) + if [ $? -ne 0 ]; then + echo " ⚠ $pkg: Scan fehlgeschlagen — ueberspringe" + SAFE_UPDATES="$SAFE_UPDATES $pkg" + 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 " ✓ $pkg: clean" + SAFE_UPDATES="$SAFE_UPDATES $pkg" + ;; + suspicious) + echo " ⚠ $pkg: SUSPICIOUS" + echo "$RESPONSE" | python3 -c " +import sys,json +d = json.loads(sys.stdin.read()) +for f in d.get('findings',[])[:3]: + print(f\" {f}\") +" 2>/dev/null + read -rp " Update $pkg trotzdem? [y/N] " FORCE + if [[ "${FORCE,,}" == "y" ]]; then + SAFE_UPDATES="$SAFE_UPDATES $pkg" + fi + ;; + malicious) + echo " ✗ $pkg: MALICIOUS — BLOCKIERT!" + 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 + BLOCKED=$((BLOCKED + 1)) + ;; + esac + done <<< "$AUR_UPDATES" + + echo "" + if [ $BLOCKED -gt 0 ]; then + echo "⚠ $BLOCKED Paket(e) blockiert!" + fi + + # Safe updates installieren + if [ -n "$SAFE_UPDATES" ]; then + echo "→ Installiere sichere AUR-Updates: $SAFE_UPDATES" + yay -S $SAFE_UPDATES + else + echo "→ Keine sicheren AUR-Updates." + fi + fi + else + echo " ⚠ yay nicht installiert — AUR-Updates manuell pruefen" + fi + + # Normale Repo-Updates installieren + echo "" + echo "→ Repo-Updates installieren..." + sudo pacman -Su 2>&1 + + echo "" + echo "System-Update abgeschlossen." exit 0 fi @@ -112,7 +266,7 @@ 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" @@ -128,9 +282,9 @@ for pkg in "$@"; do 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" @@ -156,7 +310,6 @@ for pkg in "$@"; do 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())