Marketplace examples
:::caution 0.2.0 Private Preview snapshot
This independent snapshot records what was true for 0.2.0. The legacy marketplace pages were illustrative in that release: no public marketplace, supported third-party SDK distribution, live publish path, revenue program, review SLA, analytics/ranking contract, or marketplace legal agreement was available.
:::
How to use these examples
The three retired examples below use the retired native Go SDK (github.com/nexo-proxy/plugin-sdk-go) and its contexts/results. They are preserved for design parity, not as buildable public samples. For 0.2.0, only bundle-pinned steps were supported. The repository-local WASM experiment did not create a public deployment path; any adaptation still required reworking manifests, actions, contexts, configuration, tests, and packaging and remained unsupported.
Sample 1: Request Logger
The example logs request metadata, optionally includes BSON bodies, filters commands, supports JSON/pretty output, and demonstrates configuration validation and tests. Body logging may expose credentials, PII, secrets, and regulated data; it should be disabled by default and governed by retention/redaction policy.
Sample 2: IP Allowlist
The example supports allow/deny modes, CIDR parsing, client-address matching, logging, rejection with MongoDB-compatible error information, configuration variants, and tests. A plugin check must not replace network policy, firewalling, authentication, or current verified-principal controls. Fail closed on malformed addresses and protect trusted proxy/header boundaries.
Sample 3: Query Complexity Analyzer
The example assigns costs to query operators, depth, fields, $lookup, $group, $unwind, sorting, regex, JavaScript, and document size; returns a breakdown; and can warn or reject above a threshold. Scores are heuristics, not query plans or a supported security boundary. Test against representative BSON and bound recursive parsing.
Repository-local TinyGo rate limiter
A direct local sample imports the internal Azure DevOps SDK, parses max_requests_per_second, defaults to 100, keys a plugin KV counter by client address, applies a one-second TTL, emits counters/logs, and rejects above the limit. It demonstrates local SDK calls but does not establish public package availability, distributed accuracy, atomic increments, production fairness, or marketplace installability.
// Package main implements a simple per-client rate limiter as a Nexo plugin.
//
// Build: tinygo build -o rate-limiter.wasm -target=wasi -scheduler=none ./
package main
import (
"encoding/binary"
"encoding/json"
"time"
"dev.azure.com/nexo-io/Nexo/nexo-cli.git/sdk/go/nexo"
)
// Config holds the plugin's JSON configuration.
type Config struct {
MaxRequestsPerSecond int `json:"max_requests_per_second"`
}
// RateLimiter implements nexo.NexoPlugin.
type RateLimiter struct {
nexo.BasePlugin
maxRPS int
}
func (r *RateLimiter) Init(config []byte) error {
var cfg Config
if err := json.Unmarshal(config, &cfg); err != nil {
return err
}
r.maxRPS = cfg.MaxRequestsPerSecond
if r.maxRPS <= 0 {
r.maxRPS = 100 // default
}
nexo.Log(nexo.LevelInfo, "rate-limiter initialized: max %d req/s", r.maxRPS)
return nil
}
func (r *RateLimiter) OnRequest(ctx *nexo.RequestContext) nexo.Action {
key := "rl:" + ctx.ClientAddr()
// Read current counter from KV
data := nexo.KVGet(key)
var count int64
if data != nil && len(data) == 8 {
count = int64(binary.LittleEndian.Uint64(data))
}
count++
if count > int64(r.maxRPS) {
nexo.Counter("rate_limiter.rejected", 1)
ctx.SetError("rate limit exceeded")
ctx.Log(nexo.LevelWarn, "rate limit exceeded for %s (%d > %d)", ctx.ClientAddr(), count, r.maxRPS)
return nexo.Reject
}
// Store updated count with 1-second TTL
buf := make([]byte, 8)
binary.LittleEndian.PutUint64(buf, uint64(count))
nexo.KVSet(key, buf, 1*time.Second)
nexo.Counter("rate_limiter.allowed", 1)
return nexo.Continue
}
func main() {
nexo.Register(&RateLimiter{})
}
Legacy dashboard catalog cards
| Illustrative dashboard card | Version | Phase | Price/download display | Qualification |
|---|---|---|---|---|
| Field Trimmer | 1.0.0 | response | Free; 1,830 | Seed data only |
| Timestamp Injector | 1.1.0 | request, response | Free; 1,510 | Seed data only |
| Mock Responses | 1.0.2 | response | Free; 2,090 | Seed data only |
| Chaos Monkey | 0.9.4 | request, response | Free; 1,325 | Seed data only |
| Slack Alert | 2.0.0 | response | $9/mo; 2,740 | Seed data only |
| S3 Query Archive | 1.3.1 | request, response | $19/mo; 960 | Seed data only |
| PagerDuty Incident | 1.4.0 | response | $9/mo; 1,180 | Seed data only |
| Elasticsearch Sync | 2.1.0 | request, response | $29/mo; 840 | Seed data only |
Other legacy example lists
The SDK page named rate-limiter, audit-trail, query-cache, schema-validator, query-rewriter, metrics-exporter, and wasm-request-logger as an “official repository.” Only the local TinyGo rate-limiter file was found directly; the public repository and support claims are unavailable.
The developer page proposed these additional ideas:
- Datadog Logger — query logs with APM correlation (legacy demand: High).
- Slack Alerter — alerts for slow queries, errors, and schema violations (High).
- GraphQL-to-Mongo — translate GraphQL to
find/aggregate(Medium). - IP Allowlisting — IP and geo-fencing (High).
- Query Analyzer — explain-plan suggestions in response metadata (Medium).
- RBAC Enforcer — collection/field permissions from JWT claims (High).
- Change Feed to Kafka — stream change events to Kafka (High).
- Auto-Archiver — move old documents to cold collections (Medium).
These are idea prompts, not available products or validated market-demand measurements.
Complete legacy example archive
All project structures, manifests, implementations, configurations, tests, and run commands from the retired examples page follow. Package names and commands are legacy/illustrative.
1. Request Logger project structure
request-logger/
- manifest.json
- main.go
- main_test.go
- go.mod
- go.sum
2. Request Logger manifest
{
"name": "request-logger",
"version": "1.0.0",
"display_name": "Request Logger",
"description": "Logs every MongoDB request with operation type, database, collection, client IP, and timestamp.",
"author": "nexo-examples",
"license": "MIT",
"homepage": "https://github.com/nexo-examples/request-logger",
"runtime": "go",
"entry_point": "request-logger",
"min_sdk_version": "0.5.0",
"hooks": ["request", "response"],
"config_schema": {
"type": "object",
"properties": {
"level": {
"type": "string",
"enum": ["debug", "info", "warn", "error"],
"default": "info",
"description": "Log level for request/response messages."
},
"include_body": {
"type": "boolean",
"default": false,
"description": "When true, logs the raw byte length of request bodies at debug level."
}
},
"additionalProperties": false
},
"categories": ["observability", "logging"],
"tags": ["logging", "monitoring", "debug", "observability"],
"icon": "📝"
}
3. Request Logger implementation
package main
import (
"fmt"
"time"
sdk "github.com/nexo-proxy/plugin-sdk-go"
)
type RequestLogger struct {
logLevel string
includeBody bool
}
func (r *RequestLogger) Name() string { return "request-logger" }
func (r *RequestLogger) Version() string { return "1.0.0" }
func (r *RequestLogger) Init(config map[string]interface{}) error {
r.logLevel = "info"
if level, ok := config["level"].(string); ok {
r.logLevel = level
}
if include, ok := config["include_body"].(bool); ok {
r.includeBody = include
}
return nil
}
func (r *RequestLogger) ProcessRequest(ctx *sdk.RequestContext) (*sdk.RequestResult, error) {
ctx.Logger.Info("mongodb request",
"op", ctx.OpCode.String(),
"db", ctx.Database,
"collection", ctx.Collection,
"client", ctx.ClientAddr,
"timestamp", time.Now().UTC().Format(time.RFC3339),
)
if r.includeBody {
ctx.Logger.Debug("request body", "bytes", len(ctx.MessageBytes))
}
return &sdk.RequestResult{Action: sdk.Continue}, nil
}
func (r *RequestLogger) ProcessResponse(ctx *sdk.ResponseContext) (*sdk.ResponseResult, error) {
ctx.Logger.Info("mongodb response",
"op", ctx.OpCode.String(),
"duration_ms", ctx.RequestDuration.Milliseconds(),
)
return &sdk.ResponseResult{Action: sdk.Continue}, nil
}
func (r *RequestLogger) Close() error { return nil }
// Compile-time interface check
var _ sdk.Step = (*RequestLogger)(nil)
func main() {
sdk.Register(&RequestLogger{})
}
4. Request Logger configuration
{
"plugins": {
"request-logger": {
"enabled": true,
"config": {
"level": "info",
"include_body": false
}
}
}
}
5. Request Logger alternate configuration
{
"plugins": {
"request-logger": {
"enabled": true,
"config": {
"level": "debug",
"include_body": true
}
}
}
}
6. Request Logger tests
package main
import (
"testing"
"time"
sdk "github.com/nexo-proxy/plugin-sdk-go"
"github.com/nexo-proxy/plugin-sdk-go/testutil"
)
func TestRequestLoggerName(t *testing.T) {
r := &RequestLogger{}
if r.Name() != "request-logger" {
t.Errorf("expected name 'request-logger', got '%s'", r.Name())
}
}
func TestRequestLoggerVersion(t *testing.T) {
r := &RequestLogger{}
if r.Version() != "1.0.0" {
t.Errorf("expected version '1.0.0', got '%s'", r.Version())
}
}
func TestInitDefaults(t *testing.T) {
r := &RequestLogger{}
err := r.Init(map[string]interface{}{})
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if r.logLevel != "info" {
t.Errorf("expected default log level 'info', got '%s'", r.logLevel)
}
if r.includeBody {
t.Error("expected include_body to default to false")
}
}
func TestInitCustomConfig(t *testing.T) {
r := &RequestLogger{}
err := r.Init(map[string]interface{}{
"level": "debug",
"include_body": true,
})
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if r.logLevel != "debug" {
t.Errorf("expected log level 'debug', got '%s'", r.logLevel)
}
if !r.includeBody {
t.Error("expected include_body to be true")
}
}
func TestProcessRequestContinues(t *testing.T) {
r := &RequestLogger{}
_ = r.Init(map[string]interface{}{})
ctx := testutil.NewRequestContext(
testutil.WithOpCode(sdk.OpQuery),
testutil.WithDatabase("mydb"),
testutil.WithCollection("users"),
testutil.WithClientAddr("192.168.1.100:54321"),
testutil.WithMessageBytes([]byte("test-payload")),
)
result, err := r.ProcessRequest(ctx)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if result.Action != sdk.Continue {
t.Errorf("expected Continue action, got %v", result.Action)
}
}
func TestProcessResponseContinues(t *testing.T) {
r := &RequestLogger{}
_ = r.Init(map[string]interface{}{})
ctx := testutil.NewResponseContext(
testutil.WithResponseOpCode(sdk.OpReply),
testutil.WithRequestDuration(150 * time.Millisecond),
)
result, err := r.ProcessResponse(ctx)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if result.Action != sdk.Continue {
t.Errorf("expected Continue action, got %v", result.Action)
}
}
func TestCloseReturnsNil(t *testing.T) {
r := &RequestLogger{}
if err := r.Close(); err != nil {
t.Errorf("expected nil error from Close, got %v", err)
}
}
7. Request Logger test workflow
cd request-logger
go test -v ./...
# Expected output:
# === RUN TestRequestLoggerName
# --- PASS: TestRequestLoggerName (0.00s)
# === RUN TestRequestLoggerVersion
# --- PASS: TestRequestLoggerVersion (0.00s)
# === RUN TestInitDefaults
# --- PASS: TestInitDefaults (0.00s)
# === RUN TestInitCustomConfig
# --- PASS: TestInitCustomConfig (0.00s)
# === RUN TestProcessRequestContinues
# --- PASS: TestProcessRequestContinues (0.00s)
# === RUN TestProcessResponseContinues
# --- PASS: TestProcessResponseContinues (0.00s)
# === RUN TestCloseReturnsNil
# --- PASS: TestCloseReturnsNil (0.00s)
# PASS
8. IP Allowlist project structure
ip-allowlist/
- manifest.json
- main.go
- main_test.go
- go.mod
- go.sum
9. IP Allowlist manifest
{
"name": "ip-allowlist",
"version": "1.0.0",
"display_name": "IP Allowlist",
"description": "Checks client IPs against a configured allowlist. Rejects connections from unauthorized IPs with a MongoDB error.",
"author": "nexo-examples",
"license": "MIT",
"homepage": "https://github.com/nexo-examples/ip-allowlist",
"runtime": "go",
"entry_point": "ip-allowlist",
"min_sdk_version": "0.5.0",
"hooks": ["request"],
"config_schema": {
"type": "object",
"properties": {
"allowed_ips": {
"type": "array",
"items": { "type": "string" },
"default": [],
"description": "List of individual IP addresses allowed to connect (e.g., '10.0.0.1')."
},
"allowed_cidrs": {
"type": "array",
"items": { "type": "string" },
"default": [],
"description": "List of CIDR ranges allowed to connect (e.g., '10.0.0.0/8', '192.168.1.0/24')."
},
"reject_message": {
"type": "string",
"default": "connection rejected: IP not in allowlist",
"description": "Custom error message returned to blocked clients."
},
"log_blocked": {
"type": "boolean",
"default": true,
"description": "Log a warning for each blocked connection attempt."
}
},
"additionalProperties": false
},
"categories": ["security"],
"tags": ["security", "firewall", "ip-filter", "access-control"],
"icon": "🛡️"
}
10. IP Allowlist implementation
package main
import (
"fmt"
"net"
"strings"
sdk "github.com/nexo-proxy/plugin-sdk-go"
)
type IPAllowlist struct {
allowedIPs map[string]bool
allowedCIDRs []*net.IPNet
rejectMessage string
logBlocked bool
}
func (p *IPAllowlist) Name() string { return "ip-allowlist" }
func (p *IPAllowlist) Version() string { return "1.0.0" }
func (p *IPAllowlist) Init(config map[string]interface{}) error {
p.allowedIPs = make(map[string]bool)
p.rejectMessage = "connection rejected: IP not in allowlist"
p.logBlocked = true
// Parse individual IPs
if ips, ok := config["allowed_ips"].([]interface{}); ok {
for _, raw := range ips {
ipStr, ok := raw.(string)
if !ok {
continue
}
parsed := net.ParseIP(strings.TrimSpace(ipStr))
if parsed == nil {
return fmt.Errorf("invalid IP address: %q", ipStr)
}
p.allowedIPs[parsed.String()] = true
}
}
// Parse CIDR ranges
if cidrs, ok := config["allowed_cidrs"].([]interface{}); ok {
for _, raw := range cidrs {
cidrStr, ok := raw.(string)
if !ok {
continue
}
_, network, err := net.ParseCIDR(strings.TrimSpace(cidrStr))
if err != nil {
return fmt.Errorf("invalid CIDR range %q: %w", cidrStr, err)
}
p.allowedCIDRs = append(p.allowedCIDRs, network)
}
}
// Validate that at least one rule is configured
if len(p.allowedIPs) == 0 && len(p.allowedCIDRs) == 0 {
return fmt.Errorf("at least one allowed_ips or allowed_cidrs entry is required")
}
if msg, ok := config["reject_message"].(string); ok && msg != "" {
p.rejectMessage = msg
}
if logBlocked, ok := config["log_blocked"].(bool); ok {
p.logBlocked = logBlocked
}
return nil
}
// extractHost splits a "host:port" address and returns just the host.
func extractHost(addr string) string {
host, _, err := net.SplitHostPort(addr)
if err != nil {
// addr might already be a bare IP
return addr
}
return host
}
// isAllowed checks if the given IP is in the allowlist.
func (p *IPAllowlist) isAllowed(ipStr string) bool {
ip := net.ParseIP(ipStr)
if ip == nil {
return false
}
// Check exact IP match
if p.allowedIPs[ip.String()] {
return true
}
// Check CIDR ranges
for _, network := range p.allowedCIDRs {
if network.Contains(ip) {
return true
}
}
return false
}
func (p *IPAllowlist) ProcessRequest(ctx *sdk.RequestContext) (*sdk.RequestResult, error) {
clientIP := extractHost(ctx.ClientAddr)
if p.isAllowed(clientIP) {
ctx.Logger.Debug("ip allowed", "client", clientIP)
return &sdk.RequestResult{Action: sdk.Continue}, nil
}
if p.logBlocked {
ctx.Logger.Warn("ip blocked",
"client", clientIP,
"db", ctx.Database,
"collection", ctx.Collection,
)
}
return &sdk.RequestResult{
Action: sdk.Reject,
Error: &sdk.MongoError{
Code: 13,
Message: fmt.Sprintf("%s (client: %s)", p.rejectMessage, clientIP),
},
}, nil
}
func (p *IPAllowlist) ProcessResponse(ctx *sdk.ResponseContext) (*sdk.ResponseResult, error) {
return &sdk.ResponseResult{Action: sdk.Continue}, nil
}
func (p *IPAllowlist) Close() error { return nil }
var _ sdk.Step = (*IPAllowlist)(nil)
func main() {
sdk.Register(&IPAllowlist{})
}
11. IP Allowlist allow-mode config
{
"plugins": {
"ip-allowlist": {
"enabled": true,
"config": {
"allowed_ips": [
"10.0.1.50",
"10.0.1.51",
"10.0.1.52"
],
"allowed_cidrs": [
"10.0.0.0/8",
"172.16.0.0/12",
"192.168.0.0/16",
"fd00::/8"
],
"reject_message": "connection rejected: your IP is not authorized to access this database",
"log_blocked": true
}
}
}
}
12. IP Allowlist deny-mode config
{
"plugins": {
"ip-allowlist": {
"enabled": true,
"config": {
"allowed_ips": [
"203.0.113.10",
"203.0.113.11"
],
"allowed_cidrs": [],
"reject_message": "staging access denied",
"log_blocked": true
}
}
}
}
13. IP Allowlist tests
package main
import (
"testing"
sdk "github.com/nexo-proxy/plugin-sdk-go"
"github.com/nexo-proxy/plugin-sdk-go/testutil"
)
func newPlugin(t *testing.T, config map[string]interface{}) *IPAllowlist {
t.Helper()
p := &IPAllowlist{}
if err := p.Init(config); err != nil {
t.Fatalf("Init failed: %v", err)
}
return p
}
func TestInitRequiresAtLeastOneRule(t *testing.T) {
p := &IPAllowlist{}
err := p.Init(map[string]interface{}{})
if err == nil {
t.Fatal("expected error when no IPs or CIDRs are configured")
}
}
func TestInitRejectsInvalidIP(t *testing.T) {
p := &IPAllowlist{}
err := p.Init(map[string]interface{}{
"allowed_ips": []interface{}{"not-an-ip"},
})
if err == nil {
t.Fatal("expected error for invalid IP")
}
}
func TestInitRejectsInvalidCIDR(t *testing.T) {
p := &IPAllowlist{}
err := p.Init(map[string]interface{}{
"allowed_cidrs": []interface{}{"999.999.999.0/24"},
})
if err == nil {
t.Fatal("expected error for invalid CIDR")
}
}
func TestAllowedExactIP(t *testing.T) {
p := newPlugin(t, map[string]interface{}{
"allowed_ips": []interface{}{"192.168.1.100"},
})
ctx := testutil.NewRequestContext(
testutil.WithClientAddr("192.168.1.100:54321"),
testutil.WithDatabase("mydb"),
)
result, err := p.ProcessRequest(ctx)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if result.Action != sdk.Continue {
t.Errorf("expected Continue for allowed IP, got %v", result.Action)
}
}
func TestBlockedIP(t *testing.T) {
p := newPlugin(t, map[string]interface{}{
"allowed_ips": []interface{}{"192.168.1.100"},
})
ctx := testutil.NewRequestContext(
testutil.WithClientAddr("10.99.99.99:54321"),
testutil.WithDatabase("mydb"),
)
result, err := p.ProcessRequest(ctx)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if result.Action != sdk.Reject {
t.Errorf("expected Reject for blocked IP, got %v", result.Action)
}
if result.Error == nil {
t.Fatal("expected MongoError in result")
}
if result.Error.Code != 13 {
t.Errorf("expected error code 13, got %d", result.Error.Code)
}
}
func TestAllowedByCIDR(t *testing.T) {
p := newPlugin(t, map[string]interface{}{
"allowed_cidrs": []interface{}{"10.0.0.0/8"},
})
ctx := testutil.NewRequestContext(
testutil.WithClientAddr("10.200.50.7:12345"),
testutil.WithDatabase("mydb"),
)
result, err := p.ProcessRequest(ctx)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if result.Action != sdk.Continue {
t.Errorf("expected Continue for IP in CIDR range, got %v", result.Action)
}
}
func TestBlockedNotInCIDR(t *testing.T) {
p := newPlugin(t, map[string]interface{}{
"allowed_cidrs": []interface{}{"10.0.0.0/8"},
})
ctx := testutil.NewRequestContext(
testutil.WithClientAddr("172.20.0.1:12345"),
testutil.WithDatabase("mydb"),
)
result, err := p.ProcessRequest(ctx)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if result.Action != sdk.Reject {
t.Errorf("expected Reject for IP outside CIDR, got %v", result.Action)
}
}
func TestMixedIPAndCIDR(t *testing.T) {
p := newPlugin(t, map[string]interface{}{
"allowed_ips": []interface{}{"203.0.113.50"},
"allowed_cidrs": []interface{}{"10.0.0.0/8"},
})
tests := []struct {
name string
addr string
expect sdk.Action
}{
{"exact match", "203.0.113.50:8080", sdk.Continue},
{"cidr match", "10.5.5.5:9090", sdk.Continue},
{"no match", "192.168.1.1:7070", sdk.Reject},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
ctx := testutil.NewRequestContext(
testutil.WithClientAddr(tc.addr),
testutil.WithDatabase("test"),
)
result, err := p.ProcessRequest(ctx)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if result.Action != tc.expect {
t.Errorf("addr=%s: expected %v, got %v", tc.addr, tc.expect, result.Action)
}
})
}
}
func TestCustomRejectMessage(t *testing.T) {
p := newPlugin(t, map[string]interface{}{
"allowed_ips": []interface{}{"127.0.0.1"},
"reject_message": "go away",
})
ctx := testutil.NewRequestContext(
testutil.WithClientAddr("8.8.8.8:53"),
testutil.WithDatabase("admin"),
)
result, _ := p.ProcessRequest(ctx)
if result.Action != sdk.Reject {
t.Fatal("expected Reject")
}
if result.Error == nil || result.Error.Message == "" {
t.Fatal("expected error message")
}
if !strings.Contains(result.Error.Message, "go away") {
t.Errorf("expected custom message, got %q", result.Error.Message)
}
}
func TestExtractHost(t *testing.T) {
tests := []struct {
input string
expected string
}{
{"192.168.1.1:27017", "192.168.1.1"},
{"10.0.0.1:0", "10.0.0.1"},
{"[::1]:27017", "::1"},
{"bare-ip", "bare-ip"},
}
for _, tc := range tests {
got := extractHost(tc.input)
if got != tc.expected {
t.Errorf("extractHost(%q) = %q, want %q", tc.input, got, tc.expected)
}
}
}
14. IP Allowlist test workflow
cd ip-allowlist
go test -v -count=1 ./...
# Expected output:
# === RUN TestInitRequiresAtLeastOneRule
# --- PASS: TestInitRequiresAtLeastOneRule (0.00s)
# === RUN TestInitRejectsInvalidIP
# --- PASS: TestInitRejectsInvalidIP (0.00s)
# === RUN TestInitRejectsInvalidCIDR
# --- PASS: TestInitRejectsInvalidCIDR (0.00s)
# === RUN TestAllowedExactIP
# --- PASS: TestAllowedExactIP (0.00s)
# === RUN TestBlockedIP
# --- PASS: TestBlockedIP (0.00s)
# === RUN TestAllowedByCIDR
# --- PASS: TestAllowedByCIDR (0.00s)
# === RUN TestBlockedNotInCIDR
# --- PASS: TestBlockedNotInCIDR (0.00s)
# === RUN TestMixedIPAndCIDR
# --- PASS: TestMixedIPAndCIDR (0.00s)
# === RUN TestCustomRejectMessage
# --- PASS: TestCustomRejectMessage (0.00s)
# === RUN TestExtractHost
# --- PASS: TestExtractHost (0.00s)
# PASS
15. Query Complexity project structure
query-complexity/
- manifest.json
- main.go
- main_test.go
- go.mod
- go.sum
16. Query Complexity manifest
{
"name": "query-complexity",
"version": "1.0.0",
"display_name": "Query Complexity Analyzer",
"description": "Parses OP_MSG commands, estimates query cost based on expensive operations ($lookup, $graphLookup, $where, etc.), and rejects queries exceeding a configurable threshold.",
"author": "nexo-examples",
"license": "MIT",
"homepage": "https://github.com/nexo-examples/query-complexity",
"runtime": "go",
"entry_point": "query-complexity",
"min_sdk_version": "0.5.0",
"hooks": ["request"],
"config_schema": {
"type": "object",
"properties": {
"max_cost": {
"type": "integer",
"default": 50,
"minimum": 1,
"description": "Maximum allowed query cost. Queries exceeding this threshold are rejected."
},
"weights": {
"type": "object",
"properties": {
"lookup": { "type": "integer", "default": 10 },
"graph_lookup": { "type": "integer", "default": 20 },
"unwind": { "type": "integer", "default": 3 },
"regex": { "type": "integer", "default": 5 },
"where": { "type": "integer", "default": 50 },
"no_hint": { "type": "integer", "default": 2 }
},
"additionalProperties": false,
"description": "Custom cost weights per operation type."
},
"exempt_collections": {
"type": "array",
"items": { "type": "string" },
"default": [],
"description": "Collections exempt from complexity analysis (e.g., 'system.profile')."
},
"log_analysis": {
"type": "boolean",
"default": true,
"description": "Log cost breakdown for every analyzed query."
}
},
"additionalProperties": false
},
"categories": ["security", "performance"],
"tags": ["query-analysis", "performance", "cost-control", "aggregation", "security"],
"icon": "🔬"
}
17. Query Complexity implementation
package main
import (
"fmt"
"strings"
sdk "github.com/nexo-proxy/plugin-sdk-go"
"go.mongodb.org/mongo-driver/bson"
)
// CostWeights holds the configurable cost for each expensive operation.
type CostWeights struct {
Lookup int
GraphLookup int
Unwind int
Regex int
Where int
NoHint int
}
// CostBreakdown records which operations were found and their costs.
type CostBreakdown struct {
Operations []string
Costs []int
Total int
}
func (cb *CostBreakdown) Add(op string, cost int) {
cb.Operations = append(cb.Operations, op)
cb.Costs = append(cb.Costs, cost)
cb.Total += cost
}
func (cb *CostBreakdown) Summary() string {
if len(cb.Operations) == 0 {
return "no expensive operations detected"
}
parts := make([]string, len(cb.Operations))
for i, op := range cb.Operations {
parts[i] = fmt.Sprintf("%s(+%d)", op, cb.Costs[i])
}
return fmt.Sprintf("total=%d [%s]", cb.Total, strings.Join(parts, ", "))
}
// QueryComplexity is the main plugin struct.
type QueryComplexity struct {
maxCost int
weights CostWeights
exemptCollections map[string]bool
logAnalysis bool
}
func (q *QueryComplexity) Name() string { return "query-complexity" }
func (q *QueryComplexity) Version() string { return "1.0.0" }
func (q *QueryComplexity) Init(config map[string]interface{}) error {
// Defaults
q.maxCost = 50
q.weights = CostWeights{
Lookup: 10,
GraphLookup: 20,
Unwind: 3,
Regex: 5,
Where: 50,
NoHint: 2,
}
q.exemptCollections = make(map[string]bool)
q.logAnalysis = true
if mc, ok := config["max_cost"].(float64); ok {
q.maxCost = int(mc)
}
// Parse custom weights
if w, ok := config["weights"].(map[string]interface{}); ok {
if v, ok := w["lookup"].(float64); ok {
q.weights.Lookup = int(v)
}
if v, ok := w["graph_lookup"].(float64); ok {
q.weights.GraphLookup = int(v)
}
if v, ok := w["unwind"].(float64); ok {
q.weights.Unwind = int(v)
}
if v, ok := w["regex"].(float64); ok {
q.weights.Regex = int(v)
}
if v, ok := w["where"].(float64); ok {
q.weights.Where = int(v)
}
if v, ok := w["no_hint"].(float64); ok {
q.weights.NoHint = int(v)
}
}
// Parse exempt collections
if cols, ok := config["exempt_collections"].([]interface{}); ok {
for _, raw := range cols {
if col, ok := raw.(string); ok {
q.exemptCollections[col] = true
}
}
}
if la, ok := config["log_analysis"].(bool); ok {
q.logAnalysis = la
}
return nil
}
// analyzeDocument recursively walks a BSON document and accumulates cost.
func (q *QueryComplexity) analyzeDocument(doc bson.D, breakdown *CostBreakdown) {
for _, elem := range doc {
switch elem.Key {
case "$lookup":
breakdown.Add("$lookup", q.weights.Lookup)
case "$graphLookup":
breakdown.Add("$graphLookup", q.weights.GraphLookup)
case "$unwind":
breakdown.Add("$unwind", q.weights.Unwind)
case "$regex":
breakdown.Add("$regex", q.weights.Regex)
case "$where":
breakdown.Add("$where", q.weights.Where)
}
// Recurse into nested documents
switch val := elem.Value.(type) {
case bson.D:
q.analyzeDocument(val, breakdown)
case bson.A:
q.analyzeArray(val, breakdown)
}
}
}
// analyzeArray walks a BSON array and recurses into any nested documents.
func (q *QueryComplexity) analyzeArray(arr bson.A, breakdown *CostBreakdown) {
for _, item := range arr {
switch val := item.(type) {
case bson.D:
q.analyzeDocument(val, breakdown)
case bson.A:
q.analyzeArray(val, breakdown)
}
}
}
// checkForHint returns true if the command document contains a "hint" key.
func checkForHint(doc bson.D) bool {
for _, elem := range doc {
if elem.Key == "hint" {
return true
}
}
return false
}
func (q *QueryComplexity) ProcessRequest(ctx *sdk.RequestContext) (*sdk.RequestResult, error) {
// Only analyze OP_MSG messages
if ctx.OpCode != sdk.OpMsg {
return &sdk.RequestResult{Action: sdk.Continue}, nil
}
// Skip exempt collections
if q.exemptCollections[ctx.Collection] {
ctx.Logger.Debug("skipping exempt collection", "collection", ctx.Collection)
return &sdk.RequestResult{Action: sdk.Continue}, nil
}
// Parse the OP_MSG body into a BSON document
cmdDoc, err := sdk.ParseOpMsg(ctx.MessageBytes)
if err != nil {
ctx.Logger.Warn("failed to parse OP_MSG", "error", err.Error())
// Don't block on parse errors — let the query through
return &sdk.RequestResult{Action: sdk.Continue}, nil
}
// Analyze cost
breakdown := &CostBreakdown{}
q.analyzeDocument(cmdDoc, breakdown)
// Check for missing hint on find/aggregate commands
cmdName := ""
if len(cmdDoc) > 0 {
cmdName = cmdDoc[0].Key
}
if (cmdName == "find" || cmdName == "aggregate") && !checkForHint(cmdDoc) {
breakdown.Add("no_hint", q.weights.NoHint)
}
// Log the analysis
if q.logAnalysis {
ctx.Logger.Info("query complexity analysis",
"db", ctx.Database,
"collection", ctx.Collection,
"command", cmdName,
"cost", breakdown.Total,
"max_cost", q.maxCost,
"breakdown", breakdown.Summary(),
)
}
// Reject if cost exceeds threshold
if breakdown.Total > q.maxCost {
return &sdk.RequestResult{
Action: sdk.Reject,
Error: &sdk.MongoError{
Code: 12500,
Message: fmt.Sprintf(
"query rejected: estimated cost %d exceeds maximum allowed cost %d. Breakdown: %s. "+
"Consider simplifying your query, adding index hints, or reducing pipeline stages.",
breakdown.Total, q.maxCost, breakdown.Summary(),
),
},
}, nil
}
return &sdk.RequestResult{Action: sdk.Continue}, nil
}
func (q *QueryComplexity) ProcessResponse(ctx *sdk.ResponseContext) (*sdk.ResponseResult, error) {
return &sdk.ResponseResult{Action: sdk.Continue}, nil
}
func (q *QueryComplexity) Close() error { return nil }
var _ sdk.Step = (*QueryComplexity)(nil)
func main() {
sdk.Register(&QueryComplexity{})
}
18. Query Complexity balanced config
{
"plugins": {
"query-complexity": {
"enabled": true,
"config": {
"max_cost": 50,
"weights": {
"lookup": 10,
"graph_lookup": 20,
"unwind": 3,
"regex": 5,
"where": 50,
"no_hint": 2
},
"exempt_collections": [
"system.profile",
"system.namespaces",
"migrations"
],
"log_analysis": true
}
}
}
}
19. Query Complexity strict config
{
"plugins": {
"query-complexity": {
"enabled": true,
"config": {
"max_cost": 200,
"weights": {
"lookup": 5,
"graph_lookup": 15,
"unwind": 1,
"regex": 3,
"where": 50,
"no_hint": 0
},
"exempt_collections": [],
"log_analysis": true
}
}
}
}
20. Query Complexity tests
package main
import (
"testing"
sdk "github.com/nexo-proxy/plugin-sdk-go"
"github.com/nexo-proxy/plugin-sdk-go/testutil"
"go.mongodb.org/mongo-driver/bson"
)
func newAnalyzer(t *testing.T, config map[string]interface{}) *QueryComplexity {
t.Helper()
q := &QueryComplexity{}
if err := q.Init(config); err != nil {
t.Fatalf("Init failed: %v", err)
}
return q
}
func defaultConfig() map[string]interface{} {
return map[string]interface{}{
"max_cost": float64(50),
}
}
func TestInitDefaults(t *testing.T) {
q := newAnalyzer(t, map[string]interface{}{})
if q.maxCost != 50 {
t.Errorf("expected default max_cost=50, got %d", q.maxCost)
}
if q.weights.Lookup != 10 {
t.Errorf("expected default lookup weight=10, got %d", q.weights.Lookup)
}
if q.weights.Where != 50 {
t.Errorf("expected default where weight=50, got %d", q.weights.Where)
}
}
func TestInitCustomWeights(t *testing.T) {
q := newAnalyzer(t, map[string]interface{}{
"max_cost": float64(100),
"weights": map[string]interface{}{
"lookup": float64(5),
"graph_lookup": float64(15),
},
})
if q.maxCost != 100 {
t.Errorf("expected max_cost=100, got %d", q.maxCost)
}
if q.weights.Lookup != 5 {
t.Errorf("expected lookup weight=5, got %d", q.weights.Lookup)
}
if q.weights.GraphLookup != 15 {
t.Errorf("expected graph_lookup weight=15, got %d", q.weights.GraphLookup)
}
// Unchanged defaults
if q.weights.Unwind != 3 {
t.Errorf("expected default unwind weight=3, got %d", q.weights.Unwind)
}
}
func TestSkipNonOpMsg(t *testing.T) {
q := newAnalyzer(t, defaultConfig())
ctx := testutil.NewRequestContext(
testutil.WithOpCode(sdk.OpQuery),
testutil.WithDatabase("mydb"),
testutil.WithCollection("users"),
)
result, err := q.ProcessRequest(ctx)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if result.Action != sdk.Continue {
t.Errorf("expected Continue for non-OP_MSG, got %v", result.Action)
}
}
func TestExemptCollection(t *testing.T) {
q := newAnalyzer(t, map[string]interface{}{
"exempt_collections": []interface{}{"system.profile"},
})
ctx := testutil.NewRequestContext(
testutil.WithOpCode(sdk.OpMsg),
testutil.WithDatabase("mydb"),
testutil.WithCollection("system.profile"),
)
result, err := q.ProcessRequest(ctx)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if result.Action != sdk.Continue {
t.Errorf("expected Continue for exempt collection, got %v", result.Action)
}
}
func TestSimpleFindAllowed(t *testing.T) {
q := newAnalyzer(t, defaultConfig())
// A simple find command: { find: "users", filter: { age: 25 }, hint: "age_1" }
cmdDoc := bson.D{
{Key: "find", Value: "users"},
{Key: "filter", Value: bson.D{{Key: "age", Value: 25}}},
{Key: "hint", Value: "age_1"},
}
msgBytes := testutil.BuildOpMsg(cmdDoc)
ctx := testutil.NewRequestContext(
testutil.WithOpCode(sdk.OpMsg),
testutil.WithDatabase("mydb"),
testutil.WithCollection("users"),
testutil.WithMessageBytes(msgBytes),
)
result, err := q.ProcessRequest(ctx)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if result.Action != sdk.Continue {
t.Errorf("expected Continue for simple find, got %v", result.Action)
}
}
func TestFindWithoutHintAddsCost(t *testing.T) {
q := newAnalyzer(t, map[string]interface{}{
"max_cost": float64(1), // Very low threshold
})
cmdDoc := bson.D{
{Key: "find", Value: "users"},
{Key: "filter", Value: bson.D{{Key: "name", Value: "Alice"}}},
}
msgBytes := testutil.BuildOpMsg(cmdDoc)
ctx := testutil.NewRequestContext(
testutil.WithOpCode(sdk.OpMsg),
testutil.WithDatabase("mydb"),
testutil.WithCollection("users"),
testutil.WithMessageBytes(msgBytes),
)
result, err := q.ProcessRequest(ctx)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if result.Action != sdk.Reject {
t.Errorf("expected Reject for find without hint (cost 2 > max 1), got %v", result.Action)
}
}
func TestSingleLookupAllowed(t *testing.T) {
q := newAnalyzer(t, defaultConfig()) // max=50, $lookup=10
cmdDoc := bson.D{
{Key: "aggregate", Value: "orders"},
{Key: "pipeline", Value: bson.A{
bson.D{{Key: "$lookup", Value: bson.D{
{Key: "from", Value: "products"},
{Key: "localField", Value: "product_id"},
{Key: "foreignField", Value: "_id"},
{Key: "as", Value: "product"},
}}},
}},
{Key: "hint", Value: "product_id_1"},
}
msgBytes := testutil.BuildOpMsg(cmdDoc)
ctx := testutil.NewRequestContext(
testutil.WithOpCode(sdk.OpMsg),
testutil.WithDatabase("mydb"),
testutil.WithCollection("orders"),
testutil.WithMessageBytes(msgBytes),
)
result, err := q.ProcessRequest(ctx)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if result.Action != sdk.Continue {
t.Errorf("expected Continue for single $lookup (cost 10 < 50), got %v", result.Action)
}
}
func TestGraphLookupRejected(t *testing.T) {
q := newAnalyzer(t, map[string]interface{}{
"max_cost": float64(15),
})
cmdDoc := bson.D{
{Key: "aggregate", Value: "employees"},
{Key: "pipeline", Value: bson.A{
bson.D{{Key: "$graphLookup", Value: bson.D{
{Key: "from", Value: "employees"},
{Key: "startWith", Value: "$manager_id"},
{Key: "connectFromField", Value: "manager_id"},
{Key: "connectToField", Value: "_id"},
{Key: "as", Value: "reporting_chain"},
}}},
}},
{Key: "hint", Value: "manager_id_1"},
}
msgBytes := testutil.BuildOpMsg(cmdDoc)
ctx := testutil.NewRequestContext(
testutil.WithOpCode(sdk.OpMsg),
testutil.WithDatabase("hr"),
testutil.WithCollection("employees"),
testutil.WithMessageBytes(msgBytes),
)
result, err := q.ProcessRequest(ctx)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if result.Action != sdk.Reject {
t.Errorf("expected Reject for $graphLookup (cost 20 > max 15), got %v", result.Action)
}
if result.Error == nil || result.Error.Code != 12500 {
t.Errorf("expected error code 12500, got %v", result.Error)
}
}
func TestWhereRejected(t *testing.T) {
q := newAnalyzer(t, defaultConfig()) // max=50, $where=50
cmdDoc := bson.D{
{Key: "find", Value: "users"},
{Key: "filter", Value: bson.D{
{Key: "$where", Value: "this.age > 21"},
}},
{Key: "hint", Value: "age_1"},
}
msgBytes := testutil.BuildOpMsg(cmdDoc)
ctx := testutil.NewRequestContext(
testutil.WithOpCode(sdk.OpMsg),
testutil.WithDatabase("mydb"),
testutil.WithCollection("users"),
testutil.WithMessageBytes(msgBytes),
)
result, err := q.ProcessRequest(ctx)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if result.Action != sdk.Reject {
t.Errorf("expected Reject for $where (cost 50 >= max 50), got %v", result.Action)
}
}
func TestComplexPipelineRejected(t *testing.T) {
q := newAnalyzer(t, map[string]interface{}{
"max_cost": float64(30),
})
// Pipeline with $lookup (10) + $unwind (3) + $lookup (10) + $unwind (3) = 26
// Plus no_hint (2) = 28. This is under 30, so it should pass.
cmdDoc := bson.D{
{Key: "aggregate", Value: "orders"},
{Key: "pipeline", Value: bson.A{
bson.D{{Key: "$lookup", Value: bson.D{
{Key: "from", Value: "products"},
{Key: "localField", Value: "product_id"},
{Key: "foreignField", Value: "_id"},
{Key: "as", Value: "product"},
}}},
bson.D{{Key: "$unwind", Value: "$product"}},
bson.D{{Key: "$lookup", Value: bson.D{
{Key: "from", Value: "categories"},
{Key: "localField", Value: "product.category_id"},
{Key: "foreignField", Value: "_id"},
{Key: "as", Value: "category"},
}}},
bson.D{{Key: "$unwind", Value: "$category"}},
}},
}
msgBytes := testutil.BuildOpMsg(cmdDoc)
ctx := testutil.NewRequestContext(
testutil.WithOpCode(sdk.OpMsg),
testutil.WithDatabase("shop"),
testutil.WithCollection("orders"),
testutil.WithMessageBytes(msgBytes),
)
result, err := q.ProcessRequest(ctx)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
// 10 + 3 + 10 + 3 + 2 (no hint) = 28, under 30
if result.Action != sdk.Continue {
t.Errorf("expected Continue for pipeline cost 28 < 30, got %v", result.Action)
}
}
func TestComplexPipelineOverThreshold(t *testing.T) {
q := newAnalyzer(t, map[string]interface{}{
"max_cost": float64(25),
})
// Same pipeline: $lookup(10) + $unwind(3) + $lookup(10) + $unwind(3) + no_hint(2) = 28
cmdDoc := bson.D{
{Key: "aggregate", Value: "orders"},
{Key: "pipeline", Value: bson.A{
bson.D{{Key: "$lookup", Value: bson.D{
{Key: "from", Value: "products"},
{Key: "localField", Value: "product_id"},
{Key: "foreignField", Value: "_id"},
{Key: "as", Value: "product"},
}}},
bson.D{{Key: "$unwind", Value: "$product"}},
bson.D{{Key: "$lookup", Value: bson.D{
{Key: "from", Value: "categories"},
{Key: "localField", Value: "product.category_id"},
{Key: "foreignField", Value: "_id"},
{Key: "as", Value: "category"},
}}},
bson.D{{Key: "$unwind", Value: "$category"}},
}},
}
msgBytes := testutil.BuildOpMsg(cmdDoc)
ctx := testutil.NewRequestContext(
testutil.WithOpCode(sdk.OpMsg),
testutil.WithDatabase("shop"),
testutil.WithCollection("orders"),
testutil.WithMessageBytes(msgBytes),
)
result, err := q.ProcessRequest(ctx)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
// 10 + 3 + 10 + 3 + 2 = 28, over 25
if result.Action != sdk.Reject {
t.Errorf("expected Reject for pipeline cost 28 > 25, got %v", result.Action)
}
if result.Error == nil {
t.Fatal("expected error in result")
}
if result.Error.Code != 12500 {
t.Errorf("expected error code 12500, got %d", result.Error.Code)
}
}
func TestRegexCost(t *testing.T) {
q := newAnalyzer(t, map[string]interface{}{
"max_cost": float64(4),
})
cmdDoc := bson.D{
{Key: "find", Value: "users"},
{Key: "filter", Value: bson.D{
{Key: "name", Value: bson.D{{Key: "$regex", Value: "^john"}}},
}},
{Key: "hint", Value: "name_1"},
}
msgBytes := testutil.BuildOpMsg(cmdDoc)
ctx := testutil.NewRequestContext(
testutil.WithOpCode(sdk.OpMsg),
testutil.WithDatabase("mydb"),
testutil.WithCollection("users"),
testutil.WithMessageBytes(msgBytes),
)
result, err := q.ProcessRequest(ctx)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
// $regex = 5, max = 4 → reject
if result.Action != sdk.Reject {
t.Errorf("expected Reject for $regex cost 5 > max 4, got %v", result.Action)
}
}
func TestCostBreakdownSummary(t *testing.T) {
cb := &CostBreakdown{}
cb.Add("$lookup", 10)
cb.Add("$unwind", 3)
summary := cb.Summary()
if summary != "total=13 [$lookup(+10), $unwind(+3)]" {
t.Errorf("unexpected summary: %s", summary)
}
}
func TestCostBreakdownEmpty(t *testing.T) {
cb := &CostBreakdown{}
if cb.Summary() != "no expensive operations detected" {
t.Errorf("unexpected empty summary: %s", cb.Summary())
}
}
func TestCloseReturnsNil(t *testing.T) {
q := &QueryComplexity{}
if err := q.Close(); err != nil {
t.Errorf("expected nil error from Close, got %v", err)
}
}
21. Query Complexity test workflow
cd query-complexity
go test -v -count=1 ./...
# Expected output:
# === RUN TestInitDefaults
# --- PASS: TestInitDefaults (0.00s)
# === RUN TestInitCustomWeights
# --- PASS: TestInitCustomWeights (0.00s)
# === RUN TestSkipNonOpMsg
# --- PASS: TestSkipNonOpMsg (0.00s)
# === RUN TestExemptCollection
# --- PASS: TestExemptCollection (0.00s)
# === RUN TestSimpleFindAllowed
# --- PASS: TestSimpleFindAllowed (0.00s)
# === RUN TestFindWithoutHintAddsCost
# --- PASS: TestFindWithoutHintAddsCost (0.00s)
# === RUN TestSingleLookupAllowed
# --- PASS: TestSingleLookupAllowed (0.00s)
# === RUN TestGraphLookupRejected
# --- PASS: TestGraphLookupRejected (0.00s)
# === RUN TestWhereRejected
# --- PASS: TestWhereRejected (0.00s)
# === RUN TestComplexPipelineRejected
# --- PASS: TestComplexPipelineRejected (0.00s)
# === RUN TestComplexPipelineOverThreshold
# --- PASS: TestComplexPipelineOverThreshold (0.00s)
# === RUN TestRegexCost
# --- PASS: TestRegexCost (0.00s)
# === RUN TestCostBreakdownSummary
# --- PASS: TestCostBreakdownSummary (0.00s)
# === RUN TestCostBreakdownEmpty
# --- PASS: TestCostBreakdownEmpty (0.00s)
# === RUN TestCloseReturnsNil
# --- PASS: TestCloseReturnsNil (0.00s)
# PASS
Legacy next steps and build tips
The retired page linked readers to the SDK reference, publishing guide, developer guide, and marketplace. Those destinations are represented by this documentation section, but browsing/installing/publishing remains unavailable. Its practical tips were: start simple; validate configuration with clear errors; use structured logging rather than stdout; test every path, especially errors and edges; provide a clear manifest and config schema; return descriptive client errors when rejecting; and keep request processing fast. Apply those principles to the current contract rather than the retired SDK types.