pluginsdk

package
v0.0.0-...-9b8dde8 Latest Latest
Warning

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

Go to latest
Published: Dec 2, 2025 License: Apache-2.0 Imports: 20 Imported by: 0

README

0xGen Plugin SDK for Node.js

Node.js/JavaScript SDK for creating 0xGen security scanner plugins.

Overview

This SDK allows you to create security scanner plugins in JavaScript/Node.js that integrate with the 0xGen platform via gRPC. Plugins can:

  • Receive HTTP traffic events (passive analysis)
  • Emit security findings
  • Access workspace and secrets
  • Perform active security checks

Installation

npm install @grpc/grpc-js @grpc/proto-loader

Then require the SDK in your plugin:

const { register, Severity, Capability } = require('../../sdk/plugin-sdk');

Quick Start

Basic Plugin
const { register, Severity } = require('../../sdk/plugin-sdk');

register({
  name: 'my-plugin',

  onStart: async ({ ctx, config }) => {
    ctx.log('info', 'Plugin started');

    // Emit a test finding
    await ctx.emitFinding({
      type: 'test-finding',
      message: 'Plugin initialized successfully',
      severity: Severity.INFO,
    });
  },

  onHTTPPassive: async ({ ctx, event }) => {
    // Analyze HTTP responses
    const response = event.response;

    if (response && response.headers['x-debug-enabled']) {
      await ctx.emitFinding({
        type: 'debug-header-exposed',
        message: 'Debug header exposed in response',
        severity: Severity.MEDIUM,
        target: response.headers['host'],
        evidence: 'X-Debug-Enabled: ' + response.headers['x-debug-enabled'],
      });
    }
  },
});
Passive Security Scanner
const { register, Severity, Capability } = require('../../sdk/plugin-sdk');

register({
  name: 'security-header-scanner',
  capabilities: [Capability.EMIT_FINDINGS, Capability.HTTP_PASSIVE],

  onHTTPPassive: async ({ ctx, event }) => {
    const response = event.response;
    if (!response) return;

    // Check for missing security headers
    const securityHeaders = [
      'strict-transport-security',
      'x-frame-options',
      'x-content-type-options',
      'content-security-policy',
    ];

    for (const header of securityHeaders) {
      if (!response.headers[header]) {
        await ctx.emitFinding({
          type: 'missing-security-header',
          message: `Missing security header: ${header}`,
          severity: Severity.MEDIUM,
          metadata: {
            header: header,
            status: response.statusLine,
          },
        });
      }
    }

    // Check for sensitive data exposure
    const bodyText = response.body.toString('utf-8');
    if (bodyText.includes('password') || bodyText.includes('secret')) {
      await ctx.emitFinding({
        type: 'potential-data-exposure',
        message: 'Response body may contain sensitive data',
        severity: Severity.HIGH,
        evidence: 'Body contains keywords: password, secret',
      });
    }
  },
});

API Reference

register(config)

Main entry point for registering a plugin.

Parameters:

  • config.name (string, required) - Plugin name
  • config.onStart (function) - Called when plugin starts
  • config.onHTTPPassive (function) - Called for passive HTTP events
  • config.onHTTPActive (function) - Called for active HTTP events
  • config.capabilities (array) - Required capabilities
  • config.subscriptions (array) - Event subscriptions
  • config.logger (function) - Custom logger

Returns: Promise

Context Methods
ctx.emitFinding(finding)

Emit a security finding to the host.

Parameters:

{
  type: string,        // Required: Finding type
  message: string,     // Required: Human-readable message
  severity: number,    // Optional: Severity level (default: INFO)
  id: string,          // Optional: Finding ID (auto-generated)
  target: string,      // Optional: Target URL/resource
  evidence: string,    // Optional: Proof/evidence
  metadata: object,    // Optional: Additional metadata
}

Example:

await ctx.emitFinding({
  type: 'sql-injection',
  message: 'Potential SQL injection vulnerability detected',
  severity: Severity.CRITICAL,
  target: 'https://example.com/api/users?id=1',
  evidence: 'Parameter "id" is vulnerable to SQL injection',
  metadata: {
    parameter: 'id',
    payload: "' OR '1'='1",
  },
});
ctx.log(level, message, data)

Log a message.

Parameters:

  • level (string) - Log level: 'info', 'warn', 'error'
  • message (string) - Log message
  • data (object) - Additional data

Example:

ctx.log('info', 'Processing HTTP response', { url: 'https://example.com' });
ctx.log('error', 'Failed to parse response', { error: err.message });
Constants
Severity Levels
const { Severity } = require('../../sdk/plugin-sdk');

