✨ feat: Sandbox Runtime——isolated 插件子进程隔离执行(WP-06/07 收口)

- 迁移 17:plugin_execution(五态、退出码、时长、stdout 摘要、错误留痕)
- 沙箱执行器:独立子进程(代码经 stdin JSON 安全传输,不进命令行)、CPU rlimit + RLIMIT_AS(平台不支持时尽力而为并注明)、墙钟超时强杀、输出限额;runner 仅暴露 input/output 作用域
- 执行端点:isolated_process 形态、已安装且启用后可执行;**未启用沙箱一律 503 fail-closed**(HUNJING_SANDBOX_ENABLED 显式开启,审计记录拒绝原因)——AGENTS 第 6 节"无符合策略的 Sandbox 时拒绝执行"落地
- 每次执行落 plugin_execution + 审计(状态/时长);进程级隔离的边界如实注明(网络/文件系统最小化在 Linux 容器档交付)
- 测试 96 项全绿(+2):echo 真实执行回填、死循环 5s 超时强杀、异常 failed 含 stderr、无沙箱 503
This commit is contained in:
chenxin committed 2026-09-12 13:38:41 +08:00
1 parent 21eacc8c75
commit 99a7789432
8 files changed
+389

No files matched your search

+24
View File
@@ -144,3 +144,27 @@ async def invoke(
db, domain.id, name, body.get("input", {}), body.get("run_id"),
getattr(request.state, "trace_id", None),
)
@inst_router.post("/{installation_id}/executions", status_code=201)
async def execute_plugin(
installation_id: str,
db: DbSession,
user: Annotated[User, Depends(require_state_change)],
request: Request,
body: dict[str, Any],
) -> dict[str, Any]:
"""沙箱执行 isolated 插件:未启用沙箱一律 503(fail-closed)。"""
domain = await _domain_of(db, user)
from app.core.config import get_settings
execution, result = await service.execute_isolated(
db, domain.id, installation_id, body.get("input", {}),
sandbox_enabled=get_settings().sandbox_enabled,
trace_id=getattr(request.state, "trace_id", None),
)
return {
"execution_id": execution.id, "status": result["status"],
"duration_ms": result.get("duration_ms"),
"output": result.get("output"), "error": result.get("error"),
}