Back to skill

Security audit

drawio-architecture

Security checks for vulnerabilities and agentic risk

Overview

The skill is mostly coherent for draw.io diagram creation, but its setup helper can persistently modify agent MCP configuration to run an unpinned npm package and may print existing sensitive config.

Review this skill before installing. Prefer manually adding a pinned @drawio/mcp@<VERSION> config entry instead of using the bundled helper as-is, avoid the Cursor one-click link unless it is regenerated with a pinned version, and do not paste dry-run output into logs or chats if your MCP config may contain secrets. Use the hosted draw.io endpoint only for non-sensitive diagrams.

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

T08 · Insecure Dependencies

Error
Location
scripts/setup_drawio_mcp.py:20
Finding
Persistent MCP Configuration Executes an Unpinned Remote npm Package<![CDATA[ ## Vulnerability Details **File Locations**: - `scripts/setup_drawio_mcp.py:20` - `scripts/setup_drawio_mcp.py:159-170` - `references/mcp-config.md:107` **Vulnerability Type**: Unpinned third-party dependency and mutable remote code execution **Risk Level**: High ### Vulnerable Code ```python MCP_ENTRY = {"command": "npx", "args": ["-y", "@drawio/mcp"]} ``` The unpinned entry is subsequently inserted into the selected client's persistent MCP configuration: ```python servers = existing.get(key, {}) if SERVER_KEY in servers and not force: print(f"[=] '{SERVER_KEY}' already present in {key}. Use --force to overwrite.") print(json.dumps(existing, indent=2)) return 0 servers[SERVER_KEY] = MCP_ENTRY existing[key] = servers if dry_run: print("[dry-run] Would write:") print(json.dumps(existing, indent=2)) return 0 os.makedirs(os.path.dirname(path), exist_ok=True) with open(path, "w", encoding="utf-8") as f: json.dump(existing, f, indent=2) ``` The Cursor one-click configuration in `references/mcp-config.md:107` also contains an unpinned encoded payload: ```text https://cursor.com/en/install-mcp?name=drawio&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsIkBkcmF3aW8vbWNwIl19 ``` The decoded configuration is: ```json {"command":"npx","args":["-y","@drawio/mcp"]} ``` ### Technical Analysis The helper configures clients to run: ```bash npx -y @drawio/mcp ``` No exact package version is specified. Consequently, npm resolves the package version at execution time, downloads it if necessary, and runs its code. The effective code executed by the MCP client can therefore change after this Skill has been reviewed. The `-y` option suppresses the normal installation confirmation, further reducing user visibility. Because the command is written into persistent client configuration, exposure is not limited to the initial setup invocation. The package can be resolved and executed whenever the client starts or activates the MCP server. ...[truncated 1555 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Require an exact package version as a command-line argument, for example: ```bash python3 scripts/setup_drawio_mcp.py --target vscode --version 1.2.3 ``` 2. Validate the version against a strict semantic-version pattern and reject missing versions, tags such as `latest`, ranges, URLs, and arbitrary npm specifications. 3. Construct the entry using the validated version: ```python package = f"@drawio/mcp@{validated_version}" mcp_entry = {"command": "npx", "args": ["-y", package]} ``` 4. Do not retain an unpinned default. The helper should fail closed when no exact version is supplied. 5. Replace the Cursor one-click payload with one containing an exact reviewed version. 6. Consider verifying package integrity through a lockfile, approved package hash, or controlled internal registry. 7. Document a deliberate update process in which new versions are reviewed and the pinned value is changed explicitly. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/setup_drawio_mcp.py:146
Finding
Existing MCP Configuration May Be Printed with Embedded Credentials<![CDATA[ ## Vulnerability Details **File Location**: `scripts/setup_drawio_mcp.py:146-168` **Vulnerability Type**: Sensitive configuration disclosure through standard output **Risk Level**: Medium ### Vulnerable Code ```python existing = {} if os.path.exists(path): try: with open(path, "r", encoding="utf-8") as f: existing = json.load(f) except json.JSONDecodeError: print(f"[!] Existing config is not valid JSON: {path}", file=sys.stderr) return 1 else: print(f"[*] Config does not exist yet; will create it.") servers = existing.get(key, {}) if SERVER_KEY in servers and not force: print(f"[=] '{SERVER_KEY}' already present in {key}. Use --force to overwrite.") print(json.dumps(existing, indent=2)) return 0 servers[SERVER_KEY] = MCP_ENTRY existing[key] = servers if dry_run: print("[dry-run] Would write:") print(json.dumps(existing, indent=2)) return 0 ``` ### Technical Analysis Reading the existing MCP configuration is necessary to merge the draw.io server entry without deleting unrelated settings. Printing the complete parsed configuration is not necessary for that operation. MCP configuration files can contain environment blocks or server arguments with API keys, access tokens, passwords, authorization headers, and other sensitive values. Two normal execution paths emit the complete configuration: - When a `drawio` server already exists and `--force` is not supplied. - When the helper is run with `--dry-run`. Standard output may be retained in shell transcripts, CI logs, IDE task logs, agent tool output, or chat transcripts. The disclosure therefore expands sensitive information beyond the original protected configuration file. This is a local disclosure issue; the script does not itself transmit the output over the network. ### Attack Path 1. A supported MCP client configuration contains credentials for one or more existing servers. 2. The user runs the helper with `--dry-run`, ...[truncated 1091 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Do not print the complete existing configuration in either branch. 2. When the draw.io entry already exists, print only a status message and the destination path: ```python print(f"[=] '{SERVER_KEY}' already exists in {path}. Use --force to overwrite.") ``` 3. For `--dry-run`, print only the proposed draw.io entry rather than the merged document: ```python print(json.dumps({key: {SERVER_KEY: mcp_entry}}, indent=2)) ``` 4. If displaying surrounding configuration is operationally necessary, recursively redact: - Keys matching `token`, `secret`, `password`, `apiKey`, `authorization`, or similar names. - Values under MCP `env` objects. - Credential-bearing command-line arguments and URLs. 5. Add tests using synthetic secrets to ensure they never appear in stdout or stderr. 6. Document that client configurations may contain credentials and should not be copied into issue reports, logs, or chat conversations. ]]>
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (36)

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The skill also performs validation/linting and PNG repair workflows that modify output artifacts, which are outside the narrow stated purpose of diagram generation/editing. While not inherently malicious, undisclosed file transformation behavior broadens the attack surface and can surprise users or agents about what files may be read or altered.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The skill also performs validation/linting and PNG repair workflows that modify output artifacts, which are outside the narrow stated purpose of diagram generation/editing. While not inherently malicious, undisclosed file transformation behavior broadens the attack surface and can surprise users or agents about what files may be read or altered.

MCP Config Access

High
Category
Agent Snooping
Content
Replace `<VERSION>` with the latest stable release verified on [npm](https://www.npmjs.com/package/@drawio/mcp). Do not run bare `npx -y @drawio/mcp` because it resolves to the latest remote version at runtime.

Add it to your client's MCP config under `mcpServers.drawio`. For the concrete JSON block per platform plus self-hosting, see **`references/mcp-config.md`** (Claude Desktop, Claude Code, VS Code `.vscode/mcp.json`, Cursor `~/.cursor/mcp.json`, OpenCode, Windsurf, and the `DRAWIO_BASE_URL` env for self-hosted instances).

There is also a **hosted** alternative (`https://mcp.draw.io/mcp`) that renders diagrams *inline* via the MCP Apps protocol (Claude.ai, VS Code, Cursor) — no install, but it is a *different* server type than the stdio one above and sends your diagram XML to the draw.io vendor's servers. Only use it for non-sensitive diagrams and when you trust the vendor endpoint.
Confidence
90% confidence
Finding
The skill provides instructions and helper automation to edit MCP client configuration files such as `.vscode/mcp.json` and `~/.cursor/mcp.json`. Access to these files is security-sensitive because changing them can register new tool servers, alter trust boundaries, and persist execution pathways across future sessions; the danger is increased here because the skill also references network-fetched tooling and optional remote endpoints.

Hidden Instructions

High
Category
Prompt Injection
Content
## Core shapes (vertex)

```xml
<!-- Rounded rectangle — services, modules -->
<mxCell id="2" value="Label" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1">
  <mxGeometry x="100" y="100" width="140" height="60" as="geometry"/>
</mxCell>
Confidence
70% confidence
Finding
Hidden instructions were detected in comments or invisible text. These could contain malicious directives. Manual review is recommended.

Agent Config Directory Access

High
Category
Agent Snooping
Content
| Target (`--target`) | Agent / IDE | Config file | JSON key |
|---------------------|-------------|-------------|----------|
| `claude-desktop` | Claude Desktop | `~/Library/Application Support/Claude/claude_desktop_config.json` (mac) / `%APPDATA%\Claude\...` (win) / `~/.config/Claude/...` (linux) | `mcpServers` |
| `claude-code` | Claude Code | `~/.claude/settings.json` (or `claude mcp add drawio -- npx -y @drawio/mcp@<VERSION>`) | `mcpServers` |
| `vscode` | VS Code / GitHub Copilot | `.vscode/mcp.json` (workspace) or `~/.config/Code/User/mcp.json` (global) | `servers` |
| `cursor` | Cursor | `~/.cursor/mcp.json` (global) or `.cursor/mcp.json` (project) | `mcpServers` |
| `opencode` | OpenCode | `~/.config/opencode/opencode.json` (or `opencode mcp add drawio -- npx -y @drawio/mcp@<VERSION>`) | `mcpServers` |
Confidence
90% confidence
Finding
Skill reads from agent configuration directories (.claude/, .codex/, .gemini/). These directories may contain API keys, personal settings, and other credentials that the skill has no legitimate need to access.

Agent Config Directory Access

High
Category
Agent Snooping
Content
| `devin` | Devin Desktop | `~/.devin/mcp.json` (linux) — also via Devin app/UI Integrations | `mcpServers` |
| `devin-cli` | Devin CLI | `~/.config/devin/mcp.json` — also via Devin app/UI | `mcpServers` |
| `agy` | AGY (Antigravity CLI) | `~/.gemini/antigravity-cli/mcp.json` — also via Antigravity UI | `mcpServers` |
| `antigravity` | Antigravity IDE | `~/.gemini/settings.json` (reuses Gemini CLI) | `mcpServers` |
| `gemini` | Gemini CLI | `~/.gemini/settings.json` | `mcpServers` |

The universal block for **every** stdio client (Claude, Cursor, OpenCode, Windsurf, Devin, AGY, Antigravity, Gemini, …):
Confidence
90% confidence
Finding
Skill reads from agent configuration directories (.claude/, .codex/, .gemini/). These directories may contain API keys, personal settings, and other credentials that the skill has no legitimate need to access.

Agent Config Directory Access

High
Category
Agent Snooping
Content
| `devin` | Devin Desktop | `~/.devin/mcp.json` (linux) — also via Devin app/UI Integrations | `mcpServers` |
| `devin-cli` | Devin CLI | `~/.config/devin/mcp.json` — also via Devin app/UI | `mcpServers` |
| `agy` | AGY (Antigravity CLI) | `~/.gemini/antigravity-cli/mcp.json` — also via Antigravity UI | `mcpServers` |
| `antigravity` | Antigravity IDE | `~/.gemini/settings.json` (reuses Gemini CLI) | `mcpServers` |
| `gemini` | Gemini CLI | `~/.gemini/settings.json` | `mcpServers` |

The universal block for **every** stdio client (Claude, Cursor, OpenCode, Windsurf, Devin, AGY, Antigravity, Gemini, …):
Confidence
90% confidence
Finding
Skill reads from agent configuration directories (.claude/, .codex/, .gemini/). These directories may contain API keys, personal settings, and other credentials that the skill has no legitimate need to access.

Agent Config Directory Access

High
Category
Agent Snooping
Content
| `devin` | Devin Desktop | `~/.devin/mcp.json` (linux) — also via Devin app/UI Integrations | `mcpServers` |
| `devin-cli` | Devin CLI | `~/.config/devin/mcp.json` — also via Devin app/UI | `mcpServers` |
| `agy` | AGY (Antigravity CLI) | `~/.gemini/antigravity-cli/mcp.json` — also via Antigravity UI | `mcpServers` |
| `antigravity` | Antigravity IDE | `~/.gemini/settings.json` (reuses Gemini CLI) | `mcpServers` |
| `gemini` | Gemini CLI | `~/.gemini/settings.json` | `mcpServers` |

The universal block for **every** stdio client (Claude, Cursor, OpenCode, Windsurf, Devin, AGY, Antigravity, Gemini, …):
Confidence
90% confidence
Finding
Skill reads from agent configuration directories (.claude/, .codex/, .gemini/). These directories may contain API keys, personal settings, and other credentials that the skill has no legitimate need to access.

Agent Config Directory Access

High
Category
Agent Snooping
Content
},
        "claude-code": {
            "key": "mcpServers",
            "paths": {"*": f"{h}/.claude/settings.json"},
        },
        "vscode": {
            "key": "servers",
Confidence
94% confidence
Finding
Direct access to `~/.claude/settings.json` gives the script the ability to persistently alter Claude Code MCP configuration, affecting future agent behavior outside the current task. In the context of a diagram skill, touching agent config is unusually powerful and enables persistence of external command execution pathways.

Agent Config Directory Access

High
Category
Agent Snooping
Content
"antigravity": {
            "key": "mcpServers",
            "note": "Antigravity IDE reuses the Gemini CLI settings file.",
            "paths": {"*": f"{h}/.gemini/settings.json"},
        },
        "gemini": {
            "key": "mcpServers",
Confidence
93% confidence
Finding
Referencing `~/.gemini/settings.json` for Antigravity/Gemini configuration gives this script persistent control over another agent's MCP server definitions. Because these are high-trust config files, modifying them can change what external tools are invoked in later sessions and extends the skill's reach well beyond diagram editing.

Agent Config Directory Access

High
Category
Agent Snooping
Content
},
        "gemini": {
            "key": "mcpServers",
            "paths": {"*": f"{h}/.gemini/settings.json"},
        },
    }
Confidence
93% confidence
Finding
This additional Gemini settings path again grants write access to a sensitive agent configuration location, enabling persistent MCP registration. The danger is amplified because the script couples this persistence with an unpinned `npx` package, creating a durable supply-chain execution path across future agent runs.

Lp3

Medium
Category
MCP Least Privilege
Confidence
82% confidence
Finding
The skill describes operations that can read/write local files, inspect environment/configuration, and invoke networked package/tooling, but it does not declare any explicit tool scope or permissions boundary. In an agent ecosystem, that omission can cause overbroad execution authority and makes it harder for reviewers or runtimes to constrain risky actions such as editing MCP config files or invoking external tools.

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.

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.

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.

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.

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.

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
87% confidence
Finding
The Cursor one-click install URL contains a base64-encoded config whose `args` resolve to `['-y','@drawio/mcp']` without an explicit version pin. Following that link would cause remote package resolution at install/runtime, creating supply-chain risk if the latest package version is compromised or unexpectedly changed.

Rp1

Medium
Category
MCP Rug Pull
Confidence
95% confidence
Finding
The documentation instructs users to run `npx @drawio/mcp` without pinning an exact package version, which causes the latest package to be fetched at execution time. If the upstream package is compromised, typosquatted, or publishes a breaking/malicious update, users of the skill could execute unreviewed code on their machine or in the agent environment.

Static analysis

No suspicious patterns detected.