How to build a Trivy plugin in Rust πŸ¦€

How to build a Trivy plugin in Rust πŸ¦€


8 min read

TL;DR Code



In this blog article, we're going to build a Trivy plugin using Rust πŸ¦€. The functionality of the plugin itself is pretty basic: It contains a simple terminal user interface (TUI) to display the results of a Trivy image scan.

The main motivation of this blog article, is to show how to put the learnings of my last articles into a real-world scenario. We will use jReleaser again, to create our release and upload the binaries to GitHub.

Feel free to read the blog, as we are not dig so deep into the details of jReleaser.

We're going to use also an external library called tui-rs to create the UI. And of course most Rust πŸ¦€ language elements.

Check my blog, to build up an basic understanding of the most common language elements of Rust πŸ¦€


Be sure that you have the following tools installed and configured:

What is a Trivy plugin?

First, what is Trivy at all?

Trivy (tri pronounced like trigger, vy pronounced like envy) is a simple and comprehensive vulnerability/misconfiguration/secret scanner for containers and other artifacts developed by Aqua Security.

To know more about Trivy, I highly recommend to check following videos and of course the official documentation.

Or follow the AnaΓ―s on Twitter:

Now what are Trivy plugins?

Trivy provides a plugin feature to allow others to extend the Trivy CLI without the need to change the Trivy code base. This plugin system was inspired by the plugin system used in kubectl, Helm, and Conftest.

  • They can be added and removed from a Trivy installation without impacting the core Trivy tool.
  • They can be written in any programming language.
  • They integrate with Trivy, and will show up in Trivy help and subcommands.

A plugin can be installed using the trivy plugin install command. This command takes a url and will download the plugin and install it in the plugin cache.

Welcome tui-rs

On of the major parts of this plugin is the terminal user interface. We will use the tui-rs library to create our TUI in the plugin. The library is very easy to use and provides a lot of out of the box widgets to create a nice looking TUI.

I used the lazytrivy from Owen Rumney as a starting point for the TUI. Lazytrivy is a wrapper for Trivy that allows you to run Trivy without remembering the command arguments and renders everything nicely in a TUI.

BTW, lazytrivy is written in Go, so nice to rebuild some parts in Rust πŸ¦€ with this plugin.

Create the plugin

Initialize the project

Let us jump straight into the code. We will create a new Rust πŸ¦€ project with the following command:

cargo init

And add the following dependencies to the Cargo.toml file:

cargo add tui
cargo add serde_json
cargo add serde
cargo add clap

The tui library is the main library we will use to create the TUI. The serde_json and serde libraries are used to parse the JSON output of Trivy. And the clap library is used to parse the command line arguments.

Underneath the src directory I created the and some additional Rust πŸ¦€ files to keep the code clean and readable.

Let us take a look into some parts of the Rust πŸ¦€ files, as we can not cover the whole code in this blog article.

The file

The task of the file is to execute the Trivy CLI and parse the JSON output.

The output of the command is passed to the serde library to parse the JSON into a struct. The struct is defined in the file as well.

pub fn trivy(image_name: &str) -> Trivy {
    // setup terminal
    let mut cmd = Command::new("trivy");
    let list = cmd.arg("image").arg(image_name)

    let object: Trivy;
    match list {
        Ok(out) => match String::from_utf8(out.stdout) {
            Ok(data) => {
                object = serde_json::from_str(&data.as_str()).unwrap();
            Err(_) => unreachable!("No panic happens in this block"),
        Err(_) => unreachable!("No panic happens in this block"),

As you can see, we only check for container image vulnerabilities. Feel free to add more scan options.

The file

The file contains the code to handle all the command line releated task. To keep our life simple, we use the clap library.

In the main function, we parse the arguments and pass the image name to the file to run the Trivy CLI.

In the code, we define the argument structure:

#[derive(Parser, Debug)]
#[command(author = "Engin Diri", version, long_about = None)]
/// A simple tui Trivy plugin written in Rust
pub struct Args {
    #[arg(short, long)]
    pub image_name: String,

And parse the arguments and pass it to the trivy command with the following code:

let args = Args::parse();
let object = trivy::trivy( & args.image_name);

The file

In the file, we create the TUI. The TUI consists of a table with the vulnerabilities. If you press the enter key on a vulnerability, the details of the vulnerability are shown in a new popup window.

The table with the vulnerabilities also has some styling. The vulnerabilities are colored based on the severity of the vulnerability.

pub fn critical_color(crtical: String) -> Style {
    return if crtical == "CRITICAL" {
    } else if crtical == "HIGH" {
    } else if crtical == "MEDIUM" {
    } else if crtical == "LOW" {
    } else {


The creation of the rows of the table is done with the following code:

for i in &app.trivy.results {
    match &i.vulnerabilities {
        Some(vul) => {
                Span::styled("", Style::default()),
            ]), std::ptr::null()));
                Span::styled("", Style::default()),
                Span::styled("target: ", Style::default().fg(Color::White).add_modifier(Modifier::BOLD)),
                Span::styled(, Style::default().fg(Color::Blue)),
            ]), std::ptr::null()));
                Span::styled("", Default::default()),
            ]), std::ptr::null()));
            for j in vul {
                ]), j));
        _ => {}


The interaction with the TUI is done with the following code:

if let Event::Key(key) = event::read()? {
    match key.code {
    KeyCode::Char('q') => {
    if app.show_popup {
      app.pop_scroll = 0;
      app.show_popup = false;
    } else {
      return Ok(());
  KeyCode::Esc => {
    if app.show_popup {
      app.pop_scroll = 0;
      app.show_popup = false;
    } else {
      return Ok(());
  KeyCode::Down => {
    if app.show_popup {
      app.pop_scroll += 1;
    } else {;
  KeyCode::Up => {
    if app.show_popup {
      if app.pop_scroll > 0 {
        app.pop_scroll -= 1;
    } else {
  KeyCode::Enter => app.show_popup = !app.show_popup,
          _ => {}

Here is a screenshot of the TUI:


The plugin.yaml

A Trivy plugin has a top-level directory, and then a plugin.yaml file.

The core of a plugin is a simple YAML file named plugin.yaml. Here is the content of the plugin.yaml file:

name: "trivy-ui"
version: "0.1.0"
usage: trivy ui -- --image-name <image-name>
description: |-
  A Trivy plugin that displays the vulnerabilities in a TUI.
  Usage: trivy ui -- --image-name <image-name>
  - selector:
      os: linux
      arch: amd64
    bin: ./ui
  - selector:
      os: darwin
      arch: amd64
    bin: ./ui

The plugin.yaml field should contain the following information:

  • name: The name of the plugin. This also determines how the plugin will be made available in the Trivy CLI. ( required)
  • version: The version of the plugin. (required)
  • usage: A short usage description. (required)
  • description: A long description of the plugin. This is where you could provide a helpful documentation of your plugin. (required)
  • platforms: (required)
  • selector: The OS/Architecture specific variations of a execution file. (optional)
    • os: OS information based on GOOS (linux, darwin, etc.) (optional)
    • arch: The architecture information based on GOARCH (amd64, arm64, etc.) (optional)
  • uri: Where the executable file is. Relative path from the root directory of the plugin or remote URL such as HTTP and S3. (required)
  • bin: Which file to call when the plugin is executed. Relative path from the root directory of the plugin. (required)

Creating the release with jReleaser

To create the release, we use the jReleaser tool. Please check my previous blog post on how to use jReleaser to create a release.

The jReleaser configuration file is the following:

  name: ui
  version: 0.1.0
  description: A simple tui Trivy plugin written in Rust
    - Engin Diri
  license: Apache-2.0
  inceptionYear: 2022

    artifactsDir: out/jreleaser/assemble/ui/archive

    'osx-x86_64': 'darwin-amd64'
    'linux-x86_64': 'linux-amd64'
    'windows-x86_64': 'windows-amd64'

      active: ALWAYS
      formats: [ TAR_GZ ]
      attachPlatform: true
        - input: 'target/release'
          includes: [ 'ui{.exe,}' ]
        - input: '.'
          includes: [ 'LICENSE' ]

    type: BINARY
      windowsExtension: exe
      - path: '{{artifactsDir}}/{{distributionName}}-{{projectVersion}}-darwin-amd64.tar.gz'
        platform: 'osx-x86_64'
      - path: '{{artifactsDir}}/{{distributionName}}-{{projectVersion}}-linux-amd64.tar.gz'
        platform: 'linux-x86_64'
      - path: '{{artifactsDir}}/{{distributionName}}-{{projectVersion}}-windows-amd64.tar.gz'
        platform: 'windows-x86_64'

    owner: dirien
    name: trivy-plugin-ui
    skipTag: false
    draft: false
      enabled: true
        - ASSETS
        - TITLE
        - BODY

Now, with everything in place, we can create our release in GitHub.


Under the Releases tab, you should see the v0.1.0 release:


Installing the plugin

Let's install the plugin:

trivy plugin install
2022-10-21T11:24:36.868+0200    INFO    Installing the plugin from
2022-10-21T11:24:38.614+0200    INFO    Loading the plugin metadata...

And see the plugin in the list:

trivy plugin list
Installed Plugins:
  Name:    trivy-ui
  Version: 0.1.0

And execute the plugin:

trivy trivy-ui -- --image-name dexidp/dex:latest



In this blog post, we saq how to create a Trivy plugin using Rust πŸ¦€. Plus, we used a TUI library to create a simple and good-looking TUI.

Next, we used jReleaser to create a release. And finally, we installed the plugin and ran it.

Feel free to check the source code of the plugin to create your own plugin or see how I created a TUI with Rust πŸ¦€.