Bundle
dsh-barricade
Barricade: 编码 agent 的破坏性命令拦截闸门。在 rm -rf、git reset --hard 等命令真正落地前解析、判定并要求确认。dsh 插件 + 通用 CLI。
- Source
- JohnXu22786
- stars
- 2 stars
- License
- MIT
- Updated
- Updated 6 days ago
Readme
[简体中文](README.zh.md)
# Barricade
> A destructive-command interception gate for coding agents: it parses command semantics and judges risk **before** `rm -rf`, `git reset --hard`, `git push --force` and similar commands actually land, then requires human confirmation.
Barricade is a self-contained plugin/CLI with zero runtime dependencies (pure Node.js ESM), designed for any harness that "hands the shell over to an agent". It does not sandbox and does not limit capabilities; it does only one thing: **hold irreversible operations at a confirmation gate** — including those even a sandbox can't stop (`git reset --hard` discards the working tree, `git push --force` overwrites remote history, `rm -rf` deletes untracked files in the workspace).
## Capabilities at a Glance
- **Semantic-level command parsing**: not string matching. Ships its own POSIX lexer that recognizes quotes, escapes, heredocs, command substitution `$(...)`, sub-shells and pipeline chains; `bash -c "rm -rf /"`, `sudo rm -rf /`, `eval "rm -rf /"`, `echo $(rm -rf /)` cannot slip through.
- **Per-command verifiers**: git (reset/clean/push/checkout/branch/stash/restore — 13 dangerous forms, supporting long-flag unique prefixes and short-flag unbinding), rm (tiered by target scope: root/home/.git → fatal; outside workspace / dynamic targets → high; inside workspace → medium), dd/mkfs/shred/chmod/chown, find -delete, curl|sh, interpreter one-liners, fork bombs, PowerShell forced deletion — 41 built-in rules.
- **Three levels**: `relaxed` / `balanced` (default) / `vigilant`, mapping severity → action (deny/ask/allow) tier by tier; under `vigilant`, unparseable inputs ask as needed (fail-closed).
- **Interactive confirmation**: on a TTY it shows the command and matched rules, supporting run once / deny / allow for this session / allow permanently (written to policy) / show details; non-TTY environments always deny (fail-safe).
- **Portable across harnesses**: the verdict core is harness-agnostic (input `(command, cwd, level)`, output structured verdict); three integration forms to choose from: a dsh in-process plugin, a generic stdin-hook JSON contract, and a `gate` shell wrapper.
- **Audit**: interception and confirmation records are persisted as JSONL; secret-like content is automatically redacted.
## How It Works
```
agent 准备执行命令
│
▼
┌─────────────────┐ ┌──────────────┐
│ 接入点(任一) │──▶│ 命令分析引擎 │
│ dsh 插件事件 │ │ 分词 → 分段 │
│ stdin-hook │ │ 拆包装 → 判定 │
│ gate 包装 │ └──────┬───────┘
└─────────────────┘ │
▼
┌─ 放行 ──▶ 命令执行
│
判定结果
│
└─ 需确认 ─▶ TTY 交互确认 ──▶ 执行/拒绝
非 TTY:拒绝(失败安全)
```
Key steps of the analysis engine:
1. **Tokenize**: POSIX-style lexing (quotes/escapes/operators/heredoc bodies/command-substitution extraction); falls back to a coarse-grained pattern scan when input is over the limit or unparseable.
2. **Segment**: splits command segments by `&&` `||` `;` `|` `&` and sub-shells; tracks `cd` execution directories; each segment is judged independently, and any dangerous segment blocks the whole command.
3. **Unwrap**: recursively strips wrapper commands like `sudo` / `env` / `command` / `timeout` and embedded loads in `bash -c` / `sh -c` / `su -c` (depth limit 8; beyond the limit, ask as needed).
4. **Verdict merge**: fatal first; any deny → deny; any ask → ask; policy `overrides` can adjust high/medium actions, but **fatal rules cannot be downgraded**.
## Installation
Requires Node.js ≥ 18.13, no npm dependencies.
```bash
# 直接运行(无需安装)
node bin/barricade.js --help
# 作为命令行工具使用(可选)
npm link # 之后可直接使用 barricade 命令
```
## Integrating with dsh (DeepSeek Harness)
This repository is a valid dsh bundle: `package.json` declares `dsh.bundle`, `cordis.patch.yml` is the config-layer patch, and `plugin.js` is the plugin entry point.
### Installing in DSH
```bash
dsh plugin --profile demo add github:JohnXu22786/safety-net
```
### Loading
```bash
# 在目标 profile 中安装本 bundle(本地目录或已发布的 npm 包名)
dsh plugin --profile web add ../dsh-barricade # 或 dsh plugin --profile web add dsh-barricade
# 启动
dsh --profile web
```
After loading, Cordis inserts the plugin line per `cordis.patch.yml`:
```yaml
- insert:
- id: barricade
name: dsh-barricade
```
### Plugin Interface
| Item | Value |
|---|---|
| Entry | `plugin.js` (`main` field), exports `name` / `inject` / `apply(ctx, config)` |
| Events | listens to the tool execution pipeline event `tools/pre-execute` (waterfall), intervening before the tool actually runs |
| Interception | throws `BarricadeBlocked` when a block verdict is produced; the tool call fails and the reason is visible to the model |
| Config | the `config` field in `cordis.patch.yml`, or a `dsh --patch` overlay |
Available config keys (all optional):
| Key | Default | Description |
|---|---|---|
| `mode` | `"deny"` | `deny`: deny on match; `ask`: request human confirmation through the `ctx.approval` service; if confirmation is refused or the service is unavailable, treat as deny |
| `toolNames` | common shell tool name list | only intercept these tools; can also be overridden via the `BARRICADE_TOOLS` env var (comma-separated) |
| `commandPath` | `"args.command"` | dot-path to the command text in the tool call; compatible with `input.command` / `command` and other shapes |
| `level` | policy file | `relaxed` / `balanced` / `vigilant` |
Example (written into the profile's `cordis.patch.yml` or a `--patch` overlay):
```yaml
- insert:
- id: barricade
name: dsh-barricade
config:
mode: ask
toolNames: [bash, run_code, run_command]
```
> Note: dsh is currently in developer preview and its interfaces may evolve. `apply` defensively recognizes tool-call shapes (`name/tool`, `args/input`, etc.) and probes the various calling forms of `ctx.approval`; if any form is unavailable it falls back to deny, keeping fail-safe. If the upstream event contract changes, just adjust the event name and field paths in `plugin.js`.
### Other Harness Integration
The verdict core depends on no harness; pick any of the three forms:
**① stdin-hook contract** (for harnesses that support "run a hook before tool invocation", such as PreToolUse-style hooks):
```
stdin : {"command": "<待执行命令>", "cwd": "<可选>"} # 或纯命令文本
stdout : {"action": "allow|ask|deny", "severity": ..., "matches": [...], "warnings": [...]}
exit : 0(正常输出判定);加 --exit-on-block 时拦截退出 1
```
Example (point the hook command at `node <本目录>/bin/barricade.js hook`):
```bash
echo '{"command":"git push --force origin main"}' | node bin/barricade.js hook
# {"command":"git push --force origin main","action":"ask","severity":"high",
# "reason":"强制推送覆盖远端提交历史,可能造成他人工作丢失","matches":[...],"warnings":[]}
```
**② gate wrapper** (swap the harness's shell for `barricade gate -- <command>`): executes after terminal confirmation; non-terminal environments are blocked outright.
**③ in-process reuse**: `createInterceptor(config)` returns a pure verdict function callable from any Node in-process harness (see the comments at the top of `plugin.js`).
## CLI Usage
```
barricade <子命令> [选项]
analyze [--json] <命令> 分析并输出判定(不执行;退出码恒 0)
check [--json] [--quiet] <命令> 判定;放行退出 0,拦截退出 1
gate -- <命令> 分析 + 交互确认 + 执行
hook 见上文 stdin-hook 契约
policy --show [--json] 显示合并后的策略
policy --validate [--policy F] 校验策略文件
rules [--json] 列出内置规则
audit [--tail N] 查看审计记录
-c, --command <命令> --stdin 命令输入方式
--level <等级> --policy <F> 临时等级 / 指定策略文件
--json --quiet --exit-on-block
-h, --help -v, --version --tail <N>
```
Examples:
```bash
barricade check -c "rm -rf /" # 退出 1,打印拦截原因
barricade analyze --json -c "git reset --hard"
barricade gate -- "npm run build" # 终端下交互确认
```
## Policy Configuration
Config files: user-level `~/.barricade/barricade.json` (`BARRICADE_HOME` to change), project-level `.barricade.json` (current directory, takes precedence over user-level). Both are JSON; missing/corrupt fields **rescue-fall back to defaults** with a warning — a policy file can never interrupt your workflow.
```json
{
"version": 1,
"level": "balanced",
"failClosed": false,
"allowlist": ["git status", "git log", "ls -la"],
"overrides": { "git/tag-delete": "allow" },
"rules": [
{
"id": "custom/dropdb-force",
"command": "dropdb",
"args": ["--force"],
"severity": "high",
"reason": "强制删除数据库不可恢复"
}
],
"confirmation": { "sessionMemory": true, "timeoutSeconds": 0 }
}
```
| Field | Description |
|---|---|
| `level` | `relaxed` (medium allowed) / `balanced` (medium asks) / `vigilant` (+ unparseable input asks as needed) |
| `failClosed` | require confirmation even when a command cannot be parsed |
| `allowlist` | prefix allowlist (`git status` allows `git status --porcelain`) |
| `overrides` | adjust per-rule-id actions: `allow` / `ask` / `deny` / `off`; **fatal rules cannot be downgraded** |
| `rules` | custom rules: command + optional subcommand + any arg match (short-flag unbinding supported) |
| `confirmation.timeoutSeconds` | interactive confirmation timeout (seconds); timeout counts as deny; 0 means no timeout |
Environment variables (raise-only, never lower):
| Variable | Description |
|---|---|
| `BARRICADE_HOME` | data directory (policy, audit logs), default `~/.barricade` |
| `BARRICADE_POLICY` | specify a user policy file path |
| `BARRICADE_LEVEL` | raise the level (only takes effect when higher than the file level) |
| `BARRICADE_FAIL_CLOSED=1` | enable fail-closed |
| `BARRICADE_CONFIRM_TIMEOUT` | confirmation timeout seconds |
| `BARRICADE_TOOLS` | comma-separated list of tools the dsh plugin intercepts |
| `BARRICADE_NO_COLOR` / `NO_COLOR` | disable colored output |
## Interactive Confirmation
```
⚠️ Barricade 需要确认此命令 [高危]
命令: git push --force origin main
• git/push-force — 强制推送覆盖远端提交历史,可能造成他人工作丢失
[y] 执行一次 [n] 拒绝 [s] 本会话放行 [a] 永久放行 [d] 详情 [q] 退出
>
```
- `a` writes the rule into the user policy file (`overrides`); fatal rules cannot be allowed permanently.
- `s` records the rule into this invocation's session set; within a single `gate` call it confirms only once, so `s` is equivalent to `y`; it can take effect across calls when the same session set is reused in-process.
- Control characters are escaped and the command truncated before display, preventing terminal injection (the verdict text and audit logs are handled the same way).
## Built-in Rules (excerpt)
| Rule id | Severity | Description |
|---|---|---|
| `fs/rm-root` / `fs/rm-home` | fatal | delete root / home directory |
| `fs/rm-git` | fatal | delete/write/move `.git` internals |
| `fs/mkfs-device` / `fs/dd-device` | fatal/high | format or write block devices (safe targets such as `/dev/null` excluded) |
| `fs/rm-outside` / `fs/rm-dynamic` / `fs/rm-workspace` | high/high/medium | rm -rf target scope tiers |
| `fs/find-delete` / `fs/shred` / `fs/chmod-recursive` / `fs/chown-recursive` | high/high/medium/medium | bulk or recursive destructive operations |
| `git/reset-hard` / `git/clean-force` / `git/push-force` / `git/push-delete` | high/high/high/medium | overwrite history, discard uncommitted changes, delete remote branches |
| `git/checkout-force` / `git/checkout-discard` / `git/switch-force` / `git/restore-worktree` | high | discard working-tree changes |
| `git/branch-delete-force` / `git/stash-drop` / `git/stash-clear` / `git/tag-delete` | high/high/high/medium | unrecoverable ref/stash operations |
| `git/fetch-force` / `git/ssh-env` | medium/high | overwrite remote refs / GIT_SSH* combined with network subcommands |
| `shell/curl-pipe-sh` / `shell/fork-bomb` / `interp/embedded` | high | remote-script piping, fork bombs, interpreter-embedded deletion code |
| `sys/shutdown` / `sys/reboot` / `sys/powershell-remove` / `sys/cmd-del` | high/high/medium/medium | system-level operations |
The full list and action mapping: `barricade rules`.
## Security Model and Known Boundaries
- **Not a sandbox**, and not a privilege boundary. It intercepts "commands the harness issues through supported entry points"; bypassing the integration (e.g. writing files directly with an editor tool, executing manually outside a container) is out of protection scope.
- **Inherent limits of static analysis**: runtime-only content such as `bash <unknown script>` and `eval "$X"` cannot be inspected — under `vigilant` they require confirmation; under the default level they pass (tightenable with `failClosed` or custom rules).
- Analysis handles command text with POSIX path semantics, independent of the running platform; on Windows, `gate` runs via `cmd /c`, while the command itself is still parsed with POSIX syntax.
- Input size limit 128 KiB and nesting depth limit 8; beyond the limits it falls back to coarse-grained scanning or asks as needed, preventing malformed input from stalling analysis.
## Development
```bash
node --test # 或 npm test;测试用例见 test/ 目录
```
Structure:
```
bin/barricade.js CLI 入口
plugin.js dsh(Cordis)插件入口
src/
tokenizer.js 词法分析(引号/heredoc/命令替换)
analyzer.js 命令段组织、包装拆解、分命令判定
rules.js 内置规则库与等级映射
verdict.js 判定合并模型
policy.js 策略加载与挽救式校验
prompt.js 交互确认
audit.js 审计日志(脱敏)
executor.js gate 执行器
cordis.patch.yml dsh 配置层补丁
examples/ 示例配置与 hook 契约样例
test/ 181 个测试用例
```
## License
[MIT](LICENSE)Install
dsh plugin --profile web add github:JohnXu22786/safety-net#789ff47c8a1b8f397380e42280d621ef6b85da5f
Profile: web
With the hub plugin installed, ask your agent to install it by name — it resolves the same plan shown here.
dsh plugin --profile web add github:stvlynn/dsh.fish#path:packages/dsh-plugin-hub
install dsh-barricade from the hub