lenucksi/aur-malware-check: The emergency scanner that turned an AUR panic into a forensic playbook

A Bash-and-Python toolkit that does more than list suspicious packages. It checks install timing, persistence artifacts, and eBPF traces, then folds scattered community findings into one trusted workflow.

8 to 10 min read View on GitHub More from lenucksi

A crowded workbench where loose notes, terminal windows, and package names are being gathered into one bound field manual. The scene explains how fragmented community findings became a single response tool during a supply-chain crisis.
The project’s real job was consolidation: turning scattered emergency guidance into something people could trust and run immediately.

I've just pushed 'aur-malware-check' to GitHub. It consolidates the various Gists and scripts floating around for the 'atomic-lockfile' AUR attack. Please use it to scan your systems and contribute any new signatures you find. This is a community effort. #archlinux #aur #security

lenucksi, Project Maintainer · lenucksi's announcement on X
Key Takeaways

When the AUR got hit, the first problem was not detection. It was trust. People did not need another hot take, another thread, or another half-baked gist. They needed one place where the community had already done the ugly work of gathering signatures, normalizing them, and turning them into something that could be run safely on a live system.

That is why lenucksi/aur-malware-check reads like incident response infrastructure, not a normal utility. The repository is a response layer built under pressure, and its usefulness comes from speed, specificity, and consolidation. It is less about malware as a category and more about what happens when an ecosystem loses confidence in its own package stream.

When the AUR needed one trusted answer

The attack created a familiar security problem with a very specific shape. The community had fragments of truth scattered across forums, Gists, and social posts. Some of those fragments were useful, some were stale, and some were just noise. This repo’s first contribution was editorial, in the best sense: it selected, merged, and stabilized the signal.

A hedcut-style portrait of lenucksi based on a verified GitHub avatar. The portrait supports the origin story by showing the maintainer as the person who consolidated the response into one repo.

That matters because security crises reward the most legible answer, not the most elaborate one. In the first hours of an incident, users are deciding whether to panic, wait, or execute a script they do not fully understand. A single maintained repository changes the risk profile of that decision.

A close-up forensic scene showing a package list in the foreground with hidden layers beneath it. The lower layers reveal systemd persistence markers and a concealed eBPF path, explaining that the scanner looks for post-exploitation artifacts, not just suspicious names.
The surprise in this tool is that package matching is only the first pass. It also looks for signs that malware stayed behind after installation.

What this scanner looks for that ordinary checkers miss

The obvious job is package detection. The less obvious job is forensic context. In the shell implementation, the tool checks installed foreign packages with pacman -Qmq, compares them to malicious package lists, and then filters by install timing so it can reduce false positives from people who installed the same package before the compromise window.

That temporal filter is the difference between a scary output and a useful one. Without it, a scanner can only say, "you have this package." With it, the tool can say, "you have this package, and you installed it during the period that matters." In incident response, that distinction saves hours.

The merge layer is the real brain of the project. It turns a messy stream of community data into a curated detection set before the scan even starts.

The shell script also checks for post-exploitation artifacts that ordinary package lists will never see. That includes persistence signatures like systemd services with Restart=always and RestartSec=30, plus eBPF traces such as /sys/fs/bpf/hidden_*. Those checks move the repo from "is this package suspicious" to "did something stay behind after the package ran?"

I have reviewed the code in lenucksi/aur-malware-check. It is a straightforward bash script that primarily uses grep to search for known malicious patterns. While running any third-party script requires caution, especially during a crisis, this tool appears safe and is currently our best defense against this specific attack.

eschwartz, Arch Linux Trusted User · eschwartz's code audit comment on Reddit
A hedcut-style portrait of eschwartz based on a verified GitHub avatar. The portrait supports the audit angle by showing a community reviewer validating the tool during the crisis.

The shell script is the frontline

The shell version is the emergency layer because it is frictionless. It runs on the target system with minimal dependencies, which matters when the user’s first instinct is not to install a new framework or library. It is a practical choice for a crisis tool: small enough to audit, simple enough to run, and close enough to the metal to be useful immediately.

