Claude
Fix all 5 review findings: missing method, unbounded limit, fake masking, skewed decay, inert gate
6bb5dc5 unverified
Raw
History Blame Contribute Delete
3.75 kB
#!/usr/bin/env bash
# scripts/setenv.sh — set or append KEY=VALUE lines in .env, safely.
#
# Why this exists: the operator administers this box from an iPhone, through
# the AWS console's browser terminal. In that environment `nano` is close to
# unusable and a pasted multi-line block gets swallowed by whatever the first
# line started. Every config change therefore has to be ONE line, with no
# interactive editor and no heredoc.
#
# ./scripts/setenv.sh SOLANA_LEG2_HAIRCUT_BPS=2 MIN_PROFIT_FLOOR_USD=0.20
#
# Behaviour:
# • replaces the line if the key already exists (commented or not)
# • appends it if it doesn't
# • takes ONE timestamped backup per run, so a bad paste is recoverable
# • redacts by KEY NAME — anything matching KEY/TOKEN/SECRET/PASSWORD/
# PRIVATE/RPC/URL prints as `[REDACTED — n chars set]`, with no
# recoverable characters, so the output is safe to screenshot
#
# Values are written verbatim, quoted or not, so URLs with `/` and `?` are
# safe — this uses awk on exact key matches rather than a sed substitution
# whose delimiter the value could contain. That is the specific bug that
# makes `sed -i "s|^KEY=.*|KEY=$URL|"` a bad idea for RPC endpoints.
set -euo pipefail
cd "$(dirname "$0")/.."
ENV_FILE="${ENV_FILE:-.env}"
if [ "$#" -eq 0 ]; then
echo "usage: ./scripts/setenv.sh KEY=VALUE [KEY=VALUE ...]" >&2
exit 64
fi
[ -f "$ENV_FILE" ] || { echo "no $ENV_FILE here — are you in ~/Garden-Angel-Terminal?" >&2; exit 66; }
BACKUP="${ENV_FILE}.bak.$(date +%Y%m%d-%H%M%S)"
cp "$ENV_FILE" "$BACKUP"
# Decide from the KEY NAME, not the value's length.
#
# The first version of this inspected only the value: a `case` branch that
# did nothing, then "print in full if 24 chars or shorter". That got both
# halves wrong. A short API key printed verbatim, and even a long one leaked
# a 6-character prefix and 4-character suffix — on a script whose entire
# stated purpose is being safe to screenshot.
#
# Key names are the reliable signal. A value cannot tell you whether it is a
# secret; `PUMPFUN_API_KEY` can. Secrets now print a FIXED placeholder with
# no recoverable characters and no length hint, regardless of how long they
# are. Non-secret settings (thresholds, booleans, sizes) print in full,
# because seeing those confirmed is the point of the output.
is_secret_key() {
case "$(printf '%s' "$1" | tr '[:lower:]' '[:upper:]')" in
*KEY*|*TOKEN*|*SECRET*|*PASSWORD*|*PASSPHRASE*|*PRIVATE*|*RPC*|*URL*|*DSN*|*WEBHOOK*)
return 0 ;;
esac
return 1
}
mask() {
local k="$1" v="$2"
if is_secret_key "$k"; then
printf '[REDACTED — %d chars set]' "${#v}"
else
printf '%s' "$v"
fi
}
for pair in "$@"; do
case "$pair" in
*=*) : ;;
*) echo "skipping '$pair' — expected KEY=VALUE" >&2; continue ;;
esac
key="${pair%%=*}"
val="${pair#*=}"
before="$(grep -c "^[#[:space:]]*${key}=" "$ENV_FILE" || true)"
# awk, not sed: the value may contain any delimiter character (RPC URLs
# contain `/` and sometimes `?`), and awk never re-interprets it.
KEY="$key" VAL="$val" awk '
BEGIN { k = ENVIRON["KEY"]; v = ENVIRON["VAL"]; done = 0 }
{
line = $0
stripped = line
sub(/^[#[:space:]]*/, "", stripped)
if (!done && index(stripped, k "=") == 1) { print k "=" v; done = 1; next }
print line
}
END { if (!done) print k "=" v }
' "$ENV_FILE" > "${ENV_FILE}.tmp"
mv "${ENV_FILE}.tmp" "$ENV_FILE"
if [ "$before" -gt 0 ]; then
printf ' updated %-34s = %s\n' "$key" "$(mask "$key" "$val")"
else
printf ' ADDED %-34s = %s\n' "$key" "$(mask "$key" "$val")"
fi
done
chmod 600 "$ENV_FILE"
echo
echo "backup: $BACKUP"
echo "restore with: cp $BACKUP $ENV_FILE"