How to review an AUR package
On Friday, July 18th, 2025, the Arch Linux team was notified that three AUR packages had been uploaded that contained malware. A few maintainers including myself took care of deleting these packages, removing all traces of the malicious code, and protecting against future malicious uploads.
My fellow maintainer Quentin Michaud already did a nice write-up about how the malware worked, so I won’t go into detail too much about that. If you want to know more about that, read his blog. Instead, I’d like to do a crash course on how these packaging scripts work, and how you would review them yourself.
What is the AUR?
The Arch User Repository is a collection of packaging scripts, PKGBUILD files,
created by users. Anyone who creates an account on aur.archlinux.org, can upload an Arch
Linux packaging script, granted one doesn’t already exist for the same name. There are of course
some rules around what you can submit (e.g. don’t duplicate other official or
AUR packages) but generally anything goes.
Each package has one primary maintainer, by default whoever uploaded the packaging script first, but that can change over time, either by that maintainer transferring responsibility themselves, or by moderators removing the maintainer for various reasons. This is not a democracy, or even a meritocracy, but it works, and there is a lot of useful software on there.
Installing from AUR packaging scripts is not quite as simple as installing from the main packaging
repos. You can run makepkg on a PKGBUILD and install the resulting package, but this
gets more difficult as those PKGBUILDs often depend on other packages only found in the AUR. To
make this easier, people often use AUR-helpers, which provide a pacman-like experience to manage
them. That does come with some drawbacks, however.
Anyone who wants to do so can upload their PKGBUILD to the AUR. That is great to lower the barrier
to entry, and it is how I got my start contributing to Arch Linux. Unfortunately, not everyone has
the best intentions, and it has happened that people upload malware. Because of this, it is crucial
that you vet the PKGBUILDs that you install.
What do PKGBUILDs look like?
As mentioned before, the AUR does not contain packages, but rather contains build scripts to create
packages. Arch Linux PKGBUILDs are simply bash scripts that follow a certain pattern. Take for
example the following example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# Maintainer: John Doe <john@example.org>
pkgname=example
pkgver=1.0
pkgrel=1
pkgdesc="An example package"
arch=(x86_64)
url="https://example.org/"
license=('WTFPL')
install="example.install"
source=("https://example.org/$pkgname-$pkgver.tar.gz"
"$pkgname-$pkgver.patch::https://git.example.org/example/pull/42.patch")
sha256sums=("de4bba005e86b4fbf0399e2cb492b1e941099b951a45137212507c2cbc8fae63"
"b6dc933311bc2357cc5fc636a4dbe41a01b7a33b583d043a7f870f3440697e27")
prepare() {
cd "$pkgname-$pkgver"
patch -p1 -i "$srcdir/$pkgname-$pkgver.patch"
}
build() {
cd "$pkgname-$pkgver"
./configure --prefix=/usr
make
}
check() {
cd "$pkgname-$pkgver"
make -k check
}
package() {
cd "$pkgname-$pkgver"
make DESTDIR="$pkgdir/" install
}
It starts with a maintainer-line, which is not used programmatically but helps users of the software contact the maintainer if something doesn’t work. It is good practice to also have the emails of previous maintainers there, to recognise their contributions. Then we come to a few metadata variables.
Metadata
pkgname- defines the package we are building at the moment.
pkgver- signifies which upstream release of the software is being built. This number should only ever
increase, as
pacmanwill use this number to determine if an update is necessary.1 pkgrel- is the package release number. This release number is incremented every time an update is done to
the
PKGBUILDwithout updating thepkgver, such as when the packaging options change. It resets to1every time a new upstream release is done. pkgdesc- is a short, human-readable description of the package. This is shown in package lists and is used for searching, but doesn’t affect the built software otherwise.
arch- is a list of valid CPU architectures this package can be built for. It can also be the special
array
('any'), which does not (only) denote that this package can be built for any architecture, but rather that the resulting package does not contain any architecture-specific code. This is common for packages written in languages such as Python. url- is just link a package user might go for more information and does not otherwise affect the package.
license- is the licence under which this package is distributed. This used to be an array of all applicable licences for the project, but since the meaning of that is a bit ambiguous, these days the licence array should hold a single item which is an SPDX license expression.
install- is usually not present in a
PKGBUILD. If present, it refers to the name of a script (included alongside the package file) with specific bash functions that will get executed when the package is installed, upgraded, or removed. It is run frompacman, and therefore as root. source- is an array of sources. These can be either downloadable URLs, version control URLs such as git, or simple file names, meaning the file in question in part of the package files.
sha256sums- is another array, of the same length as sources, specifying the expected SHA-256 checksums for
the package sources. There are other hashing algorithms available, from
crc32sumstob2sums. Multiple sets of digests may be specified but at least one should be. The magic valueSKIPmay be used to skip verifying checksums for certain files, for example for external checksums, or PGP signatures.
After all that metadata, we get to the actual code that does something: the packaging functions.
Build functions
Building packages is done in 4 stages, each with their own bash function. It starts with
prepare(). This function is expected to make all the necessary changes to the source code, such as
applying patch files or editing out certain lines with sed. In certain ecosystems, such as Node.js
and Rust, it also downloads the dependencies. Ideally, all steps following prepare() no longer
need network access.
Then we get to build(), which, as the name suggests, builds the package. This is the place to call
configure scripts, compilers, and whatever else produces the binaries involved. This is, generally,
the part that takes time.
After packaging, there is usually a check() step. Here we want to check whether the package
functions as intended, within the Arch Linux packaging. What to do here varies, but most PKGBUILDs
try to run the package’s unit tests. Arch Linux generally has never versions of libraries than the
ones upstream projects test with, and such tests form a reasonable smoke test that things did not
break in unexpected ways.
Finally, the package() function is called. This is the only function that’s technically required,
all the others can be omitted. This function should move the files, generated in the build() step,
into their final positions relative to some $pkgdir. Many ecosystems provide (an equivalent of)
make install for this, which makes life easier, but often you will simply have to spell this out.
What to look out for
Now that we know what the different parts of a PKGBUILD mean, we can talk about reviewing its
contents. I want to go over a few of the items I find most important, but this is by no means an
exhaustive list. The fact I’m writing this will probably affect the strategies people use in some
way. Nevertheless, let’s fist…
Check the sources array
Regardless of what the PKGBUILD itself says, if the sources it’s going to compile are malicious,
nothing else really matters. Make sure that you trust the upstream project, and that files are
downloaded from a reasonable place, such as tags in official git repositories, or source releases on
the official website. PGP signatures from the authors are generally even better, so you know who
created, or at least signed off on the sources.
The Arch Linux project has accepted an RFC on what sources should be used, in order to have the most trustworthy sources for packaging. While such rigour isn’t necessary or, in some cases, even desirable for the AUR, you can use it as inspiration.
Patches are also sources, and malware can hide in them. You should have a look at any patches being applied to the source code, and, if possible where they come from. It’s not unusual to apply merged-but-not-released changes from upstream to deal with newer versions of libraries, but make sure it makes sense.
Check if the build steps make sense
The prepare(), build(), check(), and package() functions are inherently code that will be
executed on your machine when you build a package so if any
-
No downloads should happen in
build(),check(), orpackage(), and ideally not inprepare(), though many ecosystems (Go, Node.js, Rust to name a few) require it. -
The commands run by the script should be obvious packaging commands, and any custom scripts should come from the upstream repos. Often the packaging script matches the upstream “how to install” but often it doesn’t, as package maintainers generally call the build tools directly rather than call wrapper scripts.
-
Build scripts should not run
sudoor anything similar. If it does that anyway, it’s wrong. At best, it’s a packaging error, assudoshouldn’t be expected to work in a non-interactive environment like a build chroot. Sometimes a packager mistakenly tries to move package files into place instead of adding them to the package.
Scrutinise install scripts
Install scripts are, as mentioned above, rarely seen, but if when they are, they need to be scrutinised as their contents will run, as root, when a package is installed, upgraded, or removed. Most software doesn’t need this, as common use cases have safer alternatives.
Similarly, newly installed pacman hooks should also raise an eyebrow. Pacman hooks install to
/usr/share/libalpm/hooks (and sometimes to /etc/pacman.d/hooks though that’s incorrect) and can
run a command whenever its triggers are hit. The ones included in the main Arch repos are benign and
in fact reduce the need for install scripts, by automatically regenerating font- and icon caches
as needed, regenerating the initramfs, and many more. An arbitrary PKGBUILD adding one of their
own is unusual, and you should pay attention to what that hook is trying to do.
If you don’t understand it, don’t use it
While we try to keep it clean, the AUR is freely accessible to anyone who figures out how to get past the CAPTCHA, and anyone can upload code to it. Even popular, well-intentioned code can have bugs sometimes and do harm to your system. The AUR is, at the best of times, maintained by volunteers, many of which are currently getting their feet wet for the first time while packaging. Mistakes can and will happen, and that’s okay.
When something might be malicious
Now that we have a hunch as to what might be malicious, we can decide what to do with it. The
easiest way is to simply ask. The best place for that, in my opinion, is the #archlinux-aur IRC
channel on Libera, which is the official venue for AUR discussion.
The forums are also a good place to ask. If all fails, there is also the mailing list.
If the package is indeed malicious, one of the Package Maintainers can take it down and block the offending user(s). Should they try to avoid their ban, the measures will also escalate. As a user, that’s where your input ends.
That feels too simple
And it is. The AUR was made for a different kind of internet. More cooperative, less hostile. It’s based on trust that users will generally do sensible things.2 The AUR is old software. It started as “just some FTP server” where people could upload files. Such a system would not be designed in 2026.
There are improvements to be had for sure. The moderation systems are somewhat archaic, the
contribution workflow is not what people expect. The licensing of PKGBUILD files, especially when
the original author is long gone, is tricky.3 A pull-request like system is often suggested.
I think that would be a real improvement. In general, the system needs more love. That’s what you
get with open source projects; if no one wants to work on it, it gets no work. And that’s okay.
Right now I believe there is no end in sight for the AUR. We can keep going as-is for a while longer. But if people were to step up to make things better, we can do a lot more.
-
It’s slightly more complicated than comparing just the
pkgver. Thepkgrelis used as a tiebreaker, andepochexists for when the version needs to jump backwards for whatever reason. However ↩︎ -
It is also built for a world where web scrapers do not try to gobble up as much data as they can. The git history viewer wasn’t made for LLM-crawler abuse. Anubis has helped a little though. ↩︎
-
In many or even most cases,
PKGBUILDfiles should not be copyrightable, as they are largely mandatory text without creative input. No particular author owns./configure && make && make install. But it’s not unthinkable that something required sufficiently complex code to build within the Arch Linux ecosystem, and that might qualify. What are the terms by which these files were shared? I don’t know, I’m not a lawyer. ↩︎