Back to skill

Security audit

mcp-apps-host-dev

Security checks for vulnerabilities and agentic risk

Overview

The skill is a coherent MCP Apps host-development guide, but it recommends under-scoped card permissions and persistence that can let untrusted cards influence model turns or retain sensitive card data.

Review this skill carefully before installing. It is most appropriate for developers working on MCP Apps host code, but generated changes should be tightened: require explicit authorization for card-originated model turns, separate card data from user instructions, use per-card or per-tool allowlists instead of broad server-wide permission, restrict CSP domains and forms, avoid durable localStorage for sensitive payloads, and prefer a pinned or manually reviewed installation path.

Vulnerability Patterns
  • 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
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
Findings (4)

T09 · Insecure Skill Coding Practices

Error
Location
SKILL.md:301
Finding
Untrusted MCP Card Content Is Injected into Model Conversations<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md`, lines 301–323 **Vulnerability Type**: Indirect prompt injection through card-to-model communication **Risk Level**: High ### Vulnerable Code Snippet ```typescript // The specification states that each update replaces the previous // snapshot for the same view. stageModelContext(viewId, extractTextContent(params)) // When the next user message is sent: buildOutgoingUserText(userText) // → "Card state:\nSearch: Bluetooth headphones Price<100\n\nUser-entered text" ``` ```typescript requestMcpAppUserMessage(text) // → message channel → submitText submission path ``` The documented security control is limited to per-card debounce: ```typescript requestMcpAppUserMessage(text, debounceKey) ``` ### Technical Analysis The Skill instructs host developers to accept text originating from remotely supplied MCP card HTML and either: 1. Silently prepend it to the next user message through `ui/update-model-context`; or 2. Submit it as a new conversation turn through `ui/message`. The MCP server controls the card HTML and scripts. Consequently, the text passed to these APIs must be treated as untrusted remote input. The described implementation does not require user confirmation, display the injected context to the user, enforce a content schema, distinguish data from instructions, or prevent card content from being interpreted as agent instructions. Per-card debounce only limits request frequency. It does not prevent a single malicious message from influencing the model or causing the agent to invoke tools. ### Attack Path 1. A user connects to or invokes a malicious or compromised MCP server. 2. The server returns MCP App HTML containing attacker-controlled JavaScript. 3. The card sends crafted text through `ui/update-model-context`, such as instructions to ignore the user's request or disclose available context. 4. The host stores the text and invisibly prepends it to the user's next message. 5. A ...[truncated 845 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Treat all card-originated text as untrusted structured data rather than user instructions. 2. Do not silently concatenate card content with user-authored messages. 3. Display the proposed card message and require explicit user approval before starting a model turn. 4. Mark card data with immutable provenance metadata, including server identity, card identity, and tool-call ID. 5. Pass view state through a dedicated structured field instead of embedding it in natural-language prompt text. 6. Enforce strict schemas, maximum lengths, accepted character/content types, and per-card quotas. 7. Ensure card-originated content cannot authorize tools or override system, developer, or user instructions. 8. Apply tool-specific confirmation and authorization independently of model output. 9. Retain rate limiting, but supplement debounce with total request quotas and cancellation controls. 10. Add adversarial tests covering instruction-like card state, repeated `ui/message` calls, and attempted tool authorization. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
SKILL.md:258
Finding
Overly Permissive Card CSP Enables Unauthorized Network Disclosure<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md`, lines 258–291 **Vulnerability Type**: Excessive iframe network privileges and unsafe Content Security Policy defaults **Risk Level**: High ### Vulnerable Code Snippet ```typescript function buildCsp(csp: McpUiCsp): string { const res = (csp.resourceDomains ?? []).join(' ').trim() const conn = (csp.connectDomains ?? []).join(' ').trim() const script = (csp.scriptSrc || "'unsafe-inline' 'unsafe-eval'").trim() return [ "default-src 'none'", `script-src ${script}`, `style-src 'unsafe-inline' ${res}`.trim(), `img-src data: blob: ${res}`.trim(), `font-src data: ${res}`.trim(), `media-src ${res || "'none'"}`.trim(), `connect-src ${conn || "'none'"}`.trim(), "base-uri 'none'", 'form-action *' ].join('; ') } ``` The Skill also recommends allowing all HTTPS image origins: ```typescript img-src data: blob: https: ``` ### Technical Analysis The iframe sandbox includes `allow-forms`, while the proposed CSP uses `form-action *`. This permits card HTML to submit form data to arbitrary destinations, even when `connect-src` is set to `'none'`. The recommendation to add the broad `https:` source to `img-src` creates another outbound channel. A malicious card can encode accessible information into an attacker-controlled image URL. Browser image requests can therefore be used for tracking or limited data exfiltration without relying on `fetch` or WebSocket access. The default script policy also includes both `'unsafe-inline'` and `'unsafe-eval'`. Inline scripts are expected for this card architecture, but allowing dynamic string evaluation by default unnecessarily increases the consequences of script injection and violates least privilege. ### Attack Path 1. A malicious or compromised MCP server returns attacker-controlled card HTML. 2. The host renders the HTML in an iframe with scripts and forms enabled. 3. The c ...[truncated 937 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Change the default to `form-action 'none'`. 2. Permit form submission only when a card demonstrably requires it and only to explicitly approved origins. 3. Do not use a blanket `img-src https:` policy. Require an explicit, normalized image-domain allowlist. 4. Keep `connect-src 'none'` by default and validate every requested connection origin against host policy. 5. Remove `'unsafe-eval'` from the default `script-src`. 6. Prefer script hashes or nonces where the card packaging model supports them. 7. Validate all CSP source expressions supplied by MCP servers; reject wildcards, unexpected schemes, malformed hosts, and dangerous directives. 8. Apply a host-level maximum policy so server-provided CSP metadata can narrow access but cannot grant unrestricted network access. 9. Consider proxying approved external resources through a controlled host component with size, type, and destination checks. 10. Add tests proving that forms, images, media, scripts, and connections cannot reach undeclared origins. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
SKILL.md:446
Finding
Sensitive MCP Card Payloads Are Persisted in localStorage Across Sessions<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md`, lines 446–458 **Vulnerability Type**: Persistent plaintext storage without data minimization or session isolation **Risk Level**: Medium ### Vulnerable Code Snippet ```text - cacheMcpUi() writes both the in-memory Map and localStorage (key: mcp-ui:<sessionId>) - preserveMcpUiCards() falls back to localStorage when the in-memory Map does not contain the entry - clearCachedMcpUi() clears both memory and localStorage - Cross-session ID matching: when the runtime session ID differs from the event session ID used during storage, scan all mcp-ui:* localStorage entries and match by tool-call ID ``` ```text Use localStorage rather than sessionStorage—localStorage survives both HMR reloads and application restarts. ``` ### Technical Analysis The Skill recommends persisting MCP UI payloads in browser `localStorage` so cards survive hot reloads and application restarts. These payloads may include card HTML, structured tool results, session identifiers, checkout information, addresses, or other service-specific data. `localStorage` is durable plaintext storage accessible to scripts executing in the same renderer origin. The documented design does not prescribe expiration, encryption, data classification, sensitivity filtering, storage quotas, user isolation, or cleanup on logout. Scanning all `mcp-ui:*` entries to match tool-call IDs also weakens session boundaries and increases the chance that stale data from another runtime session will be restored into the current session. ### Attack Path 1. An MCP tool returns a UI payload containing sensitive structured content or identifiers. 2. The host stores the complete payload in `localStorage`. 3. The data remains after HMR, application restart, or completion of the original conversation. 4. A later malicious card, injected renderer script, compromised dependency, or user sharing the same application profile accesses or enumerates the stored entr ...[truncated 597 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Keep full UI payloads in memory by default. 2. Persist only the minimum non-sensitive presentation metadata needed to reconstruct a card. 3. Never store credentials, bearer tokens, session secrets, payment details, addresses, or unrestricted structured tool results in `localStorage`. 4. Prefer session-scoped storage unless cross-restart persistence is an explicit user requirement. 5. Namespace cache entries by authenticated user, application profile, MCP server, and conversation session. 6. Remove cross-session wildcard scanning and require exact ownership and session matches. 7. Add short expiration times, cache versioning, maximum sizes, and deterministic cleanup. 8. Clear cached data on logout, account changes, MCP server disconnection, conversation deletion, and privacy-mode activation. 9. Where durable storage is essential, use an operating-system-backed protected store and encrypt sensitive data at rest. 10. Add tests ensuring that one session or user cannot restore another session's UI payload. ]]>

