Back to skill

Security audit

Clawprompt

Security checks for vulnerabilities and agentic risk

Overview

This teleprompter does what it claims, but its LAN remote-control server lets unauthenticated network clients read, replace, and control scripts, so users should review it carefully before installing.

Install only if you are comfortable running a LAN-accessible teleprompter server. Use it on a trusted private network, avoid confidential scripts, stop the server when finished, and prefer a version that adds a pairing token, authentication, origin checks, payload limits, local-only QR generation, and a committed lockfile.

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

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/server.js:86
Finding
Unauthenticated LAN Clients Can Read, Replace, and Control Teleprompter Sessions<![CDATA[ ## Vulnerability Details **File Location**: `scripts/server.js`, lines 86–111 **Vulnerability Type**: Missing authentication, pairing enforcement, origin validation, and role-based authorization **Risk Level**: High ### Vulnerable Code ```js wss.on('connection', (ws) => { clients.set(ws, { role: 'main' }); // default role ws.send(JSON.stringify({ type: 'state', ...state })); ws.on('message', (raw) => { try { const msg = JSON.parse(raw); if (msg.type === 'register') { clients.set(ws, { role: msg.role || 'main' }); broadcastRemoteCount(); } else if (msg.type === 'sync') { // Main teleprompter syncs state state = { idx: msg.idx, total: msg.total, current: msg.current || '', next: msg.next || '' }; broadcast({ type: 'state', ...state }, ws); } else if (msg.type === 'cmd') { // Remote sends command → broadcast to all (main will handle) broadcast({ type: 'cmd', action: msg.action }, ws); } else if (msg.type === 'text') { // Remote uploaded text → broadcast to all broadcast({ type: 'text', text: msg.text }, ws); } else if (msg.type === 'fulltext') { // Main syncs text to remotes broadcast({ type: 'fulltext', text: msg.text }, ws); } } catch {} }); ``` The network exposure is established at `scripts/server.js`, lines 117–124: ```js server.listen(PORT, '0.0.0.0', () => { console.log(`\n🎬 ClawPrompt Server`); console.log(` 电脑提词器: http://localhost:${PORT}`); console.log(` 手机遥控: http://${lanIP}:${PORT}/remote`); console.log(` (确保手机和电脑在同一 WiFi)\n`); }); ``` ### Technical Analysis The server listens on every network interface and accepts all WebSocket connections without credentials or a cryptographically random pairing token. The QR code only communicates the service URL; scanning it does not establish a trusted pairing relationship. A newly connected client immediately receives the current shared ...[truncated 1782 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Generate a cryptographically random, high-entropy pairing token for each session. 2. Include the token in the QR URL and require it during the WebSocket upgrade or an authenticated registration handshake. 3. Reject unauthenticated connections before sending any state. 4. Maintain server-assigned roles rather than accepting arbitrary client-provided roles. 5. Enforce message-level authorization: - Only the authenticated main display may send `sync` and `fulltext`. - Only approved remote clients may send `cmd` and `text`. 6. Validate the WebSocket `Origin` header against an explicit allowlist of expected local origins. 7. Require explicit approval on the main display before activating a newly paired remote. 8. Isolate independent sessions instead of placing all clients into one global broadcast group. 9. Use HTTPS and WSS where scripts may contain confidential information or the network cannot be fully trusted. 10. Bind only to localhost by default and require an explicit option before exposing the service to the LAN. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/server.js:38
Finding
Unbounded QR Generation and WebSocket Messages Permit Resource Exhaustion<![CDATA[ ## Vulnerability Details **File Location**: `scripts/server.js`, lines 38–44 and 91–111 **Vulnerability Type**: Unbounded input processing and missing application-level resource limits **Risk Level**: Medium ### Vulnerable Code The QR endpoint accepts an unrestricted image width at lines 38–44: ```js if (parsed.pathname === '/qr.png') { const url = parsed.searchParams.get('url') || `http://${lanIP}:${PORT}/remote`; const size = parseInt(parsed.searchParams.get('size') || '160', 10); QRCode.toBuffer(url, { width: size, margin: 1 }, (err, buf) => { if (err) { res.writeHead(500); res.end(); return; } res.writeHead(200, { 'Content-Type': 'image/png', 'Cache-Control': 'max-age=3600' }); res.end(buf); }); ``` WebSocket input is parsed, retained, and rebroadcast without schema or length validation at lines 91–111: ```js ws.on('message', (raw) => { try { const msg = JSON.parse(raw); if (msg.type === 'register') { clients.set(ws, { role: msg.role || 'main' }); broadcastRemoteCount(); } else if (msg.type === 'sync') { // Main teleprompter syncs state state = { idx: msg.idx, total: msg.total, current: msg.current || '', next: msg.next || '' }; broadcast({ type: 'state', ...state }, ws); } else if (msg.type === 'cmd') { // Remote sends command → broadcast to all (main will handle) broadcast({ type: 'cmd', action: msg.action }, ws); } else if (msg.type === 'text') { // Remote uploaded text → broadcast to all broadcast({ type: 'text', text: msg.text }, ws); } else if (msg.type === 'fulltext') { // Main syncs text to remotes broadcast({ type: 'fulltext', text: msg.text }, ws); } } catch {} }); ``` ### Technical Analysis The `size` query parameter is parsed and passed directly to the QR encoder. No finite-number check or minimum/maximum bound is applied. A sufficiently large width can cause expensive image allocation and encoding. WebSocket mes ...[truncated 1684 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Validate the QR width as a finite integer and constrain it to a small range, such as 64–512 pixels. 2. Reject excessive QR content lengths and malformed parameters with HTTP 400. 3. Configure `WebSocketServer` with a conservative `maxPayload` value. 4. Define and enforce a schema for every WebSocket message type. 5. Set explicit maximum lengths for `current`, `next`, `text`, and `fulltext`. 6. Require `idx` and `total` to be bounded non-negative integers. 7. Restrict command actions to an allowlist such as `next` and `prev`. 8. Reject unknown fields and message types rather than silently accepting them. 9. Add per-IP and per-connection request, message, and connection rate limits. 10. Limit the number of concurrent clients and avoid retaining oversized values in global state. 11. Log rejected requests and parsing failures without recording sensitive script contents. ]]>

other

Note
Location
scripts/index.html:143
Finding
External QR Fallback Discloses the Private Service Address<![CDATA[ ## Vulnerability Details **File Location**: `scripts/index.html`, lines 143–149 **Vulnerability Type**: Undisclosed transmission of local network metadata to an external service **Risk Level**: Low ### Vulnerable Code ```js .catch(() => { // Fallback: use current host const url = `${location.protocol}//${location.host}/remote`; $('qrUrl').textContent = url; $('qrImg').src = `https://api.qrserver.com/v1/create-qr-code/?size=160x160&data=${encodeURIComponent(url)}`; $('tpQr').src = `https://api.qrserver.com/v1/create-qr-code/?size=100x100&data=${encodeURIComponent(url)}`; }); ``` ### Technical Analysis If the local `/qr-url` request fails, the browser constructs the private remote-control URL and includes it in requests to `api.qrserver.com`. This reveals the local host or IP address, port, remote endpoint path, requester IP address, and usage timing to a third-party provider. The project already has server-side QR generation through the local `qrcode` dependency, so the external fallback is not required for normal operation. The fallback and its associated metadata disclosure are not documented in the user-facing Skill instructions. No script body is transmitted through this specific path; the disclosure is limited to service and request metadata. ### Attack Path 1. The browser fails to retrieve or parse `/qr-url`. 2. The fallback handler constructs a URL such as `http://192.168.1.20:7870/remote`. 3. The browser requests an image from the external QR provider with that private URL encoded in the query string. 4. The provider receives and can log the internal address, port, path, public requester address, browser metadata, and request time. 5. Two requests may be generated because both QR image elements use the external provider. ### Impact Assessment The external provider obtains limited network-topology and usage metadata without explicit user consent. This may reveal: - The private address and port used by the local service. - The ...[truncated 272 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove the external QR-generation fallback. 2. Continue using the existing local `/qr.png` endpoint for QR generation. 3. If local QR generation fails, display the remote URL as plain text rather than transmitting it externally. 4. If an external provider must be retained, require explicit informed user consent before making the request. 5. Document the third-party provider, transmitted fields, retention implications, and failure conditions. 6. Add a restrictive Content Security Policy that prevents unexpected external image destinations. ]]>

T08 · Insecure Dependencies

Note
Location
SKILL.md:32
Finding
Dependency Installation Is Not Reproducible or Cryptographically Locked<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md`, lines 32–36; `scripts/package.json`, lines 13–16 **Vulnerability Type**: Unpinned dependency resolution and missing lockfile **Risk Level**: Low ### Vulnerable Code The installation instructions in `SKILL.md`, lines 32–36, resolve dependencies dynamically: ```bash cd {SKILL_DIR}/scripts npm install --silent node server.js ``` The dependency declarations in `scripts/package.json`, lines 13–16, use caret ranges: ```json "dependencies": { "qrcode": "^1.5.4", "ws": "^8.19.0" } ``` No package lockfile is present in the supplied project structure. ### Technical Analysis Caret ranges allow future installations to select compatible versions that may differ from those originally reviewed. Without a committed `package-lock.json`, transitive dependency versions and integrity hashes are not fixed, so two installations can receive different dependency graphs. The `--silent` option suppresses installation output that could otherwise expose warnings or unexpected package behavior. The reviewed package names are not visibly misspelled, and no unsafe custom registry or direct remote package URL was identified. The risk arises from non-reproducible supply-chain resolution rather than evidence that the currently declared packages are malicious. ### Attack Path 1. A user follows the documented `npm install --silent` command. 2. npm resolves versions permitted by the caret ranges and resolves current transitive dependencies. 3. The resulting dependency graph may differ from the graph tested or reviewed by the author. 4. If a newly resolved dependency version is compromised or unexpectedly unsafe, npm may process its package lifecycle behavior during installation. 5. The server later loads the installed dependency code with the privileges of the user running Node.js. ### Impact Assessment A compromised or unexpectedly changed dependency could execute with the privileges of the user who installs or runs ...[truncated 430 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Generate and commit a reviewed `package-lock.json`. 2. Replace the documented `npm install --silent` command with `npm ci`. 3. Remove `--silent` so integrity, deprecation, and security warnings remain visible. 4. Review the complete resolved dependency tree before publishing the Skill. 5. Use automated dependency scanning and periodically review updates. 6. Regenerate the lockfile only through a controlled update process. 7. Consider exact direct dependency versions where operationally appropriate. 8. Verify registry configuration and package integrity in release or deployment workflows. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (13)

Lp3

Medium
Category
MCP Least Privilege
Confidence
70% confidence
Finding
Without declared permissions the skill's intent is opaque and cannot be validated.

Vague Triggers

Medium
Confidence
93% confidence
Finding
The trigger list includes broad, common phrases such as 'teleprompter', '提词', and 'I need a prompter', which can cause the skill to activate when the user is speaking generally rather than explicitly requesting this specific tool. Unintended invocation can redirect users into running a local server or opening a workflow they did not intend, increasing the chance of confusion, accidental execution, or misuse of adjacent capabilities.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
When local QR generation fails, the code sends the remote-control URL to api.qrserver.com, a third-party service, without notifying the user. Even though the transmitted value is a URL rather than the script itself, it leaks session/network metadata to an external party and introduces an undeclared dependency that may be inappropriate in privacy-sensitive environments.

External Transmission

Medium
Category
Data Exfiltration
Content
// Fallback: use current host
    const url = `${location.protocol}//${location.host}/remote`;
    $('qrUrl').textContent = url;
    $('qrImg').src = `https://api.qrserver.com/v1/create-qr-code/?size=160x160&data=${encodeURIComponent(url)}`;
    $('tpQr').src = `https://api.qrserver.com/v1/create-qr-code/?size=100x100&data=${encodeURIComponent(url)}`;
  });
}
Confidence
96% confidence
Finding
This line transmits the remote-control URL to an external QR code generation service. That external transmission is unnecessary for core functionality and can expose internal hostnames, session endpoints, or usage metadata to a third party without user awareness.

External Transmission

Medium
Category
Data Exfiltration
Content
const url = `${location.protocol}//${location.host}/remote`;
    $('qrUrl').textContent = url;
    $('qrImg').src = `https://api.qrserver.com/v1/create-qr-code/?size=160x160&data=${encodeURIComponent(url)}`;
    $('tpQr').src = `https://api.qrserver.com/v1/create-qr-code/?size=100x100&data=${encodeURIComponent(url)}`;
  });
}
Confidence
96% confidence
Finding
This second QR image request repeats the same third-party data disclosure for the teleprompter view, expanding external exposure each time the fallback path is used. In some deployments, the leaked URL could reveal internal addressing or operational details about the remote-control endpoint.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The page automatically synchronizes the full script and current prompt state over WebSocket to connected devices, but the UI does not clearly warn users that their entered text will be transmitted across the network. Because teleprompter scripts may contain unreleased content, personal data, or confidential talking points, this creates a privacy and data-exposure risk, especially if users assume the text stays local.

