Added Document Fields to SBOM generator

Added functions to generate unique spdx doc namespace and generate a
clean document name

Test: m compliance_sbom

Bug: 265472710
Change-Id: I86ea9ddf50d066e139b757e8a093f98b8df8c81f
diff --git a/tools/compliance/cmd/sbom/sbom.go b/tools/compliance/cmd/sbom/sbom.go
index 1477ca5..3cdfa0a 100644
--- a/tools/compliance/cmd/sbom/sbom.go
+++ b/tools/compliance/cmd/sbom/sbom.go
@@ -16,6 +16,8 @@
 
 import (
 	"bytes"
+	"crypto/sha1"
+	"encoding/hex"
 	"flag"
 	"fmt"
 	"io"
@@ -194,11 +196,12 @@
 	os.Exit(0)
 }
 
-type creationTimeGetter func() time.Time
+type creationTimeGetter func() string
 
 // actualTime returns current time in UTC
-func actualTime() time.Time {
-	return time.Now().UTC()
+func actualTime() string {
+	t := time.Now().UTC()
+	return t.UTC().Format("2006-01-02T15:04:05Z")
 }
 
 // replaceSlashes replaces "/" by "-" for the library path to be used for packages & files SPDXID
@@ -206,6 +209,23 @@
 	return strings.ReplaceAll(x, "/", "-")
 }
 
+// stripDocName removes the outdir prefix and meta_lic suffix from a target Name
+func stripDocName(name string) string {
+	// remove outdir prefix
+	if strings.HasPrefix(name, "out/") {
+		name = name[4:]
+	}
+
+	// remove suffix
+	if strings.HasSuffix(name, ".meta_lic") {
+		name = name[:len(name)-9]
+	} else if strings.HasSuffix(name, "/meta_lic") {
+		name = name[:len(name)-9] + "/"
+	}
+
+	return name
+}
+
 // getPackageName returns a package name of a target Node
 func getPackageName(_ *context, tn *compliance.TargetNode) string {
 	return replaceSlashes(tn.Name())
@@ -223,8 +243,7 @@
 		return replaceSlashes(tn.ModuleName())
 	}
 
-	// TO DO: Replace tn.Name() with pm.Name() + parts of the target name
-	return replaceSlashes(tn.Name())
+	return stripDocName(replaceSlashes(tn.Name()))
 }
 
 // getDownloadUrl returns the download URL if available (GIT, SVN, etc..),
@@ -295,6 +314,19 @@
 	return files
 }
 
+// generateSPDXNamespace generates a unique SPDX Document Namespace using a SHA1 checksum
+// and the CreationInfo.Created field as the date.
+func generateSPDXNamespace(created string) string {
+	// Compute a SHA1 checksum of the CreationInfo.Created field.
+	hash := sha1.Sum([]byte(created))
+	checksum := hex.EncodeToString(hash[:])
+
+	// Combine the checksum and timestamp to generate the SPDX Namespace.
+	namespace := fmt.Sprintf("SPDXRef-DOCUMENT-%s-%s", created, checksum)
+
+	return namespace
+}
+
 // sbomGenerator implements the spdx bom utility
 
 // SBOM is part of the new government regulation issued to improve national cyber security
@@ -325,8 +357,11 @@
 	// creating the license section
 	otherLicenses := []*spdx.OtherLicense{}
 
-        // main package name
-        var mainPkgName string
+	// spdx document name
+	var docName string
+
+	// main package name
+	var mainPkgName string
 
 	// implementing the licenses references for the packages
 	licenses := make(map[string]string)
@@ -365,6 +400,7 @@
 			}
 
 			if isMainPackage {
+				docName = getDocumentName(ctx, tn, pm)
 				mainPkgName = replaceSlashes(getPackageName(ctx, tn))
 				isMainPackage = false
 			}
@@ -478,11 +514,17 @@
 		return nil, nil, fmt.Errorf("Unable to build creation info section for SPDX doc: %v\n", err)
 	}
 
+	ci.Created = ctx.creationTime()
+
 	return &spdx.Document{
-		SPDXIdentifier: "DOCUMENT",
-		CreationInfo:   ci,
-		Packages:       pkgs,
-		Relationships:  relationships,
-		OtherLicenses:  otherLicenses,
+		SPDXVersion:       "SPDX-2.2",
+		DataLicense:       "CC0-1.0",
+		SPDXIdentifier:    "DOCUMENT",
+		DocumentName:      docName,
+		DocumentNamespace: generateSPDXNamespace(ci.Created),
+		CreationInfo:      ci,
+		Packages:          pkgs,
+		Relationships:     relationships,
+		OtherLicenses:     otherLicenses,
 	}, deps, nil
 }