Repository: microsoft/sbom-tool
Pull request #1016: NoAssertion bug for SBOM file and package generation
Author: login: pragnya17
Created: 2025-04-18T14:11:09Z | Merged: 2025-04-22T18:26:13Z

For files, if there is no `LicenseInfoInFiles` we should create a `NOASSERTION` element to and a `HAS_DECLARED_LICENSE` relationship between the file and the `NOASSERTION` element. However, instead of generating a NOASSERTION element to represent the license we were just generating no element. This PR fixes this bug by generating the correct NOASSERTION element when there is no license info.

Additionally, we make the NOASSERTION value for the element capitalized, so that it is consistent with SPDX 2.2. This applies to both package and files.


Diff:
diff --git a/src/Microsoft.Sbom.Common/Constants.cs b/src/Microsoft.Sbom.Common/Constants.cs
index befc9b5c9..26daed0e9 100644
--- a/src/Microsoft.Sbom.Common/Constants.cs
+++ b/src/Microsoft.Sbom.Common/Constants.cs
@@ -26,4 +26,5 @@ public static class Constants
     public const string SPDXRefFile = "SPDXRef-File";
     public const string SPDXRefPackage = "SPDXRef-Package";
     public const string SPDXRefExternalDocument = "DocumentRef";
+    public const string NoAssertionValue = "NOASSERTION";
 }
diff --git a/src/Microsoft.Sbom.Common/Spdx30Entities/NoAssertionElement.cs b/src/Microsoft.Sbom.Common/Spdx30Entities/NoAssertionElement.cs
index 4a3b9e39e..263333669 100644
--- a/src/Microsoft.Sbom.Common/Spdx30Entities/NoAssertionElement.cs
+++ b/src/Microsoft.Sbom.Common/Spdx30Entities/NoAssertionElement.cs
@@ -7,8 +7,7 @@ public class NoAssertionElement : Element
 {
     public NoAssertionElement()
     {
-        Name = "NoAssertion";
-        SpdxId = "SPDXRef-NoAssertion";
+        Name = Constants.NoAssertionValue;
         Type = nameof(Element);
     }
 }
