Fix remaining frontend security issues (3 medium, 2 low)
- Escape user-input port number in app-selector innerHTML - Replace inline onclick with addEventListener in backup history (HTML entity decode bypass) - Add Content-Security-Policy meta tag with script hash - Replace document.write with textContent for footer year - Filter __proto__/constructor/prototype in Object.assign calls Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -8,6 +8,7 @@
|
||||
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
|
||||
<meta http-equiv="Pragma" content="no-cache" />
|
||||
<meta http-equiv="Expires" content="0" />
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'sha256-6JZtsKK/PZthh+stCmmCvC2QxCiyk6SwZCBjXE+kYr0='; style-src 'self' 'unsafe-inline'; img-src 'self' https://cdn.jsdelivr.net data:; connect-src 'self' https://api.open-meteo.com https://geocoding-api.open-meteo.com; font-src 'self'; object-src 'none'; base-uri 'self'; form-action 'self'">
|
||||
|
||||
<link rel="icon" href="/assets/dashcaddy-favicon.ico" sizes="any">
|
||||
<link rel="icon" type="image/png" sizes="192x192" href="/assets/icon-192.png">
|
||||
@@ -534,13 +535,15 @@
|
||||
if (el) el.style.display = 'none';
|
||||
}
|
||||
}
|
||||
var yr = document.getElementById('footer-year');
|
||||
if (yr) yr.textContent = new Date().getFullYear();
|
||||
})();
|
||||
</script>
|
||||
|
||||
<!-- Clock rendering is handled by the bundled clock.js module -->
|
||||
|
||||
<footer class="dashcaddy-footer">
|
||||
<span class="footer-copy">© <script>document.write(new Date().getFullYear())</script></span>
|
||||
<span class="footer-copy">© <span id="footer-year"></span></span>
|
||||
<img src="/assets/sami7777-logo.png" alt="samiahmed7777" class="footer-logo">
|
||||
</footer>
|
||||
|
||||
|
||||
@@ -587,7 +587,7 @@
|
||||
|
||||
const result = await checkPortAvailability(portToCheck);
|
||||
if (result.available) {
|
||||
portStatus.innerHTML = `<span style="color: #4caf50;">Port ${portToCheck} is available</span>`;
|
||||
portStatus.innerHTML = `<span style="color: #4caf50;">Port ${escapeHtml(String(portToCheck))} is available</span>`;
|
||||
} else {
|
||||
const suggestedPort = await getSuggestedPort(defaultPort);
|
||||
portStatus.innerHTML = `
|
||||
|
||||
@@ -381,7 +381,7 @@
|
||||
<span style="font-weight: 500;">${escapeHtml(bk.name || 'backup')}</span>
|
||||
<div style="display: flex; align-items: center; gap: 8px;">
|
||||
<span class="status-badge ${bk.status === 'success' ? 'success' : 'down'}">${escapeHtml(bk.status)}</span>
|
||||
${bk.status === 'success' ? `<button onclick="window.__restoreServerBackup('${escapeHtml(bk.id)}')" style="padding: 3px 8px; font-size: 0.75rem;">Restore</button>` : ''}
|
||||
${bk.status === 'success' ? `<button class="backup-restore-btn" data-backup-id="${escapeHtml(bk.id)}" style="padding: 3px 8px; font-size: 0.75rem;">Restore</button>` : ''}
|
||||
</div>
|
||||
</div>
|
||||
<div style="font-size: 0.75rem; color: var(--muted);">
|
||||
@@ -392,6 +392,10 @@
|
||||
}
|
||||
html += '</div>';
|
||||
historyContainer.innerHTML = html;
|
||||
// Wire restore buttons with addEventListener (not inline onclick — HTML entity decode bypass)
|
||||
historyContainer.querySelectorAll('.backup-restore-btn').forEach(btn => {
|
||||
btn.addEventListener('click', () => window.__restoreServerBackup(btn.dataset.backupId));
|
||||
});
|
||||
} catch (e) {
|
||||
historyContainer.innerHTML = `<div class="panel-empty" style="color: var(--bad-fg);">Failed: ${escapeHtml(e.message)}</div>`;
|
||||
}
|
||||
|
||||
@@ -47,8 +47,10 @@ const SITE = {
|
||||
SITE.dnsIp = c.dns.ip || '';
|
||||
SITE.dnsPort = c.dns.port || DC.DEFAULTS.DNS_PORT;
|
||||
}
|
||||
if (c.dnsServers) {
|
||||
Object.assign(SITE.dnsServers, c.dnsServers);
|
||||
if (c.dnsServers && typeof c.dnsServers === 'object') {
|
||||
for (const [k, v] of Object.entries(c.dnsServers)) {
|
||||
if (k !== '__proto__' && k !== 'constructor' && k !== 'prototype') SITE.dnsServers[k] = v;
|
||||
}
|
||||
}
|
||||
if (c.configurationType) SITE.configurationType = c.configurationType;
|
||||
if (c.domain) SITE.domain = c.domain;
|
||||
@@ -352,7 +354,12 @@ const AppState = {
|
||||
},
|
||||
updateApp(id, changes) {
|
||||
const app = this._apps.find(a => a.id === id);
|
||||
if (app) { Object.assign(app, changes); DC_BUS.emit('apps:changed', this._apps); }
|
||||
if (app) {
|
||||
for (const [k, v] of Object.entries(changes)) {
|
||||
if (k !== '__proto__' && k !== 'constructor' && k !== 'prototype') app[k] = v;
|
||||
}
|
||||
DC_BUS.emit('apps:changed', this._apps);
|
||||
}
|
||||
return app;
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user