feat(license): observe pkg types option in license scanner (#9091)

Co-authored-by: DmitriyLewen <dmitriy.lewen@smartforce.io>
This commit is contained in:
Benedikt Bauer
2025-07-01 11:58:47 +02:00
committed by GitHub
parent c752ccc7ca
commit d44af8cfa2
3 changed files with 113 additions and 20 deletions

View File

@@ -364,6 +364,23 @@ license:
!!! note
`regex` is only used for text licenses and can't be used to configure license IDs.
### Enabling a Subset of Package Types
It's possible to only enable certain package types if you prefer.
You can do so by passing the `--pkg-types` option.
This flag takes a comma-separated list of package types.
Available values:
- os
- Scan OS packages managed by the OS package manager (e.g. `dpkg`, `yum`, `apk`).
- library
- Scan language-specific packages (e.g. packages installed by `pip`, `npm`, or `gem`).
```bash
$ trivy image --pkg-types os ruby:2.4.0
```
[^1]: See the list of supported language files [here](../coverage/language/index.md).
[^2]: Some lock files require additional files (e.g. files from the cache directory) to detect licenses. Check [coverage][coverage] for more information.

View File

@@ -273,13 +273,17 @@ func (s Service) scanLicenses(target types.ScanTarget, options types.ScanOptions
var results types.Results
scanner := licensing.NewScanner(options.LicenseCategories)
// Scan licenses for OS packages
if result := s.scanOSPackageLicenses(target.Packages, scanner); result != nil {
results = append(results, *result)
if slices.Contains(options.PkgTypes, types.PkgTypeOS) {
// Scan licenses for OS packages
if result := s.scanOSPackageLicenses(target.Packages, scanner); result != nil {
results = append(results, *result)
}
}
// Scan licenses for language-specific packages
results = append(results, s.scanApplicationLicenses(target.Applications, scanner)...)
if slices.Contains(options.PkgTypes, types.PkgTypeLibrary) {
// Scan licenses for language-specific packages
results = append(results, s.scanApplicationLicenses(target.Applications, scanner)...)
}
// Scan licenses in file headers or license files
if result := s.scanFileLicenses(target.Licenses, scanner, options); result != nil {

View File

@@ -357,7 +357,7 @@ func TestScanner_Scan(t *testing.T) {
},
},
{
name: "happy path license scanner",
name: "happy path license scanner (exclude language-specific packages)",
args: args{
target: "alpine:latest",
layerIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
@@ -365,6 +365,92 @@ func TestScanner_Scan(t *testing.T) {
PkgRelationships: ftypes.Relationships,
Scanners: types.Scanners{types.LicenseScanner},
LicenseFull: true,
PkgTypes: []string{
types.PkgTypeOS,
},
LicenseCategories: map[ftypes.LicenseCategory][]string{
ftypes.CategoryNotice: {
"MIT",
},
},
},
},
setupCache: func(t *testing.T) cache.Cache {
c := cache.NewMemoryCache()
require.NoError(t, c.PutBlob("sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10", ftypes.BlobInfo{
SchemaVersion: ftypes.BlobJSONSchemaVersion,
Size: 1000,
DiffID: "sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10",
OS: ftypes.OS{
Family: ftypes.Alpine,
Name: "3.11",
},
PackageInfos: []ftypes.PackageInfo{
{
FilePath: "lib/apk/db/installed",
Packages: []ftypes.Package{
muslPkg,
},
},
},
Applications: []ftypes.Application{
{
Type: ftypes.PythonPkg,
FilePath: "",
Packages: []ftypes.Package{
urllib3Pkg,
menuinstPkg,
},
},
},
}))
return c
},
want: types.ScanResponse{
Results: types.Results{
{
Target: "OS Packages",
Class: types.ClassLicense,
Licenses: []types.DetectedLicense{
{
Severity: "LOW",
Category: "notice",
PkgName: muslPkg.Name,
Name: "MIT",
Confidence: 1,
},
},
},
{
Target: "Loose File License(s)",
Class: types.ClassLicenseFile,
},
},
OS: ftypes.OS{
Family: "alpine",
Name: "3.11",
Eosl: false,
},
Layers: ftypes.Layers{
{
Size: 1000,
DiffID: "sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10",
},
},
},
},
{
name: "happy path license scanner (exclude OS packages)",
args: args{
target: "alpine:latest",
layerIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
options: types.ScanOptions{
PkgRelationships: ftypes.Relationships,
Scanners: types.Scanners{types.LicenseScanner},
LicenseFull: true,
PkgTypes: []string{
types.PkgTypeLibrary,
},
LicenseCategories: map[ftypes.LicenseCategory][]string{
ftypes.CategoryNotice: {
"MIT",
@@ -374,7 +460,6 @@ func TestScanner_Scan(t *testing.T) {
},
},
},
fixtures: []string{"testdata/fixtures/happy.yaml"},
setupCache: func(t *testing.T) cache.Cache {
c := cache.NewMemoryCache()
require.NoError(t, c.PutBlob("sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10", ftypes.BlobInfo{
@@ -415,19 +500,6 @@ func TestScanner_Scan(t *testing.T) {
},
want: types.ScanResponse{
Results: types.Results{
{
Target: "OS Packages",
Class: types.ClassLicense,
Licenses: []types.DetectedLicense{
{
Severity: "LOW",
Category: "notice",
PkgName: muslPkg.Name,
Name: "MIT",
Confidence: 1,
},
},
},
{
Target: "/app/go.mod",
Class: types.ClassLicense,