fix(security): F01 (Astra P0) — KI-Tool-Ausführung ohne Freigabe verhindern
Check Cross-Plugin Imports / check (push) Has been cancelled
Check Cross-Plugin Imports / check (push) Has been cancelled
Vorher: _execute_tool (agent_loop.py) und der KI-Chat-Loop (stream_chat_comm) führten JEDES im Registry registrierte Tool aus, wenn das LLM dessen Namen lieferte — ohne Abgleich mit der angebotenen Liste, ohne required_permission-Check. Reproduktion (Astra): Nur audit_allowed angeboten, Modell nannte audit_restricted (system:admin) → Handler lief. Fix (fail-closed, an ALLEN Ausfuehrungspfaden): - _check_tool_access: (1) Allowlist — nur Tools die dem LLM angeboten wurden duerfen laufen; (2) required_permission gegen die AKTUELLEN User-Rechte (deny-first, Rechteentzug wirkt sofort, ohne Kontext = Ablehnung). Guard vor dry-run/approval/execute-Pfaden. - stream_chat_comm: gleicher Allowlist-Guard vor execute_tool_call. - run_react_loop/agent_runner/agent_stream/agent_routes reichen user_permissions durch (perm_ctx bzw. Session-User). - check_permission: Session-Kontexte tragen denied_permissions statt denied — beide Keys werden gelesen, Deny-Liste wird nie mehr ignoriert. Tests: test_agent_loop.py 18/18 (7 neue F01-Tests nach Astra-Abnahme: nicht angeboten → Handler null; fehlende Permission → abgewiesen; Fail-closed ohne Kontext; Deny-Liste session-shape; Rechteentzug mitten im Lauf wirkt auf naechste Aktion; dry-run guardet auch). ruff clean. Pre-existing-Beweis: permission_system_live-Failures reproduzieren sich ohne diesen Patch identisch (Plugin-Aktivierung in ephemeraler Test-DB, bekanntes Vorbestands-Finding).
This commit is contained in:
@@ -20,7 +20,8 @@ import asyncio
|
||||
import json
|
||||
import logging
|
||||
import uuid
|
||||
from typing import TYPE_CHECKING, Any, AsyncGenerator
|
||||
from collections.abc import AsyncGenerator
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
from app.ai.agent_loop import ReActStep, run_react_loop
|
||||
|
||||
@@ -71,6 +72,7 @@ async def stream_react_loop(
|
||||
max_steps: int = 20,
|
||||
timeout_seconds: int = 300,
|
||||
trace_id: str | None = None,
|
||||
user_permissions: dict[str, Any] | None = None,
|
||||
) -> AsyncGenerator[str, None]:
|
||||
"""Run the ReAct loop and yield SSE-formatted events.
|
||||
|
||||
@@ -116,6 +118,7 @@ async def stream_react_loop(
|
||||
timeout_seconds=timeout_seconds,
|
||||
trace_id=trace_id,
|
||||
on_step=on_step,
|
||||
user_permissions=user_permissions, # F01: enforce at execution time
|
||||
)
|
||||
await queue.put(
|
||||
_sse(
|
||||
|
||||
Reference in New Issue
Block a user