You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
install.sh's "update an existing zi install" path runs git clean -d -f -f and git reset --hard HEAD against ${ZI_HOME}/${ZI_BIN_DIR_NAME} after only checking that a .git directory exists there — it never verifies the directory is actually zi's clone. Since ZI_HOME and ZI_BIN_DIR_NAME are both read from the environment with no validation, a misconfigured environment can point this at an unrelated git repository and destroy uncommitted/untracked work in it.
This script is distributed as a curl | sh one-liner and run by end users, many of whom will have ZI_HOME/ZI_BIN_DIR_NAME unset (safe defaults apply) — but for anyone who does have them set, there is no guard rail:
ZI_BIN_DIR_NAME defaults to bin and ZI_HOME defaults to ${XDG_DATA_HOME:-$HOME/.local/share}/zi, but both are honored verbatim from the environment if already set (public/sh/install.sh:115-121).
The only precondition for the destructive branch is test -d ".../.git" — true for any git repository, not specifically zi's.
A plausible failure scenario: a user has ZI_HOME set to $HOME (e.g. a copy-pasted env var from an unrelated guide, or XDG tooling misconfiguration) and keeps ~/bin as a personal git-tracked scripts directory — a common setup. Running the installer's update path would then:
git clean -d -f -f — wipe all untracked files in ~/bin, ignoring excludes.
git reset --hard HEAD — discard any uncommitted edits.
git pull -q origin "${BOPT}" — pull from whatever origin/${BOPT} that unrelated repo has configured.
All three run before the script has done anything to confirm it's actually looking at zi's clone.
Suggested fix
Verify the target is actually zi's clone before touching it, e.g.:
or check for a zi-specific sentinel (e.g. zi.zsh) alongside .git, and fail loudly with a clear error instead of silently operating on an unexpected directory.
Related (not filed separately, noted here for context)
Found in the same review pass, lower severity, not opening separate issues unless wanted:
public/sh/install.sh:110 — the -b/BOPT value is interpolated unescaped into a sed replacement expression; a value containing | or a trailing \ breaks the substitution or corrupts the generated init.zsh.
public/sh/install.sh:101-102,244-245 and public/sh/sync-init.sh's _legacy_url — fallback URLs under main/lib/... are dead now that lib/ was renamed to public/ in CI Workflow Improvements and Repository Cleanup #174; fail safely today but are worth pruning once external-caller blast radius is confirmed.
Summary
install.sh's "update an existing zi install" path runsgit clean -d -f -fandgit reset --hard HEADagainst${ZI_HOME}/${ZI_BIN_DIR_NAME}after only checking that a.gitdirectory exists there — it never verifies the directory is actually zi's clone. SinceZI_HOMEandZI_BIN_DIR_NAMEare both read from the environment with no validation, a misconfigured environment can point this at an unrelated git repository and destroy uncommitted/untracked work in it.Where
public/sh/install.sh:149-154Why this matters
This script is distributed as a
curl | shone-liner and run by end users, many of whom will haveZI_HOME/ZI_BIN_DIR_NAMEunset (safe defaults apply) — but for anyone who does have them set, there is no guard rail:ZI_BIN_DIR_NAMEdefaults tobinandZI_HOMEdefaults to${XDG_DATA_HOME:-$HOME/.local/share}/zi, but both are honored verbatim from the environment if already set (public/sh/install.sh:115-121).The only precondition for the destructive branch is
test -d ".../.git"— true for any git repository, not specifically zi's.A plausible failure scenario: a user has
ZI_HOMEset to$HOME(e.g. a copy-pasted env var from an unrelated guide, or XDG tooling misconfiguration) and keeps~/binas a personal git-tracked scripts directory — a common setup. Running the installer's update path would then:git clean -d -f -f— wipe all untracked files in~/bin, ignoring excludes.git reset --hard HEAD— discard any uncommitted edits.git pull -q origin "${BOPT}"— pull from whateverorigin/${BOPT}that unrelated repo has configured.All three run before the script has done anything to confirm it's actually looking at zi's clone.
Suggested fix
Verify the target is actually zi's clone before touching it, e.g.:
or check for a zi-specific sentinel (e.g.
zi.zsh) alongside.git, and fail loudly with a clear error instead of silently operating on an unexpected directory.Related (not filed separately, noted here for context)
Found in the same review pass, lower severity, not opening separate issues unless wanted:
public/sh/install.sh:110— the-b/BOPTvalue is interpolated unescaped into asedreplacement expression; a value containing|or a trailing\breaks the substitution or corrupts the generatedinit.zsh.public/sh/install.sh:101-102,244-245andpublic/sh/sync-init.sh's_legacy_url— fallback URLs undermain/lib/...are dead now thatlib/was renamed topublic/in CI Workflow Improvements and Repository Cleanup #174; fail safely today but are worth pruning once external-caller blast radius is confirmed.