feat(ospkg): add RapidFort curated image scanner#10452
Conversation
Detect RapidFort curated images via the 'maintainer' label in Docker image
config and route vulnerability detection through the RapidFort advisory DB
(ingested by trivy-db) instead of the standard Ubuntu/Alpine/Debian advisories.
Changes:
- pkg/fanal/types/artifact.go: add Labels field to ArtifactInfo and ImageConfigDetail
- pkg/fanal/artifact/image/image.go: populate Labels from config.Config.Labels
- pkg/fanal/applier/applier.go: pass Labels through to ImageConfigDetail
- pkg/types/scan.go: add ImageLabels field to ScanTarget
- pkg/scan/local/service.go: populate ImageLabels from detail.ImageConfig.Labels
- pkg/detector/ospkg/driver/driver.go: add LabelProvider function type
- pkg/detector/ospkg/detect.go: wire labelProviders, pass ImageLabels to newDriver
- pkg/detector/ospkg/rapidfort/provider.go: detect RapidFort images by maintainer label
- pkg/detector/ospkg/rapidfort/rapidfort.go: query RapidFort advisories via db.Config{}.GetAdvisories
…onstraint fix - Add ThirdPartyAware interface to driver package so scanners can opt into receiving third-party packages (e.g. MariaDB, Docker) unfiltered - Implement IncludesThirdParty on the RapidFort Scanner so curated-image scans cover packages from non-Ubuntu maintainers - Log the resolved platform name in each per-package advisory lookup for easier debugging - Add unit tests, export_test.go, and BoltDB YAML fixtures for the RapidFort scanner
Signed-off-by: Vaibhav Thatai <vaibhav@rapidfort.com>
Signed-off-by: Vaibhav Thatai <vaibhav@rapidfort.com>
…rability filtering (#1) Extend the RapidFort scanner to support RedHat-based images. Uses RPM version comparison and major-only OS version trimming. Introduces identifier-aware filtering (el9, fc39, rf) in isRPMVulnerable to prevent cross-distro false positives from RPM version ordering. Handles rf- prefixed packages and bare .rf version suffixes with a fallback to RapidFort-tagged advisory ranges.
- Reorder rapidfort import in detect.go to maintain alphabetical order - Use make(map[string]string) over composite literal in rapidfort_test.go
|
Hope you're doing well. Could you please let us know what are the next steps to get the following PRs reviewed and tested: aquasecurity/vuln-list-update#424 This integrates OS advisory support for RapidFort curated images, enabling the Trivy scanner to detect vulnerabilities using advisory data sourced from the RapidFort Security Advisories repository. Thank you |
|
Hope you're doing well. Both of us have signed the CLA. Could you please let us know what are the next steps. Thanks. |
|
Hello @vaibhav-rf Regards, Dmitriy |
|
Hello @DmitriyLewen The RapidFort team has already completed and submitted the requested form, as asked in this thread: aquasecurity/trivy-db#654 by @knqyf263 Could you please let us know if any additional information or action is required from our side? Thank you! |
| // isRapidFortImage returns true when the image config labels identify this as a | ||
| // RapidFort curated image (maintainer label contains "rapidfort", case-insensitive). | ||
| func isRapidFortImage(labels map[string]string) bool { | ||
| val, ok := labels[maintainerLabel] | ||
| if !ok { | ||
| return false | ||
| } | ||
| return strings.Contains(strings.ToLower(val), rapidfortIdentifier) | ||
| } |
There was a problem hiding this comment.
Are there any ways to detect that scanned image is RapidFort image? (e.g. os-release file, etc.)?
There was a problem hiding this comment.
No, as of now this is the only reliable way to identify Rapidfort image. Post in future, if there's any other reliable way introduced we will make sure to integrate it here as well.
There was a problem hiding this comment.
Also, we checked some images (Docker Hub: curl, redis-official, apache-official, elasticsearch-official, cassandra-official, grafana-official, couchdb-official).
For all these images, the maintainer field is either empty or set to the upstream vendor.
There was a problem hiding this comment.
In the case of Non-Rapidfort Images like the ones you mentioned, Rapidfort Advisory is not used. Our advisory only works in case of Rapidfort Labelled/Maintained images.
| // Compute the distro identifier for RedHat advisory range filtering. | ||
| // - Standard el/fc packages: identifier embedded in version string | ||
| // (e.g. "7.76.1-26.el9_3.3" → "el9", "7.76.1-26.fc43" → "fc43"). | ||
| // - RapidFort rf packages with a bare .rf/.rfN suffix carry no el/fc tag; | ||
| // use the "rf" identifier to match advisory ranges tagged for RapidFort builds. | ||
| // - Non-RPM packages (ubuntu, alpine): identifier is always "". | ||
| identifier := extractRPMIdentifier(installedVer) | ||
| if identifier == "" && s.baseOS == "redhat" && rfVersionSuffixRe.MatchString(installedVer) { | ||
| identifier = "rf" | ||
| } |
There was a problem hiding this comment.
You can move this into
trivy/pkg/detector/ospkg/rapidfort/rapidfort.go
Lines 241 to 246 in 0903124
| if result, err := s.comparer.Compare(installedVersion, fixedVer); err == nil && result == 0 { | ||
| return false | ||
| } | ||
| } | ||
|
|
||
| // No vulnerable ranges means all versions are considered vulnerable. | ||
| if len(adv.VulnerableVersions) == 0 { | ||
| return true | ||
| } | ||
|
|
||
| // For RedHat/Fedora packages, use identifier-aware RPM vulnerability check to avoid | ||
| // false positives from cross-distro RPM version ordering (e.g. el9 vs fc39 ranges). | ||
| if s.baseOS == "redhat" { | ||
| return s.isRPMVulnerable(ctx, installedVersion, identifier, isRFPackage, adv) | ||
| } | ||
|
|
||
| // Check if installed version lies in any vulnerable range. | ||
| return s.checkConstraints(ctx, installedVersion, adv.VulnerableVersions) |
There was a problem hiding this comment.
You use the comparer for OS, but after that you check the version using your own logic.
Can we stick to only one way?
There was a problem hiding this comment.
The method isRPMVulnerable doesn't really do the version check itself — it just filters out advisory ranges that belong to a different distro, then hands the rest to checkConstraints (same as the non-RedHat path). So both paths end up using the same comparator underneath.
The filter is there for a reason. RPM's version compare treats things like .el9 and .fc39 as plain text segments, so 7.76.1-20.el9 ends up satisfying a range like >= 7.76.1-14.fc39, < 7.76.1-26.fc39 (20 > 14 and 20 < 26). That would give us false positives on any CVE with events for multiple RedHat-family distros - like the rf-python3.11 advisory you pointed out earlier with rh5 + fc19.
The test RedHat: el9 curl not affected by fc39-only advisory catches this.
| // package name rather than the SRPM name. | ||
| // Query the binary name as well and merge, deduping by VulnerabilityID with srcName entries winning. | ||
| if pkg.Name != srcName { | ||
| binAdvisories, err := s.dbc.GetAdvisories(platformName, pkg.Name) |
There was a problem hiding this comment.
Use Get function from trivy-db
548ba21 to
1a285f5
Compare
DmitriyLewen
left a comment
There was a problem hiding this comment.
you need to add/update docs:
- https://github.com/aquasecurity/trivy/blob/b08bf6a0a59785ff72900a4a6873f25a9d7b2921/docs/guide/coverage/others
- https://github.com/aquasecurity/trivy/blob/676709de44639e6c4329e1c69ee34a5e2760555f/docs/guide/coverage/others/index.md
trivy/docs/guide/scanner/vulnerability.md
Line 20 in 676709d
| var matchingRanges []string | ||
| for i, constraintStr := range adv.VulnerableVersions { | ||
| if i < len(customIdentifiers) { | ||
| if !strings.HasPrefix(customIdentifiers[i], identifier) { |
There was a problem hiding this comment.
We always skip vulnerability versions for empty identifier.
e.g.
https://github.com/rapidfort/security-advisories/blob/2f1e7ef711ba2621680bce04725f24b30af9576b/OS/redhat/rf-python3.11_advisory.json#L39-L56
Is this expected?
There was a problem hiding this comment.
Yes. Ranges without an identifier should not be considered during vulnerability detection.
This has been addressed in our advisory.
| pkgs := d.target.Packages | ||
|
|
||
| // Skip third-party filtering when the driver explicitly opts in | ||
| if tp, ok := d.driver.(driver.ThirdPartyAware); !ok || !tp.IncludesThirdParty() { |
There was a problem hiding this comment.
In this case, we completely disable our filtering. That makes sense for third-party packages, but filterPkgs() also filters out the gpg-pubkey package. I think we should move this flag inside filterPkgs() instead.
| return nil | ||
| } | ||
| switch osFamily { | ||
| case ftypes.Ubuntu, ftypes.Alpine, ftypes.RedHat: |
There was a problem hiding this comment.
Debian is in progress, and will be out in next release.
There was a problem hiding this comment.
Do you mean your release?
Should we add Debian support for Trivy right away?
There was a problem hiding this comment.
The code for integration of Debian in Trivy is already done by us, and once the advisory data is published we'll share that PR with you as well.
Integrating Debian for RapidFort images without proper security advisory data wouldn't be right.
* fix(rapidfort): address review comments on the scanner PR
- Use VulnSrcGetter.Get from trivy-db instead of calling
db.Config.GetAdvisories directly, so the bucket-key format stays
owned by trivy-db and cannot drift between the two repos.
- Move the gpg-pubkey drop unconditionally inside filterPkgs and pass
an includeThirdParty flag from Detect. Previously, ThirdPartyAware
drivers (RapidFort) skipped filtering entirely, which let gpg-pubkey
reach the scanner. Now gpg-pubkey is always dropped and third-party
packages are only dropped when the driver opts out.
- Derive the distro identifier inside isRPMVulnerable (its only
consumer) instead of computing it in Detect and threading it through
isVulnerable/isRPMVulnerable. Drop the identifier parameter from
both. Update the two test cases that used a synthetic
installedVersion+identifier="" combo to use a realistic installed
version that naturally yields the "empty → defaults to el" branch.
Signed-off-by: Janit Lodha <janit.lodha@rapidfort.com>
* fix(rapidfort): rename Red Hat bucket key to match refactored trivy-db
The RapidFort bucket wrapper in trivy-db now delegates its Name() to the
underlying OS bucket (Ubuntu/Alpine/RedHat) instead of building the string
by hand. RedHat's Name() returns "Red Hat 9" (capitalized, with a space),
so the RapidFort platform key becomes "rapidfort Red Hat 9" instead of the
previous lowercase "rapidfort redhat 9". Update the scanner test fixtures
to match.
Companion change to the trivy-db bucket refactor addressing the reviewer's
request to reuse the default OS bucket for name consistency.
Signed-off-by: Janit Lodha <janit.lodha@rapidfort.com>
* fix(rapidfort): explain the trivy-db getter pattern in code comments
Address the follow-up review comments on the RapidFort scanner:
- Document why the scanner routes DB queries through trivy-db's
rfvulnsrc.VulnSrcGetter instead of holding a db.Operation directly:
the getter owns the platform-key format ("rapidfort <baseOS> <version>")
so the scanner side never has to know that shape, keeping the two repos
from drifting apart. Added comments at the import (explaining the
aliased name collision) and above the first s.vs.Get call site.
- Rewrite the Scanner.vs field docstring to state the current invariant
(scanner never composes the platform string) instead of framing it as
a change over an older implementation.
- Rewrite the isRPMVulnerable note about the missing .rf baseOS guard —
drop the "guard from the previous call site is redundant" phrasing in
favor of just stating the invariant (isRPMVulnerable is only called
when baseOS == "redhat").
- Rewrite the TestScanner_IsVulnerable header comment: drop the "identifier
is no longer a test input" framing, keep the useful bit that tells a
future test-author how to construct RedHat cases so the derived
identifier matches the scenario.
Signed-off-by: Janit Lodha <janit.lodha@rapidfort.com>
---------
Signed-off-by: Janit Lodha <janit.lodha@rapidfort.com>
|
@DmitriyLewen Kindly review the PR again. I have addressed your review comments.
I am working on this meanwhile. Thanks! |
DmitriyLewen
left a comment
There was a problem hiding this comment.
Some of the comments look outdated or redundant. Could you double-check and update them?
| // rfvulnsrc is trivy-db's RapidFort vulnsrc — aliased because this | ||
| // scanner package is also named "rapidfort". We import it so the scanner | ||
| // can query the DB through the getter it exposes (see Scanner.vs). | ||
| rfvulnsrc "github.com/aquasecurity/trivy-db/pkg/vulnsrc/rapidfort" |
There was a problem hiding this comment.
You can use go mod edit -replace github.com/aquasecurity/trivy-db=https://github.com/vaibhav-rf/trivy-db@<commit_hash>.
| return nil, xerrors.Errorf("failed to get RapidFort advisories for %s: %w", pkg.Name, err) | ||
| } | ||
| if len(binAdvisories) > 0 { | ||
| seen := make(map[string]struct{}, len(advisories)) |
There was a problem hiding this comment.
you can use our set.Set package
Summary
Adds a new
rapidfortOS package scanner that detects vulnerabilities inRapidFort curated images using advisory data
ingested from the RapidFort Security Advisories source (companion PR to
trivy-db).
RapidFort curated images are identified by a
maintainerlabel containing"rapidfort". When detected, the standard OS scanner is bypassed in favourof this scanner which queries the RapidFort advisory DB directly.
What's added
pkg/detector/ospkg/driver/driver.goThirdPartyAwareinterface: drivers that implementIncludesThirdParty() boolreceive the full unfiltered package list.Drivers that don't implement it continue to get third-party packages
stripped (existing behaviour, no regression).
pkg/detector/ospkg/detect.goThirdPartyAwarecheck beforefilterPkgscall.labelProviders []driver.LabelProviderslice — label-based providers aretried first in
newDriver, before package-pattern providers and thestandard OS map.
rapidfort.Providerregistered as the firstlabelProvider.pkg/detector/ospkg/rapidfort/provider.go—Providerdetects RapidFort images by checking themaintainerlabel (case-insensitive). Returns aScannerfor Ubuntu,Alpine, and RedHat;
nilfor unsupported OS families.rapidfort.go—ScannerimplementingDriverandThirdPartyAware:"rapidfort {baseOS} {version}"platform key.isRPMVulnerable):filters advisory version ranges by distro identifier (
el9,fc39,rf) to eliminate cross-distro false positives caused by RPM versionordering semantics.
rf-package fallback: if no range matches the primary identifier,ranges tagged
rfare also checked (handles RapidFort buildversioning inconsistencies).
IsSupportedVersionalways returnstrue— RapidFort providesadvisories including for EOL distributions.
IncludesThirdPartyreturnstrue— curated images include patchedthird-party packages that must be scanned.
export_test.go— exportsisVulnerablefor unit testing.rapidfort_test.go— 35 test cases acrossTestScanner_Detect,TestProvider, andTestScanner_IsVulnerable.Supported base OSes (initial) (for RF Curated Images)
Testing
go test ./pkg/detector/ospkg/rapidfort/...