guard-gh-release.py reads a gh api call with Python's shlex, which does not know bash's ANSI-C quoting $'…'. In such a string \' is an escaped quote, but shlex reads it as the closing quote, so the quoting after it is shifted and a real -X DELETE can end up inside a quote that bash never opens.
Found by the sixth review of #155 at 79db5bf, and present at c4427df as well:
gh api repos/o/r/releases/1 -H $'a\'' -X DELETE -H \' -> exit 0 (allowed)
bash 5.2.21 passes [-H] [a'] [-X] [DELETE] [-H] ['] to gh, which is a DELETE on a release. The command contains no #, so #155's second, uncut reading does not help.
It is unverified whether an agent would write this form. It is here so that the gap is recorded; the fix for it is not large.
What closing it needs
- Before
shlex.split, rewrite each $'…' span into a plain single-quoted word with its escapes resolved. Alternatively, treat any $' in a gh api call that names a release path as unparseable, which blocks.
- A test with the command above.
Related: #157 (bash -c, heredoc to a shell) and #158 (escaped, quoted or path-qualified command name).
Assisted by claude-code:claude-opus-5-5 — Session
guard-gh-release.pyreads agh apicall with Python'sshlex, which does not know bash's ANSI-C quoting$'…'. In such a string\'is an escaped quote, butshlexreads it as the closing quote, so the quoting after it is shifted and a real-X DELETEcan end up inside a quote that bash never opens.Found by the sixth review of #155 at
79db5bf, and present atc4427dfas well:bash 5.2.21 passes
[-H] [a'] [-X] [DELETE] [-H] [']to gh, which is a DELETE on a release. The command contains no#, so #155's second, uncut reading does not help.It is unverified whether an agent would write this form. It is here so that the gap is recorded; the fix for it is not large.
What closing it needs
shlex.split, rewrite each$'…'span into a plain single-quoted word with its escapes resolved. Alternatively, treat any$'in agh apicall that names a release path as unparseable, which blocks.Related: #157 (
bash -c, heredoc to a shell) and #158 (escaped, quoted or path-qualified command name).Assisted by claude-code:claude-opus-5-5 — Session