mirror of
https://github.com/Ed1s0nZ/CyberStrikeAI.git
synced 2026-05-01 23:35:18 +02:00
Add files via upload
This commit is contained in:
@@ -178,7 +178,6 @@
|
||||
"taskCancelled": "Task cancelled",
|
||||
"unknownTool": "Unknown tool",
|
||||
"einoAgentReplyTitle": "Sub-agent reply",
|
||||
"einoRecoveryTitle": "🔄 Invalid tool JSON · run {{n}}/{{max}} (hint appended)",
|
||||
"einoStreamErrorTitle": "⚠️ Eino stream interrupted ({{agent}})",
|
||||
"einoStreamErrorMessage": "Streaming read failed; the system will retry or terminate according to policy.",
|
||||
"iterationLimitReachedTitle": "⛔ Iteration limit reached",
|
||||
|
||||
@@ -178,7 +178,6 @@
|
||||
"taskCancelled": "任务已取消",
|
||||
"unknownTool": "未知工具",
|
||||
"einoAgentReplyTitle": "子代理回复",
|
||||
"einoRecoveryTitle": "🔄 工具参数无效 · 第 {{n}}/{{max}} 轮(已追加提示)",
|
||||
"einoStreamErrorTitle": "⚠️ Eino 流式中断({{agent}})",
|
||||
"einoStreamErrorMessage": "流式读取异常,系统将按策略重试或结束。",
|
||||
"iterationLimitReachedTitle": "⛔ 达到迭代上限",
|
||||
|
||||
@@ -2226,10 +2226,6 @@ function renderProcessDetails(messageId, processDetails) {
|
||||
itemTitle = agPx + execLine;
|
||||
} else if (eventType === 'eino_agent_reply') {
|
||||
itemTitle = agPx + '💬 ' + (typeof window.t === 'function' ? window.t('chat.einoAgentReplyTitle') : '子代理回复');
|
||||
} else if (eventType === 'eino_recovery') {
|
||||
const ri = data.runIndex != null ? data.runIndex : (data.einoRetry != null ? data.einoRetry + 1 : 1);
|
||||
const mx = data.maxRuns != null ? data.maxRuns : 3;
|
||||
itemTitle = (typeof window.t === 'function' ? window.t('chat.einoRecoveryTitle', { n: ri, max: mx }) : ('🔄 第 ' + ri + '/' + mx + ' 轮(已追加提示)'));
|
||||
} else if (eventType === 'knowledge_retrieval') {
|
||||
itemTitle = '📚 ' + (typeof window.t === 'function' ? window.t('chat.knowledgeRetrieval') : '知识检索');
|
||||
} else if (eventType === 'error') {
|
||||
|
||||
@@ -1133,24 +1133,6 @@ function handleStreamEvent(event, progressElement, progressId,
|
||||
});
|
||||
break;
|
||||
|
||||
case 'eino_recovery': {
|
||||
const d = event.data || {};
|
||||
const runIdx = d.runIndex != null ? d.runIndex : (d.einoRetry != null ? d.einoRetry + 1 : 1);
|
||||
const maxRuns = d.maxRuns != null ? d.maxRuns : 3;
|
||||
const title = typeof window.t === 'function'
|
||||
? window.t('chat.einoRecoveryTitle', { n: runIdx, max: maxRuns })
|
||||
: ('🔄 工具参数无效 · 第 ' + runIdx + '/' + maxRuns + ' 轮(已追加提示)');
|
||||
addTimelineItem(timeline, 'eino_recovery', {
|
||||
title: title,
|
||||
message: event.message || '',
|
||||
data: event.data
|
||||
});
|
||||
// If the backend triggers a recovery run, any "running" tool_call items in this progress
|
||||
// should be closed to avoid being stuck forever.
|
||||
finalizeOutstandingToolCallsForProgress(progressId, 'failed');
|
||||
break;
|
||||
}
|
||||
|
||||
case 'eino_stream_error': {
|
||||
const d = event.data || {};
|
||||
const agent = d.einoAgent ? String(d.einoAgent) : '';
|
||||
@@ -2190,15 +2172,6 @@ function addTimelineItem(timeline, type, options) {
|
||||
if (type === 'progress' && options.message) {
|
||||
item.dataset.progressMessage = options.message;
|
||||
}
|
||||
if (type === 'eino_recovery' && options.data) {
|
||||
const d = options.data;
|
||||
if (d.runIndex != null) {
|
||||
item.dataset.recoveryRunIndex = String(d.runIndex);
|
||||
}
|
||||
if (d.maxRuns != null) {
|
||||
item.dataset.recoveryMaxRuns = String(d.maxRuns);
|
||||
}
|
||||
}
|
||||
if (type === 'tool_calls_detected' && options.data && options.data.count != null) {
|
||||
item.dataset.toolCallsCount = String(options.data.count);
|
||||
}
|
||||
@@ -2309,12 +2282,6 @@ function addTimelineItem(timeline, type, options) {
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
} else if (type === 'eino_recovery' && options.message) {
|
||||
content += `
|
||||
<div class="timeline-item-content timeline-eino-recovery">
|
||||
${escapeHtml(options.message).replace(/\n/g, '<br>')}
|
||||
</div>
|
||||
`;
|
||||
} else if (type === 'cancelled') {
|
||||
const taskCancelledLabel = typeof window.t === 'function' ? window.t('chat.taskCancelled') : '任务已取消';
|
||||
content += `
|
||||
@@ -3197,10 +3164,6 @@ function refreshProgressAndTimelineI18n() {
|
||||
titleSpan.textContent = ap + icon + (success ? _t('chat.toolExecComplete', { name: name }) : _t('chat.toolExecFailed', { name: name }));
|
||||
} else if (type === 'eino_agent_reply') {
|
||||
titleSpan.textContent = ap + '\uD83D\uDCAC ' + _t('chat.einoAgentReplyTitle');
|
||||
} else if (type === 'eino_recovery' && item.dataset.recoveryRunIndex) {
|
||||
const n = parseInt(item.dataset.recoveryRunIndex, 10) || 1;
|
||||
const mx = parseInt(item.dataset.recoveryMaxRuns, 10) || 3;
|
||||
titleSpan.textContent = _t('chat.einoRecoveryTitle', { n: n, max: mx });
|
||||
} else if (type === 'cancelled') {
|
||||
titleSpan.textContent = '\u26D4 ' + _t('chat.taskCancelled');
|
||||
} else if (type === 'progress' && item.dataset.progressMessage !== undefined) {
|
||||
|
||||
@@ -10,6 +10,9 @@ let currentVulnerabilityId = null;
|
||||
let vulnerabilityFilters = {
|
||||
id: '',
|
||||
conversation_id: '',
|
||||
task_id: '',
|
||||
conversation_tag: '',
|
||||
task_tag: '',
|
||||
severity: '',
|
||||
status: ''
|
||||
};
|
||||
@@ -106,6 +109,15 @@ async function loadVulnerabilities(page = null) {
|
||||
if (vulnerabilityFilters.conversation_id) {
|
||||
params.append('conversation_id', vulnerabilityFilters.conversation_id);
|
||||
}
|
||||
if (vulnerabilityFilters.task_id) {
|
||||
params.append('task_id', vulnerabilityFilters.task_id);
|
||||
}
|
||||
if (vulnerabilityFilters.conversation_tag) {
|
||||
params.append('conversation_tag', vulnerabilityFilters.conversation_tag);
|
||||
}
|
||||
if (vulnerabilityFilters.task_tag) {
|
||||
params.append('task_tag', vulnerabilityFilters.task_tag);
|
||||
}
|
||||
if (vulnerabilityFilters.severity) {
|
||||
params.append('severity', vulnerabilityFilters.severity);
|
||||
}
|
||||
@@ -241,6 +253,10 @@ function renderVulnerabilities(vulnerabilities) {
|
||||
${vuln.type ? `<div class="detail-item"><strong>类型:</strong> ${escapeHtml(vuln.type)}</div>` : ''}
|
||||
${vuln.target ? `<div class="detail-item"><strong>目标:</strong> ${escapeHtml(vuln.target)}</div>` : ''}
|
||||
<div class="detail-item"><strong>会话ID:</strong> <code>${escapeHtml(vuln.conversation_id)}</code></div>
|
||||
${vuln.task_id ? `<div class="detail-item"><strong>任务ID:</strong> <code>${escapeHtml(vuln.task_id)}</code></div>` : ''}
|
||||
${vuln.task_queue_id ? `<div class="detail-item"><strong>任务队列ID:</strong> <code>${escapeHtml(vuln.task_queue_id)}</code></div>` : ''}
|
||||
${vuln.conversation_tag ? `<div class="detail-item"><strong>对话标签:</strong> ${escapeHtml(vuln.conversation_tag)}</div>` : ''}
|
||||
${vuln.task_tag ? `<div class="detail-item"><strong>任务标签:</strong> ${escapeHtml(vuln.task_tag)}</div>` : ''}
|
||||
</div>
|
||||
${vuln.proof ? `<div class="vulnerability-proof"><strong>证明:</strong><pre>${escapeHtml(vuln.proof)}</pre></div>` : ''}
|
||||
${vuln.impact ? `<div class="vulnerability-impact"><strong>影响:</strong> ${escapeHtml(vuln.impact)}</div>` : ''}
|
||||
@@ -338,6 +354,8 @@ function showAddVulnerabilityModal() {
|
||||
|
||||
// 清空表单
|
||||
document.getElementById('vulnerability-conversation-id').value = '';
|
||||
document.getElementById('vulnerability-conversation-tag').value = '';
|
||||
document.getElementById('vulnerability-task-tag').value = '';
|
||||
document.getElementById('vulnerability-title').value = '';
|
||||
document.getElementById('vulnerability-description').value = '';
|
||||
document.getElementById('vulnerability-severity').value = '';
|
||||
@@ -363,6 +381,8 @@ async function editVulnerability(id) {
|
||||
|
||||
// 填充表单
|
||||
document.getElementById('vulnerability-conversation-id').value = vuln.conversation_id || '';
|
||||
document.getElementById('vulnerability-conversation-tag').value = vuln.conversation_tag || '';
|
||||
document.getElementById('vulnerability-task-tag').value = vuln.task_tag || '';
|
||||
document.getElementById('vulnerability-title').value = vuln.title || '';
|
||||
document.getElementById('vulnerability-description').value = vuln.description || '';
|
||||
document.getElementById('vulnerability-severity').value = vuln.severity || '';
|
||||
@@ -393,6 +413,8 @@ async function saveVulnerability() {
|
||||
|
||||
const data = {
|
||||
conversation_id: conversationId,
|
||||
conversation_tag: document.getElementById('vulnerability-conversation-tag').value.trim(),
|
||||
task_tag: document.getElementById('vulnerability-task-tag').value.trim(),
|
||||
title: title,
|
||||
description: document.getElementById('vulnerability-description').value.trim(),
|
||||
severity: severity,
|
||||
@@ -472,6 +494,9 @@ function closeVulnerabilityModal() {
|
||||
function filterVulnerabilities() {
|
||||
vulnerabilityFilters.id = document.getElementById('vulnerability-id-filter').value.trim();
|
||||
vulnerabilityFilters.conversation_id = document.getElementById('vulnerability-conversation-filter').value.trim();
|
||||
vulnerabilityFilters.task_id = document.getElementById('vulnerability-task-filter').value.trim();
|
||||
vulnerabilityFilters.conversation_tag = document.getElementById('vulnerability-conversation-tag-filter').value.trim();
|
||||
vulnerabilityFilters.task_tag = document.getElementById('vulnerability-task-tag-filter').value.trim();
|
||||
vulnerabilityFilters.severity = document.getElementById('vulnerability-severity-filter').value;
|
||||
vulnerabilityFilters.status = document.getElementById('vulnerability-status-filter').value;
|
||||
|
||||
@@ -486,12 +511,18 @@ function filterVulnerabilities() {
|
||||
function clearVulnerabilityFilters() {
|
||||
document.getElementById('vulnerability-id-filter').value = '';
|
||||
document.getElementById('vulnerability-conversation-filter').value = '';
|
||||
document.getElementById('vulnerability-task-filter').value = '';
|
||||
document.getElementById('vulnerability-conversation-tag-filter').value = '';
|
||||
document.getElementById('vulnerability-task-tag-filter').value = '';
|
||||
document.getElementById('vulnerability-severity-filter').value = '';
|
||||
document.getElementById('vulnerability-status-filter').value = '';
|
||||
|
||||
vulnerabilityFilters = {
|
||||
id: '',
|
||||
conversation_id: '',
|
||||
task_id: '',
|
||||
conversation_tag: '',
|
||||
task_tag: '',
|
||||
severity: '',
|
||||
status: ''
|
||||
};
|
||||
@@ -565,6 +596,18 @@ function formatVulnerabilityAsMarkdown(vuln) {
|
||||
markdown += `- **目标**: ${vuln.target}\n`;
|
||||
}
|
||||
markdown += `- **会话ID**: \`${vuln.conversation_id}\`\n`;
|
||||
if (vuln.task_id) {
|
||||
markdown += `- **任务ID**: \`${vuln.task_id}\`\n`;
|
||||
}
|
||||
if (vuln.task_queue_id) {
|
||||
markdown += `- **任务队列ID**: \`${vuln.task_queue_id}\`\n`;
|
||||
}
|
||||
if (vuln.conversation_tag) {
|
||||
markdown += `- **对话标签**: ${vuln.conversation_tag}\n`;
|
||||
}
|
||||
if (vuln.task_tag) {
|
||||
markdown += `- **任务标签**: ${vuln.task_tag}\n`;
|
||||
}
|
||||
markdown += `- **创建时间**: ${createdDate}\n`;
|
||||
markdown += `- **更新时间**: ${updatedDate}\n\n`;
|
||||
|
||||
@@ -587,6 +630,58 @@ function formatVulnerabilityAsMarkdown(vuln) {
|
||||
return markdown;
|
||||
}
|
||||
|
||||
function buildVulnerabilityFilterParams() {
|
||||
const params = new URLSearchParams();
|
||||
const keys = ['id', 'conversation_id', 'task_id', 'conversation_tag', 'task_tag', 'severity', 'status'];
|
||||
keys.forEach((k) => {
|
||||
if (vulnerabilityFilters[k]) {
|
||||
params.append(k, vulnerabilityFilters[k]);
|
||||
}
|
||||
});
|
||||
return params;
|
||||
}
|
||||
|
||||
function triggerTextDownload(fileName, content) {
|
||||
const blob = new Blob([content], { type: 'text/markdown;charset=utf-8' });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
link.download = fileName;
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
|
||||
async function exportVulnerabilityReports() {
|
||||
try {
|
||||
const params = buildVulnerabilityFilterParams();
|
||||
params.set('mode', 'summary');
|
||||
params.set('group_by', 'conversation');
|
||||
const response = await apiFetch(`/api/vulnerabilities/export?${params.toString()}`);
|
||||
if (!response.ok) {
|
||||
const error = await response.json().catch(() => ({ error: '导出失败' }));
|
||||
throw new Error(error.error || '导出失败');
|
||||
}
|
||||
const data = await response.json();
|
||||
const files = Array.isArray(data.files) ? data.files : [];
|
||||
if (!files.length) {
|
||||
alert('当前筛选条件下无可导出漏洞');
|
||||
return;
|
||||
}
|
||||
files.forEach((file, idx) => {
|
||||
setTimeout(() => triggerTextDownload(file.filename || `vulnerability-export-${idx + 1}.md`, file.content || ''), idx * 120);
|
||||
});
|
||||
if (files.length > 1) {
|
||||
alert(`已开始下载 ${files.length} 份报告`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('导出漏洞报告失败:', error);
|
||||
alert('导出漏洞报告失败: ' + error.message);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 下载漏洞为Markdown格式
|
||||
async function downloadVulnerabilityAsMarkdown(id, event) {
|
||||
try {
|
||||
|
||||
@@ -2881,17 +2881,6 @@ function runWebshellAiSend(conn, inputEl, sendBtn, messagesContainer) {
|
||||
} else if (_et === 'warning') {
|
||||
appendTimelineItem('warning', '⚠️ ' + (_em || ''), '', _ed);
|
||||
|
||||
// ─── Eino recovery ───
|
||||
} else if (_et === 'eino_recovery') {
|
||||
var runIdx = _ed.runIndex != null ? _ed.runIndex : (_ed.einoRetry != null ? _ed.einoRetry + 1 : 1);
|
||||
var maxRuns = _ed.maxRuns != null ? _ed.maxRuns : 3;
|
||||
var recTitle = wsTOr('chat.einoRecoveryTitle', '') ||
|
||||
('🔄 工具参数无效 · 第 ' + runIdx + '/' + maxRuns + ' 轮(已追加提示)');
|
||||
if (typeof window.t === 'function') {
|
||||
try { recTitle = window.t('chat.einoRecoveryTitle', { n: runIdx, max: maxRuns }); } catch (e) { /* */ }
|
||||
}
|
||||
appendTimelineItem('eino_recovery', recTitle, _em, _ed);
|
||||
|
||||
// ─── Tool calls ───
|
||||
} else if (_et === 'tool_calls_detected' && _ed) {
|
||||
var count = _ed.count || 0;
|
||||
|
||||
@@ -1097,6 +1097,18 @@
|
||||
<span data-i18n="vulnerabilityPage.conversationId">会话ID</span>
|
||||
<input type="text" id="vulnerability-conversation-filter" data-i18n="vulnerabilityPage.filterConversation" data-i18n-attr="placeholder" placeholder="筛选特定会话" />
|
||||
</label>
|
||||
<label>
|
||||
<span>任务ID/队列ID</span>
|
||||
<input type="text" id="vulnerability-task-filter" placeholder="筛选任务ID或队列ID" />
|
||||
</label>
|
||||
<label>
|
||||
<span>对话标签</span>
|
||||
<input type="text" id="vulnerability-conversation-tag-filter" placeholder="筛选对话标签" />
|
||||
</label>
|
||||
<label>
|
||||
<span>任务标签</span>
|
||||
<input type="text" id="vulnerability-task-tag-filter" placeholder="筛选任务标签" />
|
||||
</label>
|
||||
<label>
|
||||
<span data-i18n="vulnerabilityPage.severity">严重程度</span>
|
||||
<select id="vulnerability-severity-filter">
|
||||
@@ -1120,6 +1132,7 @@
|
||||
</label>
|
||||
<button class="btn-secondary" onclick="filterVulnerabilities()" data-i18n="vulnerabilityPage.filter">筛选</button>
|
||||
<button class="btn-secondary" onclick="clearVulnerabilityFilters()" data-i18n="vulnerabilityPage.clear">清除</button>
|
||||
<button class="btn-primary" onclick="exportVulnerabilityReports()">导出报告</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -2599,6 +2612,14 @@
|
||||
<label for="vulnerability-conversation-id"><span data-i18n="vulnerabilityModal.conversationId">会话ID</span> <span style="color: red;">*</span></label>
|
||||
<input type="text" id="vulnerability-conversation-id" data-i18n="vulnerabilityModal.conversationIdPlaceholder" data-i18n-attr="placeholder" placeholder="输入会话ID" required />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="vulnerability-conversation-tag">对话标签</label>
|
||||
<input type="text" id="vulnerability-conversation-tag" placeholder="如:红队演练A、客户A周报" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="vulnerability-task-tag">任务标签</label>
|
||||
<input type="text" id="vulnerability-task-tag" placeholder="如:批量扫描Q2、专项复测" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="vulnerability-title"><span data-i18n="vulnerabilityModal.title">标题</span> <span style="color: red;">*</span></label>
|
||||
<input type="text" id="vulnerability-title" data-i18n="vulnerabilityModal.titlePlaceholder" data-i18n-attr="placeholder" placeholder="漏洞标题" required />
|
||||
|
||||
Reference in New Issue
Block a user