-
Notifications
You must be signed in to change notification settings - Fork 53
Description
PDD-CLI Bug: Incorrectly Applies HTML Escaping to Template Output
PDD-CLI applies HTML escaping (html.escape()) to content that's already been safely rendered by template engines, resulting in visible escape sequences like < instead of < in output.
Why this matters: Rendered emails/HTML show escaped tags as text instead of rendering as HTML.
Concrete Example
For email template rendering:
# PDD generated (WRONG):
import html
from jinja2 import Template
def render_email(template_str: str, data: dict) -> str:
template = Template(template_str)
body = template.render(**data)
# WRONG: Escaping already-safe template output!
return html.escape(body)Usage:
template = "<h1>Hello {{name}}</h1>"
email_body = render_email(template, {'name': 'Alice'})
# Result: "<h1>Hello Alice</h1>" ← Shows as text, not HTML!What went wrong: PDD escaped the rendered template output. Template engines like Jinja2 already handle escaping of variables ({{name}}). Escaping the final output double-escapes everything.
Impact: Emails display with visible HTML tags: <h1>Hello Alice</h1> instead of rendering as headings.
Why PDD Makes This Mistake
PDD-CLI currently:
- Applies security escaping broadly "to be safe"
- Doesn't distinguish between:
- User input (needs escaping)
- Template output (already safe)
- Treats all string concatenation as potentially unsafe
But it should:
- Trust template engine output (Jinja2, React, etc. handle escaping)
- Only escape raw user input BEFORE passing to templates
- Understand context: templates vs raw strings
How to Prevent This in PDD-CLI
What PDD should do differently:
-
Don't escape template engine output:
# Template engines handle escaping body = template.render(**data) # Already safe return body # Don't escape again
-
Escape user input BEFORE templates:
# If passing raw HTML (rare case) from markupsafe import Markup safe_content = Markup.escape(user_input) body = template.render(content=safe_content)
-
Learn template engine security models: Understand which frameworks auto-escape and which don't.
Example improvement:
Current: Render template → escape output
→ Shows HTML tags as text
→ Broken emails
Improved: Render template → return directly
→ (Template engine already escaped variables)
→ Proper HTML rendering
Severity
P2 - Medium Priority
- Frequency: Low - only affects template rendering
- Impact: High - completely breaks HTML rendering (shows source)
- Detectability: High - immediately visible in output
- Prevention cost: Low - just remove incorrect escaping
Category
incomplete-implementation
Related Issues
- Auto-fix skips fingerprint save causing incomplete metadata (sync_orchestration.py:1350) #430 - Missing environment configuration (different completeness issue)
- Add failing tests for #430: auto-fix fingerprint skip bug #432 - Data models without extraction logic (incomplete implementation)
For Contributors: Discovered in email rendering where html.escape() was applied to Jinja2 template output, showing escaped HTML in emails, fixed in commit 34a651d5.