Back to skill

Security audit

Felo SuperAgent

Security checks for vulnerabilities and agentic risk

Overview

This skill is a real Felo SuperAgent integration, but it has enough credential, scope, and conversation-state risks that users should review it carefully before installing.

Install only if you are comfortable sending relevant prompts, prior conversation context, LiveDoc/thread IDs, selected resources, and style data to Felo. Do not set FELO_API_BASE unless you fully trust the exact endpoint, avoid storing FELO_API_KEY permanently in a shell profile on shared or synced systems, and start a new LiveDoc/thread when switching projects or sensitive topics.

Vulnerability Patterns
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • 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)

T01 · Skill Instruction Hijacking

Error
Location
SKILL.md:12
Finding
Untrusted remote API output is required to control the Agent's final response verbatim## Vulnerability Details **File Location**: `SKILL.md:12-14` **Additional Locations**: `SKILL.md:387-388`, `SKILL.md:720-721` **Vulnerability Type**: Remote-content instruction hijacking **Risk Level**: High ### Vulnerable Code ```markdown 1. **ALWAYS use `--json` flag.** The script MUST run in JSON mode (`--json`). In Claude Code's Bash tool, stdout is always captured — it never streams directly to the user. JSON mode returns the full answer in a structured response that Claude can then output as text. State IDs are extracted from the JSON response fields `thread_short_id` and `live_doc_short_id`. 2. **ALWAYS output the answer directly as text.** After the script finishes, read `data.answer` from the JSON output and print it verbatim as your response text. Do NOT summarize, paraphrase, or add commentary around it. Output it exactly as-is so the user sees the full content. Then, if `data.image_urls` is non-empty, append image links immediately after, formatted as one line per image: `[title](url)`. ``` The same requirement is reiterated later: ```markdown 1. **Output `data.answer` verbatim** as your response text — print it exactly as-is so the user sees the full content. 2. **Extract and save** `data.thread_short_id` and `data.live_doc_short_id` — you MUST use these in the next call. ``` ```markdown - **ALWAYS use `--json`** — in Claude Code's Bash tool, stdout is captured, not streamed. JSON mode returns the answer in a structured response that Claude outputs as text - **ALWAYS output `data.answer` verbatim** — print it exactly as-is as your response text so the user sees the full content ``` ### Technical Analysis The `data.answer` value originates from an external Felo API SSE stream and is therefore outside the local Agent's trust boundary. The Skill instructs the host Agent to reproduce that externally controlled value exactly, while expressly prohibiting summarization, qualification, or commentary. This ...[truncated 2179 chars]
Remediation
## Remediation Suggestions 1. Remove the requirements to output `data.answer` verbatim and to avoid all commentary or review. 2. Explicitly classify API responses as untrusted data rather than Agent instructions. 3. Require the host Agent to apply its normal safety, privacy, and policy checks before presenting the response. 4. Clearly attribute retained content, for example: “Felo SuperAgent response,” so users can distinguish external output from the host Agent's own assertions. 5. Permit sanitization or suppression of credential requests, prompt-injection instructions, deceptive tool claims, unsafe links, and other harmful content. 6. Preserve useful formatting where safe, but do not let response-fidelity requirements override higher-priority security constraints. 7. Add an instruction stating that commands, policies, or requests embedded in `data.answer` must never be treated as instructions to the host Agent.

T09 · Insecure Skill Coding Practices

