You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tooling.apache.org by sb...@apache.org on 2025/08/27 18:49:32 UTC

(tooling-trusted-release) branch main updated: Add GitHub user suppliers, and some utility commands

This is an automated email from the ASF dual-hosted git repository.

sbp pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tooling-trusted-release.git


The following commit(s) were added to refs/heads/main by this push:
     new 4072582  Add GitHub user suppliers, and some utility commands
4072582 is described below

commit 407258222d8bc1e9cd5d64dacce6ede632100bdd
Author: Sean B. Palmer <se...@miscoranda.com>
AuthorDate: Wed Aug 27 19:49:12 2025 +0100

    Add GitHub user suppliers, and some utility commands
---
 atr/sbomtool.py | 89 +++++++++++++++++++++++++++++++++++++++++++++++++++------
 1 file changed, 80 insertions(+), 9 deletions(-)

diff --git a/atr/sbomtool.py b/atr/sbomtool.py
index 7f7356b..e25c812 100644
--- a/atr/sbomtool.py
+++ b/atr/sbomtool.py
@@ -30,6 +30,7 @@ import pydantic
 import yyjson
 
 THE_APACHE_SOFTWARE_FOUNDATION = "The Apache Software Foundation"
+VERSION = "0.0.1-dev1"
 
 
 class Lax(pydantic.BaseModel):
@@ -135,12 +136,20 @@ class ComponentProperty(enum.Enum):
 class MissingProperty:
     property: Property
 
+    def __str__(self) -> str:
+        return f"missing {self.property.name}"
+
 
 @dataclasses.dataclass
 class MissingComponentProperty:
     property: ComponentProperty
     index: int | None = None
 
+    def __str__(self) -> str:
+        if self.index is None:
+            return f"missing {self.property.name} in primary component"
+        return f"missing {self.property.name} in component {self.index}"
+
 
 type Missing = MissingProperty | MissingComponentProperty
 
@@ -161,11 +170,15 @@ class SBOMQSReport(Lax):
 
 def assemble_metadata_supplier(doc: yyjson.Document, patch: Patch) -> None:
     assemble_metadata(doc, patch)
+    # NOTE: The sbomqs tool requires a URL (or email) on a supplier
     patch.append(
         AddOp(
             op="add",
             path="/metadata/supplier",
-            value={"name": THE_APACHE_SOFTWARE_FOUNDATION},
+            value={
+                "name": THE_APACHE_SOFTWARE_FOUNDATION,
+                "url": ["https://apache.org/"],
+            },
         )
     )
 
@@ -194,7 +207,7 @@ def assemble_metadata_author(doc: yyjson.Document, patch: Patch) -> None:
             AddOp(
                 op="add",
                 path="/metadata/author",
-                value=THE_APACHE_SOFTWARE_FOUNDATION,
+                value=f"sbomtool v{VERSION}, by ASF Tooling",
             )
         )
 
@@ -221,16 +234,49 @@ def assemble_component_supplier(doc: yyjson.Document, patch: Patch, index: int)
     # We need to detect whether this is an ASF component
     # If it is, we can trivially fix it
     # If not, this is much more difficult
-    add_op = AddOp(
-        op="add",
-        path=f"/components/{index}/supplier",
-        value={"name": THE_APACHE_SOFTWARE_FOUNDATION},
+    # NOTE: The sbomqs tool requires a URL (or email) on a supplier
+    def make_supplier_op(name: str, url: str) -> AddOp:
+        nonlocal index
+        return AddOp(
+            op="add",
+            path=f"/components/{index}/supplier",
+            value={
+                "name": name,
+                "url": [url],
+            },
+        )
+
+    add_asf_op = make_supplier_op(
+        THE_APACHE_SOFTWARE_FOUNDATION,
+        "https://apache.org/",
     )
+
     if get_pointer(doc, f"/components/{index}/publisher") == THE_APACHE_SOFTWARE_FOUNDATION:
-        patch.append(add_op)
-    elif bom_ref := get_pointer(doc, f"/components/{index}/bom-ref"):
+        patch.append(add_asf_op)
+        return
+
+    if purl := get_pointer(doc, f"/components/{index}/purl"):
+        if purl.startswith("pkg:maven/org.apache."):
+            patch.append(add_asf_op)
+            return
+
+    if group_id := get_pointer(doc, f"/components/{index}/group"):
+        if group_id.startswith("org.apache."):
+            patch.append(add_asf_op)
+            return
+        elif group_id.startswith("com.github."):
+            github_user = group_id.split(".", 2)[2]
+            add_github_op = make_supplier_op(
+                f"@github/{github_user}",
+                f"https://github.com/{github_user}",
+            )
+            patch.append(add_github_op)
+            return
+
+    if bom_ref := get_pointer(doc, f"/components/{index}/bom-ref"):
         if bom_ref.startswith("pkg:maven/org.apache."):
-            patch.append(add_op)
+            patch.append(add_asf_op)
+            return
 
 
 def assemble_component_name(doc: yyjson.Document, patch: Patch, index: int) -> None:
@@ -291,6 +337,31 @@ def main() -> None:
                 print(sbomqs_total_score(bundle.doc))
         case "validate":
             print(bundle.doc.dumps())
+        case "missing":
+            _warnings, errors = ntia_2021_conformance_issues(bundle.bom)
+            for error in errors:
+                print(error)
+            # for warning in warnings:
+            #     print(warning)
+        case "where":
+            _warnings, errors = ntia_2021_conformance_issues(bundle.bom)
+            for error in errors:
+                match error:
+                    case MissingProperty():
+                        print(f"metadata.{error.property.name}")
+                        print()
+                    case MissingComponentProperty():
+                        components = bundle.bom.components
+                        primary_component = bundle.bom.metadata and bundle.bom.metadata.component
+                        if (error.index is not None) and (components is not None):
+                            print(components[error.index].model_dump_json(indent=2))
+                            print()
+                        elif primary_component is not None:
+                            print(primary_component.model_dump_json(indent=2))
+                            print()
+        case _:
+            print(f"unknown command: {sys.argv[1]}")
+            sys.exit(1)
 
 
 def ntia_2021_conformance_issues(bom: Bom) -> tuple[list[Missing], list[Missing]]:


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@tooling.apache.org
For additional commands, e-mail: commits-help@tooling.apache.org