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:53:19 UTC
(tooling-agents) branch main updated: ASVS audit: 13.4.7.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 2980a48 ASVS audit: 13.4.7.md (redacted) [source: apache/steve/v3 @ d0aa7e9]
2980a48 is described below
commit 2980a486b1b7d0bc7e8d37ddcc12d498e893540a
Author: ASF Tooling Team <ro...@apache.org>
AuthorDate: Tue May 5 22:53:14 2026 +0200
ASVS audit: 13.4.7.md (redacted) [source: apache/steve/v3 @ d0aa7e9]
---
ASVS/reports/steve/v3/d0aa7e9/all/13.4.7.md | 169 ++++++++++++++++++++++++++++
1 file changed, 169 insertions(+)
diff --git a/ASVS/reports/steve/v3/d0aa7e9/all/13.4.7.md b/ASVS/reports/steve/v3/d0aa7e9/all/13.4.7.md
new file mode 100644
index 0000000..ca7ac14
--- /dev/null
+++ b/ASVS/reports/steve/v3/d0aa7e9/all/13.4.7.md
@@ -0,0 +1,169 @@
+# Security Audit Report: ASVS 13.4.7
+
+## Executive Summary
+
+| Field | Value |
+|-------|-------|
+| **Repository** | apache/steve/v3 |
+| **Audit Date** | May 05, 2026 |
+| **Auditor** | Tooling Agents (bundled-pass mode) |
+| **ASVS Requirement** | ASVS 13.4.7 |
+| **Files Analyzed** | 8 relevant / 49 total |
+| **Files Skipped** | 12 |
+
+**Requirement description:** Verify that the web tier is configured to only serve files with specific file extensions to prevent unintentional information, configuration, and source code leakage.
+
+### Findings Overview
+
+| Severity | Count |
+|----------|-------|
+| 🔴 Critical | 0 |
+| 🟠High | 2 |
+| 🟡 Medium | 1 |
+| 🟢 Low | 0 |
+
+---
+
+## ASVS-13.4.7: Unintended Information Leakage - File Extension Restrictions
+
+### Findings for 13.4.7
+
+#### [HIGH]
+**Finding ID**: ASVS-1347-HIGH-001
+
+**File**: `v3/server/pages.py`, function `serve_static`, line 577-578
+
+**Vulnerable Code**:
+```python
+@APP.get('/static/<path:filename>')
+async def serve_static(filename):
+ return await quart.send_from_directory(STATICDIR, filename)
+```
+
+**Data Flow**: User request to `/static/<any_filename>` → No extension validation → `send_from_directory()` serves any file type
+
+**Issue**: The static file endpoint serves any file from the `STATICDIR` directory without restricting file extensions. The `<path:filename>` URL converter allows subdirectory traversal within `STATICDIR`. If any `.py`, `.yaml`, `.sql`, `.env`, `.cfg`, `.bak`, `.swp`, or other non-public files end up in the static directory (through misconfiguration, editor temp files, or deployment errors), they would be served publicly without authentication.
+
+**Proof of Concept**:
+```
+GET /static/.env HTTP/1.1
+GET /static/config.yaml HTTP/1.1
+GET /static/.htaccess HTTP/1.1
+GET /static/backup.sql HTTP/1.1
+GET /static/.DS_Store HTTP/1.1
+```
+
+**Impact**: Potential disclosure of configuration files, source code, database backups, or other sensitive files if accidentally placed in the static directory.
+
+**Remediation**:
+```python
+import os
+
+ALLOWED_STATIC_EXTENSIONS = {'.css', '.js', '.png', '.jpg', '.jpeg', '.gif', '.svg', '.ico', '.woff', '.woff2', '.ttf'}
+
+@APP.get('/static/<path:filename>')
+async def serve_static(filename):
+ # Validate file extension
+ _, ext = os.path.splitext(filename)
+ if ext.lower() not in ALLOWED_STATIC_EXTENSIONS:
+ quart.abort(404)
+
+ # Block hidden files
+ if any(part.startswith('.') for part in filename.split('/')):
+ quart.abort(404)
+
+ return await quart.send_from_directory(STATICDIR, filename)
+```
+
+---
+
+#### [HIGH]
+**Finding ID**: ASVS-1347-HIGH-002
+
+**File**: `v3/server/pages.py`, function `serve_doc`, line 560-574
+
+**Vulnerable Code**:
+```python
+@APP.get('/docs/<iid>/<docname>')
+@asfquart.auth.require
+async def serve_doc(iid, docname):
+ ...
+ ### verify the propriety of DOCNAME.
+ return await quart.send_from_directory(DOCSDIR / iid, docname)
+```
+
+**Data Flow**: User request to `/docs/<iid>/<docname>` → Authentication checked → mayvote verified → No file extension validation → Any file type served
+
+**Issue**: The document serving endpoint has no file extension whitelist. Although authentication and authorization are checked, any file type within the directory can be served. This could include configuration files, scripts, or other internal files placed alongside legitimate documents.
+
+**Proof of Concept** (requires valid authentication and mayvote entry):
+```
+GET /docs/a1b2c3d4e5/deploy.sh HTTP/1.1
+GET /docs/a1b2c3d4e5/.gitignore HTTP/1.1
+GET /docs/a1b2c3d4e5/notes.py HTTP/1.1
+```
+
+**Impact**: Authenticated users with voting rights could access any file type within the issue's document directory, potentially including internal scripts or configuration.
+
+**Remediation**: Apply extension whitelist as shown in ASVS-1345-MED-001 remediation above.
+
+### Security Controls Inventory for 13.4.7
+
+| Control | Location | Coverage Status |
+|---------|----------|-----------------|
+| File extension whitelist for /static | N/A | **NOT IMPLEMENTED** |
+| File extension whitelist for /docs | N/A | **NOT IMPLEMENTED** |
+| Hidden file blocking | N/A | **NOT IMPLEMENTED** |
+| Directory listing prevention | Framework default | Likely blocked by `send_from_directory` |
+| MIME type enforcement | Framework default | Relies on Quart defaults |
+
+### Positive Patterns for 13.4.7
+
+1. **`send_from_directory` usage**: Both endpoints use `quart.send_from_directory()` which provides protection against path traversal (escaping the base directory), though it doesn't restrict file types.
+2. **Separate directories for different content types**: Static files, documents, and templates are in separate directories.
+3. **No directory listing**: The application doesn't have directory listing endpoints.
+
+---
+
+
+
+## Cross-cutting Architecture Observations
+
+1. **Type B Gap Pattern (Control exists but not called)**: The most pervasive security issue across this codebase is the `### check authz` pattern. Authorization logic is acknowledged as needed (via comments) throughout `pages.py` but is never implemented. This creates false confidence — the code appears to have considered authorization, but any authenticated committer can manage any election, open/close any election, or manipulate any issue regardless of ownership.
+
+2. **Documentation vs. Implementation Asymmetry**: The `schema.md` documentation describes a well-thought-out security architecture (non-predictable IDs, encryption, anti-tamper), but the running application has significant gaps (CSRF placeholder, missing authz, unvalidated file serving).
+
+3. **Positive Crypto Architecture**: The cryptographic design (Argon2 for key derivation, per-voter salts, Fernet encryption, vote shuffling) is well-implemented in `crypto.py` and `election.py`. The separation of concerns (crypto operations isolated in one module) supports auditability.
+
+4. **Data Leakage Prevention in Queries**: The `queries.yaml` demonstrates conscious effort to prevent sensitive data leakage — public-facing queries explicitly exclude `salt` and `opened_key` columns, with comments documenting this decision. The `_all_metadata()` method is correctly marked as internal-only.
+
+5. **Incomplete Defense-in-Depth**: Authentication is applied (decorators on all sensitive endpoints), but authorization (who can do what to which resource) and input validation (file extensions, path components) represent the missing layers.
+
+---
+
+## Cross-cutting Recommendations
+
+### Immediate (Critical/High Priority)
+
+1. **Convert state-changing GET endpoints to POST**: Change `/do-open/<eid>` and `/do-close/<eid>` from GET to POST, requiring form submission with CSRF tokens. This addresses both ASVS 14.2.1 (operations in URLs) and CSRF vulnerability.
+
+2. **Implement file extension whitelist**: Add allowed extension sets for both `/static/` and `/docs/` endpoints. This directly addresses ASVS 13.4.7 and prevents accidental file disclosure.
+
+3. **Validate `docname` parameter**: Implement the already-planned validation noted by the `### verify the propriety of DOCNAME` comment. Block path traversal characters and restrict to allowed extensions.
+
+### Short-term (Within Sprint)
+
+4. **Suppress server version headers**: Add an `@APP.after_request` handler to remove `Server` and `X-Powered-By` headers, or configure Hypercorn to disable server header inclusion (ASVS 13.4.6).
+
+5. **Implement authorization checks**: Replace all `### check authz` comments with actual ownership/permission verification. At minimum, verify `election.owner_pid == result.uid` for management operations.
+
+6. **Create data classification document**: Formally document all sensitive data types, their protection levels, and required controls (ASVS 14.1.1, 14.1.2).
+
+### Long-term (Architecture)
+
+7. **Implement CSRF protection**: Replace `basic.csrf_token = 'placeholder'` with cryptographically-generated per-session tokens validated on all state-changing endpoints.
+
+8. **Create comprehensive data protection policy**: Document encryption requirements, retention periods, logging policies, key management lifecycle, and privacy controls for each data sensitivity level.
+
+9. **Add security headers middleware**: Implement `Content-Security-Policy`, `X-Content-Type-Options: nosniff`, `X-Frame-Options`, and `Strict-Transport-Security` headers to complement the data protection controls.
+
\ 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