Skip to content
Back to skills

Wasm Extension Integration

CSecurity

Guide for integrating WebAssembly modules into browser extensions using wasm-pack, wasm-bindgen, and cross-browser loading

  • 8 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added June 2, 2026
toolstypescriptrustgoc++bashnodenodejsdebugginggitperformance

Works with

  • mcp

Security analysis

C71/100
  • criticalPipes output to a shell interpreter
  • mediumUses curl or wget to download content
  • criticalDownloads and executes remote scripts — classic supply chain attack

Pro shows the line behind each finding and how to fix it

Scanned June 2, 2026

npx -y skills add aRustyDev/agents --skill wasm-extension-integration --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Wasm Extension Integration?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Wasm Extension Integration
[![Security: C — Skills Directory](https://www.skillsdirectory.com/api/skills/arustydev-wasm-extension-integration/badge)](https://www.skillsdirectory.com/skills/arustydev-wasm-extension-integration)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: wasm-extension-integration
description: Guide for integrating WebAssembly modules into browser extensions using wasm-pack, wasm-bindgen, and cross-browser loading
tags: [browser-extension, webassembly, wasm, wasm-pack, wasm-bindgen, rust]
---

# WASM Extension Integration

Guide for integrating WebAssembly modules into browser extensions using wasm-pack, wasm-bindgen, and cross-browser loading patterns.

## Overview

WASM in browser extensions enables:
- High-performance computations (cryptography, parsing, compression)
- Code reuse from Rust/C++ libraries
- Sandboxed execution environments

## Build Pipeline

### wasm-pack Workflow

```bash
# Install wasm-pack
curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh

# Build for web target
wasm-pack build --target web --out-dir pkg

# Build for bundler (if using webpack/vite)
wasm-pack build --target bundler --out-dir pkg
```

### Cargo.toml Configuration

```toml
[package]
name = "extension-wasm"
version = "0.1.0"
edition = "2021"

[lib]
crate-type = ["cdylib", "rlib"]

[dependencies]
wasm-bindgen = "0.2"
js-sys = "0.3"
web-sys = { version = "0.3", features = ["console"] }

[profile.release]
opt-level = "s"        # Optimize for size
lto = true             # Link-time optimization
codegen-units = 1      # Single codegen unit for better optimization
strip = true           # Strip debug symbols
```

### Build Targets

| Target | Output | Use Case |
|--------|--------|----------|
| `web` | ES modules | Direct browser loading |
| `bundler` | npm package | Webpack/Vite bundling |
| `nodejs` | CommonJS | Node.js (not for extensions) |
| `no-modules` | Global | Legacy browser support |

## WXT Integration

### Project Structure

```
extension/
├── wasm/
│   ├── Cargo.toml
│   └── src/
│       └── lib.rs
├── entrypoints/
│   └── background.ts
└── wxt.config.ts
```

### WXT Configuration

```typescript
// wxt.config.ts
import { defineConfig } from 'wxt';

export default defineConfig({
  vite: () => ({
    plugins: [
      // WASM plugin for Vite
      {
        name: 'wasm-pack',
        buildStart: async () => {
          const { execSync } = await import('child_process');
          execSync('wasm-pack build wasm --target web --out-dir ../public/wasm', {
            stdio: 'inherit'
          });
        }
      }
    ],
    build: {
      target: 'esnext',
      rollupOptions: {
        output: {
          // Preserve WASM imports
          inlineDynamicImports: false
        }
      }
    },
    optimizeDeps: {
      exclude: ['*.wasm']
    }
  })
});
```

## Cross-Browser Loading

### Synchronous Instantiation (Small Modules)

```typescript
// For modules < 4KB
async function loadWasmSync(wasmPath: string): Promise<WebAssembly.Instance> {
  const response = await fetch(chrome.runtime.getURL(wasmPath));
  const bytes = await response.arrayBuffer();
  const module = new WebAssembly.Module(bytes);
  return new WebAssembly.Instance(module);
}
```

### Asynchronous Instantiation (Large Modules)

```typescript
// For modules >= 4KB (required by browsers)
async function loadWasmAsync(wasmPath: string): Promise<WebAssembly.Instance> {
  const response = await fetch(chrome.runtime.getURL(wasmPath));

  if (WebAssembly.instantiateStreaming) {
    // Chrome, Firefox, Edge - streaming compilation
    const { instance } = await WebAssembly.instantiateStreaming(response);
    return instance;
  } else {
    // Safari fallback
    const bytes = await response.arrayBuffer();
    const { instance } = await WebAssembly.instantiate(bytes);
    return instance;
  }
}
```

### wasm-bindgen Initialization

```typescript
// Generated by wasm-pack
import init, { process_data } from './pkg/extension_wasm.js';

let wasmReady = false;

async function initWasm(): Promise<void> {
  if (wasmReady) return;

  const wasmUrl = chrome.runtime.getURL('pkg/extension_wasm_bg.wasm');
  await init(wasmUrl);
  wasmReady = true;
}

// Usage
async function processWithWasm(data: Uint8Array): Promise<Uint8Array> {
  await initWasm();
  return process_data(data);
}
```

## Browser Compatibility

### Feature Detection

```typescript
function checkWasmSupport(): {
  basic: boolean;
  streaming: boolean;
  threads: boolean;
  simd: boolean;
} {
  const basic = typeof WebAssembly !== 'undefined';

  const streaming = basic &&
    typeof WebAssembly.instantiateStreaming === 'function';

  // Check for threads (SharedArrayBuffer)
  const threads = basic &&
    typeof SharedArrayBuffer !== 'undefined';

  // Check for SIMD
  const simd = basic && (() => {
    try {
      // Minimal SIMD validation bytes
      const bytes = new Uint8Array([
        0, 97, 115, 109, 1, 0, 0, 0, 1, 5, 1, 96, 0, 1, 123,
        3, 2, 1, 0, 10, 10, 1, 8, 0, 65, 0, 253, 15, 253, 98, 11
      ]);
      return WebAssembly.validate(bytes);
    } catch {
      return false;
    }
  })();

  return { basic, streaming, threads, simd };
}
```

### Safari Considerations

Safari has stricter WASM policies:

```typescript
// Safari requires explicit WASM MIME type
// Ensure server sends: Content-Type: application/wasm

// Safari doesn't support streaming compilation from cross-origin
// Keep WASM files in extension package

// Safari may require web_accessible_resources for WASM
```

```json
{
  "web_accessible_resources": [
    {
      "resources": ["pkg/*.wasm", "pkg/*.js"],
      "matches": ["<all_urls>"]
    }
  ]
}
```

## Memory Management

### Extension Memory Limits

| Context | Chrome | Firefox | Safari |
|---------|--------|---------|--------|
| Service worker | 128MB default | 512MB | 128MB |
| Content script | Tab memory | Tab memory | Tab memory |
| Popup | 128MB | 128MB | 128MB |

### Memory-Efficient Patterns

```rust
// lib.rs - Minimize allocations

use wasm_bindgen::prelude::*;

// Reuse allocations
static mut BUFFER: Vec<u8> = Vec::new();

#[wasm_bindgen]
pub fn process_chunk(data: &[u8]) -> Vec<u8> {
    unsafe {
        BUFFER.clear();
        BUFFER.extend_from_slice(data);
        // Process in place
        BUFFER.clone()
    }
}

// Free memory explicitly
#[wasm_bindgen]
pub fn free_buffer() {
    unsafe {
        BUFFER = Vec::new();
        BUFFER.shrink_to_fit();
    }
}
```

### Streaming Processing

```typescript
// Process large data in chunks to avoid memory spikes
async function processLargeData(
  data: ArrayBuffer,
  chunkSize: number = 1024 * 1024 // 1MB chunks
): Promise<ArrayBuffer> {
  await initWasm();

  const input = new Uint8Array(data);
  const results: Uint8Array[] = [];

  for (let i = 0; i < input.length; i += chunkSize) {
    const chunk = input.slice(i, i + chunkSize);
    const processed = process_chunk(chunk);
    results.push(processed);
  }

  // Combine results
  const totalLength = results.reduce((sum, arr) => sum + arr.length, 0);
  const output = new Uint8Array(totalLength);
  let offset = 0;

  for (const result of results) {
    output.set(result, offset);
    offset += result.length;
  }

  // Free WASM memory
  free_buffer();

  return output.buffer;
}
```

## Service Worker Integration

### Loading WASM in Service Worker

```typescript
// background.ts (service worker)

let wasmModule: WebAssembly.Module | null = null;

// Pre-compile on install
chrome.runtime.onInstalled.addListener(async () => {
  const response = await fetch(chrome.runtime.getURL('pkg/module.wasm'));
  const bytes = await response.arrayBuffer();
  wasmModule = await WebAssembly.compile(bytes);

  // Store compiled module reference for quick instantiation
  console.log('WASM module compiled');
});

// Instantiate when needed
async function getWasmInstance(): Promise<WebAssembly.Instance> {
  if (!wasmModule) {
    const response = await fetch(chrome.runtime.getURL('pkg/module.wasm'));
    const bytes = await response.arrayBuffer();
    wasmModule = await WebAssembly.compile(bytes);
  }

  return new WebAssembly.Instance(wasmModule);
}
```

### Service Worker Lifecycle

```typescript
// Handle service worker termination
// WASM modules are lost when worker sleeps

chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
  if (message.type === 'PROCESS_WASM') {
    // Re-initialize WASM if needed
    processWithWasm(message.data)
      .then(sendResponse)
      .catch(error => sendResponse({ error: error.message }));
    return true;
  }
});
```

## MCP Server: wasm-bindgen

The plugin includes a wasm-bindgen MCP server for build automation.

### Configuration

```json
{
  "mcpServers": {
    "wasm-bindgen": {
      "command": "cargo",
      "args": [
        "run",
        "--manifest-path",
        "${HOME}/.local/share/mcp/wasm-bindgen-mcp/Cargo.toml",
        "--",
        "--target-dir",
        ".output/wasm"
      ],
      "env": {
        "WASM_PACK_PATH": "wasm-pack",
        "WASM_TARGET": "web",
        "WASM_EXTENSION_MODE": "true"
      }
    }
  }
}
```

### Available Tools

| Tool | Description |
|------|-------------|
| `wasm_build` | Build WASM module with wasm-pack |
| `wasm_optimize` | Optimize WASM binary size |
| `wasm_validate` | Validate WASM module |
| `wasm_inspect` | Inspect WASM module exports |

### Usage

```typescript
// AI can invoke:
// wasm_build({ profile: 'release', target: 'web' })
// wasm_optimize({ input: 'module.wasm', level: 's' })
// wasm_validate({ path: 'module.wasm' })
```

## Size Optimization

### Build Flags

```toml
# Cargo.toml
[profile.release]
opt-level = "z"        # Optimize for size (aggressive)
lto = "fat"            # Full LTO
codegen-units = 1
panic = "abort"        # No unwinding
strip = true
```

### Post-Build Optimization

```bash
# Install wasm-opt (from binaryen)
brew install binaryen

# Optimize WASM binary
wasm-opt -Os -o output.wasm input.wasm

# Or with wasm-pack
wasm-pack build --release -- --features wee_alloc
```

### wee_alloc (Smaller Allocator)

```toml
[dependencies]
wee_alloc = { version = "0.4", optional = true }

[features]
default = ["wee_alloc"]
```

```rust
#[cfg(feature = "wee_alloc")]
#[global_allocator]
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
```

## Common Use Cases

### Cryptography

```rust
use wasm_bindgen::prelude::*;
use sha2::{Sha256, Digest};

#[wasm_bindgen]
pub fn hash_sha256(data: &[u8]) -> Vec<u8> {
    let mut hasher = Sha256::new();
    hasher.update(data);
    hasher.finalize().to_vec()
}
```

### Data Compression

```rust
use wasm_bindgen::prelude::*;
use flate2::Compression;
use flate2::write::GzEncoder;
use std::io::Write;

#[wasm_bindgen]
pub fn compress_gzip(data: &[u8]) -> Vec<u8> {
    let mut encoder = GzEncoder::new(Vec::new(), Compression::default());
    encoder.write_all(data).unwrap();
    encoder.finish().unwrap()
}
```

### JSON Parsing (Fast)

```rust
use wasm_bindgen::prelude::*;
use serde_json::Value;

#[wasm_bindgen]
pub fn parse_json(input: &str) -> JsValue {
    match serde_json::from_str::<Value>(input) {
        Ok(value) => serde_wasm_bindgen::to_value(&value).unwrap(),
        Err(e) => JsValue::from_str(&format!("Error: {}", e))
    }
}
```

## Debugging

### Source Maps

```bash
# Build with debug info
wasm-pack build --dev

# Or with custom flags
RUSTFLAGS="-C debuginfo=2" wasm-pack build
```

### Browser DevTools

1. Chrome: DevTools → Sources → shows .wasm files
2. Firefox: Debugger → shows WASM as text
3. Safari: Limited WASM debugging

### Console Logging

```rust
use web_sys::console;

#[wasm_bindgen]
pub fn debug_log(message: &str) {
    console::log_1(&message.into());
}
```

## Quality Checklist

- [ ] WASM module builds with wasm-pack
- [ ] Cross-browser loading tested
- [ ] Memory usage within limits
- [ ] Service worker lifecycle handled
- [ ] Size optimized for extension bundle
- [ ] Error handling implemented
- [ ] Fallback for unsupported browsers

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…