mirror of
https://github.com/anchore/syft.git
synced 2025-11-17 16:33:21 +01:00
* [wip] single sbom doc Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * fix tests Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * fix more tests Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * fix linting Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * update cli tests Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * remove scope in import path Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * swap SPDX tag-value formatter to single sbom document Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * bust CLI cache Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * update fixture to byte diff Signed-off-by: Christopher Angelo Phillips <christopher.phillips@anchore.com> * byte for byte Signed-off-by: Christopher Angelo Phillips <christopher.phillips@anchore.com> * bust the cache Signed-off-by: Christopher Angelo Phillips <christopher.phillips@anchore.com> * who needs cache Signed-off-by: Christopher Angelo Phillips <christopher.phillips@anchore.com> * add jar for testing Signed-off-by: Christopher Angelo Phillips <christopher.phillips@anchore.com> * no more bit flips Signed-off-by: Christopher Angelo Phillips <christopher.phillips@anchore.com> * update apk with the delta for image and directory cases Signed-off-by: Christopher Angelo Phillips <christopher.phillips@anchore.com> * restore cache workflow Signed-off-by: Christopher Angelo Phillips <christopher.phillips@anchore.com> Co-authored-by: Christopher Angelo Phillips <christopher.phillips@anchore.com>
129 lines
4.1 KiB
Go
129 lines
4.1 KiB
Go
package spdx22json
|
|
|
|
import (
|
|
"fmt"
|
|
"path"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/anchore/syft/syft/sbom"
|
|
|
|
"github.com/anchore/syft/internal"
|
|
"github.com/anchore/syft/internal/formats/common/spdxhelpers"
|
|
"github.com/anchore/syft/internal/formats/spdx22json/model"
|
|
"github.com/anchore/syft/internal/spdxlicense"
|
|
"github.com/anchore/syft/internal/version"
|
|
"github.com/anchore/syft/syft/pkg"
|
|
"github.com/anchore/syft/syft/source"
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
// toFormatModel creates and populates a new JSON document struct that follows the SPDX 2.2 spec from the given cataloging results.
|
|
func toFormatModel(s sbom.SBOM) model.Document {
|
|
name := documentName(s.Source)
|
|
packages, files, relationships := extractFromCatalog(s.Artifacts.PackageCatalog)
|
|
|
|
return model.Document{
|
|
Element: model.Element{
|
|
SPDXID: model.ElementID("DOCUMENT").String(),
|
|
Name: name,
|
|
},
|
|
SPDXVersion: model.Version,
|
|
CreationInfo: model.CreationInfo{
|
|
Created: time.Now().UTC(),
|
|
Creators: []string{
|
|
// note: key-value format derived from the JSON example document examples: https://github.com/spdx/spdx-spec/blob/v2.2/examples/SPDXJSONExample-v2.2.spdx.json
|
|
"Organization: Anchore, Inc",
|
|
"Tool: " + internal.ApplicationName + "-" + version.FromBuild().Version,
|
|
},
|
|
LicenseListVersion: spdxlicense.Version,
|
|
},
|
|
DataLicense: "CC0-1.0",
|
|
DocumentNamespace: documentNamespace(name, s.Source),
|
|
Packages: packages,
|
|
Files: files,
|
|
Relationships: relationships,
|
|
}
|
|
}
|
|
|
|
func documentName(srcMetadata source.Metadata) string {
|
|
switch srcMetadata.Scheme {
|
|
case source.ImageScheme:
|
|
return cleanSPDXName(srcMetadata.ImageMetadata.UserInput)
|
|
case source.DirectoryScheme:
|
|
return cleanSPDXName(srcMetadata.Path)
|
|
}
|
|
|
|
// TODO: is this alright?
|
|
return uuid.Must(uuid.NewRandom()).String()
|
|
}
|
|
|
|
func documentNamespace(name string, srcMetadata source.Metadata) string {
|
|
input := "unknown-source-type"
|
|
switch srcMetadata.Scheme {
|
|
case source.ImageScheme:
|
|
input = "image"
|
|
case source.DirectoryScheme:
|
|
input = "dir"
|
|
}
|
|
|
|
uniqueID := uuid.Must(uuid.NewRandom())
|
|
identifier := path.Join(input, uniqueID.String())
|
|
if name != "." {
|
|
identifier = path.Join(input, fmt.Sprintf("%s-%s", name, uniqueID.String()))
|
|
}
|
|
|
|
return path.Join(anchoreNamespace, identifier)
|
|
}
|
|
|
|
func extractFromCatalog(catalog *pkg.Catalog) ([]model.Package, []model.File, []model.Relationship) {
|
|
packages := make([]model.Package, 0)
|
|
relationships := make([]model.Relationship, 0)
|
|
files := make([]model.File, 0)
|
|
|
|
for _, p := range catalog.Sorted() {
|
|
license := spdxhelpers.License(p)
|
|
packageSpdxID := model.ElementID(fmt.Sprintf("Package-%+v-%s-%s", p.Type, p.Name, p.Version)).String()
|
|
|
|
packageFiles, fileIDs, packageFileRelationships := spdxhelpers.Files(packageSpdxID, p)
|
|
files = append(files, packageFiles...)
|
|
|
|
relationships = append(relationships, packageFileRelationships...)
|
|
|
|
// 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{
|
|
Description: spdxhelpers.Description(p),
|
|
DownloadLocation: spdxhelpers.DownloadLocation(p),
|
|
ExternalRefs: spdxhelpers.ExternalRefs(p),
|
|
FilesAnalyzed: false,
|
|
HasFiles: fileIDs,
|
|
Homepage: spdxhelpers.Homepage(p),
|
|
LicenseDeclared: license, // The Declared License is what the authors of a project believe govern the package
|
|
Originator: spdxhelpers.Originator(p),
|
|
SourceInfo: spdxhelpers.SourceInfo(p),
|
|
VersionInfo: p.Version,
|
|
Item: model.Item{
|
|
LicenseConcluded: license, // The Concluded License field is the license the SPDX file creator believes governs the package
|
|
Element: model.Element{
|
|
SPDXID: packageSpdxID,
|
|
Name: p.Name,
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
return packages, files, relationships
|
|
}
|
|
|
|
func cleanSPDXName(name string) string {
|
|
// remove # according to specification
|
|
name = strings.ReplaceAll(name, "#", "-")
|
|
|
|
// remove : for url construction
|
|
name = strings.ReplaceAll(name, ":", "-")
|
|
|
|
// clean relative pathing
|
|
return path.Clean(name)
|
|
}
|