diff --git a/pkg/nuget/csproj/parse.go b/pkg/nuget/csproj/parse.go
new file mode 100644
index 00000000..0de91574
--- /dev/null
+++ b/pkg/nuget/csproj/parse.go
@@ -0,0 +1,83 @@
+package csproj
+
+import (
+ "encoding/xml"
+ "strings"
+
+ "golang.org/x/xerrors"
+
+ dio "github.com/aquasecurity/go-dep-parser/pkg/io"
+ "github.com/aquasecurity/go-dep-parser/pkg/types"
+ "github.com/aquasecurity/go-dep-parser/pkg/utils"
+)
+
+type cfgPackageReference struct {
+ XMLName xml.Name `xml:"PackageReference"`
+ Version string `xml:"Version,attr"`
+ Include string `xml:"Include,attr"`
+ PrivateAssetsTag string `xml:"PrivateAssets"`
+ PrivateAssetsAttr string `xml:"PrivateAssets,attr"`
+ ExcludeAssetsTag string `xml:"ExcludeAssets"`
+ ExcludeAssetsAttr string `xml:"ExcludeAssets,attr"`
+}
+
+type config struct {
+ XMLName xml.Name `xml:"Project"`
+ Packages []cfgPackageReference `xml:"ItemGroup>PackageReference"`
+}
+
+type Parser struct{}
+
+func NewParser() types.Parser {
+ return &Parser{}
+}
+
+func (p *Parser) Parse(r dio.ReadSeekerAt) ([]types.Library, []types.Dependency, error) {
+ var cfgData config
+ if err := xml.NewDecoder(r).Decode(&cfgData); err != nil {
+ return nil, nil, xerrors.Errorf("failed to decode .csproj file: %w", err)
+ }
+
+ libs := make([]types.Library, 0)
+ for _, pkg := range cfgData.Packages {
+ if pkg.Include == "" || isDevDependency(pkg) {
+ continue
+ }
+
+ var versionNotFloating = strings.TrimRight(pkg.Version, ".*")
+
+ lib := types.Library{
+ Name: pkg.Include,
+ Version: versionNotFloating,
+ }
+
+ libs = append(libs, lib)
+ }
+
+ return utils.UniqueLibraries(libs), nil, nil
+}
+
+func isDevDependency(pkg cfgPackageReference) bool {
+ var privateAssets = tagOrAttribute(pkg.PrivateAssetsTag, pkg.PrivateAssetsAttr)
+ var excludeAssets = tagOrAttribute(pkg.ExcludeAssetsTag, pkg.ExcludeAssetsAttr)
+ return assetListContains(privateAssets, "all") || assetListContains(excludeAssets, "all") || assetListContains(excludeAssets, "runtime")
+}
+
+func assetListContains(assets []string, needle string) bool {
+ for _, v := range assets {
+ if strings.EqualFold(v, needle) {
+ return true
+ }
+ }
+ return false
+}
+
+func tagOrAttribute(tag string, attr string) []string {
+ var strvalue = "";
+ if (tag != "") {
+ strvalue = tag
+ } else {
+ strvalue = attr
+ }
+ return strings.Split(strvalue, ";")
+}
diff --git a/pkg/nuget/csproj/parse_test.go b/pkg/nuget/csproj/parse_test.go
new file mode 100644
index 00000000..aeedc7d0
--- /dev/null
+++ b/pkg/nuget/csproj/parse_test.go
@@ -0,0 +1,64 @@
+package csproj_test
+
+import (
+ "os"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+
+ "github.com/aquasecurity/go-dep-parser/pkg/nuget/csproj"
+ "github.com/aquasecurity/go-dep-parser/pkg/types"
+)
+
+func TestParse(t *testing.T) {
+ tests := []struct {
+ name string // Test input file
+ inputFile string
+ want []types.Library
+ wantErr string
+ }{
+ {
+ name: "csproj",
+ inputFile: "testdata/packages.csproj",
+ want: []types.Library{
+ {Name: "Newtonsoft.Json", Version: "6.0.4"},
+ {Name: "Microsoft.AspNet.WebApi", Version: "5.2.2"},
+ {Name: "Floating.Version", Version: "1.2"},
+ },
+ },
+ {
+ name: "with development dependency",
+ inputFile: "testdata/dev_dependency.csproj",
+ want: []types.Library{
+ {Name: "PrivateAssets.Tag.None", Version: "1.0.0"},
+ {Name: "PrivateAssets.Conflicting.Tag.Attribute", Version: "1.0.0"},
+ {Name: "ExcludeAssets.Tag.ContentFiles", Version: "1.0.0"},
+ {Name: "ExcludeAssets.Tag.None", Version: "1.0.0"},
+ {Name: "ExcludeAssets.Conflicting.Tag.Attribute", Version: "1.0.0"},
+ {Name: "Newtonsoft.Json", Version: "8.0.3"},
+ },
+ },
+ {
+ name: "sad path",
+ inputFile: "testdata/malformed_xml.csproj",
+ wantErr: "failed to decode .csproj file: XML syntax error on line 11: unexpected EOF",
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ f, err := os.Open(tt.inputFile)
+ require.NoError(t, err)
+
+ got, _, err := csproj.NewParser().Parse(f)
+ if tt.wantErr != "" {
+ require.NotNil(t, err)
+ assert.Contains(t, err.Error(), tt.wantErr)
+ return
+ }
+
+ assert.NoError(t, err)
+ assert.ElementsMatch(t, tt.want, got)
+ })
+ }
+}
diff --git a/pkg/nuget/csproj/testdata/dev_dependency.csproj b/pkg/nuget/csproj/testdata/dev_dependency.csproj
new file mode 100644
index 00000000..a07fdd85
--- /dev/null
+++ b/pkg/nuget/csproj/testdata/dev_dependency.csproj
@@ -0,0 +1,43 @@
+
+
+
+ net46
+
+
+
+
+ all
+
+
+ none
+
+
+
+ None
+
+
+
+
+ all
+
+
+ runtime
+
+
+ contentFiles;runtime;native
+
+
+ contentFiles
+
+
+ none
+
+
+
+ None
+
+
+
+
+
+
diff --git a/pkg/nuget/csproj/testdata/malformed_xml.csproj b/pkg/nuget/csproj/testdata/malformed_xml.csproj
new file mode 100644
index 00000000..764f0cb5
--- /dev/null
+++ b/pkg/nuget/csproj/testdata/malformed_xml.csproj
@@ -0,0 +1,10 @@
+
+
+
+ net46
+
+
+
+ all
+
+
diff --git a/pkg/nuget/csproj/testdata/packages.csproj b/pkg/nuget/csproj/testdata/packages.csproj
new file mode 100644
index 00000000..b7370a3c
--- /dev/null
+++ b/pkg/nuget/csproj/testdata/packages.csproj
@@ -0,0 +1,17 @@
+
+
+
+ net45
+
+
+
+
+
+
+
+
+
+
+
+
+