Skip to content

[bug] Dangerous-command rules miss standard forms: git push -f, dd with reordered args, systemctl poweroff, dead ":>" regex #130

Description

@hobostay

What happened?

The dangerous-command rules in packages/coding-agent/src/step/command-policy.ts:93-105 miss several standard, unobfuscated forms of the exact command classes they are meant to catch. Since the default permission preset is bypass ("Run ordinary tools without approval; dangerous commands still ask") and an unmatched command analyzes as ordinary and is allowed with no confirmation (decideStepToolCall, permissions.ts), these execute silently in the default configuration:

  • dd bs=4M if=disk.img of=/dev/sda / dd of=/dev/sda if=/dev/zero — the copy-device rule /\bdd\s+if=/iu requires if= immediately after dd; any bs=/of= first defeats it.
  • git push -f origin main / git push origin +main — destructive-git only matches --force, not -f or +refspec.
  • git -C repo reset --hard / git -c x=y push --force — the regex requires the subcommand to immediately follow git, so any global option defeats all three git rules.
  • systemctl poweroff / systemctl halt / init 0 — DANGEROUS_LIFECYCLE_COMMANDS only contains reboot/shutdown.
  • :> /dev/sda — /\b:>\s*\/dev\//u can never match realistic input: \b before : requires a preceding word character, so only odd forms like x:> /dev/sda match. The rule is effectively dead code.

Steps to reproduce

/\bdd\s+if=/iu.test("dd bs=4M if=disk.img of=/dev/sda")   // false
// destructive-git:
/\bgit\s+(?:reset\s+--hard|clean\s+-[^\n]*f|push\s+[^\n]*--force(?:-with-lease)?)/iu.test("git push -f origin main") // false
/\b:>\s*\/dev\//u.test(":> /dev/sda")                     // false

Feeding each command through analyzeCommandPolicy returns { kind: "ordinary" }, and decideStepToolCall("run_command", ..., bypass) returns { action: "allow" } — while rm -rf ./build and git push --force correctly return confirm.

Expected behavior

Standard forms of these dangerous commands should trigger confirmation, as the bypass preset promises. Suggestions: match of=/dev/ anywhere in dd args; evaluate git via parsed argv (as isRecursiveForceRemove already does) to cover -f, +refspec, and git -C/-c prefixes; add poweroff/halt and init 0|6 to the lifecycle set; fix or drop the :> rule; add tests using each rule’s standard spellings. Happy to implement this if you’d like.

Version

main @ e411b1a

Activity

  1. github-actions commented on Sep 22, 2026

    @github-actions

    This issue was auto-closed. All issues from new contributors are auto-closed by default.

    Maintainers review auto-closed issues daily and reopen worthwhile ones. Issues that do not meet the quality bar in CONTRIBUTING.md will not be reopened or receive a reply.

    If a maintainer replies lgtmi on one of your issues, your future issues will stay open. If a maintainer replies lgtm, your future issues and PRs will stay open. The command must be at the start of the reply (optionally after one or more @username mentions) or at the end.

    See CONTRIBUTING.md.

  2. Zi-Yi-Ming commented on Oct 4, 2026

    @Zi-Yi-Ming

    #209 合并后,在当前 main(519e4de4)上把本 issue 列的几条逐条实测了一遍,用的是analyzeCommandPolicy 实跑,不是读码推断。另外发现一个反向问题。

    #209 已修

    • copy-device 支持 bs= / of= 前置,dd bs=4M if=disk.img of=/dev/sda 与 dd of=/dev/sda if=/dev/zero 均能识别
    • destructive-git 识别 -f 与 --force-with-lease=
    • truncate-device 的 � 定位问题已修,:> /dev/sda、: >/dev/sda、: > /dev/sda 均能识别

    仍未修

    • git push origin +main:+refspec 强推语法仍不在 destructive-git 模式内
    • git -C repo reset --hard 与 git -c x=y push --force:模式要求子命令紧接 git,全局选项仍可绕过全部三条 git 规则
    • systemctl poweroff / systemctl halt / init 0:command-policy.ts:3 的 DANGEROUS_LIFECYCLE_COMMANDS 仍只有 reboot 与 shutdown

    这三组对应本 issue 原文的第 2、3、4 条中 #209 未覆盖的部分。

    一个反向问题(漏报变误报)

    command-policy.ts:97 的 copy-device 现在是 /\bdd\b[^;&|\n]*\bif=/iu,只检查 if= 是否在场,不看 of= 指向哪。结果是 dd if=image of=out.img 这种把镜像写进普通文件的操作也被判危险。

    在默认 bypass 预设下这个误报一样影响体验:一次多余确认,而且方向与 #209 想解决的相反。

    要同时满足两边,判据需要求 if= 或 of= 之一指向 /dev/,而不是只要 if= 在场。
    本 issue 的 Expected behavior 里 match of=/dev/ anywhere in dd args 接近这个方向,但只加 of=/dev/ 会漏掉 dd if=/dev/zero of=disk.img 这种读设备的方向,所以两侧都要查。

  3. hobostay commented on Oct 9, 2026

    @hobostay
    Author

    @Zi-Yi-Ming 按你的复测结果把剩余三组和一个反向问题都改了,分支在 hobostay:fix/command-policy-gap-closures(对比视图:main...hobostay:Step-Code:fix/command-policy-gap-closures )。API 创建 PR 被权限拒绝(CreatePullRequest permission denied),所以先贴分支,欢迎直接拉取或指出问题。

    仍未修的三组(均已覆盖,analyzeCommandPolicy 实测)

    • git push origin +main:destructive-git 从行正则改为按解析后的 argv 判定(与 isRecursiveForceRemove 同一路径),push 的判据新增 +refspec 操作数
    • git -C repo reset --hard / git -c x=y push --force:新增全局选项跳过逻辑(-C/-c/--git-dir/--work-tree/--namespace/--config-env/--exec-path 及 pager/pathspec 类 flag),先定位真实子命令再判定;未知或动态选项按未命中处理,不猜子命令
    • systemctl poweroff / systemctl halt / init 0:lifecycle 集合加入 halt/poweroff,init/telinit 额外匹配运行级 0 和 6

    反向问题(copy-device 误报)

    判据改为 if= 或 of= 之一指向 /dev/:dd if=image of=out.img 回到 ordinary,dd of=/dev/sda if=image(写设备)与 dd if=/dev/zero of=disk.img(读设备)两个方向都仍然命中。

    测试:step-command-policy.test.ts 新增上述全部正例,以及必须保持 ordinary 的近似形态(git push --force-if-includes、main+topic 结尾的 +、git clean -xdn、echo 'git push --force' 等)。command-policy/permissions 相关 6 个测试文件 511 通过;全套件仅有的 17 个失败在干净 main 上同样失败(与本改动无关)。

  4. 1239636986-create commented on Oct 10, 2026

    @1239636986-create

    感谢详细反馈和持续跟进!
    我们已经针对高危命令识别规则进行了部分修复与优化,完善了部分 Git 强制操作、设备读写及文件截断命令的识别逻辑,相关改动可参考 PR #209。对于后续复测发现的剩余漏检及误报问题,我们也已关注到相关修复方案,将继续跟进代码审核与验证。
    感谢你提供详细的复现案例和测试结果,对我们完善命令执行安全机制非常有帮助!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions