mirror of
https://github.com/Ed1s0nZ/CyberStrikeAI.git
synced 2026-08-27 21:30:25 +02:00
Add files via upload
This commit is contained in:
@@ -27049,8 +27049,8 @@ html[data-theme="dark"] .chat-files-toast.chat-toast--error {
|
|||||||
z-index: 20;
|
z-index: 20;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 10px 0 12px;
|
padding: 10px 0 12px;
|
||||||
background: #fff;
|
background: var(--bg-primary);
|
||||||
border-top: 1px solid #eef2f7;
|
border-top: 1px solid var(--border-color);
|
||||||
}
|
}
|
||||||
.projects-graph-toolbar-row {
|
.projects-graph-toolbar-row {
|
||||||
align-items: flex-end;
|
align-items: flex-end;
|
||||||
@@ -31128,8 +31128,9 @@ html[data-theme="dark"] #page-projects .projects-graph-legend-divider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
html[data-theme="dark"] #page-projects .projects-graph-legend-item--node i {
|
html[data-theme="dark"] #page-projects .projects-graph-legend-item--node i {
|
||||||
background: #111827 !important;
|
background: linear-gradient(135deg, #1e293b 0%, #172033 100%) !important;
|
||||||
box-shadow: none !important;
|
border-color: var(--legend-color, #64748b) !important;
|
||||||
|
box-shadow: inset 0 1px 0 rgba(148, 163, 184, 0.06) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
html[data-theme="dark"] #page-projects .projects-settings-intro,
|
html[data-theme="dark"] #page-projects .projects-settings-intro,
|
||||||
|
|||||||
+169
-31
@@ -44,31 +44,115 @@
|
|||||||
const CARD_KEY_FONT =
|
const CARD_KEY_FONT =
|
||||||
'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace';
|
'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace';
|
||||||
|
|
||||||
|
const NODE_TYPE_STYLES = {
|
||||||
|
target: {
|
||||||
|
typeLabel: '目标',
|
||||||
|
typeEn: 'TARGET',
|
||||||
|
accent: { light: '#4F46E5', dark: '#818cf8' },
|
||||||
|
bgEnd: { light: '#F5F3FF', dark: '#1e1b4b' },
|
||||||
|
icon: 'target',
|
||||||
|
},
|
||||||
|
finding: {
|
||||||
|
typeLabel: '发现',
|
||||||
|
typeEn: 'FINDING',
|
||||||
|
accent: { light: '#E11D48', dark: '#fb7185' },
|
||||||
|
bgEnd: { light: '#FFF1F2', dark: '#3f1219' },
|
||||||
|
icon: 'finding',
|
||||||
|
},
|
||||||
|
exploit: {
|
||||||
|
typeLabel: '利用',
|
||||||
|
typeEn: 'EXPLOIT',
|
||||||
|
accent: { light: '#B45309', dark: '#fbbf24' },
|
||||||
|
bgEnd: { light: '#FFFBEB', dark: '#3b2712' },
|
||||||
|
icon: 'vulnerability',
|
||||||
|
},
|
||||||
|
vulnerability: {
|
||||||
|
typeLabel: '漏洞',
|
||||||
|
typeEn: 'VULN',
|
||||||
|
accent: { light: '#9333EA', dark: '#c084fc' },
|
||||||
|
bgEnd: { light: '#F5F3FF', dark: '#2e1065' },
|
||||||
|
icon: 'vuln',
|
||||||
|
},
|
||||||
|
auth: {
|
||||||
|
typeLabel: '认证',
|
||||||
|
typeEn: 'AUTH',
|
||||||
|
accent: { light: '#0D9488', dark: '#2dd4bf' },
|
||||||
|
bgEnd: { light: '#F0FDFA', dark: '#0f2926' },
|
||||||
|
icon: 'default',
|
||||||
|
},
|
||||||
|
infra: {
|
||||||
|
typeLabel: '基础设施',
|
||||||
|
typeEn: 'INFRA',
|
||||||
|
accent: { light: '#64748B', dark: '#94a3b8' },
|
||||||
|
bgEnd: { light: '#F8FAFC', dark: '#1e293b' },
|
||||||
|
icon: 'default',
|
||||||
|
},
|
||||||
|
chain: {
|
||||||
|
typeLabel: '攻击链',
|
||||||
|
typeEn: 'CHAIN',
|
||||||
|
accent: { light: '#7C3AED', dark: '#a78bfa' },
|
||||||
|
bgEnd: { light: '#F5F3FF', dark: '#2e1065' },
|
||||||
|
icon: 'vulnerability',
|
||||||
|
},
|
||||||
|
poc: {
|
||||||
|
typeLabel: 'POC',
|
||||||
|
typeEn: 'POC',
|
||||||
|
accent: { light: '#C2410C', dark: '#fb923c' },
|
||||||
|
bgEnd: { light: '#FFEDD5', dark: '#3b2010' },
|
||||||
|
icon: 'vulnerability',
|
||||||
|
},
|
||||||
|
business: {
|
||||||
|
typeLabel: '业务',
|
||||||
|
typeEn: 'BUSINESS',
|
||||||
|
accent: { light: '#0369A1', dark: '#38bdf8' },
|
||||||
|
bgEnd: { light: '#F0F9FF', dark: '#0c2540' },
|
||||||
|
icon: 'default',
|
||||||
|
},
|
||||||
|
missing: {
|
||||||
|
typeLabel: '缺失',
|
||||||
|
typeEn: 'MISSING',
|
||||||
|
accent: { light: '#CBD5E1', dark: '#64748b' },
|
||||||
|
bgEnd: { light: '#F1F5F9', dark: '#1e293b' },
|
||||||
|
icon: 'default',
|
||||||
|
},
|
||||||
|
note: {
|
||||||
|
typeLabel: '备注',
|
||||||
|
typeEn: 'NOTE',
|
||||||
|
accent: { light: '#94A3B8', dark: '#94a3b8' },
|
||||||
|
bgEnd: { light: '#F8FAFC', dark: '#1e293b' },
|
||||||
|
icon: 'default',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
function isDarkTheme() {
|
||||||
|
return document.documentElement.getAttribute('data-theme') === 'dark';
|
||||||
|
}
|
||||||
|
|
||||||
|
function cardPalette() {
|
||||||
|
const dark = isDarkTheme();
|
||||||
|
return {
|
||||||
|
bgStart: dark ? '#1e293b' : '#FFFFFF',
|
||||||
|
keyColor: dark ? '#94a3b8' : '#64748b',
|
||||||
|
summaryColor: dark ? '#e5e7eb' : '#0f172a',
|
||||||
|
nodeFallbackBg: dark ? '#1e293b' : '#ffffff',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function nodeTheme(type) {
|
function nodeTheme(type) {
|
||||||
switch (type) {
|
const style = NODE_TYPE_STYLES[type] || NODE_TYPE_STYLES.note;
|
||||||
case 'target':
|
const dark = isDarkTheme();
|
||||||
return { typeLabel: '目标', typeEn: 'TARGET', accent: '#4F46E5', bgEnd: '#F5F3FF', icon: 'target' };
|
const palette = cardPalette();
|
||||||
case 'finding':
|
return {
|
||||||
return { typeLabel: '发现', typeEn: 'FINDING', accent: '#E11D48', bgEnd: '#FFF1F2', icon: 'finding', cardStyle: 'default' };
|
typeLabel: style.typeLabel,
|
||||||
case 'exploit':
|
typeEn: style.typeEn,
|
||||||
return { typeLabel: '利用', typeEn: 'EXPLOIT', accent: '#B45309', bgEnd: '#FFFBEB', icon: 'vulnerability', cardStyle: 'default' };
|
accent: dark ? style.accent.dark : style.accent.light,
|
||||||
case 'vulnerability':
|
bgStart: palette.bgStart,
|
||||||
return { typeLabel: '漏洞', typeEn: 'VULN', accent: '#9333EA', bgEnd: '#F5F3FF', icon: 'vuln', cardStyle: 'default' };
|
bgEnd: dark ? style.bgEnd.dark : style.bgEnd.light,
|
||||||
case 'auth':
|
keyColor: palette.keyColor,
|
||||||
return { typeLabel: '认证', typeEn: 'AUTH', accent: '#0D9488', bgEnd: '#F0FDFA', icon: 'default' };
|
summaryColor: palette.summaryColor,
|
||||||
case 'infra':
|
icon: style.icon,
|
||||||
return { typeLabel: '基础设施', typeEn: 'INFRA', accent: '#64748B', bgEnd: '#F8FAFC', icon: 'default' };
|
cardStyle: style.cardStyle || 'default',
|
||||||
case 'chain':
|
};
|
||||||
return { typeLabel: '攻击链', typeEn: 'CHAIN', accent: '#7C3AED', bgEnd: '#F5F3FF', icon: 'vulnerability' };
|
|
||||||
case 'poc':
|
|
||||||
return { typeLabel: 'POC', typeEn: 'POC', accent: '#C2410C', bgEnd: '#FFEDD5', icon: 'vulnerability' };
|
|
||||||
case 'business':
|
|
||||||
return { typeLabel: '业务', typeEn: 'BUSINESS', accent: '#0369A1', bgEnd: '#F0F9FF', icon: 'default' };
|
|
||||||
case 'missing':
|
|
||||||
return { typeLabel: '缺失', typeEn: 'MISSING', accent: '#CBD5E1', bgEnd: '#F1F5F9', icon: 'default' };
|
|
||||||
default:
|
|
||||||
return { typeLabel: '备注', typeEn: 'NOTE', accent: '#94A3B8', bgEnd: '#F8FAFC', icon: 'default' };
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function escapeXml(str) {
|
function escapeXml(str) {
|
||||||
@@ -228,7 +312,10 @@
|
|||||||
function buildNodeCardSvgUrl(theme, layout, confidence) {
|
function buildNodeCardSvgUrl(theme, layout, confidence) {
|
||||||
const { width, height, headerLines, keyLines, summaryLines } = layout;
|
const { width, height, headerLines, keyLines, summaryLines } = layout;
|
||||||
const accent = theme.accent;
|
const accent = theme.accent;
|
||||||
|
const bgStart = theme.bgStart;
|
||||||
const bgEnd = theme.bgEnd;
|
const bgEnd = theme.bgEnd;
|
||||||
|
const keyColor = theme.keyColor;
|
||||||
|
const summaryColor = theme.summaryColor;
|
||||||
const conf = (confidence || '').toLowerCase();
|
const conf = (confidence || '').toLowerCase();
|
||||||
const isTentative = conf === 'tentative';
|
const isTentative = conf === 'tentative';
|
||||||
const isDeprecated = conf === 'deprecated';
|
const isDeprecated = conf === 'deprecated';
|
||||||
@@ -258,14 +345,14 @@
|
|||||||
const keySvg = keyLines
|
const keySvg = keyLines
|
||||||
.map(
|
.map(
|
||||||
(line, i) =>
|
(line, i) =>
|
||||||
`<text x="${CARD_TEXT_X}" y="${keyY + i * CARD_KEY_LH}" font-size="${CARD_KEY_FS}" font-weight="500" fill="#64748b" font-family='${CARD_KEY_FONT}'>${escapeXml(line)}</text>`,
|
`<text x="${CARD_TEXT_X}" y="${keyY + i * CARD_KEY_LH}" font-size="${CARD_KEY_FS}" font-weight="500" fill="${keyColor}" font-family='${CARD_KEY_FONT}'>${escapeXml(line)}</text>`,
|
||||||
)
|
)
|
||||||
.join('');
|
.join('');
|
||||||
|
|
||||||
const summarySvg = summaryLines
|
const summarySvg = summaryLines
|
||||||
.map(
|
.map(
|
||||||
(line, i) =>
|
(line, i) =>
|
||||||
`<text x="${CARD_TEXT_X}" y="${summaryY + i * CARD_SUMMARY_LH}" font-size="${CARD_SUMMARY_FS}" font-weight="600" fill="#0f172a" font-family='${CARD_FONT}'>${escapeXml(line)}</text>`,
|
`<text x="${CARD_TEXT_X}" y="${summaryY + i * CARD_SUMMARY_LH}" font-size="${CARD_SUMMARY_FS}" font-weight="600" fill="${summaryColor}" font-family='${CARD_FONT}'>${escapeXml(line)}</text>`,
|
||||||
)
|
)
|
||||||
.join('');
|
.join('');
|
||||||
|
|
||||||
@@ -275,7 +362,7 @@
|
|||||||
const svg =
|
const svg =
|
||||||
`<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}">` +
|
`<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}">` +
|
||||||
`<defs><linearGradient id="bg" x1="0%" y1="0%" x2="100%" y2="100%">` +
|
`<defs><linearGradient id="bg" x1="0%" y1="0%" x2="100%" y2="100%">` +
|
||||||
`<stop offset="0%" stop-color="#FFFFFF"/><stop offset="100%" stop-color="${bgEnd}"/></linearGradient>` +
|
`<stop offset="0%" stop-color="${bgStart}"/><stop offset="100%" stop-color="${bgEnd}"/></linearGradient>` +
|
||||||
`<clipPath id="textClip"><rect x="${CARD_TEXT_X}" y="${CARD_PAD - 2}" width="${textClipW}" height="${textClipH}"/></clipPath></defs>` +
|
`<clipPath id="textClip"><rect x="${CARD_TEXT_X}" y="${CARD_PAD - 2}" width="${textClipW}" height="${textClipH}"/></clipPath></defs>` +
|
||||||
`<g${isDeprecated ? ' opacity="0.55"' : ''}>` +
|
`<g${isDeprecated ? ' opacity="0.55"' : ''}>` +
|
||||||
`<rect x="0.75" y="0.75" width="${width - 1.5}" height="${height - 1.5}" rx="12" fill="url(#bg)" ${stroke}/>` +
|
`<rect x="0.75" y="0.75" width="${width - 1.5}" height="${height - 1.5}" rx="12" fill="url(#bg)" ${stroke}/>` +
|
||||||
@@ -290,6 +377,52 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function applyNodePalette() {
|
||||||
|
if (!_cy) return;
|
||||||
|
const palette = cardPalette();
|
||||||
|
_cy.style().selector('node').style('background-color', palette.nodeFallbackBg).update();
|
||||||
|
}
|
||||||
|
|
||||||
|
function rebuildNodeCards() {
|
||||||
|
if (!_cy) return;
|
||||||
|
applyNodePalette();
|
||||||
|
_cy.nodes().forEach((nodeEle) => {
|
||||||
|
const d = nodeEle.data();
|
||||||
|
const visualType = d.type || 'note';
|
||||||
|
const theme = nodeTheme(visualType);
|
||||||
|
const statusBadge = d.statusBadge || buildStatusBadge(d.confidence);
|
||||||
|
const layout = computeNodeLayout(
|
||||||
|
visualType,
|
||||||
|
d.summary || '—',
|
||||||
|
statusBadge,
|
||||||
|
theme,
|
||||||
|
d.factKey,
|
||||||
|
);
|
||||||
|
nodeEle.data({
|
||||||
|
cardSvgUrl: buildNodeCardSvgUrl(theme, layout, d.confidence),
|
||||||
|
accentColor: theme.accent,
|
||||||
|
nodeWidth: layout.width,
|
||||||
|
nodeHeight: layout.height,
|
||||||
|
label: layout.searchLabel,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function refreshTheme() {
|
||||||
|
rebuildNodeCards();
|
||||||
|
if (!_cy) return;
|
||||||
|
const accent = isDarkTheme() ? '#60a5fa' : '#4F46E5';
|
||||||
|
_cy.style()
|
||||||
|
.selector('edge:selected')
|
||||||
|
.style({
|
||||||
|
'line-color': accent,
|
||||||
|
'target-arrow-color': accent,
|
||||||
|
})
|
||||||
|
.selector('node:selected')
|
||||||
|
.style('border-color', accent)
|
||||||
|
.update();
|
||||||
|
}
|
||||||
|
|
||||||
function destroy() {
|
function destroy() {
|
||||||
if (_resizeObs) {
|
if (_resizeObs) {
|
||||||
_resizeObs.disconnect();
|
_resizeObs.disconnect();
|
||||||
@@ -487,6 +620,7 @@
|
|||||||
accentColor: theme.accent,
|
accentColor: theme.accent,
|
||||||
statusBadge: statusBadge,
|
statusBadge: statusBadge,
|
||||||
confidence: node.confidence || '',
|
confidence: node.confidence || '',
|
||||||
|
summary: summary,
|
||||||
nodeWidth: layout.width,
|
nodeWidth: layout.width,
|
||||||
nodeHeight: layout.height,
|
nodeHeight: layout.height,
|
||||||
cardSvgUrl: buildNodeCardSvgUrl(theme, layout, node.confidence),
|
cardSvgUrl: buildNodeCardSvgUrl(theme, layout, node.confidence),
|
||||||
@@ -510,6 +644,7 @@
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const palette = cardPalette();
|
||||||
_cy = cytoscape({
|
_cy = cytoscape({
|
||||||
container,
|
container,
|
||||||
elements,
|
elements,
|
||||||
@@ -521,7 +656,7 @@
|
|||||||
width: (ele) => ele.data('nodeWidth') || CARD_MIN_W,
|
width: (ele) => ele.data('nodeWidth') || CARD_MIN_W,
|
||||||
height: (ele) => ele.data('nodeHeight') || CARD_MIN_H,
|
height: (ele) => ele.data('nodeHeight') || CARD_MIN_H,
|
||||||
shape: 'round-rectangle',
|
shape: 'round-rectangle',
|
||||||
'background-color': '#ffffff',
|
'background-color': palette.nodeFallbackBg,
|
||||||
'background-image': (ele) => ele.data('cardSvgUrl') || 'none',
|
'background-image': (ele) => ele.data('cardSvgUrl') || 'none',
|
||||||
'background-width': (ele) => (ele.data('nodeWidth') || CARD_MIN_W) + 'px',
|
'background-width': (ele) => (ele.data('nodeWidth') || CARD_MIN_W) + 'px',
|
||||||
'background-height': (ele) => (ele.data('nodeHeight') || CARD_MIN_H) + 'px',
|
'background-height': (ele) => (ele.data('nodeHeight') || CARD_MIN_H) + 'px',
|
||||||
@@ -549,15 +684,15 @@
|
|||||||
style: {
|
style: {
|
||||||
width: 3.5,
|
width: 3.5,
|
||||||
opacity: 1,
|
opacity: 1,
|
||||||
'line-color': '#4F46E5',
|
'line-color': isDarkTheme() ? '#60a5fa' : '#4F46E5',
|
||||||
'target-arrow-color': '#4F46E5',
|
'target-arrow-color': isDarkTheme() ? '#60a5fa' : '#4F46E5',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
selector: 'node:selected',
|
selector: 'node:selected',
|
||||||
style: {
|
style: {
|
||||||
'border-width': 3,
|
'border-width': 3,
|
||||||
'border-color': '#4F46E5',
|
'border-color': isDarkTheme() ? '#60a5fa' : '#4F46E5',
|
||||||
'border-opacity': 1,
|
'border-opacity': 1,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -674,7 +809,10 @@
|
|||||||
setConnectMode,
|
setConnectMode,
|
||||||
selectEdge,
|
selectEdge,
|
||||||
clearEdgeSelection,
|
clearEdgeSelection,
|
||||||
|
refreshTheme,
|
||||||
nodeTheme,
|
nodeTheme,
|
||||||
resolveGraphNodeType,
|
resolveGraphNodeType,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
document.addEventListener('cyberstrike-themechange', refreshTheme);
|
||||||
})(typeof window !== 'undefined' ? window : globalThis);
|
})(typeof window !== 'undefined' ? window : globalThis);
|
||||||
|
|||||||
@@ -83,6 +83,11 @@
|
|||||||
root.setAttribute('data-theme', resolved);
|
root.setAttribute('data-theme', resolved);
|
||||||
root.style.colorScheme = resolved;
|
root.style.colorScheme = resolved;
|
||||||
updateButton(normalized, resolved);
|
updateButton(normalized, resolved);
|
||||||
|
document.dispatchEvent(
|
||||||
|
new CustomEvent('cyberstrike-themechange', {
|
||||||
|
detail: { preference: normalized, resolved: resolved },
|
||||||
|
}),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function savePreference(preference) {
|
function savePreference(preference) {
|
||||||
|
|||||||
Reference in New Issue
Block a user