QA security gap: no dynamic authz/IDOR test for no-REST Next.js server actions #7

Open
opened 2026-06-29 17:17:26 +00:00 by jbr870 · 0 comments
Owner

The gap

For a Next.js app built on server actions + RLS with no REST endpoints, no QA domain runs a
dynamic, adversarial authorization test — i.e. forging a call to a server action with another
tenant's IDs to prove the permission check actually holds (a real IDOR attack, not just reading the
code that is supposed to prevent it).

Coverage for such an app today:

Layer Domain Covers
Static qa-code → Static Security Reviewer Reads server actions / RLS / migrations and reasons about them. Primary coverage.
Dynamic, browser qa-security-browser XSS, CSRF, headers, UI-level auth-bypass — drives actions through the UI as a normal user.
Dynamic, API qa-security-api No-op — needs HTTP endpoints; there are none.

So qa-security-api cleanly no-ops, and qa-security-browser exercises actions only as a logged-in
user would — neither forges a tampered cross-tenant request. The authz/IDOR invariants rest on static
review + RLS as defence-in-depth. Decent, but no automated attacker ever proves the rules.

Where it surfaced

Dogfooding outwrit #6 (append-only audit trail + admin viewer; Next.js server actions + RLS, no
REST). /qa-code caught the static security bugs (incl. CR-1 TRUNCATE wipe), but two access-rule
findings were deferred precisely because they are this class:

  • CR-3 — a denied role change by a non-member isn't audited; the naive fix reopens a cross-tenant
    audit-injection vector. (outwrit sibling issue.)
  • CR-5 — the page admin gate disagrees with the RLS is_org_admin check.

Neither would be caught by a dynamic regression test under the current domain model.

Candidate fix (not committed)

Let qa-security-browser use its already-authenticated browser session to fire direct
server-action POSTs with tampered payloads/IDs
— server actions are same-origin POST endpoints, so
the browser context can reach them without going through the UI. That closes the IDOR/authz hole for
no-REST Next.js apps without inventing a new domain.

Scope / severity

Known-limitation, not a blocker. Narrow but real, and specific to the no-REST server-actions stack
(increasingly common). Decide: extend qa-security-browser, or document it as an accepted limit and
lean on static review + RLS. Related to #6 (qa-design).

## The gap For a **Next.js app built on server actions + RLS with no REST endpoints**, no QA domain runs a *dynamic, adversarial authorization test* — i.e. forging a call to a server action with another tenant's IDs to prove the permission check actually holds (a real IDOR attack, not just reading the code that is supposed to prevent it). Coverage for such an app today: | Layer | Domain | Covers | |---|---|---| | Static | `qa-code` → Static Security Reviewer | Reads server actions / RLS / migrations and reasons about them. **Primary** coverage. | | Dynamic, browser | `qa-security-browser` | XSS, CSRF, headers, UI-level auth-bypass — drives actions *through the UI* as a normal user. | | Dynamic, API | `qa-security-api` | **No-op** — needs HTTP endpoints; there are none. | So `qa-security-api` cleanly no-ops, and `qa-security-browser` exercises actions only as a logged-in user would — neither forges a tampered cross-tenant request. The authz/IDOR invariants rest on static review + RLS as defence-in-depth. Decent, but no automated attacker ever *proves* the rules. ## Where it surfaced Dogfooding **outwrit #6** (append-only audit trail + admin viewer; Next.js server actions + RLS, no REST). `/qa-code` caught the static security bugs (incl. CR-1 TRUNCATE wipe), but two access-rule findings were deferred precisely because they are this class: - **CR-3** — a denied role change by a non-member isn't audited; the naive fix reopens a cross-tenant audit-injection vector. (outwrit sibling issue.) - **CR-5** — the page admin gate disagrees with the RLS `is_org_admin` check. Neither would be caught by a *dynamic* regression test under the current domain model. ## Candidate fix (not committed) Let `qa-security-browser` use its already-authenticated browser session to fire **direct server-action POSTs with tampered payloads/IDs** — server actions are same-origin POST endpoints, so the browser context can reach them without going through the UI. That closes the IDOR/authz hole for no-REST Next.js apps without inventing a new domain. ## Scope / severity Known-limitation, not a blocker. Narrow but real, and specific to the no-REST server-actions stack (increasingly common). Decide: extend `qa-security-browser`, or document it as an accepted limit and lean on static review + RLS. Related to #6 (qa-design).
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
jbr870/devwork-skills#7
No description provided.