Backport of the shared URL check (PR #8174, merged to quattro as 68ab12f7) onto
the v4-0-1 release branch, on top of the #8067 backport it follows.
omarchy-theme-install and omarchy-plugin-add both clone a URL a stranger can
choose, and each carried its own copy of the rule that refuses a git option or a
transport helper before cloning. The rule now lives in omarchy-git-url-check and
both callers ask it: two copies of a security check drift, and the second copy
arrived four months after the first only because someone went looking for it.
That rule was also enforcing half of what it described. <helper>::<address> is
one of two shapes git resolves a remote helper from -- it also runs
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 reach the same helper while
only the first was refused. Nothing exploitable follows on a stock system:
protocol.ext.allow defaults to never, and the :// spelling hands git-remote-ext
a command name it cannot exec. But a third-party helper installed on PATH is
reachable through the scheme form alone, and a guard is worth more when it
enforces the rule it states.
The :// shape cannot be refused the way :: is, because it is also how every
legitimate URL arrives, so the scheme is checked against the transports git
still connects itself: ssh git git+ssh ssh+git http https ftp ftps file.
git+ssh and ssh+git are on that list because they are spelled like a helper and
read as plain ssh; leaving them off would refuse a URL that clones today. ext
and fd are off it deliberately. A single colon is always scp-style ssh and a
bare path is always a path, so neither needs constraining.
The check fails closed: the callers read a non-zero status as a refusal, so a
missing omarchy-git-url-check refuses the URL rather than waving it through.
Clean cherry-pick on top of the #8067 backport: every file is byte-identical to
quattro, so merging v4-0-1 into quattro resolves without a conflict. test/shell
passes: 189 files, including the one this adds. test/cli passes, so the new
command's metadata is well-formed. Exercised the check here: https, scp-style,
ssh, git+ssh and scp-style IPv6 all pass, while ext:: ext:// fd:: fd:// and a
leading-dash form are refused, as is an uppercase EXT:// spelling.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018DEMYa9UWtroz93DhMTtcV
51 lines
1.7 KiB
Bash
Executable File
51 lines
1.7 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# omarchy:summary=Check that a git URL names a repository, not a transport helper
|
|
# omarchy:args=<git-url>
|
|
# omarchy:hidden=true
|
|
|
|
set -euo pipefail
|
|
|
|
# git picks a remote helper -- an executable it runs at clone time -- out of a URL
|
|
# in exactly two shapes, and no others: `<helper>::<address>`, and
|
|
# `<scheme>://<address>` for any scheme git does not handle itself. A single
|
|
# colon is always scp-style ssh, and a bare path is always a path; neither can
|
|
# reach a helper. So constraining those two shapes covers the whole surface.
|
|
#
|
|
# The `::` shape is refused outright, because no helper reachable that way is one
|
|
# a theme or plugin URL has business naming, and `ext::` runs a shell command.
|
|
# The `://` shape cannot be refused the same way, since it is also how every
|
|
# legitimate URL arrives -- so it is allowlisted instead. The list is the
|
|
# transports git still connects itself, `git+ssh` and `ssh+git` included: those
|
|
# two are spelled like a helper but are read as plain ssh. `ext` and `fd` are
|
|
# left out deliberately -- git ships a helper for each, and `ext` runs whatever
|
|
# command the URL carries.
|
|
TRANSPORTS=(ssh git git+ssh ssh+git http https ftp ftps file)
|
|
|
|
fail() {
|
|
echo "omarchy-git-url-check: $*" >&2
|
|
exit 1
|
|
}
|
|
|
|
url="${1-}"
|
|
|
|
if [[ -z $url ]]; then
|
|
fail "a git URL is required"
|
|
fi
|
|
|
|
if [[ $url == -* || $url =~ ^[A-Za-z0-9][A-Za-z0-9+.-]*:: ]]; then
|
|
fail "'$url' names a git option or transport helper, not a repository."
|
|
fi
|
|
|
|
if [[ $url =~ ^([A-Za-z0-9][A-Za-z0-9+.-]*):// ]]; then
|
|
scheme="${BASH_REMATCH[1]}"
|
|
|
|
for transport in "${TRANSPORTS[@]}"; do
|
|
if [[ $scheme == "$transport" ]]; then
|
|
exit 0
|
|
fi
|
|
done
|
|
|
|
fail "'$url' names the '$scheme' transport, which Omarchy does not clone from."
|
|
fi
|