Consider DLL claims for dependencies of .NET packages from deps.json (#3822)

* consider child dll claims for .NET packages from deps.json

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* make dll claim propagation configurable

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

---------

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
This commit is contained in:
Alex Goodman 2025-04-24 11:59:16 -04:00 committed by GitHub
parent 2dd9d583af
commit df18edf905
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 256 additions and 76 deletions

View File

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

View File

@ -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"`
}
@ -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)`)
}
@ -28,6 +31,7 @@ func defaultDotnetConfig() dotnetConfig {
return dotnetConfig{
DepPackagesMustHaveDLL: def.DepPackagesMustHaveDLL,
DepPackagesMustClaimDLL: def.DepPackagesMustClaimDLL,
PropagateDLLClaimsToParents: def.PropagateDLLClaimsToParents,
RelaxDLLClaimsWhenBundlingDetected: def.RelaxDLLClaimsWhenBundlingDetected,
}
}

View File

@ -2,8 +2,9 @@ package licenses
import (
"context"
"github.com/stretchr/testify/require"
"testing"
"github.com/stretchr/testify/require"
)
func TestSetContextLicenseScanner(t *testing.T) {

View File

@ -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"
@ -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)",
@ -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)",
@ -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)",
@ -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)",
@ -923,11 +917,7 @@ 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)",
@ -935,13 +925,8 @@ func TestCataloger(t *testing.T) {
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)",
@ -1004,6 +989,8 @@ 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)",
@ -1011,10 +998,13 @@ func TestCataloger(t *testing.T) {
"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,
},
@ -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
@ -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 {
@ -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
}

View File

@ -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"`
@ -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,
}
}

View File

