Back to skill

Security audit

cloudflare-drop

Security checks for vulnerabilities and agentic risk

Overview

This is a legitimate Cloudflare publishing helper, but by default it can publish extra files from the same folder after only warning about them.

Install only if users can reliably publish from a clean, dedicated folder. Use --assets-only by default for ordinary static pages, read STAGED_REVIEW before sharing any URL, and do not keep credentials, database backups, private reports, source trees, or unrelated exports beside the HTML file being deployed. Cloudflare tokens should be narrowly scoped, and users should understand that permanent deployments and the local renewal archive can persist beyond the current session.

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
references/deploy.mjs:172
Finding

Sensitive sibling files are uploaded without enforced confirmation

Content
View full analysis

Vulnerability Details

File Location: references/deploy.mjs:172-188, 412-426
Vulnerability Type: Unintended public disclosure of local files
Risk Level: High

Complete Code Snippet

js
if (
  existsSync(srcDir) &&
  statSync(srcDir).isDirectory() &&
  srcDir !== stagedDir &&
  srcDir !== root
) {
  cpSync(srcDir, stagedDir, {
    recursive: true,
    filter: (s) => {
      const keep =
        !s.includes('node_modules') &&
        !s.includes('.git') &&
        !s.includes('__MACOSX') &&
        !basename(s).startsWith('.') &&
        s !== stagedDir; // guard against copying the staged dir into itself
      if (!keep) return false;
      if (!opts.assetsOnly) return true;
      // Directories still recurse; only files are filtered by type, and the
      // page itself is written separately below, so it can never be dropped.
      if (statSync(s).isDirectory()) return true;
      const kind = classifyStagedFile(basename(s));
      if (kind === 'asset') return true;
      skipped.push({ path: relative(srcDir, s).split(sep).join('/'), kind });
      return false;
    },
  });
}
js
const { stagedDir, indexPath, files, skipped } = stageForDrop(htmlPath, pageExpiry, undefined, {
  assetsOnly: opts.assetsOnly,
});
// Printed BEFORE the upload, on stderr, on every run — a review that only
// appears when something is wrong cannot be distinguished from one that
// failed to run at all.
const note = opts.note || ((line) => console.error(line));
for (const line of stagingReview(files, { skipped })) note(line);
const name = workerNameFrom(htmlPath, opts.name);
const compatibilityDate = todayISO();
let protection;
try {
  protection = opts.otp ? stageOtp(stagedDir, { name, compatibilityDate }) : null;
  const res = deployWithWrangler(stagedDir, {

The corresponding behavior is explicitly documented in SKILL.md:57-81:

text
This nev
...[truncated 2912 chars]
Remediation
View remediation

Remediation Suggestions

  1. Make allowlisted asset staging the default rather than recursively uploading all non-hidden files.
  2. Always exclude files classified as sensitive, including private-key formats, credential files, database backups, and environment/configuration secrets.
  3. If broad directory publication is required, stop before upload when flagged files are detected and require an explicit confirmation flag, such as --include-flagged.
  4. Bind confirmation to the reviewed staging manifest so files cannot be added or replaced between review and upload without invalidating approval.
  5. Print the complete upload manifest before confirmation and clearly distinguish intended static assets from unexpected or sensitive files.
  6. Preserve --assets-only behavior but make it the secure default; provide an explicitly named opt-out only for users who intentionally need additional file types.
  7. Add tests proving that recognized sensitive files cannot reach deployWithWrangler() without explicit authorization.
Vulnerability Patterns
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • 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
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (45)

Credential Access

High
Category
Privilege Escalation
Confidence
94% confidence
Finding

The documented behavior shows the tool may stage and publish credential-bearing files from the local filesystem because it recursively copies a sibling directory and only warns on suspicious files. In a deployment skill whose purpose is to make directories public, this context makes accidental credential disclosure especially dangerous.

Content

Scanner excerpt · CHANGELOG.md (reported line 54)May include surrounding context.

page's whole sibling directory, recursively, and this skill exists to make a directory public. The pre-existing filter already dropped node_modules, .git, __MACOSX and every dotfile — .env was never the gap. The gap was the ordinarily-named file: secrets.json, credentials.txt, backup.sql, id_rsa. New on stderr, before anything is uploaded:

text

Credential Access

High
Category
Privilege Escalation
Confidence
94% confidence
Finding

The documented behavior shows the tool may stage and publish credential-bearing files from the local filesystem because it recursively copies a sibling directory and only warns on suspicious files. In a deployment skill whose purpose is to make directories public, this context makes accidental credential disclosure especially dangerous.

Content

Scanner excerpt · CHANGELOG.md (reported line 54)May include surrounding context.

page's whole sibling directory, recursively, and this skill exists to make a directory public. The pre-existing filter already dropped node_modules, .git, __MACOSX and every dotfile — .env was never the gap. The gap was the ordinarily-named file: secrets.json, credentials.txt, backup.sql, id_rsa. New on stderr, before anything is uploaded:

text

Credential Access

High
Category
Privilege Escalation
Confidence
93% confidence
Finding

The staged review output demonstrates that sensitive files like secrets.json can reach the staging set before upload. Since the review is informational only and does not stop deployment, a user can still expose secrets to a public Cloudflare-hosted site.

Content

Scanner excerpt · CHANGELOG.md (reported line 60)May include surrounding context.

text
STAGED_FILES 7
STAGED_REVIEW 4 of 7 staged files are outside HTML/CSS/JS/images/fonts. …
  SENSITIVE_NAME  secrets.json  (312 bytes)
  UNEXPECTED_TYPE  backup.sql  (24 bytes)

Credential Access

High
Category
Privilege Escalation
Confidence
93% confidence
Finding

This example confirms the tool can stage secrets.json and proceed far enough to invoke deployment tooling; the failed upload in the smoke test was due to an invalid API token, not because the sensitive file was blocked. In a real authenticated run, the same behavior could publish confidential data externally.

Content

Scanner excerpt · CHANGELOG.md (reported line 88)May include surrounding context.

md
STAGED_FILES 5
STAGED_REVIEW 2 of 5 staged files are outside HTML/CSS/JS/images/fonts. …
  UNEXPECTED_TYPE  backup.sql  (24 bytes)
  SENSITIVE_NAME  secrets.json  (18 bytes)
NOTE wrangler runs with 7 allowlisted environment variables; 79 were withheld. …
DEPLOY_FAILED Command failed: npm exec --yes wrangler@4.134.0 -- deploy … --name drop-smoke-test …
  A request to the Cloudflare API (/accounts) failed. Invalid request headers

Credential Access

High
Category
Privilege Escalation
Confidence
92% confidence
Finding

The --assets-only mode is opt-in, which means the safer behavior is not the default; the default path can still include files like secrets.json and backup.sql. Because this skill is specifically designed to publish content to a public URL, accidental disclosure risk is substantial.

Content

Scanner excerpt · CHANGELOG.md (reported line 100)May include surrounding context.

md
variables detected"* warning proves the proxy variables arrived), and the
unrelated key was among the 79 withheld. The same page with `--assets-only`
printed `STAGED_FILES 3`, `STAGED_REVIEW ok`, and `STAGED_SKIPPED 2` naming
`secrets.json` and `backup.sql`.

Tests: 79 passing, including two new falsification suites
(`references/test/wrangler-env.test.mjs`, `references/test/staging-review.test.mjs`).

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 3)May include surrounding context.

md
es, AI-generated HTML, "give me a link I can share". Runs one packaged command (references/deploy.mjs) built on the Wrangler CLI, which is what Cloudflare's own

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 88)May include surrounding context.

md
es, AI-generated HTML, "give me a link I can share". Runs one packaged command (references/deploy.mjs) built on the Wrangler CLI, which is what Cloudflare's own

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 151)May include surrounding context.

