修复安全边界问题

This commit is contained in:
CN-JS-HuiBai
2026-04-09 13:37:47 +08:00
parent 09f20ec81d
commit 60d8a3d550
4 changed files with 212 additions and 65 deletions

View File

@@ -623,10 +623,11 @@
}
function updateFavicon(url) {
if (!url) return;
const safeUrl = sanitizeAssetUrl(url);
if (!safeUrl) return;
const link = dom.siteFavicon || document.querySelector("link[rel*='icon']");
if (link) {
link.href = url;
link.href = safeUrl;
}
}
@@ -675,6 +676,51 @@
}
}
function promptLogin(message = '该操作需要登录') {
openLoginModal();
if (message) {
dom.loginError.textContent = message;
dom.loginError.style.display = 'block';
}
}
function sanitizeAssetUrl(url) {
if (!url || typeof url !== 'string') return null;
const trimmed = url.trim();
if (!trimmed) return null;
if (/^(https?:|data:image\/|\/)/i.test(trimmed)) return trimmed;
return null;
}
function renderLogoImage(url) {
if (!dom.logoIconContainer) return;
const safeUrl = sanitizeAssetUrl(url);
if (safeUrl) {
const img = document.createElement('img');
img.src = safeUrl;
img.alt = 'Logo';
img.className = 'logo-icon-img';
dom.logoIconContainer.replaceChildren(img);
return;
}
dom.logoIconContainer.innerHTML = `
<svg class="logo-icon" id="logoSvg" viewBox="0 0 32 32" fill="none">
<rect x="2" y="2" width="28" height="28" rx="8" stroke="url(#logoGrad)" stroke-width="2.5"/>
<path d="M8 22 L12 14 L16 18 L20 10 L24 16" stroke="url(#logoGrad)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" fill="none"/>
<circle cx="12" cy="14" r="2" fill="url(#logoGrad)"/>
<circle cx="20" cy="10" r="2" fill="url(#logoGrad)"/>
<defs>
<linearGradient id="logoGrad" x1="0" y1="0" x2="32" y2="32">
<stop offset="0%" stop-color="#6366f1"/>
<stop offset="100%" stop-color="#06b6d4"/>
</linearGradient>
</defs>
</svg>
`;
}
function openLoginModal() {
dom.loginModal.classList.add('active');
dom.loginError.style.display = 'none';
@@ -1479,6 +1525,11 @@
try {
const url = `/api/metrics/server-details?instance=${encodeURIComponent(instance)}&job=${encodeURIComponent(job)}&source=${encodeURIComponent(source)}`;
const response = await fetch(url);
if (response.status === 401) {
closeServerDetail();
promptLogin('登录后可查看服务器详细指标');
return;
}
if (!response.ok) throw new Error('Fetch failed');
const data = await response.json();
currentServerDetail.memTotal = data.memTotal;
@@ -1697,6 +1748,10 @@
}
const res = await fetch(url);
if (res.status === 401) {
promptLogin('登录后可查看历史曲线与服务器详细信息');
return;
}
if (!res.ok) throw new Error('Query failed');
const data = await res.json();
@@ -1883,25 +1938,7 @@
logoToUse = settings.logo_url_dark;
}
if (logoToUse) {
dom.logoIconContainer.innerHTML = `<img src="${escapeHtml(logoToUse)}" alt="Logo" class="logo-icon-img">`;
} else {
// Restore default SVG
dom.logoIconContainer.innerHTML = `
<svg class="logo-icon" id="logoSvg" viewBox="0 0 32 32" fill="none">
<rect x="2" y="2" width="28" height="28" rx="8" stroke="url(#logoGrad)" stroke-width="2.5"/>
<path d="M8 22 L12 14 L16 18 L20 10 L24 16" stroke="url(#logoGrad)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" fill="none"/>
<circle cx="12" cy="14" r="2" fill="url(#logoGrad)"/>
<circle cx="20" cy="10" r="2" fill="url(#logoGrad)"/>
<defs>
<linearGradient id="logoGrad" x1="0" y1="0" x2="32" y2="32">
<stop offset="0%" stop-color="#6366f1"/>
<stop offset="100%" stop-color="#06b6d4"/>
</linearGradient>
</defs>
</svg>
`;
}
renderLogoImage(logoToUse || null);
// Favicon
updateFavicon(settings.favicon_url);
@@ -2021,6 +2058,10 @@
async function loadLatencyRoutes() {
try {
const response = await fetch('/api/latency-routes');
if (response.status === 401) {
promptLogin('登录后可管理延迟线路');
return;
}
const routes = await response.json();
renderLatencyRoutes(routes);
} catch (err) {
@@ -2253,6 +2294,10 @@
async function loadSources() {
try {
const response = await fetch('/api/sources');
if (response.status === 401) {
promptLogin('登录后可查看和管理数据源');
return;
}
const sources = await response.json();
const sourcesArray = Array.isArray(sources) ? sources : [];
const promSources = sourcesArray.filter(s => s.type !== 'blackbox');
@@ -2484,6 +2529,9 @@
async function loadSourceCount() {
try {
const response = await fetch('/api/sources');
if (response.status === 401) {
return;
}
const sources = await response.json();
const sourcesArray = Array.isArray(sources) ? sources : [];
const promSources = sourcesArray.filter(s => s.type !== 'blackbox');