Skip to content

fix(api): stop a failed ownership check from masquerading as 404 - #46

Merged
github-actions[bot] merged 1 commit into
mainfrom
fix/ownership-check-swallowed-error
Sep 6, 2026
Merged

fix(api): stop a failed ownership check from masquerading as 404#46
github-actions[bot] merged 1 commit into
mainfrom
fix/ownership-check-swallowed-error

Conversation

@catomean

@catomean catomean commented Sep 6, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • ownsProject/ownsFigure in app/src/lib/api/ownership.ts discarded the Supabase error from their select() calls, so a real query failure (RLS misconfig, DB outage, transient network error) looked identical to "caller doesn't own this row" — both leave data null.
  • Every route that gates on these helpers (surfaces, compositions, figures — all POST/PATCH/DELETE, plus several GETs) then returned a misleading 404 "Not found" instead of a 500, masking real server errors as authorization failures.
  • Same bug class as fix(api): stop surfacing DB errors as "no surface/composition yet" #40 ("stop surfacing DB errors as 'no surface/composition yet'"), just one layer down in the shared ownership gate — found via an audit for remaining instances of that pattern after fix(api): stop a failed surface delete from leaving a duplicate row #45 merged.

Changes

  • ownsProject/ownsFigure now return { owns: boolean; error: string | null } instead of a bare boolean.
  • All six call sites (surfaces, compositions, figures, figures/[id]) now check ownership.error first and return 500 before falling through to the 404 "not owned" branch.
  • Updated ownership.test.ts to match the new return shape and added two new tests asserting a query failure surfaces via .error rather than being silently reported as owns: false with no signal.

Test plan

  • npx tsc --noEmit — clean
  • npm run lint — 0 errors (8 pre-existing warnings, unrelated to this change)
  • npx vitest run — 98 pass, 0 fail (includes 2 new tests for the fixed error path)

Co-Authored-By: Claude Sonnet 5 noreply@anthropic.com

ownsProject/ownsFigure discarded the Supabase error from their select()
calls and returned false whenever data came back null — which is also
what happens on a real query failure (RLS misconfig, DB outage). Every
route's authorization check then reported "Not found" instead of
surfacing a server error, same bug class as #40 but one layer down in
the shared ownership helper, affecting every POST/PATCH/DELETE (and
several GETs) across surfaces, compositions, and figures.

ownsProject/ownsFigure now return { owns, error } so callers can tell
a real failure (500) apart from "caller doesn't own this" (404).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@github-actions
github-actions Bot merged commit 257d424 into main Sep 6, 2026
1 check passed
@github-actions
github-actions Bot deleted the fix/ownership-check-swallowed-error branch September 6, 2026 04:12
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