Back to skill

Security audit

HTTP Retry Circuit Breaker

Security checks for vulnerabilities and agentic risk

Overview

This is a coherent HTTP retry helper, but its retry and timeout behavior can make state-changing API calls unsafe if used without extra safeguards.

Review carefully before installing. Use this only where repeated requests are safe, or add idempotency keys and explicit retry controls for POST, PUT, DELETE, payments, submissions, account changes, and deletes. Do not assume a timeout means the remote service stopped processing the request.

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
http-retry-circuit-breaker.js:253
Finding
Request timeout does not cancel the underlying HTTP operation<![CDATA[ ## Vulnerability Details **File Location**: `http-retry-circuit-breaker.js`, lines 253–289 and 319–358 **Vulnerability Type**: Uncancelled asynchronous HTTP operations after timeout **Risk Level**: High ### Vulnerable Code ```javascript const response = await Promise.race([ requestFn(), new Promise((_, reject) => setTimeout(() => reject(new Error('Request timeout')), this.config.timeout) ) ]); // Check for HTTP error status codes if (response && response.status >= 400) { if (this.isRetryable(null, response.status)) { throw new Error(`HTTP ${response.status}`); } } // Success this.circuitBreaker.onSuccess(); this.stats.successfulRequests++; return response; } catch (error) { lastError = error; // Check if retryable if (!this.isRetryable(error) || attempt >= this.config.maxRetries) { this.circuitBreaker.onFailure(); this.stats.failedRequests++; if (attempt >= this.config.maxRetries && error.code !== 'CIRCUIT_OPEN') { lastError.code = 'MAX_RETRIES'; } throw lastError; } // Retry this.stats.retriedRequests++; this.emit('retry', { attempt: attempt + 1, maxRetries: this.config.maxRetries, error: error.message, delay: this.calculateDelay(attempt) }); await this.sleep(this.calculateDelay(attempt)); attempt++; } ``` The affected mechanism is also used for state-changing HTTP methods: ```javascript async post(url, data, options = {}) { return this.executeWithRetry(async () => { const response = await fetch(url, { ...options, method: 'POST', headers: { 'Content-Type': 'application/json', ...options.headers }, body: JSON.stringify(data) }); return response; }); } async put(url, data, options = {}) { return this.executeWithRetry(async () => { const response = await fetch(url, { ...options, method: 'PUT', headers: { 'Content-Type': 'application/json', ...options.heade ...[truncated 3223 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Create a separate `AbortController` for every request attempt. 2. Abort the underlying request when the configured timeout expires. 3. Clear the timeout handle in a `finally` block when the request completes, fails, or is aborted. 4. Combine an application-provided abort signal with the attempt-specific timeout signal rather than silently replacing it. 5. Change `executeWithRetry()` so custom request callbacks receive an attempt-scoped `AbortSignal`. 6. Disable retries for POST, PUT, DELETE, and other potentially non-idempotent operations by default. 7. Require explicit retry authorization for state-changing operations and support idempotency keys. 8. Document that a timeout does not guarantee that a remote server failed to process a request. 9. Apply total operation deadlines and concurrency limits so repeated attempts cannot accumulate indefinitely. 10. Assign a consistent retryable timeout code, such as `ETIMEDOUT`, only after cancellation is correctly implemented. Example hardening pattern: ```javascript async executeAttempt(requestFn) { const controller = new AbortController(); let timeoutHandle; try { timeoutHandle = setTimeout(() => { controller.abort(new Error('Request timeout')); }, this.config.timeout); return await requestFn(controller.signal); } finally { clearTimeout(timeoutHandle); } } async get(url, options = {}) { return this.executeWithRetry(async (signal) => { return fetch(url, { ...options, method: 'GET', signal }); }); } ``` For non-idempotent requests, require an explicit option and an idempotency key: ```javascript async post(url, data, options = {}) { if (options.allowRetry && !options.idempotencyKey) { throw new Error('An idempotency key is required when retrying POST requests'); } // Add the idempotency key to the request headers and disable retries // unless allowRetry was explicitly selected. } ``` ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (14)

Natural-Language Policy Violations

Medium
Confidence
92% confidence
Finding
This markdown file contains user-facing instructions exclusively in Chinese, and there is no indication that the skill is region-specific or that users can opt into another language. Under the policy, forcing a specific language without user choice is a natural-language policy violation.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The README promotes automatic HTTP retries and shows example usage without warning that retries can repeat non-idempotent operations or resend data to external services. In an agent skill context, this can cause duplicate actions such as repeated purchases, submissions, or writes if users apply the pattern beyond safe GET/idempotent requests.

External Transmission

Medium
Category
Data Exfiltration
Content
});

// Make request with automatic retry
const response = await client.get('https://api.example.com/data');
```

### Advanced Configuration
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
});

// Make request with automatic retry
const response = await client.get('https://api.example.com/data');
```

### Advanced Configuration
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
});

// Make request with automatic retry
const response = await client.get('https://api.example.com/data');
```

### Advanced Configuration
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
});

// Make request with automatic retry
const response = await client.get('https://api.example.com/data');
```

### Advanced Configuration
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
});

// Make request with automatic retry
const response = await client.get('https://api.example.com/data');
```

### Advanced Configuration
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
});

// Make request with automatic retry
const response = await client.get('https://api.example.com/data');
```

### Advanced Configuration
Confidence
50% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Intent-Code Divergence

Medium
Confidence
95% confidence
Finding
The implementation intends to retry on retryable HTTP status codes, but when a 4xx/5xx response is converted into `new Error(HTTP ...)`, that error is not marked with a retryable `code` and the subsequent catch path checks only `isRetryable(error)`. As a result, responses like 429/503 are not actually retried, which can undermine availability guarantees and cause avoidable request failures under transient upstream conditions.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
This code file performs network operations via fetch(), including POST/PUT requests that serialize and send caller-provided data, but the file contains no confirmation prompt, visible logging, or explicit warning comment/docstring about transmitting data externally. Under the code-file criteria, network/HTTP calls that transmit user or system data should have some form of disclosure unless the warning is provided elsewhere.

Natural-Language Policy Violations

Medium
Confidence
94% confidence
Finding
The manifest uses Chinese-only natural-language fields for the description and keywords, which imposes a specific language on users. There is no indication that the skill is region-specific or that users can opt into another language, which fits the language/locale policy violation criteria.

Natural-Language Policy Violations

Low
Confidence
90% confidence
Finding
The file presents all user-facing instructions in Chinese and does not provide an opt-in language choice, alternate locale, or justification that the skill is intended only for Chinese-speaking users. This can conflict with language/locale policy requirements when skills are expected to support broader audiences or avoid forcing a specific language by default.

Natural-Language Policy Violations

Low
Confidence
93% confidence
Finding
The natural-language content of the skill documentation is presented almost entirely in Chinese, with no indication that users may choose another language or that the skill is intentionally limited to a Chinese-speaking audience. This can violate a language/locale policy when a skill defaults to a specific language without user opt-in or documented justification.

Natural-Language Policy Violations

Low
Confidence
93% confidence
Finding
For all file types, natural-language policy violations include forcing a specific language without user opt-in. This summary uses Chinese throughout and does not indicate any optional locale selection or justified region-specific constraint.

Static analysis

No suspicious patterns detected.