Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions backend/app/services/agent_runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,8 @@ async def run(self, db, model_type, name: str, args: dict,
)
return f"权限不足:工具 {name} 需 {allowed_roles} 角色", True
tool_args = dict(args) if isinstance(args, dict) else {}
# tenant 是运行时保留参数,LLM/用户参数无权指定或覆盖。
for reserved in ("tenant", "tenant_id", "tenantId"):
# tenant/creator 是运行时保留参数,LLM/用户参数无权指定或覆盖。
for reserved in ("tenant", "tenant_id", "tenantId", "creator"):
tool_args.pop(reserved, None)
if ctx is not None:
tenant = str(ctx.get("tenant") or "default").strip() or "default"
Expand All @@ -131,6 +131,17 @@ async def run(self, db, model_type, name: str, args: dict,
if not tenant_aware:
return f"工具 {name} 未声明租户隔离能力,已拒绝调用", True
tool_args["tenant"] = tenant
# creator 同理取登录态:防 LLM 幻觉/注入伪造开票人(如 _t_create_ticket)。
username = str(ctx.get("username") or "").strip()
if username:
try:
creator_aware = any(
parameter.name == "creator" for parameter in parameters
)
except (TypeError, ValueError):
creator_aware = False
if creator_aware:
tool_args["creator"] = username
error = False
_tool_t0 = time.perf_counter()
try:
Expand Down
46 changes: 46 additions & 0 deletions tests/test_agent_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,49 @@ def test_registry_flag_on(monkeypatch):
reg = svc.build_default_registry()
assert reg.get("create_ticket") is not None
assert reg.get("submit_ticket") is not None


def test_creator_reserved_injected_from_ctx(monkeypatch):
"""creator 是运行时保留参数:LLM args 里的 creator 被剥掉,注入登录态用户名。"""
from app.services.agent_runtime import Tool, ToolRegistry

seen = {}

async def fake_audit(*args, **kwargs):
return None

async def handler(db, model_type, task="", tenant=None, creator=""):
seen.update(creator=creator, tenant=tenant)
return "ok"

monkeypatch.setattr(
"app.services.agent_tool_audit_service.log_tool_call", fake_audit,
)
registry = ToolRegistry()
registry.register(Tool("fake_create", "d", {}, handler))
result, error = asyncio.run(registry.run(
None, None, "fake_create",
{"task": "x", "creator": "spoofed"},
ctx={"username": "alice", "tenant": "t1"},
))
assert error is False and result == "ok"
assert seen == {"creator": "alice", "tenant": "t1"}


def test_creator_stripped_without_ctx(monkeypatch):
"""无登录态(老链路 ctx=None):creator 一律剥掉,落 handler 默认空串。"""
from app.services.agent_runtime import Tool, ToolRegistry

seen = {}

async def handler(db, model_type, task="", creator=""):
seen.update(creator=creator)
return "ok"

registry = ToolRegistry()
registry.register(Tool("fake_create", "d", {}, handler))
result, error = asyncio.run(registry.run(
None, None, "fake_create", {"task": "x", "creator": "spoofed"},
))
assert error is False and result == "ok"
assert seen == {"creator": ""}
Loading