Skip to content

feat(classifiers): optional classifyBashCommand behind /classifiers subpath#382

Merged
JackChen-me merged 1 commit into
mainfrom
feat/bash-risk-classifier
Jul 16, 2026
Merged

feat(classifiers): optional classifyBashCommand behind /classifiers subpath#382
JackChen-me merged 1 commit into
mainfrom
feat/bash-risk-classifier

Conversation

@JackChen-me

Copy link
Copy Markdown
Member

Implements #379 (the second split of #96). The per-call onToolCall gate landed
in #377; this adds the optional shell risk classifier that pairs with it.

What

  • classifyBashCommand(command): { level, reason }, level safe | review | high.
    Pure, dependency-free; no code copied from temodar-agent.
  • Exported behind a new @open-multi-agent/core/classifiers subpath so the
    pattern tables never load unless imported (answers Optional shell risk classifier for the onToolCall gate #379's packaging question:
    core subpath, not a separate package).

Classification

  • safe: read-only inspection (ls, cat, pwd, grep/rg with a path,
    git status|log|diff|show).
  • review: context bombs and unknowns, per the Proposal: Per-call risk gating for built-in shell/filesystem tools #96 discussion: ls -R,
    find / / find ~ without -maxdepth, grep -r / rg -r without a scoped
    path, tree, du, and any unrecognised command ("don't run blind").
  • high: destructive/sensitive: rm, sudo, curl | bash, dd, mkfs,
    chmod 777/-R, git push --force, npm publish, writes to system paths.

Compound commands are segmented on &&/||/;/|/newlines plus one level of
substitution, and the highest risk wins, so a safe prefix cannot smuggle a
destructive suffix (ls && rm -rf / becomes high). Quoted spans are stripped
first, so echo "rm -rf /" stays safe. Shallow heuristic, not a security
boundary; the tables are meant to be extended or replaced.

Pairs with the onToolCall gate from #377:

import { classifyBashCommand } from '@open-multi-agent/core/classifiers'

new OpenMultiAgent({
  onToolCall: async (ctx) => {
    if (ctx.toolName !== 'bash') return { action: 'allow' }
    const risk = classifyBashCommand(String(ctx.input.command))
    if (risk.level === 'safe') return { action: 'allow' }
    if (risk.level === 'high') return { action: 'deny', reason: risk.reason }
    return (await ui.confirm(ctx, risk)) ? { action: 'allow' } : { action: 'deny', reason: risk.reason }
  },
})

Scope

Classifier only. Docs and a runnable example are tracked separately in #380. No
change to the gate, tool grant, or existing behavior.

Tests

Core npm run lint and npm test are green (1312 tests). Adds table-driven
classifier tests (per level, compound-command escalation, false-positive guards,
return shape) and a subpath import smoke test.

Closes #379

…rs subpath

Ships an opt-in, dependency-free shell risk classifier for the onToolCall gate
(#377). classifyBashCommand(command) returns safe | review | high so users get
the taxonomy from #96 without writing regex tables by hand.

- safe: read-only inspection (ls, cat, grep/rg with a path, git status/log/diff)
- review: context bombs and unknowns (ls -R, find / without -maxdepth,
  grep -r / rg -r without scope, tree, du, unrecognised commands)
- high: destructive/sensitive (rm, sudo, curl | bash, dd, mkfs, chmod 777/-R,
  git push --force, npm publish)

Compound commands are segmented on shell separators and the highest risk wins;
quoted strings are stripped so patterns inside a string do not match. Convenience
heuristic, not a security boundary. Exported only via
@open-multi-agent/core/classifiers so pattern tables never load unless imported.

Closes #379
@JackChen-me
JackChen-me merged commit e6869fd into main Jul 16, 2026
7 checks passed
@JackChen-me
JackChen-me deleted the feat/bash-risk-classifier branch July 16, 2026 08:16
@JackChen-me
JackChen-me restored the feat/bash-risk-classifier branch July 16, 2026 08:37
@JackChen-me
JackChen-me deleted the feat/bash-risk-classifier branch July 16, 2026 08:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Optional shell risk classifier for the onToolCall gate

1 participant