A Literal ~ in Your PATH Hijacks Commands
PATH entries aren't shell-expanded. A quoted tilde becomes a relative directory — and anyone who creates a dir named ~ owns your shell.
There's a good chance your dotfiles contain PATH="~/bin:$PATH". In an interactive shell that line mostly works. In a crontab, a systemd unit, or a Dockerfile, that tilde is not your home directory — it's a one-character directory name, and it's relative. A security researcher's post that hit Hacker News last week lays out why that difference is exploitable, not just inconvenient.
When the tilde stays literal, command lookup searches a directory named ~ in whatever folder you happen to be standing in. Whoever creates that directory owns your commands.
Why this matters
Tilde expansion is a shell parsing step, not a property of paths. It only happens in unquoted words at assignment time. Cron, systemd's Environment=, and Docker's ENV never do it — they take your value literally, forever.
That turns one copied-and-pasted line into two different bugs: a dead entry that quietly hides your binaries, and a live one that runs other people's.
How it works
Shells and execvp resolve commands by walking PATH entries verbatim and trying <entry>/<command>. An entry of ~ builds the relative path ~/ls, which resolves to ./~/ls from your current directory.
Quoting is what bites. PATH=~/bin:$PATH expands the tilde at assignment. PATH="~/bin:$PATH" preserves the literal tilde — and once exported, every child process inherits it.
The attack is boringly simple: a repo or tarball contains a directory named ~ with an executable git inside. You clone, cd in, type git status, and run their code. ~ is a perfectly legal directory name; tar creates it without complaint.
Where this helps
- Cron jobs that mysteriously can't find your scripts — crontab
PATH=lines never tilde-expand. Use absolute paths. - systemd units and container images —
Environment=andENVvalues are literal; a tilde there is dead weight at best. - CI runners that execute PR code — a planted
~directory plus a quoted-tilde PATH in the runner image is pre-merge code execution. - Security audits — any relative PATH entry is a finding. Empty entries (a leading or trailing
:) mean CWD, the same class of bug.
Watch out
The fix isn't "stop quoting." Quote, but expand deliberately: $HOME in shell configs, absolute paths everywhere else.
Even $HOME lies. sudo often keeps your HOME, cron may reset