github.com/google/osv-scalibr@v0.4.1/extractor/filesystem/os/rpm/metadata/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 metadata defines a metadata struct for rpm packages.
    16  package metadata
    17  
    18  import (
    19  	"fmt"
    20  	"regexp"
    21  	"strings"
    22  
    23  	"github.com/google/osv-scalibr/log"
    24  
    25  	pb "github.com/google/osv-scalibr/binary/proto/scan_result_go_proto"
    26  )
    27  
    28  var (
    29  	// pattern to match: openEuler version (qualifier)
    30  	// where (qualifier) is optional. Those without qualifier are considered innovation versions.
    31  	// version: YY.MM format. e.g. 24.03
    32  	// qualifier: currently supported are LTS, LTS SPd. e.g. LTS, LTS-SP1, LTS-SP2
    33  	openEulerPrettyNameRegexp = regexp.MustCompile(`^openEuler\s+([0-9]{2}\.[0-9]{2})(?:\s*\((LTS(?:[- ]SP\d+)?)\))?$`)
    34  )
    35  
    36  // Metadata holds parsing information for an rpm package.
    37  type Metadata struct {
    38  	PackageName  string
    39  	SourceRPM    string
    40  	Epoch        int
    41  	OSName       string
    42  	OSCPEName    string
    43  	OSPrettyName string
    44  	OSID         string
    45  	OSVersionID  string
    46  	OSBuildID    string
    47  	Vendor       string
    48  	Architecture string
    49  }
    50  
    51  // ToNamespace extracts the PURL namespace from the metadata.
    52  func (m *Metadata) ToNamespace() string {
    53  	if m.OSID != "" {
    54  		return m.OSID
    55  	}
    56  	log.Errorf("os-release[ID] not set, fallback to ''")
    57  	return ""
    58  }
    59  
    60  // ToDistro extracts the OS distro from the metadata.
    61  func (m *Metadata) ToDistro() string {
    62  	v := m.OSVersionID
    63  	if v == "" {
    64  		v = m.OSBuildID
    65  		if v == "" {
    66  			log.Errorf("VERSION_ID and BUILD_ID not set in os-release")
    67  			return ""
    68  		}
    69  		log.Errorf("os-release[VERSION_ID] not set, fallback to BUILD_ID")
    70  	}
    71  
    72  	id := m.OSID
    73  	if id == "" {
    74  		log.Errorf("os-release[ID] not set, fallback to ''")
    75  		return v
    76  	}
    77  	return fmt.Sprintf("%s-%s", id, v)
    78  }
    79  
    80  // SetProto sets the RPMPackageMetadata field in the Package proto.
    81  func (m *Metadata) SetProto(p *pb.Package) {
    82  	if m == nil {
    83  		return
    84  	}
    85  	if p == nil {
    86  		return
    87  	}
    88  
    89  	p.Metadata = &pb.Package_RpmMetadata{
    90  		RpmMetadata: &pb.RPMPackageMetadata{
    91  			PackageName:  m.PackageName,
    92  			SourceRpm:    m.SourceRPM,
    93  			Epoch:        int32(m.Epoch),
    94  			OsName:       m.OSName,
    95  			OsCpeName:    m.OSCPEName,
    96  			OsPrettyName: m.OSPrettyName,
    97  			OsId:         m.OSID,
    98  			OsVersionId:  m.OSVersionID,
    99  			OsBuildId:    m.OSBuildID,
   100  			Vendor:       m.Vendor,
   101  			Architecture: m.Architecture,
   102  		},
   103  	}
   104  }
   105  
   106  // ToStruct converts the RPMPackageMetadata proto to a Metadata struct.
   107  func ToStruct(m *pb.RPMPackageMetadata) *Metadata {
   108  	if m == nil {
   109  		return nil
   110  	}
   111  
   112  	return &Metadata{
   113  		PackageName:  m.GetPackageName(),
   114  		SourceRPM:    m.GetSourceRpm(),
   115  		Epoch:        int(m.GetEpoch()),
   116  		OSName:       m.GetOsName(),
   117  		OSCPEName:    m.GetOsCpeName(),
   118  		OSPrettyName: m.GetOsPrettyName(),
   119  		OSID:         m.GetOsId(),
   120  		OSVersionID:  m.GetOsVersionId(),
   121  		OSBuildID:    m.GetOsBuildId(),
   122  		Vendor:       m.GetVendor(),
   123  		Architecture: m.GetArchitecture(),
   124  	}
   125  }
   126  
   127  // OpenEulerEcosystemSuffix returns the normalized ecosystem suffix for openEuler RPMs with uses of pretty name.
   128  // e.g. openEuler 24.03 (LTS) -> 24.03-LTS
   129  func (m *Metadata) OpenEulerEcosystemSuffix() string {
   130  	if m == nil {
   131  		return ""
   132  	}
   133  
   134  	prettyName := strings.TrimSpace(m.OSPrettyName)
   135  	if prettyName != "" {
   136  		if matches := openEulerPrettyNameRegexp.FindStringSubmatch(prettyName); len(matches) > 0 {
   137  			version := matches[1]
   138  			qualifier := strings.TrimSpace(matches[2])
   139  			if qualifier == "" {
   140  				return version
   141  			}
   142  
   143  			qualifier = strings.ReplaceAll(qualifier, " ", "-")
   144  			qualifier = strings.Trim(qualifier, "-")
   145  			if qualifier == "" {
   146  				return version
   147  			}
   148  
   149  			return version + "-" + qualifier
   150  		}
   151  	}
   152  
   153  	versionID := strings.TrimSpace(m.OSVersionID)
   154  	return versionID
   155  }