github.com/devseccon/trivy@v0.47.1-0.20231123133102-bd902a0bd996/pkg/fanal/image/oci.go (about)

     1  package image
     2  
     3  import (
     4  	"strings"
     5  
     6  	v1 "github.com/google/go-containerregistry/pkg/v1"
     7  	"github.com/google/go-containerregistry/pkg/v1/layout"
     8  	ispec "github.com/opencontainers/image-spec/specs-go/v1"
     9  	"golang.org/x/xerrors"
    10  )
    11  
    12  func tryOCI(fileName string) (v1.Image, error) {
    13  	var inputRef, inputFileName string
    14  
    15  	// Check if tag is specified in input
    16  	// e.g. /path/to/oci:0.0.1
    17  
    18  	inputFileName, inputRef, found := strings.Cut(fileName, "@")
    19  
    20  	if !found {
    21  		inputFileName, inputRef, found = strings.Cut(fileName, ":")
    22  	}
    23  
    24  	if !found {
    25  		inputFileName = fileName
    26  		inputRef = ""
    27  	}
    28  
    29  	lp, err := layout.FromPath(inputFileName)
    30  	if err != nil {
    31  		return nil, xerrors.Errorf("unable to open %s as an OCI Image: %w", fileName, err)
    32  	}
    33  
    34  	index, err := lp.ImageIndex()
    35  	if err != nil {
    36  		return nil, xerrors.Errorf("unable to retrieve index.json: %w", err)
    37  	}
    38  
    39  	m, err := index.IndexManifest()
    40  	if err != nil {
    41  		return nil, xerrors.Errorf("invalid index.json: %w", err)
    42  	}
    43  
    44  	if len(m.Manifests) == 0 {
    45  		return nil, xerrors.New("no valid manifest")
    46  	}
    47  
    48  	// Support image having tag separated by : , otherwise support first image
    49  	return getOCIImage(m, index, inputRef)
    50  }
    51  
    52  func getOCIImage(m *v1.IndexManifest, index v1.ImageIndex, inputRef string) (v1.Image, error) {
    53  	for _, manifest := range m.Manifests {
    54  		annotation := manifest.Annotations
    55  		tag := annotation[ispec.AnnotationRefName]
    56  		if inputRef == "" || // always select the first digest
    57  			tag == inputRef ||
    58  			manifest.Digest.String() == inputRef {
    59  			h := manifest.Digest
    60  			if manifest.MediaType.IsIndex() {
    61  				childIndex, err := index.ImageIndex(h)
    62  				if err != nil {
    63  					return nil, xerrors.Errorf("unable to retrieve a child image %q: %w", h.String(), err)
    64  				}
    65  				childManifest, err := childIndex.IndexManifest()
    66  				if err != nil {
    67  					return nil, xerrors.Errorf("invalid a child manifest for %q: %w", h.String(), err)
    68  				}
    69  				return getOCIImage(childManifest, childIndex, "")
    70  			}
    71  
    72  			img, err := index.Image(h)
    73  			if err != nil {
    74  				return nil, xerrors.Errorf("invalid OCI image: %w", err)
    75  			}
    76  
    77  			return img, nil
    78  		}
    79  	}
    80  
    81  	return nil, xerrors.New("invalid OCI image ref")
    82  }