diff --git a/web/static/css/style.css b/web/static/css/style.css index ada4457d..1ef67f03 100644 --- a/web/static/css/style.css +++ b/web/static/css/style.css @@ -13575,29 +13575,87 @@ header { .vulnerability-details { display: grid; - grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); - gap: 12px; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 14px 16px; margin-bottom: 16px; padding: 12px; background: var(--bg-secondary); border-radius: 6px; + align-items: start; } -.detail-item { +/* 元数据条数为奇数时,最后一项占满一行,长 URL/队列 ID 更易读 */ +.vulnerability-details .vuln-detail-field:last-child:nth-child(odd) { + grid-column: 1 / -1; +} + +@media (max-width: 768px) { + .vulnerability-details { + grid-template-columns: 1fr; + } + + .vulnerability-details .vuln-detail-field:last-child:nth-child(odd) { + grid-column: auto; + } +} + +/* 漏洞详情字段:标签与值分行,长 ID/URL 可换行、可选中复制 */ +.vuln-detail-field { + min-width: 0; font-size: 0.875rem; } -.detail-item strong { +.vuln-detail-field__label { color: var(--text-secondary); - margin-right: 4px; + font-weight: 600; + font-size: 0.75rem; + margin-bottom: 6px; + text-transform: none; + letter-spacing: normal; } -.detail-item code { +.vuln-detail-field__row { + display: flex; + align-items: flex-start; + gap: 8px; + min-width: 0; +} + +.vuln-detail-field-value { + flex: 1; + min-width: 0; + margin: 0; + padding: 8px 10px; + border-radius: 6px; background: var(--bg-tertiary); - padding: 2px 6px; - border-radius: 4px; - font-size: 0.8rem; - font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace; + border: 1px solid var(--border-color); + font-size: 0.8125rem; + line-height: 1.45; + word-break: break-word; + overflow-wrap: anywhere; + white-space: pre-wrap; + user-select: text; + -webkit-user-select: text; + color: var(--text-primary); + font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', 'Consolas', monospace; +} + +.vuln-detail-field__copy { + flex-shrink: 0; + margin-top: 2px; + padding: 6px; + line-height: 0; + border-radius: 6px; + color: var(--text-secondary); + border: 1px solid transparent; + background: transparent; + cursor: pointer; +} + +.vuln-detail-field__copy:hover { + color: var(--accent-color); + background: var(--bg-primary); + border-color: var(--border-color); } .vulnerability-proof, diff --git a/web/static/i18n/en-US.json b/web/static/i18n/en-US.json index c91db344..7a59ffae 100644 --- a/web/static/i18n/en-US.json +++ b/web/static/i18n/en-US.json @@ -1312,6 +1312,12 @@ "clear": "Clear", "vulnId": "Vuln ID", "conversationId": "Conversation ID", + "taskOrQueueId": "Task / queue ID", + "filterTaskOrQueue": "Filter by task or queue ID", + "conversationTag": "Conversation tag", + "filterConversationTag": "Filter by conversation tag", + "taskTag": "Task tag", + "filterTaskTag": "Filter by task tag", "severity": "Severity", "status": "Status", "statusOpen": "Open", @@ -1321,7 +1327,31 @@ "searchVulnId": "Search vuln ID", "filterConversation": "Filter by conversation", "loading": "Loading...", - "noRecords": "No vulnerability records" + "loadListFailed": "Failed to load", + "noRecords": "No vulnerability records", + "batchExport": "Batch export", + "downloadMarkdownTitle": "Download Markdown", + "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", + "saveFailed": "Save failed", + "fetchFailed": "Failed to fetch vulnerability", + "deleteFailed": "Delete failed", + "detailVulnId": "Vuln ID", + "detailType": "Type", + "detailTarget": "Target", + "detailConversationId": "Conversation ID", + "detailTaskId": "Task ID", + "detailTaskQueueId": "Task queue ID", + "detailConversationTag": "Conversation tag", + "detailTaskTag": "Task tag", + "detailProof": "Proof", + "detailImpact": "Impact", + "detailRecommendation": "Remediation", + "downloadOkTitle": "Downloaded", + "exportFailedMessage": "Export failed", + "downloadFailed": "Download failed" }, "tasksPage": { "statusFilter": "Status filter", @@ -1767,6 +1797,10 @@ "vulnerabilityModal": { "conversationId": "Conversation ID", "conversationIdPlaceholder": "Enter conversation ID", + "conversationTag": "Conversation tag", + "conversationTagPlaceholder": "e.g. engagement A, weekly report", + "taskTag": "Task tag", + "taskTagPlaceholder": "e.g. batch scan Q2, retest", "title": "Title", "titlePlaceholder": "Vulnerability title", "description": "Description", @@ -1794,6 +1828,25 @@ "recommendation": "Recommendation", "recommendationPlaceholder": "Remediation" }, + "vulnerabilityMd": { + "headingBasic": "Basic information", + "labelId": "Vulnerability ID", + "labelSeverity": "Severity", + "labelStatus": "Status", + "labelType": "Type", + "labelTarget": "Target", + "labelConversationId": "Conversation ID", + "labelTaskId": "Task ID", + "labelTaskQueueId": "Task queue ID", + "labelConversationTag": "Conversation tag", + "labelTaskTag": "Task tag", + "labelCreated": "Created at", + "labelUpdated": "Updated at", + "headingDescription": "Description", + "headingProof": "Proof (POC)", + "headingImpact": "Impact", + "headingRecommendation": "Remediation" + }, "roleModal": { "addRole": "Add role", "editRole": "Edit role", diff --git a/web/static/i18n/zh-CN.json b/web/static/i18n/zh-CN.json index 2da9a2e8..abc79e61 100644 --- a/web/static/i18n/zh-CN.json +++ b/web/static/i18n/zh-CN.json @@ -1312,6 +1312,12 @@ "clear": "清除", "vulnId": "漏洞ID", "conversationId": "会话ID", + "taskOrQueueId": "任务ID/队列ID", + "filterTaskOrQueue": "筛选任务ID或队列ID", + "conversationTag": "对话标签", + "filterConversationTag": "筛选对话标签", + "taskTag": "任务标签", + "filterTaskTag": "筛选任务标签", "severity": "严重程度", "status": "状态", "statusOpen": "待处理", @@ -1321,7 +1327,31 @@ "searchVulnId": "搜索漏洞ID", "filterConversation": "筛选特定会话", "loading": "加载中...", - "noRecords": "暂无漏洞记录" + "loadListFailed": "加载失败", + "noRecords": "暂无漏洞记录", + "batchExport": "批量导出", + "downloadMarkdownTitle": "下载 Markdown", + "exportNoResults": "当前筛选条件下无可导出漏洞", + "exportStarted": "已开始下载 {{count}} 份报告", + "exportFailed": "导出失败", + "saveRequiredFields": "请填写必填字段:会话ID、标题和严重程度", + "saveFailed": "保存失败", + "fetchFailed": "获取漏洞失败", + "deleteFailed": "删除失败", + "detailVulnId": "漏洞ID", + "detailType": "类型", + "detailTarget": "目标", + "detailConversationId": "会话ID", + "detailTaskId": "任务ID", + "detailTaskQueueId": "任务队列ID", + "detailConversationTag": "对话标签", + "detailTaskTag": "任务标签", + "detailProof": "证明", + "detailImpact": "影响", + "detailRecommendation": "修复建议", + "downloadOkTitle": "下载成功", + "exportFailedMessage": "导出失败", + "downloadFailed": "下载失败" }, "tasksPage": { "statusFilter": "状态筛选", @@ -1767,6 +1797,10 @@ "vulnerabilityModal": { "conversationId": "会话ID", "conversationIdPlaceholder": "输入会话ID", + "conversationTag": "对话标签", + "conversationTagPlaceholder": "如:红队演练A、客户A周报", + "taskTag": "任务标签", + "taskTagPlaceholder": "如:批量扫描Q2、专项复测", "title": "标题", "titlePlaceholder": "漏洞标题", "description": "描述", @@ -1794,6 +1828,25 @@ "recommendation": "修复建议", "recommendationPlaceholder": "修复建议" }, + "vulnerabilityMd": { + "headingBasic": "基本信息", + "labelId": "漏洞ID", + "labelSeverity": "严重程度", + "labelStatus": "状态", + "labelType": "类型", + "labelTarget": "目标", + "labelConversationId": "会话ID", + "labelTaskId": "任务ID", + "labelTaskQueueId": "任务队列ID", + "labelConversationTag": "对话标签", + "labelTaskTag": "任务标签", + "labelCreated": "创建时间", + "labelUpdated": "更新时间", + "headingDescription": "描述", + "headingProof": "证明(POC)", + "headingImpact": "影响", + "headingRecommendation": "修复建议" + }, "roleModal": { "addRole": "添加角色", "editRole": "编辑角色", diff --git a/web/static/js/vulnerability.js b/web/static/js/vulnerability.js index 1628c447..99f94f1f 100644 --- a/web/static/js/vulnerability.js +++ b/web/static/js/vulnerability.js @@ -1,5 +1,43 @@ // 漏洞管理相关功能 +function vulnT(key, opts) { + if (typeof window.t === 'function') { + return window.t(key, opts); + } + return key; +} + +function vulnDateLocale() { + try { + const lang = (window.__locale || '').toLowerCase(); + if (lang.indexOf('zh') === 0) { + return 'zh-CN'; + } + } catch (e) { /* ignore */ } + return 'en-US'; +} + +function vulnSeverityLabel(code) { + const m = { + critical: 'dashboard.severityCritical', + high: 'dashboard.severityHigh', + medium: 'dashboard.severityMedium', + low: 'dashboard.severityLow', + info: 'dashboard.severityInfo' + }; + return m[code] ? vulnT(m[code]) : code; +} + +function vulnStatusLabel(code) { + const m = { + open: 'vulnerabilityPage.statusOpen', + confirmed: 'vulnerabilityPage.statusConfirmed', + fixed: 'vulnerabilityPage.statusFixed', + false_positive: 'vulnerabilityPage.statusFalsePositive' + }; + return m[code] ? vulnT(m[code]) : code; +} + // 从localStorage读取每页显示数量,默认为20 const getVulnerabilityPageSize = () => { const saved = localStorage.getItem('vulnerabilityPageSize'); @@ -85,7 +123,7 @@ function updateVulnerabilityStats(stats) { // 加载漏洞列表 async function loadVulnerabilities(page = null) { const listContainer = document.getElementById('vulnerabilities-list'); - listContainer.innerHTML = '
加载中...
'; + listContainer.innerHTML = `
${escapeHtml(vulnT('vulnerabilityPage.loading'))}
`; try { // 检查apiFetch是否可用 @@ -160,7 +198,7 @@ async function loadVulnerabilities(page = null) { renderVulnerabilityPagination(); } catch (error) { console.error('加载漏洞列表失败:', error); - listContainer.innerHTML = `
加载失败: ${error.message}
`; + listContainer.innerHTML = `
${escapeHtml(vulnT('vulnerabilityPage.loadListFailed'))}: ${escapeHtml(error.message)}
`; } } @@ -192,22 +230,12 @@ function renderVulnerabilities(vulnerabilities) { const html = vulnerabilities.map(vuln => { const severityClass = `severity-${vuln.severity}`; - const severityText = { - 'critical': '严重', - 'high': '高危', - 'medium': '中危', - 'low': '低危', - 'info': '信息' - }[vuln.severity] || vuln.severity; - - const statusText = { - 'open': '待处理', - 'confirmed': '已确认', - 'fixed': '已修复', - 'false_positive': '误报' - }[vuln.status] || vuln.status; - - const createdDate = new Date(vuln.created_at).toLocaleString('zh-CN'); + const severityText = vulnSeverityLabel(vuln.severity); + const statusText = vulnStatusLabel(vuln.status); + const createdDate = new Date(vuln.created_at).toLocaleString(vulnDateLocale()); + const dlTitle = escapeHtml(vulnT('vulnerabilityPage.downloadMarkdownTitle')); + const editTitle = escapeHtml(vulnT('common.edit')); + const deleteTitle = escapeHtml(vulnT('common.delete')); return `
@@ -226,20 +254,20 @@ function renderVulnerabilities(vulnerabilities) {
- - -
`; }).join(''); listContainer.innerHTML = html; + if (typeof window.applyTranslations === 'function') { + window.applyTranslations(listContainer); + } } // 渲染分页控件 @@ -293,9 +324,9 @@ function renderVulnerabilityPagination() { // 左侧:显示范围信息和每页数量选择器(参考Skills样式) paginationHTML += `
- 显示 ${start}-${end} / 共 ${total} 条 + ${escapeHtml(vulnT('skillsPage.paginationShow', { start, end, total }))} - +
@@ -2613,12 +2613,12 @@
- - + +
- - + +
@@ -2838,7 +2838,7 @@ - +