mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-22 16:25:58 +00:00
fix(slack): truncate inflated original_text in approval/confirm chat.update handlers
Slack re-escapes HTML entities in the interaction payload (< -> <, > -> >, & -> &), inflating the section text past the 3000-char Block Kit limit when the button handler rebuilds updated_blocks for chat.update. The send path already budget-truncates, but the click handlers used the echoed original_text verbatim. Cap it to 3000 chars in both _handle_approval_action and _handle_slash_confirm_action. Fixes #53693
This commit is contained in:
parent
8fc0c086f5
commit
c13af09177
2 changed files with 75 additions and 0 deletions
|
|
@ -4300,6 +4300,11 @@ class SlackAdapter(BasePlatformAdapter):
|
|||
original_text = block.get("text", {}).get("text", "")
|
||||
break
|
||||
|
||||
# Slack re-escapes HTML entities in the interaction payload
|
||||
# (< → <, > → >, & → &), which can inflate the text
|
||||
# past the 3000-char section-block limit on chat.update.
|
||||
original_text = original_text[:3000]
|
||||
|
||||
updated_blocks = [
|
||||
{
|
||||
"type": "section",
|
||||
|
|
@ -4467,6 +4472,11 @@ class SlackAdapter(BasePlatformAdapter):
|
|||
original_text = block.get("text", {}).get("text", "")
|
||||
break
|
||||
|
||||
# Slack re-escapes HTML entities in the interaction payload
|
||||
# (< → <, > → >, & → &), which can inflate the text
|
||||
# past the 3000-char section-block limit on chat.update.
|
||||
original_text = original_text[:3000]
|
||||
|
||||
updated_blocks = [
|
||||
{
|
||||
"type": "section",
|
||||
|
|
|
|||
|
|
@ -275,6 +275,37 @@ class TestSlackApprovalAction:
|
|||
update_kwargs = mock_client.chat_update.call_args[1]
|
||||
assert "Denied by alice" in update_kwargs["text"]
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_truncates_inflated_original_text(self):
|
||||
"""Interaction payload re-escapes HTML entities; text must be capped."""
|
||||
adapter = _make_adapter()
|
||||
_attach_auth_runner(adapter)
|
||||
adapter._approval_resolved["1.2"] = False
|
||||
|
||||
# Simulate Slack re-escaping: original was ~2990 chars, but & → &
|
||||
# etc. inflates it past 3000.
|
||||
inflated_text = "a" * 2990 + "&" * 10 # 2990 + 50 = 3040 chars
|
||||
|
||||
ack = AsyncMock()
|
||||
body = {
|
||||
"message": {"ts": "1.2", "blocks": [
|
||||
{"type": "section", "text": {"type": "mrkdwn", "text": inflated_text}},
|
||||
]},
|
||||
"channel": {"id": "C1"},
|
||||
"user": {"name": "alice", "id": "U_ALICE"},
|
||||
}
|
||||
action = {"action_id": "hermes_approve_once", "value": "session-key"}
|
||||
|
||||
mock_client = adapter._team_clients["T1"]
|
||||
mock_client.chat_update = AsyncMock()
|
||||
|
||||
with patch("tools.approval.resolve_gateway_approval", return_value=1):
|
||||
await adapter._handle_approval_action(ack, body, action)
|
||||
|
||||
update_kwargs = mock_client.chat_update.call_args[1]
|
||||
section_text = update_kwargs["blocks"][0]["text"]["text"]
|
||||
assert len(section_text) <= 3000
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_global_allowlist_blocks_unauthorized_click(self, monkeypatch):
|
||||
adapter = _make_adapter()
|
||||
|
|
@ -410,6 +441,40 @@ class TestSlackSlashConfirmAction:
|
|||
secondary_client.chat_postMessage.assert_awaited_once()
|
||||
adapter._team_clients["T1"].chat_update.assert_not_called()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_truncates_inflated_original_text(self):
|
||||
"""Interaction payload re-escapes HTML entities; text must be capped."""
|
||||
adapter = _make_adapter()
|
||||
_attach_auth_runner(adapter)
|
||||
adapter._approval_resolved["2222.3333"] = False
|
||||
|
||||
# Simulate Slack re-escaping inflating text past 3000 chars.
|
||||
inflated_text = "b" * 2990 + "<" * 10 # 2990 + 40 = 3030 chars
|
||||
|
||||
ack = AsyncMock()
|
||||
body = {
|
||||
"message": {"ts": "2222.3333", "blocks": [
|
||||
{"type": "section", "text": {"type": "mrkdwn", "text": inflated_text}},
|
||||
]},
|
||||
"channel": {"id": "C1"},
|
||||
"user": {"name": "owner", "id": "U_OWNER"},
|
||||
}
|
||||
action = {
|
||||
"action_id": "hermes_confirm_once",
|
||||
"value": "agent:main:slack:group:C1:1111|confirm-1",
|
||||
}
|
||||
|
||||
mock_client = adapter._team_clients["T1"]
|
||||
mock_client.chat_update = AsyncMock()
|
||||
mock_client.chat_postMessage = AsyncMock()
|
||||
|
||||
with patch("tools.slash_confirm.resolve", new=AsyncMock(return_value="ok")):
|
||||
await adapter._handle_slash_confirm_action(ack, body, action)
|
||||
|
||||
update_kwargs = mock_client.chat_update.call_args[1]
|
||||
section_text = update_kwargs["blocks"][0]["text"]["text"]
|
||||
assert len(section_text) <= 3000
|
||||
|
||||
|
||||
# ===========================================================================
|
||||
# _fetch_thread_context
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue