Skip to content

Commit 8047b2c

Browse files
committed
Workspace(fix[lifecycle]): Redact hook command logs
why: Lifecycle hook commands may contain expanded credentials, so normal logs must not persist the command text. what: - Replace hook command log extras with a redacted placeholder - Add coverage that hook failure logging keeps exit codes without leaking command text
1 parent cc3d8b0 commit 8047b2c

2 files changed

Lines changed: 32 additions & 3 deletions

File tree

src/tmuxp/util.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
logger = logging.getLogger(__name__)
2424

2525
PY2 = sys.version_info[0] == 2
26+
_REDACTED_HOOK_COMMAND = "<redacted>"
2627

2728

2829
def run_before_script(
@@ -127,7 +128,9 @@ def run_hook_commands(
127128
if not joined.strip():
128129
return
129130

130-
logger.info("hook commands started", extra={"tmux_hook_cmd": joined})
131+
logger.info(
132+
"hook commands started", extra={"tmux_hook_cmd": _REDACTED_HOOK_COMMAND}
133+
)
131134
try:
132135
result = subprocess.run(
133136
joined,
@@ -140,15 +143,15 @@ def run_hook_commands(
140143
except OSError:
141144
logger.warning(
142145
"hook command failed",
143-
extra={"tmux_hook_cmd": joined},
146+
extra={"tmux_hook_cmd": _REDACTED_HOOK_COMMAND},
144147
)
145148
return
146149

147150
if result.returncode != 0:
148151
logger.warning(
149152
"hook command failed",
150153
extra={
151-
"tmux_hook_cmd": joined,
154+
"tmux_hook_cmd": _REDACTED_HOOK_COMMAND,
152155
"tmux_exit_code": result.returncode,
153156
},
154157
)

tests/test_util.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,32 @@ def test_run_hook_commands_logs_failure(
213213
assert records[0].tmux_exit_code == 7
214214

215215

216+
def test_run_hook_commands_redacts_logged_command(
217+
caplog: pytest.LogCaptureFixture,
218+
) -> None:
219+
"""run_hook_commands() keeps expanded hook commands out of logs."""
220+
secret = "secret-token-for-hook-log"
221+
222+
with caplog.at_level(logging.INFO, logger="tmuxp.util"):
223+
run_hook_commands(f"false # {secret}")
224+
225+
for record in caplog.records:
226+
assert secret not in record.getMessage()
227+
assert secret not in str(record.__dict__)
228+
229+
hook_records = [
230+
record for record in caplog.records if hasattr(record, "tmux_hook_cmd")
231+
]
232+
assert hook_records
233+
assert all(record.tmux_hook_cmd == "<redacted>" for record in hook_records)
234+
235+
failure_records = [
236+
record for record in caplog.records if hasattr(record, "tmux_exit_code")
237+
]
238+
assert len(failure_records) == 1
239+
assert failure_records[0].tmux_exit_code == 1
240+
241+
216242
def test_get_session_should_default_to_local_attached_session(
217243
server: Server,
218244
monkeypatch: pytest.MonkeyPatch,

0 commit comments

Comments
 (0)