T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/r2.py:27
- Finding
- Hard-Coded Cloudflare R2 Credentials and Account Endpoint## Vulnerability Details **File Location**: `scripts/r2.py`, lines 27–35 **Vulnerability Type**: Hard-coded cloud-storage credentials with automatic fallback **Risk Level**: High ### Vulnerable Code ```python DEFAULT_ENDPOINT = "https://b04c163cb488b020063281fc01b85b03.r2.cloudflarestorage.com" DEFAULT_ACCESS_KEY = "5230d31f45dfeccd1a1d31f51efda4e8" DEFAULT_SECRET_KEY = "dbfc3723949f8f8d6c31eacb547c89ac83f49154025916cc4f8388075019b4e8" def get_client(): """Create and return a boto3 S3 client configured for Cloudflare R2.""" endpoint = os.environ.get("R2_ENDPOINT", DEFAULT_ENDPOINT) access_key = os.environ.get("R2_ACCESS_KEY_ID", DEFAULT_ACCESS_KEY) secret_key = os.environ.get("R2_SECRET_ACCESS_KEY", DEFAULT_SECRET_KEY) ``` ### Technical Analysis The source code embeds an account-specific Cloudflare R2 endpoint, an access-key ID, and a secret access key. These values are not merely examples: `get_client()` automatically selects them whenever the corresponding environment variables are absent. `SKILL.md` also states that defaults are preconfigured for a specific account. Secrets committed to a distributable Skill package must be treated as compromised because every recipient can read and reuse them independently of the intended CLI. An attacker can instantiate a boto3 S3 client against the disclosed endpoint and sign valid requests using the exposed key pair. The exact effective permissions cannot be established from the repository alone. They depend on the R2 token's server-side policy and bucket scope. However, the supplied program uses the credentials for object listing, reading, writing, deletion, and pre-signed URL generation, so any such permissions granted to the token become available to a credential holder. The fallback behavior introduces an additional data-governance risk: a user who does not configure environment variables may unknowingly upload local data to, list data from, or modify objects in the embedded account. ### Atta ...[truncated 1770 chars]
- Remediation
- ## Remediation Suggestions 1. Immediately revoke the exposed R2 API token and issue a new key pair. Rotation is required even if the values are removed from the current source because they may persist in distributed copies and repository history. 2. Review Cloudflare R2 audit and usage records for access made with the exposed key, including unexpected list, read, write, delete, and signed-URL activity. 3. Remove all account-specific endpoints and credentials from source code. Do not retain working secrets as development defaults. 4. Require explicit configuration and fail closed when any required value is absent. For example: ```python def require_env(name: str) -> str: value = os.environ.get(name) if not value: raise RuntimeError(f"Required environment variable is not set: {name}") return value def get_client(): endpoint = require_env("R2_ENDPOINT") access_key = require_env("R2_ACCESS_KEY_ID") secret_key = require_env("R2_SECRET_ACCESS_KEY") return boto3.client( "s3", endpoint_url=endpoint, aws_access_key_id=access_key, aws_secret_access_key=secret_key, region_name="auto", ) ``` 5. Store replacement credentials in a protected secret manager, deployment credential store, or appropriately secured environment configuration. 6. Apply least privilege: restrict the replacement token to only the required buckets and operations. Separate read-only, write, and destructive-delete roles where practical. 7. Avoid silently selecting an account. Require users to provide the endpoint or choose an explicit named profile, and display the destination account and bucket before destructive or upload operations. 8. Add secret scanning to pre-commit and CI workflows and block commits containing access keys, secret keys, tokens, or account-specific credential bundles. 9. Update `SKILL.md` so it no longer claims that account credentials are preconfigured and instead documents mandatory secure configuratio ...[truncated 2 chars]
