diff --git a/bin/gstack-config b/bin/gstack-config index 1147addd..821a342a 100755 --- a/bin/gstack-config +++ b/bin/gstack-config @@ -16,16 +16,28 @@ CONFIG_FILE="$STATE_DIR/config.yaml" case "${1:-}" in get) KEY="${2:?Usage: gstack-config get }" - grep -E "^${KEY}:" "$CONFIG_FILE" 2>/dev/null | tail -1 | awk '{print $2}' | tr -d '[:space:]' || true + # Validate key (alphanumeric + underscore only) + if ! printf '%s' "$KEY" | grep -qE '^[a-zA-Z0-9_]+$'; then + echo "Error: key must contain only alphanumeric characters and underscores" >&2 + exit 1 + fi + grep -F "${KEY}:" "$CONFIG_FILE" 2>/dev/null | tail -1 | awk '{print $2}' | tr -d '[:space:]' || true ;; set) KEY="${2:?Usage: gstack-config set }" VALUE="${3:?Usage: gstack-config set }" + # Validate key (alphanumeric + underscore only) + if ! printf '%s' "$KEY" | grep -qE '^[a-zA-Z0-9_]+$'; then + echo "Error: key must contain only alphanumeric characters and underscores" >&2 + exit 1 + fi mkdir -p "$STATE_DIR" - if grep -qE "^${KEY}:" "$CONFIG_FILE" 2>/dev/null; then + # Escape sed special chars in value and drop embedded newlines + ESC_VALUE="$(printf '%s' "$VALUE" | head -1 | sed 's/[&/\]/\\&/g')" + if grep -qF "${KEY}:" "$CONFIG_FILE" 2>/dev/null; then # Portable in-place edit (BSD sed uses -i '', GNU sed uses -i without arg) _tmpfile="$(mktemp "${CONFIG_FILE}.XXXXXX")" - sed "s/^${KEY}:.*/${KEY}: ${VALUE}/" "$CONFIG_FILE" > "$_tmpfile" && mv "$_tmpfile" "$CONFIG_FILE" + sed "s/^${KEY}:.*/${KEY}: ${ESC_VALUE}/" "$CONFIG_FILE" > "$_tmpfile" && mv "$_tmpfile" "$CONFIG_FILE" else echo "${KEY}: ${VALUE}" >> "$CONFIG_FILE" fi diff --git a/bin/gstack-telemetry-log b/bin/gstack-telemetry-log index da371c38..93db8207 100755 --- a/bin/gstack-telemetry-log +++ b/bin/gstack-telemetry-log @@ -158,6 +158,8 @@ OUTCOME="$(json_safe "$OUTCOME")" SESSION_ID="$(json_safe "$SESSION_ID")" SOURCE="$(json_safe "$SOURCE")" EVENT_TYPE="$(json_safe "$EVENT_TYPE")" +REPO_SLUG="$(json_safe "$REPO_SLUG")" +BRANCH="$(json_safe "$BRANCH")" # Escape null fields — sanitize ERROR_CLASS and FAILED_STEP via json_safe() ERR_FIELD="null"