Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion backend/app/core/notifications.py
Original file line number Diff line number Diff line change
Expand Up @@ -1285,7 +1285,7 @@ def render_generation_session_report_html(
# Summary section
html_parts.append('<div class="section">')
html_parts.append('<h2>Summary</h2>')
html_parts.append(f'<div class="summary-item"><span class="summary-label">Specification:</span> {spec_path}</div>')
html_parts.append(f'<div class="summary-item"><span class="summary-label">Specification:</span> {html.escape(spec_path)}</div>')
html_parts.append(f'<div class="summary-item"><span class="summary-label">Run ID:</span> {generation_id}</div>')
html_parts.append(
f'<div class="summary-item"><span class="summary-label">{variance.label}:</span> '
Expand Down
20 changes: 20 additions & 0 deletions backend/test/api/test_email_notifications.py
Original file line number Diff line number Diff line change
Expand Up @@ -598,6 +598,26 @@ def test_html_builder_is_the_single_source_used_by_email(
assert direct_html == via_notifier_html
assert direct_plain == via_notifier_plain

def test_spec_path_is_html_escaped(
self, mock_db, sample_workspace_docs, sample_estimation_result
):
"""spec_path is a free-form, caller-suppliable string — it must not be
interpolated into the HTML report unescaped (XSS)."""
from app.core.notifications import render_generation_session_report_html

workspace_ids, _ = sample_workspace_docs

html_content, _ = render_generation_session_report_html(
generation_id="est-test-123",
workspace_ids=workspace_ids,
result=sample_estimation_result,
spec_path="<script>alert(1)</script>",
db=mock_db,
)

assert "<script>alert(1)</script>" not in html_content
assert "&lt;script&gt;alert(1)&lt;/script&gt;" in html_content


class TestMultiWorkspaceResultSerialization:
"""Stored Firestore result shape for email resend / P10Y metadata."""
Expand Down