Back to skill

Security audit

R2 Storage

Security checks for vulnerabilities and agentic risk

Overview

This R2 storage skill does what it says, but it ships hard-coded Cloudflare R2 credentials and silently falls back to a specific account.

Review this carefully before installing. Do not use the bundled credentials; the publisher should revoke them and remove them from the package. Only run the skill with your own least-privilege R2 credentials set explicitly in the environment, and be cautious with delete operations and presigned URLs because they can remove or expose stored data.

Vulnerability Patterns
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (1)

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]
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (14)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The documentation states that credentials are 'pre-configured for Marouane's account,' implying the skill may rely on embedded or implicit access to a specific third-party Cloudflare R2 account. This is dangerous because it creates undeclared access to sensitive storage resources and can enable unauthorized data access, modification, or deletion if the skill is reused in another environment.

Context-Inappropriate Capability

High
Confidence
99% confidence
Finding
The script embeds a default R2 endpoint and hard-coded access credentials, causing anyone who runs it without overriding environment variables to connect to and operate on a specific remote storage account. In a storage-management skill, this is especially dangerous because the functionality includes upload, download, list, delete, and presigned URL generation, enabling unauthorized access, data exfiltration, and destructive actions against that account.

Natural-Language Policy Violations

High
Confidence
100% confidence
Finding
The code contains fixed Cloudflare R2 access credentials in plaintext. Hard-coded secrets are immediately usable by anyone who can read the file, and because this tool supports listing, downloading, uploading, deleting, and presigning objects, compromise of these credentials can expose or destroy stored data.

Lp3

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding
The skill declares access to environment-based credentials but does not specify any explicit tool scope or permission boundaries. In an agent setting, this can lead to overbroad access to secrets or execution contexts beyond what users would reasonably expect from the manifest alone.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The skill advertises object deletion without warning that the operation is destructive and may be irreversible. In an agent or automation context, this increases the risk of accidental or socially engineered data loss, especially when bucket paths are user-supplied.

Missing User Warnings

Medium
Confidence
88% confidence
Finding
The skill promotes generating pre-signed URLs for sharing without warning that these links grant time-bound access to private objects and may be forwarded or leaked. In storage workflows, this can directly expose sensitive files outside intended trust boundaries.

Missing User Warnings

Medium
Confidence
87% confidence
Finding
The credential section explains how to supply access keys and notes they may already be configured, but gives no warning about handling secrets safely. This can normalize insecure secret exposure, accidental logging, or reuse of privileged credentials without understanding the associated risks.

Cloud Storage Exfiltration

Medium
Category
Data Exfiltration
Content
### Upload Object
```python
# From file
client.upload_file("local.txt", "my-bucket", "remote/key.txt")

# From bytes
client.put_object(Bucket="my-bucket", Key="key.txt", Body=b"hello")
Confidence
55% confidence
Finding
Data is uploaded to cloud storage (S3 / GCS / Azure Blob). This may be a legitimate backup or exfiltration to an external bucket. Manual review is recommended.

Cloud Storage Exfiltration

Medium
Category
Data Exfiltration
Content
### Upload Object
```python
# From file
client.upload_file("local.txt", "my-bucket", "remote/key.txt")

# From bytes
client.put_object(Bucket="my-bucket", Key="key.txt", Body=b"hello")
Confidence
55% confidence
Finding
Data is uploaded to cloud storage (S3 / GCS / Azure Blob). This may be a legitimate backup or exfiltration to an external bucket. Manual review is recommended.

Cloud Storage Exfiltration

Medium
Category
Data Exfiltration
Content
client.upload_file("local.txt", "my-bucket", "remote/key.txt")

# From bytes
client.put_object(Bucket="my-bucket", Key="key.txt", Body=b"hello")
```

### Download Object
Confidence
55% confidence
Finding
Data is uploaded to cloud storage (S3 / GCS / Azure Blob). This may be a legitimate backup or exfiltration to an external bucket. Manual review is recommended.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
This markdown file includes instructions for exposing an R2 bucket publicly, which can affect data privacy and confidentiality. The section gives the enablement steps but does not warn users to verify that the bucket contains only intended public content before exposing it.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The download function writes remote object contents directly to the user-supplied destination path, which can overwrite local data. Although the CLI prints a message after completion, there is no warning or confirmation before the file write occurs in the function itself.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The delete function performs an irreversible remote object deletion immediately. The only user-facing message appears after deletion succeeds, so the operation lacks a prior warning or confirmation for a destructive action.

Missing User Warnings

Low
Confidence
84% confidence
Finding
The markdown provides a delete_object example, which is a destructive operation affecting user data, but it does not mention that deletion may be irreversible depending on versioning and retention settings. For safety-oriented documentation, users should be alerted before using destructive commands on production data.

Static analysis

No suspicious patterns detected.