extensions

package
v0.1.16 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 30, 2026 License: Apache-2.0 Imports: 27 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func CheckSSRF

func CheckSSRF(ip net.IP) error

CheckSSRF returns an error if ip should be blocked to prevent Server-Side Request Forgery. Blocked ranges:

  • Loopback (127.0.0.0/8, ::1)
  • Private (RFC 1918 + RFC 4193 ULA)
  • Link-local (169.254.0.0/16, fe80::/10) — includes AWS/GCP metadata IPs

func NewSSRFSafeClient

func NewSSRFSafeClient(timeout time.Duration) *http.Client

NewSSRFSafeClient returns an http.Client whose dialer rejects private, loopback, and link-local addresses and whose redirect policy refuses all redirects. The given timeout bounds the dial, TLS handshake, response-header wait, and the whole request. Exported so other outbound senders (e.g. the SLO Slack alerter) reuse the exact same egress guard instead of duplicating it.

func WithInsecureTransport

func WithInsecureTransport(c *http.Client) func(*WebhookDispatcher)

WithInsecureTransport returns a dispatcher that bypasses SSRF and HTTPS checks. Use ONLY in tests where the target is a local httptest server.

Types

type HookResult

type HookResult struct {
	Proceed bool
	Data    map[string]any
	Error   string
}

HookResult is the outcome of a hook script execution.

type HookRunner

type HookRunner struct {
	// contains filtered or unexported fields
}

HookRunner orchestrates JS sandbox, WASM runtime, and webhook dispatch for lifecycle hooks.

func NewHookRunner

func NewHookRunner(sandbox *JSSandbox) *HookRunner

NewHookRunner creates a HookRunner backed by the provided sandbox and a production (SSRF-safe, HTTPS-only) webhook dispatcher. No WASM runtime.

func NewHookRunnerWithDispatcher

func NewHookRunnerWithDispatcher(sandbox *JSSandbox, dispatcher *WebhookDispatcher, maxConcurrent int) *HookRunner

NewHookRunnerWithDispatcher creates a HookRunner with an explicit dispatcher and an explicit bound on concurrent async dispatches. maxConcurrent <= 0 falls back to the default. Used by tests to inject an insecure (loopback) dispatcher or a small concurrency bound.

func NewHookRunnerWithWasm

func NewHookRunnerWithWasm(sandbox *JSSandbox, dispatcher *WebhookDispatcher, wasm *WasmRunner) *HookRunner

NewHookRunnerWithWasm creates a HookRunner with a WASM runtime (Capa 3) wired in alongside the JS sandbox and webhook dispatcher. A nil dispatcher falls back to the production dispatcher.

func (*HookRunner) FireAfterHook

func (hr *HookRunner) FireAfterHook(hook *schema.HookConfig, event string, record map[string]any, tenantID string) bool

FireAfterHook dispatches the after_create hook on a bounded background goroutine and returns immediately, so the request never blocks on webhook latency. It returns true if the dispatch was admitted, false if the dispatch pool was saturated and the hook was dropped (logged). A nil hook is a no-op that returns true.

This is the production entry point used by the create handler; prefer it over `go hr.RunAfterHook(...)` so the number of in-flight dispatches stays bounded.

func (*HookRunner) RunAfterHook

func (hr *HookRunner) RunAfterHook(
	ctx context.Context,
	hook *schema.HookConfig,
	event string,
	record map[string]any,
	tenantID string,
)

RunAfterHook fires an after-hook synchronously on the calling goroutine. event is the REAL lifecycle event ("after_create" | "after_update") so the webhook carries the correct X-Appximo-Event header (SEC-AUDIT-V2 Hallazgo B). Production code should call FireAfterHook (which bounds concurrency and returns immediately); RunAfterHook is the worker it invokes. Only "webhook" after-hooks do anything — js/wasm after-hooks are rejected at schema load (see schema.Validate), so the js/wasm branch here is unreachable from a validated schema and kept fail-safe.

func (*HookRunner) RunBeforeHook

func (hr *HookRunner) RunBeforeHook(
	ctx context.Context,
	hook *schema.HookConfig,
	payload map[string]any,
	userCtx map[string]any,
) (*HookResult, error)

RunBeforeHook runs the before_create hook synchronously and returns the outcome. A nil hook is a no-op that returns {Proceed:true, Data:payload}. Webhook hooks never block: they always return Proceed:true.