diff --git a/src/Microsoft.Sbom.Parsers.Spdx30SbomParser/Generator.cs b/src/Microsoft.Sbom.Parsers.Spdx30SbomParser/Generator.cs
index 9a6839114..7270aacd9 100644
--- a/src/Microsoft.Sbom.Parsers.Spdx30SbomParser/Generator.cs
+++ b/src/Microsoft.Sbom.Parsers.Spdx30SbomParser/Generator.cs
@@ -28,6 +28,8 @@ namespace Microsoft.Sbom.Parsers.Spdx30SbomParser;
 /// </summary>
 public class Generator : IManifestGenerator
 {
+    private static readonly NoAssertionElement StaticNoAssertionElement = CreateStaticNoAssertionElement();
+
     public AlgorithmName[] RequiredHashAlgorithms => new[] { AlgorithmName.SHA256, AlgorithmName.SHA1 };
 
     public string Version { get; set; } = string.Join("-", Constants.SPDXName, Constants.SPDXVersion);
@@ -525,18 +527,22 @@ private List<Element> GetSpdxRelationshipsFromSbomFile(Element spdxFileElement,
         spdxRelationshipLicenseConcludedElement.AddSpdxId();
         spdxRelationshipAndLicenseElementsToAddToSBOM.Add(spdxRelationshipLicenseConcludedElement);
 
-        // If they exist, convert licenseDeclared to SPDX license elements and add Relationship elements for them
-        if (fileInfo.LicenseInfoInFiles == null || !fileInfo.LicenseInfoInFiles.Any())
-        {
-            return spdxRelationshipAndLicenseElementsToAddToSBOM;
-        }
-
         var toRelationships = new List<string>();
-        foreach (var licenseInfoInOneFile in fileInfo.LicenseInfoInFiles)
+        if (fileInfo.LicenseInfoInFiles is null)
         {
-            var licenseDeclaredElement = GenerateLicenseElement(licenseInfoInOneFile);
+            var licenseDeclaredElement = GenerateLicenseElement(null);
+            spdxRelationshipAndLicenseElementsToAddToSBOM.Add(licenseDeclaredElement);
             toRelationships.Add(licenseDeclaredElement.SpdxId);
         }
+        else
+        {
+            foreach (var licenseInfoInOneFile in fileInfo.LicenseInfoInFiles)
+            {
+                var licenseDeclaredElement = GenerateLicenseElement(licenseInfoInOneFile);
+                spdxRelationshipAndLicenseElementsToAddToSBOM.Add(licenseDeclaredElement);
+                toRelationships.Add(licenseDeclaredElement.SpdxId);
+            }
+        }
 
         var spdxRelationshipLicenseDeclaredElement = new SpdxEntities.Relationship
         {
@@ -588,16 +594,12 @@ private List<Element> GetSpdxRelationshipsAndLicensesFromSbomPackage(SbomPackage
 
     private Element GenerateLicenseElement(string licenseInfo)
     {
-        Element licenseElement = null;
-        if (licenseInfo == null)
-        {
-            licenseElement = new NoAssertionElement();
-        }
-        else
+        if (licenseInfo is null)
         {
-            licenseElement = new AnyLicenseInfo { Name = licenseInfo };
+            return StaticNoAssertionElement;
         }
 
+        var licenseElement = new AnyLicenseInfo { Name = licenseInfo };
         licenseElement.AddSpdxId();
         return licenseElement;
     }
@@ -682,4 +684,11 @@ private PackageVerificationCode GetPackageVerificationCode(IInternalMetadataProv
 
         return (sbomToolName, sbomToolVersion, packageName, packageVersion, documentName, creationInfo);
     }
+
+    private static NoAssertionElement CreateStaticNoAssertionElement()
+    {
+        var noAssertionElement = new NoAssertionElement();
+        noAssertionElement.AddSpdxId();
+        return noAssertionElement;
+    }
 }
diff --git a/test/Microsoft.Sbom.Api.Tests/Workflows/ManifestGenerationWorkflowTests.cs b/test/Microsoft.Sbom.Api.Tests/Workflows/ManifestGenerationWorkflowTests.cs
index b6b08432c..6f0a525d4 100644
--- a/test/Microsoft.Sbom.Api.Tests/Workflows/ManifestGenerationWorkflowTests.cs
+++ b/test/Microsoft.Sbom.Api.Tests/Workflows/ManifestGenerationWorkflowTests.cs
@@ -397,7 +397,7 @@ await externalDocumentReferenceChannel.Writer.WriteAsync(new ExternalDocumentRef
             Assert.AreEqual(1, creationInfo.Count);
 
             // Test deduplication
-            var noAssertionElements = elements.Where(element => element["spdxId"].ToString() == "SPDXRef-Element-D6D57C0C9CC2CAC35C83DE0C8E4C8C37B87C0A58DA49BB31EBEBC6E200F54D4B").ToList();
+            var noAssertionElements = elements.Where(element => element["spdxId"].ToString() == "SPDXRef-Element-8560FC6692684D8DF52223FF78E30B9630A1CF5A6FA371AAE24FCA896AE20969").ToList();
             Assert.AreEqual(1, noAssertionElements.Count);
 
             var organizationElements = elements.Where(element => element["type"].ToString() == "Organization").ToList();
diff --git a/test/Microsoft.Sbom.Parsers.Spdx30SbomParser.Tests/Parser/GeneratorTests.cs b/test/Microsoft.Sbom.Parsers.Spdx30SbomParser.Tests/Parser/GeneratorTests.cs
index c1c922843..89f7d5fd6 100644
--- a/test/Microsoft.Sbom.Parsers.Spdx30SbomParser.Tests/Parser/GeneratorTests.cs
+++ b/test/Microsoft.Sbom.Parsers.Spdx30SbomParser.Tests/Parser/GeneratorTests.cs
@@ -130,14 +130,37 @@ public void GenerateJsonDocument_DependsOnId_ValidValue_GeneratesSpdxPackageId()
     }
 
     [TestMethod]
-    public void GenerateJsonDocumentTest_File()
+    public void GenerateJsonDocumentTest_File_WithConcludedAndNoDeclaredLicense()
     {
         var fileInfo = new InternalSbomFileInfo
         {
             Checksum = GetSampleChecksums(),
             FileCopyrightText = "sampleCopyright",
             LicenseConcluded = "sampleLicense1",
-            LicenseInfoInFiles = new List<string> { "sampleLicense1" },
+            LicenseInfoInFiles = null,
+            Path = "./sample/path",
+        };
+
+        var generatorResult = generator.GenerateJsonDocument(fileInfo);
+        var generatedJsonString = generatorResult.Document.RootElement.GetRawText();
+        generatedJsonString = NormalizeString(generatedJsonString);
+
+        var expectedJsonContentAsString = SbomFileJsonStrings.FileWithNoDeclaredLicense;
+        expectedJsonContentAsString = NormalizeString(expectedJsonContentAsString);
+
+        Assert.IsFalse(generatedJsonString.Contains("null"));
+        Assert.AreEqual(expectedJsonContentAsString, generatedJsonString);
+    }
+
+    [TestMethod]
+    public void GenerateJsonDocumentTest_File_WithConcludedAndDeclaredLicense()
+    {
+        var fileInfo = new InternalSbomFileInfo
+        {
+            Checksum = GetSampleChecksums(),
+            FileCopyrightText = "sampleCopyright",
+            LicenseConcluded = "sampleLicense1",
+            LicenseInfoInFiles = new List<string> { "sampleLicense2" },
             Path = "./sample/path",
         };
 
diff --git a/test/Microsoft.Sbom.Parsers.Spdx30SbomParser.Tests/Parser/JsonStrings/SbomFileJsonStrings.cs b/test/Microsoft.Sbom.Parsers.Spdx30SbomParser.Tests/Parser/JsonStrings/SbomFileJsonStrings.cs
index ff4f02c67..a642c3c79 100644
--- a/test/Microsoft.Sbom.Parsers.Spdx30SbomParser.Tests/Parser/JsonStrings/SbomFileJsonStrings.cs
+++ b/test/Microsoft.Sbom.Parsers.Spdx30SbomParser.Tests/Parser/JsonStrings/SbomFileJsonStrings.cs
@@ -47,15 +47,81 @@ public static class SbomFileJsonStrings
             ""spdxId"": ""SPDXRef-Relationship-2A69D50DCB6D763C5C4FFCE6A4F6C3166DD8F6DB3F77BDD4B6129C0B33F238DA"",
             ""type"": ""Relationship""
           },
+          {
+            ""creationInfo"": ""_:creationinfo"",
+            ""name"": ""sampleLicense2"",
+            ""spdxId"": ""SPDXRef-AnyLicenseInfo-D6DF0CBEE7E2F48211D701973C9B5EE5440D18CA45E5BD82C15C9336A1FF79A4"",
+            ""type"": ""AnyLicenseInfo""
+          },
           {
             ""from"": ""SPDXRef-File-.-sample-path-sha1Value"",
             ""relationshipType"": ""HAS_DECLARED_LICENSE"",
+            ""to"": [
+              ""SPDXRef-AnyLicenseInfo-D6DF0CBEE7E2F48211D701973C9B5EE5440D18CA45E5BD82C15C9336A1FF79A4""
+            ],
+            ""creationInfo"": ""_:creationinfo"",
+            ""spdxId"": ""SPDXRef-Relationship-222E099165617B282F2B424519FC133796AA0189D0238FD121CCF3B0340D4301"",
+            ""type"": ""Relationship""
+          }
+    ]";
+
+    [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "JSON002:Probable JSON string detected", Justification = "Need to use JSON string")]
+    public const string FileWithNoDeclaredLicense =
+    @"[
+          {
+            ""name"": ""./sample/path"",
+            ""software_copyrightText"": ""sampleCopyright"",
+            ""creationInfo"": ""_:creationinfo"",
+            ""spdxId"": ""SPDXRef-File-.-sample-path-sha1Value"",
+            ""verifiedUsing"": [
+              {
+                ""algorithm"": ""sha1"",
+                ""hashValue"": ""sha1value"",
+                ""creationInfo"": ""_:creationinfo"",
+                ""spdxId"": ""SPDXRef-PackageVerificationCode-B1565820A5CDAC40E0520D23F9D0B1497F240DDC51D72EAC6423D97D952D444F"",
+                ""type"": ""PackageVerificationCode""
+              },
+              {
+                ""algorithm"": ""sha256"",
+                ""hashValue"": ""sha256value"",
+                ""creationInfo"": ""_:creationinfo"",
+                ""spdxId"": ""SPDXRef-PackageVerificationCode-5D5B09F6DCB2D53A5FFFC60C4AC0D55FABDF556069D6631545F42AA6E3500F2E"",
+                ""type"": ""PackageVerificationCode""
+              }
+            ],
+            ""type"": ""software_File""
+          },
+          {
+            ""creationInfo"": ""_:creationinfo"",
+            ""name"": ""sampleLicense1"",
+            ""spdxId"": ""SPDXRef-AnyLicenseInfo-3BA3FA6D3D66FE2BA75992BB0850D080F1223256368A76C77BEF8E0F6AC71896"",
+            ""type"": ""AnyLicenseInfo""
+          },
+          {
+            ""from"": ""SPDXRef-File-.-sample-path-sha1Value"",
+            ""relationshipType"": ""HAS_CONCLUDED_LICENSE"",
             ""to"": [
               ""SPDXRef-AnyLicenseInfo-3BA3FA6D3D66FE2BA75992BB0850D080F1223256368A76C77BEF8E0F6AC71896""
             ],
             ""creationInfo"": ""_:creationinfo"",
+            ""spdxId"": ""SPDXRef-Relationship-2A69D50DCB6D763C5C4FFCE6A4F6C3166DD8F6DB3F77BDD4B6129C0B33F238DA"",
+            ""type"": ""Relationship""
+          },
+          {
+            ""creationInfo"": ""_:creationinfo"",
+            ""name"": ""NOASSERTION"",
+            ""spdxId"": ""SPDXRef-Element-8560FC6692684D8DF52223FF78E30B9630A1CF5A6FA371AAE24FCA896AE20969"",
+            ""type"": ""Element""
+          },
+          {
+            ""from"": ""SPDXRef-File-.-sample-path-sha1Value"",
+            ""relationshipType"": ""HAS_DECLARED_LICENSE"",
+            ""to"": [
+              ""SPDXRef-Element-8560FC6692684D8DF52223FF78E30B9630A1CF5A6FA371AAE24FCA896AE20969""
+            ],
+            ""creationInfo"": ""_:creationinfo"",
             ""spdxId"": ""SPDXRef-Relationship-222E099165617B282F2B424519FC133796AA0189D0238FD121CCF3B0340D4301"",
             ""type"": ""Relationship""
           }
-        ]";
+    ]";
 }
