Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/docs/coverage/others/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ Trivy supports them for
| [Bitnami packages](bitnami.md) | `/opt/bitnami/<component>/.spdx-<component>.spdx` | ✅ | ✅ | - | - |
| [Conda](conda.md) | `<conda-root>/envs/<env>/conda-meta/<package>.json` | ✅ | ✅ | - | - |
| | `environment.yml` | - | - | ✅ | ✅ |
| [Root.io images](rootio.md) | - | ✅ | ✅ | - | - |
| [RPM Archives](rpm.md) | `*.rpm` | ✅[^5] | ✅[^5] | ✅[^5] | ✅[^5] |

[sbom]: ../../supply-chain/sbom.md
Expand Down
20 changes: 20 additions & 0 deletions docs/docs/coverage/others/rootio.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Root.io

!!! warning "EXPERIMENTAL"
Scanning results may be inaccurate.

While it is not an OS, this page describes the details of [Root.io](https://root.io/) patch distribution service.
Root.io provides security patches for [Debian](../os/debian.md), [Ubuntu](../os/ubuntu.md), and [Alpine](../os/alpine.md)-based container images.
Root.io patches are detected when Trivy finds packages with specific version suffixes:

- **Debian/Ubuntu**: packages with `.root.io` in version string
- **Alpine**: packages with `-r\d007\d` pattern in version string (e.g., `-r10071`, `-r20072`)

When Root.io patches are detected, Trivy automatically switches to Root.io scanning mode for vulnerability detection.
Even when the original OS distributor (Debian, Ubuntu, Alpine) has not provided a patch for a vulnerability, Trivy will display Root.io patches if they are available.

For detailed information about supported scanners, features, and functionality, please refer to the documentation for the underlying OS:

- [Debian](../os/debian.md)
- [Ubuntu](../os/ubuntu.md)
- [Alpine](../os/alpine.md)
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ trivy filesystem [flags] PATH
- govulndb
- echo
- minimos
- rootio
- auto
(default [auto])
```
Expand Down
1 change: 1 addition & 0 deletions docs/docs/references/configuration/cli/trivy_image.md
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ trivy image [flags] IMAGE_NAME
- govulndb
- echo
- minimos
- rootio
- auto
(default [auto])
```
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ trivy kubernetes [flags] [CONTEXT]
- govulndb
- echo
- minimos
- rootio
- auto
(default [auto])
```
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ trivy repository [flags] (REPO_PATH | REPO_URL)
- govulndb
- echo
- minimos
- rootio
- auto
(default [auto])
```
Expand Down
1 change: 1 addition & 0 deletions docs/docs/references/configuration/cli/trivy_rootfs.md
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ trivy rootfs [flags] ROOTDIR
- govulndb
- echo
- minimos
- rootio
- auto
(default [auto])
```
Expand Down
1 change: 1 addition & 0 deletions docs/docs/references/configuration/cli/trivy_sbom.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ trivy sbom [flags] SBOM_PATH
- govulndb
- echo
- minimos
- rootio
- auto
(default [auto])
```
Expand Down
1 change: 1 addition & 0 deletions docs/docs/references/configuration/cli/trivy_vm.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ trivy vm [flags] VM_IMAGE
- govulndb
- echo
- minimos
- rootio
- auto
(default [auto])
```
Expand Down
2 changes: 2 additions & 0 deletions docs/docs/scanner/vulnerability.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ See [here](../coverage/os/index.md#supported-os) for the supported OSes.
| Azure Linux (CBL-Mariner) | [OVAL][azure] |
| OpenSUSE/SLES | [CVRF][suse] |
| Photon OS | [Photon Security Advisory][photon] |
| Root.io | [Root.io Patch Feed][rootio] |

#### Data Source Selection
Trivy **only** consumes security advisories from the sources listed in the above table.
Expand Down Expand Up @@ -394,6 +395,7 @@ Example logic for the following vendor severity levels when scanning an Alpine i
[suse]: http://ftp.suse.com/pub/projects/security/cvrf/
[photon]: https://packages.vmware.com/photon/photon_cve_metadata/
[azure]: https://github.com/microsoft/AzureLinuxVulnerabilityData/
[rootio]: https://api.root.io/external/patch_feed

[php-ghsa]: https://github.com/advisories?query=ecosystem%3Acomposer
[python-ghsa]: https://github.com/advisories?query=ecosystem%3Apip
Expand Down
6 changes: 3 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ require (
github.com/aquasecurity/testdocker v0.0.0-20250616060700-ba6845ac6d17
github.com/aquasecurity/tml v0.6.1
github.com/aquasecurity/trivy-checks v1.11.3-0.20250604022615-9a7efa7c9169
github.com/aquasecurity/trivy-db v0.0.0-20250529093513-a12dfc204b6e
github.com/aquasecurity/trivy-db v0.0.0-20250627124416-ca81c496a932
github.com/aquasecurity/trivy-java-db v0.0.0-20240109071736-184bd7481d48
github.com/aquasecurity/trivy-kubernetes v0.9.0
github.com/aws/aws-sdk-go-v2 v1.36.5
Expand Down Expand Up @@ -323,7 +323,7 @@ require (
github.com/ncruces/go-strftime v0.1.9 // indirect
github.com/nozzle/throttler v0.0.0-20180817012639-2ea982251481 // indirect
github.com/oklog/ulid v1.3.1 // indirect
github.com/oklog/ulid/v2 v2.1.0 // indirect
github.com/oklog/ulid/v2 v2.1.1 // indirect
github.com/opencontainers/runtime-spec v1.2.1 // indirect
github.com/opencontainers/selinux v1.12.0 // indirect
github.com/opentracing/opentracing-go v1.2.0 // indirect
Expand All @@ -347,7 +347,7 @@ require (
github.com/rubenv/sql-migrate v1.8.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/sagikazarmark/locafero v0.7.0 // indirect
github.com/samber/oops v1.16.1 // indirect
github.com/samber/oops v1.18.1 // indirect
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 // indirect
github.com/sassoftware/relic v7.2.1+incompatible // indirect
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
Expand Down
12 changes: 6 additions & 6 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -798,8 +798,8 @@ github.com/aquasecurity/tml v0.6.1 h1:y2ZlGSfrhnn7t4ZJ/0rotuH+v5Jgv6BDDO5jB6A9gw
github.com/aquasecurity/tml v0.6.1/go.mod h1:OnYMWY5lvI9ejU7yH9LCberWaaTBW7hBFsITiIMY2yY=
github.com/aquasecurity/trivy-checks v1.11.3-0.20250604022615-9a7efa7c9169 h1:TckzIxUX7lZaU9f2lNxCN0noYYP8fzmSQf6a4JdV83w=
github.com/aquasecurity/trivy-checks v1.11.3-0.20250604022615-9a7efa7c9169/go.mod h1:nT69xgRcBD4NlHwTBpWMYirpK5/Zpl8M+XDOgmjMn2k=
github.com/aquasecurity/trivy-db v0.0.0-20250529093513-a12dfc204b6e h1:+B/in1DQDGwQbKhW5pWL8XxBgnZKxXhUznylJ2NCyvs=
github.com/aquasecurity/trivy-db v0.0.0-20250529093513-a12dfc204b6e/go.mod h1:4zd4qZcjhNAHASz5I0O7qapv5h5gSJzSEaZXv/IPOGc=
github.com/aquasecurity/trivy-db v0.0.0-20250627124416-ca81c496a932 h1:5GKQ53uGGHEEtZ/FX94jcIdfEGeyiHZ7tmJ5nCtDz5c=
github.com/aquasecurity/trivy-db v0.0.0-20250627124416-ca81c496a932/go.mod h1:Ubl2YWA6Zg7eaojg4MDmeDdYU4+PiGPsnwo6B5UIwqw=
github.com/aquasecurity/trivy-java-db v0.0.0-20240109071736-184bd7481d48 h1:JVgBIuIYbwG+ekC5lUHUpGJboPYiCcxiz06RCtz8neI=
github.com/aquasecurity/trivy-java-db v0.0.0-20240109071736-184bd7481d48/go.mod h1:Ldya37FLi0e/5Cjq2T5Bty7cFkzUDwTcPeQua+2M8i8=
github.com/aquasecurity/trivy-kubernetes v0.9.0 h1:rp8RuXwKfFWUPR/ULksA2WpD0z6rslVkzLmPGQr61Wc=
Expand Down Expand Up @@ -1617,8 +1617,8 @@ github.com/nxadm/tail v1.4.11 h1:8feyoE3OzPrcshW5/MJ4sGESc5cqmGkGCWlco4l0bqY=
github.com/nxadm/tail v1.4.11/go.mod h1:OTaG3NK980DZzxbRq6lEuzgU+mug70nY11sMd4JXXHc=
github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/oklog/ulid/v2 v2.1.0 h1:+9lhoxAP56we25tyYETBBY1YLA2SaoLvUFgrP2miPJU=
github.com/oklog/ulid/v2 v2.1.0/go.mod h1:rcEKHmBBKfef9DhnvX7y1HZBYxjXb0cP5ExxNsTT1QQ=
github.com/oklog/ulid/v2 v2.1.1 h1:suPZ4ARWLOJLegGFiZZ1dFAkqzhMjL3J1TzI+5wHz8s=
github.com/oklog/ulid/v2 v2.1.1/go.mod h1:rcEKHmBBKfef9DhnvX7y1HZBYxjXb0cP5ExxNsTT1QQ=
github.com/oleiade/reflections v1.0.1 h1:D1XO3LVEYroYskEsoSiGItp9RUxG6jWnCVvrqH0HHQM=
github.com/oleiade/reflections v1.0.1/go.mod h1:rdFxbxq4QXVZWj0F+e9jqjDkc7dbp97vkRixKo2JR60=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
Expand Down Expand Up @@ -1743,8 +1743,8 @@ github.com/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsF
github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k=
github.com/samber/lo v1.51.0 h1:kysRYLbHy/MB7kQZf5DSN50JHmMsNEdeY24VzJFu7wI=
github.com/samber/lo v1.51.0/go.mod h1:4+MXEGsJzbKGaUEQFKBq2xtfuznW9oz/WrgyzMzRoM0=
github.com/samber/oops v1.16.1 h1:XlKkXsWM5g8hE4C+sEV9n0X282fZn3XabVmAKU2RiHI=
github.com/samber/oops v1.16.1/go.mod h1:8eXgMAJcDXRAijQsFRhfy/EHDOTiSvwkg6khFqFK078=
github.com/samber/oops v1.18.1 h1:qjhZbqbdyhWBKntkY8sxrDNKA8b4c5VHlmI1rli7X7M=
github.com/samber/oops v1.18.1/go.mod h1:xYqvimigkKV70HyLXiBZJFpIWi2CGcc6Xx7eV+2HycI=
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 h1:lZUw3E0/J3roVtGQ+SCrUrg3ON6NgVqpn3+iol9aGu4=
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1/go.mod h1:uToXkOrWAZ6/Oc07xWQrPOhJotwFIyu2bBVN41fcDUY=
github.com/sassoftware/go-rpmutils v0.4.0 h1:ojND82NYBxgwrV+mX1CWsd5QJvvEZTKddtCdFLPWhpg=
Expand Down
1 change: 1 addition & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ nav:
- Overview: docs/coverage/others/index.md
- Bitnami Images: docs/coverage/others/bitnami.md
- Conda: docs/coverage/others/conda.md
- Root.io Images: docs/coverage/others/rootio.md
- RPM Archives: docs/coverage/others/rpm.md
- Kubernetes: docs/coverage/kubernetes.md
- Configuration:
Expand Down
40 changes: 25 additions & 15 deletions pkg/detector/ospkg/detect.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,14 @@ import (
"github.com/aquasecurity/trivy/pkg/detector/ospkg/bottlerocket"
"github.com/aquasecurity/trivy/pkg/detector/ospkg/chainguard"
"github.com/aquasecurity/trivy/pkg/detector/ospkg/debian"
"github.com/aquasecurity/trivy/pkg/detector/ospkg/driver"
"github.com/aquasecurity/trivy/pkg/detector/ospkg/echo"
"github.com/aquasecurity/trivy/pkg/detector/ospkg/minimos"
"github.com/aquasecurity/trivy/pkg/detector/ospkg/oracle"
"github.com/aquasecurity/trivy/pkg/detector/ospkg/photon"
"github.com/aquasecurity/trivy/pkg/detector/ospkg/redhat"
"github.com/aquasecurity/trivy/pkg/detector/ospkg/rocky"
"github.com/aquasecurity/trivy/pkg/detector/ospkg/rootio"
"github.com/aquasecurity/trivy/pkg/detector/ospkg/suse"
"github.com/aquasecurity/trivy/pkg/detector/ospkg/ubuntu"
"github.com/aquasecurity/trivy/pkg/detector/ospkg/wolfi"
Expand All @@ -32,7 +34,7 @@ var (
// ErrUnsupportedOS defines error for unsupported OS
ErrUnsupportedOS = xerrors.New("unsupported os")

drivers = map[ftypes.OSType]Driver{
drivers = map[ftypes.OSType]driver.Driver{
ftypes.Alpine: alpine.NewScanner(),
ftypes.Alma: alma.NewScanner(),
ftypes.Amazon: amazon.NewScanner(),
Expand All @@ -55,46 +57,54 @@ var (
ftypes.Echo: echo.NewScanner(),
ftypes.MinimOS: minimos.NewScanner(),
}

// providers dynamically generate drivers based on package information
// and environment detection. They are tried before standard OS-specific drivers.
providers = []driver.Provider{
rootio.Provider,
}
)

// RegisterDriver is defined for extensibility and not supposed to be used in Trivy.
func RegisterDriver(name ftypes.OSType, driver Driver) {
drivers[name] = driver
}

// Driver defines operations for OS package scan
type Driver interface {
Detect(context.Context, string, *ftypes.Repository, []ftypes.Package) ([]types.DetectedVulnerability, error)
IsSupportedVersion(context.Context, ftypes.OSType, string) bool
func RegisterDriver(name ftypes.OSType, drv driver.Driver) {
drivers[name] = drv
}

// Detect detects the vulnerabilities
func Detect(ctx context.Context, _, osFamily ftypes.OSType, osName string, repo *ftypes.Repository, _ time.Time, pkgs []ftypes.Package) ([]types.DetectedVulnerability, bool, error) {
ctx = log.WithContextPrefix(ctx, string(osFamily))

driver, err := newDriver(osFamily)
d, err := newDriver(osFamily, pkgs)
if err != nil {
return nil, false, ErrUnsupportedOS
}

eosl := !driver.IsSupportedVersion(ctx, osFamily, osName)
eosl := !d.IsSupportedVersion(ctx, osFamily, osName)

// Package `gpg-pubkey` doesn't use the correct version.
// We don't need to find vulnerabilities for this package.
filteredPkgs := lo.Filter(pkgs, func(pkg ftypes.Package, _ int) bool {
return pkg.Name != "gpg-pubkey"
})
vulns, err := driver.Detect(ctx, osName, repo, filteredPkgs)
vulns, err := d.Detect(ctx, osName, repo, filteredPkgs)
if err != nil {
return nil, false, xerrors.Errorf("failed detection: %w", err)
}

return vulns, eosl, nil
}

func newDriver(osFamily ftypes.OSType) (Driver, error) {
if driver, ok := drivers[osFamily]; ok {
return driver, nil
func newDriver(osFamily ftypes.OSType, pkgs []ftypes.Package) (driver.Driver, error) {
// Try providers first
for _, provider := range providers {
if d := provider(osFamily, pkgs); d != nil {
return d, nil
}
}

// Fall back to standard drivers
if d, ok := drivers[osFamily]; ok {
return d, nil
}

log.Warn("Unsupported os", log.String("family", string(osFamily)))
Expand Down
17 changes: 17 additions & 0 deletions pkg/detector/ospkg/driver/driver.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package driver

import (
"context"

ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
"github.com/aquasecurity/trivy/pkg/types"
)

// Driver defines operations for OS package scan
type Driver interface {
Detect(context.Context, string, *ftypes.Repository, []ftypes.Package) ([]types.DetectedVulnerability, error)
IsSupportedVersion(context.Context, ftypes.OSType, string) bool
}

// Provider creates a specialized driver based on the environment
type Provider func(osFamily ftypes.OSType, pkgs []ftypes.Package) Driver
45 changes: 45 additions & 0 deletions pkg/detector/ospkg/rootio/provider.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package rootio

import (
"regexp"

"github.com/aquasecurity/trivy/pkg/detector/ospkg/driver"
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
)

var (
// debianRootIOPattern matches Debian/Ubuntu Root.io version pattern: .root.io
debianRootIOPattern = regexp.MustCompile(`\.root\.io`)
// alpineRootIOPattern matches Alpine Root.io version pattern: -r\d007\d (e.g., -r10071, -r20072)
alpineRootIOPattern = regexp.MustCompile(`-r\d007\d`)
)

// Provider creates a Root.io driver if Root.io packages are detected
func Provider(osFamily ftypes.OSType, pkgs []ftypes.Package) driver.Driver {
if !isRootIOEnvironment(osFamily, pkgs) {
return nil
}
return NewScanner(osFamily)
}

// isRootIOEnvironment detects if the environment is Root.io based on package suffixes
func isRootIOEnvironment(osFamily ftypes.OSType, pkgs []ftypes.Package) bool {
switch osFamily {
case ftypes.Debian, ftypes.Ubuntu:
return hasPackageWithPattern(pkgs, debianRootIOPattern)
case ftypes.Alpine:
return hasPackageWithPattern(pkgs, alpineRootIOPattern)
default:
return false
}
}

// hasPackageWithPattern checks if any package version matches the specified pattern
func hasPackageWithPattern(pkgs []ftypes.Package, pattern *regexp.Regexp) bool {
for _, pkg := range pkgs {
if pattern.MatchString(pkg.Version) {
return true
}
}
return false
}
Loading