Back to skill

Security audit

cloudflare-workers

Security checks for vulnerabilities and agentic risk

Overview

This Cloudflare Workers skill is mostly ordinary documentation, but several copy-ready examples can leak credentials or sensitive request data and it suggests live deployment tooling without enough guardrails.

Install only if you are comfortable reviewing Cloudflare deployment and logging snippets before use. Pin npm tool versions where possible, prefer staging or dry-run before any wrangler deploy, avoid copying the outbound proxy example as written, and never send raw request headers, cookies, authorization values, full URLs, stack traces, or raw emails to logs or third-party error services without explicit redaction and retention controls.

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

T09 · Insecure Skill Coding Practices

Error
Location
references/advanced-features.md:89
Finding
Platform Secret Forwarded to Attacker-Controlled Destinations<![CDATA[ ## Vulnerability Details **File Location**: `references/advanced-features.md:89-103` **Vulnerability Type**: Outbound credential disclosure through an unrestricted proxy **Risk Level**: Critical ### Vulnerable Code ```typescript export default { async fetch(request: Request, env: Env): Promise<Response> { // Validate request from customer Worker const url = new URL(request.url); // Block certain domains const blockedDomains = ["internal.example.com"]; if (blockedDomains.some((d) => url.hostname.includes(d))) { return new Response("Forbidden", { status: 403 }); } // Add authentication request.headers.set("X-Platform-Auth", env.PLATFORM_SECRET); // Forward to destination return fetch(request); }, }; ``` ### Technical Analysis The outbound destination is derived directly from the incoming request. The Worker then adds the privileged `PLATFORM_SECRET` to that request before forwarding it with `fetch(request)`. The single-domain blocklist does not establish that the destination is trusted. Any attacker-controlled hostname not matching `internal.example.com` remains permitted and receives the secret. The substring comparison is also unsuitable for security validation because it can produce both incorrect blocks and incorrect trust assumptions. Adding a platform credential to a caller-controlled request exceeds the minimum privileges necessary for an outbound routing Worker. Credentials should only be attached after the destination has been matched against a strict allowlist. ### Attack Path 1. An attacker sends a request through the outbound Worker with an attacker-controlled URL as its destination. 2. The destination hostname does not contain `internal.example.com`, so the blocklist permits it. 3. The Worker adds `X-Platform-Auth: <PLATFORM_SECRET>` to the request. 4. The Worker forwards the modified request to the attacker-controlled server. 5. The attacker captures the platform secret and attemp ...[truncated 612 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace the blocklist with an exact allowlist of approved schemes, hostnames, and ports. 2. Never attach credentials to a request whose destination remains caller-controlled. 3. Construct a new outbound `Request` rather than modifying and forwarding the inbound request. 4. Copy only explicitly approved headers and discard caller-provided authentication, forwarding, and hop-by-hop headers. 5. Add the platform credential only after destination validation has succeeded. 6. Reject redirects or manually validate every redirect destination before following it. 7. Use separate, narrowly scoped credentials for each destination or service. 8. Rotate `PLATFORM_SECRET` if the vulnerable pattern has been deployed. 9. Add tests proving that arbitrary hosts, subdomains, alternate ports, non-HTTPS schemes, redirects, and malformed hostnames are rejected. A safer design should resemble: ```typescript const allowedDestinations = new Set([ "https://api.trusted.example:443", ]); const incomingUrl = new URL(request.url); const origin = `${incomingUrl.protocol}//${incomingUrl.host}`; if (!allowedDestinations.has(origin)) { return new Response("Forbidden", { status: 403 }); } const headers = new Headers(); headers.set("Content-Type", request.headers.get("Content-Type") || "application/octet-stream"); headers.set("X-Platform-Auth", env.PLATFORM_SECRET); const outboundRequest = new Request(incomingUrl, { method: request.method, headers, body: request.body, redirect: "manual", }); return fetch(outboundRequest); ``` ]]>

T09 · Insecure Skill Coding Practices

Error
Location
references/observability.md:501
Finding
External Error Reports Include Complete Request Headers<![CDATA[ ## Vulnerability Details **File Location**: `references/observability.md:501-516` **Vulnerability Type**: Sensitive request data exposure through telemetry **Risk Level**: High ### Vulnerable Code ```typescript await fetch("https://errors.example.com/report", { method: "POST", headers: { "Content-Type": "application/json", "Authorization": `Bearer ${env.ERROR_TOKEN}`, }, body: JSON.stringify({ error: { message: error.message, stack: error.stack, name: error.name, }, request: { url: request.url, method: request.method, headers: Object.fromEntries(request.headers), }, timestamp: Date.now(), }), }); ``` ### Technical Analysis The error-reporting function serializes every inbound request header and sends the resulting data to an external service. Request headers commonly contain bearer tokens, cookies, API keys, signed authentication values, session identifiers, internal routing information, and tenant-specific metadata. The complete request URL is also exported. Query strings may contain access tokens, password-reset tokens, signed links, personal information, or other sensitive parameters. Error messages and stack traces can disclose implementation details, internal file paths, data values, or service topology. This collection exceeds what is normally required to diagnose an application error. Encoding the data as JSON does not protect it from disclosure to the receiving service. ### Attack Path 1. A user sends an authenticated request containing an `Authorization` header, session cookie, API key, or another secret-bearing header. 2. An error occurs while the request is processed. 3. The global error handler invokes the external reporting function. 4. The function converts all request headers into a plain object and includes them in the report. 5. The report is sent to `errors.example.com`. 6. Anyone with access to the external error platform, its storage, backups, logs, or ...[truncated 767 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace complete-header serialization with a strict allowlist of non-sensitive diagnostic fields. 2. Never export `Authorization`, `Cookie`, `Set-Cookie`, `Proxy-Authorization`, API-key headers, CSRF tokens, or custom authentication headers. 3. Remove query strings and URL fragments before recording URLs. 4. Sanitize error messages and stack traces before external transmission. 5. Generate a request identifier and use it to correlate internal logs rather than exporting complete request context. 6. Apply field-length limits and structured schema validation to telemetry. 7. Document the external processor, retention period, access controls, encryption, and deletion policy. 8. Rotate credentials if historical error reports may already contain active secrets. 9. Add automated tests that inject canary secrets into headers and verify that none appear in generated reports. For example: ```typescript const url = new URL(request.url); const report = { error: { name: error.name, message: "Internal processing error", }, request: { origin: url.origin, path: url.pathname, method: request.method, requestId: request.headers.get("CF-Ray"), }, timestamp: Date.now(), }; ``` ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
references/observability.md:664
Finding
Production Debug Logging Captures Complete Request and Response Headers<![CDATA[ ## Vulnerability Details **File Location**: `references/observability.md:664-678` **Vulnerability Type**: Sensitive credential and session data written to logs **Risk Level**: Medium ### Vulnerable Code ```typescript export default { async fetch(request: Request, env: Env): Promise<Response> { console.log("Request:", { url: request.url, method: request.method, headers: Object.fromEntries(request.headers), }); const response = await handleRequest(request, env); console.log("Response:", { status: response.status, headers: Object.fromEntries(response.headers), }); return response; }, }; ``` ### Technical Analysis The example logs all request and response headers. Request headers may contain bearer credentials, cookies, API keys, CSRF values, and private routing metadata. Response headers may contain `Set-Cookie`, authorization challenges, signed URLs, internal infrastructure identifiers, or application-specific secrets. In Cloudflare deployments, console output can be visible through Wrangler Tail, Workers Logs, Tail Workers, or Logpush destinations. Consequently, sensitive data may leave the request-processing boundary and become available to a broader set of operators and third-party telemetry systems. The complete request URL may also disclose sensitive query parameters. Production debugging does not require unrestricted capture of all headers. ### Attack Path 1. A user sends an authenticated request to the Worker. 2. The Worker converts every request header into a loggable object. 3. The request handler returns a response that may contain sensitive response headers. 4. The Worker logs all response headers as well. 5. Cloudflare log storage, a Tail Worker, or a configured Logpush destination retains the entries. 6. A user with log access, or an attacker who compromises the logging destination, obtains the recorded credentials or session values. ### Impact Assessment Disclosure may e ...[truncated 443 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Do not log complete request or response headers. 2. Use an explicit allowlist such as request method, sanitized path, status, request ID, and duration. 3. Redact `Authorization`, `Cookie`, `Set-Cookie`, API-key, CSRF, and custom secret headers. 4. Remove query strings before logging request URLs. 5. Disable debug-level logging in production by default. 6. Apply short retention periods and role-based access controls to production logs. 7. Encrypt exported logs and restrict downstream Logpush destinations. 8. Add automated secret-scanning rules for telemetry. 9. Review existing retained logs and rotate any credentials that may have been captured. For example: ```typescript const url = new URL(request.url); console.log("Request:", { method: request.method, path: url.pathname, requestId: request.headers.get("CF-Ray"), }); console.log("Response:", { status: response.status, }); ``` ]]>

T08 · Insecure Dependencies

Note
Location
SKILL.md:54
Finding
Unpinned npm Tools and Mutable Latest Packages Are Installed and Executed<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:54-64` **Vulnerability Type**: Unpinned third-party dependency execution **Risk Level**: Low ### Vulnerable Code ```bash npm install -g wrangler # Login to Cloudflare wrangler login ``` ```bash # Using C3 (create-cloudflare) - recommended npm create cloudflare@latest my-worker # Or create manually wrangler init my-worker cd my-worker ``` Related unpinned installation and execution guidance also appears in: - `references/development-patterns.md:14` - `references/development-patterns.md:99` - `references/wrangler-and-deployment.md:9-12` - `references/wrangler-and-deployment.md:57` ### Technical Analysis The instructions install Wrangler without a fixed version and execute `create-cloudflare@latest`, which deliberately resolves to mutable registry content at execution time. Other project instructions similarly install unpinned testing packages and invoke tools through `npx`. The package names are consistent with the declared Cloudflare development workflow, and the audit found no evidence of typosquatting or a currently malicious package. Nevertheless, the instructions do not guarantee that the code executed in the future is the same code that was reviewed. Global installation also increases the installation scope and can make the executable available to unrelated projects or users, depending on npm configuration. npm package installation and scaffolding may execute package lifecycle scripts with the current user's or CI runner's privileges. ### Attack Path 1. A user follows the Skill and runs the unpinned installation or `@latest` scaffolding command. 2. npm resolves the package version from the registry at that time. 3. If the package account, registry, dependency chain, or newly published version is compromised, npm downloads the altered code. 4. Package lifecycle scripts or the invoked CLI execute with the permissions of the developer or CI runner. 5. The compromised code may access so ...[truncated 598 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin reviewed package versions instead of using implicit latest versions or `@latest`. 2. Prefer project-local development dependencies over global installation. 3. Commit a lockfile and use `npm ci` in CI and reproducible development environments. 4. Use `npx --no-install` after the dependency has been installed and verified locally. 5. Review package provenance, maintainers, signatures, lifecycle scripts, and transitive dependencies. 6. Use automated dependency scanning and controlled update tooling. 7. Run package installation and scaffolding in an isolated environment without production secrets. 8. Give CI deployment tokens only the minimum required Cloudflare permissions. 9. Separate dependency installation jobs from deployment jobs so installation scripts cannot automatically access production credentials. For example: ```bash npm install --save-dev wrangler@<reviewed-version> npm install --save-dev vitest@<reviewed-version> \ @cloudflare/vitest-pool-workers@<reviewed-version> npm ci npx --no-install wrangler --version ``` ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • YARA SignaturesMalware Match, Webshell Match, Cryptominer Match
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (41)

Ae1

High
Category
analysis-evasion
Content
- **Deployment & CI/CD**: `references/wrangler-and-deployment.md` - Wrangler v4 migration, commands, GitHub Actions, GitLab CI/CD, gradual rollouts, remote bind
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

YARA rule 'python_webshell': Python webshell — exec/eval/os.popen on request input [webshells]

High
Category
YARA Match
Content
: "/api/users" }),
    handler: handleGetUsers,
  },
  {
    pattern: new URLPattern({ pathname: "/api/users/:id" }),
    handler: handleGetUser,
  },
  {
    pattern: new URLPattern({ pathname: "/api/users" }),
    handler: handleCreateUser,
  },
];

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    for (const route of routes) {
      const match = route.pattern.exec(request.url);
      if (match) {
        return route.handler(request, env, match);
      }
    }

    return Response.json({ error: "Not found" }, { status: 404 });
  },
};