Natural-Language Policy Violations

Medium
Confidence
97% confidence
Finding
The console output includes Chinese-only user-facing text, which forces a specific language for operators of the skill. There is no indication that the skill is region-specific or that users can opt into another language, so this appears to violate the language/locale policy.

Natural-Language Policy Violations

Low
Confidence
71% confidence
Finding
The manifest-like description hard-codes trigger phrases in multiple languages, but it does not explain whether activation depends on the user's preferred language or locale. This can violate language/locale policy expectations if the skill matches or responds based on a language the user did not opt into.

Natural-Language Policy Violations

Low
Confidence
90% confidence
Finding
The document language is set to zh-CN and the visible interface text is presented in Chinese only. There is no indication that this is a region-specific tool or that users can opt into another language, which may violate language/locale choice policy.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"author": "",
  "license": "ISC",
  "dependencies": {
    "qrcode": "^1.5.4",
    "ws": "^8.19.0"
  }
}
Confidence
95% confidence
Finding
The dependency uses a caret range (^1.5.4), which allows installation of newer compatible releases rather than a single reviewed version. This creates supply-chain uncertainty because different environments may resolve to different package contents, making builds less reproducible and potentially introducing vulnerable or malicious updates without code changes.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"license": "ISC",
  "dependencies": {
    "qrcode": "^1.5.4",
    "ws": "^8.19.0"
  }
}
Confidence
98% confidence
Finding
The ws dependency is specified with a caret range (^8.19.0), so the installed version may vary across environments and over time. Because ws is a network-facing WebSocket library in a teleprompter app with mobile pairing/remote control, version drift increases the risk of pulling in a release with exploitable bugs or inconsistent security behavior.

Unverifiable Dependency: ws has 7 known advisory(ies) (CVE-2016-10518 (Remote Memory Disclosure in ws); CVE-2024-37890 (ws affected by a DoS when handling a request with many HTTP headers); CVE-2026-45736 (ws: Uninitialized memory disclosure) +4 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
91% confidence
Finding
The manifest includes ws, which has multiple known advisories, but the dependency is not pinned, so it is impossible to verify from this file whether the actually installed version is affected. In this skill's context, ws is likely central to phone pairing and remote control features, so a vulnerable WebSocket package could expose the service to denial of service or information disclosure over the network.

Natural-Language Policy Violations

Low
Confidence
95% confidence
Finding
The document declares zh-CN and all visible UI strings are in Simplified Chinese, with no indication that users can opt into another language. This is a natural-language locale policy concern because the skill effectively forces a specific language without user choice or justification in the file.

Static analysis

No suspicious patterns detected.