VYPR
Medium severity6.3NVD Advisory· Published Apr 20, 2026· Updated Apr 24, 2026

CVE-2026-6729

CVE-2026-6729

Description

HKUDS OpenHarness prior to PR #159 remediation contains a session key derivation vulnerability that allows authenticated participants in shared chats or threads to hijack other users' sessions by exploiting a shared ohmo session key that lacks sender identity verification. Attackers can reuse another user's conversation state and replace or interrupt their active tasks by colliding into the same session boundary through the shared chat or thread scope.

Affected products

1

Patches

1
3186851c479e

fix(ohmo): isolate shared-chat sessions by sender (#159)

https://github.com/HKUDS/OpenHarnessHinotobiApr 17, 2026via nvd-ref
2 files changed · +71 12
  • ohmo/gateway/router.py+4 3 modified
    @@ -6,15 +6,16 @@
     
     
     def session_key_for_message(message: InboundMessage) -> str:
    -    """Route sessions by chat and thread when available."""
    +    """Route sessions by sender plus chat/thread when available."""
         if message.session_key_override:
             return message.session_key_override
    +    sender_id = str(message.sender_id).strip() or "anonymous"
         thread_id = (
             message.metadata.get("thread_id")
             or message.metadata.get("thread_ts")
             or message.metadata.get("message_thread_id")
         )
         if thread_id:
    -        return f"{message.channel}:{message.chat_id}:{thread_id}"
    -    return f"{message.channel}:{message.chat_id}"
    +        return f"{message.channel}:{message.chat_id}:{thread_id}:{sender_id}"
    +    return f"{message.channel}:{message.chat_id}:{sender_id}"
     
    
  • tests/test_ohmo/test_gateway.py+67 9 modified
    @@ -26,7 +26,7 @@
     from ohmo.workspace import get_gateway_restart_notice_path, initialize_workspace
     
     
    -def test_gateway_router_uses_thread_when_present():
    +def test_gateway_router_uses_thread_and_sender_when_present():
         message = InboundMessage(
             channel="slack",
             sender_id="u1",
    @@ -35,18 +35,39 @@ def test_gateway_router_uses_thread_when_present():
             timestamp=datetime.utcnow(),
             metadata={"thread_ts": "t1"},
         )
    -    assert session_key_for_message(message) == "slack:c1:t1"
    +    assert session_key_for_message(message) == "slack:c1:t1:u1"
     
     
    -def test_gateway_router_falls_back_to_chat_scope():
    +def test_gateway_router_falls_back_to_chat_and_sender_scope():
         message = InboundMessage(
             channel="telegram",
             sender_id="u1",
             chat_id="chat-1",
             content="hello",
             timestamp=datetime.utcnow(),
         )
    -    assert session_key_for_message(message) == "telegram:chat-1"
    +    assert session_key_for_message(message) == "telegram:chat-1:u1"
    +
    +
    +def test_gateway_router_separates_senders_in_same_chat_thread():
    +    first = InboundMessage(
    +        channel="slack",
    +        sender_id="alice",
    +        chat_id="shared-chat",
    +        content="hello",
    +        timestamp=datetime.utcnow(),
    +        metadata={"thread_ts": "thread-1"},
    +    )
    +    second = InboundMessage(
    +        channel="slack",
    +        sender_id="bob",
    +        chat_id="shared-chat",
    +        content="hello",
    +        timestamp=datetime.utcnow(),
    +        metadata={"thread_ts": "thread-1"},
    +    )
    +    assert session_key_for_message(first) == "slack:shared-chat:thread-1:alice"
    +    assert session_key_for_message(second) == "slack:shared-chat:thread-1:bob"
     
     
     def test_gateway_error_formats_claude_refresh_failure():
    @@ -122,18 +143,18 @@ class Result:
     
     
     @pytest.mark.asyncio
    -async def test_runtime_pool_restores_messages_for_session_key(tmp_path, monkeypatch):
    +async def test_runtime_pool_restores_messages_for_sender_scoped_session_key(tmp_path, monkeypatch):
         workspace = tmp_path / ".ohmo-home"
         initialize_workspace(workspace)
         save_session_snapshot(
             cwd=tmp_path,
             workspace=workspace,
             model="gpt-5.4",
             system_prompt="system",
    -        messages=[ConversationMessage.from_user_text("remember me")],
    +        messages=[ConversationMessage.from_user_text("remember alice only")],
             usage=UsageSnapshot(),
             session_id="sess123",
    -        session_key="feishu:chat-1",
    +        session_key="feishu:chat-1:alice",
         )
     
         captured: dict[str, object] = {}
    @@ -152,12 +173,49 @@ async def fake_start_runtime(bundle):
         monkeypatch.setattr("ohmo.gateway.runtime.start_runtime", fake_start_runtime)
     
         pool = OhmoSessionRuntimePool(cwd=tmp_path, workspace=workspace, provider_profile="codex")
    -    bundle = await pool.get_bundle("feishu:chat-1")
    +    bundle = await pool.get_bundle("feishu:chat-1:alice")
     
         assert captured["restore_messages"] is not None
         assert bundle.session_id == "sess123"
     
     
    +@pytest.mark.asyncio
    +async def test_runtime_pool_does_not_restore_other_sender_session_key(tmp_path, monkeypatch):
    +    workspace = tmp_path / ".ohmo-home"
    +    initialize_workspace(workspace)
    +    save_session_snapshot(
    +        cwd=tmp_path,
    +        workspace=workspace,
    +        model="gpt-5.4",
    +        system_prompt="system",
    +        messages=[ConversationMessage.from_user_text("remember alice only")],
    +        usage=UsageSnapshot(),
    +        session_id="sess123",
    +        session_key="feishu:chat-1:alice",
    +    )
    +
    +    captured: dict[str, object] = {}
    +
    +    async def fake_build_runtime(**kwargs):
    +        captured["restore_messages"] = kwargs.get("restore_messages")
    +        return SimpleNamespace(
    +            engine=SimpleNamespace(set_system_prompt=lambda prompt: None, messages=[]),
    +            session_id="newsession",
    +        )
    +
    +    async def fake_start_runtime(bundle):
    +        return None
    +
    +    monkeypatch.setattr("ohmo.gateway.runtime.build_runtime", fake_build_runtime)
    +    monkeypatch.setattr("ohmo.gateway.runtime.start_runtime", fake_start_runtime)
    +
    +    pool = OhmoSessionRuntimePool(cwd=tmp_path, workspace=workspace, provider_profile="codex")
    +    bundle = await pool.get_bundle("feishu:chat-1:bob")
    +
    +    assert captured["restore_messages"] is None
    +    assert bundle.session_id == "newsession"
    +
    +
     @pytest.mark.asyncio
     async def test_runtime_pool_stream_message_emits_progress_and_tool_hint(tmp_path, monkeypatch):
         workspace = tmp_path / ".ohmo-home"
    @@ -689,7 +747,7 @@ async def fake_restart(message, session_key: str) -> None:
             "🔄 正在重启 gateway,马上回来。\n"
             "Restarting the gateway now. I'll be back in a moment."
         )
    -    assert restart_payloads == [("feishu", "c1", "feishu:c1")]
    +    assert restart_payloads == [("feishu", "c1", "feishu:c1:u1")]
     
     
     @pytest.mark.asyncio
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

3

News mentions

0

No linked articles in our index yet.