fix(deployments): stop interpolating .env values into a sed script

The set_env/env_set helper escaped its value with
`esc=${val//\/\\}; esc=${esc//|/\|}; esc=${esc//&/\&}` and then
interpolated it into `sed -i -e "s|^KEY=.*|KEY=${esc}|"`. The escaping does
not do what it looks like. Tested on bash 5.2:

  set_env K 'a&b'   ->  K=aK=seedb        (sed expanded & to the whole match)
  set_env K 'a|b'   ->  sed: unknown option to `s'   (rc!=0, aborts under set -e)

So any value containing & is silently corrupted and any value containing the
s||| delimiter kills the run. That is reachable: headscale writes
OIDC_CLIENT_SECRET through this, pocket-id writes REDIRECT_URL, copyparty
writes DATA_DIR. A generated secret or a URL query string hits both cases.

The copies in headscale and pocket-id were additionally mangled when they
were introduced -- `${val//\/\}` (pattern `\/`, a literal SLASH) and a raw
newline inside `printf '%s=%s\n'`. The mangled form is a no-op rather than a
corrupter, so the practical failure mode was the same as the original.

Replace all of them with an awk rewrite that passes the key and value through
the ENVIRONMENT, so the value is never parsed as part of a script and needs no
escaping at all. ENVIRON and index() are POSIX, so busybox awk handles them.
Output goes to a temp file and is copied back with `cat >`, which preserves the
original mode and owner -- a .env holding secrets stays 0600. If awk fails,
set -e aborts before .env is touched, which `sed -i` could not promise.

Verified against plain, a&b, a|b, a\b, p@ss&w|rd\x, R&D, a URL with a query
string, s/foo/bar/, a trailing space and the empty string; plus the
append-when-key-absent path, the file-does-not-exist path, non-target lines
left intact, no line-count drift, and mode preservation.

copyparty/update.sh and the rebuilt copyparty payload are included because
update.sh is embedded; regenerated with build.sh.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-16 14:21:07 -05:00
co-authored by Claude Opus 5
parent 0f2273545a
commit 947c89931d
5 changed files with 266 additions and 204 deletions
+197 -181
View File
@@ -210,13 +210,25 @@ chown "$PUID:$PGID" "$DATA_DIR"
ENV_FILE="$STACK_DIR/.env"
set_env() { # <KEY> <value>: update KEY in .env, or append if absent
local key="$1" val="$2" esc
esc=${val//\\/\\\\}; esc=${esc//|/\\|}; esc=${esc//&/\\&}
if grep -qE "^${key}=" "$ENV_FILE"; then
sed -i -e "s|^${key}=.*|${key}=${esc}|" "$ENV_FILE"
else
printf '%s=%s\n' "$key" "$val" >> "$ENV_FILE"
# The value goes through the ENVIRONMENT, never interpolated into a sed
# script. Interpolating it corrupts any value containing & (sed expands it to
# the whole match) and aborts the run on one containing the s||| delimiter --
# which is reachable for an OIDC secret, a password or a URL query string.
local key="$1" val="$2" tmp
if [[ ! -f "$ENV_FILE" ]]; then
printf '%s=%s
' "$key" "$val" >> "$ENV_FILE"
return 0
fi
tmp="$(mktemp)"
_SE_KEY="$key" _SE_VAL="$val" awk '
BEGIN { k = ENVIRON["_SE_KEY"]; v = ENVIRON["_SE_VAL"]; seen = 0 }
!seen && index($0, k "=") == 1 { print k "=" v; seen = 1; next }
{ print }
END { if (!seen) print k "=" v }
' "$ENV_FILE" > "$tmp"
cat "$tmp" > "$ENV_FILE" # rewrite in place: keeps the original mode/owner
rm -f "$tmp"
}
# Pin COPYPARTY_TAG to the newest release so the running version is explicit.
@@ -445,178 +457,182 @@ EOF
# build.sh.
exit 0
__ARCHIVE_BELOW__
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
VKYylalMZSrT/036L64TOhwAoAAA
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
+19 -7
View File
@@ -83,14 +83,26 @@ osfam() {
# ---------------------------------------------------------------------------
ENV_FILE="$STACK_DIR/.env"
env_get() { [[ -f "$ENV_FILE" ]] && grep -E "^$1=" "$ENV_FILE" | head -n1 | cut -d= -f2- || true; }
env_set() { # <KEY> <value>
local key="$1" val="$2" esc
esc=${val//\\/\\\\}; esc=${esc//|/\\|}; esc=${esc//&/\\&}
if grep -qE "^${key}=" "$ENV_FILE"; then
sed -i -e "s|^${key}=.*|${key}=${esc}|" "$ENV_FILE"
else
printf '%s=%s\n' "$key" "$val" >> "$ENV_FILE"
env_set() { # <KEY> <value>: update KEY in .env, or append if absent
# The value goes through the ENVIRONMENT, never interpolated into a sed
# script. Interpolating it corrupts any value containing & (sed expands it to
# the whole match) and aborts the run on one containing the s||| delimiter --
# which is reachable for an OIDC secret, a password or a URL query string.
local key="$1" val="$2" tmp
if [[ ! -f "$ENV_FILE" ]]; then
printf '%s=%s
' "$key" "$val" >> "$ENV_FILE"
return 0
fi
tmp="$(mktemp)"
_SE_KEY="$key" _SE_VAL="$val" awk '
BEGIN { k = ENVIRON["_SE_KEY"]; v = ENVIRON["_SE_VAL"]; seen = 0 }
!seen && index($0, k "=") == 1 { print k "=" v; seen = 1; next }
{ print }
END { if (!seen) print k "=" v }
' "$ENV_FILE" > "$tmp"
cat "$tmp" > "$ENV_FILE" # rewrite in place: keeps the original mode/owner
rm -f "$tmp"
}
# strip a leading v and anything after the X.Y.Z core
+16 -5
View File
@@ -258,14 +258,25 @@ install -m 0640 "$SCRIPT_DIR/Caddyfile" "$STACK_DIR/Caddyfile"
# .env
set_env() { # <KEY> <value>: update KEY in .env, or append if absent
local key="$1" val="$2" esc
esc=${val//\/\\}; esc=${esc//|/\|}; esc=${esc//&/\&}
if grep -qE "^${key}=" "$ENV_FILE"; then
sed -i -e "s|^${key}=.*|${key}=${esc}|" "$ENV_FILE"
else
# The value goes through the ENVIRONMENT, never interpolated into a sed
# script. Interpolating it corrupts any value containing & (sed expands it to
# the whole match) and aborts the run on one containing the s||| delimiter --
# which is reachable for an OIDC secret, a password or a URL query string.
local key="$1" val="$2" tmp
if [[ ! -f "$ENV_FILE" ]]; then
printf '%s=%s
' "$key" "$val" >> "$ENV_FILE"
return 0
fi
tmp="$(mktemp)"
_SE_KEY="$key" _SE_VAL="$val" awk '
BEGIN { k = ENVIRON["_SE_KEY"]; v = ENVIRON["_SE_VAL"]; seen = 0 }
!seen && index($0, k "=") == 1 { print k "=" v; seen = 1; next }
{ print }
END { if (!seen) print k "=" v }
' "$ENV_FILE" > "$tmp"
cat "$tmp" > "$ENV_FILE" # rewrite in place: keeps the original mode/owner
rm -f "$tmp"
}
if [[ ! -f "$ENV_FILE" ]]; then
+16 -5
View File
@@ -181,14 +181,25 @@ install -m 0640 "$SCRIPT_DIR/docker-compose.yml" "$STACK_DIR/docker-compose.yml"
ENV_FILE="$STACK_DIR/.env"
set_env() { # <KEY> <value>: update KEY in .env, or append if absent
local key="$1" val="$2" esc
esc=${val//\/\\}; esc=${esc//|/\|}; esc=${esc//&/\&}
if grep -qE "^${key}=" "$ENV_FILE"; then
sed -i -e "s|^${key}=.*|${key}=${esc}|" "$ENV_FILE"
else
# The value goes through the ENVIRONMENT, never interpolated into a sed
# script. Interpolating it corrupts any value containing & (sed expands it to
# the whole match) and aborts the run on one containing the s||| delimiter --
# which is reachable for an OIDC secret, a password or a URL query string.
local key="$1" val="$2" tmp
if [[ ! -f "$ENV_FILE" ]]; then
printf '%s=%s
' "$key" "$val" >> "$ENV_FILE"
return 0
fi
tmp="$(mktemp)"
_SE_KEY="$key" _SE_VAL="$val" awk '
BEGIN { k = ENVIRON["_SE_KEY"]; v = ENVIRON["_SE_VAL"]; seen = 0 }
!seen && index($0, k "=") == 1 { print k "=" v; seen = 1; next }
{ print }
END { if (!seen) print k "=" v }
' "$ENV_FILE" > "$tmp"
cat "$tmp" > "$ENV_FILE" # rewrite in place: keeps the original mode/owner
rm -f "$tmp"
}
if [[ ! -f "$ENV_FILE" ]]; then
+18 -6
View File
@@ -192,13 +192,25 @@ install -m 0644 "$SCRIPT_DIR/cache-domains.regex" "$STACK_DIR/cache-domains.rege
ENV_FILE="$STACK_DIR/.env"
set_env() { # <KEY> <value>: update KEY in .env, or append if absent
local key="$1" val="$2" esc
esc=${val//\\/\\\\}; esc=${esc//|/\\|}; esc=${esc//&/\\&}
if grep -qE "^${key}=" "$ENV_FILE"; then
sed -i -e "s|^${key}=.*|${key}=${esc}|" "$ENV_FILE"
else
printf '%s=%s\n' "$key" "$val" >> "$ENV_FILE"
# The value goes through the ENVIRONMENT, never interpolated into a sed
# script. Interpolating it corrupts any value containing & (sed expands it to
# the whole match) and aborts the run on one containing the s||| delimiter --
# which is reachable for an OIDC secret, a password or a URL query string.
local key="$1" val="$2" tmp
if [[ ! -f "$ENV_FILE" ]]; then
printf '%s=%s
' "$key" "$val" >> "$ENV_FILE"
return 0
fi
tmp="$(mktemp)"
_SE_KEY="$key" _SE_VAL="$val" awk '
BEGIN { k = ENVIRON["_SE_KEY"]; v = ENVIRON["_SE_VAL"]; seen = 0 }
!seen && index($0, k "=") == 1 { print k "=" v; seen = 1; next }
{ print }
END { if (!seen) print k "=" v }
' "$ENV_FILE" > "$tmp"
cat "$tmp" > "$ENV_FILE" # rewrite in place: keeps the original mode/owner
rm -f "$tmp"
}
if [[ ! -f "$ENV_FILE" ]]; then