[DTP-Worker] Sprint 8 · buffer · Legal/CONFOTUR : cross-cohérence PERMISSIONS répliquée dans la FICHE AGENT 03_agents/onapi_legal/AGENT.md:43-45 — la MÊME table role→portail→actions que le README du module (ventes-confotur (Ventes)→read/write/create/print · legal-onapi (Direction)→read/write/create · legal-directeur (Direction)→read/write/**submit**/report), annoncée « **mot pour mot** les permissions_cibles RBAC des 3 rôles », mais sous une convention DIFFÉRENTE ( / espacé · **submit** en gras) et gatée par RIEN. Le bloc « Legal confotur perms · cross-cohérence » existant ne gate QUE la §Permissions du README du MODULE (legal/confotur/README.md), AVEUGLE à la fiche. Piège #6 : PROMOUVOIR ventes-confotur à submit (élévation de privilège · casse la séparation des pouvoirs dont is_submittable est déduit), RETIRER une action, RÉAFFECTER un portail, RENOMMER un rôle ou AJOUTER une ligne fantôme dans la FICHE la laisse périmée pendant que l'artefact (et le README, lui gaté) disent autre chose → l'agent ONAPI/Legal lirait sa PROPRE doc d'identité mentant sur le jeu de permissions qu'il porte (le risque même que « mot pour mot » promet d'écarter) — « vert trompeur » qu'aucune suite tests/ (FONCTIONS de mapping RBAC, jamais la prose de fiche) n'attrape. Classe explicitement notée en mémoire (agent-fiche-role-attrs-ungated) : une fiche peut restituer la MÊME donnée data-derived qu'un README déjà gaté, sur une surface distincte. Gate ajouté (bloc « cross-cohérence PERMISSIONS répliquée dans la FICHE AGENT ») : RÉUTILISE conf_perm_by_id/conf_portail_by_id déjà dérivés du MANIFEST par le bloc README (ZÉRO duplication du contrat #6) ; regex tolérant / et ** ; par rôle portail (casse normalisée) ET jeu d'actions exigés EXACTS (set-diff absent ET en trop) ; identité d'ensemble fiche⇔MANIFEST (aucun rôle fantôme/manquant). 6 morsures vérifiées (fiche promeut ventes-confotur→submit · fiche retire create de legal-onapi · fiche réaffecte portail Ventes→Direction · fiche renomme legal-directeur→legal-boss ligne INTROUVABLE+fantôme · sous-liste entière supprimée · artefact promeut submit fiche stale = le vrai silent green), restauré vert, 7 gates re-verts.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1392,6 +1392,82 @@ if conf_roles:
|
||||
bad(f"Legal confotur perms · {CONF_RD2} — rôles énumérés {sorted(rd_ids)} MAIS "
|
||||
f"MANIFEST {sorted(exp_ids)}{detail} → régénérer le README")
|
||||
|
||||
# ============================================================================
|
||||
# Legal · CONFOTUR — cross-cohérence PERMISSIONS répliquée dans la FICHE AGENT
|
||||
# (3e surface) : la MÊME table role→portail→actions, transcrite À LA MAIN dans
|
||||
# 03_agents/onapi_legal/AGENT.md:43-45 — jamais gatée.
|
||||
# ----------------------------------------------------------------------------
|
||||
# Le bloc ci-dessus gate la §Permissions du README du MODULE (legal/confotur/
|
||||
# README.md). Mais la FICHE de l'agent ONAPI/Legal RECOPIE la MÊME donnée
|
||||
# sécurité — annoncée « **mot pour mot** les `permissions_cibles` RBAC des 3
|
||||
# rôles » — sous une convention DIFFÉRENTE (` / ` espacé · `**submit**` en gras) :
|
||||
# - `ventes-confotur` (portail Ventes) → read / write / create / print
|
||||
# - `legal-onapi` (portail Direction) → read / write / create
|
||||
# - `legal-directeur` (portail Direction) → read / write / **submit** / report
|
||||
# Ces trois lignes sont DATA-DERIVED du MÊME out/MANIFEST.json (`roles_rbac_
|
||||
# utilises[]` : role_id · portail · `permissions`, recomputé du contrat rbac_50_
|
||||
# roles.json à chaque build · byte-gaté par check_artifacts) que le README. Le
|
||||
# gate README est AVEUGLE à la fiche : PROMOUVOIR `ventes-confotur` à `submit`
|
||||
# ici (élévation de privilège · casse la séparation des pouvoirs), RETIRER une
|
||||
# action, RÉAFFECTER un portail, RENOMMER un rôle ou AJOUTER une ligne fantôme
|
||||
# laisse la FICHE périmée pendant que l'artefact (et le README, lui gaté) disent
|
||||
# autre chose → l'agent ONAPI/Legal lirait sa PROPRE doc d'identité mentant sur
|
||||
# le jeu de permissions qu'il porte (le risque même que « mot pour mot » promet
|
||||
# d'écarter) — « vert trompeur » qu'aucune suite tests/ (FONCTIONS de mapping
|
||||
# RBAC, jamais la prose de fiche) n'attrape. Même patron d'IDENTITÉ que les
|
||||
# fiches Mobile/Faisabilité (attributs de rôle) ou la citation #10 (constantes),
|
||||
# appliqué à la SURFACE DISTINCTE qu'est la §Permissions de cette fiche. On
|
||||
# RECOMPUTE chaque jeu d'actions + portail depuis conf_perm_by_id/conf_portail_
|
||||
# by_id (déjà dérivés du MANIFEST ci-dessus · ZÉRO duplication du contrat #6) et
|
||||
# on exige que la fiche l'encode EXACTEMENT (set-diff : absent ET en trop · gras
|
||||
# `**` et casse normalisés) + l'identité d'ensemble (fiche ⇔ MANIFEST, aucun
|
||||
# rôle fantôme ni manquant). Un claim absent échoue AUSSI (traçabilité).
|
||||
# ============================================================================
|
||||
CONF_FICHE = "03_agents/onapi_legal/AGENT.md"
|
||||
if conf_roles:
|
||||
conf_fiche = read_norm(CONF_FICHE)
|
||||
if conf_fiche is None:
|
||||
bad(f"Legal confotur fiche · {CONF_FICHE} illisible")
|
||||
else:
|
||||
# une action : mot, éventuellement encadré de `**` (gras), séparé par ` / `.
|
||||
_ACT = r"(?:\*{0,2}\w+\*{0,2}\s*/\s*)*\*{0,2}\w+\*{0,2}"
|
||||
for rid in sorted(conf_perm_by_id):
|
||||
exp_perms = conf_perm_by_id[rid]
|
||||
exp_portail = conf_portail_by_id[rid]
|
||||
pat = r"`" + re.escape(rid) + r"` \(portail ([^)]+)\) → (" + _ACT + r")"
|
||||
m = re.search(pat, conf_fiche)
|
||||
if m is None:
|
||||
bad(f"Legal confotur fiche · {CONF_FICHE} — ligne du rôle `{rid}` INTROUVABLE "
|
||||
f"(attendu portail {exp_portail} · {sorted(exp_perms)})")
|
||||
continue
|
||||
got_portail = m.group(1).strip().lower()
|
||||
got_perms = {t.replace("*", "").strip().lower()
|
||||
for t in m.group(2).split("/") if t.replace("*", "").strip()}
|
||||
if got_portail != exp_portail:
|
||||
bad(f"Legal confotur fiche · `{rid}` — {CONF_FICHE} dit portail "
|
||||
f"« {m.group(1).strip()} » MAIS MANIFEST dit « {exp_portail} » → régénérer")
|
||||
elif got_perms != exp_perms:
|
||||
miss = sorted(exp_perms - got_perms); extra = sorted(got_perms - exp_perms)
|
||||
detail = (f" · absents={miss}" if miss else "") + \
|
||||
(f" · en trop={extra}" if extra else "")
|
||||
bad(f"Legal confotur fiche · `{rid}` — {CONF_FICHE} énumère {sorted(got_perms)} "
|
||||
f"MAIS MANIFEST {sorted(exp_perms)}{detail} → régénérer la fiche")
|
||||
else:
|
||||
good(f"Legal confotur fiche · `{rid}` — portail {got_portail} · actions "
|
||||
f"{sorted(got_perms)} == MANIFEST")
|
||||
# identité d'ensemble : fiche ⇔ MANIFEST (aucun rôle fantôme NI manquant).
|
||||
fiche_ids = set(re.findall(r"`([a-z][a-z-]+)` \(portail [^)]+\) →", conf_fiche))
|
||||
exp_ids = set(conf_perm_by_id)
|
||||
if fiche_ids == exp_ids:
|
||||
good(f"Legal confotur fiche · identité d'ensemble — {sorted(fiche_ids)} == "
|
||||
f"roles_rbac_utilises (aucun rôle fantôme/manquant)")
|
||||
else:
|
||||
miss = sorted(exp_ids - fiche_ids); extra = sorted(fiche_ids - exp_ids)
|
||||
detail = (f" · absents={miss}" if miss else "") + \
|
||||
(f" · en trop (fantôme)={extra}" if extra else "")
|
||||
bad(f"Legal confotur fiche · {CONF_FICHE} — rôles énumérés {sorted(fiche_ids)} MAIS "
|
||||
f"MANIFEST {sorted(exp_ids)}{detail} → régénérer la fiche")
|
||||
|
||||
# ============================================================================
|
||||
# Audit 5D · synthèse de l'audit de conformité (contrôles / dimensions / verdict
|
||||
# pass-fail-à confirmer) — même classe de « vert trompeur », transcrite À LA MAIN
|
||||
|
||||
Reference in New Issue
Block a user