github.com/google/osv-scalibr@v0.4.1/binary/proto/package.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 "fmt" 19 "reflect" 20 21 "github.com/docker/docker/api/types/container" 22 "github.com/google/osv-scalibr/converter" 23 "github.com/google/osv-scalibr/inventory/vex" 24 "github.com/google/osv-scalibr/log" 25 26 "github.com/google/osv-scalibr/extractor" 27 ctrdfs "github.com/google/osv-scalibr/extractor/filesystem/containers/containerd" 28 "github.com/google/osv-scalibr/extractor/filesystem/containers/podman" 29 archivemeta "github.com/google/osv-scalibr/extractor/filesystem/language/java/archive/metadata" 30 "github.com/google/osv-scalibr/extractor/filesystem/language/java/javalockfile" 31 "github.com/google/osv-scalibr/extractor/filesystem/language/python/requirements" 32 "github.com/google/osv-scalibr/extractor/filesystem/language/python/setup" 33 chromeextensions "github.com/google/osv-scalibr/extractor/filesystem/misc/chrome/extensions" 34 "github.com/google/osv-scalibr/extractor/filesystem/misc/vscodeextensions" 35 "github.com/google/osv-scalibr/extractor/filesystem/os/homebrew" 36 modulemeta "github.com/google/osv-scalibr/extractor/filesystem/os/kernel/module/metadata" 37 vmlinuzmeta "github.com/google/osv-scalibr/extractor/filesystem/os/kernel/vmlinuz/metadata" 38 "github.com/google/osv-scalibr/extractor/filesystem/osv" 39 cdxmeta "github.com/google/osv-scalibr/extractor/filesystem/sbom/cdx/metadata" 40 spdxmeta "github.com/google/osv-scalibr/extractor/filesystem/sbom/spdx/metadata" 41 ctrdruntime "github.com/google/osv-scalibr/extractor/standalone/containers/containerd/containerdmetadata" 42 "github.com/google/osv-scalibr/extractor/standalone/containers/docker" 43 winmetadata "github.com/google/osv-scalibr/extractor/standalone/windows/common/metadata" 44 "github.com/google/osv-scalibr/purl" 45 "github.com/google/uuid" 46 47 spb "github.com/google/osv-scalibr/binary/proto/scan_result_go_proto" 48 "google.golang.org/protobuf/types/known/timestamppb" 49 ) 50 51 // MetadataProtoSetter is an interface for metadata structs that can set themselves on a Package proto. 52 type MetadataProtoSetter interface { 53 SetProto(p *spb.Package) 54 } 55 56 // --- Struct to Proto 57 58 // PackageToProto converts a Package struct to a Package proto. 59 func PackageToProto(pkg *extractor.Package) (*spb.Package, error) { 60 if pkg == nil { 61 return nil, nil 62 } 63 64 p := converter.ToPURL(pkg) 65 66 var exps []*spb.PackageExploitabilitySignal 67 for _, exp := range pkg.ExploitabilitySignals { 68 expProto, err := PackageVEXToProto(exp) 69 if err != nil { 70 log.Errorf("Failed to convert PackageExploitabilitySignal to proto: %v", err) 71 continue 72 } 73 exps = append(exps, expProto) 74 } 75 76 var cii *spb.Package_ContainerImageMetadataIndexes 77 78 if pkg.LayerMetadata != nil && pkg.LayerMetadata.ParentContainer != nil { 79 cii = &spb.Package_ContainerImageMetadataIndexes{ 80 ContainerImageIndex: int32(pkg.LayerMetadata.ParentContainer.Index), 81 LayerIndex: int32(pkg.LayerMetadata.Index), 82 } 83 } 84 85 id, err := uuid.NewRandom() 86 if err != nil { 87 return nil, fmt.Errorf("failed to generate UUID for %q package %q version %q: %w", pkg.Ecosystem().String(), pkg.Name, pkg.Version, err) 88 } 89 90 packageProto := &spb.Package{ 91 Id: id.String(), 92 Name: pkg.Name, 93 Version: pkg.Version, 94 SourceCode: sourceCodeIdentifierToProto(pkg.SourceCode), 95 Purl: purlToProto(p), 96 Ecosystem: pkg.Ecosystem().String(), 97 Locations: pkg.Locations, 98 Plugins: pkg.Plugins, 99 ExploitabilitySignals: exps, 100 ContainerImageMetadataIndexes: cii, 101 Licenses: pkg.Licenses, 102 } 103 setProtoMetadata(pkg.Metadata, packageProto) 104 return packageProto, nil 105 } 106 107 func sourceCodeIdentifierToProto(s *extractor.SourceCodeIdentifier) *spb.SourceCodeIdentifier { 108 if s == nil { 109 return nil 110 } 111 return &spb.SourceCodeIdentifier{ 112 Repo: s.Repo, 113 Commit: s.Commit, 114 } 115 } 116 117 func setProtoMetadata(meta any, p *spb.Package) { 118 if meta == nil { 119 return 120 } 121 122 if p == nil { 123 return 124 } 125 126 if m, ok := meta.(MetadataProtoSetter); ok { 127 m.SetProto(p) 128 return 129 } 130 131 // Fallback to switch statement for types not yet implementing MetadataProtoSetter 132 // TODO: b/421456154 - Remove this switch statement once all metadata types implement MetadataProtoSetter. 133 switch m := meta.(type) { 134 case *homebrew.Metadata: 135 p.Metadata = &spb.Package_HomebrewMetadata{ 136 HomebrewMetadata: &spb.HomebrewPackageMetadata{}, 137 } 138 case *modulemeta.Metadata: 139 p.Metadata = &spb.Package_KernelModuleMetadata{ 140 KernelModuleMetadata: &spb.KernelModuleMetadata{ 141 PackageName: m.PackageName, 142 PackageVersion: m.PackageVersion, 143 PackageVermagic: m.PackageVermagic, 144 PackageSourceVersionIdentifier: m.PackageSourceVersionIdentifier, 145 OsId: m.OSID, 146 OsVersionCodename: m.OSVersionCodename, 147 OsVersionId: m.OSVersionID, 148 PackageAuthor: m.PackageAuthor}, 149 } 150 case *vmlinuzmeta.Metadata: 151 p.Metadata = &spb.Package_VmlinuzMetadata{ 152 VmlinuzMetadata: &spb.VmlinuzMetadata{ 153 Name: m.Name, 154 Version: m.Version, 155 Architecture: m.Architecture, 156 ExtendedVersion: m.ExtendedVersion, 157 Format: m.Format, 158 SwapDevice: m.SwapDevice, 159 RootDevice: m.RootDevice, 160 VideoMode: m.VideoMode, 161 OsId: m.OSID, 162 OsVersionCodename: m.OSVersionCodename, 163 OsVersionId: m.OSVersionID, 164 RwRootFs: m.RWRootFS, 165 }, 166 } 167 case *ctrdfs.Metadata: 168 p.Metadata = &spb.Package_ContainerdContainerMetadata{ 169 ContainerdContainerMetadata: &spb.ContainerdContainerMetadata{ 170 NamespaceName: m.Namespace, 171 ImageName: m.ImageName, 172 ImageDigest: m.ImageDigest, 173 Runtime: m.Runtime, 174 Id: m.ID, 175 PodName: m.PodName, 176 PodNamespace: m.PodNamespace, 177 Pid: int32(m.PID), 178 Snapshotter: m.Snapshotter, 179 SnapshotKey: m.SnapshotKey, 180 LowerDir: m.LowerDir, 181 UpperDir: m.UpperDir, 182 WorkDir: m.WorkDir, 183 }, 184 } 185 case *ctrdruntime.Metadata: 186 p.Metadata = &spb.Package_ContainerdRuntimeContainerMetadata{ 187 ContainerdRuntimeContainerMetadata: &spb.ContainerdRuntimeContainerMetadata{ 188 NamespaceName: m.Namespace, 189 ImageName: m.ImageName, 190 ImageDigest: m.ImageDigest, 191 Runtime: m.Runtime, 192 Id: m.ID, 193 Pid: int32(m.PID), 194 RootfsPath: m.RootFS, 195 }, 196 } 197 case *spdxmeta.Metadata: 198 p.Metadata = &spb.Package_SpdxMetadata{ 199 SpdxMetadata: &spb.SPDXPackageMetadata{ 200 Purl: purlToProto(m.PURL), 201 Cpes: m.CPEs, 202 }, 203 } 204 case *cdxmeta.Metadata: 205 p.Metadata = &spb.Package_CdxMetadata{ 206 CdxMetadata: &spb.CDXPackageMetadata{ 207 Purl: purlToProto(m.PURL), 208 Cpes: m.CPEs, 209 }, 210 } 211 case *archivemeta.Metadata: 212 p.Metadata = &spb.Package_JavaArchiveMetadata{ 213 JavaArchiveMetadata: &spb.JavaArchiveMetadata{ 214 ArtifactId: m.ArtifactID, 215 GroupId: m.GroupID, 216 Sha1: m.SHA1, 217 }, 218 } 219 case *javalockfile.Metadata: 220 p.Metadata = &spb.Package_JavaLockfileMetadata{ 221 JavaLockfileMetadata: &spb.JavaLockfileMetadata{ 222 ArtifactId: m.ArtifactID, 223 GroupId: m.GroupID, 224 IsTransitive: m.IsTransitive, 225 }, 226 } 227 case *osv.Metadata: 228 p.Metadata = &spb.Package_OsvMetadata{ 229 OsvMetadata: &spb.OSVPackageMetadata{ 230 PurlType: m.PURLType, 231 Commit: m.Commit, 232 Ecosystem: m.Ecosystem, 233 CompareAs: m.CompareAs, 234 }, 235 } 236 case *requirements.Metadata: 237 p.Metadata = &spb.Package_PythonRequirementsMetadata{ 238 PythonRequirementsMetadata: &spb.PythonRequirementsMetadata{ 239 HashCheckingModeValues: m.HashCheckingModeValues, 240 VersionComparator: m.VersionComparator, 241 Requirement: m.Requirement, 242 }, 243 } 244 case *setup.Metadata: 245 p.Metadata = &spb.Package_PythonSetupMetadata{ 246 PythonSetupMetadata: &spb.PythonSetupMetadata{ 247 VersionComparator: m.VersionComparator, 248 }, 249 } 250 case *winmetadata.OSVersion: 251 p.Metadata = &spb.Package_WindowsOsVersionMetadata{ 252 WindowsOsVersionMetadata: &spb.WindowsOSVersion{ 253 Product: m.Product, 254 FullVersion: m.FullVersion, 255 }, 256 } 257 case *chromeextensions.Metadata: 258 p.Metadata = &spb.Package_ChromeExtensionsMetadata{ 259 ChromeExtensionsMetadata: &spb.ChromeExtensionsMetadata{ 260 Name: m.Name, 261 Description: m.Description, 262 AuthorEmail: m.AuthorEmail, 263 HostPermissions: m.HostPermissions, 264 ManifestVersion: int32(m.ManifestVersion), 265 MinimumChromeVersion: m.MinimumChromeVersion, 266 Permissions: m.Permissions, 267 UpdateUrl: m.UpdateURL, 268 }, 269 } 270 case *vscodeextensions.Metadata: 271 p.Metadata = &spb.Package_VscodeExtensionsMetadata{ 272 VscodeExtensionsMetadata: &spb.VSCodeExtensionsMetadata{ 273 Id: m.ID, 274 PublisherId: m.PublisherID, 275 PublisherDisplayName: m.PublisherDisplayName, 276 TargetPlatform: m.TargetPlatform, 277 Updated: m.Updated, 278 IsPreReleaseVersion: m.IsPreReleaseVersion, 279 InstalledTimestamp: m.InstalledTimestamp, 280 }, 281 } 282 case *podman.Metadata: 283 exposedPorts := map[uint32]*spb.Protocol{} 284 for p, protocols := range m.ExposedPorts { 285 exposedPorts[uint32(p)] = &spb.Protocol{Names: protocols} 286 } 287 p.Metadata = &spb.Package_PodmanMetadata{ 288 PodmanMetadata: &spb.PodmanMetadata{ 289 ExposedPorts: exposedPorts, 290 Pid: int32(m.PID), 291 NamespaceName: m.NameSpace, 292 StartedTime: timestamppb.New(m.StartedTime), 293 FinishedTime: timestamppb.New(m.FinishedTime), 294 Status: m.Status, 295 ExitCode: m.ExitCode, 296 Exited: m.Exited, 297 }, 298 } 299 case *docker.Metadata: 300 ports := make([]*spb.DockerPort, 0, len(m.Ports)) 301 for _, p := range m.Ports { 302 ports = append(ports, &spb.DockerPort{ 303 Ip: p.IP, 304 PrivatePort: uint32(p.PrivatePort), 305 PublicPort: uint32(p.PublicPort), 306 Type: p.Type, 307 }) 308 } 309 p.Metadata = &spb.Package_DockerContainersMetadata{ 310 DockerContainersMetadata: &spb.DockerContainersMetadata{ 311 ImageName: m.ImageName, 312 ImageDigest: m.ImageDigest, 313 Id: m.ID, 314 Ports: ports, 315 }, 316 } 317 } 318 } 319 320 func purlToProto(p *purl.PackageURL) *spb.Purl { 321 if p == nil { 322 return nil 323 } 324 return &spb.Purl{ 325 Purl: p.String(), 326 Type: p.Type, 327 Namespace: p.Namespace, 328 Name: p.Name, 329 Version: p.Version, 330 Qualifiers: qualifiersToProto(p.Qualifiers), 331 Subpath: p.Subpath, 332 } 333 } 334 335 func qualifiersToProto(qs purl.Qualifiers) []*spb.Qualifier { 336 result := make([]*spb.Qualifier, 0, len(qs)) 337 for _, q := range qs { 338 result = append(result, &spb.Qualifier{Key: q.Key, Value: q.Value}) 339 } 340 return result 341 } 342 343 // --- Proto to Struct 344 345 // PackageToStruct converts a Package proto to a Package struct. 346 func PackageToStruct(pkgProto *spb.Package) (*extractor.Package, error) { 347 if pkgProto == nil { 348 return nil, nil 349 } 350 351 var locations []string 352 locations = append(locations, pkgProto.GetLocations()...) 353 354 // TODO - b/421463494: Remove this once windows PURLs are corrected. 355 ptype := pkgProto.GetPurl().GetType() 356 if pkgProto.GetPurl().GetType() == purl.TypeGeneric && pkgProto.GetPurl().GetNamespace() == "microsoft" { 357 ptype = "windows" 358 } 359 360 var exps []*vex.PackageExploitabilitySignal 361 for _, exp := range pkgProto.GetExploitabilitySignals() { 362 expStruct, err := PackageVEXToStruct(exp) 363 if err != nil { 364 log.Errorf("Failed to convert PackageExploitabilitySignal to struct: %v", err) 365 continue 366 } 367 exps = append(exps, expStruct) 368 } 369 370 pkg := &extractor.Package{ 371 Name: pkgProto.GetName(), 372 Version: pkgProto.GetVersion(), 373 SourceCode: sourceCodeIdentifierToStruct(pkgProto.GetSourceCode()), 374 Locations: locations, 375 PURLType: ptype, 376 Plugins: pkgProto.GetPlugins(), 377 ExploitabilitySignals: exps, 378 Metadata: metadataToStruct(pkgProto), 379 Licenses: pkgProto.GetLicenses(), 380 } 381 return pkg, nil 382 } 383 384 func sourceCodeIdentifierToStruct(s *spb.SourceCodeIdentifier) *extractor.SourceCodeIdentifier { 385 if s == nil { 386 return nil 387 } 388 return &extractor.SourceCodeIdentifier{ 389 Repo: s.Repo, 390 Commit: s.Commit, 391 } 392 } 393 394 func metadataToStruct(md *spb.Package) any { 395 if md.GetMetadata() == nil { 396 return nil 397 } 398 399 t := reflect.TypeOf(md.GetMetadata()) 400 if converter, ok := metadataTypeToStructConverter[t]; ok { 401 return converter(md) 402 } 403 404 // TODO: b/421456154 - Remove this switch statement once all metadata types implement MetadataProtoSetter. 405 switch md.GetMetadata().(type) { 406 case *spb.Package_HomebrewMetadata: 407 return &homebrew.Metadata{} 408 case *spb.Package_KernelModuleMetadata: 409 return &modulemeta.Metadata{ 410 PackageName: md.GetKernelModuleMetadata().GetPackageName(), 411 PackageVersion: md.GetKernelModuleMetadata().GetPackageVersion(), 412 PackageVermagic: md.GetKernelModuleMetadata().GetPackageVermagic(), 413 PackageSourceVersionIdentifier: md.GetKernelModuleMetadata().GetPackageSourceVersionIdentifier(), 414 OSID: md.GetKernelModuleMetadata().GetOsId(), 415 OSVersionCodename: md.GetKernelModuleMetadata().GetOsVersionCodename(), 416 OSVersionID: md.GetKernelModuleMetadata().GetOsVersionId(), 417 PackageAuthor: md.GetKernelModuleMetadata().GetPackageAuthor(), 418 } 419 case *spb.Package_VmlinuzMetadata: 420 return &vmlinuzmeta.Metadata{ 421 Name: md.GetVmlinuzMetadata().GetName(), 422 Version: md.GetVmlinuzMetadata().GetVersion(), 423 Architecture: md.GetVmlinuzMetadata().GetArchitecture(), 424 ExtendedVersion: md.GetVmlinuzMetadata().GetExtendedVersion(), 425 Format: md.GetVmlinuzMetadata().GetFormat(), 426 SwapDevice: md.GetVmlinuzMetadata().GetSwapDevice(), 427 RootDevice: md.GetVmlinuzMetadata().GetRootDevice(), 428 VideoMode: md.GetVmlinuzMetadata().GetVideoMode(), 429 OSID: md.GetVmlinuzMetadata().GetOsId(), 430 OSVersionCodename: md.GetVmlinuzMetadata().GetOsVersionCodename(), 431 OSVersionID: md.GetVmlinuzMetadata().GetOsVersionId(), 432 RWRootFS: md.GetVmlinuzMetadata().GetRwRootFs(), 433 } 434 case *spb.Package_ContainerdContainerMetadata: 435 return &ctrdfs.Metadata{ 436 Namespace: md.GetContainerdContainerMetadata().GetNamespaceName(), 437 ImageName: md.GetContainerdContainerMetadata().GetImageName(), 438 ImageDigest: md.GetContainerdContainerMetadata().GetImageDigest(), 439 Runtime: md.GetContainerdContainerMetadata().GetRuntime(), 440 ID: md.GetContainerdContainerMetadata().GetId(), 441 PodName: md.GetContainerdContainerMetadata().GetPodName(), 442 PodNamespace: md.GetContainerdContainerMetadata().GetPodNamespace(), 443 PID: int(md.GetContainerdContainerMetadata().GetPid()), 444 Snapshotter: md.GetContainerdContainerMetadata().GetSnapshotter(), 445 SnapshotKey: md.GetContainerdContainerMetadata().GetSnapshotKey(), 446 LowerDir: md.GetContainerdContainerMetadata().GetLowerDir(), 447 UpperDir: md.GetContainerdContainerMetadata().GetUpperDir(), 448 WorkDir: md.GetContainerdContainerMetadata().GetWorkDir(), 449 } 450 case *spb.Package_ContainerdRuntimeContainerMetadata: 451 return &ctrdruntime.Metadata{ 452 Namespace: md.GetContainerdRuntimeContainerMetadata().GetNamespaceName(), 453 ImageName: md.GetContainerdRuntimeContainerMetadata().GetImageName(), 454 ImageDigest: md.GetContainerdRuntimeContainerMetadata().GetImageDigest(), 455 Runtime: md.GetContainerdRuntimeContainerMetadata().GetRuntime(), 456 ID: md.GetContainerdRuntimeContainerMetadata().GetId(), 457 PID: int(md.GetContainerdRuntimeContainerMetadata().GetPid()), 458 RootFS: md.GetContainerdRuntimeContainerMetadata().GetRootfsPath(), 459 } 460 case *spb.Package_SpdxMetadata: 461 return &spdxmeta.Metadata{ 462 PURL: purlToStruct(md.GetSpdxMetadata().GetPurl()), 463 CPEs: md.GetSpdxMetadata().GetCpes(), 464 } 465 case *spb.Package_CdxMetadata: 466 return &cdxmeta.Metadata{ 467 PURL: purlToStruct(md.GetCdxMetadata().GetPurl()), 468 CPEs: md.GetCdxMetadata().GetCpes(), 469 } 470 case *spb.Package_JavaArchiveMetadata: 471 return &archivemeta.Metadata{ 472 ArtifactID: md.GetJavaArchiveMetadata().GetArtifactId(), 473 GroupID: md.GetJavaArchiveMetadata().GetGroupId(), 474 SHA1: md.GetJavaArchiveMetadata().GetSha1(), 475 } 476 case *spb.Package_JavaLockfileMetadata: 477 return &javalockfile.Metadata{ 478 ArtifactID: md.GetJavaLockfileMetadata().GetArtifactId(), 479 GroupID: md.GetJavaLockfileMetadata().GetGroupId(), 480 IsTransitive: md.GetJavaLockfileMetadata().GetIsTransitive(), 481 } 482 case *spb.Package_OsvMetadata: 483 return &osv.Metadata{ 484 PURLType: md.GetOsvMetadata().GetPurlType(), 485 Commit: md.GetOsvMetadata().GetCommit(), 486 Ecosystem: md.GetOsvMetadata().GetEcosystem(), 487 CompareAs: md.GetOsvMetadata().GetCompareAs(), 488 } 489 case *spb.Package_PythonRequirementsMetadata: 490 return &requirements.Metadata{ 491 HashCheckingModeValues: md.GetPythonRequirementsMetadata().GetHashCheckingModeValues(), 492 VersionComparator: md.GetPythonRequirementsMetadata().GetVersionComparator(), 493 Requirement: md.GetPythonRequirementsMetadata().GetRequirement(), 494 } 495 case *spb.Package_PythonSetupMetadata: 496 return &setup.Metadata{ 497 VersionComparator: md.GetPythonSetupMetadata().GetVersionComparator(), 498 } 499 case *spb.Package_WindowsOsVersionMetadata: 500 return &winmetadata.OSVersion{ 501 Product: md.GetWindowsOsVersionMetadata().GetProduct(), 502 FullVersion: md.GetWindowsOsVersionMetadata().GetFullVersion(), 503 } 504 case *spb.Package_ChromeExtensionsMetadata: 505 return &chromeextensions.Metadata{ 506 Name: md.GetChromeExtensionsMetadata().GetName(), 507 Description: md.GetChromeExtensionsMetadata().GetDescription(), 508 AuthorEmail: md.GetChromeExtensionsMetadata().GetAuthorEmail(), 509 HostPermissions: md.GetChromeExtensionsMetadata().GetHostPermissions(), 510 ManifestVersion: int(md.GetChromeExtensionsMetadata().GetManifestVersion()), 511 MinimumChromeVersion: md.GetChromeExtensionsMetadata().GetMinimumChromeVersion(), 512 Permissions: md.GetChromeExtensionsMetadata().GetPermissions(), 513 UpdateURL: md.GetChromeExtensionsMetadata().GetUpdateUrl(), 514 } 515 case *spb.Package_VscodeExtensionsMetadata: 516 return &vscodeextensions.Metadata{ 517 ID: md.GetVscodeExtensionsMetadata().GetId(), 518 PublisherID: md.GetVscodeExtensionsMetadata().GetPublisherId(), 519 PublisherDisplayName: md.GetVscodeExtensionsMetadata().GetPublisherDisplayName(), 520 TargetPlatform: md.GetVscodeExtensionsMetadata().GetTargetPlatform(), 521 Updated: md.GetVscodeExtensionsMetadata().GetUpdated(), 522 IsPreReleaseVersion: md.GetVscodeExtensionsMetadata().GetIsPreReleaseVersion(), 523 InstalledTimestamp: md.GetVscodeExtensionsMetadata().GetInstalledTimestamp(), 524 } 525 case *spb.Package_PodmanMetadata: 526 exposedPorts := map[uint16][]string{} 527 for p, protocol := range md.GetPodmanMetadata().GetExposedPorts() { 528 for _, name := range protocol.GetNames() { 529 exposedPorts[uint16(p)] = append(exposedPorts[uint16(p)], name) 530 } 531 } 532 return &podman.Metadata{ 533 ExposedPorts: exposedPorts, 534 PID: int(md.GetPodmanMetadata().GetPid()), 535 NameSpace: md.GetPodmanMetadata().GetNamespaceName(), 536 StartedTime: md.GetPodmanMetadata().GetStartedTime().AsTime(), 537 FinishedTime: md.GetPodmanMetadata().GetFinishedTime().AsTime(), 538 Status: md.GetPodmanMetadata().GetStatus(), 539 ExitCode: md.GetPodmanMetadata().GetExitCode(), 540 Exited: md.GetPodmanMetadata().GetExited(), 541 } 542 case *spb.Package_DockerContainersMetadata: 543 var ports []container.Port 544 for _, p := range md.GetDockerContainersMetadata().GetPorts() { 545 ports = append(ports, container.Port{ 546 IP: p.GetIp(), 547 PrivatePort: uint16(p.GetPrivatePort()), 548 PublicPort: uint16(p.GetPublicPort()), 549 Type: p.GetType(), 550 }) 551 } 552 return &docker.Metadata{ 553 ImageName: md.GetDockerContainersMetadata().GetImageName(), 554 ImageDigest: md.GetDockerContainersMetadata().GetImageDigest(), 555 ID: md.GetDockerContainersMetadata().GetId(), 556 Ports: ports, 557 } 558 } 559 560 return nil 561 } 562 563 func purlToStruct(p *spb.Purl) *purl.PackageURL { 564 if p == nil { 565 return nil 566 } 567 568 // There's no guarantee that the PURL fields will match the PURL string. 569 // Use the fields if the string is blank or invalid. 570 // Elese, compare the string and fields, prioritizing the fields. 571 pfs := purlFromString(p.GetPurl()) 572 if pfs == nil { 573 return &purl.PackageURL{ 574 Type: p.GetType(), 575 Namespace: p.GetNamespace(), 576 Name: p.GetName(), 577 Version: p.GetVersion(), 578 Qualifiers: qualifiersToStruct(p.GetQualifiers()), 579 Subpath: p.GetSubpath(), 580 } 581 } 582 583 // Prioritize fields from the PURL proto over the PURL string. 584 ptype := pfs.Type 585 if p.GetType() != "" { 586 ptype = p.GetType() 587 } 588 namespace := pfs.Namespace 589 if p.GetNamespace() != "" { 590 namespace = p.GetNamespace() 591 } 592 name := pfs.Name 593 if p.GetName() != "" { 594 name = p.GetName() 595 } 596 version := pfs.Version 597 if p.GetVersion() != "" { 598 version = p.GetVersion() 599 } 600 qualifiers := pfs.Qualifiers 601 if len(p.GetQualifiers()) > 0 { 602 qualifiers = qualifiersToStruct(p.GetQualifiers()) 603 } 604 subpath := pfs.Subpath 605 if p.GetSubpath() != "" { 606 subpath = p.GetSubpath() 607 } 608 609 // TODO - b/421463494: Remove this once windows PURLs are corrected. 610 if ptype == purl.TypeGeneric && namespace == "microsoft" { 611 ptype = "windows" 612 namespace = "" 613 } 614 615 return &purl.PackageURL{ 616 Type: ptype, 617 Namespace: namespace, 618 Name: name, 619 Version: version, 620 Qualifiers: qualifiers, 621 Subpath: subpath, 622 } 623 } 624 625 func purlFromString(s string) *purl.PackageURL { 626 if s == "" { 627 return nil 628 } 629 p, err := purl.FromString(s) 630 if err != nil { 631 log.Errorf("failed to parse PURL string %q: %v", s, err) 632 return nil 633 } 634 if len(p.Qualifiers) == 0 { 635 p.Qualifiers = nil 636 } 637 return &p 638 } 639 640 func qualifiersToStruct(qs []*spb.Qualifier) purl.Qualifiers { 641 if len(qs) == 0 { 642 return nil 643 } 644 qsmap := map[string]string{} 645 for _, q := range qs { 646 qsmap[q.GetKey()] = q.GetValue() 647 } 648 return purl.QualifiersFromMap(qsmap) 649 }