Skip to content

feat(ospkg): add RapidFort curated image scanner#10452

Open
vaibhav-rf wants to merge 9 commits into
aquasecurity:mainfrom
vaibhav-rf:feat/add-rapidfort-scanner
Open

feat(ospkg): add RapidFort curated image scanner#10452
vaibhav-rf wants to merge 9 commits into
aquasecurity:mainfrom
vaibhav-rf:feat/add-rapidfort-scanner

Conversation

@vaibhav-rf

Copy link
Copy Markdown

Summary

Adds a new rapidfort OS package scanner that detects vulnerabilities in
RapidFort curated images using advisory data
ingested from the RapidFort Security Advisories source (companion PR to
trivy-db).

RapidFort curated images are identified by a maintainer label containing
"rapidfort". When detected, the standard OS scanner is bypassed in favour
of this scanner which queries the RapidFort advisory DB directly.

What's added

pkg/detector/ospkg/driver/driver.go

  • New ThirdPartyAware interface: drivers that implement
    IncludesThirdParty() bool receive 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.go

  • ThirdPartyAware check before filterPkgs call.
  • labelProviders []driver.LabelProvider slice — label-based providers are
    tried first in newDriver, before package-pattern providers and the
    standard OS map.
  • rapidfort.Provider registered as the first labelProvider.

pkg/detector/ospkg/rapidfort/

  • provider.goProvider detects RapidFort images by checking the
    maintainer label (case-insensitive). Returns a Scanner for Ubuntu,
    Alpine, and RedHat; nil for unsupported OS families.
  • rapidfort.goScanner implementing Driver and ThirdPartyAware:
    • Selects the correct version comparer (DEB/APK/RPM) and trimmer per OS.
    • Queries the RapidFort advisory DB using the
      "rapidfort {baseOS} {version}" platform key.
    • RedHat/Fedora identifier-aware filtering (isRPMVulnerable):
      filters advisory version ranges by distro identifier (el9, fc39,
      rf) to eliminate cross-distro false positives caused by RPM version
      ordering semantics.
    • rf- package fallback: if no range matches the primary identifier,
      ranges tagged rf are also checked (handles RapidFort build
      versioning inconsistencies).
    • IsSupportedVersion always returns true — RapidFort provides
      advisories including for EOL distributions.
    • IncludesThirdParty returns true — curated images include patched
      third-party packages that must be scanned.
  • export_test.go — exports isVulnerable for unit testing.
  • rapidfort_test.go — 35 test cases across TestScanner_Detect,
    TestProvider, and TestScanner_IsVulnerable.

Supported base OSes (initial) (for RF Curated Images)

  • Ubuntu
  • Alpine
  • Red Hat

Testing

go test ./pkg/detector/ospkg/rapidfort/...

vaibhav-rf and others added 6 commits February 25, 2026 13:23
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
@ravinder-rapidfort

Copy link
Copy Markdown

Hi @knqyf263 @DmitriyLewen

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
aquasecurity/trivy-db#654
#10452

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

@CLAassistant

CLAassistant commented Apr 9, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@ravinder-rapidfort

Copy link
Copy Markdown

Hi @knqyf263 @DmitriyLewen

Hope you're doing well.

Both of us have signed the CLA. Could you please let us know what are the next steps.

Thanks.

@DmitriyLewen

DmitriyLewen commented May 28, 2026

Copy link
Copy Markdown
Contributor

Hello @vaibhav-rf
Can you connect with as using https://trivy.dev/partners?

Regards, Dmitriy

@vaibhav-rf

Copy link
Copy Markdown
Author

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!

Comment on lines +30 to +38
// 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)
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are there any ways to detect that scanned image is RapidFort image? (e.g. os-release file, etc.)?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines +102 to +111
// 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"
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can move this into

// When no identifier could be extracted from the package version string, default to
// the "el" family so that we still match el9/el8/… advisory ranges rather than
// skipping them.
if identifier == "" {
identifier = "el"
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed.

Comment on lines +187 to +204
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)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You use the comparer for OS, but after that you check the version using your own logic.
Can we stick to only one way?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use Get function from trivy-db

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed.

@DmitriyLewen DmitriyLewen left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

var matchingRanges []string
for i, constraintStr := range adv.VulnerableVersions {
if i < len(customIdentifiers) {
if !strings.HasPrefix(customIdentifiers[i], identifier) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. Ranges without an identifier should not be considered during vulnerability detection.
This has been addressed in our advisory.

Comment thread pkg/detector/ospkg/detect.go Outdated
pkgs := d.target.Packages

// Skip third-party filtering when the driver explicitly opts in
if tp, ok := d.driver.(driver.ThirdPartyAware); !ok || !tp.IncludesThirdParty() {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed.

return nil
}
switch osFamily {
case ftypes.Ubuntu, ftypes.Alpine, ftypes.RedHat:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about Debian?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Debian is in progress, and will be out in next release.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean your release?
Should we add Debian support for Trivy right away?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>
@rfJanit

rfJanit commented Jul 13, 2026

Copy link
Copy Markdown

@DmitriyLewen DmitriyLewen left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some of the comments look outdated or redundant. Could you double-check and update them?

Comment on lines +12 to +15
// 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"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can use our set.Set package

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants