feat(gallery): politique licence fail-closed — non-remixable par défaut - #25
Conversation
…ut, licence explicite + confirmations exigées - Migration 0077 : remixAllowed DEFAULT false + rétroactif (licenseId NULL → false). - Curation : remixAllowed=true exige licenceId+licenseText (400 REMIX_LICENSE_REQUIRED) + rightsConfirmed + piiPolicyAccepted (400 REMIX_RIGHTS_CONFIRMATION_REQUIRED). Aucun choix implicite. - Défense en profondeur : remix d'un listing sans licence en base → 403 REMIX_LICENSE_REQUIRED (aucun fallback, jamais de MIT silencieux). - 39 tests remix+gallery verts (4 négatifs fail-closed neufs), typecheck 0. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4a9fcc1dc2
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| -- un listing sans licence explicite n'est JAMAIS remixable. | ||
| ALTER TABLE "GalleryListing" ALTER COLUMN "remixAllowed" SET DEFAULT false; | ||
| -- Rétroactif : tout listing sans licence déclarée redevient non-remixable. | ||
| UPDATE "GalleryListing" SET "remixAllowed" = false WHERE "licenseId" IS NULL; |
There was a problem hiding this comment.
Mark partially licensed legacy rows non-remixable
Before this commit, the admin endpoint allowed licenseId without licenseText and defaulted remixAllowed to true, so existing rows can have a non-null ID but no text/hash. This predicate leaves those rows remixable; the public API consequently exposes remixAllowed: true and the UI enables Remix, but the new enforcement at app.ts:21809 always rejects the request because licenseTextSha256 is missing. Include incomplete license snapshots (at least a null hash/text) in this backfill so the public state matches enforcement.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Pull request overview
Cette PR implémente une politique de licence fail-closed pour les remixes de la Gallery : un listing est non-remixable par défaut et ne devient remixable que via une curation explicite (licence + confirmations), avec une vérification “défense en profondeur” côté endpoint de remix.
Changes:
- Bascule du défaut
remixAllowedàfalse(DB + stores) et rétroaction sur les listings sanslicenseId(migration 0077). - Durcissement API : validation curator
/admin/gallery-listings(licence + confirmations) et enforcement/gallery/:slug/remix(403 si licence absente). - Mise à jour/ajout de tests e2e + unit couvrant les cas négatifs fail-closed.
Reviewed changes
Copilot reviewed 7 out of 12 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| tests/e2e/gallery-remix-license.spec.ts | Fixture e2e mise à jour pour inclure les confirmations requises quand remixAllowed: true. |
| services/api/src/tests/test-api-store.ts | Défaut remixAllowed aligné sur la politique fail-closed dans le store de test. |
| services/api/src/tests/gallery-routes.spec.ts | Ajustement fixtures + ajout de tests négatifs (curation + enforcement). |
| services/api/src/prisma-store.ts | Défaut remixAllowed aligné sur fail-closed dans le store Prisma. |
| services/api/src/app.ts | Enforcement remix (403 si licence absente) + validation curator pour remixAllowed=true (licence + confirmations). |
| packages/database/prisma/schema.prisma | remixAllowed passe à @default(false) avec doc associée. |
| packages/database/prisma/migrations/0077_license_fail_closed/migration.sql | Migration : default false + rétroactif remixAllowed=false quand licenseId IS NULL. |
| packages/database/generated/client/schema.prisma | Regénération du schéma client (default false + doc). |
| packages/database/generated/client/package.json | Mise à jour du nom de package généré (hash). |
| packages/database/generated/client/index.js | Inline schema Prisma mis à jour (default false). |
| packages/database/generated/client/index.d.ts | Commentaire types générés mis à jour pour refléter fail-closed. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
|
||
| // FAIL-CLOSED en profondeur : même un listing marqué remixable ne se | ||
| // remixe pas sans licence explicite enregistrée (aucun fallback). | ||
| if (!listing.licenseId || !listing.licenseTextSha256) { |
…EVIEW_PENDING, compteurs/supersession dérivés, resoumission détaillée (#26) - RÈGLE MAÎTRESSE : CLOSED exige un ReviewReceipt COMPLET (registre + gardes validateur + dérivation) ; 22 signés → PROVEN_REVIEW_PENDING (reçu RR-20260720-CODEX-01 INCOMPLET, responseHash manquant — jamais un nom saisi à la main) ; 8 QUICK remédiés → PROVEN_REVIEW_PENDING. - COUNTER_RECONCILIATION_20260720.md GÉNÉRÉ (formule + IDs + source par métrique, drift-check CI) — 4 questions répondues. - SUPERSESSION_REGISTRY : couverture 100% des IDs hérités, 164 surfaces et 122 work items DÉRIVÉS des tables (garde CI). - REVIEWER_RESUBMISSION_A_20260720.md (+ outputs/) : resoumission POUR REVUE. - DEC-OWNER-REMIX-DEFAULT-LICENSE → DECIDED (fail-closed ; code = PR #25). - V3_05_REMIX_AUDIT_20260720.md : audit par exigence — V3-05 NON clos. Co-authored-by: Avi <avi@snatchbot.me> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Quoi (directive Avi 20/07, point 1)
remixAllowed DEFAULT false(migration 0077) + rétroactif : tout listing sanslicenseIdrepasse non-remixable.400 REMIX_LICENSE_REQUIRED; confirmation des droits + acceptation politique PII → sinon400 REMIX_RIGHTS_CONFIRMATION_REQUIRED.403 REMIX_LICENSE_REQUIRED— aucun fallback, aucune licence appliquée silencieusement.Comportement prod AVANT cette PR (réponse à la question d'Avi)
remixAllowed=true, mig 0076) ; l'UI affichait « No license specified by the author » et le serveur enregistraitlicenseId: nullsur le job — aucun fallback de valeur ;🤖 Generated with Claude Code