-- WASM Secure API Extension -- V2: Secrets management, WASM tool storage, capabilities, and leak detection -- ==================== Secrets ==================== -- Encrypted secret storage for credential injection into WASM HTTP requests. -- WASM tools NEVER see plaintext secrets; injection happens at host boundary. CREATE TABLE secrets ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id TEXT NOT NULL, name TEXT NOT NULL, -- AES-256-GCM encrypted value (nonce || ciphertext || tag) encrypted_value BYTEA NOT NULL, -- Per-secret key derivation salt (for HKDF) key_salt BYTEA NOT NULL, -- Optional metadata provider TEXT, -- e.g., "openai", "anthropic", "stripe" expires_at TIMESTAMPTZ, last_used_at TIMESTAMPTZ, usage_count BIGINT NOT NULL DEFAULT 0, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), CONSTRAINT unique_secret_per_user UNIQUE (user_id, name) ); CREATE INDEX idx_secrets_user ON secrets(user_id); CREATE INDEX idx_secrets_provider ON secrets(provider) WHERE provider IS NOT NULL; CREATE INDEX idx_secrets_expires ON secrets(expires_at) WHERE expires_at IS NOT NULL; -- Trigger to update updated_at CREATE TRIGGER update_secrets_updated_at BEFORE UPDATE ON secrets FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); -- ==================== WASM Tools ==================== -- Store compiled WASM binaries with integrity verification. CREATE TABLE wasm_tools ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id TEXT NOT NULL, name TEXT NOT NULL, version TEXT NOT NULL DEFAULT '1.0.0', description TEXT NOT NULL, wasm_binary BYTEA NOT NULL, -- BLAKE3 hash for integrity verification on load binary_hash BYTEA NOT NULL, parameters_schema JSONB NOT NULL, -- Provenance source_url TEXT, -- Trust levels: 'system' (built-in), 'verified' (audited), 'user' (untrusted) trust_level TEXT NOT NULL DEFAULT 'user', -- Status: 'active', 'disabled', 'quarantined' status TEXT NOT NULL DEFAULT 'active', created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), CONSTRAINT unique_wasm_tool_version UNIQUE (user_id, name, version) ); CREATE INDEX idx_wasm_tools_user ON wasm_tools(user_id); CREATE INDEX idx_wasm_tools_name ON wasm_tools(user_id, name); CREATE INDEX idx_wasm_tools_status ON wasm_tools(status); CREATE INDEX idx_wasm_tools_trust ON wasm_tools(trust_level); CREATE TRIGGER update_wasm_tools_updated_at BEFORE UPDATE ON wasm_tools FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); -- ==================== Tool Capabilities ==================== -- Fine-grained capability configuration per WASM tool. -- Follows principle of least privilege. CREATE TABLE tool_capabilities ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), wasm_tool_id UUID NOT NULL REFERENCES wasm_tools(id) ON DELETE CASCADE, -- HTTP capability: allowed endpoint patterns -- Each pattern is: {"host": "api.example.com", "path_prefix": "/v1/", "methods": ["GET", "POST"]} http_allowlist JSONB NOT NULL DEFAULT '[]', -- Secrets this tool can use (injected at host boundary) -- Tool never sees the actual secret values allowed_secrets TEXT[] NOT NULL DEFAULT '{}', -- Tool invocation aliases (indirection layer) -- Maps alias name to real tool name, e.g., {"search": "brave_search"} tool_aliases JSONB NOT NULL DEFAULT '{}', -- Rate limiting requests_per_minute INT NOT NULL DEFAULT 60, requests_per_hour INT NOT NULL DEFAULT 1000, -- Request/response size limits max_request_body_bytes BIGINT NOT NULL DEFAULT 1048576, -- 1 MB max_response_body_bytes BIGINT NOT NULL DEFAULT 10485760, -- 10 MB -- Workspace access (path prefixes tool can read) workspace_read_prefixes TEXT[] NOT NULL DEFAULT '{}', -- Timeout for HTTP requests (seconds) http_timeout_secs INT NOT NULL DEFAULT 30, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), CONSTRAINT unique_capabilities_per_tool UNIQUE (wasm_tool_id) ); CREATE INDEX idx_tool_capabilities_tool ON tool_capabilities(wasm_tool_id); CREATE TRIGGER update_tool_capabilities_updated_at BEFORE UPDATE ON tool_capabilities FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); -- ==================== Leak Detection Patterns ==================== -- Patterns for detecting secret leakage in tool outputs. -- Scanned before returning data to WASM or LLM. CREATE TABLE leak_detection_patterns ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), name TEXT NOT NULL UNIQUE, -- Regex pattern for detection pattern TEXT NOT NULL, -- Severity: 'critical', 'high', 'medium', 'low' severity TEXT NOT NULL DEFAULT 'high', -- Action: 'block' (fail request), 'redact' (mask secret), 'warn' (log only) action TEXT NOT NULL DEFAULT 'block', enabled BOOLEAN NOT NULL DEFAULT true, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE INDEX idx_leak_patterns_enabled ON leak_detection_patterns(enabled) WHERE enabled = true; -- Pre-populate with common API key patterns INSERT INTO leak_detection_patterns (name, pattern, severity, action) VALUES -- OpenAI (sk-proj-... or sk-... followed by alphanumeric) ('openai_api_key', 'sk-(?:proj-)?[a-zA-Z0-9]{20,}(?:T3BlbkFJ[a-zA-Z0-9_-]*)?', 'critical', 'block'), -- Anthropic (sk-ant-api followed by 90+ chars) ('anthropic_api_key', 'sk-ant-api[a-zA-Z0-9_-]{90,}', 'critical', 'block'), -- AWS Access Key ID (starts with AKIA) ('aws_access_key', 'AKIA[0-9A-Z]{16}', 'critical', 'block'), -- AWS Secret Access Key (40 char base64-ish) ('aws_secret_key', '(? NOW() - INTERVAL '24 hours' ORDER BY le.created_at DESC;