Back to skill

Security audit

Excalidraw Render

Security checks for vulnerabilities and agentic risk

Overview

The skill is for making Excalidraw diagrams, but its setup and one bundled renderer use broader, less controlled third-party code execution than a user would likely expect.

Install only if you are comfortable with npm and uv downloading current third-party packages during setup. Review setup.sh first, avoid running it with sudo/admin privileges, prefer a local-only install, and avoid the references/ renderer path unless the CDN import is removed or explicitly approved.

Vulnerability Patterns
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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 (3)

T03 · Remote Payload Retrieval and Execution

Error
Location
references/render_template.html:15
Finding

Mutable Remote JavaScript Is Retrieved and Executed During Rendering

Content
View full analysis
import { exportToSvg } from "https://esm.sh/@excalidraw/excalidraw?bundle"; ``` `references/render_excalidraw.py:119-148`: ```python template_url = template_path.as_uri() with sync_playwright() as p: try: browser = p.chromium.launch(headless=True) except Exception as e: if "Executable doesn't exist" in str(e) or "browserType.launch" in str(e): print("ERROR: Chromium not installed for Playwright.", file=sys.stderr) print("Run: cd ~/.openclaw/skills/excalidraw-diagram && uv run playwright install chromium", file=sys.stderr) sys.exit(1) raise page = browser.new_page( viewport={"width": vp_width, "height": vp_height}, device_scale_factor=scale, ) # Load the template page.goto(template_url) # Wait for the ES module to load (imports from esm.sh) page.wait_for_function("window.__moduleReady === true", timeout=30000) # Inject the diagram data and render json_str = json.dumps(data) result = page.evaluate(f"window.renderDiagram({json_str})") ``` ### Technical Analysis The reference renderer opens a local HTML document that imports an Excalidraw module directly from `esm.sh`. The URL does not pin an exact package version or immutable artifact and does not use a cryptographic integrity check. Consequently, the JavaScript executed by Chromium can change after this project has been reviewed. The remote module executes in the rendering page before `window.renderDiagram` processes the supplied Excalidraw document. This gives the downloaded payload access to page-visible diagram data and the browser execution context. The ...[truncated 1463 chars]
Remediation
View remediation

T08 · Insecure Dependencies

Warning
Location
setup.sh:17
Finding

Unpinned Dependencies Permit Unreviewed Supply-Chain Code Execution

Content
View full analysis
/dev/null 2>&1 npx esbuild node_modules/@excalidraw/excalidraw/dist/prod/index.js \ --bundle \ --format=iife \ --global-name=ExcalidrawLib \ --minify \ --outfile="$SKILL_DIR/excalidraw.iife.js" 2>&1 echo "Done. Bundle written to: $SKILL_DIR/excalidraw.iife.js" # Install excalidraw-cli locally so it doesn't need npx at runtime echo "Installing @swiftlysingh/excalidraw-cli..." npm install -g @swiftlysingh/excalidraw-cli > /dev/null 2>&1 || \ npm install --prefix "$SKILL_DIR/.npm" @swiftlysingh/excalidraw-cli > /dev/null 2>&1 ``` `pyproject.toml:4-7` and `references/pyproject.toml:4-7`: ```toml requires-python = ">=3.11" dependencies = [ "playwright>=1.40.0", ] ``` ### Technical Analysis The setup script installs the latest package versions accepted by npm because no exact versions are specified. The Python manifest similarly permits any Playwright release at or above version 1.40.0. No npm or Python lockfile was present in the audited project. This means two installations performed at different times can retrieve and execute different dependency code. npm package installation can also run lifecycle scripts. The downloaded Excalidraw package is bundled directly into `excalidraw.iife.js`, making the unverified dependency part of the renderer’s executable code. Redirecting standard output and error to `/dev/null` also hides installation warnings and lifecycle-script output that could otherwise help identify suspicious behavior. ### Attack Path 1. A user or agent follows the setup instructions and runs `bash setup.sh` or `uv sync`. 2. The package manager resolves current releases rather than a previously reviewed dependen ...[truncated 1067 chars]
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
setup.sh:28
Finding

Setup Attempts a Global npm Installation Beyond the Skill Directory

