Back to skill

Security audit

Erdmannsilva Excalidraw

Security checks for vulnerabilities and agentic risk

Overview

This diagram skill appears to do what it says, but its setup has enough dependency and download hygiene issues that users should review it before installing.

Review this before installing in a trusted environment. Use it only from an unprivileged account, avoid sensitive diagram content in shared /tmp environments, and prefer updating vulnerable dependencies plus adding verified downloads and private temporary directories before production use.

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:21
Finding

Remote Font Assets Are Downloaded Without Integrity Verification

Content
View full analysis
/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') " else echo " ⚠ fonttools not available. Install with: pip3 install --user fonttools brotli" echo " Then run: python3 -c \"from fontTools.ttLib import TTFont; f=TTFont('$FONT_DIR/Virgil.woff2'); f.flavor=None; f.save('$FONT_DIR/Virgil.ttf')\"" fi # 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 ``` ### Technical Analysis The setup script retrieves Virgil and Cascadia Code font assets over HTTPS but does not verify their cryptographic hashes or signatures. Although both URLs use pinned versions and recognizable public sources, version pinning alone does not authenticate downloaded bytes. The Cascadia URL points to Microsoft's official GitHub repository, not a personal pastebin, and the downloaded archive contains font assets rather than an executable. Therefore, this is not confirmed remote code retrieval and execution. Nevertheless, the assets are processed by complex parsers: Virgil is converted using FontTools, and the fonts are later consumed by the native-backed resvg renderer. A compromised upstream release, CDN, redir ...[truncated 1465 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/setup.sh:43
Finding

Predictable Shared Temporary Paths Permit Local File and Extraction Interference

Content
View full analysis
/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 ``` ### Technical Analysis The script uses the fixed names `/tmp/cascadia.zip` and `/tmp/cascadia_extract` in a system-wide temporary directory. Predictable paths are vulnerable to pre-creation, symbolic-link attacks, and race conditions when multiple users or concurrent setup processes share the host. An attacker may pre-create `/tmp/cascadia.zip` as a symbolic link to another path writable by the setup user. `curl -o` can then follow that link and overwrite the linked destination. An attacker may also pre-create `cascadia_extract` with controlled content. Because extraction errors are discarded with `2>/dev/null || true`, the script can continue and use `find` against pre-existing attacker-controlled files even if archive extraction failed. The unqualified relative cleanup command is less robust than cleaning a private, securely created directory. Risk is especially significant if an administrator runs the setup script, although the script itself does not invoke `sudo` or otherwise elevate privileges. ### Attack Path Potential local overwrite path: 1. A local attacker predicts that another user will run the setup script. 2. Before setup begins, the attacker creates `/tmp/cascadia.zip` as a symbolic link to a target file tha ...[truncated 1377 chars]
Remediation
View remediation
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
94% confidence
Finding

The lockfile pins form-data 4.0.5, and the reported advisory describes CRLF injection through unescaped multipart field names. In this package-lock context that is a real supply-chain exposure if the application or a transitive dependency constructs multipart requests using attacker-controlled field names, though the lockfile alone does not prove the vulnerable code path is reachable in this diagram-rendering skill.

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
92% confidence
Finding

The lockfile includes ws 8.19.0, which is flagged for memory disclosure and memory-exhaustion denial of service in 2026 advisories. This is a real dependency risk, but in the skill context it appears transitive via jsdom and may be less directly exposed unless the skill processes untrusted WebSocket traffic or enables code paths that instantiate ws.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding

The skill invokes a shell command (node <skill_dir>/scripts/render.js ...) but does not declare any permissions or allowed-tools scope. This creates an authorization gap where the runtime may permit broader tool usage than intended, and the lack of explicit scoping makes it harder to reason about or constrain what the skill can execute.

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

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
90% confidence
Finding

The workflow instructs the agent to write user-derived content to /tmp, render it, and send the resulting PNG, but it does not disclose this file creation or attachment behavior to the user. While this is not inherently malicious, it can cause unintended handling of sensitive data and reduces transparency about where user content is stored and how artifacts are transmitted.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
95% confidence
Finding

The dependency uses a caret version range, which permits automatic installation of newer compatible releases rather than a fully fixed version. This increases supply-chain risk because future upstream releases could introduce vulnerable or malicious code without an explicit review, though the package.json alone does not indicate active exploitation.

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
95% confidence
Finding

The jsdom dependency is specified with a caret range, allowing semver-compatible updates to be pulled in automatically. Because this skill processes diagram-related content and includes DOM emulation libraries, uncontrolled dependency drift can widen the attack surface if an upstream release contains a compromise or newly introduced flaw.

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
95% confidence
Finding

The roughjs dependency is not fully pinned and may resolve to newer minor or patch versions over time. This is a classic software supply-chain hygiene issue: it is not immediately exploitable by itself, but it reduces build reproducibility and increases exposure to unintended upstream changes.

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"
  }
}