fix(security): harden four smaller gallery and contract paths, drop the unmounted photo auth middleware

- the customer contract PDF stream applies assertContractPdfPath like the
  admin and public contract routes
- OG previews fall back to the site card for draft, archived and
  deactivated galleries instead of leaking name, date and welcome message
- video Range requests are validated before the 206 is written; a NaN,
  inverted or out-of-file range now answers 416
- share-token comparisons in gallery resolve/info use the constant-time
  helper share-login already used
- middleware/photoAuth.js and the galleryAuth/photoAuth/verifyGalleryAccess
  exports of middleware/auth.js were unreferenced since the static mounts
  went; the auth.js copy had neither slug binding nor issuer pin, so it is
  removed before anyone mounts it
This commit is contained in:
Paul Nothaft
2026-09-03 10:54:37 +02:00
parent 40a8a9882a
commit 835312e8e6
6 changed files with 37 additions and 465 deletions
+12 -1
View File
@@ -169,8 +169,19 @@ async function formatEventDate(value) {
}
}
// Draft, archived and deactivated galleries are refused by /info; the OG
// preview must not leak their name, date and welcome message to crawlers.
function isPubliclyVisible(event) {
if (!event) return false;
const truthy = (v) => v === true || v === 1 || v === '1' || v === 'true';
if (truthy(event.is_draft) || truthy(event.is_archived)) return false;
if (event.is_active === false || event.is_active === 0 || event.is_active === '0') return false;
return true;
}
async function buildOgMetadata(slug, requestPath) {
const event = await resolveSlug(slug);
const resolved = await resolveSlug(slug);
const event = isPubliclyVisible(resolved) ? resolved : null;
const branding = await fetchBranding();
const base = await frontendBase();
const siteName = branding.companyName || 'PicPeak';