Severity.INFO      // 1
Severity.LOW       // 2
Severity.MEDIUM    // 3
Severity.HIGH      // 4
Severity.CRITICAL  // 5
Capabilities
const { Capability } = require('../../sdk/plugin-sdk');

Capability.EMIT_FINDINGS      // Emit findings to host
Capability.HTTP_PASSIVE       // Receive passive HTTP events
Capability.AI_ANALYSIS        // Access AI analysis
Capability.FLOW_INSPECT       // Access sanitized flow events
Capability.FLOW_INSPECT_RAW   // Access raw flow events
Capability.WORKSPACE_READ     // Read from workspace
Capability.WORKSPACE_WRITE    // Write to workspace
Capability.NET_OUTBOUND       // Make outbound requests
Capability.SECRETS_READ       // Read secrets
Subscriptions
const { Subscription } = require('../../sdk/plugin-sdk');

Subscription.FLOW_RESPONSE      // HTTP responses
Subscription.FLOW_REQUEST       // HTTP requests
Subscription.FLOW_RESPONSE_RAW  // Raw HTTP responses
Subscription.FLOW_REQUEST_RAW   // Raw HTTP requests

Hook Reference

onStart

Called once when the plugin starts.

onStart: async ({ ctx, config }) => {
  // Initialize plugin
  ctx.log('info', 'Plugin initialized');
}

Context:

  • ctx (Context) - Plugin context
  • config (object) - Plugin configuration
onHTTPPassive

Called for each HTTP response event.

onHTTPPassive: async ({ ctx, event }) => {
  const response = event.response;

  // Analyze response
  if (response) {
    console.log('Status:', response.statusLine);
    console.log('Headers:', response.headers);
    console.log('Body:', response.body.toString());
  }
}

Context:

  • ctx (Context) - Plugin context
  • event.raw (Buffer) - Raw HTTP data
  • event.response (object) - Parsed HTTP response
    • statusLine (string) - HTTP status line
    • headers (object) - Response headers
    • body (Buffer) - Response body
onHTTPActive

Called for active HTTP scanning.

onHTTPActive: async ({ ctx, event }) => {
  const request = event.request;

  // Analyze or modify request
  if (request) {
    console.log('Method:', request.method);
    console.log('URL:', request.url);
  }
}

Context:

  • ctx (Context) - Plugin context
  • event.raw (Buffer) - Raw HTTP data
  • event.request (object) - Parsed HTTP request
    • method (string) - HTTP method
    • url (string) - Request URL
    • version (string) - HTTP version
    • headers (object) - Request headers
    • body (Buffer) - Request body

Environment Variables

The plugin SDK requires these environment variables to be set by the host:

  • OXGEN_PLUGIN_HOST - gRPC host address (default: localhost:50051)
  • OXGEN_AUTH_TOKEN - Authentication token (required)
  • OXGEN_CAPABILITY_TOKEN - Capability token (required)

These are automatically provided when the plugin is launched by the 0xGen platform.

Error Handling

register({
  name: 'my-plugin',

  onHTTPPassive: async ({ ctx, event }) => {
    try {
      // Plugin logic
      await ctx.emitFinding({
        type: 'test',
        message: 'Test finding',
      });
    } catch (error) {
      ctx.log('error', 'Failed to process event', {
        error: error.message,
        stack: error.stack,
      });
    }
  },
});

TypeScript Support

The SDK includes TypeScript definitions:

import { register, Severity, Capability, Context, Finding } from '../../sdk/plugin-sdk';

interface PluginConfig {
  apiKey: string;
  threshold: number;
}

register({
  name: 'typescript-plugin',

  onStart: async ({ ctx, config }: { ctx: Context; config: PluginConfig }) => {
    ctx.log('info', 'TypeScript plugin started');
  },

  onHTTPPassive: async ({ ctx, event }) => {
    const finding: Finding = {
      type: 'test-finding',
      message: 'Test from TypeScript',
      severity: Severity.INFO,
    };

    await ctx.emitFinding(finding);
  },
});

Plugin Lifecycle

  1. Initialization: Plugin process starts, SDK connects to host via gRPC
  2. Handshake: SDK sends Hello message with capabilities and subscriptions
  3. onStart Hook: Called once after successful connection
  4. Event Loop: SDK receives events and calls appropriate hooks
  5. Shutdown: Plugin receives SIGINT/SIGTERM and gracefully shuts down

Best Practices

