mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-15 14:22:43 +00:00
fix(export): escape tool-call name in HTML session export
The HTML session export interpolated the tool-call name into the page without escaping, while every sibling field went through _escape_html. A tool-call name is attacker-influenced, so a prompt-injected model can emit a name containing HTML that executes when the export is opened in a browser. Escape the tool-call name like the other fields.
This commit is contained in:
parent
a23d5073fb
commit
1f57ed2a53
2 changed files with 61 additions and 1 deletions
|
|
@ -706,7 +706,7 @@ def _generate_messages_html(messages: List[Dict[str, Any]]) -> str:
|
|||
<div class="tool-call">
|
||||
<div class="tool-call-header">
|
||||
{ICON_CHEVRON_RIGHT.replace('class="', 'class="chevron ')}
|
||||
{ICON_WRENCH} Tool Call: {fn_name}
|
||||
{ICON_WRENCH} Tool Call: {_escape_html(fn_name)}
|
||||
</div>
|
||||
<div class="tool-call-content">
|
||||
<pre><code>{_escape_html(args)}</code></pre>
|
||||
|
|
|
|||
60
tests/hermes_cli/test_session_export_html.py
Normal file
60
tests/hermes_cli/test_session_export_html.py
Normal file
|
|
@ -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
|
||||
Loading…
Add table
Add a link
Reference in a new issue