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 index
  • list – Shows locally installed plugins
  • search – Queries the index for available plugins
  • run – Executes a specific plugin directly
  • info – Displays metadata about an installed plugin
  • uninstall – Removes a plugin from the cache
  • upgrade – Updates a plugin to the latest version
  • update – 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.

  1. Discovery – On startup, Trivy scans the plugin cache directory for plugin.yaml files. Each manifest registers a plugin with its command line arguments, supported scanners, and human-readable description.

  2. Selection – Users activate plugins via CLI flags such as --output-plugin or through explicit subcommands like trivy plugin run <name>. The manager resolves these references to absolute executable paths.

  3. 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.

  4. 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.

  5. Lifecycle Management – Commands like install, uninstall, and upgrade manipulate 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 Plugin type in pkg/plugin/plugin.go, the plugin manager, and the public trivy-plugin-index catalog.
  • Plugins communicate via JSON over STDIN/STDOUT, enabling language-agnostic implementations.
  • The CLI provides comprehensive lifecycle management through trivy plugin install|run|update|uninstall commands.
  • Plugins are stored in $XDG_DATA_HOME/.trivy/plugins with versioned subdirectories and plugin.yaml manifests.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →