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:14:58 UTC

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

commit a19740f700ab155f5b3325a0950fb8c4b3670df1
Author: ASF Tooling Team <ro...@apache.org>
AuthorDate: Tue May 5 14:02:43 2026 +0200

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

diff --git a/ASVS/reports/steve/v3/d0aa7e9/all/11.7.2.md b/ASVS/reports/steve/v3/d0aa7e9/all/11.7.2.md
new file mode 100644
index 0000000..2a125c7
--- /dev/null
+++ b/ASVS/reports/steve/v3/d0aa7e9/all/11.7.2.md
@@ -0,0 +1,175 @@
+# Security Audit Report: ASVS 11.7.2
+
+## Executive Summary
+
+| Field | Value |
+|-------|-------|
+| **Repository** | apache/steve/v3 |
+| **Audit Date** | May 05, 2026 |
+| **Auditor** | Tooling Agents (bundled-pass mode) |
+| **ASVS Requirement** | ASVS 11.7.2 |
+| **Files Analyzed** | 8 relevant / 49 total |
+| **Files Skipped** | 12 |
+
+**Requirement description:** Verify that data minimization ensures the minimal amount of data is exposed during processing, and ensure that data is encrypted immediately after use or as soon as feasible.
+
+### Findings Overview
+
+| Severity | Count |
+|----------|-------|
+| 🔴 Critical | 0 |
+| 🟠 High | 0 |
+| 🟡 Medium | 0 |
+| 🟢 Low | 2 |
+
+---
+
+## ASVS-11.7.2: Data Minimization and Immediate Encryption
+
+### Findings for 11.7.2
+
+### [LOW]
+
+**Finding ID**: ASVS-1172-LOW-001
+
+**File**: `v3/steve/election.py`, function `tally_issue()`, lines 248-294
+
+**Vulnerable code:**
+```python
+def tally_issue(self, iid):
+    votes = []
+    self.q_tally.perform(iid)
+    for mayvote in self.q_tally.fetchall():
+        vote_token = crypto.gen_vote_token(md.opened_key, mayvote.pid, iid, mayvote.salt)
+        row = self.q_recent_vote.first_row(vote_token)
+        if row is None:
+            continue
+        voters.add(mayvote.pid)
+        votestring = crypto.decrypt_votestring(vote_token, mayvote.salt, row.ciphertext)
+        votes.append(votestring)
+    # ALL votes decrypted and held simultaneously in memory
+    crypto.shuffle(votes)
+    m = vtypes.vtype_module(issue.type)
+    return m.tally(votes, self.json2kv(issue.kv)), voters
+```
+
+**Data flow**: All votes for an issue are decrypted simultaneously → stored in `votes` list → remain in memory until garbage collected
+
+**Impact**: The entire set of decrypted votes exists in cleartext memory simultaneously. For elections with many voters, this creates a larger window where all vote data is exposed. There is no explicit clearing of sensitive variables after use.
+
+**Remediation**:
+```python
+def tally_issue(self, iid):
+    # Process votes through streaming tally where possible
+    # For STV (requires all votes), minimize exposure window:
+    try:
+        votes = []
+        # ... (existing decryption logic)
+        crypto.shuffle(votes)
+        m = vtypes.vtype_module(issue.type)
+        result = m.tally(votes, self.json2kv(issue.kv))
+        return result, voters
+    finally:
+        # Explicitly clear sensitive data
+        if 'votes' in locals():
+            for i in range(len(votes)):
+                votes[i] = None
+            votes.clear()
+        # Note: Python doesn't guarantee memory zeroing, 
+        # but this reduces exposure window
+```
+
+---
+
+### [LOW]
+
+**Finding ID**: ASVS-1172-LOW-002
+
+**File**: `v3/steve/election.py`, function `gather_election_data()`, lines 82-107
+
+**Vulnerable code:**
+```python
+def gather_election_data(self, pdb):
+    md = self._all_metadata()
+    mdata = md.eid + md.title
+    self.q_issues.perform(self.eid)
+    idata = ''.join(
+        f'{i.iid}{i.title}{i.description}{i.type}{i.kv}'
+        for i in self.q_issues.fetchall()
+    )
+    self.q_voting_persons.perform(self.eid)
+    pdata = ''.join(row.pid + row.email for row in self.q_voting_persons.fetchall())
+    return (mdata + idata + pdata).encode()
+```
+
+**Data flow**: All voter PIDs and emails assembled in cleartext string → encoded to bytes → passed to hash function → original strings remain in memory
+
+**Impact**: Voter identity data (PIDs and email addresses) exists in unencrypted memory longer than necessary. The assembled string is only needed for hashing but persists until garbage collected.
+
+**Remediation**: Use incremental hashing to avoid accumulating all data in a single string:
+```python
+def gather_election_data(self, pdb):
+    h = hashlib.blake2b()
+    md = self._all_metadata()
+    h.update((md.eid + md.title).encode())
+    self.q_issues.perform(self.eid)
+    for i in self.q_issues.fetchall():
+        h.update(f'{i.iid}{i.title}{i.description}{i.type}{i.kv}'.encode())
+    self.q_voting_persons.perform(self.eid)
+    for row in self.q_voting_persons.fetchall():
+        h.update((row.pid + row.email).encode())
+    return h.digest()
+```
+
+---
+
+### Security Controls Inventory for 11.7.2
+
+| Control | Location | Coverage Status |
+|---------|----------|-----------------|
+| Immediate vote encryption | `election.py:add_vote()` line 231 | ✅ Votes encrypted immediately upon submission |
+| Data minimization in queries | `queries.yaml:q_open_to_me` | ✅ SALT and OPENED_KEY excluded from public queries |
+| Metadata filtering | `election.py:get_metadata()` line 144 | ✅ SALT and OPENED_KEY never returned to callers |
+| Memory clearing after crypto ops | Not implemented | ❌ No explicit zeroing of sensitive variables |
+| Streaming/incremental processing | Not implemented | ❌ All data accumulated before processing |
+
+### Positive Patterns for 11.7.2
+
+1. **Immediate encryption on vote submission**: In `add_vote()`, the votestring is encrypted via `crypto.create_vote()` immediately upon receipt, minimizing cleartext exposure.
+2. **Sensitive column filtering**: `get_metadata()` explicitly excludes `salt` and `opened_key` from returned data, and queries like `q_open_to_me` never SELECT these columns.
+3. **Vote ciphertext storage**: Votes are never stored in plaintext — only `vote_token` (a hash) and `ciphertext` (encrypted) are persisted.
+4. **Minimal data in public interfaces**: The `get_issue()` method explicitly notes "NEVER return issue.salt".
+
+---
+
+
+
+## Cross-cutting Architecture Observations
+
+1. **Cryptographic Design is Sound**: The overall cryptographic architecture (salt generation → Argon2 key derivation → HKDF key stretching → Fernet encryption) follows a well-structured layered approach with proper separation of concerns.
+
+2. **ID Entropy vs. Authorization**: The low entropy in `create_id()` (40 bits) is partially mitigated by the incomplete but intended authorization layer. However, since authz is currently placeholder (`### check authz`), the low-entropy IDs are the *only* barrier to unauthorized access, making the entropy issue more critical in practice.
+
+3. **Consistent Use of CSPRNG**: The codebase consistently uses `secrets` for security-sensitive randomness, with only the candidate display shuffle using `random` (acceptable for non-security display ordering).
+
+4. **Anti-Tamper Mechanism**: The `opened_key` / `is_tampered()` design provides integrity verification for elections, but its effectiveness depends on the security of the underlying Argon2 derivation.
+
+5. **Encryption Transition Planning**: The comments indicate a planned migration from Fernet (AES-128-CBC + HMAC-SHA256) to XChaCha20-Poly1305, which would be an improvement in terms of modern algorithm selection and nonce management.
+
+## Cross-cutting Recommendations
+
+### Immediate (Critical)
+1. **Increase ID entropy to 128 bits**: Change `create_id()` to use `secrets.token_hex(16)` and update the database schema constraints. This is essential given the absence of proper authorization checks.
+
+### Short-term (High)
+2. **Switch from Argon2d to Argon2id**: Change `type=argon2.low_level.Type.D` to `type=argon2.low_level.Type.ID` in the `_hash()` function. This is a one-line change that improves resistance to side-channel attacks per RFC 9106 recommendations.
+
+3. **Implement authorization checks**: The multiple `### check authz` placeholders create false confidence — the decorator-based auth ensures authentication but not authorization (ownership/permission checks).
+
+### Long-term (Medium)
+4. **Memory protection for sensitive operations**: Consider using `mlock()` for memory pages containing decrypted votes during tally operations, and implement explicit memory clearing patterns.
+
+5. **Incremental hashing in `gather_election_data()`**: Refactor to use streaming hash computation instead of accumulating all voter data in memory.
+
+6. **Complete the Fernet → XChaCha20-Poly1305 migration**: As noted in the code comments, this transition would bring the system to a more modern AEAD construction with larger nonce space.
+        
\ 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