type JSSandbox

type JSSandbox struct {
	// contains filtered or unexported fields
}

JSSandbox executes untrusted JS hook scripts in a time-limited Goja VM.

func NewJSSandbox

func NewJSSandbox() *JSSandbox

NewJSSandbox returns a sandbox with 80 ms watchdog and GOMAXPROCS*2 concurrency limit.

func (*JSSandbox) RunHook

func (s *JSSandbox) RunHook(
	ctx context.Context,
	script string,
	payload map[string]any,
	userCtx map[string]any,
) (*HookResult, error)

RunHook executes script inside a fresh Goja VM with an 80 ms watchdog interrupt. payload is the mutable request data; userCtx carries role/user_id/tenant_id. The script may read and modify `data`, and must set `result.proceed` to false (with `result.error`) to abort the operation. Returns (*goja.InterruptedError, nil) if the watchdog fires.

type WasmRunner

type WasmRunner struct {
	// contains filtered or unexported fields
}

WasmRunner executes sandboxed WASM (Capa 3) modules. Each module is compiled once and cached per (tenant, module-hash); instances are created and destroyed per Execute call. Guests get NO filesystem, network, clock-walltime, or syscall access — only the two host functions appximo_log and appximo_now. Execution time is bounded by the caller's context deadline (or wasmDefaultTimeout), and the runtime is configured to abort a running guest when that context is done.

func NewWasmRunner

func NewWasmRunner(ctx context.Context) (*WasmRunner, error)

NewWasmRunner builds a runner with a 16 MiB per-module memory cap and registers the host functions. The provided ctx is used only to instantiate the host module; per-call deadlines are passed to Execute.

func (*WasmRunner) Close

func (wr *WasmRunner) Close(ctx context.Context) error

Close releases the runtime and all cached compiled modules.

func (*WasmRunner) CompileCount

func (wr *WasmRunner) CompileCount() int64

CompileCount returns how many distinct modules have actually been compiled (cache misses). Used by tests to assert the cache prevents recompilation.

func (*WasmRunner) Execute

func (wr *WasmRunner) Execute(ctx context.Context, tenantID string, wasmBytes []byte, functionName string, input []byte) ([]byte, error)

Execute runs functionName (default "transform") in the given module, passing input through the (ptr,len) ABI and returning the bytes the function points to.

ABI: the module must export "memory", "alloc"(size i32)->(ptr i32), and functionName as (ptr i32, len i32)->(outPtr i32, outLen i32). "free" is called if exported. The returned slice is copied out of guest memory before the instance is closed.

Timeout: if ctx has no deadline, wasmDefaultTimeout is applied. A guest that runs past the deadline is aborted and Execute returns an error.

func (*WasmRunner) ExecuteNamed

func (wr *WasmRunner) ExecuteNamed(ctx context.Context, tenantID, moduleName, functionName string, input []byte) ([]byte, error)

ExecuteNamed resolves a pre-loaded module by name and runs functionName on it. Used by the HookRunner for HookConfig.Type == "wasm".

func (*WasmRunner) RegisterModule

func (wr *WasmRunner) RegisterModule(name string, wasmBytes []byte)

RegisterModule pre-loads a module under a name so hooks can reference it by HookConfig.WasmModule. A defensive copy of wasmBytes is stored.

type WebhookDispatcher

type WebhookDispatcher struct {
	// contains filtered or unexported fields
}

WebhookDispatcher sends signed HTTP POST notifications to webhook endpoints.

func NewWebhookDispatcher

func NewWebhookDispatcher() *WebhookDispatcher

NewWebhookDispatcher creates a production dispatcher with:

  • SSRF egress guard (blocks loopback, private, and link-local IPs)
  • HTTPS-only enforcement
  • No redirect following (prevents open-redirect SSRF bypasses)

func NewWebhookDispatcherOpts

func NewWebhookDispatcherOpts(opts ...func(*WebhookDispatcher)) *WebhookDispatcher

NewWebhookDispatcherOpts creates a dispatcher with optional overrides.

func (*WebhookDispatcher) Dispatch

func (d *WebhookDispatcher) Dispatch(ctx context.Context, hook *schema.HookConfig, event string, payload map[string]any, tenantID string)

Dispatch sends a signed POST to hook.URL with up to 3 retries (4 total attempts) and exponential backoff (1s, 2s, 4s). Failures are logged but never returned.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL