Back to skill

Security audit

Excalidraw Diagram Generator

Security checks for vulnerabilities and agentic risk

Overview

The skill appears intended to render Excalidraw diagrams, but its setup process has supply-chain and temporary-file safety issues that should be reviewed before installation.

Install only if you are comfortable running a local Node-based renderer with setup-time network downloads. Run setup as an unprivileged user, avoid sudo, prefer npm ci from the lockfile, update the vulnerable transitive dependencies, and use unique temporary input/output paths rather than predictable /tmp names when rendering content influenced by others.

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

Warning
Location
scripts/setup.sh:18
Finding

External Font Assets Are Consumed Without Integrity Verification

Content
View full analysis

Vulnerability Details

File Location: scripts/setup.sh:18-31, 38-49
Vulnerability Type: Unverified third-party asset downloads
Risk Level: Medium

Vulnerable Code

bash
# 3. Download Virgil font (Excalidraw's handwritten font)
if [ ! -f "$FONT_DIR/Virgil.ttf" ]; then
  echo "→ Downloading Virgil font..."
  curl -sL "https://cdn.jsdelivr.net/npm/@excalidraw/excalidraw@0.17.6/dist/excalidraw-assets/Virgil.woff2" \
    -o "$FONT_DIR/Virgil.woff2"
  # Convert woff2 → ttf using fonttools (needed for resvg)
  if command -v python3 &>/dev/null && python3 -c "import fontTools" 2>/dev/null; then
    python3 -c "
from fontTools.ttLib import TTFont
font = TTFont('$FONT_DIR/Virgil.woff2')
font.flavor = None
font.save('$FONT_DIR/Virgil.ttf')
print('  Converted Virgil.woff2 → Virgil.ttf')
"
bash
# 4. Download Cascadia Code (for code font)
if [ ! -f "$FONT_DIR/CascadiaCode.ttf" ]; then
  echo "→ Downloading Cascadia Code font..."
  CASCADIA_VERSION="2404.23"
  curl -sL "https://github.com/microsoft/cascadia-code/releases/download/v${CASCADIA_VERSION}/CascadiaCode-${CASCADIA_VERSION}.zip" \
    -o /tmp/cascadia.zip
  cd /tmp
  unzip -qo cascadia.zip -d cascadia_extract 2>/dev/null || true
  find cascadia_extract -name "CascadiaCode*.woff2" -not -path "*/static/*" | head -1 | xargs -I{} cp {} "$FONT_DIR/CascadiaCode.woff2" 2>/dev/null || true
  find cascadia_extract -name "CascadiaCode-Regular.ttf" | head -1 | xargs -I{} cp {} "$FONT_DIR/CascadiaCode.ttf" 2>/dev/null || true

Technical Analysis

The setup script downloads font assets over HTTPS but does not verify an expected cryptographic digest or signature before parsing, extracting, installing, and later loading them into the renderer.

The URLs use versioned assets from jsDelivr and the official Microsoft GitHub repository. The static pre-scan characterization of the Cascadia download as an executable from a personal or pastebin site is therefore inaccurate: the downloaded object is a ...[truncated 2229 chars]

Remediation
View remediation

Remediation Suggestions

  1. Pin a trusted SHA-256 digest for each exact downloaded artifact and verify it before parsing or extraction:
bash
curl --fail --show-error --location "$URL" --output "$FILE"
printf '%s  %s\n' "$EXPECTED_SHA256" "$FILE" | sha256sum --check -
  1. Abort immediately when verification, extraction, conversion, or copying fails. Remove || true and avoid suppressing relevant error output.
  2. Prefer vendoring the reviewed font files in the Skill package when licensing permits. This removes setup-time network access and makes the audited artifact match the executed artifact.
  3. If upstream signatures are available, verify them against a pinned, trusted signing key in addition to hashes.
  4. Validate archive contents before extraction and accept only the exact expected font paths and file types.
  5. Run setup as an unprivileged user and document that it must not be run with sudo or as root.
  6. Consider npm ci --ignore-scripts instead of npm install when compatible with required dependencies, so installation exactly follows the lockfile and avoids unnecessary lifecycle-script execution.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/setup.sh:38
Finding

Predictable Shared Temporary Paths Allow Local Symlink and Race Attacks

Content
View full analysis

Vulnerability Details

File Location: scripts/setup.sh:38-53
Vulnerability Type: Unsafe temporary-file and directory handling
Risk Level: Medium

Vulnerable Code

bash
# 4. Download Cascadia Code (for code font)
if [ ! -f "$FONT_DIR/CascadiaCode.ttf" ]; then
  echo "→ Downloading Cascadia Code font..."
  CASCADIA_VERSION="2404.23"
  curl -sL "https://github.com/microsoft/cascadia-code/releases/download/v${CASCADIA_VERSION}/CascadiaCode-${CASCADIA_VERSION}.zip" \
    -o /tmp/cascadia.zip
  cd /tmp
  unzip -qo cascadia.zip -d cascadia_extract 2>/dev/null || true
  find cascadia_extract -name "CascadiaCode*.woff2" -not -path "*/static/*" | head -1 | xargs -I{} cp {} "$FONT_DIR/CascadiaCode.woff2" 2>/dev/null || true
  find cascadia_extract -name "CascadiaCode-Regular.ttf" | head -1 | xargs -I{} cp {} "$FONT_DIR/CascadiaCode.ttf" 2>/dev/null || true
  rm -rf cascadia.zip cascadia_extract
  cd "$SCRIPT_DIR"
fi

Technical Analysis

The script uses fixed names—/tmp/cascadia.zip and /tmp/cascadia_extract—inside a shared temporary directory. These names are predictable and are not created in a private, atomically allocated directory.

