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:18:51 UTC

(tooling-agents) branch main updated: ASVS audit: 2.4.1.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 c5ead3d  ASVS audit: 2.4.1.md (redacted)
c5ead3d is described below

commit c5ead3dff6b3d11a1349237c4fd4a6c94bbcd8d5
Author: ASF Tooling Team <ro...@apache.org>
AuthorDate: Tue May 5 14:06:17 2026 +0200

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

diff --git a/ASVS/reports/steve/v3/d0aa7e9/all/2.4.1.md b/ASVS/reports/steve/v3/d0aa7e9/all/2.4.1.md
new file mode 100644
index 0000000..504b2fc
--- /dev/null
+++ b/ASVS/reports/steve/v3/d0aa7e9/all/2.4.1.md
@@ -0,0 +1,276 @@
+# Security Audit Report: ASVS 2.4.1
+
+## Executive Summary
+
+| Field | Value |
+|-------|-------|
+| **Repository** | apache/steve/v3 |
+| **Audit Date** | May 05, 2026 |
+| **Auditor** | Tooling Agents (bundled-pass mode) |
+| **ASVS Requirement** | ASVS 2.4.1 |
+| **Files Analyzed** | 17 relevant / 49 total |
+| **Files Skipped** | 12 |
+
+**Requirement description:** Verify that anti-automation controls are in place to protect against excessive calls to application functions that could lead to data exfiltration, garbage-data creation, quota exhaustion, rate-limit breaches, denial-of-service, or overuse of costly resources.
+
+### Findings Overview
+
+| Severity | Count |
+|----------|-------|
+| 🔴 Critical | 0 |
+| 🟠 High | 2 |
+| 🟡 Medium | 2 |
+| 🟢 Low | 0 |
+
+---
+
+## ASVS-2.4.1: Anti-Automation Controls
+
+### Findings for 2.4.1
+
+### [HIGH]
+
+**Finding ID**: ASVS-241-HIGH-001
+**Title**: No Rate Limiting on Vote Submission Endpoint
+
+**Location**: `v3/server/pages.py`, `do_vote_endpoint()` (line 372)
+
+**Vulnerable code**:
+```python
+@APP.post('/do-vote/<eid>')
+@asfquart.auth.require({R.committer})
+@load_election
+async def do_vote_endpoint(election):
+    ...
+    for iid, votestring in votes.items():
+        ...
+        election.add_vote(result.uid, iid, votestring)
+    ...
+```
+
+**Data flow**: Authenticated user → unlimited POST `/do-vote/<eid>` requests → unlimited vote rows inserted → storage exhaustion
+
+**Proof of Concept**:
+```bash
+# Automated script by authenticated attacker
+for i in $(seq 1 100000); do
+  curl -X POST https://vote.example.org/do-vote/abc1234567 \
+    -H "Cookie: session=valid" \
+    -d "vote-issue123456=y"
+done
+```
+
+Each request adds a new row to the `vote` table. With 100,000 iterations, the table grows by 100,000 rows per issue. Since only the latest vote counts, all but the last are dead weight consuming storage and slowing tally operations.
+
+**Impact**: Storage exhaustion (denial of service), tally performance degradation, potential database file size limits exceeded. The vote table could grow unboundedly.
+
+**Remediation**:
+```python
+from quart_rate_limiter import RateLimiter, rate_limit
+
+rate_limiter = RateLimiter(APP)
+
+@APP.post('/do-vote/<eid>')
+@asfquart.auth.require({R.committer})
+@rate_limit(10, timedelta(minutes=1))  # 10 votes per minute per user
+@load_election
+async def do_vote_endpoint(election):
+    ...
+```
+
+---
+
+### [HIGH]
+
+**Finding ID**: ASVS-241-HIGH-002
+**Title**: No Rate Limiting on Election Management Operations
+
+**Location**: `v3/server/pages.py`, all management endpoints
+
+**Vulnerable code**:
+```python
+@APP.post('/do-create-election')
+@asfquart.auth.require({R.pmc_member})
+async def do_create_endpoint():
+    ...
+    election = steve.election.Election.create(DB_FNAME, form.title, result.uid)
+    ...
+
+@APP.post('/do-add-issue/<eid>')
+@asfquart.auth.require({R.committer})
+@load_election
+async def do_add_issue_endpoint(election):
+    ...
+    iid = election.add_issue(form.title, form.description, vtype, kv)
+    ...
+```
+
+**Data flow**: Authenticated user → unlimited POST requests → unlimited elections/issues created → database/storage exhaustion
+
+**Proof of Concept**:
+```bash
+for i in $(seq 1 10000); do
+  curl -X POST https://vote.example.org/do-create-election \
+    -H "Cookie: session=valid" \
+    -d "title=Spam+Election+$i"
+done
+```
+
+**Impact**: Garbage data creation (quota exhaustion), database bloat. Each election creation generates a unique 10-hex-character EID and a database row. Thousands of spam elections could degrade performance and confuse administrators.
+
+**Remediation**: Apply rate limiting per user on all mutating endpoints.
+
+---
+
+### [MEDIUM]
+
+**Finding ID**: ASVS-241-MED-001
+**Title**: No Rate Limiting on Authentication-Gated Page Views
+
+**Location**: `v3/server/pages.py`, `voter_page()` (line 133), `admin_page()` (line 279), `vote_on_page()` (line 221)
+
+**Vulnerable code**:
+```python
+@APP.get('/voter')
+@asfquart.auth.require({R.committer})
+@APP.use_template(TEMPLATES / 'voter.ezt')
+async def voter_page():
+    ...
+    with asfpy.stopwatch.Stopwatch():
+        election = steve.election.Election.open_to_pid(DB_FNAME, result.uid)
+        owned = steve.election.Election.owned_elections(DB_FNAME, result.uid)
+    ...
+```
+
+**Data flow**: Authenticated user → unlimited GET requests → repeated database queries → resource exhaustion
+
+**Impact**: Each page view triggers multiple database queries (with JOINs across mayvote, issue, election, and person tables). High-frequency requests from an authenticated user could cause denial of service via query overload on the SQLite database.
+
+**Remediation**: Apply general rate limiting at the application or reverse-proxy level.
+
+---
+
+### [MEDIUM]
+
+**Finding ID**: ASVS-241-MED-002
+**Title**: No CSRF Protection Enables Automated Cross-Site Attacks
+
+**Location**: `v3/server/pages.py`, `basic_info()` (line 83)
+
+**Vulnerable code**:
+```python
+async def basic_info():
+    ...
+    ### generate a real token and store in the session
+    basic.csrf_token = 'placeholder'
+    return basic
+```
+
+**Data flow**: Attacker page → triggers form submission to `/do-vote/<eid>` → no CSRF token validation → automated voting on behalf of victims
+
+**Proof of Concept**:
+```html
+<!-- On attacker's website -->
+<form id="f" method="POST" action="https://vote.example.org/do-vote/abc1234567">
+  <input name="csrf_token" value="placeholder">
+  <input name="vote-issue123456" value="y">
+</form>
+<script>document.getElementById('f').submit();</script>
+```
+
+Since the CSRF token is always "placeholder" and is never validated server-side, any website can submit forms on behalf of authenticated users.
+
+**Impact**: Automated vote manipulation at scale — an attacker could cause all visitors to their page who are authenticated ASF committers to unknowingly cast votes.
+
+**Remediation**:
+```python
+import secrets
+
+async def basic_info():
+    ...
+    s = await asfquart.session.read()
+    if s:
+        if 'csrf_token' not in s:
+            s['csrf_token'] = secrets.token_hex(32)
+            await asfquart.session.write(s)
+        basic.csrf_token = s['csrf_token']
+    return basic
+
+# Decorator for POST endpoints:
+def verify_csrf(func):
+    @functools.wraps(func)
+    async def wrapper(*args, **kwargs):
+        s = await asfquart.session.read()
+        form = await quart.request.form
+        if form.get('csrf_token') != s.get('csrf_token'):
+            quart.abort(403, 'CSRF validation failed')
+        return await func(*args, **kwargs)
+    return wrapper
+```
+
+### Security Controls Inventory for 2.4.1
+
+| Control | Location | Coverage Status |
+|---------|----------|-----------------|
+| Rate limiting | Not present | NOT IMPLEMENTED anywhere |
+| CSRF token | `pages.py` line 83 | DEFINED — placeholder only, NOT VALIDATED |
+| Authentication requirement | `pages.py` all endpoints | APPLIED — limits to authenticated users |
+| `R.committer` / `R.pmc_member` | `pages.py` | APPLIED — limits attack surface to authenticated users |
+| Stopwatch monitoring | `pages.py` lines 147, 287 | APPLIED — monitors but doesn't limit |
+
+### Positive Patterns for 2.4.1
+
+- All mutating operations require authentication, which limits the attack surface to authenticated users only.
+- The `R.pmc_member` requirement for election creation provides a higher barrier than general operations.
+- The re-voting design (append-only, latest wins) means automated re-voting doesn't change the election outcome — only the final vote counts. However, it still wastes storage.
+
+---
+
+
+
+## Cross-cutting Architecture Observations
+
+1. **Type B Gaps Dominate**: The most critical pattern is controls that EXIST in the codebase (owner_pid, authz, CSRF tokens, state validation) but are NOT APPLIED at the enforcement point. This creates false confidence — the schema and comments suggest security is in place, but enforcement is absent.
+
+2. **Assert vs. Exception Pattern Inconsistency**: The codebase uses `assert` for state checks in some methods (`open()`, `close()`, `add_issue()`) but proper exceptions in others (`add_vote()` via `_all_metadata(required_state)`). This inconsistency means security enforcement depends on which code path is taken.
+
+3. **GET for State Changes**: The use of GET requests for irreversible operations (`/do-open/<eid>`, `/do-close/<eid>`) combines with the CSRF gap to create a particularly dangerous attack surface.
+
+4. **Single-Layer Defense**: Security controls exist primarily at the model layer (`election.py`) with minimal web-layer enforcement. The web layer (pages.py) trusts that the model will enforce constraints, but the model uses bypassable assertions.
+
+5. **SQLite Concurrency Model**: The application relies on SQLite's implicit serialization for concurrency safety, but the check-then-act patterns in the application layer create TOCTOU windows that could be exploited in multi-process deployments.
+
+6. **Audit Trail Strength**: Logging of user actions is consistently implemented, providing after-the-fact accountability even where preventive controls are missing.
+
+## Cross-cutting Recommendations
+
+### Immediate (P0 — Address Before Production Use)
+
+1. **Replace all `assert` statements** with proper exception raising (`ElectionBadState`) in `election.py`. This is the most critical fix as it affects all state enforcement.
+
+2. **Implement authorization checks** in all management endpoints — compare `result.uid` against `election.owner_pid`/`election.authz`. This closes the most impactful Type B gap.
+
+3. **Implement CSRF protection** — generate real tokens per session and validate them in all POST endpoints. Change `/do-open/` and `/do-close/` to POST methods.
+
+4. **Add vote string validation** in `add_vote()` — validate against issue type before encryption.
+
+### Short-term (P1 — Within Next Sprint)
+
+5. **Wrap vote submission in a transaction** — ensure atomic ballot submission in `do_vote_endpoint()`.
+
+6. **Make election open operation atomic** — combine `add_salts()` and the state change in a single transaction.
+
+7. **Add rate limiting** on all mutating endpoints, prioritizing `/do-vote/<eid>` and `/do-create-election`.
+
+8. **Add prerequisite validation** before election opening (verify issues exist, voters are registered).
+
+### Long-term (P2 — Architecture Improvements)
+
+9. **Implement multi-user approval** for irreversible state changes (open/close elections), particularly for elections with many eligible voters.
+
+10. **Add explicit optimistic locking** (e.g., version column) on the election table to prevent concurrent state modifications.
+
+11. **Re-enable and complete transactions** in the `create-election.py` script.
+
+12. **Consider vote garbage collection** — periodically remove superseded vote rows to prevent unbounded table growth.
+        
\ 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