diff --git a/web/static/js/api-docs.js b/web/static/js/api-docs.js index bd80b8e0..ec075a27 100644 --- a/web/static/js/api-docs.js +++ b/web/static/js/api-docs.js @@ -198,7 +198,7 @@ function renderSidebar() { const li = document.createElement('li'); li.className = 'api-group-item'; const groupLabel = translateApiDocTag(group); - li.innerHTML = `${escapeHtml(groupLabel)}`; + li.innerHTML = `${escapeHtml(groupLabel)}`; groupList.appendChild(li); }); @@ -265,7 +265,7 @@ function createEndpointCard(endpoint) {
${endpoint.method.toUpperCase()} - ${endpoint.path} + ${escapeHtml(endpoint.path)} ${tagHtml}
@@ -541,7 +541,7 @@ function renderTestSection(endpoint) { bodyInput = `
- +
`; } @@ -554,8 +554,8 @@ function renderTestSection(endpoint) { const inputId = `test-param-${param.name}-${escapeId(path)}-${method}`; return `
- - + +
`; }).join(''); @@ -572,11 +572,11 @@ function renderTestSection(endpoint) { const required = param.required ? '*' : '' + escapeHtml(_t('apiDocs.optional')) + ''; return `
- +
`; @@ -598,13 +598,13 @@ function renderTestSection(endpoint) { ${queryParamsInput ? `
${queryParamsTitle}
${queryParamsInput}
` : ''} ${bodyInput}
- -
- + `).join(''); if (typeof window.loadVulnerabilityAlertSubscription === 'function') { window.loadVulnerabilityAlertSubscription(); diff --git a/web/static/js/c2.js b/web/static/js/c2.js index 4abaa3dc..21113669 100644 --- a/web/static/js/c2.js +++ b/web/static/js/c2.js @@ -113,10 +113,10 @@ if (!id || seen.has(id)) return; seen.add(id); const label = c2ProjectDisplayName(id, name); - html += ``; + html += ``; }); if (selected && !seen.has(selected)) { - html += ``; + html += ``; } return html; } @@ -147,7 +147,7 @@ } function c2ProjectBindSelectHtml(listener) { - return ``; + return ``; } function withC2ProjectQuery(url) { @@ -478,7 +478,7 @@ if (empty) valueClasses.push('is-empty'); const rowCls = opts && opts.full ? ' c2-session-info-dl__row--full' : ''; const copyBtn = (opts && opts.copy && !empty) - ? `` : ''; @@ -486,7 +486,7 @@
${escapeHtml(label)}
- ${escapeHtml(display)} + ${escapeHtml(display)} ${copyBtn}
`; @@ -543,16 +543,16 @@
${escapeHtml(c2t('c2.sessions.infoSectionNote'))} - +
${escapeHtml(noteText || c2t('c2.sessions.infoNoteEmpty'))}
@@ -638,15 +638,19 @@ return div.innerHTML; } + function escapeAttr(text) { + return escapeHtml(text).replace(/"/g, '"').replace(/'/g, '''); + } + /** 任务列表操作按钮(查看/取消/删除)— 事件委托 */ function bindC2TaskActionDelegation() { if (document.documentElement.dataset.c2TaskActionsBound === '1') return; document.documentElement.dataset.c2TaskActionsBound = '1'; document.addEventListener('click', function(e) { - const btn = e.target.closest('[data-c2-task-action]'); - if (!btn) return; - e.preventDefault(); - e.stopPropagation(); + const btn = e.target.closest('[data-c2-task-action]'); + if (!btn) return; + e.preventDefault(); + e.stopImmediatePropagation(); const action = btn.getAttribute('data-c2-task-action'); const id = btn.getAttribute('data-task-id'); if (!id) return; @@ -657,6 +661,75 @@ } bindC2TaskActionDelegation(); + /** C2 动态内容操作按钮 — 避免把用户可控值拼入 inline onclick */ + function bindC2SafeActionDelegation() { + if (document.documentElement.dataset.c2SafeActionsBound === '1') return; + document.documentElement.dataset.c2SafeActionsBound = '1'; + document.addEventListener('click', function(e) { + const copyBtn = e.target.closest('[data-c2-copy-value]'); + if (copyBtn) { + e.preventDefault(); + e.stopPropagation(); + C2.copyText(copyBtn.getAttribute('data-c2-copy-value') || ''); + return; + } + + const fileBtn = e.target.closest('[data-c2-file-action]'); + if (fileBtn) { + e.preventDefault(); + e.stopPropagation(); + const name = fileBtn.getAttribute('data-c2-file-name') || ''; + const action = fileBtn.getAttribute('data-c2-file-action'); + if (action === 'open') C2.openDirectory(name); + else if (action === 'download') C2.downloadFile(name); + return; + } + + const stopEl = e.target.closest('[data-c2-stop-action]'); + const actionEl = e.target.closest('[data-c2-action]'); + if (stopEl && (!actionEl || !stopEl.contains(actionEl))) return; + if (!actionEl) return; + e.preventDefault(); + e.stopPropagation(); + runC2SafeAction(actionEl); + }); + document.addEventListener('keydown', function(e) { + if (e.key !== 'Enter' && e.key !== ' ') return; + const actionEl = e.target.closest('[data-c2-action][role="button"]'); + if (!actionEl) return; + e.preventDefault(); + runC2SafeAction(actionEl); + }); + } + bindC2SafeActionDelegation(); + + function runC2SafeAction(el) { + const action = el.getAttribute('data-c2-action'); + const id = el.getAttribute('data-c2-id') || ''; + switch (action) { + case 'session-note-edit': C2.beginEditSessionNote(id); break; + case 'session-note-save': C2.saveSessionNote(id); break; + case 'listener-start': C2.startListener(id); break; + case 'listener-stop': C2.stopListener(id); break; + case 'listener-edit': C2.editListener(id); break; + case 'listener-delete': C2.deleteListener(id); break; + case 'listener-save': C2.saveListener(id); break; + case 'session-select': C2.selectSession(id); break; + case 'session-delete': C2.deleteSessionRecord(id); break; + case 'session-sleep': C2.setSessionSleep(id); break; + case 'session-kill': C2.killSession(id); break; + case 'session-tasks-refresh': C2.loadSessionTasks(id); break; + case 'task-view': C2.viewTask(id); break; + case 'event-view': C2.viewEvent(id); break; + case 'event-delete': C2.deleteEventById(id); break; + case 'profile-delete': C2.deleteProfile(id); break; + case 'payload-download': { + if (typeof window.__c2DownloadPayload === 'function') window.__c2DownloadPayload(id); + break; + } + } + } + /** 监听器表单:Malleable Profile 下拉选项 HTML(value / 文本已转义) */ function listenerProfileSelectHtml(selectedProfileId) { const sel = selectedProfileId ? String(selectedProfileId) : ''; @@ -665,7 +738,7 @@ if (!p) continue; const pid = p.id || p.ID; if (!pid) continue; - const idEsc = escapeHtml(String(pid)); + const idEsc = escapeAttr(String(pid)); const nameEsc = escapeHtml(p.name || pid); const selected = sel && String(pid) === sel ? ' selected' : ''; opts += ``; @@ -852,7 +925,7 @@ const profilePid = listenerResolvedProfileId(l); const profileName = listenerProfileDisplayName(l); const profileBadge = profilePid - ? '
' + escapeHtml(profileName) + '
' + ? '
' + escapeHtml(profileName) + '
' : ''; const cb = C2.getListenerCallbackHost(l); const cbRow = cb @@ -868,7 +941,7 @@ const bindVal = escapeHtml(String(l.bindHost)) + ':' + escapeHtml(String(l.bindPort)); return ` -
+
${typeMark}
@@ -877,7 +950,7 @@ ${pillLabel}
- ${escapeHtml(l.id)} + ${escapeHtml(l.id)}
@@ -894,11 +967,11 @@
${l.status === 'stopped' - ? `` - : `` + ? `` + : `` } - - + +
`; }).join(''); @@ -963,7 +1036,7 @@
- +
@@ -1003,7 +1076,7 @@
- +
- +
${escapeHtml(currentLine)}
@@ -2778,16 +2854,16 @@
- ${escapeHtml(entry.name)} + ${escapeHtml(entry.name)}
- ${escapeHtml(sizeLabel)} + ${escapeHtml(sizeLabel)} ${escapeHtml(entry.mode)} - ${entry.isDir - ? `` - : `` - } + `; @@ -3366,7 +3442,7 @@ ${escapeHtml(c2t('c2.tasks.sessionTaskHistory'))} 0
- +
@@ -3383,7 +3459,7 @@ ${escapeHtml(c2t('c2.tasks.sessionTaskHistory'))} ${countLabel}
- +
${tasks.map(t => { @@ -3395,11 +3471,11 @@ const isPending = status === 'queued' || status === 'sent' || status === 'running'; const timeStr = formatTime(t.completedAt || t.createdAt); return ` -
+
- + ${escapeHtml(t.taskType || '-')} -
+
${cmdShort ? `${escapeHtml(cmdShort)}` : ``} @@ -3408,8 +3484,8 @@
${escapeHtml(taskStatusLabel(status))} ${formatDuration(t.durationMs)} - ${escapeHtml(formatRelativeTime(t.completedAt || t.createdAt) || timeStr)} - + ${escapeHtml(formatRelativeTime(t.completedAt || t.createdAt) || timeStr)} +
`; }).join('')} @@ -3446,7 +3522,7 @@ -
`; @@ -4213,7 +4290,7 @@ -