mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-10-09 08:22:19 +02:00
fix(security): isolate bearer authorization paths
This commit is contained in:
@@ -7,6 +7,8 @@ from pathlib import Path
|
||||
from types import SimpleNamespace
|
||||
|
||||
from core.models import ChatMessage, Session
|
||||
from core.database import Session as DbSession
|
||||
from tests.helpers.sqlite_db import make_temp_sqlite
|
||||
from src.tool_approval_scopes import (
|
||||
CHAT_SESSION_APPROVAL_CONTEXT_MARKER,
|
||||
ToolApprovalScope,
|
||||
@@ -92,7 +94,7 @@ def test_allow_for_task_bypasses_only_the_resumed_run_gate():
|
||||
assert fresh.decision_for("bash").allowed is False
|
||||
|
||||
|
||||
def test_allow_for_chat_session_applies_to_later_turns_in_only_that_chat():
|
||||
def test_allow_for_chat_session_applies_to_later_turns_in_only_that_chat(monkeypatch):
|
||||
store = ToolApprovalStore()
|
||||
pending = _pending(store, selected_tools=["bash", "manage_skills"])
|
||||
grant = store.consume(
|
||||
@@ -109,6 +111,45 @@ def test_allow_for_chat_session_applies_to_later_turns_in_only_that_chat():
|
||||
assert grant.pending.selected_tools == ("bash", "manage_skills")
|
||||
assert grant.pending.continuation_query.startswith("inspect the project")
|
||||
|
||||
# The browser-shaped resolution card is not enough to create authority.
|
||||
# Persist the separate grant through the same helper used by the route,
|
||||
# backed by a fresh database so reload behavior is real rather than a
|
||||
# monkeypatched history predicate.
|
||||
monkeypatch.delenv("AUTH_ENABLED", raising=False)
|
||||
import core.database as database
|
||||
from src.tool_approval_provenance import create_chat_session_approval_grant
|
||||
|
||||
db_factory, _engine, _tmpfile = make_temp_sqlite(database.Base.metadata)
|
||||
monkeypatch.setattr(database, "SessionLocal", db_factory)
|
||||
db = db_factory()
|
||||
try:
|
||||
db.add(
|
||||
DbSession(
|
||||
id="session-1",
|
||||
name="Chat",
|
||||
endpoint_url="http://example.invalid",
|
||||
model="test",
|
||||
owner="Alice",
|
||||
)
|
||||
)
|
||||
db.commit()
|
||||
finally:
|
||||
db.close()
|
||||
interactive_request = SimpleNamespace(
|
||||
state=SimpleNamespace(
|
||||
api_token=False,
|
||||
current_user="alice",
|
||||
),
|
||||
headers={},
|
||||
)
|
||||
assert create_chat_session_approval_grant(
|
||||
interactive_request,
|
||||
approval=grant,
|
||||
approval_id=pending.approval_id,
|
||||
session_id="session-1",
|
||||
owner="alice",
|
||||
) is True
|
||||
|
||||
resolved_card = pending.public_payload()
|
||||
resolved_card["resolved"] = "approve"
|
||||
resolved_card["approved_by_interactive_session"] = True
|
||||
@@ -125,6 +166,7 @@ def test_allow_for_chat_session_applies_to_later_turns_in_only_that_chat():
|
||||
name="Chat",
|
||||
endpoint_url="http://example.invalid",
|
||||
model="test",
|
||||
owner="Alice",
|
||||
history=history,
|
||||
)
|
||||
|
||||
@@ -137,6 +179,31 @@ def test_allow_for_chat_session_applies_to_later_turns_in_only_that_chat():
|
||||
assert future_turn.approval_gate_bypassed is True
|
||||
assert future_turn.decision_for("bash").allowed is True
|
||||
|
||||
# A fresh in-memory Session object with the same durable id/owner sees the
|
||||
# grant after a simulated reload; the transcript card itself is still only
|
||||
# display metadata.
|
||||
reloaded = Session(
|
||||
id="session-1",
|
||||
name="Reloaded",
|
||||
endpoint_url="http://example.invalid",
|
||||
model="test",
|
||||
owner="alice",
|
||||
history=[ChatMessage("user", "after reload")],
|
||||
)
|
||||
assert reloaded.get_context_messages()[-1]["metadata"][CHAT_SESSION_APPROVAL_CONTEXT_MARKER] is True
|
||||
|
||||
wrong_owner = Session(
|
||||
id="session-1",
|
||||
name="Wrong owner",
|
||||
endpoint_url="http://example.invalid",
|
||||
model="test",
|
||||
owner="bob",
|
||||
history=[ChatMessage("user", "cross-owner")],
|
||||
)
|
||||
assert CHAT_SESSION_APPROVAL_CONTEXT_MARKER not in (
|
||||
wrong_owner.get_context_messages()[-1].get("metadata") or {}
|
||||
)
|
||||
|
||||
# The persisted card is bound to its original chat id, so a fork/copy does
|
||||
# not inherit the grant merely by copying transcript metadata.
|
||||
other_session = Session(
|
||||
|
||||
Reference in New Issue
Block a user