주기 회기 진단과 로그인 폭 보강

This commit is contained in:
Yun Chan 2026-08-12 16:33:59 +09:00
parent 946661926b
commit f97e7fadac
10 changed files with 216 additions and 17 deletions

View file

@ -183,6 +183,75 @@ def config(receipt_path: Path) -> runner.RunnerConfig:
class SafetyContractTests(unittest.TestCase):
def test_failure_diagnostics_redact_known_secrets_and_precede_cleanup(self) -> None:
class DiagnosticFake(FakeController):
def run(self, stage, argv, **kwargs):
result = super().run(stage, argv, **kwargs)
if stage == "compose_ps":
return runner.CommandResult(
0,
'[{"Service":"db","Health":"unhealthy"}]',
"",
0.02,
)
if stage == "compose_logs":
return runner.CommandResult(
0,
"db init failed with owner-secret-value for learner@hs.ac.kr",
(
"api waiting for engine-secret-value at "
"123e4567-e89b-42d3-a456-426614174000?token=visible"
),
0.03,
)
return result
fake = DiagnosticFake()
with tempfile.TemporaryDirectory() as raw:
periodic = runner.PeriodicRunner(config(Path(raw) / "receipt.json"), fake)
periodic.state.runtime = runner.RuntimeIdentity(
"20260812T070000-abcdef12",
"vignette-periodic-11111111-abcdef12",
"periodic:" + "1" * 40 + ":" + "2" * 40 + ":run",
18080,
18443,
15439,
19199,
)
periodic.state.docker_context = "desktop-linux"
periodic.state.env_file = Path(raw) / "stack.env"
periodic.state.override_file = Path(raw) / "override.yml"
periodic.state.stack_attempted = True
proof = periodic._capture_failure_diagnostics(
{
"POSTGRES_PASSWORD": "owner-secret-value",
"ENGINE_GATEWAY_SHARED_SECRET": "engine-secret-value",
"POSTGRES_DB": "vignette_periodic",
}
)
periodic._cleanup()
rendered = json.dumps(proof, ensure_ascii=False)
self.assertTrue(proof["captured"])
self.assertFalse(proof["secret_value_emitted"])
self.assertNotIn("owner-secret-value", rendered)
self.assertNotIn("engine-secret-value", rendered)
self.assertNotIn("learner@hs.ac.kr", rendered)
self.assertNotIn("123e4567-e89b-42d3-a456-426614174000", rendered)
self.assertNotIn("token=visible", rendered)
self.assertIn("<redacted>", rendered)
self.assertIn("<redacted-email>", rendered)
self.assertIn("<redacted-uuid>", rendered)
stages = [stage for stage, _ in fake.commands]
self.assertLess(stages.index("compose_logs"), stages.index("compose_down"))
def test_failure_diagnostic_tail_is_bounded(self) -> None:
sanitized = runner._sanitize_diagnostic_text(
"x" * (runner.DIAGNOSTIC_TAIL_CHARS + 50),
{"SESSION_SECRET": "not-present-secret"},
)
self.assertEqual(len(sanitized), runner.DIAGNOSTIC_TAIL_CHARS)
def test_rejects_every_protected_port_from_argv_and_runtime_env(self) -> None:
for port in sorted(runner.FORBIDDEN_PORTS):
with self.subTest(port=port):