mirror of
https://github.com/anchore/syft.git
synced 2025-11-17 00:13:15 +01:00
--------- Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Signed-off-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com> Co-authored-by: spiffcs <32073428+spiffcs@users.noreply.github.com>
133 lines
3.9 KiB
Go
133 lines
3.9 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"path"
|
|
|
|
"github.com/anchore/syft/internal"
|
|
"github.com/anchore/syft/syft/artifact"
|
|
"github.com/anchore/syft/syft/file"
|
|
"github.com/anchore/syft/syft/pkg"
|
|
)
|
|
|
|
/*
|
|
This is a contrived cataloger that attempts to capture useful APK files from the image as if it were a package.
|
|
This isn't a real cataloger, but it is a good example of how to use API elements to create a custom cataloger.
|
|
*/
|
|
|
|
var _ pkg.Cataloger = (*alpineConfigurationCataloger)(nil)
|
|
|
|
type alpineConfigurationCataloger struct {
|
|
}
|
|
|
|
func newAlpineConfigurationCataloger() pkg.Cataloger {
|
|
return alpineConfigurationCataloger{}
|
|
}
|
|
|
|
func (m alpineConfigurationCataloger) Name() string {
|
|
return "apk-configuration-cataloger"
|
|
}
|
|
|
|
func (m alpineConfigurationCataloger) Catalog(_ context.Context, resolver file.Resolver) ([]pkg.Package, []artifact.Relationship, error) {
|
|
version, versionLocations, err := getVersion(resolver)
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("unable to get alpine version: %w", err)
|
|
}
|
|
if len(versionLocations) == 0 {
|
|
// this doesn't mean we should stop cataloging, just that we don't have a version to use, thus no package to raise up
|
|
return nil, nil, nil
|
|
}
|
|
|
|
metadata, metadataLocations, err := newAlpineConfiguration(resolver)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
var locations []file.Location
|
|
locations = append(locations, versionLocations...)
|
|
locations = append(locations, metadataLocations...)
|
|
|
|
p := newPackage(version, *metadata, locations...)
|
|
|
|
return []pkg.Package{p}, nil, nil
|
|
}
|
|
|
|
func newPackage(version string, metadata AlpineConfiguration, locations ...file.Location) pkg.Package {
|
|
return pkg.Package{
|
|
Name: "alpine-configuration",
|
|
Version: version,
|
|
Locations: file.NewLocationSet(locations...),
|
|
Type: pkg.Type("system-configuration"), // you can make up your own package type here or use an existing one
|
|
Metadata: metadata,
|
|
}
|
|
}
|
|
|
|
func newAlpineConfiguration(resolver file.Resolver) (*AlpineConfiguration, []file.Location, error) {
|
|
var locations []file.Location
|
|
|
|
keys, keyLocations, err := getAPKKeys(resolver)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
locations = append(locations, keyLocations...)
|
|
|
|
return &AlpineConfiguration{
|
|
APKKeys: keys,
|
|
}, locations, nil
|
|
}
|
|
|
|
func getVersion(resolver file.Resolver) (string, []file.Location, error) {
|
|
locations, err := resolver.FilesByPath("/etc/alpine-release")
|
|
if err != nil {
|
|
return "", nil, fmt.Errorf("unable to get alpine version: %w", err)
|
|
}
|
|
if len(locations) == 0 {
|
|
return "", nil, nil
|
|
}
|
|
|
|
reader, err := resolver.FileContentsByLocation(locations[0])
|
|
if err != nil {
|
|
return "", nil, fmt.Errorf("unable to read alpine version: %w", err)
|
|
}
|
|
defer internal.CloseAndLogError(reader, locations[0].RealPath)
|
|
|
|
version, err := io.ReadAll(reader)
|
|
if err != nil {
|
|
return "", nil, fmt.Errorf("unable to read alpine version: %w", err)
|
|
}
|
|
|
|
return string(version), locations, nil
|
|
}
|
|
|
|
func getAPKKeys(resolver file.Resolver) (map[string]string, []file.Location, error) {
|
|
// name-to-content values
|
|
keyContent := make(map[string]string)
|
|
|
|
locations, err := resolver.FilesByGlob("/etc/apk/keys/*.rsa.pub")
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("unable to get apk keys: %w", err)
|
|
}
|
|
for _, location := range locations {
|
|
basename := path.Base(location.RealPath)
|
|
//nolint:gocritic
|
|
reader, err := resolver.FileContentsByLocation(location)
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("unable to resolve file contents by location at %s: %w", location.RealPath, err)
|
|
}
|
|
content, err := io.ReadAll(reader)
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("unable to read apk key content at %s: %w", location.RealPath, err)
|
|
}
|
|
keyContent[basename] = string(content)
|
|
}
|
|
return keyContent, locations, nil
|
|
}
|
|
|
|
type AlpineConfiguration struct {
|
|
APKKeys map[string]string `json:"apkKeys" yaml:"apkKeys"`
|
|
// Add more data you want to capture as part of the package metadata here...
|
|
}
|