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 12:08:20 UTC

(tooling-agents) branch main updated: ASVS audit: 4.1.4.md (redacted)

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 bb98bed  ASVS audit: 4.1.4.md (redacted)
bb98bed is described below

commit bb98beda999444e25517751e68addb44870fed3b
Author: ASF Tooling Team <ro...@apache.org>
AuthorDate: Tue May 5 14:07:25 2026 +0200

    ASVS audit: 4.1.4.md (redacted)
---
 ASVS/reports/steve/v3/d0aa7e9/all/4.1.4.md | 199 +++++++++++++++++++++++++++++
 1 file changed, 199 insertions(+)

diff --git a/ASVS/reports/steve/v3/d0aa7e9/all/4.1.4.md b/ASVS/reports/steve/v3/d0aa7e9/all/4.1.4.md
new file mode 100644
index 0000000..b58a52a
--- /dev/null
+++ b/ASVS/reports/steve/v3/d0aa7e9/all/4.1.4.md
@@ -0,0 +1,199 @@
+# Security Audit Report: ASVS 4.1.4
+
+## Executive Summary
+
+| Field | Value |
+|-------|-------|
+| **Repository** | apache/steve/v3 |
+| **Audit Date** | May 05, 2026 |
+| **Auditor** | Tooling Agents (bundled-pass mode) |
+| **ASVS Requirement** | ASVS 4.1.4 |
+| **Files Analyzed** | 9 relevant / 49 total |
+| **Files Skipped** | 12 |
+
+**Requirement description:** Verify that only HTTP methods that are explicitly supported by the application or its API (including OPTIONS during preflight requests) can be used and that unused methods are blocked.
+
+### Findings Overview
+
+| Severity | Count |
+|----------|-------|
+| 🔴 Critical | 0 |
+| 🟠 High | 0 |
+| 🟡 Medium | 1 |
+| 🟢 Low | 1 |
+
+---
+
+## ASVS-4.1.4: HTTP Method Restriction
+
+### Findings for 4.1.4
+
+### [MEDIUM] ASVS-414-MED-001
+**Finding: State-Changing Operations Using GET Method**
+
+- **File:** `v3/server/pages.py`
+- **Location:** `do_open_endpoint()` line 431, `do_close_endpoint()` line 451
+
+**Vulnerable code quote:**
+```python
+@APP.get('/do-open/<eid>')
+@asfquart.auth.require({R.committer})
+@load_election
+async def do_open_endpoint(election):
+    result = await basic_info()
+    ### check authz
+    pdb = steve.persondb.PersonDB.open(DB_FNAME)
+    election.open(pdb)
+    _LOGGER.info(f'User[U:{result.uid}] opened election[E:{election.eid}]')
+    title = election.get_metadata().title
+    await flash_success(f'Opened election: {title}')
+    return quart.redirect(f'/manage/{election.eid}', code=303)
+
+
+@APP.get('/do-close/<eid>')
+@asfquart.auth.require({R.committer})
+@load_election
+async def do_close_endpoint(election):
+    result = await basic_info()
+    ### check authz
+    election.close()
+    _LOGGER.info(f'User[U:{result.uid}] closed election[E:{election.eid}]')
+    title = election.get_metadata().title
+    await flash_success(f'Closed election: {title}')
+    return quart.redirect(f'/manage/{election.eid}', code=303)
+```
+
+- **Data flow:** GET `/do-open/<eid>` → `load_election` loads election → `election.open(pdb)` permanently opens election for voting → GET request performs irreversible state change
+- **Proof of concept:**
+```html
+<!-- CSRF via image tag or link prefetch -->
+<img src="https://steve.apache.org/do-close/target-election-id" style="display:none">
+<!-- Or browser prefetching -->
+<link rel="prefetch" href="https://steve.apache.org/do-open/target-eid">
+```
+A browser prefetcher, search engine crawler, or CSRF attack via `<img>` tag could trigger these state-changing operations because they're registered as GET endpoints.
+
+- **Impact:** Irreversible election state changes (opening/closing elections) can be triggered by:
+  1. Browser prefetch mechanisms
+  2. Link preview bots
+  3. CSRF attacks using `<img>` tags (no POST form needed)
+  4. Search engine crawlers
+  This is critical for election integrity — an election could be prematurely opened or closed.
+
+- **Gap Type:** Type A — GET is explicitly used where only POST should be allowed.
+
+**Remediation:**
+```python
+@APP.post('/do-open/<eid>')
+@asfquart.auth.require({R.committer})
+@load_election
+async def do_open_endpoint(election):
+    # ... same logic ...
+    return quart.redirect(f'/manage/{election.eid}', code=303)
+
+@APP.post('/do-close/<eid>')
+@asfquart.auth.require({R.committer})
+@load_election
+async def do_close_endpoint(election):
+    # ... same logic ...
+    return quart.redirect(f'/manage/{election.eid}', code=303)
+```
+
+Update the corresponding JavaScript in templates:
+```javascript
+// manage.ezt - Change from window.location.href to form POST
+confirmOpen.addEventListener('click', () => {
+    openModal.hide();
+    const form = document.createElement('form');
+    form.method = 'POST';
+    form.action = '/do-open/[eid]';
+    form.innerHTML = '<input type="hidden" name="csrf_token" value="' + csrfToken + '">';
+    document.body.appendChild(form);
+    form.submit();
+});
+```
+
+### [LOW] ASVS-414-LOW-001
+**Finding: No Explicit OPTIONS Method Handling for CORS Preflight**
+
+- **File:** `v3/server/pages.py`
+- **Location:** Application-wide
+
+**Analysis:** The application uses JSON API endpoints (`/do-set-open_at/<eid>`, `/do-set-close_at/<eid>`) called via `fetch()` from JavaScript. If the application ever serves cross-origin requests, OPTIONS preflight handling would be needed. Currently, no explicit OPTIONS handler or CORS configuration is present.
+
+- **Impact:** Low — the application appears to be same-origin only. However, if CORS is ever needed, unsupported OPTIONS requests would fail silently.
+- **Gap Type:** Type A — No explicit OPTIONS handling, though may not be needed for same-origin.
+
+**Remediation:** If CORS is not needed (same-origin), this is acceptable. If ever needed:
+```python
+from quart_cors import cors
+app = cors(app, allow_origin="https://steve.apache.org")
+```
+
+### Security Controls Inventory for 4.1.4
+
+| Control | Location | Coverage Status |
+|---------|----------|----------------|
+| Route-specific method decorators (`@APP.get`, `@APP.post`) | All endpoints in `pages.py` | Applied to all routes |
+| Framework 405 response for unsupported methods | Quart framework default | **Implicit** (framework handles) |
+| OPTIONS/CORS handling | Not found | Not applicable (same-origin) |
+| GET-only for state-changing operations | Lines 431, 451 | **VIOLATION** — should be POST |
+
+### Positive Patterns for 4.1.4
+
+- All POST endpoints correctly use `@APP.post()`: `do-set-open_at`, `do-set-close_at`, `do-vote`, `do-create-election`, `do-add-issue`, `do-edit-issue`, `do-delete-issue`.
+- Quart framework automatically returns `405 Method Not Allowed` for requests using methods not registered on a route.
+- Read-only pages correctly use `@APP.get()`.
+- The `do-vote` endpoint correctly uses POST for vote submission.
+
+---
+
+
+
+## Cross-cutting Architecture Observations
+
+1. **CSRF Protection is Completely Non-Functional:** The `basic_info()` function sets `basic.csrf_token = 'placeholder'` (line 83). This value is rendered in templates and sent in `X-CSRFToken` headers, but **no server-side validation exists**. This is a Type B gap (control exists in UI but is never validated), creating false confidence. All state-changing endpoints are vulnerable to CSRF, compounded by the GET-method issue on open/close.
+
+2. **Authorization Gaps (Pervasive):** Multiple `### check authz` placeholders indicate that fine-grained authorization (e.g., "is this user the owner of this election?") is NOT implemented. Any authenticated committer can manage/open/close any election. This is explicitly acknowledged in code comments but represents a critical security gap for a voting system.
+
+3. **Defense-in-Depth Layers Missing:** The application lacks:
+   - Security response headers (CSP, X-Content-Type-Options, X-Frame-Options)
+   - Rate limiting
+   - Request size limits
+   - Audit trail integrity
+
+4. **Deployment Architecture Dependency:** The config comment "a proxy sits in front of this server" means some controls (HSTS, HTTP→HTTPS redirect, proxy header trust) might be implemented at the infrastructure layer. However, the application provides no documentation, verification, or enforcement of these assumptions.
+
+5. **Template vs API Endpoint Confusion:** The application mixes template-rendered pages with JSON API endpoints (`/do-set-open_at`, `/do-set-close_at`) without clear architectural separation. This makes it difficult to apply appropriate security policies (Content-Type, redirect behavior, CORS) per endpoint type.
+
+---
+
+## Cross-cutting Recommendations
+
+### Immediate (Critical — must fix before production)
+1. **Implement CSRF protection:** Replace `'placeholder'` token with a cryptographically random token tied to the session. Validate on all POST endpoints. Consider using `quart-csrf` or similar library.
+2. **Change `/do-open` and `/do-close` to POST methods** to prevent CSRF via GET and accidental triggering by prefetchers/crawlers.
+3. **Add HSTS header with preload directive** and submit domain to hstspreload.org.
+4. **Add charset to all responses:** Fix `raise_404()` and add an `after_request` handler ensuring all `text/*` responses include `; charset=utf-8`.
+
+### Short-term (High — fix within current sprint)
+5. **Configure trusted proxy headers:** Add Hypercorn `forwarded_allow_ips` or application-level middleware to prevent header spoofing.
+6. **Implement HTTP rejection for API endpoints:** Non-browser endpoints should return 403 on HTTP rather than redirecting.
+7. **Add browser feature detection:** Implement client-side checks that warn users if their browser cannot support the voting interface.
+8. **Add security headers via `after_request` middleware:**
+   ```python
+   @app.after_request
+   async def security_headers(response):
+       response.headers['X-Content-Type-Options'] = 'nosniff'
+       response.headers['X-Frame-Options'] = 'DENY'
+       response.headers['Strict-Transport-Security'] = 'max-age=63072000; includeSubDomains; preload'
+       response.headers['Content-Security-Policy'] = "default-src 'self'"
+       return response
+   ```
+
+### Long-term (Medium — plan for next release)
+9. **Implement fine-grained authorization** (replace all `### check authz` placeholders) — verify election ownership before allowing management operations.
+10. **Architectural separation:** Create distinct middleware chains for browser-facing pages vs. JSON API endpoints with different security policies.
+11. **Document deployment requirements:** Create a security deployment guide specifying required proxy configuration, TLS requirements, and header trust settings.
+12. **Add integration tests** that verify security headers, method restrictions, and CSRF protection are functioning correctly in the deployed configuration.
+        
\ 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