Add files via upload

This commit is contained in:
公明
2026-07-03 10:46:04 +08:00
committed by GitHub
parent 1ff2df68ac
commit 5254ca52fb
5 changed files with 164 additions and 50 deletions
+32
View File
@@ -21370,6 +21370,13 @@ tr.mcp-stats-tool-row[data-tool-name]:focus-visible {
flex: 1;
}
.vulnerability-repro {
display: flex;
flex-direction: column;
gap: 12px;
}
.vulnerability-section,
.vulnerability-proof,
.vulnerability-impact,
.vulnerability-recommendation {
@@ -21381,6 +21388,10 @@ tr.mcp-stats-tool-row[data-tool-name]:focus-visible {
line-height: 1.6;
}
.vulnerability-section:first-child {
margin-top: 0;
}
.vulnerability-proof pre {
margin-top: 8px;
padding: 12px;
@@ -21394,6 +21405,27 @@ tr.mcp-stats-tool-row[data-tool-name]:focus-visible {
word-wrap: break-word;
}
.vulnerability-section-body {
margin-top: 8px;
color: var(--text-primary);
white-space: pre-wrap;
overflow-wrap: anywhere;
}
.vulnerability-section-body--code {
margin-top: 8px;
padding: 12px;
background: var(--bg-primary);
border: 1px solid var(--border-color);
border-radius: 4px;
overflow-x: auto;
font-size: 0.8rem;
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
white-space: pre-wrap;
word-wrap: break-word;
}
.vulnerability-section strong,
.vulnerability-proof strong,
.vulnerability-impact strong,
.vulnerability-recommendation strong {
+23 -11
View File
@@ -2027,7 +2027,7 @@
"exportNoResults": "No vulnerabilities match the current filters",
"exportStarted": "Started downloading {{count}} file(s)",
"exportFailed": "Export failed",
"saveRequiredFields": "Please fill in conversation ID, title, and severity",
"saveRequiredFields": "Please fill in conversation ID, title, description, severity, type, target, reproduction steps, evidence/POC, impact, and remediation",
"saveFailed": "Save failed",
"fetchFailed": "Failed to fetch vulnerability",
"deleteFailed": "Delete failed",
@@ -2046,9 +2046,12 @@
"detailTaskQueueId": "Task queue ID",
"detailConversationTag": "Conversation tag",
"detailTaskTag": "Task tag",
"detailProof": "Proof",
"detailPreconditions": "Preconditions",
"detailReproductionSteps": "Reproduction steps",
"detailEvidence": "Evidence / POC",
"detailImpact": "Impact",
"detailRecommendation": "Remediation",
"detailRetestNotes": "Retest method",
"downloadOkTitle": "Downloaded",
"exportFailedMessage": "Export failed",
"downloadFailed": "Download failed"
@@ -2800,9 +2803,9 @@
"taskTag": "Task tag",
"taskTagPlaceholder": "e.g. batch scan Q2, retest",
"title": "Title",
"titlePlaceholder": "Vulnerability title",
"titlePlaceholder": "/api/login is vulnerable to SQL injection",
"description": "Description",
"descriptionPlaceholder": "Detailed description",
"descriptionPlaceholder": "Describe the summary, trigger point, observed abnormal behavior, and why it is exploitable.",
"severity": "Severity",
"pleaseSelect": "Please select",
"severityCritical": "Critical",
@@ -2819,13 +2822,19 @@
"type": "Vulnerability type",
"typePlaceholder": "e.g. SQL injection, XSS, CSRF",
"target": "Target",
"targetPlaceholder": "Affected target (URL, IP, etc.)",
"proof": "Proof (POC)",
"proofPlaceholder": "Proof: request/response, screenshots, etc.",
"targetPlaceholder": "Be specific: URL, IP:port, endpoint path, and parameter name.",
"preconditions": "Preconditions",
"preconditionsPlaceholder": "Login state, permissions, account, headers/cookies, required data, environment/version; write none if not needed.",
"reproductionSteps": "Reproduction steps",
"reproductionStepsPlaceholder": "Number the steps and include entry point, parameter, payload, command, and observation point.",
"evidence": "Evidence / POC",
"evidencePlaceholder": "Raw request/response, curl/tool command, screenshot notes, logs, DNSLog/callback records, database results, file paths, timestamps, etc.",
"impact": "Impact",
"impactPlaceholder": "Impact description",
"impactPlaceholder": "Describe the verified real-world impact, such as which data can be read or changed.",
"recommendation": "Recommendation",
"recommendationPlaceholder": "Remediation"
"recommendationPlaceholder": "Write the concrete fix and retest criteria.",
"retestNotes": "Retest method",
"retestNotesPlaceholder": "How to verify the fix, including expected status code, error message, or access-control result."
},
"vulnerabilityMd": {
"headingBasic": "Basic information",
@@ -2842,9 +2851,12 @@
"labelCreated": "Created at",
"labelUpdated": "Updated at",
"headingDescription": "Description",
"headingProof": "Proof (POC)",
"headingPreconditions": "Preconditions",
"headingReproductionSteps": "Reproduction steps",
"headingEvidence": "Evidence / POC",
"headingImpact": "Impact",
"headingRecommendation": "Remediation"
"headingRecommendation": "Remediation",
"headingRetestNotes": "Retest method"
},
"roleModal": {
"addRole": "Add role",
+23 -11
View File
@@ -2015,7 +2015,7 @@
"exportNoResults": "当前筛选条件下无可导出漏洞",
"exportStarted": "已开始下载 {{count}} 份报告",
"exportFailed": "导出失败",
"saveRequiredFields": "请填写必填字段:会话ID、标题和严重程度",
"saveRequiredFields": "请填写必填字段:会话ID、标题、描述、严重程度、漏洞类型、目标、复现步骤、证据/POC、影响和修复建议",
"saveFailed": "保存失败",
"fetchFailed": "获取漏洞失败",
"deleteFailed": "删除失败",
@@ -2034,9 +2034,12 @@
"detailTaskQueueId": "任务队列ID",
"detailConversationTag": "对话标签",
"detailTaskTag": "任务标签",
"detailProof": "证明",
"detailPreconditions": "前置条件",
"detailReproductionSteps": "复现步骤",
"detailEvidence": "证据 / POC",
"detailImpact": "影响",
"detailRecommendation": "修复建议",
"detailRetestNotes": "复测方式",
"downloadOkTitle": "下载成功",
"exportFailedMessage": "导出失败",
"downloadFailed": "下载失败"
@@ -2788,9 +2791,9 @@
"taskTag": "任务标签",
"taskTagPlaceholder": "如:批量扫描Q2、专项复测",
"title": "标题",
"titlePlaceholder": "漏洞标题",
"titlePlaceholder": "/api/login 存在 SQL 注入",
"description": "描述",
"descriptionPlaceholder": "漏洞详细描述",
"descriptionPlaceholder": "说明漏洞摘要、触发点、异常现象和为什么可被利用。",
"severity": "严重程度",
"pleaseSelect": "请选择",
"severityCritical": "严重",
@@ -2807,13 +2810,19 @@
"type": "漏洞类型",
"typePlaceholder": "如:SQL注入、XSS、CSRF等",
"target": "目标",
"targetPlaceholder": "受影响的目标(URLIP地址等)",
"proof": "证明(POC",
"proofPlaceholder": "漏洞证明,如请求/响应、截图等",
"targetPlaceholder": "精确到 URL/IP:端口/接口路径/参数名",
"preconditions": "前置条件",
"preconditionsPlaceholder": "登录状态、权限、账号、Header/Cookie、特定数据、环境/版本;无则写无。",
"reproductionSteps": "复现步骤",
"reproductionStepsPlaceholder": "按 1/2/3 编号,写清入口、参数、payload、执行命令、观察点。",
"evidence": "证据 / POC",
"evidencePlaceholder": "原始请求/响应、curl/工具命令、截图说明、日志、DNSLog/回连记录、数据库结果、文件路径、时间戳等。",
"impact": "影响",
"impactPlaceholder": "漏洞影响说明",
"impactPlaceholder": "结合已验证事实说明实际影响,例如越权读取哪些数据。",
"recommendation": "修复建议",
"recommendationPlaceholder": "修复建议"
"recommendationPlaceholder": "写具体修复点和复测标准。",
"retestNotes": "复测方式",
"retestNotesPlaceholder": "修复后如何验证漏洞已关闭,包括应返回的状态码、错误信息或访问控制结果。"
},
"vulnerabilityMd": {
"headingBasic": "基本信息",
@@ -2830,9 +2839,12 @@
"labelCreated": "创建时间",
"labelUpdated": "更新时间",
"headingDescription": "描述",
"headingProof": "证明(POC",
"headingPreconditions": "前置条件",
"headingReproductionSteps": "复现步骤",
"headingEvidence": "证据 / POC",
"headingImpact": "影响",
"headingRecommendation": "修复建议"
"headingRecommendation": "修复建议",
"headingRetestNotes": "复测方式"
},
"roleModal": {
"addRole": "添加角色",
+62 -16
View File
@@ -1303,9 +1303,14 @@ function renderVulnerabilities(vulnerabilities, renderOptions) {
${vuln.conversation_tag ? vulnDetailField(vulnT('vulnerabilityPage.detailConversationTag'), vuln.conversation_tag, false) : ''}
${vuln.task_tag ? vulnDetailField(vulnT('vulnerabilityPage.detailTaskTag'), vuln.task_tag, false) : ''}
</div>
${vuln.proof ? `<div class="vulnerability-proof"><strong>${escapeHtml(vulnT('vulnerabilityPage.detailProof'))}:</strong><pre>${escapeHtml(vuln.proof)}</pre></div>` : ''}
${vuln.impact ? `<div class="vulnerability-impact"><strong>${escapeHtml(vulnT('vulnerabilityPage.detailImpact'))}:</strong> ${escapeHtml(vuln.impact)}</div>` : ''}
${vuln.recommendation ? `<div class="vulnerability-recommendation"><strong>${escapeHtml(vulnT('vulnerabilityPage.detailRecommendation'))}:</strong> ${escapeHtml(vuln.recommendation)}</div>` : ''}
<div class="vulnerability-repro">
${vulnNarrativeSection(vulnT('vulnerabilityPage.detailPreconditions'), vuln.preconditions)}
${vulnNarrativeSection(vulnT('vulnerabilityPage.detailReproductionSteps'), vuln.reproduction_steps)}
${vulnNarrativeSection(vulnT('vulnerabilityPage.detailEvidence'), vuln.evidence, { code: true })}
${vulnNarrativeSection(vulnT('vulnerabilityPage.detailImpact'), vuln.impact)}
${vulnNarrativeSection(vulnT('vulnerabilityPage.detailRecommendation'), vuln.recommendation)}
${vulnNarrativeSection(vulnT('vulnerabilityPage.detailRetestNotes'), vuln.retest_notes)}
</div>
<div class="vulnerability-related-facts" id="vuln-related-facts-${vuln.id}" data-project-id="${escapeHtml(vuln.project_id || '')}" data-vuln-id="${escapeHtml(vuln.id)}" hidden></div>
</div>
</div>
@@ -1467,9 +1472,12 @@ async function showAddVulnerabilityModal() {
document.getElementById('vulnerability-status').value = 'open';
document.getElementById('vulnerability-type').value = '';
document.getElementById('vulnerability-target').value = '';
document.getElementById('vulnerability-proof').value = '';
document.getElementById('vulnerability-preconditions').value = '';
document.getElementById('vulnerability-reproduction-steps').value = '';
document.getElementById('vulnerability-evidence').value = '';
document.getElementById('vulnerability-impact').value = '';
document.getElementById('vulnerability-recommendation').value = '';
document.getElementById('vulnerability-retest-notes').value = '';
openAppModal('vulnerability-modal');
}
@@ -1493,9 +1501,12 @@ async function editVulnerability(id) {
document.getElementById('vulnerability-status').value = vuln.status || 'open';
document.getElementById('vulnerability-type').value = vuln.type || '';
document.getElementById('vulnerability-target').value = vuln.target || '';
document.getElementById('vulnerability-proof').value = vuln.proof || '';
document.getElementById('vulnerability-preconditions').value = vuln.preconditions || '';
document.getElementById('vulnerability-reproduction-steps').value = vuln.reproduction_steps || '';
document.getElementById('vulnerability-evidence').value = vuln.evidence || '';
document.getElementById('vulnerability-impact').value = vuln.impact || '';
document.getElementById('vulnerability-recommendation').value = vuln.recommendation || '';
document.getElementById('vulnerability-retest-notes').value = vuln.retest_notes || '';
await populateVulnerabilityModalProjectSelect(vuln.project_id || '');
document.getElementById('vulnerability-title')?.focus();
});
@@ -1510,9 +1521,16 @@ async function editVulnerability(id) {
async function saveVulnerability() {
const conversationId = document.getElementById('vulnerability-conversation-id').value.trim();
const title = document.getElementById('vulnerability-title').value.trim();
const description = document.getElementById('vulnerability-description').value.trim();
const severity = document.getElementById('vulnerability-severity').value;
const type = document.getElementById('vulnerability-type').value.trim();
const target = document.getElementById('vulnerability-target').value.trim();
const reproductionSteps = document.getElementById('vulnerability-reproduction-steps').value.trim();
const evidence = document.getElementById('vulnerability-evidence').value.trim();
const impact = document.getElementById('vulnerability-impact').value.trim();
const recommendation = document.getElementById('vulnerability-recommendation').value.trim();
if (!conversationId || !title || !severity) {
if (!conversationId || !title || !description || !severity || !type || !target || !reproductionSteps || !evidence || !impact || !recommendation) {
alert(vulnT('vulnerabilityPage.saveRequiredFields'));
return;
}
@@ -1525,14 +1543,17 @@ async function saveVulnerability() {
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(),
description: description,
severity: severity,
status: document.getElementById('vulnerability-status').value,
type: document.getElementById('vulnerability-type').value.trim(),
target: document.getElementById('vulnerability-target').value.trim(),
proof: document.getElementById('vulnerability-proof').value.trim(),
impact: document.getElementById('vulnerability-impact').value.trim(),
recommendation: document.getElementById('vulnerability-recommendation').value.trim()
type: type,
target: target,
preconditions: document.getElementById('vulnerability-preconditions').value.trim(),
reproduction_steps: reproductionSteps,
evidence: evidence,
impact: impact,
recommendation: recommendation,
retest_notes: document.getElementById('vulnerability-retest-notes').value.trim()
};
try {
@@ -1553,9 +1574,12 @@ async function saveVulnerability() {
status: data.status,
type: data.type,
target: data.target,
proof: data.proof,
preconditions: data.preconditions,
reproduction_steps: data.reproduction_steps,
evidence: data.evidence,
impact: data.impact,
recommendation: data.recommendation,
retest_notes: data.retest_notes,
};
}
@@ -1864,6 +1888,17 @@ function vulnDetailField(label, value, asCode) {
</div>`;
}
function vulnNarrativeSection(label, value, options) {
if (value === undefined || value === null || String(value).trim() === '') return '';
const opts = options || {};
const tag = opts.code ? 'pre' : 'div';
const cls = opts.code ? 'vulnerability-section-body vulnerability-section-body--code' : 'vulnerability-section-body';
return `<section class="vulnerability-section">
<strong>${escapeHtml(label)}</strong>
<${tag} class="${cls}">${escapeHtml(String(value))}</${tag}>
</section>`;
}
// 将漏洞格式化为Markdown(章节标题随界面语言)
function formatVulnerabilityAsMarkdown(vuln) {
const severityText = vulnSeverityLabel(vuln.severity);
@@ -1905,8 +1940,16 @@ function formatVulnerabilityAsMarkdown(vuln) {
markdown += `## ${L('headingDescription')}\n\n${vuln.description}\n\n`;
}
if (vuln.proof) {
markdown += `## ${L('headingProof')}\n\n\`\`\`\n${vuln.proof}\n\`\`\`\n\n`;
if (vuln.preconditions) {
markdown += `## ${L('headingPreconditions')}\n\n${vuln.preconditions}\n\n`;
}
if (vuln.reproduction_steps) {
markdown += `## ${L('headingReproductionSteps')}\n\n${vuln.reproduction_steps}\n\n`;
}
if (vuln.evidence) {
markdown += `## ${L('headingEvidence')}\n\n\`\`\`\n${vuln.evidence}\n\`\`\`\n\n`;
}
if (vuln.impact) {
@@ -1917,6 +1960,10 @@ function formatVulnerabilityAsMarkdown(vuln) {
markdown += `## ${L('headingRecommendation')}\n\n${vuln.recommendation}\n\n`;
}
if (vuln.retest_notes) {
markdown += `## ${L('headingRetestNotes')}\n\n${vuln.retest_notes}\n\n`;
}
return markdown;
}
@@ -2194,4 +2241,3 @@ window.setVulnerabilityIdFilter = setVulnerabilityIdFilter;
window.bindVulnerabilityProject = bindVulnerabilityProject;
window.buildVulnerabilityProjectOptionsHtml = buildVulnerabilityProjectOptionsHtml;
window.changeVulnerabilityStatus = changeVulnerabilityStatus;
+24 -12
View File
@@ -4385,11 +4385,11 @@
</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 />
<input type="text" id="vulnerability-title" data-i18n="vulnerabilityModal.titlePlaceholder" data-i18n-attr="placeholder" placeholder="/api/login 存在 SQL 注入" required />
</div>
<div class="form-group">
<label for="vulnerability-description" data-i18n="vulnerabilityModal.description">描述</label>
<textarea id="vulnerability-description" rows="5" data-i18n="vulnerabilityModal.descriptionPlaceholder" data-i18n-attr="placeholder" placeholder="漏洞详细描述"></textarea>
<label for="vulnerability-description"><span data-i18n="vulnerabilityModal.description">描述</span> <span style="color: red;">*</span></label>
<textarea id="vulnerability-description" rows="7" data-i18n="vulnerabilityModal.descriptionPlaceholder" data-i18n-attr="placeholder" placeholder="建议包含:摘要、测试环境与范围、前置条件、复现步骤、预期结果、实际结果。"></textarea>
</div>
<div class="form-group">
<label for="vulnerability-severity"><span data-i18n="vulnerabilityModal.severity">严重程度</span> <span style="color: red;">*</span></label>
@@ -4413,24 +4413,36 @@
</select>
</div>
<div class="form-group">
<label for="vulnerability-type" data-i18n="vulnerabilityModal.type">漏洞类型</label>
<label for="vulnerability-type"><span data-i18n="vulnerabilityModal.type">漏洞类型</span> <span style="color: red;">*</span></label>
<input type="text" id="vulnerability-type" data-i18n="vulnerabilityModal.typePlaceholder" data-i18n-attr="placeholder" placeholder="如:SQL注入、XSS、CSRF等" />
</div>
<div class="form-group">
<label for="vulnerability-target" data-i18n="vulnerabilityModal.target">目标</label>
<input type="text" id="vulnerability-target" data-i18n="vulnerabilityModal.targetPlaceholder" data-i18n-attr="placeholder" placeholder="受影响的目标(URLIP地址等)" />
<label for="vulnerability-target"><span data-i18n="vulnerabilityModal.target">目标</span> <span style="color: red;">*</span></label>
<input type="text" id="vulnerability-target" data-i18n="vulnerabilityModal.targetPlaceholder" data-i18n-attr="placeholder" placeholder="精确到 URL/IP:端口/接口路径/参数名" />
</div>
<div class="form-group">
<label for="vulnerability-proof" data-i18n="vulnerabilityModal.proof">证明(POC</label>
<textarea id="vulnerability-proof" rows="5" data-i18n="vulnerabilityModal.proofPlaceholder" data-i18n-attr="placeholder" placeholder="漏洞证明,如请求/响应、截图等"></textarea>
<label for="vulnerability-preconditions" data-i18n="vulnerabilityModal.preconditions">前置条件</label>
<textarea id="vulnerability-preconditions" rows="3" data-i18n="vulnerabilityModal.preconditionsPlaceholder" data-i18n-attr="placeholder" placeholder="登录状态、权限、账号、Header/Cookie、特定数据、环境/版本;无则写无。"></textarea>
</div>
<div class="form-group">
<label for="vulnerability-impact" data-i18n="vulnerabilityModal.impact">影响</label>
<textarea id="vulnerability-impact" rows="3" data-i18n="vulnerabilityModal.impactPlaceholder" data-i18n-attr="placeholder" placeholder="漏洞影响说明"></textarea>
<label for="vulnerability-reproduction-steps"><span data-i18n="vulnerabilityModal.reproductionSteps">复现步骤</span> <span style="color: red;">*</span></label>
<textarea id="vulnerability-reproduction-steps" rows="6" data-i18n="vulnerabilityModal.reproductionStepsPlaceholder" data-i18n-attr="placeholder" placeholder="按 1/2/3 编号,写清入口、参数、payload、执行命令、观察点。"></textarea>
</div>
<div class="form-group">
<label for="vulnerability-recommendation" data-i18n="vulnerabilityModal.recommendation">修复建议</label>
<textarea id="vulnerability-recommendation" rows="3" data-i18n="vulnerabilityModal.recommendationPlaceholder" data-i18n-attr="placeholder" placeholder="修复建议"></textarea>
<label for="vulnerability-evidence"><span data-i18n="vulnerabilityModal.evidence">证据 / POC</span> <span style="color: red;">*</span></label>
<textarea id="vulnerability-evidence" rows="8" data-i18n="vulnerabilityModal.evidencePlaceholder" data-i18n-attr="placeholder" placeholder="原始请求/响应、curl/工具命令、截图说明、日志、DNSLog/回连记录、数据库结果、文件路径、时间戳等。"></textarea>
</div>
<div class="form-group">
<label for="vulnerability-impact"><span data-i18n="vulnerabilityModal.impact">影响</span> <span style="color: red;">*</span></label>
<textarea id="vulnerability-impact" rows="3" data-i18n="vulnerabilityModal.impactPlaceholder" data-i18n-attr="placeholder" placeholder="结合已验证事实说明实际影响,例如越权读取哪些数据。"></textarea>
</div>
<div class="form-group">
<label for="vulnerability-recommendation"><span data-i18n="vulnerabilityModal.recommendation">修复建议</span> <span style="color: red;">*</span></label>
<textarea id="vulnerability-recommendation" rows="3" data-i18n="vulnerabilityModal.recommendationPlaceholder" data-i18n-attr="placeholder" placeholder="写具体修复点和复测标准。"></textarea>
</div>
<div class="form-group">
<label for="vulnerability-retest-notes" data-i18n="vulnerabilityModal.retestNotes">复测方式</label>
<textarea id="vulnerability-retest-notes" rows="3" data-i18n="vulnerabilityModal.retestNotesPlaceholder" data-i18n-attr="placeholder" placeholder="修复后如何验证漏洞已关闭,包括应返回的状态码、错误信息或访问控制结果。"></textarea>
</div>
</div>
<div class="modal-footer">