1. Handle Errors Gracefully
onHTTPPassive: async ({ ctx, event }) => {
  try {
    // Your logic here
  } catch (error) {
    ctx.log('error', 'Error in passive hook', { error: error.message });
    // Don't throw - let plugin continue processing other events
  }
}
2. Use Appropriate Severity Levels
  • CRITICAL - Exploitable vulnerabilities (SQL injection, RCE)
  • HIGH - Security issues requiring immediate attention
  • MEDIUM - Security weaknesses that should be fixed
  • LOW - Minor security concerns
  • INFO - Informational findings
3. Include Detailed Evidence
await ctx.emitFinding({
  type: 'xss-vulnerability',
  message: 'Reflected XSS vulnerability in search parameter',
  severity: Severity.HIGH,
  target: 'https://example.com/search?q=test',
  evidence: 'Payload: <script>alert(1)</script>\nReflected in: <div>test</div>',
  metadata: {
    parameter: 'q',
    payload: '<script>alert(1)</script>',
    injection_point: 'div.search-results',
  },
});
4. Log Important Events
ctx.log('info', 'Analyzing response', { url: targetUrl });
ctx.log('warn', 'Unusual response detected', { status: 999 });
ctx.log('error', 'Failed to parse body', { error: err.message });

Examples

See the /plugins directory for complete plugin examples:

  • excavator - Web crawler plugin
  • seer - Secrets detection
  • cryptographer - Cryptographic analysis
  • ranker - Finding prioritization

Troubleshooting

"OXGEN_AUTH_TOKEN environment variable is required"

The plugin must be launched by the 0xGen platform, which sets these variables. Don't run plugins directly.

"Failed to load protobuf definitions"

Ensure the plugin is run from the correct directory with access to /proto/oxg/ protobuf files.

"Missing capability: CAP_EMIT_FINDINGS"

Add the required capability to your plugin configuration:

register({
  name: 'my-plugin',
  capabilities: [Capability.EMIT_FINDINGS],
  ...
});

Contributing

See the main 0xGen repository for contribution guidelines.

License

MIT

Documentation

Index

Constants

This section is empty.

Variables

View Source
var ErrBrokerUnavailable = errors.New("broker endpoint unavailable")

ErrBrokerUnavailable is returned when a broker endpoint is not configured.

View Source
var ErrNotFound = errors.New("resource not found")

ErrNotFound signals that the requested resource was not found by the broker.

Functions

func Run

func Run(cfg Config, hooks Hooks) error

Run is a convenience helper that handles system interrupts and blocks until Serve returns.

func Serve

func Serve(parent context.Context, cfg Config, hooks Hooks) error

Serve launches the plugin runtime and blocks until the context is cancelled or an error occurs.

func UseFilesystem

func UseFilesystem(ctx *Context, cap Capability, fn func(FilesystemBroker) error) error

UseFilesystem executes fn with the filesystem broker after verifying capability.

func UseNetwork

func UseNetwork(ctx *Context, fn func(NetworkBroker) error) error

UseNetwork executes fn with the network broker after verifying CAP_NET_OUTBOUND.

func UseSecrets

func UseSecrets(ctx *Context, fn func(SecretsBroker) error) error

UseSecrets executes fn with the secrets broker after verifying CAP_SECRETS_READ.

Types

type Broker

type Broker interface {
	Filesystem() FilesystemBroker
	Network() NetworkBroker
	Secrets() SecretsBroker
}

Broker exposes sandbox-safe helpers implemented by the 0xgen broker.

type Capability

type Capability string

Capability represents a permission that must be granted to the plugin by the host before certain operations are allowed.

const (
	// CapabilityEmitFindings allows the plugin to report findings to the host.
	CapabilityEmitFindings Capability = "CAP_EMIT_FINDINGS"
	// CapabilityHTTPPassive allows the plugin to receive passive HTTP events.
	CapabilityHTTPPassive Capability = "CAP_HTTP_PASSIVE"
	// CapabilityAIAnalysis allows the plugin to access the AI-assisted analysis surface.
	CapabilityAIAnalysis Capability = "CAP_AI_ANALYSIS"
	// CapabilityFlowInspect grants access to sanitized HTTP flow events.
	CapabilityFlowInspect Capability = "CAP_FLOW_INSPECT"
	// CapabilityFlowInspectRaw grants access to raw HTTP flow events.
	CapabilityFlowInspectRaw Capability = "CAP_FLOW_INSPECT_RAW"
	// CapabilityWorkspaceRead allows the plugin to read from its allocated workspace.
	CapabilityWorkspaceRead Capability = "CAP_WORKSPACE_READ"
	// CapabilityWorkspaceWrite allows the plugin to write to its allocated workspace.
	CapabilityWorkspaceWrite Capability = "CAP_WORKSPACE_WRITE"
	// CapabilityNetOutbound allows the plugin to make outbound network requests via the broker.
	CapabilityNetOutbound Capability = "CAP_NET_OUTBOUND"
	// CapabilitySecretsRead allows the plugin to retrieve secrets from the broker.
	CapabilitySecretsRead Capability = "CAP_SECRETS_READ"
)

