v0.4.2 — Tier 2 allowlist hardening (security)
Security — Tier 2 executor allowlist hardening
A multi-agent adversarial audit of the Tier 2 native-automation executors (AppleScript, PowerShell, D-Bus, Shortcuts — shipped in v0.2.0) surfaced and fixed 10 confirmed allowlist bypasses. The shared root cause across most: the allowlist validated only the first token/statement while the OS interpreter ran the entire submitted script. No known public exploitation — these are hardening fixes, each with a dedicated regression test.
The audit ran as an adversarial find→verify workflow: per-executor finders proposed candidate bypasses with concrete exploit inputs, then independent skeptical verifiers traced each through the real matcher to confirm or refute. 13 candidates → 10 verified real → 3 rejected.
AppleScript (4 fixes)
- Whole-script validation. Every
tell application/process "X"target must now be allowlisted, not just the first — closing the multi-tellbypass where an allowed firsttellsmuggled a secondtellto a denied target. do shell script/do scriptdenied by default unless an explicit"do shell script"allowlist entry opts in. Previously they rode through any bare-verb or allowed-leading-statement script, escalating an app-automation grant to arbitrary shell.- Separator normalization (CR, CRLF, U+2028, U+2029 → LF) before parsing, closing the
\r-separator variant.
PowerShell (3 fixes)
- Statement chaining / sub-expressions rejected (
;,|,&, backtick,$(...),@(...), newlines) before the allowlist check.pwsh -Commandruns the whole string, so an allowlisted producer cmdlet could otherwise smuggle arbitrary downstream stages. The guard fails closed (also rejects these inside quoted strings).
D-Bus (2 fixes)
- Bus-scoped matching. A session-bus grant no longer authorizes a privileged system-bus call.
⚠️ Behavior change: legacy 4-part allowlist entries are now session-bus-only; system-bus calls require an explicitsystem:-prefixed 5-part entry. - Segment-bounded wildcards. Trailing-
*on dot-separated service/interface and slash-separated paths no longer leaks into sibling namespaces (org.freedesktop.login1*≠org.freedesktop.login1Manager). Method-name wildcards stay plain prefixes (Get*still matchesGetAll).
Shortcuts (1 fix)
- ASCII-only name folding. Go's
EqualFoldtreats Unicode confusables like U+212A (KELVIN SIGN) as ASCIIk, which let a look-alike name pass the allowlist as "Backup" whileshortcuts runcould resolve a different, attacker-created shortcut. Matching now folds only ASCII A-Z.
Compatibility
One intentional behavior change: D-Bus 4-part allowlist entries are now session-bus-only (add a system: prefix for system-bus calls). Everything else tightens enforcement without changing legitimate usage. No new dependencies; pure-Go, zero-CGO single binary preserved. Cross-compiles verified for linux/arm64, windows/amd64, darwin/arm64.
Tests
10 new regression tests (one per confirmed bypass) plus explicit-opt-in, single-statement, and ASCII-case happy-path coverage. Full repo go test -race -count=1 ./... passes across all 128 packages.
Full changelog: https://github.com/LumabyteCo/aibutler/blob/main/CHANGELOG.md