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/05/05 20:50:02 UTC

(tooling-agents) branch main updated: ASVS audit: 1.1.2.md (redacted) [source: apache/steve/v3 @ d0aa7e9]

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 00795ab  ASVS audit: 1.1.2.md (redacted) [source: apache/steve/v3 @ d0aa7e9]
00795ab is described below

commit 00795abcbf94b7c0ad932203fc5f0d34bde08490
Author: ASF Tooling Team <ro...@apache.org>
AuthorDate: Tue May 5 22:49:53 2026 +0200

    ASVS audit: 1.1.2.md (redacted) [source: apache/steve/v3 @ d0aa7e9]
---
 ASVS/reports/steve/v3/d0aa7e9/all/1.1.2.md | 185 +++++++++++++++++++++++++++++
 1 file changed, 185 insertions(+)

diff --git a/ASVS/reports/steve/v3/d0aa7e9/all/1.1.2.md b/ASVS/reports/steve/v3/d0aa7e9/all/1.1.2.md
new file mode 100644
index 0000000..0c4a84f
--- /dev/null
+++ b/ASVS/reports/steve/v3/d0aa7e9/all/1.1.2.md
@@ -0,0 +1,185 @@
+# Security Audit Report: ASVS 1.1.2
+
+## Executive Summary
+
+| Field | Value |
+|-------|-------|
+| **Repository** | apache/steve/v3 |
+| **Audit Date** | May 05, 2026 |
+| **Auditor** | Tooling Agents (bundled-pass mode) |
+| **ASVS Requirement** | ASVS 1.1.2 |
+| **Files Analyzed** | 8 relevant / 49 total |
+| **Files Skipped** | 12 |
+
+**Requirement description:** Verify that the application performs output encoding and escaping either as a final step before being used by the interpreter for which it is intended or by the interpreter itself.
+
+### Findings Overview
+
+| Severity | Count |
+|----------|-------|
+| 🔴 Critical | 0 |
+| 🟠 High | 1 |
+| 🟡 Medium | 1 |
+| 🟢 Low | 0 |
+
+---
+
+## ASVS-1.1.2: Encoding and Sanitization Architecture - Output Encoding as Final Step
+
+### Findings for 1.1.2
+
+#### [HIGH]
+**Finding ID:** ASVS-112-HIGH-001
+
+**File:** `v3/server/pages.py`, function `rewrite_description`, lines 50-59
+
+**Vulnerable Code:**
+```python
+def rewrite_description(issue):
+    """Rewrite issue description: wrap in <pre> and convert doc:filename to links."""
+    import re
+    desc = issue.description
+    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)
+    issue.description = f'<pre>{desc}</pre>'
+```
+
+**Data Flow:** `issue.description` (from database, originally user-input) → raw HTML construction → stored back on object → rendered in template without encoding
+
+**Issue:** Output encoding/escaping is being performed too early (or rather, not at all). The function constructs HTML markup and stores it on the object property. This pre-baked HTML is then output in the template. Since EZT doesn't HTML-encode by default (evidenced by the need for explicit `[format "js,html"]` elsewhere), the description content is never encoded. The "encoding" should happen at template render time, but the function makes this impossible by mixing raw user content with [...]
+
+**Proof of Concept:** If issue description contains: `<img src=x onerror=alert(document.cookie)> doc:test.pdf`
+The output becomes: `<pre><img src=x onerror=alert(document.cookie)> <a href="/docs/IID/test.pdf">test.pdf</a></pre>`
+
+**Impact:** Stored XSS — any voter viewing the ballot page would execute attacker-controlled JavaScript. In a voting system, this could manipulate votes, steal session tokens, or alter ballot appearance.
+
+**Remediation:**
+```python
+import html
+
+def rewrite_description(issue):
+    """Rewrite issue description: wrap in <pre> and convert doc:filename to links."""
+    import re
+    desc = html.escape(issue.description)  # Encode FIRST
+    def repl(match):
+        filename = html.escape(match.group(1))
+        url_filename = urllib.parse.quote(match.group(1))
+        return f'<a href="/docs/{issue.iid}/{url_filename}">{filename}</a>'
+    desc = re.sub(r'doc:([^\s]+)', repl, desc)
+    issue.description = f'<pre>{desc}</pre>'
+```
+
+#### [MEDIUM]
+**Finding ID:** ASVS-112-MED-001
+
+**File:** `v3/server/pages.py`, flash message construction, multiple locations
+
+**Vulnerable Code (example at line ~430):**
+```python
+await flash_success(f'Created election: {form.title}')
+```
+
+**Other instances:**
+- Line ~491: `await flash_success(f'Issue "{form.title}" has been added.')`
+- Line ~513: `await flash_success(f'Issue "{form.title}" has been updated.')`
+- Line ~531: `await flash_success(f'Issue "{issue.title}" has been deleted.')`
+
+**Data Flow:** `form.title` (user-controlled form input) → embedded in flash message string → stored in session → rendered in `flashes.ezt` template
+
+**Issue:** User-controlled input (`form.title`) is embedded in flash messages. If the `flashes.ezt` template outputs these without HTML encoding, and since encoding is not applied at the point of message creation, the encoding responsibility is deferred to the template. This violates the principle of encoding as the "final step before the interpreter" because the control is split across two locations with no guarantee the template applies it.
+
+**Impact:** Reflected XSS via crafted election/issue titles that appear in flash messages.
+
+**Remediation:** Either HTML-encode at flash message creation, or ensure the flashes template uses `[format "html"]`:
+```python
+import html
+await flash_success(f'Created election: {html.escape(form.title)}')
+```
+
+### Security Controls Inventory for 1.1.2
+
+| Control | Location | Coverage |
+|---------|----------|----------|
+| `[format "js,html"]` EZT directive | `manage.ezt` lines ~155, 164; `manage-stv.ezt` similar | Applied for JS onclick parameters ✓ |
+| `escapeHtml()` JS function | `vote-on.ezt` line ~467 | Applied only in DOM manipulation ✓ |
+| `html.escape` or equivalent | Not found | NOT APPLIED to server-side output ✗ |
+| Template-level encoding | EZT base format | NOT configured for auto-HTML-encoding ✗ |
+
+### Positive Patterns for 1.1.2
+
+1. **`[format "js,html"]` usage in manage.ezt:** When embedding values in JavaScript onclick handlers within HTML attributes, the template correctly applies both JS and HTML encoding as the final step (e.g., `[format "js,html"][issues.title][end]`).
+2. **Client-side `escapeHtml()`:** The vote-on template defines and uses an HTML escape function when dynamically inserting candidate names into the DOM.
+3. **SRI on external scripts:** `<script src="/static/js/Sortable.min.js" integrity="sha384-..." crossorigin="anonymous">` ensures script integrity at the point of execution.
+
+---
+
+
+
+## Cross-cutting Architecture Observations
+
+### 1. Inconsistent Encoding Strategy (Type B Pattern)
+The most critical cross-cutting observation is the **inconsistent application of encoding controls**:
+- `manage.ezt` and `manage-stv.ezt` correctly use `[format "js,html"]` for JavaScript-in-HTML contexts
+- `vote-on.ezt` (the voter-facing page) does NOT use any format directives for the same type of data
+
+This is a Type B gap (control exists but not applied) and represents false confidence — developers may believe the voter page is safe because they applied controls elsewhere.
+
+### 2. EZT Template Engine Default Behavior
+The application relies on an EZT template engine that does NOT HTML-encode output by default. This is evidenced by:
+- The `rewrite_description` function constructing raw HTML that must pass through without escaping
+- The selective use of `[format]` directives only in specific contexts
+- No visible `base_format` configuration
+
+This architectural choice means every template variable is a potential XSS vector unless explicitly wrapped in a `[format]` directive.
+
+### 3. Data Storage in Original Form (Positive)
+The application correctly stores data in its original form in the database — no pre-encoding. This prevents double-encoding issues but places the full burden of output encoding on the template layer, which currently has gaps.
+
+### 4. Mixed HTML Construction Patterns
+HTML is constructed in multiple locations:
+- Server-side in Python (`rewrite_description`)
+- Server-side in EZT templates (all templates)
+- Client-side in JavaScript (`makeItem()`)
+
+Each location needs its own encoding strategy, but only the client-side JavaScript consistently applies it.
+
+### 5. State-Changing GET Requests
+The `/do-open/<eid>` and `/do-close/<eid>` endpoints use GET methods for state-changing operations. Combined with the placeholder CSRF tokens, these are trivially exploitable via CSRF (e.g., `<img src="/do-open/KNOWN_EID">`).
+
+---
+
+## Cross-cutting Recommendations
+
+### Immediate (Critical — address before next deployment)
+
+1. **Add `[format "js"]` encoding to ALL JavaScript-embedded values in `vote-on.ezt`** (ASVS-123-CRIT-001, ASVS-121-CRIT-001). This is the highest-priority fix as it affects the voter-facing ballot page and could allow manipulation of election results.
+
+2. **Fix `rewrite_description` to HTML-encode description content** before wrapping in `<pre>` tags (ASVS-112-HIGH-001). Apply `html.escape()` to the description text and `urllib.parse.quote()` to filenames in constructed URLs.
+
+### Short-term (within current sprint)
+
+3. **Establish a template encoding standard:** Either:
+   - Configure EZT with `base_format = FORMAT_HTML` and use `[format "raw"]` only where intentional raw HTML is needed, OR
+   - Audit ALL templates and add explicit `[format "html"]` to every user-derived variable in HTML contexts
+
+4. **Add `[format "html"]` to all unencoded template outputs** including:
+   - `election.title`, `election.owner_name`, `election.authz` in all templates
+   - `issues.title`, `issues.description` in manage templates
+   - Flash message content in `flashes.ezt`
+
+5. **Fix flash messages** to either encode at creation or ensure template-level encoding (ASVS-112-MED-001).
+
+6. **Add explicit validation to `serve_doc`** for the `docname` parameter — restrict to alphanumeric plus safe punctuation (ASVS-111-MED-001).
+
+### Long-term (architectural)
+
+7. **Consider migrating to JSON embedding pattern** for complex JavaScript data: serialize server-side with `json.dumps()` and parse client-side with `JSON.parse()` from a `<script type="application/json">` tag. This is inherently safer than template-based JS construction.
+
+8. **Implement a Content Security Policy (CSP)** header to mitigate XSS impact. At minimum, restrict `script-src` to prevent inline script execution, which would require refactoring inline scripts to external files.
+
+9. **Implement real CSRF protection** — the placeholder tokens provide zero security. Use a proper CSRF library that generates per-session tokens and validates them on all state-changing requests.
+
+10. **Convert state-changing GET endpoints** (`/do-open/`, `/do-close/`) to POST methods with CSRF validation.
+        
\ 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