summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--guix/cve.scm162
-rw-r--r--guix/lint.scm10
-rw-r--r--tests/cve.scm14
3 files changed, 115 insertions, 71 deletions
diff --git a/guix/cve.scm b/guix/cve.scm
index 9e1cf5b587d..6a6d8406bf5 100644
--- a/guix/cve.scm
+++ b/guix/cve.scm
@@ -25,11 +25,11 @@
25 #:use-module (web uri) 25 #:use-module (web uri)
26 #:use-module (srfi srfi-1) 26 #:use-module (srfi srfi-1)
27 #:use-module (srfi srfi-9) 27 #:use-module (srfi srfi-9)
28 #:use-module (srfi srfi-11)
29 #:use-module (srfi srfi-19) 28 #:use-module (srfi srfi-19)
30 #:use-module (srfi srfi-26) 29 #:use-module (srfi srfi-26)
31 #:use-module (srfi srfi-34) 30 #:use-module (srfi srfi-34)
32 #:use-module (srfi srfi-35) 31 #:use-module (srfi srfi-35)
32 #:use-module (srfi srfi-71)
33 #:use-module (ice-9 match) 33 #:use-module (ice-9 match)
34 #:use-module (ice-9 regex) 34 #:use-module (ice-9 regex)
35 #:use-module (ice-9 vlist) 35 #:use-module (ice-9 vlist)
@@ -108,15 +108,16 @@
108 ;; "cpe:2.3:a:VENDOR:PACKAGE:VERSION:PATCH-LEVEL". 108 ;; "cpe:2.3:a:VENDOR:PACKAGE:VERSION:PATCH-LEVEL".
109 (make-regexp "^cpe:2\\.3:a:([^:]+):([^:]+):([^:]+):([^:]+):")) 109 (make-regexp "^cpe:2\\.3:a:([^:]+):([^:]+):([^:]+):([^:]+):"))
110 110
111(define (cpe->package-name cpe) 111(define (cpe->package-identifier cpe)
112 "Converts the Common Platform Enumeration (CPE) string CPE to a package 112 "Converts the Common Platform Enumeration (CPE) string CPE to a package
113name, in a very naive way. Return two values: the package name, and its 113identifier, in a very naive way. Return three values: the CPE vendor, the
114version string. Return #f and #f if CPE does not look like an application CPE 114package name, and its version string.
115string." 115Return three #f values if CPE does not look like an application CPE string."
116 (cond ((regexp-exec %cpe-package-rx cpe) 116 (cond ((regexp-exec %cpe-package-rx cpe)
117 => 117 =>
118 (lambda (matches) 118 (lambda (matches)
119 (values (match:substring matches 2) 119 (values (match:substring matches 1)
120 (match:substring matches 2)
120 (match (match:substring matches 3) 121 (match (match:substring matches 3)
121 ("*" '_) 122 ("*" '_)
122 (version 123 (version
@@ -128,7 +129,7 @@ string."
128 ;; "cpe:2.3:a:openbsd:openssh:6.8:p1". 129 ;; "cpe:2.3:a:openbsd:openssh:6.8:p1".
129 (string-drop patch-level 1))))))))) 130 (string-drop patch-level 1)))))))))
130 (else 131 (else
131 (values #f #f)))) 132 (values #f #f #f))))
132 133
133(define (cpe-match->cve-configuration alist) 134(define (cpe-match->cve-configuration alist)
134 "Convert ALIST, a \"cpe_match\" alist, into an sexp representing the package 135 "Convert ALIST, a \"cpe_match\" alist, into an sexp representing the package
@@ -142,17 +143,18 @@ package."
142 ;; Normally "cpe23Uri" is here in each "cpe_match" item, but CVE-2020-0534 143 ;; Normally "cpe23Uri" is here in each "cpe_match" item, but CVE-2020-0534
143 ;; has a configuration that lacks it. 144 ;; has a configuration that lacks it.
144 (and cpe 145 (and cpe
145 (let-values (((package version) (cpe->package-name cpe))) 146 (let ((vendor package version (cpe->package-identifier cpe)))
146 (and package 147 (and package
147 `(,package 148 `(,vendor
148 ,(cond ((and (or starti starte) (or endi ende)) 149 ,package
149 `(and ,(if starti `(>= ,starti) `(> ,starte)) 150 ,(cond ((and (or starti starte) (or endi ende))
150 ,(if endi `(<= ,endi) `(< ,ende)))) 151 `(and ,(if starti `(>= ,starti) `(> ,starte))
151 (starti `(>= ,starti)) 152 ,(if endi `(<= ,endi) `(< ,ende))))
152 (starte `(> ,starte)) 153 (starti `(>= ,starti))
153 (endi `(<= ,endi)) 154 (starte `(> ,starte))
154 (ende `(< ,ende)) 155 (endi `(<= ,endi))
155 (else version)))))))) 156 (ende `(< ,ende))
157 (else version))))))))
156 158
157(define (configuration-data->cve-configurations alist) 159(define (configuration-data->cve-configurations alist)
158 "Given ALIST, a JSON dictionary for the baroque \"configurations\" 160 "Given ALIST, a JSON dictionary for the baroque \"configurations\"
@@ -228,6 +230,30 @@ records."
228 (('>= min) 230 (('>= min)
229 (version>=? version min)))) 231 (version>=? version min))))
230 232
233(define (vulnerability-matches? vuln vendor hidden-vendors)
234 "Checks if a VENDOR matches at least one of <vulnerability> VULN
235packages. When VENDOR is #f, ignore packages that have a vendor among
236HIDDEN-VENDORS."
237 (define hidden-vendor?
238 (if (list? hidden-vendors)
239 (cut member <> hidden-vendors)
240 (const #f)))
241 (define vendor=?
242 (if vendor
243 (cut string=? <> vendor)
244 (const #f)))
245
246 (match vuln
247 (($ <vulnerability> id packages)
248 (any (match-lambda
249 ((? vendor=?)
250 #t)
251 ((? hidden-vendor?)
252 #f)
253 (otherwise
254 (not vendor)))
255 (map car packages))))) ;candidate vendors
256
231 257
232;;; 258;;;
233;;; High-level interface. 259;;; High-level interface.
@@ -259,7 +285,7 @@ records."
259 (vulnerability id packages) 285 (vulnerability id packages)
260 vulnerability? 286 vulnerability?
261 (id vulnerability-id) ;string 287 (id vulnerability-id) ;string
262 (packages vulnerability-packages)) ;((p1 sexp1) (p2 sexp2) ...) 288 (packages vulnerability-packages)) ;((v1 p1 sexp1) (v2 p2 sexp2) ...)
263 289
264(define vulnerability->sexp 290(define vulnerability->sexp
265 (match-lambda 291 (match-lambda
@@ -271,40 +297,52 @@ records."
271 (('v id (packages ...)) 297 (('v id (packages ...))
272 (vulnerability id packages)))) 298 (vulnerability id packages))))
273 299
300(define sexp-v1->vulnerability
301 (match-lambda
302 (('v id (packages ...))
303 (vulnerability id (map (cut cons #f <>) packages)))))
304
305(define vendor-tuple?
306 (match-lambda
307 ((vendor package _)
308 #t)
309 (otherwise
310 #f)))
311
274(define (cve-configuration->package-list config) 312(define (cve-configuration->package-list config)
275 "Parse CONFIG, a config sexp, and return a list of the form (P SEXP) 313 "Parse CONFIG, a config sexp, and return a list of the form (V P SEXP)
276where P is a package name and SEXP expresses constraints on the matching 314where V is a CPE vendor, P is a package name and SEXP expresses constraints on
277versions." 315the matching versions."
278 (let loop ((config config) 316 (let loop ((config config)
279 (packages '())) 317 (results '()))
280 (match config 318 (match config
281 (('or configs ...) 319 (('or configs ...)
282 (fold loop packages configs)) 320 (fold loop results configs))
283 (('and config _ ...) ;XXX 321 (('and config _ ...) ;XXX
284 (loop config packages)) 322 (loop config results))
285 (((? string? package) '_) ;any version 323 (((? string? vendor) (? string? package) '_) ;any version
286 (cons `(,package _) 324 (cons `(,vendor ,package _) (remove vendor-tuple? results)))
287 (alist-delete package packages))) 325 (((? string? vendor) (? string? package) sexp)
288 (((? string? package) sexp) 326 (match (assoc-ref (assoc-ref results vendor) package)
289 (let ((previous (assoc-ref packages package))) 327 ((previous)
290 (if previous 328 (cons `(,vendor ,package (or ,sexp ,previous))
291 (cons `(,package (or ,sexp ,@previous)) 329 (remove vendor-tuple? results)))
292 (alist-delete package packages)) 330 (_
293 (cons `(,package ,sexp) packages))))))) 331 (cons `(,vendor ,package ,sexp) results)))))))
294 332
295(define (merge-package-lists lst) 333(define (merge-package-lists lst)
296 "Merge the list in LST, each of which has the form (p sexp), where P 334 "Merge the list in LST, each of which has the form (V P SEXP), where V is a
297is the name of a package and SEXP is an sexp that constrains matching 335CPE vendor, P is the name of a package and SEXP is an sexp that constrains
298versions." 336matching versions."
299 (fold (lambda (plist result) ;XXX: quadratic 337 (fold (lambda (plist result) ;XXX: quadratic
300 (fold (match-lambda* 338 (fold (match-lambda*
301 (((package version) result) 339 (((vendor package version) result)
302 (match (assoc-ref result package) 340 (match (assoc-ref result vendor)
303 (#f 341 (((? (cut string=? package <>)) previous)
304 (cons `(,package ,version) result)) 342 (cons `(,vendor ,package (or ,version ,previous))
305 ((previous) 343 (remove vendor-tuple? result)))
306 (cons `(,package (or ,version ,previous)) 344 (_
307 (alist-delete package result)))))) 345 (cons `(,vendor ,package ,version) result)))))
308 result 346 result
309 plist)) 347 plist))
310 '() 348 '()
@@ -337,7 +375,7 @@ sexp to CACHE."
337 (json->vulnerabilities input)) 375 (json->vulnerabilities input))
338 376
339 (write `(vulnerabilities 377 (write `(vulnerabilities
340 1 ;format version 378 2 ;format version
341 ,(map vulnerability->sexp vulns)) 379 ,(map vulnerability->sexp vulns))
342 cache)))) 380 cache))))
343 381
@@ -371,8 +409,10 @@ the given TTL (fetch from the NIST web site when TTL has expired)."
371 (sexp (read* port))) 409 (sexp (read* port)))
372 (close-port port) 410 (close-port port)
373 (match sexp 411 (match sexp
374 (('vulnerabilities 1 vulns) 412 (('vulnerabilities 2 vulns)
375 (map sexp->vulnerability vulns))))) 413 (map sexp->vulnerability vulns))
414 (('vulnerabilities 1 vulns) ;old format, lacks vendor info
415 (map sexp-v1->vulnerability vulns)))))
376 416
377(define* (current-vulnerabilities #:key (timeout 10)) 417(define* (current-vulnerabilities #:key (timeout 10))
378 "Return the current list of Common Vulnerabilities and Exposures (CVE) as 418 "Return the current list of Common Vulnerabilities and Exposures (CVE) as
@@ -404,7 +444,7 @@ vulnerabilities affecting the given package version."
404 (($ <vulnerability> id packages) 444 (($ <vulnerability> id packages)
405 (fold (lambda (package table) 445 (fold (lambda (package table)
406 (match package 446 (match package
407 ((name . versions) 447 ((vendor name . versions)
408 (vhash-cons name (cons vuln versions) 448 (vhash-cons name (cons vuln versions)
409 table)))) 449 table))))
410 table 450 table
@@ -412,20 +452,18 @@ vulnerabilities affecting the given package version."
412 vlist-null 452 vlist-null
413 vulnerabilities)) 453 vulnerabilities))
414 454
415 (lambda* (package #:optional version) 455 (lambda* (package #:optional version #:key (vendor #f) (hidden-vendors '()))
416 (vhash-fold* (if version 456 (vhash-fold*
417 (lambda (pair result) 457 (lambda (pair result)
418 (match pair 458 (match pair
419 ((vuln sexp) 459 ((vuln sexp)
420 (if (version-matches? version sexp) 460 (if (and (or (and (not vendor) (null? hidden-vendors))
421 (cons vuln result) 461 (vulnerability-matches? vuln vendor hidden-vendors))
422 result)))) 462 (or (not version) (version-matches? version sexp)))
423 (lambda (pair result) 463 (cons vuln result)
424 (match pair 464 result))))
425 ((vuln . _) 465 '()
426 (cons vuln result))))) 466 package table)))
427 '()
428 package table)))
429 467
430 468
431;;; cve.scm ends here 469;;; cve.scm ends here
diff --git a/guix/lint.scm b/guix/lint.scm
index d4d08f7af46..deea25c5ce3 100644
--- a/guix/lint.scm
+++ b/guix/lint.scm
@@ -1595,8 +1595,14 @@ CVE vulnerabilities from '~a': ~a (~a)~%")
1595 (package-name package))) 1595 (package-name package)))
1596 (version (or (assoc-ref (package-properties package) 1596 (version (or (assoc-ref (package-properties package)
1597 'cpe-version) 1597 'cpe-version)
1598 (package-version package)))) 1598 (package-version package)))
1599 ((force lookup) name version))))) 1599 (vendor (assoc-ref (package-properties package)
1600 'cpe-vendor))
1601 (hidden-vendors (assoc-ref (package-properties package)
1602 'lint-hidden-cpe-vendors)))
1603 ((force lookup) name version
1604 #:vendor vendor
1605 #:hidden-vendors hidden-vendors)))))
1600 1606
1601;; Prevent Guile 3 from inlining this procedure so we can mock it in tests. 1607;; Prevent Guile 3 from inlining this procedure so we can mock it in tests.
1602(set! package-vulnerabilities package-vulnerabilities) 1608(set! package-vulnerabilities package-vulnerabilities)
diff --git a/tests/cve.scm b/tests/cve.scm
index b69da0e1204..90ada2b6472 100644
--- a/tests/cve.scm
+++ b/tests/cve.scm
@@ -34,19 +34,19 @@
34 (vulnerability "CVE-2019-0001" 34 (vulnerability "CVE-2019-0001"
35 ;; Only the "a" CPE configurations are kept; the "o" 35 ;; Only the "a" CPE configurations are kept; the "o"
36 ;; configurations are discarded. 36 ;; configurations are discarded.
37 '(("junos" (or "18.21-s4" (or "18.21-s3" "18.2"))))) 37 '(("juniper" "junos" (or "18.2" (or "18.21-s3" "18.21-s4")))))
38 (vulnerability "CVE-2019-0005" 38 (vulnerability "CVE-2019-0005"
39 '(("junos" (or "18.11" "18.1")))) 39 '(("juniper" "junos" (or "18.1" "18.11"))))
40 ;; CVE-2019-0005 has no "a" configurations. 40 ;; CVE-2019-0005 has no "a" configurations.
41 (vulnerability "CVE-2019-14811" 41 (vulnerability "CVE-2019-14811"
42 '(("ghostscript" (< "9.28")))) 42 '(("artifex" "ghostscript" (< "9.28"))))
43 (vulnerability "CVE-2019-17365" 43 (vulnerability "CVE-2019-17365"
44 '(("nix" (<= "2.3")))) 44 '(("nixos" "nix" (<= "2.3"))))
45 (vulnerability "CVE-2019-1010180" 45 (vulnerability "CVE-2019-1010180"
46 '(("gdb" _))) ;any version 46 '(("gnu" "gdb" _))) ;any version
47 (vulnerability "CVE-2019-1010204" 47 (vulnerability "CVE-2019-1010204"
48 '(("binutils" (and (>= "2.21") (<= "2.31.1"))) 48 '(("gnu" "binutils" (and (>= "2.21") (<= "2.31.1")))
49 ("binutils_gold" (and (>= "1.11") (<= "1.16"))))) 49 ("gnu" "binutils_gold" (and (>= "1.11") (<= "1.16")))))
50 ;; CVE-2019-18192 has no associated configurations. 50 ;; CVE-2019-18192 has no associated configurations.
51 )) 51 ))
52 52