~ruther/guix-local

1655930d9f4da8a70103d904d399f12be5cffabb — Arne Babenhauserheide 7 months ago bbd2d8c
graph: Add cyclonedx-json backend.

Validated with:

  guix graph guile -b cyclonedx-json > /tmp/cyc.json && \
    docker run -v /tmp/:/tmp/  cyclonedx/cyclonedx-cli validate --input-format json --input-file /tmp/cyc.json

* guix/graph.scm (emit-cyclonedx-prologue, emit-cyclonedx-epilogue,
emit-cyclonedx-node, emit-cyclonedx-edge): New procedures.
(%cyclonedx-backend): New variable.
(%graph-backends): Add %cyclonedx-backend.

Change-Id: Icc8c33cbc08da0137489d13bdad618ef55a14923
1 files changed, 72 insertions(+), 1 deletions(-)

M guix/graph.scm
M guix/graph.scm => guix/graph.scm +72 -1
@@ 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)


@@ 258,6 259,75 @@ NODE1 to NODE2 of the given TYPE.  Return #f when there is no path."


;;;
;;; 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 <graph-backend> 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."