Back to skill

Security audit

MyLister - Organizer

Security checks for vulnerabilities and agentic risk

Overview

The skill is a real MyLister task-management integration, but it exposes high-impact chat commands for API redirection, API-key handling, local file upload, emailing/sharing data, and permission changes without clear safety gates.

Review this skill carefully before installing. Use it only with a dedicated low-privilege MyLister API key, avoid the chat commands that set API keys or custom API base URLs, and do not let it process untrusted text that could trigger uploads, deletes, email sends, sharing, or permission changes. Prefer a host that requires confirmation for file uploads, outbound emails, deletes, and access changes.

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

T09 · Insecure Skill Coding Practices

Error
Location
src/index.ts:1716
Finding
API Key Disclosure Through Arbitrary Runtime API Endpoint Switching<![CDATA[ ## Vulnerability Details **File Location**: `src/index.ts:348-351`, `src/index.ts:1716-1773`, and `src/index.ts:2064-2090` **Vulnerability Type**: Arbitrary credential destination and production credential reuse **Risk Level**: Critical ### Vulnerable Code ```typescript const DEFAULT_PRODUCTION_BASE_URL = normalizeBaseUrl( process.env.LISTER_PRODUCTION_BASE_URL || 'https://api.mylister.dev' ); const DEFAULT_STAGING_BASE_URL = process.env.LISTER_STAGING_BASE_URL || process.env.LISTER_STAGING_URL; const DEFAULT_PRODUCTION_API_KEY = process.env.LISTER_PRODUCTION_API_KEY || process.env.LISTER_API_KEY || ''; const DEFAULT_STAGING_API_KEY = process.env.LISTER_STAGING_API_KEY || process.env.LISTER_API_KEY || ''; ``` ```typescript let runtimeBaseUrl = normalizeBaseUrl(CONFIG.baseUrl); const runtimeApiKeys: Record<ApiEnvironment, string> = { production: DEFAULT_PRODUCTION_API_KEY, staging: DEFAULT_STAGING_API_KEY, custom: CONFIG.apiKey, }; let runtimeApiEnvironment = resolveRuntimeApiEnvironment(runtimeBaseUrl); let runtimeApiKey = runtimeApiKeys[runtimeApiEnvironment]; let client = new ListerClient(runtimeBaseUrl, runtimeApiKey); function getConfiguredApiKey(targetEnvironment: ApiEnvironment): string { return runtimeApiKeys[targetEnvironment]; } function switchApiEnvironment(targetBaseUrl: string, note?: string): string { const normalizedBaseUrl = normalizeBaseUrl(targetBaseUrl); try { new URL(normalizedBaseUrl); } catch { return `❌ Could not switch API environment because "${targetBaseUrl}" is not a valid URL.`; } runtimeBaseUrl = normalizedBaseUrl; runtimeApiEnvironment = resolveRuntimeApiEnvironment(runtimeBaseUrl); runtimeApiKey = getConfiguredApiKey(runtimeApiEnvironment); client = new ListerClient(runtimeBaseUrl, runtimeApiKey); const environment = runtimeApiEnvironment; const suffix = note ? ` ${note}` : ''; if (!runtimeApiKey) { return `✅ API ${environment} environment set to ${environment === ' ...[truncated 3760 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove arbitrary runtime API URL switching unless it is essential to the supported functionality. 2. Enforce an exact allowlist of trusted HTTPS origins, such as the documented production and staging MyLister origins. 3. Initialize the custom-environment key as empty. Never inherit `LISTER_API_KEY`, production keys, or staging keys for custom origins. 4. Bind each credential to a specific origin and refuse to send it when the request origin differs. 5. Reject: - Plain HTTP URLs. - URLs containing usernames or passwords. - Unsupported schemes. - Loopback, link-local, private-network, and metadata-service destinations. 6. Require explicit user confirmation that displays the complete destination origin before changing environments. 7. Do not permit untrusted Agent content to set credentials or endpoints without a trusted user-approval step. 8. Add tests proving that production credentials are never sent to custom hosts and that unapproved origins are rejected. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
src/index.ts:850
Finding
Cross-Origin Redirect Validation Occurs After Sensitive Requests Are Sent<![CDATA[ ## Vulnerability Details **File Location**: `src/index.ts:850-889`, affecting authenticated fetch operations including `src/index.ts:909-913` and `src/index.ts:1560-1575` **Vulnerability Type**: Post-request redirect validation **Risk Level**: High ### Vulnerable Code ```typescript constructor(baseUrl = CONFIG.baseUrl, apiKey = CONFIG.apiKey) { this.baseUrl = normalizeBaseUrl(baseUrl); this.apiKey = apiKey; this.expectedHost = new URL(this.baseUrl).host; } private getAuthHeader(): Record<string, string> { return { 'X-API-Key': this.apiKey, 'Accept': 'application/json', 'Content-Type': 'application/json', }; } private async parseResponse(res: any): Promise<ParsedApiResponse> { const finalHost = res.url ? new URL(res.url).host : this.expectedHost; const contentType = res.headers?.get?.('content-type') ?? ''; if (finalHost !== this.expectedHost) { return { ok: false, status: res.status, data: null, error: `API request was redirected from ${this.expectedHost} to ${finalHost}. Check your active API base URL and API DNS routing.`, }; } if (contentType && !contentType.includes('application/json')) { const text = await res.text().catch(() => ''); return { ok: false, status: res.status, data: null, error: `Expected JSON from API but received ${contentType || 'non-JSON'} (${res.status}). ${text.slice(0, 160).trim()}`, }; } const raw = await res.json().catch(() => null) as any; const data = Array.isArray(raw) ? raw : (raw?.data ?? raw); const detail = raw?.detail ?? raw?.message ?? raw?.error ?? res.statusText; const error = typeof detail === 'string' ? detail : JSON.stringify(detail); return { ok: res.ok, status: res.status, data, error }; } ``` A representative authenticated request is: ```typescript const res = await fetch(url, { headers: this.getAuthHeader(), }); const { ok, data, error } = await this.parseResponse(res); ``` The upload pa ...[truncated 2067 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Set `redirect: "manual"` on every authenticated request. 2. Reject all cross-origin redirects by default. 3. If redirects are operationally necessary: - Resolve each `Location` value against the current URL. - Validate the complete destination origin before following it. - Require HTTPS. - Apply an exact trusted-origin allowlist. - Strip API keys and all other sensitive headers whenever the origin changes. 4. Never automatically replay multipart bodies or non-idempotent requests across redirects. 5. Centralize network requests in one hardened request wrapper so individual API methods cannot omit the redirect policy. 6. Compare complete origins, including scheme, hostname, and port, rather than only `URL.host`. 7. Add tests for same-origin redirects, cross-origin redirects, HTTPS-to-HTTP downgrade attempts, and upload redirects. ]]>

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
src/index.ts:1560
Finding
Natural-Language Upload Commands Can Read Any Process-Accessible Local File<![CDATA[ ## Vulnerability Details **File Location**: `src/index.ts:1560-1623` and upload handlers around `src/index.ts:1980-2048` **Vulnerability Type**: Unrestricted local-file access and network upload **Risk Level**: Medium ### Vulnerable Code ```typescript private async uploadFile( url: string, filePath: string, fields: Record<string, string | number | boolean | undefined> ): Promise<ListerResponse> { try { const form = new FormData(); form.append('file', await fileFrom(filePath)); for (const [name, value] of Object.entries(fields)) { if (value !== undefined) form.append(name, String(value)); } const res = await fetch(url, { method: 'POST', headers: this.getMultipartHeader(), body: form, }); const { ok, data, error } = await this.parseResponse(res); return { success: ok, message: ok ? 'File uploaded' : `Failed: ${error}`, data }; } catch (err) { return { success: false, message: `Error uploading file: ${err}` }; } } async uploadAttachment( itemId: string, filePath: string ): Promise<ListerResponse> { return this.uploadFile( `${this.baseUrl}/v1/items/${itemId}/attachments`, filePath, {} ); } async uploadImage( filePath: string, options: { description?: string; listId?: string; itemId?: string } ): Promise<ListerResponse> { return this.uploadFile(`${this.baseUrl}/v1/upload/image`, filePath, { description: options.description, list_id: options.listId, item_id: options.itemId, }); } async uploadVoice( filePath: string, options: { duration?: number; transcribe?: boolean; listId?: string; itemId?: string } ): Promise<ListerResponse> { return this.uploadFile(`${this.baseUrl}/v1/upload/voice`, filePath, { duration: options.duration, transcribe: options.transcribe, list_id: options.listId, item_id: options.itemId, }); } ``` The command handler forwards the parsed path directly: ```typ ...[truncated 2565 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Restrict uploads to explicitly configured, user-approved directories. 2. Resolve the requested path with `realpath()` and verify that the canonical path remains beneath an approved root. 3. Reject symlinks or validate their resolved targets to prevent boundary bypass. 4. Require an explicit confirmation that displays: - The canonical local path. - File size and detected type. - The exact destination origin. - The associated list or item. 5. Deny known sensitive locations and filenames, including credential stores, SSH directories, environment files, cloud configuration, and operating-system secrets. 6. Apply strict file-size and file-type limits separately for attachments, images, and voice recordings. 7. Keep custom network destinations disabled for file uploads. 8. Avoid accepting upload paths solely from untrusted model-generated text; use a trusted file-picker or host-provided attachment reference where possible. 9. Add tests for path traversal, absolute paths, symlinks, sensitive files, oversized files, and destinations outside approved roots. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (24)

Lp3

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding
The skill clearly requires environment variables and broad network access but does not declare permissions or present a scoped permission model. This creates hidden capability risk: users and hosting platforms may not realize the skill can read API keys and send data to remote services, reducing informed consent and policy enforcement.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The skill is described as simple task/list management, but the documented behavior also includes emailing data to others, uploading local files, sharing lists, managing user permissions, generating file URLs, and runtime API-key/base-URL switching. This mismatch can mislead users or orchestrators into invoking a far more powerful data-exfiltration and administration surface than expected.

Description-Behavior Mismatch

High
Confidence
95% confidence
Finding
The tests explicitly confirm capabilities well beyond the declared skill scope of natural-language task and list management, including file/media handling, API diagnostics, and API configuration changes. Hidden or unnecessary capability expansion increases attack surface and can enable misuse of the skill as a transport, probing, or reconfiguration tool rather than a simple list manager.

Context-Inappropriate Capability

High
Confidence
98% confidence
Finding
Runtime API key, base-URL, and environment switching are highly sensitive controls that can redirect requests to attacker-controlled infrastructure or swap trusted credentials at runtime. In an agent skill, this can lead to credential exfiltration, SSRF-style behavior, unauthorized access to alternate backends, or silent policy bypass by moving from production to staging/custom endpoints.

Context-Inappropriate Capability

Medium
Confidence
88% confidence
Finding
Attachment and voice upload plus file URL retrieval introduce data ingress/egress capabilities that are not justified by the stated list-management purpose. Such features can be abused to upload sensitive local content, shuttle data externally, or generate retrievable links for files, materially increasing exfiltration and malware-handling risk.

Intent-Code Divergence

Medium
Confidence
82% confidence
Finding
The file header claims all tests are pure with no network access, but later code imports the real module and comments acknowledge fetch may still be used. Misleading test guarantees can mask real outbound calls during CI or local runs, causing accidental data leakage, flaky tests, or unintended interaction with external services.

Description-Behavior Mismatch

High
Confidence
97% confidence
Finding
The skill exposes capabilities well beyond its stated role as a natural-language task/list manager, including emailing task data, exporting content, uploading local files, sharing lists with other users, and dynamically reconfiguring the API endpoint and credentials. Scope expansion like this is dangerous because a user or upstream agent may invoke data-exfiltrating or environment-changing actions without realizing the skill can access local files or redirect sensitive data to arbitrary destinations.

Context-Inappropriate Capability

High
Confidence
99% confidence
Finding
The upload helpers accept arbitrary file paths from natural-language input and call fileFrom(filePath), which reads local files and transmits them to the remote API. In the context of a task manager skill, this creates a clear path for unintended local file exfiltration, since a prompt can cause the skill to access sensitive files unrelated to list management.

Context-Inappropriate Capability

High
Confidence
99% confidence
Finding
The skill allows natural-language commands to switch to a custom API base URL and set runtime API keys, after which all subsequent authenticated requests use the attacker-chosen endpoint and credential. This is especially dangerous because it enables credential redirection and silent data exfiltration to arbitrary infrastructure under the guise of normal task-management actions.

Vague Triggers

Medium
Confidence
86% confidence
Finding
The OpenClaw guidance says to 'ask naturally' after installation, which encourages broad natural-language activation without clearly scoping when the skill should run. In a task-management skill that can add, remove, email, attach, and reconfigure API settings, ambiguous triggering raises the chance of unintended invocation and accidental state-changing actions.

Vague Triggers

Medium
Confidence
85% confidence
Finding
The Hermes instructions allow either '/lister' or describing the task normally, which leaves trigger conditions ambiguous. Because this skill can send emails, delete items, upload files, and switch API environments, normal conversation text could be misinterpreted as an instruction and cause unintended external API operations.

Vague Triggers

Medium
Confidence
83% confidence
Finding
The Claude Code section says users can 'ask naturally, or invoke /lister,' which does not define the activation scope precisely. In context, this is risky because the skill exposes commands that modify tasks, transmit data to contacts, and handle local files, so accidental routing of ordinary text to the skill could have real side effects.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The README advertises destructive and data-transmitting capabilities like removing items, emailing lists, attaching local files, uploading media, exporting content, changing API base URLs, and setting API keys, but it does not warn users about side effects or recommend confirmation. In an agent skill, documenting these commands without safeguards increases the likelihood of accidental deletion, unintended data exfiltration, secret mishandling, or redirection of requests to unsafe endpoints.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The skill supports emailing list and item contents to arbitrary recipients but does not prominently warn that task data may be transmitted خارج the original account boundary. In a natural-language interface, this raises a meaningful risk of accidental disclosure of potentially sensitive personal or work task content.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The skill can upload arbitrary local files, images, and voice recordings from the filesystem to a remote API, yet the documentation lacks a clear warning about this data-transfer behavior. In an agent setting, that creates significant exfiltration risk because users may not realize a natural-language request can cause local file contents to leave the host.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The skill allows runtime switching of API base URLs and API keys without an explicit warning about credential exposure or trust boundaries. This is dangerous because an attacker or misled user could redirect traffic to an attacker-controlled endpoint and send valid API keys and task data to it.

Vague Triggers

Medium
Confidence
94% confidence
Finding
The command trigger list includes many generic everyday terms such as 'list', 'get', 'add', 'set', 'send', 'email', 'api', and 'key', which increases the chance the skill will be invoked unintentionally during unrelated user conversations. In a skill that can manipulate tasks and may interact with external services or environments, accidental invocation could cause unintended actions, data exposure, or operation against the wrong backend.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The tests cover destructive and sensitive flows such as permanent deletion and API key setting without validating any confirmation, warning, or authorization behavior. In an agent context, absence of safety interlocks for irreversible or secret-handling actions raises the likelihood of accidental destructive execution or prompt-induced misuse.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The skill accepts API keys via plain natural-language commands and stores them in runtime state without any warning that the secret may be exposed in logs, transcripts, or higher-level agent memory. Even if this is intended functionality, handling secrets through ordinary chat input materially increases leakage risk.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
The email/export functions send list or item data to external destinations, but the skill provides no explicit disclosure or confirmation that user task contents will leave the current system. In a task-management context, that can expose sensitive notes, priorities, or project details through a simple natural-language command that sounds routine.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The file upload path reads local file contents and posts them to the API without an explicit warning that local machine data is being accessed and transmitted. This weakens informed consent and makes prompt-induced disclosure of sensitive local documents more likely.

Unrestricted Tool Access

Medium
Category
Excessive Agency
Content
**Keywords:** `share`, `with`, `as`

**Permissions:** `read`, `edit` (default), `admin`

### 20. List Users
Show users who have access to a list.
Confidence
80% confidence
Finding
The skill exposes broad sharing and permission-management operations through natural-language commands, including granting read/edit/admin access, without documenting meaningful authorization guardrails at the skill layer. In an agent context, unrestricted access-management actions can be triggered too easily and may unintentionally expand who can access list data.

Unrestricted Tool Access

Medium
Category
Excessive Agency
Content
| `set user [email] permission on my [list] list to [perm]` | `set user alice@example.com permission on my work list to admin` |
| `change user [email] access on [list] list to [perm]` | `change user bob@example.com access on projects list to read` |

**Permissions:** `read`, `edit`, `admin`

**Keywords:** `update user`, `change user`, `set permission`
Confidence
82% confidence
Finding
Changing user permissions on shared lists to read/edit/admin is a sensitive administrative capability exposed via conversational input. Without stronger guardrails, a mistaken or manipulated instruction could escalate another user's access and broaden exposure of task data.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"author": "Larry",
  "license": "MIT",
  "dependencies": {
    "node-fetch": "^3.3.2"
  },
  "devDependencies": {
    "@types/node": "^20.11.0",
Confidence
74% confidence
Finding
Using a caret range for a runtime dependency allows future minor/patch releases to be installed, which can introduce supply-chain risk or unexpected behavior changes if an upstream package is compromised or publishes a bad release. Because this is a runtime dependency, the skill could consume that package in production builds.

Static analysis

Detected: suspicious.env_credential_access

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
src/index.ts:21