Files
OwnCord/Server/plugin/examples/hello/README.md
T
J3vb 3d2dd19001 feat(plugin): enforce manifest-declared per-command ACL
Closes audit-2026-04-07 CRITICAL #3. Holding the `commands` capability used
to bind whatever names the guest module returned from `list_commands`, so an
admin enabling a plugin could not know which commands it would claim and a
plugin could widen its own command surface after review.

The manifest is now the authority. `plugin.json` gains a `commands` block
(`[{"name": "hello"}]`) and `RegisterCommand` refuses any undeclared name —
the single choke point both auto-registration and direct registration route
through, so no caller can bypass it. Declared names are validated to the
dispatcher's canonical lowercase form, deduplicated, and capped at 64.
The object shape matches docs/plans/slash-commands.md so the richer
per-command schema can land later without a manifest migration.

Also pins the two neighbouring CRITICALs that verification found already
closed, and adds the storage key cap host_storage.go's doc comment already
promised:

- #2 (storage key isolation): TestStorageKeysIsolatedPerPlugin — the KV
  namespace is the caller's Instance.ID with no parameter to override it,
  and plugin_kv PRIMARY KEY (plugin_id, key) makes the split structural.
- #4 (event rate limit): TestEventDeliveryHasNoGuestPath — EventSink.Dispatch
  invokes no guest code and has no callers, so there is nothing to limit yet;
  a SECURITY GATE comment requires the limiter in whatever change wires
  delivery.
- #5 mitigation: TestEmptyAllowlistDeniesEveryHost — the shipped empty
  http_allowlist must fail closed.

BREAKING CHANGE: a plugin declaring the `commands` capability must now list
its commands in the manifest's `commands` block; undeclared names no longer
bind. Only the in-repo `hello` example is affected and is updated here.
2026-07-20 14:10:02 +02:00

1.8 KiB
Raw Blame History

hello plugin

Phase C Step 9 — proof-of-life plugin used by Server/plugin/plugin_test.go.

Manifest

plugin.json declares the commands, events, and storage capabilities. The manifest is the only file the default (no--tags wazero) build needs — the registry persists it into the plugins table without executing the .wasm.

The commands block is the per-command ACL: activation binds only the names listed there, so list_commands returning anything else is ignored. Keep plugin.json's list and listCommandsJSON in main.go in sync — a name in the WASM but not the manifest simply never binds.

Building the WASM

main.go in this directory implements the full plugin ABI (allocate, deallocate, list_commands, command_dispatch, on_event). The pre-built hello.wasm (925 KiB) is checked in, but you can rebuild it:

Prerequisites

Tool Version Notes
TinyGo 0.40.1 Supports Go 1.191.25 only
Go 1.25.x TinyGo 0.40.1 rejects Go 1.26+
wasm-opt Binaryen 129 Required by TinyGo for the wasi target

On Windows, extract TinyGo to e.g. D:\Local-Lab\Coding\Software\tinygo and add <tinygo>\bin plus <binaryen>\bin to PATH. Then point TinyGo at the compatible Go SDK:

$env:GOROOT = "$env:USERPROFILE\sdk\go1.25.3"   # installed via: go install golang.org/dl/go1.25.3@latest
$env:PATH   = "$env:GOROOT\bin;$env:PATH"

Build command

# Run from this directory (Server/plugin/examples/hello/)
tinygo build -o hello.wasm -target wasi ./main.go

Tests

Server/plugin/plugin_test.go exercises the manifest parser and the loader against this directory. It does not require the .wasm to be present — manifest-only validation is the default-build coverage path.