md
es, AI-generated HTML, "give me a link I can share". Runs one packaged command (references/deploy.mjs) built on the Wrangler CLI, which is what Cloudflare's own

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 159)May include surrounding context.

md
es, AI-generated HTML, "give me a link I can share". Runs one packaged command (references/deploy.mjs) built on the Wrangler CLI, which is what Cloudflare's own

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 230)May include surrounding context.

md
es, AI-generated HTML, "give me a link I can share". Runs one packaged command (references/deploy.mjs) built on the Wrangler CLI, which is what Cloudflare's own

Credential Access

High
Category
Privilege Escalation
Confidence
97% confidence
Finding

The same underlying issue applies to ordinarily named sensitive files such as credentials.txt. Because the skill's purpose is to make a folder publicly reachable and it stages recursively, any misplaced credential file in that directory can be unintentionally published, and the fact that the tool only reports rather than blocks increases the chance of real-world leakage.

Content

Scanner excerpt · SKILL.md (reported line 61)May include surrounding context.

md
Staging copies the page's whole sibling directory, recursively, because a
multi-file page renders broken without its assets. `node_modules`, `.git`,
`__MACOSX` and every dotfile (so `.env` too) were already excluded. What was
never visible is the ordinarily-named file — `secrets.json`, `credentials.txt`,
`backup.sql`, `id_rsa` — and this skill's whole job is to turn a folder into a
public URL.

