hermes-sfw 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.
@@ -0,0 +1,28 @@
1
+ ---
2
+ name: Bug Report
3
+ about: Report a bug in hermes-sfw
4
+ title: ''
5
+ labels: bug
6
+ assignees: ''
7
+ ---
8
+
9
+ **Describe the bug**
10
+ A clear description of what the bug is.
11
+
12
+ **To reproduce**
13
+ Steps to reproduce the behavior:
14
+ 1. ...
15
+ 2. ...
16
+
17
+ **Expected behavior**
18
+ What you expected to happen.
19
+
20
+ **Environment**
21
+ - OS: [e.g. Ubuntu 24.04, macOS 15]
22
+ - Python version: [e.g. 3.12.3]
23
+ - Hermes version: [e.g. 0.5.0]
24
+ - hermes-sfw version: [e.g. 0.1.0]
25
+ - sfw version: [e.g. 1.0.0]
26
+
27
+ **Additional context**
28
+ Any other context (logs, config, etc.).
@@ -0,0 +1,16 @@
1
+ ---
2
+ name: Feature Request
3
+ about: Suggest a feature for hermes-sfw
4
+ title: ''
5
+ labels: enhancement
6
+ assignees: ''
7
+ ---
8
+
9
+ **Description**
10
+ What you'd like to happen.
11
+
12
+ **Use case**
13
+ Why this would be useful.
14
+
15
+ **Alternatives considered**
16
+ Any workarounds or alternative approaches you've thought of.
@@ -0,0 +1,23 @@
1
+ ## Description
2
+
3
+ What does this PR do?
4
+
5
+ ## Type of change
6
+
7
+ - [ ] Bug fix
8
+ - [ ] New feature
9
+ - [ ] Breaking change
10
+ - [ ] Documentation update
11
+
12
+ ## Testing
13
+
14
+ - [ ] Tests pass (`pytest`)
15
+ - [ ] Formatting passes (`black --check`)
16
+ - [ ] Type check passes (`mypy`)
17
+ - [ ] New tests added (if bug fix)
18
+
19
+ ## Checklist
20
+
21
+ - [ ] Code follows project style
22
+ - [ ] Self-review completed
23
+ - [ ] Documentation updated (if needed)
@@ -0,0 +1,43 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ concurrency:
10
+ group: ci-${{ github.ref }}
11
+ cancel-in-progress: true
12
+
13
+ permissions:
14
+ contents: read
15
+
16
+ jobs:
17
+ lint:
18
+ name: Format & Type Check
19
+ runs-on: ubuntu-latest
20
+ steps:
21
+ - uses: actions/checkout@v4
22
+ - uses: actions/setup-python@v5
23
+ with:
24
+ python-version: "3.13"
25
+ - run: pip install black==26.5.1 mypy==1.16.0
26
+ - run: black --check src/hermes_sfw/ tests/
27
+ - run: mypy src/hermes_sfw/
28
+
29
+ test:
30
+ name: Tests (Python ${{ matrix.python-version }})
31
+ runs-on: ubuntu-latest
32
+ strategy:
33
+ matrix:
34
+ python-version: ["3.11", "3.12", "3.13"]
35
+ steps:
36
+ - uses: actions/checkout@v4
37
+ - uses: actions/setup-python@v5
38
+ with:
39
+ python-version: ${{ matrix.python-version }}
40
+ - run: pip install -e '.[dev]'
41
+ - run: black --check src/hermes_sfw/ tests/
42
+ - run: mypy src/hermes_sfw/
43
+ - run: pytest
@@ -0,0 +1,40 @@
1
+ # Python
2
+ __pycache__/
3
+ *.pyc
4
+ *.pyo
5
+ *.egg-info/
6
+ dist/
7
+ build/
8
+ .eggs/
9
+
10
+ # Virtual environments
11
+ .venv/
12
+ venv/
13
+
14
+ # Testing
15
+ .pytest_cache/
16
+ .mypy_cache/
17
+ .ruff_cache/
18
+
19
+ # IDE
20
+ .vscode/
21
+ .idea/
22
+
23
+ # OS
24
+ .DS_Store
25
+ Thumbs.db
26
+
27
+ # Runtime data (secrets, credentials, logs — NEVER commit)
28
+ **/data/
29
+ **/sockets/
30
+ *.tmp
31
+ *.lock
32
+ !**/uv.lock
33
+
34
+ # Environment
35
+ .env
36
+ .env.*
37
+
38
+ # Hermes runtime (if symlinked from install)
39
+ *.log
40
+ node_modules
@@ -0,0 +1,26 @@
1
+ # Changelog
2
+
3
+ ## [0.2.1] - 2026-07-13
4
+
5
+ - Fix the release artifact path so the tagged build can reach PyPI.
6
+
7
+ ## [0.2.0] - 2026-07-13
8
+
9
+ - Route package-manager commands through Hermes dangerous-command approval checks.
10
+ - Fail closed when the approval system is unavailable.
11
+ - Block package-manager subcommands that directly execute arbitrary commands.
12
+ - Validate `verbose` as a strict boolean.
13
+ - Add packaged-plugin discovery metadata and manifest tool declarations.
14
+
15
+ ## 0.1.0 — Initial release
16
+
17
+ - `sfw` tool — run package manager commands through Socket Firewall Free
18
+ - `sfw status` — check installation and version
19
+ - Command prefix allowlist (npm, yarn, pnpm, pip, cargo, etc.)
20
+ - Output parsing for blocked/installed package indicators
21
+ - Working directory validation with path traversal prevention
22
+ - Output truncation at 10K chars with size notes
23
+ - Timeout protection (default 5 minutes)
24
+ - `OSError` errno mapping for clean error messages
25
+ - `shlex.split()` command parsing with error handling
26
+ - Tests covering status, run, validation, parsing, timeout, and edge cases
@@ -0,0 +1,64 @@
1
+ # Contributing
2
+
3
+ PRs welcome. Here's the workflow.
4
+
5
+ ## Setup
6
+
7
+ ```bash
8
+ git clone https://github.com/TheEpTic/hermes-plugins.git
9
+ cd hermes-plugins/hermes-sfw
10
+ python -m venv .venv && source .venv/bin/activate
11
+ pip install -e '.[dev]'
12
+ ```
13
+
14
+ ## Development
15
+
16
+ ```bash
17
+ black src/hermes_sfw/ tests/
18
+ mypy src/hermes_sfw/
19
+ pytest
20
+ ```
21
+
22
+ ## Testing
23
+
24
+ Run the full test suite with:
25
+
26
+ ```bash
27
+ pytest
28
+ ```
29
+
30
+ Aim for **90%+ coverage** on new code. The test suite uses pytest fixtures defined in `tests/conftest.py` for mocking subprocess calls. When adding new functionality, write tests that cover:
31
+
32
+ - Happy path (valid input, successful execution)
33
+ - Error cases (missing params, invalid commands, timeouts)
34
+ - Edge cases (empty output, long output, special characters)
35
+
36
+ ## Guidelines
37
+
38
+ - **Tests required for bug fixes.** Each fix gets a test that reproduces the issue.
39
+ - **Separate PRs for separate concerns.** Don't bundle unrelated changes.
40
+ - Run `black` and `mypy` before pushing. CI will catch it if you don't.
41
+
42
+ ## Project Structure
43
+
44
+ ```
45
+ src/hermes_sfw/
46
+ ├── __init__.py # Plugin registration + Hermes hooks
47
+ ├── manager.py # SFWManager — command execution, parsing, validation
48
+ ├── schemas.py # Tool schema (what the LLM sees)
49
+ ├── utils.py # ok(), err(), require() helpers
50
+ ├── py.typed # PEP 561 marker
51
+ └── handlers/
52
+ ├── __init__.py
53
+ └── sfw.py # sfw tool handler
54
+ tests/
55
+ ├── conftest.py # Shared fixtures
56
+ └── test_sfw.py # Full test suite
57
+ ```
58
+
59
+ ## Architecture
60
+
61
+ - **`SFWManager`** owns all state. No module-level mutable state.
62
+ - **Handlers** are thin wrappers — validate params, dispatch to manager, return JSON.
63
+ - **`utils.py`** provides `ok()`, `err()`, `require()` to eliminate boilerplate.
64
+ - **Command allowlist** prevents arbitrary command execution through sfw.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 TheEpTic
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION, CONTRACT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,219 @@
1
+ Metadata-Version: 2.4
2
+ Name: hermes-sfw
3
+ Version: 0.2.1
4
+ Summary: Socket Firewall Free wrapper for Hermes — block malicious dependencies during install
5
+ Project-URL: Homepage, https://github.com/TheEpTic/hermes-plugins
6
+ Project-URL: Repository, https://github.com/TheEpTic/hermes-plugins
7
+ Project-URL: Issues, https://github.com/TheEpTic/hermes-plugins/issues
8
+ Project-URL: Changelog, https://github.com/TheEpTic/hermes-plugins/blob/main/hermes-sfw/CHANGELOG.md
9
+ Author-email: TheEpTic <nexus@eptic.me>
10
+ License-Expression: MIT
11
+ License-File: LICENSE
12
+ Keywords: agent,cargo,dependencies,hermes,npm,pip,plugin,security,sfw
13
+ Classifier: Development Status :: 3 - Alpha
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Topic :: System :: Systems Administration
21
+ Classifier: Typing :: Typed
22
+ Requires-Python: >=3.11
23
+ Provides-Extra: dev
24
+ Requires-Dist: black==26.5.1; extra == 'dev'
25
+ Requires-Dist: mypy==1.16.0; extra == 'dev'
26
+ Requires-Dist: pytest==9.0.3; extra == 'dev'
27
+ Description-Content-Type: text/markdown
28
+
29
+ # hermes-sfw
30
+
31
+ [![CI](https://github.com/TheEpTic/hermes-plugins/actions/workflows/ci.yml/badge.svg)](https://github.com/TheEpTic/hermes-plugins/actions/workflows/ci.yml)
32
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
33
+ [![Python 3.11+](https://img.shields.io/badge/python-3.11+-blue.svg)](https://www.python.org/downloads/)
34
+
35
+ Socket Firewall Free plugin for [Hermes Agent](https://github.com/NousResearch/hermes-agent).
36
+
37
+ Block malicious dependencies at install time. Wrap any package manager command with `sfw` to get automatic protection — no API key, no config.
38
+
39
+ ```
40
+ sfw action=run command="npm install express"
41
+ sfw action=status
42
+ ```
43
+
44
+ ## Quick Start
45
+
46
+ > **Requires Python 3.11+** and the [sfw CLI](https://github.com/SocketDev/sfw-free) installed on the host system.
47
+
48
+ ### Option 1: Deploy script (recommended)
49
+
50
+ ```bash
51
+ git clone https://github.com/TheEpTic/hermes-plugins.git
52
+ cd hermes-plugins/hermes-sfw
53
+ ./deploy.sh
54
+ ```
55
+
56
+ Then restart Hermes with `/reset`.
57
+
58
+ ### Option 2: Manual symlink
59
+
60
+ ```bash
61
+ git clone https://github.com/TheEpTic/hermes-plugins.git
62
+ ln -s "$(pwd)/hermes-plugins/hermes-sfw/src/hermes_sfw" ~/.hermes/plugins/hermes-sfw
63
+ ```
64
+
65
+ Then `/reset` in Hermes. The symlink points at the source tree, but Python modules are imported once, so code changes still require `/reset` or a Hermes process restart before they load.
66
+
67
+ ### Option 3: As a Python package
68
+
69
+ ```bash
70
+ pip install git+https://github.com/TheEpTic/hermes-plugins.git#subdirectory=hermes-sfw
71
+ ```
72
+
73
+ Then enable it and restart Hermes:
74
+
75
+ ```bash
76
+ hermes plugins enable hermes-sfw
77
+ ```
78
+
79
+ ## Features
80
+
81
+ ### `sfw run` — Execute Commands
82
+
83
+ Run any package manager command through sfw. Malicious packages are blocked automatically.
84
+
85
+ ```bash
86
+ # Install a package
87
+ sfw action=run command="npm install express"
88
+
89
+ # Uninstall
90
+ sfw action=run command="npm uninstall lodash"
91
+
92
+ # Python packages
93
+ sfw action=run command="pip install flask"
94
+ sfw action=run command="uv pip install -r requirements.txt"
95
+
96
+ # Rust crates
97
+ sfw action=run command="cargo add serde"
98
+
99
+ # With verbose output
100
+ sfw action=run command="pnpm add -D vitest" verbose=true
101
+
102
+ # In a specific directory
103
+ sfw action=run command="npm install" workdir="/path/to/project"
104
+ ```
105
+
106
+ **Supported package managers:** npm, yarn, pnpm (JS/TS), pip, pip3, uv (Python), cargo, rustup (Rust). `npx` is intentionally blocked because it can execute arbitrary package code.
107
+
108
+ **Blocked packages:** When sfw detects a malicious package, the install is blocked and the package name is returned in the response. Non-package-manager commands (like `cat`, `rm`, `curl`) are rejected by the prefix allowlist.
109
+
110
+ **Output truncation:** Output exceeding 10,000 characters is automatically truncated with a size note.
111
+
112
+ ### `sfw status` — Check Installation
113
+
114
+ Verify sfw is installed and get the version.
115
+
116
+ ```bash
117
+ sfw action=status
118
+ ```
119
+
120
+ Returns: `installed` (bool), `version` (string), `binary` (path).
121
+
122
+ ## How It Works
123
+
124
+ hermes-sfw is a thin wrapper around the [sfw CLI](https://github.com/SocketDev/sfw-free). It:
125
+
126
+ 1. Validates the command starts with an allowed package manager prefix
127
+ 2. Resolves and validates the working directory (if specified)
128
+ 3. Executes the command through `sfw` with timeout protection
129
+ 4. Parses stdout/stderr for blocked and installed package indicators
130
+ 5. Returns structured JSON with success status, output, and parsed results
131
+
132
+ ## Configuration
133
+
134
+ All settings live in `src/hermes_sfw/manager.py` as an `SFWConfig` dataclass:
135
+
136
+ | Setting | Default | Description |
137
+ |---------|---------|-------------|
138
+ | `sfw_bin` | `sfw` | Path to the sfw binary |
139
+ | `timeout` | 300s | Max seconds per command |
140
+
141
+ ## Architecture
142
+
143
+ ```
144
+ src/hermes_sfw/
145
+ ├── __init__.py # Plugin registration + Hermes hooks
146
+ ├── manager.py # SFWManager — command execution + output parsing
147
+ ├── schemas.py # Tool schema (what the LLM sees)
148
+ ├── utils.py # ok(), err(), require() helpers
149
+ ├── py.typed # PEP 561 marker
150
+ └── handlers/
151
+ ├── __init__.py
152
+ └── sfw.py # sfw tool handler
153
+ ```
154
+
155
+ **Key design decisions:**
156
+
157
+ - `SFWManager` owns all state. No module-level mutable state.
158
+ - Command prefix allowlist prevents arbitrary command execution through sfw.
159
+ - `shlex.split()` parsing with error handling catches malformed commands early.
160
+ - Output sanitization truncates long outputs to prevent context overflow.
161
+ - `OSError` errno mapping provides clean error messages without leaking internals.
162
+
163
+ ## Security
164
+
165
+ See [SECURITY.md](SECURITY.md) for the full picture.
166
+
167
+ **Defaults you should know about:**
168
+
169
+ - Only package manager commands are allowed (prefix allowlist: npm, yarn, pnpm, pip, cargo, etc.)
170
+ - Non-package-manager commands (`cat`, `rm`, `curl`, etc.) are rejected
171
+ - Commands run with the permissions of the Hermes agent process
172
+
173
+ **Hardening applied:**
174
+
175
+ - Command prefix validation via allowlist before execution
176
+ - `shlex.split()` with error handling prevents shell injection
177
+ - Workdir resolved with `os.path.realpath()` to prevent path traversal
178
+ - Output truncated at 10K chars to prevent context overflow
179
+ - Timeout protection prevents hanging installs
180
+
181
+ ## Requirements
182
+
183
+ - Python 3.11+
184
+ - [sfw CLI](https://github.com/SocketDev/sfw-free) installed on PATH
185
+ - [Hermes Agent](https://github.com/NousResearch/hermes-agent)
186
+
187
+ ## Troubleshooting
188
+
189
+ **"sfw is not installed"**
190
+ Install sfw globally: `npm i -g sfw`. The plugin searches PATH and common locations (`~/.local/share/pnpm/bin/`, `/usr/local/bin/`, `~/.npm-global/bin/`).
191
+
192
+ **Command rejected with "not allowed"**
193
+ Only package manager commands are allowed. If you need to add a prefix, modify `_ALLOWED_PREFIXES` in `manager.py`.
194
+
195
+ **Command timeout**
196
+ Default timeout is 5 minutes (300s). For very large installs, this may not be enough. Override via `SFWConfig(timeout=...)` when creating the manager.
197
+
198
+ **Output looks truncated**
199
+ This is intentional — outputs over 10K chars are truncated with a size note. The full output is in the raw stdout/stderr fields.
200
+
201
+ ## Development
202
+
203
+ ```bash
204
+ git clone https://github.com/TheEpTic/hermes-plugins.git
205
+ cd hermes-plugins/hermes-sfw
206
+ python -m venv .venv && source .venv/bin/activate
207
+ pip install -e '.[dev]'
208
+
209
+ # Run checks
210
+ black --check src/hermes_sfw/ tests/
211
+ mypy src/hermes_sfw/
212
+ pytest
213
+ ```
214
+
215
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
216
+
217
+ ## License
218
+
219
+ MIT — see [LICENSE](LICENSE).
@@ -0,0 +1,191 @@
1
+ # hermes-sfw
2
+
3
+ [![CI](https://github.com/TheEpTic/hermes-plugins/actions/workflows/ci.yml/badge.svg)](https://github.com/TheEpTic/hermes-plugins/actions/workflows/ci.yml)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
5
+ [![Python 3.11+](https://img.shields.io/badge/python-3.11+-blue.svg)](https://www.python.org/downloads/)
6
+
7
+ Socket Firewall Free plugin for [Hermes Agent](https://github.com/NousResearch/hermes-agent).
8
+
9
+ Block malicious dependencies at install time. Wrap any package manager command with `sfw` to get automatic protection — no API key, no config.
10
+
11
+ ```
12
+ sfw action=run command="npm install express"
13
+ sfw action=status
14
+ ```
15
+
16
+ ## Quick Start
17
+
18
+ > **Requires Python 3.11+** and the [sfw CLI](https://github.com/SocketDev/sfw-free) installed on the host system.
19
+
20
+ ### Option 1: Deploy script (recommended)
21
+
22
+ ```bash
23
+ git clone https://github.com/TheEpTic/hermes-plugins.git
24
+ cd hermes-plugins/hermes-sfw
25
+ ./deploy.sh
26
+ ```
27
+
28
+ Then restart Hermes with `/reset`.
29
+
30
+ ### Option 2: Manual symlink
31
+
32
+ ```bash
33
+ git clone https://github.com/TheEpTic/hermes-plugins.git
34
+ ln -s "$(pwd)/hermes-plugins/hermes-sfw/src/hermes_sfw" ~/.hermes/plugins/hermes-sfw
35
+ ```
36
+
37
+ Then `/reset` in Hermes. The symlink points at the source tree, but Python modules are imported once, so code changes still require `/reset` or a Hermes process restart before they load.
38
+
39
+ ### Option 3: As a Python package
40
+
41
+ ```bash
42
+ pip install git+https://github.com/TheEpTic/hermes-plugins.git#subdirectory=hermes-sfw
43
+ ```
44
+
45
+ Then enable it and restart Hermes:
46
+
47
+ ```bash
48
+ hermes plugins enable hermes-sfw
49
+ ```
50
+
51
+ ## Features
52
+
53
+ ### `sfw run` — Execute Commands
54
+
55
+ Run any package manager command through sfw. Malicious packages are blocked automatically.
56
+
57
+ ```bash
58
+ # Install a package
59
+ sfw action=run command="npm install express"
60
+
61
+ # Uninstall
62
+ sfw action=run command="npm uninstall lodash"
63
+
64
+ # Python packages
65
+ sfw action=run command="pip install flask"
66
+ sfw action=run command="uv pip install -r requirements.txt"
67
+
68
+ # Rust crates
69
+ sfw action=run command="cargo add serde"
70
+
71
+ # With verbose output
72
+ sfw action=run command="pnpm add -D vitest" verbose=true
73
+
74
+ # In a specific directory
75
+ sfw action=run command="npm install" workdir="/path/to/project"
76
+ ```
77
+
78
+ **Supported package managers:** npm, yarn, pnpm (JS/TS), pip, pip3, uv (Python), cargo, rustup (Rust). `npx` is intentionally blocked because it can execute arbitrary package code.
79
+
80
+ **Blocked packages:** When sfw detects a malicious package, the install is blocked and the package name is returned in the response. Non-package-manager commands (like `cat`, `rm`, `curl`) are rejected by the prefix allowlist.
81
+
82
+ **Output truncation:** Output exceeding 10,000 characters is automatically truncated with a size note.
83
+
84
+ ### `sfw status` — Check Installation
85
+
86
+ Verify sfw is installed and get the version.
87
+
88
+ ```bash
89
+ sfw action=status
90
+ ```
91
+
92
+ Returns: `installed` (bool), `version` (string), `binary` (path).
93
+
94
+ ## How It Works
95
+
96
+ hermes-sfw is a thin wrapper around the [sfw CLI](https://github.com/SocketDev/sfw-free). It:
97
+
98
+ 1. Validates the command starts with an allowed package manager prefix
99
+ 2. Resolves and validates the working directory (if specified)
100
+ 3. Executes the command through `sfw` with timeout protection
101
+ 4. Parses stdout/stderr for blocked and installed package indicators
102
+ 5. Returns structured JSON with success status, output, and parsed results
103
+
104
+ ## Configuration
105
+
106
+ All settings live in `src/hermes_sfw/manager.py` as an `SFWConfig` dataclass:
107
+
108
+ | Setting | Default | Description |
109
+ |---------|---------|-------------|
110
+ | `sfw_bin` | `sfw` | Path to the sfw binary |
111
+ | `timeout` | 300s | Max seconds per command |
112
+
113
+ ## Architecture
114
+
115
+ ```
116
+ src/hermes_sfw/
117
+ ├── __init__.py # Plugin registration + Hermes hooks
118
+ ├── manager.py # SFWManager — command execution + output parsing
119
+ ├── schemas.py # Tool schema (what the LLM sees)
120
+ ├── utils.py # ok(), err(), require() helpers
121
+ ├── py.typed # PEP 561 marker
122
+ └── handlers/
123
+ ├── __init__.py
124
+ └── sfw.py # sfw tool handler
125
+ ```
126
+
127
+ **Key design decisions:**
128
+
129
+ - `SFWManager` owns all state. No module-level mutable state.
130
+ - Command prefix allowlist prevents arbitrary command execution through sfw.
131
+ - `shlex.split()` parsing with error handling catches malformed commands early.
132
+ - Output sanitization truncates long outputs to prevent context overflow.
133
+ - `OSError` errno mapping provides clean error messages without leaking internals.
134
+
135
+ ## Security
136
+
137
+ See [SECURITY.md](SECURITY.md) for the full picture.
138
+
139
+ **Defaults you should know about:**
140
+
141
+ - Only package manager commands are allowed (prefix allowlist: npm, yarn, pnpm, pip, cargo, etc.)
142
+ - Non-package-manager commands (`cat`, `rm`, `curl`, etc.) are rejected
143
+ - Commands run with the permissions of the Hermes agent process
144
+
145
+ **Hardening applied:**
146
+
147
+ - Command prefix validation via allowlist before execution
148
+ - `shlex.split()` with error handling prevents shell injection
149
+ - Workdir resolved with `os.path.realpath()` to prevent path traversal
150
+ - Output truncated at 10K chars to prevent context overflow
151
+ - Timeout protection prevents hanging installs
152
+
153
+ ## Requirements
154
+
155
+ - Python 3.11+
156
+ - [sfw CLI](https://github.com/SocketDev/sfw-free) installed on PATH
157
+ - [Hermes Agent](https://github.com/NousResearch/hermes-agent)
158
+
159
+ ## Troubleshooting
160
+
161
+ **"sfw is not installed"**
162
+ Install sfw globally: `npm i -g sfw`. The plugin searches PATH and common locations (`~/.local/share/pnpm/bin/`, `/usr/local/bin/`, `~/.npm-global/bin/`).
163
+
164
+ **Command rejected with "not allowed"**
165
+ Only package manager commands are allowed. If you need to add a prefix, modify `_ALLOWED_PREFIXES` in `manager.py`.
166
+
167
+ **Command timeout**
168
+ Default timeout is 5 minutes (300s). For very large installs, this may not be enough. Override via `SFWConfig(timeout=...)` when creating the manager.
169
+
170
+ **Output looks truncated**
171
+ This is intentional — outputs over 10K chars are truncated with a size note. The full output is in the raw stdout/stderr fields.
172
+
173
+ ## Development
174
+
175
+ ```bash
176
+ git clone https://github.com/TheEpTic/hermes-plugins.git
177
+ cd hermes-plugins/hermes-sfw
178
+ python -m venv .venv && source .venv/bin/activate
179
+ pip install -e '.[dev]'
180
+
181
+ # Run checks
182
+ black --check src/hermes_sfw/ tests/
183
+ mypy src/hermes_sfw/
184
+ pytest
185
+ ```
186
+
187
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
188
+
189
+ ## License
190
+
191
+ MIT — see [LICENSE](LICENSE).