Files
session-history/ohm/0026/SESSION-0026.9-TRANSCRIPT-2026-05-28T13-46--2026-05-28T14-09.md
T
Ben Stull 9e6756f678 ohm: migrate orphaned subagent transcripts (0019.x, 0026.x) + renumber dup 0046.0 -> 0073
Backfills transcripts that existed only locally in ohm-infra:
- 0019.1/.2/.3 - UX-polish wave subagent transcripts (driver 0019.0 never finalized)
- 0026.1-.9 - security-audit-0026 subagent transcripts (driver abandoned/closed-out by 0068; audit drove published 0030 remediation)
- 0073.0 - PPE/progressive-delivery + engineering-handbook session, originally drafted as a duplicate 0046.0; reassigned next free number (0072 taken by a concurrent session)

sessions.json: add 0019/0073 titles, update 0026 title.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-05 03:28:59 -07:00

3.4 KiB

SESSION-0026.9 — flotilla audit finding L1 remediation

Parent: SESSION-0026.0-TRANSCRIPT-2026-05-28T13-46--INPROGRESS.md

Mode: REMEDIATION (operator authorized). Subsession of OHM session 0026. Scope: fix flotilla audit finding L1 in ohm-rfc-app-flotilla/ohm_rfc_app_flotilla/deploy.py + a test. No commit, no push, no VERSION bump, no VM/deploy actions.

Finding L1 (recap)

_PhaseRunner.run persisted each phase's detail (which can embed a failed command's stderr[:240]) into the deploys.phases JSON column via deploy_log.append_phase WITHOUT redaction. Secret redaction (_ResolvedSecrets.redact_live) was applied only to the separate error_summary field in _finalize_failure. A secret surfacing in a command's stderr would land unredacted in flotilla state — a §3 invariant 1 / §10.2 leak.

Design choice — how the redactor was threaded

  • Added an OPTIONAL field redactor: _ResolvedSecrets | None = None to the _PhaseRunner dataclass, plus a small _redact(text) helper that calls redactor.redact_live(text) when the redactor is present and falls back to the raw text when it's None.
  • Chose OPTIONAL over REQUIRED because it's the lower-churn option: it preserves the ability to construct a _PhaseRunner without a resolved-secrets context (none of the current tests do, but the nullable style matches the rest of the module's injected-dependency shape), and required no signature changes at any other call site.
  • run_deploy constructs the only production runner and now passes redactor=resolved. Phases run inside the _ResolvedSecrets with/try scope, so the bytearrays are still live when _redact runs during the phase — redaction happens before scrub() zeroes them at scope exit, exactly as the finding requires.
  • Both the success-path detail and the failure-path detail are now redacted before they reach the PhaseRecord (persisted JSON) AND before the on_progress(...) strings that interpolate the detail (those can reach operator terminals/logs).
  • _finalize_failure still redacts the summary it builds from the last phase's detail; since that detail is now already redacted, the second pass is idempotent (no behavior change).

Diff summary

deploy.py:

  • _PhaseRunner: new redactor field + _redact helper; run computes safe_detail = self._redact(...) on both paths and uses it for the PhaseRecord.detail, the on_progress FAILED message, and the returned detail.
  • run_deploy: _PhaseRunner(...) construction now passes redactor=resolved.

tests/test_deploy.py:

  • Extended test_run_deploy_redacts_secret_bytes_from_error_summary to ALSO load the persisted row and assert the failing phase's detail contains [REDACTED] and not the stub secret, and that no phase detail anywhere carries the raw stub bytes.

Test added / extended

Reused the existing redaction test (which drives a real run_deploy with a LeakyFail ssh stub that vomits a fake secret into stderr) and added the deploys.phases assertions. Stub value is the module's STUB_SECRET_BYTES = b"NEVER-LEAK-stub-secret-payload" — a fake, never a real secret.

Test-run result

.venv/bin/pytest tests/ -q → 157 passed in 0.28s. Suite was green before the change (full-suite run, not a known-broken baseline).

Cut state

Code change + test in place. No commit, no push, no VERSION bump, no VM/deploy action. Driver handles release.