Back to skill

Security audit

Send Me My Files - R2 upload with short lived signed urls

Security checks for vulnerabilities and agentic risk

Overview

The skill does what it says at a high level, but it can upload local files, store cloud credentials, expose links, and delete remote objects with limited safeguards.

Install only if you are comfortable giving the skill bucket-scoped R2/S3 credentials and allowing it to upload selected local files, generate shareable links, list objects, and delete objects. Use a dedicated bucket, least-privilege credentials, HTTPS endpoints, short URL expirations, and verify ~/.r2-upload.yml is chmod 600; avoid using it for sensitive files unless you trust the storage destination and the agent workflow invoking the tools.

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 (2)

T09 · Insecure Skill Coding Practices

Warning
Location
src/onboard.ts:130
Finding

Unvalidated S3-Compatible Endpoints May Expose Credentials and Uploaded Files

Content
View full analysis

Vulnerability Details

File Location: src/onboard.ts:130-140, with the network sink in src/index.ts:49-57 and src/index.ts:253-264
Vulnerability Type: Unvalidated network endpoint and insecure transport
Risk Level: Medium

Vulnerable Code

typescript
const endpoint = await prompt('Endpoint URL (e.g., https://minio.example.com): ');
const bucketName = await prompt('Bucket name: ');
const accessKeyId = await prompt('Access Key ID: ');
const secretAccessKey = await prompt('Secret Access Key: ');
const region = await prompt('Region (default: us-east-1): ') || 'us-east-1';

config.default = bucketName;
config.buckets[bucketName] = {
  endpoint,
  access_key_id: accessKeyId,
  secret_access_key: secretAccessKey,
  bucket_name: bucketName,
  region,
};

The configured endpoint is subsequently used without validation:

typescript
function getS3Client(bucketConfig: BucketConfig): S3Client {
  return new S3Client({
    endpoint: bucketConfig.endpoint,
    region: bucketConfig.region || 'auto',
    credentials: {
      accessKeyId: bucketConfig.access_key_id,
      secretAccessKey: bucketConfig.secret_access_key,
    },
  });
}

Uploaded file content is sent through this client:

typescript
const s3 = getS3Client(bucketConfig);

await s3.send(
  new PutObjectCommand({
    Bucket: bucketConfig.bucket_name,
    Key: objectKey,
    Body: fileContent,
    ContentType: contentType,
  })
);

Technical Analysis

The onboarding process accepts an arbitrary S3-compatible endpoint without parsing the value, enforcing HTTPS, restricting the destination, or warning about insecure transport. Manually supplied configuration is subject to the same issue because getS3Client() directly trusts bucketConfig.endpoint.

The AWS SDK sends credential-derived authorization data with requests. During uploads, it also sends the complete selected file to ...[truncated 1638 chars]

Remediation
View remediation

Remediation Suggestions

  1. Parse every configured endpoint with the standard URL class and reject malformed values.
  2. Require the https: protocol by default.
  3. If HTTP support is necessary for local MinIO development, require an explicit opt-in setting and restrict it to loopback or approved private-network destinations.
  4. Display the normalized destination and require user confirmation before testing a newly configured endpoint.
  5. Consider an optional endpoint allowlist for managed deployments.
  6. Revalidate endpoints when loading manually edited configuration rather than relying only on onboarding checks.
  7. Document that files and signed authorization requests are transmitted to the configured storage operator.

T09 · Insecure Skill Coding Practices

Warning
Location
README.md:36
Finding

Manual Setup May Store Plaintext Storage Credentials with Excessive File Permissions

Content
View full analysis

Vulnerability Details

File Location: README.md:36-40
Vulnerability Type: Insecure plaintext credential-file permissions
Risk Level: Medium

Vulnerable Code

bash
2. Create config file:
```bash
cp example-config.yml ~/.r2-upload.yml
# Edit ~/.r2-upload.yml with your credentials
text

The documentation separately makes an unconditional security claim in `SECURITY.md:5-8`:

```markdown
### 1. Config File Permissions
- ✅ `~/.r2-upload.yml` is created with `0600` permissions (owner read/write only)
- ✅ Prevents other users from reading your credentials

Technical Analysis

The automated onboarding path securely creates the file with mode 0600:

typescript
await writeFile(configFile, yamlContent, { mode: 0o600 });

However, the documented manual setup uses ordinary cp and does not apply restrictive permissions. The resulting mode depends on the source file permissions and the user's umask. On systems with common permissive defaults, the copied file may remain readable by the user's group or by all local users.

The file contains plaintext S3 or R2 access-key IDs and secret access keys. The implementation does not validate file ownership or permissions when loading the configuration, so an insecurely created file is accepted silently. The documentation's claim that the file is always protected by mode 0600 is therefore inaccurate for the manual installation path.

Attack Path

  1. A user follows the documented manual setup and copies example-config.yml with cp.
  2. The user inserts valid access and secret keys into the copied file.
  3. The resulting ~/.r2-upload.yml retains group-readable or world-readable permissions because of its source mode and the user's umask.
  4. Another local user or compromised process running under a different account reads the file.
  5. The attacker uses the recovered credentials against the configured storage endpoint.

Imp

...[truncated 614 chars]

Remediation
View remediation

Remediation Suggestions

  1. Replace the manual copy command with one that explicitly creates the destination using mode 0600:
bash
install -m 600 example-config.yml ~/.r2-upload.yml
  1. Alternatively, immediately enforce permissions after copying:
