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 cmd/syft/internal/options/catalog.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ func (cfg Catalog) ToPackagesConfig() pkgcataloging.Config {
Dotnet: dotnet.DefaultCatalogerConfig().
WithDepPackagesMustHaveDLL(cfg.Dotnet.DepPackagesMustHaveDLL).
WithDepPackagesMustClaimDLL(cfg.Dotnet.DepPackagesMustClaimDLL).
WithPropagateDLLClaimsToParents(cfg.Dotnet.PropagateDLLClaimsToParents).
WithRelaxDLLClaimsWhenBundlingDetected(cfg.Dotnet.RelaxDLLClaimsWhenBundlingDetected),
Golang: golang.DefaultCatalogerConfig().
WithSearchLocalModCacheLicenses(*multiLevelOption(false, enrichmentEnabled(cfg.Enrich, task.Go, task.Golang), cfg.Golang.SearchLocalModCacheLicenses)).
Expand Down
8 changes: 6 additions & 2 deletions cmd/syft/internal/options/dotnet.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ type dotnetConfig struct {

DepPackagesMustClaimDLL bool `mapstructure:"dep-packages-must-claim-dll" json:"dep-packages-must-claim-dll" yaml:"dep-packages-must-claim-dll"`

PropagateDLLClaimsToParents bool `mapstructure:"propagate-dll-claims-to-parents" json:"propagate-dll-claims-to-parents" yaml:"propagate-dll-claims-to-parents"`

RelaxDLLClaimsWhenBundlingDetected bool `mapstructure:"relax-dll-claims-when-bundling-detected" json:"relax-dll-claims-when-bundling-detected" yaml:"relax-dll-claims-when-bundling-detected"`
}

Expand All @@ -18,8 +20,9 @@ var _ interface {
} = (*dotnetConfig)(nil)

func (o *dotnetConfig) DescribeFields(descriptions clio.FieldDescriptionSet) {
descriptions.Add(&o.DepPackagesMustHaveDLL, `only keep dep.json packages which an executable on disk can be found for`)
descriptions.Add(&o.DepPackagesMustClaimDLL, `only keep dep.json packages which have a runtime/resource DLL claimed in the deps.json targets section (but not necessarily found on disk)`)
descriptions.Add(&o.DepPackagesMustHaveDLL, `only keep dep.json packages which an executable on disk is found. The package is also included if a DLL is found for any child package, even if the package itself does not have a DLL.`)
descriptions.Add(&o.DepPackagesMustClaimDLL, `only keep dep.json packages which have a runtime/resource DLL claimed in the deps.json targets section (but not necessarily found on disk). The package is also included if any child package claims a DLL, even if the package itself does not claim a DLL.`)
descriptions.Add(&o.PropagateDLLClaimsToParents, `treat DLL claims or on-disk evidence for child packages as DLL claims or on-disk evidence for any parent package`)
descriptions.Add(&o.RelaxDLLClaimsWhenBundlingDetected, `show all packages from the deps.json if bundling tooling is present as a dependency (e.g. ILRepack)`)
}

Expand All @@ -28,6 +31,7 @@ func defaultDotnetConfig() dotnetConfig {
return dotnetConfig{
DepPackagesMustHaveDLL: def.DepPackagesMustHaveDLL,
DepPackagesMustClaimDLL: def.DepPackagesMustClaimDLL,
PropagateDLLClaimsToParents: def.PropagateDLLClaimsToParents,
RelaxDLLClaimsWhenBundlingDetected: def.RelaxDLLClaimsWhenBundlingDetected,
}
}
3 changes: 2 additions & 1 deletion internal/licenses/context_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ package licenses

import (
"context"
"github.com/stretchr/testify/require"
"testing"

"github.com/stretchr/testify/require"
)

func TestSetContextLicenseScanner(t *testing.T) {
Expand Down
161 changes: 100 additions & 61 deletions syft/pkg/cataloger/dotnet/cataloger_test.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package dotnet

import (
"strings"
"testing"

"github.com/scylladb/go-set/strset"
"github.com/stretchr/testify/assert"

"github.com/anchore/syft/syft/artifact"
Expand Down Expand Up @@ -119,6 +119,10 @@ func TestCataloger(t *testing.T) {
}
net8AppExpectedDepPkgs = append(net8AppExpectedDepPkgs, net8AppExpectedDepPkgsWithoutUnpairedDlls...)

var net8AppExpectedDepPkgsWithRuntime []string
net8AppExpectedDepPkgsWithRuntime = append(net8AppExpectedDepPkgsWithRuntime, net8AppExpectedDepPkgs...)
net8AppExpectedDepPkgsWithRuntime = append(net8AppExpectedDepPkgsWithRuntime, "Microsoft.NETCore.App.Runtime.linux-x64 @ 8.0.14 (/usr/share/dotnet/shared/Microsoft.NETCore.App/8.0.14/Microsoft.NETCore.App.deps.json)")

// app binaries (always dlls)
net8AppBinaryOnlyPkgs := []string{
"Humanizer @ 2.14.1.48190 (/app/Humanizer.dll)",
Expand Down Expand Up @@ -280,11 +284,17 @@ func TestCataloger(t *testing.T) {
net8AppDepOnlyRelationships = append(net8AppDepOnlyRelationships, net8AppDepOnlyRelationshipsWithoutHumanizer...)
net8AppDepOnlyRelationships = append(net8AppDepOnlyRelationships, humanizerToAppDepsRelationship)

var net8AppDepOnlyRelationshipsWithRuntime []string
net8AppDepOnlyRelationshipsWithRuntime = append(net8AppDepOnlyRelationshipsWithRuntime, net8AppDepOnlyRelationships...)
net8AppDepOnlyRelationshipsWithRuntime = append(net8AppDepOnlyRelationshipsWithRuntime,
"Microsoft.NETCore.App.Runtime.linux-x64 @ 8.0.14 (/usr/share/dotnet/shared/Microsoft.NETCore.App/8.0.14/Microsoft.NETCore.App.deps.json) [dependency-of] dotnetapp @ 1.0.0 (/app/dotnetapp.deps.json)",
)

var net8AppExpectedDepRelationships []string
net8AppExpectedDepRelationships = append(net8AppExpectedDepRelationships, net8AppDepOnlyRelationships...)

var net8AppExpectedDepSelfContainedPkgs []string
net8AppExpectedDepSelfContainedPkgs = append(net8AppExpectedDepSelfContainedPkgs, net8AppExpectedDepPkgsWithoutUnpairedDlls...)
net8AppExpectedDepSelfContainedPkgs = append(net8AppExpectedDepSelfContainedPkgs, net8AppExpectedDepPkgs...)
net8AppExpectedDepSelfContainedPkgs = append(net8AppExpectedDepSelfContainedPkgs,
// add the CLR runtime packages...
"runtimepack.Microsoft.NETCore.App.Runtime.win-x64 @ 8.0.14 (/app/dotnetapp.deps.json)",
Expand All @@ -298,7 +308,7 @@ func TestCataloger(t *testing.T) {
)

var net8AppExpectedDepSelfContainedRelationships []string
net8AppExpectedDepSelfContainedRelationships = append(net8AppExpectedDepSelfContainedRelationships, net8AppDepOnlyRelationshipsWithoutHumanizer...)
net8AppExpectedDepSelfContainedRelationships = append(net8AppExpectedDepSelfContainedRelationships, net8AppDepOnlyRelationships...)
net8AppExpectedDepSelfContainedRelationships = append(net8AppExpectedDepSelfContainedRelationships,
// add the CLR runtime relationships...
"runtimepack.Microsoft.NETCore.App.Runtime.win-x64 @ 8.0.14 (/app/dotnetapp.deps.json) [dependency-of] dotnetapp @ 1.0.0 (/app/dotnetapp.deps.json)",
Expand Down Expand Up @@ -706,36 +716,20 @@ func TestCataloger(t *testing.T) {
assertion: assertAllDepEntriesWithoutExecutables,
},
{
name: "combined cataloger",
fixture: "image-net8-app",
cataloger: NewDotnetDepsBinaryCataloger(DefaultCatalogerConfig()),

// if we don't care about DLL claims in the deps.json, then this is right
//expectedPkgs: net8AppExpectedDepPkgs,
//expectedRels: net8AppExpectedDepRelationships,

// we care about DLL claims in the deps.json, so the main application inherits all relationships to/from humanizer
expectedPkgs: net8AppExpectedDepPkgsWithoutUnpairedDlls,
expectedRels: replaceAll(net8AppDepOnlyRelationshipsWithoutHumanizer, "Humanizer @ 2.14.1", "dotnetapp @ 1.0.0"),

assertion: assertAlmostAllDepEntriesWithExecutables, // important! this is what makes this case different from the previous one... dep entries have attached executables
name: "combined cataloger",
fixture: "image-net8-app",
cataloger: NewDotnetDepsBinaryCataloger(DefaultCatalogerConfig()),
expectedPkgs: net8AppExpectedDepPkgs,
expectedRels: net8AppDepOnlyRelationships,
assertion: assertAlmostAllDepEntriesWithExecutables, // important! this is what makes this case different from the previous one... dep entries have attached executables
},
{
name: "combined cataloger (with runtime)",
fixture: "image-net8-app-with-runtime",
cataloger: NewDotnetDepsBinaryCataloger(DefaultCatalogerConfig()),
expectedPkgs: func() []string {
pkgs := net8AppExpectedDepPkgsWithoutUnpairedDlls
pkgs = append(pkgs, "Microsoft.NETCore.App.Runtime.linux-x64 @ 8.0.14 (/usr/share/dotnet/shared/Microsoft.NETCore.App/8.0.14/Microsoft.NETCore.App.deps.json)")
return pkgs
}(),
expectedRels: func() []string {
x := replaceAll(net8AppDepOnlyRelationshipsWithoutHumanizer, "Humanizer @ 2.14.1", "dotnetapp @ 1.0.0")
// the main application should also have a relationship to the runtime package
x = append(x, "Microsoft.NETCore.App.Runtime.linux-x64 @ 8.0.14 (/usr/share/dotnet/shared/Microsoft.NETCore.App/8.0.14/Microsoft.NETCore.App.deps.json) [dependency-of] dotnetapp @ 1.0.0 (/app/dotnetapp.deps.json)")
return x
}(),
assertion: assertAccurateNetRuntimePackage,
name: "combined cataloger (with runtime)",
fixture: "image-net8-app-with-runtime",
cataloger: NewDotnetDepsBinaryCataloger(DefaultCatalogerConfig()),
expectedPkgs: net8AppExpectedDepPkgsWithRuntime,
expectedRels: net8AppDepOnlyRelationshipsWithRuntime,
assertion: assertAccurateNetRuntimePackage,
},
{
name: "combined cataloger (with runtime, no deps.json anywhere)",
Expand Down Expand Up @@ -923,25 +917,16 @@ func TestCataloger(t *testing.T) {
fixture: "image-net8-app-self-contained",
cataloger: NewDotnetDepsCataloger(),
expectedPkgs: net8AppExpectedDepsSelfContainedPkgs,
expectedRels: func() []string {
x := net8AppExpectedDepSelfContainedRelationships
x = append(x, humanizerToAppDepsRelationship)
return x
}(),
expectedRels: net8AppExpectedDepSelfContainedRelationships,
},
{
name: "combined cataloger (self-contained)",
fixture: "image-net8-app-self-contained",
cataloger: NewDotnetDepsBinaryCataloger(DefaultCatalogerConfig()),
// we care about DLL claims in the deps.json, so the main application inherits all relationships to/from humarizer
expectedPkgs: net8AppExpectedDepSelfContainedPkgs,
expectedRels: func() []string {
x := replaceAll(net8AppExpectedDepSelfContainedRelationships, "Humanizer @ 2.14.1", "dotnetapp @ 1.0.0")
// the main application also has a dependency on the runtime package
x = append(x, "runtimepack.Microsoft.NETCore.App.Runtime.win-x64 @ 8.0.14 (/app/dotnetapp.deps.json) [dependency-of] dotnetapp @ 1.0.0 (/app/dotnetapp.deps.json)")
return x
}(),
assertion: assertAccurateNetRuntimePackage,
expectedRels: net8AppExpectedDepSelfContainedRelationships,
assertion: assertAccurateNetRuntimePackage,
},
{
name: "pe cataloger (self-contained)",
Expand Down Expand Up @@ -1004,17 +989,22 @@ func TestCataloger(t *testing.T) {
fixture: "image-net2-app",
cataloger: NewDotnetDepsBinaryCataloger(DefaultCatalogerConfig()),
expectedPkgs: []string{
"Microsoft.NETCore.App @ 2.2.8 (/usr/share/dotnet/shared/Microsoft.NETCore.App/2.2.8/Microsoft.NETCore.App.deps.json)",
"Microsoft.NETCore.DotNetHostPolicy @ 2.2.8 (/usr/share/dotnet/shared/Microsoft.NETCore.App/2.2.8/Microsoft.NETCore.App.deps.json)",
"Serilog @ 2.10.0 (/app/helloworld.deps.json)",
"Serilog.Sinks.Console @ 4.0.1 (/app/helloworld.deps.json)",
"helloworld @ 1.0.0 (/app/helloworld.deps.json)",
"runtime.linux-x64.Microsoft.NETCore.App @ 2.2.8 (/usr/share/dotnet/shared/Microsoft.NETCore.App/2.2.8/Microsoft.NETCore.App.deps.json)",
"runtime.linux-x64.Microsoft.NETCore.DotNetHostPolicy @ 2.2.8 (/usr/share/dotnet/shared/Microsoft.NETCore.App/2.2.8/Microsoft.NETCore.App.deps.json)", // a compile target reference
},
expectedRels: []string{
"Microsoft.NETCore.DotNetHostPolicy @ 2.2.8 (/usr/share/dotnet/shared/Microsoft.NETCore.App/2.2.8/Microsoft.NETCore.App.deps.json) [dependency-of] Microsoft.NETCore.App @ 2.2.8 (/usr/share/dotnet/shared/Microsoft.NETCore.App/2.2.8/Microsoft.NETCore.App.deps.json)",
"Serilog @ 2.10.0 (/app/helloworld.deps.json) [dependency-of] Serilog.Sinks.Console @ 4.0.1 (/app/helloworld.deps.json)",
"Serilog @ 2.10.0 (/app/helloworld.deps.json) [dependency-of] helloworld @ 1.0.0 (/app/helloworld.deps.json)",
"Serilog.Sinks.Console @ 4.0.1 (/app/helloworld.deps.json) [dependency-of] helloworld @ 1.0.0 (/app/helloworld.deps.json)",
"runtime.linux-x64.Microsoft.NETCore.App @ 2.2.8 (/usr/share/dotnet/shared/Microsoft.NETCore.App/2.2.8/Microsoft.NETCore.App.deps.json) [dependency-of] Microsoft.NETCore.App @ 2.2.8 (/usr/share/dotnet/shared/Microsoft.NETCore.App/2.2.8/Microsoft.NETCore.App.deps.json)",
"runtime.linux-x64.Microsoft.NETCore.App @ 2.2.8 (/usr/share/dotnet/shared/Microsoft.NETCore.App/2.2.8/Microsoft.NETCore.App.deps.json) [dependency-of] helloworld @ 1.0.0 (/app/helloworld.deps.json)",
"runtime.linux-x64.Microsoft.NETCore.DotNetHostPolicy @ 2.2.8 (/usr/share/dotnet/shared/Microsoft.NETCore.App/2.2.8/Microsoft.NETCore.App.deps.json) [dependency-of] Microsoft.NETCore.DotNetHostPolicy @ 2.2.8 (/usr/share/dotnet/shared/Microsoft.NETCore.App/2.2.8/Microsoft.NETCore.App.deps.json)",
},
assertion: assertAccurateNetRuntimePackage,
},
Expand All @@ -1037,6 +1027,25 @@ func TestCataloger(t *testing.T) {
}

func TestDotnetDepsCataloger_regressions(t *testing.T) {

assertPackages := func(mustHave []string, mustNotHave []string) func(t *testing.T, pkgs []pkg.Package, relationships []artifact.Relationship) {
expected := strset.New(mustHave...)
notExpected := strset.New(mustNotHave...)
return func(t *testing.T, pkgs []pkg.Package, relationships []artifact.Relationship) {

for _, p := range pkgs {
expected.Remove(p.Name)
if notExpected.Has(p.Name) {
t.Errorf("unexpected package: %s", p.Name)
}
}
if expected.IsEmpty() {
return
}
t.Errorf("missing packages: %s", expected.List())
}
}

cases := []struct {
name string
fixture string
Expand Down Expand Up @@ -1074,18 +1083,56 @@ func TestDotnetDepsCataloger_regressions(t *testing.T) {
},
},
{
name: "compile target reference",
name: "indirect packages references",
fixture: "image-net8-compile-target",
cataloger: NewDotnetDepsBinaryCataloger(DefaultCatalogerConfig()),
assertion: func(t *testing.T, pkgs []pkg.Package, relationships []artifact.Relationship) {
// ensure we find the DotNetNuke.Core package (which is using the compile target reference)
for _, p := range pkgs {
if p.Name == "DotNetNuke.Core" {
return
}
}
t.Error("expected to find DotNetNuke.Core package")
},
assertion: assertPackages(
[]string{
"DotNetNuke.Core", // uses a compile target reference in the deps.json
"Umbraco.Cms", // this is the parent of other packages which do have DLLs included (even though it does not have any DLLs)
},
[]string{
"StyleCop.Analyzers", // this is a development tool
"Microsoft.NET.Test.Sdk", // this is a development tool
"jQuery", // has no DLLs but has javascript assets
},
),
},
{
name: "not propagating claims",
fixture: "image-net8-compile-target",
cataloger: NewDotnetDepsBinaryCataloger(DefaultCatalogerConfig().WithPropagateDLLClaimsToParents(false)),
assertion: assertPackages(
[]string{
"DotNetNuke.Core", // uses a compile target reference in the deps.json

},
[]string{
"Umbraco.Cms", // this is the parent of other packages which do have DLLs included (even though it does not have any DLLs)
"StyleCop.Analyzers", // this is a development tool
"Microsoft.NET.Test.Sdk", // this is a development tool under the debug configuration (we build the release configuration)
"jQuery", // has no DLLs but has javascript assets -- this is bad behavior (as we want to detect this)
},
),
},
{
name: "not requiring claims finds jquery",
fixture: "image-net8-compile-target",
cataloger: NewDotnetDepsBinaryCataloger(CatalogerConfig{
DepPackagesMustHaveDLL: false,
DepPackagesMustClaimDLL: false,
PropagateDLLClaimsToParents: false,
RelaxDLLClaimsWhenBundlingDetected: false,
}),
assertion: assertPackages(
[]string{
"jQuery", // has no DLLs but has javascript assets
"StyleCop.Analyzers", // this is a development tool -- this is bad behavior (since we should not detect this), but cannot be helped
},
[]string{
"Microsoft.NET.Test.Sdk", // this is a development tool under the debug configuration (we build the release configuration)
},
),
},
}
for _, tt := range cases {
Expand Down Expand Up @@ -1475,11 +1522,3 @@ func extractMatchingPackage(t *testing.T, name string, pkgs []pkg.Package) pkg.P
t.Fatalf("expected to find package %s", name)
return pkg.Package{}
}

func replaceAll(ss []string, find, replace string) []string {
var results []string
for _, s := range ss {
results = append(results, strings.ReplaceAll(s, find, replace))
}
return results
}
9 changes: 9 additions & 0 deletions syft/pkg/cataloger/dotnet/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ type CatalogerConfig struct {
// This does not require such claimed DLLs to exist on disk. The behavior of this
DepPackagesMustClaimDLL bool `mapstructure:"dep-packages-must-claim-dll" json:"dep-packages-must-claim-dll" yaml:"dep-packages-must-claim-dll"`

// PropagateDLLClaimsToParents allows for deps.json packages to be included if any child (transitive) package claims a DLL. This applies to both the claims configuration and evidence-on-disk configurations.
PropagateDLLClaimsToParents bool `mapstructure:"propagate-dll-claims-to-parents" json:"propagate-dll-claims-to-parents" yaml:"propagate-dll-claims-to-parents"`

// RelaxDLLClaimsWhenBundlingDetected will look for indications of IL bundle tooling via deps.json package names
// and, if found (and this config option is enabled), will relax the DepPackagesMustClaimDLL value to `false` only in those cases.
RelaxDLLClaimsWhenBundlingDetected bool `mapstructure:"relax-dll-claims-when-bundling-detected" json:"relax-dll-claims-when-bundling-detected" yaml:"relax-dll-claims-when-bundling-detected"`
Expand All @@ -28,10 +31,16 @@ func (c CatalogerConfig) WithRelaxDLLClaimsWhenBundlingDetected(relax bool) Cata
return c
}

func (c CatalogerConfig) WithPropagateDLLClaimsToParents(propagate bool) CatalogerConfig {
c.PropagateDLLClaimsToParents = propagate
return c
}

func DefaultCatalogerConfig() CatalogerConfig {
return CatalogerConfig{
DepPackagesMustHaveDLL: false,
DepPackagesMustClaimDLL: true,
PropagateDLLClaimsToParents: true,
RelaxDLLClaimsWhenBundlingDetected: true,
}
}
12 changes: 6 additions & 6 deletions syft/pkg/cataloger/dotnet/deps_binary_cataloger.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ func (c depsBinaryCataloger) Catalog(_ context.Context, resolver file.Resolver)
runtimePkgs = append(runtimePkgs, &rtp)
}

// create a relationship from every runtime package to every root package
// create a relationship from every runtime package to every root package...
for _, root := range roots {
for _, runtimePkg := range runtimePkgs {
relationships = append(relationships, artifact.Relationship{
Expand All @@ -122,7 +122,8 @@ func (c depsBinaryCataloger) Catalog(_ context.Context, resolver file.Resolver)
}
}

return pkgs, relationships, unknowns
// in the process of creating root-to-runtime relationships, we may have created duplicate relationships. Use the relationship index to deduplicate.
return pkgs, relationship.NewIndex(relationships...).All(), unknowns
}

var runtimeDLLPathPattern = regexp.MustCompile(`/Microsoft\.NETCore\.App/(?P<version>\d+\.\d+\.\d+)/[^/]+\.dll`)
Expand Down Expand Up @@ -267,15 +268,14 @@ func packagesFromLogicalDepsJSON(doc logicalDepsJSON, config CatalogerConfig) (*
continue
}
lp := doc.PackagesByNameVersion[nameVersion]
if config.DepPackagesMustHaveDLL && len(lp.Executables) == 0 {
if config.DepPackagesMustHaveDLL && !lp.FoundDLLs(config.PropagateDLLClaimsToParents) {
// could not find a paired DLL and the user required this...
skippedDepPkgs[nameVersion] = lp
continue
}

claimsDLLs := len(lp.RuntimePathsByRelativeDLLPath) > 0 || len(lp.ResourcePathsByRelativeDLLPath) > 0 || len(lp.CompilePathsByRelativeDLLPath) > 0 || len(lp.NativePaths.List()) > 0

if config.DepPackagesMustClaimDLL && !claimsDLLs {
// check to see if we should skip this package because it does not claim a DLL (or has not dependency that claims a DLL)
if config.DepPackagesMustClaimDLL && !lp.ClaimsDLLs(config.PropagateDLLClaimsToParents) {
if config.RelaxDLLClaimsWhenBundlingDetected && !doc.BundlingDetected || !config.RelaxDLLClaimsWhenBundlingDetected {
// could not find a runtime or resource path and the user required this...
// and there is no evidence of a bundler in the dependencies (e.g. ILRepack)
Expand Down
Loading
Loading