github.com/kastenhq/syft@v0.0.0-20230821225854-0710af25cdbe/syft/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/kastenhq/syft/internal/log"
    12  	"github.com/kastenhq/syft/syft/pkg"
    13  	"github.com/kastenhq/syft/syft/pkg/cataloger/alpm"
    14  	"github.com/kastenhq/syft/syft/pkg/cataloger/apkdb"
    15  	"github.com/kastenhq/syft/syft/pkg/cataloger/binary"
    16  	"github.com/kastenhq/syft/syft/pkg/cataloger/cpp"
    17  	"github.com/kastenhq/syft/syft/pkg/cataloger/dart"
    18  	"github.com/kastenhq/syft/syft/pkg/cataloger/deb"
    19  	"github.com/kastenhq/syft/syft/pkg/cataloger/dotnet"
    20  	"github.com/kastenhq/syft/syft/pkg/cataloger/elixir"
    21  	"github.com/kastenhq/syft/syft/pkg/cataloger/erlang"
    22  	"github.com/kastenhq/syft/syft/pkg/cataloger/golang"
    23  	"github.com/kastenhq/syft/syft/pkg/cataloger/haskell"
    24  	"github.com/kastenhq/syft/syft/pkg/cataloger/java"
    25  	"github.com/kastenhq/syft/syft/pkg/cataloger/javascript"
    26  	"github.com/kastenhq/syft/syft/pkg/cataloger/kernel"
    27  	"github.com/kastenhq/syft/syft/pkg/cataloger/nix"
    28  	"github.com/kastenhq/syft/syft/pkg/cataloger/php"
    29  	"github.com/kastenhq/syft/syft/pkg/cataloger/portage"
    30  	"github.com/kastenhq/syft/syft/pkg/cataloger/python"
    31  	"github.com/kastenhq/syft/syft/pkg/cataloger/r"
    32  	"github.com/kastenhq/syft/syft/pkg/cataloger/rpm"
    33  	"github.com/kastenhq/syft/syft/pkg/cataloger/ruby"
    34  	"github.com/kastenhq/syft/syft/pkg/cataloger/rust"
    35  	"github.com/kastenhq/syft/syft/pkg/cataloger/sbom"
    36  	"github.com/kastenhq/syft/syft/pkg/cataloger/swift"
    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.NewDotnetPortableExecutableCataloger(),
    49  		golang.NewGoModuleBinaryCataloger(cfg.Golang),
    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  		dotnet.NewDotnetPortableExecutableCataloger(),
    75  		elixir.NewMixLockCataloger(),
    76  		erlang.NewRebarLockCataloger(),
    77  		golang.NewGoModFileCataloger(cfg.Golang),
    78  		golang.NewGoModuleBinaryCataloger(cfg.Golang),
    79  		haskell.NewHackageCataloger(),
    80  		java.NewJavaCataloger(cfg.Java()),
    81  		java.NewJavaGradleLockfileCataloger(),
    82  		java.NewJavaPomCataloger(),
    83  		java.NewNativeImageCataloger(),
    84  		javascript.NewLockCataloger(),
    85  		nix.NewStoreCataloger(),
    86  		php.NewComposerLockCataloger(),
    87  		portage.NewPortageCataloger(),
    88  		python.NewPythonIndexCataloger(cfg.Python),
    89  		python.NewPythonPackageCataloger(),
    90  		rpm.NewFileCataloger(),
    91  		rpm.NewRpmDBCataloger(),
    92  		ruby.NewGemFileLockCataloger(),
    93  		rust.NewCargoLockCataloger(),
    94  		sbom.NewSBOMCataloger(),
    95  		swift.NewCocoapodsCataloger(),
    96  		swift.NewSwiftPackageManagerCataloger(),
    97  	}, cfg.Catalogers)
    98  }
    99  
   100  // AllCatalogers returns all implemented catalogers
   101  func AllCatalogers(cfg Config) []pkg.Cataloger {
   102  	return filterCatalogers([]pkg.Cataloger{
   103  		alpm.NewAlpmdbCataloger(),
   104  		apkdb.NewApkdbCataloger(),
   105  		binary.NewCataloger(),
   106  		cpp.NewConanCataloger(),
   107  		dart.NewPubspecLockCataloger(),
   108  		deb.NewDpkgdbCataloger(),
   109  		dotnet.NewDotnetDepsCataloger(),
   110  		dotnet.NewDotnetPortableExecutableCataloger(),
   111  		elixir.NewMixLockCataloger(),
   112  		erlang.NewRebarLockCataloger(),
   113  		golang.NewGoModFileCataloger(cfg.Golang),
   114  		golang.NewGoModuleBinaryCataloger(cfg.Golang),
   115  		haskell.NewHackageCataloger(),
   116  		java.NewJavaCataloger(cfg.Java()),
   117  		java.NewJavaGradleLockfileCataloger(),
   118  		java.NewJavaPomCataloger(),
   119  		java.NewNativeImageCataloger(),
   120  		javascript.NewLockCataloger(),
   121  		javascript.NewPackageCataloger(),
   122  		kernel.NewLinuxKernelCataloger(cfg.LinuxKernel),
   123  		nix.NewStoreCataloger(),
   124  		php.NewComposerInstalledCataloger(),
   125  		php.NewComposerLockCataloger(),
   126  		portage.NewPortageCataloger(),
   127  		python.NewPythonIndexCataloger(cfg.Python),
   128  		python.NewPythonPackageCataloger(),
   129  		r.NewPackageCataloger(),
   130  		rpm.NewFileCataloger(),
   131  		rpm.NewRpmDBCataloger(),
   132  		ruby.NewGemFileLockCataloger(),
   133  		ruby.NewGemSpecCataloger(),
   134  		rust.NewAuditBinaryCataloger(),
   135  		rust.NewCargoLockCataloger(),
   136  		sbom.NewSBOMCataloger(),
   137  		swift.NewCocoapodsCataloger(),
   138  		swift.NewSwiftPackageManagerCataloger(),
   139  	}, cfg.Catalogers)
   140  }
   141  
   142  func RequestedAllCatalogers(cfg Config) bool {
   143  	for _, enableCatalogerPattern := range cfg.Catalogers {
   144  		if enableCatalogerPattern == AllCatalogersPattern {
   145  			return true
   146  		}
   147  	}
   148  	return false
   149  }
   150  
   151  func filterCatalogers(catalogers []pkg.Cataloger, enabledCatalogerPatterns []string) []pkg.Cataloger {
   152  	// if cataloger is not set, all applicable catalogers are enabled by default
   153  	if len(enabledCatalogerPatterns) == 0 {
   154  		return catalogers
   155  	}
   156  	for _, enableCatalogerPattern := range enabledCatalogerPatterns {
   157  		if enableCatalogerPattern == AllCatalogersPattern {
   158  			return catalogers
   159  		}
   160  	}
   161  	var keepCatalogers []pkg.Cataloger
   162  	for _, cataloger := range catalogers {
   163  		if contains(enabledCatalogerPatterns, cataloger.Name()) {
   164  			keepCatalogers = append(keepCatalogers, cataloger)
   165  			continue
   166  		}
   167  		log.Infof("skipping cataloger %q", cataloger.Name())
   168  	}
   169  	return keepCatalogers
   170  }
   171  
   172  func contains(enabledPartial []string, catalogerName string) bool {
   173  	catalogerName = strings.TrimSuffix(catalogerName, "-cataloger")
   174  	for _, partial := range enabledPartial {
   175  		partial = strings.TrimSuffix(partial, "-cataloger")
   176  		if partial == "" {
   177  			continue
   178  		}
   179  		if hasFullWord(partial, catalogerName) {
   180  			return true
   181  		}
   182  	}
   183  	return false
   184  }
   185  
   186  func hasFullWord(targetPhrase, candidate string) bool {
   187  	if targetPhrase == "cataloger" || targetPhrase == "" {
   188  		return false
   189  	}
   190  	start := strings.Index(candidate, targetPhrase)
   191  	if start == -1 {
   192  		return false
   193  	}
   194  
   195  	if start > 0 && candidate[start-1] != '-' {
   196  		return false
   197  	}
   198  
   199  	end := start + len(targetPhrase)
   200  	if end < len(candidate) && candidate[end] != '-' {
   201  		return false
   202  	}
   203  	return true
   204  }