Cretezy Cretezy Technology & Programming

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.

A suspicious AUR package assessment with its highlighted commit diff

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:

  1. opens the AUR Git repository;
  2. records the current commit;
  3. reads the complete PKGBUILD;
  4. builds a commit diff with PKGBUILD first, followed by the other changed files;
  5. sends that context to the selected AI provider;
  6. 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.

paru displaying remote and local AUR security assessments before an upgrade

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.

Avatar

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.