Merge pull request #11874 from acrogenesis/feature/usb-device-authorization

Require approval for new USB and Thunderbolt devices by default.

Reported-by: Jurre van Bergen me@jurrevanbergen.nl
This commit is contained in:
Erik Melton authored and GitHub committed 2026-10-09 21:16:12 +02:00
commit 5cb31317b4
57 files changed
+5775 -2

No files matched your search

@@ -0,0 +1,87 @@
#!/bin/bash
# Runs against the private real boltd created by bolt-integration.py. No policy,
# busctl, firmware reads, or writes are mocked here.
set -euo pipefail
source "$ROOT/install/helpers/thunderbolt-policy.sh"
source "$ROOT/install/helpers/thunderbolt-setup.sh"
TB_STATE="$TB_TEST_DIR/policy.json"
TB_RUNTIME="$TB_TEST_DIR/runtime"
mkdir -p "$TB_RUNTIME"
check() { jq -e "$1" "$2" >/dev/null; }
reject() { if "$@"; then echo 'Unexpected approval success' >&2; exit 1; fi; }
case "$1" in
approve)
echo '{"version":1,"enabled":true,"trusted":{}}' | tb_json_write "$TB_STATE"
cat /proc/sys/kernel/random/uuid > "$TB_RUNTIME/generation"
tb_snapshot > "$TB_TEST_DIR/before.json"
capture=$(tb_capture)
tb_inventory | jq -e --arg uid "$TB_TEST_UID" --argjson capture "$capture" \
'.devices[] | select(.Uid==$uid) | $capture[$uid]=={Uid,Name,Vendor}' >/dev/null
check 'all(.devices[]; .status=="connected" and (.stored|not))' "$TB_TEST_DIR/before.json"
id=$(jq -c --arg uid "$TB_TEST_UID" '.devices[] | select(.identity.Uid==$uid) | .identity' "$TB_TEST_DIR/before.json")
printf '%s\n' "$id" > "$TB_TEST_DIR/identity.json"
tb_approve "$id" true true > "$TB_TEST_DIR/after.json"
jq -e --arg uid "$TB_TEST_UID" '.devices[] | select(.identity.Uid==$uid) | .trusted and .status=="authorized" and .policy=="manual"' "$TB_TEST_DIR/after.json" >/dev/null
jq -e --arg uid "$TB_TEST_OTHER" '.devices[] | select(.identity.Uid==$uid) | .status=="connected"' "$TB_TEST_DIR/after.json" >/dev/null
check 'all(.domains[0].BootACL[]; .=="")' "$TB_TEST_DIR/after.json"
inventory=$(tb_inventory)
if [[ $TB_TEST_SECURITY == "secure" ]]; then
jq -e --arg uid "$TB_TEST_UID" '.devices[] | select(.Uid==$uid) | .Stored and .Key=="have"' <<< "$inventory" >/dev/null
[[ -s $TB_TEST_KEYS/$TB_TEST_UID ]]
sha256sum "$TB_TEST_KEYS/$TB_TEST_UID" > "$TB_TEST_DIR/key-checksum"
fi
owner=$(jq -r .owner <<< "$inventory")
device=$(jq -c --arg uid "$TB_TEST_UID" '.stored[] | select(.Uid==$uid)' <<< "$inventory")
domain=$(jq -c '.domains[0]' <<< "$inventory")
device_path=$(jq -r .path <<< "$device"); domain_path=$(jq -r .path <<< "$domain")
tb_set "$owner" "$device_path" "$TB_DEVICE" Policy '"auto"'
acl=$(jq -c --arg uid "$TB_TEST_UID" '.BootACL | map("") | .[0]=$uid' <<< "$domain")
tb_set "$owner" "$domain_path" "$TB_DOMAIN" BootACL "$acl" as
tb_boot_change true
check .boot_protection "$TB_STATE"
tb_inventory > "$TB_TEST_DIR/inventory.json"
check 'all(.domains[0].BootACL[]; .=="") and .stored[0].Policy=="manual"' "$TB_TEST_DIR/inventory.json"
tb_boot_change false
check '.boot_protection==false' "$TB_STATE"
tb_inventory > "$TB_TEST_DIR/inventory.json"
jq -e --argjson acl "$acl" '.domains[0].BootACL==$acl and .stored[0].Policy=="auto"' "$TB_TEST_DIR/inventory.json" >/dev/null
jq --arg other "$TB_TEST_OTHER" 'del(.trusted[$other])' "$TB_STATE" | tb_json_write "$TB_STATE"
tb_set "$owner" "$device_path" "$TB_DEVICE" Policy '"manual"'
tb_set "$owner" "$domain_path" "$TB_DOMAIN" BootACL "$(jq -c 'map("")' <<< "$acl")" as
;;
restart)
cat /proc/sys/kernel/random/uuid > "$TB_RUNTIME/generation"
reject tb_approve "$(cat "$TB_TEST_DIR/identity.json")" true
tb_reconcile > "$TB_TEST_DIR/after.json"
jq -e --arg uid "$TB_TEST_UID" '.devices[] | select(.identity.Uid==$uid) | .trusted and .status=="authorized"' "$TB_TEST_DIR/after.json" >/dev/null
jq -e --arg uid "$TB_TEST_OTHER" '.devices[] | select(.identity.Uid==$uid) | .status=="connected"' "$TB_TEST_DIR/after.json" >/dev/null
tb_inventory | jq -e '.manager.AuthMode=="disabled"' >/dev/null
if [[ $TB_TEST_SECURITY == "secure" ]]; then
# Bolt must challenge with the originally saved key, not provision a new
# key merely because a reconnecting device advertises the same identity.
tb_inventory | jq -e --arg uid "$TB_TEST_UID" \
'.devices[] | select(.Uid==$uid) | .Key=="have" and (.AuthFlags | contains("secure"))' >/dev/null
sha256sum --status -c "$TB_TEST_DIR/key-checksum"
fi
;;
daemon)
TB_MARKER="$TB_TEST_DIR/enabled" TB_LOCK="$TB_TEST_DIR/root.lock"
touch "$TB_MARKER"
tb_daemon
;;
status)
snapshot=$(tb_public_snapshot)
jq -e --arg owner "$(tb_owner)" '.owner==$owner' <<< "$snapshot" >/dev/null
printf '%s\n' "$snapshot"
;;
once)
snapshot=$(tb_public_snapshot)
id=$(jq -c --arg uid "$TB_TEST_OTHER" '.devices[] | select(.identity.Uid==$uid) | .identity' <<< "$snapshot")
exec 9> "$TB_TEST_DIR/root.lock"
flock -x 9
tb_approve "$id" false > "$TB_TEST_DIR/once.json"
jq -e --arg uid "$TB_TEST_OTHER" '.devices[] | select(.identity.Uid==$uid) | .status=="authorized" and (.trusted|not)' "$TB_TEST_DIR/once.json" >/dev/null
;;
*) exit 64 ;;
esac
@@ -0,0 +1,185 @@
"""Only the hardware fixture is Python: upstream Bolt uses UMockdev/GObject.
All Omarchy behavior and assertions run in bolt-exercise.sh against real boltd,
real busctl, a private system bus, and disposable sysfs/firmware/store state.
"""
import importlib.machinery
import importlib.util
import os
from pathlib import Path
import subprocess
import sys
import time
import unittest
root = Path(sys.argv.pop(1))
loader = importlib.machinery.SourceFileLoader('bolt_upstream', str(Path(os.environ['BOLT_TEST_SOURCE']) / 'tests/test-integration'))
spec = importlib.util.spec_from_loader(loader.name, loader)
u = importlib.util.module_from_spec(spec)
loader.exec_module(u)
class ApprovalIntegration(u.BoltTest):
def exercise(self, security, numeric=False):
self.user_config(AuthMode='disabled', DefaultPolicy='manual')
_, host = self.add_domain_host(security=security, iommu='1')
path, uid = self.add_device(host, 1, 'Dock', 'Example', authorized=0, key='' if security == 'secure' else None, boot='0')
_, other = self.add_device(host, 2, 'Unknown', 'Example', authorized=0, key=None, boot='0')
if numeric:
# Linux omits these files when the peripheral has no descriptive names.
for attribute in ('device_name', 'vendor_name'):
Path(self.testbed.get_root_dir(), path.lstrip('/'), attribute).unlink()
self.daemon_start()
self.polkitd_start()
self.polkitd.SetAllowed(['org.freedesktop.bolt.authorize', 'org.freedesktop.bolt.enroll', 'org.freedesktop.bolt.manage'])
env = os.environ | {'ROOT': str(root), 'UMOCKDEV_DIR': self.testbed.get_root_dir(),
'TB_TEST_DIR': self.rundir, 'TB_TEST_UID': uid, 'TB_TEST_OTHER': other,
'TB_TEST_KEYS': str(Path(self.dbpath, 'keys')), 'TB_TEST_SECURITY': security}
command = ['bash', str(root / 'test/shell.d/fixtures/thunderbolt/bolt-exercise.sh')]
subprocess.run(command + ['approve'], env=env, check=True)
self.daemon_stop()
self.testbed.set_attribute(path, 'authorized', '0')
if security == 'secure':
self.testbed.set_attribute(path, 'key', '\n')
self.daemon_start()
subprocess.run(command + ['restart'], env=env, check=True)
# The real Bash daemon publishes a fresh readable snapshot and reconnects
# to Bolt after a restart. No custom Omarchy D-Bus server is involved.
child = subprocess.Popen(command + ['daemon'], env=env)
try:
for _ in range(100):
result = subprocess.run(command + ['status'], env=env, capture_output=True)
if result.returncode == 0:
break
time.sleep(0.1)
else:
raise RuntimeError('Bash daemon did not publish a valid snapshot')
subprocess.run(command + ['once'], env=env, check=True)
self.daemon_stop()
self.daemon_start()
for _ in range(100):
if subprocess.run(command + ['status'], env=env, capture_output=True).returncode == 0:
break
time.sleep(0.1)
else:
raise RuntimeError('Bash daemon did not publish the new Bolt generation')
finally:
child.terminate()
child.wait(timeout=15)
self.daemon_stop()
def test_user_approval(self):
self.exercise('user')
def test_secure_approval(self):
self.exercise('secure')
def test_numeric_identity(self):
self.exercise('user', numeric=True)
def removal(self, security, boot=False, disconnected=False):
self.user_config(AuthMode='disabled')
_, host = self.add_domain_host(security=security, iommu='1')
path, uid = self.add_device(host, 1, 'Dock', 'Example', authorized=0, key='' if security == 'secure' else None, boot='0')
other_path, other = self.add_device(host, 2, 'Existing', 'Example', authorized=0, key=None, boot='0')
self.daemon_start()
self.polkitd_start()
self.polkitd.SetAllowed(['org.freedesktop.bolt.authorize', 'org.freedesktop.bolt.enroll', 'org.freedesktop.bolt.manage'])
env = os.environ | {'ROOT': str(root), 'UMOCKDEV_DIR': self.testbed.get_root_dir(),
'TB_TEST_DIR': self.rundir, 'TB_TEST_UID': uid, 'TB_TEST_OTHER': other,
'TB_TEST_STORE': self.dbpath, 'TB_TEST_SECURITY': security}
command = ['bash', str(root / 'test/shell.d/fixtures/thunderbolt/bolt-removal.sh')]
service = 'omarchy-thunderbolt-authorization.service'
child = None
def operation(action, unit):
nonlocal child
if unit == 'bolt.service':
if action == 'stop':
self.daemon_stop()
elif self.daemon is None:
self.daemon_start()
elif unit == service:
if action == 'stop' and child is not None:
child.terminate()
child.wait(timeout=15)
child = None
elif action == 'start' and child is None:
child = subprocess.Popen(['bash', str(root / 'test/shell.d/fixtures/thunderbolt/bolt-exercise.sh'), 'daemon'], env=env)
else:
raise RuntimeError('Unexpected private service: ' + unit)
marker = Path(self.rundir, unit + '.active')
if action == 'start':
marker.touch()
else:
marker.unlink(missing_ok=True)
def run(action):
process = subprocess.Popen(command + [action], env=env)
control = Path(self.rundir, 'control')
deadline = time.monotonic() + 90
try:
while process.poll() is None:
if time.monotonic() > deadline:
raise RuntimeError('Private setup timed out: ' + action)
if control.exists():
operation(*control.read_text().strip().split())
control.unlink()
time.sleep(0.05)
if process.returncode:
raise subprocess.CalledProcessError(process.returncode, command + [action])
finally:
if process.poll() is None:
process.terminate()
process.wait(timeout=15)
def wait_for(action):
for _ in range(50):
result = subprocess.run(command + [action], env=env, capture_output=True, text=True)
if result.returncode == 0:
print(result.stdout, end='', flush=True)
return
time.sleep(0.1)
raise RuntimeError(action + ': ' + result.stdout + result.stderr)
def unplug():
for device_path in (path, other_path):
self.testbed.uevent(device_path, 'remove')
self.testbed.remove_device(device_path)
wait_for('disconnected')
try:
Path(self.rundir, 'bolt.service.active').touch()
run('approve')
operation('start', service)
Path(self.rundir, service + '.enabled').touch()
if boot:
run('boot-enable')
run('rollback')
if disconnected:
unplug()
run('disable')
if not disconnected:
unplug()
for number, device_uid, name in ((1, uid, 'Dock'), (2, other, 'Existing')):
attributes = ['device_name', name, 'device', '0x23', 'vendor_name', 'Example',
'vendor', '0x23', 'authorized', '0', 'unique_id', device_uid, 'boot', '0']
if number == 1 and security == 'secure':
attributes += ['key', '\n']
self.testbed.add_device('thunderbolt', '0-' + str(number), host, attributes, ['DEVTYPE', 'thunderbolt_device'])
wait_for('reconnected')
finally:
operation('stop', service)
self.daemon_stop()
def test_user_removal(self):
self.removal('user')
def test_secure_removal(self):
self.removal('secure', boot=True, disconnected=True)
names = sys.argv[1:] or ('test_user_approval', 'test_secure_approval', 'test_numeric_identity', 'test_user_removal', 'test_secure_removal')
suite = unittest.TestSuite(ApprovalIntegration(name) for name in names)
result = unittest.TextTestRunner(verbosity=2).run(suite)
sys.exit(not result.wasSuccessful())
@@ -0,0 +1,95 @@
#!/bin/bash
# Real Bolt policy/approval/setup calls. Only systemd is bridged to the private
# daemon processes owned by bolt-integration.py; never contact host systemd.
set -euo pipefail
source "$ROOT/install/helpers/thunderbolt-policy.sh"
source "$ROOT/install/helpers/thunderbolt-setup.sh"
TB_STATE="$TB_TEST_DIR/policy.json" TB_CONFIG="$TB_TEST_STORE/boltd.conf"
TB_MARKER="$TB_TEST_DIR/enabled" TB_PENDING="$TB_TEST_DIR/pending"
TB_RUNTIME="$TB_TEST_DIR/runtime" TB_LOCK="$TB_TEST_DIR/root.lock"
mkdir -p "$TB_RUNTIME"
service_request() {
printf '%s\n' "$*" > "$TB_TEST_DIR/control.tmp"
mv "$TB_TEST_DIR/control.tmp" "$TB_TEST_DIR/control"
for (( attempt=0; attempt<300; attempt++ )); do
[[ -f $TB_TEST_DIR/control ]] || return 0
sleep 0.05
done
echo "Private service operation timed out: $*" >&2
return 1
}
systemctl() {
local action=$1 unit
shift
printf '%s\n' "$action $*" >> "$TB_TEST_DIR/services.log"
case "$action" in
show)
unit=${!#}
if [[ -f $TB_TEST_DIR/$unit.active ]]; then echo ActiveState=active; else echo ActiveState=inactive; fi
if [[ -f $TB_TEST_DIR/$unit.enabled ]]; then echo UnitFileState=enabled; else echo UnitFileState=disabled; fi ;;
start|stop)
for unit in "$@"; do service_request "$action" "$unit" || return 1; done ;;
enable)
unit=${!#}
touch "$TB_TEST_DIR/$unit.enabled"
if [[ $1 == "--now" ]]; then service_request start "$unit"; fi ;;
disable)
if [[ -f $TB_TEST_DIR/fail-disable ]]; then rm "$TB_TEST_DIR/fail-disable"; return 1; fi
rm -f -- "$TB_TEST_DIR/$1.enabled" ;;
daemon-reload) ;;
*) echo "Unexpected systemctl action: $action $*" >&2; return 1 ;;
esac
}
check_inventory() { tb_inventory | jq -e --arg uid "$TB_TEST_UID" --arg other "$TB_TEST_OTHER" "$1" >/dev/null; }
case "$1" in
approve)
jq -cn '{version:1,enabled:true,trusted:{},original_authmode:"enabled"}' > "$TB_STATE"
echo removal-test > "$TB_RUNTIME/generation"
touch "$TB_MARKER"
identity=$(tb_snapshot | jq -c --arg uid "$TB_TEST_UID" '.devices[]|select(.identity.Uid==$uid)|.identity')
tb_approve "$identity" true true >/dev/null
# An existing manual record, even explicitly trusted through Omarchy,
# must keep the policy its administrator chose before enrollment.
inventory=$(tb_inventory)
owner=$(jq -r .owner <<< "$inventory")
path=$(jq -r --arg uid "$TB_TEST_OTHER" '.devices[]|select(.Uid==$uid)|.path' <<< "$inventory")
tb_bus call "$owner" "$path" "$TB_DEVICE" Authorize s '' >/dev/null
tb_bus call "$owner" "$TB_PATH" "$TB_MANAGER" EnrollDevice sss "$TB_TEST_OTHER" manual '' >/dev/null
identity=$(tb_snapshot | jq -c --arg uid "$TB_TEST_OTHER" '.devices[]|select(.identity.Uid==$uid)|.identity')
tb_approve "$identity" true true >/dev/null
jq -e --arg uid "$TB_TEST_UID" --arg other "$TB_TEST_OTHER" \
'.enrolled[$uid].StoreTime>0 and .enrolled[$other]==null and .trusted[$other]!=null' "$TB_STATE" >/dev/null
if [[ $TB_TEST_SECURITY == "secure" ]]; then
sha256sum "$TB_TEST_STORE/keys/$TB_TEST_UID" > "$TB_TEST_DIR/key-checksum"
fi ;;
boot-enable) tb_admin boot-enable ;;
rollback)
cp "$TB_STATE" "$TB_TEST_DIR/old-policy.json"
tb_inventory | tb_boot_saved > "$TB_TEST_DIR/old-firmware.json"
touch "$TB_TEST_DIR/fail-disable"
if tb_admin disable; then echo 'Failed removal unexpectedly succeeded' >&2; exit 1; fi
cmp "$TB_STATE" "$TB_TEST_DIR/old-policy.json"
cmp <(tb_inventory | tb_boot_saved | jq -cS .) <(jq -cS . "$TB_TEST_DIR/old-firmware.json")
[[ -f $TB_MARKER && -f $TB_TEST_DIR/$TB_SERVICE.active && -f $TB_TEST_DIR/$TB_SERVICE.enabled && ! -f $TB_TEST_DIR/setup-recovery.json ]]
check_inventory '.manager.AuthMode=="disabled"'
echo 'Removal failure restored policy, firmware ACLs and approval service' ;;
disable)
tb_admin disable
[[ ! -f $TB_MARKER && ! -f $TB_TEST_DIR/$TB_SERVICE.active && ! -f $TB_TEST_DIR/$TB_SERVICE.enabled ]]
jq -e '.enabled==false and .enrolled==null' "$TB_STATE" >/dev/null
check_inventory '.manager.AuthMode=="enabled" and any(.stored[]; .Uid==$uid and .Policy=="auto") and any(.stored[]; .Uid==$other and .Policy=="manual")'
if [[ $TB_TEST_SECURITY == "secure" ]]; then sha256sum --status -c "$TB_TEST_DIR/key-checksum"; fi
echo 'Removal handed our record back to Bolt and preserved existing manual policy' ;;
disconnected)
check_inventory 'any(.stored[]; .Uid==$uid and .Status=="disconnected") and any(.stored[]; .Uid==$other and .Status=="disconnected")' ;;
reconnected)
check_inventory 'any(.devices[]; .Uid==$uid and .Policy=="auto" and (.Status|startswith("authorized"))) and any(.devices[]; .Uid==$other and .Policy=="manual" and .Status=="connected")'
if [[ $TB_TEST_SECURITY == "secure" ]]; then
check_inventory 'any(.devices[]; .Uid==$uid and .Key=="have" and (.AuthFlags|contains("secure")))'
sha256sum --status -c "$TB_TEST_DIR/key-checksum"
fi
echo "Automatic reconnect succeeded in $TB_TEST_SECURITY mode; pre-existing manual device stayed blocked" ;;
*) exit 64 ;;
esac
@@ -0,0 +1,640 @@
#!/bin/bash
# These behavior cases were first run as the existing Python assertions against
# the Bash implementation. Fixtures replace only Bolt, systemd and desktop I/O.
set -euo pipefail
source "$ROOT/test/shell.d/base-test.sh"
source "$ROOT/install/helpers/thunderbolt-policy.sh"
source "$ROOT/install/helpers/thunderbolt-setup.sh"
source "$ROOT/install/helpers/thunderbolt-review.sh"
fixture() {
T=$(mktemp -d)
trap 'rm -rf -- "$T"' EXIT
export HOME="$T/home"
TB_STATE="$T/policy.json" TB_CONFIG="$T/boltd.conf" TB_MARKER="$T/enabled"
TB_PENDING="$T/pending" TB_SYSFS="$T/sysfs" TB_KEYS="$T/keys"
TB_RUNTIME="$T/runtime" TB_LOCK="$T/root.lock"
mkdir -p "$TB_RUNTIME" "$HOME" "$TB_SYSFS" "$TB_KEYS"
echo generation-a > "$TB_RUNTIME/generation"
echo '{"version":1,"enabled":true,"trusted":{},"original_authmode":"enabled"}' > "$TB_STATE"
cat > "$T/inventory" <<'JSON'
{"owner":":1.5","manager":{"AuthMode":"disabled"},"domains":[{"Uid":"host","SecurityLevel":"user","IOMMU":true,"SysfsPath":"/sys/domain","path":"/domain","BootACL":["device-1",""]}],"devices":[{"Uid":"device-1","Name":"Dock","Vendor":"Vendor","Parent":"host","SysfsPath":"/sys/devices/test/0-1","ConnectTime":1,"Status":"connected","Stored":false,"Policy":"default","AuthFlags":"","path":"/devices/1","inode":"1"}]}
JSON
: > "$T/operations"; : > "$T/units"; : > "$T/notifications"
echo '{}' > "$T/capture"
tb_requests
}
edit_inventory() { jq "$@" "$T/inventory" > "$T/next"; mv "$T/next" "$T/inventory"; }
edit_state() { jq "$@" "$TB_STATE" | tb_json_write "$TB_STATE"; }
check() { jq -e "$1" "$2" >/dev/null || fail "$1" "$(cat "$2")"; }
reject() { if ("$@") > "$T/output" 2> "$T/error"; then fail "unexpected success: $*"; fi; }
identity() { tb_snapshot | jq -c '.devices[0].identity'; }
tb_inventory() {
[[ ! -f $T/inventory-failure ]] || { tb_fail 'inventory unavailable'; return 1; }
jq '. + {stored:[.devices[] | select(.Stored)]}' "$T/inventory"
}
tb_properties() { jq -c --arg path "$2" '(.devices + .domains)[] | select(.path==$path)' "$T/inventory"; }
tb_bus() {
local command=$1 path=$3 member=$5 value
printf '%s\n' "$member" >> "$T/operations"
case "$member" in
Authorize)
[[ ! -f $T/authorize-failure ]] || return 1
if [[ ! -f $T/authorize-noop ]]; then edit_inventory --arg path "$path" '(.devices[] | select(.path==$path)).Status="authorized"'; fi ;;
EnrollDevice)
if [[ ! -f $T/enroll-noop ]]; then
edit_inventory '(.devices[0] | .Stored=true | .Policy="manual" | .StoreTime=100) as $d | .devices[0]=$d'
if [[ -f $TB_SYSFS/0-1/key && $(cat "$TB_SYSFS/0-1/key") =~ ^[0-9a-f]{64}$ && ! -f $T/key-store-failure ]]; then
edit_inventory '.devices[0].Key="have"'
fi
fi
if [[ -f $T/enroll-lost-reply ]]; then rm "$T/enroll-lost-reply"; return 1; fi ;;
Policy|BootACL)
if [[ $member == "BootACL" && -f $T/boot-failure ]] || [[ $member == "Policy" && ${7:-} == "auto" && -f $T/restore-failure ]]; then
tb_fail 'firmware rejected write'; return 1
fi
[[ $member != "Policy" || ! -f $T/policy-noop ]] || return 0
if [[ $member == "BootACL" ]]; then
shift 7
value=$(jq -cn '$ARGS.positional' --args "$@")
else
value=$(jq -cn --arg value "$7" '$value')
fi
edit_inventory --arg path "$path" --arg key "$member" --argjson value "$value" \
'((.devices[],.domains[]) | select(.path==$path))[$key]=$value'
if [[ $member == "Policy" ]]; then
# Real Bolt changes firmware ACLs when a stored policy changes.
edit_inventory --arg path "$path" --arg value "$7" '
(.devices[] | select(.path==$path) | .Uid) as $uid |
.domains[].BootACL |= (if $value=="auto" and (index($uid)==null) then
(index("") // 0) as $slot | .[$slot]=$uid
elif $value!="auto" then map(if .==$uid then "" else . end) else . end)'
fi ;;
*) tb_fail "Unexpected Bolt call: $*"; return 1 ;;
esac
}
tb_capture() { [[ ! -f $T/capture-failure ]] && cat "$T/capture"; }
tb_unit_state() { jq -cn --arg unit "$1" '{active:($unit=="bolt.service"),enabled:false}'; }
systemctl() {
if [[ $1 == --root=* ]]; then command systemctl "$@"; return; fi
printf '%s\n' "$*" >> "$T/units"
if [[ -f $T/unit-failure && $* == "$(cat "$T/unit-failure")" ]]; then
[[ -f $T/repeat-failure ]] || rm "$T/unit-failure"
tb_fail 'systemd failed'; return 1
fi
if [[ $* == "start bolt.service" && -f $TB_CONFIG ]]; then
edit_inventory --arg mode "$(tb_config_authmode "$TB_CONFIG")" '.manager.AuthMode=$mode'
if [[ ! -f $TB_MARKER && -f $T/restart-policy-failure ]]; then
rm "$T/restart-policy-failure"
edit_inventory '.devices[0].Policy="manual"'
fi
fi
}
tb_public_snapshot() { cat "$T/public"; }
tb_notify() {
echo "$1" >> "$T/notifications"
if [[ -f $T/notify-failure ]]; then rm "$T/notify-failure"; return 1; fi
}
tb_attention() { echo attention >> "$T/notifications"; }
gum() { if [[ $1 == "choose" ]]; then cat "$T/choice"; fi; }
tb_request_apply() {
jq --argjson identity "$2" --argjson permanent "$1" \
'(.devices[] | select(.identity==$identity)) |= (.status="authorized" | .trusted=$permanent | .stored=$permanent)' "$T/public" > "$T/next"
mv "$T/next" "$T/public"
[[ ! -f $T/lost-reply ]] || { tb_fail 'reply lost'; return 1; }
cat "$T/public"
}
request() { local paths=("$TB_REQUESTS"/request-*.json); printf '%s\n' "${paths[0]}"; }
request_count() { local paths; shopt -s nullglob; paths=("$TB_REQUESTS"/request-*.json); echo "${#paths[@]}"; }
public_fixture() { tb_snapshot > "$T/public"; }
boot_fixture() { edit_inventory '.devices[0] |= (.Stored=true | .Policy="auto")'; }
review_tick() {
local path watch
path=$(request); watch=$(jq -r .watch "$path")
tb_scan "$watch" >/dev/null
}
test_iommu_is_not_consent() {
edit_inventory '.devices[0] |= (.Stored=true | .Policy="iommu")'
tb_reconcile > "$T/result"
[[ ! -s $T/operations ]]; check '.devices[0].trusted==false' "$T/result"
}
test_once_is_not_saved() {
tb_approve "$(identity)" false > "$T/result"
check '.warnings==[]' "$T/result"; check '.trusted=={}' "$TB_STATE"
edit_inventory '.devices[0].Status="connected"'
tb_reconcile >/dev/null
[[ $(cat "$T/operations") == "Authorize" ]]
}
test_always_survives_restart() {
tb_approve "$(identity)" true >/dev/null
edit_inventory '.owner=":1.9" | .devices[0] |= (.Status="connected" | .inode="2")'
echo generation-b > "$TB_RUNTIME/generation"
tb_reconcile > "$T/result"
check '.devices[0].status=="authorized" and .devices[0].trusted' "$T/result"
check '.manager.AuthMode=="disabled"' "$T/inventory"
}
test_enrollment_lost_reply_retains_ownership() {
touch "$T/enroll-lost-reply"
reject tb_approve "$(identity)" true
check '.trusted=={} and .enrolled["device-1"].StoreTime==null' "$TB_STATE"
tb_approve "$(identity)" true >/dev/null
check '.enrolled["device-1"].StoreTime==100 and .trusted["device-1"]!=null' "$TB_STATE"
}
secure_fixture() {
mkdir -p "$TB_SYSFS/0-1"
echo 11111111-2222-3333-4444-555555555555 > "$TB_SYSFS/0-1/unique_id"
echo 0 > "$TB_SYSFS/0-1/authorized"
: > "$TB_SYSFS/0-1/key"
edit_inventory --arg path "$TB_SYSFS/0-1" --arg inode "$(stat -Lc %i "$TB_SYSFS/0-1")" \
'.domains[0].SecurityLevel="secure" | .devices[0] |= (.Secure=true | .Key="missing" |
.Uid="11111111-2222-3333-4444-555555555555" | .SysfsPath=$path | .inode=$inode)'
}
test_secure_approval_initializes_before_trust() {
secure_fixture
tb_approve "$(identity)" true true > "$T/result"
[[ $(cat "$TB_SYSFS/0-1/key") =~ ^[0-9a-f]{64}$ ]]
check '.devices[0].trusted and .devices[0].stored' "$T/result"
cp "$TB_SYSFS/0-1/key" "$T/original-key"
tb_approve "$(identity)" true true >/dev/null
cmp "$TB_SYSFS/0-1/key" "$T/original-key"
}
test_secure_automatic_restore_cannot_initialize_keys() {
secure_fixture
jq '.devices[0] | {(.Uid):{Uid,Name,Vendor}}' "$T/inventory" > "$T/trusted"
edit_state --slurpfile trusted "$T/trusted" '.trusted=$trusted[0]'
tb_reconcile > "$T/result"
check '.devices[0].trusted==false' "$T/result"
[[ ! -s $T/operations && ! -s $TB_SYSFS/0-1/key ]]
reject tb_approve "$(identity)" true
[[ ! -s $T/operations ]]
}
test_secure_missing_saved_key_cannot_claim_success() {
secure_fixture
touch "$T/key-store-failure"
reject tb_approve "$(identity)" true true
check '.trusted=={}' "$TB_STATE"
tb_snapshot > "$T/result"; check '.devices[0].trusted==false' "$T/result"
}
test_secure_key_failure_does_not_authorize() {
secure_fixture
openssl() { return 1; }
reject tb_approve "$(identity)" true true
[[ ! -s $T/operations && ! -s $TB_SYSFS/0-1/key ]]
unset -f openssl
echo invalid > "$TB_SYSFS/0-1/key"
reject tb_approve "$(identity)" true true
[[ ! -s $T/operations ]]
}
test_authorized_keyless_device_requires_explicit_reconnect() {
secure_fixture
edit_inventory '.devices[0].Status="authorized"'
echo 1 > "$TB_SYSFS/0-1/authorized"
reject tb_approve "$(identity)" true true
rg -q 'disconnect, reconnect' "$T/error"
[[ ! -s $TB_SYSFS/0-1/key ]]; check '.trusted=={}' "$TB_STATE"
edit_inventory '.devices[0].Status="connected"'
tb_reconcile >/dev/null
[[ $(cat "$T/operations") == "EnrollDevice" ]]
}
test_stale_identity_rejected() {
local id field
id=$(identity)
for field in inode Name ConnectTime; do
cp "$T/inventory" "$T/original"
edit_inventory --arg field "$field" '.devices[0][$field]="replacement"'
reject tb_approve "$id" true
mv "$T/original" "$T/inventory"
done
edit_inventory '.owner=":1.9"'; reject tb_approve "$id" true
[[ ! -s $T/operations ]]
}
test_false_success_rejected() {
local id; id=$(identity)
touch "$T/authorize-noop"; reject tb_approve "$id" true; check '.trusted=={}' "$TB_STATE"
rm "$T/authorize-noop"; touch "$T/enroll-noop"
reject tb_approve "$id" true; check '.trusted=={}' "$TB_STATE"
rm "$T/enroll-noop"; tb_approve "$id" true > "$T/result"
check '.devices[0].trusted' "$T/result"
[[ $(rg -c '^Authorize$' "$T/operations") == 2 ]]
}
test_write_failure_retains_retry() {
local id; id=$(identity)
eval "$(declare -f tb_json_write | sed '1s/tb_json_write/disk_write/')"
tb_json_write() { [[ ! -f $T/disk-full ]] && disk_write "$@"; }
touch "$T/disk-full"; reject tb_approve "$id" true; check '.trusted=={}' "$TB_STATE"
rm "$T/disk-full"; tb_approve "$id" true > "$T/result"
check '.devices[0].trusted' "$T/result"
[[ $(rg -c '^Authorize$' "$T/operations") == 1 ]]
}
test_firmware_bypass_warns() {
edit_inventory '.domains[0].SecurityLevel="none" | .devices[0] |= (.Status="authorized" | .AuthFlags="boot")'
tb_snapshot > "$T/result"; check '.warnings|length==2' "$T/result"; [[ ! -s $T/operations ]]
}
test_global_enable_not_protected() {
edit_inventory '.manager.AuthMode="enabled"'
reject tb_snapshot; rg -q 'not active' "$T/error"
reject tb_reconcile
}
test_display_only_needs_no_approval() {
edit_state '.trusted["device-1"]={Uid:"device-1",Name:"Dock",Vendor:"Vendor"}'
edit_inventory '.devices[0].AuthFlags="nopcie"'
tb_reconcile > "$T/result"; check '.error==""' "$T/result"; [[ ! -s $T/operations ]]
}
test_empty_capture_preserved() {
rm "$TB_STATE"; tb_prepare
touch "$T/capture-failure"; tb_prepare
check '.trusted=={}' "$TB_STATE"; [[ $(tb_config_authmode "$TB_CONFIG") == "disabled" ]]
}
test_capture_failure_does_not_publish() {
rm "$TB_STATE"; touch "$T/capture-failure"; reject tb_prepare
[[ ! -e $TB_STATE && ! -e $TB_MARKER ]]
}
test_capture_optional_names_match_numeric_identity() {
source "$ROOT/install/helpers/thunderbolt-setup.sh"
mkdir -p "$TB_SYSFS/0-1"
echo device-1 > "$TB_SYSFS/0-1/unique_id"
echo 0x1234 > "$TB_SYSFS/0-1/device"
echo 0xab > "$TB_SYSFS/0-1/vendor"
tb_capture > "$T/result"
check '.["device-1"]=={Uid:"device-1",Name:"0x1234",Vendor:"0xab"}' "$T/result"
echo Dock > "$TB_SYSFS/0-1/device_name"
tb_capture > "$T/result"; check '.["device-1"].Name=="Dock" and .["device-1"].Vendor=="0xab"' "$T/result"
rm "$TB_SYSFS/0-1/device_name"; echo Vendor > "$TB_SYSFS/0-1/vendor_name"
tb_capture > "$T/result"; check '.["device-1"].Name=="0x1234" and .["device-1"].Vendor=="Vendor"' "$T/result"
mkdir "$TB_SYSFS/0-1/device_name"
reject tb_capture
}
test_first_activation_defers_without_touching_bolt() {
secure_fixture; rm "$TB_STATE"
printf '[config]\nAuthMode=enabled\n' > "$TB_CONFIG"; cp "$TB_CONFIG" "$T/original"
tb_admin owner
[[ -f $TB_PENDING && ! -f $TB_STATE && ! -f $TB_MARKER && ! -s $T/units ]]
cmp "$TB_CONFIG" "$T/original"
rm -rf "$TB_SYSFS/0-1"
tb_admin migrate
[[ -f $TB_PENDING && ! -f $TB_MARKER && ! -s $T/units ]]
tb_admin enable
[[ ! -f $TB_PENDING && -f $TB_MARKER ]]
}
test_saved_secure_binding_allows_initial_capture() {
secure_fixture; rm "$TB_STATE"
edit_inventory '.devices[0] |= (.Stored=true | .Key="have")'
tb_admin owner
[[ ! -f $TB_PENDING && -f $TB_MARKER ]]
}
test_key_without_record_defers_initial_capture() {
secure_fixture; rm "$TB_STATE"
edit_inventory '.devices[0].Key="have"'
tb_admin owner
[[ -f $TB_PENDING && ! -f $TB_MARKER && ! -s $T/units ]]
}
test_offline_prepare_cannot_trust_installers_bolt() {
secure_fixture; rm "$TB_STATE"
edit_inventory '.devices[0] |= (.Stored=true | .Key="have")'
tb_admin prepare
[[ -f $TB_PENDING && ! -f $TB_MARKER && ! -s $T/units ]]
}
test_deferred_opt_out_retains_initial_enrollment() {
secure_fixture; rm "$TB_STATE"
tb_admin owner
tb_admin disable
[[ ! -f $TB_PENDING && ! -f $TB_MARKER && ! -s $T/units ]]
check '.enabled==false and .enrollment_pending==true' "$TB_STATE"
tb_admin migrate
[[ ! -f $TB_PENDING && ! -s $T/units ]]
tb_admin enable
[[ -f $TB_PENDING && ! -f $TB_MARKER && ! -s $T/units ]]
rm -rf "$TB_SYSFS/0-1"
echo '{"new-device":{}}' > "$T/capture"
tb_admin enable
check '.enabled and .enrollment_pending==null and .trusted=={"new-device":{}}' "$TB_STATE"
[[ ! -f $TB_PENDING && -f $TB_MARKER ]]
}
test_pending_notice_retries_without_claiming_protection() {
touch "$TB_PENDING"
local count=0 notices=0
sleep() { count=$((count + 1)); (( count < 3 )) || exit 99; }
tb_setup_notice() { notices=$((notices + 1)); (( notices > 1 )) && echo pending >> "$T/notifications"; }
reject tb_watch
[[ $(cat "$T/notifications") == "pending" ]]
tb_status > "$T/result"; rg -q 'protection is deferred' "$T/result"
}
test_active_policy_is_not_deferred() {
secure_fixture; touch "$TB_MARKER"
tb_admin owner
[[ ! -f $TB_PENDING && -f $TB_MARKER ]]
}
test_pending_only_factory_reset_reaches_both_roots() {
local function_name root="$T/top/@factory"
local TOP_MNT="$T/top" NEXT_NAME=@next PROVISIONING_DIR=/var/lib/omarchy/provisioning LOG_FILE="$T/reset.log" swap_done=0
mkdir -p "$root/etc/omarchy" "$root/etc/systemd/system" "$root/usr/bin" \
"$root/usr/share/omarchy/install/provisioning" "$T/top/@"
touch "$root/etc/omarchy/thunderbolt-authorization.pending" "$root/usr/bin/omarchy-provision-owner"
chmod +x "$root/usr/bin/omarchy-provision-owner"
echo 'root:x:0:0:root:/root:/bin/bash' > "$root/etc/passwd"
cp "$ROOT/install/provisioning/omarchy-provision-owner.service" \
"$ROOT/install/provisioning/omarchy-system-factory-reset-finish.service" "$root/usr/share/omarchy/install/provisioning/"
cp "$ROOT/etc/systemd/system/omarchy-thunderbolt-authorization.service" "$root/etc/systemd/system/"
for function_name in install_provisioning_units scrub_factory_accounts sanitize_factory_baseline stage_full_reset; do
eval "$(sed -n "/^$function_name() {$/,/^}/p" "$ROOT/bin/omarchy-system-factory-reset")"
done
log() { :; }
btrfs() {
if [[ $1 == "subvolume" && $2 == "snapshot" ]]; then cp -a "$3" "$4"
elif [[ $1 == "property" ]]; then :
else return 1; fi
}
usermod() { [[ $1 == "--root" && $2 == "$TOP_MNT/"* && $3 == "--password" && $4 == "!" && $5 == "root" ]]; }
systemd-id128() { echo new-machine-id; }
encrypted_install() { return 1; }
rebuild_next_boot() { :; }
sync() { :; }
systemctl() {
[[ ( $1 == "--root=$TOP_MNT/@next" || $1 == "--root=$TOP_MNT/@factory" ) && $2 == "disable" && $3 == "$TB_SERVICE" ]] || return 1
command systemctl "$@"
}
function /usr/bin/omarchy-thunderbolt-authorization-admin() {
[[ $1 == "reset-root" ]] || return 1
printf '%s\n' "$2" >> "$T/reset-roots"
(
source "$ROOT/install/helpers/thunderbolt-policy.sh"
source "$ROOT/install/helpers/thunderbolt-setup.sh"
tb_reset_root "$2"
)
}
stage_full_reset
[[ $(wc -l < "$T/reset-roots") == 2 && $swap_done == 1 ]]
[[ ! -f $TOP_MNT/@/etc/omarchy/thunderbolt-authorization.pending &&
! -f $TOP_MNT/@factory/etc/omarchy/thunderbolt-authorization.pending ]]
[[ -f $TOP_MNT/@/var/lib/omarchy/provisioning/pending ]]
}
test_config_preserves_settings() {
rm "$TB_STATE"; printf '[config]\nAuthMode=enabled\nDefaultPolicy=manual\n' > "$TB_CONFIG"
tb_prepare; rg -q '^DefaultPolicy=manual$' "$TB_CONFIG"
check '.original_authmode=="enabled"' "$TB_STATE"
}
test_fresh_owner_replaces_trust() {
rm "$TB_STATE"; echo '{"old":{}}' > "$T/capture"; tb_prepare
echo '{"new":{}}' > "$T/capture"; tb_prepare true
check '.trusted=={"new":{}} and .original_authmode=="enabled"' "$TB_STATE"
}
test_enable_rollback() {
rm "$TB_STATE"; printf '[config]\nAuthMode=enabled\nDefaultPolicy=manual\n' > "$TB_CONFIG"
cp "$TB_CONFIG" "$T/original"; echo 'start bolt.service' > "$T/unit-failure"
reject tb_transaction tb_enable
cmp "$TB_CONFIG" "$T/original"; [[ ! -e $TB_STATE && ! -e $TB_MARKER && ! -e $T/setup-recovery.json ]]
[[ $(tail -2 "$T/units") == $'start bolt.service\nstop omarchy-thunderbolt-authorization.service' ]]
}
test_failed_restore_keeps_checkpoint() {
rm "$TB_STATE"; echo '[config]' > "$TB_CONFIG"; cp "$TB_CONFIG" "$T/original"
echo 'stop omarchy-thunderbolt-authorization.service bolt.service' > "$T/unit-failure"; touch "$T/repeat-failure"
reject tb_transaction tb_enable; rg -q 'restoration is incomplete' "$T/error"
[[ -f $T/setup-recovery.json ]]; reject tb_transaction tb_enable; rg -q 'needs recovery' "$T/error"
echo partial > "$TB_CONFIG"; rm "$T/unit-failure"
tb_config_restore; cmp "$TB_CONFIG" "$T/original"
}
test_disable_restores_matching_boot_state() {
boot_fixture; tb_prepare; tb_boot_change true; cp "$TB_STATE" "$T/original"
echo 'disable omarchy-thunderbolt-authorization.service' > "$T/unit-failure"
reject tb_transaction tb_disable
cmp "$TB_STATE" "$T/original"; [[ -f $TB_MARKER && ! -f $T/setup-recovery.json ]]
check '.devices[0].Policy=="manual" and .domains[0].BootACL==["",""]' "$T/inventory"
}
test_disable_hands_off_only_our_records() {
tb_prepare
tb_approve "$(identity)" true >/dev/null
edit_inventory '.devices += [(.devices[0] | .Uid="existing" | .path="/devices/2" | .Policy="manual")]'
tb_approve "$(tb_snapshot | jq -c '.devices[1].identity')" true >/dev/null
tb_admin disable
check '.enabled==false and .enrolled==null and (.trusted|length)==2' "$TB_STATE"
check '.manager.AuthMode=="enabled" and .devices[0].Policy=="auto" and .devices[1].Policy=="manual"' "$T/inventory"
[[ ! -f $TB_MARKER && ! -f $T/setup-recovery.json ]]
# After a completed handoff, an administrator may choose manual themselves.
edit_inventory '.devices[0].Policy="manual"'
tb_admin enable
tb_admin disable
check 'all(.devices[]; .Policy=="manual")' "$T/inventory"
}
test_disable_hands_off_disconnected_device() {
tb_prepare; tb_approve "$(identity)" true >/dev/null
edit_inventory '.devices[0].Status="disconnected"'
tb_admin disable
check '.devices[0].Policy=="auto"' "$T/inventory"
}
test_disable_preserves_recreated_manual_record() {
tb_prepare; tb_approve "$(identity)" true >/dev/null
edit_inventory '.devices[0].StoreTime=200'
tb_admin disable
check '.devices[0].Policy=="manual"' "$T/inventory"
}
test_disable_preserves_original_disabled_mode() {
tb_prepare; tb_approve "$(identity)" true >/dev/null
edit_state '.original_authmode="disabled"'
tb_admin disable
check '.manager.AuthMode=="disabled" and .devices[0].Policy=="auto"' "$T/inventory"
}
test_disable_handoff_after_boot_protection() {
tb_prepare; tb_boot_change true
tb_approve "$(identity)" true >/dev/null
tb_admin disable
check '.enabled==false and .boot_protection==false' "$TB_STATE"
check '.devices[0].Policy=="auto" and (.domains[0].BootACL|index("device-1"))!=null' "$T/inventory"
}
test_disable_handoff_rolls_back_policies_and_acl() {
tb_prepare; tb_approve "$(identity)" true >/dev/null
edit_inventory '.domains[0].BootACL=["unrelated",""]'
cp "$TB_STATE" "$T/original"
echo 'disable omarchy-thunderbolt-authorization.service' > "$T/unit-failure"
reject tb_admin disable
cmp "$TB_STATE" "$T/original"
check '.manager.AuthMode=="disabled" and .devices[0].Policy=="manual" and .domains[0].BootACL==["unrelated",""]' "$T/inventory"
[[ -f $TB_MARKER && ! -f $T/setup-recovery.json ]]
}
test_disable_handoff_failure_keeps_protection() {
tb_prepare; tb_approve "$(identity)" true >/dev/null
tb_unit_state() { echo '{"active":true,"enabled":true}'; }
touch "$T/restore-failure"
reject tb_admin disable
check '.enabled and .enrolled["device-1"]!=null' "$TB_STATE"
check '.manager.AuthMode=="disabled" and .devices[0].Policy=="manual"' "$T/inventory"
[[ -f $TB_MARKER && ! -f $T/setup-recovery.json ]]
! rg -q '^disable ' "$T/units"
[[ $(tail -1 "$T/units") == "start omarchy-thunderbolt-authorization.service" ]]
}
test_disable_handoff_verifies_policy_writes() {
tb_prepare; tb_approve "$(identity)" true >/dev/null
touch "$T/policy-noop"
reject tb_admin disable
rg -q 'did not retain the requested Policy' "$T/error"
check '.enabled and .enrolled["device-1"]!=null' "$TB_STATE"
check '.manager.AuthMode=="disabled" and .devices[0].Policy=="manual"' "$T/inventory"
[[ -f $TB_MARKER && ! -f $T/setup-recovery.json ]]
}
test_disable_handoff_verifies_policy_after_restart() {
tb_prepare; tb_approve "$(identity)" true >/dev/null
touch "$T/restart-policy-failure"
reject tb_admin disable
check '.enabled and .enrolled["device-1"]!=null' "$TB_STATE"
check '.manager.AuthMode=="disabled" and .devices[0].Policy=="manual"' "$T/inventory"
[[ -f $TB_MARKER && ! -f $T/setup-recovery.json ]]
}
test_disable_handoff_incomplete_rollback_is_recoverable() {
tb_prepare; tb_approve "$(identity)" true >/dev/null
echo 'disable omarchy-thunderbolt-authorization.service' > "$T/unit-failure"
touch "$T/boot-failure"
reject tb_admin disable
rg -q 'restoration is incomplete' "$T/error"
[[ -f $T/setup-recovery.json ]]
rm "$T/boot-failure"
tb_admin recover
check '.enabled and .enrolled["device-1"]!=null' "$TB_STATE"
check '.manager.AuthMode=="disabled" and .devices[0].Policy=="manual" and .domains[0].BootACL==["device-1",""]' "$T/inventory"
[[ -f $TB_MARKER && ! -f $T/setup-recovery.json ]]
}
test_disable_offline_controller_is_unchanged() {
tb_prepare; tb_approve "$(identity)" true >/dev/null
edit_inventory '.domains[0].SysfsPath=""'
reject tb_admin disable
rg -q 'Reconnect every Thunderbolt controller' "$T/error"
check '.enabled' "$TB_STATE"
[[ ! -s $T/units && ! -f $T/setup-recovery.json ]]
}
test_boot_policy_reconnection_and_restore() {
boot_fixture; tb_boot_change true
check '.devices[0].Policy=="manual" and .domains[0].BootACL==["",""]' "$T/inventory"
check '.boot_protection and .trusted["device-1"]!=null' "$TB_STATE"
edit_inventory '.devices[0].Policy="auto" | .domains[0].BootACL=["device-1",""]'
tb_snapshot > "$T/result"; check '.warnings|length>0' "$T/result"
tb_reconcile > "$T/result"; check '.error==""' "$T/result"
check '.domains[0].BootACL==["",""]' "$T/inventory"
tb_boot_change false
check '.devices[0].Policy=="auto" and .domains[0].BootACL==["device-1",""]' "$T/inventory"
}
test_boot_change_rollback() {
boot_fixture; edit_inventory '.domains[0].BootACL=["device-1","other"]'
cp "$TB_STATE" "$T/original"; touch "$T/boot-failure"
reject tb_boot_change true
check '.devices[0].Policy=="auto" and .domains[0].BootACL==["device-1","other"]' "$T/inventory"
cmp <(jq -cS . "$TB_STATE") <(jq -cS . "$T/original"); [[ ! -e $T/boot-recovery.json ]]
}
test_boot_recovery_retained() {
boot_fixture; edit_inventory '.domains[0].BootACL=["device-1","other"]'
cp "$TB_STATE" "$T/original"; touch "$T/boot-failure" "$T/restore-failure"
reject tb_boot_change true; rg -q 'recovery is incomplete' "$T/error"; [[ -f $T/boot-recovery.json ]]
rm "$T/boot-failure" "$T/restore-failure"; tb_boot_recover
cmp <(jq -cS . "$TB_STATE") <(jq -cS . "$T/original"); [[ ! -e $T/boot-recovery.json ]]
}
test_unsupported_boot_cannot_succeed() {
boot_fixture; cp "$T/inventory" "$T/original"
local expression
for expression in '.SysfsPath=""' '.SecurityLevel="none"' '.BootACL=[]'; do
cp "$T/original" "$T/inventory"; edit_inventory ".domains[0] |= ($expression)"
reject tb_boot_change true
done
[[ ! -s $T/operations ]]
}
test_install_defers_owner() {
install() { :; }
function /usr/bin/omarchy-thunderbolt-authorization-admin() { echo "helper $*"; }
export OMARCHY_PATH=$ROOT OMARCHY_INSTALL_USER=''
source "$ROOT/install/config/thunderbolt-authorization.sh" > "$T/output"
! rg -q helper "$T/output"; [[ ! -s $T/units ]]
OMARCHY_INSTALL_USER=lab
source "$ROOT/install/config/thunderbolt-authorization.sh" > "$T/output"
rg -q 'helper prepare' "$T/output"; rg -q '^enable omarchy-thunderbolt-authorization.service$' "$T/units"
}
test_offline_reset_scrubs_private_state() {
source "$ROOT/install/helpers/thunderbolt-policy.sh"
local root="$T/clone" unit="$TB_SERVICE"
mkdir -p "$root/etc/systemd/system" "$root${TB_STATE%/*}" "$root${TB_MARKER%/*}"
cp "$ROOT/etc/systemd/system/$unit" "$root/etc/systemd/system/"
command systemctl --root="$root" enable "$unit" >/dev/null 2>&1
touch "$root$TB_MARKER" "$root$TB_STATE" "$root${TB_STATE%/*}/setup-recovery.json"
local kind
for kind in devices keys domains; do mkdir -p "$root/var/lib/boltd/$kind"; touch "$root/var/lib/boltd/$kind/old"; done
tb_reset_root "$root"
[[ ! -e $root$TB_MARKER && ! -e $root${TB_STATE%/*} && ! -e $root/etc/systemd/system/multi-user.target.wants/$unit ]]
[[ $(ls "$root/var/lib/boltd") == "boltd.conf" && $(tb_config_authmode "$root/var/lib/boltd/boltd.conf") == "enabled" ]]
}
test_live_reset_refused() { reject tb_reset_root /; }
test_trusted_reconnect_no_alert() {
edit_state '.trusted["device-1"]={Uid:"device-1",Name:"Dock",Vendor:"Vendor"}'
public_fixture; tb_scan watch1 >/dev/null; [[ ! -s $T/notifications ]]
}
test_unavailable_warns_once() {
tb_scan() { return 1; }
sleep() { count=$((count+1)); ((count<4)) || exit 99; }
local count=0
reject tb_watch
[[ $(cat "$T/notifications") == "attention" ]]
}
test_failed_delivery_retries_other_devices() {
edit_inventory '.devices += [(.devices[0] | .Uid="device-2")]'; public_fixture; touch "$T/notify-failure"
tb_scan watch1 >/dev/null; [[ $(request_count) == 1 ]]
tb_scan watch1 >/dev/null; [[ $(request_count) == 2 && $(wc -l < "$T/notifications") == 3 ]]
}
test_restarts_restore_requests() {
public_fixture; tb_scan watch1 >/dev/null; local old; old=$(request)
tb_scan watch1 >/dev/null; [[ $(wc -l < "$T/notifications") == 1 ]]
edit_inventory '.owner=":1.9"'; public_fixture
tb_scan watch1 >/dev/null; [[ $(request) != "$old" ]]
tb_scan watch2 >/dev/null; [[ $(request_count) == 1 && $(wc -l < "$T/notifications") == 3 ]]
}
test_inventory_failure_preserves_request() {
public_fixture; tb_scan watch1 >/dev/null; local path; path=$(request)
rm "$T/public"; reject tb_scan watch1; [[ -f $path ]]
}
test_replacement_during_dialog_rejected() {
public_fixture; tb_scan watch1 >/dev/null; local path; path=$(request)
gum() {
if [[ $1 == "choose" ]]; then
jq '.devices[0].identity.Uid="replacement"' "$T/public" > "$T/next"; mv "$T/next" "$T/public"
echo 'Always allow this device'
fi
}
reject tb_review "${path##*/}" # A filename is not a valid token.
reject tb_review "$(basename "$path" .json)"
check '.devices[0].status=="connected"' "$T/public"
}
test_lost_reply_keeps_approval_intent() {
public_fixture; tb_scan watch1 >/dev/null; local path; path=$(request)
echo 'Always allow this device' > "$T/choice"; touch "$T/lost-reply"
sleep() { review_tick; }
reject tb_review "$(basename "$path" .json)"
check '.approval=="Always allow this device"' "$path"
tb_scan watch1 >/dev/null; [[ -f $path && $(wc -l < "$T/notifications") == 2 ]]
tb_scan watch2 >/dev/null; [[ ! -f $path && $(wc -l < "$T/notifications") == 3 ]]; path=$(request)
rm "$T/lost-reply"; gum() { fail 'Saved approval must not ask again'; }
tb_review "$(basename "$path" .json)" >/dev/null
[[ ! -f $path ]]
}
test_disconnect_and_untrusted_text() {
edit_inventory '.devices[0].Name="$(touch /tmp/untrusted)\u001b[2J"'
public_fixture; tb_scan watch1 >/dev/null
[[ $(basename "$(request)") =~ ^request-[0-9a-f]{64}.json$ ]]
[[ $(jq '.devices[0].Name' "$T/inventory" | tb_safe_text) != *$'\e'* ]]
edit_inventory '.devices=[]'; public_fixture; tb_scan watch1 >/dev/null; [[ $(request_count) == 0 ]]
}
test_snapshot_freshness_and_disabled_state() {
# Exercise the real public reader, including freshness after daemon failure.
source "$ROOT/install/helpers/thunderbolt-policy.sh"
TB_RUNTIME="$T/runtime"
jq -cn --argjson now "$(date +%s)" '{available:true,time:$now,generation:"generation-a"}' > "$TB_RUNTIME/snapshot.json"
tb_public_snapshot >/dev/null
jq '.time-=30' "$TB_RUNTIME/snapshot.json" > "$T/next"; mv "$T/next" "$TB_RUNTIME/snapshot.json"
reject tb_public_snapshot
echo generation-b > "$TB_RUNTIME/generation"; reject tb_public_snapshot
}
test_invalid_state_not_skipped_by_migration() {
echo '{"enabled":false}' > "$TB_STATE"; reject tb_admin migrate
[[ ! -s $T/units ]]
}
for case in $(declare -F | awk '$3 ~ /^test_/ {print $3}'); do
(fixture; "$case")
pass "Thunderbolt ${case#test_}"
done
@@ -0,0 +1,31 @@
#!/bin/bash
set -euo pipefail
source "$ROOT/test/shell.d/base-test.sh"
fixture=$(mktemp -d)
trap 'rm -rf -- "$fixture"' EXIT
for name in admin approve daemon; do
# Map the security library and stop before service work, including when the
# suite itself runs as root. Startup checks remain the production code.
sed -e "s|source /usr/bin/omarchy-security-functions|source $ROOT/bin/omarchy-security-functions|" \
-e 's|source /usr/share/omarchy/install/helpers/thunderbolt-policy.sh|exit 126|' \
"$ROOT/bin/omarchy-thunderbolt-authorization-$name" > "$fixture/entry"
chmod 755 "$fixture/entry"
printf 'set -p\n' > "$fixture/decoy"
if BASH_ENV="$fixture/decoy" /usr/bin/bash "$fixture/entry" -p; then
fail "$name accepts ordinary Bash with a decoy -p"
fi
printf 'touch "%s"\n' "$fixture/injected" > "$fixture/startup"
# The entrypoint must suppress startup files and exported functions before
# checking EUID or reaching the fixture's service-work boundary.
if BASH_ENV="$fixture/startup" "$fixture/entry" >/dev/null 2>&1; then
fail "$name passed the fixture service-work boundary"
fi
[[ ! -e $fixture/injected ]] || fail "$name executed BASH_ENV"
if /usr/bin/env 'BASH_FUNC_source%%=() { echo injected > "$TB_TEST_INJECTED"; }' \
TB_TEST_INJECTED="$fixture/injected" "$fixture/entry" >/dev/null 2>&1; then
fail "$name passed the fixture boundary with an exported function"
fi
[[ ! -e $fixture/injected ]] || fail "$name imported an exported function"
pass "Thunderbolt $name rejects unsafe Bash startup"
done
@@ -0,0 +1,57 @@
#include <usbguard/Rule.hpp>
#include <fstream>
#include <iostream>
#include <stdexcept>
#include <string>
static std::string read(const char* path) {
std::ifstream file(path);
std::string text;
std::getline(file, text);
if (text.empty()) throw std::runtime_error("empty rule");
return text;
}
static std::string replace(std::string text, const std::string& from, const std::string& to) {
const auto index = text.find(from);
if (index == std::string::npos) throw std::runtime_error("missing attribute");
text.replace(index, from.size(), to);
return text;
}
static void check(const char* name, const usbguard::Rule& policy, const usbguard::Rule& device, bool expected) {
if (policy.appliesTo(device) != expected) throw std::runtime_error(name);
std::cout << "ok - native USBGuard: " << name << '\n';
}
int main(int argc, char** argv) {
if (argc != 3) return 2;
// Test the policy produced by the real Bash implementation, not a C++ copy.
const auto policy = usbguard::Rule::fromString(read(argv[1]));
const auto text = read(argv[2]);
const auto device = usbguard::Rule::fromString(text);
check("original connection", policy, device, true);
auto moved = device;
moved.setViaPort("9-9.9");
check("another port", policy, moved, true);
moved.setParentHash("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=");
check("another hub", policy, moved, true);
auto reloaded = usbguard::Rule::fromString(policy.toString());
check("serialized policy reload on new hub", reloaded, moved, true);
check("reconnect on original port", reloaded, device, true);
auto different = moved;
different.setHash("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=");
check("different hash denied", policy, different, false);
different = moved;
different.setSerial("different");
check("different serial denied", policy, different, false);
different = moved;
different.setName("different");
check("different name denied", policy, different, false);
check("different product denied", policy, usbguard::Rule::fromString(replace(text, "046d:c53a", "046d:c53b")), false);
check("different vendor denied", policy, usbguard::Rule::fromString(replace(text, "046d:c53a", "046e:c53a")), false);
check("different interface denied", policy, usbguard::Rule::fromString(replace(text, "03:00:00", "08:06:50")), false);
check("different connection type denied", policy, usbguard::Rule::fromString(replace(text, "with-connect-type \"unknown\"", "with-connect-type \"hotplug\"")), false);
// Existing unmarked/manual rules remain location-bound.
check("legacy rule still restricts ports and hubs", device, moved, false);
}