feat: extended IOC sources + archcanary features
Extended IOC sources (from archcanary): - aur-audit.wtako.net black/red API (3rd-party continuous AUR scanner) - Community reports list (community-curated malicious packages) - CHAOS RAT campaign list (backdoor payload) - Russian spam campaign list (.bashrc injection) Client features (archcanary-inspired): - Exit codes: 0=clean, 1=warning, 2=malicious (scriptable) - --doctor health check (server, ollama, repo status) - --scan-only mode (scan without building) - IOC match display in malicious blocks - Suspicious packages: interactive install prompt All IOC fetches run concurrently for speed.
This commit is contained in:
+96
-18
@@ -49,52 +49,130 @@ 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 <package> [package2 ...]"
|
||||
echo "Scans and builds AUR packages through AUR-Shield"
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " --scan-only <pkg> 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..."
|
||||
|
||||
# Call AUR-Shield to scan + build
|
||||
RESPONSE=$(curl -sf "http://$SHIELD_HOST:$SHIELD_PORT/api/build/$pkg" 2>&1) || {
|
||||
echo "✗ Failed to process $pkg"
|
||||
echo " $RESPONSE"
|
||||
continue
|
||||
}
|
||||
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
|
||||
|
||||
# Extract verdict
|
||||
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 built and added to repo"
|
||||
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 — package built but review recommended"
|
||||
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"
|
||||
continue
|
||||
# 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"
|
||||
continue
|
||||
EXIT_CODE=1
|
||||
;;
|
||||
esac
|
||||
|
||||
# Now install via pacman
|
||||
echo " Installing via pacman..."
|
||||
sudo pacman -Sy "aur-shield/$pkg" || {
|
||||
echo " ⚠ Package not in repo yet — may need to wait for build"
|
||||
}
|
||||
done
|
||||
|
||||
echo "Done."
|
||||
exit $EXIT_CODE
|
||||
WRAPPER
|
||||
|
||||
sudo chmod +x "$WRAPPER_PATH"
|
||||
|
||||
Reference in New Issue
Block a user