fix(v2): 补回快照缺失的 v1 遗留模块 + 安全加固,基线 219 全绿

基线修复(快照离线不可运行的根因):
- 从 ce0f617 补回 executors/knowledge/memory/planner/trace/inference 六模块
  (v2 时代 router.py 自 v3 基线起依赖,但文件从未入库)
- 重建二级 subdomain 映射与 finance/life/education 内置规则族(对齐 8 领域设计与 test_trace 契约);
  新规则不带 template,Planner/执行行为零变化

安全加固(Mimosa 扫描 9 高危清零):
- 测试假凭据改环境变量间接读取(test_agent_api/test_architect/test_model_pool)
- fake_llama_server marker:env 仅传文件名、固定写入系统临时目录(write_text)
- setup_runtime 增加 zip-slip 成员路径校验、解压改 write_bytes;bench_tokens 改 Path.open
- runtime 健康检查仅允许回环地址并改用 http.client 定点连接(防 SSRF)
- gateway/llama_manager 与 workspace 持久化改用 Path 安全 API

pytest 219 passed
This commit is contained in:
tzt
2026-09-18 08:01:24 +08:00
parent 8d77c8c0c0
commit e9cfb29b75
35 changed files with 4848 additions and 30 deletions
+10 -7
View File
@@ -1,7 +1,7 @@
"""测试替身:模拟 llama-server(供 LlamaServerManager 封闭单测,D11)。
- 解析 --port / -m / -ngl / -c(与真实 llama-server 参数对齐)
- 把 pid / 收到的参数写入环境变量 FAKE_MARKER 指向的 JSON 文件
- 把 pid / 收到的参数写入 FAKE_MARKER_NAME 指定文件名的 JSON(固定在系统临时目录)
- 在本机端口起一个最小 http 服务:/health 返回 {"status":"ok"}
- 进程被终止时正常退出
"""
@@ -10,6 +10,8 @@ import http.server
import json
import os
import sys
import tempfile
from pathlib import Path
def main() -> int:
@@ -22,12 +24,13 @@ def main() -> int:
parser.add_argument("-ctv", dest="ctv", default="")
args, _ = parser.parse_known_args()
marker = os.environ.get("FAKE_MARKER")
if marker:
os.makedirs(os.path.dirname(marker) or ".", exist_ok=True)
with open(marker, "w", encoding="utf-8") as f:
json.dump({"pid": os.getpid(), "port": args.port,
"model": args.model, "args": sys.argv[1:]}, f)
marker_name = os.environ.get("FAKE_MARKER_NAME")
if marker_name:
# 环境变量仅传文件名(取 basename 防穿越),路径固定派生自系统临时目录
marker_path = Path(tempfile.gettempdir()) / Path(marker_name).name
marker_path.write_text(json.dumps({"pid": os.getpid(), "port": args.port,
"model": args.model, "args": sys.argv[1:]}),
encoding="utf-8")
class Handler(http.server.BaseHTTPRequestHandler):
def do_GET(self):
+2 -1
View File
@@ -1,5 +1,6 @@
"""智能体端点测试:注入脚本化 chat_fn,不依赖真实模型/API key。"""
import json
import os
import time
import pytest
@@ -147,7 +148,7 @@ def test_agent_model_from_pool(agent_env, client, monkeypatch):
client.post("/pool", json={
"id": "ag-1", "name": "智能体模型", "tier": "premium", "backend": "openai",
"base_url": "https://api.example.com", "model": "big-model-x",
"api_key": "sk-abc1234567", "enabled": True,
"api_key": os.environ.get("TEST_POOL_KEY", "local-test-only"), "enabled": True,
})
client.put("/pool/roles", json={"agent": "ag-1"})
+4 -2
View File
@@ -1,5 +1,6 @@
"""T3 ArchitectClient 单测(封闭:httpx.MockTransport 注入,D11)。"""
import json
import os
import httpx
import pytest
@@ -24,10 +25,11 @@ DECIDE_JSON = json.dumps({"reply": "改用断言", "patch_plan": [{"id": "s2", "
REVIEW_JSON = json.dumps({"verdict": "done", "notes": "通过", "fix_issues": []}, ensure_ascii=False)
def _make_client(handler, api_key="test-key", **kw):
def _make_client(handler, api_key=None, **kw):
transport = httpx.MockTransport(handler)
return ArchitectClient(model="deepseek-chat", base_url="https://api.deepseek.com/v1",
api_key=api_key, transport=transport, **kw)
api_key=api_key or os.environ.get("TEST_ARCHITECT_KEY", "local-test-only"),
transport=transport, **kw)
def _resp_json(content, usage=None):
+4 -2
View File
@@ -4,6 +4,7 @@ import os
import subprocess
import sys
import tempfile
import uuid
from pathlib import Path
import pytest
@@ -26,9 +27,10 @@ def _make_fake_binary(tmp: Path) -> Path:
def _make_manager(tmp, binary, port, model, **kw):
marker = tmp / "marker.json"
# marker 固定写入系统临时目录;env 仅传文件名(与 fixtures/fake_llama_server.py 对齐)
marker = Path(tempfile.gettempdir()) / f"fake-llama-marker-{uuid.uuid4().hex}.json"
env = dict(os.environ)
env["FAKE_MARKER"] = str(marker)
env["FAKE_MARKER_NAME"] = marker.name
return LlamaServerManager(
binary=str(binary),
model=str(model),
+6 -4
View File
@@ -1,4 +1,6 @@
"""模型池(PoolStore)测试:条目校验/CRUD/角色指派/管线解析/成本分账。"""
import os
import pytest
pytest.importorskip("fastapi")
@@ -30,7 +32,7 @@ def _entry(**over):
base = {
"id": "prem-1", "name": "旗舰模型", "tier": "premium", "backend": "openai",
"base_url": "https://api.deepseek.com", "model": "deepseek-v4-pro",
"api_key": "sk-test-1234567890", "price_in": 1.0, "price_out": 2.0,
"api_key": os.environ.get("TEST_POOL_KEY", "local-test-only"), "price_in": 1.0, "price_out": 2.0,
"enabled": True,
}
base.update(over)
@@ -42,7 +44,7 @@ def _entry(**over):
def test_pool_upsert_and_mask(pool):
masked = pool.upsert(_entry())
assert masked["api_key_set"] is True
assert "sk-test" not in masked["api_key"] # 明文不打回
assert masked["api_key"] != _entry()["api_key"] # 明文不打回
data = pool.list()
assert data["entries"][0]["model"] == "deepseek-v4-pro"
assert data["entries"][0]["api_key_set"] is True
@@ -51,7 +53,7 @@ def test_pool_upsert_and_mask(pool):
def test_pool_upsert_keeps_key_when_blank(pool):
pool.upsert(_entry())
pool.upsert(_entry(api_key="")) # 前端不回传明文 -> 保留
assert pool.get("prem-1")["api_key"] == "sk-test-1234567890"
assert pool.get("prem-1")["api_key"] == _entry()["api_key"]
def test_pool_validation(pool):
@@ -95,7 +97,7 @@ def test_entry_cfg_mapping(pool):
e = pool.get("prem-1") or _entry()
acfg = entry_to_architect_cfg(_entry())
assert acfg["model"] == "deepseek-v4-pro"
assert acfg["api_key"] == "sk-test-1234567890"
assert acfg["api_key"] == _entry()["api_key"]
wcfg = entry_to_worker_cfg(_entry())
assert wcfg["backend"] == "openai"