I opened Ubuntu's App Center, scrolled through ~90 installed packages, and asked the obvious question: is any of this malware?
The list looked suspicious in all the wrong ways. A font package last updated nine years ago. A dozen unlabeled GStreamer plugins with generic gray icons. Something called BRLTTY I had no memory of installing. Plenty of surface area for paranoia.
None of it turned out to matter, and the reason is worth writing down: App Center is a package browser, not an inventory. It shows you the two sources it manages — the Ubuntu archive and the Snap Store — and nothing else. Every path an attacker would actually use to get code onto a developer workstation is invisible to it.
This is the audit I ran instead, and what each command is actually good for.
Reading the GUI correctly first
Two things I had wrong at a glance.
"Updated 9 years ago" is not neglect. That's the package version date. (URW)++ Core Font Set 20200910-8, TeXInfo, BRLTTY — these are frozen-by-design dependencies pulled in by other packages. A font metric file doesn't need a 2026 release to be safe.
Gray placeholder icons are not unsigned packages. They mean no AppStream metadata or .desktop file. Most are libraries that were never meant to appear in a launcher. A few, as I found later, are a real (and benign) finding.
Neither observation tells you anything about integrity. For that you need the actual tooling.
1. Snap publishers
snap list
for s in $(snap list | awk 'NR>1{print $1}'); do
snap info "$s" | grep -E '^(name|publisher)'
done
The publisher column is the entire point. Snap uses two markers:
| Marker | Meaning |
|---|---|
✓ |
Verified publisher — identity confirmed by Canonical |
✪ |
Starred — a trusted community maintainer |
On my machine: 21 snaps, all marked. Canonical for the base and GNOME runtimes, mozilla✓ for Firefox, bitwarden✓ (8bit Solutions LLC), spotify✓. The three starred ones were Discord and ffmpeg-2404 from Snapcrafters, and Pinta from James Carroll — the legitimate upstream maintainers in both cases.
An unmarked publisher on a snap claiming to be a well-known app is the thing to look for. There weren't any.
2. Third-party APT repositories
This is the real risk vector, and it's the one App Center will never show you.
grep -rhE '^deb ' /etc/apt/sources.list /etc/apt/sources.list.d/ 2>/dev/null
Seven results, all using signed-by= with a dedicated keyring — which is the modern, correct form. The deprecated apt-key approach trusts a key for every repo on the system; signed-by scopes it to one. If you see repos without it, that's worth fixing regardless of whether anything is compromised.
Six were immediately recognizable: Docker, GitHub CLI, Heroku, Cloudflare WARP, mise, and the Ulauncher PPA. The seventh was truncated in my terminal and read, in part, antigravity-repo-key.gpg ... us-central1-apt.pkg.dev/projects/antigra....
Unfamiliar name, Google Cloud infrastructure hostname. Exactly the shape of thing that deserves thirty seconds of verification rather than a shrug.
It's Google's Antigravity IDE. Google's official Linux download page publishes that repo verbatim — same keyring path, same Artifact Registry host, same project. Confirmed, but confirming the URL is only half the check.
3. Verifying the signing key
A repo URL you recognize means nothing if the key trusting it was swapped.
gpg --show-keys --fingerprint /etc/apt/keyrings/antigravity-repo-key.gpg
pub rsa2048 2021-05-04 [SC]
35BA A0B3 3E9E B396 F59C A838 C0BA 5CE6 DC63 15A3
uid Artifact Registry Repository Signer <artifact-registry-repository-signer@google.com>
Full-length match against the published fingerprint, and a single pub key with no extra subkeys smuggled into the file. Compare all 40 characters — matching the last eight is a known-weak check that collision attacks have historically defeated.
One nuance that matters. This is Google's shared Artifact Registry signing key. It signs every public AR apt repo, not just Antigravity's. So the fingerprint proves "Google Artifact Registry published this" — it does not prove "the Antigravity team published this." The trust boundary is the repository path. Verify both halves or you've verified neither.
4. Binary integrity
This is the command that actually answers the malware question.
sudo apt install debsums
sudo debsums -s
debsums recomputes the checksum of every file installed from a .deb and compares it against what the package shipped. -s prints only problems. If something had patched a system binary, this is where it surfaces.
My output — three lines, all of the same kind:
debsums: missing file /usr/share/applications/btop.desktop (from btop package)
debsums: missing file /usr/share/applications/org.flameshot.Flameshot.desktop
debsums: missing file /usr/share/applications/org.gnome.SystemMonitor.desktop
Missing is not modified, and the distinction is the whole finding. Zero checksum mismatches means not one binary or library on the system differs from what Ubuntu published. What's missing is three launcher entries — which is also why those three showed gray placeholder icons back in the GUI. Almost certainly an app-grid cleanup I did and forgot. Malware adds .desktop files for persistence; it has no reason to delete them from unrelated packages.
sudo apt install --reinstall btop flameshot gnome-system-monitor
One caveat: not every package ships checksums, so debsums coverage is good but not total. Add sudo debsums -ca to list modified config files, which is a separate and noisier category.
5. Running services and persistence
systemctl list-units --type=service --state=running
ls -la ~/.config/autostart/ /etc/systemd/system/
Thirty-four running services, every one accounted for: Docker and containerd, warp-svc matching the Cloudflare repo, ClamAV daemon and freshclam, and the usual GNOME and systemd stack. Autostart held two entries, both mine.
The /etc/systemd/system/ listing had a detail I liked seeing:
avahi-daemon.service -> /dev/null
cups.service -> /dev/null
libvirtd.service -> /dev/null
Symlinking a unit to /dev/null is masking — a stronger form of disable that can't be overridden by another unit's dependencies. Past-me hardening the box and leaving a legible trace of it.
What this audit does not cover
Every check above is scoped to the package layer. That layer came back clean, and it's also not where compromise on a developer machine tends to originate.
The paths that stay invisible to all of it:
-
Language package managers —
npm -g,pip,cargo install,go install. Typosquats and compromised maintainer accounts are a live and recurring problem here, and postinstall scripts run with your privileges. -
curl | bashinstallers — no signature, no manifest, no uninstall path, nothing fordebsumsto compare against. - Editor and IDE extensions — VS Code marketplace extensions run with full user permissions and are minimally reviewed.
- Agentic coding tools — anything with filesystem write access and an autonomous execution loop. This is the newest item on the list and the one with the least established review culture around it. That is the sensible next audit. The system package layer is the part with actual cryptographic guarantees behind it — signed repos, verified publishers, reproducible checksums. Everything above it is running on trust.
The runbook
# Snap publishers — every entry should be ✓ or ✪
snap list
for s in $(snap list | awk 'NR>1{print $1}'); do
snap info "$s" | grep -E '^(name|publisher)'
done
# Third-party APT repos — all should use signed-by=
grep -rhE '^deb ' /etc/apt/sources.list /etc/apt/sources.list.d/ 2>/dev/null
# Verify any unfamiliar repo key against its published fingerprint (all 40 chars)
gpg --show-keys --fingerprint /etc/apt/keyrings/<name>.gpg
# Binary integrity — the check that matters
sudo apt install debsums && sudo debsums -s
# Persistence and running services
systemctl list-units --type=service --state=running
ls -la ~/.config/autostart/ /etc/systemd/system/
Five minutes end to end. Worth running on any machine you build client work on, if only to replace a vague sense of unease with an actual answer.
This article was originally published by DEV Community and written by Vicente G. Reyes.
Read original article on DEV Community