Documentation
¶
Overview ¶
Package plugin runs third-party analyzers out of process, so the community (and other AI tools) can extend detection without forking or being linked into the binary. A plugin is any executable that speaks a tiny JSON protocol over stdin/stdout; a JSON manifest declares its name, the target types it handles, and how to launch it. The host adapts each plugin to the engine.Module interface, so a loaded plugin appears in the CLI, HTTP API, and MCP server exactly like a built-in module.
Out-of-process is a deliberate isolation boundary. A plugin cannot corrupt the engine's memory, and a plugin that hangs, crashes, or floods stdout is contained: every run is time-bounded (context), output-bounded (a hard byte cap), launched with no shell and a scrubbed environment, and any failure is recorded as a module error rather than taking down the scan.
WASM and gRPC transports were considered for stronger sandboxing but both need a third-party runtime; per the project's zero-dependency rule they are parked (see NOTES.md). The subprocess protocol is the stdlib-only design that still delivers real isolation today.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Command ¶
Command implements `dsecrat plugins`: discover and inspect out-of-process plugins. It does not need the engine registry (it reports on the plugins themselves), so its signature is the standard Command(args). The master wires it as (see NOTES.md):
case "plugins":
return plugin.Command(rest)
Subcommands: list (default) prints discovered plugins; validate exits non-zero if any manifest in the directory is invalid.
Types ¶
type Host ¶
type Host struct {
// contains filtered or unexported fields
}
Host is a loaded set of plugins.
func LoadDir ¶
LoadDir discovers and loads every *.json manifest in dir (non-recursive). It returns a Host with all valid plugins plus an error summarizing any that were skipped, so one bad manifest never hides the good ones. A missing directory is not an error — it yields an empty host (plugins are opt-in).
type Manifest ¶
type Manifest struct {
Name string `json:"name"`
Description string `json:"description"`
Version string `json:"version,omitempty"`
Domains []string `json:"domains,omitempty"`
// TargetTypes lists the engine target types this plugin handles, e.g.
// ["dockerfile","filesystem"]. Empty means it handles none (inert).
TargetTypes []string `json:"target_types,omitempty"`
// Exec is the argv used to launch the plugin: the command followed by its
// arguments. It is executed directly (no shell), so nothing is interpolated.
Exec []string `json:"exec"`
// TimeoutMS bounds one Analyze call. Zero uses defaultTimeoutMS.
TimeoutMS int `json:"timeout_ms,omitempty"`
// contains filtered or unexported fields
}
Manifest declares a plugin's identity and how to launch it. It is plain JSON so a plugin author writes it by hand. Any Exec element containing the token ${dir} has it replaced with the manifest's directory, so a manifest can point at a script shipped alongside it without hard-coding an absolute path.
type Plugin ¶
type Plugin struct {
// contains filtered or unexported fields
}
Plugin adapts one manifested executable to the engine.Module interface.
func Load ¶
Load reads and validates a single manifest file, returning a Plugin. The plugin's launch argv is resolved (${dir} expanded to the manifest directory) but the executable is not run.
func (*Plugin) Analyze ¶
Analyze runs the plugin subprocess and projects its findings. Any failure — launch error, timeout, oversized or malformed output, or a plugin-reported error — is returned so the engine records it as a module error; it never panics the run. The plugin's declared name is stamped onto every finding so a plugin cannot impersonate another module.