* Share the git URL check between theme-install and plugin-add Both commands clone a URL a stranger can choose, and each carried its own copy of the rule that refuses a git option or a `<helper>::<address>` transport helper before cloning. Two copies of a security check drift: the second one arrived four months after the first, and only because someone went looking for it. The rule now lives in omarchy-git-url-check and the callers ask it. Its absence refuses the URL rather than waving it through, since the callers read a non-zero status as a refusal and a missing command exits 127. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Co-Authored-By: Codex XHigh <noreply@openai.com> * Refuse a git URL naming a transport Omarchy does not clone from `<helper>::<address>` is only one of the two ways a URL reaches a remote helper. git also resolves git-remote-<scheme> for `<scheme>://<address>` whenever the scheme is not one it connects itself, so `ext::sh -c id` and `ext://sh -c id` arrive at the same helper while only the first was refused. That shape cannot be refused outright, because it is also how every legitimate URL arrives, so the scheme is checked against the transports git still connects itself. `git+ssh` and `ssh+git` are on that list: they are spelled like a helper and read as plain ssh, and leaving them off would refuse a URL that clones today. `ext` and `fd` are off it deliberately -- git ships a helper for each, and `ext` runs whatever command the URL carries. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Co-Authored-By: Codex XHigh <noreply@openai.com> --------- Co-authored-by: David Heinemeier Hansson <david@hey.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com> Co-authored-by: Codex XHigh <noreply@openai.com>
178 lines
6.4 KiB
Bash
178 lines
6.4 KiB
Bash
#!/bin/bash
|
|
|
|
set -euo pipefail
|
|
|
|
source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh"
|
|
|
|
TMPDIR=$(mktemp -d)
|
|
trap 'rm -rf "$TMPDIR"' EXIT
|
|
|
|
write_plugin() {
|
|
local dir="$1"
|
|
local id="$2"
|
|
local name="$3"
|
|
|
|
mkdir -p "$dir"
|
|
cat >"$dir/manifest.json" <<JSON
|
|
{
|
|
"schemaVersion": 1,
|
|
"id": "$id",
|
|
"name": "$name",
|
|
"version": "1.0.0",
|
|
"kinds": ["bar-widget"],
|
|
"entryPoints": { "barWidget": "Widget.qml" },
|
|
"barWidget": {
|
|
"displayName": "$name",
|
|
"category": "Test",
|
|
"allowMultiple": false
|
|
}
|
|
}
|
|
JSON
|
|
printf 'import QtQuick\nItem {}\n' >"$dir/Widget.qml"
|
|
}
|
|
|
|
stub_dir="$TMPDIR/stubs"
|
|
mkdir -p "$stub_dir"
|
|
cat >"$stub_dir/omarchy-shell" <<'STUB'
|
|
#!/bin/bash
|
|
exit 0
|
|
STUB
|
|
chmod +x "$stub_dir/omarchy-shell"
|
|
|
|
test_home="$TMPDIR/home"
|
|
write_plugin "$test_home/.config/omarchy/plugins/different-folder" "acme.same" "Installed"
|
|
|
|
incoming="$TMPDIR/incoming"
|
|
write_plugin "$incoming" "acme.same" "Incoming"
|
|
git -C "$incoming" init -q
|
|
git -C "$incoming" add .
|
|
git -C "$incoming" -c user.name=Test -c user.email=test@example.com commit -qm "Initial"
|
|
|
|
output=$(HOME="$test_home" OMARCHY_PATH="$ROOT" PATH="$stub_dir:$ROOT/bin:$PATH" \
|
|
omarchy-plugin-add "$incoming" --yes 2>&1) &&
|
|
fail "plugin add accepts an id already installed under another directory" "$output"
|
|
grep -qF "plugin id 'acme.same' is already used by" <<<"$output" ||
|
|
fail "plugin add explains the installed id collision" "$output"
|
|
[[ ! -e $test_home/.config/omarchy/plugins/acme.same ]] ||
|
|
fail "plugin add leaves a target behind after refusing a duplicate id"
|
|
pass "plugin add refuses an installed manifest id regardless of directory name"
|
|
|
|
# --- URL transport-helper guard -------------------------------------------
|
|
#
|
|
# The guard refuses git transport helpers (`<name>::…`) and option-shaped URLs
|
|
# before `git clone` runs, matching omarchy-theme-install. A git stub records
|
|
# whether clone was reached, so the guard is exercised with no network: reaching
|
|
# the stub proves a URL passed the guard; not reaching it proves the guard
|
|
# rejected the URL first.
|
|
|
|
guard_stubs="$TMPDIR/guard-stubs"
|
|
mkdir -p "$guard_stubs"
|
|
cat >"$guard_stubs/omarchy-shell" <<'STUB'
|
|
#!/bin/bash
|
|
exit 0
|
|
STUB
|
|
chmod +x "$guard_stubs/omarchy-shell"
|
|
|
|
clone_marker="$TMPDIR/git-clone-reached"
|
|
cat >"$guard_stubs/git" <<STUB
|
|
#!/bin/bash
|
|
if [[ \$1 == "clone" ]]; then
|
|
touch "$clone_marker"
|
|
exit 1
|
|
fi
|
|
exit 0
|
|
STUB
|
|
chmod +x "$guard_stubs/git"
|
|
|
|
# A gum stub that answers `gum input` with a caller-chosen value, so a test can
|
|
# drive any URL through the interactive prompt path.
|
|
cat >"$guard_stubs/gum" <<'STUB'
|
|
#!/bin/bash
|
|
if [[ $1 == "input" ]]; then
|
|
printf '%s\n' "$GUM_INPUT_VALUE"
|
|
fi
|
|
STUB
|
|
chmod +x "$guard_stubs/gum"
|
|
|
|
add_url() {
|
|
HOME="$test_home" OMARCHY_PATH="$ROOT" PATH="$guard_stubs:$ROOT/bin:$PATH" \
|
|
omarchy-plugin-add "$1" --yes 2>&1
|
|
}
|
|
|
|
# Transport helpers reach the guard, are named as such, and never reach clone.
|
|
for bad in "ext::sh -c touch /tmp/omarchy-guard-test" "fd::17"; do
|
|
rm -f "$clone_marker"
|
|
output=$(add_url "$bad") &&
|
|
fail "plugin add rejects a transport-helper URL: $bad" "$output"
|
|
grep -qF "names a git option or transport helper" <<<"$output" ||
|
|
fail "plugin add names the transport-helper rejection: $bad" "$output"
|
|
[[ ! -e $clone_marker ]] ||
|
|
fail "plugin add reached git clone for a transport-helper URL: $bad"
|
|
done
|
|
pass "plugin add rejects transport-helper URLs before cloning"
|
|
|
|
# The `://` spelling of the same thing: git resolves git-remote-<scheme> for any
|
|
# scheme it does not implement itself, so `ext::` and `ext://` reach the same
|
|
# helper and both have to be refused.
|
|
for bad in "ext://sh -c id" "gcrypt://example.com/x"; do
|
|
rm -f "$clone_marker"
|
|
output=$(add_url "$bad") &&
|
|
fail "plugin add rejects a transport-scheme URL: $bad" "$output"
|
|
grep -qF "which Omarchy does not clone from" <<<"$output" ||
|
|
fail "plugin add names the transport-scheme rejection: $bad" "$output"
|
|
[[ ! -e $clone_marker ]] ||
|
|
fail "plugin add reached git clone for a transport-scheme URL: $bad"
|
|
done
|
|
pass "plugin add rejects transport-scheme URLs before cloning"
|
|
|
|
# Option-shaped URLs on argv are refused before clone — by the option parser
|
|
# (`-*` falls to "unknown add option"), not the guard. The guard's own
|
|
# leading-dash arm is only reachable through the interactive gum prompt and is
|
|
# exercised separately below.
|
|
for bad in "-oProxyCommand=x" "--upload-pack=x"; do
|
|
rm -f "$clone_marker"
|
|
output=$(add_url "$bad") &&
|
|
fail "plugin add rejects an option-shaped URL: $bad" "$output"
|
|
[[ ! -e $clone_marker ]] ||
|
|
fail "plugin add reached git clone for an option-shaped URL: $bad"
|
|
done
|
|
pass "plugin add rejects option-shaped URLs before cloning"
|
|
|
|
# The guard's leading-dash arm is only reachable through `gum input`: argv
|
|
# dashes die in the option parser first. interactive() requires a TTY on stdin
|
|
# and stdout, so run this one case on a pty via util-linux `script -qec` (the
|
|
# suite's existing pty idiom); gum itself is stubbed, so no rendering happens.
|
|
# Probe script's util-linux syntax first and skip cleanly where it is missing.
|
|
if script -qec true /dev/null >/dev/null 2>&1; then
|
|
rm -f "$clone_marker"
|
|
status=0
|
|
raw=$(GUM_INPUT_VALUE="-oProxyCommand=x" HOME="$test_home" OMARCHY_PATH="$ROOT" \
|
|
PATH="$guard_stubs:$ROOT/bin:$PATH" \
|
|
script -qec "omarchy-plugin-add --yes" /dev/null) || status=$?
|
|
output=$(tr -d '\r' <<<"$raw")
|
|
(( status != 0 )) ||
|
|
fail "plugin add rejects an option-shaped URL from the gum prompt" "$output"
|
|
grep -qF "names a git option or transport helper" <<<"$output" ||
|
|
fail "plugin add names the guard rejection for the gum-prompt URL" "$output"
|
|
[[ ! -e $clone_marker ]] ||
|
|
fail "plugin add reached git clone for an option-shaped gum-prompt URL"
|
|
pass "plugin add guard rejects an option-shaped URL from the interactive prompt"
|
|
else
|
|
pass "script -qec unavailable; skipping the interactive gum-prompt guard case"
|
|
fi
|
|
|
|
# Legitimate URL forms pass the guard and reach git clone (stubbed, no network).
|
|
for good in \
|
|
"https://github.com/acme/omarchy-weather.git" \
|
|
"git@github.com:acme/repo.git" \
|
|
"ssh://git@github.com/acme/repo.git" \
|
|
"git@[2001:db8::1]:org/repo.git"; do
|
|
rm -f "$clone_marker"
|
|
output=$(add_url "$good") || true
|
|
! grep -qF "names a git option or transport helper" <<<"$output" ||
|
|
fail "plugin add wrongly rejected a legitimate URL: $good" "$output"
|
|
[[ -e $clone_marker ]] ||
|
|
fail "plugin add did not reach git clone for a legitimate URL: $good" "$output"
|
|
done
|
|
pass "plugin add lets legitimate git URLs reach git clone"
|