fix(cli): trim bash allowlist to safe commands, handle legacy config

- Remove commands that can execute arbitrary code (node, python, curl,
  docker, etc.) from the default bash allowlist
- Keep only read-only/informational commands, text processing, file
  operations, git, package managers, compilers, and archive tools
- Handle legacy TOML config file in bash migration to detect existing
  users who only have the old config format
This commit is contained in:
Mark IJbema
2026-03-25 12:22:41 +01:00
parent 87fab14e94
commit b73963578d
2 changed files with 13 additions and 30 deletions
+3 -26
View File
@@ -92,8 +92,6 @@ export namespace Agent {
"grep *": "allow",
"rg *": "allow",
"ag *": "allow",
"awk *": "allow",
"sed *": "allow",
"sort *": "allow",
"uniq *": "allow",
"cut *": "allow",
@@ -106,41 +104,20 @@ export namespace Agent {
"mv *": "allow",
// version control
"git *": "allow",
// package managers & runtimes
"node *": "allow",
"npx *": "allow",
// package managers (install/build, not arbitrary execution)
"npm *": "allow",
"yarn *": "allow",
"pnpm *": "allow",
"bun *": "allow",
"bunx *": "allow",
"deno *": "allow",
// build & language tools
// build tools (compilers, not script runners)
"tsc *": "allow",
"tsgo *": "allow",
"cargo *": "allow",
"go *": "allow",
"python *": "allow",
"python3 *": "allow",
"pip *": "allow",
"pip3 *": "allow",
"uv *": "allow",
"ruby *": "allow",
"gem *": "allow",
"make *": "allow",
"cmake *": "allow",
"dotnet *": "allow",
// http & archive
"curl *": "allow",
"wget *": "allow",
// archive
"tar *": "allow",
"unzip *": "allow",
"gzip *": "allow",
"gunzip *": "allow",
// containers
"docker *": "allow",
"docker-compose *": "allow",
"kubectl *": "allow",
}
// kilocode_change end
const defaults = PermissionNext.fromConfig({
+10 -4
View File
@@ -1340,12 +1340,15 @@ export namespace Config {
async function migrateBashPermission() {
const files = GLOBAL_CONFIG_FILES.map((file) => path.join(Global.Path.config, file))
// also check legacy TOML config — its presence means existing user
const legacy = path.join(Global.Path.config, "config")
const existing: string[] = []
for (const file of files) {
if (existsSync(file)) existing.push(file)
}
const hasLegacy = existsSync(legacy)
// no global config → new user, they'll get the new bash:ask default
if (existing.length === 0) return
if (existing.length === 0 && !hasLegacy) return
// check if any config file already has an explicit bash permission
for (const file of existing) {
const text = await Bun.file(file).text()
@@ -1353,9 +1356,12 @@ export namespace Config {
if (data.permission?.bash) return
}
// existing user without bash permission in any file → write bash:allow to the
// highest-precedence existing file to preserve their current behavior
const target = existing[existing.length - 1]
const text = await Bun.file(target).text()
// highest-precedence existing file to preserve their current behavior.
// if only the legacy TOML file exists, write to config.json (the TOML migration will merge into it)
const target = existing.length > 0 ? existing[existing.length - 1] : path.join(Global.Path.config, "config.json")
const text = await Bun.file(target)
.text()
.catch(() => "{}")
if (target.endsWith(".jsonc")) {
const edits = modify(text, ["permission", "bash"], "allow", {
formattingOptions: { insertSpaces: true, tabSize: 2 },