feat: gatekeeper service + gate de phase (v0.11.0)

Gatekeeper vérifie le statut d'asset avant chaque exécution : privilégié
inconnu → BLOQUÉ (aucun sursis) refusé jusqu'à review + hook d'alerte ;
approuvé → exécutable. Lecture seule autorisée sans asset. Gate de phase
CHLOVA_PHASE (défaut 1, fail-safe) ; assertReadOnlyPhase phase-aware. 7
nouveaux tests.

Palier de risque : reversible (logique de contrôle, n'exécute aucune mutation).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Kantin-Petit
2026-06-23 01:27:35 +02:00
parent 56e948c976
commit 93d93bef0e
5 changed files with 221 additions and 6 deletions
+16 -1
View File
@@ -39,11 +39,26 @@ describe("verrou lecture seule Phase 1", () => {
expect(() => assertReadOnlyPhase(loadConfig(fullEnv()))).not.toThrow();
});
it("refuse PORTAINER_READ_ONLY=false", () => {
it("refuse PORTAINER_READ_ONLY=false en Phase 1", () => {
const env = fullEnv();
env.PORTAINER_READ_ONLY = "false";
expect(() => assertReadOnlyPhase(loadConfig(env))).toThrow(/lecture seule/);
});
it("Phase 2 autorise PORTAINER_READ_ONLY=false", () => {
const env = fullEnv();
env.CHLOVA_PHASE = "2";
env.PORTAINER_READ_ONLY = "false";
const cfg = loadConfig(env);
expect(cfg.phase).toBe(2);
expect(() => assertReadOnlyPhase(cfg)).not.toThrow();
});
it("phase invalide retombe sur 1 (fail-safe)", () => {
const env = fullEnv();
env.CHLOVA_PHASE = "9";
expect(loadConfig(env).phase).toBe(1);
});
});
describe("redactedConfig masque les secrets", () => {