Verify USB approval before reporting success

This commit is contained in:
acrogenesis committed 2026-09-16 16:06:13 -06:00
1 parent 67d173c9f9
commit 58a9c95bf6
2 files changed
+95 -8

No files matched your search

+40 -7
View File
@@ -60,16 +60,49 @@ if [[ $choice != "Keep blocked" ]]; then
fi
fi
approval_failed() {
echo "Could not confirm USB approval. The request has been kept; try again." >&2
exit 1
}
case "$choice" in
"Allow once")
"Allow once" | "Always allow this device")
# Target the complete identity rule instead of USBGuard's numeric ID. IDs can
# be reassigned when the daemon restarts while the review dialog is open.
usbguard allow-device "$expected_rule"
echo "USB accessory allowed until it is disconnected."
;;
"Always allow this device")
usbguard allow-device --permanent "$expected_rule"
echo "USB accessory added to the trusted policy."
options=()
if [[ $choice == "Always allow this device" ]]; then
options+=(--permanent)
fi
usbguard allow-device "${options[@]}" "$expected_rule" || approval_failed
# Rule-based USBGuard commands swallow applyDevicePolicy exceptions and can
# return success without authorizing anything. Verify the exact device.
allowed_rule="allow ${expected_rule#block }"
allowed=$(usbguard list-devices --allowed) || approval_failed
grep -Fqx -- "$id: $allowed_rule" <<<"$allowed" || approval_failed
if [[ $choice == "Always allow this device" ]]; then
# Let USBGuard match policy rules: saved rules can omit the device's port.
# --show-devices indents the devices matching each policy rule.
policy=$(usbguard list-rules --show-devices) || approval_failed
policy_allows=0
matched=0
while IFS= read -r line; do
if [[ $line =~ ^[0-9]+:[[:space:]] ]]; then
policy_allows=0
if [[ $line =~ ^[0-9]+:[[:space:]]allow[[:space:]] ]]; then
policy_allows=1
fi
elif (( policy_allows )) && [[ $line == $'\t'"$id: $allowed_rule" ]]; then
matched=1
break
fi
done <<<"$policy"
(( matched )) || approval_failed
echo "USB accessory added to the trusted policy."
else
echo "USB accessory allowed until it is disconnected."
fi
;;
"Keep blocked")
echo "USB accessory remains blocked."
+55 -1
View File
@@ -66,7 +66,13 @@ generate-policy)
fi
;;
list-devices)
if [[ ${2:-} == "--blocked" ]]; then
if [[ ${2:-} == "--allowed" ]]; then
[[ ${APPROVAL_QUERY_FAIL:-0} == 0 ]] || exit 1
if [[ -f $TEST_ALLOWED_RULE ]]; then
printf '17: %s\n' "$(<"$TEST_ALLOWED_RULE")"
fi
elif [[ ${2:-} == "--blocked" ]]; then
[[ ! -f $TEST_ALLOWED_RULE ]] || exit 0
if [[ -n ${BLOCKED_DEVICES_FILE:-} ]]; then cat "$BLOCKED_DEVICES_FILE"; exit 0; fi
if [[ ${BLOCKED_DEVICE_PRESENT:-1} == 1 ]]; then
rule="${BLOCKED_DEVICE_RULE:?}"
@@ -80,10 +86,27 @@ list-devices)
echo '5: allow id 0627:0001 name "QEMU USB Tablet" hash "tablet"'
fi
;;
list-rules)
[[ ${APPROVAL_POLICY_QUERY_FAIL:-0} == 0 ]] || exit 1
if [[ -f $TEST_SAVED_RULE ]]; then
printf '40: %s\n' "$(<"$TEST_SAVED_RULE")"
printf '\t17: %s\n' "$(<"$TEST_ALLOWED_RULE")"
fi
;;
*)
printf 'usbguard' >>"$CALLS"
printf ' <%s>' "$@" >>"$CALLS"
printf '\n' >>"$CALLS"
if [[ $1 == "allow-device" && ${!#} == block* ]]; then
[[ ${APPROVAL_EXIT_FAIL:-0} == 0 ]] || exit 1
if [[ ${APPROVAL_SILENT_FAIL:-0} == 0 ]]; then
rule="${!#}"
printf '%s\n' "${APPROVAL_REPLACEMENT_RULE:-allow ${rule#block }}" >"$TEST_ALLOWED_RULE"
if [[ $2 == "--permanent" && ${APPROVAL_NO_POLICY:-0} == 0 ]]; then
cp "$TEST_ALLOWED_RULE" "$TEST_SAVED_RULE"
fi
fi
fi
;;
esac
STUB
@@ -148,6 +171,8 @@ export CALLS="$calls"
export PATH="$stub_bin:$ROOT/bin:$PATH"
export TEST_DAEMON_CONFIG="$daemon_config"
export TEST_RULES="$rules"
export TEST_ALLOWED_RULE="$scratch/allowed-rule"
export TEST_SAVED_RULE="$scratch/saved-rule"
if "$ROOT/bin/omarchy-usb-authorization-restore-default" >/dev/null 2>&1; then
fail "the fixed-path USB authorization restore helper requires root"
@@ -486,6 +511,8 @@ grep -Fqx "usbguard <allow-device> <$malicious_rule>" "$calls" || fail "review c
[[ ! -e $request ]] || fail "a completed review consumes its request"
pass "USB review allows a still-matching device once"
rm -f "$TEST_ALLOWED_RULE" "$TEST_SAVED_RULE"
# A daemon restart discards the temporary approval. The IPC reconnect must
# recover the blocked device even when its startup policy events were missed.
notification_count=$(grep -c '^notification' "$calls")
@@ -515,6 +542,7 @@ token=$(basename "${request%.json}")
GUM_CHOICE='Always allow this device' "$ROOT/bin/omarchy-usb-authorization-review" "$token" >"$scratch/review-always-output"
grep -Fqx "usbguard <allow-device> <--permanent> <$malicious_rule>" "$calls" || fail "review can persist an exact-device rule"
pass "USB review supports explicit persistent trust"
rm -f "$TEST_ALLOWED_RULE" "$TEST_SAVED_RULE"
USBGUARD_IPC_SIGNAL=Device.PolicyApplied \
USBGUARD_DEVICE_ID=17 \
@@ -556,6 +584,32 @@ fi
[[ ! -e $request ]] || fail "a stale review request is discarded"
pass "USB review binds approval to the device snapshot"
for choice in 'Allow once' 'Always allow this device'; do
for failure in APPROVAL_SILENT_FAIL APPROVAL_EXIT_FAIL APPROVAL_QUERY_FAIL APPROVAL_REPLACEMENT_RULE; do
rm -f "$TEST_ALLOWED_RULE" "$TEST_SAVED_RULE"
USBGUARD_IPC_SIGNAL=IPC.Connected "$ROOT/bin/omarchy-usb-authorization-event"
value=1
[[ $failure != "APPROVAL_REPLACEMENT_RULE" ]] || value='allow id 9999:9999 hash "replacement"'
if env "$failure=$value" GUM_CHOICE="$choice" "$ROOT/bin/omarchy-usb-authorization-review" "$token" >"$scratch/review-failed" 2>&1; then
fail "$choice must reject $failure"
fi
[[ -f $request ]] || fail "unverified approval must retain the request"
! grep -Eq 'USB accessory (allowed until|added to)' "$scratch/review-failed" || fail "failed approval must not report success"
done
done
for failure in APPROVAL_NO_POLICY APPROVAL_POLICY_QUERY_FAIL; do
rm -f "$TEST_ALLOWED_RULE" "$TEST_SAVED_RULE"
if env "$failure=1" GUM_CHOICE='Always allow this device' "$ROOT/bin/omarchy-usb-authorization-review" "$token" >"$scratch/review-no-policy" 2>&1; then
fail "permanent approval must reject $failure"
fi
[[ -f $request ]] || fail "unverified permanent approval must retain the request"
done
rm -f "$TEST_ALLOWED_RULE" "$TEST_SAVED_RULE"
GUM_CHOICE='Always allow this device' "$ROOT/bin/omarchy-usb-authorization-review" "$token" >"$scratch/review-retry"
[[ ! -e $request ]] || fail "a verified retry consumes the retained request"
rm -f "$TEST_ALLOWED_RULE" "$TEST_SAVED_RULE"
pass "USB approvals verify device and permanent policy before consuming requests"
notification_count=$(grep -c '^notification' "$calls")
printf '17: %s\n18: %s\n' "$malicious_rule" 'block id 1234:5678 name "Second blocked device" hash "second"' >"$scratch/two-blocked"
NOTIFICATION_READY_FILE="$scratch/notification-ready" \