github.com/anchore/syft@v1.38.2/syft/pkg/cataloger/snap/parse_system_manifest.go (about)

     1  package snap
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"strings"
     7  
     8  	"gopkg.in/yaml.v3"
     9  
    10  	"github.com/anchore/syft/syft/artifact"
    11  	"github.com/anchore/syft/syft/file"
    12  	"github.com/anchore/syft/syft/pkg"
    13  	"github.com/anchore/syft/syft/pkg/cataloger/generic"
    14  )
    15  
    16  // systemManifest represents the structure of manifest.yaml files found in system/gadget snaps
    17  type systemManifest struct {
    18  	Name                 string   `yaml:"name"`
    19  	Version              string   `yaml:"version"`
    20  	Base                 string   `yaml:"base"`
    21  	Grade                string   `yaml:"grade"`
    22  	Confinement          string   `yaml:"confinement"`
    23  	PrimedStagePackages  []string `yaml:"primed-stage-packages"`
    24  	Architectures        []string `yaml:"architectures"`
    25  	SnapcraftVersion     string   `yaml:"snapcraft-version"`
    26  	SnapcraftOSReleaseID string   `yaml:"snapcraft-os-release-id"`
    27  }
    28  
    29  // parseSystemManifest parses manifest.yaml files from system/gadget snaps
    30  func parseSystemManifest(_ context.Context, _ file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
    31  	var manifest systemManifest
    32  
    33  	decoder := yaml.NewDecoder(reader)
    34  	if err := decoder.Decode(&manifest); err != nil {
    35  		return nil, nil, fmt.Errorf("failed to parse manifest.yaml: %w", err)
    36  	}
    37  
    38  	var packages []pkg.Package
    39  
    40  	// Determine snap type - could be system, gadget, or app
    41  	snapType := pkg.SnapTypeApp // Default
    42  	if manifest.Name != "" {
    43  		// Try to infer type from name patterns or content
    44  		switch {
    45  		case strings.Contains(strings.ToLower(manifest.Name), "gadget"):
    46  			snapType = pkg.SnapTypeGadget
    47  		default:
    48  			snapType = pkg.SnapTypeApp // System snaps are often just regular apps
    49  		}
    50  	}
    51  
    52  	snapMetadata := pkg.SnapEntry{
    53  		SnapType:    snapType,
    54  		Base:        manifest.Base,
    55  		SnapName:    manifest.Name,
    56  		SnapVersion: manifest.Version,
    57  	}
    58  
    59  	// Set architecture if available
    60  	if len(manifest.Architectures) > 0 {
    61  		snapMetadata.Architecture = manifest.Architectures[0]
    62  	}
    63  
    64  	// Parse primed-stage-packages entries
    65  	for _, pkgEntry := range manifest.PrimedStagePackages {
    66  		if !strings.Contains(pkgEntry, "=") {
    67  			continue // Skip malformed entries
    68  		}
    69  
    70  		parts := strings.SplitN(pkgEntry, "=", 2)
    71  		if len(parts) != 2 {
    72  			continue
    73  		}
    74  
    75  		name := strings.TrimSpace(parts[0])
    76  		version := strings.TrimSpace(parts[1])
    77  
    78  		// Skip empty names or versions
    79  		if name == "" || version == "" {
    80  			continue
    81  		}
    82  
    83  		// Handle architecture suffixes if present
    84  		currentMetadata := snapMetadata
    85  		if strings.Contains(name, ":") {
    86  			archParts := strings.SplitN(name, ":", 2)
    87  			name = archParts[0]
    88  			currentMetadata.Architecture = archParts[1]
    89  		}
    90  
    91  		debPkg := newDebianPackageFromSnap(
    92  			name,
    93  			version,
    94  			currentMetadata,
    95  			reader.Location,
    96  		)
    97  
    98  		packages = append(packages, debPkg)
    99  	}
   100  
   101  	return packages, nil, nil
   102  }