Back to skill

Security audit

Drawio Skill

Security checks across malware telemetry and agentic risk

Overview

This appears to be a legitimate draw.io diagramming skill, but it needs Review because some live-infrastructure and preset-management flows are under-scoped for the sensitive data and file operations they can touch.

Install only if you are comfortable with a diagramming skill that can read project files, run local CLI tools, render local diagrams, and parse live Terraform/Docker/Kubernetes outputs. Avoid using the live-cluster recipe with Secrets unless you intentionally want secret names and relationships included; prefer sanitized snapshots or omit secrets for shareable diagrams. Use simple preset names, confirm exact file paths before delete/rename operations, and isolate rendering when processing untrusted repositories or pull requests.

SkillSpector

By NVIDIA
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Output HandlingUnvalidated Output Injection, Cross-Context Output, Unbounded Output
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Rogue AgentSelf-Modification, Session Persistence
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
Findings (14)

subprocess module call

Medium
Category
Dangerous Code Execution
Content
def export_png(src_drawio, out_png):
    """CLI-export page 1 of src_drawio to out_png. True on success."""
    r = subprocess.run(["drawio", "-x", "-f", "png", "--page-index", "1", "-o", out_png, src_drawio],
                       capture_output=True)
    return r.returncode == 0 and os.path.exists(out_png)
Confidence
89% confidence
Finding
r = subprocess.run(["drawio", "-x", "-f", "png", "--page-index", "1", "-o", out_png, src_drawio], capture_output=True)

subprocess module call

Medium
Category
Dangerous Code Execution
Content
def build_frame(importer, importer_args, work_path, direction, tmp):
    """Importer -> autolayout -> PNG for one commit. Returns (png_bytes, n, e) or None."""
    graph_json = os.path.join(tmp, "graph.json")
    imp = subprocess.run(
        [sys.executable, os.path.join(HERE, importer + ".py"), work_path,
         "-o", graph_json, *importer_args],
        capture_output=True)
Confidence
82% confidence
Finding
imp = subprocess.run( [sys.executable, os.path.join(HERE, importer + ".py"), work_path, "-o", graph_json, *importer_args], capture_output=True)

subprocess module call

Medium
Category
Dangerous Code Execution
Content
if lay.returncode != 0 or not os.path.exists(drawio):
        return None
    png = os.path.join(tmp, "frame.png")
    exp = subprocess.run(["drawio", "-x", "-f", "png", "--width", "1600",
                          "-o", png, drawio], capture_output=True)
    if exp.returncode != 0 or not os.path.exists(png):
        return None
Confidence
88% confidence
Finding
exp = subprocess.run(["drawio", "-x", "-f", "png", "--width", "1600", "-o", png, drawio], capture_output=True)

Lp3

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding
The skill clearly instructs the agent to read and write local files, invoke shell commands, and in some cases generate browser URLs or fetch remote brand assets, yet it declares no permissions. This creates a governance gap: operators may enable the skill assuming it is low-risk, while it actually has substantial filesystem, process-execution, and limited network-facing capability.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The published description frames the skill as diagram generation/export, but the body supports much broader behaviors: parsing code and infrastructure artifacts, inspecting live environments, diffing existing diagrams, generating shareable HTML/URLs, processing git history, and using remote resources for icons. This mismatch can mislead users and policy engines, causing the skill to be invoked in contexts where sensitive repositories, deployment state, or internal architecture data are exposed unexpectedly.

Vague Triggers

Medium
Confidence
87% confidence
Finding
The trigger scope is intentionally expansive and proactive, including vague conditions like explaining systems with 3+ components or complex relationships. That increases the chance of unintended invocation, causing the agent to read project files, manifests, code, or diagrams and possibly execute local tooling when the user only wanted a textual explanation.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The Kubernetes example explicitly includes `secret` objects in a live-cluster export pipeline without warning the user that secret names, relationships, and potentially sensitive configuration context may be embedded in the generated graph or downstream exported artifacts. In this skill context, users are specifically being guided to diagram live infrastructure, which increases the chance of exposing production-sensitive metadata in files that may be shared broadly for documentation or troubleshooting.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The instructions direct the agent to write a temporary .drawio file and a rendered PNG, including saving the PNG into the user's current working directory, without requiring explicit prior user consent or a clear warning. Unannounced filesystem writes can surprise users, overwrite existing files with predictable names, and leave artifacts containing potentially sensitive diagram content.

Missing User Warnings

Medium
Confidence
97% confidence
Finding
This approval step persists a learned style JSON under the user's home directory, creating lasting state outside the current task without an upfront warning about storage location and retention. Persistent writes to a dot-directory can silently accumulate user-derived data and may store information inferred from sensitive source diagrams.

Vague Triggers

Medium
Confidence
88% confidence
Finding
The management operations accept natural-language commands like 'delete <name>' and 'remove <name>' without strong scope binding to presets, which can collide with ordinary user intent. In an agent setting, ambiguous deletion verbs can cause unintended destructive file operations against user preset files if the model misresolves what object the user meant to remove.

