aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xbin/weather-fetch.sh16
-rw-r--r--docs/superpowers/plans/2026-09-17-weather-widget.md16
-rw-r--r--docs/superpowers/specs/2026-09-17-weather-widget-design.md11
3 files changed, 33 insertions, 10 deletions
diff --git a/bin/weather-fetch.sh b/bin/weather-fetch.sh
index aaa48e2..76aa07b 100755
--- a/bin/weather-fetch.sh
+++ b/bin/weather-fetch.sh
@@ -33,6 +33,12 @@ mkdir -p "$CACHE_DIR"
TMP="$CACHE.tmp.$$"
trap 'rm -f "$TMP"' EXIT
+# The response carries the configured city and its coordinates. That is not a
+# secret, but it is location data and it shares a directory with UDT's other
+# generated state, all of which is 600. Set the mode before the body lands in
+# the file rather than after, so it is never briefly world-readable.
+umask 077
+
URL="https://api.openweathermap.org/data/2.5/weather"
if ! curl -fsS --max-time 15 --get "$URL" \
--data-urlencode "appid=$KEY" \
@@ -44,9 +50,13 @@ if ! curl -fsS --max-time 15 --get "$URL" \
exit 1
fi
-# A bad key returns HTTP 200 with a JSON error body, so the status code alone
-# proves nothing. This check is carried over from the polybar script, which
-# learned it the same way.
+# Belt and braces behind `curl -f`. Measured against the live API: a bad key
+# returns 401 and an unknown city 404, so curl -f already rejects both and this
+# branch is not what catches them. It stays for the case -f cannot see: a 200
+# whose body is not usable weather, which is what the polybar script this was
+# ported from actually hit, since it ran curl WITHOUT -f and so received error
+# bodies with a success exit. Without this, such a body would reach the parser
+# and replace a good cache.
if [ "$(jq -r '.cod // empty' "$TMP" 2>/dev/null)" != "200" ]; then
echo "weather-fetch: API error: $(jq -r '.message // "unknown"' "$TMP" 2>/dev/null)" >&2
exit 1
diff --git a/docs/superpowers/plans/2026-09-17-weather-widget.md b/docs/superpowers/plans/2026-09-17-weather-widget.md
index 0b78b83..36e2dc1 100644
--- a/docs/superpowers/plans/2026-09-17-weather-widget.md
+++ b/docs/superpowers/plans/2026-09-17-weather-widget.md
@@ -778,6 +778,12 @@ mkdir -p "$CACHE_DIR"
TMP="$CACHE.tmp.$$"
trap 'rm -f "$TMP"' EXIT
+# The response carries the configured city and its coordinates. That is not a
+# secret, but it is location data and it shares a directory with UDT's other
+# generated state, all of which is 600. Set the mode before the body lands in
+# the file rather than after, so it is never briefly world-readable.
+umask 077
+
URL="https://api.openweathermap.org/data/2.5/weather"
if ! curl -fsS --max-time 15 --get "$URL" \
--data-urlencode "appid=$KEY" \
@@ -789,9 +795,13 @@ if ! curl -fsS --max-time 15 --get "$URL" \
exit 1
fi
-# A bad key returns HTTP 200 with a JSON error body, so the status code alone
-# proves nothing. This check is carried over from the polybar script, which
-# learned it the same way.
+# Belt and braces behind `curl -f`. Measured against the live API: a bad key
+# returns 401 and an unknown city 404, so curl -f already rejects both and this
+# branch is not what catches them. It stays for the case -f cannot see: a 200
+# whose body is not usable weather, which is what the polybar script this was
+# ported from actually hit, since it ran curl WITHOUT -f and so received error
+# bodies with a success exit. Without this, such a body would reach the parser
+# and replace a good cache.
if [ "$(jq -r '.cod // empty' "$TMP" 2>/dev/null)" != "200" ]; then
echo "weather-fetch: API error: $(jq -r '.message // "unknown"' "$TMP" 2>/dev/null)" >&2
exit 1
diff --git a/docs/superpowers/specs/2026-09-17-weather-widget-design.md b/docs/superpowers/specs/2026-09-17-weather-widget-design.md
index 412adac..60fa7d9 100644
--- a/docs/superpowers/specs/2026-09-17-weather-widget-design.md
+++ b/docs/superpowers/specs/2026-09-17-weather-widget-design.md
@@ -174,10 +174,13 @@ single transient failure does not flag the card.
The fetch script exits non-zero with a message on stderr when the key is
missing or curl fails, and **leaves any existing cache untouched** rather than
-overwriting it with an error body. OWM returns HTTP 200 with a JSON error body
-for a bad key, so the script checks that `.cod` is 200 before replacing the
-cache. That check is carried over from the reference script, which learned it
-the same way.
+overwriting it with an error body.
+
+The script checks `.cod` is 200 before replacing the cache, behind `curl -f`.
+Measured against the live API, a bad key returns 401 and an unknown city 404,
+so `-f` rejects both before `.cod` is read; the check covers the remaining case
+of a 200 whose body is not usable weather. The reference script needed it as
+its only defence because it ran curl without `-f`.
## Secrets and personal data