skipped 5 lines 6 6 package source 7 7 8 8 import ( 9 - "context" 10 - "fmt" 11 - "os" 12 - "path/filepath" 13 - "strings" 14 - "sync" 15 - 16 - "github.com/bmatcuk/doublestar/v4" 17 - "github.com/mholt/archiver/v3" 18 - digest "github.com/opencontainers/go-digest" 19 - "github.com/spf13/afero" 20 - 21 - "github.com/anchore/stereoscope" 22 - "github.com/anchore/stereoscope/pkg/image" 23 - "github.com/anchore/syft/internal/log" 24 9 "github.com/anchore/syft/syft/artifact" 10 + "io" 11 + "strings" 25 12 ) 26 13 27 - // Source is an object that captures the data source to be cataloged, configuration, and a specific resolver used 28 - // in cataloging (based on the data source and configuration) 29 - type Source struct { 30 - id artifact.ID `hash:"ignore"` 31 - Image *image.Image `hash:"ignore"` // the image object to be cataloged (image only) 32 - Metadata Metadata 33 - directoryResolver *directoryResolver `hash:"ignore"` 34 - path string 35 - base string 36 - mutex *sync.Mutex 37 - Exclusions []string `hash:"ignore"` 14 + type Source interface { 15 + artifact.Identifiable 16 + FileResolver(Scope) (FileResolver, error) 17 + Describe() Description 18 + io.Closer 38 19 } 39 20 40 - // Input is an object that captures the detected user input regarding source location, scheme, and provider type. 41 - // It acts as a struct input for some source constructors. 42 - type Input struct { 43 - UserInput string 44 - Scheme Scheme 45 - ImageSource image.Source 46 - Location string 47 - Platform string 48 - Name string 49 - } 21 + / / // Input is an object that captures the detected user input regarding source location, scheme, and provider type. 22 + / / // It acts as a struct input for some source constructors. 23 + / / type Input struct { 24 + / / UserInput string 25 + / / Scheme Scheme 26 + / / ImageSource image.Source 27 + / / Location string 28 + / / Platform string 29 + / / Name string 30 + / / } 31 + // 32 + //// ParseInput generates a source Input that can be used as an argument to generate a new source 33 + //// from specific providers including a registry. 34 + //func ParseInput(userInput string, platform string) (*Input, error) { 35 + // return ParseInputWithName(userInput, platform, "", "") 36 + //} 37 + // 38 + //// ParseInputWithName generates a source Input that can be used as an argument to generate a new source 39 + //// from specific providers including a registry, with an explicit name. 40 + //func ParseInputWithName(userInput string, platform, name, defaultImageSource string) (*Input, error) { 41 + // fs := afero.NewOsFs() 42 + // scheme, source, location, err := DetectScheme(fs, image.DetectSource, userInput) 43 + // if err != nil { 44 + // return nil, err 45 + // } 46 + // 47 + // if source == image.UnknownSource { 48 + // // only run for these two scheme 49 + // // only check on packages command, attest we automatically try to pull from userInput 50 + // switch scheme { 51 + // case ImageScheme, UnknownScheme: 52 + // scheme = ImageScheme 53 + // location = userInput 54 + // if defaultImageSource != "" { 55 + // source = parseDefaultImageSource(defaultImageSource) 56 + // } else { 57 + // imagePullSource := image.DetermineDefaultImagePullSource(userInput) 58 + // source = imagePullSource 59 + // } 60 + // if location == "" { 61 + // location = userInput 62 + // } 63 + // default: 64 + // } 65 + // } 66 + // 67 + // if scheme != ImageScheme && platform != "" { 68 + // return nil, fmt.Errorf("cannot specify a platform for a non-image source") 69 + // } 70 + // 71 + // // collect user input for downstream consumption 72 + // return &Input{ 73 + // UserInput: userInput, 74 + // Scheme: scheme, 75 + // ImageSource: source, 76 + // Location: location, 77 + // Platform: platform, 78 + // Name: name, 79 + // }, nil 80 + //} 81 + // 82 + //func parseDefaultImageSource(defaultImageSource string) image.Source { 83 + // switch defaultImageSource { 84 + // case "registry": 85 + // return image.OciRegistrySource 86 + // case "docker": 87 + // return image.DockerDaemonSource 88 + // case "podman": 89 + // return image.PodmanDaemonSource 90 + // default: 91 + // return image.UnknownSource 92 + // } 93 + //} 94 + // 95 + //type sourceDetector func(string) (image.Source, string, error) 50 96 51 - // ParseInput generates a source Input that can be used as an argument to generate a new source 52 - // from specific providers including a registry. 53 - func ParseInput(userInput string, platform string) (*Input, error) { 54 - return ParseInputWithName(userInput, platform, "", "") 55 - } 97 + //func NewFromRegistry(in Input, registryOptions *image.RegistryOptions, exclusions []string) (*Source, func(), error) { 98 + // source, cleanupFn, err := generateImageSource(in, registryOptions) 99 + // if source != nil { 100 + // source.Exclusions = exclusions 101 + / / } 102 + // return source, cleanupFn, err 103 + //} 104 + // 105 + //// New produces a Source based on userInput like dir: or image:tag 106 + //func New(in Input, registryOptions *image.RegistryOptions, exclusions []string) (*Source, func(), error) { 107 + // var err error 108 + // fs := afero.NewOsFs() 109 + // var source *Source 110 + // cleanupFn := func() {} 111 + // 112 + // switch in.Scheme { 113 + // case FileScheme: 114 + // source, cleanupFn, err = generateFileSource(fs, in) 115 + // case DirectoryScheme: 116 + // source, cleanupFn, err = generateDirectorySource(fs, in) 117 + // case ImageScheme: 118 + // source, cleanupFn, err = generateImageSource(in, registryOptions) 119 + // default: 120 + // err = fmt.Errorf("unable to process input for scanning: %q", in.UserInput) 121 + // } 122 + // 123 + // if err == nil { 124 + // source.Exclusions = exclusions 125 + // } 126 + // 127 + // return source, cleanupFn, err 128 + //} 56 129 57 - // ParseInputWithName generates a source Input that can be used as an argument to generate a new source 58 - // from specific providers including a registry, with an explicit name. 59 - func ParseInputWithName(userInput string, platform, name, defaultImageSource string) (*Input, error) { 60 - fs := afero.NewOsFs() 61 - scheme, source, location, err := DetectScheme(fs, image.DetectSource, userInput) 62 - if err != nil { 63 - return nil, err 64 - } 65 - 66 - if source == image.UnknownSource { 67 - // only run for these two scheme 68 - // only check on packages command, attest we automatically try to pull from userInput 69 - switch scheme { 70 - case ImageScheme, UnknownScheme: 71 - scheme = ImageScheme 72 - location = userInput 73 - if defaultImageSource != "" { 74 - source = parseDefaultImageSource(defaultImageSource) 75 - } else { 76 - imagePullSource := image.DetermineDefaultImagePullSource(userInput) 77 - source = imagePullSource 78 - } 79 - if location == "" { 80 - location = userInput 81 - } 82 - default: 83 - } 84 - } 85 - 86 - if scheme != ImageScheme && platform != "" { 87 - return nil, fmt.Errorf("cannot specify a platform for a non-image source") 88 - } 89 - 90 - // collect user input for downstream consumption 91 - return &Input{ 92 - UserInput: userInput, 93 - Scheme: scheme, 94 - ImageSource: source, 95 - Location: location, 96 - Platform: platform, 97 - Name: name, 98 - }, nil 99 - } 100 - 101 - func parseDefaultImageSource(defaultImageSource string) image.Source { 102 - switch defaultImageSource { 103 - case "registry": 104 - return image.OciRegistrySource 105 - case "docker": 106 - return image.DockerDaemonSource 107 - case "podman": 108 - return image.PodmanDaemonSource 109 - default: 110 - return image.UnknownSource 111 - } 112 - } 113 - 114 - type sourceDetector func(string) (image.Source, string, error) 115 - 116 - func NewFromRegistry(in Input, registryOptions *image.RegistryOptions, exclusions []string) (*Source, func(), error) { 117 - source, cleanupFn, err := generateImageSource(in, registryOptions) 118 - if source != nil { 119 - source.Exclusions = exclusions 120 - } 121 - return source, cleanupFn, err 122 - } 123 - 124 - // New produces a Source based on userInput like dir: or image:tag 125 - func New(in Input, registryOptions *image.RegistryOptions, exclusions []string) (*Source, func(), error) { 126 - var err error 127 - fs := afero.NewOsFs() 128 - var source *Source 129 - cleanupFn := func() {} 130 - 131 - switch in.Scheme { 132 - case FileScheme: 133 - source, cleanupFn, err = generateFileSource(fs, in) 134 - case DirectoryScheme: 135 - source, cleanupFn, err = generateDirectorySource(fs, in) 136 - case ImageScheme: 137 - source, cleanupFn, err = generateImageSource(in, registryOptions) 138 - default: 139 - err = fmt.Errorf("unable to process input for scanning: %q", in.UserInput) 140 - } 141 - 142 - if err == nil { 143 - source.Exclusions = exclusions 144 - } 145 - 146 - return source, cleanupFn, err 147 - } 148 - 149 - func generateImageSource(in Input, registryOptions *image.RegistryOptions) (*Source, func(), error) { 150 - img, cleanup, err := getImageWithRetryStrategy(in, registryOptions) 151 - if err != nil || img == nil { 152 - return nil, cleanup, fmt.Errorf("could not fetch image %q: %w", in.Location, err) 153 - } 154 - 155 - s, err := NewFromImageWithName(img, in.Location, in.Name) 156 - if err != nil { 157 - return nil, cleanup, fmt.Errorf("could not populate source with image: %w", err) 158 - } 159 - 160 - return &s, cleanup, nil 161 - } 130 + //func generateImageSource(in Input, registryOptions *image.RegistryOptions) (*Source, func(), error) { 131 + // img, cleanup, err := getImageWithRetryStrategy(in, registryOptions) 132 + // if err != nil || img == nil { 133 + // return nil, cleanup, fmt.Errorf("could not fetch image %q: %w", in.Location, err) 134 + // } 135 + // 136 + // s, err := NewFromImageWithName(img, in.Location, in.Name) 137 + // if err != nil { 138 + // return nil, cleanup, fmt.Errorf("could not populate source with image: %w", err) 139 + // } 140 + // 141 + // return &s, cleanup, nil 142 + //} 162 143 163 144 func parseScheme(userInput string) string { 164 145 parts := strings.SplitN(userInput, ":", 2) skipped 4 lines 169 150 return parts[0] 170 151 } 171 152 172 - func getImageWithRetryStrategy(in Input, registryOptions *image.RegistryOptions) (*image.Image, func(), error) { 173 - ctx := context.TODO() 153 + //func generateDirectorySource(fs afero.Fs, in Input) (*Source, func(), error) { 154 + // fileMeta, err := fs.Stat(in.Location) 155 + // if err != nil { 156 + // return nil, func() {}, fmt.Errorf("unable to stat dir=%q: %w", in.Location, err) 157 + // } 158 + // 159 + // if !fileMeta.IsDir() { 160 + // return nil, func() {}, fmt.Errorf("given path is not a directory (path=%q): %w", in.Location, err) 161 + // } 162 + // 163 + // s, err := NewFromDirectoryWithName(in.Location, in.Name) 164 + // if err != nil { 165 + // return nil, func() {}, fmt.Errorf("could not populate source from path=%q: %w", in.Location, err) 166 + // } 167 + // 168 + // return &s, func() {}, nil 169 + //} 174 170 175 - var opts []stereoscope.Option 176 - if registryOptions != nil { 177 - opts = append(opts, stereoscope.WithRegistryOptions(*registryOptions)) 178 - } 171 + //func generateFileSource(fs afero.Fs, in Input) (*Source, func(), error) { 172 + // fileMeta, err := fs.Stat(in.Location) 173 + // if err != nil { 174 + // return nil, func() {}, fmt.Errorf("unable to stat dir=%q: %w", in.Location, err) 175 + // } 176 + // 177 + // if fileMeta.IsDir() { 178 + // return nil, func() {}, fmt.Errorf("given path is not a directory (path=%q): %w", in.Location, err) 179 + // } 180 + // 181 + // s, cleanupFn := NewFromFileWithName(in.Location, in.Name) 182 + // 183 + // return &s, cleanupFn, nil 184 + //} 179 185 180 - if in.Platform != "" { 181 - opts = append(opts, stereoscope.WithPlatform(in.Platform)) 182 - } 186 + //// NewFromDirectory creates a new source object tailored to catalog a given filesystem directory recursively. 187 + //func NewFromDirectory(path string) (Source, error) { 188 + // return NewFromDirectoryWithName(path, "") 189 + //} 190 + // 191 + //// NewFromDirectory creates a new source object tailored to catalog a given filesystem directory recursively. 192 + //func NewFromDirectoryRoot(path string) (Source, error) { 193 + // return NewFromDirectoryRootWithName(path, "") 194 + //} 195 + // 196 + //// NewFromDirectoryWithName creates a new source object tailored to catalog a given filesystem directory recursively, with an explicitly provided name. 197 + //func NewFromDirectoryWithName(path string, name string) (Source, error) { 198 + // s := Source{ 199 + // mutex: &sync.Mutex{}, 200 + // Metadata: Metadata{ 201 + // Name: name, 202 + // Scheme: DirectoryScheme, 203 + // Path: path, 204 + // }, 205 + // path: path, 206 + // } 207 + // s.SetID() 208 + // return s, nil 209 + //} 210 + // 211 + //// NewFromDirectoryRootWithName creates a new source object tailored to catalog a given filesystem directory recursively, with an explicitly provided name. 212 + //func NewFromDirectoryRootWithName(path string, name string) (Source, error) { 213 + // s := Source{ 214 + // mutex: &sync.Mutex{}, 215 + // Metadata: Metadata{ 216 + // Name: name, 217 + // Scheme: DirectoryScheme, 218 + // Path: path, 219 + // Base: path, 220 + // }, 221 + // path: path, 222 + // base: path, 223 + // } 224 + // s.SetID() 225 + // return s, nil 226 + //} 183 227 184 - img, err := stereoscope.GetImageFromSource(ctx, in.Location, in.ImageSource, opts...) 185 - cleanup := func() { 186 - if err := img.Cleanup(); err != nil { 187 - log.Warnf("unable to cleanup image=%q: %w", in.UserInput, err) 188 - } 189 - } 190 - if err == nil { 191 - // Success on the first try! 192 - return img, cleanup, nil 193 - } 228 + //// NewFromFile creates a new source object tailored to catalog a file. 229 + //func NewFromFile(path string) (Source, func()) { 230 + // return NewFromFileWithName(path, "") 231 + //} 232 + // 233 + //// NewFromFileWithName creates a new source object tailored to catalog a file, with an explicitly provided name. 234 + //func NewFromFileWithName(path string, name string) (Source, func()) { 235 + // analysisPath, cleanupFn := fileAnalysisPath(path) 236 + // 237 + // s := Source{ 238 + // mutex: &sync.Mutex{}, 239 + // Metadata: Metadata{ 240 + // Name: name, 241 + // Scheme: FileScheme, 242 + // Path: path, 243 + // }, 244 + // path: analysisPath, 245 + // } 246 + // 247 + // s.SetID() 248 + // return s, cleanupFn 249 + //} 194 250 195 - scheme := parseScheme(in.UserInput) 196 - if !(scheme == "docker" || scheme == "registry") { 197 - // Image retrieval failed, and we shouldn't retry it. It's most likely that the 198 - // user _did_ intend the parsed scheme, but there was a legitimate failure with 199 - // using the scheme to load the image. Alert the user to this failure, so they 200 - // can fix the problem. 201 - return nil, nil, err 202 - } 251 + // 252 + //// NewFromImage creates a new source object tailored to catalog a given container image, relative to the 253 + //// option given (e.g. all-layers, squashed, etc) 254 + //func NewFromImage(img *image.Image, userImageStr string) (Source, error) { 255 + // return NewFromImageWithName(img, userImageStr, "") 256 + //} 257 + // 258 + //// NewFromImageWithName creates a new source object tailored to catalog a given container image, relative to the 259 + //// option given (e.g. all-layers, squashed, etc), with an explicit name. 260 + //func NewFromImageWithName(img *image.Image, userImageStr string, name string) (Source, error) { 261 + // if img == nil { 262 + // return Source{}, fmt.Errorf("no image given") 263 + // } 264 + // 265 + // s := Source{ 266 + // Image: img, 267 + // Metadata: Metadata{ 268 + // Name: name, 269 + // Scheme: ImageScheme, 270 + // metadata: NewImageMetadata(img, userImageStr), 271 + // }, 272 + // } 273 + // s.SetID() 274 + // return s, nil 275 + //} 203 276 204 - // Maybe the user wanted "docker" or "registry" to refer to an _image name_ 205 - // (e.g. "docker:latest"), not a scheme. We'll retry image retrieval with this 206 - // alternative interpretation, in an attempt to avoid unnecessary user friction. 207 - 208 - log.Warnf( 209 - "scheme %q specified, but it coincides with a common image name; re-examining user input %q"+ 210 - " without scheme parsing because image retrieval using scheme parsing was unsuccessful: %v", 211 - scheme, 212 - in.UserInput, 213 - err, 214 - ) 215 - 216 - // We need to determine the image source again, such that this determination 217 - // doesn't take scheme parsing into account. 218 - in.ImageSource = image.DetermineDefaultImagePullSource(in.UserInput) 219 - img, err = stereoscope.GetImageFromSource(ctx, in.UserInput, in.ImageSource, opts...) 220 - cleanup = func() { 221 - if err := img.Cleanup(); err != nil { 222 - log.Warnf("unable to cleanup image=%q: %w", in.UserInput, err) 223 - } 224 - } 225 - return img, cleanup, err 226 - } 227 - 228 - func generateDirectorySource(fs afero.Fs, in Input) (*Source, func(), error) { 229 - fileMeta, err := fs.Stat(in.Location) 230 - if err != nil { 231 - return nil, func() {}, fmt.Errorf("unable to stat dir=%q: %w", in.Location, err) 232 - } 233 - 234 - if !fileMeta.IsDir() { 235 - return nil, func() {}, fmt.Errorf("given path is not a directory (path=%q): %w", in.Location, err) 236 - } 237 - 238 - s, err := NewFromDirectoryWithName(in.Location, in.Name) 239 - if err != nil { 240 - return nil, func() {}, fmt.Errorf("could not populate source from path=%q: %w", in.Location, err) 241 - } 242 - 243 - return &s, func() {}, nil 244 - } 245 - 246 - func generateFileSource(fs afero.Fs, in Input) (*Source, func(), error) { 247 - fileMeta, err := fs.Stat(in.Location) 248 - if err != nil { 249 - return nil, func() {}, fmt.Errorf("unable to stat dir=%q: %w", in.Location, err) 250 - } 251 - 252 - if fileMeta.IsDir() { 253 - return nil, func() {}, fmt.Errorf("given path is not a directory (path=%q): %w", in.Location, err) 254 - } 255 - 256 - s, cleanupFn := NewFromFileWithName(in.Location, in.Name) 257 - 258 - return &s, cleanupFn, nil 259 - } 260 - 261 - // NewFromDirectory creates a new source object tailored to catalog a given filesystem directory recursively. 262 - func NewFromDirectory(path string) (Source, error) { 263 - return NewFromDirectoryWithName(path, "") 264 - } 265 - 266 - // NewFromDirectory creates a new source object tailored to catalog a given filesystem directory recursively. 267 - func NewFromDirectoryRoot(path string) (Source, error) { 268 - return NewFromDirectoryRootWithName(path, "") 269 - } 270 - 271 - // NewFromDirectoryWithName creates a new source object tailored to catalog a given filesystem directory recursively, with an explicitly provided name. 272 - func NewFromDirectoryWithName(path string, name string) (Source, error) { 273 - s := Source{ 274 - mutex: &sync.Mutex{}, 275 - Metadata: Metadata{ 276 - Name: name, 277 - Scheme: DirectoryScheme, 278 - Path: path, 279 - }, 280 - path: path, 281 - } 282 - s.SetID() 283 - return s , nil 284 - } 285 - 286 - // NewFromDirectoryRootWithName creates a new source object tailored to catalog a given filesystem directory recursively, with an explicitly provided name. 287 - func NewFromDirectoryRootWithName(path string, name string) (Source, error) { 288 - s := Source{ 289 - mutex: &sync.Mutex{}, 290 - Metadata: Metadata{ 291 - Name: name, 292 - Scheme: DirectoryScheme, 293 - Path: path, 294 - Base: path, 295 - }, 296 - path: path, 297 - base: path, 298 - } 299 - s.SetID() 300 - return s, nil 301 - } 302 - 303 - // NewFromFile creates a new source object tailored to catalog a file. 304 - func NewFromFile(path string) (Source, func()) { 305 - return NewFromFileWithName(path, "") 306 - } 307 - 308 - // NewFromFileWithName creates a new source object tailored to catalog a file, with an explicitly provided name. 309 - func NewFromFileWithName(path string, name string) (Source, func()) { 310 - analysisPath, cleanupFn := fileAnalysisPath(path) 311 - 312 - s := Source{ 313 - mutex: &sync.Mutex{}, 314 - Metadata: Metadata{ 315 - Name: name, 316 - Scheme: FileScheme, 317 - Path: path, 318 - }, 319 - path: analysisPath, 320 - } 321 - 322 - s.SetID() 323 - return s, cleanupFn 324 - } 325 - 326 - // fileAnalysisPath returns the path given, or in the case the path is an archive, the location where the archive 327 - // contents have been made available. A cleanup function is provided for any temp files created (if any). 328 - func fileAnalysisPath(path string) (string, func()) { 329 - var analysisPath = path 330 - var cleanupFn = func() {} 331 - 332 - // if the given file is an archive (as indicated by the file extension and not MIME type) then unarchive it and 333 - // use the contents as the source. Note: this does NOT recursively unarchive contents, only the given path is 334 - // unarchived. 335 - envelopedUnarchiver, err := archiver.ByExtension(path) 336 - if unarchiver, ok := envelopedUnarchiver.(archiver.Unarchiver); err == nil && ok { 337 - if tar, ok := unarchiver.(*archiver.Tar); ok { 338 - // when tar files are extracted, if there are multiple entries at the same 339 - // location, the last entry wins 340 - // NOTE: this currently does not display any messages if an overwrite happens 341 - tar.OverwriteExisting = true 342 - } 343 - unarchivedPath, tmpCleanup, err := unarchiveToTmp(path, unarchiver) 344 - if err != nil { 345 - log.Warnf("file could not be unarchived: %+v", err) 346 - } else { 347 - log.Debugf("source path is an archive") 348 - analysisPath = unarchivedPath 349 - } 350 - if tmpCleanup != nil { 351 - cleanupFn = tmpCleanup 352 - } 353 - } 354 - 355 - return analysisPath, cleanupFn 356 - } 357 - 358 - // NewFromImage creates a new source object tailored to catalog a given container image, relative to the 359 - // option given (e.g. all-layers, squashed, etc) 360 - func NewFromImage(img *image.Image, userImageStr string) (Source, error) { 361 - return NewFromImageWithName(img, userImageStr, "") 362 - } 363 - 364 - // NewFromImageWithName creates a new source object tailored to catalog a given container image, relative to the 365 - // option given (e.g. all-layers, squashed, etc), with an explicit name. 366 - func NewFromImageWithName(img *image.Image, userImageStr string, name string) (Source, error) { 367 - if img == nil { 368 - return Source{}, fmt.Errorf("no image given") 369 - } 370 - 371 - s := Source{ 372 - Image: img, 373 - Metadata: Metadata{ 374 - Name: name, 375 - Scheme: ImageScheme, 376 - ImageMetadata: NewImageMetadata(img, userImageStr), 377 - }, 378 - } 379 - s.SetID() 380 - return s, nil 381 - } 382 - 383 - func (s *Source) ID() artifact.ID { 384 - if s.id == "" { 385 - s.SetID() 386 - } 387 - return s.id 388 - } 389 - 390 - func (s *Source) SetID() { 391 - var d string 392 - switch s.Metadata.Scheme { 393 - case DirectoryScheme: 394 - d = digest.FromString(s.Metadata.Path).String() 395 - case FileScheme: 396 - // attempt to use the digest of the contents of the file as the ID 397 - file, err := os.Open(s.Metadata.Path) 398 - if err != nil { 399 - d = digest.FromString(s.Metadata.Path).String() 400 - break 401 - } 402 - defer file.Close() 403 - di, err := digest.FromReader(file) 404 - if err != nil { 405 - d = digest.FromString(s.Metadata.Path).String() 406 - break 407 - } 408 - d = di.String() 409 - case ImageScheme: 410 - manifestDigest := digest.FromBytes(s.Metadata.ImageMetadata.RawManifest).String() 411 - if manifestDigest != "" { 412 - d = manifestDigest 413 - break 414 - } 415 - 416 - // calcuate chain ID for image sources where manifestDigest is not available 417 - // https://github.com/opencontainers/image-spec/blob/main/config.md#layer-chainid 418 - d = calculateChainID(s.Metadata.ImageMetadata.Layers) 419 - if d == "" { 420 - // TODO what happens here if image has no layers? 421 - // Is this case possible 422 - d = digest.FromString(s.Metadata.ImageMetadata.UserInput).String() 423 - } 424 - default: // for UnknownScheme we hash the struct 425 - id, _ := artifact.IDByHash(s) 426 - d = string(id) 427 - } 428 - 429 - s.id = artifact.ID(strings.TrimPrefix(d, "sha256:")) 430 - s.Metadata.ID = strings.TrimPrefix(d, "sha256:") 431 - } 432 - 433 - func calculateChainID(lm []LayerMetadata) string { 434 - if len(lm) < 1 { 435 - return "" 436 - } 437 - 438 - // DiffID(L0) = digest of layer 0 439 - // https://github.com/anchore/stereoscope/blob/1b1b744a919964f38d14e1416fb3f25221b761ce/pkg/image/layer_metadata.go#L19-L32 440 - chainID := lm[0].Digest 441 - id := chain(chainID, lm[1:]) 442 - 443 - return id 444 - } 445 - 446 - func chain(chainID string, layers []LayerMetadata) string { 447 - if len(layers) < 1 { 448 - return chainID 449 - } 450 - 451 - chainID = digest.FromString(layers[0].Digest + " " + chainID).String() 452 - return chain(chainID, layers[1:]) 453 - } 454 - 455 - func (s *Source) FileResolver(scope Scope) (FileResolver, error) { 456 - switch s.Metadata.Scheme { 457 - case DirectoryScheme, FileScheme: 458 - s.mutex.Lock() 459 - defer s.mutex.Unlock() 460 - if s.directoryResolver == nil { 461 - exclusionFunctions, err := getDirectoryExclusionFunctions(s.path, s.Exclusions) 462 - if err != nil { 463 - return nil, err 464 - } 465 - resolver, err := newDirectoryResolver(s.path, s.base, exclusionFunctions...) 466 - if err != nil { 467 - return nil, fmt.Errorf("unable to create directory resolver: %w", err) 468 - } 469 - s.directoryResolver = resolver 470 - } 471 - return s.directoryResolver, nil 472 - case ImageScheme: 473 - var resolver FileResolver 474 - var err error 475 - switch scope { 476 - case SquashedScope: 477 - resolver, err = newImageSquashResolver(s.Image) 478 - case AllLayersScope: 479 - resolver, err = newAllLayersResolver(s.Image) 480 - default: 481 - return nil, fmt.Errorf("bad image scope provided: %+v", scope) 482 - } 483 - if err != nil { 484 - return nil, err 485 - } 486 - // image tree contains all paths, so we filter out the excluded entries afterwards 487 - if len(s.Exclusions) > 0 { 488 - resolver = NewExcludingResolver(resolver, getImageExclusionFunction(s.Exclusions)) 489 - } 490 - return resolver, nil 491 - } 492 - return nil, fmt.Errorf("unable to determine FilePathResolver with current scheme=%q", s.Metadata.Scheme) 493 - } 494 - 495 - func unarchiveToTmp(path string, unarchiver archiver.Unarchiver) (string, func(), error) { 496 - tempDir, err := os.MkdirTemp("", "syft-archive-contents-") 497 - if err != nil { 498 - return "", func() {}, fmt.Errorf("unable to create tempdir for archive processing: %w", err) 499 - } 500 - 501 - cleanupFn := func() { 502 - if err := os.RemoveAll(tempDir); err != nil { 503 - log.Warnf("unable to cleanup archive tempdir: %+v", err) 504 - } 505 - } 506 - 507 - return tempDir, cleanupFn, unarchiver.Unarchive(path, tempDir) 508 - } 509 - 510 - func getImageExclusionFunction(exclusions []string) func(string) bool { 511 - if len(exclusions) == 0 { 512 - return nil 513 - } 514 - // add subpath exclusions 515 - for _, exclusion := range exclusions { 516 - exclusions = append(exclusions, exclusion+"/**") 517 - } 518 - return func(path string) bool { 519 - for _, exclusion := range exclusions { 520 - matches, err := doublestar.Match(exclusion, path) 521 - if err != nil { 522 - return false 523 - } 524 - if matches { 525 - return true 526 - } 527 - } 528 - return false 529 - } 530 - } 531 - 532 - func getDirectoryExclusionFunctions(root string, exclusions []string) ([]pathIndexVisitor, error) { 533 - if len(exclusions) == 0 { 534 - return nil, nil 535 - } 536 - 537 - // this is what directoryResolver.indexTree is doing to get the absolute path: 538 - root, err := filepath.Abs(root) 539 - if err != nil { 540 - return nil, err 541 - } 542 - 543 - // this handles Windows file paths by converting them to C:/something/else format 544 - root = filepath.ToSlash(root) 545 - 546 - if !strings.HasSuffix(root, "/") { 547 - root += "/" 548 - } 549 - 550 - var errors []string 551 - for idx, exclusion := range exclusions { 552 - // check exclusions for supported paths, these are all relative to the "scan root" 553 - if strings.HasPrefix(exclusion, "./") || strings.HasPrefix(exclusion, "*/") || strings.HasPrefix(exclusion, "**/") { 554 - exclusion = strings.TrimPrefix(exclusion, "./") 555 - exclusions[idx] = root + exclusion 556 - } else { 557 - errors = append(errors, exclusion) 558 - } 559 - } 560 - 561 - if errors != nil { 562 - return nil, fmt.Errorf("invalid exclusion pattern(s): '%s' (must start with one of: './', '*/', or '**/')", strings.Join(errors, "', '")) 563 - } 564 - 565 - return []pathIndexVisitor{ 566 - func(path string, info os.FileInfo, _ error) error { 567 - for _, exclusion := range exclusions { 568 - // this is required to handle Windows filepaths 569 - path = filepath.ToSlash(path) 570 - matches, err := doublestar.Match(exclusion, path) 571 - if err != nil { 572 - return nil 573 - } 574 - if matches { 575 - if info != nil && info.IsDir() { 576 - return filepath.SkipDir 577 - } 578 - return errSkipPath 579 - } 580 - } 581 - return nil 582 - }, 583 - }, nil 584 - } 277 + //func (s *Source) ID() artifact.ID { 278 + // if s.id == "" { 279 + // s.SetID() 280 + // } 281 + // return s.id 282 + //} 283 + // 284 + //func (s *Source) SetID() { 285 + // var d string 286 + // switch s.Metadata.Scheme { 287 + // case DirectoryScheme: 288 + // d = digest.FromString(s.Metadata.Path).String() 289 + // case FileScheme: 290 + // // attempt to use the digest of the contents of the file as the ID 291 + // file, err := os.Open(s.Metadata.Path) 292 + // if err != nil { 293 + // d = digest.FromString(s.Metadata.Path).String() 294 + // break 295 + // } 296 + // defer file.Close() 297 + // di, err := digest.FromReader(file) 298 + // if err != nil { 299 + // d = digest.FromString(s.Metadata.Path).String() 300 + // break 301 + // } 302 + // d = di.String() 303 + // case ImageScheme: 304 + // manifestDigest := digest.FromBytes(s.Metadata.ImageMetadata.RawManifest).String() 305 + // if manifestDigest != "" { 306 + // d = manifestDigest 307 + // break 308 + // } 309 + // 310 + // // calcuate chain ID for image sources where manifestDigest is not available 311 + // // https://github.com/opencontainers/image-spec/blob/main/config.md#layer-chainid 312 + // d = calculateChainID(s.Metadata.ImageMetadata.Layers) 313 + // if d == "" { 314 + // // TODO what happens here if image has no layers? 315 + // // Is this case possible 316 + // d = digest.FromString(s.Metadata.ImageMetadata.UserInput).String() 317 + / / } 318 + // default: // for UnknownScheme we hash the struct 319 + // id, _ := artifact.IDByHash(s) 320 + // d = string(id) 321 + // } 322 + // 323 + // s.id = artifact.ID(strings.TrimPrefix(d, "sha256:")) 324 + // s.Metadata.ID = strings.TrimPrefix(d, "sha256:") 325 + //} 326 + // 327 + //func (s *Source) FileResolver(scope Scope) (FileResolver, error) { 328 + // switch s.Metadata.Scheme { 329 + // case DirectoryScheme, FileScheme: 330 + // s.mutex.Lock() 331 + // defer s.mutex.Unlock() 332 + // if s.directoryResolver == nil { 333 + // exclusionFunctions, err := getDirectoryExclusionFunctions(s.path, s.Exclusions) 334 + // if err != nil { 335 + // return nil, err 336 + // } 337 + // resolver, err := newDirectoryResolver(s.path, s.base, exclusionFunctions...) 338 + // if err != nil { 339 + // return nil, fmt.Errorf("unable to create directory resolver: %w", err) 340 + // } 341 + // s.directoryResolver = resolver 342 + / / } 343 + // return s.directoryResolver, nil 344 + // case ImageScheme: 345 + // var resolver FileResolver 346 + // var err error 347 + // switch scope { 348 + // case SquashedScope: 349 + // resolver, err = newImageSquashResolver(s.Image) 350 + // case AllLayersScope: 351 + // resolver, err = newAllLayersResolver(s.Image) 352 + // default: 353 + // return nil, fmt.Errorf("bad image scope provided: %+v", scope) 354 + / / } 355 + // if err != nil { 356 + / / return nil , err 357 + / / } 358 + // // image tree contains all paths, so we filter out the excluded entries afterwards 359 + // if len(s.Exclusions) > 0 { 360 + // resolver = NewExcludingResolver(resolver, getImageExclusionFunction(s.Exclusions)) 361 + // } 362 + // return resolver, nil 363 + // } 364 + // return nil, fmt.Errorf("unable to determine FilePathResolver with current scheme=%q", s.Metadata.Scheme) 365 + //} 585 366