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.
- hermes_sfw-0.2.1/.github/ISSUE_TEMPLATE/bug_report.md +28 -0
- hermes_sfw-0.2.1/.github/ISSUE_TEMPLATE/feature_request.md +16 -0
- hermes_sfw-0.2.1/.github/PULL_REQUEST_TEMPLATE.md +23 -0
- hermes_sfw-0.2.1/.github/workflows/ci.yml +43 -0
- hermes_sfw-0.2.1/.gitignore +40 -0
- hermes_sfw-0.2.1/CHANGELOG.md +26 -0
- hermes_sfw-0.2.1/CONTRIBUTING.md +64 -0
- hermes_sfw-0.2.1/LICENSE +21 -0
- hermes_sfw-0.2.1/PKG-INFO +219 -0
- hermes_sfw-0.2.1/README.md +191 -0
- hermes_sfw-0.2.1/SECURITY.md +50 -0
- hermes_sfw-0.2.1/deploy.sh +44 -0
- hermes_sfw-0.2.1/llms.txt +120 -0
- hermes_sfw-0.2.1/pyproject.toml +72 -0
- hermes_sfw-0.2.1/src/hermes_sfw/__init__.py +35 -0
- hermes_sfw-0.2.1/src/hermes_sfw/approval.py +41 -0
- hermes_sfw-0.2.1/src/hermes_sfw/handlers/__init__.py +7 -0
- hermes_sfw-0.2.1/src/hermes_sfw/handlers/sfw.py +74 -0
- hermes_sfw-0.2.1/src/hermes_sfw/manager.py +448 -0
- hermes_sfw-0.2.1/src/hermes_sfw/plugin.yaml +10 -0
- hermes_sfw-0.2.1/src/hermes_sfw/py.typed +0 -0
- hermes_sfw-0.2.1/src/hermes_sfw/schemas.py +52 -0
- hermes_sfw-0.2.1/src/hermes_sfw/utils.py +28 -0
- hermes_sfw-0.2.1/tests/__init__.py +0 -0
- hermes_sfw-0.2.1/tests/conftest.py +66 -0
- hermes_sfw-0.2.1/tests/test_sfw.py +587 -0
- hermes_sfw-0.2.1/uv.lock +235 -0
|
@@ -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.
|
hermes_sfw-0.2.1/LICENSE
ADDED
|
@@ -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
|
+
[](https://github.com/TheEpTic/hermes-plugins/actions/workflows/ci.yml)
|
|
32
|
+
[](LICENSE)
|
|
33
|
+
[](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
|
+
[](https://github.com/TheEpTic/hermes-plugins/actions/workflows/ci.yml)
|
|
4
|
+
[](LICENSE)
|
|
5
|
+
[](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).
|