Reviewing the Arch User Repository with AUR Security
The Arch User Repository is one of the reasons I
like using Arch Linux. If a piece of software exists, there is a good chance
someone has already written a PKGBUILD for it.
That convenience comes with a specific trust model: a PKGBUILD is shell code
written by another user, and installing a package means running it. In June
2026, that stopped being an abstract warning.
On June 12, Arch Linux published an official notice about an active malicious-packages incident, describing a high volume of malicious package adoptions and updates. The team was removing malicious commits and trying to prevent new ones while account creation, package updates, and package adoption were restricted.
It was not a one-off incident. On July 31, Phoronix reported another wave of malicious packages and that Arch had halted package adoptions. The previous campaign had affected more than 1,500 packages, and the new wave already included dozens more. Arch disabled adoptions while the team handled the influx and again asked users to stay vigilant.
Arch’s advice was direct: review every PKGBUILD and install-script change
when updating AUR packages, especially while the incident was active.
Those incidents are the specific reason I started this project. The problem was not limited to an obviously fake new package nobody had heard of. A familiar or orphaned package could be adopted, receive a malicious update, and arrive through the same update workflow as a boring version bump.
Reading every update by hand does not scale very well, but reducing the answer to “an AI said it was safe” is not useful either. I wanted another review layer for exactly these changes: automatically inspect current package updates, preserve exactly what was reviewed, and make the evidence easy to inspect afterward.
Most AUR changes are still completely ordinary version bumps. Occasionally a download moves to another domain, a repository changes owners, or a new command appears in a packaging step. Those are the changes I want to notice before installing an update.
That became AUR Security, a small Rust service that indexes AUR package versions, uses AI to review each package update’s Git diff, and serves the results in a web interface.

You can browse the current results in the live demo.
The project is still early, but the complete index-to-review flow works. This post goes over what it records, how a check works, and the parts that took more iteration than I expected.
The problem with reviewing only a PKGBUILD
Looking at the current PKGBUILD is a useful start, but it loses the most
important context: what changed?
A package downloading a binary from its established upstream GitHub release is normal. The same package suddenly downloading from an unrelated domain is much more interesting. A new checksum is expected when the version changes. A new repository owner or delivery mechanism deserves attention even if the shell code still looks clean.
The AUR already stores each package base in Git, so the useful unit of review
is not just a file. It is a package version, its repository commit, the full
PKGBUILD, and the diff that introduced it.
How it works
The core is a reusable AI checker. A CLI uses it to review packages collected by the indexer, while a web application catalogs the resulting assessments and their evidence.
Checker library
The checker is a Rust library rather than a service of its own. It can clone an AUR package-base repository or inspect an existing checkout. For each update, it:
- opens the AUR Git repository;
- records the current commit;
- reads the complete
PKGBUILD; - builds a commit diff with
PKGBUILDfirst, followed by the other changed files; - sends that context to the selected AI provider;
- returns the verdict and explanation together with the source, diff, and commit it reviewed.
The project uses Rig for its AI agent
loop, which lets it support multiple providers without tying the checker to one
API. OpenAI, Anthropic, and OpenRouter are currently supported. The agent has a
single read_file tool so it can inspect other files in the cloned repository
when the diff needs more context.
It also supports the Codex and Claude Code CLIs, making it possible to run checks using a ChatGPT or Claude subscription instead of a separate API account. Both run from inside the cloned AUR repository with structured output and isolated sessions. Codex disables its shell tool and web search, while Claude runs in safe mode with only its read-only file, glob, and search tools.
The library has no dependency on the index database. The batch-checking CLI
uses it, and so does the experimental paru integration described below when
it needs to assess an update locally.
CLI and web application
The update-index CLI command downloads the AUR metadata index and appends
newly seen package versions to SQLite:
cargo run -p aur_security -- update-index
Package metadata is scoped to package name and version. The index keeps the AUR package and package-base IDs, submitter, last-modified time, snapshot path, and popularity. Versions present in the latest index are marked current, while old rows remain available for historical checks.
The check command selects current versions that have not already been checked
with the chosen provider and model, then passes them through the checker
library. It can be limited to exact package names or to packages modified after
a timestamp:
cargo run -p aur_security -- check \
--provider codex \
--model gpt-5.6-luna \
--since 24h \
--filter netcatty-bin zed-preview-bin
The package index and check results are stored in a SQLite database shared by the CLI and website. The web application is built with Topcoat, a server-rendered Rust web framework, and Tailwind. It provides a searchable catalog of packages and checks, with the source and diff behind each verdict available for review.
Run it with:
topcoat dev --package aur_security_web
From there, packages can be searched and their latest assessment, check
history, PKGBUILD, and update diff can be reviewed in the browser.
paru integration
I also built an experimental
aur-security branch
in my paru fork to bring these checks into the package installation workflow.
It can use the hosted API, the local checker library, or both. In remote-only
mode, paru looks up assessments for every commit since the last accepted
version. In local-only mode, it uses the checker library to review the
cumulative diff locally.
With both enabled, paru checks the hosted API first. It only runs a local
assessment when the remote results do not cover the complete update. The
results are combined, and the package is safe only when the full update is
covered and safe.
For example, these options enable remote lookups with a local Codex fallback:
[options]
AurSecurityRemote
# AurSecurityRemoteUrl = https://aur-security.cretezy.com
AurSecurityProvider = codex
AurSecurityModel = gpt-5.6-luna
# SkipSafeReviews
# SkipAurSecurity
The remote URL is optional and defaults to the hosted service. To use remote
assessments alone, omit AurSecurityProvider and AurSecurityModel. To run
only local assessments, omit AurSecurityRemote and set both the provider and
model.
SkipSafeReviews makes skipping manual review for safely assessed packages the
default and remembers them as the baseline for the next update, while
SkipAurSecurity disables both remote and local assessments.

The checks run after paru downloads the AUR repositories and before it starts
any pre-build commands or package builds.
Safe is not a score
Each check returns one of three verdicts:
- safe for ordinary packaging behavior;
- suspicious when a concrete change deserves human review;
- dangerous when there is strong evidence of malicious behavior.
Suspicious and dangerous results require an explanation. Safe results do not need filler text saying that normal packaging looks normal.
The prompt treats a version and checksum bump from the same upstream source as ordinary, along with using a verified upstream binary to generate completions or metadata. Changes to domains, repository owners, or download mechanisms receive more attention.
None of these verdicts prove that a package is safe. A model can miss malicious behavior, produce false positives, or trust an upstream binary that has itself been compromised. The project stores the evidence behind each result so it can be inspected; it reviews packaging and supply-chain signals, not the contents of downloaded binaries.
What is next
The immediate work is running the checker continuously, adding notifications for dangerous results, and improving the prompt where the models are consistently too noisy or too trusting.
The goal is not to fully trust AI with deciding whether a package is safe. It is to use it as an early-warning layer: when an update is clearly malicious, I want to know quickly, see the exact source behind the verdict, and be able to address it before more users are affected.
Who am I?
Hello! My name is Cretezy and I am a software developer. I write about programming, personal projects, and more.
See more posts here.