diff options
| author | Nicolas Graves via Guix-patches via <guix-patches@gnu.org> | 2024-11-24 21:16:19 +0100 |
|---|---|---|
| committer | Zheng Junjie <z572@z572.online> | 2025-06-23 12:31:09 +0800 |
| commit | 229674573cd3d2d2006acec7a6dc0d2a39f987df (patch) | |
| tree | f23da2b4a8bf3b914e2ce7b97264c4d1da63f809 | |
| parent | f511be7bccdb3e5bbe4faed2829be73ad4e1ee01 (diff) | |
cve: Add cpe-vendor and lint-hidden-cpe-vendors properties.
* guix/cve.scm: Exploit cpe vendors information.
(cpe->package-name): Rename to...
(cpe->package-identifier): Renamed from cpe->package-name. Use
cpe_vendor:cpe_name in place or cpe_name.
(vulnerabily-matches?): Add helper function.
(vulnerabilities->lookup-proc): Extract cpe_name for table
hashes. Add vendor and hidden-vendor arguments. Adapt condition to
pass vulnerabilities to result in the fold.
(write-cache, fetch-vulnerabilities): Update the format version.
* guix/lint.scm (package-vulnerabilities): Use additional arguments
from vulnerabilities->lookup-proc.
* tests/cve.scm (%expected-vulnerabilities): Adapt variable to changes
in guix/cve.scm.
Signed-off-by: Zheng Junjie <z572@z572.online>
| -rw-r--r-- | guix/cve.scm | 162 | ||||
| -rw-r--r-- | guix/lint.scm | 10 | ||||
| -rw-r--r-- | tests/cve.scm | 14 |
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 |
| 113 | name, in a very naive way. Return two values: the package name, and its | 113 | identifier, in a very naive way. Return three values: the CPE vendor, the |
| 114 | version string. Return #f and #f if CPE does not look like an application CPE | 114 | package name, and its version string. |
| 115 | string." | 115 | Return 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 | ||
| 235 | packages. When VENDOR is #f, ignore packages that have a vendor among | ||
| 236 | HIDDEN-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) |
| 276 | where P is a package name and SEXP expresses constraints on the matching | 314 | where V is a CPE vendor, P is a package name and SEXP expresses constraints on |
| 277 | versions." | 315 | the 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 |
| 297 | is the name of a package and SEXP is an sexp that constrains matching | 335 | CPE vendor, P is the name of a package and SEXP is an sexp that constrains |
| 298 | versions." | 336 | matching 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 | ||