@ -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{
@ -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`)
@ -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)

View File

@ -84,6 +84,12 @@ type logicalDepsJSONPackage struct {
Targets *depsTarget
Library *depsLibrary
// anyChildClaimsDLLs is a flag that indicates if any of the children of this package claim a DLL associated with them in the deps.json.
anyChildClaimsDLLs bool
// anyChildHasDLLs is a flag that indicates if any of the children of this package have a DLL associated with them (found on disk).
anyChildHasDLLs bool
// RuntimePathsByRelativeDLLPath is a map of the relative path to the DLL relative to the deps.json file
// to the target path as described in the deps.json target entry under "runtime".
RuntimePathsByRelativeDLLPath map[string]string
@ -107,6 +113,34 @@ type logicalDepsJSONPackage struct {
Executables []logicalPE
}
func (l *logicalDepsJSONPackage) dependencyNameVersions() []string {
if l.Targets == nil {
return nil
}
var results []string
for name, version := range l.Targets.Dependencies {
results = append(results, createNameAndVersion(name, version))
}
return results
}
// ClaimsDLLs indicates if this package has any DLLs associated with it (directly or indirectly with a dependency).
func (l *logicalDepsJSONPackage) ClaimsDLLs(includeChildren bool) bool {
selfClaim := len(l.RuntimePathsByRelativeDLLPath) > 0 || len(l.ResourcePathsByRelativeDLLPath) > 0 || len(l.CompilePathsByRelativeDLLPath) > 0 || len(l.NativePaths.List()) > 0
if !includeChildren {
return selfClaim
}
return selfClaim || l.anyChildClaimsDLLs
}
func (l *logicalDepsJSONPackage) FoundDLLs(includeChildren bool) bool {
selfClaim := len(l.Executables) > 0
if !includeChildren {
return selfClaim
}
return selfClaim || l.anyChildHasDLLs
}
type logicalDepsJSON struct {
Location file.Location
RuntimeTarget runtimeTarget
@ -199,6 +233,8 @@ func getLogicalDepsJSON(deps depsJSON) logicalDepsJSON {
if !bundlingDetected && knownBundlers.Has(name) {
bundlingDetected = true
}
p.anyChildClaimsDLLs = searchForDLLClaims(packageMap, p.dependencyNameVersions()...)
p.anyChildHasDLLs = searchForDLLEvidence(packageMap, p.dependencyNameVersions()...)
packages[p.NameVersion] = *p
}
@ -211,6 +247,47 @@ func getLogicalDepsJSON(deps depsJSON) logicalDepsJSON {
}
}
type visitorFunc func(p *logicalDepsJSONPackage) bool
// searchForDLLEvidence recursively searches for executables found for any of the given nameVersions and children recursively.
func searchForDLLEvidence(packageMap map[string]*logicalDepsJSONPackage, nameVersions ...string) bool {
return traverseDependencies(packageMap, func(p *logicalDepsJSONPackage) bool {
return p.FoundDLLs(true)
}, nameVersions...)
}
// searchForDLLClaims recursively searches for DLL claims in the deps.json for any of the given nameVersions and children recursively.
func searchForDLLClaims(packageMap map[string]*logicalDepsJSONPackage, nameVersions ...string) bool {
return traverseDependencies(packageMap, func(p *logicalDepsJSONPackage) bool {
return p.ClaimsDLLs(true)
}, nameVersions...)
}
func traverseDependencies(packageMap map[string]*logicalDepsJSONPackage, visitor visitorFunc, nameVersions ...string) bool {
if len(nameVersions) == 0 {
return false
}
for _, nameVersion := range nameVersions {
if p, ok := packageMap[nameVersion]; ok {
if visitor(p) {
return true
}
var children []string
for name, version := range p.Targets.Dependencies {
children = append(children, createNameAndVersion(name, version))
}
if traverseDependencies(packageMap, visitor, children...) {
return true
}
}
}
return false
}
var libPathPattern = regexp.MustCompile(`^(?:runtimes/[^/]+/)?lib/net[^/]+/(?P<targetPath>.+)`)
// trimLibPrefix removes prefixes like "lib/net6.0/" or "runtimes/linux-arm/lib/netcoreapp2.2/" from a path.

View File

@ -196,9 +196,8 @@ func extractNameAndVersion(nameVersion string) (name, version string) {
return
}
func createNameAndVersion(name, version string) (nameVersion string) {
nameVersion = fmt.Sprintf("%s/%s", name, version)
return
func createNameAndVersion(name, version string) string {
return fmt.Sprintf("%s/%s", name, version)
}
func packageURL(m pkg.DotnetDepsEntry) string {

View File

@ -6,8 +6,7 @@ COPY src/helloworld.csproj .
RUN dotnet restore -r $RUNTIME
COPY src/*.cs .
RUN dotnet publish -c Release --no-restore -o /app
RUN dotnet publish -c Release -r $RUNTIME --self-contained false -o /app
# note: we're not using a runtime here to make testing easier... so you cannot run this container and expect it to work
# we do this to keep the test assertions small since the don't want to include the several other runtime packages

View File

@ -1,4 +1,6 @@
using System;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Hosting;
namespace HelloWorld
{
@ -6,7 +8,22 @@ namespace HelloWorld
{
public static void Main(string[] args)
{
Console.WriteLine("Hello World!");
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
// configure the HTTP request pipeline
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
}
// enable serving static files (including jQuery)
app.UseStaticFiles();
app.MapGet("/", () => "Hello World!");
Console.WriteLine("Application starting...");
app.Run();
}
}
}

View File

@ -8,17 +8,51 @@
<ItemGroup>
<PackageReference Include="DotNetNuke.Core" Version="9.9.1"/>
<!-- jQuery wraps the javascript library, so no DLLs -->
<PackageReference Include="jQuery" Version="3.4.1" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor" Version="2.2.0" />
<!-- ChakraCore is an open source Javascript engine with a C API -->
<PackageReference Include="Microsoft.ChakraCore" Version="1.11.24">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<!-- Nuget.CommandLine is a command line tool for managing NuGet packages -->
<PackageReference Include="Nuget.CommandLine" Version="6.3.1">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<!-- Umbraco.Cms has dependencies with software, but itself has no DLLs -->
<PackageReference Include="Umbraco.Cms" Version="11.3.0" />
<!-- For code analysis/style checking, only needed during development -->
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.435" PrivateAssets="All" />
<!-- For unit testing, only needed during development -->
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" Condition="'$(Configuration)' == 'Debug'" PrivateAssets="All" />
</ItemGroup>
<!-- create directories if they don't exist -->
<Target Name="CreateWwwroot" BeforeTargets="PrepareForPublish">
<MakeDir Directories="$(PublishDir)wwwroot\lib\jquery" />
</Target>
<!-- explicitly copy jQuery files during publish phase -->
<Target Name="CopyJQueryToPublishOutput" AfterTargets="CreateWwwroot" BeforeTargets="ComputeFilesToPublish">
<ItemGroup>
<!-- find jQuery files in the packages folder -->
<JQueryFiles Include="$(NuGetPackageRoot)\jquery\3.4.1\Content\Scripts\*.js" />
<!-- add these to the publish output -->
<ResolvedFileToPublish Include="@(JQueryFiles)">
<RelativePath>wwwroot\lib\jquery\%(Filename)%(Extension)</RelativePath>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</ResolvedFileToPublish>
</ItemGroup>
</Target>
</Project>