fix(ntask-core): correct the attachment insert, and add the presign actions - #11
Merged
Merged
Conversation
CREATE_ATTACHMENT declared `$bucket: String!` and passed it in the insert object. `bucket` is not client-insertable: getDownloadUrl honours attachment.bucket and then signs the storage key with the storage root credentials, so a caller-chosen bucket allowed cross-bucket traversal — a user could register a row pointing at another user's object and receive a presigned URL for it. The Hasura permission now drops the column, so the mutation as written fails with: field 'bucket' not found in type: 'np_attachments_insert_input' Nothing consumes it yet (the web app carried its own copy), which is why this was not already breaking. It would have broken the mobile attachment work that is about to use it. `bucket` stays in the response selection so callers can see what the server chose, and CreateAttachmentInput drops it to match. Adds a regression test asserting the field names against the real schema: never filename/size_bytes/user_id, never bucket or uploader_id on insert. Verified the guard actually fires by reintroducing bucket and watching it fail. That check matters — the original web bug survived because its fixtures repeated the same wrong column names the client used, so the test and the client agreed while both disagreed with the server.
GET_UPLOAD_URL and GET_DOWNLOAD_URL existed only as a private copy inside the web app, so any other surface wiring attachments had to reinvent them. That private copy is exactly how the field-name drift happened. Signatures taken from backend/hasura/metadata/actions.graphql. Note the Action arguments are String!, not uuid! — they are Action arguments rather than columns, so the uuid scalar does not apply. The three-step upload contract is documented on the export, because skipping step 3 leaves an orphaned object no query will ever return, and adding an Authorization header to the PUT invalidates the signature.
Contributor
Author
|
Merging on local verification rather than waiting on the hosted run. Why: the org has exactly one self-hosted runner (
Plus the lefthook pre-commit gates (license-check, typecheck, test) on each commit. The runner shortage is a separate problem worth fixing on its own terms — it is currently the throughput ceiling for every repo in the org. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Two changes to
@nself/ntask-core's attachment layer, both driven by making attachments actually work end to end.1.
bucketmust not be sent on insertCREATE_ATTACHMENTdeclared$bucket: String!and passed it in the insert object.bucketis not client-insertable.getDownloadUrlhonoursattachment.bucketand then signs the storage key with the storage root credentials, so a caller-chosen bucket allowed cross-bucket traversal: register a row pointing at another user's object, receive a working presigned URL for it. The Hasura insert permission drops the column and the database default (ntask) applies, so the mutation as written now fails with:Nothing consumed this operation yet — the web app carried its own hand-written copy — which is why it was not already failing. It would have broken the mobile attachment work about to use it.
bucketstays in the response selection so callers can see what the server chose.uploader_idis documented as absent-by-design (Hasura presets it to the caller).2. The presign actions were missing entirely
GET_UPLOAD_URL/GET_DOWNLOAD_URLexisted only inside the web app, so any other surface had to reinvent them — which is precisely how the field-name drift happened in the first place. Signatures taken frombackend/hasura/metadata/actions.graphql.Note the Action arguments are
String!, notuuid!: they are Action arguments rather than columns, so the uuid scalar does not apply.The three-step upload contract is documented on the export, because skipping step 3 leaves an orphaned object no query will ever return, and adding an
Authorizationheader to the PUT invalidates the signature.Regression tests
Assert field names against the real schema: never
filename/size_bytes/user_id, neverbucketoruploader_idon insert, and the action argument types.I verified the guard actually fails by reintroducing
bucketand watching the suite go red, then restoring. That check matters here — the original web bug survived because its fixtures repeated the same wrong column names the client used, so the test and the client agreed with each other while both disagreed with the server. A guard that cannot fail is worse than none.63 tests pass;
tscbuild clean; lefthook typecheck + license gates green.