Patch 0002

security: harden WeatherAPI cache update script.

0002-security-harden-WeatherAPI-cache-update-script.patch

0001 0002 0003 0004 0005 0006 0007
From 49118a49d4cd5cabb4802ba21bf24cd86729231d Mon Sep 17 00:00:00 2001
From: "Jory A. Pratt" <geekypenguin@gmail.com>
Date: Thu, 2 Jul 2026 07:25:52 -0500
Subject: [PATCH 2/7] security: harden WeatherAPI cache update script.

Parse weatherapi.ini with asl3-ini.sh instead of sourcing it, validate
CACHE_DIR, keep the API key out of the process list via curl --config,
tighten cache permissions to 0640, sanitize cached text fields, and
invalidate stale cache when LOCATION changes.
---
 bin/asl3-weatherapi-update.sh | 47 +++++++++++++++++++++++++----------
 1 file changed, 34 insertions(+), 13 deletions(-)

diff --git a/bin/asl3-weatherapi-update.sh b/bin/asl3-weatherapi-update.sh
index 7a22633..1ca460e 100755
--- a/bin/asl3-weatherapi-update.sh
+++ b/bin/asl3-weatherapi-update.sh
@@ -1,6 +1,10 @@
 #!/usr/bin/env bash
 set -euo pipefail
 
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+# shellcheck source=bin/asl3-ini.sh
+source "$SCRIPT_DIR/asl3-ini.sh"
+
 CONFIG_FILE="/etc/asterisk/local/weatherapi.ini"
 
 if [ ! -f "$CONFIG_FILE" ]; then
@@ -8,12 +12,10 @@ if [ ! -f "$CONFIG_FILE" ]; then
   exit 1
 fi
 
-# shellcheck disable=SC1090
-source "$CONFIG_FILE"
-
-: "${API_KEY:=}"
-: "${LOCATION:=}"
-: "${CACHE_DIR:=/var/cache/asl3-saytime-weather}"
+API_KEY="$(asl3_ini_get "$CONFIG_FILE" API_KEY)"
+LOCATION="$(asl3_ini_get "$CONFIG_FILE" LOCATION)"
+CACHE_DIR="$(asl3_ini_get "$CONFIG_FILE" CACHE_DIR)"
+CACHE_DIR="${CACHE_DIR:-/var/cache/asl3-saytime-weather}"
 
 if [ -z "$API_KEY" ] || [ "$API_KEY" = "PUT_YOUR_WEATHERAPI_KEY_HERE" ]; then
   echo "WeatherAPI key is not set in $CONFIG_FILE" >&2
@@ -25,15 +27,30 @@ if [ -z "$LOCATION" ]; then
   exit 1
 fi
 
+asl3_validate_dir_path "$CACHE_DIR" "CACHE_DIR"
+
 mkdir -p "$CACHE_DIR"
 
 TMP_JSON="$CACHE_DIR/weather.json.tmp"
 OUT_JSON="$CACHE_DIR/weather.json"
 OUT_ENV="$CACHE_DIR/current.env"
 
-URL="https://api.weatherapi.com/v1/current.json?key=${API_KEY}&q=$(printf '%s' "$LOCATION" | jq -sRr @uri)&aqi=no"
+# Drop stale cache when the configured location changes.
+if [ -f "$OUT_ENV" ]; then
+  cached_location="$(asl3_ini_get "$OUT_ENV" LOCATION_QUERY)"
+  if [ -n "$cached_location" ] && [ "$cached_location" != "$LOCATION" ]; then
+    rm -f "$OUT_JSON" "$OUT_ENV"
+  fi
+fi
+
+encoded_location="$(printf '%s' "$LOCATION" | jq -sRr @uri)"
+curl_cfg="$(mktemp "$CACHE_DIR/curl.XXXXXX")"
+chmod 600 "$curl_cfg"
+trap 'rm -f "$curl_cfg"' EXIT
+printf 'url = "https://api.weatherapi.com/v1/current.json?key=%s&q=%s&aqi=no"\n' \
+  "$API_KEY" "$encoded_location" > "$curl_cfg"
 
-curl -fsSL --retry 3 --connect-timeout 10 --max-time 30 "$URL" -o "$TMP_JSON"
+curl -fsSL --retry 3 --connect-timeout 10 --max-time 30 --config "$curl_cfg" -o "$TMP_JSON"
 
 # Validate JSON before replacing the good cache.
 jq -e '.current.condition.text and .current.temp_f and .location.name' "$TMP_JSON" >/dev/null
@@ -43,19 +60,23 @@ mv "$TMP_JSON" "$OUT_JSON"
 jq -r '
   def safe_round:
     if . == null then "" else (round | tostring) end;
+  def safe_text:
+    if . == null then "" else (tostring | gsub("\r|\n"; " ")) end;
 
-  "LOCATION_NAME=\(.location.name // "")",
-  "REGION=\(.location.region // "")",
-  "COUNTRY=\(.location.country // "")",
+  "LOCATION_NAME=\(.location.name // "" | safe_text)",
+  "REGION=\(.location.region // "" | safe_text)",
+  "COUNTRY=\(.location.country // "" | safe_text)",
   "TEMP_F=\(.current.temp_f | safe_round)",
   "TEMP_C=\(.current.temp_c | safe_round)",
   "FEELSLIKE_F=\(.current.feelslike_f | safe_round)",
   "FEELSLIKE_C=\(.current.feelslike_c | safe_round)",
   "HUMIDITY=\((.current.humidity // "") | tostring)",
-  "CONDITION=\(.current.condition.text // "")",
+  "CONDITION=\(.current.condition.text // "" | safe_text)",
   "UPDATED_EPOCH=\((.current.last_updated_epoch // .location.localtime_epoch // now) | floor | tostring)"
 ' "$OUT_JSON" > "$OUT_ENV"
 
-chmod 0644 "$OUT_JSON" "$OUT_ENV"
+asl3_ini_set "$OUT_ENV" LOCATION_QUERY "$LOCATION"
+
+chmod 0640 "$OUT_JSON" "$OUT_ENV"
 
 echo "Weather cache updated for $LOCATION"
-- 
2.47.3