test: cover request debug dump redaction

Keep request dump writes on the shared atomic JSON path, add regression coverage for request body/error/stdout redaction, and map the salvaged contributor email for release attribution.
This commit is contained in:
Teknium 2026-06-15 05:19:34 -07:00
parent ad58dd51ac
commit aab2e99bae
4 changed files with 62 additions and 6 deletions

View file

@ -1223,18 +1223,17 @@ def dump_api_request_debug(
# values), and this path fires unconditionally on API errors — so it
# otherwise lands any context-embedded secret in cleartext on disk.
# Run the serialized dump through the same scrubber used for logs/tool
# output. Atomicity preserved via temp-file + Path.replace.
# output, then hand the resulting payload back to the shared atomic
# JSON writer so request dumps keep the same write semantics as before.
from agent.redact import redact_sensitive_text
_serialized = json.dumps(dump_payload, ensure_ascii=False, indent=2, default=str)
_redacted = redact_sensitive_text(_serialized, force=True)
_tmp = dump_file.with_name(dump_file.name + ".tmp")
_tmp.write_text(_redacted, encoding="utf-8")
_tmp.replace(dump_file)
_redacted_payload = json.loads(redact_sensitive_text(_serialized, force=True))
atomic_json_write(dump_file, _redacted_payload, default=str)
agent._vprint(f"{agent.log_prefix}🧾 Request debug dump written to: {dump_file}")
if env_var_enabled("HERMES_DUMP_REQUEST_STDOUT"):
print(_redacted)
print(json.dumps(_redacted_payload, ensure_ascii=False, indent=2, default=str))
return dump_file
except Exception as dump_error:

View file

@ -46,6 +46,7 @@ ACP_REGISTRY_MANIFEST = REPO_ROOT / "acp_registry" / "agent.json"
# Auto-extracted from noreply emails + manual overrides
AUTHOR_MAP = {
"kenmege@yahoo.com": "Kenmege",
"tianying.x@eukarya.io": "xtymac",
"dkobi16@gmail.com": "Diyoncrz18",
"arnaud@nolimitdevelopment.com": "ali-nld",
"sswdarius@gmail.com": "necoweb3",

View file

@ -53,6 +53,10 @@ class TestKnownPrefixes:
result = redact_sensitive_text("fal_abc123def456ghi789jkl")
assert "abc123def456" not in result
def test_notion_internal_integration_token(self):
result = redact_sensitive_text("ntn_abc123def456ghi789jkl")
assert "abc123def456" not in result
def test_short_token_fully_masked(self):
result = redact_sensitive_text("key=sk-short1234567")
assert "***" in result

View file

@ -1913,6 +1913,58 @@ def test_dump_api_request_debug_uses_chat_completions_url(monkeypatch, tmp_path)
assert payload["request"]["url"] == "http://127.0.0.1:9208/v1/chat/completions"
def test_dump_api_request_debug_redacts_request_and_error_secrets(monkeypatch, tmp_path, capsys):
"""Request debug dumps should redact secrets before disk/stdout output."""
import json
_patch_agent_bootstrap(monkeypatch)
monkeypatch.setenv("HERMES_DUMP_REQUEST_STDOUT", "1")
agent = run_agent.AIAgent(
model="gpt-4o",
base_url="http://127.0.0.1:9208/v1",
api_key="sk-ant-providersecret1234567890",
quiet_mode=True,
max_iterations=1,
skip_context_files=True,
skip_memory=True,
)
agent.logs_dir = tmp_path
notion_token = "ntn_abc123def456ghi789jkl"
error_secret = "sk-ant-errorsecret1234567890"
response_secret = "sk-ant-responsesecret1234567890"
response = SimpleNamespace(status_code=400, text=f"provider echoed {response_secret}")
class ProviderError(RuntimeError):
body: object
response: object
error = ProviderError(f"bad token {error_secret}")
error.body = {"message": f"bad token {error_secret}"}
error.response = response
dump_file = agent._dump_api_request_debug(
{
"model": "gpt-4o",
"messages": [{"role": "user", "content": f"use {notion_token}"}],
"metadata": {"NOTION_API_KEY": notion_token},
},
reason="provider_error",
error=error,
)
assert dump_file is not None
dumped_text = dump_file.read_text()
stdout_text = capsys.readouterr().out
for raw in (notion_token, error_secret, response_secret, "providersecret1234567890"):
assert raw not in dumped_text
assert raw not in stdout_text
payload = json.loads(dumped_text)
assert payload["request"]["headers"]["Authorization"].startswith("Bearer sk-ant-p...")
assert "***" in dumped_text or "..." in dumped_text
# --- Reasoning-only response tests (fix for empty content retry loop) ---