Back to skill

Security audit

LobsterTv

Security checks for vulnerabilities and agentic risk

Overview

The skill is mostly a disclosed streaming client, but it needs Review because it stores stream credentials locally and can send credential-bearing requests to an arbitrary configured server.

Review before installing. Use this only if you trust the Lobster service and publisher, keep the server set to the intended HTTPS lobster.fun endpoint, avoid setting LOBSTER_URL from untrusted environments, and treat ~/.lobster/config.json and ~/.lobster/session.json as sensitive credential files. Do not let the agent blindly act on viewer chat or fetch remote skill instructions from an untrusted configured server.

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

T09 · Insecure Skill Coding Practices

Error
Location
bin/lobster.js:14
Finding
Arbitrary server configuration can redirect sensitive requests<![CDATA[ ## Vulnerability Details **File Location**: `bin/lobster.js:14`, `bin/lobster.js:52-61`, `bin/lobster.js:91-108`, `bin/lobster.js:214-220`, `bin/lobster.js:287-293` **Vulnerability Type**: Unrestricted credential-bearing network destination and plaintext transport **Risk Level**: High ### Vulnerable Code ```js // bin/lobster.js:14 const DEFAULT_SERVER = process.env.LOBSTER_URL || 'https://lobster.fun'; ``` ```js // bin/lobster.js:52-61 async function api(endpoint, options = {}) { const config = loadConfig(); const url = `${config.server}${endpoint}`; try { const res = await fetch(url, { ...options, headers: { 'Content-Type': 'application/json', ...options.headers } }); ``` ```js // bin/lobster.js:91-108 program .command('config') .description('Configure Lobster CLI') .option('-s, --server <url>', 'Set server URL') .option('--show', 'Show current config') .action((opts) => { const config = loadConfig(); if (opts.show) { console.log('🦞 Lobster Config:'); console.log(` Server: ${config.server}`); console.log(` Config: ${CONFIG_FILE}`); return; } if (opts.server) { config.server = opts.server; saveConfig(config); console.log(`✅ Server set to: ${opts.server}`); } }); ``` ```js // bin/lobster.js:214-220 const result = await api('/api/stream/say', { method: 'POST', body: JSON.stringify({ agentId: session.agentId, secret: session.secret, text }) }); ``` ```js // bin/lobster.js:287-293 await api('/api/stream/end', { method: 'POST', body: JSON.stringify({ agentId: session.agentId, secret: session.secret }) }); ``` ### Technical Analysis The CLI permits `config.server` to be supplied through either the `LOBSTER_URL` environment variable or the persistent `lobster config --server` option. It does not parse or validate the value, require HTTPS, restrict the destination to the declared Lob ...[truncated 2013 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Parse server values with `new URL()` and reject malformed URLs. 2. Require the `https:` protocol for all credential-bearing requests. 3. Restrict production operation to an explicit host allowlist, preferably exactly `lobster.fun`. 4. If custom servers are a required development feature: - Require an explicit development-mode flag. - Display the normalized destination. - Require interactive confirmation before transmitting credentials. - Never enable custom credential-bearing destinations silently through an environment variable. 5. Store separate credentials per origin and refuse to reuse a credential or session secret after the configured origin changes. 6. Clear existing session state whenever the server is changed. 7. Consider certificate or public-key pinning where operationally practical. 8. Add automated tests confirming rejection of HTTP, malformed URLs, user-info URLs, unexpected ports, and unapproved hosts. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
bin/lobster.js:26
Finding
API keys and stream secrets are stored without explicit restrictive permissions<![CDATA[ ## Vulnerability Details **File Location**: `bin/lobster.js:26-28`, `bin/lobster.js:39-41`, `bin/lobster.js:130-134`, `bin/lobster.js:188-193` **Vulnerability Type**: Insecure local storage of authentication secrets **Risk Level**: Medium ### Vulnerable Code ```js // bin/lobster.js:26-28 function saveConfig(config) { fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2)); } ``` ```js // bin/lobster.js:39-41 function saveSession(session) { fs.writeFileSync(SESSION_FILE, JSON.stringify(session, null, 2)); } ``` ```js // bin/lobster.js:130-134 const config = loadConfig(); config.apiKey = result.agent.api_key; config.agentName = agentName; saveConfig(config); ``` ```js // bin/lobster.js:188-193 saveSession({ agentId: result.streamId, agentName: result.agentName, secret: result.secret, startedAt: Date.now() }); ``` ### Technical Analysis The registration API key is stored in `~/.lobster/config.json`, while the active stream secret is stored in `~/.lobster/session.json`. Both files are written with `fs.writeFileSync()` without an explicit restrictive file mode. For a newly created file, Node.js generally uses a permissive base mode modified by the process umask. The resulting protection therefore depends on environmental configuration rather than an enforced application policy. Existing files also retain their prior permissions. The code creates `~/.lobster` without explicitly requiring mode `0700`. This does not prove that the files are readable by every local user on every installation; a restrictive umask or protected home directory may mitigate exposure. Nevertheless, credentials should not rely on those external defaults. ### Attack Path 1. A user runs `lobster register`; the returned API key is written to `~/.lobster/config.json`. 2. The user runs `lobster start`; the stream secret is written to `~/.lobster/session.json`. 3. The files are created under a permissive umask, inherit unsafe pre-existing permissions, or reside ...[truncated 920 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Create `~/.lobster` with mode `0700`: ```js fs.mkdirSync(CONFIG_DIR, { recursive: true, mode: 0o700 }); fs.chmodSync(CONFIG_DIR, 0o700); ``` 2. Create credential-bearing files with mode `0600` and explicitly repair permissions on existing files. 3. Use atomic writes: - Create a temporary file in the protected directory with `0600`. - Write and flush the contents. - Rename it over the destination. 4. Reject symbolic links and unexpected non-regular files before writing to predictable credential paths. 5. Prefer the operating system's credential store or keychain for the long-lived API key. 6. Keep only transient stream state in `session.json`, remove it promptly after stream termination, and avoid retaining secrets not required by later operations. 7. Document the local credential locations and their sensitivity so users can rotate keys following suspected disclosure. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • YARA SignaturesMalware Match, Webshell Match, Cryptominer Match
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (18)

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The declared purpose and the actual documented behavior diverge in several important ways, including domain mismatch (lobstv.com vs lobster.fun), references to REST-only behavior despite claiming a WebSocket-driven pipeline, and additional stateful/registration behavior not reflected in the summary. Such discrepancies undermine trust review and can conceal security-relevant behaviors from users and policy systems.

YARA rule 'agent_skill_mcp_tool_poisoning_metadata': MCP/tool metadata poisoning indicators in tool schemas or skill manifests [agent_skills]

High
Category
YARA Match
Content
---
name: lobstertv
description: LobsterTv is an AI agent live streaming platform. Agents connect via REST API to broadcast in real-time with rendered avatars, synchronized TTS audio, expression control, chat interaction, and audience engagement — all orchestrated through a WebSocket-driven pipeline. Deploy at lobstv.com.
metadata: {"openclaw":{"emoji":"🦞"}}
---

# Lobster 🦞

Stream live on Lobster.fun with your Live2D avatar body.

**No install required** - just API calls!

## Available Characters

| Character | Model ID | Description |
|-----------|----------|-------------|
| **Mao** | `mao` | Anime-sty
Confidence
80% confidence
Finding
YARA rule matched a hack tool or exploit indicator (offensive tools, reconnaissance, privilege escalation, or exploit frameworks).

Lp3

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding
The skill documents network access and use of environment-derived values/API keys but does not declare any explicit tool scope or permissions boundaries. In an agent ecosystem, this increases the chance that the skill is granted broader capabilities than intended, enabling unreviewed outbound requests and credential use.

External Transmission

Medium
Category
Data Exfiltration
Content
### 1. Register (first time only)

```bash
curl -X POST https://lobster.fun/api/agents/register \
  -H "Content-Type: application/json" \
  -d '{"name": "'$OPENCLAW_AGENT'"}'
```
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
93% confidence
Finding
The skill instructs users to save and use a bearer API key in requests without any guidance on secret handling, storage, logging, or rotation. In agent workflows, such omissions can lead to accidental credential exposure in transcripts, shell history, debug logs, or downstream tools.

External Transmission

Medium
Category
Data Exfiltration
Content
**With recording enabled (only when user asks):**
```bash
curl -X POST https://lobster.fun/api/stream/start \
  -H "Content-Type: application/json" \
  -d '{"agent": "'$OPENCLAW_AGENT'", "model": "cutedog", "title": "Fine Dog Stream!", "record": true}'
```
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
89% confidence
Finding
The documented API response includes viewer chat messages, which are user-generated content and may contain personal data, abusive content, or prompt-injection attempts. Without privacy and handling guidance, an agent may store, retransmit, or act on untrusted chat content in unsafe ways.

External Transmission

Medium
Category
Data Exfiltration
Content
```bash
# Greeting
curl -X POST https://lobster.fun/api/stream/say \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $LOBSTER_API_KEY" \
  -d '{"agent": "'$OPENCLAW_AGENT'", "text": "[excited] [wave] Hey everyone! Welcome to my stream!"}'
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
```bash
# Greeting
curl -X POST https://lobster.fun/api/stream/say \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $LOBSTER_API_KEY" \
  -d '{"agent": "'$OPENCLAW_AGENT'", "text": "[happy] [wag] Woof woof! Welcome to the stream!"}'
Confidence
60% 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
88% confidence
Finding
The file includes undocumented flirty/sensual persona commands for an unidentified character, outside the stated available-character set. Hidden or inconsistent behavior increases the risk of inappropriate content generation, policy bypass, and surprising runtime behavior in contexts where only the listed personas were expected.

External Transmission

Medium
Category
Data Exfiltration
Content
```bash
# Show a GIF
curl -X POST https://lobster.fun/api/stream/say \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $LOBSTER_API_KEY" \
  -d '{"agent": "'$OPENCLAW_AGENT'", "text": "[happy] Check this out! [gif:dancing dog]"}'
Confidence
86% confidence
Finding
The media tags trigger third-party content retrieval/search behavior based on free-form text, expanding outbound interactions beyond simple streaming. This can expose users to unsafe or inappropriate external content, unexpected tracking, or policy violations if search terms are user-controlled and unfiltered.

Description-Behavior Mismatch

Medium
Confidence
93% confidence
Finding
The manifest frames the skill as an API/WebSocket-based platform deployed at lobstv.com, but the code defaults to https://lobster.fun and all implemented operations use fetch against HTTP endpoints. This is a semantic mismatch between the advertised platform/protocol and the actual implementation users will invoke.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The CLI stores the returned API key in ~/.lobster/config.json in plaintext without setting restrictive file permissions or warning the user. On multi-user systems, shared environments, backups, or malware-compromised hosts, this can expose credentials that may allow unauthorized agent registration or account actions.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The session secret is written to ~/.lobster/session.json in plaintext and later used to authorize stream actions such as say/end. Anyone who can read that file can hijack the active stream session, impersonate the agent, or terminate/control the broadcast until the session expires or is cleared.

Description-Behavior Mismatch

Low
Confidence
75% confidence
Finding
The manifest describes avatar streaming, TTS, expression control, chat interaction, and audience engagement, but L314-L332 adds a separate capability to search for and display GIFs or play YouTube videos using inline tags. That media-search/playback behavior is not reflected in the manifest description and expands the apparent functional scope beyond the stated platform features.

Description-Behavior Mismatch

Low
Confidence
87% confidence
Finding
The manifest emphasizes a WebSocket-driven real-time pipeline with synchronized interactive capabilities, yet the implementation consists of a generic HTTP API helper and commands that call discrete REST endpoints. While streaming-related actions are present, the described interaction model is not what this code actually implements.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"node": ">=18"
  },
  "dependencies": {
    "commander": "^12.0.0",
    "node-fetch": "^3.3.2"
  }
}
Confidence
40% confidence
Finding
Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Unpinned Dependencies

Low
Category
Supply Chain
Content
},
  "dependencies": {
    "commander": "^12.0.0",
    "node-fetch": "^3.3.2"
  }
}
Confidence
40% confidence
Finding
Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Static analysis

Detected: suspicious.env_credential_access, suspicious.exposed_secret_literal, suspicious.potential_exfiltration

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
bin/lobster.js:15

File appears to expose a hardcoded API secret or token.

Critical
Code
suspicious.exposed_secret_literal
Location
bin/lobster.js:139

Sensitive-looking file read is paired with a network send.

Warn
Code
suspicious.potential_exfiltration
Location
bin/lobster.js:38