github.com/anchore/syft@v1.38.2/internal/task/file_tasks.go (about)

     1  package task
     2  
     3  import (
     4  	"context"
     5  	"crypto"
     6  
     7  	"github.com/anchore/syft/internal/sbomsync"
     8  	"github.com/anchore/syft/syft/artifact"
     9  	"github.com/anchore/syft/syft/cataloging/filecataloging"
    10  	"github.com/anchore/syft/syft/file"
    11  	"github.com/anchore/syft/syft/file/cataloger/executable"
    12  	"github.com/anchore/syft/syft/file/cataloger/filecontent"
    13  	"github.com/anchore/syft/syft/file/cataloger/filedigest"
    14  	"github.com/anchore/syft/syft/file/cataloger/filemetadata"
    15  	"github.com/anchore/syft/syft/pkg"
    16  	"github.com/anchore/syft/syft/sbom"
    17  )
    18  
    19  func DefaultFileTaskFactories() Factories {
    20  	return Factories{
    21  		newFileDigestCatalogerTaskFactory("digest"),
    22  		newFileMetadataCatalogerTaskFactory("file-metadata"),
    23  		newFileContentCatalogerTaskFactory("content"),
    24  		newExecutableCatalogerTaskFactory("binary-metadata"),
    25  	}
    26  }
    27  
    28  func newFileDigestCatalogerTaskFactory(tags ...string) factory {
    29  	return func(cfg CatalogingFactoryConfig) Task {
    30  		return newFileDigestCatalogerTask(cfg.FilesConfig.Selection, cfg.FilesConfig.Hashers, tags...)
    31  	}
    32  }
    33  
    34  func newFileDigestCatalogerTask(selection file.Selection, hashers []crypto.Hash, tags ...string) Task {
    35  	fn := func(ctx context.Context, resolver file.Resolver, builder sbomsync.Builder) error {
    36  		if selection == file.NoFilesSelection || len(hashers) == 0 {
    37  			return nil
    38  		}
    39  
    40  		accessor := builder.(sbomsync.Accessor)
    41  
    42  		coordinates, ok := coordinatesForSelection(selection, builder.(sbomsync.Accessor))
    43  		if !ok {
    44  			return nil
    45  		}
    46  
    47  		result, err := filedigest.NewCataloger(hashers).Catalog(ctx, resolver, coordinates...)
    48  
    49  		accessor.WriteToSBOM(func(sbom *sbom.SBOM) {
    50  			sbom.Artifacts.FileDigests = result
    51  		})
    52  
    53  		return err
    54  	}
    55  
    56  	return NewTask("file-digest-cataloger", fn, commonFileTags(tags)...)
    57  }
    58  
    59  func newFileMetadataCatalogerTaskFactory(tags ...string) factory {
    60  	return func(cfg CatalogingFactoryConfig) Task {
    61  		return newFileMetadataCatalogerTask(cfg.FilesConfig.Selection, tags...)
    62  	}
    63  }
    64  
    65  func newFileMetadataCatalogerTask(selection file.Selection, tags ...string) Task {
    66  	fn := func(ctx context.Context, resolver file.Resolver, builder sbomsync.Builder) error {
    67  		if selection == file.NoFilesSelection {
    68  			return nil
    69  		}
    70  
    71  		accessor := builder.(sbomsync.Accessor)
    72  
    73  		coordinates, ok := coordinatesForSelection(selection, builder.(sbomsync.Accessor))
    74  		if !ok {
    75  			return nil
    76  		}
    77  
    78  		result, err := filemetadata.NewCataloger().Catalog(ctx, resolver, coordinates...)
    79  
    80  		accessor.WriteToSBOM(func(sbom *sbom.SBOM) {
    81  			sbom.Artifacts.FileMetadata = result
    82  		})
    83  
    84  		return err
    85  	}
    86  
    87  	return NewTask("file-metadata-cataloger", fn, commonFileTags(tags)...)
    88  }
    89  
    90  func newFileContentCatalogerTaskFactory(tags ...string) factory {
    91  	return func(cfg CatalogingFactoryConfig) Task {
    92  		return newFileContentCatalogerTask(cfg.FilesConfig.Content, tags...)
    93  	}
    94  }
    95  
    96  func newFileContentCatalogerTask(cfg filecontent.Config, tags ...string) Task {
    97  	fn := func(ctx context.Context, resolver file.Resolver, builder sbomsync.Builder) error {
    98  		if len(cfg.Globs) == 0 {
    99  			return nil
   100  		}
   101  
   102  		accessor := builder.(sbomsync.Accessor)
   103  
   104  		result, err := filecontent.NewCataloger(cfg).Catalog(ctx, resolver)
   105  
   106  		accessor.WriteToSBOM(func(sbom *sbom.SBOM) {
   107  			sbom.Artifacts.FileContents = result
   108  		})
   109  
   110  		return err
   111  	}
   112  
   113  	return NewTask("file-content-cataloger", fn, commonFileTags(tags)...)
   114  }
   115  
   116  func newExecutableCatalogerTaskFactory(tags ...string) factory {
   117  	return func(cfg CatalogingFactoryConfig) Task {
   118  		return newExecutableCatalogerTask(cfg.FilesConfig.Selection, cfg.FilesConfig.Executable, tags...)
   119  	}
   120  }
   121  
   122  func newExecutableCatalogerTask(selection file.Selection, cfg executable.Config, tags ...string) Task {
   123  	fn := func(ctx context.Context, resolver file.Resolver, builder sbomsync.Builder) error {
   124  		if selection == file.NoFilesSelection {
   125  			return nil
   126  		}
   127  
   128  		accessor := builder.(sbomsync.Accessor)
   129  
   130  		result, err := executable.NewCataloger(cfg).CatalogCtx(ctx, resolver)
   131  
   132  		accessor.WriteToSBOM(func(sbom *sbom.SBOM) {
   133  			sbom.Artifacts.Executables = result
   134  		})
   135  
   136  		return err
   137  	}
   138  
   139  	return NewTask("file-executable-cataloger", fn, commonFileTags(tags)...)
   140  }
   141  
   142  // TODO: this should be replaced with a fix that allows passing a coordinate or location iterator to the cataloger
   143  // Today internal to both cataloger this functions differently: a slice of coordinates vs a channel of locations
   144  func coordinatesForSelection(selection file.Selection, accessor sbomsync.Accessor) ([]file.Coordinates, bool) {
   145  	if selection == file.AllFilesSelection {
   146  		return nil, true
   147  	}
   148  
   149  	if selection == file.FilesOwnedByPackageSelection {
   150  		var coordinates file.CoordinateSet
   151  
   152  		accessor.ReadFromSBOM(func(sbom *sbom.SBOM) {
   153  			// get any file coordinates that are owned by a package
   154  			for _, r := range sbom.Relationships {
   155  				if r.Type != artifact.ContainsRelationship {
   156  					continue
   157  				}
   158  				if _, ok := r.From.(pkg.Package); !ok {
   159  					continue
   160  				}
   161  				if c, ok := r.To.(file.Coordinates); ok {
   162  					coordinates.Add(c)
   163  				}
   164  			}
   165  
   166  			// get any file coordinates referenced by a package directly
   167  			for p := range sbom.Artifacts.Packages.Enumerate() {
   168  				coordinates.Add(p.Locations.CoordinateSet().ToSlice()...)
   169  			}
   170  		})
   171  
   172  		coords := coordinates.ToSlice()
   173  
   174  		if len(coords) == 0 {
   175  			return nil, false
   176  		}
   177  
   178  		return coords, true
   179  	}
   180  
   181  	return nil, false
   182  }
   183  
   184  func commonFileTags(tags []string) []string {
   185  	tags = append(tags, filecataloging.FileTag)
   186  	return tags
   187  }