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 }