diff --git a/backend/app/api/admin.py b/backend/app/api/admin.py index cefc181..fd3c5ce 100644 --- a/backend/app/api/admin.py +++ b/backend/app/api/admin.py @@ -97,11 +97,19 @@ async def images_bulk_delete(): ) ) - if dry_run: - return jsonify(projected) - sha8 = _bulk_image_confirm_token(image_ids) expected = f"delete-images-{sha8}" + + if dry_run: + # Hand the canonical Tier-C confirm token back with the + # projection so the frontend doesn't have to recompute SHA-256 + # client-side via crypto.subtle (Secure-Context-gated, + # undefined on plain-HTTP origins per the homelab posture). + # Operator-flagged 2026-05-27. + projected["confirm_token"] = expected + return jsonify(projected) + + if supplied_confirm != expected: return _bad( "confirm_mismatch", diff --git a/frontend/src/components/cleanup/MinDimensionCard.vue b/frontend/src/components/cleanup/MinDimensionCard.vue index b1a1e9b..140d549 100644 --- a/frontend/src/components/cleanup/MinDimensionCard.vue +++ b/frontend/src/components/cleanup/MinDimensionCard.vue @@ -52,8 +52,8 @@ v-model="showModal" action="delete" kind="min-dim" - :run-id="tokenSuffix" tier="C" + :expected-token-override="preview?.confirm_token || ''" :projected-counts="projectedCounts" :description="`Width < ${minW} OR height < ${minH}`" @confirm="onConfirmedDelete" @@ -62,11 +62,19 @@