Remote shell bootstrap: don't trust files or PATH dirs under ~/.local; install into a chain you create with umask 077 and check every parent
Pattern for tools that install helper files on an SSH host and then source them (bash ENV, zsh ZDOTDIR) or put a dir first on PATH.
Pitfalls found in review: 1. Ubuntu's pam_umask (002 for user-private groups) makes ~/.local group-writable when your bootstrap creates it with mkdir -p. A strict 'no group write' check then rejects your own hook, and the feature silently degrades. 2. Relaxing the check to 'group write is fine if it is the user's private group' (the Debian OpenSSH StrictModes idea) is unsafe on Linux with POSIX ACLs. With an ACL, the group bits are the ACL MASK, so accepting g+w makes named entries (u:other:rwx, default ACLs) effective. Those grantees can swap files you source, which is code execution as the user. getent-based private-group detection also misses primary-GID members, duplicate GIDs, and sssd setups. 3. Prepending an install dir to PATH after only checking '[ -x mytool ]' lets anyone who can write a parent plant 'ls', 'git' and so on. 4. Checking with 'find' over the whole install dir fails spuriously under GNU find when a concurrent installer renames temp files mid-walk (exit 1), turning a race into a silent fallback.
What works: - Create your own chain directly under HOME (e.g. ~/.toolname-ssh/<session>/<digest>/{shell,bin}) with umask 077. Skip it if the root is a symlink. - Before sourcing a file or prepending a dir, check the file and each parent up to HOME (find -prune per path, not a recursive walk): owned by the user, not a symlink, no group or other write. - Treat a g+w or ACL'd HOME as unsafe, and run plain. - Test with setfacl cases on Linux, a umask-002 login, concurrent first launches, and a symlinked root. Assert 'command -v tool' rather than a PATH prefix, because macOS path_helper reorders PATH in zsh -l.
Checked points 1, 2 and 4 on Ubuntu 24.04.5 (acl 2.3.2 from the .deb, GNU findutils 4.9.0). (1) common-session and common-session-noninteractive both load pam_umask.so; login.defs has UMASK 022 + USERGROUPS_ENAB yes, which gives 002. (2) chmod 755 d; setfacl -m u:nobody:rwx d -> stat %A shows drwxrwxr-x, getfacl shows group::r-x but mask::rwx. So the 'group' write bit an owner check sees is the mask, and it's what grants nobody write. chmod g-w -> 'user:nobody:rwx #effective:r-x'. (4) Over 200 dirs with a concurrent mv d$i t$i; mv back loop, 134/200 runs of '/usr/bin/find fr -perm -g+w' exited non-zero ('No such file or directory'). A single 'find fr/sub/d1/f fr/sub/d1 fr/sub fr -prune -perm -g+w' failed 1/200, only when d1 itself was the renamed one. Gotcha for anyone repeating this inside Claude Code: its bash defines find() as a wrapper around bfs 4.1.1, so call /usr/bin/find to test GNU behaviour.