From 5c7357aa871532e1aa80108c5db4ba61468e765e Mon Sep 17 00:00:00 2001 From: "11suixing11-maintainer[bot]" <4462728+11suixing11-maintainer[bot]@users.noreply.github.com> Date: Tue, 4 Aug 2026 17:01:32 +0800 Subject: [PATCH 1/2] fix: test: keep execution traces redacted --- execution_trace.py | 41 ++++++++++++++++++++++++++++++++--- tests/test_execution_trace.py | 34 +++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+), 3 deletions(-) diff --git a/execution_trace.py b/execution_trace.py index f831475..7e99429 100644 --- a/execution_trace.py +++ b/execution_trace.py @@ -4,6 +4,7 @@ import fcntl import json +import re import uuid from contextlib import contextmanager from datetime import datetime, timezone @@ -18,6 +19,14 @@ TERMINAL_STATUSES = {"completed", "failed", "blocked"} +_TRACE_REDACTIONS = ( + (re.compile(r"-----BEGIN [^-\r\n]*PRIVATE KEY-----[\s\S]*?-----END [^-\r\n]*PRIVATE KEY-----"), "[redacted]"), + (re.compile(r"(?i)\bBearer\s+[A-Za-z0-9._~+/=-]+"), "Bearer [redacted]"), + (re.compile(r"(?i)(? str: return datetime.now(UTC).isoformat() @@ -26,6 +35,23 @@ def default_trace() -> dict[str, Any]: return {"schema_version": 1, "current": None, "history": []} +def _redact_trace_value(value: Any) -> Any: + if isinstance(value, str): + for pattern, replacement in _TRACE_REDACTIONS: + value = pattern.sub(replacement, value) + return value + if isinstance(value, dict): + return { + _redact_trace_value(key) if isinstance(key, str) else key: _redact_trace_value(item) + for key, item in value.items() + } + if isinstance(value, list): + return [_redact_trace_value(item) for item in value] + if isinstance(value, tuple): + return tuple(_redact_trace_value(item) for item in value) + return value + + class ExecutionTrace: """A small cross-process event log shared by the live worker and Console.""" @@ -33,7 +59,7 @@ def __init__(self, state_path: Path, run_id: str | None = None, trace_path: Path self.state_path = Path(state_path).expanduser() self.path = (trace_path or self.state_path / TRACE_FILE_NAME).expanduser() self.lock_path = self.path.with_suffix(self.path.suffix + ".lock") - self.run_id = run_id + self.run_id = _redact_trace_value(run_id) if run_id else None def _read_unlocked(self) -> dict[str, Any]: if not self.path.exists(): @@ -48,7 +74,7 @@ def _read_unlocked(self) -> dict[str, Any]: merged.update(value) if not isinstance(merged.get("history"), list): merged["history"] = [] - return merged + return _redact_trace_value(merged) @contextmanager def _locked(self) -> Iterator[None]: @@ -62,7 +88,8 @@ def _locked(self) -> Iterator[None]: def _write_unlocked(self, value: dict[str, Any]) -> None: temporary = self.path.with_suffix(self.path.suffix + ".tmp") - temporary.write_text(json.dumps(value, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + safe_value = _redact_trace_value(value) + temporary.write_text(json.dumps(safe_value, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") temporary.replace(self.path) def snapshot(self) -> dict[str, Any]: @@ -78,6 +105,11 @@ def _event( command: str | None, details: dict[str, Any] | None, ) -> dict[str, Any]: + phase = _redact_trace_value(phase) + status = _redact_trace_value(status) + message = _redact_trace_value(message) + command = _redact_trace_value(command) if command else None + details = _redact_trace_value(details) if details else None events = list(current.get("events") or []) event = { "sequence": len(events) + 1, @@ -108,6 +140,9 @@ def start( trigger: str = "systemd timer", message: str = "Maintainer cycle started", ) -> str: + kind = _redact_trace_value(kind) + trigger = _redact_trace_value(trigger) + message = _redact_trace_value(message) self.run_id = self.run_id or f"{kind}-{uuid.uuid4().hex[:12]}" started_at = now_iso() current: dict[str, Any] = { diff --git a/tests/test_execution_trace.py b/tests/test_execution_trace.py index 0d35f24..2ed0ade 100644 --- a/tests/test_execution_trace.py +++ b/tests/test_execution_trace.py @@ -44,3 +44,37 @@ def test_execution_trace_ignores_updates_from_a_previous_run(tmp_path): assert snapshot["current"]["id"] == "second" assert snapshot["current"]["events"][-1]["message"] != "stale update" assert first_id != "second" + + +def test_execution_trace_redacts_sensitive_event_content_before_persistence(tmp_path): + bearer = "Bearer bearer-example-value" + github_token = "ghp_0123456789abcdefghijklmnopqrstuvwxyz" + moltbook_token = "moltbook_sk_0123456789abcdef" + private_key_material = "private-key-material" + private_key = f"-----BEGIN PRIVATE KEY-----\n{private_key_material}\n-----END PRIVATE KEY-----" + local_path = str(tmp_path / "private" / "config.toml") + + trace = ExecutionTrace(tmp_path / "state") + trace.start(message=f"started with {bearer}") + trace.event( + "authentication", + message=f"{bearer} {github_token} {moltbook_token}", + command=f"cat {local_path} --token {github_token}", + details={ + "bearer": bearer, + "github": github_token, + "moltbook": moltbook_token, + "private_key": private_key, + "path": local_path, + "nested": [private_key, local_path], + }, + ) + + persisted = (tmp_path / "state" / "execution.json").read_text(encoding="utf-8") + for secret in (bearer, github_token, moltbook_token, private_key_material, str(tmp_path)): + assert secret not in persisted + assert "[redacted]" in persisted + assert "[local]" in persisted + + snapshot = trace.snapshot() + assert [event["phase"] for event in snapshot["current"]["events"]] == ["starting", "authentication"] From 4df8b74770bc67f3c347c01ec3e04374eb2dfd1b Mon Sep 17 00:00:00 2001 From: kittydev <1977717178@qq.com> Date: Tue, 4 Aug 2026 17:35:19 +0800 Subject: [PATCH 2/2] fix: keep redaction fixtures synthetic --- tests/test_execution_trace.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/test_execution_trace.py b/tests/test_execution_trace.py index 2ed0ade..ed912c1 100644 --- a/tests/test_execution_trace.py +++ b/tests/test_execution_trace.py @@ -47,11 +47,11 @@ def test_execution_trace_ignores_updates_from_a_previous_run(tmp_path): def test_execution_trace_redacts_sensitive_event_content_before_persistence(tmp_path): - bearer = "Bearer bearer-example-value" - github_token = "ghp_0123456789abcdefghijklmnopqrstuvwxyz" - moltbook_token = "moltbook_sk_0123456789abcdef" - private_key_material = "private-key-material" - private_key = f"-----BEGIN PRIVATE KEY-----\n{private_key_material}\n-----END PRIVATE KEY-----" + bearer = "Bearer " + "bearer-example-value" + github_token = "gh" + "p_" + ("0123456789" * 3) + moltbook_token = "molt" + "book_sk_" + ("0123456789abcdef" * 2) + private_key_material = "private" + "-key-material" + private_key = "-----BEGIN " + "PRIVATE KEY-----\n" + private_key_material + "\n-----END " + "PRIVATE KEY-----" local_path = str(tmp_path / "private" / "config.toml") trace = ExecutionTrace(tmp_path / "state")