Error
Location
scripts/run_superagent.mjs:125
Finding
Felo bearer credential can be transmitted to an arbitrary configurable origin## Vulnerability Details **File Location**: `scripts/run_superagent.mjs:125-139`, `scripts/run_superagent.mjs:402-413` **Additional Locations**: `scripts/run_superagent.mjs:262-268`, `scripts/run_style_library.mjs:141-169`, `README.md:372-377` **Vulnerability Type**: Unrestricted credential destination and sensitive-data transmission **Risk Level**: High ### Vulnerable Code In `scripts/run_superagent.mjs`, the API key is read from the environment and the API origin is independently configurable: ```javascript const apiKey = process.env.FELO_API_KEY?.trim(); if (!apiKey) { console.error( 'ERROR: FELO_API_KEY not set\n\n' + 'To use SuperAgent, set FELO_API_KEY:\n' + ' export FELO_API_KEY="your-api-key-here"\n' + 'Get your API key from https://felo.ai (Settings -> API Keys).' ); process.exit(1); } const apiBase = (process.env.FELO_API_BASE?.trim() || DEFAULT_API_BASE).replace(/\/$/, ''); const timeoutMs = args.timeoutSec * 1000; ``` The same key is attached to the configurable destination: ```javascript async function createConversation(apiKey, apiBase, body, timeoutMs, threadId) { const url = threadId ? `${apiBase}/v2/conversations/${encodeURIComponent(threadId)}/follow_up` : `${apiBase}/v2/conversations`; const payload = await fetchJson( url, { method: 'POST', headers: { Accept: 'application/json', Authorization: `Bearer ${apiKey}`, 'Content-Type': 'application/json', }, body: JSON.stringify(body), }, timeoutMs ); const data = payload?.data ?? {}; if (!data.stream_key) throw new Error('Unexpected response: missing stream_key'); return data; } ``` The SSE request also sends the bearer key to a URL derived from the same unrestricted base: ```javascript const res = await fetch(connectUrl, { method: 'GET', headers: { Accept: 'text/event-stream', ...[truncated 4353 chars]
Remediation
## Remediation Suggestions 1. Restrict production credentials to an explicit allowlist, with `https://openapi.felo.ai` as the default and approved origin. 2. Parse the base with `new URL()` and reject malformed URLs, embedded credentials, unexpected ports, URL fragments, and all protocols other than HTTPS. 3. Do not attach `Authorization` until the final request URL has passed origin validation. 4. If custom API bases are required, use a separate environment variable for the custom endpoint's credential rather than forwarding `FELO_API_KEY`. 5. Require explicit user confirmation that identifies the exact destination before sending a credential to a non-default endpoint. 6. Prevent or carefully validate redirects. Never forward authorization headers when the effective origin differs from the validated origin. 7. Apply the same centralized endpoint-validation helper to conversation creation, SSE streaming, and style-library requests. 8. Minimize transmitted fields and avoid sending selected resources, style data, or conversation identifiers unless needed for the requested operation. 9. Document that changing the API base changes the credential's recipient, and remove examples that imply any arbitrary hostname is safe. 10. After suspected exposure, revoke and rotate the affected `FELO_API_KEY`, review account activity, and issue a least-privilege replacement key.
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
Findings (28)

Context Leakage

High
Category
Data Exfiltration
Content
### Example 2: Follow-up in thread

