github.com/devseccon/trivy@v0.47.1-0.20231123133102-bd902a0bd996/pkg/fanal/analyzer/language/dotnet/nuget/nuget.go (about)

     1  package nuget
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"io"
     7  	"io/fs"
     8  	"os"
     9  	"path/filepath"
    10  	"sort"
    11  
    12  	"golang.org/x/exp/slices"
    13  	"golang.org/x/xerrors"
    14  
    15  	"github.com/aquasecurity/go-dep-parser/pkg/nuget/config"
    16  	"github.com/aquasecurity/go-dep-parser/pkg/nuget/lock"
    17  	godeptypes "github.com/aquasecurity/go-dep-parser/pkg/types"
    18  	"github.com/devseccon/trivy/pkg/fanal/analyzer"
    19  	"github.com/devseccon/trivy/pkg/fanal/analyzer/language"
    20  	"github.com/devseccon/trivy/pkg/fanal/types"
    21  	"github.com/devseccon/trivy/pkg/utils/fsutils"
    22  )
    23  
    24  func init() {
    25  	analyzer.RegisterPostAnalyzer(analyzer.TypeNuget, newNugetLibraryAnalyzer)
    26  }
    27  
    28  const (
    29  	version    = 3
    30  	lockFile   = types.NuGetPkgsLock
    31  	configFile = types.NuGetPkgsConfig
    32  )
    33  
    34  var requiredFiles = []string{lockFile, configFile}
    35  
    36  type nugetLibraryAnalyzer struct {
    37  	lockParser    godeptypes.Parser
    38  	configParser  godeptypes.Parser
    39  	licenseParser nuspecParser
    40  }
    41  
    42  func newNugetLibraryAnalyzer(_ analyzer.AnalyzerOptions) (analyzer.PostAnalyzer, error) {
    43  	return &nugetLibraryAnalyzer{
    44  		lockParser:    lock.NewParser(),
    45  		configParser:  config.NewParser(),
    46  		licenseParser: newNuspecParser(),
    47  	}, nil
    48  }
    49  
    50  func (a *nugetLibraryAnalyzer) PostAnalyze(_ context.Context, input analyzer.PostAnalysisInput) (*analyzer.AnalysisResult, error) {
    51  	var apps []types.Application
    52  	foundLicenses := make(map[string][]string)
    53  
    54  	// We saved only config and lock files in the FS,
    55  	// so we need to parse all saved files
    56  	required := func(path string, d fs.DirEntry) bool {
    57  		return true
    58  	}
    59  
    60  	err := fsutils.WalkDir(input.FS, ".", required, func(path string, d fs.DirEntry, r io.Reader) error {
    61  		// Set the default parser
    62  		parser := a.lockParser
    63  
    64  		targetFile := filepath.Base(path)
    65  		if targetFile == configFile {
    66  			parser = a.configParser
    67  		}
    68  
    69  		app, err := language.Parse(types.NuGet, path, r, parser)
    70  		if err != nil {
    71  			return xerrors.Errorf("NuGet parse error: %w", err)
    72  		}
    73  
    74  		// nuget file doesn't contain dependencies
    75  		if app == nil {
    76  			return nil
    77  		}
    78  
    79  		for i, lib := range app.Libraries {
    80  			license, ok := foundLicenses[lib.ID]
    81  			if !ok {
    82  				license, err = a.licenseParser.findLicense(lib.Name, lib.Version)
    83  				if err != nil && !errors.Is(err, fs.ErrNotExist) {
    84  					return xerrors.Errorf("license find error: %w", err)
    85  				}
    86  				foundLicenses[lib.ID] = license
    87  			}
    88  
    89  			app.Libraries[i].Licenses = license
    90  		}
    91  
    92  		sort.Sort(app.Libraries)
    93  		apps = append(apps, *app)
    94  		return nil
    95  	})
    96  	if err != nil {
    97  		return nil, xerrors.Errorf("NuGet walk error: %w", err)
    98  	}
    99  
   100  	return &analyzer.AnalysisResult{
   101  		Applications: apps,
   102  	}, nil
   103  }
   104  
   105  func (a *nugetLibraryAnalyzer) Required(filePath string, _ os.FileInfo) bool {
   106  	fileName := filepath.Base(filePath)
   107  	return slices.Contains(requiredFiles, fileName)
   108  }
   109  
   110  func (a *nugetLibraryAnalyzer) Type() analyzer.Type {
   111  	return analyzer.TypeNuget
   112  }
   113  
   114  func (a *nugetLibraryAnalyzer) Version() int {
   115  	return version
   116  }