diff --git a/test/Microsoft.Sbom.Parsers.Spdx30SbomParser.Tests/Parser/JsonStrings/SbomPackageJsonStrings.cs b/test/Microsoft.Sbom.Parsers.Spdx30SbomParser.Tests/Parser/JsonStrings/SbomPackageJsonStrings.cs
index b83929a80..aba11234f 100644
--- a/test/Microsoft.Sbom.Parsers.Spdx30SbomParser.Tests/Parser/JsonStrings/SbomPackageJsonStrings.cs
+++ b/test/Microsoft.Sbom.Parsers.Spdx30SbomParser.Tests/Parser/JsonStrings/SbomPackageJsonStrings.cs
@@ -35,15 +35,15 @@ public static class SbomPackageJsonStrings
         },
         {
         ""creationInfo"": ""_:creationinfo"",
-        ""name"": ""NoAssertion"",
-        ""spdxId"": ""SPDXRef-Element-D6D57C0C9CC2CAC35C83DE0C8E4C8C37B87C0A58DA49BB31EBEBC6E200F54D4B"",
+        ""name"": ""NOASSERTION"",
+        ""spdxId"": ""SPDXRef-Element-8560FC6692684D8DF52223FF78E30B9630A1CF5A6FA371AAE24FCA896AE20969"",
         ""type"": ""Element""
         },
         {
         ""from"": ""SPDXRef-Package-4739C82D88855A138C811B8CE05CC97113BEC7F7C7F66EC7E4C6C176EEA0FECE"",
         ""relationshipType"": ""HAS_CONCLUDED_LICENSE"",
         ""to"": [
-            ""SPDXRef-Element-D6D57C0C9CC2CAC35C83DE0C8E4C8C37B87C0A58DA49BB31EBEBC6E200F54D4B""
+            ""SPDXRef-Element-8560FC6692684D8DF52223FF78E30B9630A1CF5A6FA371AAE24FCA896AE20969""
         ],
         ""creationInfo"": ""_:creationinfo"",
         ""spdxId"": ""SPDXRef-Relationship-2A69D50DCB6D763C5C4FFCE6A4F6C3166DD8F6DB3F77BDD4B6129C0B33F238DA"",
@@ -51,15 +51,15 @@ public static class SbomPackageJsonStrings
         },
         {
         ""creationInfo"": ""_:creationinfo"",
-        ""name"": ""NoAssertion"",
-        ""spdxId"": ""SPDXRef-Element-D6D57C0C9CC2CAC35C83DE0C8E4C8C37B87C0A58DA49BB31EBEBC6E200F54D4B"",
+        ""name"": ""NOASSERTION"",
+        ""spdxId"": ""SPDXRef-Element-8560FC6692684D8DF52223FF78E30B9630A1CF5A6FA371AAE24FCA896AE20969"",
         ""type"": ""Element""
         },
         {
         ""from"": ""SPDXRef-Package-4739C82D88855A138C811B8CE05CC97113BEC7F7C7F66EC7E4C6C176EEA0FECE"",
         ""relationshipType"": ""HAS_DECLARED_LICENSE"",
         ""to"": [
-            ""SPDXRef-Element-D6D57C0C9CC2CAC35C83DE0C8E4C8C37B87C0A58DA49BB31EBEBC6E200F54D4B""
+            ""SPDXRef-Element-8560FC6692684D8DF52223FF78E30B9630A1CF5A6FA371AAE24FCA896AE20969""
         ],
         ""creationInfo"": ""_:creationinfo"",
         ""spdxId"": ""SPDXRef-Relationship-222E099165617B282F2B424519FC133796AA0189D0238FD121CCF3B0340D4301"",
@@ -106,15 +106,15 @@ public static class SbomPackageJsonStrings
       },
       {
         ""creationInfo"": ""_:creationinfo"",
-        ""name"": ""NoAssertion"",
-        ""spdxId"": ""SPDXRef-Element-D6D57C0C9CC2CAC35C83DE0C8E4C8C37B87C0A58DA49BB31EBEBC6E200F54D4B"",
+        ""name"": ""NOASSERTION"",
+        ""spdxId"": ""SPDXRef-Element-8560FC6692684D8DF52223FF78E30B9630A1CF5A6FA371AAE24FCA896AE20969"",
         ""type"": ""Element""
       },
       {
         ""from"": ""SPDXRef-RootPackage"",
         ""relationshipType"": ""HAS_DECLARED_LICENSE"",
         ""to"": [
-          ""SPDXRef-Element-D6D57C0C9CC2CAC35C83DE0C8E4C8C37B87C0A58DA49BB31EBEBC6E200F54D4B""
+          ""SPDXRef-Element-8560FC6692684D8DF52223FF78E30B9630A1CF5A6FA371AAE24FCA896AE20969""
         ],
         ""creationInfo"": ""_:creationinfo"",
         ""spdxId"": ""SPDXRef-Relationship-222E099165617B282F2B424519FC133796AA0189D0238FD121CCF3B0340D4301"",
@@ -124,7 +124,7 @@ public static class SbomPackageJsonStrings
         ""from"": ""SPDXRef-RootPackage"",
         ""relationshipType"": ""HAS_CONCLUDED_LICENSE"",
         ""to"": [
-          ""SPDXRef-Element-D6D57C0C9CC2CAC35C83DE0C8E4C8C37B87C0A58DA49BB31EBEBC6E200F54D4B""
+          ""SPDXRef-Element-8560FC6692684D8DF52223FF78E30B9630A1CF5A6FA371AAE24FCA896AE20969""
         ],
         ""creationInfo"": ""_:creationinfo"",
         ""spdxId"": ""SPDXRef-Relationship-2A69D50DCB6D763C5C4FFCE6A4F6C3166DD8F6DB3F77BDD4B6129C0B33F238DA"",


Reviews:
user: login: DaveTryon
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-21T21:25:32Z
user: login: DaveTryon
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-21T21:31:37Z
user: login: pragnya17
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-21T22:10:06Z
user: login: DaveTryon
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-21T22:36:19Z
user: login: DaveTryon
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-21T22:38:08Z
user: login: pragnya17
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-21T23:30:25Z
user: login: DaveTryon
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-21T23:35:34Z
user: login: DaveTryon
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-22T18:25:54Z

Review comments:
diff_hunk: @@ -524,18 +524,22 @@ private List<Element> GetSpdxRelationshipsFromSbomFile(Element spdxFileElement,
         spdxRelationshipLicenseConcludedElement.AddSpdxId();
         spdxRelationshipAndLicenseElementsToAddToSBOM.Add(spdxRelationshipLicenseConcludedElement);
 
-        // If they exist, convert licenseDeclared to SPDX license elements and add Relationship elements for them
-        if (fileInfo.LicenseInfoInFiles == null || !fileInfo.LicenseInfoInFiles.Any())
-        {
-            return spdxRelationshipAndLicenseElementsToAddToSBOM;
-        }
-
         var toRelationships = new List<string>();
-        foreach (var licenseInfoInOneFile in fileInfo.LicenseInfoInFiles)
+        if (fileInfo.LicenseInfoInFiles == null)
path: src/Microsoft.Sbom.Parsers.Spdx30SbomParser/Generator.cs
user: login: DaveTryon
body: ```suggestion
        if (fileInfo.LicenseInfoInFiles is null)
```
created_at: 2025-04-21T21:25:32Z
author_association: CONTRIBUTOR
diff_hunk: @@ -524,18 +524,22 @@ private List<Element> GetSpdxRelationshipsFromSbomFile(Element spdxFileElement,
         spdxRelationshipLicenseConcludedElement.AddSpdxId();
         spdxRelationshipAndLicenseElementsToAddToSBOM.Add(spdxRelationshipLicenseConcludedElement);
 
-        // If they exist, convert licenseDeclared to SPDX license elements and add Relationship elements for them
-        if (fileInfo.LicenseInfoInFiles == null || !fileInfo.LicenseInfoInFiles.Any())
-        {
-            return spdxRelationshipAndLicenseElementsToAddToSBOM;
-        }
-
         var toRelationships = new List<string>();
-        foreach (var licenseInfoInOneFile in fileInfo.LicenseInfoInFiles)
+        if (fileInfo.LicenseInfoInFiles == null)
         {
-            var licenseDeclaredElement = GenerateLicenseElement(licenseInfoInOneFile);
+            var licenseDeclaredElement = GenerateLicenseElement(null);
path: src/Microsoft.Sbom.Parsers.Spdx30SbomParser/Generator.cs
user: login: DaveTryon
body: This always generates a new `NoAssertionElement`. Would it be useful to create a single immutable `NoAssertionElement` that we just use over and over? The "immutable" part may make this more trouble that it's worth
created_at: 2025-04-21T21:31:37Z
author_association: CONTRIBUTOR
diff_hunk: @@ -524,18 +524,22 @@ private List<Element> GetSpdxRelationshipsFromSbomFile(Element spdxFileElement,
         spdxRelationshipLicenseConcludedElement.AddSpdxId();
         spdxRelationshipAndLicenseElementsToAddToSBOM.Add(spdxRelationshipLicenseConcludedElement);
 
-        // If they exist, convert licenseDeclared to SPDX license elements and add Relationship elements for them
-        if (fileInfo.LicenseInfoInFiles == null || !fileInfo.LicenseInfoInFiles.Any())
-        {
-            return spdxRelationshipAndLicenseElementsToAddToSBOM;
-        }
-
         var toRelationships = new List<string>();
-        foreach (var licenseInfoInOneFile in fileInfo.LicenseInfoInFiles)
+        if (fileInfo.LicenseInfoInFiles == null)
         {
-            var licenseDeclaredElement = GenerateLicenseElement(licenseInfoInOneFile);
+            var licenseDeclaredElement = GenerateLicenseElement(null);
path: src/Microsoft.Sbom.Parsers.Spdx30SbomParser/Generator.cs
user: login: pragnya17
body: I created a static NoAssertion element. Does that accomplish what you were thinking of?
created_at: 2025-04-21T22:10:06Z
author_association: CONTRIBUTOR
diff_hunk: @@ -587,16 +593,12 @@ private List<Element> GetSpdxRelationshipsAndLicensesFromSbomPackage(SbomPackage
 
     private Element GenerateLicenseElement(string licenseInfo)
     {
-        Element licenseElement = null;
         if (licenseInfo == null)
path: src/Microsoft.Sbom.Parsers.Spdx30SbomParser/Generator.cs
user: login: DaveTryon
body: ```suggestion
        if (licenseInfo is null)
```
created_at: 2025-04-21T22:36:19Z
author_association: CONTRIBUTOR
diff_hunk: @@ -524,18 +524,22 @@ private List<Element> GetSpdxRelationshipsFromSbomFile(Element spdxFileElement,
         spdxRelationshipLicenseConcludedElement.AddSpdxId();
         spdxRelationshipAndLicenseElementsToAddToSBOM.Add(spdxRelationshipLicenseConcludedElement);
 
-        // If they exist, convert licenseDeclared to SPDX license elements and add Relationship elements for them
-        if (fileInfo.LicenseInfoInFiles == null || !fileInfo.LicenseInfoInFiles.Any())
-        {
-            return spdxRelationshipAndLicenseElementsToAddToSBOM;
-        }
-
         var toRelationships = new List<string>();
-        foreach (var licenseInfoInOneFile in fileInfo.LicenseInfoInFiles)
+        if (fileInfo.LicenseInfoInFiles == null)
         {
-            var licenseDeclaredElement = GenerateLicenseElement(licenseInfoInOneFile);
+            var licenseDeclaredElement = GenerateLicenseElement(null);
path: src/Microsoft.Sbom.Parsers.Spdx30SbomParser/Generator.cs
user: login: DaveTryon
body: That looks OK, as long as we're confident that we never change its properties. The underlying `Element` object is not immutable, so it creates a potential for an unintended change to apply to multiple places
created_at: 2025-04-21T22:38:08Z
author_association: CONTRIBUTOR
diff_hunk: @@ -524,18 +524,22 @@ private List<Element> GetSpdxRelationshipsFromSbomFile(Element spdxFileElement,
         spdxRelationshipLicenseConcludedElement.AddSpdxId();
         spdxRelationshipAndLicenseElementsToAddToSBOM.Add(spdxRelationshipLicenseConcludedElement);
 
-        // If they exist, convert licenseDeclared to SPDX license elements and add Relationship elements for them
-        if (fileInfo.LicenseInfoInFiles == null || !fileInfo.LicenseInfoInFiles.Any())
-        {
-            return spdxRelationshipAndLicenseElementsToAddToSBOM;
-        }
-
         var toRelationships = new List<string>();
-        foreach (var licenseInfoInOneFile in fileInfo.LicenseInfoInFiles)
+        if (fileInfo.LicenseInfoInFiles == null)
         {
-            var licenseDeclaredElement = GenerateLicenseElement(licenseInfoInOneFile);
+            var licenseDeclaredElement = GenerateLicenseElement(null);
path: src/Microsoft.Sbom.Parsers.Spdx30SbomParser/Generator.cs
user: login: pragnya17
body: The properties of a NoAssertion element are creationInfo, name, and SPDXID. The name is set to a constant NOASSERTION value and therefore the SPDXID will also be constant. CreationInfo is a constant for all elements. So as long as the constant value for NOASSERTION is not changed, it will remain static. 

E2E testing confirms this as well.
created_at: 2025-04-21T23:30:25Z
author_association: CONTRIBUTOR

PR comments:
user: login: pragnya17
created_at: 2025-04-18T15:47:56Z
body: /azp run
author_association: CONTRIBUTOR
user: login: pragnya17
created_at: 2025-04-18T19:26:32Z
body: /azp run
author_association: CONTRIBUTOR
user: login: pragnya17
created_at: 2025-04-18T21:23:22Z
body: /azp run
author_association: CONTRIBUTOR
user: login: pragnya17
created_at: 2025-04-21T22:31:25Z
body: /azp run
author_association: CONTRIBUTOR
user: login: pragnya17
created_at: 2025-04-21T23:30:46Z
body: /azp run
author_association: CONTRIBUTOR
user: login: pragnya17
created_at: 2025-04-22T16:01:34Z
body: /azp run
author_association: CONTRIBUTOR
user: login: pragnya17
created_at: 2025-04-22T17:53:21Z
body: /azp run
author_association: CONTRIBUTOR