Changelog
Current channels: stable 3.0.0 (aur-scanner / ks-aur-scanner) and
the release-candidate channel (aur-scanner-rc), which builds the current
stable tag between candidates. See Install for how the channels
differ. Every release tag is GPG-signed — verify with
git verify-tag v<version>.
This page tracks notable changes. The format follows Keep a Changelog; full per-release notes live on GitHub Releases.
3.0.0 — 2026-11-10 (stable)
3.0.0 closes a set of ways a crafted package could hide code from the scanner
in 2.2.2 and earlier, and changes how the scanner treats anything it can’t
model with certainty: it reports the uncertainty instead of trusting it. Results
differ from 2.x on some real packages and the library API changed, so this is a
major release. 25 new detection codes (142 → 167). Upgrade from any 2.x version
with paru -S aur-scanner. Details on What’s in 3.0.
Upgrading from 2.x
- Expect new findings. Printed text can no longer hide a High or Critical
finding. A
.installfile that printssudo systemctl enable ...instructions in a heredoc, or through its ownnote()helper, is now reported at the rule’s severity. A one-lineecho "..."of a plain literal is reported as the MediumMETA-008instead (see Changed). Packages that commit opaque archives get a MediumSCAN-002. At the default gate (AUR_SCAN_SEVERITY=high), a few packages that installed quietly under 2.2.2 now stop at the prompt: review the finding and confirm, or build deliberately withaur-scan install --force. If you enabled the pacman hook, it aborts the transaction on a new Critical finding; High findings print a warning. - The whole package directory is scanned, not only the files the PKGBUILD names, so a stray file in an AUR repo can produce findings.
- The example rule no longer loads.
EXAMPLE-001moved out ofrules.d/to/usr/share/aur-scanner/example-rule.toml. Copy it into a rules directory if you used it as a template. - History records use a new key (rule, file and a hash of the normalized
snippet). Records written by 2.x are still read, falling back to rule IDs, so
the first
diffafter upgrading reports nothing spurious. - Library users (
aur-scanner-core):AnalysisContextandParsedPkgbuildhave new fields,informational_linesis replaced by theInertclassification, andpkgfiles::collecttypes files differently. Code that builds these structs by hand needs updating.
Security
Each item below is a way a package could get code past 2.2.2. All of them are fixed in 3.0.0. Advisories with full details will follow.
- Shell-lexing blind spots. A fake heredoc opener (
echo "see <<END",cat <<END-OF-NOTES,"E"OF, a numeric delimiter,$(cat <<X ...)inside double quotes) or a comment ending in\could hide every following line from every analyzer. Heredocs and comments are now handled by one quote-, comment-,${ }- and$( )-aware lexer, and a heredoc whose terminator never arrives isn’t treated as text. More importantly, no printed text (a heredoc body or a print line) can hide a High or Critical finding any more, so a future desync costs at most a false positive. Pipe-to-shell sinks also cover|&, a pipe at the end of a line,. /dev/stdin,xargs ... bash -c,tee >(bash)andbash < <(curl ...). - Obfuscated command names. Empty insertions (
n$()pm,npm ``,n${unset}pm),$"..."quoting, parameter-expansion operators (${v:1:3},${v//x/},${v#p},${v,,},${v:-npm},${a[1]}),+=,printf -v,read <<<,trrot13, brace lists, aliases, helper functions called with literal arguments, assignments made in another function and variables set in a sourced sidecar are now resolved before the rules run. An unresolvable command word next to a fetch-and-run or an install verb is reported asOBF-020. The install-hook package-manager rules (ATOMIC-002,INSTALL-004) match options before the subcommand (npm -g install,npm --prefix /usr install) and the npm, yarn, pnpm, bun, pipx and uv aliases (npm in,bun a,uvx, …). - Scriptlets the scanner didn’t type as scriptlets.
install=inside a split package’spackage_<name>()used the firstpkgname, operators and chained variables weren’t evaluated, anddeclare/eval/&&forms weren’t seen, so the payload scriptlet was scanned without the install-script rules. Scriptlet typing no longer depends on resolvinginstall=: every fileinstall=could name under any package name, every*.install, and every file that definespre_/post_install/upgrade/removeis scanned as a scriptlet. A name that can’t be resolved soundly raisesMETA-007(High). - Files the scanner never read. Committed local archives, NUL-bearing
files, files whose name or header looked like media or a certificate, and
files in subdirectories were skipped. Every file in the package tree is now
read (bounded: depth 8, 4096 files, 20 s; hitting a bound is a Critical
SCAN-001). Plain tar members are scanned in memory; a tar outside a strict, well-understood subset raisesSCAN-004(High), a member path that escapes raisesSCAN-003(High), and an archive the scanner can’t open raisesSCAN-002(Medium, not blocking by default). Arch-specific arrays (source_x86_64,sha256sums_aarch64,depends_x86_64, …) are folded in the way makepkg does, so aSKIPon an arch-specific download is reported, and a checksum array can no longer be silenced by one uncountable element or a"SKIP${unset}"entry. - Change detection.
aur-scan diffand the history store keyed findings by rule ID only and hashed only*.install/*.hookfiles, so a second hit of a rule already raised, a scriptlet named throughinstall=foo.scr, or a changed C2 address went unnoticed. Findings are now keyed by rule, file and normalized snippet (only the package’s own version-shapedpkgveris masked), and every file typed as a scriptlet is hashed. - Denial of service. Inputs that made 2.2.2 run for minutes
(self-referential variables, backtracking globs, very long lines, deep
$(nests, many heredocs or helper functions) are bounded and linear. A panic in any stage becomes a CriticalSCAN-001for that package only, and the per-package deadline now fires and exits promptly.
Added
- Live-path writes outside
$pkgdir:PERSIST-007(authorized_keys, Critical),PERSIST-008(AI coding-assistant config and hook files such as~/.claude/settings.json, Critical),PERSIST-009(pacman hook written outside the package, Critical),PERSIST-010(udev rule written outside the package, High),TAMPER-018(/etc/hosts,resolv.conf,nsswitch.conf, High),TAMPER-019/TAMPER-020(gitcore.fsmonitorandcore.hooksPath, High) andTAMPER-021(the same system-wide, Critical). Directory destinations (cp authorized_keys /root/.ssh/,install -t) count. - Shipped hooks and udev rules, recognised by content:
PERSIST-011(pacman hook on every transaction, Medium),PERSIST-012(one that fetches, runs a shell or uses a temp or home path, High),PERSIST-013(udev rule that runs a program, Medium),PERSIST-014(one that runs a shell, fetches or uses a temp or home path, High) andPERSIST-015(hook or rule content generated at build time, Medium). - Build-time installs:
ATOMIC-005(a named npm/pnpm/yarn/bun package ornpx -y/dlxfetch in a PKGBUILD function, Medium),ATOMIC-006(one from a URL or git remote, High) andATOMIC-007(barenpx/bunx/exec, Low). EXFIL-010(Medium):.onionhosts and the local Tor SOCKS port.SRC-011(High): references above the package directory ($startdir/..,$srcdir/../..), which can run files nobody reviewed.CHK-009(Medium): checksum alignment can’t be determined.META-007(High),META-008(Medium),OBF-020(High),SCAN-002(Medium),SCAN-003(High) andSCAN-004(High), described above.
Changed
META-008, install messages. A one-lineecho/printf(or, in a PKGBUILD,msg/msg2/plain/warning/error) whose only argument is one literal quoted string with no$, backtick or redirection, with nothing before it left open, is reported as the MediumMETA-008naming the rule it matched, instead of that rule’s High or Critical. The exemption is off for the whole package if any printer is defined, aliased orenabled, or ifeval,trap,shopt,BASH_ALIASES,command_not_found_handle,BASH_ENVor a command-positionsourceappears. Heredocs are never exempt.FUNC-001also coverspkgver(),check()and executed top-level code, and follows helper functions through the whole call chain.prepare()isn’t covered, because fetching there is what the Arch packaging guidelines ask for.- Rule and IOC loading warnings are also emitted as
tracingevents (targetaur_scanner_core::loud) for embedders; the stderr line stays. - Every published crate ships the license text; the CLI and plugin crates no longer ship tests that only run inside the repository.
- Dependencies moved to their current major versions: thiserror 2, toml 1.2, reqwest 0.13 (still native-tls), colored 3, base64 0.23 and dirs 7. Unused dependencies were removed and tokio features trimmed. sha2 stays on 0.10 because 0.11 needs Rust 1.88, above the 1.85 MSRV. The workspace uses resolver 3.
Fixed
- False positives removed on real packages:
CHK-006count mismatches from unexpanded{,.sig}/{,.asc}brace lists (1password, cursor-bin, openssl-1.1, rustdesk, units and others),CHK-005on packages whose binaries are pinned in arch-specific arrays (claude-code),CHK-008on checksums held in variables, andSRC-005on packages whose sources are only in arch-specific arrays (localsend-bin). aur-scanner-gitbuildsmainagain: a merge whose tree is identical to a release-key-signed PR head’s tree is trusted, since that signature covers the whole tree. Merges that add an edit or merge an unsigned head are still refused.aur-scan scanon very large or crafted inputs no longer runs for minutes.
Known limitations
Tracked for 3.0.1:
- A Makefile recipe added by a patch that builds a command from Make variables
(
$(F)$(G) url | $(SHELL)) isn’t resolved. - A README that documents
curl ... | shin a code block is reported at the rule’s severity. PERSIST-006reports systemd drop-ins installed into$pkgdir(nvidia-580xx-utils).- Synthetic inputs near the 2 MiB file cap take several seconds.
2.2.2 — 2026-11-09 (stable)
Upgrade with paru -S aur-scanner.
Security
checkscans the packages an AUR helper can substitute for a dependency. A dependency was scanned only under its exact name, but helpers offer every AUR package thatprovidesit, and a dependency cycle forces the substitute:xmladaneedsgprbuild, andgprbuild-bootstrapis what fills it.check, and so the shell integration andaur-scan-wrap, now resolves and scans those providers too and lists them in the notes.installbuilds exact names and is unchanged. Reported by @kohlrak (GHSA-xp97-gh99-hghq).
Fixed
PERSIST-002no longer fires onREUSE.toml. The"*.timer"glob in the REUSE licence file from the standard Arch packaging template was reported as a Critical systemd timer and, with the pacman hook onAbortOnFail, blocked the install. A bare*.timerglob no longer matches on its own; named timer units and timer globs copied into systemd directories still do. Thanks to @salvoza (#54) and @innateessence (#56).
Added
PERSIST-002also catches a timer enabled by linking it intotimers.target.wants, and transient timers fromsystemd-run --on-calendar,--on-bootand the other--on-*triggers.
2.2.1 — 2026-11-09 (stable)
A security fix to change detection and to variable resolution. Everyone on 2.2.0 should upgrade.
Security
- Scan history is keyed by the package, not by what its PKGBUILD claims.
Baselines were stored under the PKGBUILD’s raw
pkgnameline, which the author controls and the parser doesn’t expand. Packages written aspkgname="$_pkgname"shared one record and raised falseDIFF-003warnings about each other, and a package declaring another package’s name could replace that package’s baseline and silence the next real change. Records are now filed under the package base the resolver got from the AUR; illegal names are refused, a record filed under the wrong name is discarded, and old records such as$_pkgname.jsonare pruned. Expect one quiet first scan for packages whose record was filed under a different name. Reported by AD, with a clean reproduction (GHSA-9m24-g9qq-3v4r). - A decoded line break can no longer hide a payload. A
printfvalue containing\n, used once, shifted the resolver’s line-aligned output, so a variable-builtcurl | shbelow it scanned clean. Line breaks in substituted values are now flattened (GHSA-w3p6-4wj2-7q48). - tokio 1.54.1 (RUSTSEC-2026-0386), and futures 0.3.34 and hyper 1.12.0, which
drop the unmaintained
pin-utils(RUSTSEC-2026-0441).
2.2.0 — 2026-10-02 (stable)
Stable release of the 2.2 line: everything in rc.1 and rc.2 below, plus the
fixes from an internal audit of rc.2 against its own documentation. Every
fail-open path below was reproduced before it was changed and ships with a test
that failed on rc.2. Four new detection codes (138 → 142): SCAN-001,
TI-UNCHECKED-001, OBF-012 and PRIV-009. Install with
paru -S aur-scanner. Details in the 2.2 section of What’s in 3.0.
Security
- A package file the scanner could not read no longer counts as clean. One
non-UTF-8 byte, or padding past the 2 MiB cap, made an
.install,.hookor localsource=script drop out of the scan with only a log line, so acurl | bashscriptlet reported “No security issues found”. Files are now decoded lossily and read through a bounded reader; anything oversized, unreadable, symlinked or not a regular file (a FIFO, a link to/dev/zero) produces the new CriticalSCAN-001, which every gate treats as unreviewed andinstall --forcecannot override. Symlinks are never followed. - A
rules.dfile could silently switch off a built-in detection. A community rule reusing a built-in id replaced it. Rules that reuse an existing id are now rejected with a warning, and as root only/usr/shareand/etcrule directories are read. - Dependencies were trusted by default. Any name the AUR RPC didn’t return
was shown as
[repo] (official, trusted)without asking pacman, including nonexistent names and dependencies only an AUR packageprovides. Classification now asks pacman; AUR providers are found and scanned; anything left is[UNRESOLVED]and fails the run. A tree cut short by the depth or size cap fails too. aur-scan-wrappassed what it couldn’t parse straight to the helper.paru -S aur/pkginstalled unscanned with exit 0. The wrapper now resolvesrepo/nameoperands, blocks anything else it can’t validate, and delegates toaur-scan check, so it scans the full dependency tree and the upgrade set (-Syu,-Sua, bareparu/yay). The Nushell integration, which routes through the wrapper, gets the same gate.- Install gates that failed open: a failed
-Quaqupgrade query, a missingaur-scan, and local builds (-B <dir>,-Ui,-U <dir>) all ran the helper unscanned in bash, zsh, fish, Nushell and the wrapper. They are now refused or scanned from disk;<helper>-unsaferemains the deliberate bypass. checklet piped input approve findings and ignored--fail-onbelow High in interactive mode. The prompt now appears whenever the gate trips, and only a real terminal can answer it.min_severitydisabled gates. It filtered findings before every gate, somin_severity = "critical"removed High findings from the wrapper prompt,install --gate highand JSON. It is now display-only.- Split packages: only the last
install=was scanned. Everyinstall=is now scanned. - Detection hardening for staged and indirect fetch-and-execute forms,
variable indirection across and within functions, inline encoded payloads
(bounded decoding), cron persistence, and helper functions that execute their
arguments (new Critical
OBF-012). - Files the scan never read: every text file in the package directory is now
scanned, plus any path the PKGBUILD reaches through
$startdiror$srcdir, not just files with a script extension. A declaredinstall=that is missing or a directory isSCAN-001. - Kernel modules loaded from an install scriptlet were not checked at all.
Module commands there are now
PRIV-005, and loading a module the package itself ships is the new CriticalPRIV-009. - Report and help flags bypassed the wrapper and shell gates.
--stats,--news,--order,--comments,--gendbor--helpanywhere on the command line madeparu -S pkg --statsinstall unscanned. - Search-menu installs are refused by default.
paru <term>,yay <term>,yay -Y…(including-Ys/-Yi) and-S --interactivepick the package after the scan. Name it with-S <name>, or setAUR_SCAN_ALLOW_MENU=1.
Fixed
- Pacman hook found nothing in common setups and exited 0: doas, run0,
pkexec, root shells, home directories outside
/home, and split packages. It now finds the invoking user through sudo, doas, pkexec or the login uid, matches split packages through.SRCINFO, reads paru/yay cache settings, warns per foreign package it couldn’t scan or whose cached version differs, and aborts instead in strict mode (AUR_SCAN_HOOK_STRICT=1or/etc/aur-scanner/hook-strict). Its split-package lookup always consults the.SRCINFOindex, and differing candidate versions are scanned and reported. installprinted “All packages built and installed” after silently skipping a repo package it was asked for; it now refuses non-AUR roots. Declining or a missing terminal exits non-zero, dependencies are installed--asdeps, and the scanned files are hashed again right beforemakepkg. Packages whose sourcesmakepkgfetches unpinned after the scan are listed.- Change detection never ran for AUR packages in
check: the fetched copy was deleted before the history comparison read it. git clonenow runs with a scrubbed environment, ignores system and global git config, verifies objects, and is time-bounded.timeout_secondswas documented but read by nothing. It now bounds the AUR RPC, the clone, and each package’s fetch and scan; a timeout fails closed.- JSON and SARIF on stdout were corrupted by log lines; logs go to stderr.
SARIF rules are unique and indexed, findings without a line no longer get a
made-up
startLine, and URIs are relative to the scanned root. - CycloneDX SBOM: unique
bom-refs, no danglingdependsOn, percent-encoded purls (epochs), and the real repository name instead of a guessed one.metadata.componentnow describes the install request and depends on the requested packages, which are all listed incomponents; a single-package SBOM no longer ends up with an emptycomponentslist. - Duplicate findings when two patterns of one rule matched the same line.
- Rule loader: one bad regex no longer disables the whole directory; unknown
keys and pattern-less rules are rejected;
file_types = ["source_file"]now works. Community rule ids are trimmed and uppercased before the collision check and must match theACME-001shape. aur-scan ruleswas a stale hand-written list; it is generated from the catalog.codesrejects an unknown--formator--category.aur-scan systemalways exited 0. It now fails on--fail-on(default Critical), IOC matches, and packages it could not scan, which it lists.- Threat intel: VirusTotal lookups default to 4 per scan in declared order
(
vt_max_lookups), stop after a rate-limit reply, and report what went unchecked asTI-UNCHECKED-001instead of nothing. Response bodies are capped and credentials in URLs are stripped before lookup. - A malformed system IOC override and an explicit
rules_paththat fails to load are hard errors, like the config file. An analyzer error fails the scan instead of dropping that analyzer’s findings. - False positives at High/Critical on ordinary packages: a setuid
chrome-sandboxin Electron-binpackages (now Low),systemctl daemon-reload, shipping amodprobe.dfile, and reading${XDG_CONFIG_HOME:-~/.config}. - The printed dependency tree is nested instead of flat.
check --localin an owned namespace raised a CriticalSQUAT-004even when the configured maintainer was right. The real AUR maintainer is now looked up; a local package with no AUR record is reported at High as “publisher cannot be verified” instead of as a confirmed impostor.- A spawn allowlist test replaces the string grep that guarded the
static-only invariant: every subprocess the shipped code can start must be on a
reviewed list.
lddis no longer treated as an inert command, since it runs its target through the loader. - Upgrade scanning forwards the helper flags that change the update set
(
--aururl,--config,--ignore,--root, …) to-Quaq, and with--develalso scans installed VCS packages. More value-taking options are recognized so their values aren’t mistaken for packages, andaur-scan-wrap --helpprints usage. - Hidden gate findings: when
--severityhides a finding that trips--fail-on, the text output now says so instead of “No security issues found”. JSON and SARIF always carry every finding.
Changed
- Minimum supported Rust is 1.85, declared in
rust-versionand checked in CI; CI builds with--locked. - The root and
-gitPKGBUILDs conflict withaur-scanner-rc. - Documentation corrected where it overstated the code: runtime dependencies
(
gcc-libsandopenssl, dynamically linked), the pacman hook’s role and install path (an opt-in post-build backstop in/etc/pacman.d/hooks/), theinstallTOCTOU guarantee, threat-intel egress and cache wording, what the GitHub rulesets enforce, and the-gitpackage’s signature check.
2.2.0-rc.2 — 2026-10-01 (release candidate — superseded by 2.2.0)
Second 2.2.0 release candidate. No new detection codes (still 138); it gets
testers the fixes made since rc.1, including the 2.1.1 security fixes merged
forward. Install aur-scanner-rc.
Security
A comment could hide a download from
FUNC-001. The check skipped the whole function if# curlappeared anywhere in it, so one comment line silenced a realcurlon the next. It now works line by line and skips only comment lines; a tab after the command, a quoted"curl", and${wget}are caught too.The pacman hook no longer reads a user-writable config as root. As root it now reads
/etc/aur-scanner/config.tomland nothing else. Before, a hostilebuild()could drop broken TOML in~/.config/aur-scanner/and, because a malformed security config is a hard error, wedge every pacman transaction after it.
Fixed
FUNC-001false positive on build targets likecmake --target libcurl(#35, thanks @gulamovzavohir02-glitch)./usr/bin/curl,\curland;curlstill fire.aur-scanner-gitbuilds again. It refused every GitHub merge commit, since GitHub signs those with its own key. It now acceptsmainonly when every merge is a clean merge of a release-key-signed PR head, and checks the key by fingerprint rather than any key in your keyring.ATOMIC-004missed the natural form of a sudo shim.cp payload /usr/local/bin/sudoandinstall -Dm755 stealer "$HOME/.local/bin/sudo"now match; the rule anchors on thePATHdirectory, not on how home was spelled.PERSIST-003was narrowed too far after #21:echo '…' | crontab -and friends passed clean. They match again, and removing a stale cron entry still stays silent. A separate false positive onpython-python-crontabis gone; measured against 503 live PKGBUILDs, the rule now fires on one,dcron-git, which does install cron files.- An empty
XDG_CONFIG_HOMEno longer drops the user config path. - A malformed config no longer breaks the commands that diagnose it.
explain,rules,iocandcodesrun before config loading again. scanned_fileslists every file read (hook scriptlets, localsource=()scripts, committed binaries), so SARIF consumers get a complete manifest.
2.1.1 — 2026-10-01 (stable)
Security patch for the 2.1 line. No detection, rule, or output changes, so
upgrading is safe. Install with paru -S aur-scanner.
Security
aur-scan check --no-confirmexited 0 on a Critical when no--fail-onwas given, which is how the shell integrations ran it withAUR_SCAN_INTERACTIVE=0. A non-interactivechecknow fails closed on Critical by itself, and the bash, zsh and fish integrations pass--fail-on "$AUR_SCAN_SEVERITY"when non-interactive. Interactive use is unchanged. Re-source your shell integration after upgrading.- The pacman hook read a user-writable config as root, so broken TOML in
~/.config/aur-scanner/could abort every pacman transaction. As root it now reads/etc/aur-scanner/config.tomlonly.
Fixed
- An empty
XDG_CONFIG_HOMEfalls back to~/.configinstead of disabling the user config (#25).
2.2.0-rc.1 — 2026-09-10 (release candidate — superseded by rc.2)
Change detection, name impersonation, ownership signals and static binary
analysis: 20 new detection codes (118 → 138), and every scanning path rewired.
Install aur-scanner-rc to test it. The full tour, with the measurements behind
each threshold, is in the 2.2 section of What’s in 3.0.
Added
- Change detection (
DIFF-001..004) oncheckandinstall: new findings since the last scan, ownership moved, upstream moved, install script added or changed. Silent on a first scan and on a plain version bump. aur-scan diff <old> <new>: stateless comparison of two package directories.--fail-ontrips only on newly added findings.- Name impersonation (
SQUAT-001..004) and[[owned_namespaces]]to declare the names you publish. - Ownership signals (
OWN-001..004): orphaned, orphaned and out-of-date, stale out-of-date flag, new with no community validation. - Static binary analysis (
BIN-001..005) of executables committed to the AUR repo, with a bounds-checked ELF reader. Nothing is executed;lddis off-limits. SRC-010(source from a different owner’s copy of upstream),DEEP-003(Unicode bidi / Trojan Source),ESCAPE-001(writes outside$srcdir/$pkgdir), and localsource=()files are now read.PERM-001/PERM-002world-writable rules, shipped as a community TOML file (#8).aur-scan completions <shell>for bash, zsh and fish, installed by the packages.aur-scan versionvalidates the config and exits 2 if it’s broken.
Fixed
aur-scan check --no-confirmexited 0 on a Critical when--fail-onwasn’t passed, which is how the shell integrations called it. WithAUR_SCAN_INTERACTIVE=0the gate was a no-op.AUR_SCAN_SEVERITYis now both the display floor and the block threshold.- Registry context reached only
check.install, the wrapper,systemand the hook ran a smaller analyzer set; it’s now a required argument everywhere. AUR_SCAN_MODE=installwas the weaker mode while documented as stronger.SHELL-002false positive on thencat the end ofMEGAsync(#32).CHK-004/CHK-005name the sources they count (#31).- Terminal escape injection: package-controlled text in findings is sanitized before printing.
- Shell integrations wrote their banner to stdout, breaking
scp,rsyncandssh host cmd. - Scan history hardening: poisoning via a
--localdirectory’s self-declared name, a torn-write race between concurrent scans, unbounded growth, and a world-writable fallback path. - Every config key is validated, including inside
[threat_intel]and[cache]. A typo used to silently do nothing. - Packages installed only
example.tomlfromrules.d/; every shipped rule file installs now.
Known gaps: decompression bombs and hostile makedepends aren’t detected,
and VirusTotal keys off declared sha256sums, not a hash of a committed binary.
2.1.0 — 2026-09-10 (stable)
Promotes 2.1.0-rc.2 unchanged after six weeks of soak: no detection or
behavior differences from the RC. Everything in the two candidates below is in
this release. Install with paru -S aur-scanner.
Security
anyhow1.0.104, past RUSTSEC-2026-0190 (unsoundError::downcast_mut()).
Changed
- CI and the advisories workflow on
actions/checkout@v5.
2.1.0-rc.2 — 2026-07-28 (release candidate — promoted to 2.1.0)
Same code as 2.1.0-rc.1, plus packaging and CI hygiene (repo-wide cargo fmt,
rustfmt as a hard CI gate). Release tags are immutable, so this re-points
aur-scanner-rc at the clean tip.
2.1.0-rc.1 — 2026-07-28 (release candidate — promoted to 2.1.0)
Correctness and Atomic Arch coverage depth.
Added
- Config is found automatically. Without
-c, the CLI, the pacman hook,aur-scan-wrapandaur-scan installload the first of$XDG_CONFIG_HOME/aur-scanner/config.toml(or~/.config/…) and/etc/aur-scanner/config.toml. A present-but-malformed file is a hard error. This is why threat intel looked dead when keys lived only in the system config (#25). [output]config table to choose which fields each finding prints (line,snippet,recommendation,cwe). Display-only: it never changes findings, exit codes, or gates.checkcompact output now showsfile:line.- ALPM
.hookside scripts in the package directory are read and analyzed like install scriptlets (the Atomic Arch wave-4 delivery path). Never executed. ATOMIC-004: credential-stealingsudoshim in~/.local/binand similar.ATOMIC-001gains the wave-3 package names;ENV-003covers fish, zsh andprofile.dstartup paths.
Fixed
- The wrapper, plugin and
installignored your config. They used built-in defaults, so XDG and/etcsettings (threat intel included) never applied when scanning through the wrapper. SRC-004false positive onraw.githubusercontent.com(#22).CHK-006counted commented-out sources (#24).PRIV-002fired on clearing SUID.u-s,g-sand-sno longer fire; setting forms (u+s,=s, octal 2–7xxx) still do (#21). Browser sandbox helpers still set it on purpose, so they still report; see the FAQ.PERSIST-003fired on cron removal (#21), andENV-003/HIDDEN-001fired on install messages that merely mention~/.bashrc(#15).aur-scanner-gitprepare() handles GitHub-signed merge commits (#20).
Security
- The pacman hook stays fail-closed in multi-package transactions: a Critical aborts the whole transaction. Skipping just the offending package would let a malicious one ride along with clean ones.
2.0.0 — 2026-06-17 (stable)
Major release. Promotes the 1.2.0-rc1 line to stable. The default scan is
unchanged — fully offline and static; everything new is opt-in or additive.
Install with paru -S aur-scanner.
Added — opt-in threat intelligence
- VirusTotal & URLhaus lookups. With
enable_threat_intelset and your own key, the scanner checks declaredsha256sumsagainst VirusTotal andsource=URLs against abuse.ch/URLhaus, emittingTI-VT-001/TI-URLHAUS-001on a malicious verdict. Off by default — only already-public hashes/URLs are sent, every lookup fails open, and all egress is isolated in one auditable file. See Threat Intelligence. The VirusTotal-by-hash idea came from @SuitablyMysterious (#9). - Active verdict cache. The per-user, MAC-authenticated cache now backs threat-intel lookups so repeat scans respect VirusTotal’s request quota; transient failures are never cached as a verdict.
Added — broader coverage & integrations
- Every maintained AUR helper is covered by the
systemaudit and the pacman hook — yay, paru, pikaur, aura, pakku, trizen, aurutils, rua, pat-aur — at each helper’s real PKGBUILD location. The shell integration also wrapspikaur/trizen/pakkualongsideparu/yay. - Nushell integration (
install/integration.nu) joins bash/zsh/fish. --no-colorglobal flag (andNO_COLORenv) for clean output in logs/CI.
Changed
-c/--confignow applies to every command, includingsystem— so a threat-intel config can VT-check every installed AUR package, not justscan.
1.2.0-rc1 — 2026-06-17 (release candidate — promoted to 2.0.0)
The release candidate that became 2.0.0 above. Lived in the
repository while it baked, then
shipped as stable 2.0.0.
1.1.0 — 2026-06-15 (stable)
Stable. Promotes the 1.1.0 release-candidate line and adds a second hardening wave that closes the residual evasion classes from an adversarial self-audit. Install with paru -S aur-scanner.
Detection — evasion classes closed
- Variable-indirection (taint pass). A fetch/exec hidden behind a shell variable now resolves and is flagged — it used to trip nothing.
- Case-insensitive analyzers, host-aware URL/IOC matching, and a supply-chain / packaging-metadata analyzer (dependency confusion, core-package displacement, signature theatre, sensitive
backup=, malformed hashes). - Quote-aware printed-message filtering (a
;inside a quotedechono longer false-positives).
Hardening
- Authenticated cache verdicts (per-user MAC), an allowlisted
makepkgbuild environment,--forcecan never override an unscannable package, and--localonly attributes a verdict to a name that provably matches.
Quality
- A self-adversarial evasion fuzzer runs as a release gate — every malicious fixture is mutated through semantics-preserving evasion transforms and the gate must still block each variant.
Threat reports that informed this line: #2 (@LunarEclipse363) and #10 (@zebulon2).
1.1.0-rc2 — 2026-06-14 (release candidate)
Proactive detection expansion, prompted by a live obfuscated AUR campaign. Try
it with paru -S aur-scanner-rc.
Anti-evasion
- De-obfuscation pass. A new wave hid a
bun add <js-payload>in apost_installhook using ANSI-C quoting ($'\x63') and adjacent-quote word-splitting ("b"'u''n') — the previous RC caught it only as a generic high “hex payload.” The scanner now decodes ANSI-C escapes and collapses quote-splitting, then runs every rule against the decoded text, so the whole catalog resists this evasion at once. That sample is now correctly flagged critical.
Detection — catalog 72 → 106 codes
- Reverse/bind shells beyond bash/nc: perl, php, ruby/lua/awk, node, openssl
s_client,mkfifobackpipe, busybox-nc/telnet/ncat-ssl. - Exfiltration: DNS tunnelling, HTTP upload, Slack/Teams webhooks, anonymous file-drop/tunnel hosts, environment dumps, cloud/CI credential + keyring/wallet access.
- Auth & system tampering: privileged-account creation, password changes,
writes to the auth database, doas/NOPASSWD, PAM, pacman
SigLevel=Never, disabling security controls, rogue CA trust anchors. - Supply-chain trust: pacman-key / gpg key imports, package-index overrides, obfuscated IPs in source URLs.
- More obfuscation & RCE forms: printf assembly, base32/16 decode, interpreter
here-strings,
sh -c "$(curl)", detachedsetsid/nohup.
Other
aur-scan installcleans up its own build directory after a successful install (--keep-buildto retain).
1.1.0-rc1 — 2026-06-13 (release candidate)
Security-hardening release resolving a full security & quality audit. Try it with
paru -S aur-scanner-rc; read the behavior changes before pointing automation at it.
⚠️ Behavior changes (read before upgrading)
- The scanner now fails closed. The
paru/yaywrapper and the pacman hook previously continued past a fetch/scan error, a timeout, or a non-interactive prompt; they now deny in those cases. If you driveparu/yayfrom a script, cron, or CI (no TTY), an install that can’t be fully analyzed is refused rather than silently proceeding. A security gate that fails open is not a gate. scan --format json/--format sarifemit only the machine document on stdout. The human-readable summary moved to stderr, soaur-scan scan --format json | jqworks. If you scraped the summary out of stdout, read stderr instead.
Security
- Input-validation chokepoint for package names/bases — illegal identifiers are rejected before they can become URL path segments or filesystem paths, closing a path-traversal vector and request-injection into the AUR RPC.
- Network hardening: redirects refused, HTTPS-only enforced, response bodies size-capped (streaming), all RPC URLs percent-encoded.
- Pacman hook drops root (groups → gid → uid, verified irreversible) before reading user cache files, validates names, and refuses symlinked PKGBUILDs.
- Detection-evasion fixes: backslash-newline line-continuation splicing;
quote- and comment-aware brace scanning (no more
echo "}"/# }truncation); broadened reverse-shell and bare crypto-address detection; checksum SKIP-laundering closed across all hash arrays. - Dependency advisory RUSTSEC-2026-0007 resolved (
bytes→ 1.11.1).
Added
SRC-007: warns when a VCS source isn’t pinned to a commit (Low).- CLI integration suite that runs the real binary against PKGBUILD fixtures.
- CI (format, clippy-as-error, tests, release build) and
cargo-denysupply-chain gating, plus a weekly advisory scan. - The rolling
aur-scanner-gitpackage now verifies the signed HEAD commit at build time.
Fixed
- False positive: a printed
note "...~/.config/..."message no longer tripsHIDDEN-001(it mentions a path; it doesn’t write one) — seen ongoogle-chromeandvisual-studio-code-bin. - Parser:
source+=(...)appends are no longer dropped; inline comments are handled quote-aware; single-line function bodies are captured. - Cache writes are atomic (
0600) in an owner-only (0700) directory, entries are key-bound, and a corrupt entry is a miss rather than trusted data. - Provenance store distinguishes an absent baseline from a corrupt one.
- Removed a panicking
AurClient::default()and a responseunwrap.
1.0.3 and earlier
Released before this changelog was introduced. See the
GitHub Releases for
1.0.3 (race-free install, dependency-tree SBOM), 1.0.1 (INSTALL-004 —
language package managers in install hooks), and 1.0.0 (first public release).