How Trivy’s Plugin System Extends Security Scanning Capabilities
Trivy’s plugin system delegates specific detection tasks to external binaries through a lightweight, language-agnostic framework that uses JSON communication over STDIN/STDOUT, allowing the community to add new scanners and output formats without modifying the core codebase.
The aquasecurity/trivy repository implements a plugin architecture modeled after kubectl and helm that keeps the main binary small while enabling limitless extensibility. By isolating functionality into independent processes, the Trivy plugin system supports custom scanners, specialized output formats, and third-party integrations written in any programming language.
Core Architecture and Components
The plugin implementation in pkg/plugin/plugin.go defines several key abstractions that work together to discover, manage, and execute extensions.
The Plugin Type and Metadata
At the heart of the system is the Plugin struct, which holds metadata including the plugin name, version, command, description, and supported scanner types. This definition also tracks the executable path to the plugin binary on the local filesystem. When Trivy initializes, it reads plugin.yaml manifest files from the plugin cache directory—typically $XDG_DATA_HOME/.trivy/plugins or ~/.trivy/plugins—and unmarshals them into these structures.
Plugin Manager
The plugin manager handles the lifecycle of all installed extensions. Implemented in the same pkg/plugin/plugin.go file, it loads plugin manifests, validates their schema, and registers them in a map keyed by name for fast lookup. The manager also resolves plugin names to their executable paths when Trivy needs to invoke them during scans.
Centralized Plugin Index
Trivy maintains a public catalog called trivy-plugin-index (hosted in a separate repository) that lists available plugins, their versions, and download URLs. When users run trivy plugin update, Trivy syncs this index to the local cache, enabling discovery of community-contributed extensions without manual searching.
CLI Integration
The command-line interface provides thin wrappers around the manager through the trivy plugin subcommand group. According to the CLI reference in docs/guide/references/configuration/cli/trivy_plugin.md, supported operations include:
install– Downloads and installs a plugin from the indexlist– Shows locally installed pluginssearch– Queries the index for available pluginsrun– Executes a specific plugin directlyinfo– Displays metadata about an installed pluginuninstall– Removes a plugin from the cacheupgrade– Updates a plugin to the latest versionupdate– Refreshes the local copy of the plugin index
Plugin Discovery and Execution Flow
The Trivy plugin system follows a standardized five-step execution model that ensures consistent behavior across all extensions.
-
Discovery – On startup, Trivy scans the plugin cache directory for
plugin.yamlfiles. Each manifest registers a plugin with its command line arguments, supported scanners, and human-readable description. -
Selection – Users activate plugins via CLI flags such as
--output-pluginor through explicit subcommands liketrivy plugin run <name>. The manager resolves these references to absolute executable paths. -
Invocation – When a scan reaches a step handled by a plugin, Trivy spawns the plugin binary as an isolated process. It streams a JSON payload containing scan results or input data to the plugin’s STDIN.
-
Result Handling – The plugin processes the input and returns a JSON response on STDOUT. Trivy reads this output, merges the results into its own data structures, and continues with the remaining scan workflow.
-
Lifecycle Management – Commands like
install,uninstall, andupgrademanipulate the plugin cache and index, ensuring reproducible environments across different machines.
This architecture allows plugins to function as black boxes—Trivy does not need to know the implementation language or internal logic, only the JSON contract defined in the Plugin interface.
Installing and Managing Plugins
Working with the Trivy plugin system begins with synchronizing the local index and installing desired extensions.
Refresh the plugin catalog to ensure access to the latest community contributions:
trivy plugin update
Search for specific functionality, such as referrer capabilities:
trivy plugin search referrer
Install the official referrer plugin, which downloads the binary into the versioned cache directory at $XDG_DATA_HOME/.trivy/plugins/referrer_<version>:
trivy plugin install referrer
List installed plugins to verify successful installation:
trivy plugin list
Running Plugins in Scans
Plugins integrate seamlessly into standard Trivy workflows through CLI flags or direct execution.
Execute the referrer plugin directly against an image reference:
trivy plugin run referrer \
--name my-image \
--type image \
--arg my-registry.example.com/my-image:latest
Use an output plugin to customize result formatting during a normal image scan:
trivy image --output-plugin json \
--output-plugin-arg '{"indent":2}' \
nginx:latest
Custom output plugins follow the same interface—reference them by name using --output-plugin my-custom to process findings through external formatting logic.
Programmatic Plugin Integration
Go developers can interact with the plugin system directly using the internal packages exposed in pkg/plugin/plugin.go.
The following example demonstrates loading the plugin manager, resolving a specific plugin, and executing it with custom input:
import (
"encoding/json"
"github.com/aquasecurity/trivy/pkg/plugin"
"github.com/aquasecurity/trivy/pkg/scanner"
)
func runCustomPlugin() error {
// Initialize manager and load plugins from cache
pm, err := plugin.NewManager()
if err != nil { return err }
// Resolve plugin by name
plug, err := pm.Get("referrer")
if err != nil { return err }
// Prepare JSON input payload
input := []byte(`{"image":"nginx:latest"}`)
// Execute plugin and capture STDOUT
out, err := plug.Run(input)
if err != nil { return err }
// Process plugin response
var result scanner.Results
if err := json.Unmarshal(out, &result); err != nil {
return err
}
return nil
}
The plugin.Manager handles cache validation, while the Plugin.Run() method manages the STDIN/STDOUT streaming and process lifecycle.
Summary
- Trivy’s plugin system uses external binaries to extend core scanning capabilities while maintaining a small, focused main executable.
- The architecture centers on the
Plugintype inpkg/plugin/plugin.go, the plugin manager, and the publictrivy-plugin-indexcatalog. - Plugins communicate via JSON over STDIN/STDOUT, enabling language-agnostic implementations.
- The CLI provides comprehensive lifecycle management through
trivy plugin install|run|update|uninstallcommands. - Plugins are stored in
$XDG_DATA_HOME/.trivy/pluginswith versioned subdirectories andplugin.yamlmanifests.
Frequently Asked Questions
How does Trivy discover installed plugins at runtime?
Trivy scans the plugin cache directory—defaulting to $XDG_DATA_HOME/.trivy/plugins or ~/.trivy/plugins on startup—looking for plugin.yaml manifest files. Each valid manifest is parsed into a Plugin struct and registered in the plugin manager’s internal map, keyed by plugin name for quick resolution during command execution.
Can plugins be written in languages other than Go?
Yes. Because plugins execute as isolated processes communicating through JSON streams on STDIN and STDOUT, they can be implemented in any language that handles JSON I/O. The plugin binary only needs to adhere to the JSON contract defined in the Trivy documentation and return properly formatted results on STDOUT.
What is the difference between trivy plugin update and trivy plugin upgrade?
The update command refreshes the local copy of the trivy-plugin-index repository, downloading the latest catalog of available plugins and versions. The upgrade command, by contrast, updates an already-installed plugin to its latest version by downloading the new binary and replacing the cached executable while preserving the plugin configuration.
Where does Trivy store plugin binaries and metadata?
Trivy stores plugins in a cache directory following the XDG Base Directory Specification—typically $XDG_DATA_HOME/.trivy/plugins or falling back to ~/.trivy/plugins. Each plugin resides in a versioned subdirectory containing the executable binary and its plugin.yaml manifest file defining metadata and supported scanner types.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →