Add WASM bridge for sandboxed Salesforce API access#71
Conversation
Implements a three-crate bridge design using Extism that allows WASM guest plugins to invoke Salesforce APIs without ever seeing access tokens. The host manages all authentication and credentials. New crates: - sf-wasm-types: shared ABI types (BridgeResult<T>, request/response structs) that compile to both native and wasm32-unknown-unknown - sf-bridge: Extism host that registers 13 host functions (query, create, get, update, delete, upsert, describe, search, composite, collections, limits) wrapping SalesforceRestClient - sf-guest-sdk: ergonomic Rust wrappers for WASM plugin authors Also includes an example guest plugin and updated copilot instructions documenting the bridge architecture, workspace membership, credential isolation model, and concurrency design. https://claude.ai/code/session_014bCFaRUfSfuRjSaJTy3XoJ
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
… Metadata) Expands the bridge from 13 REST-only operations to 40 host functions covering all four Salesforce API crates. This addresses the CI coverage gap (40.14% -> target 50%+) by adding comprehensive tests (134 total across sf-bridge and sf-wasm-types). - sf-wasm-types: Add 65 new types for Bulk, Tooling, and Metadata APIs with 78 serialization roundtrip tests - sf-bridge: Add 27 new host function handlers (10 Bulk, 5 Tooling, 6 Metadata, 6 new REST), 56 tests with wiremock-based integration - sf-bridge/lib.rs: Wire all 40 host functions with BridgeState holding REST, Bulk, Tooling clients + on-demand MetadataClient construction - sf-guest-sdk: Add all 40 ergonomic wrappers for guest WASM plugins https://claude.ai/code/session_014bCFaRUfSfuRjSaJTy3XoJ
There was a problem hiding this comment.
Pull request overview
This PR introduces a comprehensive WASM bridge architecture that enables sandboxed WASM plugins to access Salesforce APIs while maintaining credential isolation. The host manages all authentication tokens, which never cross into WASM guest memory.
Changes:
- Added three new crates: sf-wasm-types (shared ABI types), sf-bridge (Extism host), and sf-guest-sdk (guest SDK)
- Created an example WASM guest plugin demonstrating the bridge usage
- Updated workspace configuration to include/exclude appropriate crates
- Added comprehensive documentation to copilot-instructions.md explaining the WASM bridge architecture
Reviewed changes
Copilot reviewed 12 out of 12 changed files in this pull request and generated 10 comments.
Show a summary per file
| File | Description |
|---|---|
| Cargo.toml | Adds new workspace members (sf-wasm-types, sf-bridge) and excludes WASM-only crates (sf-guest-sdk, wasm-guest-plugin) |
| crates/sf-wasm-types/src/lib.rs | Defines 1987 lines of shared ABI types with extensive test coverage (846+ lines of tests) |
| crates/sf-wasm-types/Cargo.toml | Minimal configuration for the pure-serde types crate |
| crates/sf-bridge/src/lib.rs | Extism host implementation with 1118 lines registering 40 host functions |
| crates/sf-bridge/src/host_functions.rs | Business logic for all 40 host functions with comprehensive unit tests |
| crates/sf-bridge/src/error.rs | Bridge-level error types covering all Salesforce API clients |
| crates/sf-bridge/Cargo.toml | Dependencies for the host bridge including extism, tokio, and all SF clients |
| crates/sf-guest-sdk/src/lib.rs | Ergonomic Rust wrappers for WASM plugin authors |
| crates/sf-guest-sdk/Cargo.toml | Guest SDK dependencies (extism-pdk, wasm-types) |
| examples/wasm-guest-plugin/src/lib.rs | Example plugin demonstrating query, create, composite, and limits operations |
| examples/wasm-guest-plugin/Cargo.toml | Example plugin configuration |
| .github/copilot-instructions.md | Adds 66 lines documenting WASM bridge architecture, credential isolation, and concurrency model |
Brings in CI coverage workflow fix and latest changes from main. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Jason Lantz <jason@muselab.com>
|
@copilot open a new pull request to apply changes based on the comments in this thread |
…error sanitization - Fix type from UpdateRecord to UpdateMultipleRecord in update_multiple - Fix type from u32 to u64 for max_records parameter - Update default API version from 62.0 to 65.0 (sf-wasm-types and example) - Use Arc<str> for access_token and instance_url to reduce memory copies - Sanitize error codes instead of exposing Debug format to WASM guests - Update security documentation to clarify SOQL injection responsibility Co-authored-by: jlantz <1697127+jlantz@users.noreply.github.com>
… sf-guest-sdk - Add integration tests for sf-bridge (module loading, WASM validation) - Document why unit tests aren't feasible for sf-guest-sdk (requires Extism runtime) - Explain that guest SDK is tested via bridge integration tests and examples - Add busbar-sf-bridge to dev-dependencies for integration tests Co-authored-by: jlantz <1697127+jlantz@users.noreply.github.com>
- Make invalid WASM test assertion specific (expect rejection at construction) - Move testing strategy documentation to module-level doc comment for visibility - Remove duplicate testing notes at end of sf-guest-sdk file Co-authored-by: jlantz <1697127+jlantz@users.noreply.github.com>
Replace serde_json with rmp_serde (MessagePack) for the host<->guest serialization at the WASM boundary. This is faster and produces smaller payloads than JSON while remaining self-describing for forward compatibility. The HTTP layer still uses JSON since that's what Salesforce expects. Uses to_vec_named() to include field names in the MessagePack output, preserving compatibility with serde's named-field deserialization. https://claude.ai/code/session_014bCFaRUfSfuRjSaJTy3XoJ
Address PR review comments: type safety, error sanitization, and testing
Signed-off-by: Jason Lantz <jason@muselab.com>
|
@copilot please resolve the error I introduced trying to resolve the merge conflict with main and expand the wasm ABI to include all the new endpoints we've added |
Updated the integration test command to use a multi-line run block for better readability. Signed-off-by: Jason Lantz <jason@muselab.com>
Modified the run command to use a multi-line format for better readability. Signed-off-by: Jason Lantz <jason@muselab.com>
|
@copilot all of your wasm integration tests seem to be failing? And, you should have access to the running 33 tests failures: failures: test result: FAILED. 32 passed; 1 failed; |
Co-authored-by: jlantz <1697127+jlantz@users.noreply.github.com>
Co-authored-by: jlantz <1697127+jlantz@users.noreply.github.com>
Fix WASM test plugin path in bridge integration tests
|
@copilot - we are still failing tests??? You should have access to the SF_AUTH_URL in your copilot env? For the wasm tests, you are supposed to be reusing or recreating the same test interactions with the org as our integration testsrunning 33 tests thread 'bridge::test_bridge_composite_tree_operation' (15018) panicked at tests/integration/bridge.rs:342:5: failures: failures: test result: FAILED. 30 passed; 3 failed; 0 ignored; 0 measured; 136 filtered out; finished in 41.22s error: test failed, to rerun pass Integration test failure... not quite sure how this one changed? thread 'rest::test_invocable_actions_describe_custom' (3257) panicked at tests/integration/rest.rs:1389:18: |
Co-authored-by: jlantz <1697127+jlantz@users.noreply.github.com>
Fix composite tree record structure in WASM test plugin
|
@copilot There are still test failures, same as last time? thread 'bridge::test_bridge_composite_tree_operation' (15011) panicked at tests/integration/bridge.rs:342:5: failures: |
Summary
This PR introduces a complete WASM bridge architecture enabling untrusted or declarative code to run in sandboxed WASM plugins while securely accessing Salesforce APIs. The host manages all credentials — WASM guests never see access tokens.
Key Changes
sf-wasm-types (new crate): Pure serde types shared by host and guest, compiling to both native and
wasm32-unknown-unknown. DefinesBridgeResult<T>for all ABI boundary values and host function name constants.sf-bridge (new crate): Extism host implementation that registers 13 host functions (
sf_query,sf_create,sf_get,sf_update,sf_delete,sf_upsert,sf_describe_global,sf_describe_sobject,sf_search,sf_composite,sf_create_multiple,sf_delete_multiple,sf_limits). Each function wraps the nativeSalesforceRestClient, deserializes requests from guest memory, executes async Salesforce API calls viaHandle::block_on(), and returns typedBridgeResultresponses.sf-guest-sdk (new crate): Ergonomic Rust wrappers for WASM plugin authors. Provides functions like
query(),create(),get(), etc. that call host function imports and handle serialization. Excluded from workspace (targetswasm32-unknown-unknownonly).examples/wasm-guest-plugin (new): Example WASM guest plugin demonstrating the guest SDK usage.
Workspace configuration: Updated
Cargo.tomlto include new crates and exclude guest-only targets.Documentation: Added comprehensive WASM Bridge Architecture section to
copilot-instructions.mdcovering design, credential isolation, concurrency model, and adding new host functions.Implementation Details
Credential Isolation
sf-bridgesf-authand injects tokens into requestsConcurrency Model
SfBridge::call()usestokio::task::spawn_blocking()to run each WASM invocation on a blocking threadHandle::block_on()bridges back to the async runtime for Salesforce API callsreqwest::Clientconnection pool is shared across all concurrent guest invocationsType Safety
sf-wasm-typeswith#[derive(Serialize, Deserialize)]BridgeResult<T>is a tagged enum (ok/err) for all ABI boundary valueshttps://claude.ai/code/session_014bCFaRUfSfuRjSaJTy3XoJ