Validate the web app URL before anything fetches it

The scheme check ran after the interactive branch, which had already handed the URL to curl: a refused `file://` or `ftp://` URL was dereferenced first, the whole string went out to Google's favicon endpoint in a `domain=` query, and a fetch that happened to succeed left an orphan icon and a rebuilt icon cache behind. Validating immediately after normalization puts the refusal ahead of every dereference in both branches.

A leading space also kept the URL out of the scheme test entirely. `normalize_webapp_url` saw no scheme, prefixed it, and ` file:///etc/passwd` became `https:// file:///etc/passwd`, which passes `^https?://`. The desktop `Exec` field is unquoted, so it splits back into two arguments, and `omarchy-launch-webapp` forwards everything after the first to the browser, where a bare argument is another URL to open. Refusing whitespace closes that without touching desktop-entry escaping.

Scheme comparison is case-insensitive because schemes are: `HTTPS://example.com` installed before this check existed and has no reason to stop.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Codex XHigh <noreply@openai.com>
This commit is contained in:
David Heinemeier Hansson
2026-08-27 20:10:15 +02:00
co-authored by Claude Opus 5 Codex XHigh
parent 877f1e96ef
commit 9382410026
2 changed files with 74 additions and 6 deletions
+14 -6
View File
@@ -54,13 +54,21 @@ normalize_webapp_url() {
printf '%s' "$url"
}
# A space in the URL becomes a second desktop Exec argument, which
# omarchy-launch-webapp hands to the browser as another URL to open. Schemes
# are case-insensitive.
require_http_url() {
local url=$1
if [[ $url =~ ^https?:// ]]; then
return 0
if [[ $url =~ [[:space:]] ]]; then
echo "Error: web app URL must not contain whitespace." >&2
exit 1
fi
if [[ ! ${url,,} =~ ^https?:// ]]; then
echo "Error: web app URL must be http or https." >&2
exit 1
fi
echo "Error: web app URL must be http or https." >&2
exit 1
}
fetch_site_icon() {
@@ -91,6 +99,7 @@ if (( $# < 3 )); then
APP_NAME=$(gum input --prompt "Name> " --placeholder "My favorite web app")
APP_URL=$(gum input --prompt "URL> " --placeholder "https://example.com")
APP_URL=$(normalize_webapp_url "$APP_URL")
require_http_url "$APP_URL"
# Try to fetch the site's icon automatically first.
mkdir -p "$ICON_DIR"
@@ -108,6 +117,7 @@ if (( $# < 3 )); then
else
APP_NAME="$1"
APP_URL=$(normalize_webapp_url "$2")
require_http_url "$APP_URL"
ICON_REF="$3"
CUSTOM_EXEC="$4" # Optional custom exec command
MIME_TYPES="$5" # Optional mime types
@@ -120,8 +130,6 @@ if [[ -z $APP_NAME || -z $APP_URL ]]; then
exit 1
fi
require_http_url "$APP_URL"
if [[ -z $ICON_REF ]]; then
ICON_VALUE=$(safe_icon_name "$APP_NAME")
mkdir -p "$ICON_DIR"