fix(sandbox): "Chặn mạng" chặn mọi đường ra mạng, trừ nhà cung cấp AI

Trước đây công tắc chỉ chặn tool mạng của agent; lệnh shell chỉ bị proxy
giả, còn M365, Teams, nút Test, MCP đang chạy, task script, link đính kèm
task, pip tự cài và tài nguyên web trong xem trước HTML vẫn ra mạng tự do.

- Cổng chung application/network/network_guard.py, nối vào cấu hình sống
  ở Composition Root; nhà cung cấp AI (chat, danh sách model, thử model)
  không đi qua cổng này.
- Lệnh shell của agent và task script chạy trong Windows AppContainer
  không có quyền mạng (macOS: sandbox-exec, Linux: unshare --net);
  không cô lập được thì từ chối chạy.
- Không cấp quyền kế thừa của AppContainer lên thư mục chứa PySide6:
  Chromium không nạp được Qt6WebEngineCore.dll và tab Graph bị hỏng.
- Bật chặn thì dừng MCP đang chạy; tool OneDrive đồng bộ trên máy vẫn dùng.
- Mặc định tắt khi mở app lần đầu; nhãn và tooltip 3 ngôn ngữ cập nhật.
- Test: tests/test_network_guard_lanes.py (có bài AppContainer thật).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
minhanhpkpro
2026-09-17 22:32:44 +09:00
co-authored by Claude Opus 5
parent b7a41b3658
commit b78d48320c
28 changed files with 1198 additions and 58 deletions
+1 -13
View File
@@ -19,7 +19,6 @@ in Waiting Input), so executors here run with an auto gate.
"""
from __future__ import annotations
import subprocess
import time
import uuid
from datetime import datetime
@@ -30,6 +29,7 @@ from . import agent_roles
from . import agent_security
from . import projects
from .permissions import PermissionGate
from .task_script import run_script as _run_script
from .tasks import ARTIFACTS_DIR, resolve_input_text
from .tools import ToolContext
@@ -358,18 +358,6 @@ def _run_agent(ctx, task_type: str, prompt: str, out_dir: Path,
return _last_assistant_text(messages), timed_out(), incomplete
def _run_script(command: str, out_dir: Path, timeout_sec: int) -> str:
"""Chạy một task kiểu script bằng shell trong thư mục kết quả, có hạn giờ."""
if not command.strip():
raise RuntimeError("Script task has no command configured.")
proc = subprocess.run(command, shell=True, cwd=str(out_dir),
capture_output=True, text=True, timeout=max(1, timeout_sec))
output = (proc.stdout or "") + (("\n[stderr]\n" + proc.stderr) if proc.stderr else "")
if proc.returncode != 0:
raise RuntimeError(f"Script exited with code {proc.returncode}:\n{output[-2000:]}")
return output
def execute_task(ctx, task: Dict[str, Any], run_id: str,
emit: Optional[EmitFn] = None, cancel: Optional[CancelFn] = None,
tasks_dir: Path = None) -> Dict[str, Any]: