Trivy can find secrets and misconfigurations in your code, but its real magic is how it models your entire filesystem, not just files.
Let’s see Trivy in action. Imagine you have a simple Go application:
package main
import (
"fmt"
"os"
)
func main() {
apiKey := os.Getenv("MY_API_KEY")
if apiKey == "" {
fmt.Println("API key not set. Please set the MY_API_KEY environment variable.")
return
}
fmt.Printf("Using API Key: %s\n", apiKey)
// ... your application logic using the API key ...
}
And you’ve accidentally committed your API key directly into a configuration file:
# config.yaml
database_url: "postgres://user:password@host:port/dbname"
api_key: "sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
Now, let’s scan this with Trivy. First, make sure you have Trivy installed. You can download it from the Trivy GitHub releases page.
To scan for secrets in the current directory, you’d run:
trivy fs . --secret-config .trivyignore
Trivy will output something like this:
secret-file://config.yaml:api_key
secret-file://config.yaml:database_url.password
It found both your API key and the database password. The .trivyignore file is a powerful tool. It works just like .gitignore, allowing you to specify files or patterns that Trivy should not scan for secrets. For instance, if you wanted to exclude all files in a testdata directory and any .env files, your .trivyignore might look like this:
testdata/
*.env
This tells Trivy to skip those locations, preventing false positives or accidental exposure of legitimate test credentials.
Trivy’s filesystem scanner builds an in-memory representation of your project’s directory structure. It doesn’t just read files one by one; it understands the relationships between directories and files. This allows it to perform more sophisticated checks, like identifying secrets that might be split across multiple lines or even across different files that are conventionally related (though the primary mode is file-based).
When it scans, Trivy uses a vast, regularly updated database of regular expressions and patterns specifically designed to detect common secret formats. This includes API keys for various services (AWS, Stripe, GitHub, etc.), private keys, passwords, and more. For misconfigurations, it checks against known insecure settings in common infrastructure-as-code files (like Terraform, CloudFormation, Kubernetes manifests) and application configuration files.
The power of Trivy lies in its configurability and extensibility. You can define your own custom rules for secrets and misconfigurations. For instance, if your organization uses a proprietary secret format, you can add a custom regex to Trivy’s configuration.
Here’s the mental model: Trivy starts by building a comprehensive map of your filesystem. For each file, it applies a series of checks based on its type. For secrets, it uses pattern matching. For misconfigurations, it parses the file content (if it’s a recognized configuration format) and compares the settings against a database of security best practices and known vulnerabilities. The --secret-config flag is crucial for tuning this process, ensuring you only scan what you intend to, thereby reducing noise and focusing on what matters.
What most people don’t realize is that Trivy’s secret detection isn’t just about finding strings that look like secrets. It also incorporates a level of context awareness. For example, it’s less likely to flag a generic string like "api_key" as a secret if it appears in a documentation file compared to a configuration file. This contextual understanding is achieved by analyzing file paths, file contents, and known patterns associated with different programming languages and configuration formats.
Once you’ve addressed the secrets found, your next challenge will be integrating Trivy into your CI/CD pipeline to automate these checks.