type CapabilityError

type CapabilityError struct {
	Capability Capability
}

CapabilityError indicates a capability is missing for the requested action.

func (CapabilityError) Error

func (e CapabilityError) Error() string

type CapabilitySet

type CapabilitySet struct {
	EmitFindings   bool
	HTTPPassive    bool
	AIAnalysis     bool
	FlowInspect    bool
	FlowInspectRaw bool
	WorkspaceRead  bool
	WorkspaceWrite bool
	NetOutbound    bool
	SecretsRead    bool
}

CapabilitySet centralises capability declarations for plugin scaffolds.

func (CapabilitySet) Enabled

func (s CapabilitySet) Enabled(cap Capability) bool

Enabled reports whether the provided capability is present in the set.

func (CapabilitySet) List

func (s CapabilitySet) List() []Capability

List returns the enabled capabilities as a slice suitable for manifests or configs.

type Config

type Config struct {
	// PluginName is the name reported to the host. It should match the manifest.
	PluginName string
	// Host is the host:port combination to dial the 0xgen core.
	Host string
	// AuthToken is the shared secret required by the host.
	AuthToken string
	// CapabilityToken binds this invocation to the capabilities granted by the host.
	CapabilityToken string
	// SecretsToken authorises this invocation to retrieve secrets from the broker.
	SecretsToken string
	// SecretsScope binds the secrets token to the specific plugin run scope. If left blank
	// the capability token is used as a fallback.
	SecretsScope string
	// Capabilities is the set of capabilities granted by the manifest.
	Capabilities []Capability
	// Subscriptions lists the host events the plugin wants to receive.
	Subscriptions []Subscription
	// Logger allows callers to customise logging output. A sensible default is used otherwise.
	Logger *slog.Logger
	// Broker injects broker helpers for filesystem, network, and secrets access.
	Broker Broker
}

Config encapsulates the runtime configuration for a plugin instance.

type Context

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

func (*Context) Broker

func (c *Context) Broker() Broker

Broker exposes the configured broker for advanced scenarios.

func (*Context) CapabilityGranted

func (c *Context) CapabilityGranted(cap Capability) bool

CapabilityGranted reports whether the capability is present on the context.

func (*Context) EmitFinding

func (c *Context) EmitFinding(f Finding) error

EmitFinding reports a finding to the host if the plugin has the required capability.

func (*Context) Logger

func (c *Context) Logger() *slog.Logger

Logger returns the logger bound to the plugin context.

func (*Context) WithCapability

func (c *Context) WithCapability(cap Capability, fn func(Broker) error) error

WithCapability ensures the provided capability is available before invoking fn.

type FakeAuditEntry

type FakeAuditEntry struct {
	Operation string
	Target    string
	Allowed   bool
	Message   string
}

FakeAuditEntry captures a broker operation and whether it was allowed.

type FakeBroker

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

FakeBroker implements Broker for tests and local development.

func NewFakeBroker

func NewFakeBroker() *FakeBroker

NewFakeBroker creates a FakeBroker with empty storage.

func (*FakeBroker) AuditLog

func (b *FakeBroker) AuditLog() []FakeAuditEntry

AuditLog returns the recorded broker interactions.

func (*FakeBroker) DenyFilesystem

func (b *FakeBroker) DenyFilesystem(path, reason string)

DenyFilesystem configures a denial for a specific path. Provide an empty path to reject all filesystem operations.

func (*FakeBroker) DenyNetwork

func (b *FakeBroker) DenyNetwork(method, url, reason string)

DenyNetwork configures a denial for a given method and URL.

func (*FakeBroker) DenySecret

func (b *FakeBroker) DenySecret(name, reason string)

DenySecret configures a denial for a named secret.

func (*FakeBroker) Filesystem

func (b *FakeBroker) Filesystem() FilesystemBroker

Filesystem implements Broker.

func (*FakeBroker) Network

func (b *FakeBroker) Network() NetworkBroker

Network implements Broker.

func (*FakeBroker) Requests

func (b *FakeBroker) Requests() []HTTPRequest

Requests returns a snapshot of recorded HTTP requests.

func (*FakeBroker) Secrets

func (b *FakeBroker) Secrets() SecretsBroker

Secrets implements Broker.

func (*FakeBroker) SetFile

func (b *FakeBroker) SetFile(path string, data []byte)

