Skip to content

Commit dd7c65d

Browse files
abrichrclaude
andcommitted
fix: Add XSS protection to PageBuilder
Escape user-provided titles and subtitles using html.escape() to prevent cross-site scripting (XSS) attacks when rendering user content in HTML. - Escape page title in <title> tag - Escape header title and subtitle - Escape section titles Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent a7afa80 commit dd7c65d

1 file changed

Lines changed: 11 additions & 4 deletions

File tree

src/openadapt_viewer/builders/page_builder.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
from __future__ import annotations
77

8+
import html
89
from pathlib import Path
910
from typing import Any
1011

@@ -77,13 +78,15 @@ def add_header(
7778
Returns:
7879
Self for chaining
7980
"""
80-
subtitle_html = f'<p style="font-size: 0.85rem; color: var(--oa-text-secondary);">{subtitle}</p>' if subtitle else ""
81+
# Escape title and subtitle for XSS prevention
82+
safe_title = html.escape(title)
83+
subtitle_html = f'<p style="font-size: 0.85rem; color: var(--oa-text-secondary);">{html.escape(subtitle)}</p>' if subtitle else ""
8184

8285
self._header_html = f'''
8386
<header style="padding: 16px 24px; background: var(--oa-bg-secondary); border-bottom: 1px solid var(--oa-border-color); margin-bottom: 24px;">
8487
<div style="display: flex; align-items: center; justify-content: space-between;">
8588
<div>
86-
<h1 style="font-size: 1.25rem; font-weight: 600; margin: 0;">{title}</h1>
89+
<h1 style="font-size: 1.25rem; font-weight: 600; margin: 0;">{safe_title}</h1>
8790
{subtitle_html}
8891
</div>
8992
<div style="display: flex; align-items: center; gap: 16px;">
@@ -128,7 +131,8 @@ def add_section(
128131
Self for chaining
129132
"""
130133
extra_class = f" {class_name}" if class_name else ""
131-
title_html = f'<h2 style="font-size: 1.1rem; font-weight: 600; margin-bottom: 16px;">{title}</h2>' if title else ""
134+
# Escape title for XSS prevention
135+
title_html = f'<h2 style="font-size: 1.1rem; font-weight: 600; margin-bottom: 16px;">{html.escape(title)}</h2>' if title else ""
132136

133137
self._sections.append(f'''
134138
<section class="oa-section{extra_class}" style="margin-bottom: 24px;">
@@ -216,12 +220,15 @@ def render(self) -> str:
216220
</script>
217221
'''
218222

223+
# Escape title for XSS prevention
224+
safe_title = html.escape(self.title)
225+
219226
return f'''<!DOCTYPE html>
220227
<html lang="en">
221228
<head>
222229
<meta charset="UTF-8">
223230
<meta name="viewport" content="width=device-width, initial-scale=1.0">
224-
<title>{self.title}</title>
231+
<title>{safe_title}</title>
225232
{scripts_html}
226233
<style>
227234
{core_css}

0 commit comments

Comments
 (0)