Skip to content

Commit 8e02e63

Browse files
Chosen9115claude
andauthored
fix(cli): register --server <unreachable> now fails with network_error (#72)
cmd_register ran its own curl with `… || true`, throwing away curl's exit code. When the connection fails, `-w "%{http_code}"` prints "000"; both the fallback (4xx) and error (>= 400) checks are false for "000", so an unreachable --server fell through to the success path and exited 0 — the CLI silently claimed a registration that never happened. Capture curl's exit code (no `|| true`) and treat a non-zero exit OR an HTTP "000" as a network_error, on both the JSON-wrapped POST and the raw-YAML fallback. Mirrors api_call's existing handling. The regression test (U2a #10) is hardened: it isolates OPENSOP_HOME so --server is the only URL source (a developer's ~/.opensop would otherwise mask the failure and the test could pass for the wrong reason) and asserts the error code is network_error. Verified: pre-fix the test fails with the exact reported symptom ("got exit 0"); post-fix the suite is 289 PASS. Co-authored-by: Carlos <carlos> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 42a07ed commit 8e02e63

3 files changed

Lines changed: 37 additions & 4 deletions

File tree

cli/CHANGELOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,18 @@ This project follows [Semantic Versioning](https://semver.org/) and the
2323
always returns 0. Added a source-level guard in `test/test.sh` forbidding standalone
2424
`(( var++ ))` / `(( var-- ))` increments so the footgun cannot reappear.
2525

26+
- **`register --server <unreachable>` exited 0 instead of failing.**
27+
`cmd_register` ran its own `curl` with `… || true`, discarding curl's exit
28+
code, and `-w "%{http_code}"` prints `000` when no connection is made. Both
29+
the fallback check (`4xx`) and the error check (`>= 400`) are false for `000`,
30+
so an unreachable server fell through to the success path and exited 0 — the
31+
CLI silently reported a registration that never happened. Now curl's exit code
32+
is captured (no `|| true`) and a non-zero exit or an HTTP `000` code is treated
33+
as a `network_error` (mirroring `api_call`), applied to both the JSON-wrapped
34+
POST and the raw-YAML fallback. The regression test isolates `OPENSOP_HOME` so
35+
`--server` is the only URL source and asserts the `network_error` code, rather
36+
than passing for the wrong reason when a developer `~/.opensop` is present.
37+
2638
## [0.8.0] — 2026-06-11
2739

2840
> **BREAKING:** The default backend is now **LOCAL**. Commands that previously hit a remote server by default now run locally (no server, no curl). Scripts relying on the old default-remote behaviour must add `--remote` (uses the configured `OPENSOP_URL`) or `--server <url>`. The `--local` flag is now a deprecated no-op and can be omitted — it is still accepted for script compatibility but prints a deprecation note to stderr.

cli/bin/opensop

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1662,7 +1662,17 @@ cmd_register() {
16621662
[[ -n "${OPENSOP_TOKEN:-}" ]] && curl_args+=(-H "X-SOP-Token: $OPENSOP_TOKEN")
16631663
curl_args+=(-d "$body")
16641664

1665-
status=$(curl "${curl_args[@]}" "$url" 2>/dev/null || true)
1665+
# A failed connection is a network error, NOT a success. curl exits non-zero
1666+
# (e.g. 7, connection refused) and `-w "%{http_code}"` prints "000" when no
1667+
# HTTP response was received. Capture curl's exit (don't `|| true` it away)
1668+
# and treat either signal as a network_error — otherwise an unreachable
1669+
# `--server` falls through to the success path and exits 0. Mirrors api_call.
1670+
local curl_rc=0
1671+
status=$(curl "${curl_args[@]}" "$url" 2>/dev/null) || curl_rc=$?
1672+
if (( curl_rc != 0 )) || [[ "$status" == "000" ]]; then
1673+
rm -f "$tmp_resp"
1674+
die "request failed: POST $url" "network_error" "is the server reachable at ${OPENSOP_URL%/}?"
1675+
fi
16661676

16671677
if [[ "$status" -ge 400 && "$status" -lt 500 ]]; then
16681678
# Fall back to raw YAML body.
@@ -1673,7 +1683,12 @@ cmd_register() {
16731683
-w "%{http_code}")
16741684
[[ -n "${OPENSOP_TOKEN:-}" ]] && curl_args+=(-H "X-SOP-Token: $OPENSOP_TOKEN")
16751685
curl_args+=(--data-binary "@$file")
1676-
status=$(curl "${curl_args[@]}" "$url" 2>/dev/null || true)
1686+
curl_rc=0
1687+
status=$(curl "${curl_args[@]}" "$url" 2>/dev/null) || curl_rc=$?
1688+
if (( curl_rc != 0 )) || [[ "$status" == "000" ]]; then
1689+
rm -f "$tmp_resp"
1690+
die "request failed: POST $url" "network_error" "is the server reachable at ${OPENSOP_URL%/}?"
1691+
fi
16771692
fi
16781693

16791694
resp=$(cat "$tmp_resp")

cli/test/test.sh

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3475,13 +3475,19 @@ echo "$u2a_reg_out" | jq -r '.hint' | grep -q "\-\-remote\|\-\-server" \
34753475
echo "PASS: U2a register no --remote — exits non-zero with usage_error, hint mentions --remote/--server"
34763476

34773477
# --- (10) 'register --server http://127.0.0.1:1' → attempts remote (fails with network error) ---
3478+
# OPENSOP_HOME isolates from any developer ~/.opensop config so --server is the
3479+
# only URL source — otherwise a configured URL would mask the network failure
3480+
# and the test could pass for the wrong reason. A curl that can't connect prints
3481+
# HTTP code "000"; register must treat that as a network_error, not a success.
34783482
set +e
3479-
"$cli" --server http://127.0.0.1:1 register "$u2a_proc" --json >/dev/null 2>&1
3483+
u2a_reg_server_out="$(OPENSOP_HOME="$u2a_home" "$cli" --server http://127.0.0.1:1 register "$u2a_proc" --json 2>&1)"
34803484
u2a_reg_server_rc=$?
34813485
set -e
34823486
[ "$u2a_reg_server_rc" -ne 0 ] \
34833487
|| { echo "FAIL: U2a register --server <url> should fail (no server at 127.0.0.1:1), got exit 0"; exit 1; }
3484-
echo "PASS: U2a register --server <url> — attempts remote (fails with network error, not usage_error)"
3488+
echo "$u2a_reg_server_out" | jq -e '.error == "network_error"' >/dev/null \
3489+
|| { echo "FAIL: U2a register --server <unreachable> should emit network_error (curl 000 must not be treated as success), got: $u2a_reg_server_out"; exit 1; }
3490+
echo "PASS: U2a register --server <url> — unreachable server yields network_error (curl 000 not treated as success)"
34853491

34863492
# --- (11) 'run' with NO flag, explicitly NO --local — the file path form ---
34873493
# Exercise that the default-local path works when --local is ABSENT entirely.

0 commit comments

Comments
 (0)