Repository: conan-io/conan
Pull request: [CycloneDX sbom] Improve the default name and add a "name" parameter
Changelog: Omit
Docs: Omit

Close: https://github.com/conan-io/conan/issues/17707


Problem statement:
[feature] cyclonedx sbom : Override metadata.component.name
### What is your suggestion?

I'm generating a cyclonedx sbom (with the experimental extension) :

metadata section looks like this:

> "metadata": {
>         "component": {
>             "author": "Conan",
>             "bom-ref": "c51464d2-1b72-4a20-b4d0-b051a62fbee8",
>             "name": "conanfile.py (ddi/None): [HOOK - hook_cdx.py] post_build()",
>             "type": "library"
>         },
> 
> 

Might be a good idea to give the ability to override metadata.component.name to have a friendlier name ?


### Have you read the CONTRIBUTING guide?

- [ ] I've read the CONTRIBUTING guide

Hints from issue discussion:
Hi @sbouchexbellomie-Philips Thanks for your feedback.
The function returns a JSON object, so you can always edit it with something like `response["metadata"]["component"]["name"] = "my-new-name"`. 
Or would you suggest to define it on call as `cyclonedx_1_4(..., name="my-new-name")`
@ErniGH I would go with the "name" option (which is optional and default to the former implementation

Commit message: use getattr

Patch to review:
diff --git a/conan/tools/sbom/cyclonedx.py b/conan/tools/sbom/cyclonedx.py
index 967e452150a..f0500ed0fde 100644
--- a/conan/tools/sbom/cyclonedx.py
+++ b/conan/tools/sbom/cyclonedx.py
@@ -1,6 +1,6 @@
 
 
-def cyclonedx_1_4(graph, add_build=False, add_tests=False, **kwargs):
+def cyclonedx_1_4(graph, name=None, add_build=False, add_tests=False, **kwargs):
     """
     (Experimental) Generate cyclone 1.4 sbom with json format
     """
@@ -10,6 +10,9 @@ def cyclonedx_1_4(graph, add_build=False, add_tests=False, **kwargs):
 
     has_special_root_node = not (getattr(graph.root.ref, "name", False) and getattr(graph.root.ref, "version", False) and getattr(graph.root.ref, "revision", False))
     special_id = str(uuid.uuid4())
+
+    name_default = graph.root.ref.name if bool(getattr(graph.root.ref, "name", False)) else "conan-sbom"
+    name_default += f"/{graph.root.ref.version}" if bool(getattr(graph.root.ref, "version", False)) else ""
     components = [node for node in graph.nodes if (node.context == "host" or add_build) and (not node.test or add_tests)]
     if has_special_root_node:
         components = components[1:]
@@ -58,7 +61,7 @@ def _calculate_licenses(component):
             "component": {
                 "author": "Conan",
                 "bom-ref": special_id if has_special_root_node else f"pkg:conan/{c.name}@{c.ref.version}?rref={c.ref.revision}",
-                "name": graph.root.conanfile.display_name,
+                "name": name if name else name_default,
                 "type": "library"
             },
             "timestamp": f"{datetime.fromtimestamp(time.time(), tz=timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')}",
diff --git a/test/functional/sbom/test_cyclonedx.py b/test/functional/sbom/test_cyclonedx.py
index 3c1b80a0bd6..b24f3beb72c 100644
--- a/test/functional/sbom/test_cyclonedx.py
+++ b/test/functional/sbom/test_cyclonedx.py
@@ -138,7 +138,7 @@ def test_sbom_generation_dependency_test_require(hook_setup_post_package_no_test
 from conan.tools.sbom import cyclonedx_1_4
 
 def post_generate(conanfile):
-    sbom_cyclonedx_1_4 = cyclonedx_1_4(conanfile.subgraph)
+    sbom_cyclonedx_1_4 = cyclonedx_1_4(conanfile.subgraph, name=%s)
     generators_folder = conanfile.generators_folder
     file_name = "sbom.cdx.json"
     os.mkdir(os.path.join(generators_folder, "sbom"))
@@ -151,7 +151,7 @@ def post_generate(conanfile):
 def hook_setup_post_generate():
     tc = TestClient()
     hook_path = os.path.join(tc.paths.hooks_path, "hook_sbom.py")
-    save(hook_path, sbom_hook_post_generate)
+    save(hook_path, sbom_hook_post_generate % "None")
     return tc
 
 def test_sbom_generation_install_requires(hook_setup_post_generate):
@@ -199,3 +199,17 @@ def test_sbom_generation_install_path_txt(hook_setup_post_generate):
     #foo -> dep
     tc.run("install .")
     assert os.path.exists(os.path.join(tc.current_folder, "sbom", "sbom.cdx.json"))
+
+@pytest.mark.parametrize("name, result", [
+    ("None", "conan-sbom"),
+    ('"custom-name"', "custom-name")
+])
+def test_sbom_generation_custom_name(name, result):
+    tc = TestClient()
+    hook_path = os.path.join(tc.paths.hooks_path, "hook_sbom.py")
+    save(hook_path, sbom_hook_post_generate % name)
+
+    tc.save({"conanfile.py": GenConanfile()})
+    tc.run("install .")
+    assert os.path.exists(os.path.join(tc.current_folder, "sbom", "sbom.cdx.json"))
+    assert f'"name": "{result}"' in tc.load(os.path.join(tc.current_folder, "sbom", "sbom.cdx.json"))


Code review comments:
diff_hunk: @@ -10,6 +10,9 @@ def cyclonedx_1_4(graph, add_build=False, add_tests=False, **kwargs):
 
     has_special_root_node = not (getattr(graph.root.ref, "name", False) and getattr(graph.root.ref, "version", False) and getattr(graph.root.ref, "revision", False))
     special_id = str(uuid.uuid4())
+
+    name_default = graph.root.ref.name if bool(getattr(graph.root.ref, "name", False)) else "conan-sbom"
path: conan/tools/sbom/cyclonedx.py
text: @user1:
A ``if bool(something)`` is basically the same as ``if something``, or is this an exception?
Then you can use short-circuit ``or``
```suggestion
    name_default = getattr(graph.root.ref, "name", False) or "conan-sbom"
```