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

@@ -135,7 +135,8 @@
{% endblock %}
{% block extra_scripts %}
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
<script src="/static/js/marked.min.js"></script>
<script src="/static/js/purify.min.js"></script>
<script>
let rawMarkdown = '';
@@ -186,7 +187,7 @@ document.getElementById('generateForm').addEventListener('submit', async (e) =>
tagsList.appendChild(span);
});
document.getElementById('markdownPreview').innerHTML = marked.parse(data.content);
document.getElementById('markdownPreview').innerHTML = DOMPurify.sanitize(marked.parse(data.content));
document.getElementById('previewLoading').classList.add('hidden');
document.getElementById('previewContent').classList.remove('hidden');
document.getElementById('previewActions').classList.remove('hidden');