[FEATURE] add exec tool as more secure alternative to bash tool #1460

Open
opened 2026-02-16 17:31:04 -05:00 by yindo · 23 comments
Owner

Originally created by @aspiers on GitHub (Aug 23, 2025).

Originally assigned to: @thdxr on GitHub.

[N.B. posted a similar issue at https://github.com/anthropics/claude-code/issues/6046 since Claude Code has a very similar problem]

TL;DR summary

I propose a new exec tool which invokes subprocess commands directly, not via bash or any other shell.

Rationale: bash tool is inherently unsafe

https://opencode.ai/docs/permissions/#bash mentions that wildcard patterns can restrict specific commands:

https://github.com/sst/opencode/blob/6456350564fd53a7aa93374a5e5d4c529d9a2f0f/packages/web/src/content/docs/docs/permissions.mdx?plain=1#L101-L112

but it doesn't acknowledge the risks of allowing the LLM to run arbitrary code based on simple substring- or pattern-matching. In fact, it doesn't explicitly mention the possibility of allowing more complicated wildcards than just *, although this Terraform example in the section above shows that it is supported:

https://github.com/sst/opencode/blob/6456350564fd53a7aa93374a5e5d4c529d9a2f0f/packages/web/src/content/docs/docs/permissions.mdx?plain=1#L76-L80

For comparison, https://docs.anthropic.com/en/docs/claude-code/iam#tool-specific-permission-rules states:

Claude Code is aware of shell operators (like &&) so a prefix match rule like Bash(safe-cmd:*) won’t give it permission to run the command safe-cmd && other-cmd

However even this is very vague and fundamentally unsafe. It's impossible for any kind of pattern-matching approach to guarantee that arbitrary shell code is both secure and not bypassing file access controls imposed on the agent.

For example, even if you ban operators like &&, ||, and ;, the agent can still construct Bash commands to circumvent these which would be allowed by innocent looking patterns like npm run test * or git status *, e.g.

npm run test <(rm -rf $HOME/*)
git status $(rm -rf $HOME/*)

This issue is demonstrated in depth by the report in https://github.com/anthropics/claude-code/issues/4956 which lists many other techniques for bypassing basic checks.

The above problems are a symptom of the fundamentally flawed approach of only allowing command invocation via a bash tool. It has been well-known for decades that allowing semi-arbitrary shell code is a significant vector for attacks and accidental side effects.

Proposed (partial) solution: a new exec tool

It is also well-established for decades that when building mechanisms which launch new processes, it's best practice to simply avoid using shells to launch them.

For example, Python defaults to invoking subprocess commands directly rather than via a shell. Similarly, Node.js offers child_process.execFile() which does not spawn a shell by default.

So in many cases, a similar approach of avoiding usage of bash or any other shell should work fine for opencode.

Therefore I propose a new exec tool which invokes commands directly, not via bash or any shell.

For example, with this new tool, even if combined with a very limited * wildcard scheme, if we put the following in opencode.json:

{
  "$schema": "https://opencode.ai/config.json",
  "permission": {
    "bash": {
      "npm run test *": "allow",
      "*": "ask"
    }
  }
}

then it would guarantee that anything after the npm run test would be supplied as arguments to the npm command and therefore could not directly launch any other subprocess. So npm run test <(rm -rf $HOME/*) would have the equivalent effect of running this from a shell:

npm run test '<(rm' '-rf' '$HOME/*)'

which doesn't make sense, but at least it would be safe.

Beyond an exec tool: the need for a full sandboxing solution

Whilst the above proposal for a new exec tool is serious, would certainly offer opencode users better protection in many circumstances, and should be easy to implement, it can never be a full solution. For example, here are a few ways to edit an arbitrary file without requiring any shell at all:

sed -i -e '... some editing code ...' foo.txt
python -c '... some editing code ...'
perl -i -pe '... some editing code ...' foo.txt

and of course the latter two examples allow not just file editing but arbitrary execution.

And it doesn't always make sense to add these commands to the deny list because there are perfectly valid use cases for invoking those. There is no pattern-matching method which can determine whether one of these commands is legitimate or not.

In general, attempting to determine the safety or side effects of arbitrary commands by performing pattern matching or even complex static analysis on those commands is doomed to fail. A much more effective approach is to ensure that the commands are run within a sandboxed environment which limits what can be done at the OS level.

If opencode is interested in stealing ideas from Claude Code, it's worth noting that one proposal along these lines has already been made:

And Gemini CLI natively supports running in a container.

However there are probably other approaches which could also work, e.g. on Linux apparmor and selinux are alternative sandboxing mechanisms.

Originally created by @aspiers on GitHub (Aug 23, 2025). Originally assigned to: @thdxr on GitHub. *[N.B. posted a similar issue at https://github.com/anthropics/claude-code/issues/6046 since Claude Code has a very similar problem]* ## TL;DR summary **I propose a new `exec` tool which invokes subprocess commands directly, not via bash or any other shell.** ## Rationale: `bash` tool is inherently unsafe https://opencode.ai/docs/permissions/#bash mentions that wildcard patterns can restrict specific commands: https://github.com/sst/opencode/blob/6456350564fd53a7aa93374a5e5d4c529d9a2f0f/packages/web/src/content/docs/docs/permissions.mdx?plain=1#L101-L112 but it doesn't acknowledge the risks of allowing the LLM to run arbitrary code based on simple substring- or pattern-matching. In fact, it doesn't explicitly mention the possibility of allowing more complicated wildcards than just `*`, although this Terraform example in the section above shows that it is supported: https://github.com/sst/opencode/blob/6456350564fd53a7aa93374a5e5d4c529d9a2f0f/packages/web/src/content/docs/docs/permissions.mdx?plain=1#L76-L80 For comparison, https://docs.anthropic.com/en/docs/claude-code/iam#tool-specific-permission-rules states: > Claude Code is aware of shell operators (like `&&`) so a prefix match rule like `Bash(safe-cmd:*)` won’t give it permission to run the command `safe-cmd && other-cmd` However even this is very vague and fundamentally unsafe. It's impossible for any kind of pattern-matching approach to guarantee that arbitrary shell code is both secure and not bypassing file access controls imposed on the agent. For example, even if you ban operators like `&&`, `||`, and `;`, the agent can still construct Bash commands to circumvent these which would be allowed by innocent looking patterns like `npm run test *` or `git status *`, e.g. npm run test <(rm -rf $HOME/*) git status $(rm -rf $HOME/*) This issue is demonstrated in depth by the report in https://github.com/anthropics/claude-code/issues/4956 which lists many other techniques for bypassing basic checks. The above problems are a symptom of the fundamentally flawed approach of only allowing command invocation via a `bash` tool. It has been well-known for [decades](https://www.w3.org/Security/faq/wwwsf4.html) that allowing semi-arbitrary shell code is a significant vector for attacks and accidental side effects. ## Proposed (partial) solution: a new `exec` tool It is also well-established for decades that when building mechanisms which launch new processes, it's best practice to simply avoid using shells to launch them. For example, [Python defaults to invoking subprocess commands directly rather than via a shell](https://docs.python.org/3/library/subprocess.html#security-considerations). Similarly, Node.js offers [`child_process.execFile()`](https://nodejs.org/api/child_process.html#child_processexecfilefile-args-options-callback) which does not spawn a shell by default. So in many cases, a similar approach of avoiding usage of bash or any other shell should work fine for opencode. Therefore **I propose a new `exec` tool which invokes commands directly, not via bash or any shell.** For example, with this new tool, even if combined with a very limited `*` wildcard scheme, if we put the following in `opencode.json`: ```json { "$schema": "https://opencode.ai/config.json", "permission": { "bash": { "npm run test *": "allow", "*": "ask" } } } ``` then it would guarantee that anything after the `npm run test` would be supplied as arguments to the `npm` command and therefore could not directly launch any other subprocess. So `npm run test <(rm -rf $HOME/*)` would have the equivalent effect of running this from a shell: npm run test '<(rm' '-rf' '$HOME/*)' which doesn't make sense, but at least it would be safe. ## Beyond an `exec` tool: the need for a full sandboxing solution Whilst the above proposal for a new `exec` tool is serious, would certainly offer opencode users better protection in many circumstances, and should be easy to implement, it can never be a full solution. For example, here are a few ways to edit an arbitrary file without requiring any shell at all: sed -i -e '... some editing code ...' foo.txt python -c '... some editing code ...' perl -i -pe '... some editing code ...' foo.txt and of course the latter two examples allow not just file editing but arbitrary execution. And it doesn't always make sense to add these commands to the deny list because there are perfectly valid use cases for invoking those. There is no pattern-matching method which can determine whether one of these commands is legitimate or not. In general, attempting to determine the safety or side effects of arbitrary commands by performing pattern matching or even complex static analysis on those commands is doomed to fail. A much more effective approach is to ensure that the commands are run within a sandboxed environment which limits what can be done at the OS level. If opencode is interested in stealing ideas from Claude Code, it's worth noting that one proposal along these lines has already been made: - https://github.com/anthropics/claude-code/issues/4320 And Gemini CLI natively supports running in a container. However there are probably other approaches which could also work, e.g. on Linux apparmor and selinux are alternative sandboxing mechanisms.
Author
Owner

@github-actions[bot] commented on GitHub (Aug 23, 2025):

This issue might be a duplicate of existing issues. Please check:

  • #1538: Similar security concerns about bash tool safety, proposes command filtering/blocking for harmful patterns
  • #1229: Discusses security issues with destructive commands like rm -rf and proposes a command block list approach

Feel free to ignore if none of these address your specific case.

@github-actions[bot] commented on GitHub (Aug 23, 2025): This issue might be a duplicate of existing issues. Please check: - #1538: Similar security concerns about bash tool safety, proposes command filtering/blocking for harmful patterns - #1229: Discusses security issues with destructive commands like `rm -rf` and proposes a command block list approach Feel free to ignore if none of these address your specific case.
Author
Owner

@thdxr commented on GitHub (Aug 23, 2025):

have you looked at how our permissions check on the bash tool is implemented? it's not a simple string check we parse the command with treesitter and check each subcommand against what's configured

not saying this is 100% effective but not sure if you were aware of that

@thdxr commented on GitHub (Aug 23, 2025): have you looked at how our permissions check on the bash tool is implemented? it's not a simple string check we parse the command with treesitter and check each subcommand against what's configured not saying this is 100% effective but not sure if you were aware of that
Author
Owner

@aspiers commented on GitHub (Aug 23, 2025):

Hey @thdxr thanks for the reply, and indeed I wasn't aware - that's really cool, and probably far better than some or perhaps even all of the competitors!

That said, I'm pretty sure you're right that this isn't 100% effective, and I have many doubts it ever could be. For example, from just a quick scan of the code, I see that it relies on realpath to figure out whether a command argument references a restricted path, but this doesn't take into account things like parameter expansion. So if I'm reading it right, even rm $HOME/foo would be allowed because realpath won't expand $HOME.

I'm pretty sure that trying to make arbitrary shell 100% safe is an impossible game of whack-a-mole which is highly dependent on the individual user's needs and environment. In the above example, the fundamental problem is that it relies on static analysis, but bash is a dynamic language and its runtime effects can't be fully predicted in a reliable fashion purely through static analysis.

Don't get me wrong - of course I'm not proposing to remove the bash tool, as that would cripple functionality. And it's definitely great to apply best efforts to improving security of the bash tool. But reducing attack surface is a well-known principle of security best practices, so I think being able to avoid shell altogether when it's not needed is an extra weapon in the arsenal. And that's what an exec tool would allow.

@aspiers commented on GitHub (Aug 23, 2025): Hey @thdxr thanks for the reply, and indeed I wasn't aware - that's really cool, and probably far better than some or perhaps even all of the competitors! That said, I'm pretty sure you're right that this isn't 100% effective, and I have many doubts it ever could be. For example, from just a quick scan of the code, I see that it relies on `realpath` to figure out whether a command argument references a restricted path, but this doesn't take into account things like parameter expansion. So if I'm reading it right, even `rm $HOME/foo` would be allowed because `realpath` won't expand `$HOME`. I'm pretty sure that trying to make arbitrary shell 100% safe is an impossible game of whack-a-mole which is highly dependent on the individual user's needs and environment. In the above example, the fundamental problem is that it relies on static analysis, but bash is a dynamic language and its runtime effects can't be fully predicted in a reliable fashion purely through static analysis. Don't get me wrong - of course I'm not proposing to remove the `bash` tool, as that would cripple functionality. And it's definitely great to apply best efforts to improving security of the `bash` tool. But reducing attack surface is a well-known principle of security best practices, so I think being able to avoid shell altogether when it's not needed is an extra weapon in the arsenal. And that's what an `exec` tool would allow.
Author
Owner

@rekram1-node commented on GitHub (Aug 28, 2025):

@aspiers just btw:

"permission": {
    "bash": {
      "*": "deny",
      "npm run test *": "allow",
    },
  },

would block:
npm run test <(rm -rf $HOME/*)

@rekram1-node commented on GitHub (Aug 28, 2025): @aspiers just btw: ``` "permission": { "bash": { "*": "deny", "npm run test *": "allow", }, }, ``` would block: npm run test <(rm -rf $HOME/*)
Author
Owner

@aspiers commented on GitHub (Aug 29, 2025):

Yeah I realised that as soon as I looked at your treesitter code. As I said, it's very nice, but bash + treesitter is still an enormous attack surface when compared to exec(3), so would be great to add the latter as an alternative (and presumably pretty easy to do).

@aspiers commented on GitHub (Aug 29, 2025): Yeah I realised that as soon as I looked at your treesitter code. As I said, it's very nice, but bash + treesitter is still an enormous attack surface when compared to `exec(3)`, so would be great to add the latter as an alternative (and presumably pretty easy to do).
Author
Owner

@anuramat commented on GitHub (Sep 13, 2025):

what about an additional AI check in a separate thread with small_model + json mode? "check if this bash command breaks the rules above and/or if the description is misleading", with an optional user defined prompt

would be useful e.g. when the model keeps trying to edit in plan mode by abusing bash

I'm not sure if the false positive rate would be tolerable tho

@anuramat commented on GitHub (Sep 13, 2025): what about an additional AI check in a separate thread with small_model + json mode? "check if this bash command breaks the rules above and/or if the description is misleading", with an optional user defined prompt would be useful e.g. when the model keeps trying to edit in plan mode by abusing bash I'm not sure if the false positive rate would be tolerable tho
Author
Owner

@aspiers commented on GitHub (Sep 14, 2025):

what about an additional AI check in a separate thread with small_model + json mode? "check if this bash command breaks the rules above and/or if the description is misleading", with an optional user defined prompt

would be useful e.g. when the model keeps trying to edit in plan mode by abusing bash

No, please let's not go down this road.

I'm not sure if the false positive rate would be tolerable tho

The accuracy wouldn't be tolerable. LLMs are nowhere near reliable enough for acting as security mechanisms, e.g. take a look at https://simonwillison.net/2025/Aug/15/the-summer-of-johann/

Additionally, using an LLM would be a needlessly heavyweight solution given that far cheaper and more reliable ones exist, such as the exec tool I propose here, and the treesitter approach which opencode already has.

@aspiers commented on GitHub (Sep 14, 2025): > what about an additional AI check in a separate thread with small_model + json mode? "check if this bash command breaks the rules above and/or if the description is misleading", with an optional user defined prompt > > would be useful e.g. when the model keeps trying to edit in plan mode by abusing bash No, please let's not go down this road. > I'm not sure if the false positive rate would be tolerable tho The accuracy wouldn't be tolerable. LLMs are nowhere near reliable enough for acting as security mechanisms, e.g. take a look at https://simonwillison.net/2025/Aug/15/the-summer-of-johann/ Additionally, using an LLM would be a needlessly heavyweight solution given that far cheaper and more reliable ones exist, such as the `exec` tool I propose here, and the treesitter approach which opencode already has.
Author
Owner

@anuramat commented on GitHub (Sep 22, 2025):

The way I understand your idea, adding a single finegrained bash permission rule will force the model to switch from its typical workflow (long one shot scripts with io redirection) to long chains of isolated tool calls

A sandbox takes like 5 minutes to set up, why bother tanking the performance of the model for slightly safer execution without one?

LLMs are nowhere near reliable enough for acting as security mechanisms

I am mostly talking about mitigating poor instruction following, not prompt injection;

take a look at

I don't see any cases of LLM-based safety mechanisms failing in the post you provided; there's quite a few papers on LLM-as-a-guard being a decent idea, eg https://arxiv.org/abs/2308.07308 and https://arxiv.org/abs/2312.06674

accuracy wouldn't be tolerable

Accuracy doesn't really need to be high, only the precision, as this would be an additional check

needlessly heavyweight solution

Requests would be comparable to title generation in terms of token usage, the only problem is that it would slow down the tools; it's probably only feasible if you have a fast enough backend (cerebras/local) for the guard model OR a really slow primary agent model

This could probably be implemented as a plugin though

@anuramat commented on GitHub (Sep 22, 2025): The way I understand your idea, adding a single finegrained bash permission rule will force the model to switch from its typical workflow (long one shot scripts with io redirection) to long chains of isolated tool calls A sandbox takes like 5 minutes to set up, why bother tanking the performance of the model for slightly safer execution without one? > LLMs are nowhere near reliable enough for acting as security mechanisms I am mostly talking about mitigating poor instruction following, not prompt injection; > take a look at I don't see any cases of LLM-based safety mechanisms failing in the post you provided; there's quite a few papers on LLM-as-a-guard being a decent idea, eg https://arxiv.org/abs/2308.07308 and https://arxiv.org/abs/2312.06674 > accuracy wouldn't be tolerable Accuracy doesn't really need to be high, only the precision, as this would be an additional check > needlessly heavyweight solution Requests would be comparable to title generation in terms of token usage, the only problem is that it would slow down the tools; it's probably only feasible if you have a fast enough backend (cerebras/local) for the guard model OR a really slow primary agent model This could probably be implemented as a plugin though
Author
Owner

@aspiers commented on GitHub (Sep 23, 2025):

The way I understand your idea, adding a single finegrained bash permission rule will force the model to switch from its typical workflow (long one shot scripts with io redirection) to long chains of isolated tool calls

Not really, but I can see how I might have accidentally given that impression (sorry for that). The idea is not so much to substitute bash tool calls for exec tool calls when there's a genuine need for bash; instead, it's to avoid bash when it's completely overkill and exec is enough.

So for example, if all that's needed is to run npm run test, most likely invoking a shell is way overkill, and instead invoking exec to run npm directly will dramatically shrink the attack surface (and avoid consuming unnecessary system resources as a minor but nice side effect).

A sandbox takes like 5 minutes to set up, why bother tanking the performance of the model for slightly safer execution without one?

I'm totally in favour of sandboxes in general. But they don't always take 5 minutes to set up, plus sometimes there are technical constraints which could make them hard or impossible to set up at all. So the idea is to give the user multiple good options. If they can run in a sandbox then great, use that. But whether they can or not, it's always good to design efficient secure systems which minimise attack surfaces through the Principle of Least Privilege, and an exec tool would be one simple contribution in that direction.

LLMs are nowhere near reliable enough for acting as security mechanisms

I am mostly talking about mitigating poor instruction following, not prompt injection;

OK, then we are probably talking about different things. The main motivation for this proposal is improved security by eliminating the use of shells in the cases where they're not needed.

take a look at

I don't see any cases of LLM-based safety mechanisms failing in the post you provided; there's quite a few papers on LLM-as-a-guard being a decent idea, eg arxiv.org/abs/2308.07308 and arxiv.org/abs/2312.06674

Even if research indicates that all of this is feasible, there are still several concerns:

  1. The papers you cite are about safeguards against human agents trying to get an LLM to respond in natural language on topics such as illegal weapons / violence / hate; however here I'm talking about defending against an AI agent being tricked by an LLM into invoking malicious commands. That's very different.
  2. The research is new and probably not proven yet. E.g. the first paper says "LLM SELF DEFENSE reduces attack success rate to virtually 0", which effectively means that it makes attacks harder but still not impossible.
  3. There are also papers on LLM scheming, e.g. https://arxiv.org/pdf/2412.04984, so it's conceivable that an LLM could pretend to do a good job of being a safety guard in order to be given that job and then abuse that position of power.
  4. It doesn't negate the importance of the Principles of Least Privilege.
  5. Using an LLM to defend against an LLM can substantially increase costs and resource consumption. Maybe in the future it will be a valid approach, and certainly it's a worthwhile avenue of research. But I don't see it as a viable short-term solution.
  6. It still suffers from the problem I previously mentioned, that it can only do static analysis outside the run-time environment, and runtime effects can't be fully predicted in a reliable fashion purely through static analysis.

accuracy wouldn't be tolerable

Accuracy doesn't really need to be high, only the precision, as this would be an additional check

I'm not sure what's the difference between accuracy and precision here.

needlessly heavyweight solution

Requests would be comparable to title generation in terms of token usage

Maybe I'm misunderstanding but I don't see why that would be the case. The guard would need to do more complex reasoning based on static analysis predicting the effects of commands and code in a runtime which it doesn't fully understand.

the only problem is that it would slow down the tools; it's probably only feasible if you have a fast enough backend (cerebras/local) for the guard model OR a really slow primary agent model

Agreed.

This could probably be implemented as a plugin though

Sure. If someone wants to experiment with building that, I certainly wouldn't stand in their way. But for the above reasons I wouldn't be too optimistic that the experiment would succeed.

@aspiers commented on GitHub (Sep 23, 2025): > The way I understand your idea, adding a single finegrained bash permission rule will force the model to switch from its typical workflow (long one shot scripts with io redirection) to long chains of isolated tool calls Not really, but I can see how I might have accidentally given that impression (sorry for that). The idea is not so much to substitute `bash` tool calls for `exec` tool calls when there's a genuine need for `bash`; instead, it's to avoid `bash` when it's completely overkill and `exec` is enough. So for example, if all that's needed is to run `npm run test`, most likely invoking a shell is way overkill, and instead invoking `exec` to run `npm` directly will dramatically shrink the attack surface (and avoid consuming unnecessary system resources as a minor but nice side effect). > A sandbox takes like 5 minutes to set up, why bother tanking the performance of the model for slightly safer execution without one? I'm totally in favour of sandboxes in general. But they don't always take 5 minutes to set up, plus sometimes there are technical constraints which could make them hard or impossible to set up at all. So the idea is to give the user multiple good options. If they can run in a sandbox then great, use that. But whether they can or not, it's always good to design efficient secure systems which minimise attack surfaces through the Principle of Least Privilege, and an `exec` tool would be one simple contribution in that direction. > > LLMs are nowhere near reliable enough for acting as security mechanisms > > I am mostly talking about mitigating poor instruction following, not prompt injection; OK, then we are probably talking about different things. The main motivation for this proposal is improved security by eliminating the use of shells in the cases where they're not needed. > > take a look at > > I don't see any cases of LLM-based safety mechanisms failing in the post you provided; there's quite a few papers on LLM-as-a-guard being a decent idea, eg [arxiv.org/abs/2308.07308](https://arxiv.org/abs/2308.07308) and [arxiv.org/abs/2312.06674](https://arxiv.org/abs/2312.06674) Even if research indicates that all of this is feasible, there are still several concerns: 1. The papers you cite are about safeguards against human agents trying to get an LLM to respond in natural language on topics such as illegal weapons / violence / hate; however here I'm talking about defending against an AI agent being tricked by an LLM into invoking malicious commands. That's very different. 2. The research is new and probably not proven yet. E.g. the first paper says "LLM SELF DEFENSE reduces attack success rate to virtually 0", which effectively means that it makes attacks harder but still not impossible. 3. There are also papers on LLM scheming, e.g. https://arxiv.org/pdf/2412.04984, so it's conceivable that an LLM could pretend to do a good job of being a safety guard in order to be given that job and then abuse that position of power. 4. It doesn't negate the importance of the Principles of Least Privilege. 5. Using an LLM to defend against an LLM can substantially increase costs and resource consumption. Maybe in the future it will be a valid approach, and certainly it's a worthwhile avenue of research. But I don't see it as a viable short-term solution. 6. It still suffers from the problem I [previously mentioned](https://github.com/sst/opencode/issues/2206#issuecomment-3217170432), that it can only do static analysis outside the run-time environment, and runtime effects can't be fully predicted in a reliable fashion purely through static analysis. > > accuracy wouldn't be tolerable > > Accuracy doesn't really need to be high, only the precision, as this would be an additional check I'm not sure what's the difference between accuracy and precision here. > > needlessly heavyweight solution > > Requests would be comparable to title generation in terms of token usage Maybe I'm misunderstanding but I don't see why that would be the case. The guard would need to do more complex reasoning based on static analysis predicting the effects of commands and code in a runtime which it doesn't fully understand. > the only problem is that it would slow down the tools; it's probably only feasible if you have a fast enough backend (cerebras/local) for the guard model OR a really slow primary agent model Agreed. > This could probably be implemented as a plugin though Sure. If someone wants to experiment with building that, I certainly wouldn't stand in their way. But for the above reasons I wouldn't be too optimistic that the experiment would succeed.
Author
Owner

@Nindaleth commented on GitHub (Nov 25, 2025):

have you looked at how our permissions check on the bash tool is implemented? it's not a simple string check we parse the command with treesitter and check each subcommand against what's configured

not saying this is 100% effective but not sure if you were aware of that

@thdxr This is great to read! I had a look at permissions and tools#bash, but couldn't find any details on this. It's the number one reason I'd rather approve each command manually, because I'm not risking anything and 100% understand what I'm allowing. Is my understanding correct that all ways to run multiple commands within one line, such as semicolons, <() and $() driven subshells, bash -c "cmd", pipes, &&, || or \ multilines... are all handled by the treesitter parsing approach?

@Nindaleth commented on GitHub (Nov 25, 2025): > have you looked at how our permissions check on the bash tool is implemented? it's not a simple string check we parse the command with treesitter and check each subcommand against what's configured > > not saying this is 100% effective but not sure if you were aware of that @thdxr This is great to read! I had a look at [permissions](https://opencode.ai/docs/permissions/) and [tools#bash](https://opencode.ai/docs/tools/#bash), but couldn't find any details on this. It's the number one reason I'd rather approve each command manually, because I'm not risking anything and 100% understand what I'm allowing. Is my understanding correct that all ways to run multiple commands within one line, such as semicolons, `<()` and `$()` driven subshells, `bash -c "cmd"`, pipes, `&&`, `||` or `\` multilines... are all handled by the treesitter parsing approach?
Author
Owner

@rekram1-node commented on GitHub (Nov 25, 2025):

@Nindaleth I believe everything BUT the bash -c stuff is handled but I will fix that

@rekram1-node commented on GitHub (Nov 25, 2025): @Nindaleth I believe everything BUT the `bash -c` stuff is handled but I will fix that
Author
Owner

@mlanza commented on GitHub (Dec 5, 2025):

Sorry. I didn't read the entire thread, but have you considered, to disallow sudo-style access, disabling bash always and creating custom tools as ports to vetted commands? The ease with which these can be defined is one of the most appealing things about the project.

@mlanza commented on GitHub (Dec 5, 2025): Sorry. I didn't read the entire thread, but have you considered, to disallow sudo-style access, disabling bash always and creating [custom tools](https://opencode.ai/docs/custom-tools) as ports to vetted commands? The ease with which these can be defined is one of the most appealing things about the project.
Author
Owner

@aspiers commented on GitHub (Dec 10, 2025):

@rekram1-node Will it catch commands like this?

$'\x65\x76\x61\x6c' "echo foo && echo bar"

This is an example of what I meant by my earlier comment:

I'm pretty sure that trying to make arbitrary shell 100% safe is an impossible game of whack-a-mole which is highly dependent on the individual user's needs and environment. In the above example, the fundamental problem is that it relies on static analysis, but bash is a dynamic language and its runtime effects can't be fully predicted in a reliable fashion purely through static analysis.

@aspiers commented on GitHub (Dec 10, 2025): @rekram1-node Will it catch commands like this? ```bash $'\x65\x76\x61\x6c' "echo foo && echo bar" ``` This is an example of what I meant by [my earlier comment](https://github.com/sst/opencode/issues/2206#issuecomment-3217170432): > I'm pretty sure that trying to make arbitrary shell 100% safe is an impossible game of whack-a-mole which is highly dependent on the individual user's needs and environment. In the above example, the fundamental problem is that it relies on static analysis, but bash is a dynamic language and its runtime effects can't be fully predicted in a reliable fashion purely through static analysis.
Author
Owner

@rekram1-node commented on GitHub (Dec 10, 2025):

it depends if you allow "$" or not

@rekram1-node commented on GitHub (Dec 10, 2025): it depends if you allow "$" or not
Author
Owner

@aspiers commented on GitHub (Dec 10, 2025):

It doesn't; you could just type eval instead. I was just demonstrating one way how an obvious eval-based attack could be obfuscated, and I'm sure there are countless other ways too.

@aspiers commented on GitHub (Dec 10, 2025): It doesn't; you could just type `eval` instead. I was just demonstrating one way how an obvious `eval`-based attack could be obfuscated, and I'm sure there are countless other ways too.
Author
Owner

@rekram1-node commented on GitHub (Dec 10, 2025):

Yeah ultimately if you're super concerned you'll be better off using a heavier sandbox, we can definitely do more in our approach but it's really difficult to catch any edge case. An agent could write javascript that deletes directories and run that, it's not foolproof by any means

@rekram1-node commented on GitHub (Dec 10, 2025): Yeah ultimately if you're super concerned you'll be better off using a heavier sandbox, we can definitely do more in our approach but it's really difficult to catch any edge case. An agent could write javascript that deletes directories and run that, it's not foolproof by any means
Author
Owner

@rekram1-node commented on GitHub (Dec 10, 2025):

Some people do mounting of volumes w/ docker, or use cloud sandboxes. I think there is more to come in the sandbox realm soon

@rekram1-node commented on GitHub (Dec 10, 2025): Some people do mounting of volumes w/ docker, or use cloud sandboxes. I think there is more to come in the sandbox realm soon
Author
Owner

@aspiers commented on GitHub (Dec 10, 2025):

Yeah ultimately if you're super concerned you'll be better off using a heavier sandbox

Sure.

we can definitely do more in our approach but it's really difficult to catch any edge case.

Basically I'm suggesting that it might be not just really difficult but actually impossible to properly secure the bash tool. Yes, the more sandboxing support we get the better, but in many cases all the agent wants to do is run a simple command like git status which doesn't even require wrapping in a bash shell. In those cases sandboxing is like using a sledgehammer to crack a walnut, and an exec tool would be a far simpler solution which follows the standard best practice security of not exposing an unnecessarily large attack surface.

An agent could write javascript that deletes directories and run that, it's not foolproof by any means

True, that's not quite the point though. I wouldn't put node or similar commands on the automatic allowlist for my bash or exec tool, so the agent wouldn't be able to slip javascript past me.

That said, upon further investigation I think the tree-sitter guard probably does a lot better than I originally thought and does actually catch the eval trick.

@aspiers commented on GitHub (Dec 10, 2025): > Yeah ultimately if you're super concerned you'll be better off using a heavier sandbox Sure. > we can definitely do more in our approach but it's really difficult to catch any edge case. Basically I'm suggesting that it might be not just really difficult but actually impossible to properly secure the bash tool. Yes, the more sandboxing support we get the better, but in many cases all the agent wants to do is run a simple command like `git status` which doesn't even require wrapping in a bash shell. In those cases sandboxing is like using a sledgehammer to crack a walnut, and an `exec` tool would be a far simpler solution which follows the standard best practice security of not exposing an unnecessarily large attack surface. > An agent could write javascript that deletes directories and run that, it's not foolproof by any means True, that's not quite the point though. I wouldn't put `node` or similar commands on the automatic allowlist for my `bash` or `exec` tool, so the agent wouldn't be able to slip javascript past me. That said, upon further investigation I think the tree-sitter guard probably does a lot better than I originally thought and does actually catch the eval trick.
Author
Owner

@lostmsu commented on GitHub (Feb 13, 2026):

A sidenote: on Windows I don't want to use bash at all. I want to use pwsh. Then the agent could decide which shell to use and call it via exec explicitly.

@lostmsu commented on GitHub (Feb 13, 2026): A sidenote: on Windows I don't want to use bash at all. I want to use pwsh. Then the agent could decide which shell to use and call it via exec explicitly.
Author
Owner

@smola commented on GitHub (Feb 14, 2026):

If you are looking at securing the agent (e.g. in YOLO mode), you'd want a sandbox, not an exec tool. See https://github.com/anomalyco/opencode/issues/2242

That being said, having implementing sandboxing in my fork, I still see the value of having an exec tool.bash tool will source ~/.bashrc et al. In a sandbox with read-only filesystem, this leads to quite a few popular developer tools generating noise in the output (e.g. pyenv rehash) because they require some write operation during initialization. An exec is much simpler, and would not lead to that, unless it's used itself to call bash.

@smola commented on GitHub (Feb 14, 2026): If you are looking at securing the agent (e.g. in YOLO mode), you'd want a sandbox, not an `exec` tool. See https://github.com/anomalyco/opencode/issues/2242 That being said, having implementing sandboxing in my fork, I still see the value of having an `exec` tool.`bash` tool will source `~/.bashrc` et al. In a sandbox with read-only filesystem, this leads to quite a few popular developer tools generating noise in the output (e.g. `pyenv rehash`) because they require some write operation during initialization. An `exec` is much simpler, and would not lead to that, unless it's used itself to call `bash`.
Author
Owner

@aspiers commented on GitHub (Feb 14, 2026):

On a related note, I have recently discovered https://github.com/Dicklesworthstone/destructive_command_guard which in several ways is a far superior approach to what any of the agent harnesses currently offer in terms of protecting against dangerous command invocations (and also part of a marvellous suite of related tools). The only potential downside is that it defaults to allowing commands it recognizes, although this can be changed.

That said, I still strongly believe in the need for an exec tool in specific cases where a shell isn't required.

@aspiers commented on GitHub (Feb 14, 2026): On a related note, I have recently discovered https://github.com/Dicklesworthstone/destructive_command_guard which in several ways is a far superior approach to what any of the agent harnesses currently offer in terms of protecting against dangerous command invocations (and also part of [a marvellous suite of related tools](https://agent-flywheel.com/)). The only potential downside is that [it defaults to allowing commands it recognizes](https://github.com/Dicklesworthstone/destructive_command_guard#2-fail-safe-defaults), although [this can be changed](https://github.com/Dicklesworthstone/destructive_command_guard#environment-variables). That said, I still strongly believe in the need for an `exec` tool in specific cases where a shell isn't required.
Author
Owner

@smola commented on GitHub (Feb 15, 2026):

@aspiers That sort of guardrails is a fool's errand. They are bound to miss all kinds of edge cases. For example, find has at least 4 flags that allow you to execute arbitrary commands (exec, execdir, ok, okdir). That project, as far as I can tell, does not detect all of them.
Any pattern-matching solution is bound to fail in one of the following ways (or both):

  • To err on the side of caution with edge cases, it ends up rejecting some safe commands that would have been useful.
  • To avoid nerfing the tool, it makes some compromise that ends up allowing destructive commands.
@smola commented on GitHub (Feb 15, 2026): @aspiers That sort of guardrails is a fool's errand. They are bound to miss all kinds of edge cases. For example, `find` has at least 4 flags that allow you to execute arbitrary commands (exec, execdir, ok, okdir). That project, as far as I can tell, does not detect all of them. Any pattern-matching solution is bound to fail in one of the following ways (or both): - To err on the side of caution with edge cases, it ends up rejecting some safe commands that would have been useful. - To avoid nerfing the tool, it makes some compromise that ends up allowing destructive commands.
Author
Owner

@aspiers commented on GitHub (Feb 16, 2026):

@smola I largely agree, which is not surprising because most of your points have already been stated either above or in other issues some of which are linked from above. For example see "Beyond an exec tool: the need for a full sandboxing solution" in the original issue, or #9516, or this statement above:

However even this is very vague and fundamentally unsafe. It's impossible for any kind of pattern-matching approach to guarantee that arbitrary shell code is both secure and not bypassing file access controls imposed on the agent.

That said, I disagree with the "fool's errand" categorisation:

  • Best practice is to implement defence in depth. For example if you have kernel-level sandboxing AND a scanner for destructive commands, that is better than relying solely on the sandbox.
  • Different people have different security requirements. In some scenarios, occasionally missing edge cases is an acceptable risk. In others, running the whole thing on isolated hardware can mean that operating in YOLO mode is fine. In fact I recently set up a VPS specifically for this.
  • DCG uses language-specific AST grammars rather than naive pattern matching.
  • DCG offers a rich set of patterns out of the box.
  • DCG encourages reusability of and collaboration on pattern sets.

This is not really any different from a million other discussions on the tradeoffs between security and convenience. There's no such thing as perfect security, and that's OK. Each approach is another tool in the box which gives users more options to find the right balance for them. In the case of this specific issue, the idea of an exec tool is simply to be able to allow specific commands which are known to be definitely safe (and this would of course exclude commands like find/xargs/timeout which can fork other commands) without requiring the huge extra attack surface of a shell process wrapped around them.

@aspiers commented on GitHub (Feb 16, 2026): @smola I largely agree, which is not surprising because most of your points have already been stated either above or in other issues some of which are linked from above. For example see "Beyond an exec tool: the need for a full sandboxing solution" in the original issue, or #9516, or this statement above: > However even this is very vague and fundamentally unsafe. It's impossible for any kind of pattern-matching approach to guarantee that arbitrary shell code is both secure and not bypassing file access controls imposed on the agent. That said, I disagree with the "fool's errand" categorisation: - Best practice is to implement defence in depth. For example if you have kernel-level sandboxing AND a scanner for destructive commands, that is better than relying solely on the sandbox. - Different people have different security requirements. In some scenarios, occasionally missing edge cases is an acceptable risk. In others, running the whole thing on isolated hardware can mean that operating in YOLO mode is fine. In fact I recently set up a VPS specifically for this. - DCG uses language-specific AST grammars rather than naive pattern matching. - DCG offers a rich set of patterns out of the box. - DCG encourages reusability of and collaboration on pattern sets. This is not really any different from a million other discussions on the tradeoffs between security and convenience. There's no such thing as perfect security, and that's OK. Each approach is another tool in the box which gives users more options to find the right balance for them. In the case of this specific issue, the idea of an `exec` tool is simply to be able to allow specific commands which are known to be definitely safe (and this would of course exclude commands like find/xargs/timeout which can fork other commands) without requiring the huge extra attack surface of a shell process wrapped around them.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: anomalyco/opencode#1460