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 16:21:17 UTC

(tooling-trusted-release) branch main updated: Add code to show the sbomqs score

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 f88b9d6  Add code to show the sbomqs score
f88b9d6 is described below

commit f88b9d6a566c7afc680065e55c6b00106c5f2152
Author: Sean B. Palmer <se...@miscoranda.com>
AuthorDate: Wed Aug 27 17:21:01 2025 +0100

    Add code to show the sbomqs score
---
 atr/sbomtool.py | 80 +++++++++++++++++++++++++++++++++++++++++++--------------
 1 file changed, 61 insertions(+), 19 deletions(-)

diff --git a/atr/sbomtool.py b/atr/sbomtool.py
index 17e9ce8..7f7356b 100644
--- a/atr/sbomtool.py
+++ b/atr/sbomtool.py
@@ -21,7 +21,9 @@ import dataclasses
 import datetime
 import enum
 import pathlib
+import subprocess
 import sys
+import tempfile
 from typing import Any, Literal
 
 import pydantic
@@ -149,6 +151,14 @@ class Bundle:
     bom: Bom
 
 
+class SBOMQSSummary(Lax):
+    total_score: float
+
+
+class SBOMQSReport(Lax):
+    summary: SBOMQSSummary
+
+
 def assemble_metadata_supplier(doc: yyjson.Document, patch: Patch) -> None:
     assemble_metadata(doc, patch)
     patch.append(
@@ -244,6 +254,16 @@ def bundle_to_patch(bundle: Bundle) -> Patch:
     return patch_ops
 
 
+def get_pointer(doc: yyjson.Document, path: str) -> Any | None:
+    try:
+        return doc.get_pointer(path)
+    except ValueError as e:
+        # TODO: This is not necessarily stable
+        if str(e) == "JSON pointer cannot be resolved":
+            return None
+        raise
+
+
 def main() -> None:
     path = pathlib.Path(sys.argv[2])
     bundle = path_to_bundle(path)
@@ -262,20 +282,17 @@ def main() -> None:
                 print(merged.dumps())
             else:
                 print(bundle.doc.dumps())
+        case "scores":
+            if patch_ops:
+                patch_data = patch_to_data(patch_ops)
+                merged = bundle.doc.patch(yyjson.Document(patch_data))
+                print(sbomqs_total_score(bundle.doc), "->", sbomqs_total_score(merged))
+            else:
+                print(sbomqs_total_score(bundle.doc))
         case "validate":
             print(bundle.doc.dumps())
 
 
-def get_pointer(doc: yyjson.Document, path: str) -> Any | None:
-    try:
-        return doc.get_pointer(path)
-    except ValueError as e:
-        # TODO: This is not necessarily stable
-        if str(e) == "JSON pointer cannot be resolved":
-            return None
-        raise
-
-
 def ntia_2021_conformance_issues(bom: Bom) -> tuple[list[Missing], list[Missing]]:
     # 1. Supplier
     # ECMA-424 1st edition says that this is the supplier of the primary component
@@ -376,6 +393,7 @@ def ntia_2021_conformance_issues(bom: Bom) -> tuple[list[Missing], list[Missing]
 
 def ntia_2021_conformance_patch(doc: yyjson.Document, errors: list[Missing]) -> Patch:
     patch: Patch = []
+    # TODO: Add tool metadata
     for error in errors:
         match error:
             case MissingProperty(property):
@@ -392,16 +410,16 @@ def ntia_2021_conformance_patch(doc: yyjson.Document, errors: list[Missing]) ->
                         assemble_metadata_timestamp(doc, patch)
                     case Property.DEPENDENCIES:
                         assemble_dependencies(doc, patch)
-            case MissingComponentProperty(property, _index):
+            case MissingComponentProperty(property, index):
                 match property:
-                    case ComponentProperty.SUPPLIER if _index is not None:
-                        assemble_component_supplier(doc, patch, _index)
-                    case ComponentProperty.NAME if _index is not None:
-                        assemble_component_name(doc, patch, _index)
-                    case ComponentProperty.VERSION if _index is not None:
-                        assemble_component_version(doc, patch, _index)
-                    case ComponentProperty.IDENTIFIER if _index is not None:
-                        assemble_component_identifier(doc, patch, _index)
+                    case ComponentProperty.SUPPLIER if index is not None:
+                        assemble_component_supplier(doc, patch, index)
+                    case ComponentProperty.NAME if index is not None:
+                        assemble_component_name(doc, patch, index)
+                    case ComponentProperty.VERSION if index is not None:
+                        assemble_component_version(doc, patch, index)
+                    case ComponentProperty.IDENTIFIER if index is not None:
+                        assemble_component_identifier(doc, patch, index)
     return patch
 
 
@@ -414,5 +432,29 @@ def path_to_bundle(path: pathlib.Path) -> Bundle:
     return Bundle(doc=yyjson.Document(text), bom=Bom.model_validate_json(text))
 
 
+def sbomqs_total_score(value: pathlib.Path | str | yyjson.Document) -> float:
+    args = ["sbomqs", "compliance", "--ntia", "--json"]
+    with tempfile.NamedTemporaryFile("w", encoding="utf-8", suffix=".json") as tf:
+        match value:
+            case yyjson.Document():
+                tf.write(value.dumps())
+            case pathlib.Path():
+                tf.write(pathlib.Path(value).read_text(encoding="utf-8"))
+            case str():
+                tf.write(value)
+        args.append(tf.name)
+
+        proc = subprocess.run(
+            args,
+            text=True,
+            capture_output=True,
+        )
+    if proc.returncode != 0:
+        err = proc.stderr.strip() or "sbomqs failed"
+        raise RuntimeError(err)
+    report = SBOMQSReport.model_validate_json(proc.stdout)
+    return report.summary.total_score
+
+
 if __name__ == "__main__":
     main()


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