bash
cp example-config.yml ~/.r2-upload.yml
chmod 600 ~/.r2-upload.yml
  1. In loadConfig(), inspect the file's owner and permission bits before reading it.
  2. Reject the configuration or emit a prominent warning if group or other users have any access.
  3. Avoid claiming that all configuration files are created securely unless every documented setup path enforces the same permissions.
  4. Continue recommending bucket-specific, least-privilege credentials to limit impact if the file is exposed.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Output HandlingUnvalidated Output Injection, Cross-Context Output, Unbounded Output
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (12)

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding

The skill documentation describes use of environment variables and local configuration containing cloud credentials, but no explicit permission declaration is present. This can mislead users about the skill's ability to access sensitive local configuration and secrets, increasing the chance that credentials are exposed or used without informed consent.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The declared purpose focuses on uploading files and generating presigned links, but the documented tools and analysis indicate additional destructive and sensitive behaviors: listing objects, deleting objects, and onboarding that collects credentials and writes them to ~/.r2-upload.yml. This mismatch undermines informed consent and could cause users to grant access without realizing the skill can enumerate or remove stored data and persist secrets locally.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The skill metadata says it uploads files and generates signed URLs, but the implemented toolset also exposes bucket listing and object deletion. That scope mismatch is security-relevant because users or orchestrating agents may grant trust based on the narrower description while the skill can perform broader destructive and reconnaissance actions against configured storage.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The documentation exposes a destructive r2_delete capability without any warning, confirmation guidance, or mention of safeguards such as dry-run, prefix restrictions, or explicit user approval. In an agent-driven context, this increases the risk of accidental or overbroad deletion of remote objects, especially if the tool is invoked from ambiguous natural-language instructions.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The skill promotes uploading files to Cloudflare R2, AWS S3, or other S3-compatible providers but does not prominently warn that file contents are transmitted to third-party cloud services. Users may incorrectly assume data remains local or may upload sensitive material without understanding the external transfer and storage implications.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The documentation mentions --public and presigned URLs but does not clearly warn that these links enable file access by anyone possessing the URL, and that --public may expose objects without authentication. This creates a realistic risk of accidental oversharing or permanent public exposure of sensitive files.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The upload path reads an arbitrary local file and sends its contents to remote S3-compatible storage with no consent gate, path restriction, or warning about exfiltration risk. In an agent setting, this is dangerous because a model can be induced to upload sensitive local files such as SSH keys, environment files, or documents to attacker-controlled or unintended buckets.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
86% confidence
Finding

The delete operation permanently removes remote objects based solely on the provided key, with no confirmation, soft-delete, or policy guardrails. In an agent workflow, prompt injection or user misunderstanding could trigger destructive deletion of important data in the configured bucket.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The onboarding script prompts for long-lived storage credentials and writes them to a predictable local file in the user's home directory. Although it sets restrictive file permissions (0600), it does not clearly warn the user that sensitive secrets will be stored on disk, increasing the risk of accidental exposure through backups, endpoint compromise, shell history misunderstandings, or multi-tool access to the home directory. In the context of an upload/download skill for S3-compatible storage, these credentials can grant direct access to buckets and enable unauthorized read, write, or deletion of stored files.

Content

No source excerpt is available for this finding.

Unbounded Output

Medium
Category
Output Handling
Confidence
93% confidence
Finding

This is a real security gap acknowledged by the document: uploads currently have no size limits. An attacker or careless user could upload very large files, causing denial of service through bandwidth, storage, memory, or cost exhaustion, especially in an automation context where uploads may be triggered repeatedly.

Content

Scanner excerpt · SECURITY.md (reported line 103)May include surrounding context.

md
- ✅ Credential exposure in code (external config)

### What we don't protect against:
- ⚠️ Large file uploads (no size limits)
- ⚠️ Malicious file types (no validation)
- ⚠️ Path traversal in custom keys
- ⚠️ Rate limiting / abuse

Known Vulnerable Dependency: @modelcontextprotocol/sdk==1.0.4 — 1 advisory(ies): CVE-2025-66414 (Model Context Protocol (MCP) TypeScript SDK does not enable DNS rebinding protec)

High
Category
Supply Chain
Confidence
97% confidence
Finding

The manifest explicitly includes @modelcontextprotocol/sdk version 1.0.4, which is flagged as affected by a DNS rebinding protection vulnerability. In the context of an MCP-integrated skill, this is especially relevant because the SDK may broker communications with local or internal services; a rebinding flaw can let an attacker pivot browser-origin trust to reach internal endpoints or localhost-exposed services unexpectedly.

Content

No source excerpt is available for this finding.

Known Vulnerable Dependency: js-yaml==4.1.0 — 5 advisory(ies): CVE-2026-84375 (js-yaml: maxTotalMergeKeys does not limit CPU use for empty merge sources); CVE-2026-59869 (js-yaml: YAML merge-key chains can force quadratic CPU consumption); GHSA-5p4m-2wfm-xmqj (JS-YAML: Quadratic CPU consumption in !!omap resolution (3.x and 4.x) — CVE-2026) +2 more

High
Category
Supply Chain
Confidence
96% confidence
Finding

The manifest includes js-yaml 4.1.0, which is flagged for multiple CPU-consumption issues involving merge keys and object-map parsing. If this skill parses attacker-controlled YAML anywhere in its configuration, onboarding, or runtime flows, an adversary could trigger denial of service through excessive CPU use; in an agent skill context, that can disrupt automation or tie up shared worker resources.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.