Beta · macOS

Know what your desktop apps are really shipping.

Achilles scans your /Applications folder and tells you which installed apps ship outdated runtimes, weakened hardened-runtime entitlements, or known-CVE versions of Electron, Tauri, Chromium, Node.js, Flutter, Qt, WKWebView and eight other runtimes it detects — built as a ~7 MB Tauri 2 app, so you can see what an alternative to the 150 MB Electron apps it audits actually looks like.

Status: Beta, macOS-verified. Windows & Linux experimental Detection is reliable against the handful of apps tested (Discord, Signal, 1Password, VS Code, Claude, HyperMeet, Zephyr Agency). Severity scoring is deliberately simple — this is a risk indicator tool, not a verdict.

What it detects

For every bundle in standard install locations (/Applications, /System/Applications, ~/Applications) Achilles extracts:

Framework & runtime versions

Identifies Electron, Tauri, NW.js, Flutter, Qt, React Native, Wails, Sciter, Java, CEF, or native Cocoa — with a high/medium/low confidence rating. Surfaces the precise version of each detected runtime from Info.plists, the Mach-O string table, or bundled release files.

Hardened-runtime entitlements

Flags the load-bearing ones: allow-jit, allow-unsigned-executable-memory, disable-library-validation, get-task-allow, and more.

Code signature & notarization

Signing authority chain, Team ID, hardened-runtime flag, and whether the app carries a stapled notarization ticket.

ASAR integrity

Compares the declared ElectronAsarIntegrity hash against the actual hash of app.asar and tells you whether the archive was modified after signing.

Runtime CVEs

Every detected runtime is checked against four user-toggleable vulnerability feeds, with a max-age filter to keep wide-net CPEs (Safari, Java, Qt) from returning decades of history. See sources below.

Bundled-dependency CVEs

Reads package-lock.json from inside the app's app.asar and runs a batched OSV query across every (name, version) pair it finds.

System side effects

Enumerates what an app installs outside its bundle: browser native-messaging-host bridges, launchd agents/daemons, helper XPC services, and log directories.

Static analysis

An AST-driven rule engine reads app.asar directly and flags insecure Electron configuration (nodeIntegration, missing CSP, disabled sandbox, …). See static analysis.

CVE sources

Achilles leads with ENISA's EUVD — the European Vulnerability Database — because EU-CNA advisories don't always make it into US-centric feeds in time. OSV and NVD are queried alongside for the runtime-specific coverage they do best. All four are configurable from the in-app Settings dialog.

Source Default Scope Auth
EUVD on (primary) ENISA EU-CNA feed — vendor/product search, every runtime none
OSV on Electron (npm), Tauri (crates.io), bundled npm deps none
NVD on Chromium, Node.js, Flutter, Qt, NW.js, Java/JDK, WebKit — by CPE optional API key
GHSA off GitHub Global Security Advisories — npm/rust/go PAT required

Results are cached on disk for 24 hours in ~/Library/Caches/achilles/cve/. Historical CVE data is immutable once published, so repeat scans only pay for newly-seen versions.

Architecture

A vanilla-JS UI over a Tauri 2 core, with each capability in its own isolated Rust crate. Every crate ships an examples/ binary so you can exercise it from the command line.

┌─ ui/ ────────────────────────────────┐   vanilla JS, no bundler
│  index.html + main.js + styles.css   │   listens on scan_event,
└────────────────┬─────────────────────┘   calls invoke() per row click
                 │
┌─ src-tauri/ ───▼─────────────────────┐
│  Tauri 2 app — commands::{discover,  │
│  scan, detect_one, audit, cve_lookup,│
│  static_scan, dependency_scan}       │
└─┬────────────────────────────────────┘
  ├─ crates/detect        framework + version extraction (Mach-O scan)
  ├─ crates/scan          mdfind + concurrent detect(), streams events
  ├─ crates/cve           EUVD + OSV + NVD + GHSA client with disk cache
  ├─ crates/macho-audit   entitlements / codesign / Info.plist / ASAR
  ├─ crates/static-scan   ASAR reader + oxc AST rule engine
  └─ crates/sideeffects   out-of-bundle installs: bridges, launch agents

Static analysis

The static-scan crate reads Contents/Resources/app.asar directly — no extraction, no external runtime — and runs an oxc-driven AST rule set against every JS/TS/HTML file. Boolean checks handle minified forms (sandbox: !1) out of the box. Rule IDs mirror Electronegativity so findings stay portable.

Rule Severity Confidence
CONTEXT_ISOLATION_JS_CHECKCriticalFirm
CSP_GLOBAL_CHECKHighFirm
SANDBOX_JS_CHECKHighFirm
NODE_INTEGRATION_JS_CHECKHighFirm
WEB_SECURITY_JS_CHECKHighFirm
ALLOW_RUNNING_INSECURE_CONTENT_JS_CHECKHighFirm
EXPERIMENTAL_FEATURES_JS_CHECKMediumFirm
OPEN_EXTERNAL_JS_CHECKMediumTentative

Build from source

Requirements: Rust 1.80+, macOS 12+. The GUI needs nothing else to run.

# launch the desktop app (auto-scans on open)
cargo run -p achilles

# or exercise a single crate from the CLI
cargo run -p detect      --example detect      -- "/Applications/Signal.app"
cargo run -p cve         --example lookup      -- electron 40.4.1 npm
cargo run -p static-scan --example static-scan -- \
  "/Applications/Signal.app/Contents/Resources/app.asar"

Privacy & non-goals

No telemetry

Scanning happens entirely locally. CVE lookups hit the feeds directly by version string — nothing about your installed apps is transmitted. This is a design commitment, not just a current gap.

Not a verdict tool

The output is risk signals, not "this app is compromised." A phrase like "unsafe" never appears in the UI on purpose.

Not a vendor-triage replacement

If an advisory fires on an app you care about, read the upstream changelog before concluding anything.