fix: close SSRF/rebinding, IDOR, and stale-session authz gaps found in audit
Bump to 0.5.1. Fixes: unfollowed-redirect SSRF + DNS-rebinding in AI url-import and webhook dispatch (new safeFetch with IP-pinned undici dispatcher); cross-user photo deletion via unvalidated recipe/review storage keys; comment-moderation and tier-quota checks trusting a stale cached session role/tier instead of the DB. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -44,3 +44,19 @@ export async function deleteObject(key: string): Promise<void> {
|
||||
export function getPublicUrl(key: string): string {
|
||||
return `${clientPublicUrl}/${bucket}/${key}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* A recipe/review photo's storage key is only trustworthy if it matches the
|
||||
* exact prefix issued by /api/v1/upload/presign for this recipe + user +
|
||||
* purpose — otherwise a client could submit another user's key (discoverable
|
||||
* from a public recipe's rendered <Image> src, or a review's photoKey in API
|
||||
* responses) and have it adopted, then later evicted, triggering
|
||||
* deleteObject() against an object it never owned.
|
||||
*/
|
||||
export function isOwnedRecipePhotoKey(key: string, recipeId: string, userId: string): boolean {
|
||||
return key.startsWith(`recipes/${recipeId}/photos/${userId}-`);
|
||||
}
|
||||
|
||||
export function isOwnedReviewPhotoKey(key: string, recipeId: string, userId: string): boolean {
|
||||
return key.startsWith(`recipes/${recipeId}/reviews/${userId}-`);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user