#!/bin/bash # omarchy:hidden=true # omarchy:summary=Review a blocked USB device authorization request set -euo pipefail token="${1:-}" if [[ ! $token =~ ^request-[0-9a-f]{64}$ ]]; then echo "This USB authorization request is invalid or expired." >&2 exit 1 fi request="$HOME/.local/state/omarchy/usb-authorization/requests/$token.json" # Serialize request updates with watcher events, but release the lock while the # user chooses. A changed request must never inherit an old dialog's approval. if [[ ! -d ${request%/*} ]]; then echo "This USB authorization request is no longer available." >&2 exit 1 fi exec 9>"${request%/*}/.lock" flock 9 if [[ ! -f $request || -L $request ]]; then echo "This USB authorization request is no longer available." >&2 exit 1 fi snapshot=$(cat "$request") id=$(jq -er '.id | select(test("^[0-9]+$"))' <<<"$snapshot") expected_rule=$(jq -er '.rule | select(type == "string" and startswith("block "))' <<<"$snapshot") choice=$(jq -er '.approval // "" | select(. == "" or . == "Allow once" or . == "Always allow this device")' <<<"$snapshot") allowed_rule="allow ${expected_rule#block }" approval_failed() { echo "Could not confirm USB approval. The request has been kept; try again." >&2 exit 1 } read_current_rule() { local devices line current_rule="" devices=$(usbguard list-devices) || approval_failed while IFS= read -r line; do if [[ $line == "$id: "* ]]; then current_rule="${line#"$id: "}" return fi done <<<"$devices" } validate_device() { read_current_rule if [[ $current_rule == "$expected_rule" ]] || [[ -n $choice && $current_rule == "$allowed_rule" ]]; then return fi rm -f "$request" echo "That USB device was removed or changed. Connect it again to review it." >&2 exit 1 } validate_device if [[ -z $choice ]]; then flock -u 9 gum style --foreground 212 --bold "Blocked USB accessory" echo echo "The following details come from the device and can be forged:" printf '%s\n' "$current_rule" | LC_ALL=C tr -c '\11\12\15\40-\176' '?' echo choice=$(gum choose \ "Allow once" \ "Always allow this device" \ "Keep blocked") || exit 130 flock 9 if [[ ! -f $request || -L $request || $(cat "$request") != "$snapshot" ]]; then echo "This USB authorization request changed. Open the latest notification to review it." >&2 exit 1 fi # A fresh choice can authorize only the blocked identity shown in the dialog. read_current_rule if [[ $current_rule != "$expected_rule" ]]; then rm -f "$request" echo "That USB device was removed or changed. Connect it again to review it." >&2 exit 1 fi fi case "$choice" in "Allow once" | "Always allow this device") # Save intent before applying policy: a later query can fail after the device # was authorized. Retry must verify that approval, including persistent trust. temporary=$(mktemp "${request%/*}/.request.XXXXXXXXXX") trap 'rm -f "$temporary"' EXIT jq --arg approval "$choice" '.approval = $approval' "$request" >"$temporary" mv -f "$temporary" "$request" if [[ $current_rule == "$expected_rule" ]]; then # Match the complete identity, since numeric IDs can be reassigned. options=() if [[ $choice == "Always allow this device" ]]; then options+=(--permanent) fi usbguard allow-device "${options[@]}" "$expected_rule" || approval_failed fi # Rule-based USBGuard commands swallow applyDevicePolicy exceptions and can # return success without authorizing anything. Verify the exact device. 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." ;; *) exit 1 ;; esac rm -f "$request"