#3630·sure

Bug: provider connection cards render accounts the viewer cannot access

Author: brianespinozaCreated Sep 18, 2026Updated Sep 18, 2026

How are you using Sure?

  • I am a self-hosted user (local only)

Self hoster checklist

  • Self hosted app commit SHA: main @ b883643c6
  • I have confirmed that my app's commit is the latest version of Sure
  • Where are you hosting? Docker Compose

Bug description

AccountsController#visible_provider_items (app/controllers/accounts_controller.rb:349-354) admits a provider item for a non-admin when any one of its accounts is accessible:

ruby
def visible_provider_items(items)
  items.select do |item|
    Current.user.admin? ||
      (item.respond_to?(:accounts) && (item.accounts.map(&:id) & @accessible_account_ids).any?)
  end
end

The card partials then render the item's full account collection, with no per-viewer filtering. For Plaid, app/views/plaid_items/_plaid_item.html.erb:87:

erb
<%= render "accounts/index/account_groups", accounts: plaid_item.accounts %>

So a member who has been shared one account on a multi-account connection sees the name and balance of every other account on that connection, including accounts deliberately never shared with them.

This is not Plaid-specific. Every provider card does it — 22 follow the pattern above, and SimpleFIN does the same via a local (accounts = simplefin_item.accounts at _simplefin_item.html.erb:4, rendered at :197).

To Reproduce

  1. Set the family's default account sharing to private.
  2. As an admin, link one provider connection that covers two accounts, e.g. Shared Checking and Private Savings.
  3. Share only Shared Checking with a member.
  4. Sign in as that member and open /accounts.

The connection card is visible, which is correct — they do have access to one account on it — but it lists both accounts with their balances.

Expected behavior

The card lists only the accounts the viewer may access. Accounts on the same connection that have not been shared with them are omitted.

Proposed fix

The obvious fix — filtering by Current.user inside the partial — breaks a second render path, so it needs care.

Each *_item partial is also rendered from its SyncCompleteEvent, outside any request, where Current.user is nil. For Plaid, app/models/plaid_item/sync_complete_event.rb:13-18 broadcasts family-wide with only locals: { plaid_item: }. A Current.user-dependent filter evaluates to "nothing is accessible" there, so the account list empties for every viewer, admins included, on every completed sync until the page is reloaded. I hit exactly this in #3613 and had to revert it.

A correct fix threads an explicit viewer (or accessible-account-id set) local through both call sites, the way family: is threaded into accounts/_sync_controls.html.erb — which already documents this gotcha in a comment — and applies it across the provider partials. The alternative is broadcasting per-user instead of per-family, which is a larger change to a pattern every provider shares.

Worth noting what doesn't work: narrowing visible_provider_items to require that all of an item's accounts be accessible is simpler, but it is not behaviour-neutral. It also hides the card from a member who legitimately shares one account on it, costing them the sync status and balance of an account they are entitled to see.

Context

Pre-existing on main and independent of #3613, which was careful not to widen it. Raised there by CodeRabbit during review; @jjmata asked that it be logged separately so it doesn't get lost.