A local-first shell firewall that inspects commands before they execute.
Stops Unicode homoglyph attacks, hidden characters, ANSI injection, and dangerous curl | bash patterns.
Trust nothing after paste.
Modern dev workflows are built on:
- copying commands from GitHub READMEs
- pasting from ChatGPT
- one-liners like
curl | bash
This is convenient — and extremely dangerous.
Attackers exploit:
- Unicode homoglyphs (Latin
avs Cyrillicа) - Zero-width characters that hide real payloads
- ANSI escape sequences that lie in your terminal
- Hidden pipes & subshells
- Dotfile overwrites for persistence
- Blind remote execution
PreExec makes your shell paranoid by default.
PreExec hooks into your shell and inspects every command before execution.
It can:
- block execution
- warn with explanation
- require confirmation
- or silently allow
All locally. No telemetry. No network calls.
- Detects mixed-script tokens (Latin/Cyrillic/Greek)
- Flags homoglyph characters
- Catches zero-width & invisible Unicode
- BiDi control characters (RLO/LRO/FSI/PDI)
- ANSI escape sequence injection
- OSC clipboard abuse
- Cursor movement & output rewriting
- Prompt spoofing
curl|bash,wget|sh,eval $(...)- Subshells:
$(), backticks - Hidden heredocs / here-strings
- Silent redirects (
>/dev/null 2>&1) - Process substitution
<( )
- Writes to:
~/.bashrc~/.zshrc~/.profile~/.ssh/*
- Alias / function hijacking
- PATH poisoning
- Cron / systemd / launch agents
- SSH key reads
- Token patterns
- Suspicious POST requests
- Shortener domains
brew install preexecgo install github.com/makalin/preexec/cmd/preexec@latestFrom source:
git clone https://github.com/makalin/preexec.git && cd preexec && go build -o preexec ./cmd/preexeccargo install preexecpreexec check -- "curl -sSL https://instаll.example | bash"preexec check --clipboardpreexec check --json -- "curl example.com | sh"preexec scan ./install.sh
preexec scan ./README.md --extractpreexec history --shell zsh --last 1000echo "curl https://instаll.example" | preexec rewrite --normalizepreexec diff "curl example.com" "curl instаll.example"preexec pre-commit
# or with paths:
preexec pre-commit ./scripts/install.sh# ~/.zshrc
eval "$(preexec hook zsh)"# ~/.bashrc
eval "$(preexec hook bash)"# ~/.config/fish/config.fish
preexec hook fish | source# Add to $PROFILE
preexec hook powershellBehavior:
- BLOCK → command not executed
- WARN → explain + require confirmation
- PASS → normal execution
BLOCK unicode-homoglyph
token: instаll.example
issue: Cyrillic 'а' (U+0430) used instead of Latin 'a' (U+0061)
position: 18
suggestion: use ASCII-only domain
WARN ansi-escape
issue: ESC (U+001B) control sequence detected
suggestion: paste as plain text
WARN pipe-to-shell
pattern: curl ... | bash
suggestion: download, inspect, then execute
preexec explain pipe-to-shell
# pipe-to-shell: Flags curl|bash, wget|sh and similar patterns; suggests download-then-inspect.| Command | Description |
|---|---|
preexec check [--json] [--clipboard] -- <command> |
Inspect a single command (or clipboard) |
preexec scan [--extract] <path> |
Scan files or directories; --extract = code blocks only from Markdown |
preexec history [--shell zsh|bash] [--last N] |
Scan shell history |
preexec show --codepoints [--] [string] |
Reveal hidden Unicode (codepoints) |
preexec show --urls [--] [string] |
Extract URLs and show IDN (non-ASCII host) hints |
preexec hook zsh|bash|fish|powershell |
Print shell hook script |
preexec rules list |
List all rules |
preexec rules test <rule> [command] |
Test one rule on a command |
preexec explain [rule...] |
Describe rule(s); no args = list all descriptions |
preexec diff <cmd1> <cmd2> |
Compare two commands (or two lines from stdin) |
preexec rewrite [--normalize] [--] <command> |
Safe-rewrite: strip ANSI, zero-width; --normalize = homoglyphs → ASCII |
preexec pre-commit [path...] |
Scan staged files (git) or given paths; for use in pre-commit hooks |
preexec config init |
Create default config at ~/.config/preexec/config.toml |
| Code | Meaning |
|---|---|
| 0 | PASS |
| 10 | WARN |
| 20 | BLOCK |
| 2 | ERROR |
~/.config/preexec/config.toml
mode = "warn"
confirm_on_warn = true
ascii_only_domains = true
[allow]
domains = ["github.com", "raw.githubusercontent.com"]
[deny]
domains = ["bit.ly", "tinyurl.com"]
[rules]
unicode_homoglyph = true
zero_width = true
bidi_controls = true
ansi_escape = true
pipe_to_shell = true
dotfile_write = true
persistence_patterns = true
shortener_domains = true
subshell_command = trueRule names for preexec explain and rules test: unicode-homoglyph, zero-width, bidi-controls, ansi-escape, pipe-to-shell, dotfile-write, persistence-patterns, shortener-domains, subshell-command.
PreExec assumes:
- input is hostile
- terminals lie
- users don’t read
- copy/paste is the new attack vector
It prefers false positives over silent compromise.
- Personal dev machines
- CI pipelines (lint install scripts)
- Security teams auditing onboarding docs
- Open source maintainers checking README snippets
- AI-generated shell commands
- IDN / URL hints (
show --urls) - Fish & PowerShell hooks
- Pre-commit integration (
pre-commit) - Safe-rewrite mode (
rewrite [--normalize]) - Clipboard watcher daemon
- TUI interactive inspector
- Full Punycode decode in output
PRs welcome:
- new rules
- real-world malicious samples
- better Unicode confusable mapping
- platform hooks
MIT
Mehmet Turgay Akalın — Full Stack Developer
GitHub: https://github.com/makalin
You are always one paste away from compromise. PreExec makes sure that paste isn’t fatal.