Spaces:
Sleeping
Sleeping
File size: 2,771 Bytes
6424951 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 | """HTML sanitization utilities for XSS protection."""
import html
from typing import Literal
import streamlit as st
# Valid heading levels
HeadingLevel = Literal[1, 2, 3, 4, 5, 6]
def escape_html(text: str) -> str:
"""Escape HTML special characters to prevent XSS.
Args:
text: Raw text that may contain HTML
Returns:
Escaped text safe for HTML insertion
"""
return html.escape(str(text))
def safe_heading(
text: str,
level: HeadingLevel = 1,
color: str = "steelblue",
align: str = "center",
) -> None:
"""Render a heading with escaped text to prevent XSS.
Args:
text: Heading text (will be escaped)
level: Heading level 1-6
color: CSS color value
align: CSS text-align value
"""
# Escape all user-provided values
safe_text = escape_html(text)
safe_color = escape_html(color)
safe_align = escape_html(align)
st.markdown(
f"<h{level} style='text-align: {safe_align}; color: {safe_color};'>"
f"{safe_text}</h{level}>",
unsafe_allow_html=True,
)
def safe_paragraph(
text: str,
color: str = "white",
align: str = "center",
) -> None:
"""Render a paragraph with escaped text to prevent XSS.
Args:
text: Paragraph text (will be escaped)
color: CSS color value
align: CSS text-align value
"""
safe_text = escape_html(text)
safe_color = escape_html(color)
safe_align = escape_html(align)
st.markdown(
f"<p style='text-align: {safe_align}; color: {safe_color};'>"
f"{safe_text}</p>",
unsafe_allow_html=True,
)
def safe_styled_text(
text: str,
tag: str = "span",
color: str | None = None,
align: str | None = None,
**styles: str,
) -> str:
"""Generate HTML string with escaped text and validated styles.
Args:
text: Text content (will be escaped)
tag: HTML tag to use
color: Optional CSS color
align: Optional CSS text-align
**styles: Additional CSS properties
Returns:
Safe HTML string
"""
safe_text = escape_html(text)
safe_tag = escape_html(tag)
style_parts: list[str] = []
if color:
style_parts.append(f"color: {escape_html(color)}")
if align:
style_parts.append(f"text-align: {escape_html(align)}")
for prop, value in styles.items():
# Convert underscores to hyphens for CSS properties
css_prop = prop.replace("_", "-")
style_parts.append(f"{escape_html(css_prop)}: {escape_html(value)}")
style_str = "; ".join(style_parts)
if style_str:
return f"<{safe_tag} style='{style_str}'>{safe_text}</{safe_tag}>"
return f"<{safe_tag}>{safe_text}</{safe_tag}>"
|