github.com/anchore/syft@v1.38.2/syft/pkg/cataloger/php/parse_pecl_pear.go (about)

     1  package php
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"io"
     7  
     8  	"github.com/elliotchance/phpserialize"
     9  
    10  	"github.com/anchore/syft/internal/unknown"
    11  	"github.com/anchore/syft/syft/artifact"
    12  	"github.com/anchore/syft/syft/file"
    13  	"github.com/anchore/syft/syft/pkg"
    14  	"github.com/anchore/syft/syft/pkg/cataloger/generic"
    15  )
    16  
    17  type peclPearData struct {
    18  	Name    string
    19  	Channel string
    20  	Version string
    21  	License []string
    22  }
    23  
    24  func (p *peclPearData) ToPear() pkg.PhpPearEntry {
    25  	return pkg.PhpPearEntry{
    26  		Name:    p.Name,
    27  		Channel: p.Channel,
    28  		Version: p.Version,
    29  		License: p.License,
    30  	}
    31  }
    32  
    33  func (p *peclPearData) ToPecl() pkg.PhpPeclEntry { //nolint:staticcheck
    34  	return pkg.PhpPeclEntry(p.ToPear()) //nolint:staticcheck
    35  }
    36  
    37  func parsePecl(ctx context.Context, _ file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
    38  	m, err := parsePeclPearSerialized(reader)
    39  	if err != nil {
    40  		return nil, nil, err
    41  	}
    42  	if m == nil {
    43  		return nil, nil, unknown.New(reader.Location, fmt.Errorf("no pecl package found"))
    44  	}
    45  	return []pkg.Package{newPeclPackage(ctx, *m, reader.Location)}, nil, nil
    46  }
    47  
    48  func parsePear(ctx context.Context, _ file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
    49  	m, err := parsePeclPearSerialized(reader)
    50  	if err != nil {
    51  		return nil, nil, err
    52  	}
    53  	if m == nil {
    54  		return nil, nil, unknown.New(reader.Location, fmt.Errorf("no pear package found"))
    55  	}
    56  	return []pkg.Package{newPearPackage(ctx, *m, reader.Location)}, nil, nil
    57  }
    58  
    59  // parsePeclPearSerialized is a parser function for Pear metadata contents, returning "Default" php packages discovered.
    60  func parsePeclPearSerialized(reader file.LocationReadCloser) (*peclPearData, error) {
    61  	data, err := io.ReadAll(reader)
    62  
    63  	if err != nil {
    64  		return nil, fmt.Errorf("failed to read file: %w", err)
    65  	}
    66  
    67  	metadata, err := phpserialize.UnmarshalAssociativeArray(
    68  		data,
    69  	)
    70  
    71  	if err != nil {
    72  		return nil, fmt.Errorf("failed to parse pear metadata file: %w", err)
    73  	}
    74  
    75  	name, ok := metadata["name"].(string)
    76  	if !ok {
    77  		return nil, fmt.Errorf("failed to parse pear package name: %w", err)
    78  	}
    79  
    80  	channel, ok := metadata["channel"].(string)
    81  	if !ok {
    82  		// this could be the v5 format
    83  		channel = ""
    84  	}
    85  
    86  	version := readStruct(metadata, "version", "release")
    87  	license := readStruct(metadata, "license", "_content")
    88  
    89  	return &peclPearData{
    90  		Name:    name,
    91  		Channel: channel,
    92  		Version: version,
    93  		License: []string{
    94  			license,
    95  		},
    96  	}, nil
    97  }
    98  
    99  func readStruct(metadata any, fields ...string) string {
   100  	if len(fields) > 0 {
   101  		value, ok := metadata.(map[any]any)
   102  		if !ok {
   103  			return ""
   104  		}
   105  		return readStruct(value[fields[0]], fields[1:]...)
   106  	}
   107  	value, ok := metadata.(string)
   108  	if !ok {
   109  		return ""
   110  	}
   111  	return value
   112  }