Homework — 403 vs 404, and the guard in SQL
Expand the app. Give your app the same owner surface, or work on SignFlow.
-
Feel 403 vs 404. Temporarily change
ownedDocumentto fetch with an unscoped query and return403whendoc.OwnerID != me. Log in as user B and request user A's id — you get 403, which confirms the id is real. Now revert to the owner-scoped query + 404. Write two sentences on what an attacker learns in each case. -
Delete the check, keep the guard. Comment out the
if doc.Status != "draft"branch inDeleteDocument. Flip a document tosentand try to delete it — the UI will not show the button, so craft the POST by hand. ConfirmDeleteDraftDocumentreports zero rows deleted and the document survives. The business rule held with the handler check gone, because it lives in theWHERE. -
Download integrity. Download a document, run
sha256sumon the file, and compare to thefile_hashon the detail page — they match. Now edit the stored blob inuploads/directly (append a byte) and download again: the bytes changed butfile_hashdid not. What would it take to detect that drift? (You build exactly that check in the signing phase.) -
A missing id. Request
/documents/not-a-uuidand/documents/00000000-0000-0000-0000-000000000000. Both should be 404 — one becauseparseUUIDrejects it before the DB, the other because the query returns no rows. Confirm neither reachesserverError(no 500 in the log).
Next phase, documents stop being private: you invite signers by email, and a document moves from draft to sent — the immutability rule you just built is what makes that transition safe.