fix: 修复35个安全与功能缺陷,补全知识进化/数字孪生/行为采集模块
## 安全修复 (12项) - Webhook接口添加全局Token认证,过滤敏感请求头 - 修复JWT Base64 padding公式,防止签名验证绕过 - 数据库密码/飞书Token从源码移除,改为环境变量 - 工作流引擎添加路径遍历防护 (_resolve_safe_path) - eval()添加模板长度上限检查 - 审批API添加认证依赖 - 前端v-html增强XSS转义,console.log仅开发模式输出 - 500错误不再暴露内部异常详情 ## Agent运行时修复 (7项) - 删除_inject_knowledge_context中未定义db变量的finally块 - 工具执行添加try/except保护,异常不崩溃Agent - LLM重试计入budget计数器 - self_review异常时passed=False - max_iterations截断标记success=False - 工具参数JSON解析失败时记录警告日志 - run()开始时重置_llm_invocations计数器 ## 配置与基础设施 - DEBUG默认False,SQL_ECHO独立配置项 - init_db()补全13个缺失模型导入 - 新增WEBHOOK_AUTH_TOKEN/SQL_ECHO配置项 - 新增.env.example模板文件 ## 前端修复 (12项) - 登录改用URLSearchParams替代FormData - 401拦截器通过Pinia store统一清理状态 - SSE流超时从60s延长至300s - final/error事件时清除streamTimeout - localStorage聊天记录添加24h TTL - safeParseArgCount替代模板中裸JSON.parse - fetchUser 401时同时清除user对象 ## 新增模块 - 知识进化: knowledge_extractor/retriever/tasks - 数字孪生: shadow_executor/comparison模型 - 行为采集: behavior_middleware/collector/fingerprint_engine - 代码审查: code_review_agent/document_review_agent - 反馈学习: feedback_learner - 瓶颈检测/优化引擎/成本估算/需求估算 - 速率限制器 (rate_limiter) - Alembic迁移 015-020 ## 文档 - 商业化落地计划 - 8篇docs文档 (架构/API/部署/开发/贡献等) - Docker Compose生产配置 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -680,12 +680,22 @@ class WorkflowEngine:
|
||||
if in_degree[neighbor] == 0:
|
||||
queue.append(neighbor)
|
||||
|
||||
# 检查是否有环(只检查可达节点)
|
||||
reachable_nodes = set(result)
|
||||
if len(reachable_nodes) < len(self.nodes):
|
||||
# 有些节点不可达,这是正常的(条件分支)
|
||||
pass
|
||||
|
||||
# 检查是否有环:Kahn 算法结束后仍有非零入度的节点之间存在边 → 环路
|
||||
remaining = [n for n in self.nodes.keys() if n not in result and in_degree.get(n, 0) > 0]
|
||||
if remaining:
|
||||
cycle_nodes = set()
|
||||
for n in remaining:
|
||||
for nb in graph.get(n, []):
|
||||
if nb in remaining:
|
||||
cycle_nodes.add(n)
|
||||
cycle_nodes.add(nb)
|
||||
if cycle_nodes:
|
||||
logger.error("DAG 环路检测: 涉及节点 %s", list(cycle_nodes))
|
||||
raise WorkflowExecutionError(
|
||||
detail=f"工作流存在循环依赖,涉及节点: {list(cycle_nodes)}",
|
||||
)
|
||||
logger.debug("不可达节点(非环路,可能被条件分支排除): %s", remaining)
|
||||
|
||||
self.execution_graph = result
|
||||
return result
|
||||
|
||||
@@ -949,6 +959,27 @@ class WorkflowEngine:
|
||||
|
||||
return result
|
||||
|
||||
def _resolve_safe_path(self, file_path: str) -> str:
|
||||
"""路径遍历防护:解析并验证路径在工作区根目录内。"""
|
||||
import os
|
||||
from pathlib import Path
|
||||
from app.core.config import settings
|
||||
raw_root = (getattr(settings, "LOCAL_FILE_TOOLS_ROOT", None) or "").strip()
|
||||
if raw_root:
|
||||
workspace_root = Path(raw_root).expanduser().resolve()
|
||||
else:
|
||||
workspace_root = Path(__file__).resolve().parent.parent.parent.parent
|
||||
try:
|
||||
p = Path(file_path).expanduser()
|
||||
if not p.is_absolute():
|
||||
p = (workspace_root / p).resolve()
|
||||
else:
|
||||
p = p.resolve()
|
||||
p.relative_to(workspace_root)
|
||||
return str(p)
|
||||
except (ValueError, OSError) as e:
|
||||
raise ValueError(f"路径访问被拒绝: {file_path} (工作区根: {workspace_root})") from e
|
||||
|
||||
def _resolve_llm_prompt_placeholder(self, input_data: Dict[str, Any], var_path: str) -> Any:
|
||||
"""
|
||||
解析 LLM 提示词中的 {{path}}。
|
||||
@@ -1385,8 +1416,11 @@ class WorkflowEngine:
|
||||
merged: Dict[str, Any] = {**root}
|
||||
if isinstance(input_data, dict):
|
||||
merged = {**merged, **input_data}
|
||||
decision = merged.get('__hil_decision')
|
||||
comment = merged.get('__hil_comment')
|
||||
# 使用 per-node 决策键,避免多审批节点冲突
|
||||
decision_key = f'__hil_decision_{node_id}'
|
||||
comment_key = f'__hil_comment_{node_id}'
|
||||
decision = merged.get(decision_key) or merged.get('__hil_decision')
|
||||
comment = merged.get(comment_key) or merged.get('__hil_comment')
|
||||
if decision == 'approved':
|
||||
out = {
|
||||
'approved': True,
|
||||
@@ -2590,6 +2624,8 @@ class WorkflowEngine:
|
||||
|
||||
if file_path:
|
||||
file_path = replace_variables(file_path, input_data)
|
||||
# 路径遍历防护:确保路径在允许的工作区内
|
||||
file_path = self._resolve_safe_path(file_path)
|
||||
if isinstance(content, str):
|
||||
content = replace_variables(content, input_data)
|
||||
|
||||
@@ -3837,6 +3873,9 @@ class WorkflowEngine:
|
||||
logger.info(f"[rjb] Cache节点 {node_id} 执行value模板")
|
||||
logger.info(f"[rjb] value_str前300字符: {value_str[:300]}")
|
||||
logger.info(f"[rjb] user_input: {user_input[:50]}, output: {str(output)[:50]}, timestamp: {timestamp}")
|
||||
# 安全校验:禁止过长的模板字符串,防止资源耗尽
|
||||
if len(value_str) > 10000:
|
||||
raise ValueError(f"模板表达式过长 ({len(value_str)} 字符),拒绝执行")
|
||||
value = eval(value_str, {"__builtins__": {}}, safe_dict)
|
||||
logger.info(f"[rjb] Cache节点 {node_id} value模板执行成功,类型: {type(value)}")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user