On a multi-user system, another local user or concurrent process can create or replace these paths before or during setup. A pre-existing /tmp/cascadia.zip symlink may cause the downloader to truncate and overwrite its target, subject to operating-system protections and the invoking user's permissions. A pre-created or raced extraction directory can also let an attacker influence files discovered by find and copied into the Skill's font directory.

The broad error suppression makes path manipulation and partial installation difficult to detect. Cleanup also operates on predictable shared names, potentially interfering with a concurrent setup process.

Attack Path

  1. A local attacker predicts that the victim will execute scripts/setup.sh.
  2. Before the download, the attacker creates `/tmp/cascad ...[truncated 1236 chars]
Remediation
View remediation

Remediation Suggestions

  1. Allocate an exclusive temporary directory and place all transient files inside it:
bash
umask 077
TMP_DIR="$(mktemp -d "${TMPDIR:-/tmp}/excalidraw-setup.XXXXXX")"
trap 'rm -rf -- "$TMP_DIR"' EXIT INT TERM

ARCHIVE="$TMP_DIR/cascadia.zip"
EXTRACT_DIR="$TMP_DIR/extract"
mkdir -- "$EXTRACT_DIR"
  1. Pass the absolute private paths to curl, unzip, and find; do not change into the shared /tmp directory.
  2. Reject unexpected symbolic links and verify that selected files are regular files owned or created by the current setup process.
  3. Use curl --fail --show-error --location and stop on every failed operation.
  4. Validate the ZIP member list before extraction and copy only an exact expected filename rather than the first wildcard match.
  5. Use install with controlled permissions for final files, and write to a temporary destination in FONT_DIR before performing an atomic rename.
  6. Keep setup unprivileged and explicitly warn users not to execute it as root.
  7. Update the documented workflow to use uniquely generated input and output files rather than reusable /tmp/<name> paths when inputs may be influenced by untrusted users.
Vulnerability Patterns
  • 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
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (8)

Known Vulnerable Dependency: form-data==4.0.5 — 1 advisory(ies): CVE-2026-12143 (form-data: CRLF injection in form-data via unescaped multipart field names and f)

High
Category
Supply Chain
Confidence
96% confidence
Finding

The lockfile pins form-data 4.0.5, which is flagged with a CRLF injection advisory affecting multipart field names/filenames. Even though this package is transitive via jsdom, a vulnerable dependency in the shipped dependency graph is still a real supply-chain risk if any code path constructs multipart requests from attacker-controlled values. In this skill’s diagram-rendering context, direct exploitability may be lower than in an HTTP-upload service, but the presence of jsdom and its networking-related dependencies means the issue should not be dismissed outright.

Content

No source excerpt is available for this finding.

Known Vulnerable Dependency: ws==8.19.0 — 2 advisory(ies): CVE-2026-45736 (ws: Uninitialized memory disclosure); CVE-2026-48779 (ws: Memory exhaustion DoS from tiny fragments and data chunks)

High
Category
Supply Chain
Confidence
97% confidence
Finding

The lockfile includes ws 8.19.0, which is reported as affected by memory disclosure and memory exhaustion denial-of-service issues. Because ws is a network-facing parser library used by jsdom, retaining a vulnerable version can expose consumers to resource exhaustion or data leakage if untrusted WebSocket traffic is ever processed. In this rendering skill, the main purpose is offline diagram generation, so contextual risk is somewhat reduced, but jsdom expands the attack surface enough that this remains a legitimate dependency vulnerability.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding

The skill invokes shell-capable behavior (node <skill_dir>/scripts/render.js ...) but does not declare any tool restrictions or permissions boundary in the skill metadata. That omission increases the chance of overbroad execution in environments where agents may have more capabilities than intended, making command execution harder to govern and audit.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The workflow directs the agent to write attacker-influenced content to /tmp and execute a local Node renderer without any safety notice, validation guidance, or execution constraints. In agent contexts, this creates risk of unsafe local file creation, overwriting predictable paths, and passing untrusted input into a renderer that may have parser or dependency vulnerabilities.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
70% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/setup.sh (reported line 21)May include surrounding context.

sh
# 3. Download Virgil font (Excalidraw's handwritten font)
if [ ! -f "$FONT_DIR/Virgil.ttf" ]; then
  echo "→ Downloading Virgil font..."
  curl -sL "https://cdn.jsdelivr.net/npm/@excalidraw/excalidraw@0.17.6/dist/excalidraw-assets/Virgil.woff2" \
    -o "$FONT_DIR/Virgil.woff2"
  # Convert woff2 → ttf using fonttools (needed for resvg)
  if command -v python3 &>/dev/null && python3 -c "import fontTools" 2>/dev/null; then

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
40% confidence
Finding

Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Content

Scanner excerpt · scripts/package.json (reported line 7)May include surrounding context.

json
"private": true,
  "type": "module",
  "dependencies": {
    "@resvg/resvg-js": "^2.6.2",
    "jsdom": "^25.0.1",
    "roughjs": "^4.6.6"
  }

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
40% confidence
Finding

Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Content

Scanner excerpt · scripts/package.json (reported line 8)May include surrounding context.

json
"type": "module",
  "dependencies": {
    "@resvg/resvg-js": "^2.6.2",
    "jsdom": "^25.0.1",
    "roughjs": "^4.6.6"
  }
}

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
40% confidence
Finding

Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Content

Scanner excerpt · scripts/package.json (reported line 9)May include surrounding context.

json
"dependencies": {
    "@resvg/resvg-js": "^2.6.2",
    "jsdom": "^25.0.1",
    "roughjs": "^4.6.6"
  }
}

Static analysis

No suspicious patterns detected.