```bash
# First question — capture thread_short_id from stderr [state]
node felo-superAgent/scripts/run_superagent.mjs \
  --query "What is machine learning?" \
  --live-doc-id "PvyKouzJirXjFdst4uKRK3"
Confidence
75% confidence
Finding
Code or instructions that leak agent conversation context to external services, potentially exposing sensitive user interactions.

Ae1

High
Category
analysis-evasion
Content
3. **`--live-doc-id` is REQUIRED when creating a conversation.** Never call `run_superagent.mjs` without `--live-doc-id`. If you do not have one yet, obtain it
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
3. **`--live-doc-id` is REQUIRED when creating a conversation.** Never call `run_superagent.mjs` without `--live-doc-id`. If you do not have one yet, obtain it
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
3. **`--live-doc-id` is REQUIRED when creating a conversation.** Never call `run_superagent.mjs` without `--live-doc-id`. If you do not have one yet, obtain it
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ssd 3

High
Confidence
98% confidence
Finding
The instruction to reuse `live_doc_id` from any source, including other skills or user-provided input, creates a clear cross-context data exposure risk. It allows one workflow or untrusted input to bind this skill to an unrelated LiveDoc, potentially disclosing prior project content or causing operations to run against the wrong workspace.

Ae1

High
Category
analysis-evasion
Content
- The style string is the exact text block output by `run_style_library.mjs` for that entry. Fields vary by category:
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
- The style string is the exact text block output by `run_style_library.mjs` for that entry. Fields vary by category:
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
- The style string is the exact text block output by `run_style_library.mjs` for that entry. Fields vary by category:
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
- The style string is the exact text block output by `run_style_library.mjs` for that entry. Fields vary by category:
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
- The style string is the exact text block output by `run_style_library.mjs` for that entry. Fields vary by category:
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Description-Behavior Mismatch

High
Confidence
98% confidence
Finding
The top-level description says this skill must not be used for tweet/X post writing, yet the execution logic explicitly routes tweet-writing requests through this skill with `twitter-writer`. That contradiction can cause the wrong skill to be invoked, bypass intended separation of duties, and send user data into an unexpected workflow.

Context Leakage

High
Category
Data Exfiltration
Content
Run script (WITH --json, Bash timeout >= 600000ms) --> parse JSON, output data.answer verbatim
        |
        v
Extract thread_short_id + live_doc_id from stderr [state] line
        |
        v
Do NOT repeat or summarize the answer (already shown)
Confidence
89% confidence
Finding
The workflow explicitly persists `thread_short_id` and `live_doc_id` across runs, enabling continued access to a persistent conversation/workspace. In this skill's context, that persistence is dangerous because the same state can be reused across unrelated requests, increasing the chance of context leakage and accidental access to prior content.

Session Persistence

Medium
Category
Rogue Agent
Content
## Features

- **Streaming conversation** — create a conversation and receive AI responses in real-time via SSE
- **LiveDoc association** — each conversation is linked to a LiveDoc for resource tracking
- **Continuous conversation** — continue asking questions in an existing thread using `--thread-id`
- **Brand style support** — pass a writing style via `--ext` to guide output (used by `felo-twitter-writer` and other skills)
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.

Description-Behavior Mismatch

Medium
Confidence
98% confidence
Finding
The manifest context says this skill should not be used for tweet/X post writing and directs users to a different skill, but the README lists tweet writing as a use case and provides dedicated examples for `twitter-writer` plus `search_x` output handling. This is a direct semantic mismatch between the skill's declared scope and its documented behavior.

Lp3

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding
The skill clearly instructs use of networked scripts and environment-based secrets (`FELO_API_KEY`), but the manifest declares no explicit tool scope or permission boundary. That mismatch weakens reviewability and can lead to overbroad execution in environments where operators assume the manifest accurately constrains capabilities.

Ssd 3

Medium
Confidence
95% confidence
Finding
The skill requires verbatim relay of upstream `data.answer` with no review, filtering, or summarization. If the upstream system echoes sensitive user data, hidden prompt content, or unsafe generated instructions, the agent is forced to disclose it directly to the user.

Session Persistence

Medium
Category
Rogue Agent
Content
4. **Reuse `live_doc_id` from ANY source.** If you already have a `live_doc_id` from any previous operation in this session — whether from a prior SuperAgent call, a `felo-livedoc` skill operation, user-provided input, or any other skill — use it directly. Do NOT request the LiveDoc list again. Only fetch the list when no `live_doc_id` is available from any source. (Note: `live_doc_id` corresponds to the API field `live_doc_short_id` and the `[state]` output key `live_doc_short_id`.)

5. **One LiveDoc per session.** All conversations within a session MUST use the same `--live-doc-id`. Do NOT create a new LiveDoc unless the user explicitly asks to "open a new canvas" / "start a new LiveDoc" / "create a new workspace".

6. **Default behavior is follow-up, not new conversation.** After the first question, every subsequent user message is a follow-up. You MUST pass `--thread-id` from the previous response. Only omit `--thread-id` (to start a new thread on the same LiveDoc) when:
   - The user explicitly says "new topic" / "change subject" / "start over"
Confidence
93% confidence
Finding
The 'one LiveDoc per session' and default follow-up behavior create persistent state by default, even when the topic may drift. That increases retention and linkage of unrelated user interactions, making it easier to leak or mix context across tasks in a persistent external workspace.

Vague Triggers

Medium
Confidence
95% confidence
Finding
The trigger phrases are broad and overlap with ordinary conversation patterns such as 'follow-up question' or 'continuous chat'. That can cause accidental invocation of a networked external service, sending user content and session context to third-party infrastructure without sufficiently explicit user intent.

Description-Behavior Mismatch

Medium
Confidence
84% confidence
Finding
The manifest advertises a narrower purpose, while the body documents broader capabilities including document/PPT/HTML generation and X search. This kind of capability drift increases the chance that reviewers or users underestimate what data the skill may process or what actions it may trigger.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The setup instructions encourage persisting the API key in shell profile files as plaintext without warning about local exposure risk. On shared systems or synced home directories, this can leak long-lived credentials to other users, backup systems, or unintended processes.

Ssd 3

Medium
Confidence
88% confidence
Finding
The query-construction guidance tells the agent to incorporate prior context and previously mentioned details into every refined query. Without strict scoping, that encourages over-sharing of earlier conversation data to the external API, including details not necessary for the current request.

Context-Inappropriate Capability

Medium
Confidence
80% confidence
Finding
The manifest narrows this skill away from tweet/X post writing and directs such use to a different skill, but this script explicitly supports a TWITTER style category. Even though it fetches style metadata rather than generating tweets directly, adding Twitter-specific style support is not well-justified by the stated restriction and indicates capability overlap with the excluded domain.

Description-Behavior Mismatch

Medium
Confidence
85% confidence
Finding
The manifest narrows this skill's purpose to SuperAgent chat, continuous conversation, branding design, and e-commerce product images, and explicitly says tweet/X-related use should go to a different skill. The code nevertheless parses and surfaces `search_x` tool results, indicating built-in X/Twitter capability beyond the described scope.

Natural-Language Policy Violations

Medium
Confidence
94% confidence
Finding
At L445, the script constructs `liveDocUrl` using the fixed path segment `/zh-Hans/`, which forces a specific locale regardless of the user's `--accept-language` setting or other preferences. This is a natural-language locale policy issue because the skill imposes a language/locale choice rather than offering or deriving one from user opt-in.

Context-Inappropriate Capability

Medium
Confidence
88% confidence
Finding
The tool-call formatter contains bespoke logic for `search_x`, including query, type, and limit, showing the skill intentionally supports invoking and presenting Twitter/X searches. That capability is not justified by the manifest's stated use cases and conflicts with the instruction to avoid tweet/X-related usage in this skill.

Static analysis

Detected: suspicious.env_credential_access

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
scripts/run_style_library.mjs:141

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
scripts/run_superagent.mjs:402