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/04/21 19:53:31 UTC
(tooling-agents) branch main updated: ASVS L3 audit: 15.1.4 (dependency_and_supply_chain)
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 44cd996 ASVS L3 audit: 15.1.4 (dependency_and_supply_chain)
44cd996 is described below
commit 44cd9964f21f63dbbb1a758672e2712807247cb1
Author: ASF Tooling Team <ro...@apache.org>
AuthorDate: Tue Apr 21 21:53:27 2026 +0200
ASVS L3 audit: 15.1.4 (dependency_and_supply_chain)
---
.../d0aa7e9/dependency_and_supply_chain/15.1.4.md | 529 +++++++++++++++++++++
1 file changed, 529 insertions(+)
diff --git a/repos/apache/ASVS/reports/steve/v3/d0aa7e9/dependency_and_supply_chain/15.1.4.md b/repos/apache/ASVS/reports/steve/v3/d0aa7e9/dependency_and_supply_chain/15.1.4.md
new file mode 100644
index 0000000..9208aa5
--- /dev/null
+++ b/repos/apache/ASVS/reports/steve/v3/d0aa7e9/dependency_and_supply_chain/15.1.4.md
@@ -0,0 +1,529 @@
+# ASVS 15.1.4 Security Audit Report
+
+## Executive Summary
+
+- **Repository:** apache/steve
+- **Audit Date:** Apr 21, 2026
+- **Auditor:** Tooling Agents
+- **ASVS Requirement:** 15.1.4 — Verify that application documentation highlights third-party libraries which are considered to be "risky components". Level: 3, Section: Secure Coding and Architecture Documentation
+- **Files analyzed:** 3 relevant out of 3 total, 0 skipped
+
+### Finding Summary
+
+| Severity | Count | Finding IDs |
+|----------|-------|-------------|
+| Critical | 0 | — |
+| High | 1 | ASVS-1514-HIGH-001 |
+| Medium | 3 | ASVS-1514-MED-002, ASVS-1514-MED-003, ASVS-1514-MED-004 |
+| Low | 0 | — |
+| **Total** | **4** | |
+
+**Compliance Status:** ❌ **NON-COMPLIANT**
+
+ASVS 15.1.4 requires that application documentation highlights third-party libraries considered to be "risky components." Analysis of the provided source code reveals the use of multiple third-party libraries—some with characteristics that qualify as "risky"—without any corresponding documentation classifying component risk. Additionally, use of a low-level cryptographic API with a non-recommended algorithm variant evidences exactly the kind of risk that documentation should capture and [...]
+
+---
+
+## Security Controls Inventory
+
+| Control | Defined | Applied | Coverage |
+|---------|---------|---------|----------|
+| Component risk documentation | NOT FOUND | N/A | **0% — No documentation exists** |
+| SBOM / dependency manifest | NOT PROVIDED | N/A | Cannot verify |
+| Version pinning (requirements.txt) | NOT PROVIDED | N/A | Cannot verify |
+| Vulnerability update procedures | NOT FOUND | N/A | **0%** |
+| Dependency risk classification | NOT FOUND | N/A | **0%** |
+
+---
+
+## Critical File Review
+
+### Third-Party Component Inventory
+
+| Library | Usage Location | Purpose | Risk Assessment |
+|---------|---------------|---------|-----------------|
+| `asfquart` (Quart wrapper) | `main.py:37` | Web framework, OAuth | ASF-internal; limited public security review |
+| `asfpy.db` | `election.py:23` | Database access wrapper | ASF-internal; limited public security review |
+| `cryptography` (Fernet, HKDF) | `crypto.py:27-28` | Vote encryption, key derivation | Well-maintained; handles dangerous operations |
+| `argon2-cffi` (`argon2.low_level`) | `crypto.py:25` | Key hashing | Low-level API used directly |
+| `easydict` | `election.py:24` | Dict attribute access | Small utility; minimal maintenance |
+| `sqlite3` | `election.py:22` | Database engine | Standard library — not third-party |
+
+### Files Analyzed for Third-Party Dependencies
+
+| File | Third-Party Imports | Risk Classification Documented | Status |
+|------|-------------------|-------------------------------|--------|
+| `crypto.py` | `argon2.low_level`, `cryptography.fernet`, `cryptography.hazmat.*` | No | ❌ FAIL |
+| `election.py` | `asfpy.db`, `easydict` | No | ❌ FAIL |
+| `main.py` | `asfquart`, `asfquart.generics` | No | ❌ FAIL |
+
+---
+
+## Findings
+
+### High Severity Findings
+
+#### ASVS-1514-HIGH-001: No Documentation Classifying Third-Party Component Risk
+
+**Severity:** HIGH
+**Location:** Project-wide (no documentation files provided)
+**ASVS Requirement:** 15.1.4 (Level 3)
+
+**Related Functions Checked:**
+- All import statements in `crypto.py`, `election.py`, `main.py`
+- Project root for documentation files (DEPENDENCIES.md, SECURITY.md, SBOM)
+
+**Description:**
+
+No documentation was found in the provided codebase that identifies, classifies, or highlights third-party libraries based on their risk profile. ASVS 15.1.4 specifically requires documentation that flags "risky components" — defined as third-party libraries that are poorly maintained, unsupported, at end-of-life, or have a history of significant vulnerabilities.
+
+The application depends on at least five third-party packages, several of which have characteristics that warrant explicit risk documentation:
+
+- **`asfpy` and `asfquart`**: ASF-internal libraries without broad public security review processes, limited external contributor base, and no publicly documented vulnerability disclosure/response policy.
+- **`easydict`**: Small convenience library with minimal maintenance activity and no security-focused development process.
+- **`argon2-cffi` (low-level API)**: While the library itself is well-maintained, the code exclusively uses the `argon2.low_level` sub-module which bypasses higher-level safety defaults.
+
+**Vulnerable Code:**
+
+From `crypto.py`:
+```python
+import argon2.low_level # Low-level API, bypasses PasswordHasher safety
+import cryptography.fernet # Symmetric encryption
+from cryptography.hazmat.primitives import hashes
+from cryptography.hazmat.primitives.kdf import hkdf
+```
+
+From `election.py`:
+```python
+import asfpy.db # ASF-internal DB wrapper
+from easydict import EasyDict as edict # Small utility library
+```
+
+From `main.py`:
+```python
+import asfquart.generics # ASF-internal Quart wrapper
+```
+
+**Data Flow:**
+
+All third-party dependencies → No risk classification → No documented vulnerability response procedures → No prioritized monitoring strategy
+
+**Proof of Concept:**
+
+The absence of documentation means:
+1. No defined update cadence for risky vs. standard components
+2. No vulnerability response timeframe differentiation
+3. No documented justification for using ASF-internal libraries vs. public alternatives
+4. No transitive dependency tracking
+
+**Impact:**
+
+Without documented risk classification, the development team and stakeholders cannot:
+- Prioritize monitoring and updates for high-risk dependencies
+- Make informed decisions about replacing risky components
+- Establish appropriate vulnerability response timeframes per component risk level
+- Satisfy supply chain security requirements (e.g., SLSA, SSDF)
+- Pass ASVS Level 3 compliance audits
+
+**Remediation:**
+
+Create a dependency risk assessment document (e.g., `DEPENDENCIES.md` or integrate into an SBOM) that classifies each third-party component:
+
+```markdown
+# Third-Party Component Risk Assessment
+
+## Risky Components (Require Enhanced Monitoring)
+
+### easydict
+- **Risk Level:** Medium
+- **Justification:** Small library with limited maintenance, no security-focused development process
+- **Mitigation:** Pin version, consider replacing with dataclasses or attrs
+- **Review Cadence:** Quarterly
+
+### asfpy / asfquart
+- **Risk Level:** Medium
+- **Justification:** Internal ASF libraries with limited external security review
+- **Mitigation:** Pin to known-good versions, coordinate with ASF infra team on updates
+- **Review Cadence:** Monthly
+
+### argon2-cffi (low_level API)
+- **Risk Level:** Medium
+- **Justification:** Using low-level API bypasses safety defaults of PasswordHasher
+- **Mitigation:** Document parameter choices; consider migration to high-level API
+- **Review Cadence:** On each release
+
+## Standard Components
+
+### cryptography
+- **Risk Level:** Low (well-maintained, audited)
+- **Review Cadence:** Track CVE advisories; update within 7 days of security release
+
+## Vulnerability Response Timeframes
+- Critical CVE in risky component: Patch within 24 hours
+- High CVE in risky component: Patch within 72 hours
+- Critical CVE in standard component: Patch within 7 days
+```
+
+---
+
+### Medium Severity Findings
+
+#### ASVS-1514-MED-002: `easydict` Library Used Without Documented Risk Assessment
+
+**Severity:** MEDIUM
+**Location:** `v3/steve/election.py:24` and throughout `Election` class
+**Functions:** `get_metadata()` (lines 146-156), `list_issues()` (line 216), `get_issue()` (line 259), `list_votes()` (line 310)
+
+**Related Functions Checked:**
+- `Election.get_metadata()` — `election.py:146-156`
+- `Election.list_issues()` — `election.py:216-224`
+- `Election.get_issue()` — `election.py:259-267`
+- `Election.list_votes()` — `election.py:310-319`
+
+**Description:**
+
+`easydict` is used pervasively throughout the `Election` class to wrap database results and return data structures. This library:
+- Is a small utility package with a narrow contributor base
+- Has no documented security review process
+- Converts dict keys to object attributes, which could mask key collisions or unexpected attribute access patterns
+- Is used to wrap security-sensitive data (election metadata including `owner_pid`, `authz`, `salt`, `opened_key`)
+
+Per the ASVS 15.1.4 definition, a library that is "poorly maintained" or lacks "security controls around its development processes" qualifies as a risky component that must be highlighted in documentation.
+
+**Vulnerable Code:**
+
+```python
+# election.py:24
+from easydict import EasyDict as edict
+
+# election.py:146-156 (get_metadata)
+return edict(
+ eid=md.eid,
+ title=md.title,
+ owner_pid=md.owner_pid,
+ authz=md.authz,
+ state=md.state,
+ created=md.created,
+ salt=md.salt,
+ opened_key=md.opened_key,
+ owner_name=md.owner_name,
+ owner_email=md.owner_email,
+)
+
+# election.py:216 (list_issues)
+return edict(
+ iid=row.iid,
+ title=row.title,
+ description=row.description,
+ nominators=row.nominators,
+ candidates=row.candidates,
+ monitors=row.monitors,
+)
+```
+
+**Data Flow:**
+
+Database query → SQLite Row object → `easydict.EasyDict` wrapper → API response containing security-sensitive fields (`authz`, `owner_pid`, `salt`, `opened_key`)
+
+**Proof of Concept:**
+
+If `easydict` introduces a vulnerability (e.g., through attribute injection with special dict keys like `__class__`, `__dict__`, or through unexpected behavior with Unicode keys), it would affect all election data handling paths.
+
+**Impact:**
+
+- All election metadata and vote data passes through `easydict`
+- Security-critical fields (`salt`, `opened_key`, `authz`) are wrapped by a minimally-maintained library
+- No documented risk acceptance or mitigation strategy
+- Potential attribute injection surface if `easydict` mishandles special keys
+
+**Remediation:**
+
+1. Document `easydict` as a risky component per ASVS 15.1.4
+2. Consider replacing with Python standard library alternatives:
+
+```python
+# Option A: Use dataclasses (Python 3.7+)
+from dataclasses import dataclass
+
+@dataclass
+class ElectionMetadata:
+ eid: str
+ title: str
+ owner_pid: str
+ authz: str | None
+ state: str
+ created: int
+ salt: bytes
+ opened_key: bytes | None
+ owner_name: str
+ owner_email: str
+
+# Option B: Use typing.NamedTuple
+from typing import NamedTuple
+
+class ElectionMetadata(NamedTuple):
+ eid: str
+ title: str
+ owner_pid: str
+ authz: str | None
+ state: str
+ created: int
+ salt: bytes
+ opened_key: bytes | None
+ owner_name: str
+ owner_email: str
+```
+
+---
+
+#### ASVS-1514-MED-003: Low-Level Argon2 API with Argon2d Variant Not Documented as Risky Decision
+
+**Severity:** MEDIUM
+**Location:** `v3/steve/crypto.py:87-97` (`_hash` function)
+**Lines:** 87-97
+
+**Related Functions Checked:**
+- `_hash()` — `crypto.py:87-97` (uses Argon2d)
+- `benchmark()` — `crypto.py:125-145` (uses Argon2id)
+- `gen_opened_key()` — `crypto.py:33-46` (calls `_hash`)
+- `gen_vote_token()` — `crypto.py:113-123` (calls `_hash`)
+
+**Description:**
+
+The code uses `argon2.low_level` directly instead of the safer `argon2.PasswordHasher` high-level API, and selects `Type.D` (Argon2d) instead of the OWASP/RFC 9106-recommended `Type.ID` (Argon2id). This decision is undocumented as a risk acceptance.
+
+Notably, the benchmark function in the same file uses the recommended variant:
+```python
+# crypto.py:141 (benchmark function)
+type=argon2.low_level.Type.ID, # Uses Argon2id here
+```
+
+This inconsistency between benchmark (Argon2id) and production code (Argon2d) suggests the variant choice may be unintentional rather than a deliberate risk-accepted decision. Argon2d is vulnerable to side-channel attacks (timing/cache attacks), which is precisely why RFC 9106 recommends Argon2id for general-purpose use.
+
+**Vulnerable Code:**
+
+```python
+# crypto.py:87-97
+def _hash(data: bytes, salt: bytes) -> bytes:
+ "Use Argon2 to hash the data, with default tuning parameters."
+
+ return argon2.low_level.hash_secret_raw(
+ secret=data,
+ salt=salt,
+ time_cost=2, # Passlib default
+ memory_cost=65536, # Passlib default
+ parallelism=4, # Passlib default
+ hash_len=32, # Standard Argon2 digest length
+ type=argon2.low_level.Type.D, # <-- Argon2d, not Argon2id
+ )
+```
+
+**Data Flow:**
+
+1. `election.open()` → `crypto.gen_opened_key()` → `_hash(Type.D)` → election `opened_key` (stored in database)
+2. `election.add_vote()` → `crypto.gen_vote_token()` → `_hash(Type.D)` → vote token derivation → vote encryption key
+
+**Proof of Concept:**
+
+An attacker with local access to the server could potentially use cache-timing attacks against Argon2d to recover:
+- The `opened_key` during election opening operations
+- Vote tokens during vote submission
+
+While this requires significant attack sophistication and local access, RFC 9106 specifically recommends Argon2id to mitigate this class of side-channel attacks.
+
+**Impact:**
+
+- Side-channel attacks on the server could potentially recover `opened_key` or `vote_token` values
+- The inconsistency between benchmark and production code indicates a gap in security review
+- Without documentation, future developers may not understand why this variant was chosen or whether it should be changed
+- Use of low-level API bypasses `argon2.PasswordHasher` safety defaults (though this may be intentional for key derivation use case)
+
+**Remediation:**
+
+1. Document this as a risky API usage decision if Argon2d is intentional
+2. If not intentional, switch to Argon2id:
+
+```python
+def _hash(data: bytes, salt: bytes) -> bytes:
+ "Use Argon2id to hash the data (RFC 9106 recommended variant)."
+ return argon2.low_level.hash_secret_raw(
+ secret=data,
+ salt=salt,
+ time_cost=2,
+ memory_cost=65536,
+ parallelism=4,
+ hash_len=32,
+ type=argon2.low_level.Type.ID, # RFC 9106 recommended
+ )
+```
+
+3. Document in the risk assessment why `argon2.low_level` is used instead of `argon2.PasswordHasher` (likely because this is key derivation, not password verification, and the high-level API doesn't support raw output).
+
+---
+
+#### ASVS-1514-MED-004: Undocumented Cryptographic Library Transition State
+
+**Severity:** MEDIUM
+**Location:** `v3/steve/crypto.py:58-69` (`_b64_vote_key` function)
+**Lines:** 58-69
+
+**Related Functions Checked:**
+- `_b64_vote_key()` — `crypto.py:58-69` (HKDF with mismatched `info` parameter)
+- `encrypt_vote()` — `crypto.py:72-84` (uses Fernet, not XChaCha20)
+- `decrypt_votes()` — `crypto.py:165-180` (uses Fernet, not XChaCha20)
+
+**Description:**
+
+The code is in a transitional state between Fernet (AES-128-CBC + HMAC-SHA256) and XChaCha20-Poly1305 encryption. The HKDF parameters are already configured for the future algorithm (`info=b'xchacha20_key'`, `length=32` for XChaCha20), but the actual encryption still uses Fernet. This transitional state:
+
+1. Creates a mismatch between the key derivation context (`info` parameter) and actual usage
+2. The `TODO` comment (`### still using Fernet now, but will switch soon`) indicates planned changes to a cryptographic dependency
+3. No documentation captures this planned migration, its timeline, or associated risks
+
+The `info` parameter in HKDF provides domain separation — using `xchacha20_key` as the info while actually using the key for Fernet means the cryptographic binding is technically incorrect (though not exploitable in this specific case).
+
+**Vulnerable Code:**
+
+```python
+# crypto.py:58-69
+def _b64_vote_key(vote_token: bytes, salt: bytes) -> bytes:
+ "Key-stretch the vote_token. (ref: PBKDF)"
+
+ ### still using Fernet now, but will switch soon. Leaving comments.
+ keymaker = hkdf.HKDF(
+ algorithm=hashes.SHA256(),
+ length=32, # 32-byte key for XChaCha20-Poly1305
+ salt=salt,
+ info=b'xchacha20_key', # <-- References future algorithm
+ )
+ vote_key = keymaker.derive(vote_token)
+ return base64.urlsafe_b64encode(vote_key)
+```
+
+**Data Flow:**
+
+Vote submission → `gen_vote_token()` → `_b64_vote_key(info=b'xchacha20_key')` → Fernet key (not XChaCha20 key) → `encrypt_vote()` using Fernet
+
+**Proof of Concept:**
+
+The cryptographic binding is weakened because:
+1. HKDF `info` parameter claims `xchacha20_key` but key is used for Fernet
+2. Future migration to XChaCha20-Poly1305 will introduce a new dependency (not yet assessed for risk)
+3. No documented migration timeline means this inconsistent state may persist indefinitely
+
+**Impact:**
+
+- The planned cryptographic migration introduces a new third-party dependency risk (whichever library provides XChaCha20-Poly1305) that should be pre-assessed
+- The transitional code state could cause confusion during security reviews
+- Without documented migration plan, the transition may stall indefinitely with misleading parameters in production
+- HKDF domain separation is technically incorrect (though not currently exploitable)
+
+**Remediation:**
+
+1. Document the cryptographic migration plan including timeline and risk assessment
+2. Fix the HKDF `info` parameter to match current usage:
+
+```python
+# Current state - use correct info parameter for Fernet
+def _b64_vote_key(vote_token: bytes, salt: bytes) -> bytes:
+ "Key-stretch the vote_token using HKDF-SHA256 for Fernet encryption."
+ keymaker = hkdf.HKDF(
+ algorithm=hashes.SHA256(),
+ length=32,
+ salt=salt,
+ info=b'fernet_vote_key', # Correct for current usage
+ )
+ vote_key = keymaker.derive(vote_token)
+ return base64.urlsafe_b64encode(vote_key)
+
+# When migrating to XChaCha20-Poly1305, update to:
+# info=b'xchacha20_key',
+```
+
+3. Document the future XChaCha20-Poly1305 library dependency in the component risk assessment before adoption.
+
+---
+
+## Positive Security Patterns
+
+| Pattern | Location | Assessment |
+|---------|----------|------------|
+| Use of `secrets` module for all random generation | `crypto.py:20,33,113,118` | ✅ Cryptographically secure RNG throughout |
+| Fisher-Yates shuffle with `secrets.randbelow()` | `crypto.py:101-115` | ✅ Unpredictable vote ordering |
+| HKDF for key stretching | `crypto.py:62-68` | ✅ Proper key derivation function |
+| Per-voter unique salts | `election.py:103-121` | ✅ Prevents vote correlation |
+| Transactional database operations | `election.py:57-70,109-121` | ✅ Referential integrity maintained |
+| State machine assertions before sensitive operations | `election.py:75,98,105,167,228` | ✅ Prevents invalid state transitions |
+| Anti-tampering integrity check | `election.py:332-347` | ✅ Detects election data modification |
+| Collision-retry for ID generation | `election.py:170-177,385-390` | ✅ Handles `IntegrityError` gracefully |
+
+---
+
+## Architecture Observations
+
+1. **Cryptographic library usage is sophisticated** — The application demonstrates competent use of HKDF, Argon2, and Fernet. However, the low-level API choice and algorithm variant selection need documented justification.
+
+2. **ASF-internal dependencies create a trust chain** — `asfpy` and `asfquart` are not publicly audited in the same way as `cryptography` or `argon2-cffi`. The application's security depends significantly on these ASF-internal packages for authentication, database access, and OAuth integration.
+
+3. **No dependency management artifacts provided** — Without `requirements.txt`, `pyproject.toml`, or an SBOM, it's impossible to verify version pinning, transitive dependency tracking, or update procedures.
+
+4. **Transitional cryptographic state** — The codebase shows evidence of planned migration from Fernet to XChaCha20-Poly1305, but this transition is undocumented and incomplete.
+
+---
+
+## Recommendations Summary
+
+### Immediate Actions (0-7 days)
+
+| Priority | Action | Addresses Finding | Effort |
+|----------|--------|------------------|--------|
+| **1** | Create a third-party component risk assessment document classifying all dependencies | ASVS-1514-HIGH-001 | 4 hours |
+| **2** | Document Argon2d variant choice or change to Argon2id | ASVS-1514-MED-003 | 2 hours |
+| **3** | Fix HKDF `info` parameter mismatch | ASVS-1514-MED-004 | 1 hour |
+
+### Short-Term Actions (1-4 weeks)
+
+| Priority | Action | Addresses Finding | Effort |
+|----------|--------|------------------|--------|
+| **4** | Generate and maintain an SBOM (e.g., CycloneDX) including transitive dependencies | ASVS-1514-HIGH-001 | 8 hours |
+| **5** | Establish documented vulnerability response timeframes per component risk tier | ASVS-1514-HIGH-001 | 4 hours |
+| **6** | Document cryptographic migration plan (Fernet → XChaCha20-Poly1305) | ASVS-1514-MED-004 | 4 hours |
+| **7** | Add version pins for all dependencies with documented update review cadence | ASVS-1514-HIGH-001 | 4 hours |
+
+### Long-Term Actions (1-3 months)
+
+| Priority | Action | Addresses Finding | Effort |
+|----------|--------|------------------|--------|
+| **8** | Evaluate replacing `easydict` with standard library alternatives (`dataclasses`, `NamedTuple`) | ASVS-1514-MED-002 | 16 hours |
+| **9** | Implement automated dependency vulnerability scanning (e.g., Dependabot, pip-audit) | ASVS-1514-HIGH-001 | 8 hours |
+| **10** | Coordinate with ASF infrastructure team on security review process for `asfpy`/`asfquart` | ASVS-1514-HIGH-001 | Ongoing |
+
+---
+
+## Appendix: Files Analyzed
+
+<details>
+<summary>Click to expand file list (3 files)</summary>
+
+1. **`v3/server/main.py`** (relevance: 10/10)
+ - Third-party imports: `asfquart`, `asfquart.generics`
+ - Purpose: Web application entry point, OAuth integration
+ - Risk documentation: None found
+
+2. **`v3/steve/crypto.py`** (relevance: 10/10)
+ - Third-party imports: `argon2.low_level`, `cryptography.fernet`, `cryptography.hazmat.primitives.hashes`, `cryptography.hazmat.primitives.kdf.hkdf`
+ - Purpose: Cryptographic operations (key derivation, encryption, hashing)
+ - Risk documentation: None found
+ - Key findings: Argon2d variant usage, HKDF `info` mismatch, planned migration to XChaCha20
+
+3. **`v3/steve/election.py`** (relevance: 10/10)
+ - Third-party imports: `asfpy.db`, `easydict`
+ - Purpose: Election management, vote handling, database operations
+ - Risk documentation: None found
+ - Key findings: Pervasive use of `easydict` for security-sensitive data structures
+
+</details>
+
+---
+
+**End of Report**
\ 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