fix(security): close GHSA-g94x (cross-gallery photo read) + GHSA-pv6w (admin DB export) (stable) (#925)
* fix(security): close two access-control advisories (GHSA-g94x, GHSA-pv6w) (stable) GHSA-g94x-8vv8-3c9f (HIGH) — the secure-image VIEW route (/secure-images/:slug/secure/:photoId/:token) validated only the token signature and took the gallery/photo from the URL, so a token minted on any PUBLIC gallery read every other gallery's photos with no password (its download sibling has verifyGalleryAccess; the view route can't — it serves via <img src> with no header). Bind the token to its scope instead: the URL photoId must equal the token's minted photoId (photos belong to exactly one gallery, and minting is gallery-scoped), and the gallery embedded in the token's sessionId must equal the URL gallery. GHSA-pv6w-rj34-wj9v (MEDIUM) — GET /admin/backup/picpeak/export dumps every table unredacted (bcrypt hashes, 2FA, SMTP/SSO/WhatsApp/webhook/S3 secrets) and was gated only by backup.create, which the built-in admin role holds. Gate it behind super_admin, matching the restore side (backup.restore, already admin-denied) and the masked config APIs. Regression tests pin both: cross-gallery token reads 403 (photo and gallery checks), backup export 403 for admin / passes for super_admin. Stable port of #924. secureImages on stable has no reveal-mode block, so only the token-binding checks are added; the backup export gate is identical. * fix(security): review follow-ups on the export gate (GHSA-pv6w) - test: place the mocked export in its own mkdtemp dir. The route recursively deletes path.dirname(filePath) after download, so a stub in bare os.tmpdir() made the super_admin test wipe the whole temp root — other jest workers' DB files included (latent CI flake). - ui: hide PicpeakExportCard from non-super_admins. The role keeps settings.view + backup.create, so after the gate its Download button always 403'd with a generic toast; gate the card on role super_admin to match the endpoint. * fix(security): keep the token-mismatch audit values within varchar(20) (GHSA-g94x review) image_access_logs.access_type is varchar(20) (migration 038), but 'token_gallery_mismatch' is 22 chars — on Postgres the audit write threw value-too-long and logImageAccess swallowed it, so the security event went unrecorded (the 403 still fired; log is best-effort). Shorten to 'photo_mismatch' / 'gallery_mismatch' (14/16). --------- Co-authored-by: Paul Nothaft <[email protected]>
This commit is contained in:
co-authored by
Paul Nothaft
parent
a27d19b4d1
commit
60cbda5b22
@@ -19,6 +19,7 @@ import { useTranslation } from 'react-i18next';
|
||||
import { Button, Card, Loading } from '../../components/common';
|
||||
import { useLocalizedDate } from '../../hooks/useLocalizedDate';
|
||||
import { useMutationWithToast } from '../../hooks';
|
||||
import { useAdminAuth } from '../../contexts/AdminAuthContext';
|
||||
import { BackupDashboard } from '../../components/admin/BackupDashboard';
|
||||
import { BackupConfiguration } from '../../components/admin/BackupConfiguration';
|
||||
import { BackupHistory } from '../../components/admin/BackupHistory';
|
||||
@@ -33,6 +34,11 @@ type TabId = 'dashboard' | 'configuration' | 'history' | 'restore' | 'integrity'
|
||||
export const BackupManagement: React.FC = () => {
|
||||
const [activeTab, setActiveTab] = useState<TabId>('dashboard');
|
||||
const { t } = useTranslation();
|
||||
// Full-instance export contains every secret, so the endpoint is
|
||||
// super_admin-only (GHSA-pv6w) — hide the card for other roles instead
|
||||
// of showing a button that always 403s.
|
||||
const { user } = useAdminAuth();
|
||||
const isSuperAdmin = user?.role?.name === 'super_admin';
|
||||
const { formatDateTime: fmtDateTime } = useLocalizedDate();
|
||||
|
||||
const tabs = [
|
||||
@@ -201,7 +207,7 @@ export const BackupManagement: React.FC = () => {
|
||||
onRunBackup={() => manualBackupMutation.mutate()}
|
||||
isBackupRunning={backupStatus?.isRunning || manualBackupMutation.isPending}
|
||||
/>
|
||||
<PicpeakExportCard />
|
||||
{isSuperAdmin && <PicpeakExportCard />}
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user