feat(permissions): global shell allowlist that cascades with per-project (#2059)#2091
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 26afe9c926
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| export function loadGlobalShellAllowed(path: string = defaultConfigPath()): string[] { | ||
| return readConfig(path).shellAllowedGlobal ?? []; |
There was a problem hiding this comment.
Validate the global allowlist before returning it
If a user hand-edits config.json with a malformed shellAllowedGlobal value (for example an object or string), this returns that value as string[], and the new callers then spread it in buildCodeToolset and iterate it in /permissions listing. That can throw before any shell command can be checked or before permissions can be listed; readConfig only sanitizes fields included in STRING_ARRAY_FIELDS, and this new field is not added there.
Useful? React with 👍 / 👎.
| const head = (args[0] ?? "").toLowerCase(); | ||
| if (head === "global" || head === "g") return { global: true, rest: args.slice(1) }; |
There was a problem hiding this comment.
Preserve project prefixes named g or global
With this scope parser, /permissions add g and /permissions remove g are always treated as global-scope operations with no remaining prefix, so an existing or desired project allowlist entry for a command named g (a common git alias) can no longer be managed from the slash command. The same applies to any project command prefix whose first token is global, so users with those project entries are forced out of this UI path.
Useful? React with 👍 / 👎.
esengine
left a comment
There was a problem hiding this comment.
Good direction — closes the right gap (#2059) and the cascade-at-extraAllowed site is the cleanest place to do this. Tests, i18n × 4, and the CRUD mirror are all solid. Two issues to fix before merge (Codex caught both; I agree with both):
-
Sanitize
shellAllowedGlobalon read. The repo already has a registry for exactly this —STRING_ARRAY_FIELDSinsrc/config.ts(around L409), whichreadConfigruns throughsanitizeStringArrayField. Please add["shellAllowedGlobal"]to that list. Otherwise a hand-edited config likeshellAllowedGlobal: "foo"orshellAllowedGlobal: {}will round-trip as a non-array and crash[...new Set([...loadGlobalShellAllowed(), ...])]insetup.tsbefore any shell command can be checked. -
g/globalscope keyword collides with real project entries.takeScopetreats the first arg as a scope keyword, so/permissions add gis interpreted as "add to global, empty prefix" and returns the usage hint — there's no way to addg(a popular git alias) to a project allowlist via the slash command. Same for any project entry whose first token isglobal. Either:- Use a flag:
/permissions add --global <prefix>(parse before joining the rest), or - Require explicit scope syntax:
/permissions add @global <prefix>.
I lean
--global— it's the convention readers already expect and won't collide with any plausible prefix. - Use a flag:
Once those two are addressed, this is ready. Thanks for hooking into the existing renderListing / load* / add* / remove* / clear* shape — the symmetry made review easy.
26afe9c to
625a81b
Compare
|
Both addressed in
|
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 625a81b831
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| for (const a of args) { | ||
| if (a === "--global" || a === "-g") global = true; | ||
| else rest.push(a); |
There was a problem hiding this comment.
Restrict --global parsing to the option position
Because this strips --global/-g from every argument after the subcommand, a user cannot add or remove project allowlist prefixes for real commands that contain those flags, such as /permissions add git config --global user.name or npm install --global foo; the handler instead switches to the global list and stores a different prefix with the flag removed, so the intended command still prompts while an unintended prefix may be globally allowed. Parse the scope flag only before the prefix (or provide a -- terminator) so command arguments are preserved.
Useful? React with 👍 / 👎.
625a81b to
a683c97
Compare
|
Fixed in
Two new tests cover both cases. |
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a683c97db8
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| extraAllowed: () => loadProjectShellAllowed(root), | ||
| // Global allowlist applies everywhere; project list adds to it (#2059). | ||
| extraAllowed: () => [ | ||
| ...new Set([...loadGlobalShellAllowed(), ...loadProjectShellAllowed(root)]), |
There was a problem hiding this comment.
Read the global allowlist from the selected config
When buildCodeToolset is constructed with opts.configPath (the option is documented for pinning a non-default config, and is already used for loadEditMode(opts.configPath)), the new global allowlist still reads ~/.reasonix/config.json via loadGlobalShellAllowed() instead of the selected config file. In that context, entries saved in the active config's shellAllowedGlobal will not auto-approve commands, while entries from the user's default config may be applied unexpectedly; pass opts.configPath through when loading the new global list.
Useful? React with 👍 / 👎.
a683c97 to
e77f6b4
Compare
|
Fixed in
@codex review |
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
@esengine 您好,您在 2026-05-28 02:16 提出的两条修改建议( |
esengine
left a comment
There was a problem hiding this comment.
Re-reviewed — both issues from my review are fixed exactly as discussed:
["shellAllowedGlobal"]is now inSTRING_ARRAY_FIELDS, so a hand-edited non-array value gets sanitized on read instead of crashing the[...new Set(...)]cascade in setup.ts.- Scope is now a leading
--global/-gflag viatakeScope(not a positional keyword) — sogis addable to a project allowlist again, a non-leading--global(e.g.git config --global user.name) is preserved, and the i18n usage hints updated to--global. That's the option I leaned toward.
CI green. Merging — thanks for hooking into the existing field-registry + CRUD shape.
|
Approving the content — both fixes are verified correct. But heads up: it now conflicts with main (recent merges touched |
…ect (esengine#2059) Adds a top-level `shellAllowedGlobal` config list, auto-approved across ALL projects and merged (union) with the per-project allowlist when deciding whether a shell command needs approval. Manage it via a `--global` (or `-g`) flag on the existing `/permissions` subcommands: /permissions add --global <prefix> /permissions remove --global <prefix-or-N> /permissions clear --global confirm /permissions list # now shows a Global section above Project The flag is parsed as a flag (not a positional keyword), so command prefixes like `g` or `global` remain manageable from the slash UI. `shellAllowedGlobal` is registered in `STRING_ARRAY_FIELDS` so a malformed hand-edited value is sanitized on read instead of crashing the merge in `code/setup.ts`'s `extraAllowed` getter. Config accessors (load/add/remove/clearGlobalShellAllowed) mirror the project ones; EN/zh-CN/ja/de strings added (ru falls back to EN).
e77f6b4 to
81fd787
Compare
|
Rebased onto current main ( |
|
Windows CI 失败的是 |
Problem
Closes #2059. Shell command allowlists were per-project only (
config.projects[<root>].shellAllowed). There was no way to auto-approve a command (e.g.brew install,cargo build) once and have it apply everywhere — every new project re-prompted. The request: a cascade like Claude/Codex/OpenCode (global ∪ local).Change
Add a top-level
shellAllowedGlobalconfig list, merged (union) with the per-project list at the single point where the shell tool resolves its extra allowlist:Manage it via a
--global(or-g) flag on the existing/permissionssubcommands (works in chat mode too — global entries need no project root):The flag is parsed as a flag, not as a positional keyword, so command prefixes like
g(a populargitalias) orglobalstay manageable from the slash UI.load/add/remove/clearGlobalShellAllowedmirror the project ones (same dedup, exact-match removal, atomic write).shellAllowedGlobalis registered inSTRING_ARRAY_FIELDSso a malformed hand-edited value gets sanitized on read instead of crashing the merge.extraAllowed.Out of scope (follow-up)
The HTTP
/permissionsAPI + desktop settings panel still expose only the project list; global management there can be a follow-up. The/permissionsslash command already covers CLI and desktop (command palette).Verification
tests/config.test.ts: global CRUD (load/add/dedup/remove/clear) + independence from project lists.tests/permissions-slash.test.ts:--globaladd/remove/clear routes to the global list;add gaddsgto the project list (not hijacked);--globalworks without a code root.npm run verify(build + lint + typecheck + full suite) passes.