From b4317516d57f31f520c2cdbd6b9b4466864dad34 Mon Sep 17 00:00:00 2001 From: Murat Cakir Date: Wed, 10 Dec 2025 02:15:26 +0100 Subject: [PATCH 01/10] Add SBOM generation to build --- .../Smartstore.Build/Build.cs | 58 ++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) diff --git a/src/Smartstore.Build/Smartstore.Build/Build.cs b/src/Smartstore.Build/Smartstore.Build/Build.cs index 993a7a6200..a3c7e4cf99 100644 --- a/src/Smartstore.Build/Smartstore.Build/Build.cs +++ b/src/Smartstore.Build/Smartstore.Build/Build.cs @@ -1,11 +1,13 @@ using System; using System.IO; using System.IO.Compression; +using System.Text.Json; using Nuke.Common; using Nuke.Common.CI; using Nuke.Common.Git; using Nuke.Common.IO; using Nuke.Common.ProjectModel; +using Nuke.Common.Tooling; using Nuke.Common.Tools.DotNet; using Nuke.Common.Tools.GitVersion; using Serilog; @@ -45,6 +47,8 @@ class Build : NukeBuild AbsolutePath SourceDirectory => RootDirectory / "src"; AbsolutePath TestsDirectory => RootDirectory / "test"; AbsolutePath ArtifactsDirectory => RootDirectory / "build" / "artifacts"; + AbsolutePath ToolsDirectory => RootDirectory / "build" / ".tools"; + AbsolutePath CycloneDxToolPath => ToolsDirectory / (EnvironmentInfo.Platform == PlatformFamily.Windows ? "dotnet-CycloneDX.exe" : "dotnet-CycloneDX"); string GetPublishName() { @@ -97,7 +101,7 @@ string GetPublishName() Target Deploy => _ => _ .DependsOn(Compile) - .Triggers(Zip) + .Triggers(Zip, GenerateSbom) .Executes(() => { var publishName = GetPublishName(); @@ -123,6 +127,29 @@ string GetPublishName() .SetOutput(outputDir)); }); + Target GenerateSbom => _ => _ + .DependsOn(Deploy) + .Executes(() => + { + EnsureCycloneDxTool(); + + var publishName = GetPublishName(); + AbsolutePath publishDirectory = ArtifactsDirectory / publishName; + + if (!publishDirectory.DirectoryExists()) + { + throw new Exception($"Published output for {publishName} not found in {ArtifactsDirectory}."); + } + + AbsolutePath sbomFile = publishDirectory / $"Smartstore.{publishName}.sbom.cyclone.json"; + Log.Information($"Generating CycloneDX SBOM at {sbomFile}..."); + + ProcessTasks.StartProcess(CycloneDxToolPath, $"\"{Solution.Path}\" --output \"{sbomFile}\" --json") + .AssertZeroExitCode(); + + PrettifyJson(sbomFile); + }); + Target Zip => _ => _ .Executes(() => { @@ -152,4 +179,33 @@ string GetPublishName() .SetConfiguration(Configuration)); }); + void EnsureCycloneDxTool() + { + FileSystemTasks.EnsureExistingDirectory(ToolsDirectory); + + if (File.Exists(CycloneDxToolPath)) + { + return; + } + + Log.Information("Installing CycloneDX dotnet tool..."); + DotNet($"tool install --tool-path \"{ToolsDirectory}\" CycloneDX"); + } + + void PrettifyJson(AbsolutePath file) + { + if (!File.Exists(file)) + { + throw new Exception($"SBOM file not found: {file}"); + } + + Log.Information("Prettifying SBOM JSON output..."); + + var content = File.ReadAllText(file); + using var document = JsonDocument.Parse(content); + var formatted = JsonSerializer.Serialize(document.RootElement, new JsonSerializerOptions { WriteIndented = true }); + + File.WriteAllText(file, formatted); + } + } From ff59b1fd40d2a2dd57029fe032edd57e8dec40c1 Mon Sep 17 00:00:00 2001 From: Murat Cakir Date: Wed, 10 Dec 2025 02:34:52 +0100 Subject: [PATCH 02/10] Use Microsoft SBOM Tool for release artifacts --- .../Smartstore.Build/Build.cs | 42 +++++++++++++++---- 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/src/Smartstore.Build/Smartstore.Build/Build.cs b/src/Smartstore.Build/Smartstore.Build/Build.cs index a3c7e4cf99..50b413363c 100644 --- a/src/Smartstore.Build/Smartstore.Build/Build.cs +++ b/src/Smartstore.Build/Smartstore.Build/Build.cs @@ -2,6 +2,7 @@ using System.IO; using System.IO.Compression; using System.Text.Json; +using System.Linq; using Nuke.Common; using Nuke.Common.CI; using Nuke.Common.Git; @@ -48,7 +49,7 @@ class Build : NukeBuild AbsolutePath TestsDirectory => RootDirectory / "test"; AbsolutePath ArtifactsDirectory => RootDirectory / "build" / "artifacts"; AbsolutePath ToolsDirectory => RootDirectory / "build" / ".tools"; - AbsolutePath CycloneDxToolPath => ToolsDirectory / (EnvironmentInfo.Platform == PlatformFamily.Windows ? "dotnet-CycloneDX.exe" : "dotnet-CycloneDX"); + AbsolutePath SbomToolPath => ToolsDirectory / (EnvironmentInfo.Platform == PlatformFamily.Windows ? "sbom-tool.exe" : "sbom-tool"); string GetPublishName() { @@ -131,7 +132,7 @@ string GetPublishName() .DependsOn(Deploy) .Executes(() => { - EnsureCycloneDxTool(); + EnsureSbomTool(); var publishName = GetPublishName(); AbsolutePath publishDirectory = ArtifactsDirectory / publishName; @@ -141,12 +142,35 @@ string GetPublishName() throw new Exception($"Published output for {publishName} not found in {ArtifactsDirectory}."); } - AbsolutePath sbomFile = publishDirectory / $"Smartstore.{publishName}.sbom.cyclone.json"; - Log.Information($"Generating CycloneDX SBOM at {sbomFile}..."); + AbsolutePath manifestDirectory = publishDirectory / "_manifest"; + AbsolutePath generatedManifest = manifestDirectory / "manifest.spdx.json"; + AbsolutePath sbomFile = publishDirectory / $"Smartstore.{publishName}.sbom.json"; - ProcessTasks.StartProcess(CycloneDxToolPath, $"\"{Solution.Path}\" --output \"{sbomFile}\" --json") + Log.Information($"Generating SBOM for {publishName} using Microsoft SBOM Tool..."); + + manifestDirectory.CreateOrCleanDirectory(); + + var arguments = string.Join(" ", new[] + { + "generate", + "-b", publishDirectory, + "-bc", publishDirectory, + "-ps", "Smartstore", + "-nsb", "https://smartstore.com", + "-pn", "Smartstore", + "-pv", Version, + "-m", manifestDirectory + }.Select(x => $"\"{x}\"")); + + ProcessTasks.StartProcess(SbomToolPath, arguments) .AssertZeroExitCode(); + if (!File.Exists(generatedManifest)) + { + throw new Exception($"SBOM manifest not found at {generatedManifest}."); + } + + File.Copy(generatedManifest, sbomFile, overwrite: true); PrettifyJson(sbomFile); }); @@ -179,17 +203,17 @@ string GetPublishName() .SetConfiguration(Configuration)); }); - void EnsureCycloneDxTool() + void EnsureSbomTool() { FileSystemTasks.EnsureExistingDirectory(ToolsDirectory); - if (File.Exists(CycloneDxToolPath)) + if (File.Exists(SbomToolPath)) { return; } - Log.Information("Installing CycloneDX dotnet tool..."); - DotNet($"tool install --tool-path \"{ToolsDirectory}\" CycloneDX"); + Log.Information("Installing Microsoft SBOM Tool dotnet tool..."); + DotNet($"tool install --tool-path \"{ToolsDirectory}\" Microsoft.Sbom.Tool"); } void PrettifyJson(AbsolutePath file) From 60e3528280218b4d280a2e4b3b94c83d8707f896 Mon Sep 17 00:00:00 2001 From: Murat Cakir Date: Wed, 10 Dec 2025 02:34:57 +0100 Subject: [PATCH 03/10] Fix SBOM tool directory creation --- src/Smartstore.Build/Smartstore.Build/Build.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Smartstore.Build/Smartstore.Build/Build.cs b/src/Smartstore.Build/Smartstore.Build/Build.cs index 50b413363c..b06bdc3f4c 100644 --- a/src/Smartstore.Build/Smartstore.Build/Build.cs +++ b/src/Smartstore.Build/Smartstore.Build/Build.cs @@ -205,7 +205,7 @@ string GetPublishName() void EnsureSbomTool() { - FileSystemTasks.EnsureExistingDirectory(ToolsDirectory); + ToolsDirectory.CreateDirectory(); if (File.Exists(SbomToolPath)) { From fe9a4cfc4c1c36e1ee6f9832272a38fabe17c9f1 Mon Sep 17 00:00:00 2001 From: Murat Cakir Date: Wed, 10 Dec 2025 02:41:56 +0100 Subject: [PATCH 04/10] Pin SBOM tool version --- src/Smartstore.Build/Smartstore.Build/Build.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Smartstore.Build/Smartstore.Build/Build.cs b/src/Smartstore.Build/Smartstore.Build/Build.cs index b06bdc3f4c..c789b30b93 100644 --- a/src/Smartstore.Build/Smartstore.Build/Build.cs +++ b/src/Smartstore.Build/Smartstore.Build/Build.cs @@ -50,6 +50,8 @@ class Build : NukeBuild AbsolutePath ArtifactsDirectory => RootDirectory / "build" / "artifacts"; AbsolutePath ToolsDirectory => RootDirectory / "build" / ".tools"; AbsolutePath SbomToolPath => ToolsDirectory / (EnvironmentInfo.Platform == PlatformFamily.Windows ? "sbom-tool.exe" : "sbom-tool"); + const string SbomToolPackage = "microsoft.sbom.tool"; + const string SbomToolVersion = "1.2.0"; string GetPublishName() { @@ -213,7 +215,7 @@ void EnsureSbomTool() } Log.Information("Installing Microsoft SBOM Tool dotnet tool..."); - DotNet($"tool install --tool-path \"{ToolsDirectory}\" Microsoft.Sbom.Tool"); + DotNet($"tool install --tool-path \"{ToolsDirectory}\" {SbomToolPackage} --version {SbomToolVersion}"); } void PrettifyJson(AbsolutePath file) From 79cf9a5177b39a9efa92aa6ec641c8f28355347d Mon Sep 17 00:00:00 2001 From: Murat Cakir Date: Wed, 10 Dec 2025 02:42:00 +0100 Subject: [PATCH 05/10] Use Microsoft SBOM dotnet tool package --- src/Smartstore.Build/Smartstore.Build/Build.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Smartstore.Build/Smartstore.Build/Build.cs b/src/Smartstore.Build/Smartstore.Build/Build.cs index c789b30b93..c916df0d8a 100644 --- a/src/Smartstore.Build/Smartstore.Build/Build.cs +++ b/src/Smartstore.Build/Smartstore.Build/Build.cs @@ -50,7 +50,7 @@ class Build : NukeBuild AbsolutePath ArtifactsDirectory => RootDirectory / "build" / "artifacts"; AbsolutePath ToolsDirectory => RootDirectory / "build" / ".tools"; AbsolutePath SbomToolPath => ToolsDirectory / (EnvironmentInfo.Platform == PlatformFamily.Windows ? "sbom-tool.exe" : "sbom-tool"); - const string SbomToolPackage = "microsoft.sbom.tool"; + const string SbomToolPackage = "Microsoft.Sbom.DotNetTool"; const string SbomToolVersion = "1.2.0"; string GetPublishName() From ba5e0826bd5c97c0bc852167c34764bdb2916359 Mon Sep 17 00:00:00 2001 From: Murat Cakir Date: Wed, 10 Dec 2025 02:58:03 +0100 Subject: [PATCH 06/10] Fix SBOM manifest output handling --- .../Smartstore.Build/Build.cs | 24 ++++--------------- 1 file changed, 4 insertions(+), 20 deletions(-) diff --git a/src/Smartstore.Build/Smartstore.Build/Build.cs b/src/Smartstore.Build/Smartstore.Build/Build.cs index c916df0d8a..dbf050a4c8 100644 --- a/src/Smartstore.Build/Smartstore.Build/Build.cs +++ b/src/Smartstore.Build/Smartstore.Build/Build.cs @@ -1,7 +1,6 @@ using System; using System.IO; using System.IO.Compression; -using System.Text.Json; using System.Linq; using Nuke.Common; using Nuke.Common.CI; @@ -145,8 +144,9 @@ string GetPublishName() } AbsolutePath manifestDirectory = publishDirectory / "_manifest"; - AbsolutePath generatedManifest = manifestDirectory / "manifest.spdx.json"; - AbsolutePath sbomFile = publishDirectory / $"Smartstore.{publishName}.sbom.json"; + AbsolutePath generatedManifest = manifestDirectory / "spdx_2.2" / "manifest.spdx.json"; + AbsolutePath sbomDirectory = publishDirectory / "sbom"; + AbsolutePath sbomFile = sbomDirectory / "manifest.spdx.json"; Log.Information($"Generating SBOM for {publishName} using Microsoft SBOM Tool..."); @@ -172,8 +172,8 @@ string GetPublishName() throw new Exception($"SBOM manifest not found at {generatedManifest}."); } + sbomDirectory.CreateOrCleanDirectory(); File.Copy(generatedManifest, sbomFile, overwrite: true); - PrettifyJson(sbomFile); }); Target Zip => _ => _ @@ -218,20 +218,4 @@ void EnsureSbomTool() DotNet($"tool install --tool-path \"{ToolsDirectory}\" {SbomToolPackage} --version {SbomToolVersion}"); } - void PrettifyJson(AbsolutePath file) - { - if (!File.Exists(file)) - { - throw new Exception($"SBOM file not found: {file}"); - } - - Log.Information("Prettifying SBOM JSON output..."); - - var content = File.ReadAllText(file); - using var document = JsonDocument.Parse(content); - var formatted = JsonSerializer.Serialize(document.RootElement, new JsonSerializerOptions { WriteIndented = true }); - - File.WriteAllText(file, formatted); - } - } From f8c1e9d0b3fbbf978b1cc31465e358779489dd9a Mon Sep 17 00:00:00 2001 From: Murat Cakir Date: Wed, 10 Dec 2025 03:02:16 +0100 Subject: [PATCH 07/10] Fix SBOM tool package discovery --- src/Smartstore.Build/Smartstore.Build/Build.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Smartstore.Build/Smartstore.Build/Build.cs b/src/Smartstore.Build/Smartstore.Build/Build.cs index dbf050a4c8..525dd95e5f 100644 --- a/src/Smartstore.Build/Smartstore.Build/Build.cs +++ b/src/Smartstore.Build/Smartstore.Build/Build.cs @@ -137,6 +137,7 @@ string GetPublishName() var publishName = GetPublishName(); AbsolutePath publishDirectory = ArtifactsDirectory / publishName; + AbsolutePath buildComponentDirectory = SourceDirectory; if (!publishDirectory.DirectoryExists()) { @@ -156,7 +157,7 @@ string GetPublishName() { "generate", "-b", publishDirectory, - "-bc", publishDirectory, + "-bc", buildComponentDirectory, "-ps", "Smartstore", "-nsb", "https://smartstore.com", "-pn", "Smartstore", From 9eb1a7235e9a3f68d4ddf8d4b95fb2634f4fa00e Mon Sep 17 00:00:00 2001 From: Murat Cakir Date: Wed, 10 Dec 2025 03:09:42 +0100 Subject: [PATCH 08/10] Adjust SBOM manifest output handling --- src/Smartstore.Build/Smartstore.Build/Build.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Smartstore.Build/Smartstore.Build/Build.cs b/src/Smartstore.Build/Smartstore.Build/Build.cs index 525dd95e5f..77805b25c1 100644 --- a/src/Smartstore.Build/Smartstore.Build/Build.cs +++ b/src/Smartstore.Build/Smartstore.Build/Build.cs @@ -144,7 +144,8 @@ string GetPublishName() throw new Exception($"Published output for {publishName} not found in {ArtifactsDirectory}."); } - AbsolutePath manifestDirectory = publishDirectory / "_manifest"; + AbsolutePath manifestRootDirectory = publishDirectory; + AbsolutePath manifestDirectory = manifestRootDirectory / "_manifest"; AbsolutePath generatedManifest = manifestDirectory / "spdx_2.2" / "manifest.spdx.json"; AbsolutePath sbomDirectory = publishDirectory / "sbom"; AbsolutePath sbomFile = sbomDirectory / "manifest.spdx.json"; @@ -153,7 +154,7 @@ string GetPublishName() manifestDirectory.CreateOrCleanDirectory(); - var arguments = string.Join(" ", new[] + var arguments = string.Join(" ", new string[] { "generate", "-b", publishDirectory, @@ -162,7 +163,7 @@ string GetPublishName() "-nsb", "https://smartstore.com", "-pn", "Smartstore", "-pv", Version, - "-m", manifestDirectory + "-m", manifestRootDirectory }.Select(x => $"\"{x}\"")); ProcessTasks.StartProcess(SbomToolPath, arguments) From 97919e2a0b4638c0780d84557b054d55939b4847 Mon Sep 17 00:00:00 2001 From: Murat Cakir Date: Wed, 10 Dec 2025 03:21:31 +0100 Subject: [PATCH 09/10] Update SBOM tool configuration --- src/Smartstore.Build/Smartstore.Build/Build.cs | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/Smartstore.Build/Smartstore.Build/Build.cs b/src/Smartstore.Build/Smartstore.Build/Build.cs index 77805b25c1..19d32e0114 100644 --- a/src/Smartstore.Build/Smartstore.Build/Build.cs +++ b/src/Smartstore.Build/Smartstore.Build/Build.cs @@ -50,7 +50,7 @@ class Build : NukeBuild AbsolutePath ToolsDirectory => RootDirectory / "build" / ".tools"; AbsolutePath SbomToolPath => ToolsDirectory / (EnvironmentInfo.Platform == PlatformFamily.Windows ? "sbom-tool.exe" : "sbom-tool"); const string SbomToolPackage = "Microsoft.Sbom.DotNetTool"; - const string SbomToolVersion = "1.2.0"; + const string SbomToolVersion = "4.1.4"; string GetPublishName() { @@ -146,7 +146,8 @@ string GetPublishName() AbsolutePath manifestRootDirectory = publishDirectory; AbsolutePath manifestDirectory = manifestRootDirectory / "_manifest"; - AbsolutePath generatedManifest = manifestDirectory / "spdx_2.2" / "manifest.spdx.json"; + AbsolutePath generatedManifestDirectory = manifestDirectory / "spdx_2.2"; + AbsolutePath generatedManifest = generatedManifestDirectory / "manifest.spdx.json"; AbsolutePath sbomDirectory = publishDirectory / "sbom"; AbsolutePath sbomFile = sbomDirectory / "manifest.spdx.json"; @@ -163,7 +164,13 @@ string GetPublishName() "-nsb", "https://smartstore.com", "-pn", "Smartstore", "-pv", Version, - "-m", manifestRootDirectory + "-m", manifestRootDirectory, + "-e", "*.cshtml", + "-e", "*.scss", + "-e", "*.css", + "-e", "*.png", + "-e", "*.gif", + "-e", "*.jpg" }.Select(x => $"\"{x}\"")); ProcessTasks.StartProcess(SbomToolPath, arguments) @@ -176,6 +183,11 @@ string GetPublishName() sbomDirectory.CreateOrCleanDirectory(); File.Copy(generatedManifest, sbomFile, overwrite: true); + + if (Directory.Exists(generatedManifestDirectory)) + { + Directory.Delete(generatedManifestDirectory, recursive: true); + } }); Target Zip => _ => _ From 4011114267dbf96880369f55252e43a859b7f617 Mon Sep 17 00:00:00 2001 From: Murat Cakir Date: Wed, 10 Dec 2025 03:26:11 +0100 Subject: [PATCH 10/10] Fix SBOM exclusion argument --- src/Smartstore.Build/Smartstore.Build/Build.cs | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/Smartstore.Build/Smartstore.Build/Build.cs b/src/Smartstore.Build/Smartstore.Build/Build.cs index 19d32e0114..1888514499 100644 --- a/src/Smartstore.Build/Smartstore.Build/Build.cs +++ b/src/Smartstore.Build/Smartstore.Build/Build.cs @@ -155,6 +155,16 @@ string GetPublishName() manifestDirectory.CreateOrCleanDirectory(); + var exclusions = string.Join(';', new string[] + { + "*.cshtml", + "*.scss", + "*.css", + "*.png", + "*.gif", + "*.jpg" + }); + var arguments = string.Join(" ", new string[] { "generate", @@ -165,12 +175,7 @@ string GetPublishName() "-pn", "Smartstore", "-pv", Version, "-m", manifestRootDirectory, - "-e", "*.cshtml", - "-e", "*.scss", - "-e", "*.css", - "-e", "*.png", - "-e", "*.gif", - "-e", "*.jpg" + "-e", exclusions }.Select(x => $"\"{x}\"")); ProcessTasks.StartProcess(SbomToolPath, arguments)