diff --git a/guix/graph.scm b/guix/graph.scm index e661c95467..42288062da 100644 --- a/guix/graph.scm +++ b/guix/graph.scm @@ -26,6 +26,7 @@ #:autoload (guix i18n) (G_) #:use-module (srfi srfi-1) #:use-module (srfi srfi-9) + #:use-module (srfi srfi-19) ;; date->string #:use-module (srfi srfi-26) #:use-module (srfi srfi-34) #:use-module (ice-9 match) @@ -256,6 +257,75 @@ NODE1 to NODE2 of the given TYPE. Return #f when there is no path." emit-prologue emit-epilogue emit-node emit-edge)) + +;;; +;;; SBOM CycloneDX JSON export. +;;; +;;; Schema: https://cyclonedx.org/docs/1.6/json/#metadata_tools_oneOf_i0_components_items_version +;;; + + +(define (emit-cyclonedx-prologue name port) + (format port "{ + \"bomFormat\": \"CycloneDX\", + \"specVersion\": \"1.6\", + \"metadata\": { + \"timestamp\": \"~a\", + \"tools\": { + \"components\": [ + { + \"type\": \"operating-system\", + \"name\": \"guix\" + }, + { + \"type\": \"application\", + \"name\": \"guix-graph\" + } + ] + } + }, + \"components\": [ +" + (date->string (current-date 0) "~5Z"))) ;; UTC time, iso date-time + +(define (emit-cyclonedx-epilogue port) + ;; the safety of each tool built on Guix depends on Guix + (display "\n { + \"type\": \"operating-system\", + \"name\": \"guix\" + } + ]\n}\n" port)) + +(define (emit-cyclonedx-node id label port) + (match (if (string-contains label "@") + (string-split label #\@) + (list label "N/A")) + ((name version) + (format port "\n { + \"type\": \"application\", + \"name\": \"~a\", + \"version\": \"~a\" + }," + name version)) + (else ;; more than one @ + (format port "\n { + \"type\": \"application\", + \"name\": \"~a\", + }," + label)))) + +(define (emit-cyclonedx-edge id1 id2 port) + ;; Left empty: does not include edges at the moment. Adding them as + ;; dependencies would require to include a separator between + ;; nodes and edges. + "") + +(define %cyclonedx-backend + (graph-backend "cyclonedx-json" + "Generate an SBOM in CycloneDX JSON format for use with dependencytrack." + emit-cyclonedx-prologue emit-cyclonedx-epilogue + emit-cyclonedx-node emit-cyclonedx-edge)) + ;;; ;;; d3js export. @@ -375,7 +445,8 @@ nodeArray.push(nodes[\"~a\"]);~%" (list %graphviz-backend %d3js-backend %cypher-backend - %graphml-backend)) + %graphml-backend + %cyclonedx-backend)) (define (lookup-backend name) "Return the graph backend called NAME. Raise an error if it is not found."