You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The download handler wraps the whole fetch in a two-way catch (src/routes/attachments.ts:37-42):
}catch(e){if(einstanceofStackPermissionError)returnc.json({error: 'Unauthorized'},401);returnc.json({error: 'Attachment not found'},404);}
Two defects:
401 for authenticated requesters. The spec's error table is explicit: 401 = missing/invalid token; 403 = StackPermissionError, requester lacks access. An authenticated entity denied by the access rule gets 401 "Unauthorized," telling well-behaved clients their token is bad — an adapter-api that reacts to 401 by re-authing or dropping its session will do exactly the wrong thing. Should be 403 (permission code per Core sync: wire error contract — typed code vocabulary, structured bodies, 400/422 discipline (core #53) #33) when a token was presented, 401 only when the requester is anonymous.
The catch-all eats real failures. Any other error — disk I/O failure, a corrupted blob directory, a bug in the access predicate — is reported as "Attachment not found" with a 404. Genuine server faults become invisible: no 500, no log line (this route bypasses the global error middleware that would have logged it). Only translate the errors we mean (StackNotFoundError/missing-file → 404); let everything else propagate to errorMiddleware.
One deliberate behavior to preserve while fixing this: for non-owner requesters, core's ScopedStack.getAttachment throws StackPermissionError for missing and inaccessible files alike, so both currently map to the same response. That indistinguishability is load-bearing — fileIds are content hashes, so distinguishing "not found" from "forbidden" is a confirmation oracle for whether the stack contains specific bytes (the exact oracle haverstack/core#51 documents). The fix should keep missing ≡ forbidden for non-owners (one status for both — 403 or 404, decide with the owner and pin it) while still separating both from transport-auth failures and internal errors.
Server design-review finding.
Problem
The download handler wraps the whole fetch in a two-way catch (
src/routes/attachments.ts:37-42):Two defects:
StackPermissionError, requester lacks access. An authenticated entity denied by the access rule gets 401 "Unauthorized," telling well-behaved clients their token is bad — anadapter-apithat reacts to 401 by re-authing or dropping its session will do exactly the wrong thing. Should be 403 (permissioncode per Core sync: wire error contract — typedcodevocabulary, structured bodies, 400/422 discipline (core #53) #33) when a token was presented, 401 only when the requester is anonymous.StackNotFoundError/missing-file → 404); let everything else propagate toerrorMiddleware.One deliberate behavior to preserve while fixing this: for non-owner requesters, core's
ScopedStack.getAttachmentthrowsStackPermissionErrorfor missing and inaccessible files alike, so both currently map to the same response. That indistinguishability is load-bearing — fileIds are content hashes, so distinguishing "not found" from "forbidden" is a confirmation oracle for whether the stack contains specific bytes (the exact oracle haverstack/core#51 documents). The fix should keep missing ≡ forbidden for non-owners (one status for both — 403 or 404, decide with the owner and pin it) while still separating both from transport-auth failures and internal errors.Work items
codevocabulary, structured bodies, 400/422 discipline (core #53) #33Refs #33, #37, haverstack/core#51 (anti-oracle rule), spec §Error responses.