chore: import upstream snapshot with attribution
Deploy Site / deploy-vercel (push) Has been skipped
Deploy Site / deploy-docs (push) Has been skipped
Build Skills Index / build-index (push) Has been skipped
CI / Deny unrelated histories (push) Has been skipped
CI / Detect affected areas (push) Successful in 27m35s
CI / OSV scan (push) Failing after 4s
CI / Build&Test Docker image (push) Successful in 9s
CI / Supply-chain scan (push) Has been skipped
CI / Lint Docker scripts (push) Failing after 5m13s
CI / Check contributors (push) Failing after 12m8s
CI / Docs Site (push) Failing after 12m8s
CI / TypeScript (push) Failing after 12m8s
CI / Python lints (push) Failing after 12m9s
CI / Python tests (push) Failing after 12m9s
CI / Check uv.lock (push) Failing after 23m22s
CI / CI timing report (push) Has been cancelled
Build Skills Index / trigger-deploy (push) Has been cancelled
CI / All required checks pass (push) Has been cancelled
Deploy Site / deploy-vercel (push) Has been skipped
Deploy Site / deploy-docs (push) Has been skipped
Build Skills Index / build-index (push) Has been skipped
CI / Deny unrelated histories (push) Has been skipped
CI / Detect affected areas (push) Successful in 27m35s
CI / OSV scan (push) Failing after 4s
CI / Build&Test Docker image (push) Successful in 9s
CI / Supply-chain scan (push) Has been skipped
CI / Lint Docker scripts (push) Failing after 5m13s
CI / Check contributors (push) Failing after 12m8s
CI / Docs Site (push) Failing after 12m8s
CI / TypeScript (push) Failing after 12m8s
CI / Python lints (push) Failing after 12m9s
CI / Python tests (push) Failing after 12m9s
CI / Check uv.lock (push) Failing after 23m22s
CI / CI timing report (push) Has been cancelled
Build Skills Index / trigger-deploy (push) Has been cancelled
CI / All required checks pass (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,60 @@
|
||||
"""Tests for the HTML session export renderer."""
|
||||
|
||||
from hermes_cli.session_export_html import (
|
||||
_generate_messages_html,
|
||||
generate_multi_session_html_export,
|
||||
)
|
||||
|
||||
|
||||
def test_tool_call_name_is_escaped():
|
||||
"""A tool-call name is attacker-influenced (a prompt-injected model can emit
|
||||
an arbitrary name), so it must be HTML-escaped like every sibling field."""
|
||||
payload = '<img src=x onerror="alert(1)">'
|
||||
html = _generate_messages_html([
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": "",
|
||||
"tool_calls": [
|
||||
{
|
||||
"id": "call_1",
|
||||
"type": "function",
|
||||
"function": {"name": payload, "arguments": "{}"},
|
||||
}
|
||||
],
|
||||
}
|
||||
])
|
||||
assert payload not in html
|
||||
assert "<img src=x onerror=" in html
|
||||
|
||||
|
||||
def test_tool_call_arguments_stay_escaped():
|
||||
html = _generate_messages_html([
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": "",
|
||||
"tool_calls": [
|
||||
{
|
||||
"id": "call_1",
|
||||
"type": "function",
|
||||
"function": {"name": "terminal", "arguments": "<b>x</b>"},
|
||||
}
|
||||
],
|
||||
}
|
||||
])
|
||||
assert "<b>x</b>" in html
|
||||
assert "<b>x</b>" not in html
|
||||
|
||||
|
||||
def test_multi_session_export_keeps_switcher_script():
|
||||
"""The multi-session export drives session switching with an inline script,
|
||||
so the escaping fix must not remove or block that script."""
|
||||
sessions = [
|
||||
{"id": "aaaa1111", "title": "First", "started_at": 0,
|
||||
"messages": [{"role": "user", "content": "one"}]},
|
||||
{"id": "bbbb2222", "title": "Second", "started_at": 0,
|
||||
"messages": [{"role": "user", "content": "two"}]},
|
||||
]
|
||||
html = generate_multi_session_html_export(sessions)
|
||||
assert "function showSession" in html
|
||||
assert 'data-id="aaaa1111"' in html
|
||||
assert 'id="view-bbbb2222"' in html
|
||||
Reference in New Issue
Block a user