Back to skill

Security audit

Cloudflare R2 CLI

Security checks for vulnerabilities and agentic risk

Overview

This is a coherent Cloudflare R2 storage CLI, but it handles powerful credentials and destructive storage actions with under-disclosed safety gaps.

Review before installing. Use a narrowly scoped R2 key, avoid broad delete/write permissions unless needed, install dependencies in an isolated environment, and treat download paths and delete commands carefully because they can overwrite local files or remove remote objects immediately.

Vulnerability Patterns
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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
Findings (2)

T09 · Insecure Skill Coding Practices

Error
Location
r2.py:158
Finding

Signed Authorization Headers May Be Forwarded to an Unvalidated Redirect Destination

Content
View full analysis

Vulnerability Details

File Location: r2.py, lines 158–174
Vulnerability Type: Authorization header exposure through automatic cross-origin redirects
Risk Level: High

python
_validate_url(url)

headers = _aws_headers(method, canonical_uri, query, body)

req = urllib.request.Request(
    url,
    data=body if body else None,
    headers=headers,
    method=method,
)

# Create restricted opener (HTTPS only)
opener = urllib.request.build_opener(
    urllib.request.HTTPSHandler()
)

try:
    with opener.open(req, timeout=30) as resp:  # nosec B310
        return resp.status, resp.read()

Technical Analysis

The code validates only the initial URL before creating the request. The opener constructed by urllib.request.build_opener() includes standard redirect handling in addition to the explicitly supplied HTTPS handler. Redirect destinations are therefore not passed through _validate_url().

Request headers supplied through the Request constructor can be copied to a redirected request. This may include the AWS Signature Version 4 Authorization header, the R2 access-key identifier contained in its credential scope, x-amz-date, and x-amz-content-sha256.

HTTPS enforcement alone does not prevent disclosure because an attacker-controlled redirect destination can also use HTTPS. Moreover, the current hostname check at line 139 uses:

python
if not parsed.netloc.endswith("cloudflarestorage.com"):
    raise ValueError("Unexpected host")

A suffix comparison without a hostname boundary is weaker than comparison against the exact expected R2 hostname. Validation should use parsed.hostname and an exact allowlist.

Attack Path

  1. A user invokes an operation such as download, list, or delete.
  2. _aws_headers() generates a signed request containing the access-key identifier and AWS Signature Version 4 authentication material.
  3. The Cloudflare endpoint, ...[truncated 1289 chars]
Remediation
View remediation

Remediation Suggestions

  • Disable automatic redirects by installing a custom urllib.request.HTTPRedirectHandler that rejects every redirect.
  • If redirects are operationally required, validate every redirect target before following it.
  • Require parsed.scheme == "https" and compare parsed.hostname against the exact expected hostname:
    python
    expected_host = f"{ACCOUNT_ID}.r2.cloudflarestorage.com"
    if parsed.scheme != "https" or parsed.hostname != expected_host:
        raise ValueError("Unexpected destination")
    
  • Reject URLs containing user information, unexpected ports, fragments, or other authority ambiguity.
  • Never forward an existing Authorization header across origins.
  • Regenerate the AWS Signature Version 4 header only after an approved redirect destination and canonical request have been validated.
  • Add automated tests covering cross-origin redirects, same-origin redirects, HTTPS-to-HTTP redirects, suffix-confusion hostnames, and redirects containing user information.

T08 · Insecure Dependencies

Warning
Location
SKILL.md:27
Finding

Third-Party Dependency Is Installed Without Version Locking or Integrity Verification

Content
View full analysis

Vulnerability Details

File Location: SKILL.md, lines 27–29 and 71–75
Vulnerability Type: Unpinned and unhashed third-party dependency installation
Risk Level: Medium

yaml
dependencies:
  - "defusedxml>=0.7.1"
markdown
If `defusedxml` is not already available:

```bash
pip install defusedxml
text

### Technical Analysis

The declared version constraint accepts any current or future release greater than or equal to version 0.7.1. The documented installation command is even less restrictive and asks `pip` to resolve the latest available version from its configured package index.

No lock file, exact version, package hash, or trusted index configuration is provided. Consequently, installations are not reproducible and package content is not cryptographically checked against a project-controlled expectation.

Python package installation may execute package-controlled build logic. A compromised future release, compromised package index, or malicious package supplied through an untrusted index configuration could therefore execute code with the privileges of the user performing installation.

### Attack Path

1. A user follows the documented installation command or installs dependencies from the permissive metadata.
2. `pip` queries the package indexes configured in the user's environment.
3. The resolver selects any available release satisfying the mutable constraint.
4. A compromised release or package served through a malicious or incorrectly prioritized index is downloaded.
5. Package build or installation logic executes locally, or malicious runtime code executes when `r2.py` imports `defusedxml`.
6. The malicious dependency gains the privileges of the installing or invoking user and can access the Cloudflare R2 credentials inherited through environment variables.

### Impact Assessment

Successful supply-chain exploitation could execute arbitrary Python code under the account ins
...[truncated 585 chars]
Remediation
View remediation

Remediation Suggestions

  • Pin defusedxml to an exact, reviewed version rather than using a lower-bound-only constraint.
  • Maintain a dependency lock file and review dependency updates deliberately.
  • Record and enforce package hashes using a requirements file:
    text
    defusedxml==REVIEWED_VERSION \
        --hash=sha256:EXPECTED_PACKAGE_HASH
    
  • Install with hash enforcement:
    bash
    python -m pip install --require-hashes -r requirements.txt
    
  • Explicitly document and enforce the trusted package index, such as the official PyPI simple index, while preventing unintended fallback to untrusted indexes.
  • Perform dependency vulnerability and provenance checks in continuous integration.
  • Install dependencies in an isolated virtual environment using an unprivileged account.
Vulnerability Patterns
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (4)

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The documentation states that paths are sanitized to prevent injection/path traversal, suggesting protective handling of filesystem paths. In practice, download() opens whatever file_path the user supplies and writes to it directly, with no normalization, restriction, or traversal checks.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

This code writes downloaded object data directly to a user-specified local path, which can modify or overwrite local files. Although the script prints success/failure messages, there is no warning, confirmation, or comment/docstring near the operation disclosing that the command performs a local file write.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The delete command issues a DELETE request against the remote object and only reports the outcome afterward. There is no prior confirmation prompt, warning comment, or user-facing disclosure near this destructive action that it permanently removes remote data.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
99% confidence
Finding

The module docstring explicitly says the tool uses only Python's standard library and has no external dependencies. However, the code imports defusedxml, which is a third-party package and directly contradicts that claim.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.