NK-WP-0056-T19: executor approval exercise; parse approval-engine's stored assurance object

The exercise runs the real request phase, KeyCape client credentials, preflight and consume
against KeyCape's pinned image and approval-engine's source with synthetic keys.

It found that approval-engine stores an entry's assurance as the verified token's JSON object
(KeyCape assuranceClaim), so the executor's comparison with a bare "aal2" refused every real
founder approval. entry_is_aal2 now requires level aal2/aal3 with mfa true and fails closed
otherwise. Fakes use the real shape.

The request-phase tests freeze the clock inside the example admission's window (they had
expired on 2026-11-03T11:00Z).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 523928@bnt-lap001
Assistant-Session: 6c737afb-9c14-4152-9f02-477214b9c350
This commit is contained in:
tegwick 2026-11-03 23:14:29 +01:00
parent 338483f621
commit 625ad652b9
6 changed files with 458 additions and 6 deletions

View file

@ -6,6 +6,8 @@ from __future__ import annotations
import base64
import contextlib
import copy
import datetime as real_dt
import types
import importlib.util
import io
import json
@ -16,6 +18,7 @@ import threading
import unittest
import urllib.parse
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from unittest import mock
import yaml
@ -38,6 +41,18 @@ CLIENT_KV = "platform/workloads/keycape/nk-bind-executor-approval"
APPROVAL_ROLE = "keycape-nk-bind-executor-approval-bounded-executor-workload-kv-read"
class _Frozen(real_dt.datetime):
"""Inside the example admission's window (2026-11-03T09:00Z..11:00Z), so the request phase's window check does
not turn these tests into a time bomb."""
@classmethod
def now(cls, tz=None):
return cls(2026, 11, 3, 9, 30, tzinfo=real_dt.UTC)
FROZEN_DT = types.SimpleNamespace(**{k: getattr(real_dt, k) for k in dir(real_dt) if not k.startswith("__")})
FROZEN_DT.datetime = _Frozen
class World:
def __init__(self):
self.requests = [] # (method, path, headers, body)
@ -117,6 +132,9 @@ def serve(w: World):
class Base(unittest.TestCase):
def setUp(self):
frozen = mock.patch.object(X, "dt", FROZEN_DT)
frozen.start()
self.addCleanup(frozen.stop)
self.w = World()
self.server = serve(self.w)
self.addCleanup(self.server.shutdown)