Verify executes a WASM policy against the provided input
(ctx context.Context, policy *engine.Policy, input []byte, args map[string]any)
| 92 | |
| 93 | // Verify executes a WASM policy against the provided input |
| 94 | func (e *Engine) Verify(ctx context.Context, policy *engine.Policy, input []byte, args map[string]any) (*engine.EvaluationResult, error) { |
| 95 | e.logger.Debug().Str("policy", policy.Name).Int("wasm_size", len(policy.Source)).Int("input_size", len(input)).Int("args_count", len(args)).Msg("Starting WASM policy execution") |
| 96 | |
| 97 | // Prepare config with args if present |
| 98 | configMap := make(map[string]string) |
| 99 | if len(args) > 0 { |
| 100 | // Marshal args to JSON and store in config |
| 101 | argsJSON, err := json.Marshal(args) |
| 102 | if err != nil { |
| 103 | return nil, fmt.Errorf("failed to marshal args: %w", err) |
| 104 | } |
| 105 | configMap["args"] = string(argsJSON) |
| 106 | e.logger.Debug().Str("policy", policy.Name).Int("args_count", len(args)).Msg("Passing policy arguments via Extism config") |
| 107 | } |
| 108 | |
| 109 | // Create Extism manifest with config and allowed hosts |
| 110 | manifest := extism.Manifest{ |
| 111 | Wasm: []extism.Wasm{ |
| 112 | extism.WasmData{Data: policy.Source}, |
| 113 | }, |
| 114 | Config: configMap, |
| 115 | AllowedHosts: e.AllowedHostnames, |
| 116 | Timeout: uint64(e.executionTimeout.Milliseconds()), |
| 117 | } |
| 118 | |
| 119 | // Log allowed hosts configuration |
| 120 | if len(e.AllowedHostnames) > 0 { |
| 121 | e.logger.Debug().Str("policy", policy.Name).Int("allowed_hosts", len(e.AllowedHostnames)).Strs("hostnames", e.AllowedHostnames).Msg("Configured allowed hosts for HTTP requests") |
| 122 | } |
| 123 | |
| 124 | config := extism.PluginConfig{ |
| 125 | EnableWasi: true, |
| 126 | } |
| 127 | |
| 128 | // Register host functions |
| 129 | // Registers in both "env" (for Go/TinyGo) and "extism:host/user" (for JavaScript) namespaces |
| 130 | hostFunctions := builtins.CreateDiscoverHostFunctions(e.ControlPlaneConnection) |
| 131 | |
| 132 | // Create plugin with host functions |
| 133 | plugin, err := extism.NewPlugin(ctx, manifest, config, hostFunctions) |
| 134 | if err != nil { |
| 135 | e.logger.Error().Err(err).Str("policy", policy.Name).Msg("Failed to create WASM plugin") |
| 136 | return nil, fmt.Errorf("failed to create WASM plugin: %w", err) |
| 137 | } |
| 138 | defer plugin.Close(ctx) |
| 139 | |
| 140 | // Check if Execute function is exported |
| 141 | if !plugin.FunctionExists(entryFunction) { |
| 142 | e.logger.Error().Str("policy", policy.Name).Str("function", entryFunction).Msg("WASM module missing required function export") |
| 143 | return nil, fmt.Errorf("wasm module validation failed: missing required '%s' function export", entryFunction) |
| 144 | } |
| 145 | |
| 146 | // Set up logger for WASM plugin output |
| 147 | plugin.SetLogger(func(level extism.LogLevel, message string) { |
| 148 | switch level { |
| 149 | case extism.LogLevelTrace: |
| 150 | e.logger.Trace().Str("policy", policy.Name).Msg(message) |
| 151 | case extism.LogLevelDebug: |