Documentation
¶
Overview ¶
Package plugin provides a simple SDK for building Deputy extractor plugins.
Plugins are standalone executables that Deputy invokes via pluginrpc to extract package inventory from custom file formats. This SDK provides a high-level interface that handles all the pluginrpc boilerplate.
Architecture ¶
┌─────────────────────────────────────────────────────────────────┐
│ Deputy Process │
│ ┌───────────┐ ┌──────────────┐ ┌───────────────────┐ │
│ │ scan │───▶│ inventory │───▶│ plugin.Client │ │
│ │ command │ │ extraction │ │ (invokes plugin) │ │
│ └───────────┘ └──────────────┘ └─────────┬─────────┘ │
│ │ │
└─────────────────────────────────────────────────│──────────────┘
│
┌────────────────┬──────┴───────┐
│ stdin (proto) │ spawn │
▼ ▼ │
┌─────────────────────────────────────────────────────────│──────┐
│ Plugin Process │ │
│ ┌───────────────────┐ ┌───────────────┐ ┌───────┴────┐ │
│ │ pluginrpc.Server │◀───│ Extractor │◀───│ sdk/plugin│ │
│ │ (protocol logic) │ │ (your impl) │ │ (helpers) │ │
│ └─────────┬─────────┘ └───────────────┘ └────────────┘ │
│ │ │
│ ▼ stdout (proto) │
└────────────│───────────────────────────────────────────────────┘
│
└────▶ back to Deputy
Quick Start ¶
Create a plugin in a single main.go file:
package main
import (
"github.com/picatz/deputy/sdk/plugin"
)
func main() {
plugin.Main(&myExtractor{})
}
type myExtractor struct{}
func (e *myExtractor) Name() string { return "custom/myformat" }
func (e *myExtractor) DisplayName() string { return "My Custom Format" }
func (e *myExtractor) Ecosystem() string { return "custom" }
func (e *myExtractor) Version() int { return 1 }
func (e *myExtractor) Description() string { return "Extracts packages from .myformat files" }
func (e *myExtractor) FilePatterns() []string { return []string{"*.myformat"} }
func (e *myExtractor) FileRequired(path string, isDir bool, mode uint32, size int64) bool {
return strings.HasSuffix(path, ".myformat")
}
func (e *myExtractor) Extract(path string, contents []byte, root string) ([]*plugin.Package, error) {
// Parse contents and return packages
return []*plugin.Package{
{Name: "example-pkg", Version: "1.0.0", Ecosystem: "custom"},
}, nil
}
Building the Plugin ¶
go build -o deputy-extractor-myformat ./cmd/myformat-plugin
Registering with Deputy ¶
Plugins can be registered via configuration or discovered from PATH:
# .deputy.yaml
plugins:
extractors:
- path: /usr/local/bin/deputy-extractor-myformat
- name: deputy-extractor-gemspec # searches PATH
Or programmatically via the SDK:
client, _ := sdk.NewClient(ctx) client.RegisterExtractor(ctx, "deputy-extractor-myformat")
Distributed Tracing ¶
Plugins automatically participate in distributed traces when the OTEL_EXPORTER_OTLP_ENDPOINT environment variable is set. The SDK extracts trace context from requests and creates child spans.
Deputy Scan (trace-id: abc123) │ ├── inventory.Extract │ │ │ ├── plugin.client.FileRequired ──────────────────┐ │ │ │ │ TraceContext │ │ └──[spawn]──▶ plugin.FileRequired (abc123) ◀─┘ in request │ │ │ └── plugin.client.Extract ───────────────────────┐ │ │ │ TraceContext │ └──[spawn]──▶ plugin.Extract (abc123) ◀──────┘ in request │ └── vulnerability.Lookup
The TraceContext field in FileRequiredRequest and ExtractRequest carries the W3C traceparent header value, enabling end-to-end tracing.
Plugin Interface ¶
Implement the Extractor interface to create a plugin. The SDK handles:
- Pluginrpc protocol negotiation (--protocol, --spec flags)
- Request/response serialization
- OpenTelemetry trace context propagation
- Error handling and exit codes
See the Extractor interface for detailed documentation.
Distributed tracing support for Deputy extractor plugins.
This file provides OpenTelemetry trace context propagation, enabling plugins to participate in distributed traces with Deputy. When OTEL_EXPORTER_OTLP_ENDPOINT is set, traces flow seamlessly across process boundaries.
Trace Context Flow ¶
Deputy Process Plugin Process +-----------------------+ +------------------------+ | | | | | ctx with span | | extractTraceContext() | | | | | | | | v | | v | | injectTraceContext() | | ctx with linked span | | | | | | | | v | TraceContext | v | | "00-abc-def-01" -----|---- field ------->| startSpan() | | | in request | | | | | | v | | | | child span created | +-----------------------+ +------------------------+
The TraceContext field in FileRequiredRequest and ExtractRequest carries the W3C traceparent header value (e.g., "00-traceid-spanid-01").
Index ¶
- Constants
- func Main(extractor Extractor)
- type Extractor
- type ExtractorInfo
- type Package
- type PackageBuilder
- func (b *PackageBuilder) Build() *Package
- func (b *PackageBuilder) WithDirect(direct bool) *PackageBuilder
- func (b *PackageBuilder) WithLicenses(licenses ...string) *PackageBuilder
- func (b *PackageBuilder) WithLocations(locations ...string) *PackageBuilder
- func (b *PackageBuilder) WithManifestRef(path, manager string, groups ...string) *PackageBuilder
- func (b *PackageBuilder) WithPURL(purl string) *PackageBuilder
Constants ¶
const ( // TracerName is the tracer name for plugin spans. // All spans created by plugin SDK use this tracer for identification. TracerName = "github.com/picatz/deputy/plugin" )
Variables ¶
This section is empty.
Functions ¶
Types ¶
type Extractor ¶
type Extractor interface {
// Name returns the extractor identifier.
// Convention: "<ecosystem>/<format>" (e.g., "ruby/gemspec", "custom/myformat").
Name() string
// DisplayName returns a human-readable name for UI display.
DisplayName() string
// Ecosystem returns the package ecosystem this extractor supports.
// Use standard ecosystem names: go, npm, pypi, maven, rubygems, cargo, etc.
Ecosystem() string
// Version returns the extractor version (increment on behavior changes).
Version() int
// Description returns context about what this extractor does.
Description() string
// FilePatterns returns glob patterns for files this extractor handles.
// Examples: ["Gemfile.lock", "*.gemspec", "vendor/*/Gemfile"]
FilePatterns() []string
// FileRequired checks if this extractor should process a file.
// Return true to receive an Extract call for this file.
// This is called for every file in the scan - keep it fast (no I/O).
//
// Parameters:
// - path: File path relative to scan root
// - isDir: Whether the path is a directory
// - mode: Unix file permission mode
// - size: File size in bytes
FileRequired(path string, isDir bool, mode uint32, size int64) bool
// Extract extracts packages from a file's contents.
// Called only for files where FileRequired returned true.
//
// Parameters:
// - path: File path relative to scan root
// - contents: Raw file bytes
// - root: Absolute path to scan root (for resolving relative paths)
//
// Returns a slice of discovered packages and any error.
Extract(path string, contents []byte, root string) ([]*Package, error)
}
Extractor is the interface that plugins must implement.
The interface is designed to be simple while matching the semantics of OSV-SCALIBR extractors for consistency across the ecosystem.
type ExtractorInfo ¶
type ExtractorInfo = pluginv1.ExtractorInfo
ExtractorInfo is an alias for the proto ExtractorInfo type.
type Package ¶
type Package = dependencyv1.Package
Package is an alias for the proto Package type for convenience. This allows plugin authors to use plugin.Package instead of importing the dependency proto package directly.
func NewPackage ¶
NewPackage creates a new Package with the given name, version, and ecosystem. This is a convenience function for plugin authors.
Example:
pkg := plugin.NewPackage("lodash", "4.17.21", "npm")
type PackageBuilder ¶
type PackageBuilder struct {
// contains filtered or unexported fields
}
PackageBuilder provides a fluent interface for building Package instances.
Example:
pkg := plugin.NewPackageBuilder("lodash", "4.17.21", "npm").
WithPURL("pkg:npm/lodash@4.17.21").
WithLicenses("MIT").
WithDirect(true).
Build()
func NewPackageBuilder ¶
func NewPackageBuilder(name, version, ecosystem string) *PackageBuilder
NewPackageBuilder creates a new PackageBuilder with required fields.
func (*PackageBuilder) Build ¶
func (b *PackageBuilder) Build() *Package
Build returns the constructed Package.
func (*PackageBuilder) WithDirect ¶
func (b *PackageBuilder) WithDirect(direct bool) *PackageBuilder
WithDirect marks the package as a direct dependency.
func (*PackageBuilder) WithLicenses ¶
func (b *PackageBuilder) WithLicenses(licenses ...string) *PackageBuilder
WithLicenses sets the SPDX license identifiers.
func (*PackageBuilder) WithLocations ¶
func (b *PackageBuilder) WithLocations(locations ...string) *PackageBuilder
WithLocations sets the file paths where this package was found.
func (*PackageBuilder) WithManifestRef ¶
func (b *PackageBuilder) WithManifestRef(path, manager string, groups ...string) *PackageBuilder
WithManifestRef adds a manifest reference describing where the dependency is declared.
func (*PackageBuilder) WithPURL ¶
func (b *PackageBuilder) WithPURL(purl string) *PackageBuilder
WithPURL sets the Package URL (PURL).