Is Drawio Skill safe?
- Python shell/command execution
- Python filesystem write/delete
- Python filesystem read
repo is an AI directory analyzed by SkillTotal's deterministic static scanner. The scan found no malicious indicators, though 4 risky constructs are reported for review. It can: filesystem read, filesystem write, network egress and shell execution — capabilities are what the code can do, not a verdict on intent. Risk score 0/100 (low).
repo
Automated static-analysis result. It can contain false positives and false negatives, and is not a claim about the intent of Drawio Skill's authors. Report a false positive.
Behavioral traits
How this component maps to the CSA agentic threat model. Descriptive — it never affects the risk score.
Findings (4)
The component can run operating-system commands or spawn processes.
proc = subprocess.run(
["dot", "-Tplain"], input=dot_src,
capture_output=True, text=True, check=True,
)r = subprocess.run(["drawio", "-x", "-f", "png", "--page-index", "1",
"--width", "2000", "-o", png_path, src], capture_output=True)r = subprocess.run(
[sys.executable, os.path.join(HERE, "autolayout.py"), gpath, "-o", opath],
capture_output=True, text=True,
)proc = subprocess.run(cmd, text=True, capture_output=True)
p = subprocess.run(
[drawio, "--version"], capture_output=True, text=True, timeout=8
)proc = subprocess.run(cmd)
proc = subprocess.run(
[sys.executable, str(HERE / "drawiohtml.py"), args.input, "-o", args.output]
)proc = subprocess.run(
argv, capture_output=True, text=True, timeout=CALL_TIMEOUT_SECONDS
)r = subprocess.run(["drawio", "-x", "-f", "png", "--page-index", str(index),
"-s", str(scale), "-o", out_png, drawio_file],
capture_output=True)r = subprocess.run(["drawio", "-x", "-f", "svg", "--embed-svg-images",
"--page-index", str(index),
"-o", out_svg, drawio_file], capture_output=True)out = subprocess.run(
["tred"], input=dot, capture_output=True, text=True, check=True
).stdoutout = subprocess.run(
["tred"], input=dot, capture_output=True, text=True, check=True
).stdoutr = subprocess.run(
["git", "-C", repo, "diff", "--name-status", f"{base}..{head}", "--", "*.drawio"],
capture_output=True, text=True)r = subprocess.run(["git", "-C", repo, "show", f"{ref}:{path}"], capture_output=True)r = subprocess.run(["drawio", "-x", "-f", "png", "--page-index", "1", "-o", out_png, src_drawio],
capture_output=True)r1 = subprocess.run([sys.executable, os.path.join(HERE, "drawiodiff.py"),
base_drawio, head_drawio, "-o", diff_json], capture_output=True)r2 = subprocess.run([sys.executable, os.path.join(HERE, "autolayout.py"),
diff_json, "-o", diff_drawio], capture_output=True)out = subprocess.run(["tred"], input=dot, capture_output=True,
text=True, check=True).stdoutout = subprocess.run(
["tred"], input=dot, capture_output=True, text=True, check=True
).stdoutr = subprocess.run([sys.executable, autolayout, graph_path],
capture_output=True, text=True)out = subprocess.run(
["tred"], input=dot, capture_output=True, text=True, check=True
).stdoutr = subprocess.run(["drawio", "-x", "-f", "svg", "--embed-svg-images", "-o", out, path],
capture_output=True)out = subprocess.run(["tred"], input=dot, capture_output=True,
text=True, check=True).stdoutp = subprocess.run(["git", "-C", root, *args], capture_output=True)
imp = subprocess.run(
[sys.executable, os.path.join(HERE, importer + ".py"), work_path,
"-o", graph_json, *importer_args],
capture_output=True)Why it matters: Powerful and often legitimate — confirm the commands aren't built from untrusted input.
Fix: Confirm the command and its arguments are fully controlled and not derived from untrusted input; avoid shell=True.
The component reads files from disk.
with open(MANIFEST, encoding="utf-8") as f:
with open(path, encoding="utf-8") as handle:
with open(_PALETTE_FILE, encoding="utf-8") as fh:
with open(args.input, encoding="utf-8") as f:
with open(png_path, "rb") as f:
with open(args.input, encoding="utf-8") as f:
with open(path, encoding="utf-8") as f:
with open(path, encoding="utf-8") as f:
with open(opath, encoding="utf-8") as f:
with open(MANIFEST, encoding="utf-8") as f:
with open(MANIFEST, encoding="utf-8") as f:
with open(path, encoding="utf-8") as fh:
data = json.loads(p.read_text(encoding="utf-8"))
text = p.read_text(encoding="utf-8", errors="ignore")[:10000]
raw = json.loads(graph_path.read_text(encoding="utf-8"))
text = Path(path).read_text(encoding="utf-8")
with open(args.input, encoding="utf-8") as f:
with open(path, "rb") as f:
with open(out, encoding="utf-8") as f:
with open(args[0], "r", encoding="utf-8") as f:
with open(gomod, encoding="utf-8", errors="ignore") as f:
with open(path, encoding="utf-8", errors="ignore") as f:
with open(path, encoding="utf-8", errors="replace") as fh:
with open(path, encoding="utf-8") as f:
with open(path, encoding="utf-8", errors="ignore") as f:
Why it matters: Usually legitimate, but worth confirming it can't be steered into reading sensitive files.
Fix: Confirm which files are read and that paths cannot be influenced by untrusted input to reach sensitive locations.
The component writes or deletes files on disk.
with open(args.output, "w", encoding="utf-8") as handle:
with open(args.output, "w", encoding="utf-8") as f:
with open(f"{os.path.splitext(out)[0]}-frame{k + 1:03d}.png", "wb") as f:with open(out, "w", encoding="utf-8") as f:
with open(args.output, "w", encoding="utf-8") as f:
with open(args.output, "w", encoding="utf-8") as f:
with open(args.output, "w", encoding="utf-8") as f:
with open(gpath, "w", encoding="utf-8") as f:
with open(args.output, "w", encoding="utf-8") as f:
with open(MANIFEST, "w", encoding="utf-8", newline="\n") as f:
with open(path, "w", encoding="utf-8") as fh:
with open(path, "a", encoding="utf-8") as fh:
with open(output_path, "a", encoding="utf-8") as fh:
Path(output).write_text(text, encoding="utf-8")
Path(args.output).write_text(text, encoding="utf-8")
Path(args.output).write_text(
story_html(ir, title=args.title, scenario=scenario), encoding="utf-8"
)Path(args.output).write_text(story_html(ir, title=args.title), encoding="utf-8")
with open(args.output, "w", encoding="utf-8") as f:
with open(args.output, "w", encoding="utf-8") as f:
with open(args.output, "w", encoding="utf-8") as f:
with open(out, "w", encoding="utf-8") as f:
with open(args.output, "w", encoding="utf-8") as f:
with open(args.output, "w", encoding="utf-8") as f:
with open(args.output, "w", encoding="utf-8") as fh:
with open(args.output, "w", encoding="utf-8") as f:
Why it matters: Usually legitimate, but worth confirming the paths can't be controlled by untrusted input.
Fix: Confirm which files are written/deleted and that paths cannot be influenced by untrusted input.
The component makes outbound network requests.
import urllib.request
parsed = urllib.parse.urlparse(url)
return urllib.request.urlopen(safe_url(url), timeout=15).read()
import urllib.request
parsed = urllib.parse.urlparse(url)
req = urllib.request.Request(url, headers=headers)
with urllib.request.urlopen(req, timeout=30) as resp:
hosted = urllib.parse.urlparse(manifest.get("hostedBase", ""))Why it matters: Usually legitimate, but confirm the destinations are expected and no sensitive data leaves.
Fix: Confirm the destination hosts are expected and that no sensitive data is sent off-host.
Check your own component
Run the same evidence-backed scan on any MCP server, agent skill, or package.
Scan your own componentHow we determine this: deterministic static analysis (regex + AST), evidence-anchored, no code execution. Methodology →