Threat Intelligence

A default aur-scan is fully offline and static — it reads the PKGBUILD and nothing leaves your machine. Threat intelligence is an opt-in layer on top: when you turn it on and supply your own API key, the scanner cross-checks the artifacts a package declares against two reputation services. It is off until you enable it, and it is designed so that turning it on can’t quietly turn into a privacy or security problem.

What it checks

Two providers, each fed only data that is already public in the PKGBUILD:

ProviderLooks upEmits
VirusTotaleach declared sha256sums hash (file by hash)TI-VT-001 when engines flag it
abuse.ch / URLhauseach source= http(s) URLTI-URLHAUS-001 when the URL is a known malware-distribution URL

The point is the gap that static analysis can’t close on its own: a -bin package’s prebuilt artifact, or a source URL, may be known bad to the wider security community even when the PKGBUILD text looks clean. A hash or URL lookup answers “has anyone already seen this and flagged it?” without ever downloading or running the artifact.

Off by default — and why that matters

A security tool that phones home by default is its own problem. So:

  • The networked analyzer is never even constructed unless you set enable_threat_intel and a provider key is present. No key, no analyzer, no calls.
  • A scan with no configuration makes zero outbound requests. The offline, static behavior described in How it works is unchanged.

What leaves your machine (and what never does)

Least disclosure. Only data already public in the PKGBUILD is ever transmitted — a declared content hash, or a source= URL. Never file contents, never anything about you or your system. URLs have their fragment stripped; hashes are validated as clean hex before they’re ever placed in a request path.

Every threat-intel call lives in one auditable file (threat_intel/remote.rs), so that egress can be reviewed in one place. The only other network access is the AUR itself (the RPC client in aur.rs and the hardened git clone), held to the same rules. Every call is:

  • HTTPS-only, with redirects refused (a followed redirect is the classic exfil amplifier),
  • time-bounded (a short timeout — intel is advisory, never a blocker),
  • fail-open: a network error, bad key, quota limit, or outage yields no finding and never fails the scan. A provider being down must never block an install.
  • bounded per scan: VirusTotal lookups default to 4 per scan (the public API’s per-minute quota), in the order the PKGBUILD declares them, so a hostile PKGBUILD declaring hundreds of sources can’t burn your API quota. Response bodies are capped too, and credentials embedded in a URL (user:pass@) are stripped before lookup.

Verdict cache

Lookups are cached in your private (mode 0700) cache directory, with an integrity check that rejects corrupted entries, so repeat scans respect VirusTotal’s public quota (4 requests/minute). The cache draws a deliberate line between a definitive answer (e.g. VirusTotal has genuinely never seen a hash — cached) and a transient failure (network blip, quota, outage — not cached, so a one-off outage can never suppress a real verdict for the cache lifetime). Verdict lifetime is configurable.

Turning it on

Turn it on in your config file. aur-scan finds it automatically at ~/.config/aur-scanner/config.toml (or $XDG_CONFIG_HOME/aur-scanner/), else /etc/aur-scanner/config.toml, or you can point at any file with -c. Keep it chmod 600 (it holds your keys) and never commit it.

# ~/.config/aur-scanner/config.toml   (chmod 600)
enable_threat_intel = true
min_severity = "low"

[threat_intel]
# VirusTotal: https://www.virustotal.com/gui/my-apikey
virustotal_api_key = "PASTE_YOUR_VT_KEY"

# URLhaus / abuse.ch: an Auth-Key is now mandatory — free at https://auth.abuse.ch/
urlhaus_enabled = true
urlhaus_auth_key = "PASTE_YOUR_URLHAUS_KEY"

cache_duration_hours = 24

# VirusTotal lookups per scan (default 4). Hashes past the cap, or after a
# rate-limit reply, are reported as TI-UNCHECKED-001, never dropped.
# vt_max_lookups = 4
# with the config in a default location, every command uses it
aur-scan scan ./mypkg
aur-scan check <package>
aur-scan system                                     # VT-check every installed AUR package

# or keep it somewhere else and point at it
aur-scan -c ~/secrets/aur-scan-ti.toml check <package>

The shell integration, aur-scan-wrap, aur-scan install and the pacman hook all read the same default locations, so threat intel applies on every path, not just when you run aur-scan by hand. A mistyped key such as virustotal_apikey is silently ignored on 2.1.x; since 2.2.0 any unknown key is a hard error.

Keys can also come from the environment instead of the file: VT_API_KEY / VIRUSTOTAL_API_KEY and URLHAUS_AUTH_KEY. The lookup cap can come from AUR_SCAN_VT_MAX_LOOKUPS (default 4); the vt_max_lookups config key wins when both are set.

When the cap or a rate limit stops lookups

A quiet result must never mean “checked and clean” when it wasn’t. Hashes past the vt_max_lookups cap, or left after VirusTotal replies with a rate limit, are reported as TI-UNCHECKED-001 instead of nothing. Lookups still fail open: a provider error, quota limit or outage never fails or blocks a scan. The finding just tells you what went unchecked.

The offline cousin: the IOC database

Separate from the network lookups, every build ships a local, offline indicator-of-compromise database — known-malicious package names, npm/bun payload names, and file artifacts from tracked campaigns (including the June 2026 Atomic Arch incident). It needs no key and no network:

aur-scan ioc                 # campaigns + indicator stats
aur-scan ioc --check <name>  # is this a known-malicious indicator?

aur-scan system cross-references everything you have installed against it automatically — the fastest answer to “am I affected?”

Honest limits

This is advisory, defense-in-depth — not a verdict you can rely on alone. A clean reputation result means “nobody has flagged this yet,” not “this is safe,” and every lookup fails open. It strengthens the static scan; it doesn’t replace the judgment of reading what a package does. As always, aur-scan never follows or runs the things it’s suspicious of.