SetFile seeds the fake filesystem.

func (*FakeBroker) SetHTTPResponse

func (b *FakeBroker) SetHTTPResponse(method, url string, res HTTPResult)

SetHTTPResponse configures a canned response for the given URL and method.

func (*FakeBroker) SetSecret

func (b *FakeBroker) SetSecret(name, value string)

SetSecret seeds the fake secret store.

type FilesystemBroker

type FilesystemBroker interface {
	ReadFile(ctx context.Context, path string) ([]byte, error)
	WriteFile(ctx context.Context, path string, data []byte) error
	Remove(ctx context.Context, path string) error
}

FilesystemBroker offers workspace-scoped filesystem helpers.

type Finding

type Finding struct {
	ID         string
	Type       string
	Message    string
	Target     string
	Evidence   string
	Severity   Severity
	Metadata   map[string]string
	DetectedAt time.Time
}

Finding captures the structured data that will be sent back to the host when the plugin observes an issue.

type HTTPPassiveEvent

type HTTPPassiveEvent struct {
	Raw      []byte
	Response *HTTPResponse
}

HTTPPassiveEvent wraps a passive HTTP response observed by the plugin.

type HTTPPassiveHook

type HTTPPassiveHook func(ctx *Context, event HTTPPassiveEvent) error

HTTPPassiveHook handles passive HTTP response events streamed from the host.

type HTTPRequest

type HTTPRequest struct {
	Method  string
	URL     string
	Headers http.Header
	Body    []byte
}

HTTPRequest models an outbound HTTP request performed by the broker.

type HTTPResponse

type HTTPResponse struct {
	StatusLine string
	Headers    http.Header
	Body       []byte
}

HTTPResponse summarises an HTTP response derived from a passive flow event.

type HTTPResult

type HTTPResult struct {
	StatusCode int
	Headers    http.Header
	Body       []byte
}

HTTPResult captures the result of a broker-mediated HTTP request.

type Hooks

type Hooks struct {
	OnStart       OnStartHook
	OnHTTPPassive HTTPPassiveHook
}

Hooks contains the callbacks provided by a plugin implementation.

type LocalRunConfig

type LocalRunConfig struct {
	PluginName    string
	Capabilities  []Capability
	Broker        Broker
	Logger        *slog.Logger
	Hooks         Hooks
	PassiveEvents []HTTPPassiveEvent
}

LocalRunConfig configures the local integration test harness.

type LocalRunResult

type LocalRunResult struct {
	Findings []Finding
}

LocalRunResult captures the results emitted by the plugin during a local run.

func RunLocal

func RunLocal(ctx context.Context, cfg LocalRunConfig) (*LocalRunResult, error)

RunLocal executes the plugin hooks without connecting to a real 0xgen host.

type NetworkBroker

type NetworkBroker interface {
	Do(ctx context.Context, req HTTPRequest) (HTTPResult, error)
}

NetworkBroker mediates outbound HTTP requests through the broker.

type OnStartHook

type OnStartHook func(ctx *Context) error

OnStartHook is invoked once after the plugin successfully connects to the host.

type SecretsBroker

type SecretsBroker interface {
	Get(ctx context.Context, name string) (string, error)
}

SecretsBroker retrieves secret material from the broker.

type Severity

type Severity pb.Severity

Severity describes how serious a finding is considered by the plugin.

const (
	SeverityInfo     Severity = Severity(pb.Severity_INFO)
	SeverityLow      Severity = Severity(pb.Severity_LOW)
	SeverityMedium   Severity = Severity(pb.Severity_MEDIUM)
	SeverityHigh     Severity = Severity(pb.Severity_HIGH)
	SeverityCritical Severity = Severity(pb.Severity_CRITICAL)
)

type Subscription

type Subscription string

Subscription identifies the type of host events a plugin is interested in.

const (
	// SubscriptionFlowResponse subscribes to HTTP response flow events from the host.
	SubscriptionFlowResponse Subscription = "FLOW_RESPONSE"
	// SubscriptionFlowRequest subscribes to sanitized HTTP request flow events from the host.
	SubscriptionFlowRequest Subscription = "FLOW_REQUEST"
	// SubscriptionFlowResponseRaw subscribes to raw HTTP response flow events.
	SubscriptionFlowResponseRaw Subscription = "FLOW_RESPONSE_RAW"
	// SubscriptionFlowRequestRaw subscribes to raw HTTP request flow events.
	SubscriptionFlowRequestRaw Subscription = "FLOW_REQUEST_RAW"
)

Directories

Path Synopsis
cmd
capassert command
lint

Jump to

Keyboard shortcuts

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