Skip to content

fix(security): reject all-zero Ed25519 registry key and verify hook script integrity#3901

Merged
houko merged 3 commits into
mainfrom
worktree-agent-a3f6daeb297524bab
Apr 28, 2026
Merged

fix(security): reject all-zero Ed25519 registry key and verify hook script integrity#3901
houko merged 3 commits into
mainfrom
worktree-agent-a3f6daeb297524bab

Conversation

@houko

@houko houko commented Apr 28, 2026

Copy link
Copy Markdown
Contributor

Summary

#3799 — Plugin registry public key is all-zero placeholder

OFFICIAL_REGISTRY_PUBKEY_B64 was a 32-byte zero placeholder. Both fetch_verified_index and install_from_registry silently skipped verification when is_placeholder was true. Now both return a hard Err — installs are blocked until a real key is configured. Escape hatch via LIBREFANG_REGISTRY_VERIFY=0 / LIBREFANG_ARCHIVE_VERIFY=0 for operators who need to opt out.

New tests: official_registry_pubkey_is_placeholder_all_zeros, is_placeholder_detects_built_in_constant, is_placeholder_passes_real_key

#3804 — Hook scripts not checksummed (only plugin.toml was)

After archive signature passes, the installed plugin.toml is re-parsed and every hook declared in [hooks] is checked against the [integrity] map. Any declared hook without an integrity entry causes the install to be rolled back (target dir removed).

All 7 hook fields checked: ingest, after_turn, bootstrap, assemble, compact, prepare_subagent, merge_subagent.

New tests: 4 covering no-hooks, partial coverage, full coverage, all fields.

Fixes

Test plan

  • CI passes
  • All 7 new tests pass
  • Plugin install with placeholder key returns error

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@github-actions github-actions Bot added area/runtime Agent loop, LLM drivers, WASM sandbox size/L 250-999 lines changed labels Apr 28, 2026
houko and others added 3 commits April 28, 2026 23:11
…eholder (#3799)

The official registry Ed25519 public key constant was an all-zero 32-byte
placeholder.  The code detected this (`is_placeholder`) but silently skipped
verification instead of rejecting the operation.  An all-zero key is a valid
Ed25519 key whose private key is trivially known, so any attacker can sign
arbitrary payloads for it — skipping is indistinguishable from no security.

Both call sites now return a hard error when the placeholder is active:
- `fetch_verified_index`: refuses to download/use the registry index
- `install_from_registry`: refuses to install the plugin archive

Three unit tests pin the behaviour:
- `official_registry_pubkey_is_placeholder_all_zeros` — constant decodes to zeros
- `is_placeholder_detects_built_in_constant` — gate correctly flags the constant
- `is_placeholder_passes_real_key` — gate does not false-positive on a real key

Operators who need to bypass verification for development can still set
`LIBREFANG_REGISTRY_VERIFY=0` / `LIBREFANG_ARCHIVE_VERIFY=0`.
…#3804)

The checksum verification in `install_from_registry` only covered the
manifest (`plugin.toml`), not the hook scripts it references.  An attacker
who controls the download path can serve a legitimate manifest with a valid
checksum while substituting arbitrary Python/Node/shell hook scripts that
execute under the daemon's privileges.

The fix adds a post-install check: after the manifest and archive signature
are accepted, every hook script path declared in `[hooks]` must have a
corresponding entry in `[integrity]`.  A missing entry causes the install to
fail and the partial installation to be cleaned up.

The `[integrity]` section already existed in `PluginManifest` and was
enforced by `load_plugin_manifest` at load time, but was never required at
install time — meaning a plugin with no integrity entries could be installed
from the registry without any hook-file verification.

Four unit tests pin the detection logic:
- `hook_integrity_no_hooks_no_requirement` — no hooks → no entries required
- `hook_integrity_missing_entries_detected` — partial coverage is flagged
- `hook_integrity_all_covered_passes` — full coverage passes
- `hook_integrity_all_hook_fields_checked` — all seven hook fields are inspected

Operators who need to install unverified plugins during development can use
`PluginSource::Local` or `PluginSource::Git` which do not go through the
registry install path.
@houko
houko force-pushed the worktree-agent-a3f6daeb297524bab branch from 9930b3a to 8005ed9 Compare April 28, 2026 14:11
@houko
houko merged commit e65c64a into main Apr 28, 2026
19 of 20 checks passed
@houko
houko deleted the worktree-agent-a3f6daeb297524bab branch April 28, 2026 15:28
@houko houko mentioned this pull request May 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/runtime Agent loop, LLM drivers, WASM sandbox size/L 250-999 lines changed

Projects

None yet

1 participant