bash-classify 0.1.4__tar.gz → 0.2.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {bash_classify-0.1.4 → bash_classify-0.2.1}/.claude-plugin/marketplace.json +1 -1
- {bash_classify-0.1.4 → bash_classify-0.2.1}/PKG-INFO +6 -5
- {bash_classify-0.1.4 → bash_classify-0.2.1}/README.md +5 -4
- {bash_classify-0.1.4 → bash_classify-0.2.1}/SPEC.md +71 -75
- {bash_classify-0.1.4 → bash_classify-0.2.1}/coding-agent-plugins/claude-code/.claude-plugin/plugin.json +1 -1
- {bash_classify-0.1.4 → bash_classify-0.2.1}/coding-agent-plugins/claude-code/hooks/classify-bash.sh +1 -1
- {bash_classify-0.1.4 → bash_classify-0.2.1}/docs/classification-guidance.md +38 -32
- {bash_classify-0.1.4 → bash_classify-0.2.1}/schemas/command.schema.json +1 -1
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/classifier.py +3 -3
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/apt.yaml +3 -1
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/awk.yaml +2 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/basename.yaml +2 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/bash.yaml +2 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/brew.yaml +7 -5
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/builtin.yaml +2 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/cargo.yaml +10 -8
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/cat.yaml +2 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/chgrp.yaml +3 -1
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/chmod.yaml +3 -1
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/chown.yaml +3 -1
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/column.yaml +2 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/comm.yaml +2 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/command.yaml +2 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/cp.yaml +3 -1
- bash_classify-0.2.1/src/bash_classify/commands/curl.yaml +30 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/cut.yaml +2 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/date.yaml +2 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/df.yaml +2 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/diff.yaml +2 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/dig.yaml +2 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/dirname.yaml +2 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/docker.yaml +18 -17
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/du.yaml +2 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/echo.yaml +2 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/env.yaml +2 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/expand.yaml +2 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/false.yaml +2 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/file.yaml +2 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/find.yaml +2 -4
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/fmt.yaml +2 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/fold.yaml +2 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/free.yaml +2 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/gawk.yaml +2 -0
- bash_classify-0.2.1/src/bash_classify/commands/gh.yaml +615 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/git.yaml +90 -88
- bash_classify-0.2.1/src/bash_classify/commands/glab.yaml +740 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/go.yaml +10 -8
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/grep.yaml +2 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/groups.yaml +2 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/gunzip.yaml +3 -1
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/gzip.yaml +3 -1
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/head.yaml +2 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/helm.yaml +13 -11
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/hostname.yaml +3 -1
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/id.yaml +2 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/install.yaml +3 -1
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/join.yaml +2 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/journalctl.yaml +5 -3
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/jq.yaml +2 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/kill.yaml +2 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/killall.yaml +2 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/kubectl.yaml +23 -22
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/less.yaml +2 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/ln.yaml +3 -1
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/ls.yaml +2 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/make.yaml +3 -1
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/md5sum.yaml +2 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/mkdir.yaml +3 -1
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/more.yaml +2 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/mv.yaml +3 -1
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/netstat.yaml +2 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/nice.yaml +3 -1
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/nl.yaml +2 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/node.yaml +2 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/nohup.yaml +3 -1
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/npm.yaml +10 -8
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/nslookup.yaml +2 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/paste.yaml +2 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/ping.yaml +2 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/pip.yaml +6 -4
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/pip3.yaml +6 -4
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/pkill.yaml +2 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/poetry.yaml +17 -15
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/printenv.yaml +2 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/printf.yaml +2 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/ps.yaml +2 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/pwd.yaml +2 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/pytest.yaml +3 -1
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/python.yaml +0 -1
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/python3.yaml +0 -1
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/read.yaml +2 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/readlink.yaml +2 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/realpath.yaml +2 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/rev.yaml +2 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/rg.yaml +2 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/rm.yaml +2 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/rsync.yaml +3 -1
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/ruff.yaml +6 -4
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/scp.yaml +3 -1
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/sed.yaml +4 -2
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/seq.yaml +2 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/sh.yaml +2 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/sha1sum.yaml +2 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/sha256sum.yaml +2 -0
- bash_classify-0.2.1/src/bash_classify/commands/sort.yaml +10 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/ss.yaml +2 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/ssh.yaml +2 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/stat.yaml +2 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/strace.yaml +3 -1
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/sudo.yaml +2 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/systemctl.yaml +5 -3
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/tac.yaml +2 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/tail.yaml +2 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/tar.yaml +3 -1
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/tee.yaml +3 -1
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/terraform.yaml +9 -7
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/test.yaml +2 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/time.yaml +2 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/timeout.yaml +2 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/top.yaml +2 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/touch.yaml +3 -1
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/tr.yaml +2 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/traceroute.yaml +2 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/true.yaml +2 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/type.yaml +2 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/uname.yaml +2 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/unexpand.yaml +2 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/uniq.yaml +2 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/unzip.yaml +3 -1
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/uptime.yaml +2 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/uv.yaml +23 -21
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/wc.yaml +2 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/wget.yaml +3 -1
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/whereis.yaml +2 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/which.yaml +2 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/whoami.yaml +2 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/xargs.yaml +2 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/xmllint.yaml +2 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/yes.yaml +2 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/yq.yaml +4 -2
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/zip.yaml +3 -1
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/commands/zsh.yaml +2 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/matcher.py +35 -9
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/models.py +7 -5
- {bash_classify-0.1.4 → bash_classify-0.2.1}/tests/test_classifier.py +32 -18
- {bash_classify-0.1.4 → bash_classify-0.2.1}/tests/test_cli.py +1 -1
- {bash_classify-0.1.4 → bash_classify-0.2.1}/tests/test_cli_serialization.py +3 -3
- {bash_classify-0.1.4 → bash_classify-0.2.1}/tests/test_database.py +3 -3
- {bash_classify-0.1.4 → bash_classify-0.2.1}/tests/test_matcher.py +50 -4
- {bash_classify-0.1.4 → bash_classify-0.2.1}/tests/test_models.py +9 -8
- {bash_classify-0.1.4 → bash_classify-0.2.1}/tests/test_real_world.py +28 -28
- bash_classify-0.1.4/src/bash_classify/commands/curl.yaml +0 -28
- bash_classify-0.1.4/src/bash_classify/commands/sort.yaml +0 -8
- {bash_classify-0.1.4 → bash_classify-0.2.1}/.claude/rules/command-database.md +0 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/.editorconfig +0 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/.github/workflows/ci.yml +0 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/.github/workflows/publish.yml +0 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/.gitignore +0 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/.python-version +0 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/CLAUDE.md +0 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/LICENSE +0 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/coding-agent-plugins/claude-code/hooks/hooks.json +0 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/pyproject.toml +0 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/ruff.toml +0 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/__init__.py +0 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/__main__.py +0 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/cli.py +0 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/database.py +0 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/parser.py +0 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/src/bash_classify/py.typed +0 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/tests/__init__.py +0 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/tests/conftest.py +0 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/tests/test_parser.py +0 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/tests/test_schema.py +0 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/tests/test_smoke.py +0 -0
- {bash_classify-0.1.4 → bash_classify-0.2.1}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: bash-classify
|
|
3
|
-
Version: 0.1
|
|
3
|
+
Version: 0.2.1
|
|
4
4
|
Summary: Classify bash commands as READONLY, WRITE, DANGEROUS, or UNKNOWN
|
|
5
5
|
Project-URL: Homepage, https://github.com/fprochazka/bash-classify
|
|
6
6
|
Project-URL: Repository, https://github.com/fprochazka/bash-classify
|
|
@@ -33,7 +33,7 @@ Classify bash commands by their side-effect risk level.
|
|
|
33
33
|
|
|
34
34
|
## What it does
|
|
35
35
|
|
|
36
|
-
bash-classify parses bash expressions using tree-sitter, classifies each command against a database of 120+ known commands, and outputs a structured JSON verdict. Commands are classified into
|
|
36
|
+
bash-classify parses bash expressions using tree-sitter, classifies each command against a database of 120+ known commands, and outputs a structured JSON verdict. Commands are classified into five levels: `READONLY`, `LOCAL_EFFECTS`, `EXTERNAL_EFFECTS`, `DANGEROUS`, and `UNKNOWN`.
|
|
37
37
|
|
|
38
38
|
Designed primarily as a [Claude Code](https://docs.anthropic.com/en/docs/claude-code) hook to automatically allow safe, read-only commands while flagging risky ones for human review.
|
|
39
39
|
|
|
@@ -82,7 +82,7 @@ claude plugin marketplace update fprochazka-bash-classify
|
|
|
82
82
|
claude plugin update bash-classify-hook@fprochazka-bash-classify
|
|
83
83
|
```
|
|
84
84
|
|
|
85
|
-
Once installed, any Bash tool call classified as `READONLY` is auto-approved — no permission prompt. Everything else (
|
|
85
|
+
Once installed, any Bash tool call classified as `READONLY` is auto-approved — no permission prompt. Everything else (LOCAL_EFFECTS, EXTERNAL_EFFECTS, DANGEROUS, UNKNOWN) still requires confirmation.
|
|
86
86
|
|
|
87
87
|
## Command database
|
|
88
88
|
|
|
@@ -95,8 +95,9 @@ The classification database includes 120+ command definitions covering common Un
|
|
|
95
95
|
|
|
96
96
|
| Level | Description | Examples |
|
|
97
97
|
|---|---|---|
|
|
98
|
-
| `READONLY` | No side effects,
|
|
99
|
-
| `
|
|
98
|
+
| `READONLY` | No side effects, auto-approved | `ls`, `cat`, `grep`, `kubectl get` |
|
|
99
|
+
| `LOCAL_EFFECTS` | Modifies local files or state only | `git add`, `git commit`, `cp`, `mkdir`, `pytest` |
|
|
100
|
+
| `EXTERNAL_EFFECTS` | Interacts with external systems | `git push`, `kubectl apply`, `curl -d` |
|
|
100
101
|
| `DANGEROUS` | Destructive, system-wide, or irreversible | `rm -rf`, `git push --force`, `chmod` |
|
|
101
102
|
| `UNKNOWN` | Command not in database | Any unrecognized command |
|
|
102
103
|
|
|
@@ -4,7 +4,7 @@ Classify bash commands by their side-effect risk level.
|
|
|
4
4
|
|
|
5
5
|
## What it does
|
|
6
6
|
|
|
7
|
-
bash-classify parses bash expressions using tree-sitter, classifies each command against a database of 120+ known commands, and outputs a structured JSON verdict. Commands are classified into
|
|
7
|
+
bash-classify parses bash expressions using tree-sitter, classifies each command against a database of 120+ known commands, and outputs a structured JSON verdict. Commands are classified into five levels: `READONLY`, `LOCAL_EFFECTS`, `EXTERNAL_EFFECTS`, `DANGEROUS`, and `UNKNOWN`.
|
|
8
8
|
|
|
9
9
|
Designed primarily as a [Claude Code](https://docs.anthropic.com/en/docs/claude-code) hook to automatically allow safe, read-only commands while flagging risky ones for human review.
|
|
10
10
|
|
|
@@ -53,7 +53,7 @@ claude plugin marketplace update fprochazka-bash-classify
|
|
|
53
53
|
claude plugin update bash-classify-hook@fprochazka-bash-classify
|
|
54
54
|
```
|
|
55
55
|
|
|
56
|
-
Once installed, any Bash tool call classified as `READONLY` is auto-approved — no permission prompt. Everything else (
|
|
56
|
+
Once installed, any Bash tool call classified as `READONLY` is auto-approved — no permission prompt. Everything else (LOCAL_EFFECTS, EXTERNAL_EFFECTS, DANGEROUS, UNKNOWN) still requires confirmation.
|
|
57
57
|
|
|
58
58
|
## Command database
|
|
59
59
|
|
|
@@ -66,8 +66,9 @@ The classification database includes 120+ command definitions covering common Un
|
|
|
66
66
|
|
|
67
67
|
| Level | Description | Examples |
|
|
68
68
|
|---|---|---|
|
|
69
|
-
| `READONLY` | No side effects,
|
|
70
|
-
| `
|
|
69
|
+
| `READONLY` | No side effects, auto-approved | `ls`, `cat`, `grep`, `kubectl get` |
|
|
70
|
+
| `LOCAL_EFFECTS` | Modifies local files or state only | `git add`, `git commit`, `cp`, `mkdir`, `pytest` |
|
|
71
|
+
| `EXTERNAL_EFFECTS` | Interacts with external systems | `git push`, `kubectl apply`, `curl -d` |
|
|
71
72
|
| `DANGEROUS` | Destructive, system-wide, or irreversible | `rm -rf`, `git push --force`, `chmod` |
|
|
72
73
|
| `UNKNOWN` | Command not in database | Any unrecognized command |
|
|
73
74
|
|
|
@@ -93,15 +93,14 @@ $ echo 'find . -name "*.tmp" -exec rm -f {} \;' | bash-classify
|
|
|
93
93
|
"argv": ["find", ".", "-name", "*.tmp", "-exec", "rm", "-f", "{}", ";"],
|
|
94
94
|
"classification": "DANGEROUS",
|
|
95
95
|
"matched_rule": "find",
|
|
96
|
-
"overriding_option": "-exec",
|
|
97
96
|
"inner_commands": [
|
|
98
97
|
{
|
|
99
98
|
"delegation_mode": "terminated_argv",
|
|
100
99
|
"delegation_source": "-exec",
|
|
101
100
|
"command": ["rm"],
|
|
102
101
|
"argv": ["rm", "-f"],
|
|
103
|
-
"classification": "
|
|
104
|
-
"matched_rule":
|
|
102
|
+
"classification": "DANGEROUS",
|
|
103
|
+
"matched_rule": "rm",
|
|
105
104
|
"inner_commands": []
|
|
106
105
|
}
|
|
107
106
|
]
|
|
@@ -196,7 +195,8 @@ $ echo 'sh -c "ls /tmp | grep log"' | bash-classify
|
|
|
196
195
|
| Level | Meaning |
|
|
197
196
|
|---|---|
|
|
198
197
|
| `READONLY` | Only reads data, no side effects |
|
|
199
|
-
| `
|
|
198
|
+
| `LOCAL_EFFECTS` | Modifies local files or state only |
|
|
199
|
+
| `EXTERNAL_EFFECTS` | Interacts with external systems or network |
|
|
200
200
|
| `DANGEROUS` | Destructive, hard to reverse, or affects critical systems |
|
|
201
201
|
| `UNKNOWN` | Command or subcommand not in database |
|
|
202
202
|
|
|
@@ -205,10 +205,10 @@ $ echo 'sh -c "ls /tmp | grep log"' | bash-classify
|
|
|
205
205
|
The overall `classification` of a full expression is the **maximum severity** across all commands in the expression:
|
|
206
206
|
|
|
207
207
|
```
|
|
208
|
-
DANGEROUS > UNKNOWN >
|
|
208
|
+
DANGEROUS > UNKNOWN > EXTERNAL_EFFECTS > LOCAL_EFFECTS > READONLY
|
|
209
209
|
```
|
|
210
210
|
|
|
211
|
-
`UNKNOWN` is ranked above `
|
|
211
|
+
`UNKNOWN` is ranked above `EXTERNAL_EFFECTS` because an unrecognized command should not be silently trusted — it must be reviewed.
|
|
212
212
|
|
|
213
213
|
## Bash Parsing
|
|
214
214
|
|
|
@@ -237,7 +237,7 @@ The parser walks the CST and extracts a list of `CommandInvocation` objects, eac
|
|
|
237
237
|
| Subshells `(cmd)` | Inner command extracted recursively |
|
|
238
238
|
| Heredocs `<<EOF` | Content captured but not parsed as commands |
|
|
239
239
|
| Variable assignments `X=1 cmd` | Prefix assignments stripped, `cmd` extracted |
|
|
240
|
-
| Backgrounding `cmd &` | Classified as
|
|
240
|
+
| Backgrounding `cmd &` | Classified as EXTERNAL_EFFECTS (side effect: background process) |
|
|
241
241
|
|
|
242
242
|
### Constructs that force DANGEROUS
|
|
243
243
|
|
|
@@ -352,15 +352,15 @@ subcommands:
|
|
|
352
352
|
can-i:
|
|
353
353
|
classification: READONLY
|
|
354
354
|
reconcile:
|
|
355
|
-
classification:
|
|
355
|
+
classification: EXTERNAL_EFFECTS
|
|
356
356
|
|
|
357
357
|
apply:
|
|
358
|
-
classification:
|
|
358
|
+
classification: EXTERNAL_EFFECTS
|
|
359
359
|
options:
|
|
360
360
|
--dry-run: {overrides: READONLY}
|
|
361
361
|
|
|
362
362
|
create:
|
|
363
|
-
classification:
|
|
363
|
+
classification: EXTERNAL_EFFECTS
|
|
364
364
|
|
|
365
365
|
delete:
|
|
366
366
|
classification: DANGEROUS
|
|
@@ -368,13 +368,13 @@ subcommands:
|
|
|
368
368
|
--dry-run: {overrides: READONLY}
|
|
369
369
|
|
|
370
370
|
edit:
|
|
371
|
-
classification:
|
|
371
|
+
classification: EXTERNAL_EFFECTS
|
|
372
372
|
|
|
373
373
|
patch:
|
|
374
|
-
classification:
|
|
374
|
+
classification: EXTERNAL_EFFECTS
|
|
375
375
|
|
|
376
376
|
scale:
|
|
377
|
-
classification:
|
|
377
|
+
classification: EXTERNAL_EFFECTS
|
|
378
378
|
|
|
379
379
|
rollout:
|
|
380
380
|
subcommands:
|
|
@@ -383,40 +383,40 @@ subcommands:
|
|
|
383
383
|
history:
|
|
384
384
|
classification: READONLY
|
|
385
385
|
restart:
|
|
386
|
-
classification:
|
|
386
|
+
classification: EXTERNAL_EFFECTS
|
|
387
387
|
undo:
|
|
388
388
|
classification: DANGEROUS
|
|
389
389
|
pause:
|
|
390
|
-
classification:
|
|
390
|
+
classification: EXTERNAL_EFFECTS
|
|
391
391
|
resume:
|
|
392
|
-
classification:
|
|
392
|
+
classification: EXTERNAL_EFFECTS
|
|
393
393
|
|
|
394
394
|
exec:
|
|
395
395
|
classification: DANGEROUS
|
|
396
396
|
|
|
397
397
|
port-forward:
|
|
398
|
-
classification:
|
|
398
|
+
classification: EXTERNAL_EFFECTS
|
|
399
399
|
|
|
400
400
|
cp:
|
|
401
|
-
classification:
|
|
401
|
+
classification: EXTERNAL_EFFECTS
|
|
402
402
|
|
|
403
403
|
drain:
|
|
404
404
|
classification: DANGEROUS
|
|
405
405
|
|
|
406
406
|
cordon:
|
|
407
|
-
classification:
|
|
407
|
+
classification: EXTERNAL_EFFECTS
|
|
408
408
|
|
|
409
409
|
uncordon:
|
|
410
|
-
classification:
|
|
410
|
+
classification: EXTERNAL_EFFECTS
|
|
411
411
|
|
|
412
412
|
taint:
|
|
413
|
-
classification:
|
|
413
|
+
classification: EXTERNAL_EFFECTS
|
|
414
414
|
|
|
415
415
|
label:
|
|
416
|
-
classification:
|
|
416
|
+
classification: EXTERNAL_EFFECTS
|
|
417
417
|
|
|
418
418
|
annotate:
|
|
419
|
-
classification:
|
|
419
|
+
classification: EXTERNAL_EFFECTS
|
|
420
420
|
|
|
421
421
|
config:
|
|
422
422
|
subcommands:
|
|
@@ -427,15 +427,15 @@ subcommands:
|
|
|
427
427
|
current-context:
|
|
428
428
|
classification: READONLY
|
|
429
429
|
use-context:
|
|
430
|
-
classification:
|
|
430
|
+
classification: EXTERNAL_EFFECTS
|
|
431
431
|
set:
|
|
432
|
-
classification:
|
|
432
|
+
classification: EXTERNAL_EFFECTS
|
|
433
433
|
set-context:
|
|
434
|
-
classification:
|
|
434
|
+
classification: EXTERNAL_EFFECTS
|
|
435
435
|
set-cluster:
|
|
436
|
-
classification:
|
|
436
|
+
classification: EXTERNAL_EFFECTS
|
|
437
437
|
set-credentials:
|
|
438
|
-
classification:
|
|
438
|
+
classification: EXTERNAL_EFFECTS
|
|
439
439
|
delete-context:
|
|
440
440
|
classification: DANGEROUS
|
|
441
441
|
delete-cluster:
|
|
@@ -471,34 +471,34 @@ subcommands:
|
|
|
471
471
|
branch:
|
|
472
472
|
classification: READONLY
|
|
473
473
|
options:
|
|
474
|
-
-d: {overrides:
|
|
474
|
+
-d: {overrides: EXTERNAL_EFFECTS}
|
|
475
475
|
-D: {overrides: DANGEROUS}
|
|
476
|
-
--delete: {overrides:
|
|
477
|
-
--move: {overrides:
|
|
478
|
-
--copy: {overrides:
|
|
479
|
-
--edit-description: {overrides:
|
|
480
|
-
--set-upstream-to: {overrides:
|
|
481
|
-
--unset-upstream: {overrides:
|
|
476
|
+
--delete: {overrides: EXTERNAL_EFFECTS}
|
|
477
|
+
--move: {overrides: EXTERNAL_EFFECTS}
|
|
478
|
+
--copy: {overrides: EXTERNAL_EFFECTS}
|
|
479
|
+
--edit-description: {overrides: EXTERNAL_EFFECTS}
|
|
480
|
+
--set-upstream-to: {overrides: EXTERNAL_EFFECTS, takes_value: true}
|
|
481
|
+
--unset-upstream: {overrides: EXTERNAL_EFFECTS}
|
|
482
482
|
remote:
|
|
483
483
|
classification: READONLY
|
|
484
484
|
subcommands:
|
|
485
485
|
add:
|
|
486
|
-
classification:
|
|
486
|
+
classification: EXTERNAL_EFFECTS
|
|
487
487
|
remove:
|
|
488
|
-
classification:
|
|
488
|
+
classification: EXTERNAL_EFFECTS
|
|
489
489
|
rename:
|
|
490
|
-
classification:
|
|
490
|
+
classification: EXTERNAL_EFFECTS
|
|
491
491
|
set-url:
|
|
492
|
-
classification:
|
|
492
|
+
classification: EXTERNAL_EFFECTS
|
|
493
493
|
prune:
|
|
494
|
-
classification:
|
|
494
|
+
classification: EXTERNAL_EFFECTS
|
|
495
495
|
tag:
|
|
496
496
|
classification: READONLY
|
|
497
497
|
options:
|
|
498
|
-
-d: {overrides:
|
|
499
|
-
--delete: {overrides:
|
|
500
|
-
-a: {overrides:
|
|
501
|
-
-s: {overrides:
|
|
498
|
+
-d: {overrides: EXTERNAL_EFFECTS}
|
|
499
|
+
--delete: {overrides: EXTERNAL_EFFECTS}
|
|
500
|
+
-a: {overrides: EXTERNAL_EFFECTS}
|
|
501
|
+
-s: {overrides: EXTERNAL_EFFECTS}
|
|
502
502
|
blame:
|
|
503
503
|
classification: READONLY
|
|
504
504
|
shortlog:
|
|
@@ -519,49 +519,49 @@ subcommands:
|
|
|
519
519
|
classification: READONLY
|
|
520
520
|
|
|
521
521
|
add:
|
|
522
|
-
classification:
|
|
522
|
+
classification: EXTERNAL_EFFECTS
|
|
523
523
|
commit:
|
|
524
|
-
classification:
|
|
524
|
+
classification: EXTERNAL_EFFECTS
|
|
525
525
|
merge:
|
|
526
|
-
classification:
|
|
526
|
+
classification: EXTERNAL_EFFECTS
|
|
527
527
|
options:
|
|
528
528
|
--abort: {overrides: DANGEROUS}
|
|
529
529
|
rebase:
|
|
530
|
-
classification:
|
|
530
|
+
classification: EXTERNAL_EFFECTS
|
|
531
531
|
options:
|
|
532
532
|
--abort: {overrides: DANGEROUS}
|
|
533
533
|
cherry-pick:
|
|
534
|
-
classification:
|
|
534
|
+
classification: EXTERNAL_EFFECTS
|
|
535
535
|
options:
|
|
536
536
|
--abort: {overrides: DANGEROUS}
|
|
537
537
|
|
|
538
538
|
push:
|
|
539
|
-
classification:
|
|
539
|
+
classification: EXTERNAL_EFFECTS
|
|
540
540
|
options:
|
|
541
541
|
--force: {overrides: DANGEROUS}
|
|
542
542
|
-f: {overrides: DANGEROUS}
|
|
543
|
-
--force-with-lease: {overrides:
|
|
543
|
+
--force-with-lease: {overrides: EXTERNAL_EFFECTS}
|
|
544
544
|
--delete: {overrides: DANGEROUS}
|
|
545
545
|
|
|
546
546
|
pull:
|
|
547
|
-
classification:
|
|
547
|
+
classification: EXTERNAL_EFFECTS
|
|
548
548
|
|
|
549
549
|
fetch:
|
|
550
|
-
classification:
|
|
550
|
+
classification: EXTERNAL_EFFECTS
|
|
551
551
|
options:
|
|
552
|
-
--prune: {overrides:
|
|
552
|
+
--prune: {overrides: EXTERNAL_EFFECTS}
|
|
553
553
|
|
|
554
554
|
checkout:
|
|
555
|
-
classification:
|
|
555
|
+
classification: EXTERNAL_EFFECTS
|
|
556
556
|
|
|
557
557
|
switch:
|
|
558
|
-
classification:
|
|
558
|
+
classification: EXTERNAL_EFFECTS
|
|
559
559
|
|
|
560
560
|
restore:
|
|
561
|
-
classification:
|
|
561
|
+
classification: EXTERNAL_EFFECTS
|
|
562
562
|
|
|
563
563
|
stash:
|
|
564
|
-
classification:
|
|
564
|
+
classification: EXTERNAL_EFFECTS
|
|
565
565
|
subcommands:
|
|
566
566
|
list:
|
|
567
567
|
classification: READONLY
|
|
@@ -573,18 +573,18 @@ subcommands:
|
|
|
573
573
|
classification: DANGEROUS
|
|
574
574
|
|
|
575
575
|
reset:
|
|
576
|
-
classification:
|
|
576
|
+
classification: EXTERNAL_EFFECTS
|
|
577
577
|
options:
|
|
578
578
|
--hard: {overrides: DANGEROUS}
|
|
579
579
|
|
|
580
580
|
revert:
|
|
581
|
-
classification:
|
|
581
|
+
classification: EXTERNAL_EFFECTS
|
|
582
582
|
|
|
583
583
|
clean:
|
|
584
584
|
classification: DANGEROUS
|
|
585
585
|
|
|
586
586
|
rm:
|
|
587
|
-
classification:
|
|
587
|
+
classification: EXTERNAL_EFFECTS
|
|
588
588
|
options:
|
|
589
589
|
-r: {overrides: DANGEROUS}
|
|
590
590
|
```
|
|
@@ -598,22 +598,18 @@ strict: false # find has too many predicates to enumerate
|
|
|
598
598
|
options:
|
|
599
599
|
-delete: {overrides: DANGEROUS}
|
|
600
600
|
-exec:
|
|
601
|
-
overrides: DANGEROUS
|
|
602
601
|
delegates_to:
|
|
603
602
|
mode: terminated_argv
|
|
604
603
|
terminator: ";" # tokens between -exec and \; are the inner command
|
|
605
604
|
-execdir:
|
|
606
|
-
overrides: DANGEROUS
|
|
607
605
|
delegates_to:
|
|
608
606
|
mode: terminated_argv
|
|
609
607
|
terminator: ";"
|
|
610
608
|
-ok:
|
|
611
|
-
overrides: WRITE
|
|
612
609
|
delegates_to:
|
|
613
610
|
mode: terminated_argv
|
|
614
611
|
terminator: ";"
|
|
615
612
|
-okdir:
|
|
616
|
-
overrides: WRITE
|
|
617
613
|
delegates_to:
|
|
618
614
|
mode: terminated_argv
|
|
619
615
|
terminator: ";"
|
|
@@ -668,10 +664,10 @@ options:
|
|
|
668
664
|
|
|
669
665
|
```yaml
|
|
670
666
|
command: sudo
|
|
671
|
-
classification:
|
|
667
|
+
classification: EXTERNAL_EFFECTS # sudo itself elevates privileges
|
|
672
668
|
delegates_to:
|
|
673
669
|
mode: rest_are_argv
|
|
674
|
-
min_classification:
|
|
670
|
+
min_classification: EXTERNAL_EFFECTS # inner command is at least EXTERNAL_EFFECTS regardless of its own classification
|
|
675
671
|
options:
|
|
676
672
|
-u: {takes_value: true}
|
|
677
673
|
--user: {takes_value: true, aliases: [-u]}
|
|
@@ -749,7 +745,7 @@ The `delegates_to` field defines how a command (or option like `find -exec`) han
|
|
|
749
745
|
| `terminator` | `string` | For `terminated_argv`: the token that ends the inner argv |
|
|
750
746
|
| `flag` | `string` | For `flag_value_is_expression`: which flag's value to parse |
|
|
751
747
|
| `strip_assignments` | `bool` | For `rest_are_argv`: strip leading `KEY=VALUE` tokens before the inner command |
|
|
752
|
-
| `min_classification` | `enum` | Floor classification for the inner command (e.g. `sudo` forces at least `
|
|
748
|
+
| `min_classification` | `enum` | Floor classification for the inner command (e.g. `sudo` forces at least `EXTERNAL_EFFECTS`) |
|
|
753
749
|
|
|
754
750
|
### Database field reference
|
|
755
751
|
|
|
@@ -781,13 +777,13 @@ Output redirects affect the overall classification:
|
|
|
781
777
|
|
|
782
778
|
| Redirect | Effect |
|
|
783
779
|
|---|---|
|
|
784
|
-
| `> file` | Elevates to at least `
|
|
785
|
-
| `>> file` | Elevates to at least `
|
|
780
|
+
| `> file` | Elevates to at least `EXTERNAL_EFFECTS` |
|
|
781
|
+
| `>> file` | Elevates to at least `EXTERNAL_EFFECTS` |
|
|
786
782
|
| `> /dev/null` | No effect (discarding output is not a write) |
|
|
787
|
-
| `2> file` / `2>> file` | Elevates to at least `
|
|
788
|
-
| `&> file` | Elevates to at least `
|
|
783
|
+
| `2> file` / `2>> file` | Elevates to at least `EXTERNAL_EFFECTS` (unless `/dev/null`) |
|
|
784
|
+
| `&> file` | Elevates to at least `EXTERNAL_EFFECTS` (unless `/dev/null`) |
|
|
789
785
|
| `< file` | No effect (input redirect is reading) |
|
|
790
|
-
| `\| tee file` | `tee` is classified as `
|
|
786
|
+
| `\| tee file` | `tee` is classified as `EXTERNAL_EFFECTS` via its own database entry |
|
|
791
787
|
|
|
792
788
|
## Directory Detection
|
|
793
789
|
|
|
@@ -829,13 +825,13 @@ The `commands` list is recursive — any command entry can contain `inner_comman
|
|
|
829
825
|
```json
|
|
830
826
|
{
|
|
831
827
|
"expression": "string — the original input",
|
|
832
|
-
"classification": "READONLY |
|
|
828
|
+
"classification": "READONLY | LOCAL_EFFECTS | EXTERNAL_EFFECTS | DANGEROUS | UNKNOWN",
|
|
833
829
|
"directories": ["string — detected directories"],
|
|
834
830
|
"commands": [
|
|
835
831
|
{
|
|
836
832
|
"command": ["string — binary + subcommand chain (without inner command tokens)"],
|
|
837
833
|
"argv": ["string — full original argv for this command"],
|
|
838
|
-
"classification": "READONLY |
|
|
834
|
+
"classification": "READONLY | LOCAL_EFFECTS | EXTERNAL_EFFECTS | DANGEROUS | UNKNOWN",
|
|
839
835
|
"matched_rule": "string — dotted path in database, or null",
|
|
840
836
|
"ignored_options": ["string — global options that were stripped"],
|
|
841
837
|
"remaining_options": ["string — options that were not in database"],
|
{bash_classify-0.1.4 → bash_classify-0.2.1}/coding-agent-plugins/claude-code/hooks/classify-bash.sh
RENAMED
|
@@ -39,6 +39,6 @@ if [[ "$classification" == "READONLY" ]]; then
|
|
|
39
39
|
echo '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"allow"}}'
|
|
40
40
|
fi
|
|
41
41
|
|
|
42
|
-
# For anything else (
|
|
42
|
+
# For anything else (LOCAL_EFFECTS, EXTERNAL_EFFECTS, DANGEROUS, UNKNOWN), output nothing
|
|
43
43
|
# — this lets the normal permission flow handle it
|
|
44
44
|
exit 0
|
|
@@ -16,11 +16,17 @@ No side effects. Safe to auto-allow without user confirmation.
|
|
|
16
16
|
|
|
17
17
|
Examples: `ls`, `cat`, `grep`, `kubectl get`, `git status`, `git log`, `docker ps`, `terraform plan`, `curl https://example.com`
|
|
18
18
|
|
|
19
|
-
###
|
|
19
|
+
### LOCAL_EFFECTS
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
Modifies local state only (files, git index, local config). No network or external system interaction. Requires user confirmation.
|
|
22
22
|
|
|
23
|
-
Examples: `git commit`, `cp`, `touch`, `mkdir`, `
|
|
23
|
+
Examples: `git add`, `git commit`, `cp`, `touch`, `mkdir`, `sed -i`, `chmod`
|
|
24
|
+
|
|
25
|
+
### EXTERNAL_EFFECTS
|
|
26
|
+
|
|
27
|
+
Creates, modifies, or deletes data beyond the local machine, or interacts with external systems. Requires user confirmation.
|
|
28
|
+
|
|
29
|
+
Examples: `git push`, `kubectl apply`, `docker build`, `curl -d '...'`, `npm publish`
|
|
24
30
|
|
|
25
31
|
### DANGEROUS
|
|
26
32
|
|
|
@@ -35,10 +41,10 @@ Command or subcommand not in the database. Treated as requiring confirmation.
|
|
|
35
41
|
### Severity Ordering
|
|
36
42
|
|
|
37
43
|
```
|
|
38
|
-
DANGEROUS > UNKNOWN >
|
|
44
|
+
DANGEROUS > UNKNOWN > EXTERNAL_EFFECTS > LOCAL_EFFECTS > READONLY
|
|
39
45
|
```
|
|
40
46
|
|
|
41
|
-
UNKNOWN is ranked **above
|
|
47
|
+
UNKNOWN is ranked **above EXTERNAL_EFFECTS** because an unrecognized command should not be silently trusted -- it must be reviewed. A known EXTERNAL_EFFECTS command (like `git push`) is predictable; an unknown command could do anything.
|
|
42
48
|
|
|
43
49
|
The overall classification of a full expression (e.g. a pipeline) is the **maximum severity** across all commands in the expression.
|
|
44
50
|
|
|
@@ -55,8 +61,8 @@ description: "Stream editor" # (optional) short one-liner
|
|
|
55
61
|
classification: READONLY # (optional, default READONLY) base classification
|
|
56
62
|
strict: false # (optional, default true) unrecognized options -> UNKNOWN?
|
|
57
63
|
options: # options that affect classification
|
|
58
|
-
-i: {overrides:
|
|
59
|
-
--in-place: {overrides:
|
|
64
|
+
-i: {overrides: EXTERNAL_EFFECTS} # -i changes classification to EXTERNAL_EFFECTS
|
|
65
|
+
--in-place: {overrides: EXTERNAL_EFFECTS, aliases: [-i]}
|
|
60
66
|
-e: {takes_value: true} # -e consumes the next token as its value
|
|
61
67
|
--expression: {takes_value: true, aliases: [-e]}
|
|
62
68
|
-f: {takes_value: true}
|
|
@@ -101,7 +107,7 @@ Add `# $schema: ../../../schemas/command.schema.json` as the first line of every
|
|
|
101
107
|
- The command is a pure filter/transformer (`grep`, `awk`, `sed` without `-i`, `jq`, `sort` without `-o`)
|
|
102
108
|
- The command queries a remote system without changing it (`kubectl get`, `curl` without `-d`/`-F`/`-o`, `dig`, `ping`)
|
|
103
109
|
|
|
104
|
-
### Use
|
|
110
|
+
### Use EXTERNAL_EFFECTS when:
|
|
105
111
|
|
|
106
112
|
- The command creates or modifies files (`touch`, `cp`, `mv`, `mkdir`, `tee`)
|
|
107
113
|
- The command modifies local state (`git commit`, `git checkout`, `kubectl apply`)
|
|
@@ -149,21 +155,21 @@ There are two distinct mechanisms for controlling classification — don't confu
|
|
|
149
155
|
Each subcommand has its **own** `classification` field. It does not inherit from or override the parent — it simply IS the classification for that subcommand. No special keyword is needed.
|
|
150
156
|
|
|
151
157
|
```yaml
|
|
152
|
-
# git.worktree is
|
|
158
|
+
# git.worktree is EXTERNAL_EFFECTS, but git.worktree.list is READONLY
|
|
153
159
|
# No "overrides" keyword — list has its own classification
|
|
154
160
|
subcommands:
|
|
155
161
|
worktree:
|
|
156
|
-
classification:
|
|
162
|
+
classification: EXTERNAL_EFFECTS
|
|
157
163
|
subcommands:
|
|
158
164
|
list:
|
|
159
165
|
classification: READONLY # independent, not an override
|
|
160
166
|
add:
|
|
161
|
-
classification:
|
|
167
|
+
classification: EXTERNAL_EFFECTS
|
|
162
168
|
remove:
|
|
163
|
-
classification:
|
|
169
|
+
classification: EXTERNAL_EFFECTS
|
|
164
170
|
```
|
|
165
171
|
|
|
166
|
-
The parent's classification (`
|
|
172
|
+
The parent's classification (`EXTERNAL_EFFECTS`) is used only when **no subcommand matches** — e.g., bare `git worktree` or `git worktree unknown-thing`.
|
|
167
173
|
|
|
168
174
|
### Option overrides: replace the matched subcommand's classification
|
|
169
175
|
|
|
@@ -174,15 +180,15 @@ The `overrides` keyword is for **options** (flags) that change the classificatio
|
|
|
174
180
|
A flag that makes a normally safe command destructive:
|
|
175
181
|
|
|
176
182
|
```yaml
|
|
177
|
-
# git push is
|
|
183
|
+
# git push is EXTERNAL_EFFECTS, but --force makes it DANGEROUS
|
|
178
184
|
command: git
|
|
179
185
|
subcommands:
|
|
180
186
|
push:
|
|
181
|
-
classification:
|
|
187
|
+
classification: EXTERNAL_EFFECTS
|
|
182
188
|
options:
|
|
183
189
|
--force: {overrides: DANGEROUS}
|
|
184
190
|
-f: {overrides: DANGEROUS}
|
|
185
|
-
--force-with-lease: {overrides:
|
|
191
|
+
--force-with-lease: {overrides: EXTERNAL_EFFECTS} # safer force push stays EXTERNAL_EFFECTS
|
|
186
192
|
```
|
|
187
193
|
|
|
188
194
|
```yaml
|
|
@@ -190,7 +196,7 @@ subcommands:
|
|
|
190
196
|
command: sed
|
|
191
197
|
classification: READONLY
|
|
192
198
|
options:
|
|
193
|
-
-i: {overrides:
|
|
199
|
+
-i: {overrides: EXTERNAL_EFFECTS}
|
|
194
200
|
```
|
|
195
201
|
|
|
196
202
|
### Lowering classification
|
|
@@ -198,28 +204,28 @@ options:
|
|
|
198
204
|
A flag that makes a normally writing command safe to auto-allow:
|
|
199
205
|
|
|
200
206
|
```yaml
|
|
201
|
-
# kubectl apply is
|
|
207
|
+
# kubectl apply is EXTERNAL_EFFECTS, but --dry-run only prints what would happen
|
|
202
208
|
subcommands:
|
|
203
209
|
apply:
|
|
204
|
-
classification:
|
|
210
|
+
classification: EXTERNAL_EFFECTS
|
|
205
211
|
options:
|
|
206
212
|
--dry-run: {takes_value: true, overrides: READONLY}
|
|
207
213
|
```
|
|
208
214
|
|
|
209
215
|
```yaml
|
|
210
|
-
# tar is
|
|
216
|
+
# tar is EXTERNAL_EFFECTS (creates/extracts archives), but -t only lists contents
|
|
211
217
|
command: tar
|
|
212
|
-
classification:
|
|
218
|
+
classification: EXTERNAL_EFFECTS
|
|
213
219
|
options:
|
|
214
220
|
-t: {overrides: READONLY}
|
|
215
221
|
--list: {overrides: READONLY, aliases: [-t]}
|
|
216
222
|
```
|
|
217
223
|
|
|
218
224
|
```yaml
|
|
219
|
-
# git branch is
|
|
225
|
+
# git branch is EXTERNAL_EFFECTS (creates branches), but -l only lists them
|
|
220
226
|
subcommands:
|
|
221
227
|
branch:
|
|
222
|
-
classification:
|
|
228
|
+
classification: EXTERNAL_EFFECTS
|
|
223
229
|
options:
|
|
224
230
|
-l: {overrides: READONLY}
|
|
225
231
|
--list: {overrides: READONLY, aliases: [-l]}
|
|
@@ -251,10 +257,10 @@ options:
|
|
|
251
257
|
|
|
252
258
|
```yaml
|
|
253
259
|
command: sudo
|
|
254
|
-
classification:
|
|
260
|
+
classification: EXTERNAL_EFFECTS
|
|
255
261
|
delegates_to:
|
|
256
262
|
mode: rest_are_argv
|
|
257
|
-
min_classification:
|
|
263
|
+
min_classification: EXTERNAL_EFFECTS # inner command is at least EXTERNAL_EFFECTS
|
|
258
264
|
```
|
|
259
265
|
|
|
260
266
|
**env:** `env FOO=bar BAZ=1 python script.py` -- strips `FOO=bar BAZ=1`, inner command is `["python", "script.py"]`
|
|
@@ -331,7 +337,7 @@ options:
|
|
|
331
337
|
|
|
332
338
|
### `min_classification`
|
|
333
339
|
|
|
334
|
-
Forces the inner command to be classified at least at the given level. sudo uses this to ensure that even `sudo ls` is at least
|
|
340
|
+
Forces the inner command to be classified at least at the given level. sudo uses this to ensure that even `sudo ls` is at least EXTERNAL_EFFECTS -- because running anything under elevated privileges is not a no-op.
|
|
335
341
|
|
|
336
342
|
## 7. Special Cases
|
|
337
343
|
|
|
@@ -339,7 +345,7 @@ Forces the inner command to be classified at least at the given level. sudo uses
|
|
|
339
345
|
|
|
340
346
|
Some commands default to a higher classification when used without a recognized subcommand:
|
|
341
347
|
|
|
342
|
-
- **kubectl** -- base `
|
|
348
|
+
- **kubectl** -- base `EXTERNAL_EFFECTS` (bare `kubectl` without a known subcommand should not be auto-allowed)
|
|
343
349
|
- **terraform** -- base `DANGEROUS` (unknown terraform subcommands could modify infrastructure)
|
|
344
350
|
- **docker** -- no explicit base classification, so commands like `docker unknown-thing` fall through as UNKNOWN
|
|
345
351
|
|
|
@@ -367,13 +373,13 @@ The first line `# $schema: ../../../schemas/command.schema.json` is a convention
|
|
|
367
373
|
| Pattern | Example | Classification |
|
|
368
374
|
|---------|---------|---------------|
|
|
369
375
|
| Pure reader | `cat`, `grep`, `ls` | READONLY |
|
|
370
|
-
| Filter with in-place mode | `sed` base, `sed -i` | READONLY /
|
|
371
|
-
| File creator/modifier | `cp`, `mv`, `touch` |
|
|
376
|
+
| Filter with in-place mode | `sed` base, `sed -i` | READONLY / EXTERNAL_EFFECTS |
|
|
377
|
+
| File creator/modifier | `cp`, `mv`, `touch` | EXTERNAL_EFFECTS |
|
|
372
378
|
| Subcommand-driven | `git`, `kubectl` | Per subcommand |
|
|
373
379
|
| Arbitrary code executor | `python`, `sh`, `eval` | DANGEROUS |
|
|
374
380
|
| Wrapper/delegator | `sudo`, `xargs`, `env` | Delegation-based |
|
|
375
|
-
| Lister with create mode | `git branch`, `git tag` |
|
|
376
|
-
| Dry-run capable | `make`, `kubectl apply` |
|
|
381
|
+
| Lister with create mode | `git branch`, `git tag` | EXTERNAL_EFFECTS base, `-l` overrides to READONLY |
|
|
382
|
+
| Dry-run capable | `make`, `kubectl apply` | EXTERNAL_EFFECTS base, `--dry-run` overrides to READONLY |
|
|
377
383
|
| Network tool (read) | `curl`, `ping`, `dig` | READONLY |
|
|
378
|
-
| Network tool (write) | `curl -d`, `wget` |
|
|
384
|
+
| Network tool (write) | `curl -d`, `wget` | EXTERNAL_EFFECTS |
|
|
379
385
|
| System admin | `systemctl`, `kill` | DANGEROUS |
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"$defs": {
|
|
42
42
|
"Classification": {
|
|
43
43
|
"type": "string",
|
|
44
|
-
"enum": ["READONLY", "
|
|
44
|
+
"enum": ["READONLY", "LOCAL_EFFECTS", "EXTERNAL_EFFECTS", "DANGEROUS", "UNKNOWN"],
|
|
45
45
|
"description": "Classification level for a command or option override"
|
|
46
46
|
},
|
|
47
47
|
"DelegationConfig": {
|