Security scanner for AUR PKGBUILDs. Detects common malicious patterns
(curl … | bash, reverse shells, writes to authorized_keys, sudo inside
the PKGBUILD, suid bits, fork bombs, dd to /dev/sd*, all-SKIP checksums,
sources over plain HTTP, etc.) before makepkg runs the build script, and
optionally as a second layer right before pacman installs the package.
Goal: provide a safety net against compromised AUR PKGBUILDs without having to read every PKGBUILD by hand. It does not replace manual review — it complements it.
aur-guard installs two checkpoints:
makepkgshim (/usr/local/bin/makepkg) — runs before the realmakepkg. This is the only useful point at which a malicious PKGBUILD can actually be blocked. It also fires when paru, yay or any AUR helper invokesmakepkg.- pacman PreTransaction hook (
/etc/pacman.d/hooks/aur-guard.hook) — second layer. Audits the PKGBUILDs of foreign (AUR) packages by locating them in the known AUR-helper caches, and lets you abort the transaction.
Both checkpoints prompt for interactive confirmation over /dev/tty when
there are high- or critical-severity findings. With no interactive terminal,
they block by default (unless AUR_GUARD_ASSUME=yes).
git clone <repo> aur-guard
cd aur-guard
sudo ./install.shinstall.sh builds the release binary, places it in /usr/local/bin/,
installs the makepkg shim and registers the pacman hook. Useful flags:
sudo ./install.sh --no-hook # shim only
sudo ./install.sh --no-shim # hook only
sudo ./install.sh uninstall # remove binary, shim and hook
aur-guard scan PKGBUILD # scan and print findings
aur-guard scan /path/to/package/ # finds PKGBUILD inside the directory
aur-guard check PKGBUILD # same scan; exit 0 if clean, 2 with findings
aur-guard check --threshold critical PKGBUILD
aur-guard rules # list every active ruleOnce the shim is installed the flow is transparent:
yay -S some-aur-package
# → paru/yay clones and calls makepkg
# → the shim invokes aur-guard, which scans the PKGBUILD
# → on high/critical findings it asks for confirmation and aborts on "no"
# → if clean, it execs the real makepkgaur-guard plugs into AUR helpers (paru, yay, pikaur, trizen, aurutils, …)
through PATH order, without touching the helper's configuration. There is
nothing to register, no plugin to install — only one assumption: that
/usr/local/bin sits before /usr/bin in your PATH.
On a default Arch user account the PATH looks like:
/usr/local/sbin:/usr/local/bin:/usr/bin
Every AUR helper invokes makepkg by name, letting the shell resolve it
through PATH. With /usr/local/bin first, the binary the helper finds is the
aur-guard shim. The shim scans the PKGBUILD in the current working directory
and, if the user accepts (or there are no findings), execs the real
/usr/bin/makepkg with the original arguments. The helper sees the exit code
of the real makepkg and behaves exactly as it would have without the shim.
which makepkg
# expected: /usr/local/bin/makepkgIf you instead see /usr/bin/makepkg, something (a shell rc file, an entry in
/etc/profile.d/, a custom systemd user unit) is putting /usr/bin before
/usr/local/bin. Fix the PATH or use one of the explicit integrations below.
| Helper | Default behaviour | Notes |
|---|---|---|
| paru | Works out of the box | The config key [bin] Makepkg = "makepkg" is the default and resolves via PATH. If you previously set it to an absolute path (/usr/bin/makepkg) in ~/.config/paru/paru.conf or /etc/paru.conf, change it back to "makepkg" or point it at "/usr/local/bin/makepkg". |
| yay | Works out of the box | Same situation: MakepkgBin defaults to makepkg. Check it with yay -P --getconfig | grep -i makepkg. If you ran yay --save --makepkg /usr/bin/makepkg at some point, undo it with yay --save --makepkg makepkg. |
| pikaur | Works out of the box | Resolves makepkg through PATH. |
| trizen | Works out of the box | Same. |
aurutils (aur build) |
Works out of the box | Same. |
paru --chroot or yay --chroot |
Does not see the shim | These build inside a clean systemd-nspawn / mkarchroot container that has no /usr/local/bin/makepkg. The pacman PreTransaction hook still fires once the resulting .pkg.tar.zst is about to be installed, so the second layer of defence still applies. |
Manual pacman -U some-package.pkg.tar.zst |
Hook only | The shim is bypassed entirely because makepkg is not invoked, but the pacman hook still audits the PKGBUILD if it is available in a known AUR-helper cache. |
Both points below are optional. Use them if the PATH trick is not viable in your environment.
paru PreBuildCommand — paru natively supports running a command before
each build. In ~/.config/paru/paru.conf (or /etc/paru.conf):
[bin]
PreBuildCommand = /usr/local/bin/aur-guard check --threshold high .PreBuildCommand is treated as a gate by paru: if it exits non-zero, paru
aborts the build. Using check --threshold high is recommended in this slot
because there is no interactive prompt — the exit code decides.
yay does not expose a pre-build hook. With yay, stick to the PATH shim, or use the two-step flow:
yay -G some-aur-package # only clone the package
aur-guard scan some-aur-package/ # scan
cd some-aur-package && makepkg -siSometimes you really do want to bypass the scan (you have already audited the PKGBUILD by hand, you are reinstalling a known-good package, …):
AUR_GUARD_DISABLE=1 yay -S some-aur-packageThe shim detects the variable and execs the real makepkg directly without
scanning. The pacman hook is independent and will still run unless you also
remove or disable /etc/pacman.d/hooks/aur-guard.hook.
The shim is the only checkpoint that runs before the PKGBUILD executes,
so it is the only one that can prevent a prepare()/build() payload from
doing damage. The pacman hook is downstream and only sees the already-built
.pkg.tar.zst, but it covers cases the shim cannot: chroot builds, manual
pacman -U, sudo PATH issues, and corrupted helper configs. The two layers
together mean a malicious PKGBUILD has to slip past both PATH resolution and
the post-build audit.
| Variable | Effect |
|---|---|
AUR_GUARD_DISABLE=1 |
The makepkg shim skips the scan and calls the real makepkg directly. |
AUR_GUARD_ASSUME=yes |
With no interactive TTY, assume "yes" at the prompt. Do not use in cron or unattended scripts. |
AUR_GUARD_REAL_MAKEPKG |
Path to the real makepkg (default /usr/bin/makepkg). |
AUR_GUARD_BIN |
Path to the aur-guard binary used by the shim (default /usr/local/bin/aur-guard). |
AUR_GUARD_CACHE_DIRS |
Colon-separated list of extra directories where the pacman hook should look for PKGBUILDs. |
NO_COLOR=1 |
Disable coloured output. |
30 rules grouped into families. Run aur-guard rules for the full list.
| Family | Covers |
|---|---|
| AG001–AG004 | Remote content execution (`curl |
| AG010–AG013 | Reverse shells (nc -e, /dev/tcp, python, perl) |
| AG020–AG023 | Destructive commands (rm -rf /, dd to disk, mkfs, fork bomb) |
| AG030–AG034 | Persistence (authorized_keys, .bashrc, crontab, systemd, useradd) |
| AG040–AG042 | Privilege escalation (sudo in PKGBUILD, suid, setcap) |
| AG050–AG052 | Obfuscation (base64, xxd, huge base64 strings) |
| AG060–AG062 | Suspicious network (literal IPs, URL shorteners, tunnels) |
| AG070–AG072 | Credential / wallet access and exfiltration |
| AG080–AG082 | PKGBUILD metadata (SKIP checksums, http / git+http sources) |
Severities are CRITICAL, HIGH, MEDIUM, LOW. By default the shim asks
for confirmation when at least one finding is ≥HIGH.
- Not a full bash static analyser. The rules are carefully tuned regexes: they may produce false positives in unusual projects and false negatives if an attacker obfuscates with enough effort. This is a safety net, not a guarantee.
- A pure pacman hook runs after
makepkg, so only the shim can prevent the malicious PKGBUILD from executing. The hook serves as auditing and as a way to abort the final install. - If an attacker replaces the PKGBUILD with something that looks benign and
ships the malicious payload inside the source tarball (a packaged binary, a
script called from
make install, etc.),aur-guardwill not see it. Rules AG080/AG081/AG082 at least warn when source integrity is disabled or sent over an unencrypted channel.
Edit src/patterns.rs and add a rule!(id, severity, title, description, regex) entry. If the regex needs to match quote characters, use hash raw
strings (r#"…"#) rather than r"…".
Test with:
cargo build --release
./target/release/aur-guard scan test-fixtures/PKGBUILD.maliciousMIT — see LICENSE.