# Core idea: build a trusted list, then scan the system against it.
mapfile -t foreign_pkgs < <(pacman -Qmq)
for pkg in "${foreign_pkgs[@]}"; do
  if [[ -n ${MALICIOUS[$pkg]} ]]; then
    echo "MATCH: $pkg"
  fi
done

# Filter by install window to reduce false positives.
date_in_window() {
  local installed="$1"
  [[ "$installed" > "2026-06-09" && "$installed" < "2026-06-12" ]]
}

That performance story matters more than it sounds. Version 2 replaced a lot of subprocess-heavy text wrangling with Bash-native regex and associative arrays. In plain English, the script got faster because it stopped asking the shell to do every tiny thing by spawning more tools to do it.

In a live response workflow, that is a material improvement. People do not want a forensic scanner that feels like a build pipeline. They want something that finishes while the situation is still actionable.

Why the Python port exists at all

The Python implementation is not a vanity rewrite. It exists because a crisis tool eventually needs structure. Bash can be excellent for portability and immediate response, but Python gives the project dataclasses, clearer parsing, unit tests, and a result model that can survive beyond the emergency window.

from dataclasses import dataclass

@dataclass(frozen=True)
class PackageMatch:
    name: str
    source: str
    installed_at: str | None

@dataclass(frozen=True)
class ScanResult:
    package_matches: list[PackageMatch]
    log_hits: list[str]
    persistence_hits: list[str]
    ebpf_hits: list[str]
DimensionBash frontlinePython port
Primary useImmediate incident responseStructured analysis and CI use
StrengthZero-friction portabilityReadable models and tests
Trade-offHarder to extend cleanlyRequires a Python runtime
Best fitFast local scan on ArchMaintenance and downstream tooling

The two implementations are complementary. Bash is the front door. Python is the internal office where the findings get organized, tested, and prepared for the next round of work.

The real innovation is the merge layer

This is the part that makes the repo feel bigger than a detector. The project ingests the official HedgeDoc list, local overrides, custom URLs, and archived community sources. It extracts package names with regular expressions, strips noise, deduplicates entries, and produces one merged threat set before scanning begins.

That is the hidden product. The scanner is useful because the list is trustworthy. The list is trustworthy because the repo treats curation as a first-class operation, not a side effect. In security, especially during an active attack, curation is infrastructure.

The interactive diagram above should be read as a pipeline of confidence. Each source starts noisy. Normalization reduces ambiguity. Enrichment adds context. The final scan is then able to ask better questions of the system because the input set was already disciplined.

Why this beats the obvious alternatives

This repo sits in a narrow lane. General-purpose malware scanners are too broad and too slow to be the right answer for a highly specific AUR compromise. AUR helpers are preventative tools, useful for review before installation, but they do not help once something has already landed. Static PKGBUILD analysis is valuable, but it solves a different problem at a different stage of the lifecycle.

Tool classWhat it is good atWhere it falls shortWhy aur-malware-check is different
General malware scannersBroad threat coverageWeak specificity for AUR incidentsTargets a single known attack with current community intelligence
AUR helpersPackage review before installNot an after-the-fact incident toolChecks installed systems and forensic artifacts
Static analysis toolsInspecting PKGBUILD logicRequires adaptation for live responseFocuses on operational detection, not source linting

That does not make aur-malware-check a permanent security platform. It makes it a very good crisis tool. Its value is that it knew exactly which problem it was solving, and it solved that problem quickly enough to matter.

From gist pile to incident-response standard

The broader lesson is uncomfortable but useful. Open-source communities can build real security infrastructure under pressure, but the win is not just code. It is coordination. The repository becomes the place where fragments stop competing and start composing.

This tool just saved me. It found a compromised `python-urllib3` package on my system that I would have never suspected. Thank you to everyone involved in creating this.

A Reddit User, Arch Linux User · User testimonial on Reddit

That is the lasting idea here. The repo is not impressive because it is complex. It is impressive because it creates a single, reliable workflow out of a mess that would otherwise force every user to improvise their own response. In a supply-chain incident, that kind of standardization is the product.