T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/pve_client.py:55
- Finding
- TLS Certificate Verification Is Disabled for Authenticated API Requests<![CDATA[ ## Vulnerability Details **File Location**: `scripts/pve_client.py:32`, `scripts/pve_client.py:55-64`; additional insecure examples at `SKILL.md:77-86` and `SKILL.md:755-772` **Vulnerability Type**: Improper certificate validation **Risk Level**: High ### Vulnerable Code ```python urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) ``` ```python def request(self, method, endpoint, **kwargs): """Make an API request""" url = f"{self.base_url}/{endpoint}" response = requests.request( method, url, headers=self.headers, verify=False, **kwargs ) if response.status_code >= 400: ``` The Skill documentation also recommends commands that disable certificate validation: ```bash # 1. Get ticket and CSRF token curl -k -d 'username=root@pam' --data-urlencode 'password=secret' \ https://pve.example.com:8006/api2/json/access/ticket # Response: { "data": { "ticket": "...", "CSRFPreventionToken": "..." } } # 2. Use ticket in subsequent requests curl -k -b "PVEAuthCookie=<ticket>" \ -H "CSRFPreventionToken: <csrf_token>" \ https://pve.example.com:8006/api2/json/nodes ``` ### Technical Analysis The client uses HTTPS but explicitly passes `verify=False` to every request. This prevents `requests` from validating whether the server certificate is trusted and whether it belongs to the requested host. Suppression of `InsecureRequestWarning` further conceals this unsafe state from operators. Every request includes the PVE API token in the `Authorization` header: ```python self.headers = { 'Authorization': f'PVEAPIToken={self.user}!{self.token_id}={self.token_secret}' } ``` Consequently, an attacker who can intercept or redirect network traffic can present an arbitrary TLS certificate. The client will accept the certificate and send its authentication token to the attacker-controlled endpoint. The documented `curl -k` ticket-authentication workflow creates the same exposure for usernames, ...[truncated 2028 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Remove `verify=False` and rely on certificate validation by default: ```python response = requests.request( method, url, headers=self.headers, timeout=30, **kwargs ) ``` 2. Support a configurable CA bundle for self-signed or privately issued PVE certificates: ```python def __init__(self, host=None, user=None, token_id=None, token_secret=None, ca_bundle=None): # Existing initialization omitted self.ca_bundle = ca_bundle or os.environ.get("PVE_CA_BUNDLE", True) def request(self, method, endpoint, **kwargs): url = f"{self.base_url}/{endpoint}" return requests.request( method, url, headers=self.headers, verify=self.ca_bundle, timeout=30, **kwargs ) ``` 3. Distribute the PVE server certificate or private CA certificate through a trusted administrative channel and configure `requests` to use it. 4. Remove global suppression of `InsecureRequestWarning`. 5. Remove `curl -k` from documentation. Use normal certificate verification or `--cacert /trusted/path/pve-ca.pem`. 6. If an insecure development mode must exist, make it an explicit opt-in flag, reject it by default, and emit a prominent warning. Do not recommend it for production or credential-bearing requests. 7. Rotate any API tokens or passwords that may previously have traversed untrusted networks with verification disabled. 8. Continue applying least-privilege ACLs so compromise of one token does not grant cluster-wide administration. ]]>
