Back to skill

Security audit

Helpscout

Security checks for vulnerabilities and agentic risk

Overview

This Help Scout skill uses the expected Help Scout API, but its documented permissions and implemented controls do not line up clearly for customer conversation access and replies.

Review this skill before installing. Use a Help Scout app with the narrowest possible permissions, assume the configured inbox list is not a hard security boundary, and do not rely on the documented sendReply examples until the code and documentation are reconciled. Update dependencies and add explicit allowlist checks before using it with real customer data.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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)

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/fetchConversations.js:4
Finding
Mailbox Filter Bypass Causes Account-Wide Conversation Retrieval<![CDATA[ ## Vulnerability Details **File Location**: `index.js:10-15`; `scripts/fetchConversations.js:4-31` **Vulnerability Type**: Improper enforcement of the configured inbox access boundary **Risk Level**: High ### Vulnerable Code `index.js:10-15`: ```javascript async function fetchAllInboxes(options = {}) { const { inboxIds } = getCredentials(); const results = await Promise.all( inboxIds.map(inboxId => fetchConversations(inboxId, options)) ); return results; } ``` `scripts/fetchConversations.js:4-31`: ```javascript async function fetchConversations({ inboxId = null, status = null, folderId = null, assignedTo = null, customerId = null, number = null, modifiedSince = null, sortField = null, sortOrder = null, tag = null, query = null, page = null}) { const token = await getToken(); const parameters = new URLSearchParams({ mailbox: inboxId, status: status, folderId: folderId, assignedTo: assignedTo, customerId: customerId, number: number, modifiedSince: modifiedSince, sortField: sortField, sortOrder: sortOrder, tag: tag, query: query, page: page }); // Filter out null parameters for (const [key, value] of parameters.entries()) { if (value === null) { parameters.delete(key); } } ``` ### Technical Analysis `fetchAllInboxes` calls `fetchConversations` with two positional arguments: ```javascript fetchConversations(inboxId, options) ``` However, `fetchConversations` accepts only one object argument and destructures its properties. The numeric or string inbox ID supplied as the firs ...[truncated 2412 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pass a single object matching the declared function interface: ```javascript async function fetchAllInboxes(options = {}) { const { inboxIds } = getCredentials(); return Promise.all( inboxIds.map(inboxId => fetchConversations({ ...options, inboxId }) ) ); } ``` 2. Reject requests that omit the mailbox identifier rather than silently issuing an unrestricted query: ```javascript async function fetchConversations(options = {}) { const { inboxId } = options; if (inboxId === null || inboxId === undefined || inboxId === '') { throw new Error('A valid inboxId is required'); } // Continue constructing the request. } ``` 3. Enforce the configured allowlist inside `fetchConversations`, not only in `fetchAllInboxes`. This prevents direct callers from requesting arbitrary mailboxes: ```javascript const { inboxIds } = getCredentials(); if (!inboxIds.map(String).includes(String(inboxId))) { throw new Error('The requested inbox is not configured for this Skill'); } ``` 4. Normalize and validate `INBOX_IDS` and `inboxId` types before comparison. Reject malformed, empty, or unexpected values. 5. Update every documented example to use the object-based API: ```javascript await fetchConversations({ inboxId: 321755, status: 'closed', sortField: 'modifiedAt', sortOrder: 'desc', page: 1 }); ``` 6. Add tests that inspect the outgoing request and verify: - The `mailbox` parameter is always present. - The mailbox value belongs to `INBOX_IDS`. - User-supplied filters are preserved. - Missing or unauthorized inbox IDs cause a local error. - `fetchAllInboxes()` does not issue an unrestricted request. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Rogue AgentSelf-Modification, Session Persistence
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (29)

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The supplied code does not implement any Help Scout conversation fetching or replying behavior. Instead, it only configures the test runner. This is a materially different purpose from the declared functionality, so the description is not accurately represented by this code chunk. No undeclared sensitive capability is present, but the actual code is unrelated to the declared purpose.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The implemented behavior matches the 'fetch' portion of the description: it retrieves Help Scout conversations from the v2 conversations endpoint using optional filters. However, the declared description also states 'reply to Helpscout conversations,' and this code chunk contains no functionality to post replies, modify conversations, or call any reply endpoint. This is a material description-to-behavior mismatch because part of the stated primary capability is absent from the supplied code.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
There is a partial mismatch between the declared description and the actual code. The declared purpose is to fetch and reply to HelpScout conversations. In this code chunk, the exported behavior is createNote, which posts internal notes to conversations; that is a distinct capability from replying and is not mentioned in the description. The chunk also includes a sendReply implementation that can send customer-visible replies, but it is intentionally not exported, so the active exposed behavior here is note creation. The helper also fetches conversation data to derive a customer ID, which aligns with 'fetch' as a supporting detail. Overall, the main mismatch is that the code performs undeclared note creation and does not primarily expose reply functionality in this chunk.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The declared purpose centers on interacting with Help Scout conversations, specifically fetching and replying to them. However, the supplied code only concerns authentication token retrieval for the Help Scout API. While token fetching could be a supporting detail for a conversation-handling skill, this code chunk itself does not implement or demonstrate conversation retrieval or replying behavior. That makes the observed behavior materially different from the declared purpose for this chunk.

Known Vulnerable Dependency: brace-expansion==1.1.12 — 4 advisory(ies): CVE-2026-13149 (brace-expansion: DoS via exponential-time expansion of consecutive non-expanding); CVE-2026-33750 (brace-expansion: Zero-step sequence causes process hang and memory exhaustion); CVE-2026-14257 (brace-expansion: DoS via unbounded expansion length causing an out-of-memory pro) +1 more

High
Category
Supply Chain
Confidence
80% confidence
Finding
Dependency has known vulnerabilities (CVEs). Using packages with unpatched security flaws exposes the environment to known exploits.

Known Vulnerable Dependency: minimatch==3.1.2 — 3 advisory(ies): CVE-2026-27904 (minimatch ReDoS: nested *() extglobs generate catastrophically backtracking regu); CVE-2026-26996 (minimatch has a ReDoS via repeated wildcards with non-matching literal in patter); CVE-2026-27903 (minimatch has ReDoS: matchOne() combinatorial backtracking via multiple non-adja)

High
Category
Supply Chain
Confidence
80% confidence
Finding
Dependency has known vulnerabilities (CVEs). Using packages with unpatched security flaws exposes the environment to known exploits.

Known Vulnerable Dependency: browserslist==4.28.1 — 2 advisory(ies): CVE-2026-73088 (Browserslist: Uncaught crash / prototype write via untrusted browserslist-stats.); CVE-2026-73089 (Browserslist: Unbounded memory growth (no cache eviction) via distinct query res)

High
Category
Supply Chain
Confidence
80% confidence
Finding
Dependency has known vulnerabilities (CVEs). Using packages with unpatched security flaws exposes the environment to known exploits.

Known Vulnerable Dependency: js-yaml==3.14.2 — 4 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) +1 more

High
Category
Supply Chain
Confidence
80% confidence
Finding
Dependency has known vulnerabilities (CVEs). Using packages with unpatched security flaws exposes the environment to known exploits.

Known Vulnerable Dependency: picomatch==2.3.1 — 2 advisory(ies): CVE-2026-33672 (Picomatch: Method Injection in POSIX Character Classes causes incorrect Glob Mat); CVE-2026-33671 (Picomatch has a ReDoS vulnerability via extglob quantifiers)

High
Category
Supply Chain
Confidence
80% confidence
Finding
Dependency has known vulnerabilities (CVEs). Using packages with unpatched security flaws exposes the environment to known exploits.

Lp3

Medium
Category
MCP Least Privilege
Confidence
87% confidence
Finding
The skill documentation declares a network-capable integration with Help Scout and requires API credentials, but the manifest does not define any explicit tool scope such as permissions or allowed-tools. That creates an authorization ambiguity where a runtime may permit broader network access than reviewers or operators expect, increasing the chance of overprivileged execution.

Session Persistence

Medium
Category
Rogue Agent
Content
### 1. Retrieve Helpscout API Key & App Secret
1. Go to your Helpscout account.
2. Navigate to **Manage > Apps**.
3. Create or open your app to retrieve the following details:
   - **API Key**
   - **App Secret**
Confidence
60% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
This code retrieves an access token and sends a remote API request to Help Scout, potentially transmitting user-provided search/filter values such as customerId, assignedTo, tag, and query. Although comments describe the function's purpose, there is no user-facing confirmation, warning, or disclosure about the outbound network access or data sent.

External Transmission

Medium
Category
Data Exfiltration
Content
async function getToken() {
  const { apiKey, appSecret } = getCredentials();

  const response = await fetch('https://api.helpscout.net/v2/oauth2/token', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded'
Confidence
60% 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
async function getToken() {
  const { apiKey, appSecret } = getCredentials();

  const response = await fetch('https://api.helpscout.net/v2/oauth2/token', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded'
Confidence
60% 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
async function getToken() {
  const { apiKey, appSecret } = getCredentials();

  const response = await fetch('https://api.helpscout.net/v2/oauth2/token', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded'
Confidence
60% 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
async function getToken() {
  const { apiKey, appSecret } = getCredentials();

  const response = await fetch('https://api.helpscout.net/v2/oauth2/token', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded'
Confidence
60% 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
async function getToken() {
  const { apiKey, appSecret } = getCredentials();

  const response = await fetch('https://api.helpscout.net/v2/oauth2/token', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded'
Confidence
70% 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
98% confidence
Finding
The file contains fully functional code to send customer-visible replies even though the documentation explicitly states the skill is not allowed to do so. Although the function is not currently exported, retaining live unauthorized reply logic creates a latent capability that could be exposed by a small future change, monkey-patching, or internal reuse, undermining intended security boundaries.

External Transmission

Medium
Category
Data Exfiltration
Content
payload.user = userId;
  }

  const response = await fetch(`https://api.helpscout.net/v2/conversations/${conversationId}/reply`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
Confidence
90% confidence
Finding
This outbound request transmits customer-visible reply content to HelpScout through a hidden-but-functional sendReply path that contradicts the stated restriction that the skill must not send replies. In this context, the danger is not the external transmission itself but that it enables an unauthorized action against end users if the dormant function becomes reachable.

Known Vulnerable Dependency: @babel/core==7.29.0 — 1 advisory(ies): CVE-2026-49356 (@babel/core: Arbitrary File Read via sourceMappingURL Comment)

Low
Category
Supply Chain
Confidence
60% confidence
Finding
Dependency has known vulnerabilities (CVEs). Using packages with unpatched security flaws exposes the environment to known exploits.

Known Vulnerable Dependency: baseline-browser-mapping==2.9.19 — 1 advisory(ies): CVE-2026-45819 (baseline-browser-mapping process termination on invalid input causes denial of s)

Low
Category
Supply Chain
Confidence
60% confidence
Finding
Dependency has known vulnerabilities (CVEs). Using packages with unpatched security flaws exposes the environment to known exploits.

Known Vulnerable Dependency: fflate==0.8.2 — 1 advisory(ies): CVE-2026-45820 (fflate unzipSync can enter an infinite loop when parsing malformed ZIP64 archive)

Low
Category
Supply Chain
Confidence
60% confidence
Finding
Dependency has known vulnerabilities (CVEs). Using packages with unpatched security flaws exposes the environment to known exploits.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"test": "jest"
  },
  "devDependencies": {
    "clawdhub": "^0.3.0",
    "jest": "^29.0.0",
    "nock": "^13.0.0"
  },
Confidence
40% confidence
Finding
Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Unpinned Dependencies

Low
Category
Supply Chain
Content
},
  "devDependencies": {
    "clawdhub": "^0.3.0",
    "jest": "^29.0.0",
    "nock": "^13.0.0"
  },
  "dependencies": {
Confidence
40% confidence
Finding
Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"devDependencies": {
    "clawdhub": "^0.3.0",
    "jest": "^29.0.0",
    "nock": "^13.0.0"
  },
  "dependencies": {
    "node-fetch": "^2.6.8",
Confidence
40% confidence
Finding
Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Static analysis

No suspicious patterns detected.