Skip to content

About

OMP plugin: hard-block and ask-first filter for destructive bash commands (rm, git push/reset, sudo, publishes)

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

3 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

omp-bash-guard

A standalone Oh My Pi extension that intercepts every bash tool call and blocks or challenges destructive commands before they run.

How it works

The plugin registers a tool_call handler. On every bash call it splits the command into segments on ;, &&, ||, |, and newlines (splitBashSegments in bash-policy.ts), then checks each segment through three tiers, in order:

  1. Hard blocks (BASH_HARD_BLOCKS) — always denied, no override:
    • git commit, git push, git reset --hard, git push --force
    • cargo publish, npm publish, pnpm publish, bun publish, yarn publish
    • rm -rf /...
    • sudo
  2. Project blocks (BASH_PROJECT_BLOCKS) — denied only when the current working directory matches a project kind (detected via lib/project-kind.ts, which looks for Cargo.toml, package.json, prisma/schema.prisma):
    • cargo test in a Rust project → use cargo nextest run instead
  3. Ask-first (BASH_ASK) — denied with a reason telling the agent to surface the command to the user instead of running it itself:
    • rm, git reset, curl, wget, chmod, dd, eval, source
    • npm install, pnpm install, bun install, pip install, cargo install

Any segment that matches returns { block: true, reason } from the tool_call handler, which the harness surfaces back to the agent instead of executing the command. Commands that match nothing pass through untouched.

Script scanning

Blocking the literal bash command isn't enough — bash ship.sh or python3 deploy.py can hide the exact same destructive commands one level down. After the three tiers above pass, each segment is checked for a script invocation (extractScriptTarget in bash-policy.ts):

  • Interpreter form: bash <file>, sh <file>, zsh <file>, source <file>, . <file>, python <file> / python3 <file>, bun <file>, node <file>
  • Direct execution: ./file or an absolute path as the first token of the segment

If a segment matches, the file is resolved against the session's cwd (captured on session_start) and read from disk, capped at 1MB (node:fs openSync/readSync, no third-party deps). If the file can't be read, the call is blocked as ask-first — unreadable content is treated as unknown, not safe:

BLOCKED: '<segment>' invokes '<file>' ..., which could not be read. Its content is unknown — ask the user before running it.

Otherwise the content is scanned line by line (scanScriptContent):

  • Shell scripts (bash/sh/zsh/source/./bun/node/direct-exec targets that aren't .py) — every line is split with the same splitBashSegments and checked against BASH_HARD_BLOCKS then BASH_ASK, exactly like a top-level bash command.
  • Python scripts (python/python3/direct-exec targets ending in .py) additionally get:
    • shutil.rmtree(...), os.remove(...), os.unlink(...) calls — flagged as ask-first file/directory deletion.
    • os.system(...) and subprocess.call/run/Popen/check_call/check_output(...) calls whose string argument matches BASH_HARD_BLOCKS or BASH_ASK (e.g. os.system("git push"), subprocess.run("rm -rf /")) — flagged with the same reason as if that command had been run directly.

A match blocks with a reason naming both the script file and the offending line, e.g.:

BLOCKED: 'rm -rf /...' is catastrophic. Ask the user. Found in script '/tmp/evil.sh' (line 3), invoked via 'bash /tmp/evil.sh'.

Scripts with no match (e.g. a plain echo-only shell script) pass through untouched, same as any other command.

Install

omp plugin install github:gyoz-ai/omp-bash-guard

This runs bun install against ~/.omp/plugins, symlinks the package into node_modules, and validates that index.ts resolves and exports a plugin factory before enabling it.

Local development (symlink)

To work on this repo directly instead of through the plugin manager, symlink it into your OMP extensions directory — the repo root doubles as the extension directory (it contains index.ts at the top level, which is exactly what the loader expects for a single-plugin extension):

ln -s "$(pwd)/omp-bash-guard" ~/.omp/agent/extensions/bash-guard

(Use ~/.omp/agent/extensions/ for a user-wide install, or <project>/.omp/extensions/ to scope it to one project.)

The extension loads automatically on the next session, and OMP live-reloads it whenever index.ts or bash-policy.ts changes.

Files

  • index.ts — the plugin: registers the tool_call hook, tracks cwd/project kind across session_start, applies the three-tier bash policy, and resolves/reads script files for scanning (readScriptCapped, node:fs only).
  • bash-policy.ts — the policy data, splitBashSegments, and the script scanning logic (extractScriptTarget, scanScriptContent, PYTHON_ASK_PATTERNS): no side effects (no disk reads), easy to unit test or extend.
  • lib/project-kind.ts — vendored copy of the project-kind detector used for the Rust cargo test → cargo nextest run project block.

License

MIT — see LICENSE.

About

OMP plugin: hard-block and ask-first filter for destructive bash commands (rm, git push/reset, sudo, publishes)

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages