Skip to content

Commit

Permalink
feat(nuget): Parse deps specified as PackageReference in .csproj files
Browse files Browse the repository at this point in the history
C# project files, with the extension .csproj, are XML files that can specify project dependencies in `<PackageReference>` tags.

See also:
* aquasecurity/trivy#2668
* https://docs.microsoft.com/en-us/nuget/consume-packages/package-references-in-project-files
  • Loading branch information
Sjord committed Aug 8, 2022
1 parent b5a21d2 commit 0f8900c
Show file tree
Hide file tree
Showing 5 changed files with 148 additions and 0 deletions.
52 changes: 52 additions & 0 deletions pkg/nuget/csproj/parse.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package csproj

import (
"encoding/xml"

"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"`
PrivateAssets string `xml:"PrivateAssets"`
}

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 == "" || pkg.PrivateAssets != "" {
continue
}

lib := types.Library{
Name: pkg.Include,
Version: pkg.Version,
}

libs = append(libs, lib)
}

return utils.UniqueLibraries(libs), nil, nil
}
58 changes: 58 additions & 0 deletions pkg/nuget/csproj/parse_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
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: "with development dependency",
inputFile: "testdata/dev_dependency.csproj",
want: []types.Library{
{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)
})
}
}
12 changes: 12 additions & 0 deletions pkg/nuget/csproj/testdata/dev_dependency.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<TargetFramework>net46</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Net.Compilers" Version="1.0.0">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Newtonsoft.Json" Version="8.0.3" />
</ItemGroup>
</Project>
10 changes: 10 additions & 0 deletions pkg/nuget/csproj/testdata/malformed_xml.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<TargetFramework>net46</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Net.Compilers" Version="1.0.0">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Newtonsoft.Json" Version="8.0.3" />
16 changes: 16 additions & 0 deletions pkg/nuget/csproj/testdata/packages.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<TargetFramework>net45</TargetFramework>
</PropertyGroup>
<ItemGroup>
<!-- other ItemGroup elements can lack PackageReference elements -->
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNet.WebApi" Version="5.2.2" />
<PackageReference Include="Newtonsoft.Json" Version="6.0.4" />
</ItemGroup>
<ItemGroup>
<!-- other ItemGroup elements can lack PackageReference elements -->
</ItemGroup>
</Project>

0 comments on commit 0f8900c

Please sign in to comment.