Skip to content

Fix internal-detail leak in Setup.php's error handling - #78

Merged
jules-paystack merged 1 commit into
masterfrom
fix/setup-error-message-leak
Sep 29, 2026
Merged

jules-paystack merged 1 commit into
masterfrom
fix/setup-error-message-leak

Conversation

@jules-paystack

Copy link
Copy Markdown
Collaborator

Summary

  • Controller/Payment/Setup.php passed raw ApiException::getMessage() (built from curl_error() and Paystack's raw API response body — can carry internal hostnames, TLS detail, gateway-side state) directly to the customer-facing checkout failure page. Same leak class D5 already closed on Callback.php; missed here.
  • Found by diffing the now-merged fix/r2.1-settlement-gate reconciliation (PR Reconcile settlement-gate: full accept-side verification, D7/D8/D12 fixes #77) against master after the fact, to confirm everything from that abandoned branch had actually been ported — it hadn't, in this one spot.
  • Fix: log the raw detail (context array, not string concatenation, so an embedded newline can't forge log lines) and keep writing it to order status history (admin-only); the customer now gets a fixed, safe string.
  • Also added a bare catch (\Throwable) matching Callback.php's D5 fix — the ApiException-only catch left every other exception processAuthorization() can throw (a missing store URL, a malformed Paystack response, a save failure) uncaught, which in developer-mode stores would print the raw message + trace to the browser.
  • Adversarial (security-critic) review of this diff via mutation testing found the same class of gap in the test itself (an exact-equality guard that a "give the customer a bit more detail" edit could slip past while staying green) — tightened to str_contains.

Test plan

  • Test/Unit/vendor/bin/phpunit -c phpunit.xml --no-coverage — 303 tests, 592 assertions, all green
  • New test for the \Throwable path, confirming a non-ApiException throw also shows the safe message, not its own text
  • Security-critic review of this diff, verified via mutation testing that the test assertions are actually discriminating

🤖 Generated with Claude Code

Controller/Payment/Setup.php passed ApiException::getMessage() --
built from curl_error() and Paystack's raw API response body --
straight to the customer-facing checkout failure page. Same leak
class D5 already closed on Callback.php, missed here. The abandoned
fix/r2.1-settlement-gate branch (reconciled in PR #77) had already
fixed this but it wasn't part of that reconciliation's scope; found
by diffing that branch against master after the fact.

Detail now goes to the logger (context array, not string
concatenation, so an embedded newline can't forge log lines) and
order history (admin-only) instead; the customer gets a fixed, safe
string. Also added a bare \Throwable catch matching Callback.php's
D5 fix -- the ApiException-only catch left every other exception
processAuthorization() can throw (a missing store URL, a malformed
response, a save failure) uncaught.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@jules-paystack
jules-paystack merged commit 09bb8f2 into master Sep 29, 2026
5 checks passed
@jules-paystack
jules-paystack deleted the fix/setup-error-message-leak branch September 29, 2026 11:26
@jules-paystack jules-paystack mentioned this pull request Sep 29, 2026
1 task done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant