github.com/google/osv-scalibr@v0.4.1/binary/proto/container_image_metadata.go (about)

     1  // Copyright 2025 Google LLC
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package proto
    16  
    17  import (
    18  	"github.com/google/osv-scalibr/extractor"
    19  	"github.com/google/osv-scalibr/log"
    20  	"github.com/opencontainers/go-digest"
    21  
    22  	spb "github.com/google/osv-scalibr/binary/proto/scan_result_go_proto"
    23  )
    24  
    25  func layerMetadataToProto(lm *extractor.LayerMetadata) *spb.LayerMetadata {
    26  	if lm == nil {
    27  		return nil
    28  	}
    29  	return &spb.LayerMetadata{
    30  		Index:          int32(lm.Index),
    31  		DiffId:         lm.DiffID.String(),
    32  		ChainId:        lm.ChainID.String(),
    33  		Command:        lm.Command,
    34  		IsEmpty:        lm.IsEmpty,
    35  		BaseImageIndex: int32(lm.BaseImageIndex),
    36  	}
    37  }
    38  
    39  func baseImageDetailsToProto(bid *extractor.BaseImageDetails) *spb.BaseImageDetails {
    40  	if bid == nil {
    41  		return nil
    42  	}
    43  	return &spb.BaseImageDetails{
    44  		Repository: bid.Repository,
    45  		Registry:   bid.Registry,
    46  		Plugin:     bid.Plugin,
    47  	}
    48  }
    49  
    50  func containerImageMetadataToProto(cim *extractor.ContainerImageMetadata) *spb.ContainerImageMetadata {
    51  	if cim == nil {
    52  		return nil
    53  	}
    54  	var layerMetadata []*spb.LayerMetadata
    55  	for _, lm := range cim.LayerMetadata {
    56  		layerMetadata = append(layerMetadata, layerMetadataToProto(lm))
    57  	}
    58  
    59  	baseImageChains := []*spb.BaseImageChain{
    60  		// The first base image is always empty.
    61  		&spb.BaseImageChain{},
    62  	}
    63  
    64  	if len(cim.BaseImages) > 1 {
    65  		for _, bi := range cim.BaseImages[1:] {
    66  			var baseImageDetails []*spb.BaseImageDetails
    67  			for _, bid := range bi {
    68  				baseImageDetails = append(baseImageDetails, baseImageDetailsToProto(bid))
    69  			}
    70  			if len(bi) == 0 {
    71  				// This should never happen
    72  				continue
    73  			}
    74  
    75  			baseImageChains = append(baseImageChains, &spb.BaseImageChain{
    76  				BaseImages: baseImageDetails,
    77  				ChainId:    bi[0].ChainID.String(),
    78  			})
    79  		}
    80  	}
    81  
    82  	return &spb.ContainerImageMetadata{
    83  		Index:           int32(cim.Index),
    84  		OsInfo:          cim.OSInfo,
    85  		LayerMetadata:   layerMetadata,
    86  		BaseImageChains: baseImageChains,
    87  	}
    88  }
    89  
    90  func layerMetadataToStruct(lm *spb.LayerMetadata) *extractor.LayerMetadata {
    91  	if lm == nil {
    92  		return nil
    93  	}
    94  	diffID, err := digest.Parse(lm.GetDiffId())
    95  	if err != nil {
    96  		log.Errorf("Failed to parse diff ID %q: %v", lm.GetDiffId(), err)
    97  	}
    98  	chainID, err := digest.Parse(lm.GetChainId())
    99  	if err != nil {
   100  		log.Errorf("Failed to parse chain ID %q: %v", lm.GetChainId(), err)
   101  	}
   102  	return &extractor.LayerMetadata{
   103  		Index:          int(lm.GetIndex()),
   104  		DiffID:         diffID,
   105  		ChainID:        chainID,
   106  		Command:        lm.GetCommand(),
   107  		IsEmpty:        lm.GetIsEmpty(),
   108  		BaseImageIndex: int(lm.GetBaseImageIndex()),
   109  	}
   110  }
   111  
   112  func baseImageDetailsToStruct(bid *spb.BaseImageDetails, chainID digest.Digest) *extractor.BaseImageDetails {
   113  	if bid == nil {
   114  		return nil
   115  	}
   116  
   117  	return &extractor.BaseImageDetails{
   118  		Repository: bid.GetRepository(),
   119  		Registry:   bid.GetRegistry(),
   120  		Plugin:     bid.GetPlugin(),
   121  		ChainID:    chainID,
   122  	}
   123  }
   124  
   125  func containerImageMetadataToStruct(cim *spb.ContainerImageMetadata) *extractor.ContainerImageMetadata {
   126  	if cim == nil {
   127  		return nil
   128  	}
   129  	var layerMetadata []*extractor.LayerMetadata
   130  	for _, lm := range cim.GetLayerMetadata() {
   131  		layerMetadata = append(layerMetadata, layerMetadataToStruct(lm))
   132  	}
   133  	baseImages := [][]*extractor.BaseImageDetails{
   134  		// The first base image is always empty.
   135  		[]*extractor.BaseImageDetails{},
   136  	}
   137  	baseImageChains := cim.GetBaseImageChains()
   138  	if len(baseImageChains) > 1 {
   139  		for _, bic := range baseImageChains[1:] {
   140  			chainID, err := digest.Parse(bic.GetChainId())
   141  			if err != nil {
   142  				log.Errorf("Failed to parse chain ID %q: %v", bic.GetChainId(), err)
   143  				continue
   144  			}
   145  
   146  			var baseImageDetails []*extractor.BaseImageDetails
   147  			for _, bid := range bic.GetBaseImages() {
   148  				baseImageDetails = append(baseImageDetails, baseImageDetailsToStruct(bid, chainID))
   149  			}
   150  			baseImages = append(baseImages, baseImageDetails)
   151  		}
   152  	}
   153  
   154  	return &extractor.ContainerImageMetadata{
   155  		Index:         int(cim.GetIndex()),
   156  		OSInfo:        cim.GetOsInfo(),
   157  		LayerMetadata: layerMetadata,
   158  		BaseImages:    baseImages,
   159  	}
   160  }