feat(seal): add seal support (#9370)

This commit is contained in:
DmitriyLewen
2025-09-29 14:44:40 +06:00
committed by GitHub
parent e149094f9b
commit e4af279b29
17 changed files with 711 additions and 25 deletions

View File

@@ -68,6 +68,7 @@ jobs:
windows
minimos
rootio
seal
# Languages
ruby

View File

@@ -16,6 +16,7 @@ Trivy supports them for
| [Conda](conda.md) | `<conda-root>/envs/<env>/conda-meta/<package>.json` | ✅ | ✅ | - | - |
| | `environment.yml` | - | - | ✅ | ✅ |
| [Root.io images](rootio.md) | - | ✅ | ✅ | - | - |
| [Seal Security](seal.md) | - | ✅ | ✅ | - | - |
| [RPM Archives](rpm.md) | `*.rpm` | ✅[^5] | ✅[^5] | ✅[^5] | ✅[^5] |
[sbom]: ../../supply-chain/sbom.md

View File

@@ -0,0 +1,27 @@
# Seal Security
!!! warning "EXPERIMENTAL"
Scanning results may be inaccurate.
While it is not an OS, this page describes the details of the [Seal Security]( https://sealsecurity.io/) vulnerability feed.
Seal provides security advisories and patched versions for multiple Linux distributions, including [Debian](../os/debian.md), [Ubuntu](../os/ubuntu.md), [Alpine](../os/alpine.md), [Red Hat Enterprise Linux](../os/rhel.md), [CentOS](../os/centos.md), [Oracle Linux](../os/oracle.md), and [Azure Linux (CBLMariner)](../os/azure.md).
Seal advisories are used when Trivy finds packages that indicate Seal-provided components:
- Packages whose name or source name starts with `seal-` (for example, `seal-wget`, `seal-zlib`).
When such Seal packages are detected, Trivy automatically enables Seal scanning for those packages while continuing to use the base OS scanner for the rest.
!!! note
For vulnerabilities, Trivy prefers severity from the base OS vendor when available.
For details on supported scanners, features, and behavior for each base OS, refer to their respective pages:
- [Debian](../os/debian.md)
- [Ubuntu](../os/ubuntu.md)
- [Alpine](../os/alpine.md)
- [Red Hat Enterprise Linux](../os/rhel.md)
- [CentOS](../os/centos.md)
- [Oracle Linux](../os/oracle.md)
- [Azure Linux (CBLMariner)](../os/azure.md)

View File

@@ -38,6 +38,7 @@ See [here](../coverage/os/index.md#supported-os) for the supported OSes.
| OpenSUSE/SLES | [CVRF][suse] |
| Photon OS | [Photon Security Advisory][photon] |
| Root.io | [Root.io Patch Feed][rootio] |
| Seal Security | [Seal Security vulnerability feed][seal] |
#### Data Source Selection
Trivy **only** consumes security advisories from the sources listed in the above table.
@@ -404,6 +405,7 @@ Example logic for the following vendor severity levels when scanning an Alpine i
[photon]: https://packages.vmware.com/photon/photon_cve_metadata/
[azure]: https://github.com/microsoft/AzureLinuxVulnerabilityData/
[rootio]: https://api.root.io/external/patch_feed
[seal]: http://vulnfeed.sealsecurity.io/v1/osv/renamed/vulnerabilities.zip
[php-ghsa]: https://github.com/advisories?query=ecosystem%3Acomposer
[python-ghsa]: https://github.com/advisories?query=ecosystem%3Apip
@@ -433,4 +435,4 @@ Example logic for the following vendor severity levels when scanning an Alpine i
[RHSA-2023:4520]: https://access.redhat.com/errata/RHSA-2023:4520
[ghsa]: https://github.com/advisories
[requests]: https://pypi.org/project/requests/
[precision-recall]: https://developers.google.com/machine-learning/crash-course/classification/precision-and-recall
[precision-recall]: https://developers.google.com/machine-learning/crash-course/classification/precision-and-recall

3
go.mod
View File

@@ -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-20250912085155-990a6528209a
github.com/aquasecurity/trivy-db v0.0.0-20250929072116-eba1ced2340a
github.com/aquasecurity/trivy-java-db v0.0.0-20240109071736-184bd7481d48
github.com/aquasecurity/trivy-kubernetes v0.9.1
github.com/aws/aws-sdk-go-v2 v1.39.0
@@ -369,6 +369,7 @@ require (
github.com/opencontainers/selinux v1.12.0 // indirect
github.com/opentracing/opentracing-go v1.2.0 // indirect
github.com/owenrumney/squealer v1.2.11 // indirect
github.com/pandatix/go-cvss v0.6.2 // indirect
github.com/pelletier/go-toml v1.9.5 // indirect
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect

6
go.sum
View File

@@ -219,8 +219,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-20250912085155-990a6528209a h1:mcPk1ovUuUFnJwbRMRKtSIe3j0BQfJ33RQdB/kB5QZY=
github.com/aquasecurity/trivy-db v0.0.0-20250912085155-990a6528209a/go.mod h1:upAJqDQkN5FdIJbtJMpokncGNhYAPGkpoCbaGciWPt4=
github.com/aquasecurity/trivy-db v0.0.0-20250929072116-eba1ced2340a h1:Wmvjq3zQGsZ8Wlqh75zvujh7LZNTXU4YoEf8tyL1LoM=
github.com/aquasecurity/trivy-db v0.0.0-20250929072116-eba1ced2340a/go.mod h1:upAJqDQkN5FdIJbtJMpokncGNhYAPGkpoCbaGciWPt4=
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.1 h1:bSErQcavKXDh7XMwbGX7Vy//jR5+xhe/bOgfn9G+9lQ=
@@ -1027,6 +1027,8 @@ github.com/owenrumney/squealer v1.2.11 h1:vMudrj70VeOzY+t7Phz9Yo0wAgm4kXes9DcTLB
github.com/owenrumney/squealer v1.2.11/go.mod h1:8KOuitfOfmS/OtzgxQbxnnrbngAGopfgKB/BiGGpqGA=
github.com/package-url/packageurl-go v0.1.3 h1:4juMED3hHiz0set3Vq3KeQ75KD1avthoXLtmE3I0PLs=
github.com/package-url/packageurl-go v0.1.3/go.mod h1:nKAWB8E6uk1MHqiS/lQb9pYBGH2+mdJ2PJc2s50dQY0=
github.com/pandatix/go-cvss v0.6.2 h1:TFiHlzUkT67s6UkelHmK6s1INKVUG7nlKYiWWDTITGI=
github.com/pandatix/go-cvss v0.6.2/go.mod h1:jDXYlQBZrc8nvrMUVVvTG8PhmuShOnKrxP53nOFkt8Q=
github.com/pborman/getopt v0.0.0-20170112200414-7148bc3a4c30/go.mod h1:85jBQOZwpVEaDAr341tbn15RS4fCAsIst0qp7i8ex1o=
github.com/pborman/uuid v1.2.1 h1:+ZZIw58t/ozdjRaXh/3awHfmWRbzYxJoAdNJxe/3pvw=
github.com/pborman/uuid v1.2.1/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=

View File

@@ -119,6 +119,7 @@ nav:
- Bitnami Images: docs/coverage/others/bitnami.md
- Conda: docs/coverage/others/conda.md
- Root.io Images: docs/coverage/others/rootio.md
- Seal Security: docs/coverage/others/seal.md
- RPM Archives: docs/coverage/others/rpm.md
- Kubernetes: docs/coverage/kubernetes.md
- Configuration:
@@ -291,4 +292,3 @@ extra:
plugins:
- search
- macros

View File

@@ -8,6 +8,7 @@ import (
"golang.org/x/xerrors"
"github.com/aquasecurity/trivy-db/pkg/db"
"github.com/aquasecurity/trivy-db/pkg/ecosystem"
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
"github.com/aquasecurity/trivy-db/pkg/vulnsrc/vulnerability"
"github.com/aquasecurity/trivy/pkg/detector/library/compare"
@@ -23,63 +24,63 @@ import (
// NewDriver returns a driver according to the library type
func NewDriver(libType ftypes.LangType) (Driver, bool) {
var ecosystem dbTypes.Ecosystem
var eco ecosystem.Type
var comparer compare.Comparer
switch libType {
case ftypes.Bundler, ftypes.GemSpec:
ecosystem = vulnerability.RubyGems
eco = ecosystem.RubyGems
comparer = rubygems.Comparer{}
case ftypes.RustBinary, ftypes.Cargo:
ecosystem = vulnerability.Cargo
eco = ecosystem.Cargo
comparer = compare.GenericComparer{}
case ftypes.Composer, ftypes.ComposerVendor:
ecosystem = vulnerability.Composer
eco = ecosystem.Composer
comparer = compare.GenericComparer{}
case ftypes.GoBinary, ftypes.GoModule:
ecosystem = vulnerability.Go
eco = ecosystem.Go
comparer = compare.GenericComparer{}
case ftypes.Jar, ftypes.Pom, ftypes.Gradle, ftypes.Sbt:
ecosystem = vulnerability.Maven
eco = ecosystem.Maven
comparer = maven.Comparer{}
case ftypes.Npm, ftypes.Yarn, ftypes.Pnpm, ftypes.Bun, ftypes.NodePkg, ftypes.JavaScript:
ecosystem = vulnerability.Npm
eco = ecosystem.Npm
comparer = npm.Comparer{}
case ftypes.NuGet, ftypes.DotNetCore, ftypes.PackagesProps:
ecosystem = vulnerability.NuGet
eco = ecosystem.NuGet
comparer = compare.GenericComparer{}
case ftypes.Pipenv, ftypes.Poetry, ftypes.Pip, ftypes.PythonPkg, ftypes.Uv:
ecosystem = vulnerability.Pip
eco = ecosystem.Pip
comparer = pep440.Comparer{}
case ftypes.Pub:
ecosystem = vulnerability.Pub
eco = ecosystem.Pub
comparer = compare.GenericComparer{}
case ftypes.Hex:
ecosystem = vulnerability.Erlang
eco = ecosystem.Erlang
comparer = compare.GenericComparer{}
case ftypes.Conan:
ecosystem = vulnerability.Conan
eco = ecosystem.Conan
// Only semver can be used for version ranges
// https://docs.conan.io/en/latest/versioning/version_ranges.html
comparer = compare.GenericComparer{}
case ftypes.Swift:
// Swift uses semver
// https://www.swift.org/package-manager/#importing-dependencies
ecosystem = vulnerability.Swift
eco = ecosystem.Swift
comparer = compare.GenericComparer{}
case ftypes.Cocoapods:
// CocoaPods uses RubyGems version specifiers
// https://guides.cocoapods.org/making/making-a-cocoapod.html#cocoapods-versioning-specifics
ecosystem = vulnerability.Cocoapods
eco = ecosystem.Cocoapods
comparer = rubygems.Comparer{}
case ftypes.CondaPkg, ftypes.CondaEnv:
log.Warn("Conda package is supported for SBOM, not for vulnerability scanning")
return Driver{}, false
case ftypes.Bitnami:
ecosystem = vulnerability.Bitnami
eco = ecosystem.Bitnami
comparer = bitnami.Comparer{}
case ftypes.K8sUpstream:
ecosystem = vulnerability.Kubernetes
eco = ecosystem.Kubernetes
comparer = compare.GenericComparer{}
case ftypes.Julia:
log.Warn("Julia is supported for SBOM, not for vulnerability scanning")
@@ -90,7 +91,7 @@ func NewDriver(libType ftypes.LangType) (Driver, bool) {
return Driver{}, false
}
return Driver{
ecosystem: ecosystem,
ecosystem: eco,
comparer: comparer,
dbc: db.Config{},
}, true
@@ -98,7 +99,7 @@ func NewDriver(libType ftypes.LangType) (Driver, bool) {
// Driver represents security advisories for each programming language
type Driver struct {
ecosystem dbTypes.Ecosystem
ecosystem ecosystem.Type
comparer compare.Comparer
dbc db.Config
}

View File

@@ -22,6 +22,7 @@ import (
"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/seal"
"github.com/aquasecurity/trivy/pkg/detector/ospkg/suse"
"github.com/aquasecurity/trivy/pkg/detector/ospkg/ubuntu"
"github.com/aquasecurity/trivy/pkg/detector/ospkg/wolfi"
@@ -63,6 +64,7 @@ var (
// and environment detection. They are tried before standard OS-specific drivers.
providers = []driver.Provider{
rootio.Provider,
seal.Provider,
}
)

View File

@@ -0,0 +1,29 @@
package seal
import (
"slices"
"github.com/aquasecurity/trivy/pkg/detector/ospkg/driver"
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
"github.com/aquasecurity/trivy/pkg/set"
)
var (
supportedOSFamilies = set.New(
ftypes.Alpine,
ftypes.CBLMariner,
ftypes.CentOS,
ftypes.RedHat,
ftypes.Debian,
ftypes.Oracle,
ftypes.Ubuntu,
)
)
// Provider creates a Root.io driver if Root.io packages are detected
func Provider(osFamily ftypes.OSType, pkgs []ftypes.Package) driver.Driver {
if supportedOSFamilies.Contains(osFamily) && slices.ContainsFunc(pkgs, sealPkg) {
return NewScanner(osFamily)
}
return nil
}

View File

@@ -0,0 +1,81 @@
package seal_test
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/aquasecurity/trivy/pkg/detector/ospkg/seal"
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
)
func TestProvider(t *testing.T) {
tests := []struct {
name string
osFamily ftypes.OSType
pkgs []ftypes.Package
want bool // true if driver should be returned, false if nil
}{
{
name: "returns driver when package name starts with seal",
osFamily: ftypes.Debian,
pkgs: []ftypes.Package{
{Name: "seal-agent", Version: "1.0.0"},
{Name: "bash", Version: "5.1"},
},
want: true,
},
{
name: "returns driver when src name starts with seal",
osFamily: ftypes.Ubuntu,
pkgs: []ftypes.Package{
{Name: "libssl", SrcName: "seal-ssl", Version: "1.2.3"},
{Name: "curl", Version: "7.81.0"},
},
want: true,
},
{
name: "returns nil when no seal packages present",
osFamily: ftypes.Alpine,
pkgs: []ftypes.Package{
{Name: "musl", Version: "1.2.3"},
{Name: "busybox", Version: "1.36.1"},
},
want: false,
},
{
name: "returns nil for empty package list",
osFamily: ftypes.Debian,
pkgs: []ftypes.Package{},
want: false,
},
{
name: "case-insensitive: Seal prefix matched",
osFamily: ftypes.Ubuntu,
pkgs: []ftypes.Package{
{Name: "Seal-agent", Version: "2.0.0"},
},
want: true,
},
{
name: "returns nil for unsupported OS family even with seal package",
osFamily: ftypes.Fedora,
pkgs: []ftypes.Package{
{Name: "seal-agent", Version: "1.0.0"},
},
want: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
d := seal.Provider(tt.osFamily, tt.pkgs)
if tt.want {
require.NotNil(t, d, "expected a non-nil driver when seal package is present")
} else {
assert.Nil(t, d, "expected nil driver when no seal package is present")
}
})
}
}

View File

@@ -0,0 +1,203 @@
package seal
import (
"cmp"
"context"
"strings"
"github.com/samber/lo"
"golang.org/x/xerrors"
"github.com/aquasecurity/trivy-db/pkg/db"
"github.com/aquasecurity/trivy-db/pkg/ecosystem"
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
"github.com/aquasecurity/trivy-db/pkg/vulnsrc/seal"
"github.com/aquasecurity/trivy/pkg/detector/ospkg/alpine"
"github.com/aquasecurity/trivy/pkg/detector/ospkg/azure"
"github.com/aquasecurity/trivy/pkg/detector/ospkg/debian"
"github.com/aquasecurity/trivy/pkg/detector/ospkg/driver"
"github.com/aquasecurity/trivy/pkg/detector/ospkg/oracle"
"github.com/aquasecurity/trivy/pkg/detector/ospkg/redhat"
"github.com/aquasecurity/trivy/pkg/detector/ospkg/ubuntu"
"github.com/aquasecurity/trivy/pkg/detector/ospkg/version"
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/scan/utils"
"github.com/aquasecurity/trivy/pkg/types"
)
// Scanner implements the Seal scanner
type Scanner struct {
comparer version.Comparer
scanner driver.Driver
vsg seal.VulnSrcGetter
logger *log.Logger
}
// NewScanner is the factory method for Scanner
func NewScanner(baseOS ftypes.OSType) *Scanner {
var scanner driver.Driver
var comparer version.Comparer
var vsg seal.VulnSrcGetter
switch baseOS {
case ftypes.Alpine:
scanner = alpine.NewScanner()
comparer = version.NewAPKComparer()
vsg = seal.NewVulnSrcGetter(ecosystem.Alpine)
case ftypes.CBLMariner:
scanner = azure.NewMarinerScanner()
comparer = version.NewRPMComparer()
vsg = seal.NewVulnSrcGetter(ecosystem.RedHat)
case ftypes.CentOS, ftypes.RedHat:
scanner = redhat.NewScanner()
comparer = version.NewRPMComparer()
vsg = seal.NewVulnSrcGetter(ecosystem.RedHat)
case ftypes.Debian:
scanner = debian.NewScanner()
comparer = version.NewDEBComparer()
vsg = seal.NewVulnSrcGetter(ecosystem.Debian)
case ftypes.Oracle:
scanner = oracle.NewScanner()
comparer = version.NewRPMComparer()
vsg = seal.NewVulnSrcGetter(ecosystem.RedHat)
case ftypes.Ubuntu:
scanner = ubuntu.NewScanner()
comparer = version.NewDEBComparer()
vsg = seal.NewVulnSrcGetter(ecosystem.Debian)
default:
// Should never happen as it's validated in the provider
scanner = debian.NewScanner()
comparer = version.NewDEBComparer()
vsg = seal.NewVulnSrcGetter(ecosystem.Debian)
}
return &Scanner{
scanner: scanner,
comparer: comparer,
vsg: vsg,
logger: log.WithPrefix("seal"),
}
}
// Detect vulnerabilities in package using Seal scanner
func (s *Scanner) Detect(ctx context.Context, osVer string, _ *ftypes.Repository, pkgs []ftypes.Package) ([]types.DetectedVulnerability, error) {
log.InfoContext(ctx, "Detecting vulnerabilities...", log.String("os_version", osVer), log.Int("pkg_num", len(pkgs)))
var vulns []types.DetectedVulnerability
var baseOSPkgs ftypes.Packages
for _, pkg := range pkgs {
// Keep non-Seal packages to scan them with the base OS scanner later
if !sealPkg(pkg) {
baseOSPkgs = append(baseOSPkgs, pkg)
continue
}
srcName := cmp.Or(pkg.SrcName, pkg.Name)
srcRelease := lo.Ternary(pkg.SrcName != "", pkg.SrcRelease, pkg.Release)
advisories, err := s.vsg.Get(db.GetParams{
// Detect release version from pkg Release (`el` version for RPM-based distros, empty for other distros).
// cf. https://github.com/aquasecurity/vuln-list-update/pull/364#issuecomment-3244733993
Release: releaseVersion(srcRelease),
PkgName: srcName,
})
if err != nil {
return nil, xerrors.Errorf("failed to get Seal advisories: %w", err)
}
for _, adv := range advisories {
if !s.isVulnerable(ctx, utils.FormatSrcVersion(pkg), adv) {
continue
}
vuln := types.DetectedVulnerability{
VulnerabilityID: adv.VulnerabilityID,
PkgID: pkg.ID,
PkgName: pkg.Name,
InstalledVersion: utils.FormatVersion(pkg),
FixedVersion: strings.Join(adv.PatchedVersions, ", "),
Layer: pkg.Layer,
PkgIdentifier: pkg.Identifier,
Custom: adv.Custom,
DataSource: adv.DataSource,
}
if adv.Severity != dbTypes.SeverityUnknown {
// Package-specific severity
vuln.SeveritySource = adv.DataSource.BaseID
vuln.Vulnerability = dbTypes.Vulnerability{
Severity: adv.Severity.String(),
}
}
vulns = append(vulns, vuln)
}
}
// Detect vulns for baseOS packages.
baseOSVulns, err := s.scanner.Detect(ctx, osVer, nil, baseOSPkgs)
if err != nil {
return nil, xerrors.Errorf("failed to detect vulnerabilities with base OS scanner: %w", err)
}
return append(baseOSVulns, vulns...), nil
}
func (s *Scanner) isVulnerable(ctx context.Context, installedVersion string, adv dbTypes.Advisory) bool {
// Handle unfixed vulnerabilities
if len(adv.VulnerableVersions) == 0 {
// If no vulnerable versions are specified, it means the package is always vulnerable
return true
}
// For fixed vulnerabilities, check if installed version satisfies the constraint
return s.checkConstraints(ctx, installedVersion, adv.VulnerableVersions)
}
func (s *Scanner) checkConstraints(ctx context.Context, installedVersion string, constraintsStr []string) bool {
if installedVersion == "" {
return false
}
for _, constraintStr := range constraintsStr {
constraints, err := version.NewConstraints(constraintStr, s.comparer)
if err != nil {
s.logger.DebugContext(ctx, "Failed to parse constraints",
log.String("constraints", constraintStr), log.Err(err))
return false
}
if satisfied, err := constraints.Check(installedVersion); err != nil {
s.logger.DebugContext(ctx, "Failed to check version constraints",
log.String("version", installedVersion),
log.String("constraints", constraintStr), log.Err(err))
return false
} else if satisfied {
return true
}
}
return false
}
// IsSupportedVersion checks if the version is supported.
// Seal creates fixes for EOL distributions, so we assume all versions are supported.
func (s *Scanner) IsSupportedVersion(_ context.Context, _ ftypes.OSType, _ string) bool {
return true
}
func sealPkg(pkg ftypes.Package) bool {
// Seal packages start with "seal-"
return strings.HasPrefix(strings.ToLower(pkg.Name), "seal-") || strings.HasPrefix(strings.ToLower(pkg.SrcName), "seal-")
}
func releaseVersion(release string) string {
// Seal takes RedHat version from `el` version of package.
// cf. https://github.com/aquasecurity/vuln-list-update/pull/364#issuecomment-3244733993
if !strings.Contains(release, "el") {
return ""
}
_, osVer, _ := strings.Cut(release, "el") // Remove `el` and text before.
osVer, _, _ = strings.Cut(osVer, "_") // Remove `_.*` suffix (e.g. `9.el10_0.1`)
osVer, _, _ = strings.Cut(osVer, "+") // Remove `+.*` suffix (e.g. `3.el8+sp1`)
return osVer
}

View File

@@ -0,0 +1,238 @@
package seal_test
import (
"testing"
"github.com/stretchr/testify/require"
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
"github.com/aquasecurity/trivy/internal/dbtest"
"github.com/aquasecurity/trivy/pkg/detector/ospkg/seal"
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
"github.com/aquasecurity/trivy/pkg/types"
)
func TestScanner_Detect(t *testing.T) {
type args struct {
osVer string
pkgs []ftypes.Package
}
tests := []struct {
name string
baseOS ftypes.OSType
fixtures []string
args args
want []types.DetectedVulnerability
wantErr string
}{
{
name: "Debian scanner",
baseOS: ftypes.Debian,
fixtures: []string{
"testdata/fixtures/seal.yaml",
"testdata/fixtures/data-source.yaml",
},
args: args{
osVer: "12",
pkgs: []ftypes.Package{
{
Name: "seal-wget",
Version: "1.21",
Release: "1+deb11u1",
SrcName: "seal-wget",
SrcVersion: "1.21",
SrcRelease: "1+deb11u1",
},
},
},
want: []types.DetectedVulnerability{
{
PkgName: "seal-wget",
VulnerabilityID: "CVE-2024-10524",
InstalledVersion: "1.21-1+deb11u1",
FixedVersion: "1.21-1+deb11u1+sp999",
DataSource: &dbTypes.DataSource{
ID: "seal",
Name: "Seal Security Database",
URL: "http://vulnfeed.sealsecurity.io/v1/osv/renamed/vulnerabilities.zip",
BaseID: "debian",
},
},
},
},
{
name: "Ubuntu scanner",
baseOS: ftypes.Ubuntu,
fixtures: []string{
"testdata/fixtures/seal.yaml",
"testdata/fixtures/data-source.yaml",
},
args: args{
osVer: "22.04",
pkgs: []ftypes.Package{
{
Name: "seal-wget",
Version: "1.21",
Release: "1+deb11u1",
SrcName: "seal-wget",
SrcVersion: "1.21",
SrcRelease: "1+deb11u1",
},
},
},
want: []types.DetectedVulnerability{
{
PkgName: "seal-wget",
VulnerabilityID: "CVE-2024-10524",
InstalledVersion: "1.21-1+deb11u1",
FixedVersion: "1.21-1+deb11u1+sp999",
DataSource: &dbTypes.DataSource{
ID: "seal",
Name: "Seal Security Database",
URL: "http://vulnfeed.sealsecurity.io/v1/osv/renamed/vulnerabilities.zip",
BaseID: "debian",
},
},
},
},
{
name: "Alpine scanner",
baseOS: ftypes.Alpine,
fixtures: []string{
"testdata/fixtures/seal.yaml",
"testdata/fixtures/data-source.yaml",
},
args: args{
osVer: "3.21.5",
pkgs: []ftypes.Package{
{
Name: "seal-zlib",
Version: "1.2.8-r2",
SrcName: "seal-zlib",
SrcVersion: "1.2.8-r2",
},
},
},
want: []types.DetectedVulnerability{
{
PkgName: "seal-zlib",
VulnerabilityID: "CVE-2023-6992",
InstalledVersion: "1.2.8-r2",
FixedVersion: "1.2.8-r25341999",
DataSource: &dbTypes.DataSource{
ID: "seal",
Name: "Seal Security Database",
URL: "http://vulnfeed.sealsecurity.io/v1/osv/renamed/vulnerabilities.zip",
BaseID: "alpine",
},
},
},
},
{
name: "RedHat scanner",
baseOS: ftypes.RedHat,
fixtures: []string{
"testdata/fixtures/seal.yaml",
"testdata/fixtures/data-source.yaml",
},
args: args{
osVer: "9.3",
pkgs: []ftypes.Package{
{
Name: "seal-wget",
Version: "1.12",
Release: "10.el6",
SrcName: "seal-wget",
SrcVersion: "1.12",
SrcRelease: "10.el6",
},
},
},
want: []types.DetectedVulnerability{
{
PkgName: "seal-wget",
VulnerabilityID: "CVE-2024-10524",
InstalledVersion: "1.12-10.el6",
FixedVersion: "1.12-10.el6+sp999",
DataSource: &dbTypes.DataSource{
ID: "seal",
Name: "Seal Security Database",
URL: "http://vulnfeed.sealsecurity.io/v1/osv/renamed/vulnerabilities.zip",
BaseID: "redhat",
},
},
},
},
{
name: "CentOS scanner",
baseOS: ftypes.CentOS,
fixtures: []string{
"testdata/fixtures/seal.yaml",
"testdata/fixtures/data-source.yaml",
},
args: args{
osVer: "8.9",
pkgs: []ftypes.Package{
{
Name: "seal-wget",
Version: "1.12",
Release: "10.el6",
SrcName: "seal-wget",
SrcVersion: "1.12",
SrcRelease: "10.el6",
},
},
},
want: []types.DetectedVulnerability{
{
PkgName: "seal-wget",
VulnerabilityID: "CVE-2024-10524",
InstalledVersion: "1.12-10.el6",
FixedVersion: "1.12-10.el6+sp999",
DataSource: &dbTypes.DataSource{
ID: "seal",
Name: "Seal Security Database",
URL: "http://vulnfeed.sealsecurity.io/v1/osv/renamed/vulnerabilities.zip",
BaseID: "redhat",
},
},
},
},
{
name: "Get returns an error",
baseOS: ftypes.Alpine,
fixtures: []string{
"testdata/fixtures/invalid.yaml",
"testdata/fixtures/data-source.yaml",
},
args: args{
osVer: "3.20",
pkgs: []ftypes.Package{
{
Name: "seal-jq",
Version: "1.5-12",
SrcName: "seal-jq",
SrcVersion: "1.5-12",
},
},
},
wantErr: "failed to get Seal advisories",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
_ = dbtest.InitDB(t, tt.fixtures)
defer dbtest.Close()
scanner := seal.NewScanner(tt.baseOS)
got, err := scanner.Detect(t.Context(), tt.args.osVer, nil, tt.args.pkgs)
if tt.wantErr != "" {
require.ErrorContains(t, err, tt.wantErr)
return
}
require.NoError(t, err)
require.Equal(t, tt.want, got)
})
}
}

View File

@@ -0,0 +1,25 @@
- bucket: data-source
pairs:
- key: debian 12
value:
ID: "debian"
Name: "Debian Security Tracker"
URL: "https://salsa.debian.org/security-tracker-team/security-tracker"
- key: seal debian
value:
ID: "seal"
Name: "Seal Security Database"
URL: "http://vulnfeed.sealsecurity.io/v1/osv/renamed/vulnerabilities.zip"
BaseID: "debian"
- key: seal alpine
value:
ID: "seal"
Name: "Seal Security Database"
URL: "http://vulnfeed.sealsecurity.io/v1/osv/renamed/vulnerabilities.zip"
BaseID: "alpine"
- key: seal Red Hat 6
value:
ID: "seal"
Name: "Seal Security Database"
URL: "http://vulnfeed.sealsecurity.io/v1/osv/renamed/vulnerabilities.zip"
BaseID: "redhat"

View File

@@ -0,0 +1,9 @@
- bucket: seal alpine
pairs:
- bucket: seal-jq
pairs:
- key: CVE-2020-8177
value:
FixedVersion:
- foo
- bar

View File

@@ -0,0 +1,41 @@
- bucket: debian 12
pairs:
- bucket: openssl
pairs:
- key: CVE-2025-27587
value:
VulnerableVersions:
- "<3.0.16-1~deb12u1"
PatchedVersions:
- "3.0.16-1~deb12u1"
Severity: 1
- bucket: seal debian
pairs:
- bucket: seal-wget
pairs:
- key: CVE-2024-10524
value:
VulnerableVersions:
- ">=1.21-1+deb11u1, <1.21-1+deb11u1+sp999"
PatchedVersions:
- "1.21-1+deb11u1+sp999"
- bucket: seal alpine
pairs:
- bucket: seal-zlib
pairs:
- key: CVE-2023-6992
value:
VulnerableVersions:
- ">=1.2.8-r2, <1.2.8-r25341999"
PatchedVersions:
- "1.2.8-r25341999"
- bucket: seal Red Hat 6
pairs:
- bucket: seal-wget
pairs:
- key: CVE-2024-10524
value:
VulnerableVersions:
- ">=1.12-10.el6, <1.12-10.el6+sp999"
PatchedVersions:
- "1.12-10.el6+sp999"

View File

@@ -3,6 +3,8 @@ package version
import (
apkver "github.com/knqyf263/go-apk-version"
debver "github.com/knqyf263/go-deb-version"
rpmver "github.com/knqyf263/go-rpm-version"
"golang.org/x/xerrors"
)
// Comparer defines the interface for version comparison
@@ -53,13 +55,34 @@ func NewAPKComparer() *APKComparer {
func (c *APKComparer) Compare(version1, version2 string) (int, error) {
v1, err := apkver.NewVersion(version1)
if err != nil {
return 0, err
return 0, xerrors.Errorf("failed to parse apk %q version: %w", version1, err)
}
v2, err := apkver.NewVersion(version2)
if err != nil {
return 0, err
return 0, xerrors.Errorf("failed to parse apk %q version: %w", version2, err)
}
return v1.Compare(v2), nil
}
// RPMComparer implements Comparer for RedHat packages
type RPMComparer struct{}
// NewRPMComparer creates a new RedHat version comparer
func NewRPMComparer() *RPMComparer {
return &RPMComparer{}
}
// Compare compares two RedHat package versions
// Returns:
// - positive if version1 > version2
// - negative if version1 < version2
// - zero if version1 == version2
func (c *RPMComparer) Compare(version1, version2 string) (int, error) {
v1 := rpmver.NewVersion(version1)
v2 := rpmver.NewVersion(version2)
return v1.Compare(v2), nil
}