Content
View full analysis
/dev/null 2>&1 || \ npm install --prefix "$SKILL_DIR/.npm" @swiftlysingh/excalidraw-cli > /dev/null 2>&1 ``` ### Technical Analysis Although the comment says the package should be installed locally, the script first attempts `npm install -g`. A successful global installation writes packages and executable shims outside the project directory into npm’s shared global prefix. Global installation is unnecessary because the script already contains a project-local fallback. It expands the modification scope from one Skill directory to shared user or system npm state. The use of an unpinned package further increases the risk because a mutable third-party package is placed on a globally visible command path. The command does not itself invoke `sudo` or bypass operating-system access controls. Its effective privileges remain those of the user running setup. However, users may run setup under an elevated account to make the global installation succeed, substantially increasing the consequences of a compromised package. ### Attack Path 1. A user or agent executes `bash setup.sh`. 2. The script attempts to install `@swiftlysingh/excalidraw-cli` into npm’s global prefix. 3. If the invoking account can write to that prefix, npm installs the package and command shims outside the Skill directory. 4. Package lifecycle scripts run with the invoking account’s permissions. 5. The globally installed executable becomes available to other projects and shell sessions. 6. If the package is compromised, its installer or command can affect shared state and any later invocation that resolves to the global binary. # ...[truncated 566 chars]
Remediation
View remediation
``` 3. Invoke the binary through its explicit local path: ```bash "$SKILL_DIR/.npm/node_modules/.bin/excalidraw-cli" ``` 4. Never instruct users to run setup with `sudo` or an administrator account. 5. Run setup in a restricted environment with access limited to the Skill directory and required package caches. 6. Check and report installation failures rather than silently redirecting all output. 7. Document all paths modified by setup and verify that no files are written outside the Skill directory. ]]>
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
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (12)

Anti-Refusal Statement

High
Category
Anti-Refusal
Confidence
80% confidence
Finding

Skill instructs the agent to omit warnings, disclaimers, or ethical commentary. Stripping safety caveats hides risk from the user and is a common jailbreak preamble.

Content

Scanner excerpt · SKILL.md (reported line 210)May include surrounding context.

md
- No text overflow or overlapping
- Arrows route cleanly
- Consistent spacing, balanced composition
- You'd show it without caveats

---

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
84% confidence
Finding

The skill instructs the agent to read generated PNGs and several local reference files, but it does not declare an explicit tool scope such as allowed-tools or permissions. This creates an authorization gap where the runtime may permit broader file-read behavior than intended, making it harder to constrain what the skill can access during execution.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
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.

Content

Scanner excerpt · SKILL.md (reported line 154)May include surrounding context.

md
Build JSON **one section at a time** (Claude has ~32k token output limit):

1. Create base file + first section
2. Add one section per edit — use descriptive IDs (`"trigger_rect"`, `"auth_arrow"`)
3. Namespace seeds by section (100xxx, 200xxx, etc.)
4. Update cross-section bindings as you go

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The renderer loads a local HTML template and then explicitly waits for an ES module imported from esm.sh, which causes outbound network requests during rendering without any user-facing disclosure or opt-in. This creates a supply-chain and privacy risk: rendering untrusted or sensitive diagrams may unexpectedly contact third-party infrastructure, and availability of the feature depends on an external service at runtime.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The template imports executable code at runtime from a third-party CDN, which creates a supply-chain and integrity risk outside the skill author's direct control. If the CDN, package, or transit path is compromised, untrusted code would run in the rendering context and could alter output, exfiltrate data available to the page, or abuse the environment.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The skill's purpose is diagram rendering, but the implementation adds a runtime dependency on externally loaded code, increasing attack surface without clear necessity. This makes rendering depend on live network content, enabling unexpected code changes, outages, or hostile package updates to affect behavior.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The page performs an external network import automatically and without any user-facing disclosure, which can violate operator expectations in environments assumed to be local or offline. Lack of transparency increases risk because users may unknowingly permit data exposure, policy violations, or execution of third-party code during rendering.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The setup script performs multiple network package installations and may modify the global system (npm install -g) without prominently warning the user about those side effects. In a skill setup context, this increases supply-chain and environment-modification risk because running the script implicitly trusts external npm packages and can alter the host outside the skill directory.

Content

No source excerpt is available for this finding.

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.

Content

No source excerpt is available for this finding.

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.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
67% confidence
Finding

The code unconditionally sets a white background and disables dark mode export, imposing a fixed presentation choice rather than honoring user or input preferences. This is a natural-language policy concern because it enforces a specific mode without any opt-in or documented justification.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
81% confidence
Finding

This code writes a PNG to disk via svg_el.screenshot(path=str(output_path)), which is a safety-relevant file write. While the script behavior is inferable from the tool name and CLI arguments, there is no explicit confirmation prompt or user-facing warning near the write path about creating or overwriting the output file.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.