feat(qa): pass H, authorization and abuse
The passes stopped at G, and A-G share an assumption that hides an entire class of defect: every one of them asks a legitimate user to do legitimate things. Nothing looked at what happens when the caller is not who they claim, does not own what they ask for, or asks too often. That assumption has already cost this portfolio once. requireCoupleContext in Closer-Couples never verified the caller belonged to the couple whose data was returned -- authentication present, correct, and proving nothing about ownership. No pass A-G would have found it. Pass H covers six cases: authenticated endpoints called logged-out, a list endpoint checked for rows the caller should not see, User A requesting User B's object by id, a privileged route opened as an ordinary user, the expensive endpoint hit repeatedly, and the built bundle and localStorage inspected. The organising sentence, which is the reason it is a separate pass rather than more rows in B: authenticated is not the same as owning, and neither is the same as permitted. closes #2 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
8b2e4121dd
commit
29fb908932
|
|
@ -23,6 +23,7 @@ Review trigger: Any QA round run
|
|||
| E — End of loop | **Blocked** | <what blocks it, and the issue number> |
|
||||
| F — Accessibility | Not run | <why, and when it will be> |
|
||||
| G — Performance | Pass | <the numbers, not "felt fine"> |
|
||||
| H — Authorization and abuse | Not run | <which of the six cases were exercised, and as which user> |
|
||||
|
||||
## Standing gaps
|
||||
|
||||
|
|
|
|||
|
|
@ -33,10 +33,28 @@ skipped one is visible.
|
|||
| E | The end of the loop — the state that is hardest to reach on purpose |
|
||||
| F | Accessibility: keyboard only, screen reader labels, contrast, text scaling |
|
||||
| G | Performance under the load this product will actually see |
|
||||
| H | Authorization and abuse: every authenticated endpoint called logged-out; a list endpoint checked for rows the caller should not see; User A requesting User B's object by id; a privileged route opened as an ordinary user; the most expensive endpoint hit repeatedly; the built bundle and `localStorage` inspected for anything secret |
|
||||
|
||||
<Add, remove and rename to fit. A pass that never applies is noise; a pass that
|
||||
is always skipped is a lie.>
|
||||
|
||||
### Why H is separate from B
|
||||
|
||||
**Authenticated is not the same as owning, and neither is the same as
|
||||
permitted.** Login is the front door; every room inside still needs its own
|
||||
lock. Pass B walks the flow as a real user, which means it walks it as a user
|
||||
who is entitled to everything it touches — so B cannot see the entire class of
|
||||
defect where the entitlement was never checked.
|
||||
|
||||
This has already cost this portfolio once. `requireCoupleContext` never verified
|
||||
that the caller belonged to the couple whose data was being returned:
|
||||
authentication was present, correct, and proved nothing. No pass A-G would have
|
||||
looked for it, because every one of them asks a legitimate user to do legitimate
|
||||
things.
|
||||
|
||||
Run H as somebody who is not entitled: logged out, logged in as the wrong
|
||||
person, and logged in as the right person asking too often.
|
||||
|
||||
## What counts as a finding
|
||||
|
||||
A finding needs: what was done, what happened, what should have happened, and
|
||||
|
|
|
|||
Loading…
Reference in New Issue