diff options
Diffstat (limited to 'guix/cve.scm')
-rw-r--r-- | guix/cve.scm | 103 |
1 files changed, 43 insertions, 60 deletions
diff --git a/guix/cve.scm b/guix/cve.scm index 6a6d8406bf..8dc293e85d 100644 --- a/guix/cve.scm +++ b/guix/cve.scm @@ -1,5 +1,6 @@ ;;; GNU Guix --- Functional package management for GNU -;;; Copyright © 2015, 2016, 2017, 2018, 2019, 2020, 2021 Ludovic Courtès <ludo@gnu.org> +;;; Copyright © 2015-2021 Ludovic Courtès <ludo@gnu.org> +;;; Copyright © 2025 Nicolas Graves <ngraves@ngraves.fr> ;;; ;;; This file is part of GNU Guix. ;;; @@ -36,17 +37,11 @@ #:export (json->cve-items cve-item? - cve-item-cve + cve-item-id cve-item-configurations cve-item-published-date cve-item-last-modified-date - cve? - cve-id - cve-data-type - cve-data-format - cve-references - cve-reference? cve-reference-url cve-reference-tags @@ -68,28 +63,17 @@ ;;; Code: (define (string->date* str) - (string->date str "~Y-~m-~dT~H:~M~z")) + (string->date str "~Y-~m-~dT~H:~M:~S")) (define-json-mapping <cve-item> cve-item cve-item? json->cve-item - (cve cve-item-cve "cve" json->cve) ;<cve> - (configurations cve-item-configurations ;list of sexps + (id cve-item-id "id") ;string + (configurations cve-item-configurations ;list of sexps "configurations" configuration-data->cve-configurations) (published-date cve-item-published-date - "publishedDate" string->date*) + "published" string->date*) (last-modified-date cve-item-last-modified-date - "lastModifiedDate" string->date*)) - -(define-json-mapping <cve> cve cve? - json->cve - (id cve-id "CVE_data_meta" ;string - (cut assoc-ref <> "ID")) - (data-type cve-data-type ;'CVE - "data_type" string->symbol) - (data-format cve-data-format ;'MITRE - "data_format" string->symbol) - (references cve-references ;list of <cve-reference> - "references" reference-data->cve-references)) + "lastModified" string->date*)) (define-json-mapping <cve-reference> cve-reference cve-reference? json->cve-reference @@ -97,12 +81,6 @@ (tags cve-reference-tags ;list of strings "tags" vector->list)) -(define (reference-data->cve-references alist) - (map json->cve-reference - ;; Normally "reference_data" is always present but rejected CVEs such - ;; as CVE-2020-10020 can lack it. - (vector->list (or (assoc-ref alist "reference_data") '#())))) - (define %cpe-package-rx ;; For applications: "cpe:2.3:a:VENDOR:PACKAGE:VERSION", or sometimes ;; "cpe:2.3:a:VENDOR:PACKAGE:VERSION:PATCH-LEVEL". @@ -132,15 +110,15 @@ Return three #f values if CPE does not look like an application CPE string." (values #f #f #f)))) (define (cpe-match->cve-configuration alist) - "Convert ALIST, a \"cpe_match\" alist, into an sexp representing the package + "Convert ALIST, a \"cpeMatch\" alist, into an sexp representing the package and versions matched. Return #f if ALIST doesn't correspond to an application package." - (let ((cpe (assoc-ref alist "cpe23Uri")) + (let ((cpe (assoc-ref alist "criteria")) (starti (assoc-ref alist "versionStartIncluding")) (starte (assoc-ref alist "versionStartExcluding")) (endi (assoc-ref alist "versionEndIncluding")) (ende (assoc-ref alist "versionEndExcluding"))) - ;; Normally "cpe23Uri" is here in each "cpe_match" item, but CVE-2020-0534 + ;; Normally "criteria" is here in each "cpeMatch" item, but CVE-2020-0534 ;; has a configuration that lacks it. (and cpe (let ((vendor package version (cpe->package-identifier cpe))) @@ -156,7 +134,7 @@ package." (ende `(< ,ende)) (else version)))))))) -(define (configuration-data->cve-configurations alist) +(define (configuration-data->cve-configurations vector) "Given ALIST, a JSON dictionary for the baroque \"configurations\" element found in CVEs, return an sexp such as (\"binutils\" (< \"2.31\")) that represents matching configurations." @@ -165,10 +143,13 @@ element found in CVEs, return an sexp such as (\"binutils\" (< ("OR" 'or) ("AND" 'and))) + (define (maybe-vector->alist vector) + (vector->list (or (and (unspecified? vector) #()) vector #()))) + (define (node->configuration node) (let ((operator (string->operator (assoc-ref node "operator")))) (cond - ((assoc-ref node "cpe_match") + ((assoc-ref node "cpeMatch") => (lambda (matches) (let ((matches (vector->list matches))) @@ -187,28 +168,31 @@ element found in CVEs, return an sexp such as (\"binutils\" (< (else #f)))) - (let ((nodes (vector->list (assoc-ref alist "nodes")))) + (let* ((alist (maybe-vector->alist vector)) + (nodes (if (null? alist) + '() + (maybe-vector->alist (assoc-ref (car alist) "nodes"))))) (filter-map node->configuration nodes))) (define (json->cve-items json) "Parse JSON, an input port or a string, and return a list of <cve-item> records." - (let* ((alist (json->scm json)) - (type (assoc-ref alist "CVE_data_type")) - (format (assoc-ref alist "CVE_data_format")) - (version (assoc-ref alist "CVE_data_version"))) - (unless (equal? type "CVE") - (raise (condition (&message - (message "invalid CVE feed"))))) - (unless (equal? format "MITRE") - (raise (formatted-message (G_ "unsupported CVE format: '~a'") - format))) - (unless (equal? version "4.0") - (raise (formatted-message (G_ "unsupported CVE data version: '~a'") - version))) + (let ((alist (json->scm json))) + (match (assoc-ref alist "format") + ("NVD_CVE" + #t) + (format + (raise (formatted-message (G_ "unsupported CVE format: '~a'") + format)))) + (match (assoc-ref alist "version") + ("2.0" + #t) + (version + (raise (formatted-message (G_ "unsupported CVE data version: '~a'") + version)))) - (map json->cve-item - (vector->list (assoc-ref alist "CVE_Items"))))) + (map (compose json->cve-item (cut assoc-ref <> "cve")) + (vector->list (assoc-ref alist "vulnerabilities"))))) (define (version-matches? version sexp) "Return true if VERSION, a string, matches SEXP." @@ -269,7 +253,7 @@ HIDDEN-VENDORS." (define (yearly-feed-uri year) "Return the URI for the CVE feed for YEAR." (string->uri - (string-append "https://nvd.nist.gov/feeds/json/cve/1.1/nvdcve-1.1-" + (string-append "https://nvd.nist.gov/feeds/json/cve/2.0/nvdcve-2.0-" (number->string year) ".json.gz"))) (define %current-year-ttl @@ -352,14 +336,13 @@ matching versions." "Return a <vulnerability> corresponding to ITEM, a <cve-item> record; return #f if ITEM does not list any configuration or if it does not list any \"a\" (application) configuration." - (let ((id (cve-id (cve-item-cve item)))) - (match (cve-item-configurations item) - (() ;no configurations - #f) - ((configs ...) - (vulnerability id - (merge-package-lists - (map cve-configuration->package-list configs))))))) + (match (cve-item-configurations item) + (() ;no configurations + #f) + ((configs ...) + (vulnerability (cve-item-id item) + (merge-package-lists + (map cve-configuration->package-list configs)))))) (define (json->vulnerabilities json) "Parse JSON, an input port or a string, and return the list of |