fix: security hardening — self-hosted JS, XSS protection, SSRF blocking

- Replace all CDN script tags (marked, plotly) with self-hosted static files
- Add DOMPurify for sanitizing markdown-rendered HTML
- Add escapeHtml() helper to base.html for all innerHTML operations
- Sanitize dynamic data in innerHTML across 13 templates
- Add security headers (X-Content-Type-Options, X-Frame-Options, Referrer-Policy)
- Add SSRF protection to proposal intake URL fetcher (block private/loopback IPs)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-09 04:47:32 +01:00
parent d1a20fa02e
commit f8ed2b83e9
18 changed files with 94 additions and 42 deletions

View File

@@ -5,7 +5,7 @@
{% block extra_head %}
<script src="/static/js/d3.v7.min.js"></script>
<script src="https://cdn.plot.ly/plotly-2.27.0.min.js"></script>
<script src="/static/js/plotly.min.js"></script>
<style>
#citationSvg {
width: 100%;
@@ -462,7 +462,7 @@ document.querySelectorAll('.tab-btn').forEach(btn => {
<td class="px-4 py-2.5 text-slate-500 text-xs">${i + 1}</td>
<td class="px-4 py-2.5">
<a href="https://www.rfc-editor.org/rfc/rfc${parseInt(rfc.ref_id)}" target="_blank" rel="noopener"
class="text-orange-400 hover:text-orange-300 transition font-medium text-sm">${rfc.title}</a>
class="text-orange-400 hover:text-orange-300 transition font-medium text-sm">${escapeHtml(rfc.title)}</a>
</td>
<td class="px-4 py-2.5 text-right">
<span class="px-2 py-0.5 rounded-full text-xs font-medium
@@ -540,9 +540,9 @@ document.querySelectorAll('.tab-btn').forEach(btn => {
node.on('mouseover', function(event, d) {
const typeLabel = d.type === 'rfc' ? 'RFC' : d.type === 'bcp' ? 'BCP' : 'Draft';
const catLine = d.category ? `<div class="text-slate-500 text-xs mb-1">${d.category}</div>` : '';
const catLine = d.category ? `<div class="text-slate-500 text-xs mb-1">${escapeHtml(d.category)}</div>` : '';
tooltip.innerHTML = `
<div class="font-semibold text-white mb-1">${d.title}</div>
<div class="font-semibold text-white mb-1">${escapeHtml(d.title)}</div>
${catLine}
<div class="flex gap-4 text-xs">
<span class="text-slate-400">${typeLabel}</span>