feat(wasm): Complete WASM auth plugins support - #1938
Merged
Conversation
Adds osc plugin search/install/update against an HTTPS-hosted registry index (bootstrapped at plugins/registry/index.json in this repo), with sha256 verification before any bytes touch disk. Before a registry-installed plugin is trusted, verify it was actually published by CI in its claimed GitHub repo: fetch the GitHub artifact attestation, verify the DSSE envelope signature against the Fulcio leaf cert, chain-verify against vendored pinned Fulcio root/intermediate CAs, and check the cert's OIDC-issuer/repository identity extensions match the plugin's declared source_repo. Rekor transparency-log inclusion is recorded for display but not cryptographically verified (documented, deliberate scope limit - see provenance.rs module docs). An unverified or tampered plugin fails closed (Untrusted) unless the caller passes --allow-unsigned, which is loudly logged and surfaced by `osc plugin list`. Local --file installs now go through the same explicit escape hatch, since a local file has no attestation to check. `osc plugin update` re-verifies provenance on every run rather than trusting a stored record from first install. Move the browser-based SSO callback server (bind/listen, HTML response, browser opening) out of sdk/auth-websso into a new sdk/websso-host crate, so both the native WebSSO plugin and the upcoming WASM SSO ABI (Phase 5) can share one implementation of this security-sensitive logic instead of duplicating it. The shared CallbackServer now embeds a host-generated, per-flow random state token in the callback URL's query string and rejects any callback whose state doesn't match via a constant-time comparison, closing a CSRF gap the previous implementation didn't have. The browser-open path is gated by a BrowserOpenPolicy so callers can choose whether to allow plain http:// (kept for the native plugin's existing local/dev Keystone use case) or require https://. sdk/auth-websso is trimmed down to a thin caller of the new crate; its local hyper server, HTML template and tests move to sdk/websso-host along with their coverage. Extends osc plugin's WASM auth ABI with a second, mutually-exclusive flavor for browser-based SSO logins: `sso_build_request` (pure, guest computes an https:// URL to open and declares its intended redirect host) and `sso_parse_callback` (pure, guest turns an already CSRF-validated callback into a token). Neither export gets a socket or browser-opening capability — both are structurally guest-sandboxed (`Manifest::disallow_all_hosts`) — so all I/O (the local callback listener, anti-CSRF state check, and browser launch) stays in the host, reusing the `openstack-sdk-websso-host` service extracted earlier. `WasmAuthPlugin::load` detects which flavor a module implements via `Plugin::function_exists` and rejects modules exporting both or neither. Before ever prompting the user or opening a browser, `auth_via_sso` hard-rejects a non-https `url` and any `redirect_host` that doesn't exactly match the host-bound callback listener's own authority — both checks are unconditional, with no `--allow-unsigned`- style override, since a mismatch here means the plugin is trying to redirect the callback somewhere the host didn't intend. Adds an example SSO WASM plugin fixture (fixtures/example-sso-plugin, checked in as tests/fixtures/example_sso.wasm) and an integration test suite exercising ABI-flavor detection, the two hard-fail security checks (proven to trigger before the interactive confirmation step), the guest ABI's request/callback round trip, and the shared callback server's anti-CSRF rejection. Adds three libFuzzer targets covering the untrusted-input surfaces where a WASM auth plugin's own bytes cross into host code, extending the existing fuzz/ crate rather than starting a new one: - fuzz_wasm_plugin_identity_http_request: the one host function every plugin can call, fuzzing its request-parsing/validation step (JSON decode, relative-path check, URL join, method parse) ahead of any network I/O. - fuzz_wasm_plugin_sso_build_response: the SSO ABI flavor's security-relevant validation (URL must parse and be https, redirect_host must match the host-bound callback listener) that runs before a browser is ever opened. - fuzz_wasm_plugin_auth_result: the AuthResultMsg deserialization every guest response (`auth`, `sso_parse_callback`) is parsed through. Each target exercises the crate's real (previously inline, now extracted into standalone functions: `host::resolve_request` and `plugin::validate_sso_build_response`) parsing/validation logic directly, via new `fuzzing`-feature-gated entry points, rather than reimplementing it -- matching this workspace's existing fuzz-target conventions (openstack-sdk-auth-core, openstack_sdk_core). Wired into the `fuzz` CI job alongside the existing targets. Documents the osc plugin feature end to end: the operator-facing guide (sandbox model, installing, trust model), the plugin author's guest ABI reference (both auth and sso flavors, host-mediated HTTP capability, build/test/publish flow), and how entries land in and are trusted out of the plugins/registry index. Wires all three pages into the mdBook table of contents. cargo deny check already passes cleanly against the extism/wasmtime and sigstore-adjacent (x509-parser/ring/rcgen) dependency tree added in earlier phases, so no deny.toml changes were needed for Phase 6. Assisted-By: Claude Sonnet 5 <noreply@anthropic.com> Signed-off-by: Artem Goncharov <artem.goncharov@gmail.com>
gtema
force-pushed
the
claude/extism-plugin-registry-d0p7e3
branch
from
August 12, 2026 08:11
56c63cc to
f85ee8d
Compare
Open
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds osc plugin search/install/update against an HTTPS-hosted registry index (bootstrapped at plugins/registry/index.json in this repo), with sha256 verification before any bytes touch disk.
Before a registry-installed plugin is trusted, verify it was actually published by CI in its claimed GitHub repo: fetch the GitHub artifact attestation, verify the DSSE envelope signature against the Fulcio leaf cert, chain-verify against vendored pinned Fulcio root/intermediate CAs, and check the cert's OIDC-issuer/repository identity extensions match the plugin's declared source_repo. Rekor transparency-log inclusion is recorded for display but not cryptographically verified (documented, deliberate scope limit - see provenance.rs module docs).
An unverified or tampered plugin fails closed (Untrusted) unless the caller passes --allow-unsigned, which is loudly logged and surfaced by
osc plugin list. Local --file installs now go through the same explicit escape hatch, since a local file has no attestation to check.osc plugin updatere-verifies provenance on every run rather than trusting a stored record from first install.Move the browser-based SSO callback server (bind/listen, HTML response, browser opening) out of sdk/auth-websso into a new sdk/websso-host crate, so both the native WebSSO plugin and the upcoming WASM SSO ABI (Phase 5) can share one implementation of this security-sensitive logic instead of duplicating it.
The shared CallbackServer now embeds a host-generated, per-flow random state token in the callback URL's query string and rejects any callback whose state doesn't match via a constant-time comparison, closing a CSRF gap the previous implementation didn't have. The browser-open path is gated by a BrowserOpenPolicy so callers can choose whether to allow plain http:// (kept for the native plugin's existing local/dev Keystone use case) or require https://.
sdk/auth-websso is trimmed down to a thin caller of the new crate; its local hyper server, HTML template and tests move to sdk/websso-host along with their coverage.
Extends osc plugin's WASM auth ABI with a second, mutually-exclusive flavor for browser-based SSO logins:
sso_build_request(pure, guest computes an https:// URL to open and declares its intended redirect host) andsso_parse_callback(pure, guest turns an already CSRF-validated callback into a token). Neither export gets a socket or browser-opening capability — both are structurally guest-sandboxed (Manifest::disallow_all_hosts) — so all I/O (the local callback listener, anti-CSRF state check, and browser launch) stays in the host, reusing theopenstack-sdk-websso-hostservice extracted earlier.WasmAuthPlugin::loaddetects which flavor a module implements viaPlugin::function_existsand rejects modules exporting both or neither. Before ever prompting the user or opening a browser,auth_via_ssohard-rejects a non-httpsurland anyredirect_hostthat doesn't exactly match the host-bound callback listener's own authority — both checks are unconditional, with no--allow-unsigned- style override, since a mismatch here means the plugin is trying to redirect the callback somewhere the host didn't intend.Adds an example SSO WASM plugin fixture (fixtures/example-sso-plugin, checked in as tests/fixtures/example_sso.wasm) and an integration test suite exercising ABI-flavor detection, the two hard-fail security checks (proven to trigger before the interactive confirmation step), the guest ABI's request/callback round trip, and the shared callback server's anti-CSRF rejection.
Adds three libFuzzer targets covering the untrusted-input surfaces where a WASM auth plugin's own bytes cross into host code, extending the existing fuzz/ crate rather than starting a new one:
auth,sso_parse_callback) is parsed through.Each target exercises the crate's real (previously inline, now extracted into standalone functions:
host::resolve_requestandplugin::validate_sso_build_response) parsing/validation logic directly, via newfuzzing-feature-gated entry points, rather than reimplementing it -- matching this workspace's existing fuzz-target conventions (openstack-sdk-auth-core, openstack_sdk_core). Wired into thefuzzCI job alongside the existing targets.Documents the osc plugin feature end to end: the operator-facing guide (sandbox model, installing, trust model), the plugin author's guest ABI reference (both auth and sso flavors, host-mediated HTTP capability, build/test/publish flow), and how entries land in and are trusted out of the plugins/registry index. Wires all three pages into the mdBook table of contents.
cargo deny check already passes cleanly against the extism/wasmtime and sigstore-adjacent (x509-parser/ring/rcgen) dependency tree added in earlier phases, so no deny.toml changes were needed for Phase 6.
Assisted-By: Claude Sonnet 5 noreply@anthropic.com