[community] Archcanary - AUR supply-chain scanner

Hi all,

archcanary

Arch Linux security canary — layered AUR supply-chain scanner with systemd, eBPF, and kmod auditing.

From now on archcanary can be installed from aur repo.

If you have archcanary installed with ./install.sh --system
First do …

./install.sh uninstall --system

Then archcanary can be installed with

yay -S archcanary
archcanary --doctor

:penguin:

Latest update…

I removed the GUI for a bash menu. Nothing realy changes apart from skipping yad.

In short → The reason is piping output to a yad window is buggy.

🐤 archcanary
----------------------------------------------
 1) Full scan                        ✅
 2) Refresh + Full scan
 3) Individual checks
 4) Root checks                       [root]
 5) Lynis hardening report            [root]
 6) Run Lynis audit                   [root]
 7) Setup health check (--doctor)
 8) Search packages
 9) Manage allowlists
10) Edit config
11) Settings
12) About
 0) Quit

Choice: 

The latest commits are not in the latest release. Those are false positives. Archcanaries own test on TOR.

Fix shipped in the next release… Or use ./install.sh --system again.

Update:

Archcanary v0.1.33 released — full-AUR false-positive pass

archcanary v0.1.33 is now on the AUR (archcanary) and tagged in the repo.

Most of this release came out of running archcanary’s PKGBUILD checks (–check-pkgbuild, and the yay/paru pre-build hooks) against every live package on the AUR — roughly 111,000 PKGBUILDs. Thanks to Andreas Reichel, who ran the scan and sent back the full hit list on 25 Aug. That turned up two things: a couple of genuine detection gaps, and several patterns firing on entirely legitimate packages. The false positives were the priority — a few of them weren’t just noise, the yay hook was hard-blocking the install.

