* fix(accounts): honor stored return_to after subtype account creation
Closes#1766.
The savings-goals empty-state "Add an account" CTA passes ?return_to, which
StoreLocation captures into session[:return_to], but account-creation flows
didn't always consume it:
- AccountableResource#create honored a form-carried return_to but not the
session value, so if the param wasn't threaded through the multi-step
new-account flow the user still landed on the account page. Added a
session[:return_to] fallback (the form param still wins).
- PropertiesController is a 3-step wizard (create → balances → address) that
never threaded return_to as a form param, and its final redirect went
straight to account_path. It now honors session[:return_to] on completion.
Rails blocks external-host redirects, so return_to can't open-redirect.
valuations#create uses redirect_back_or_to (referer-based) — different flow,
left as-is.
Tests: depository create prefers the form return_to and falls back to the
session value; property wizard completion honors the stored return_to.
* fix(accounts): block open-redirect via return_to; consume session value
Two AI-review findings on #2109:
- Open-redirect (codex): the property wizard's turbo_stream completion uses
stream_redirect_to, which the client resolves with Turbo.visit — that
full-navigates cross-origin, bypassing Rails' redirect host-guard. A crafted
?return_to=https://evil could walk the user off-site. Filter return_to at the
StoreLocation choke point (store time) to internal absolute paths only, and
sanitize the separate form-param channel, so an unsafe value can't reach
redirect_to / stream_redirect_to.
- Stale session (coderabbit): session[:return_to] was read but never consumed.
Consume it with delete at redirect time so it can't leak into a later flow.
Adds guard tests (external return_to falls back to the account page).
* fix(security): guard safe_return_to against non-String return_to
A crafted `?return_to[]=foo` makes params[:return_to] an Array, and
Array#match? doesn't exist, so safe_return_to raised NoMethodError
before the open-redirect hardening could reject it. Add an
is_a?(String) check as the first gate. Other CodeRabbit/Codex
return_to findings on this PR were already addressed (consume-side
re-validation + session.delete).