Credential Access

High
Category
Privilege Escalation
Confidence
97% confidence
Finding

The same underlying issue applies to ordinarily named sensitive files such as credentials.txt. Because the skill's purpose is to make a folder publicly reachable and it stages recursively, any misplaced credential file in that directory can be unintentionally published, and the fact that the tool only reports rather than blocks increases the chance of real-world leakage.

Content

Scanner excerpt · SKILL.md (reported line 61)May include surrounding context.

md
Staging copies the page's whole sibling directory, recursively, because a
multi-file page renders broken without its assets. `node_modules`, `.git`,
`__MACOSX` and every dotfile (so `.env` too) were already excluded. What was
never visible is the ordinarily-named file — `secrets.json`, `credentials.txt`,
`backup.sql`, `id_rsa` — and this skill's whole job is to turn a folder into a
public URL.

Credential Access

High
Category
Privilege Escalation
Confidence
95% confidence
Finding

This example output confirms that the tool may detect a sensitive file like secrets.json yet still proceed with deployment, since the documentation states the review never blocks a deploy. In context, that makes the exposure pathway more dangerous: the skill is designed for public sharing, so continuing after identifying likely secrets can directly leak confidential data to anyone with the resulting URL.

Content

Scanner excerpt · SKILL.md (reported line 70)May include surrounding context.

text
STAGED_FILES 7
STAGED_REVIEW 4 of 7 staged files are outside HTML/CSS/JS/images/fonts. …
  SENSITIVE_NAME  secrets.json  (312 bytes)
  UNEXPECTED_TYPE  backup.sql  (24 bytes)

Description-Behavior Mismatch

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

The code explicitly copies the entire sibling directory into the staged public site by default, while only warning about unexpected or sensitive files rather than blocking them. In a tool whose purpose is to publish content to a live public URL, this creates a strong risk of accidental exposure of non-asset files such as credentials, backups, configs, or private documents.

Content

No source excerpt is available for this finding.

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · references/deploy.mjs (reported line 72)May include surrounding context.

js
/api[-_]?key/i, /\.env\b/i, /(^|[^a-z])env$/i,
  /id_rsa/i, /id_ed25519/i, /id_ecdsa/i, /id_dsa/i,
  /\.pem$/i, /\.key$/i, /\.p12$/i, /\.pfx$/i, /\.keystore$/i,
  /htpasswd/i, /\.netrc$/i, /kubeconfig/i,
];