False positives fixed

  • ANSI colour output in printf. The hex/octal-escape check flagged any printf containing a \x or \0 escape, so a normal printf ‘\033[1m…’ bold/colour line tripped a WARNING on 246 AUR packages — and the yay hook aborted the build outright. It now only fires when an escape decodes to an actual command byte (a letter or digit), or when the printf output is piped into a shell.
  • The Chrome extension-ID line. echo $key | base64 -d | sha256sum | head -c32 | tr 0-9a-f a-p — the standard snippet in dozens of browser-extension -bin packages — was read as … | sh and flagged, again with a hard abort in the yay hook. Pipe-to-shell detection is now properly word-anchored: | sha256sum, | grep bash, | pacman -S bash no longer count; | sh, | bash, | sudo bash still do.
  • Commented-out lines. A trailing # sudo pacman -U --noconfirm ./pkg.tar.zst build note — common in hand-written PKGBUILDs — was matched as a real non-interactive pacman call (this was the EndeavourOS-forum timeshift report, against a years-old cached copy). Whole-line comments are now skipped by that check and several others (the aur@ self-propagation check, sudo/doas/pkexec, the MR/PR-patch check, base64-to-shell). 21 packages carrying a # push this + .SRCINFO to ssh://aur@… boilerplate comment are no longer flagged. Checks where a payload hidden in a comment is never legitimate — Tor/onion fetches, downloads into system paths, byte-assembled commands — still read comment text.
  • Overly broad new rules, calibrated down. The sudo-in-build()/package() check went from ~500 hits to 121 once it stopped counting .install scriptlets (already root) and sudo -u someuser steps. The mutable-patch-URL check went from ~320 to 46 once patch URLs carrying a real checksum — already pinned by makepkg’s own verification — were excluded.
  • One rule pulled. A pkgver-vs-source-URL version-mismatch check flagged 800+ packages at low precision (rolling heads/master tarballs, _pkgver/_commit URL variables, and versionless CDN links are all normal). Removed for now, parked for a rework in v0.1.34.

New detections

  • sudo / doas / pkexec inside a PKGBUILD’s build() or package(). makepkg runs those functions as your user, so shelling out through sudo writes onto the live system instead of into the package directory — e.g. tarah runs sudo cp … /usr/bin. .install scriptlets (meant to run as root) and sudo -u are not flagged.
  • An unchecksummed source= entry pointing at a forge merge-request or pull-request diff URL. That content can be force-pushed after you’ve reviewed it. A pinned hash, or a /commit/.patch URL, is fine and isn’t flagged. Example: freetype2-wps.

Both are in the yay hook too, as warnings rather than blocks.

Also

  • Some of the yay hook’s Lua pattern anchors were dead code — yay 13 runs the hook under gopher-lua, which doesn’t support the %f frontier pattern they used. Rewritten so they actually match, with a lint test to
yay -Syu archcanary
archcanary --doctor

Weird… after uninstall and install via yay, archcanary --doctor runs fine, but archcanary with no args returns this:

me@mine ~ $ archcanary
/usr/bin/archcanary: line 1456: /home/me/.cache/archcanary/aur-check-20260902-084819.log: Permission denied
ERROR: Cannot write log file: /home/me/.cache/archcanary/aur-check-20260902-084819.log
me@mine ~ $ 

Seems like /home/me/.cache/archcanary is created with root ownership. It does have previous logs in it though, non-root.

sudo chown me:me /home/me/.cache/archcanary/ allows it to work.

Say, question… just noticed that pamac does not seem to use traur. Are we supposed to always be using yay instead? Too many package managers. :slight_smile:

Interesting. Could you please elaborate? :nerd_face:

The GUI was a yad window reading the scan output off a pipe. Piped output gets buffered, so the window looked frozen mid-scan and dumped everything at the end.

Add the colour codes, exit-code clashes and resize glitches to work around,
and it wasn’t worth it for a tool that’s CLI-first.

The bash menu just launches
the same commands straight in the terminal — nothing about the scans changed.

Update:

Archcanary v0.1.33 released — full-AUR false-positive pass

archcanary v0.1.33 is now on the AUR (archcanary) and tagged in the repo.

Most of this release came out of running archcanary’s PKGBUILD checks (–check-pkgbuild, and the yay/paru pre-build hooks) against every live package on the AUR — roughly 111,000 PKGBUILDs. Thanks to Andreas Reichel, who ran the scan and sent back the full hit list on 25 Aug. That turned up two things: a couple of genuine detection gaps, and several patterns firing on entirely legitimate packages. The false positives were the priority — a few of them weren’t just noise, the yay hook was hard-blocking the install.

False positives fixed

  • ANSI colour output in printf. The hex/octal-escape check flagged any printf containing a \x or \0 escape, so a normal printf ‘\033[1m…’ bold/colour line tripped a WARNING on 246 AUR packages — and the yay hook aborted the build outright. It now only fires when an escape decodes to an actual command byte (a letter or digit), or when the printf output is piped into a shell.
  • The Chrome extension-ID line. echo $key | base64 -d | sha256sum | head -c32 | tr 0-9a-f a-p — the standard snippet in dozens of browser-extension -bin packages — was read as … | sh and flagged, again with a hard abort in the yay hook. Pipe-to-shell detection is now properly word-anchored: | sha256sum, | grep bash, | pacman -S bash no longer count; | sh, | bash, | sudo bash still do.
  • Commented-out lines. A trailing # sudo pacman -U --noconfirm ./pkg.tar.zst build note — common in hand-written PKGBUILDs — was matched as a real non-interactive pacman call (this was the EndeavourOS-forum timeshift report, against a years-old cached copy). Whole-line comments are now skipped by that check and several others (the aur@ self-propagation check, sudo/doas/pkexec, the MR/PR-patch check, base64-to-shell). 21 packages carrying a # push this + .SRCINFO to ssh://aur@… boilerplate comment are no longer flagged. Checks where a payload hidden in a comment is never legitimate — Tor/onion fetches, downloads into system paths, byte-assembled commands — still read comment text.
  • Overly broad new rules, calibrated down. The sudo-in-build()/package() check went from ~500 hits to 121 once it stopped counting .install scriptlets (already root) and sudo -u someuser steps. The mutable-patch-URL check went from ~320 to 46 once patch URLs carrying a real checksum — already pinned by makepkg’s own verification — were excluded.
  • One rule pulled. A pkgver-vs-source-URL version-mismatch check flagged 800+ packages at low precision (rolling heads/master tarballs, _pkgver/_commit URL variables, and versionless CDN links are all normal). Removed for now, parked for a rework in v0.1.34.

New detections

  • sudo / doas / pkexec inside a PKGBUILD’s build() or package(). makepkg runs those functions as your user, so shelling out through sudo writes onto the live system instead of into the package directory — e.g. tarah runs sudo cp … /usr/bin. .install scriptlets (meant to run as root) and sudo -u are not flagged.
  • An unchecksummed source= entry pointing at a forge merge-request or pull-request diff URL. That content can be force-pushed after you’ve reviewed it. A pinned hash, or a /commit/.patch URL, is fine and isn’t flagged. Example: freetype2-wps.

Both are in the yay hook too, as warnings rather than blocks.

Also

  • Some of the yay hook’s Lua pattern anchors were dead code — yay 13 runs the hook under gopher-lua, which doesn’t support the %f frontier pattern they used. Rewritten so they actually match, with a lint test to
yay -Syu archcanary
archcanary --doctor

Hi, currently archcanary always pauses, even when pkgbuild is clean:

==> ARCHCANARY: visual-studio-code-bin # ---------- PKGBUILD CHECKS CLEAN ---------- #
ARCHCANARY: verdict above -- press Enter to continue... 

Would it be helpful if it only paused when something other than ‘clean’ was found?

v0.1.34 released

yay -Syu archcanary
archcanary --doctor

The yay init.lua press to proceed for canary had a function for me in the beginning.
But I agree for clean it does not need to stop. The log still shows the output though.

Thanks :wink:

Update: v0.1.35

:penguin:

Update release v0.1.37

:penguin: