Repository navigation
Description
Moved out of #25. #25's provider part was fixed by LeanerCloud/cloud-commitments-cli#1122, and #25 is being closed. This is the account part, from audit finding A11-014 (2026-09-02 audit, docs/audits/codebase-audit-2026-09-02.md).
Audit finding A11-014 (medium), as recorded on #25
getSavingsAnalytics(frontend/src/api/history.ts:43-45) sends bothaccount_ids=a,b,candaccount_id=a, but the analytics handlers read only the singular param (accountID := params["account_id"]at internal/api/handler_analytics.go:40, :131, :197, thenresolveSingleAccountFilterIDs) andaccount_idsis parsed nowhere for these routes. So with three accounts selected the chart silently renders only account A's savings while the chip row advertises three, an under-reported figure with no indication that two were dropped. The siblinggetHistory(history.ts:22) sends only the plural form, whichinternal/api/handler_history.go:678does honour, so the two panels on the same page disagree for the same filter. Either plumbaccount_idsthrough the analytics path alongsideprovider, or have the caller disable/annotate the chart when more than one account is selected.
Still true on main (6b586cc), from reading the code
frontend/src/api/history.ts:33-48:getSavingsAnalyticssetsaccount_idsto the full list andaccount_idto the first id only. The code comment says the plural form is "for forward-compatibility".internal/api/handler_analytics.go:getAnalyticsTrends(:47),getHistoryAnalytics(:155) andgetHistoryBreakdown(:199) all pass onlyparams["account_id"]toresolveAnalyticsAccountFilter. Nothing readsaccount_ids.- Callers: the dashboard (
frontend/src/dashboard.ts:1177,:1348) and the Savings History module (frontend/src/modules/savings-history.ts:120). GET /historydoes parseaccount_ids(internal/api/handler_history.go:748,parseAccountIDs), so the purchase list and the savings chart disagree when several accounts are selected.
Impact
With more than one account selected, the savings chart and totals show only the first account's savings, with no warning. Users read an under-reported savings figure.
Suggested fix
Parse account_ids in the analytics handlers with the existing parseAccountIDs (UUID-validated, capped at 200), and check scope for every id, the same way /history does. Pass the union of the dual-column filters (UUIDs plus external ids per provider) to QueryHistory / QueryBreakdown. Keep the singular account_id working for older clients. If both are sent, the plural wins. The frontend can then stop sending the singular form.
Acceptance criteria
- With two or more accounts selected,
/history/analytics(and the trends and breakdown endpoints) return savings for all of them. - A scoped user who asks for an account outside their scope gets the same answer as for a single out-of-scope
account_idtoday. - Handler and store tests cover multiple accounts, including rows that carry only the external account id.
- A frontend test shows that the chart request carries every selected account.
Refs #25, A11-014.
Filed by IAmJSD's agent at IAmJSD's request (acting owner while the maintainers are away).
Activity
fix(analytics): honour account_ids on the savings analytics routes (#…