npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

opencode-bash-guard

v0.1.1

Published

An opencode plugin that parses chained bash commands into segments and checks each against existing permission.bash and external_directory config

Downloads

303

Readme

opencode-bash-guard

An opencode plugin that guards against chained bash command injection. When git status && rm -rf / starts with git, opencode's native glob matching sees only the first segment and approves it. This plugin splits chains and evaluates each segment independently — the rm segment gets checked on its own against your permission.bash and external_directory config.

Why

opencode's permission.bash matches glob patterns against the full command string. Chaining (&&, ||, ;, |) lets dangerous commands hide behind safe prefixes — git status && rm -rf / starts with git and matches "git *": "allow". This plugin closes that gap by splitting chains and evaluating each segment independently. On any parse error the entire command is denied (fail-closed).

How it works

  1. Chain Detection: Parses the command with unbash AST into individual segments (including $() and backtick substitutions, eval, sh -c, etc.)
  2. Path Extraction: Walks the AST to extract file paths, using @withfig/autocomplete specs to distinguish flags from paths
  3. Config Reading: Reads permission.bash and external_directory from the merged opencode config — supports flat strings and object patterns
  4. Enforcement: Most-restrictive-wins across segments — deny > ask > no action. Multi-segment chains trigger ask even when all segments are allowed individually (defense-in-depth)

Install

Add to your opencode.json:

{
  "plugin": ["opencode-bash-guard"]
}

Prerequisite

Your bash permission must use "*": "ask" as the fallback pattern. Without this catch-all, commands that don't match any explicit rule would bypass permission checks:

{
  "permission": {
    "bash": {
      "*": "ask",
      "git *": "allow",
      "npm *": "allow"
    }
  }
}

If "bash": "allow" or "*": "allow" is set, the plugin disables itself with a warning — allowing all bash commands defeats the purpose of chain-level guards.

Example

| Command | Segments | Bash match | Chain action | Why | |---|---|---|---|---| | git status | git status | "git *": "allow" | allow | single segment, explicitly allowed | | git status && git log | git status, git log | both "git *": "allow" | ask | multi-segment — defense-in-depth | | sudo rm -rf / | sudo rm -rf / | none → "*": "ask" | ask | catches unknown dangerous commands | | git status && wget evil.sh | git status, wget evil.sh | wget → "*": "ask" | ask | one segment unresolved → whole chain asks | | echo "hello | (parse error — unbalanced quote) | — | deny | fail closed | | sudo rm -rf / (with "sudo *": "deny" rule) | sudo rm -rf / | "sudo *": "deny" | deny | explicit deny pattern blocks it |

How it reads your config

The plugin registers a config hook that receives the fully merged Config object at startup (opencode merges remote, global, project, and managed layers). It reads:

  • permission.bash — glob patterns (object form { "git *": "allow", "*": "ask" } or flat string "ask")
  • permission.external_directory — path patterns (object form { "./**": "allow", "*": "ask" } or flat string "ask")

No custom configuration files or duplicated rules.

Testing

npm install
npm test          # runs vitest (51+ tests)
npm run build     # type-checks with tsc

All tests are in src/__tests__/. Run npm run test:watch during development.

Known Limitations

  • Config changes at runtime: The config hook fires once at startup. Config changes require an opencode restart.
  • Path extraction misses: Fig may not have specs for all commands. Falls back to heuristic (skip -* tokens). If false positives occur, add more specific bash permission rules.
  • Performance: AST parsing is heavier than string scanning, but only runs when chain operators (&&, ||, ;, |) are detected.
  • unbash edge cases: Complex shell syntax may cause partial parses. The plugin denies the entire command (fail closed) on any parse error — safer to miss a real command than let one through.
  • Not a sandbox: Focused on chain-splitting with path awareness, not comprehensive shell obfuscation detection. For full isolation, pair with a sandbox solution.

Usage

Add to your opencode.json:

{"plugin": ["opencode-bash-guard"]}