A loan id is not a credential — scope every LoanController read to the org

Fixkamolos
Shipped
August 15, 2026 at 2:28 AM UTC
Author
Kamo
Commit
d5b2376

LoanController.get(uid) took a loan id and returned whatever it found. Any caller who reached the service got the loan, balances and borrower included, whichever tenant it belonged to; loan ids are sequential, so reading another organization's whole book was a matter of counting. payoff-quote had the same shape, and by-borrower took a member id alone and returned that member's loans without checking they were this organization's member. All three now take the org and filter on it. list already did. The check lives in the service rather than the controller on purpose. The org arrives in X-Org-Id, which is only trustworthy while ProxyHmacFilter is enabled — los.proxy-hmac.enabled defaults to false and is not set in the deployment — so a check written beside the reading of the header shares the header's weakest assumption. Filtering the query result means an unsigned header can at worst name an organization; it cannot hand back another organization's loans. "Not found" and "not yours" deliberately collapse into the same 404. Telling them apart tells an unauthorised caller which ids exist, which is most of what enumeration is trying to learn, and a test pins the two answers as identical. This is the cheap moment for it: kamolos-service exists but has no deployment, so nothing is in flight and nobody is relying on the old behaviour.

All changes

Like what you see shipping?

Every one of these updates lands in your workspace automatically. Start free and watch it grow week after week.

Start Free ForeverView Pricing