fix(security): production read agent is now actually read-only #81

Merged
arcodange merged 1 commits from arcodange/prod-apply into main 2026-08-09 19:33:27 +02:00
Owner

Migration appliquée en production, dans le seul ordre qui ne casse pas le promote.

1. Création d'ai_agent_prod_prod_write (id=5) avec le scope étroit prod-write — factures, règlements, et soumission de documents (nécessaire à builddoc pour régénérer le PDF d'une facture modifiée). Vérifié fonctionnellement : lectures OK, DELETE sur une facture → 403.

2. Pipeline de promote repointé sur la clé de cet utilisateur. Il n'emprunte plus le credential des skills de lecture ; si la clé est absente il meurt en affichant la commande de provisionnement, au lieu de retomber silencieusement sur autre chose.

3. Révocation de 12 droits d'écriture et de suppression sur ai_agent (id=3), le credential que détiennent toutes les skills de lecture : créer/modifier factures clients et fournisseur, tiers, contacts, coordonnées de paiement, propositions, exports, liens comptables — et supprimer propositions, événements, et documents de la GED.

Vérification après migration — factures, tiers, contacts, produits, propositions, factures fournisseur et comptes bancaires se lisent toujours ; créer une facture renvoie 403 Forbidden: Insuffisant rights. La doctrine et la réalité coïncident enfin.

Une correction qui valait le détour

262 n'est pas « créer/modifier les produits » comme mon premier catalogue le supposait, mais l'extension ACL voir_tous — un droit de lecture dont les skills dépendent : sans lui, les endpoints de liste renvoient des tableaux vides au lieu d'un 403 (le piège documenté par la skill dolibarr). Le révoquer aurait silencieusement aveuglé toutes les lectures, sans la moindre erreur pour le signaler.

C'est précisément pourquoi l'audit lit les libellés sur /user/perms.php plutôt que de faire confiance à des ids en dur. Le socle READ_ONLY est désormais la surface de lecture auditée (35 droits), pas une supposition.

🤖 Generated with Claude Code

https://claude.ai/code/session_01VRShc4QhLLU73FLHx9vskh

Migration appliquée en production, dans le seul ordre qui ne casse pas le promote. **1.** Création d'`ai_agent_prod_prod_write` (id=5) avec le scope étroit `prod-write` — factures, règlements, et soumission de documents (nécessaire à `builddoc` pour régénérer le PDF d'une facture modifiée). Vérifié fonctionnellement : lectures OK, `DELETE` sur une facture → **403**. **2.** Pipeline de promote repointé sur la clé de cet utilisateur. Il n'emprunte plus le credential des skills de lecture ; si la clé est absente il meurt en affichant la commande de provisionnement, au lieu de retomber silencieusement sur autre chose. **3.** Révocation de **12 droits d'écriture et de suppression** sur `ai_agent` (id=3), le credential que détiennent toutes les skills de lecture : créer/modifier factures clients **et fournisseur**, tiers, contacts, coordonnées de paiement, propositions, exports, liens comptables — et **supprimer** propositions, événements, et documents de la GED. **Vérification après migration** — factures, tiers, contacts, produits, propositions, factures fournisseur et comptes bancaires se lisent toujours ; créer une facture renvoie `403 Forbidden: Insuffisant rights`. La doctrine et la réalité coïncident enfin. ## Une correction qui valait le détour `262` n'est **pas** « créer/modifier les produits » comme mon premier catalogue le supposait, mais l'extension ACL **`voir_tous`** — un droit de **lecture** dont les skills dépendent : sans lui, les endpoints de liste renvoient des tableaux vides au lieu d'un 403 (le piège documenté par la skill `dolibarr`). Le révoquer aurait silencieusement aveuglé toutes les lectures, sans la moindre erreur pour le signaler. C'est précisément pourquoi l'audit lit les libellés sur `/user/perms.php` plutôt que de faire confiance à des ids en dur. Le socle `READ_ONLY` est désormais la surface de lecture **auditée** (35 droits), pas une supposition. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01VRShc4QhLLU73FLHx9vskh
arcodange added 1 commit 2026-08-09 19:33:19 +02:00
Migration applied to production, in the only order that does not break the
promote flow:

1. Created `ai_agent_prod_prod_write` (id=5) with the narrow `prod-write` scope —
   invoices, payments, and document submission (needed by builddoc to regenerate
   a modified invoice's PDF). Verified functionally: reads pass, DELETE on an
   invoice returns 403.
2. Repointed the promote pipeline at that user's key. It no longer borrows the
   read skills' credential; if the key is absent it dies with the provisioning
   command rather than silently falling back.
3. Revoked 12 write/delete rights from `ai_agent` (id=3), the credential every
   read skill holds: create/modify on customer AND supplier invoices,
   thirdparties, contacts, thirdparty payment details, proposals, exports,
   accounting links — and delete on proposals, events, and GED documents.

Verified after: invoices, thirdparties, contacts, products, proposals, supplier
invoices and bank accounts all still read; creating an invoice returns
`403 Forbidden: Insuffisant rights`. The documented posture and the real one
finally agree.

scopes.ts corrected against the live instance: 262 is NOT "créer/modifier les
produits" as the first catalogue guessed but the `voir_tous` ACL extension — a
READ right the skills depend on (without it, list endpoints return empty arrays
instead of 403). Revoking it would have silently blinded every read skill. This
is why the audit reads labels off /user/perms.php rather than trusting ids in
code. The READ_ONLY baseline is now the audited read surface (35 rights), not a
guess.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01VRShc4QhLLU73FLHx9vskh
arcodange merged commit c6d16d96f9 into main 2026-08-09 19:33:27 +02:00
arcodange deleted branch arcodange/prod-apply 2026-08-09 19:33:28 +02:00
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: arcodange-org/erp#81