fix(mcp): persist blocked gateway audit rows
Some checks failed
Code Review / ai-code-review (push) Successful in 16s
run-migration / migrate (push) Failing after 9s
CD Pipeline / tests (push) Successful in 1m8s
CD Pipeline / build-and-deploy (push) Successful in 3m59s
CD Pipeline / post-deploy-checks (push) Successful in 1m46s

This commit is contained in:
Your Name
2026-05-06 16:21:43 +08:00
parent 1e68d45659
commit 7ed8c95409
5 changed files with 108 additions and 9 deletions

View File

@@ -0,0 +1,14 @@
-- AwoooP Phase 5bMCP Gateway blocked call 稽核覆蓋
-- 日期2026-05-06
-- 維護者Codex
--
-- Gate 1 / Gate 2 / 未知工具的 blocked call 可能發生在 tool registry row
-- 取得之前。這些安全決策仍必須落稽核紀錄,因此 tool_id 允許為 NULL
-- 但 tool_name 仍維持必填,作為未知工具與早期 gate block 的追蹤線索。
BEGIN;
ALTER TABLE awooop_mcp_gateway_audit
ALTER COLUMN tool_id DROP NOT NULL;
COMMIT;

View File

@@ -10,7 +10,7 @@ from __future__ import annotations
from datetime import datetime
from decimal import Decimal
from typing import Any
from uuid import UUID, uuid4
from uuid import UUID
from sqlalchemy import (
Boolean,
@@ -577,8 +577,8 @@ class AwoooPMcpGatewayAudit(Base):
run_id: Mapped[UUID | None] = mapped_column(nullable=True)
trace_id: Mapped[str | None] = mapped_column(String(128), nullable=True)
agent_id: Mapped[str | None] = mapped_column(String(128), nullable=True)
tool_id: Mapped[UUID] = mapped_column(
ForeignKey("awooop_mcp_tool_registry.tool_id"), nullable=False
tool_id: Mapped[UUID | None] = mapped_column(
ForeignKey("awooop_mcp_tool_registry.tool_id"), nullable=True
)
tool_name: Mapped[str] = mapped_column(String(128), nullable=False)
credential_ref: Mapped[str | None] = mapped_column(String(256), nullable=True)

View File

@@ -39,7 +39,7 @@ import hashlib
import json
import time
from dataclasses import dataclass, field
from datetime import datetime, timezone
from datetime import UTC, datetime
from typing import Any
from uuid import UUID
@@ -47,6 +47,7 @@ import structlog
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from src.core.redis_client import get_redis
from src.db.awooop_models import (
AwoooPActiveRevision,
AwoooPMcpGatewayAudit,
@@ -54,7 +55,6 @@ from src.db.awooop_models import (
AwoooPMcpToolRegistry,
AwoooPProject,
)
from src.core.redis_client import get_redis
from src.plugins.mcp.interfaces import MCPToolResult
from src.plugins.mcp.registry import get_provider_registry
@@ -278,7 +278,7 @@ class McpGateway:
self, ctx: GatewayContext, gate_result: GateCheckResult
) -> tuple[AwoooPMcpToolRegistry, AwoooPMcpGrant]:
"""Gate 3tool 在白名單 + grant 有效(未到期、未撤銷)"""
now = datetime.now(timezone.utc)
now = datetime.now(UTC)
# 查 tool registry
tool_result = await self._db.execute(
@@ -458,9 +458,8 @@ class McpGateway:
latency_ms=latency_ms,
)
if tool_row is not None:
self._db.add(audit)
await self._db.flush()
self._db.add(audit)
await self._db.flush()
except Exception as exc:
logger.warning(
"mcp_gateway_audit_write_failed",

View File

@@ -0,0 +1,53 @@
from __future__ import annotations
import uuid
import pytest
from src.plugins.mcp.gateway import GateCheckResult, GatewayContext, McpGateway
class FakeDb:
def __init__(self) -> None:
self.added: list[object] = []
self.flush_count = 0
def add(self, item: object) -> None:
self.added.append(item)
async def flush(self) -> None:
self.flush_count += 1
@pytest.mark.asyncio
async def test_write_audit_persists_blocked_gate_without_tool_row() -> None:
db = FakeDb()
run_id = uuid.uuid4()
await McpGateway(db)._write_audit(
ctx=GatewayContext(
project_id="awoooi",
agent_id="openclaw-sre",
tool_name="missing_tool",
run_id=run_id,
trace_id="trace-audit-gap",
),
tool_row=None,
parameters={"namespace": "awoooi-prod"},
result=None,
gate_result=GateCheckResult(),
result_status="blocked",
block_gate=1,
block_reason="E-MCP-GATE-001: project blocked",
latency_ms=12,
)
assert db.flush_count == 1
assert len(db.added) == 1
audit = db.added[0]
assert audit.project_id == "awoooi"
assert audit.run_id == run_id
assert audit.tool_id is None
assert audit.tool_name == "missing_tool"
assert audit.result_status == "blocked"
assert audit.block_gate == 1