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:
@@ -95,7 +95,7 @@
|
||||
</div>
|
||||
<div v-for="(tc, j) in msg.tool_calls" :key="j" class="tool-call-item">
|
||||
<span class="tool-name">{{ tc.function?.name || '?' }}</span>
|
||||
<el-tag size="small" type="info">{{ Object.keys(JSON.parse(tc.function?.arguments || '{}')).length }} 个参数</el-tag>
|
||||
<el-tag size="small" type="info">{{ safeParseArgCount(tc.function?.arguments) }} 个参数</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -286,7 +286,8 @@ function saveState() {
|
||||
chatMode: chatMode.value,
|
||||
orchestrateMode: orchestrateMode.value,
|
||||
orchestrateAgents: orchestrateAgents.value,
|
||||
}
|
||||
_savedAt: Date.now(),
|
||||
} as any
|
||||
localStorage.setItem(STORAGE_KEY, JSON.stringify(state))
|
||||
} catch { /* quota exceeded, ignore */ }
|
||||
}
|
||||
@@ -296,6 +297,11 @@ function loadState(): ChatState | null {
|
||||
const raw = localStorage.getItem(STORAGE_KEY)
|
||||
if (!raw) return null
|
||||
const parsed = JSON.parse(raw)
|
||||
// TTL 过期检查:24小时后自动清除聊天记录
|
||||
if (parsed._savedAt && Date.now() - parsed._savedAt > 24 * 3600 * 1000) {
|
||||
localStorage.removeItem(STORAGE_KEY)
|
||||
return null
|
||||
}
|
||||
// 兼容旧格式:旧版本 messages 是数组,迁移为 Record
|
||||
if (Array.isArray(parsed.messages)) {
|
||||
const oldSessionId = parsed.sessionId || ''
|
||||
@@ -442,7 +448,7 @@ async function sendMessage() {
|
||||
let placeholderIdx = -1
|
||||
streamingActive.value = false
|
||||
const abortController = new AbortController()
|
||||
const streamTimeout = setTimeout(() => abortController.abort(), 60000)
|
||||
const streamTimeout = setTimeout(() => abortController.abort(), 300000) // 5分钟超时,适应复杂 Agent 任务
|
||||
try {
|
||||
const token = localStorage.getItem('token') || ''
|
||||
const resp = await fetch(streamEndpoint, {
|
||||
@@ -526,6 +532,7 @@ async function sendMessage() {
|
||||
approvalArgs.value = data.args || {}
|
||||
showApprovalDialog.value = true
|
||||
} else if (eventType === 'final') {
|
||||
clearTimeout(streamTimeout)
|
||||
currentMsg.content = data.content || ''
|
||||
currentMsg.iterations = data.iterations_used || 0
|
||||
currentMsg.tool_calls_made = data.tool_calls_made || 0
|
||||
@@ -535,6 +542,7 @@ async function sendMessage() {
|
||||
streamingActive.value = false
|
||||
loading.value = false
|
||||
} else if (eventType === 'error') {
|
||||
clearTimeout(streamTimeout)
|
||||
currentMsg.content = data.content || ''
|
||||
currentMsg.status = 'error'
|
||||
streamingActive.value = false
|
||||
@@ -598,6 +606,13 @@ function clearChat() {
|
||||
}
|
||||
function scrollToBottom() { nextTick(() => { if (messagesRef.value) messagesRef.value.scrollTop = messagesRef.value.scrollHeight }) }
|
||||
|
||||
function safeParseArgCount(args?: string): number {
|
||||
if (!args) return 0
|
||||
try {
|
||||
return Object.keys(JSON.parse(args)).length
|
||||
} catch { return 0 }
|
||||
}
|
||||
|
||||
function relativeTime(ts: number): string {
|
||||
const diff = Date.now() - ts
|
||||
if (diff < 60000) return '刚刚'
|
||||
@@ -661,7 +676,9 @@ function retryMessage(idx: number) {
|
||||
|
||||
function renderMarkdown(text: string): string {
|
||||
if (!text) return ''
|
||||
return text.replace(/</g, '<').replace(/>/g, '>')
|
||||
// XSS 防护: 先转义 HTML 特殊字符,再对安全模式做 Markdown 渲染
|
||||
return text
|
||||
.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"')
|
||||
.replace(/```(\w*)\n([\s\S]*?)```/g, '<pre><code class="language-$1">$2</code></pre>')
|
||||
.replace(/`([^`]+)`/g, '<code>$1</code>')
|
||||
.replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>')
|
||||
|
||||
Reference in New Issue
Block a user