github.com/nextlinux/gosbom@v0.81.1-0.20230627115839-1ff50c281391/gosbom/pkg/cataloger/cataloger.go (about)

     1  /*
     2  Package cataloger provides the ability to process files from a container image or file system and discover packages
     3  (gems, wheels, jars, rpms, debs, etc). Specifically, this package contains both a catalog function to utilize all
     4  catalogers defined in child packages as well as the interface definition to implement a cataloger.
     5  */
     6  package cataloger
     7  
     8  import (
     9  	"strings"
    10  
    11  	"github.com/nextlinux/gosbom/gosbom/pkg"
    12  	"github.com/nextlinux/gosbom/gosbom/pkg/cataloger/alpm"
    13  	"github.com/nextlinux/gosbom/gosbom/pkg/cataloger/apkdb"
    14  	"github.com/nextlinux/gosbom/gosbom/pkg/cataloger/binary"
    15  	"github.com/nextlinux/gosbom/gosbom/pkg/cataloger/cpp"
    16  	"github.com/nextlinux/gosbom/gosbom/pkg/cataloger/dart"
    17  	"github.com/nextlinux/gosbom/gosbom/pkg/cataloger/deb"
    18  	"github.com/nextlinux/gosbom/gosbom/pkg/cataloger/dotnet"
    19  	"github.com/nextlinux/gosbom/gosbom/pkg/cataloger/elixir"
    20  	"github.com/nextlinux/gosbom/gosbom/pkg/cataloger/erlang"
    21  	"github.com/nextlinux/gosbom/gosbom/pkg/cataloger/golang"
    22  	"github.com/nextlinux/gosbom/gosbom/pkg/cataloger/haskell"
    23  	"github.com/nextlinux/gosbom/gosbom/pkg/cataloger/java"
    24  	"github.com/nextlinux/gosbom/gosbom/pkg/cataloger/javascript"
    25  	"github.com/nextlinux/gosbom/gosbom/pkg/cataloger/kernel"
    26  	"github.com/nextlinux/gosbom/gosbom/pkg/cataloger/nix"
    27  	"github.com/nextlinux/gosbom/gosbom/pkg/cataloger/php"
    28  	"github.com/nextlinux/gosbom/gosbom/pkg/cataloger/portage"
    29  	"github.com/nextlinux/gosbom/gosbom/pkg/cataloger/python"
    30  	"github.com/nextlinux/gosbom/gosbom/pkg/cataloger/r"
    31  	"github.com/nextlinux/gosbom/gosbom/pkg/cataloger/rpm"
    32  	"github.com/nextlinux/gosbom/gosbom/pkg/cataloger/ruby"
    33  	"github.com/nextlinux/gosbom/gosbom/pkg/cataloger/rust"
    34  	"github.com/nextlinux/gosbom/gosbom/pkg/cataloger/sbom"
    35  	"github.com/nextlinux/gosbom/gosbom/pkg/cataloger/swift"
    36  	"github.com/nextlinux/gosbom/internal/log"
    37  )
    38  
    39  const AllCatalogersPattern = "all"
    40  
    41  // ImageCatalogers returns a slice of locally implemented catalogers that are fit for detecting installations of packages.
    42  func ImageCatalogers(cfg Config) []pkg.Cataloger {
    43  	return filterCatalogers([]pkg.Cataloger{
    44  		alpm.NewAlpmdbCataloger(),
    45  		apkdb.NewApkdbCataloger(),
    46  		binary.NewCataloger(),
    47  		deb.NewDpkgdbCataloger(),
    48  		dotnet.NewDotnetDepsCataloger(),
    49  		golang.NewGoModuleBinaryCataloger(cfg.Go()),
    50  		java.NewJavaCataloger(cfg.Java()),
    51  		java.NewNativeImageCataloger(),
    52  		javascript.NewPackageCataloger(),
    53  		nix.NewStoreCataloger(),
    54  		php.NewComposerInstalledCataloger(),
    55  		portage.NewPortageCataloger(),
    56  		python.NewPythonPackageCataloger(),
    57  		r.NewPackageCataloger(),
    58  		rpm.NewRpmDBCataloger(),
    59  		ruby.NewGemSpecCataloger(),
    60  		sbom.NewSBOMCataloger(),
    61  	}, cfg.Catalogers)
    62  }
    63  
    64  // DirectoryCatalogers returns a slice of locally implemented catalogers that are fit for detecting packages from index files (and select installations)
    65  func DirectoryCatalogers(cfg Config) []pkg.Cataloger {
    66  	return filterCatalogers([]pkg.Cataloger{
    67  		alpm.NewAlpmdbCataloger(),
    68  		apkdb.NewApkdbCataloger(),
    69  		binary.NewCataloger(),
    70  		cpp.NewConanCataloger(),
    71  		dart.NewPubspecLockCataloger(),
    72  		deb.NewDpkgdbCataloger(),
    73  		dotnet.NewDotnetDepsCataloger(),
    74  		elixir.NewMixLockCataloger(),
    75  		erlang.NewRebarLockCataloger(),
    76  		golang.NewGoModFileCataloger(cfg.Go()),
    77  		golang.NewGoModuleBinaryCataloger(cfg.Go()),
    78  		haskell.NewHackageCataloger(),
    79  		java.NewJavaCataloger(cfg.Java()),
    80  		java.NewJavaGradleLockfileCataloger(),
    81  		java.NewJavaPomCataloger(),
    82  		java.NewNativeImageCataloger(),
    83  		javascript.NewLockCataloger(),
    84  		nix.NewStoreCataloger(),
    85  		php.NewComposerLockCataloger(),
    86  		portage.NewPortageCataloger(),
    87  		python.NewPythonIndexCataloger(),
    88  		python.NewPythonPackageCataloger(),
    89  		rpm.NewFileCataloger(),
    90  		rpm.NewRpmDBCataloger(),
    91  		ruby.NewGemFileLockCataloger(),
    92  		rust.NewCargoLockCataloger(),
    93  		sbom.NewSBOMCataloger(),
    94  		swift.NewCocoapodsCataloger(),
    95  	}, cfg.Catalogers)
    96  }
    97  
    98  // AllCatalogers returns all implemented catalogers
    99  func AllCatalogers(cfg Config) []pkg.Cataloger {
   100  	return filterCatalogers([]pkg.Cataloger{
   101  		alpm.NewAlpmdbCataloger(),
   102  		apkdb.NewApkdbCataloger(),
   103  		binary.NewCataloger(),
   104  		cpp.NewConanCataloger(),
   105  		dart.NewPubspecLockCataloger(),
   106  		deb.NewDpkgdbCataloger(),
   107  		dotnet.NewDotnetDepsCataloger(),
   108  		elixir.NewMixLockCataloger(),
   109  		erlang.NewRebarLockCataloger(),
   110  		golang.NewGoModFileCataloger(cfg.Go()),
   111  		golang.NewGoModuleBinaryCataloger(cfg.Go()),
   112  		haskell.NewHackageCataloger(),
   113  		java.NewJavaCataloger(cfg.Java()),
   114  		java.NewJavaGradleLockfileCataloger(),
   115  		java.NewJavaPomCataloger(),
   116  		java.NewNativeImageCataloger(),
   117  		javascript.NewLockCataloger(),
   118  		javascript.NewPackageCataloger(),
   119  		kernel.NewLinuxKernelCataloger(cfg.Kernel()),
   120  		nix.NewStoreCataloger(),
   121  		php.NewComposerInstalledCataloger(),
   122  		php.NewComposerLockCataloger(),
   123  		portage.NewPortageCataloger(),
   124  		python.NewPythonIndexCataloger(),
   125  		python.NewPythonPackageCataloger(),
   126  		r.NewPackageCataloger(),
   127  		rpm.NewFileCataloger(),
   128  		rpm.NewRpmDBCataloger(),
   129  		ruby.NewGemFileLockCataloger(),
   130  		ruby.NewGemSpecCataloger(),
   131  		rust.NewAuditBinaryCataloger(),
   132  		rust.NewCargoLockCataloger(),
   133  		sbom.NewSBOMCataloger(),
   134  		swift.NewCocoapodsCataloger(),
   135  	}, cfg.Catalogers)
   136  }
   137  
   138  func RequestedAllCatalogers(cfg Config) bool {
   139  	for _, enableCatalogerPattern := range cfg.Catalogers {
   140  		if enableCatalogerPattern == AllCatalogersPattern {
   141  			return true
   142  		}
   143  	}
   144  	return false
   145  }
   146  
   147  func filterCatalogers(catalogers []pkg.Cataloger, enabledCatalogerPatterns []string) []pkg.Cataloger {
   148  	// if cataloger is not set, all applicable catalogers are enabled by default
   149  	if len(enabledCatalogerPatterns) == 0 {
   150  		return catalogers
   151  	}
   152  	for _, enableCatalogerPattern := range enabledCatalogerPatterns {
   153  		if enableCatalogerPattern == AllCatalogersPattern {
   154  			return catalogers
   155  		}
   156  	}
   157  	var keepCatalogers []pkg.Cataloger
   158  	for _, cataloger := range catalogers {
   159  		if contains(enabledCatalogerPatterns, cataloger.Name()) {
   160  			keepCatalogers = append(keepCatalogers, cataloger)
   161  			continue
   162  		}
   163  		log.Infof("skipping cataloger %q", cataloger.Name())
   164  	}
   165  	return keepCatalogers
   166  }
   167  
   168  func contains(enabledPartial []string, catalogerName string) bool {
   169  	catalogerName = strings.TrimSuffix(catalogerName, "-cataloger")
   170  	for _, partial := range enabledPartial {
   171  		partial = strings.TrimSuffix(partial, "-cataloger")
   172  		if partial == "" {
   173  			continue
   174  		}
   175  		if hasFullWord(partial, catalogerName) {
   176  			return true
   177  		}
   178  	}
   179  	return false
   180  }
   181  
   182  func hasFullWord(targetPhrase, candidate string) bool {
   183  	if targetPhrase == "cataloger" || targetPhrase == "" {
   184  		return false
   185  	}
   186  	start := strings.Index(candidate, targetPhrase)
   187  	if start == -1 {
   188  		return false
   189  	}
   190  
   191  	if start > 0 && candidate[start-1] != '-' {
   192  		return false
   193  	}
   194  
   195  	end := start + len(targetPhrase)
   196  	if end < len(candidate) && candidate[end] != '-' {
   197  		return false
   198  	}
   199  	return true
   200  }