<commit_before>package spdx22json

import (
	"fmt"
	"sort"
	"strings"
	"time"

	"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/log"
	"github.com/anchore/syft/internal/spdxlicense"
	"github.com/anchore/syft/internal/version"
	"github.com/anchore/syft/syft/artifact"
	"github.com/anchore/syft/syft/file"
	"github.com/anchore/syft/syft/pkg"
	"github.com/anchore/syft/syft/sbom"
	"github.com/anchore/syft/syft/source"
)

// 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, error) {
	name, namespace, err := spdxhelpers.DocumentNameAndNamespace(s.Source)
	if err != nil {
		return nil, err
	}

	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: namespace,
		Packages:          toPackages(s.Artifacts.PackageCatalog, s.Relationships),
		Files:             toFiles(s),
		Relationships:     toRelationships(s.Relationships),
	}, nil
}

func toPackages(catalog *pkg.Catalog, relationships []artifact.Relationship) []model.Package {
	packages := make([]model.Package, 0)

	for _, p := range catalog.Sorted() {
		license := spdxhelpers.License(p)
		packageSpdxID := model.ElementID(p.ID()).String()
		filesAnalyzed := false

		// 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:     digest.Algorithm,
						ChecksumValue: digest.Value,
					})
				}
			}
		}
		// note: the license concluded and declared should be the same since we are collecting license information<commit_msg>Update spdx22json to only take uppercase checksum algorithm (#946)<commit_after>package spdx22json

import (
	"fmt"
	"sort"
	"strings"
	"time"

	"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/log"
	"github.com/anchore/syft/internal/spdxlicense"
	"github.com/anchore/syft/internal/version"
	"github.com/anchore/syft/syft/artifact"
	"github.com/anchore/syft/syft/file"
	"github.com/anchore/syft/syft/pkg"
	"github.com/anchore/syft/syft/sbom"
	"github.com/anchore/syft/syft/source"
)

// 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, error) {
	name, namespace, err := spdxhelpers.DocumentNameAndNamespace(s.Source)
	if err != nil {
		return nil, err
	}

	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: namespace,
		Packages:          toPackages(s.Artifacts.PackageCatalog, s.Relationships),
		Files:             toFiles(s),
		Relationships:     toRelationships(s.Relationships),
	}, nil
}

func toPackages(catalog *pkg.Catalog, relationships []artifact.Relationship) []model.Package {
	packages := make([]model.Package, 0)

	for _, p := range catalog.Sorted() {
		license := spdxhelpers.License(p)
		packageSpdxID := model.ElementID(p.ID()).String()
		filesAnalyzed := false

		// 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
{'fraction_non_alphanumeric': 0.12754912866147572, 'fraction_numerical': 0.009084167593622544, 'mean_word_length': 3.9224452554744524, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 5, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1207165', 'n_tokens_mistral': 1992, 'n_tokens_neox': 1788, 'n_words': 359}