[DTP-Worker 20260802_063519] Sprint 8 · buffer · RBAC SPEC §2 : ancrage de l'encadré « Défense en profondeur » (NOM du détenteur de set_user_permissions · scope · famille) sur l'artefact byte-gaté
Extension du bloc « RBAC fixtures » (points 4 & 5) : la 3e surface prose du fait de sécurité set_user_permissions (SPEC §2 encadré + §6 row) était NON gatée. Le test test_rbac impose scope/famille à tout détenteur mais jamais le NOM du rôle ni la prose §2. Réutilise fx_sup (singleton déjà byte-gaté · pas de doublon d'unicité, #5) comme nom-autorité + src_roles pour scope/famille. Prouvé M6 SILENT-GREEN : rename détenteur dans le contrat + régen artefacts => check_artifacts VERT mais §2 name-gate RED (autorité = artefact, pas cohérence SPEC). 6 morsures adversariales, 6 gates re-verts exit 0. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,59 @@
|
|||||||
# Activity log · 2026-08-02 · Claude Code DTP Worker
|
# Activity log · 2026-08-02 · Claude Code DTP Worker
|
||||||
|
|
||||||
|
## Sprint 8 · buffer · RBAC SPEC §2 — ancrage de l'encadré « Défense en profondeur » (NOM du rôle détenteur de `set_user_permissions` · scope · famille) sur l'artefact byte-gaté
|
||||||
|
|
||||||
|
**Contexte** — Série anti-dérive Sprint 8 (CLAUDE.md #6 « zéro invention »). Le §2
|
||||||
|
« Modèle RBAC » de `05_deliverables_mvp/rbac/RBAC_50_ROLES_SPEC.md` ferme sur un
|
||||||
|
encadré de sécurité « **Défense en profondeur** » : le pouvoir `set_user_permissions`
|
||||||
|
(élévation de privilège) est « **réservé** au SEUL rôle `OTO Plateforme RBAC Admin`
|
||||||
|
(`scope_donnees = groupe`, `famille = plateforme`) — séparation des pouvoirs vérifiée
|
||||||
|
par test automatisé ». La SPEC se réclame donc d'un **fait de sécurité tiré du
|
||||||
|
contrat** `rbac_50_roles.json`, sur **trois attributs** : NOM du détenteur · scope ·
|
||||||
|
famille. Or le test cité (`test_rbac::test_set_user_permissions_reserve_admin`) impose
|
||||||
|
bien scope==groupe ET famille==plateforme à TOUT détenteur — MAIS **jamais le NOM du
|
||||||
|
rôle** (`OTO Plateforme RBAC Admin`) NI cette prose §2. L'**UNICITÉ** (« au SEUL rôle »)
|
||||||
|
et le NOM étaient DÉJÀ gatés côté **artefact** (bloc « RBAC fixtures », singleton
|
||||||
|
`len(fx_sup)==1` depuis `custom_docperm.json` byte-gaté + prose du **README fixtures**
|
||||||
|
« émis que pour le rôle **NAME** ») — mais **PAS la 3ᵉ surface prose, le §2 de la SPEC**.
|
||||||
|
|
||||||
|
**Le « vert trompeur »** — RENOMMER le détenteur dans le contrat (+régénérer
|
||||||
|
fixtures/out + audit 4Big) laisse le §2 citer un rôle **DISPARU** en silence :
|
||||||
|
`check_artifacts` ne voit qu'un **HASH de TAILLE** du doc (agnostique au nom · la SPEC
|
||||||
|
n'est pas un `out/*.json`), les 10 tests restent verts (scope/famille inchangés).
|
||||||
|
**Prouvé (M6 SILENT-GREEN)** : renommer `…RBAC Admin`→`…Securite Admin` dans le
|
||||||
|
contrat + régénérer les 4 générateurs RBAC & l'audit ⇒ **`check_artifacts` VERT**
|
||||||
|
pendant que le nouveau gate mord « §2 nomme ['OTO Plateforme RBAC Admin'] MAIS
|
||||||
|
set_user_permissions réservé à 'OTO Plateforme Securite Admin' » — preuve que
|
||||||
|
l'autorité est l'**ARTEFACT byte-gaté**, pas la cohérence interne de la SPEC.
|
||||||
|
|
||||||
|
**Gate ajouté** (`ci/check_readme_claims.sh`, **extension** du bloc « RBAC fixtures »,
|
||||||
|
points 4 & 5 — pas un bloc neuf) — RÉUTILISE `fx_sup` (le singleton déjà byte-gaté
|
||||||
|
ci-dessus · **zéro re-dérivation de l'unicité**, interdit #5 « jamais de doublon »)
|
||||||
|
comme **NOM-autorité** et `src_roles` (contrat byte-gaté) pour scope/famille. Capte
|
||||||
|
l'encadré `**Défense en profondeur** … vérifiée par test automatisé` (spec_rd
|
||||||
|
whitespace-normalisé) et exige : le pouvoir `set_user_permissions` y est **nommé** ;
|
||||||
|
**(a)** le NOM du détenteur cité (accents/casse neutralisés) == le seul porteur du
|
||||||
|
flag ; **(b)/(c)** les `scope_donnees = …` / `famille = …` cités == attributs du
|
||||||
|
détenteur dans `rbac_50_roles.json` ; **(5)** §6 (row « Séparation des pouvoirs »)
|
||||||
|
cite bien `set_user_permissions` réservé (anti-évaporation). Encadré/row INTROUVABLE =
|
||||||
|
échec (#6). La glose « (gestion des habilitations) » N'est PAS gatée (éditoriale).
|
||||||
|
|
||||||
|
**Note process** (interdit #5) — un premier jet ajoutait un bloc §2 autonome
|
||||||
|
RECALCULANT le singleton depuis `src_roles` : **doublon** de la vérification d'unicité
|
||||||
|
déjà portée par le bloc fixtures (via `custom_docperm.json` byte-gaté, équivalent par
|
||||||
|
`check_artifacts`). Écarté ; les 4 checks réécrits en **extension** du bloc existant,
|
||||||
|
sans ré-affirmer l'invariant d'unicité.
|
||||||
|
|
||||||
|
**Vérif** — 4 checks verts sur l'arbre propre. **6 morsures** adversariales, gate
|
||||||
|
lancé seul, reverts par `cp`/`tar` ciblés (jamais `git checkout` large — cf. incident
|
||||||
|
consigné) : **M1** §2 renomme le détenteur (prose) ⇒ NOM ≠ artefact · **M2** §2
|
||||||
|
scope `groupe`→`entite` ⇒ (b) ≠ détenteur · **M3** §2 famille `plateforme`→`ventes`
|
||||||
|
⇒ (c) ≠ détenteur · **M4** encadré supprimé ⇒ INTROUVABLE · **M5** §6 retire
|
||||||
|
`set_user_permissions` ⇒ ancre évaporée · **M6 SILENT-GREEN** (ci-dessus) contrat
|
||||||
|
renommé + artefacts régénérés, `check_artifacts` VERT ⇒ §2 (a) mord. Suite complète
|
||||||
|
re-verte : `check_artifacts` · `check_docs` · `guard_constraints` ·
|
||||||
|
`check_ci_integrity` · `check_regression` · `check_readme_claims` tous exit 0.
|
||||||
|
|
||||||
## Sprint 8 · buffer · Traçabilité roadmap — ancrage de la CITATION `roadmap_ref` VERBATIM (n° ligne + libellé) des artefacts sur la ligne RÉELLE de la roadmap
|
## Sprint 8 · buffer · Traçabilité roadmap — ancrage de la CITATION `roadmap_ref` VERBATIM (n° ligne + libellé) des artefacts sur la ligne RÉELLE de la roadmap
|
||||||
|
|
||||||
**Contexte** — Série anti-dérive Sprint 8 (CLAUDE.md #6 « zéro invention · toujours
|
**Contexte** — Série anti-dérive Sprint 8 (CLAUDE.md #6 « zéro invention · toujours
|
||||||
|
|||||||
@@ -4516,6 +4516,72 @@ if fx_cdt is not None:
|
|||||||
(f" · en trop={extra6}" if extra6 else "")
|
(f" · en trop={extra6}" if extra6 else "")
|
||||||
bad(f"RBAC fixtures · {SPEC_RD} §6 — énumère {sorted(got6)} MAIS "
|
bad(f"RBAC fixtures · {SPEC_RD} §6 — énumère {sorted(got6)} MAIS "
|
||||||
f"MANIFEST {sorted(exp6)}{detail6} → régénérer la SPEC")
|
f"MANIFEST {sorted(exp6)}{detail6} → régénérer la SPEC")
|
||||||
|
# 4) RBAC_50_ROLES_SPEC.md §2 — encadré « Défense en profondeur » : 3ᵉ surface prose
|
||||||
|
# du MÊME fait de sécurité. Les points (2)/(3) ancrent la prose du README fixtures
|
||||||
|
# (« émis que pour le rôle **NAME** ») + le catalogue §6 ; le §2 « Modèle RBAC »
|
||||||
|
# ferme sur « **réservé** au seul rôle `<NAME>` (`scope_donnees = groupe`,
|
||||||
|
# `famille = plateforme`) — séparation des pouvoirs vérifiée par test automatisé » :
|
||||||
|
# TROIS attributs (NOM · scope · famille) qu'AUCUN gate ne touchait. Le test
|
||||||
|
# `test_rbac::test_set_user_permissions_reserve_admin` impose scope/famille à TOUT
|
||||||
|
# détenteur mais JAMAIS le NOM du rôle ni cette prose §2 → renommer le détenteur
|
||||||
|
# (contrat + régénération fixtures/out + audit 4Big) laisse le §2 citer un rôle
|
||||||
|
# DISPARU, VERT (check_artifacts ne voit qu'un HASH de TAILLE du doc, la SPEC n'est
|
||||||
|
# pas un out/*.json). On RÉUTILISE fx_sup (singleton déjà byte-gaté ci-dessus · zéro
|
||||||
|
# re-dérivation de l'unicité #5) comme NOM-autorité et src_roles (contrat byte-gaté)
|
||||||
|
# pour scope/famille. La glose « (gestion des habilitations) » N'est PAS gatée.
|
||||||
|
if spec_rd is not None and len(fx_sup) == 1:
|
||||||
|
_su_hn = fx_sup[0]
|
||||||
|
_su_role = next((r for r in (src_roles or [])
|
||||||
|
if r.get("erpnext_role_name") == _su_hn), None)
|
||||||
|
_su_def = re.search(r"\*\*Défense en profondeur\*\*(.*?)"
|
||||||
|
r"vérifiée par test automatis", spec_rd)
|
||||||
|
if _su_def is None:
|
||||||
|
bad(f"RBAC fixtures · {SPEC_RD} §2 — encadré « **Défense en profondeur** … "
|
||||||
|
f"vérifiée par test automatisé » INTROUVABLE (anti-évaporation #6)")
|
||||||
|
else:
|
||||||
|
_su_reg = _su_def.group(1)
|
||||||
|
_su_toks = re.findall(r"`([^`]+)`", _su_reg)
|
||||||
|
# ancrage : le pouvoir lui-même doit être nommé dans l'encadré
|
||||||
|
if "set_user_permissions" not in _su_toks:
|
||||||
|
bad(f"RBAC fixtures · {SPEC_RD} §2 — encadré ne nomme pas "
|
||||||
|
f"`set_user_permissions` → mal ancré")
|
||||||
|
# (a) NOM du détenteur cité == le seul porteur du flag (byte-gaté)
|
||||||
|
if _fx_acc(_su_hn) in {_fx_acc(t) for t in _su_toks}:
|
||||||
|
good(f"RBAC fixtures · {SPEC_RD} §2 — détenteur nommé « {_su_hn} » == le "
|
||||||
|
f"seul porteur de set_user_permissions (custom_docperm.json)")
|
||||||
|
else:
|
||||||
|
_su_named = [t for t in _su_toks
|
||||||
|
if "=" not in t and t != "set_user_permissions"]
|
||||||
|
bad(f"RBAC fixtures · {SPEC_RD} §2 — nomme {_su_named} MAIS "
|
||||||
|
f"set_user_permissions réservé à {_su_hn!r} → régénérer la SPEC "
|
||||||
|
f"(rôle renommé côté contrat ?)")
|
||||||
|
# (b)/(c) scope + famille cités == attributs du détenteur (contrat byte-gaté)
|
||||||
|
if _su_role is None:
|
||||||
|
bad(f"RBAC fixtures · {SPEC_RD} §2 — détenteur {_su_hn!r} INTROUVABLE dans "
|
||||||
|
f"rbac_50_roles.json (fixtures ↔ contrat désynchronisés)")
|
||||||
|
else:
|
||||||
|
for _su_lbl in ("scope_donnees", "famille"):
|
||||||
|
_su_m = re.search(rf"{_su_lbl}\s*=\s*(\w+)", _su_reg)
|
||||||
|
_su_want = _su_role.get(_su_lbl)
|
||||||
|
if _su_m is None:
|
||||||
|
bad(f"RBAC fixtures · {SPEC_RD} §2 — `{_su_lbl} = …` INTROUVABLE "
|
||||||
|
f"dans l'encadré")
|
||||||
|
elif _su_m.group(1) == _su_want:
|
||||||
|
good(f"RBAC fixtures · {SPEC_RD} §2 — {_su_lbl} `{_su_m.group(1)}` "
|
||||||
|
f"== détenteur (rbac_50_roles.json)")
|
||||||
|
else:
|
||||||
|
bad(f"RBAC fixtures · {SPEC_RD} §2 — encadré dit {_su_lbl}="
|
||||||
|
f"`{_su_m.group(1)}` MAIS le détenteur {_su_hn!r} a "
|
||||||
|
f"`{_su_want}` → aligner sur le contrat")
|
||||||
|
# 5) §6 row « Séparation des pouvoirs » cite le pouvoir réservé (ancre présente)
|
||||||
|
_su_m6 = re.search(r"## 6\..*?(?=## 7\.)", spec_rd)
|
||||||
|
if _su_m6 and "Séparation des pouvoirs" in _su_m6.group(0) \
|
||||||
|
and "set_user_permissions" in _su_m6.group(0):
|
||||||
|
good(f"RBAC fixtures · {SPEC_RD} §6 — row « Séparation des pouvoirs » cite "
|
||||||
|
f"bien `set_user_permissions` réservé (ancre présente)")
|
||||||
|
else:
|
||||||
|
bad(f"RBAC fixtures · {SPEC_RD} §6 — row « Séparation des pouvoirs » / "
|
||||||
|
f"`set_user_permissions` réservé INTROUVABLE → ancre évaporée (#6)")
|
||||||
|
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
# SEO · la COMPOSITION du graphe schema.org (JSON-LD) — un nœud racine
|
# SEO · la COMPOSITION du graphe schema.org (JSON-LD) — un nœud racine
|
||||||
|
|||||||
Reference in New Issue
Block a user