Unvalidated Output Injection

High
Category
Output Handling
Content
def export_png(src_drawio, out_png):
    """CLI-export page 1 of src_drawio to out_png. True on success."""
    r = subprocess.run(["drawio", "-x", "-f", "png", "--page-index", "1", "-o", out_png, src_drawio],
                       capture_output=True)
    return r.returncode == 0 and os.path.exists(out_png)
Confidence
91% confidence
Finding
subprocess.run(["drawio", "-x", "-f", "png", "--page-index", "1", "-o", out_png, src_drawio], capture_output

Session Persistence

Medium
Category
Rogue Agent
Content
### Rendering the sample

1. Write the filled XML to `/tmp/drawio-preset-<name>.drawio`.
2. Run the same `drawio -x -f png -e -s 2 -o <preset-name>-sample.png <tmp>.drawio` command the main workflow uses (substitute the binary name you resolved in SKILL.md Step 1 if it isn't `drawio`).
3. Repair the IEND chunk: `python3 <this-skill-dir>/scripts/repair_png.py <preset-name>-sample.png` — the `-e` flag truncates the PNG the same way the main workflow's step 7 does, so the sample needs the same fix to be readable.
4. Save the PNG as `./preset-<name>-sample.png` (the user's working directory).
Confidence
83% confidence
Finding
Write the filled XML to `/tmp/drawio-preset-<name>.drawio`. 2. Run the same `drawio -x -f png -e -s 2 -o <preset-name>-sample.png <tmp>.drawio` command the main workflow uses (substitute the binary na

Session Persistence

Medium
Category
Rogue Agent
Content
1. **Load the extraction reference.** Read `references/style-extraction.md` into context.
2. **Extract** following the XML path or image path procedure in the reference.
3. **Normalize and build candidate.** Convert the user-provided preset name to lowercase. Use this normalized name for ALL file paths in this flow. Build the candidate preset JSON and write it to `/tmp/drawio-preset-<name>.json` (where `<name>` is the already-normalized name). Do **not** save to `~/.drawio-skill/styles/<name>.json` yet.
4. **Render a sample** using the sample-diagram skeleton in `references/style-extraction.md`, parameterized by the candidate preset. Export PNG to `./preset-<name>-sample.png` using the same `drawio -x -f png -e -s 2 -o ./preset-<name>-sample.png /tmp/drawio-preset-<name>.drawio` command the main workflow uses, then run `repair_png.py` on it (see the Rendering the sample steps in `style-extraction.md`).
5. **Show the user:**
   - Preset summary table (palette hex values, shapes per role, font, edge style, extras).
Confidence
79% confidence
Finding
write it to `/tmp/drawio-preset-<name>.json` (where `<name>` is the already-normalized name). Do **not** save to `~/.drawio-skill/styles/<name>.json` yet. 4. **Render a sample** using the sample-diagr

Tool Parameter Abuse

High
Category
Tool Misuse
Content
| "show my `<name>` style", "what's in `<name>`" | Print the preset JSON (pretty-printed) + a one-line summary (source, confidence, is-default). |
| "make `<name>` the default", "set `<name>` as default" | If `<name>` is a user preset: set `default: true` on it; clear `default` on any other user preset that had it; save both files. If `<name>` is a built-in: copy `<this-skill-dir>/styles/built-in/<name>.json` → `~/.drawio-skill/styles/<name>.json` first, then set `default: true` on the copy. Never mutate the shipped built-in. |
| "remove default", "unset default" | Clear `default: true` from whichever user preset has it. |
| "delete `<name>`", "remove `<name>`" | Confirm first. Then `rm ~/.drawio-skill/styles/<name>.json`. Refuse to delete files under `<this-skill-dir>/styles/built-in/` — suggest shadowing with a user preset of the same name. |
| "rename `<a>` to `<b>`" | `mv ~/.drawio-skill/styles/<a>.json ~/.drawio-skill/styles/<b>.json`, then update the `name` field inside. Fails if `<a>` is a built-in (offer to copy-then-rename instead). |
| "learn my style from `<path>` as `<name>`" | Dispatch to the Learn flow above. |
Confidence
95% confidence
Finding
rm ~/.drawio-skill/styles/

VirusTotal

65/65 vendors flagged this skill as clean.

View on VirusTotal

Static analysis

Detected: suspicious.dynamic_code_execution

Dynamic code execution detected.

Critical
Code
suspicious.dynamic_code_execution
Location
scripts/autolayout.py:353

Dynamic code execution detected.

Critical
Code
suspicious.dynamic_code_execution
Location
scripts/c4.py:92

Dynamic code execution detected.

Critical
Code
suspicious.dynamic_code_execution
Location
scripts/tfimports.py:119

Dynamic code execution detected.

Critical
Code
suspicious.dynamic_code_execution
Location
scripts/tfstate.py:33