# Security Considerations for Instatic Plugins: Architecture and Implementation

> Learn Instatic plugin security: QuickJS-WASM sandboxes, permission manifests, capability gating, and static analysis prevent arbitrary code execution. Secure your integrations.

- Repository: [CoreBunch/Instatic](https://github.com/CoreBunch/Instatic)
- Tags: architecture
- Published: 2026-07-02

---

**Instatic isolates third-party plugins inside QuickJS-WASM sandboxes with strict permission manifests, capability gating, and static analysis to prevent arbitrary code execution.**

The CoreBunch/Instatic repository implements a defense-in-depth security model that balances extensibility with host protection. This article examines the technical mechanisms—from VM isolation to manifest validation—that prevent malicious or buggy plugins from compromising the core system.

## Sandbox Isolation with QuickJS-WASM

Instatic loads each plugin into a **QuickJS-WASM VM** that has zero access to Node.js or Bun native APIs, the filesystem, or process bindings. According to [`server/plugins/runtime.ts`](https://github.com/CoreBunch/Instatic/blob/main/server/plugins/runtime.ts), the activation process boots plugins inside this VM context, exposing only a narrow SDK surface while preventing escape to the underlying runtime environment.

The sandbox explicitly lacks bindings to `process`, `fs`, or any native modules, ensuring that even if plugin code attempts to require system resources, the execution context prevents arbitrary code execution.

## Permission Manifest and Capability Gating

Every plugin must declare its required capabilities in a [`plugin.json`](https://github.com/CoreBunch/Instatic/blob/main/plugin.json) manifest file. The host validates these permissions at install time and enforces them at runtime through capability gating defined in [`src/core/capabilities.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/core/capabilities.ts).

### Declaring Permissions

As documented in [`examples/plugins/template/README.md`](https://github.com/CoreBunch/Instatic/blob/main/examples/plugins/template/README.md), the manifest uses an explicit permission list pattern. Valid capabilities include `editor.commands`, `cms.routes.public`, and `site.structure.edit`:

```json
{
  "id": "acme.my-plugin",
  "name": "My Awesome Plugin",
  "version": "1.0.0",
  "permissions": [
    "editor.commands",
    "cms.routes.public",
    "plugins.configure"
  ],
  "entrypoints": {
    "server": "dist/server.js",
    "editor": "dist/editor.js"
  }
}

```

### Runtime Capability Checks

Core capabilities are verified via `requireCapability` or `requireAnyCapability` before any privileged operation. The [`docs/reference/capabilities.md`](https://github.com/CoreBunch/Instatic/blob/main/docs/reference/capabilities.md) file documents the full permission matrix available to plugins.

For example, deleting CMS content requires the `site.structure.edit` capability:

```ts
import { requireCapability } from '@core/capabilities';

export async function deletePage(pageId: string) {
  requireCapability('site.structure.edit');
  // ...perform deletion via core API...
}

```

SDK calls are mapped to capabilities in [`apiDispatch.ts`](https://github.com/CoreBunch/Instatic/blob/main/apiDispatch.ts), ensuring consistent enforcement across all plugin entry points.

## Network Restrictions and Static Analysis

### Network Whitelisting

By default, plugin VMs cannot make outbound HTTP requests. The [`server/plugins/quickjs/vm.ts`](https://github.com/CoreBunch/Instatic/blob/main/server/plugins/quickjs/vm.ts) file implements a **networkAllowedHosts** whitelist that restricts fetch operations to explicitly declared domains in the plugin manifest.

### Static Bundle Vetting

Before installation, Instatic scans plugin bundles for forbidden patterns including `node:`, `bun:`, `require(`, and `process.binding`. The test suite [`src/__tests__/architecture/plugin-sandbox-invariants.test.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/__tests__/architecture/plugin-sandbox-invariants.test.ts) guarantees that no forbidden imports slip through validation, effectively blocking attempts to import native modules.

## Error Isolation and Content Access Control

### Fault Isolation

Plugin failures are caught and logged with a `[plugin:<id>]` prefix without crashing the server. As implemented in [`server/plugins/runtime.ts`](https://github.com/CoreBunch/Instatic/blob/main/server/plugins/runtime.ts), a misbehaving plugin is disabled while the host continues running other extensions.

### Content Security Policies

CRUD operations on CMS data require explicit `cms.content.*` permissions. The system enforces these centrally in [`apiDispatch.ts`](https://github.com/CoreBunch/Instatic/blob/main/apiDispatch.ts) and at the handler level via `assertContentTableAccess`, ensuring plugins only access content tables explicitly allowed by the administrator.

## Manifest Validation and Upload Security

### Schema Validation

The `parsePluginManifest` function in [`src/core/plugins/manifest.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/core/plugins/manifest.ts) uses TypeBox schemas to validate that only well-formed JSON is accepted and that declared permissions match the allowed set defined by the core system.

### Secure Plugin Distribution

Plugin bundles are served from `/uploads/plugins/*` with `Access-Control-Allow-Origin: *` restrictions because they execute within sandboxed iframes configured with `sandbox="allow-scripts"`. According to [`server/plugins/package.ts`](https://github.com/CoreBunch/Instatic/blob/main/server/plugins/package.ts), this prevents same-origin access and data leakage between plugins and the host application.

## Summary

- **QuickJS-WASM isolation** in [`server/plugins/runtime.ts`](https://github.com/CoreBunch/Instatic/blob/main/server/plugins/runtime.ts) prevents access to Node.js, Bun, filesystem, or native bindings
- **Explicit permission manifests** in [`plugin.json`](https://github.com/CoreBunch/Instatic/blob/main/plugin.json) require administrators to consent to each capability before installation
- **Capability gating** via `requireCapability` enforces runtime permission checks for all privileged operations
- **Network whitelisting** blocks outbound HTTP requests to undeclared hosts by default
- **Static analysis** in [`plugin-sandbox-invariants.test.ts`](https://github.com/CoreBunch/Instatic/blob/main/plugin-sandbox-invariants.test.ts) scans bundles for forbidden imports before activation
- **Error isolation** ensures plugin crashes do not affect the host server or other plugins
- **Schema validation** rejects malformed manifests and invalid permission strings at install time

## Frequently Asked Questions

### How does Instatic prevent plugins from accessing the filesystem?

Instatic loads plugins into a QuickJS-WASM VM that lacks bindings to Node.js or Bun filesystem APIs. The runtime exposes only a narrow SDK surface, and static analysis explicitly scans for forbidden patterns like `node:` imports or `process.binding` calls that could provide filesystem access.

### What happens if a plugin tries to make an unauthorized network request?

The VM configured in [`server/plugins/quickjs/vm.ts`](https://github.com/CoreBunch/Instatic/blob/main/server/plugins/quickjs/vm.ts) maintains a `networkAllowedHosts` whitelist parsed from the plugin manifest. If a plugin attempts to fetch from a host not declared in its permissions, the runtime blocks the request, preventing data exfiltration or unauthorized API calls.

### Can a malicious plugin crash the entire Instatic server?

No. Error handling in [`server/plugins/runtime.ts`](https://github.com/CoreBunch/Instatic/blob/main/server/plugins/runtime.ts) catches all plugin exceptions with `[plugin:<id>]` prefixes. A malfunctioning plugin is isolated and disabled without terminating the host process or affecting other installed plugins.

### How are plugin permissions validated during installation?

The `parsePluginManifest` function in [`src/core/plugins/manifest.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/core/plugins/manifest.ts) validates the [`plugin.json`](https://github.com/CoreBunch/Instatic/blob/main/plugin.json) against a TypeBox schema that enforces allowed permission strings. Unknown capabilities are rejected at install time, ensuring the permission system cannot be bypassed through malformed manifests.