T08 · Insecure Dependencies

Warning
Location
README.md:56
Finding
Recommended Installation Executes an Unpinned Third-Party Package<![CDATA[ ## Vulnerability Details **File Location**: `README.md`, lines 56–60 **Vulnerability Type**: Unpinned dependency execution during installation **Risk Level**: Medium ### Vulnerable Code Snippet ```bash npx skills add oriliz/mcp-apps-host-dev ``` ### Technical Analysis The recommended installation method invokes `npx`, which may download and execute the currently resolved version of the `skills` package. The command does not pin an audited version or integrity digest and does not instruct users to inspect the downloaded package before execution. As a result, the effective installer can change after the Skill has been reviewed. A package-owner compromise, registry compromise, malicious future release, or dependency-chain compromise could cause attacker-controlled code to run under the installing user's account. The repository also provides a manual Git clone method, but the unpinned `npx` command is explicitly presented as the recommended installation path. ### Attack Path 1. An attacker compromises the package publisher, registry account, package distribution path, or a transitive dependency used by the `skills` installer. 2. The attacker publishes a malicious version that resolves under the unpinned package name. 3. A user follows the documented `npx skills add ...` command. 4. `npx` downloads and executes the compromised package. 5. The malicious installer runs with the user's permissions and can access files, environment variables, credentials, and agent configuration available to that account. ### Impact Assessment Successful exploitation can execute arbitrary code with the privileges of the user running the installation command. Depending on the local environment, this may expose source repositories, agent configuration, API credentials, SSH material, and other user-accessible data. No malicious package behavior is present in the audited two-file project itself; the risk arises from the unpinned, mutable third-party installation channel ...[truncated 5 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin the installer to a specific audited version rather than resolving the latest version. 2. Publish and verify an integrity hash or signed provenance statement for the installer. 3. Document the expected package publisher, registry, source repository, and release signature. 4. Instruct users to inspect the package contents and lifecycle scripts before execution. 5. Prefer a non-executing installation path, such as downloading a verified archive or cloning a specific signed commit. 6. If `npx` remains supported, use an exact version and disable interactive substitution or unexpected package resolution. 7. Periodically re-audit the pinned installer and its transitive dependencies before updating the documented version. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
Findings (5)

Rp1

Medium
Category
MCP Rug Pull
Confidence
70% confidence
Finding
npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Rp1

Medium
Category
MCP Rug Pull
Confidence
70% confidence
Finding
npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Natural-Language Policy Violations

Medium
Confidence
97% confidence
Finding
The skill content is written entirely in Chinese starting with the title at L010 and continuing throughout the document, while the manifest description at L003 is in English and does not state that the skill is China-specific or requires Chinese. This creates a language-policy issue because users are not given an opt-in or alternative language choice.

External Transmission

Medium
Category
Data Exfiltration
Content
# 3. 如果日志停在几分钟前且没有新 turn → gateway 挂了

# 4. 检查 API 端点是否可达
curl -s --max-time 10 "<base_url>/chat/completions" \
  -H "Authorization: Bearer <key>" \
  -d '{"model":"<model>","messages":[{"role":"user","content":"hi"}],"max_tokens":5}'
```
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Rp1

Medium
Category
MCP Rug Pull
Confidence
70% confidence
Finding
npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Static analysis

No suspicious patterns detected.