Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add go binary h1 digest to SPDX #1265

Merged
merged 7 commits into from
Oct 19, 2022
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions syft/formats/common/spdxhelpers/to_syft_model.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package spdxhelpers

import (
"encoding/base64"
"encoding/hex"
"errors"
"fmt"
"net/url"
"strconv"
"strings"
Expand Down Expand Up @@ -293,6 +296,7 @@ func toSyftPackage(p *spdx.Package2_2) *pkg.Package {
return &sP
}

//nolint:funlen
func extractMetadata(p *spdx.Package2_2, info pkgInfo) (pkg.MetadataType, interface{}) {
arch := info.qualifierValue(pkg.PURLQualifierArch)
upstreamValue := info.qualifierValue(pkg.PURLQualifierUpstream)
Expand Down Expand Up @@ -352,6 +356,25 @@ func extractMetadata(p *spdx.Package2_2, info pkgInfo) (pkg.MetadataType, interf
return pkg.JavaMetadataType, pkg.JavaMetadata{
ArchiveDigests: digests,
}
case pkg.GoModulePkg:
var h1Digest string
for _, value := range p.PackageChecksums {
// golang h1 hash == sha256
if value.Algorithm != "SHA256" {
break
}
checksum, err := hex.DecodeString(value.Value)
if err != nil {
log.Debugf("invalid hex encoded digest: %v", err)
break
}
// hash is base64, but we need hex encode
h1Digest = base64.StdEncoding.EncodeToString(checksum)
}

return pkg.GolangBinMetadataType, pkg.GolangBinMetadata{
H1Digest: fmt.Sprintf("h1:%s", h1Digest),
}
}
return pkg.UnknownMetadataType, nil
}
Expand Down
68 changes: 48 additions & 20 deletions syft/formats/spdx22json/to_format_model.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package spdx22json

import (
"encoding/base64"
"encoding/hex"
"fmt"
"sort"
"strings"
Expand Down Expand Up @@ -53,32 +55,15 @@ func toPackages(catalog *pkg.Catalog, relationships []artifact.Relationship) []m
for _, p := range catalog.Sorted() {
license := spdxhelpers.License(p)
packageSpdxID := model.ElementID(p.ID()).String()
filesAnalyzed := false
kzantow marked this conversation as resolved.
Show resolved Hide resolved

// we generate digest for some Java packages
// see page 33 of the spdx specification for 2.2
// spdx.github.io/spdx-spec/package-information/#710-package-checksum-field
var checksums []model.Checksum
if p.MetadataType == pkg.JavaMetadataType {
javaMetadata := p.Metadata.(pkg.JavaMetadata)
if len(javaMetadata.ArchiveDigests) > 0 {
filesAnalyzed = true
for _, digest := range javaMetadata.ArchiveDigests {
checksums = append(checksums, model.Checksum{
Algorithm: strings.ToUpper(digest.Algorithm),
ChecksumValue: digest.Value,
})
}
}
}

// note: the license concluded and declared should be the same since we are collecting license information
// from the project data itself (the installed package files).
packages = append(packages, model.Package{
Checksums: checksums,
Checksums: toPackageChecksums(p),
Description: spdxhelpers.Description(p),
DownloadLocation: spdxhelpers.DownloadLocation(p),
ExternalRefs: spdxhelpers.ExternalRefs(p),
FilesAnalyzed: filesAnalyzed,
FilesAnalyzed: false, // this directly relates to packageVerificationCode
kzantow marked this conversation as resolved.
Show resolved Hide resolved
HasFiles: fileIDsForPackage(packageSpdxID, relationships),
Homepage: spdxhelpers.Homepage(p),
// The Declared License is what the authors of a project believe govern the package
Expand All @@ -100,6 +85,49 @@ func toPackages(catalog *pkg.Catalog, relationships []artifact.Relationship) []m
return packages
}

func toPackageChecksums(p pkg.Package) []model.Checksum {
var checksums []model.Checksum
switch meta := p.Metadata.(type) {
// we generate digest for some Java packages
// see page 33 of the spdx specification for 2.2
// spdx.github.io/spdx-spec/package-information/#710-package-checksum-field
case pkg.JavaMetadata:
if len(meta.ArchiveDigests) > 0 {
for _, digest := range meta.ArchiveDigests {
checksums = append(checksums, model.Checksum{
Algorithm: strings.ToUpper(digest.Algorithm),
ChecksumValue: digest.Value,
})
}
}
case pkg.GolangBinMetadata:
// hash is base64, but we need hex encode
digest := strings.Split(meta.H1Digest, ":")
if len(digest) == 2 {
algo := digest[0]
hash := digest[1]
checksum, err := base64.StdEncoding.DecodeString(hash)
if err != nil {
log.Debugf("invalid base64 encoded digest: %v", err)
break
}

hexStr := hex.EncodeToString(checksum)

// golang h1 hash == sha256
if algo == "h1" {
algo = "sha256"
}

checksums = append(checksums, model.Checksum{
Algorithm: strings.ToUpper(algo),
ChecksumValue: hexStr,
})
}
}
return checksums
}

func fileIDsForPackage(packageSpdxID string, relationships []artifact.Relationship) (fileIDs []string) {
for _, relationship := range relationships {
if relationship.Type != artifact.ContainsRelationship {
Expand Down
69 changes: 49 additions & 20 deletions syft/formats/spdx22tagvalue/to_format_model.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
package spdx22tagvalue

import (
"encoding/base64"
"encoding/hex"
"fmt"
"strings"
"time"

"github.com/spdx/tools-golang/spdx"

"github.com/anchore/syft/internal"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/internal/spdxlicense"
"github.com/anchore/syft/syft/formats/common/spdxhelpers"
"github.com/anchore/syft/syft/pkg"
Expand Down Expand Up @@ -102,24 +106,6 @@ func toFormatPackages(catalog *pkg.Catalog) map[spdx.ElementID]*spdx.Package2_2
// the Comments on License field (section 3.16) is preferred.
license := spdxhelpers.License(p)

filesAnalyzed := false
checksums := make(map[spdx.ChecksumAlgorithm]spdx.Checksum)

// If the pkg type is Java we have attempted to generated a digest
// FilesAnalyzed should be true in this case
if p.MetadataType == pkg.JavaMetadataType {
javaMetadata := p.Metadata.(pkg.JavaMetadata)
if len(javaMetadata.ArchiveDigests) > 0 {
filesAnalyzed = true
for _, digest := range javaMetadata.ArchiveDigests {
checksums[spdx.ChecksumAlgorithm(digest.Algorithm)] = spdx.Checksum{
Algorithm: spdx.ChecksumAlgorithm(digest.Algorithm),
Value: digest.Value,
}
}
}
}

results[spdx.ElementID(id)] = &spdx.Package2_2{

// NOT PART OF SPEC
Expand Down Expand Up @@ -176,7 +162,7 @@ func toFormatPackages(catalog *pkg.Catalog) map[spdx.ElementID]*spdx.Package2_2

// Intent: A package can refer to a project, product, artifact, distribution or a component that is
// external to the SPDX document.
FilesAnalyzed: filesAnalyzed,
FilesAnalyzed: false,
// NOT PART OF SPEC: did FilesAnalyzed tag appear?
IsFilesAnalyzedTagPresent: true,

Expand All @@ -197,7 +183,7 @@ func toFormatPackages(catalog *pkg.Catalog) map[spdx.ElementID]*spdx.Package2_2
// to determine if any file in the original package has been changed. If the SPDX file is to be included
// in a package, this value should not be calculated. The SHA-1 algorithm will be used to provide the
// checksum by default.
PackageChecksums: checksums,
PackageChecksums: toPackageChecksums(p),

// note: based on the purpose above no discovered checksums should be provided, but instead, only
// tool-derived checksums.
Expand Down Expand Up @@ -280,6 +266,49 @@ func toFormatPackages(catalog *pkg.Catalog) map[spdx.ElementID]*spdx.Package2_2
return results
}

func toPackageChecksums(p pkg.Package) map[spdx.ChecksumAlgorithm]spdx.Checksum {
checksums := map[spdx.ChecksumAlgorithm]spdx.Checksum{}
switch meta := p.Metadata.(type) {
// we generate digest for some Java packages
// see page 33 of the spdx specification for 2.2
// spdx.github.io/spdx-spec/package-information/#710-package-checksum-field
case pkg.JavaMetadata:
if len(meta.ArchiveDigests) > 0 {
for _, digest := range meta.ArchiveDigests {
checksums[spdx.ChecksumAlgorithm(digest.Algorithm)] = spdx.Checksum{
Algorithm: spdx.ChecksumAlgorithm(digest.Algorithm),
Value: digest.Value,
}
}
}
case pkg.GolangBinMetadata:
// hash is base64, but we need hex encode
digest := strings.Split(meta.H1Digest, ":")
if len(digest) == 2 {
algo := digest[0]
hash := digest[1]
checksum, err := base64.StdEncoding.DecodeString(hash)
if err != nil {
log.Debugf("invalid base64 encoded digest: %v", err)
break
}

hexStr := hex.EncodeToString(checksum)

// golang h1 hash == sha256
if algo == "h1" {
algo = "sha256"
}

checksums[spdx.ChecksumAlgorithm(algo)] = spdx.Checksum{
Algorithm: spdx.ChecksumAlgorithm(algo),
Value: hexStr,
}
}
}
return checksums
}

func formatSPDXExternalRefs(p pkg.Package) (refs []*spdx.PackageExternalReference2_2) {
for _, ref := range spdxhelpers.ExternalRefs(p) {
refs = append(refs, &spdx.PackageExternalReference2_2{
Expand Down