async function handleGetUsers(request: Request, env: Env): Promise<Response> {
  const users = await env.DB.prepare("SELECT * FROM users").all();
  return Response.json(users.results);
}
```

### Middleware Pattern

Chain middleware for cross-cutting concerns.

```typescript
type Middleware = (
  request: Request,
  env: Env,
  next: () => Promise<Response>
) => Promise<Response>;

const corsMiddleware: Middleware =
Confidence
75% confidence
Finding
YARA rule matched a known webshell pattern (PHP, Python, JSP, or ASPX webshell).

Intent-Code Divergence

High
Confidence
99% confidence
Finding
The error-reporting example sends complete request headers to an external error service, which can disclose bearer tokens, cookies, client certificates, and other secrets to a third party. Because this is framed as normal error tracking, it encourages broad exfiltration of user and service credentials beyond the application boundary.

Missing User Warnings

High
Confidence
99% confidence
Finding
This example exports full request headers to an external service during error reporting, directly risking exposure of credentials and personal data. The lack of any warning or redaction guidance makes it especially likely to be copied into production unchanged.

Missing User Warnings

High
Confidence
98% confidence
Finding
The remote debugging snippet logs full request and response headers with no warning about credential or session leakage. In practice, this can expose Authorization, Cookie, Set-Cookie, CSRF, and internal routing headers to anyone with log access or to integrated logging backends.

