feat: -Syu system update + --check-installed for installed AUR packages
- safe-yay -Syu: scans AUR updates through AUR-Shield before installing, blocks malicious updates, prompts for suspicious ones - safe-yay --check-installed: checks all installed AUR packages (pacman -Qmq) against IOC lists — like archcanary's installed-package scan - Both use exit codes: 0=clean, 1=warning, 2=malicious
This commit is contained in:
@@ -136,8 +136,29 @@ Based on [AegisAUR](https://gitea.die-heimatlosen.eu/arch_agent/aegisaur) IOC fe
|
|||||||
- `GET /api/build/<package>` — Scan + build + add to repo
|
- `GET /api/build/<package>` — Scan + build + add to repo
|
||||||
- `GET /api/status` — Server status + cache info
|
- `GET /api/status` — Server status + cache info
|
||||||
- `GET /api/report/<package>` — Get last scan report
|
- `GET /api/report/<package>` — Get last scan report
|
||||||
|
- `GET /api/cache` — List all cached scan results
|
||||||
|
- `DELETE /api/cache/<package>` — Clear cached scan for a package
|
||||||
- `GET /repo/<file>` — Pacman repo endpoint (for client pacman.conf)
|
- `GET /repo/<file>` — Pacman repo endpoint (for client pacman.conf)
|
||||||
|
|
||||||
|
## Client Usage
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Neue Pakete installieren (Scan + Build + Install)
|
||||||
|
safe-yay <package>
|
||||||
|
|
||||||
|
# System-Update (AUR-Updates werden gescannt vor Installation)
|
||||||
|
safe-yay -Syu
|
||||||
|
|
||||||
|
# Nur scannen, nicht installieren
|
||||||
|
safe-yay --scan-only <package>
|
||||||
|
|
||||||
|
# Installierte AUR-Pakete gegen IOC-Listen pruefen
|
||||||
|
safe-yay --check-installed
|
||||||
|
|
||||||
|
# Setup-Verifikation
|
||||||
|
safe-yay --doctor
|
||||||
|
```
|
||||||
|
|
||||||
## Models
|
## Models
|
||||||
|
|
||||||
Tested models (smallest to best):
|
Tested models (smallest to best):
|
||||||
|
|||||||
+170
-17
@@ -54,11 +54,10 @@ SHIELD_PORT="${AUR_SHIELD_PORT:-8443}"
|
|||||||
|
|
||||||
if [ $# -eq 0 ]; then
|
if [ $# -eq 0 ]; then
|
||||||
echo "Usage: safe-yay <package> [package2 ...]"
|
echo "Usage: safe-yay <package> [package2 ...]"
|
||||||
echo "Scans and builds AUR packages through AUR-Shield"
|
echo " safe-yay -Syu # System-Update mit AUR-Scan"
|
||||||
echo ""
|
echo " safe-yay --scan-only <pkg> # Scan without building"
|
||||||
echo "Options:"
|
echo " safe-yay --check-installed # Installierte AUR-Pakete pruefen"
|
||||||
echo " --scan-only <pkg> Scan without building"
|
echo " safe-yay --doctor # Check AUR-Shield setup"
|
||||||
echo " --doctor Check AUR-Shield setup"
|
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -67,8 +66,6 @@ if [ "$1" = "--doctor" ]; then
|
|||||||
echo "AUR-Shield Doctor"
|
echo "AUR-Shield Doctor"
|
||||||
echo "================"
|
echo "================"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
# Check server
|
|
||||||
STATUS=$(curl -sf "http://$SHIELD_HOST:$SHIELD_PORT/api/status" 2>/dev/null)
|
STATUS=$(curl -sf "http://$SHIELD_HOST:$SHIELD_PORT/api/status" 2>/dev/null)
|
||||||
if [ $? -eq 0 ]; then
|
if [ $? -eq 0 ]; then
|
||||||
MODEL=$(echo "$STATUS" | python3 -c "import sys,json; print(json.loads(sys.stdin.read()).get('model','?'))" 2>/dev/null)
|
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"
|
echo " ✗ Server: http://$SHIELD_HOST:$SHIELD_PORT NOT REACHABLE"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Check ollama
|
|
||||||
OLLAMA=$(curl -sf "http://$SHIELD_HOST:11434/api/tags" 2>/dev/null)
|
OLLAMA=$(curl -sf "http://$SHIELD_HOST:11434/api/tags" 2>/dev/null)
|
||||||
if [ $? -eq 0 ]; then
|
if [ $? -eq 0 ]; then
|
||||||
echo " ✓ Ollama: running"
|
echo " ✓ Ollama: running"
|
||||||
else
|
else
|
||||||
echo " ⚠ Ollama: not reachable on $SHIELD_HOST"
|
echo " ⚠ Ollama: not reachable on $SHIELD_HOST"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Check repo
|
|
||||||
if grep -q "^\[aur-shield\]" /etc/pacman.conf 2>/dev/null; then
|
if grep -q "^\[aur-shield\]" /etc/pacman.conf 2>/dev/null; then
|
||||||
echo " ✓ pacman repo: [aur-shield] configured"
|
echo " ✓ pacman repo: [aur-shield] configured"
|
||||||
else
|
else
|
||||||
echo " ⚠ pacman repo: [aur-shield] NOT in /etc/pacman.conf"
|
echo " ⚠ pacman repo: [aur-shield] NOT in /etc/pacman.conf"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo ""
|
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
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -112,7 +266,7 @@ EXIT_CODE=0
|
|||||||
|
|
||||||
for pkg in "$@"; do
|
for pkg in "$@"; do
|
||||||
echo "→ Processing $pkg..."
|
echo "→ Processing $pkg..."
|
||||||
|
|
||||||
if [ "$SCAN_ONLY" = true ]; then
|
if [ "$SCAN_ONLY" = true ]; then
|
||||||
RESPONSE=$(curl -sf "http://$SHIELD_HOST:$SHIELD_PORT/api/scan/$pkg" 2>&1) || {
|
RESPONSE=$(curl -sf "http://$SHIELD_HOST:$SHIELD_PORT/api/scan/$pkg" 2>&1) || {
|
||||||
echo " ✗ Failed to scan $pkg"
|
echo " ✗ Failed to scan $pkg"
|
||||||
@@ -128,9 +282,9 @@ for pkg in "$@"; do
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
fi
|
fi
|
||||||
|
|
||||||
VERDICT=$(echo "$RESPONSE" | python3 -c "import sys,json; print(json.loads(sys.stdin.read()).get('verdict','error'))" 2>/dev/null)
|
VERDICT=$(echo "$RESPONSE" | python3 -c "import sys,json; print(json.loads(sys.stdin.read()).get('verdict','error'))" 2>/dev/null)
|
||||||
|
|
||||||
case "$VERDICT" in
|
case "$VERDICT" in
|
||||||
clean)
|
clean)
|
||||||
echo " ✓ Clean — package verified"
|
echo " ✓ Clean — package verified"
|
||||||
@@ -156,7 +310,6 @@ for pkg in "$@"; do
|
|||||||
malicious)
|
malicious)
|
||||||
echo " ✗ MALICIOUS — package blocked!"
|
echo " ✗ MALICIOUS — package blocked!"
|
||||||
echo " Report: http://$SHIELD_HOST:$SHIELD_PORT/api/report/$pkg"
|
echo " Report: http://$SHIELD_HOST:$SHIELD_PORT/api/report/$pkg"
|
||||||
# Show IOC matches if available
|
|
||||||
echo "$RESPONSE" | python3 -c "
|
echo "$RESPONSE" | python3 -c "
|
||||||
import sys,json
|
import sys,json
|
||||||
d = json.loads(sys.stdin.read())
|
d = json.loads(sys.stdin.read())
|
||||||
|
|||||||
Reference in New Issue
Block a user