{"slug": "bashka-static-analyzer-for-bash-install-scripts-for-safety-and-convenience", "title": "Bashka – static analyzer for bash install scripts (for safety and convenience)", "summary": "Developer dmtrKovalenko released Bashka, a static analyzer that inspects scripts run through the `curl … | bash` install pattern and scores them against more than 20 checks across four severity levels: 💀 critical, 🚩 red, 🟡 yellow, and ✅ green. Bashka follows forwarded scripts so every layer is analyzed, and flags issues including credential theft, exfiltration, reverse shells, obfuscation, and insecure TLS while rewarding good-citizen signals such as checksum verification and `set -euo pipefail`. The tool is installed via `curl --proto '=https' --tlsv1.2 -fsSL https://bashka.dmtrkovalenko.dev | bash` and is used by appending `bashka` in place of `bash` in an existing install command.", "body_md": "A drop-in safety guard for the `curl … | bash` install pattern. Bashka parses the incoming script, scores it against many different checks, follows forwarded scripts so every layer is analyzed and if it found some unsafety it gives you an easy way to read a script or analyze using your AI agent.\n\nUseage is simple as adding **ka** after the standard installation script\n\n```\ncurl -fsSL https://pyenv.run | bashka\n```\n\n## bashka.mp4\n\nInstall via bash. One last time.\n\n```\ncurl --proto '=https' --tlsv1.2 -fsSL https://bashka.dmtrkovalenko.dev | bash\n```\n\nWe guarantee absolute safety of this script! [Read it yourself](https://raw.githubusercontent.com/dmtrKovalenko/bashka/main/install.sh)\n\nFindings come in four kinds: 💀 **💀** (critically malicious, blocks hard), 🚩 **red** (dangerous),\n🟡 **yellow** (advisory, never changes the verdict), ✅ **green** (good-citizen signal).\n\n| kind | id | what it looks for | \n|---|---|---|\n| 💀 | `exfil_destination` | sends data to webhook.site/Discord/Telegram/ngrok/paste sites, the cloud-metadata IP, or a raw IP (Codecov, Shai-Hulud) | \n| 💀 | `credential_theft` | reads SSH keys, cloud credentials, `.netrc` , the keychain | \n| 💀 | `exfiltration` | environment or secrets sent to the network ( `env \\| curl` ,`curl -d \"$TOKEN\"` ) | \n| 💀 | `reverse_shell` | backdoors: `/dev/tcp` ,`nc -e` ,`socat EXEC` ,`mkfifo` pipe-to-shell | \n| 🟢 | `checksum` | download digest checked: `sha256sum -c` ,`$(shasum …)` compared,`openssl dgst` | \n| 🟢 | `cleanup_artifacts` | `trap … EXIT` , or`rm` of a`mktemp` path | \n| 🟢 | `https_only` | every download URL uses HTTPS | \n| 🟢 | `install_dir` | installs into `/usr/local/bin` or`~/.local/bin` | \n| 🟢 | `privilege_escalation` | (🟢) never escalates; (🟡) uses `sudo` /`doas` . Some scripts genuinly need sudo and you will be prompted for it. | \n| 🟢 | `strict_mode` | `set -euo pipefail` | \n| 🟢 | `tls_hardening` | curl pins HTTPS-only and TLS 1.2+ ( `--proto '=https' --tlsv1.2` ) | \n| 🟢 | `trusted_domains` | HTTPS downloads from GitHub, or the same domain the script was fetched from | \n| 🟢 | `verify` | signature verified: `gpg --verify` ,`cosign` ,`minisign` ,`openssl dgst -verify` | \n| 🔴 | `anti_forensics` | hides tracks: `HISTFILE=/dev/null` ,`history -c` , log truncation,`journalctl --vacuum` , killing EDR/audit agents | \n| 🔴 | `banned_commands` | `rm -rf /` ,`dd of=/dev/*` ,`mkfs` , fork bomb,`chmod -R 777` | \n| 🔴 | `domain_refs` | plaintext HTTP, raw-IP hosts, URL shorteners | \n| 🔴 | `env_hijack` | hijacks auto-run hooks: `BASH_ENV` /`PROMPT_COMMAND` /`LD_PRELOAD` /`NODE_OPTIONS --require` ,`sitecustomize.py` | \n| 🔴 | `git_hooks` | repoints git execution: `core.hooksPath` ,`core.fsmonitor` ,`alias.x '!cmd'` , or writes into`.git/hooks` | \n| 🔴 | `insecure_tls` | certificate checks off: `curl -k` ,`--no-check-certificate` ,`GIT_SSL_NO_VERIFY` | \n| 🔴 | `install_name` | downloads files but nothing names the software (no URL hint, product variable, GitHub repo or bin target); yellow when the script takes the project from its arguments | \n| 🔴 | `install_target` | downloads files but never names where they are installed | \n| 🔴 | `macos_bypass` | strips Gatekeeper quarantine ( `xattr … com.apple.quarantine` ), tampers with TCC;`osascript … hidden answer` password phish is 💀 | \n| 🔴 | `max_commands` | more commands than `limit` | \n| 🔴 | `not_a_script` | the body is an HTTP redirect stub, an HTML/JSON page, or has no recognizable command (fetch with `curl -fsSL` ) | \n| 🔴 | `obfuscation` | `eval` of opaque code (`eval \"$CMD\"` ,`eval $(…)` ), decode-then-execute pipelines | \n| 🔴 | `package_managers` | pulls code from npm/npx/pip/cargo/go/gem/brew/docker or editor extensions; 🔴 on URL/git/mutable ref/foreign registry/ `--privileged` , yellow on a plain global install | \n| 🔴 | `path_suspicious` | `PATH` gains a temp, relative or world-writable directory | \n| 🔴 | `remote_exec` | fetch->exec forward sinks (drives chain following) | \n| 🔴 | `scheduled_tasks` | schedules code via cron, `at` , systemd timers, autostart or rc.local | \n| 🔴 | `security_tampering` | disables firewall/SELinux/AppArmor/Gatekeeper/SIP | \n| 🔴 | `self_extract` | reads its own bytes ( `$0` ) with sed/tail/dd/base64 and pipes the result into a shell | \n| 🔴 | `sensitive_write` | writes to shell rc files, `~/.ssh` ,`/etc/sudoers` , crontab | \n| 🔴 | `staged_installer` | downloads a program and runs it to do the install; the second stage is opaque to review | \n| 🔴 | `unicode_tricks` | invisible, bidi, or homoglyph characters in a command name or URL, or a punycode host | \n| 🔴 | `unsafe_rm` | `rm -rf \"$VAR/\"` where the variable may be empty and there is no guard or`set -u` | \n| 🔴 | `upload_exfil` | uploads files ( `curl -T` ,`-F @file` ,`--data @file` ), copies out via scp/rsync, or DNS-exfil via`dig $(…)` | \n| 🟡 | `checksum` | green if checks a digest, yellow if downloads are unverified | \n| 🟡 | `many_downloads` | fetches from more than `limit` distinct URLs (default 2) | \n| 🟡 | `mutable_refs` | downloads from `master` /`main` /`HEAD` /`latest` instead of a pinned version | \n| 🟡 | `package_repos` | adds apt/yum/zypper repositories or signing keys | \n| 🟡 | `persistence` | installs systemd/launchd services or init scripts | \n| 🟡 | `telemetry` | sends data out ( `POST` /`--data` , or analytics URLs) | \n\nSome of the additional commands\n\n```\nbashka list [--long]                 # table of software installed through bashka\nbashka info <name>                   # everything recorded about one package\nbashka update <name>                 # re-fetch the recorded installer and run it again\nbashka remove <name> [--dry-run]     # delete every recorded binary and created directory, forget the package\nbashka flags                         # list every registered flag with its options\nbashka config init                   # print default configuration\n```\n\nbashka writes a lock file of everything it installed to `$XDG_DATA_HOME/bashka/installed.toml`\n(`~/.local/share/bashka/installed.toml`; `BASHKA_LOCKFILE` overrides the path). er that exits\nnon-zero leaves no record.\n\n`bashka list` prints package list, inspired by `pacman -Q`:\n\n```\nNAME  VERSION        INSTALLED   UPDATED     FILES          SOURCE\nmise  2026..1       2026-0-15  -           1              https://mise.run\nuv    0.9.2          2026-09-01  2026-09-15  2              https://astral.sh/uv/install.sh\ndemo  0.0.0-unknown  2026-09-15  -           2 (1 missing)  <stdin>\n```\n\n`bashka info <name>` (or `bashka list --long` for all) shows the full record in `pacman -Qi` style, with each binary and created directory on its own line and missing paths marked.\n\n`bashka remove <name>` deletes the recorded binaries and created directories, then drops the entry. There is a possibility that bashka couldn't track where the file is installed (which is a red flag) but after your approval it will still be tracked but during the uninstall the binary files wouldn't be deleted.\n\n`bashka update <name>` fetches the recorded URL again and runs it through the full review with the recorded options and shell arguments.\n\n`~/.config/bashka/config.toml` is deep-merged over the embedded [`data/defaults.toml`](https://github.com/dmtrKovalenko/bashka/blob/main/data/defaults.toml).\n\n```\n[flags]\nstrict_mode     = false\nmax_commands    = { limit = 200 }\ntrusted_domains = { additional_domains = [\"get.example.com\"] }\n\n[interaction]\non_red        = \"ask\"      # ask | abort | proceed\nfollow_remote = \"always\"   # ask | always | never\ndescend       = \"hybrid\"   # hybrid | fetch_ahead | shim\nmax_depth     = 5\n\n[ui]\nicons      = \"emoji\"       # emoji | nerd | ascii\nanimations = true          # spinners while fetching forwards and on hand-off\n```\n\n- Bash is Turing-complete. Even though we try to detect obfuscation it can evade static analysis.\n- The shim intercepts `bash` /`sh` resolved through`PATH` but`/bin/bash` bypasses it.\n- `trusted_domains` keeps a deliberately strict allowlist (GitHub only). Everything else is\ntrusted only when it matches the domain the script was fetched from. Trusted domains can be modified via cofig using`trusted_domains = { additional_domains = [\"get.acme.io\"] }` .\n- A server may serve different bytes at run time than at fetch-ahead.\n- The install registry only sees executables that land in the watched directories. If bashka couldn't detect where the binaries went, but you still approved it - we won't be able to manage and uninstall the binary\n\nMIT and opensource. Support my work at [https://github.com/sponsors/dmtrKovalenko](https://github.com/sponsors/dmtrKovalenko)", "url": "https://wpnews.pro/news/bashka-static-analyzer-for-bash-install-scripts-for-safety-and-convenience", "canonical_source": "https://github.com/dmtrKovalenko/bashka", "published_at": "2026-09-16 05:11:41+00:00", "updated_at": "2026-09-16 05:37:54.185397+00:00", "lang": "en", "topics": ["developer-tools", "ai-tools"], "entities": ["Bashka", "dmtrKovalenko", "GitHub", "curl", "bash"], "alternates": {"html": "https://wpnews.pro/news/bashka-static-analyzer-for-bash-install-scripts-for-safety-and-convenience", "markdown": "https://wpnews.pro/news/bashka-static-analyzer-for-bash-install-scripts-for-safety-and-convenience.md", "text": "https://wpnews.pro/news/bashka-static-analyzer-for-bash-install-scripts-for-safety-and-convenience.txt", "jsonld": "https://wpnews.pro/news/bashka-static-analyzer-for-bash-install-scripts-for-safety-and-convenience.jsonld"}}