Credential Access

High
Category
Privilege Escalation
Content
# Delete secret
wrangler secret delete API_KEY

# Bulk import from .env
wrangler secret bulk .env.production
```
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
wrangler secret delete API_KEY

# Bulk import from .env
wrangler secret bulk .env.production
```

### Bindings Configuration
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
## Workers Builds (Git Integration)

Enable automatic deployments on git push via the dashboard.

**Setup:**
1. Connect your GitHub/GitLab repository
Confidence
70% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The skill gives direct `wrangler deploy` commands without clearly warning that they publish code and configuration to a real Cloudflare environment. In an agent-assisted workflow, this increases the chance of accidental production deployment, exposure of unfinished code, unintended route changes, or modification of bound resources when a user may have expected a local or dry-run action.

External Transmission

Medium
Category
Data Exfiltration
Content
env: Env
): Promise<void> {
  const response = await fetch(
    `https://api.cloudflare.com/client/v4/accounts/${env.ACCOUNT_ID}/workers/dispatch/namespaces/${env.NAMESPACE}/scripts/${customerId}`,
    {
      method: "PUT",
      headers: {
Confidence
50% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
env: Env
): Promise<void> {
  const response = await fetch(
    `https://api.cloudflare.com/client/v4/accounts/${env.ACCOUNT_ID}/workers/dispatch/namespaces/${env.NAMESPACE}/scripts/${customerId}`,
    {
      method: "PUT",
      headers: {
Confidence
50% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
env: Env
): Promise<void> {
  const response = await fetch(
    `https://api.cloudflare.com/client/v4/accounts/${env.ACCOUNT_ID}/workers/dispatch/namespaces/${env.NAMESPACE}/scripts/${customerId}`,
    {
      method: "PUT",
      headers: {
Confidence
50% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
This markdown file includes a code example that forwards incoming emails and stores raw email bodies, which can contain sensitive personal or confidential information. The surrounding documentation does not provide any warning or disclosure about privacy impact, data retention, or handling of sensitive content.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The example posts exception data, script name, and request timestamp to an external endpoint. In markdown documentation, this should be accompanied by a warning that logs may contain sensitive operational or user-related data and will be transmitted to a third-party service.

External Transmission

Medium
Category
Data Exfiltration
Content
for (const event of events) {
      if (event.outcome === "exception") {
        // Log errors to external service
        await fetch("https://logs.example.com", {
          method: "POST",
          body: JSON.stringify({
            scriptName: event.scriptName,
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Missing User Warnings

Medium
Confidence
87% confidence
Finding
This markdown file includes examples that delete KV data, but the surrounding documentation does not warn that the operation permanently removes stored user/application data. For markdown files, safety-relevant behaviors that can affect user data should be disclosed so readers do not treat the example as risk-free boilerplate.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
The guide includes an R2 `delete()` example but provides no caution about deleting stored objects that may contain user files or backups. In markdown documentation, destructive storage operations should be accompanied by an explicit warning about impact and reversibility.

Missing User Warnings

Medium
Confidence
83% confidence
Finding
The `alarm()` example removes stored state with `storage.delete("count")`, but the documentation does not mention that persisted state is being erased. Because this markdown teaches a stateful storage API, readers should be warned when examples perform destructive cleanup.

Missing User Warnings

Medium
Confidence
85% confidence
Finding
The Analytics Engine example writes a data point containing a user identifier-like value (`"user-123"`) without any note about logging user-related data. Markdown guidance should warn when examples transmit or persist potentially sensitive telemetry so users can assess privacy and retention implications.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The AI Gateway section explicitly retrieves logs and patches metadata containing a user identifier, but the markdown provides no warning that logs may expose or retain prompt content and user-linked metadata. This is a privacy-relevant behavior that should be disclosed in documentation.

Rp1

Medium
Category
MCP Rug Pull
Confidence
70% confidence
Finding
npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The logging examples explicitly include `userId` and full `request.url`, which can normalize collection of personal data, secrets in query strings, and other sensitive identifiers without any privacy minimization warning. In documentation, this is risky because users often copy patterns directly into production observability pipelines, leading to unnecessary exposure in logs and downstream systems.

External Transmission

Medium
Category
Data Exfiltration
Content
```bash
# Via GraphQL API
curl -X POST https://api.cloudflare.com/client/v4/graphql \
  -H "Authorization: Bearer $TOKEN" \
  -d '{
    "query": "query { viewer { accounts(filter: {accountTag: \"$ACCOUNT_ID\"}) { workersLogsData(filter: {datetime_gt: \"2025-09-01T00:00:00Z\"}) { logs { timestamp message } } } } }"
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The custom logging examples transmit request metadata, error messages, and stack traces to external observability services without any caution about sensitive-data handling. While external logging is expected in this context, the missing sanitization and warning materially increase the chance that secrets, PII, or internal details are exported unintentionally.

External Transmission

Medium
Category
Data Exfiltration
Content
private async send(entry: LogEntry) {
    // Send to logging service
    await fetch("https://logs.example.com/ingest", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Static analysis

Detected: suspicious.exposed_secret_literal

File appears to expose a hardcoded API secret or token.

Critical
Code
suspicious.exposed_secret_literal
Location
references/observability.md:384