#!/usr/bin/env bash # The Sharing page must report what is true, and never claim a security # property it cannot back up. # # The two failures worth a test: # # A service that is not installed shown as a switch. That is what the panel # this replaces does, and the switch does nothing. # # "Keys only" claimed for SSH when the configuration is silent. OpenSSH's # default accepts passwords, so stating the stronger thing without evidence # would tell someone their machine is safer than it is. # # Read-only: this reads service state and never enables or disables anything. set -uo pipefail repo_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" helper="$repo_dir/config/dot/quickshell/scripts/panama-sharing" service="$repo_dir/config/dot/quickshell/services/Sharing.qml" page="$repo_dir/config/dot/quickshell/modules/settings/SharingPage.qml" fail() { printf 'sharing contract: %s\n' "$1" >&2 exit 1 } for path in "$helper" "$service" "$page"; do [[ -r "$path" ]] || fail "missing $path" done [[ -x "$helper" ]] || fail 'panama-sharing is not executable' # ── The password-authentication claim is evidence-based ───────────────────── summary="$(sed -n '/function passwordLoginSummary/,/^ }/p' "$service")" [[ -n "$summary" ]] || fail 'the service does not summarize password sign-in' grep -q 'stated === ""' <<<"$summary" \ || fail 'the summary does not distinguish "configured" from "silent"' grep -qE 'system default' <<<"$summary" \ || fail 'a silent configuration is not reported as the system default' # "Keys only" may only be said when the file actually says no. keys_line="$(grep -n 'keys only' <<<"$summary" | head -1)" [[ -n "$keys_line" ]] || fail 'the summary never reports keys-only' grep -q 'toLowerCase() === "no"' <<<"$summary" \ || fail 'keys-only is claimed without checking what the configuration says' # ── Absent software is reported, not offered ──────────────────────────────── grep -q 'is not installed' "$page" \ || fail 'the page does not say when the software for a row is missing' # A switch for a service that is not installed must be disabled. grep -q 'Sharing.remoteLogin?.installed === true' "$page" \ || fail 'the remote login switch is enabled regardless of whether SSH is installed' grep -q 'Sharing.remoteDesktop?.available === true' "$page" \ || fail 'the remote desktop switch is enabled regardless of whether it is available' # Turning on remote desktop without credentials would start a service nobody # can connect to; the page must require them first. grep -q 'hasCredentials === true' "$page" \ || fail 'remote desktop can be enabled with no credentials set' grep -q 'hasCredentials' "$helper" \ || fail 'the helper does not know whether credentials exist' grep -qiE 'grdctl.*(password|username)[^)]*\)' "$helper" \ && fail 'the helper passes remote desktop credentials on a command line' # ── Privilege boundaries ──────────────────────────────────────────────────── # Remote login is system-wide and must go through a prompt; remote desktop is a # user service and must not ask for one. login_body="$(sed -n '/^def set_remote_login/,/^def /p' "$helper")" grep -q 'pkexec' <<<"$login_body" \ || fail 'changing a system-wide service does not ask for authorization' desktop_body="$(sed -n '/^def set_remote_desktop/,/^def /p' "$helper")" grep -q 'pkexec' <<<"$desktop_body" \ && fail 'a user service asks for administrator rights it does not need' grep -q '"--user"' <<<"$desktop_body" \ || fail 'remote desktop is not managed as a user service' # ── The remote desktop password never passes through Panama ──────────────── # grdctl takes it on a terminal and core-dumps without one, so the only two # options were a terminal hand-off or an argument -- and an argument publishes # it through /proc to every process on this machine. grep -q 'set-credentials' "$service" \ || fail 'the service cannot set remote desktop credentials at all' grep -qE 'set-credentials".*(password|secret)' "$service" \ && fail 'the service puts a password on the command line' grep -q 'set-credentials' "$helper" \ && fail 'the helper handles credentials; that path cannot prompt and must stay in a terminal' grep -q 'kitty' "$service" \ || fail 'credentials are not handed to a terminal, so nothing can prompt for them' grep -q 'clear-rdp-credentials' "$helper" \ || fail 'stored credentials cannot be cleared' # ── The snapshot reflects the machine ─────────────────────────────────────── command -v jq >/dev/null 2>&1 || { printf 'sharing contract: SKIP (no jq)\n'; exit 0; } snapshot="$("$helper" snapshot 2>/dev/null)" || fail 'snapshot failed' jq -e '.hostname | length > 0' <<<"$snapshot" >/dev/null || fail 'no hostname reported' jq -e '.remoteLogin | has("installed") and has("active") and has("enabled")' <<<"$snapshot" >/dev/null \ || fail 'remote login state is incomplete' jq -e '.remoteDesktop | has("available") and has("hasCredentials")' <<<"$snapshot" >/dev/null \ || fail 'remote desktop state is incomplete' # Installed-ness must match what is actually on this machine, not a guess. expected_samba=$(command -v smbd >/dev/null 2>&1 && echo true || echo false) actual_samba="$(jq -r '.fileSharing.installed' <<<"$snapshot")" [[ "$expected_samba" == "$actual_samba" ]] \ || fail "file sharing reports installed=$actual_samba but smbd presence is $expected_samba" # No credential may appear in the snapshot. offenders="$(jq -r '[paths | map(tostring) | join(".")] | map(select(test("(password|secret|credential)$";"i"))) | join(", ")' <<<"$snapshot")" [[ -z "$offenders" ]] || fail "the snapshot carries credential-shaped fields: $offenders" printf 'sharing contract: PASS (remote login %s, remote desktop %s)\n' \ "$(jq -r 'if .remoteLogin.active then "on" else "off" end' <<<"$snapshot")" \ "$(jq -r 'if .remoteDesktop.active then "on" else "off" end' <<<"$snapshot")"