/**

Credential Access

High
Category
Privilege Escalation
Confidence
80% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · references/deploy.mjs (reported line 72)May include surrounding context.

js
/api[-_]?key/i, /\.env\b/i, /(^|[^a-z])env$/i,
  /id_rsa/i, /id_ed25519/i, /id_ecdsa/i, /id_dsa/i,
  /\.pem$/i, /\.key$/i, /\.p12$/i, /\.pfx$/i, /\.keystore$/i,
  /htpasswd/i, /\.netrc$/i, /kubeconfig/i,
];

/**

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · references/deploy.mjs (reported line 42)May include surrounding context.

js
// recursively, and this skill's job is to turn a directory into a PUBLIC URL.
// The pre-existing filter already dropped `node_modules`, `.git`, `__MACOSX`
// and every dotfile, so `.env` was never the gap — the gap is the ordinarily
// named file (`secrets.json`, `credentials.txt`, `backup.sql`, `id_rsa`), which
// was staged and published with nothing printed about it.
//
// So these tests plant exactly those files and assert they are NAMED in the

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · references/deploy.mjs (reported line 65)May include surrounding context.

js
// recursively, and this skill's job is to turn a directory into a PUBLIC URL.
// The pre-existing filter already dropped `node_modules`, `.git`, `__MACOSX`
// and every dotfile, so `.env` was never the gap — the gap is the ordinarily
// named file (`secrets.json`, `credentials.txt`, `backup.sql`, `id_rsa`), which
// was staged and published with nothing printed about it.
//
// So these tests plant exactly those files and assert they are NAMED in the

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · references/test/staging-review.test.mjs (reported line 8)May include surrounding context.

js
// recursively, and this skill's job is to turn a directory into a PUBLIC URL.
// The pre-existing filter already dropped `node_modules`, `.git`, `__MACOSX`
// and every dotfile, so `.env` was never the gap — the gap is the ordinarily
// named file (`secrets.json`, `credentials.txt`, `backup.sql`, `id_rsa`), which
// was staged and published with nothing printed about it.
//
// So these tests plant exactly those files and assert they are NAMED in the

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · references/test/staging-review.test.mjs (reported line 47)May include surrounding context.

js
// recursively, and this skill's job is to turn a directory into a PUBLIC URL.
// The pre-existing filter already dropped `node_modules`, `.git`, `__MACOSX`
// and every dotfile, so `.env` was never the gap — the gap is the ordinarily
// named file (`secrets.json`, `credentials.txt`, `backup.sql`, `id_rsa`), which
// was staged and published with nothing printed about it.
//
// So these tests plant exactly those files and assert they are NAMED in the

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · references/test/staging-review.test.mjs (reported line 58)May include surrounding context.

js
// recursively, and this skill's job is to turn a directory into a PUBLIC URL.
// The pre-existing filter already dropped `node_modules`, `.git`, `__MACOSX`
// and every dotfile, so `.env` was never the gap — the gap is the ordinarily
// named file (`secrets.json`, `credentials.txt`, `backup.sql`, `id_rsa`), which
// was staged and published with nothing printed about it.
//
// So these tests plant exactly those files and assert they are NAMED in the

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · references/test/staging-review.test.mjs (reported line 106)May include surrounding context.

js
// recursively, and this skill's job is to turn a directory into a PUBLIC URL.
// The pre-existing filter already dropped `node_modules`, `.git`, `__MACOSX`
// and every dotfile, so `.env` was never the gap — the gap is the ordinarily
// named file (`secrets.json`, `credentials.txt`, `backup.sql`, `id_rsa`), which
// was staged and published with nothing printed about it.
//
// So these tests plant exactly those files and assert they are NAMED in the

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · references/test/staging-review.test.mjs (reported line 107)May include surrounding context.

js
// recursively, and this skill's job is to turn a directory into a PUBLIC URL.
// The pre-existing filter already dropped `node_modules`, `.git`, `__MACOSX`
// and every dotfile, so `.env` was never the gap — the gap is the ordinarily
// named file (`secrets.json`, `credentials.txt`, `backup.sql`, `id_rsa`), which
// was staged and published with nothing printed about it.
//
// So these tests plant exactly those files and assert they are NAMED in the

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · references/test/staging-review.test.mjs (reported line 121)May include surrounding context.

js
// recursively, and this skill's job is to turn a directory into a PUBLIC URL.
// The pre-existing filter already dropped `node_modules`, `.git`, `__MACOSX`
// and every dotfile, so `.env` was never the gap — the gap is the ordinarily
// named file (`secrets.json`, `credentials.txt`, `backup.sql`, `id_rsa`), which
// was staged and published with nothing printed about it.
//
// So these tests plant exactly those files and assert they are NAMED in the

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · references/test/staging-review.test.mjs (reported line 129)May include surrounding context.

js
// recursively, and this skill's job is to turn a directory into a PUBLIC URL.
// The pre-existing filter already dropped `node_modules`, `.git`, `__MACOSX`
// and every dotfile, so `.env` was never the gap — the gap is the ordinarily
// named file (`secrets.json`, `credentials.txt`, `backup.sql`, `id_rsa`), which
// was staged and published with nothing printed about it.
//
// So these tests plant exactly those files and assert they are NAMED in the

Static analysis

Detected: suspicious.dangerous_exec, suspicious.env_credential_access

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
references/wrangler.mjs:228

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
references/test/otp.test.mjs:39