fix(security): block guest access to hidden/client-only photos across bulk + secure routes (#939)
* fix(security): block guest access to hidden/client-only photos across bulk + secure routes * fix(security): harden hidden-photo fix per review (stale ZIP cache, legacy token mint, SQLite bool, client rebuild) * fix(security): invalidate ZIP cache on photo visibility/category change (codex r2) * fix(security): recheck photo visibility at signed/secure serve time (TOCTOU) + invalidate ZIP on client visibility change (codex r3) --------- Co-authored-by: Paul Nothaft <[email protected]>
This commit is contained in:
co-authored by
Paul Nothaft
parent
fe615c82e4
commit
8a87c9274b
@@ -112,8 +112,15 @@ class DownloadZipService {
|
||||
const event = await db('events').where({ id: eventId }).first();
|
||||
if (!event) return { success: false, error: 'Event not found' };
|
||||
|
||||
// The prebuilt zip is served to ordinary gallery guests (the
|
||||
// download-all fast path), so it must exclude hidden/client-only
|
||||
// photos — NULL visibility counts as visible (pre-migration rows).
|
||||
// PIN-clients bypass this cache and stream a full archive instead.
|
||||
const photos = await db('photos')
|
||||
.where({ event_id: eventId })
|
||||
.where(function () {
|
||||
this.where('visibility', 'visible').orWhereNull('visibility');
|
||||
})
|
||||
.select('*')
|
||||
.orderBy('type', 'asc')
|
||||
.orderBy('uploaded_at', 'desc');
|
||||
|
||||
@@ -25,7 +25,11 @@ class SecureImageService {
|
||||
// Reveal mode (#838): whether the minting context bypasses the
|
||||
// hidden-gallery gate — re-checked at SERVE time so a re-hide
|
||||
// invalidates in-flight guest tokens without breaking the slideshow.
|
||||
revealBypass = false
|
||||
revealBypass = false,
|
||||
// Whether the minter was a PIN-client — lets the serve route keep
|
||||
// delivering a photo hidden AFTER minting (TOCTOU). A guest's token
|
||||
// carries false, so it stops the moment the photo is hidden.
|
||||
clientBypass = false
|
||||
} = options;
|
||||
|
||||
const tokenData = {
|
||||
@@ -37,6 +41,7 @@ class SecureImageService {
|
||||
usedCount: 0,
|
||||
protectionLevel,
|
||||
revealBypass,
|
||||
clientBypass,
|
||||
createdAt: Date.now()
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user