From b176d87d4c67f8ce6777ca7e1eae9105a3aa3032 Mon Sep 17 00:00:00 2001 From: Flegma Date: Fri, 10 Jul 2026 22:04:52 +0200 Subject: [PATCH 1/2] fix: give each RAND32 secret a distinct value, write env vars literally, pin kustomize - replace_rand32_in_env_files: generate a fresh random value per $(RAND32) occurrence instead of one per file. The global sed made paired secrets identical (APP_KEY == ENC_SECRET, S3_ACCESS_KEY == S3_SECRET), so learning the semi-public key leaked its partner. Rewritten line by line with bash replace-first so values are distinct and never interpreted as sed patterns (#547). - update_env_var: stop feeding the value through sed. A secret containing sed metacharacters ('|', '&', trailing '\') corrupted or silently dropped the value. Now removes any existing line and appends with printf, writing the value verbatim (#547). - setup_kustomize: pin the installer to an immutable release tag instead of the moving master branch, and download-then-run instead of piping the network straight into bash (#556). --- utils/replace_rand32_in_env_files.sh | 26 ++++++++++++++++++-------- utils/setup_kustomize.sh | 20 ++++++++++++++++++-- utils/update_env_var.sh | 19 +++++++++---------- 3 files changed, 45 insertions(+), 20 deletions(-) diff --git a/utils/replace_rand32_in_env_files.sh b/utils/replace_rand32_in_env_files.sh index f4673a8..58f4dad 100755 --- a/utils/replace_rand32_in_env_files.sh +++ b/utils/replace_rand32_in_env_files.sh @@ -8,16 +8,26 @@ replace_rand32_in_env_files() { return fi - # Replace $(RAND32) with a random base64 encoded string in all non-example env files + # Replace each $(RAND32) with its OWN fresh random value. The previous global + # (g) sed used one value per file, so paired secrets came out identical + # (e.g. APP_KEY == ENC_SECRET, S3_ACCESS_KEY == S3_SECRET) and learning the + # semi-public one leaked its partner. Rewrite line by line using bash's + # replace-first substitution (not sed) so each occurrence gets a distinct + # value and no secret is ever interpreted as a sed pattern. for env_file in "$secrets_dir"/*.env; do if [[ -f "$env_file" && ! "$env_file" == *.example ]]; then - # Generate a random base64 encoded string - random_string=$(openssl rand -base64 32 | tr '/' '_' | tr '=' '_') - if [[ "$OSTYPE" == "darwin"* ]]; then - sed -i '' "s/\$(RAND32)/$random_string/g" "$env_file" - else - sed -i "s/\$(RAND32)/$random_string/g" "$env_file" - fi + local tmp_file + tmp_file=$(mktemp) + while IFS= read -r line || [[ -n "$line" ]]; do + while [[ "$line" == *'$(RAND32)'* ]]; do + # base64 alphabet minus '/' and padding '='; the remaining + # chars ([A-Za-z0-9+_]) are all literal in the replacement. + random_string=$(openssl rand -base64 32 | tr '/' '_' | tr '=' '_') + line=${line/'$(RAND32)'/$random_string} + done + printf '%s\n' "$line" >> "$tmp_file" + done < "$env_file" + mv "$tmp_file" "$env_file" fi done } diff --git a/utils/setup_kustomize.sh b/utils/setup_kustomize.sh index dca0021..3812dc6 100644 --- a/utils/setup_kustomize.sh +++ b/utils/setup_kustomize.sh @@ -1,9 +1,25 @@ #!/bin/bash setup_kustomize() { - if ! [ -f "$(dirname "$0")/kustomize" ] || ! [ -x "$(dirname "$0")/kustomize" ] + local dir + dir="$(dirname "$0")" + if ! [ -f "$dir/kustomize" ] || ! [ -x "$dir/kustomize" ] then echo "kustomize not found. Installing..." - curl -s "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh" | bash + # Pin the installer to an immutable release tag rather than the moving + # `master` branch, and download-then-run instead of piping the network + # straight into bash. The installer itself checksum-verifies the + # kustomize binary it fetches for the requested version. + local version="5.5.0" + local script + script="$(mktemp)" + if curl -fsSL "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/kustomize/v${version}/hack/install_kustomize.sh" -o "$script"; then + bash "$script" "$version" "$dir" + else + echo "Failed to download kustomize installer" >&2 + rm -f "$script" + return 1 + fi + rm -f "$script" fi } \ No newline at end of file diff --git a/utils/update_env_var.sh b/utils/update_env_var.sh index c008510..61ad8f1 100755 --- a/utils/update_env_var.sh +++ b/utils/update_env_var.sh @@ -4,16 +4,15 @@ update_env_var() { local file=$1 local key=$2 local value=$3 - - if grep -q "^$key=" "$file" 2>/dev/null; then - # Key exists, update it - if [[ "$OSTYPE" == "darwin"* ]]; then - sed -i '' "s|^$key=.*|$key=$value|" "$file" - else - sed -i "s|^$key=.*|$key=$value|" "$file" - fi - else - echo "$key=$value" >> "$file" + + # Never feed the value through sed: a secret containing sed metacharacters + # ('|' delimiter, '&', trailing '\') would corrupt or silently drop the + # value (a Steam password like 'p@ss|word' broke the substitution). Instead + # remove any existing line for the key, then append the value literally with + # printf. Env files are order-independent, so re-appending is safe. + if [ -f "$file" ] && grep -q "^$key=" "$file" 2>/dev/null; then + grep -v "^$key=" "$file" > "$file.tmp" && mv "$file.tmp" "$file" fi + printf '%s=%s\n' "$key" "$value" >> "$file" } From 3f81a1ea1b416172d18f1246a837d468bf0224ee Mon Sep 17 00:00:00 2001 From: Flegma Date: Fri, 10 Jul 2026 22:17:03 +0200 Subject: [PATCH 2/2] fix: update_env_var mv must run even when grep filters all lines Review follow-up: when the file's only lines matched the key, grep -v exited 1 and the &&-gated mv was skipped, leaving the stale line plus the appended one (duplicate key + leaked .tmp). Decouple mv from grep's exit code so it always applies the filtered result. --- utils/update_env_var.sh | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/utils/update_env_var.sh b/utils/update_env_var.sh index 61ad8f1..093d7b2 100755 --- a/utils/update_env_var.sh +++ b/utils/update_env_var.sh @@ -11,7 +11,12 @@ update_env_var() { # remove any existing line for the key, then append the value literally with # printf. Env files are order-independent, so re-appending is safe. if [ -f "$file" ] && grep -q "^$key=" "$file" 2>/dev/null; then - grep -v "^$key=" "$file" > "$file.tmp" && mv "$file.tmp" "$file" + # `|| true`: when every surviving line is filtered out (a single-key + # file), grep exits 1; without this the mv would be skipped, leaving the + # stale line in place next to the appended one. The redirect writes the + # (possibly empty) tmp regardless, so mv always runs. + grep -v "^$key=" "$file" > "$file.tmp" 2>/dev/null || true + mv "$file.tmp" "$file" fi printf '%s=%s\n' "$key" "$value" >> "$file" }