What happened?
splitCompoundCommandSegments does not model # comments, so an operator sitting inside a trailing comment is read as real command structure and the line is split where bash does not split it. The permission decision then asks for confirmation on a command an allow rule already covers.
For echo 'a' # comment ; echo B, bash runs a single command — the comment swallows everything from # to the end of the line. The splitter returns two segments, echo 'a' # comment and echo B, so a user who has allowed Bash(echo *) still gets a prompt.
The direction is fail-closed: the extra segment can only add a rule that must pass, never remove one, so no verdict is weakened. It is a usability defect rather than a security one.
Steps to reproduce
Allow only echo in settings.json:
{
"permissions": {
"allow": ["Bash(echo *)"]
}
}
Then have the model run echo 'a' # comment ; echo B. The call stops for confirmation even though bash runs one echo, which the rule covers.
That bash runs one command is plain from bash itself:
$ bash -xc "echo 'a' # comment ; echo B"
+ echo a
a
Measured shapes
split is what splitCompoundCommand returns; bash is what the shell actually runs.
| command |
bash |
split |
echo 'a' # comment ; echo B |
1 |
2 |
echo 'a\' # note: use ; carefully |
1 |
2 |
echo 'a\' # trailing && touch /tmp/x |
1 |
2 |
echo 'a\' # trailing | touch /tmp/x |
1 |
2 |
echo hi # nothing here |
1 |
1 |
git status # don't ; echo B |
1 |
1 |
echo a#b ; echo B |
2 |
2 |
The last two are why this is not a one-line change. git status # don't ; echo B happens to come out right only because the apostrophe in don't opens a quote that masks the ;, and echo a#b is correctly left alone because # is only a comment at the start of a word.
What did you expect to happen?
A line that bash runs as one command should be one segment, so an allow rule that covers that command applies without a prompt.
Client information
Client Information
Measured on main at 8af09af29a's merge base, and against the branch of #11765.
Version: 0.23.3
OS: macOS 26.5.2 (arm64)
Node: v22.22.1
Shell: GNU bash 3.2.57(1)-release
Login information
Not relevant — the defect is in permission rule splitting and does not involve the model provider or authentication.
Anything else we need to know?
Filed as the follow-up agreed in review on #11765, which fixes a different defect in the same scanner: a backslash inside a plain '…' string was read as an escape, which swallowed the closing quote and glued a whole line into one segment.
Two of the rows above predate that PR and two are widened by it, and the distinction matters for whoever picks this up:
So #11765 widens an existing gap rather than creating one, and narrowing its quote handling to avoid these shapes would re-open the bypass it closes, since the two are the same shape. Comment handling has to be added deliberately instead, and needs to cover at least:
# starts a comment only at the start of a word — echo a#b is not a comment.
# inside a quoted string is literal.
- Heredoc bodies, where
# is ordinary text.
- The interaction with the line-continuation handling, since a comment runs to the end of the logical line.
A regression test for each row in the table above would pin the behaviour either way.
中文
发生了什么
splitCompoundCommandSegments 没有建模 # 注释,因此位于行尾注释内部的操作符会被当作真实的命令结构,于是在 bash 并不切分的位置把一行切开。随后的权限判定会对一条授权规则本已覆盖的命令弹出确认。
对 echo 'a' # comment ; echo B 而言,bash 只执行一条命令——注释会吞掉从 # 到行尾的全部内容。切分器却返回两个片段 echo 'a' # comment 和 echo B,于是已经授权了 Bash(echo *) 的用户仍会收到确认提示。
方向是「失效即收紧」的:多出来的片段只会增加一条必须通过的规则,不会减少,因此任何判定都不会被放松。这是一个易用性缺陷,而非安全缺陷。
复现步骤
在 settings.json 中只授权 echo,然后让模型执行 echo 'a' # comment ; echo B。尽管 bash 只执行一条被规则覆盖的 echo,该调用仍会停下来等待确认。
bash 只执行一条命令,从 bash 自身即可看出(见上方英文部分的 console 块)。
实测形态
表格见上方英文部分:split 是 splitCompoundCommand 的返回结果,bash 是 shell 实际执行的命令数。
最后两行说明这不是一处一行就能改好的地方。git status # don't ; echo B 结果正确纯属巧合——don't 里的单引号开启了一个字符串,把那个 ; 挡住了;而 echo a#b 被正确地保持完整,是因为 # 只有位于词首时才是注释。
期望的行为
bash 当作一条命令执行的行,应当是一个片段,这样覆盖该命令的授权规则就能生效而不弹出提示。
客户端信息
见上方英文部分。
登录信息
与本问题无关 —— 该缺陷位于权限规则切分环节,不涉及模型提供方或身份认证。
其他补充信息
这是在 #11765 的评审中约定的后续 issue。那个 PR 修复的是同一个扫描器上的另一个缺陷:普通 '…' 字符串内的反斜杠被当成了转义,从而吞掉闭合引号、把整行粘成一个片段。
上表中有两行早于那个 PR,另有两行是被它扩大的,这个区别对接手的人很重要:
因此 #11765 是扩大了一处既有缺口,而不是制造了一个新缺口;而为了规避这些形态去收窄它的引号处理,会重新打开它所关闭的那个绕过,因为两者是同一类形态。注释处理必须作为一项独立工作有意识地加入,至少需要覆盖:
# 只有在词首才开启注释 —— echo a#b 不是注释。
- 引号字符串内部的
# 是字面字符。
- heredoc 正文,其中
# 是普通文本。
- 与续行处理的相互作用,因为注释延续到逻辑行的末尾。
为上表每一行补一条回归测试,即可把行为双向钉住。
What happened?
splitCompoundCommandSegmentsdoes not model#comments, so an operator sitting inside a trailing comment is read as real command structure and the line is split where bash does not split it. The permission decision then asks for confirmation on a command an allow rule already covers.For
echo 'a' # comment ; echo B, bash runs a single command — the comment swallows everything from#to the end of the line. The splitter returns two segments,echo 'a' # commentandecho B, so a user who has allowedBash(echo *)still gets a prompt.The direction is fail-closed: the extra segment can only add a rule that must pass, never remove one, so no verdict is weakened. It is a usability defect rather than a security one.
Steps to reproduce
Allow only
echoinsettings.json:{ "permissions": { "allow": ["Bash(echo *)"] } }Then have the model run
echo 'a' # comment ; echo B. The call stops for confirmation even though bash runs oneecho, which the rule covers.That bash runs one command is plain from bash itself:
Measured shapes
splitis whatsplitCompoundCommandreturns;bashis what the shell actually runs.echo 'a' # comment ; echo Becho 'a\' # note: use ; carefullyecho 'a\' # trailing && touch /tmp/xecho 'a\' # trailing | touch /tmp/xecho hi # nothing heregit status # don't ; echo Becho a#b ; echo BThe last two are why this is not a one-line change.
git status # don't ; echo Bhappens to come out right only because the apostrophe indon'topens a quote that masks the;, andecho a#bis correctly left alone because#is only a comment at the start of a word.What did you expect to happen?
A line that bash runs as one command should be one segment, so an allow rule that covers that command applies without a prompt.
Client information
Client Information
Measured on
mainat8af09af29a's merge base, and against the branch of #11765.Login information
Not relevant — the defect is in permission rule splitting and does not involve the model provider or authentication.
Anything else we need to know?
Filed as the follow-up agreed in review on #11765, which fixes a different defect in the same scanner: a backslash inside a plain
'…'string was read as an escape, which swallowed the closing quote and glued a whole line into one segment.Two of the rows above predate that PR and two are widened by it, and the distinction matters for whoever picks this up:
echo 'a' # comment ; echo Bover-splits onmaintoday, unchanged by fix(core): read a backslash inside single quotes as literal when splitting #11765.echo 'a\' # … ; …does not over-split onmain, but only by accident — the old scanner was still stuck inside an unterminated quote, which masked the comment. Once the quote correctly closes, the;inside the comment becomes visible and the line splits.So #11765 widens an existing gap rather than creating one, and narrowing its quote handling to avoid these shapes would re-open the bypass it closes, since the two are the same shape. Comment handling has to be added deliberately instead, and needs to cover at least:
#starts a comment only at the start of a word —echo a#bis not a comment.#inside a quoted string is literal.#is ordinary text.A regression test for each row in the table above would pin the behaviour either way.
中文
发生了什么
splitCompoundCommandSegments没有建模#注释,因此位于行尾注释内部的操作符会被当作真实的命令结构,于是在 bash 并不切分的位置把一行切开。随后的权限判定会对一条授权规则本已覆盖的命令弹出确认。对
echo 'a' # comment ; echo B而言,bash 只执行一条命令——注释会吞掉从#到行尾的全部内容。切分器却返回两个片段echo 'a' # comment和echo B,于是已经授权了Bash(echo *)的用户仍会收到确认提示。方向是「失效即收紧」的:多出来的片段只会增加一条必须通过的规则,不会减少,因此任何判定都不会被放松。这是一个易用性缺陷,而非安全缺陷。
复现步骤
在
settings.json中只授权echo,然后让模型执行echo 'a' # comment ; echo B。尽管 bash 只执行一条被规则覆盖的echo,该调用仍会停下来等待确认。bash 只执行一条命令,从 bash 自身即可看出(见上方英文部分的 console 块)。
实测形态
表格见上方英文部分:
split是splitCompoundCommand的返回结果,bash是 shell 实际执行的命令数。最后两行说明这不是一处一行就能改好的地方。
git status # don't ; echo B结果正确纯属巧合——don't里的单引号开启了一个字符串,把那个;挡住了;而echo a#b被正确地保持完整,是因为#只有位于词首时才是注释。期望的行为
bash 当作一条命令执行的行,应当是一个片段,这样覆盖该命令的授权规则就能生效而不弹出提示。
客户端信息
见上方英文部分。
登录信息
与本问题无关 —— 该缺陷位于权限规则切分环节,不涉及模型提供方或身份认证。
其他补充信息
这是在 #11765 的评审中约定的后续 issue。那个 PR 修复的是同一个扫描器上的另一个缺陷:普通
'…'字符串内的反斜杠被当成了转义,从而吞掉闭合引号、把整行粘成一个片段。上表中有两行早于那个 PR,另有两行是被它扩大的,这个区别对接手的人很重要:
echo 'a' # comment ; echo B在今天的main上就会过度切分,fix(core): read a backslash inside single quotes as literal when splitting #11765 未改变这一点。echo 'a\' # … ; …在main上不会过度切分,但纯属巧合 —— 旧扫描器当时仍卡在一个未闭合的引号里,把注释挡住了。一旦引号正确闭合,注释里的;就暴露出来,这一行就被切开了。因此 #11765 是扩大了一处既有缺口,而不是制造了一个新缺口;而为了规避这些形态去收窄它的引号处理,会重新打开它所关闭的那个绕过,因为两者是同一类形态。注释处理必须作为一项独立工作有意识地加入,至少需要覆盖:
#只有在词首才开启注释 ——echo a#b不是注释。#是字面字符。#是普通文本。为上表每一行补一条回归测试,即可把行为双向钉住。