mirror of
https://github.com/anchore/syft.git
synced 2026-10-11 21:57:22 +02:00
Compare commits
7
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
91c5c850ea | ||
|
|
42c6259d4b | ||
|
|
abed9f2d5f | ||
|
|
e4f4aa2956 | ||
|
|
0afe26152f | ||
|
|
dd0e7dc20f | ||
|
|
e5fd03d2f6 |
@@ -35,7 +35,7 @@ require (
|
||||
github.com/charmbracelet/lipgloss v1.1.0
|
||||
github.com/dave/jennifer v1.7.1
|
||||
github.com/deitch/magic v0.0.0-20230404182410-1ff89d7342da
|
||||
github.com/diskfs/go-diskfs v1.6.1-0.20250601133945-2af1c7ece24c
|
||||
github.com/diskfs/go-diskfs v1.7.0
|
||||
github.com/distribution/reference v0.6.0
|
||||
github.com/dustin/go-humanize v1.0.1
|
||||
github.com/elliotchance/phpserialize v1.4.0
|
||||
@@ -80,7 +80,7 @@ require (
|
||||
github.com/spdx/tools-golang v0.5.5
|
||||
github.com/spf13/afero v1.14.0
|
||||
github.com/spf13/cobra v1.9.1
|
||||
github.com/stretchr/testify v1.10.0
|
||||
github.com/stretchr/testify v1.11.0
|
||||
github.com/vbatts/go-mtree v0.5.4
|
||||
github.com/vifraa/gopom v1.0.0
|
||||
github.com/wagoodman/go-partybus v0.0.0-20230516145632-8ccac152c651
|
||||
|
||||
@@ -851,8 +851,8 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8Yc
|
||||
github.com/deitch/magic v0.0.0-20230404182410-1ff89d7342da h1:ZOjWpVsFZ06eIhnh4mkaceTiVoktdU67+M7KDHJ268M=
|
||||
github.com/deitch/magic v0.0.0-20230404182410-1ff89d7342da/go.mod h1:B3tI9iGHi4imdLi4Asdha1Sc6feLMTfPLXh9IUYmysk=
|
||||
github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1/go.mod h1:+hnT3ywWDTAFrW5aE+u2Sa/wT555ZqwoCS+pk3p6ry4=
|
||||
github.com/diskfs/go-diskfs v1.6.1-0.20250601133945-2af1c7ece24c h1:Vg+RNk+3Kwbe3wUYsgXsk43+7oyOJ5tRDzOrcpV40yQ=
|
||||
github.com/diskfs/go-diskfs v1.6.1-0.20250601133945-2af1c7ece24c/go.mod h1:LhQyXqOugWFRahYUSw47NyZJPezFzB9UELwhpszLP/k=
|
||||
github.com/diskfs/go-diskfs v1.7.0 h1:vonWmt5CMowXwUc79jWyGrf2DIMeoOjkLlMnQYGVOs8=
|
||||
github.com/diskfs/go-diskfs v1.7.0/go.mod h1:LhQyXqOugWFRahYUSw47NyZJPezFzB9UELwhpszLP/k=
|
||||
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
|
||||
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
|
||||
github.com/djherbis/times v1.6.0 h1:w2ctJ92J8fBvWPxugmXIv7Nz7Q3iDMKNx9v5ocVH20c=
|
||||
@@ -1507,8 +1507,8 @@ github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o
|
||||
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/stretchr/testify v1.11.0 h1:ib4sjIrwZKxE5u/Japgo/7SJV3PvgjGiRNAvTVGqQl8=
|
||||
github.com/stretchr/testify v1.11.0/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
||||
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
||||
|
||||
@@ -126,6 +126,7 @@ func DefaultPackageTaskFactories() Factories {
|
||||
pkgcataloging.InstalledTag, pkgcataloging.ImageTag, pkgcataloging.DirectoryTag, pkgcataloging.LanguageTag, "dotnet", "c#",
|
||||
),
|
||||
newSimplePackageTaskFactory(dotnet.NewDotnetPackagesLockCataloger, pkgcataloging.DeclaredTag, pkgcataloging.ImageTag, pkgcataloging.DirectoryTag, pkgcataloging.LanguageTag, "dotnet", "c#"),
|
||||
newSimplePackageTaskFactory(dotnet.NewDotnetCsprojCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.LanguageTag, "dotnet", "c#"),
|
||||
newSimplePackageTaskFactory(python.NewInstalledPackageCataloger, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, pkgcataloging.LanguageTag, "python"),
|
||||
newPackageTaskFactory(
|
||||
func(cfg CatalogingFactoryConfig) pkg.Cataloger {
|
||||
|
||||
@@ -29,3 +29,9 @@ func NewDotnetPackagesLockCataloger() pkg.Cataloger {
|
||||
return generic.NewCataloger("dotnet-packages-lock-cataloger").
|
||||
WithParserByGlobs(parseDotnetPackagesLock, "**/packages.lock.json")
|
||||
}
|
||||
|
||||
// NewDotnetCsprojCataloger returns a cataloger based on .csproj files.
|
||||
func NewDotnetCsprojCataloger() pkg.Cataloger {
|
||||
return generic.NewCataloger("dotnet-csproj-cataloger").
|
||||
WithParserByGlobs(parseDotnetCsproj, "**/*.csproj")
|
||||
}
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
package dotnet
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest"
|
||||
)
|
||||
|
||||
func TestDotnetCsprojCataloger(t *testing.T) {
|
||||
fixture := "test-fixtures/weatherforecast-steeltoe"
|
||||
|
||||
expectedLocation := file.NewLocation("Sample.csproj")
|
||||
fixtureLocationSet := file.NewLocationSet(expectedLocation)
|
||||
|
||||
expected := []pkg.Package{
|
||||
{
|
||||
Name: "Steeltoe.Common.Hosting",
|
||||
Version: "3.2.*",
|
||||
FoundBy: "dotnet-csproj-cataloger",
|
||||
Language: pkg.Dotnet,
|
||||
Type: pkg.DotnetPkg,
|
||||
PURL: "pkg:nuget/Steeltoe.Common.Hosting@3.2.%2A",
|
||||
Locations: fixtureLocationSet,
|
||||
Metadata: pkg.DotnetDepsEntry{
|
||||
Name: "Steeltoe.Common.Hosting",
|
||||
Version: "3.2.*",
|
||||
Path: ".",
|
||||
Sha512: "",
|
||||
HashPath: "",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "Steeltoe.Management.EndpointCore",
|
||||
Version: "3.2.*",
|
||||
FoundBy: "dotnet-csproj-cataloger",
|
||||
Language: pkg.Dotnet,
|
||||
Type: pkg.DotnetPkg,
|
||||
PURL: "pkg:nuget/Steeltoe.Management.EndpointCore@3.2.%2A",
|
||||
Locations: fixtureLocationSet,
|
||||
Metadata: pkg.DotnetDepsEntry{
|
||||
Name: "Steeltoe.Management.EndpointCore",
|
||||
Version: "3.2.*",
|
||||
Path: ".",
|
||||
Sha512: "",
|
||||
HashPath: "",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "Swashbuckle.AspNetCore",
|
||||
Version: "6.2.*",
|
||||
FoundBy: "dotnet-csproj-cataloger",
|
||||
Language: pkg.Dotnet,
|
||||
Type: pkg.DotnetPkg,
|
||||
PURL: "pkg:nuget/Swashbuckle.AspNetCore@6.2.%2A",
|
||||
Locations: fixtureLocationSet,
|
||||
Metadata: pkg.DotnetDepsEntry{
|
||||
Name: "Swashbuckle.AspNetCore",
|
||||
Version: "6.2.*",
|
||||
Path: ".",
|
||||
Sha512: "",
|
||||
HashPath: "",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
pkgtest.TestCataloger(t, fixture, NewDotnetCsprojCataloger(), expected, nil)
|
||||
}
|
||||
|
||||
func TestDotnetCsprojCataloger_GlopsMatch(t *testing.T) {
|
||||
expectedPackages := []pkg.Package{
|
||||
{
|
||||
Name: "Steeltoe.Common.Hosting",
|
||||
Version: "3.2.*",
|
||||
FoundBy: "dotnet-csproj-cataloger",
|
||||
Language: pkg.Dotnet,
|
||||
Type: pkg.DotnetPkg,
|
||||
PURL: "pkg:nuget/Steeltoe.Common.Hosting@3.2.%2A",
|
||||
Locations: file.NewLocationSet(file.NewLocation("Sample.csproj")),
|
||||
Metadata: pkg.DotnetDepsEntry{
|
||||
Name: "Steeltoe.Common.Hosting",
|
||||
Version: "3.2.*",
|
||||
Path: ".",
|
||||
Sha512: "",
|
||||
HashPath: "",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "Steeltoe.Management.EndpointCore",
|
||||
Version: "3.2.*",
|
||||
FoundBy: "dotnet-csproj-cataloger",
|
||||
Language: pkg.Dotnet,
|
||||
Type: pkg.DotnetPkg,
|
||||
PURL: "pkg:nuget/Steeltoe.Management.EndpointCore@3.2.%2A",
|
||||
Locations: file.NewLocationSet(file.NewLocation("Sample.csproj")),
|
||||
Metadata: pkg.DotnetDepsEntry{
|
||||
Name: "Steeltoe.Management.EndpointCore",
|
||||
Version: "3.2.*",
|
||||
Path: ".",
|
||||
Sha512: "",
|
||||
HashPath: "",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "Swashbuckle.AspNetCore",
|
||||
Version: "6.2.*",
|
||||
FoundBy: "dotnet-csproj-cataloger",
|
||||
Language: pkg.Dotnet,
|
||||
Type: pkg.DotnetPkg,
|
||||
PURL: "pkg:nuget/Swashbuckle.AspNetCore@6.2.%2A",
|
||||
Locations: file.NewLocationSet(file.NewLocation("Sample.csproj")),
|
||||
Metadata: pkg.DotnetDepsEntry{
|
||||
Name: "Swashbuckle.AspNetCore",
|
||||
Version: "6.2.*",
|
||||
Path: ".",
|
||||
Sha512: "",
|
||||
HashPath: "",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
pkgtest.NewCatalogTester().
|
||||
FromDirectory(t, "test-fixtures/weatherforecast-steeltoe").
|
||||
Expects(expectedPackages, nil).
|
||||
TestCataloger(t, NewDotnetCsprojCataloger())
|
||||
}
|
||||
@@ -0,0 +1,231 @@
|
||||
package dotnet
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"io"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/anchore/packageurl-go"
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
||||
)
|
||||
|
||||
// csprojProject represents the root element of a .csproj file
|
||||
type csprojProject struct {
|
||||
XMLName xml.Name `xml:"Project"`
|
||||
Sdk string `xml:"Sdk,attr"`
|
||||
PropertyGroups []csprojPropertyGroup `xml:"PropertyGroup"`
|
||||
ItemGroups []csprojItemGroup `xml:"ItemGroup"`
|
||||
}
|
||||
|
||||
// csprojPropertyGroup represents a PropertyGroup element containing MSBuild properties
|
||||
type csprojPropertyGroup struct {
|
||||
Properties []csprojProperty `xml:",any"`
|
||||
}
|
||||
|
||||
// csprojProperty represents any property within a PropertyGroup
|
||||
type csprojProperty struct {
|
||||
XMLName xml.Name
|
||||
Value string `xml:",chardata"`
|
||||
}
|
||||
|
||||
// csprojItemGroup represents an ItemGroup element containing references
|
||||
type csprojItemGroup struct {
|
||||
PackageReferences []csprojPackageReference `xml:"PackageReference"`
|
||||
ProjectReferences []csprojProjectReference `xml:"ProjectReference"`
|
||||
}
|
||||
|
||||
// csprojPackageReference represents a PackageReference element
|
||||
type csprojPackageReference struct {
|
||||
Include string `xml:"Include,attr"`
|
||||
Version string `xml:"Version,attr"`
|
||||
PrivateAssets string `xml:"PrivateAssets,attr"`
|
||||
IncludeAssets string `xml:"IncludeAssets,attr"`
|
||||
Condition string `xml:"Condition,attr"`
|
||||
}
|
||||
|
||||
// csprojProjectReference represents a ProjectReference element
|
||||
type csprojProjectReference struct {
|
||||
Include string `xml:"Include,attr"`
|
||||
Condition string `xml:"Condition,attr"`
|
||||
}
|
||||
|
||||
func parseDotnetCsproj(_ context.Context, _ file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
|
||||
contents, err := io.ReadAll(reader)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("unable to read .csproj file: %w", err)
|
||||
}
|
||||
|
||||
var project csprojProject
|
||||
if err := xml.Unmarshal(contents, &project); err != nil {
|
||||
return nil, nil, fmt.Errorf("unable to parse .csproj XML: %w", err)
|
||||
}
|
||||
|
||||
// Build property map from PropertyGroups
|
||||
properties := buildPropertyMap(project.PropertyGroups)
|
||||
|
||||
var pkgs []pkg.Package
|
||||
var relationships []artifact.Relationship
|
||||
|
||||
// Process PackageReference elements
|
||||
for _, itemGroup := range project.ItemGroups {
|
||||
for _, pkgRef := range itemGroup.PackageReferences {
|
||||
// Skip packages that are build-time only or analyzers
|
||||
if shouldSkipPackageReference(pkgRef) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Resolve any MSBuild property variables in the version
|
||||
resolvedVersion := resolveProperties(pkgRef.Version, properties)
|
||||
pkgRef.Version = resolvedVersion
|
||||
|
||||
p := buildPackageFromReference(pkgRef, reader.Location)
|
||||
if p != nil {
|
||||
pkgs = append(pkgs, *p)
|
||||
}
|
||||
}
|
||||
|
||||
// Process ProjectReference elements (if we want to include them as relationships)
|
||||
for _, projRef := range itemGroup.ProjectReferences {
|
||||
// ProjectReferences represent internal project dependencies
|
||||
// We could create relationships here, but for now we skip them
|
||||
// since they represent source-to-source dependencies within the same solution
|
||||
_ = projRef
|
||||
}
|
||||
}
|
||||
|
||||
return pkgs, relationships, nil
|
||||
}
|
||||
|
||||
// shouldSkipPackageReference determines if a package reference should be skipped
|
||||
func shouldSkipPackageReference(ref csprojPackageReference) bool {
|
||||
// Skip packages that are private assets only (build-time dependencies)
|
||||
if ref.PrivateAssets == "all" || ref.PrivateAssets == "All" {
|
||||
return true
|
||||
}
|
||||
|
||||
// Skip conditional references that are likely build/development only
|
||||
condition := strings.ToLower(ref.Condition)
|
||||
if strings.Contains(condition, "debug") && !strings.Contains(condition, "release") {
|
||||
return true
|
||||
}
|
||||
|
||||
// Skip packages that are commonly build-time only
|
||||
lowerName := strings.ToLower(ref.Include)
|
||||
buildTimePackages := map[string]bool{
|
||||
"microsoft.net.test.sdk": true,
|
||||
"stylecop.analyzers": true,
|
||||
"microsoft.codeanalysis": true,
|
||||
"coverlet.collector": true,
|
||||
"xunit.runner.visualstudio": true,
|
||||
"nunit": true,
|
||||
"nunit3testadapter": true,
|
||||
"mstest.testadapter": true,
|
||||
"mstest.testframework": true,
|
||||
}
|
||||
|
||||
for buildPkg := range buildTimePackages {
|
||||
if strings.Contains(lowerName, buildPkg) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// buildPropertyMap creates a map of MSBuild properties from PropertyGroups
|
||||
func buildPropertyMap(propertyGroups []csprojPropertyGroup) map[string]string {
|
||||
properties := make(map[string]string)
|
||||
|
||||
for _, group := range propertyGroups {
|
||||
for _, prop := range group.Properties {
|
||||
propertyName := prop.XMLName.Local
|
||||
propertyValue := strings.TrimSpace(prop.Value)
|
||||
if propertyName != "" && propertyValue != "" {
|
||||
properties[propertyName] = propertyValue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return properties
|
||||
}
|
||||
|
||||
// resolveProperties resolves MSBuild property variables like $(PropertyName) in a string
|
||||
func resolveProperties(input string, properties map[string]string) string {
|
||||
if input == "" {
|
||||
return input
|
||||
}
|
||||
|
||||
// Pattern matches $(PropertyName)
|
||||
propertyPattern := `\$\(([^)]+)\)`
|
||||
re := regexp.MustCompile(propertyPattern)
|
||||
|
||||
result := re.ReplaceAllStringFunc(input, func(match string) string {
|
||||
// Extract property name from $(PropertyName)
|
||||
propertyName := match[2 : len(match)-1] // Remove $( and )
|
||||
|
||||
if value, exists := properties[propertyName]; exists {
|
||||
return value
|
||||
}
|
||||
|
||||
// Return original if property not found (preserve for debugging)
|
||||
return match
|
||||
})
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// buildPackageFromReference creates a Package from a PackageReference element
|
||||
func buildPackageFromReference(ref csprojPackageReference, location file.Location) *pkg.Package {
|
||||
name := strings.TrimSpace(ref.Include)
|
||||
if name == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
version := strings.TrimSpace(ref.Version)
|
||||
// If version is empty, this might be a framework reference or implicit version
|
||||
// For now, we'll skip packages without explicit versions since we can't determine them
|
||||
// from the .csproj alone (would need props/targets files or lock files)
|
||||
if version == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Skip packages with unresolved MSBuild properties (contains $(...))
|
||||
if strings.Contains(version, "$(") {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Generate PURL following the established pattern for .NET packages
|
||||
purl := packageurl.NewPackageURL(
|
||||
packageurl.TypeNuget,
|
||||
"",
|
||||
name,
|
||||
version,
|
||||
nil,
|
||||
"",
|
||||
)
|
||||
|
||||
p := &pkg.Package{
|
||||
Name: name,
|
||||
Version: version,
|
||||
Language: pkg.Dotnet,
|
||||
Type: pkg.DotnetPkg,
|
||||
PURL: purl.ToString(),
|
||||
Locations: file.NewLocationSet(location),
|
||||
Metadata: pkg.DotnetDepsEntry{
|
||||
Name: name,
|
||||
Version: version,
|
||||
Path: filepath.Dir(location.RealPath),
|
||||
Sha512: "",
|
||||
HashPath: "",
|
||||
},
|
||||
}
|
||||
|
||||
return p
|
||||
}
|
||||
@@ -0,0 +1,658 @@
|
||||
package dotnet
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/xml"
|
||||
"io"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
||||
)
|
||||
|
||||
func TestParseDotnetCsproj(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
expected []pkg.Package
|
||||
expectedError bool
|
||||
}{
|
||||
{
|
||||
name: "basic PackageReference parsing",
|
||||
input: `<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="Serilog" Version="2.10.0" />
|
||||
</ItemGroup>
|
||||
</Project>`,
|
||||
expected: []pkg.Package{
|
||||
{
|
||||
Name: "Newtonsoft.Json",
|
||||
Version: "13.0.3",
|
||||
Language: pkg.Dotnet,
|
||||
Type: pkg.DotnetPkg,
|
||||
PURL: "pkg:nuget/Newtonsoft.Json@13.0.3",
|
||||
},
|
||||
{
|
||||
Name: "Serilog",
|
||||
Version: "2.10.0",
|
||||
Language: pkg.Dotnet,
|
||||
Type: pkg.DotnetPkg,
|
||||
PURL: "pkg:nuget/Serilog@2.10.0",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "skip private assets",
|
||||
input: `<Project Sdk="Microsoft.NET.Sdk">
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0" PrivateAssets="all" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
</Project>`,
|
||||
expected: []pkg.Package{
|
||||
{
|
||||
Name: "Newtonsoft.Json",
|
||||
Version: "13.0.3",
|
||||
Language: pkg.Dotnet,
|
||||
Type: pkg.DotnetPkg,
|
||||
PURL: "pkg:nuget/Newtonsoft.Json@13.0.3",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "skip conditional debug-only references",
|
||||
input: `<Project Sdk="Microsoft.NET.Sdk">
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" Condition="'$(Configuration)' == 'Debug'" />
|
||||
</ItemGroup>
|
||||
</Project>`,
|
||||
expected: []pkg.Package{
|
||||
{
|
||||
Name: "Newtonsoft.Json",
|
||||
Version: "13.0.3",
|
||||
Language: pkg.Dotnet,
|
||||
Type: pkg.DotnetPkg,
|
||||
PURL: "pkg:nuget/Newtonsoft.Json@13.0.3",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "complex IncludeAssets and PrivateAssets handling",
|
||||
input: `<Project Sdk="Microsoft.NET.Sdk">
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0" PrivateAssets="all" />
|
||||
<PackageReference Include="Microsoft.ChakraCore" Version="1.11.24" IncludeAssets="runtime; build; native" />
|
||||
<PackageReference Include="NUnit" Version="3.13.3" />
|
||||
</ItemGroup>
|
||||
</Project>`,
|
||||
expected: []pkg.Package{
|
||||
{
|
||||
Name: "Newtonsoft.Json",
|
||||
Version: "13.0.3",
|
||||
Language: pkg.Dotnet,
|
||||
Type: pkg.DotnetPkg,
|
||||
PURL: "pkg:nuget/Newtonsoft.Json@13.0.3",
|
||||
},
|
||||
{
|
||||
Name: "Microsoft.ChakraCore",
|
||||
Version: "1.11.24",
|
||||
Language: pkg.Dotnet,
|
||||
Type: pkg.DotnetPkg,
|
||||
PURL: "pkg:nuget/Microsoft.ChakraCore@1.11.24",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "skip build-time packages",
|
||||
input: `<Project Sdk="Microsoft.NET.Sdk">
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
|
||||
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" />
|
||||
</ItemGroup>
|
||||
</Project>`,
|
||||
expected: []pkg.Package{
|
||||
{
|
||||
Name: "Newtonsoft.Json",
|
||||
Version: "13.0.3",
|
||||
Language: pkg.Dotnet,
|
||||
Type: pkg.DotnetPkg,
|
||||
PURL: "pkg:nuget/Newtonsoft.Json@13.0.3",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "skip packages without version",
|
||||
input: `<Project Sdk="Microsoft.NET.Sdk">
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.App" />
|
||||
</ItemGroup>
|
||||
</Project>`,
|
||||
expected: []pkg.Package{
|
||||
{
|
||||
Name: "Newtonsoft.Json",
|
||||
Version: "13.0.3",
|
||||
Language: pkg.Dotnet,
|
||||
Type: pkg.DotnetPkg,
|
||||
PURL: "pkg:nuget/Newtonsoft.Json@13.0.3",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "multiple ItemGroup elements",
|
||||
input: `<Project Sdk="Microsoft.NET.Sdk">
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Serilog" Version="2.10.0" />
|
||||
</ItemGroup>
|
||||
</Project>`,
|
||||
expected: []pkg.Package{
|
||||
{
|
||||
Name: "Newtonsoft.Json",
|
||||
Version: "13.0.3",
|
||||
Language: pkg.Dotnet,
|
||||
Type: pkg.DotnetPkg,
|
||||
PURL: "pkg:nuget/Newtonsoft.Json@13.0.3",
|
||||
},
|
||||
{
|
||||
Name: "Serilog",
|
||||
Version: "2.10.0",
|
||||
Language: pkg.Dotnet,
|
||||
Type: pkg.DotnetPkg,
|
||||
PURL: "pkg:nuget/Serilog@2.10.0",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "empty project",
|
||||
input: `<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
</Project>`,
|
||||
expected: []pkg.Package{},
|
||||
},
|
||||
{
|
||||
name: "ProjectReference ignored",
|
||||
input: `<Project Sdk="Microsoft.NET.Sdk">
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<ProjectReference Include="../SharedLibrary/SharedLibrary.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>`,
|
||||
expected: []pkg.Package{
|
||||
{
|
||||
Name: "Newtonsoft.Json",
|
||||
Version: "13.0.3",
|
||||
Language: pkg.Dotnet,
|
||||
Type: pkg.DotnetPkg,
|
||||
PURL: "pkg:nuget/Newtonsoft.Json@13.0.3",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "property variable resolution",
|
||||
input: `<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<SteeltoeVersion>3.2.0</SteeltoeVersion>
|
||||
<TestVersion>1.0.0</TestVersion>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Steeltoe.Common" Version="$(SteeltoeVersion)" />
|
||||
<PackageReference Include="Test.Package" Version="$(TestVersion)" />
|
||||
<PackageReference Include="Regular.Package" Version="2.0.0" />
|
||||
</ItemGroup>
|
||||
</Project>`,
|
||||
expected: []pkg.Package{
|
||||
{
|
||||
Name: "Steeltoe.Common",
|
||||
Version: "3.2.0",
|
||||
Language: pkg.Dotnet,
|
||||
Type: pkg.DotnetPkg,
|
||||
PURL: "pkg:nuget/Steeltoe.Common@3.2.0",
|
||||
},
|
||||
{
|
||||
Name: "Test.Package",
|
||||
Version: "1.0.0",
|
||||
Language: pkg.Dotnet,
|
||||
Type: pkg.DotnetPkg,
|
||||
PURL: "pkg:nuget/Test.Package@1.0.0",
|
||||
},
|
||||
{
|
||||
Name: "Regular.Package",
|
||||
Version: "2.0.0",
|
||||
Language: pkg.Dotnet,
|
||||
Type: pkg.DotnetPkg,
|
||||
PURL: "pkg:nuget/Regular.Package@2.0.0",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "wildcard versions preserved",
|
||||
input: `<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<SteeltoeVersion>3.2.*</SteeltoeVersion>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Steeltoe.Common" Version="$(SteeltoeVersion)" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.2.*" />
|
||||
</ItemGroup>
|
||||
</Project>`,
|
||||
expected: []pkg.Package{
|
||||
{
|
||||
Name: "Steeltoe.Common",
|
||||
Version: "3.2.*",
|
||||
Language: pkg.Dotnet,
|
||||
Type: pkg.DotnetPkg,
|
||||
PURL: "pkg:nuget/Steeltoe.Common@3.2.%2A",
|
||||
},
|
||||
{
|
||||
Name: "Swashbuckle.AspNetCore",
|
||||
Version: "6.2.*",
|
||||
Language: pkg.Dotnet,
|
||||
Type: pkg.DotnetPkg,
|
||||
PURL: "pkg:nuget/Swashbuckle.AspNetCore@6.2.%2A",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "unresolved property preserved",
|
||||
input: `<Project Sdk="Microsoft.NET.Sdk">
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Some.Package" Version="$(UndefinedProperty)" />
|
||||
<PackageReference Include="Regular.Package" Version="2.0.0" />
|
||||
</ItemGroup>
|
||||
</Project>`,
|
||||
expected: []pkg.Package{
|
||||
{
|
||||
Name: "Regular.Package",
|
||||
Version: "2.0.0",
|
||||
Language: pkg.Dotnet,
|
||||
Type: pkg.DotnetPkg,
|
||||
PURL: "pkg:nuget/Regular.Package@2.0.0",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "malformed XML",
|
||||
input: `<Project><ItemGroup><PackageReference Include="Test"`,
|
||||
expected: nil,
|
||||
expectedError: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
reader := file.NewLocationReadCloser(
|
||||
file.NewLocation("/test/project.csproj"),
|
||||
io.NopCloser(strings.NewReader(test.input)),
|
||||
)
|
||||
|
||||
packages, relationships, err := parseDotnetCsproj(context.Background(), nil, &generic.Environment{}, reader)
|
||||
|
||||
if test.expectedError {
|
||||
require.Error(t, err)
|
||||
return
|
||||
}
|
||||
|
||||
require.NoError(t, err)
|
||||
assert.Empty(t, relationships)
|
||||
require.Len(t, packages, len(test.expected))
|
||||
|
||||
for i, expectedPkg := range test.expected {
|
||||
actualPkg := packages[i]
|
||||
assert.Equal(t, expectedPkg.Name, actualPkg.Name)
|
||||
assert.Equal(t, expectedPkg.Version, actualPkg.Version)
|
||||
assert.Equal(t, expectedPkg.Language, actualPkg.Language)
|
||||
assert.Equal(t, expectedPkg.Type, actualPkg.Type)
|
||||
assert.Equal(t, expectedPkg.PURL, actualPkg.PURL)
|
||||
|
||||
// Verify metadata structure
|
||||
metadata, ok := actualPkg.Metadata.(pkg.DotnetDepsEntry)
|
||||
require.True(t, ok, "expected DotnetDepsEntry metadata")
|
||||
assert.Equal(t, expectedPkg.Name, metadata.Name)
|
||||
assert.Equal(t, expectedPkg.Version, metadata.Version)
|
||||
assert.NotEmpty(t, metadata.Path)
|
||||
|
||||
// Verify locations are set
|
||||
assert.NotEmpty(t, actualPkg.Locations)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestShouldSkipPackageReference(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
ref csprojPackageReference
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
name: "regular package",
|
||||
ref: csprojPackageReference{
|
||||
Include: "Newtonsoft.Json",
|
||||
Version: "13.0.3",
|
||||
},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "private assets all",
|
||||
ref: csprojPackageReference{
|
||||
Include: "StyleCop.Analyzers",
|
||||
Version: "1.2.0",
|
||||
PrivateAssets: "all",
|
||||
},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "private assets All (capitalized)",
|
||||
ref: csprojPackageReference{
|
||||
Include: "StyleCop.Analyzers",
|
||||
Version: "1.2.0",
|
||||
PrivateAssets: "All",
|
||||
},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "debug condition",
|
||||
ref: csprojPackageReference{
|
||||
Include: "Microsoft.NET.Test.Sdk",
|
||||
Version: "17.8.0",
|
||||
Condition: "'$(Configuration)' == 'Debug'",
|
||||
},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "test SDK package",
|
||||
ref: csprojPackageReference{
|
||||
Include: "Microsoft.NET.Test.Sdk",
|
||||
Version: "17.8.0",
|
||||
},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "stylecop analyzer",
|
||||
ref: csprojPackageReference{
|
||||
Include: "StyleCop.Analyzers",
|
||||
Version: "1.2.0",
|
||||
},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "code analysis package",
|
||||
ref: csprojPackageReference{
|
||||
Include: "Microsoft.CodeAnalysis.Analyzers",
|
||||
Version: "3.3.4",
|
||||
},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "includeAssets runtime only",
|
||||
ref: csprojPackageReference{
|
||||
Include: "Some.Package",
|
||||
Version: "1.0.0",
|
||||
IncludeAssets: "runtime",
|
||||
},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "mixed condition with release",
|
||||
ref: csprojPackageReference{
|
||||
Include: "Some.Package",
|
||||
Version: "1.0.0",
|
||||
Condition: "'$(Configuration)' == 'Debug' OR '$(Configuration)' == 'Release'",
|
||||
},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "nunit test package",
|
||||
ref: csprojPackageReference{
|
||||
Include: "NUnit",
|
||||
Version: "3.13.3",
|
||||
},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "mstest framework",
|
||||
ref: csprojPackageReference{
|
||||
Include: "MSTest.TestFramework",
|
||||
Version: "3.1.1",
|
||||
},
|
||||
expected: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
result := shouldSkipPackageReference(test.ref)
|
||||
assert.Equal(t, test.expected, result)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildPackageFromReference(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
ref csprojPackageReference
|
||||
location file.Location
|
||||
expected *pkg.Package
|
||||
}{
|
||||
{
|
||||
name: "valid package reference",
|
||||
ref: csprojPackageReference{
|
||||
Include: "Newtonsoft.Json",
|
||||
Version: "13.0.3",
|
||||
},
|
||||
location: file.NewLocation("/test/project.csproj"),
|
||||
expected: &pkg.Package{
|
||||
Name: "Newtonsoft.Json",
|
||||
Version: "13.0.3",
|
||||
Language: pkg.Dotnet,
|
||||
Type: pkg.DotnetPkg,
|
||||
PURL: "pkg:nuget/Newtonsoft.Json@13.0.3",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "empty name",
|
||||
ref: csprojPackageReference{
|
||||
Include: "",
|
||||
Version: "13.0.3",
|
||||
},
|
||||
location: file.NewLocation("/test/project.csproj"),
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
name: "empty version",
|
||||
ref: csprojPackageReference{
|
||||
Include: "Newtonsoft.Json",
|
||||
Version: "",
|
||||
},
|
||||
location: file.NewLocation("/test/project.csproj"),
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
name: "whitespace in name and version",
|
||||
ref: csprojPackageReference{
|
||||
Include: " Newtonsoft.Json ",
|
||||
Version: " 13.0.3 ",
|
||||
},
|
||||
location: file.NewLocation("/test/project.csproj"),
|
||||
expected: &pkg.Package{
|
||||
Name: "Newtonsoft.Json",
|
||||
Version: "13.0.3",
|
||||
Language: pkg.Dotnet,
|
||||
Type: pkg.DotnetPkg,
|
||||
PURL: "pkg:nuget/Newtonsoft.Json@13.0.3",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
result := buildPackageFromReference(test.ref, test.location)
|
||||
|
||||
if test.expected == nil {
|
||||
assert.Nil(t, result)
|
||||
return
|
||||
}
|
||||
|
||||
require.NotNil(t, result)
|
||||
assert.Equal(t, test.expected.Name, result.Name)
|
||||
assert.Equal(t, test.expected.Version, result.Version)
|
||||
assert.Equal(t, test.expected.Language, result.Language)
|
||||
assert.Equal(t, test.expected.Type, result.Type)
|
||||
assert.Equal(t, test.expected.PURL, result.PURL)
|
||||
|
||||
// Verify metadata
|
||||
metadata, ok := result.Metadata.(pkg.DotnetDepsEntry)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, test.expected.Name, metadata.Name)
|
||||
assert.Equal(t, test.expected.Version, metadata.Version)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildPropertyMap(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
propertyGroups []csprojPropertyGroup
|
||||
expected map[string]string
|
||||
}{
|
||||
{
|
||||
name: "empty property groups",
|
||||
propertyGroups: []csprojPropertyGroup{},
|
||||
expected: map[string]string{},
|
||||
},
|
||||
{
|
||||
name: "single property group",
|
||||
propertyGroups: []csprojPropertyGroup{
|
||||
{
|
||||
Properties: []csprojProperty{
|
||||
{XMLName: xml.Name{Local: "SteeltoeVersion"}, Value: "3.2.0"},
|
||||
{XMLName: xml.Name{Local: "TestVersion"}, Value: "1.0.0"},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: map[string]string{
|
||||
"SteeltoeVersion": "3.2.0",
|
||||
"TestVersion": "1.0.0",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "multiple property groups",
|
||||
propertyGroups: []csprojPropertyGroup{
|
||||
{
|
||||
Properties: []csprojProperty{
|
||||
{XMLName: xml.Name{Local: "SteeltoeVersion"}, Value: "3.2.0"},
|
||||
},
|
||||
},
|
||||
{
|
||||
Properties: []csprojProperty{
|
||||
{XMLName: xml.Name{Local: "TestVersion"}, Value: "1.0.0"},
|
||||
{XMLName: xml.Name{Local: "TargetFramework"}, Value: "net6.0"},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: map[string]string{
|
||||
"SteeltoeVersion": "3.2.0",
|
||||
"TestVersion": "1.0.0",
|
||||
"TargetFramework": "net6.0",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "properties with whitespace",
|
||||
propertyGroups: []csprojPropertyGroup{
|
||||
{
|
||||
Properties: []csprojProperty{
|
||||
{XMLName: xml.Name{Local: "Version"}, Value: " 3.2.0 "},
|
||||
{XMLName: xml.Name{Local: "EmptyValue"}, Value: " "},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: map[string]string{
|
||||
"Version": "3.2.0",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
result := buildPropertyMap(test.propertyGroups)
|
||||
assert.Equal(t, test.expected, result)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveProperties(t *testing.T) {
|
||||
properties := map[string]string{
|
||||
"SteeltoeVersion": "3.2.*",
|
||||
"TestVersion": "1.0.0",
|
||||
"MajorVersion": "2",
|
||||
"MinorVersion": "1",
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "no properties to resolve",
|
||||
input: "1.2.3",
|
||||
expected: "1.2.3",
|
||||
},
|
||||
{
|
||||
name: "single property resolution",
|
||||
input: "$(SteeltoeVersion)",
|
||||
expected: "3.2.*",
|
||||
},
|
||||
{
|
||||
name: "multiple property resolution",
|
||||
input: "$(MajorVersion).$(MinorVersion).0",
|
||||
expected: "2.1.0",
|
||||
},
|
||||
{
|
||||
name: "mixed resolved and unresolved",
|
||||
input: "$(SteeltoeVersion)-$(UnknownProperty)",
|
||||
expected: "3.2.*-$(UnknownProperty)",
|
||||
},
|
||||
{
|
||||
name: "empty input",
|
||||
input: "",
|
||||
expected: "",
|
||||
},
|
||||
{
|
||||
name: "unresolved property preserved",
|
||||
input: "$(UnknownProperty)",
|
||||
expected: "$(UnknownProperty)",
|
||||
},
|
||||
{
|
||||
name: "complex pattern",
|
||||
input: "prefix-$(TestVersion)-suffix",
|
||||
expected: "prefix-1.0.0-suffix",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
result := resolveProperties(test.input, properties)
|
||||
assert.Equal(t, test.expected, result)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<AspNetCoreHostingModel>InProcess</AspNetCoreHostingModel>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<SteeltoeVersion>3.2.*</SteeltoeVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Steeltoe.Common.Hosting" Version="$(SteeltoeVersion)" />
|
||||
<PackageReference Include="Steeltoe.Management.EndpointCore" Version="$(SteeltoeVersion)" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.2.*" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
Reference in New Issue
Block a user