Security analysis of the dotfiles distribution. Covers trust boundaries, threat actors, attack surfaces, and mitigations.
The primary trust boundary is the local machine. Dotfiles are deployed to and executed on the user’s own system. All configuration files, scripts, and templates run with the user’s privileges.
┌─────────────────────────────────────────────────┐
│ Local Machine (trusted zone) │
│ ┌─────────────────┐ ┌──────────────────────┐ │
│ │ ~/.dotfiles/ │ │ ~/.config/ (deployed)│ │
│ │ (source repo) │──│ Shell configs, nvim │ │
│ └─────────────────┘ └──────────────────────┘ │
│ │
│ ┌─────────────────┐ ┌──────────────────────┐ │
│ │ ~/.local/bin/ │ │ ~/.cache/shell/ │ │
│ │ (user scripts) │ │ (cached eval output) │ │
│ └─────────────────┘ └──────────────────────┘ │
└─────────────────────────────────────────────────┘
│
─────┼──────── Trust boundary ────────
│
┌─────────────────────────────────────────────────┐
│ External (untrusted zone) │
│ GitHub (repo hosting), Homebrew, Nix, │
│ Zinit plugins, Neovim plugins, npm, pip │
└─────────────────────────────────────────────────┘
| Actor | Motivation | Capability |
|---|---|---|
| Supply chain attacker | Inject malicious code via dependency | Compromise upstream packages (Homebrew, npm, Zinit plugins) |
| Secrets harvester | Steal API keys, SSH keys, tokens | Scrape git history, shell history, env vars |
| Privilege escalation | Gain root access | Exploit sudo aliases, writable scripts in PATH |
| Network attacker | Intercept credentials | MITM on install scripts, curl-pipe-bash |
Risk: Malicious code in sourced files executes with user privileges on every shell start.
Mitigations:
shellcheck --severity=errorset -euo pipefail enforced in all operational scriptseval usage hardened with process substitution (. /dev/stdin)DOTFILES_ULTRA_FAST=1 bypasses all non-core sourcingRisk: API keys, tokens, and SSH keys leaked via git history, shell history, or environment.
Mitigations:
history_filter excludes sensitive patternsdot secrets set uses secure prompts (no shell history).gitignore excludes key.txt, .env, credential filesRisk: Compromised upstream packages injected into the toolchain.
Mitigations:
ver"0.8.0")lazy-lock.jsoninstall.sh uses HTTPS for all downloadsRisk: Attacker places malicious binary in a PATH directory that shadows legitimate tools.
Mitigations:
~/.local/bin is user-controlled and checked for writabilityprependpath function validates directory existenceRisk: Chezmoi template variables could inject shell code if not properly quoted.
Mitigations:
.chezmoidata.toml (user-controlled)chezmoi apply --dry-run in CIRisk: Stale or tampered cache files in ~/.cache/shell/ sourced at startup.
Mitigations:
_cached_eval validates cache against binary mtime$XDG_CACHE_HOME (user-controlled)rm -rf ~/.cache/shell/| Risk | Likelihood | Impact | Status |
|---|---|---|---|
| Zinit plugin compromise | Low | High | Mitigated by version pinning |
| Shell history leak | Medium | Medium | Mitigated by Atuin filter |
| Stale Nix closure | Low | Low | Accepted (manual nix flake update) |
| macOS Keychain access | Low | Medium | Accepted (OS-level protection) |
gitleaks detect before every push (pre-commit hook enforced)lazy-lock.json diffs when updating Neovim pluginsDOTFILES_ULTRA_FAST=1 in CI to minimize attack surface