mirror of
https://github.com/anchore/syft.git
synced 2026-02-12 02:26:42 +01:00
Better represent .NET runtime packages (#3768)
* clean up .NET runtime packages Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * add runtime relationships Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * remove runtime references from binary package name 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:
parent
40dd5d0bbd
commit
c53f2fbad3
@ -121,55 +121,55 @@ func TestCataloger(t *testing.T) {
|
||||
|
||||
// app binaries (always dlls)
|
||||
net8AppBinaryOnlyPkgs := []string{
|
||||
"Humanizer (net6.0) @ 2.14.1.48190 (/app/Humanizer.dll)",
|
||||
"Humanizer (net6.0) @ 2.14.1.48190 (/app/af/Humanizer.resources.dll)",
|
||||
"Humanizer (net6.0) @ 2.14.1.48190 (/app/ar/Humanizer.resources.dll)",
|
||||
"Humanizer (net6.0) @ 2.14.1.48190 (/app/az/Humanizer.resources.dll)",
|
||||
"Humanizer (net6.0) @ 2.14.1.48190 (/app/bg/Humanizer.resources.dll)",
|
||||
"Humanizer (net6.0) @ 2.14.1.48190 (/app/bn-BD/Humanizer.resources.dll)",
|
||||
"Humanizer (net6.0) @ 2.14.1.48190 (/app/cs/Humanizer.resources.dll)",
|
||||
"Humanizer (net6.0) @ 2.14.1.48190 (/app/da/Humanizer.resources.dll)",
|
||||
"Humanizer (net6.0) @ 2.14.1.48190 (/app/de/Humanizer.resources.dll)",
|
||||
"Humanizer (net6.0) @ 2.14.1.48190 (/app/el/Humanizer.resources.dll)",
|
||||
"Humanizer (net6.0) @ 2.14.1.48190 (/app/es/Humanizer.resources.dll)",
|
||||
"Humanizer (net6.0) @ 2.14.1.48190 (/app/fa/Humanizer.resources.dll)",
|
||||
"Humanizer (net6.0) @ 2.14.1.48190 (/app/fi-FI/Humanizer.resources.dll)",
|
||||
"Humanizer (net6.0) @ 2.14.1.48190 (/app/fr-BE/Humanizer.resources.dll)",
|
||||
"Humanizer (net6.0) @ 2.14.1.48190 (/app/fr/Humanizer.resources.dll)",
|
||||
"Humanizer (net6.0) @ 2.14.1.48190 (/app/he/Humanizer.resources.dll)",
|
||||
"Humanizer (net6.0) @ 2.14.1.48190 (/app/hr/Humanizer.resources.dll)",
|
||||
"Humanizer (net6.0) @ 2.14.1.48190 (/app/hu/Humanizer.resources.dll)",
|
||||
"Humanizer (net6.0) @ 2.14.1.48190 (/app/hy/Humanizer.resources.dll)",
|
||||
"Humanizer (net6.0) @ 2.14.1.48190 (/app/id/Humanizer.resources.dll)",
|
||||
"Humanizer (net6.0) @ 2.14.1.48190 (/app/is/Humanizer.resources.dll)",
|
||||
"Humanizer (net6.0) @ 2.14.1.48190 (/app/it/Humanizer.resources.dll)",
|
||||
"Humanizer (net6.0) @ 2.14.1.48190 (/app/ja/Humanizer.resources.dll)",
|
||||
"Humanizer (net6.0) @ 2.14.1.48190 (/app/ku/Humanizer.resources.dll)",
|
||||
"Humanizer (net6.0) @ 2.14.1.48190 (/app/lv/Humanizer.resources.dll)",
|
||||
"Humanizer (net6.0) @ 2.14.1.48190 (/app/nb-NO/Humanizer.resources.dll)",
|
||||
"Humanizer (net6.0) @ 2.14.1.48190 (/app/nb/Humanizer.resources.dll)",
|
||||
"Humanizer (net6.0) @ 2.14.1.48190 (/app/nl/Humanizer.resources.dll)",
|
||||
"Humanizer (net6.0) @ 2.14.1.48190 (/app/pl/Humanizer.resources.dll)",
|
||||
"Humanizer (net6.0) @ 2.14.1.48190 (/app/pt/Humanizer.resources.dll)",
|
||||
"Humanizer (net6.0) @ 2.14.1.48190 (/app/ro/Humanizer.resources.dll)",
|
||||
"Humanizer (net6.0) @ 2.14.1.48190 (/app/ru/Humanizer.resources.dll)",
|
||||
"Humanizer (net6.0) @ 2.14.1.48190 (/app/sk/Humanizer.resources.dll)",
|
||||
"Humanizer (net6.0) @ 2.14.1.48190 (/app/sl/Humanizer.resources.dll)",
|
||||
"Humanizer (net6.0) @ 2.14.1.48190 (/app/sr-Latn/Humanizer.resources.dll)",
|
||||
"Humanizer (net6.0) @ 2.14.1.48190 (/app/sr/Humanizer.resources.dll)",
|
||||
"Humanizer (net6.0) @ 2.14.1.48190 (/app/sv/Humanizer.resources.dll)",
|
||||
"Humanizer (net6.0) @ 2.14.1.48190 (/app/tr/Humanizer.resources.dll)",
|
||||
"Humanizer (net6.0) @ 2.14.1.48190 (/app/uk/Humanizer.resources.dll)",
|
||||
"Humanizer (net6.0) @ 2.14.1.48190 (/app/uz-Cyrl-UZ/Humanizer.resources.dll)",
|
||||
"Humanizer (net6.0) @ 2.14.1.48190 (/app/uz-Latn-UZ/Humanizer.resources.dll)",
|
||||
"Humanizer (net6.0) @ 2.14.1.48190 (/app/vi/Humanizer.resources.dll)",
|
||||
"Humanizer (net6.0) @ 2.14.1.48190 (/app/zh-CN/Humanizer.resources.dll)",
|
||||
"Humanizer (net6.0) @ 2.14.1.48190 (/app/zh-Hans/Humanizer.resources.dll)",
|
||||
"Humanizer (net6.0) @ 2.14.1.48190 (/app/zh-Hant/Humanizer.resources.dll)",
|
||||
"Humanizer (netstandard2.0) @ 2.14.1.48190 (/app/ko-KR/Humanizer.resources.dll)",
|
||||
"Humanizer (netstandard2.0) @ 2.14.1.48190 (/app/ms-MY/Humanizer.resources.dll)",
|
||||
"Humanizer (netstandard2.0) @ 2.14.1.48190 (/app/mt/Humanizer.resources.dll)",
|
||||
"Humanizer (netstandard2.0) @ 2.14.1.48190 (/app/th-TH/Humanizer.resources.dll)",
|
||||
"Humanizer @ 2.14.1.48190 (/app/Humanizer.dll)",
|
||||
"Humanizer @ 2.14.1.48190 (/app/af/Humanizer.resources.dll)",
|
||||
"Humanizer @ 2.14.1.48190 (/app/ar/Humanizer.resources.dll)",
|
||||
"Humanizer @ 2.14.1.48190 (/app/az/Humanizer.resources.dll)",
|
||||
"Humanizer @ 2.14.1.48190 (/app/bg/Humanizer.resources.dll)",
|
||||
"Humanizer @ 2.14.1.48190 (/app/bn-BD/Humanizer.resources.dll)",
|
||||
"Humanizer @ 2.14.1.48190 (/app/cs/Humanizer.resources.dll)",
|
||||
"Humanizer @ 2.14.1.48190 (/app/da/Humanizer.resources.dll)",
|
||||
"Humanizer @ 2.14.1.48190 (/app/de/Humanizer.resources.dll)",
|
||||
"Humanizer @ 2.14.1.48190 (/app/el/Humanizer.resources.dll)",
|
||||
"Humanizer @ 2.14.1.48190 (/app/es/Humanizer.resources.dll)",
|
||||
"Humanizer @ 2.14.1.48190 (/app/fa/Humanizer.resources.dll)",
|
||||
"Humanizer @ 2.14.1.48190 (/app/fi-FI/Humanizer.resources.dll)",
|
||||
"Humanizer @ 2.14.1.48190 (/app/fr-BE/Humanizer.resources.dll)",
|
||||
"Humanizer @ 2.14.1.48190 (/app/fr/Humanizer.resources.dll)",
|
||||
"Humanizer @ 2.14.1.48190 (/app/he/Humanizer.resources.dll)",
|
||||
"Humanizer @ 2.14.1.48190 (/app/hr/Humanizer.resources.dll)",
|
||||
"Humanizer @ 2.14.1.48190 (/app/hu/Humanizer.resources.dll)",
|
||||
"Humanizer @ 2.14.1.48190 (/app/hy/Humanizer.resources.dll)",
|
||||
"Humanizer @ 2.14.1.48190 (/app/id/Humanizer.resources.dll)",
|
||||
"Humanizer @ 2.14.1.48190 (/app/is/Humanizer.resources.dll)",
|
||||
"Humanizer @ 2.14.1.48190 (/app/it/Humanizer.resources.dll)",
|
||||
"Humanizer @ 2.14.1.48190 (/app/ja/Humanizer.resources.dll)",
|
||||
"Humanizer @ 2.14.1.48190 (/app/ku/Humanizer.resources.dll)",
|
||||
"Humanizer @ 2.14.1.48190 (/app/lv/Humanizer.resources.dll)",
|
||||
"Humanizer @ 2.14.1.48190 (/app/nb-NO/Humanizer.resources.dll)",
|
||||
"Humanizer @ 2.14.1.48190 (/app/nb/Humanizer.resources.dll)",
|
||||
"Humanizer @ 2.14.1.48190 (/app/nl/Humanizer.resources.dll)",
|
||||
"Humanizer @ 2.14.1.48190 (/app/pl/Humanizer.resources.dll)",
|
||||
"Humanizer @ 2.14.1.48190 (/app/pt/Humanizer.resources.dll)",
|
||||
"Humanizer @ 2.14.1.48190 (/app/ro/Humanizer.resources.dll)",
|
||||
"Humanizer @ 2.14.1.48190 (/app/ru/Humanizer.resources.dll)",
|
||||
"Humanizer @ 2.14.1.48190 (/app/sk/Humanizer.resources.dll)",
|
||||
"Humanizer @ 2.14.1.48190 (/app/sl/Humanizer.resources.dll)",
|
||||
"Humanizer @ 2.14.1.48190 (/app/sr-Latn/Humanizer.resources.dll)",
|
||||
"Humanizer @ 2.14.1.48190 (/app/sr/Humanizer.resources.dll)",
|
||||
"Humanizer @ 2.14.1.48190 (/app/sv/Humanizer.resources.dll)",
|
||||
"Humanizer @ 2.14.1.48190 (/app/tr/Humanizer.resources.dll)",
|
||||
"Humanizer @ 2.14.1.48190 (/app/uk/Humanizer.resources.dll)",
|
||||
"Humanizer @ 2.14.1.48190 (/app/uz-Cyrl-UZ/Humanizer.resources.dll)",
|
||||
"Humanizer @ 2.14.1.48190 (/app/uz-Latn-UZ/Humanizer.resources.dll)",
|
||||
"Humanizer @ 2.14.1.48190 (/app/vi/Humanizer.resources.dll)",
|
||||
"Humanizer @ 2.14.1.48190 (/app/zh-CN/Humanizer.resources.dll)",
|
||||
"Humanizer @ 2.14.1.48190 (/app/zh-Hans/Humanizer.resources.dll)",
|
||||
"Humanizer @ 2.14.1.48190 (/app/zh-Hant/Humanizer.resources.dll)",
|
||||
"Humanizer @ 2.14.1.48190 (/app/ko-KR/Humanizer.resources.dll)",
|
||||
"Humanizer @ 2.14.1.48190 (/app/ms-MY/Humanizer.resources.dll)",
|
||||
"Humanizer @ 2.14.1.48190 (/app/mt/Humanizer.resources.dll)",
|
||||
"Humanizer @ 2.14.1.48190 (/app/th-TH/Humanizer.resources.dll)",
|
||||
"Json.NET @ 13.0.3.27908 (/app/Newtonsoft.Json.dll)",
|
||||
"dotnetapp @ 1.0.0.0 (/app/dotnetapp.dll)",
|
||||
}
|
||||
@ -287,7 +287,6 @@ func TestCataloger(t *testing.T) {
|
||||
net8AppExpectedDepSelfContainedPkgs = append(net8AppExpectedDepSelfContainedPkgs, net8AppExpectedDepPkgsWithoutUnpairedDlls...)
|
||||
net8AppExpectedDepSelfContainedPkgs = append(net8AppExpectedDepSelfContainedPkgs,
|
||||
// add the CLR runtime packages...
|
||||
".NET Runtime @ 8,0,1425,11118 (/app/coreclr.dll)",
|
||||
"runtimepack.Microsoft.NETCore.App.Runtime.win-x64 @ 8.0.14 (/app/dotnetapp.deps.json)",
|
||||
)
|
||||
|
||||
@ -581,6 +580,10 @@ func TestCataloger(t *testing.T) {
|
||||
assertAllBinaryEntries := func(t *testing.T, pkgs []pkg.Package, relationships []artifact.Relationship) {
|
||||
t.Helper()
|
||||
for _, p := range pkgs {
|
||||
if p.Name == "Microsoft.NETCore.App" {
|
||||
// for the runtime app we created ourselves there is no metadata for
|
||||
continue
|
||||
}
|
||||
// assert that all packages have an executable associated with it
|
||||
m, ok := p.Metadata.(pkg.DotnetPortableExecutableEntry)
|
||||
if !ok {
|
||||
@ -674,6 +677,18 @@ func TestCataloger(t *testing.T) {
|
||||
pkgtest.AssertPackagesEqualIgnoreLayers(t, expected, actual)
|
||||
}
|
||||
|
||||
assertAccurateNetRuntimePackage := func(t *testing.T, pkgs []pkg.Package, relationships []artifact.Relationship) {
|
||||
// the package with the CPE is the runtime package
|
||||
for _, p := range pkgs {
|
||||
if len(p.CPEs) == 0 {
|
||||
continue
|
||||
}
|
||||
assert.Contains(t, p.Name, "Microsoft.NETCore.App")
|
||||
return
|
||||
}
|
||||
t.Error("expected at least one runtime package with a CPE")
|
||||
}
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
fixture string
|
||||
@ -699,12 +714,45 @@ func TestCataloger(t *testing.T) {
|
||||
//expectedPkgs: net8AppExpectedDepPkgs,
|
||||
//expectedRels: net8AppExpectedDepRelationships,
|
||||
|
||||
// we care about DLL claims in the deps.json, so the main application inherits all relationships to/from humarizer
|
||||
// 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 (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, no deps.json anywhere)",
|
||||
fixture: "image-net8-app-with-runtime-nodepsjson",
|
||||
cataloger: NewDotnetDepsBinaryCataloger(DefaultCatalogerConfig()),
|
||||
expectedPkgs: func() []string {
|
||||
// all the same packages we found in "image-net8-app-with-runtime", however we create a runtime package out of all of the DLLs we found instead
|
||||
x := net8AppBinaryOnlyPkgs
|
||||
x = append(x, "Microsoft.NETCore.App @ 8.0.14 (/usr/share/dotnet/shared/Microsoft.NETCore.App/8.0.14/Microsoft.CSharp.dll)")
|
||||
return x
|
||||
}(),
|
||||
// important: no relationships should be found
|
||||
assertion: func(t *testing.T, pkgs []pkg.Package, relationships []artifact.Relationship) {
|
||||
assertAllBinaryEntries(t, pkgs, relationships)
|
||||
assertAccurateNetRuntimePackage(t, pkgs, relationships)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "combined cataloger (require dll pairings)",
|
||||
fixture: "image-net8-app",
|
||||
@ -887,7 +935,13 @@ 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: replaceAll(net8AppExpectedDepSelfContainedRelationships, "Humanizer @ 2.14.1", "dotnetapp @ 1.0.0"),
|
||||
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,
|
||||
},
|
||||
{
|
||||
name: "pe cataloger (self-contained)",
|
||||
@ -945,6 +999,24 @@ func TestCataloger(t *testing.T) {
|
||||
return x
|
||||
}(),
|
||||
},
|
||||
{
|
||||
name: "net2 app, combined cataloger (private assets)",
|
||||
fixture: "image-net2-app",
|
||||
cataloger: NewDotnetDepsBinaryCataloger(DefaultCatalogerConfig()),
|
||||
expectedPkgs: []string{
|
||||
"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)",
|
||||
},
|
||||
expectedRels: []string{
|
||||
"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] helloworld @ 1.0.0 (/app/helloworld.deps.json)",
|
||||
},
|
||||
assertion: assertAccurateNetRuntimePackage,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range cases {
|
||||
|
||||
@ -35,7 +35,7 @@ func (c depsBinaryCataloger) Name() string {
|
||||
return "dotnet-deps-binary-cataloger"
|
||||
}
|
||||
|
||||
func (c depsBinaryCataloger) Catalog(_ context.Context, resolver file.Resolver) ([]pkg.Package, []artifact.Relationship, error) {
|
||||
func (c depsBinaryCataloger) Catalog(_ context.Context, resolver file.Resolver) ([]pkg.Package, []artifact.Relationship, error) { //nolint:funlen
|
||||
depJSONDocs, unknowns, err := findDepsJSON(resolver)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
@ -61,21 +61,86 @@ func (c depsBinaryCataloger) Catalog(_ context.Context, resolver file.Resolver)
|
||||
depDocGroups = append(depDocGroups, remainingDepsJSONs)
|
||||
}
|
||||
|
||||
var roots []*pkg.Package
|
||||
for _, docs := range depDocGroups {
|
||||
for _, doc := range docs {
|
||||
ps, rs := packagesFromLogicalDepsJSON(doc, c.config)
|
||||
rts, ps, rs := packagesFromLogicalDepsJSON(doc, c.config)
|
||||
if rts != nil {
|
||||
roots = append(roots, rts)
|
||||
}
|
||||
pkgs = append(pkgs, ps...)
|
||||
relationships = append(relationships, rs...)
|
||||
}
|
||||
}
|
||||
|
||||
// track existing runtime packages so we don't create duplicates
|
||||
existingRuntimeVersions := strset.New()
|
||||
var runtimePkgs []*pkg.Package
|
||||
for i := range pkgs {
|
||||
p := &pkgs[i]
|
||||
if isRuntime(p.Name) {
|
||||
existingRuntimeVersions.Add(p.Version)
|
||||
runtimePkgs = append(runtimePkgs, p)
|
||||
}
|
||||
}
|
||||
|
||||
runtimes := make(map[string][]file.Location)
|
||||
for _, pe := range remainingPeFiles {
|
||||
runtimeVer, isRuntimePkg := isRuntimePackageLocation(pe.Location)
|
||||
if isRuntimePkg {
|
||||
runtimes[runtimeVer] = append(runtimes[runtimeVer], pe.Location)
|
||||
// we should never catalog runtime DLLs as packages themselves, instead there should be a single logical package
|
||||
continue
|
||||
}
|
||||
pkgs = append(pkgs, newDotnetBinaryPackage(pe.VersionResources, pe.Location))
|
||||
}
|
||||
|
||||
// if we found any runtime DLLs we ignored, then make packages for each version found
|
||||
for version, locs := range runtimes {
|
||||
if len(locs) == 0 || existingRuntimeVersions.Has(version) {
|
||||
continue
|
||||
}
|
||||
rtp := pkg.Package{
|
||||
Name: "Microsoft.NETCore.App",
|
||||
Version: version,
|
||||
Type: pkg.DotnetPkg,
|
||||
CPEs: runtimeCPEs(version),
|
||||
Locations: file.NewLocationSet(locs...),
|
||||
}
|
||||
pkgs = append(pkgs, rtp)
|
||||
runtimePkgs = append(runtimePkgs, &rtp)
|
||||
}
|
||||
|
||||
// create a relationship from every runtime package to every root package
|
||||
for _, root := range roots {
|
||||
for _, runtimePkg := range runtimePkgs {
|
||||
relationships = append(relationships, artifact.Relationship{
|
||||
From: *runtimePkg,
|
||||
To: *root,
|
||||
Type: artifact.DependencyOfRelationship,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return pkgs, relationships, unknowns
|
||||
}
|
||||
|
||||
var runtimeDLLPathPattern = regexp.MustCompile(`/Microsoft\.NETCore\.App/(?P<version>\d+\.\d+\.\d+)/[^/]+\.dll`)
|
||||
|
||||
func isRuntimePackageLocation(loc file.Location) (string, bool) {
|
||||
// we should look at the realpath to see if it is a "**/Microsoft.NETCore.App/\d+.\d+.\d+/*.dll"
|
||||
// and if so treat it as a runtime package
|
||||
if match := runtimeDLLPathPattern.FindStringSubmatch(loc.RealPath); match != nil {
|
||||
versionIndex := runtimeDLLPathPattern.SubexpIndex("version")
|
||||
if versionIndex != -1 {
|
||||
version := match[versionIndex]
|
||||
return version, true
|
||||
}
|
||||
}
|
||||
|
||||
return "", false
|
||||
}
|
||||
|
||||
// partitionPEs pairs PE files with the deps.json based on directory containment.
|
||||
func partitionPEs(depJsons []logicalDepsJSON, peFiles []logicalPE) ([]logicalDepsJSON, []logicalPE, []logicalDepsJSON) {
|
||||
// sort deps.json paths from longest to shortest. This is so we are processing the most specific match first.
|
||||
@ -136,24 +201,20 @@ func attachAssociatedExecutables(dep *logicalDepsJSON, pe logicalPE) bool {
|
||||
p.Executables = append(p.Executables, pe)
|
||||
dep.PackagesByNameVersion[key] = p // update the map with the modified package
|
||||
found = true
|
||||
continue
|
||||
}
|
||||
|
||||
if p.NativePaths.Has(relativeDllPath) {
|
||||
pe.TargetPath = relativeDllPath
|
||||
p.Executables = append(p.Executables, pe)
|
||||
dep.PackagesByNameVersion[key] = p // update the map with the modified package
|
||||
found = true
|
||||
continue
|
||||
}
|
||||
}
|
||||
return found
|
||||
}
|
||||
|
||||
var libPrefixPattern = regexp.MustCompile(`^lib/net[^/]+/`)
|
||||
|
||||
// trimLibPrefix removes prefixes like "lib/net6.0/" from a path.
|
||||
func trimLibPrefix(s string) string {
|
||||
if match := libPrefixPattern.FindString(s); match != "" {
|
||||
parts := strings.Split(s, "/")
|
||||
if len(parts) > 2 {
|
||||
return strings.Join(parts[2:], "/")
|
||||
}
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// isParentOf checks if parentFile's directory is a prefix of childFile's directory.
|
||||
func isParentOf(parentFile, childFile string) bool {
|
||||
parentDir := path.Dir(parentFile)
|
||||
@ -166,7 +227,7 @@ func packagesFromDepsJSON(docs []logicalDepsJSON, config CatalogerConfig) ([]pkg
|
||||
var pkgs []pkg.Package
|
||||
var relationships []artifact.Relationship
|
||||
for _, ldj := range docs {
|
||||
ps, rs := packagesFromLogicalDepsJSON(ldj, config)
|
||||
_, ps, rs := packagesFromLogicalDepsJSON(ldj, config)
|
||||
pkgs = append(pkgs, ps...)
|
||||
relationships = append(relationships, rs...)
|
||||
}
|
||||
@ -174,9 +235,9 @@ func packagesFromDepsJSON(docs []logicalDepsJSON, config CatalogerConfig) ([]pkg
|
||||
}
|
||||
|
||||
// packagesFromLogicalDepsJSON converts a logicalDepsJSON (using the new map type) into catalog packages.
|
||||
func packagesFromLogicalDepsJSON(doc logicalDepsJSON, config CatalogerConfig) ([]pkg.Package, []artifact.Relationship) {
|
||||
func packagesFromLogicalDepsJSON(doc logicalDepsJSON, config CatalogerConfig) (*pkg.Package, []pkg.Package, []artifact.Relationship) {
|
||||
var rootPkg *pkg.Package
|
||||
if rootLpkg, hasRoot := doc.RootPackage(); !hasRoot {
|
||||
if rootLpkg, hasRoot := doc.RootPackage(); hasRoot {
|
||||
rootPkg = newDotnetDepsPackage(rootLpkg, doc.Location)
|
||||
}
|
||||
|
||||
@ -222,7 +283,7 @@ func packagesFromLogicalDepsJSON(doc logicalDepsJSON, config CatalogerConfig) ([
|
||||
}
|
||||
}
|
||||
|
||||
return pkgs, relationshipsFromLogicalDepsJSON(doc, pkgMap, skippedDepPkgs)
|
||||
return rootPkg, pkgs, relationshipsFromLogicalDepsJSON(doc, pkgMap, skippedDepPkgs)
|
||||
}
|
||||
|
||||
// relationshipsFromLogicalDepsJSON creates relationships from a logicalDepsJSON document for only the given syft packages.
|
||||
@ -253,7 +314,7 @@ func relationshipsFromLogicalDepsJSON(doc logicalDepsJSON, pkgMap map[string]pkg
|
||||
}
|
||||
// we have a skipped package, so we need to create a relationship but looking a the nearest
|
||||
// package with an associated PE file for even dependency listed on the skipped package.
|
||||
// Take note that the skipped depedency's dependency could also be skipped, so we need to
|
||||
// Take note that the skipped dependency's dependency could also be skipped, so we need to
|
||||
// do this recursively.
|
||||
depPkgs = findNearestDependencyPackages(skippedDepPkg, pkgMap, skipped, strset.New())
|
||||
} else {
|
||||
|
||||
@ -3,6 +3,7 @@ package dotnet
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/scylladb/go-set/strset"
|
||||
@ -25,6 +26,7 @@ type depsTarget struct {
|
||||
Dependencies map[string]string `json:"dependencies"`
|
||||
Runtime map[string]map[string]string `json:"runtime"`
|
||||
Resources map[string]map[string]string `json:"resources"`
|
||||
Native map[string]map[string]string `json:"native"`
|
||||
}
|
||||
|
||||
type depsLibrary struct {
|
||||
@ -49,6 +51,11 @@ type logicalDepsJSONPackage struct {
|
||||
// to the target path as described in the deps.json target entry under "resource".
|
||||
ResourcePathsByRelativeDLLPath map[string]string
|
||||
|
||||
// NativePathsByRelativeDLLPath 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 "native". These should not have
|
||||
// any runtime references to trim from the front of the path.
|
||||
NativePaths *strset.Set
|
||||
|
||||
// Executables is a list of all the executables that are part of this package. This is populated by the PE cataloger
|
||||
// and not something that is found in the deps.json file. This allows us to associate the PE files with this package
|
||||
// based on the relative path to the DLL.
|
||||
@ -117,35 +124,43 @@ func getLogicalDepsJSON(deps depsJSON) logicalDepsJSON {
|
||||
for _, targets := range deps.Targets {
|
||||
for libName, target := range targets {
|
||||
_, exists := packageMap[libName]
|
||||
if !exists {
|
||||
var lib *depsLibrary
|
||||
l, ok := deps.Libraries[libName]
|
||||
if ok {
|
||||
lib = &l
|
||||
}
|
||||
runtimePaths := make(map[string]string)
|
||||
for path := range target.Runtime {
|
||||
runtimePaths[trimLibPrefix(path)] = path
|
||||
}
|
||||
resourcePaths := make(map[string]string)
|
||||
for path := range target.Resources {
|
||||
trimmedPath := trimLibPrefix(path)
|
||||
if _, exists := resourcePaths[trimmedPath]; exists {
|
||||
continue
|
||||
}
|
||||
resourcePaths[trimmedPath] = path
|
||||
}
|
||||
|
||||
p := &logicalDepsJSONPackage{
|
||||
NameVersion: libName,
|
||||
Library: lib,
|
||||
Targets: &target,
|
||||
RuntimePathsByRelativeDLLPath: runtimePaths,
|
||||
ResourcePathsByRelativeDLLPath: resourcePaths,
|
||||
}
|
||||
packageMap[libName] = p
|
||||
nameVersions.Add(libName)
|
||||
if exists {
|
||||
continue
|
||||
}
|
||||
|
||||
var lib *depsLibrary
|
||||
l, ok := deps.Libraries[libName]
|
||||
if ok {
|
||||
lib = &l
|
||||
}
|
||||
runtimePaths := make(map[string]string)
|
||||
for path := range target.Runtime {
|
||||
runtimePaths[trimLibPrefix(path)] = path
|
||||
}
|
||||
resourcePaths := make(map[string]string)
|
||||
for path := range target.Resources {
|
||||
trimmedPath := trimLibPrefix(path)
|
||||
if _, exists := resourcePaths[trimmedPath]; exists {
|
||||
continue
|
||||
}
|
||||
resourcePaths[trimmedPath] = path
|
||||
}
|
||||
|
||||
nativePaths := strset.New()
|
||||
for path := range target.Native {
|
||||
nativePaths.Add(path)
|
||||
}
|
||||
|
||||
p := &logicalDepsJSONPackage{
|
||||
NameVersion: libName,
|
||||
Library: lib,
|
||||
Targets: &target,
|
||||
RuntimePathsByRelativeDLLPath: runtimePaths,
|
||||
ResourcePathsByRelativeDLLPath: resourcePaths,
|
||||
NativePaths: nativePaths,
|
||||
}
|
||||
packageMap[libName] = p
|
||||
nameVersions.Add(libName)
|
||||
}
|
||||
}
|
||||
packages := make(map[string]logicalDepsJSONPackage)
|
||||
@ -166,3 +181,18 @@ func getLogicalDepsJSON(deps depsJSON) logicalDepsJSON {
|
||||
BundlingDetected: bundlingDetected,
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
// It captures and returns everything after the framework version section using a named capture group.
|
||||
func trimLibPrefix(s string) string {
|
||||
if match := libPathPattern.FindStringSubmatch(s); len(match) > 1 {
|
||||
// Get the index of the named capture group
|
||||
targetPathIndex := libPathPattern.SubexpIndex("targetPath")
|
||||
if targetPathIndex != -1 {
|
||||
return match[targetPathIndex]
|
||||
}
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
73
syft/pkg/cataloger/dotnet/deps_json_test.go
Normal file
73
syft/pkg/cataloger/dotnet/deps_json_test.go
Normal file
@ -0,0 +1,73 @@
|
||||
package dotnet
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestTrimLibPrefix(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "Empty path",
|
||||
input: "",
|
||||
expected: "",
|
||||
},
|
||||
{
|
||||
name: "simple .NET 6.0 path",
|
||||
input: "lib/net6.0/Humanizer.dll",
|
||||
expected: "Humanizer.dll",
|
||||
},
|
||||
{
|
||||
name: "locale-specific resource path",
|
||||
input: "lib/net6.0/af/Humanizer.resources.dll",
|
||||
expected: "af/Humanizer.resources.dll",
|
||||
},
|
||||
{
|
||||
name: "netstandard path",
|
||||
input: "lib/netstandard2.0/Serilog.Sinks.Console.dll",
|
||||
expected: "Serilog.Sinks.Console.dll",
|
||||
},
|
||||
{
|
||||
name: "runtime-specific path",
|
||||
input: "runtimes/linux-arm/lib/netcoreapp2.2/System.Collections.Concurrent.dll",
|
||||
expected: "System.Collections.Concurrent.dll",
|
||||
},
|
||||
{
|
||||
name: "runtime-specific path with locale",
|
||||
input: "runtimes/win/lib/net6.0/fr-ME/re/Microsoft.Data.SqlClient.resources.dll",
|
||||
expected: "fr-ME/re/Microsoft.Data.SqlClient.resources.dll",
|
||||
},
|
||||
{
|
||||
name: "subdirectories",
|
||||
input: "lib/net7.0/Microsoft/Extensions/Logging.dll",
|
||||
expected: "Microsoft/Extensions/Logging.dll",
|
||||
},
|
||||
{
|
||||
name: "doesn't match the pattern",
|
||||
input: "content/styles/main.css",
|
||||
expected: "content/styles/main.css",
|
||||
},
|
||||
{
|
||||
name: "different framework format",
|
||||
input: "lib/net472/Newtonsoft.Json.dll",
|
||||
expected: "Newtonsoft.Json.dll",
|
||||
},
|
||||
{
|
||||
name: "frameworkless lib",
|
||||
input: "lib/Newtonsoft.Json.dll",
|
||||
expected: "lib/Newtonsoft.Json.dll", // should not match our pattern
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
result := trimLibPrefix(tc.input)
|
||||
if result != tc.expected {
|
||||
t.Errorf("trimLibPrefix(%q) = %q; want %q", tc.input, result, tc.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -2,12 +2,15 @@ package dotnet
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/anchore/go-version"
|
||||
"github.com/anchore/packageurl-go"
|
||||
"github.com/anchore/syft/internal/log"
|
||||
"github.com/anchore/syft/syft/cpe"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
)
|
||||
@ -31,6 +34,11 @@ func newDotnetDepsPackage(lp logicalDepsJSONPackage, depsLocation file.Location)
|
||||
|
||||
m := newDotnetDepsEntry(lp)
|
||||
|
||||
var cpes []cpe.CPE
|
||||
if isRuntime(name) {
|
||||
cpes = runtimeCPEs(ver)
|
||||
}
|
||||
|
||||
p := &pkg.Package{
|
||||
Name: name,
|
||||
Version: ver,
|
||||
@ -38,6 +46,7 @@ func newDotnetDepsPackage(lp logicalDepsJSONPackage, depsLocation file.Location)
|
||||
PURL: packageURL(m),
|
||||
Language: pkg.Dotnet,
|
||||
Type: pkg.DotnetPkg,
|
||||
CPEs: cpes,
|
||||
Metadata: m,
|
||||
}
|
||||
|
||||
@ -46,6 +55,69 @@ func newDotnetDepsPackage(lp logicalDepsJSONPackage, depsLocation file.Location)
|
||||
return p
|
||||
}
|
||||
|
||||
func isRuntime(name string) bool {
|
||||
// found in a self-contained net8 app in the deps.json for the application
|
||||
selfContainedRuntimeDependency := strings.HasPrefix(name, "runtimepack.Microsoft.NETCore.App.Runtime")
|
||||
// found in net8 apps in the deps.json for the runtime
|
||||
explicitRuntimeDependency := strings.HasPrefix(name, "Microsoft.NETCore.App.Runtime")
|
||||
// found in net2 apps in the deps.json for the runtime
|
||||
producesARuntime := strings.HasPrefix(name, "runtime") && strings.HasSuffix(name, "Microsoft.NETCore.App")
|
||||
return selfContainedRuntimeDependency || explicitRuntimeDependency || producesARuntime
|
||||
}
|
||||
|
||||
func runtimeCPEs(ver string) []cpe.CPE {
|
||||
// .NET Core Versions
|
||||
// 2016: .NET Core 1.0, cpe:2.3:a:microsoft:dotnet_core:1.0:*:*:*:*:*:*:*
|
||||
// 2016: .NET Core 1.1, cpe:2.3:a:microsoft:dotnet_core:1.1:*:*:*:*:*:*:*
|
||||
// 2017: .NET Core 2.0, cpe:2.3:a:microsoft:dotnet_core:2.0:*:*:*:*:*:*:*
|
||||
// 2018: .NET Core 2.1, cpe:2.3:a:microsoft:dotnet_core:2.1:*:*:*:*:*:*:*
|
||||
// 2018: .NET Core 2.2, cpe:2.3:a:microsoft:dotnet_core:2.2:*:*:*:*:*:*:*
|
||||
// 2019: .NET Core 3.0, cpe:2.3:a:microsoft:dotnet_core:3.0:*:*:*:*:*:*:*
|
||||
// 2019: .NET Core 3.1, cpe:2.3:a:microsoft:dotnet_core:3.1:*:*:*:*:*:*:*
|
||||
|
||||
// Unified .NET Versions
|
||||
// 2020: .NET 5.0, cpe:2.3:a:microsoft:dotnet:5.0:*:*:*:*:*:*:*
|
||||
// 2021: .NET 6.0, cpe:2.3:a:microsoft:dotnet:6.0:*:*:*:*:*:*:*
|
||||
// 2022: .NET 7.0, cpe:2.3:a:microsoft:dotnet:7.0:*:*:*:*:*:*:*
|
||||
// 2023: .NET 8.0, cpe:2.3:a:microsoft:dotnet:8.0:*:*:*:*:*:*:*
|
||||
// 2024: .NET 9.0, cpe:2.3:a:microsoft:dotnet:9.0:*:*:*:*:*:*:*
|
||||
// 2025 ...?
|
||||
|
||||
fields := strings.Split(ver, ".")
|
||||
majorVersion, err := strconv.Atoi(fields[0])
|
||||
if err != nil {
|
||||
log.WithFields("error", err).Tracef("failed to parse .NET major version from %q", ver)
|
||||
return nil
|
||||
}
|
||||
|
||||
var minorVersion int
|
||||
if len(fields) > 1 {
|
||||
minorVersion, err = strconv.Atoi(fields[1])
|
||||
if err != nil {
|
||||
log.WithFields("error", err).Tracef("failed to parse .NET minor version from %q", ver)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
productName := "dotnet"
|
||||
if majorVersion < 5 {
|
||||
productName = "dotnet_core"
|
||||
}
|
||||
|
||||
return []cpe.CPE{
|
||||
{
|
||||
Attributes: cpe.Attributes{
|
||||
Part: "a",
|
||||
Vendor: "microsoft",
|
||||
Product: productName,
|
||||
Version: fmt.Sprintf("%d.%d", majorVersion, minorVersion),
|
||||
},
|
||||
// we didn't find this in the underlying material, but this is the convention in NVD and we are certain this is a runtime package
|
||||
Source: cpe.DeclaredSource,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// newDotnetDepsEntry creates a Dotnet dependency entry using the new logicalDepsJSONPackage.
|
||||
func newDotnetDepsEntry(lp logicalDepsJSONPackage) pkg.DotnetDepsEntry {
|
||||
name, ver := extractNameAndVersion(lp.NameVersion)
|
||||
@ -145,7 +217,14 @@ func packageURL(m pkg.DotnetDepsEntry) string {
|
||||
}
|
||||
|
||||
func newDotnetBinaryPackage(versionResources map[string]string, f file.Location) pkg.Package {
|
||||
name := findNameFromVersionResources(versionResources)
|
||||
// TODO: we may decide to use the runtime information in the metadata, but that is not captured today
|
||||
name, _ := findNameAndRuntimeFromVersionResources(versionResources)
|
||||
|
||||
if name == "" {
|
||||
// older .NET runtime dlls may not have any version resources
|
||||
name = strings.TrimSuffix(strings.TrimSuffix(path.Base(f.RealPath), ".exe"), ".dll")
|
||||
}
|
||||
|
||||
ver := findVersionFromVersionResources(versionResources)
|
||||
|
||||
metadata := newDotnetPortableExecutableEntryFromMap(versionResources)
|
||||
@ -179,26 +258,37 @@ func binaryPackageURL(name, version string) string {
|
||||
).ToString()
|
||||
}
|
||||
|
||||
func findNameFromVersionResources(versionResources map[string]string) string {
|
||||
var binRuntimeSuffixPattern = regexp.MustCompile(`\s*\((?P<runtime>net[^)]*[0-9]+(\.[0-9]+)?)\)$`)
|
||||
|
||||
func findNameAndRuntimeFromVersionResources(versionResources map[string]string) (string, string) {
|
||||
// PE files not authored by Microsoft tend to use ProductName as an identifier.
|
||||
nameFields := []string{"ProductName", "FileDescription", "InternalName", "OriginalFilename"}
|
||||
|
||||
if isMicrosoftVersionResource(versionResources) {
|
||||
// For Microsoft files, prioritize FileDescription.
|
||||
// for Microsoft files, prioritize FileDescription.
|
||||
nameFields = []string{"FileDescription", "InternalName", "OriginalFilename", "ProductName"}
|
||||
}
|
||||
|
||||
var name string
|
||||
for _, field := range nameFields {
|
||||
value := spaceNormalize(versionResources[field])
|
||||
if value == "" {
|
||||
continue
|
||||
}
|
||||
return value
|
||||
name = value
|
||||
break
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
var runtime string
|
||||
// look for indications of the runtime, such as "(net8.0)" or "(netstandard2.2)" suffixes
|
||||
runtimes := binRuntimeSuffixPattern.FindStringSubmatch(name)
|
||||
if len(runtimes) > 1 {
|
||||
runtime = strings.TrimSpace(runtimes[1])
|
||||
name = strings.TrimSpace(strings.TrimSuffix(name, runtimes[0]))
|
||||
}
|
||||
|
||||
return name, runtime
|
||||
}
|
||||
func isMicrosoftVersionResource(versionResources map[string]string) bool {
|
||||
return strings.Contains(strings.ToLower(versionResources["CompanyName"]), "microsoft") ||
|
||||
strings.Contains(strings.ToLower(versionResources["ProductName"]), "microsoft")
|
||||
|
||||
@ -1,10 +1,12 @@
|
||||
package dotnet
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/anchore/syft/syft/cpe"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest"
|
||||
@ -377,3 +379,183 @@ func Test_spaceNormalize(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRuntimeCPEs(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
version string
|
||||
expected []cpe.CPE
|
||||
}{
|
||||
{
|
||||
name: ".NET Core 1.0",
|
||||
version: "1.0",
|
||||
expected: []cpe.CPE{
|
||||
{
|
||||
Attributes: cpe.Attributes{
|
||||
Part: "a",
|
||||
Vendor: "microsoft",
|
||||
Product: "dotnet_core",
|
||||
Version: "1.0",
|
||||
},
|
||||
Source: cpe.DeclaredSource,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: ".NET Core 2.1",
|
||||
version: "2.1",
|
||||
expected: []cpe.CPE{
|
||||
{
|
||||
Attributes: cpe.Attributes{
|
||||
Part: "a",
|
||||
Vendor: "microsoft",
|
||||
Product: "dotnet_core",
|
||||
Version: "2.1",
|
||||
},
|
||||
Source: cpe.DeclaredSource,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: ".NET Core 3.1",
|
||||
version: "3.1",
|
||||
expected: []cpe.CPE{
|
||||
{
|
||||
Attributes: cpe.Attributes{
|
||||
Part: "a",
|
||||
Vendor: "microsoft",
|
||||
Product: "dotnet_core",
|
||||
Version: "3.1",
|
||||
},
|
||||
Source: cpe.DeclaredSource,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: ".NET Core 4.9 (hypothetical)",
|
||||
version: "4.9",
|
||||
expected: []cpe.CPE{
|
||||
{
|
||||
Attributes: cpe.Attributes{
|
||||
Part: "a",
|
||||
Vendor: "microsoft",
|
||||
Product: "dotnet_core",
|
||||
Version: "4.9",
|
||||
},
|
||||
Source: cpe.DeclaredSource,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: ".NET 5.0",
|
||||
version: "5.0",
|
||||
expected: []cpe.CPE{
|
||||
{
|
||||
Attributes: cpe.Attributes{
|
||||
Part: "a",
|
||||
Vendor: "microsoft",
|
||||
Product: "dotnet",
|
||||
Version: "5.0",
|
||||
},
|
||||
Source: cpe.DeclaredSource,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: ".NET 6.0",
|
||||
version: "6.0",
|
||||
expected: []cpe.CPE{
|
||||
{
|
||||
Attributes: cpe.Attributes{
|
||||
Part: "a",
|
||||
Vendor: "microsoft",
|
||||
Product: "dotnet",
|
||||
Version: "6.0",
|
||||
},
|
||||
Source: cpe.DeclaredSource,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: ".NET 8.0",
|
||||
version: "8.0",
|
||||
expected: []cpe.CPE{
|
||||
{
|
||||
Attributes: cpe.Attributes{
|
||||
Part: "a",
|
||||
Vendor: "microsoft",
|
||||
Product: "dotnet",
|
||||
Version: "8.0",
|
||||
},
|
||||
Source: cpe.DeclaredSource,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: ".NET 10.0 (future version)",
|
||||
version: "10.0",
|
||||
expected: []cpe.CPE{
|
||||
{
|
||||
Attributes: cpe.Attributes{
|
||||
Part: "a",
|
||||
Vendor: "microsoft",
|
||||
Product: "dotnet",
|
||||
Version: "10.0",
|
||||
},
|
||||
Source: cpe.DeclaredSource,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Patch version should not be included",
|
||||
version: "6.0.21",
|
||||
expected: []cpe.CPE{
|
||||
{
|
||||
Attributes: cpe.Attributes{
|
||||
Part: "a",
|
||||
Vendor: "microsoft",
|
||||
Product: "dotnet",
|
||||
Version: "6.0",
|
||||
},
|
||||
Source: cpe.DeclaredSource,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Assumed minor version",
|
||||
version: "6",
|
||||
expected: []cpe.CPE{
|
||||
{
|
||||
Attributes: cpe.Attributes{
|
||||
Part: "a",
|
||||
Vendor: "microsoft",
|
||||
Product: "dotnet",
|
||||
Version: "6.0",
|
||||
},
|
||||
Source: cpe.DeclaredSource,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Invalid version format",
|
||||
version: "invalid",
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
name: "Empty version",
|
||||
version: "",
|
||||
expected: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
result := runtimeCPEs(tc.version)
|
||||
|
||||
if !reflect.DeepEqual(result, tc.expected) {
|
||||
t.Errorf("runtimeCPEs(%q) = %+v; want %+v",
|
||||
tc.version, result, tc.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
2
syft/pkg/cataloger/dotnet/test-fixtures/image-net2-app/.gitignore
vendored
Normal file
2
syft/pkg/cataloger/dotnet/test-fixtures/image-net2-app/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
/app
|
||||
/extract.sh
|
||||
@ -0,0 +1,17 @@
|
||||
FROM --platform=linux/amd64 mcr.microsoft.com/dotnet/core/sdk:2.2 AS build
|
||||
ARG RUNTIME=win-x64
|
||||
WORKDIR /src
|
||||
|
||||
COPY src/helloworld.csproj .
|
||||
RUN dotnet restore -r $RUNTIME
|
||||
|
||||
COPY src/*.cs .
|
||||
|
||||
RUN dotnet publish -c Release --no-restore -o /app
|
||||
|
||||
FROM --platform=linux/amd64 mcr.microsoft.com/dotnet/core/runtime:2.2
|
||||
WORKDIR /app
|
||||
COPY --from=build /app .
|
||||
|
||||
# this is a realistic application image since the runtime is with the app
|
||||
ENTRYPOINT ["dotnet", "HelloWorld.dll"]
|
||||
@ -0,0 +1,42 @@
|
||||
using System;
|
||||
using Serilog;
|
||||
|
||||
namespace HelloWorld
|
||||
{
|
||||
/// <summary>
|
||||
/// Main program class!
|
||||
/// </summary>
|
||||
public class Program
|
||||
{
|
||||
/// <summary>
|
||||
/// Entry point for the application!
|
||||
/// </summary>
|
||||
/// <param name="args">Command line arguments!</param>
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
// configure Serilog
|
||||
Log.Logger = new LoggerConfiguration()
|
||||
.MinimumLevel.Information()
|
||||
.WriteTo.Console()
|
||||
.CreateLogger();
|
||||
|
||||
try
|
||||
{
|
||||
Log.Information("Starting up the application");
|
||||
Console.WriteLine("Hello World!");
|
||||
Log.Information("Application completed successfully");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Fatal(ex, "Application terminated unexpectedly");
|
||||
}
|
||||
finally
|
||||
{
|
||||
Log.CloseAndFlush();
|
||||
}
|
||||
|
||||
Console.WriteLine("Press any key to exit...");
|
||||
Console.ReadKey();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,18 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>netcoreapp2.2</TargetFramework>
|
||||
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Serilog" Version="2.10.0" />
|
||||
<PackageReference Include="Serilog.Sinks.Console" Version="4.0.1" />
|
||||
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@ -1 +1,2 @@
|
||||
/app
|
||||
/extract.sh
|
||||
2
syft/pkg/cataloger/dotnet/test-fixtures/image-net8-app-with-runtime-nodepsjson/.gitignore
vendored
Normal file
2
syft/pkg/cataloger/dotnet/test-fixtures/image-net8-app-with-runtime-nodepsjson/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
/app
|
||||
/extract.sh
|
||||
@ -0,0 +1,32 @@
|
||||
# This represents a basic .NET project build where the project dependencies are downloaded and the project is built.
|
||||
# The output is a directory tree of DLLs, a project.lock.json (not used in these tests), a .deps.json file, and
|
||||
# a .runtimeconfig.json file (not used in these tests).
|
||||
# With this deployment strategy there IS a bundled runtime.
|
||||
FROM --platform=linux/amd64 mcr.microsoft.com/dotnet/sdk:8.0-alpine@sha256:7d3a75ca5c8ac4679908ef7a2591b9bc257c62bd530167de32bba105148bb7be AS build
|
||||
ARG RUNTIME=win-x64
|
||||
WORKDIR /src
|
||||
|
||||
# copy csproj and restore as distinct layers
|
||||
COPY src/*.csproj .
|
||||
COPY src/packages.lock.json .
|
||||
RUN dotnet restore -r $RUNTIME --verbosity normal --locked-mode
|
||||
|
||||
# copy and publish app and libraries
|
||||
COPY src/ .
|
||||
RUN dotnet publish -r $RUNTIME --no-restore -o /app
|
||||
|
||||
# remove the deps.json ... important!
|
||||
RUN rm -f /app/dotnetapp.deps.json
|
||||
|
||||
FROM --platform=linux/amd64 mcr.microsoft.com/dotnet/runtime:8.0@sha256:a6fc92280fbf2149cd6846d39c5bf7b9b535184e470aa68ef2847b9a02f6b99e
|
||||
WORKDIR /app
|
||||
COPY --from=build /app .
|
||||
# just a nice to have for later...
|
||||
#COPY --from=build /src/packages.lock.json .
|
||||
|
||||
# this is an odd choice, but possible
|
||||
RUN rm -f /usr/share/dotnet/shared/Microsoft.NETCore.App/8.0.14/Microsoft.NETCore.App.deps.json
|
||||
|
||||
# this is a more realistic application image since the runtime is with the app
|
||||
ENTRYPOINT ["dotnet", "dotnetapp.dll"]
|
||||
|
||||
@ -0,0 +1,31 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Runtime.InteropServices;
|
||||
using static System.Console;
|
||||
|
||||
WriteLine("Runtime and Environment Information");
|
||||
|
||||
// OS and .NET information
|
||||
WriteLine($"{nameof(RuntimeInformation.OSArchitecture)}: {RuntimeInformation.OSArchitecture}");
|
||||
WriteLine($"{nameof(RuntimeInformation.OSDescription)}: {RuntimeInformation.OSDescription}");
|
||||
WriteLine($"{nameof(RuntimeInformation.FrameworkDescription)}: {RuntimeInformation.FrameworkDescription}");
|
||||
WriteLine();
|
||||
|
||||
// Environment information
|
||||
WriteLine($"{nameof(Environment.UserName)}: {Environment.UserName}");
|
||||
WriteLine($"HostName: {Dns.GetHostName()}");
|
||||
WriteLine($"{nameof(Environment.ProcessorCount)}: {Environment.ProcessorCount}");
|
||||
WriteLine();
|
||||
|
||||
// Memory information
|
||||
WriteLine($"Available Memory (GC): {GetInBestUnit(GC.GetGCMemoryInfo().TotalAvailableMemoryBytes)}");
|
||||
|
||||
string GetInBestUnit(long size)
|
||||
{
|
||||
const double Mebi = 1024 * 1024;
|
||||
const double Gibi = Mebi * 1024;
|
||||
|
||||
if (size < Mebi) return $"{size} bytes";
|
||||
if (size < Gibi) return $"{size / Mebi:F} MiB";
|
||||
return $"{size / Gibi:F} GiB";
|
||||
}
|
||||
@ -0,0 +1,15 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="Humanizer" Version="2.14.1" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@ -0,0 +1,459 @@
|
||||
{
|
||||
"version": 1,
|
||||
"dependencies": {
|
||||
"net8.0": {
|
||||
"Humanizer": {
|
||||
"type": "Direct",
|
||||
"requested": "[2.14.1, )",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "/FUTD3cEceAAmJSCPN9+J+VhGwmL/C12jvwlyM1DFXShEMsBzvLzLqSrJ2rb+k/W2znKw7JyflZgZpyE+tI7lA==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core.af": "2.14.1",
|
||||
"Humanizer.Core.ar": "2.14.1",
|
||||
"Humanizer.Core.az": "2.14.1",
|
||||
"Humanizer.Core.bg": "2.14.1",
|
||||
"Humanizer.Core.bn-BD": "2.14.1",
|
||||
"Humanizer.Core.cs": "2.14.1",
|
||||
"Humanizer.Core.da": "2.14.1",
|
||||
"Humanizer.Core.de": "2.14.1",
|
||||
"Humanizer.Core.el": "2.14.1",
|
||||
"Humanizer.Core.es": "2.14.1",
|
||||
"Humanizer.Core.fa": "2.14.1",
|
||||
"Humanizer.Core.fi-FI": "2.14.1",
|
||||
"Humanizer.Core.fr": "2.14.1",
|
||||
"Humanizer.Core.fr-BE": "2.14.1",
|
||||
"Humanizer.Core.he": "2.14.1",
|
||||
"Humanizer.Core.hr": "2.14.1",
|
||||
"Humanizer.Core.hu": "2.14.1",
|
||||
"Humanizer.Core.hy": "2.14.1",
|
||||
"Humanizer.Core.id": "2.14.1",
|
||||
"Humanizer.Core.is": "2.14.1",
|
||||
"Humanizer.Core.it": "2.14.1",
|
||||
"Humanizer.Core.ja": "2.14.1",
|
||||
"Humanizer.Core.ko-KR": "2.14.1",
|
||||
"Humanizer.Core.ku": "2.14.1",
|
||||
"Humanizer.Core.lv": "2.14.1",
|
||||
"Humanizer.Core.ms-MY": "2.14.1",
|
||||
"Humanizer.Core.mt": "2.14.1",
|
||||
"Humanizer.Core.nb": "2.14.1",
|
||||
"Humanizer.Core.nb-NO": "2.14.1",
|
||||
"Humanizer.Core.nl": "2.14.1",
|
||||
"Humanizer.Core.pl": "2.14.1",
|
||||
"Humanizer.Core.pt": "2.14.1",
|
||||
"Humanizer.Core.ro": "2.14.1",
|
||||
"Humanizer.Core.ru": "2.14.1",
|
||||
"Humanizer.Core.sk": "2.14.1",
|
||||
"Humanizer.Core.sl": "2.14.1",
|
||||
"Humanizer.Core.sr": "2.14.1",
|
||||
"Humanizer.Core.sr-Latn": "2.14.1",
|
||||
"Humanizer.Core.sv": "2.14.1",
|
||||
"Humanizer.Core.th-TH": "2.14.1",
|
||||
"Humanizer.Core.tr": "2.14.1",
|
||||
"Humanizer.Core.uk": "2.14.1",
|
||||
"Humanizer.Core.uz-Cyrl-UZ": "2.14.1",
|
||||
"Humanizer.Core.uz-Latn-UZ": "2.14.1",
|
||||
"Humanizer.Core.vi": "2.14.1",
|
||||
"Humanizer.Core.zh-CN": "2.14.1",
|
||||
"Humanizer.Core.zh-Hans": "2.14.1",
|
||||
"Humanizer.Core.zh-Hant": "2.14.1"
|
||||
}
|
||||
},
|
||||
"Newtonsoft.Json": {
|
||||
"type": "Direct",
|
||||
"requested": "[13.0.3, )",
|
||||
"resolved": "13.0.3",
|
||||
"contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ=="
|
||||
},
|
||||
"Humanizer.Core": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "lQKvtaTDOXnoVJ20ibTuSIOf2i0uO0MPbDhd1jm238I+U/2ZnRENj0cktKZhtchBMtCUSRQ5v4xBCUbKNmyVMw=="
|
||||
},
|
||||
"Humanizer.Core.af": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "BoQHyu5le+xxKOw+/AUM7CLXneM/Bh3++0qh1u0+D95n6f9eGt9kNc8LcAHLIOwId7Sd5hiAaaav0Nimj3peNw==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.ar": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "3d1V10LDtmqg5bZjWkA/EkmGFeSfNBcyCH+TiHcHP+HGQQmRq3eBaLcLnOJbVQVn3Z6Ak8GOte4RX4kVCxQlFA==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.az": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "8Z/tp9PdHr/K2Stve2Qs/7uqWPWLUK9D8sOZDNzyv42e20bSoJkHFn7SFoxhmaoVLJwku2jp6P7HuwrfkrP18Q==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.bg": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "S+hIEHicrOcbV2TBtyoPp1AVIGsBzlarOGThhQYCnP6QzEYo/5imtok6LMmhZeTnBFoKhM8yJqRfvJ5yqVQKSQ==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.bn-BD": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "U3bfj90tnUDRKlL1ZFlzhCHoVgpTcqUlTQxjvGCaFKb+734TTu3nkHUWVZltA1E/swTvimo/aXLtkxnLFrc0EQ==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.cs": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "jWrQkiCTy3L2u1T86cFkgijX6k7hoB0pdcFMWYaSZnm6rvG/XJE40tfhYyKhYYgIc1x9P2GO5AC7xXvFnFdqMQ==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.da": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "5o0rJyE/2wWUUphC79rgYDnif/21MKTTx9LIzRVz9cjCIVFrJ2bDyR2gapvI9D6fjoyvD1NAfkN18SHBsO8S9g==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.de": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "9JD/p+rqjb8f5RdZ3aEJqbjMYkbk4VFii2QDnnOdNo6ywEfg/A5YeOQ55CaBJmy7KvV4tOK4+qHJnX/tg3Z54A==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.el": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "Xmv6sTL5mqjOWGGpqY7bvbfK5RngaUHSa8fYDGSLyxY9mGdNbDcasnRnMOvi0SxJS9gAqBCn21Xi90n2SHZbFA==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.es": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "e//OIAeMB7pjBV1HqqI4pM2Bcw3Jwgpyz9G5Fi4c+RJvhqFwztoWxW57PzTnNJE2lbhGGLQZihFZjsbTUsbczA==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.fa": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "nzDOj1x0NgjXMjsQxrET21t1FbdoRYujzbmZoR8u8ou5CBWY1UNca0j6n/PEJR/iUbt4IxstpszRy41wL/BrpA==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.fi-FI": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "Vnxxx4LUhp3AzowYi6lZLAA9Lh8UqkdwRh4IE2qDXiVpbo08rSbokATaEzFS+o+/jCNZBmoyyyph3vgmcSzhhQ==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.fr": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "2p4g0BYNzFS3u9SOIDByp2VClYKO0K1ecDV4BkB9EYdEPWfFODYnF+8CH8LpUrpxL2TuWo2fiFx/4Jcmrnkbpg==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.fr-BE": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "o6R3SerxCRn5Ij8nCihDNMGXlaJ/1AqefteAssgmU2qXYlSAGdhxmnrQAXZUDlE4YWt/XQ6VkNLtH7oMqsSPFQ==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.he": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "FPsAhy7Iw6hb+ZitLgYC26xNcgGAHXb0V823yFAzcyoL5ozM+DCJtYfDPYiOpsJhEZmKFTM9No0jUn1M89WGvg==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.hr": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "chnaD89yOlST142AMkAKLuzRcV5df3yyhDyRU5rypDiqrq2HN8y1UR3h1IicEAEtXLoOEQyjSAkAQ6QuXkn7aw==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.hu": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "hAfnaoF9LTGU/CmFdbnvugN4tIs8ppevVMe3e5bD24+tuKsggMc5hYta9aiydI8JH9JnuVmxvNI4DJee1tK05A==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.hy": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "sVIKxOiSBUb4gStRHo9XwwAg9w7TNvAXbjy176gyTtaTiZkcjr9aCPziUlYAF07oNz6SdwdC2mwJBGgvZ0Sl2g==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.id": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "4Zl3GTvk3a49Ia/WDNQ97eCupjjQRs2iCIZEQdmkiqyaLWttfb+cYXDMGthP42nufUL0SRsvBctN67oSpnXtsg==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.is": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "R67A9j/nNgcWzU7gZy1AJ07ABSLvogRbqOWvfRDn4q6hNdbg/mjGjZBp4qCTPnB2mHQQTCKo3oeCUayBCNIBCw==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.it": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "jYxGeN4XIKHVND02FZ+Woir3CUTyBhLsqxu9iqR/9BISArkMf1Px6i5pRZnvq4fc5Zn1qw71GKKoCaHDJBsLFw==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.ja": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "TM3ablFNoYx4cYJybmRgpDioHpiKSD7q0QtMrmpsqwtiiEsdW5zz/q4PolwAczFnvrKpN6nBXdjnPPKVet93ng==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.ko-KR": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "CtvwvK941k/U0r8PGdEuBEMdW6jv/rBiA9tUhakC7Zd2rA/HCnDcbr1DiNZ+/tRshnhzxy/qwmpY8h4qcAYCtQ==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.ku": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "vHmzXcVMe+LNrF9txpdHzpG7XJX65SiN9GQd/Zkt6gsGIIEeECHrkwCN5Jnlkddw2M/b0HS4SNxdR1GrSn7uCA==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.lv": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "E1/KUVnYBS1bdOTMNDD7LV/jdoZv/fbWTLPtvwdMtSdqLyRTllv6PGM9xVQoFDYlpvVGtEl/09glCojPHw8ffA==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.ms-MY": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "vX8oq9HnYmAF7bek4aGgGFJficHDRTLgp/EOiPv9mBZq0i4SA96qVMYSjJ2YTaxs7Eljqit7pfpE2nmBhY5Fnw==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.mt": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "pEgTBzUI9hzemF7xrIZigl44LidTUhNu4x/P6M9sAwZjkUF0mMkbpxKkaasOql7lLafKrnszs0xFfaxQyzeuZQ==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.nb": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "mbs3m6JJq53ssLqVPxNfqSdTxAcZN3njlG8yhJVx83XVedpTe1ECK9aCa8FKVOXv93Gl+yRHF82Hw9T9LWv2hw==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.nb-NO": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "AsJxrrVYmIMbKDGe8W6Z6//wKv9dhWH7RsTcEHSr4tQt/80pcNvLi0hgD3fqfTtg0tWKtgch2cLf4prorEV+5A==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.nl": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "24b0OUdzJxfoqiHPCtYnR5Y4l/s4Oh7KW7uDp+qX25NMAHLCGog2eRfA7p2kRJp8LvnynwwQxm2p534V9m55wQ==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.pl": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "17mJNYaBssENVZyQHduiq+bvdXS0nhZJGEXtPKoMhKv3GD//WO0mEfd9wjEBsWCSmWI7bjRqhCidxzN+YtJmsg==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.pt": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "8HB8qavcVp2la1GJX6t+G9nDYtylPKzyhxr9LAooIei9MnQvNsjEiIE4QvHoeDZ4weuQ9CsPg1c211XUMVEZ4A==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.ro": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "psXNOcA6R8fSHoQYhpBTtTTYiOk8OBoN3PKCEDgsJKIyeY5xuK81IBdGi77qGZMu/OwBRQjQCBMtPJb0f4O1+A==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.ru": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "zm245xUWrajSN2t9H7BTf84/2APbUkKlUJpcdgsvTdAysr1ag9fi1APu6JEok39RRBXDfNRVZHawQ/U8X0pSvQ==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.sk": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "Ncw24Vf3ioRnbU4MsMFHafkyYi8JOnTqvK741GftlQvAbULBoTz2+e7JByOaasqeSi0KfTXeegJO+5Wk1c0Mbw==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.sl": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "l8sUy4ciAIbVThWNL0atzTS2HWtv8qJrsGWNlqrEKmPwA4SdKolSqnTes9V89fyZTc2Q43jK8fgzVE2C7t009A==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.sr": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "rnNvhpkOrWEymy7R/MiFv7uef8YO5HuXDyvojZ7JpijHWA5dXuVXooCOiA/3E93fYa3pxDuG2OQe4M/olXbQ7w==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.sr-Latn": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "nuy/ykpk974F8ItoQMS00kJPr2dFNjOSjgzCwfysbu7+gjqHmbLcYs7G4kshLwdA4AsVncxp99LYeJgoh1JF5g==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.sv": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "E53+tpAG0RCp+cSSI7TfBPC+NnsEqUuoSV0sU+rWRXWr9MbRWx1+Zj02XMojqjGzHjjOrBFBBio6m74seFl0AA==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.th-TH": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "eSevlJtvs1r4vQarNPfZ2kKDp/xMhuD00tVVzRXkSh1IAZbBJI/x2ydxUOwfK9bEwEp+YjvL1Djx2+kw7ziu7g==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.tr": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "rQ8N+o7yFcFqdbtu1mmbrXFi8TQ+uy+fVH9OPI0CI3Cu1om5hUU/GOMC3hXsTCI6d79y4XX+0HbnD7FT5khegA==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.uk": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "2uEfujwXKNm6bdpukaLtEJD+04uUtQD65nSGCetA1fYNizItEaIBUboNfr3GzJxSMQotNwGVM3+nSn8jTd0VSg==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.uz-Cyrl-UZ": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "TD3ME2sprAvFqk9tkWrvSKx5XxEMlAn1sjk+cYClSWZlIMhQQ2Bp/w0VjX1Kc5oeKjxRAnR7vFcLUFLiZIDk9Q==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.uz-Latn-UZ": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "/kHAoF4g0GahnugZiEMpaHlxb+W6jCEbWIdsq9/I1k48ULOsl/J0pxZj93lXC3omGzVF1BTVIeAtv5fW06Phsg==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.vi": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "rsQNh9rmHMBtnsUUlJbShMsIMGflZtPmrMM6JNDw20nhsvqfrdcoDD8cMnLAbuSovtc3dP+swRmLQzKmXDTVPA==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.zh-CN": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "uH2dWhrgugkCjDmduLdAFO9w1Mo0q07EuvM0QiIZCVm6FMCu/lGv2fpMu4GX+4HLZ6h5T2Pg9FIdDLCPN2a67w==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.zh-Hans": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "WH6IhJ8V1UBG7rZXQk3dZUoP2gsi8a0WkL8xL0sN6WGiv695s8nVcmab9tWz20ySQbuzp0UkSxUQFi5jJHIpOQ==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.zh-Hant": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "VIXB7HCUC34OoaGnO3HJVtSv2/wljPhjV7eKH4+TFPgQdJj2lvHNKY41Dtg0Bphu7X5UaXFR4zrYYyo+GNOjbA==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
}
|
||||
},
|
||||
"net8.0/win-x64": {}
|
||||
}
|
||||
}
|
||||
2
syft/pkg/cataloger/dotnet/test-fixtures/image-net8-app-with-runtime/.gitignore
vendored
Normal file
2
syft/pkg/cataloger/dotnet/test-fixtures/image-net8-app-with-runtime/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
/app
|
||||
/extract.sh
|
||||
@ -0,0 +1,26 @@
|
||||
# This represents a basic .NET project build where the project dependencies are downloaded and the project is built.
|
||||
# The output is a directory tree of DLLs, a project.lock.json (not used in these tests), a .deps.json file, and
|
||||
# a .runtimeconfig.json file (not used in these tests).
|
||||
# With this deployment strategy there IS a bundled runtime.
|
||||
FROM --platform=linux/amd64 mcr.microsoft.com/dotnet/sdk:8.0-alpine@sha256:7d3a75ca5c8ac4679908ef7a2591b9bc257c62bd530167de32bba105148bb7be AS build
|
||||
ARG RUNTIME=win-x64
|
||||
WORKDIR /src
|
||||
|
||||
# copy csproj and restore as distinct layers
|
||||
COPY src/*.csproj .
|
||||
COPY src/packages.lock.json .
|
||||
RUN dotnet restore -r $RUNTIME --verbosity normal --locked-mode
|
||||
|
||||
# copy and publish app and libraries
|
||||
COPY src/ .
|
||||
RUN dotnet publish -r $RUNTIME --no-restore -o /app
|
||||
|
||||
FROM --platform=linux/amd64 mcr.microsoft.com/dotnet/runtime:8.0@sha256:a6fc92280fbf2149cd6846d39c5bf7b9b535184e470aa68ef2847b9a02f6b99e
|
||||
WORKDIR /app
|
||||
COPY --from=build /app .
|
||||
# just a nice to have for later...
|
||||
#COPY --from=build /src/packages.lock.json .
|
||||
|
||||
# this is a more realistic application image since the runtime is with the app
|
||||
ENTRYPOINT ["dotnet", "dotnetapp.dll"]
|
||||
|
||||
@ -0,0 +1,31 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Runtime.InteropServices;
|
||||
using static System.Console;
|
||||
|
||||
WriteLine("Runtime and Environment Information");
|
||||
|
||||
// OS and .NET information
|
||||
WriteLine($"{nameof(RuntimeInformation.OSArchitecture)}: {RuntimeInformation.OSArchitecture}");
|
||||
WriteLine($"{nameof(RuntimeInformation.OSDescription)}: {RuntimeInformation.OSDescription}");
|
||||
WriteLine($"{nameof(RuntimeInformation.FrameworkDescription)}: {RuntimeInformation.FrameworkDescription}");
|
||||
WriteLine();
|
||||
|
||||
// Environment information
|
||||
WriteLine($"{nameof(Environment.UserName)}: {Environment.UserName}");
|
||||
WriteLine($"HostName: {Dns.GetHostName()}");
|
||||
WriteLine($"{nameof(Environment.ProcessorCount)}: {Environment.ProcessorCount}");
|
||||
WriteLine();
|
||||
|
||||
// Memory information
|
||||
WriteLine($"Available Memory (GC): {GetInBestUnit(GC.GetGCMemoryInfo().TotalAvailableMemoryBytes)}");
|
||||
|
||||
string GetInBestUnit(long size)
|
||||
{
|
||||
const double Mebi = 1024 * 1024;
|
||||
const double Gibi = Mebi * 1024;
|
||||
|
||||
if (size < Mebi) return $"{size} bytes";
|
||||
if (size < Gibi) return $"{size / Mebi:F} MiB";
|
||||
return $"{size / Gibi:F} GiB";
|
||||
}
|
||||
@ -0,0 +1,15 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="Humanizer" Version="2.14.1" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@ -0,0 +1,459 @@
|
||||
{
|
||||
"version": 1,
|
||||
"dependencies": {
|
||||
"net8.0": {
|
||||
"Humanizer": {
|
||||
"type": "Direct",
|
||||
"requested": "[2.14.1, )",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "/FUTD3cEceAAmJSCPN9+J+VhGwmL/C12jvwlyM1DFXShEMsBzvLzLqSrJ2rb+k/W2znKw7JyflZgZpyE+tI7lA==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core.af": "2.14.1",
|
||||
"Humanizer.Core.ar": "2.14.1",
|
||||
"Humanizer.Core.az": "2.14.1",
|
||||
"Humanizer.Core.bg": "2.14.1",
|
||||
"Humanizer.Core.bn-BD": "2.14.1",
|
||||
"Humanizer.Core.cs": "2.14.1",
|
||||
"Humanizer.Core.da": "2.14.1",
|
||||
"Humanizer.Core.de": "2.14.1",
|
||||
"Humanizer.Core.el": "2.14.1",
|
||||
"Humanizer.Core.es": "2.14.1",
|
||||
"Humanizer.Core.fa": "2.14.1",
|
||||
"Humanizer.Core.fi-FI": "2.14.1",
|
||||
"Humanizer.Core.fr": "2.14.1",
|
||||
"Humanizer.Core.fr-BE": "2.14.1",
|
||||
"Humanizer.Core.he": "2.14.1",
|
||||
"Humanizer.Core.hr": "2.14.1",
|
||||
"Humanizer.Core.hu": "2.14.1",
|
||||
"Humanizer.Core.hy": "2.14.1",
|
||||
"Humanizer.Core.id": "2.14.1",
|
||||
"Humanizer.Core.is": "2.14.1",
|
||||
"Humanizer.Core.it": "2.14.1",
|
||||
"Humanizer.Core.ja": "2.14.1",
|
||||
"Humanizer.Core.ko-KR": "2.14.1",
|
||||
"Humanizer.Core.ku": "2.14.1",
|
||||
"Humanizer.Core.lv": "2.14.1",
|
||||
"Humanizer.Core.ms-MY": "2.14.1",
|
||||
"Humanizer.Core.mt": "2.14.1",
|
||||
"Humanizer.Core.nb": "2.14.1",
|
||||
"Humanizer.Core.nb-NO": "2.14.1",
|
||||
"Humanizer.Core.nl": "2.14.1",
|
||||
"Humanizer.Core.pl": "2.14.1",
|
||||
"Humanizer.Core.pt": "2.14.1",
|
||||
"Humanizer.Core.ro": "2.14.1",
|
||||
"Humanizer.Core.ru": "2.14.1",
|
||||
"Humanizer.Core.sk": "2.14.1",
|
||||
"Humanizer.Core.sl": "2.14.1",
|
||||
"Humanizer.Core.sr": "2.14.1",
|
||||
"Humanizer.Core.sr-Latn": "2.14.1",
|
||||
"Humanizer.Core.sv": "2.14.1",
|
||||
"Humanizer.Core.th-TH": "2.14.1",
|
||||
"Humanizer.Core.tr": "2.14.1",
|
||||
"Humanizer.Core.uk": "2.14.1",
|
||||
"Humanizer.Core.uz-Cyrl-UZ": "2.14.1",
|
||||
"Humanizer.Core.uz-Latn-UZ": "2.14.1",
|
||||
"Humanizer.Core.vi": "2.14.1",
|
||||
"Humanizer.Core.zh-CN": "2.14.1",
|
||||
"Humanizer.Core.zh-Hans": "2.14.1",
|
||||
"Humanizer.Core.zh-Hant": "2.14.1"
|
||||
}
|
||||
},
|
||||
"Newtonsoft.Json": {
|
||||
"type": "Direct",
|
||||
"requested": "[13.0.3, )",
|
||||
"resolved": "13.0.3",
|
||||
"contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ=="
|
||||
},
|
||||
"Humanizer.Core": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "lQKvtaTDOXnoVJ20ibTuSIOf2i0uO0MPbDhd1jm238I+U/2ZnRENj0cktKZhtchBMtCUSRQ5v4xBCUbKNmyVMw=="
|
||||
},
|
||||
"Humanizer.Core.af": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "BoQHyu5le+xxKOw+/AUM7CLXneM/Bh3++0qh1u0+D95n6f9eGt9kNc8LcAHLIOwId7Sd5hiAaaav0Nimj3peNw==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.ar": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "3d1V10LDtmqg5bZjWkA/EkmGFeSfNBcyCH+TiHcHP+HGQQmRq3eBaLcLnOJbVQVn3Z6Ak8GOte4RX4kVCxQlFA==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.az": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "8Z/tp9PdHr/K2Stve2Qs/7uqWPWLUK9D8sOZDNzyv42e20bSoJkHFn7SFoxhmaoVLJwku2jp6P7HuwrfkrP18Q==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.bg": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "S+hIEHicrOcbV2TBtyoPp1AVIGsBzlarOGThhQYCnP6QzEYo/5imtok6LMmhZeTnBFoKhM8yJqRfvJ5yqVQKSQ==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.bn-BD": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "U3bfj90tnUDRKlL1ZFlzhCHoVgpTcqUlTQxjvGCaFKb+734TTu3nkHUWVZltA1E/swTvimo/aXLtkxnLFrc0EQ==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.cs": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "jWrQkiCTy3L2u1T86cFkgijX6k7hoB0pdcFMWYaSZnm6rvG/XJE40tfhYyKhYYgIc1x9P2GO5AC7xXvFnFdqMQ==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.da": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "5o0rJyE/2wWUUphC79rgYDnif/21MKTTx9LIzRVz9cjCIVFrJ2bDyR2gapvI9D6fjoyvD1NAfkN18SHBsO8S9g==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.de": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "9JD/p+rqjb8f5RdZ3aEJqbjMYkbk4VFii2QDnnOdNo6ywEfg/A5YeOQ55CaBJmy7KvV4tOK4+qHJnX/tg3Z54A==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.el": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "Xmv6sTL5mqjOWGGpqY7bvbfK5RngaUHSa8fYDGSLyxY9mGdNbDcasnRnMOvi0SxJS9gAqBCn21Xi90n2SHZbFA==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.es": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "e//OIAeMB7pjBV1HqqI4pM2Bcw3Jwgpyz9G5Fi4c+RJvhqFwztoWxW57PzTnNJE2lbhGGLQZihFZjsbTUsbczA==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.fa": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "nzDOj1x0NgjXMjsQxrET21t1FbdoRYujzbmZoR8u8ou5CBWY1UNca0j6n/PEJR/iUbt4IxstpszRy41wL/BrpA==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.fi-FI": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "Vnxxx4LUhp3AzowYi6lZLAA9Lh8UqkdwRh4IE2qDXiVpbo08rSbokATaEzFS+o+/jCNZBmoyyyph3vgmcSzhhQ==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.fr": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "2p4g0BYNzFS3u9SOIDByp2VClYKO0K1ecDV4BkB9EYdEPWfFODYnF+8CH8LpUrpxL2TuWo2fiFx/4Jcmrnkbpg==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.fr-BE": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "o6R3SerxCRn5Ij8nCihDNMGXlaJ/1AqefteAssgmU2qXYlSAGdhxmnrQAXZUDlE4YWt/XQ6VkNLtH7oMqsSPFQ==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.he": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "FPsAhy7Iw6hb+ZitLgYC26xNcgGAHXb0V823yFAzcyoL5ozM+DCJtYfDPYiOpsJhEZmKFTM9No0jUn1M89WGvg==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.hr": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "chnaD89yOlST142AMkAKLuzRcV5df3yyhDyRU5rypDiqrq2HN8y1UR3h1IicEAEtXLoOEQyjSAkAQ6QuXkn7aw==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.hu": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "hAfnaoF9LTGU/CmFdbnvugN4tIs8ppevVMe3e5bD24+tuKsggMc5hYta9aiydI8JH9JnuVmxvNI4DJee1tK05A==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.hy": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "sVIKxOiSBUb4gStRHo9XwwAg9w7TNvAXbjy176gyTtaTiZkcjr9aCPziUlYAF07oNz6SdwdC2mwJBGgvZ0Sl2g==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.id": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "4Zl3GTvk3a49Ia/WDNQ97eCupjjQRs2iCIZEQdmkiqyaLWttfb+cYXDMGthP42nufUL0SRsvBctN67oSpnXtsg==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.is": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "R67A9j/nNgcWzU7gZy1AJ07ABSLvogRbqOWvfRDn4q6hNdbg/mjGjZBp4qCTPnB2mHQQTCKo3oeCUayBCNIBCw==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.it": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "jYxGeN4XIKHVND02FZ+Woir3CUTyBhLsqxu9iqR/9BISArkMf1Px6i5pRZnvq4fc5Zn1qw71GKKoCaHDJBsLFw==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.ja": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "TM3ablFNoYx4cYJybmRgpDioHpiKSD7q0QtMrmpsqwtiiEsdW5zz/q4PolwAczFnvrKpN6nBXdjnPPKVet93ng==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.ko-KR": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "CtvwvK941k/U0r8PGdEuBEMdW6jv/rBiA9tUhakC7Zd2rA/HCnDcbr1DiNZ+/tRshnhzxy/qwmpY8h4qcAYCtQ==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.ku": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "vHmzXcVMe+LNrF9txpdHzpG7XJX65SiN9GQd/Zkt6gsGIIEeECHrkwCN5Jnlkddw2M/b0HS4SNxdR1GrSn7uCA==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.lv": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "E1/KUVnYBS1bdOTMNDD7LV/jdoZv/fbWTLPtvwdMtSdqLyRTllv6PGM9xVQoFDYlpvVGtEl/09glCojPHw8ffA==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.ms-MY": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "vX8oq9HnYmAF7bek4aGgGFJficHDRTLgp/EOiPv9mBZq0i4SA96qVMYSjJ2YTaxs7Eljqit7pfpE2nmBhY5Fnw==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.mt": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "pEgTBzUI9hzemF7xrIZigl44LidTUhNu4x/P6M9sAwZjkUF0mMkbpxKkaasOql7lLafKrnszs0xFfaxQyzeuZQ==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.nb": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "mbs3m6JJq53ssLqVPxNfqSdTxAcZN3njlG8yhJVx83XVedpTe1ECK9aCa8FKVOXv93Gl+yRHF82Hw9T9LWv2hw==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.nb-NO": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "AsJxrrVYmIMbKDGe8W6Z6//wKv9dhWH7RsTcEHSr4tQt/80pcNvLi0hgD3fqfTtg0tWKtgch2cLf4prorEV+5A==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.nl": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "24b0OUdzJxfoqiHPCtYnR5Y4l/s4Oh7KW7uDp+qX25NMAHLCGog2eRfA7p2kRJp8LvnynwwQxm2p534V9m55wQ==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.pl": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "17mJNYaBssENVZyQHduiq+bvdXS0nhZJGEXtPKoMhKv3GD//WO0mEfd9wjEBsWCSmWI7bjRqhCidxzN+YtJmsg==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.pt": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "8HB8qavcVp2la1GJX6t+G9nDYtylPKzyhxr9LAooIei9MnQvNsjEiIE4QvHoeDZ4weuQ9CsPg1c211XUMVEZ4A==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.ro": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "psXNOcA6R8fSHoQYhpBTtTTYiOk8OBoN3PKCEDgsJKIyeY5xuK81IBdGi77qGZMu/OwBRQjQCBMtPJb0f4O1+A==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.ru": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "zm245xUWrajSN2t9H7BTf84/2APbUkKlUJpcdgsvTdAysr1ag9fi1APu6JEok39RRBXDfNRVZHawQ/U8X0pSvQ==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.sk": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "Ncw24Vf3ioRnbU4MsMFHafkyYi8JOnTqvK741GftlQvAbULBoTz2+e7JByOaasqeSi0KfTXeegJO+5Wk1c0Mbw==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.sl": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "l8sUy4ciAIbVThWNL0atzTS2HWtv8qJrsGWNlqrEKmPwA4SdKolSqnTes9V89fyZTc2Q43jK8fgzVE2C7t009A==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.sr": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "rnNvhpkOrWEymy7R/MiFv7uef8YO5HuXDyvojZ7JpijHWA5dXuVXooCOiA/3E93fYa3pxDuG2OQe4M/olXbQ7w==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.sr-Latn": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "nuy/ykpk974F8ItoQMS00kJPr2dFNjOSjgzCwfysbu7+gjqHmbLcYs7G4kshLwdA4AsVncxp99LYeJgoh1JF5g==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.sv": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "E53+tpAG0RCp+cSSI7TfBPC+NnsEqUuoSV0sU+rWRXWr9MbRWx1+Zj02XMojqjGzHjjOrBFBBio6m74seFl0AA==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.th-TH": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "eSevlJtvs1r4vQarNPfZ2kKDp/xMhuD00tVVzRXkSh1IAZbBJI/x2ydxUOwfK9bEwEp+YjvL1Djx2+kw7ziu7g==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.tr": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "rQ8N+o7yFcFqdbtu1mmbrXFi8TQ+uy+fVH9OPI0CI3Cu1om5hUU/GOMC3hXsTCI6d79y4XX+0HbnD7FT5khegA==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.uk": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "2uEfujwXKNm6bdpukaLtEJD+04uUtQD65nSGCetA1fYNizItEaIBUboNfr3GzJxSMQotNwGVM3+nSn8jTd0VSg==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.uz-Cyrl-UZ": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "TD3ME2sprAvFqk9tkWrvSKx5XxEMlAn1sjk+cYClSWZlIMhQQ2Bp/w0VjX1Kc5oeKjxRAnR7vFcLUFLiZIDk9Q==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.uz-Latn-UZ": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "/kHAoF4g0GahnugZiEMpaHlxb+W6jCEbWIdsq9/I1k48ULOsl/J0pxZj93lXC3omGzVF1BTVIeAtv5fW06Phsg==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.vi": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "rsQNh9rmHMBtnsUUlJbShMsIMGflZtPmrMM6JNDw20nhsvqfrdcoDD8cMnLAbuSovtc3dP+swRmLQzKmXDTVPA==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.zh-CN": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "uH2dWhrgugkCjDmduLdAFO9w1Mo0q07EuvM0QiIZCVm6FMCu/lGv2fpMu4GX+4HLZ6h5T2Pg9FIdDLCPN2a67w==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.zh-Hans": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "WH6IhJ8V1UBG7rZXQk3dZUoP2gsi8a0WkL8xL0sN6WGiv695s8nVcmab9tWz20ySQbuzp0UkSxUQFi5jJHIpOQ==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core.zh-Hant": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.14.1",
|
||||
"contentHash": "VIXB7HCUC34OoaGnO3HJVtSv2/wljPhjV7eKH4+TFPgQdJj2lvHNKY41Dtg0Bphu7X5UaXFR4zrYYyo+GNOjbA==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "[2.14.1]"
|
||||
}
|
||||
}
|
||||
},
|
||||
"net8.0/win-x64": {}
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user