Skip to content

fix(api): constrain image upload grants - #180

Merged
GSTJ merged 1 commit into
mainfrom
security/harden-image-uploads
Aug 31, 2026
Merged

fix(api): constrain image upload grants#180
GSTJ merged 1 commit into
mainfrom
security/harden-image-uploads

Conversation

@GSTJ

@GSTJ GSTJ commented Aug 31, 2026

Copy link
Copy Markdown
Owner

Summary

Locks each photo upload to the user who requested it, signs the compressed file's exact byte size, and cleans up expired temporary files and failed promotions.

Local upload security proof

Details

  • Limits each account to 12 upload grants per hour and expires signed URLs after 10 minutes.
  • The mobile client compresses the photo before requesting an upload grant. The grant signs the WEBP file's exact byte length, and the object store rejects uploads with a different size.
  • Records grant ownership before issuing the signed URL. Only that user can claim it when creating or editing a dog.
  • Schedules cleanup for expired temporary objects and orphaned permanent copies. Active and newly consumed grants stay out of the first cleanup phase to avoid racing dog creation.
  • Deletes stored photos when a dog or account is deleted.
  • Keeps the legacy signed-upload response working for installed app versions with the same ownership, rate, expiry, and cleanup controls.

Testing steps

  1. Run:

    pnpm test
    pnpm typecheck
    pnpm lint
    pnpm format
    pnpm exec dotenv -e .env.test -- pnpm -F @pegada/nextjs build

    Each command should finish successfully.

  2. Against the local MinIO and PostgreSQL setup, upload a file with a different byte size from the signed request. Confirm the upload returns 403.

  3. Upload the file with the exact signed byte size. Confirm the upload returns 200.

  4. Create a dog with that photo. Confirm the temporary object is removed and the permanent photo still loads.

  5. Delete the dog. Confirm the permanent object is removed and no stored image record remains.

Copilot AI lite review requested due to automatic review settings August 31, 2026 15:16

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@GSTJ
GSTJ merged commit cd6d0a0 into main Aug 31, 2026
13 checks passed
@GSTJ
GSTJ deleted the security/harden-image-uploads branch August 31, 2026 15:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants