@hamzaabili/opencode-integrity 0.0.0-stage → 0.1.0
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.
- package/CHANGELOG.md +19 -0
- package/LICENSE +21 -0
- package/README.md +189 -2
- package/SECURITY.md +61 -0
- package/dist/cli.mjs +1260 -0
- package/index.ts +1 -0
- package/package.json +70 -4
- package/scripts/build.ts +23 -0
- package/scripts/smoke.ts +39 -0
- package/src/assets.ts +344 -0
- package/src/audit.ts +129 -0
- package/src/cli.ts +200 -0
- package/src/config.ts +123 -0
- package/src/index.ts +19 -0
- package/src/lockfile.ts +298 -0
- package/src/options.ts +55 -0
- package/src/policy.ts +25 -0
- package/src/runtime.ts +213 -0
- package/src/sarif.ts +71 -0
- package/src/tool.ts +53 -0
- package/src/types.ts +102 -0
- package/src/util.ts +349 -0
- package/src/v1.ts +121 -0
- package/src/v2.ts +385 -0
- package/src/version.ts +1 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.0
|
|
4
|
+
|
|
5
|
+
First release.
|
|
6
|
+
|
|
7
|
+
- Audit, lock, check, diff and policy CLI with JSON and SARIF output.
|
|
8
|
+
- Lockfile (`opencode.lock.json`) for plugins, MCP configuration, MCP tool
|
|
9
|
+
descriptors and skills.
|
|
10
|
+
- Runtime capture of live MCP tool descriptions and input schemas, including
|
|
11
|
+
remote servers and Code Mode.
|
|
12
|
+
- Warn/enforce modes: disable changed MCP servers, remove changed skills, deny
|
|
13
|
+
changed MCP tools and skills before execution.
|
|
14
|
+
- Plugin content hashing, detected capabilities and version/source pinning.
|
|
15
|
+
- Deterministic suspicious-content checks (zero-width, bidi, HTML comments,
|
|
16
|
+
hidden instructions, exfiltration language).
|
|
17
|
+
- `integrity_status` and `integrity_lock` tools plus `/integrity` command.
|
|
18
|
+
- V1 static audit adapter; V2 full runtime adapter.
|
|
19
|
+
- Zero runtime dependencies; local only; no telemetry.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Hamza-Abili
|
|
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 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,190 @@
|
|
|
1
|
-
#
|
|
1
|
+
# opencode-integrity
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://github.com/Hamza-Abili/opencode-integrity/actions/workflows/ci.yml)
|
|
4
|
+
[](https://www.npmjs.com/package/@hamzaabili/opencode-integrity)
|
|
5
|
+
[](https://www.npmjs.com/package/@hamzaabili/opencode-integrity)
|
|
6
|
+
[](https://github.com/Hamza-Abili/opencode-integrity/stargazers)
|
|
7
|
+
[](LICENSE)
|
|
8
|
+
|
|
9
|
+
**The tool you approved is not the tool you are running.**
|
|
10
|
+
|
|
11
|
+
`opencode-integrity` locks what OpenCode can load — plugin content, MCP tool
|
|
12
|
+
descriptions and schemas, and skill files — and detects when any of them
|
|
13
|
+
change. No proxy, no config rewrite, no cloud. Local, zero runtime
|
|
14
|
+
dependencies, OpenCode V1 and V2.
|
|
15
|
+
|
|
16
|
+
```text
|
|
17
|
+
audit → see plugins, MCP tools and skills with capabilities and hashes
|
|
18
|
+
lock → write opencode.lock.json (commit it)
|
|
19
|
+
check → fail CI when anything drifts
|
|
20
|
+
runtime→ warn or deny changed MCP tools and skills during a session
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Why
|
|
24
|
+
|
|
25
|
+
An MCP tool description is not inert data: it is instructions the model reads.
|
|
26
|
+
A server can serve one toolset today and a different one tomorrow. Skills are
|
|
27
|
+
Markdown files that can be edited after review. Plugins are npm/Git code that
|
|
28
|
+
runs in the OpenCode server with the agent's privileges. OpenCode policies
|
|
29
|
+
(`plugin.use`, `mcp.use`, `skill.use`) only match names; they do not verify
|
|
30
|
+
content.
|
|
31
|
+
|
|
32
|
+
Existing MCP scanners are on-demand CLIs, and the strongest pinning tool
|
|
33
|
+
(`mcp-pin`) is a stdio proxy for other clients. This is the OpenCode-native
|
|
34
|
+
layer: it sees what OpenCode actually loaded, including remote MCP servers and
|
|
35
|
+
Code Mode, and covers plugins and skills too.
|
|
36
|
+
|
|
37
|
+
## Install (OpenCode V2)
|
|
38
|
+
|
|
39
|
+
```jsonc
|
|
40
|
+
{
|
|
41
|
+
"$schema": "https://opencode.ai/config.json",
|
|
42
|
+
"plugins": [
|
|
43
|
+
{
|
|
44
|
+
"package": "@hamzaabili/opencode-integrity",
|
|
45
|
+
"options": { "mode": "warn" }
|
|
46
|
+
}
|
|
47
|
+
]
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Install (OpenCode V1)
|
|
52
|
+
|
|
53
|
+
```jsonc
|
|
54
|
+
{
|
|
55
|
+
"plugin": [
|
|
56
|
+
["@hamzaabili/opencode-integrity", { "mode": "warn" }]
|
|
57
|
+
]
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
V1 can hash project plugins, MCP configuration and skills, and in `enforce`
|
|
62
|
+
mode it blocks changed MCP configuration and skills. The V1 API does not expose
|
|
63
|
+
live MCP tool descriptors, so tool-level drift is V2-only.
|
|
64
|
+
|
|
65
|
+
## Quick start
|
|
66
|
+
|
|
67
|
+
```sh
|
|
68
|
+
# 1. See what is loaded, without a lockfile
|
|
69
|
+
npx @hamzaabili/opencode-integrity audit
|
|
70
|
+
|
|
71
|
+
# 2. Inside OpenCode, capture the live MCP tool definitions
|
|
72
|
+
/integrity lock
|
|
73
|
+
|
|
74
|
+
# 3. Commit the lockfile
|
|
75
|
+
git add opencode.lock.json
|
|
76
|
+
|
|
77
|
+
# 4. In CI
|
|
78
|
+
npx @hamzaabili/opencode-integrity check --format sarif
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
`/integrity lock` is important: only the running OpenCode instance can see the
|
|
82
|
+
tool descriptions and schemas returned by MCP servers (local and remote, with
|
|
83
|
+
or without Code Mode). The CLI locks project plugins, MCP configuration and
|
|
84
|
+
skills; it preserves runtime tool hashes when the server configuration did not
|
|
85
|
+
change.
|
|
86
|
+
|
|
87
|
+
## What is hashed
|
|
88
|
+
|
|
89
|
+
| Asset | Fields |
|
|
90
|
+
| --- | --- |
|
|
91
|
+
| Plugin | package.json, every package file (capped at 5000 files / 50 MiB), version, source, detected capabilities |
|
|
92
|
+
| MCP server | transport, command, URL, cwd, environment/header **names** and redacted values, codemode |
|
|
93
|
+
| MCP tool | name, description, input/output schema, annotations, suspicious-instruction scan |
|
|
94
|
+
| Skill | full `SKILL.md` content, size, suspicious-instruction scan |
|
|
95
|
+
|
|
96
|
+
Secret-looking environment values and headers (`TOKEN`, `KEY`, `SECRET`,
|
|
97
|
+
`PASSWORD`, `AUTHORIZATION`, `COOKIE`, `CREDENTIAL`) are replaced with
|
|
98
|
+
`[redacted]` before hashing, so rotating a token does not create drift. Their
|
|
99
|
+
names still change the hash.
|
|
100
|
+
|
|
101
|
+
## Runtime modes
|
|
102
|
+
|
|
103
|
+
| Mode | Behavior |
|
|
104
|
+
| --- | --- |
|
|
105
|
+
| `warn` (default) | Inject a drift report into the session and log it; nothing is blocked. |
|
|
106
|
+
| `enforce` | Disable MCP servers whose configuration changed, remove changed skills, and deny changed MCP tools and skills before execution. |
|
|
107
|
+
|
|
108
|
+
`enforce` is fail-closed for **asset drift**, not a sandbox. A plugin that is
|
|
109
|
+
already loaded cannot be unloaded by a plugin hook; review plugin changes with
|
|
110
|
+
the lock diff and restart OpenCode. MCP and skill transforms are applied at
|
|
111
|
+
startup, so after `/integrity lock` in an existing session, reload to re-enable
|
|
112
|
+
previously quarantined assets.
|
|
113
|
+
|
|
114
|
+
## CLI
|
|
115
|
+
|
|
116
|
+
```sh
|
|
117
|
+
npx @hamzaabili/opencode-integrity audit [--json] [--strict]
|
|
118
|
+
npx @hamzaabili/opencode-integrity lock
|
|
119
|
+
npx @hamzaabili/opencode-integrity check [--format text|json|sarif]
|
|
120
|
+
npx @hamzaabili/opencode-integrity diff
|
|
121
|
+
npx @hamzaabili/opencode-integrity policy
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
- `check` exits `1` on drift or when no lockfile exists.
|
|
125
|
+
- CLI `check` verifies project files/configuration; live MCP tool descriptors are
|
|
126
|
+
only verifiable by the running V2 adapter. Use `/integrity check` or the
|
|
127
|
+
`integrity_status` tool inside OpenCode for the full check.
|
|
128
|
+
- `audit --strict` exits `1` on drift or an unlocked project.
|
|
129
|
+
- `policy` emits native OpenCode `mcp.use`, `skill.use` and `plugin.use`
|
|
130
|
+
allow statements from the lockfile. It is an allowlist, not a hash check;
|
|
131
|
+
pair it with `check`/runtime enforcement.
|
|
132
|
+
|
|
133
|
+
## Options
|
|
134
|
+
|
|
135
|
+
| Option | Default | Meaning |
|
|
136
|
+
| --- | --- | --- |
|
|
137
|
+
| `lockfile` | `<project>/opencode.lock.json` | Lockfile path. |
|
|
138
|
+
| `mode` | `"warn"` | `warn` or `enforce`. |
|
|
139
|
+
| `include` | `["plugins","mcp","skills"]` | Asset classes to inspect. |
|
|
140
|
+
| `ignore` | `[]` | Asset ids or globs to ignore, e.g. `mcp:local-dev`, `skill:experimental-*`. |
|
|
141
|
+
| `includeGlobal` | `false` | Include global MCP config and, at runtime, global plugins/skills. Project-scoped assets are the default so a committed lock stays portable. |
|
|
142
|
+
| `trace` | unset | Reserved for redacted diagnostics. |
|
|
143
|
+
|
|
144
|
+
## Capabilities and suspicious content
|
|
145
|
+
|
|
146
|
+
`audit` reports heuristic capabilities found in plugin entry code (`network`,
|
|
147
|
+
`filesystem`, `process`, `environment`, `dynamic-code`) and flags zero-width
|
|
148
|
+
characters, bidi controls, HTML comments, “ignore previous instructions”,
|
|
149
|
+
“do not tell”, and exfiltration language in tool descriptions and skills.
|
|
150
|
+
|
|
151
|
+
These are **signals**, not proof. They are deliberately deterministic and
|
|
152
|
+
offline; a clean scan does not make a plugin safe.
|
|
153
|
+
|
|
154
|
+
## Differences from other tools
|
|
155
|
+
|
|
156
|
+
| | opencode-integrity | mcp-pin | mcp-scan / Cisco | Socket/npm audit |
|
|
157
|
+
| --- | --- | --- | --- | --- |
|
|
158
|
+
| OpenCode-native runtime | yes | no (proxy) | no (on-demand) | no |
|
|
159
|
+
| MCP tool descriptor pinning | yes | yes (stdio only) | scan only | no |
|
|
160
|
+
| Remote MCP | yes | no | yes | no |
|
|
161
|
+
| Plugins and skills | yes | no | no | packages only |
|
|
162
|
+
| Runtime deny/quarantine | yes | proxy block | no | no |
|
|
163
|
+
| Policy/SARIF output | yes | no | partial | no |
|
|
164
|
+
| Zero runtime deps | yes | yes | varies | varies |
|
|
165
|
+
|
|
166
|
+
## Security model
|
|
167
|
+
|
|
168
|
+
The lockfile is a review artifact, not a signature. It catches changes between
|
|
169
|
+
the version you reviewed and the version that runs; it does not prove that the
|
|
170
|
+
reviewed version was safe. Use it with code review, OpenCode permissions,
|
|
171
|
+
`opencode-sandbox`, and least-privilege MCP servers.
|
|
172
|
+
|
|
173
|
+
Read [SECURITY.md](SECURITY.md) before enabling `enforce` on a shared machine.
|
|
174
|
+
|
|
175
|
+
## Support
|
|
176
|
+
|
|
177
|
+
- Bugs: [open an issue](https://github.com/Hamza-Abili/opencode-integrity/issues/new/choose)
|
|
178
|
+
with `audit --json` and the relevant lock diff (redact secrets).
|
|
179
|
+
- Questions: [Discussions](https://github.com/Hamza-Abili/opencode-integrity/discussions).
|
|
180
|
+
- Security: [private advisory](https://github.com/Hamza-Abili/opencode-integrity/security/advisories/new),
|
|
181
|
+
never a public issue.
|
|
182
|
+
|
|
183
|
+
## Development
|
|
184
|
+
|
|
185
|
+
```sh
|
|
186
|
+
bun install
|
|
187
|
+
bun run ci # biome + tsc + tests + build + smoke
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
MIT. Zero runtime dependencies.
|
package/SECURITY.md
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Security
|
|
2
|
+
|
|
3
|
+
## What this tool is
|
|
4
|
+
|
|
5
|
+
`opencode-integrity` records SHA-256 hashes of OpenCode plugin content, MCP
|
|
6
|
+
server configuration, MCP tool descriptors and skill files, then compares them
|
|
7
|
+
with the running environment. It is a **drift detector**, not a malware
|
|
8
|
+
scanner, sandbox, signature verifier or proof of safety.
|
|
9
|
+
|
|
10
|
+
A lockfile catches the classic rug pull: the plugin/MCP/skill you reviewed is
|
|
11
|
+
not the one now loaded. It cannot tell whether the reviewed version was
|
|
12
|
+
malicious.
|
|
13
|
+
|
|
14
|
+
## Trust model
|
|
15
|
+
|
|
16
|
+
- `opencode.lock.json` is committed and reviewed like code. Anyone who can
|
|
17
|
+
change both the lockfile and the asset can approve the change.
|
|
18
|
+
- The model-callable `integrity_lock` tool is exposed only when an evaluate
|
|
19
|
+
hook can force the dedicated `integrity_lock` action to `ask`; the
|
|
20
|
+
user-invoked `/integrity lock` command asks for the same permission.
|
|
21
|
+
- `warn` mode never blocks; `enforce` mode blocks **changed** assets only.
|
|
22
|
+
- Runtime enforcement covers MCP tools and skills that the plugin can observe.
|
|
23
|
+
A plugin that is already executing cannot be unloaded by a plugin hook.
|
|
24
|
+
- V1 enforces static MCP configuration and skill drift, but cannot verify live
|
|
25
|
+
MCP tool descriptors; tool-level enforcement is V2-only.
|
|
26
|
+
|
|
27
|
+
## Secrets
|
|
28
|
+
|
|
29
|
+
Config hashes replace values of secret-looking keys (`TOKEN`, `SECRET`,
|
|
30
|
+
`PASSWORD`, `API_KEY`, `AUTHORIZATION`, `COOKIE`, `CREDENTIAL`) with
|
|
31
|
+
`[redacted]`. The keys still affect the hash; rotating a token does not.
|
|
32
|
+
|
|
33
|
+
The lockfile contains hashes, paths, versions, capability labels and
|
|
34
|
+
suspicious-content labels. It never stores tool descriptions, skill bodies or
|
|
35
|
+
environment values.
|
|
36
|
+
|
|
37
|
+
## Capability and poisoning heuristics
|
|
38
|
+
|
|
39
|
+
Capability detection and suspicious-instruction labels are deterministic text
|
|
40
|
+
heuristics. They produce false positives and false negatives. A clean report is
|
|
41
|
+
not a safety guarantee; a suspicious label is a reason to review, not proof of
|
|
42
|
+
malice.
|
|
43
|
+
|
|
44
|
+
## Limitations
|
|
45
|
+
|
|
46
|
+
- MCP tool descriptors are only visible at runtime. A CLI `check` verifies
|
|
47
|
+
configuration and files, not what a server returns today.
|
|
48
|
+
- A server can change behavior without changing names, descriptions or schemas.
|
|
49
|
+
- A tool can be malicious while its descriptor is clean.
|
|
50
|
+
- `enforce` disables changed MCP servers and skills at load time; a changed
|
|
51
|
+
plugin is reported but cannot be unloaded by a hook.
|
|
52
|
+
- The lockfile is not signed. Protect the repository and review lockfile diffs.
|
|
53
|
+
- Windows is not tested. Use WSL.
|
|
54
|
+
|
|
55
|
+
## Reporting
|
|
56
|
+
|
|
57
|
+
Open a private advisory:
|
|
58
|
+
<https://github.com/Hamza-Abili/opencode-integrity/security/advisories/new>
|
|
59
|
+
|
|
60
|
+
Do not include live tokens, private MCP URLs or full environment dumps. A
|
|
61
|
+
minimal reproduction with fake values is enough.
|