* [bug#31442] [PATCH 2/5] packages: Add 'package-patched-vulnerabilities'.
2018-05-14 8:25 ` [bug#31442] [PATCH 1/5] profiles: Add '%current-profile', 'user-friendly-profile', & co Ludovic Courtès
@ 2018-05-14 8:25 ` Ludovic Courtès
2018-05-14 8:25 ` [bug#31442] [PATCH 3/5] profiles: Add 'properties' field to manifest entries Ludovic Courtès
` (2 subsequent siblings)
3 siblings, 0 replies; 7+ messages in thread
From: Ludovic Courtès @ 2018-05-14 8:25 UTC (permalink / raw)
To: 31442
* guix/packages.scm (patch-file-name): New procedure.
(%vulnerability-regexp): New variable.
(package-patched-vulnerabilities): New procedure.
* guix/scripts/lint.scm (patch-file-name): Remove.
(check-vulnerabilities): Adjust to use
'package-patched-vulnerabilities'.
* tests/packages.scm ("package-patched-vulnerabilities"): New test.
---
guix/packages.scm | 28 ++++++++++++++++++++++++++++
guix/scripts/lint.scm | 23 ++++-------------------
tests/packages.scm | 15 +++++++++++++++
3 files changed, 47 insertions(+), 19 deletions(-)
diff --git a/guix/packages.scm b/guix/packages.scm
index e0ab72086..f536597ae 100644
--- a/guix/packages.scm
+++ b/guix/packages.scm
@@ -35,6 +35,7 @@
#:use-module (guix sets)
#:use-module (ice-9 match)
#:use-module (ice-9 vlist)
+ #:use-module (ice-9 regex)
#:use-module (srfi srfi-1)
#:use-module (srfi srfi-9 gnu)
#:use-module (srfi srfi-11)
@@ -106,6 +107,7 @@
package-cross-derivation
package-output
package-grafts
+ package-patched-vulnerabilities
package/inherit
transitive-input-references
@@ -394,6 +396,32 @@ DELIMITER (a string), you can customize what will appear between the name and
the version. By default, DELIMITER is \"@\"."
(string-append (package-name package) delimiter (package-version package)))
+(define (patch-file-name patch)
+ "Return the basename of PATCH's file name, or #f if the file name could not
+be determined."
+ (match patch
+ ((? string?)
+ (basename patch))
+ ((? origin?)
+ (and=> (origin-actual-file-name patch) basename))))
+
+(define %vulnerability-regexp
+ ;; Regexp matching a CVE identifier in patch file names.
+ (make-regexp "CVE-[0-9]{4}-[0-9]+"))
+
+(define (package-patched-vulnerabilities package)
+ "Return the list of patched vulnerabilities of PACKAGE as a list of CVE
+identifiers. The result is inferred from the file names of patches."
+ (define (patch-vulnerabilities patch)
+ (map (cut match:substring <> 0)
+ (list-matches %vulnerability-regexp patch)))
+
+ (let ((patches (filter-map patch-file-name
+ (or (and=> (package-source package)
+ origin-patches)
+ '()))))
+ (append-map patch-vulnerabilities patches)))
+
(define (%standard-patch-inputs)
(let* ((canonical (module-ref (resolve-interface '(gnu packages base))
'canonical-package))
diff --git a/guix/scripts/lint.scm b/guix/scripts/lint.scm
index cd802985d..e477bf0dd 100644
--- a/guix/scripts/lint.scm
+++ b/guix/scripts/lint.scm
@@ -1,7 +1,7 @@
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2014 Cyril Roelandt <tipecaml@gmail.com>
;;; Copyright © 2014, 2015 Eric Bavier <bavier@member.fsf.org>
-;;; Copyright © 2013, 2014, 2015, 2016, 2017 Ludovic Courtès <ludo@gnu.org>
+;;; Copyright © 2013, 2014, 2015, 2016, 2017, 2018 Ludovic Courtès <ludo@gnu.org>
;;; Copyright © 2015, 2016 Mathieu Lirzin <mthl@gnu.org>
;;; Copyright © 2016 Danny Milosavljevic <dannym+a@scratchpost.org>
;;; Copyright © 2016 Hartmut Goebel <h.goebel@crazy-compilers.com>
@@ -809,15 +809,6 @@ descriptions maintained upstream."
(emit-warning package (G_ "invalid license field")
'license))))
-(define (patch-file-name patch)
- "Return the basename of PATCH's file name, or #f if the file name could not
-be determined."
- (match patch
- ((? string?)
- (basename patch))
- ((? origin?)
- (and=> (origin-actual-file-name patch) basename))))
-
(define (call-with-networking-fail-safe message error-value proc)
"Call PROC catching any network-related errors. Upon a networking error,
display a message including MESSAGE and return ERROR-VALUE."
@@ -878,20 +869,14 @@ the NIST server non-fatal."
(()
#t)
((vulnerabilities ...)
- (let* ((patches (filter-map patch-file-name
- (or (and=> (package-source package)
- origin-patches)
- '())))
+ (let* ((patched (package-patched-vulnerabilities package))
(known-safe (or (assq-ref (package-properties package)
'lint-hidden-cve)
'()))
(unpatched (remove (lambda (vuln)
(let ((id (vulnerability-id vuln)))
- (or
- (find (cute string-contains
- <> id)
- patches)
- (member id known-safe))))
+ (or (member id patched)
+ (member id known-safe))))
vulnerabilities)))
(unless (null? unpatched)
(emit-warning package
diff --git a/tests/packages.scm b/tests/packages.scm
index 9e19c3992..642a3efa5 100644
--- a/tests/packages.scm
+++ b/tests/packages.scm
@@ -941,6 +941,21 @@
((("x" dep))
(eq? dep findutils)))))))))
+(test-equal "package-patched-vulnerabilities"
+ '(("CVE-2015-1234")
+ ("CVE-2016-1234" "CVE-2018-4567")
+ ())
+ (let ((p1 (dummy-package "pi"
+ (source (dummy-origin
+ (patches (list "/a/b/pi-CVE-2015-1234.patch"))))))
+ (p2 (dummy-package "pi"
+ (source (dummy-origin
+ (patches (list
+ "/a/b/pi-CVE-2016-1234-CVE-2018-4567.patch"))))))
+ (p3 (dummy-package "pi" (source (dummy-origin)))))
+ (map package-patched-vulnerabilities
+ (list p1 p2 p3))))
+
(test-eq "fold-packages" hello
(fold-packages (lambda (p r)
(if (string=? (package-name p) "hello")
--
2.17.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [bug#31442] [PATCH 3/5] profiles: Add 'properties' field to manifest entries.
2018-05-14 8:25 ` [bug#31442] [PATCH 1/5] profiles: Add '%current-profile', 'user-friendly-profile', & co Ludovic Courtès
2018-05-14 8:25 ` [bug#31442] [PATCH 2/5] packages: Add 'package-patched-vulnerabilities' Ludovic Courtès
@ 2018-05-14 8:25 ` Ludovic Courtès
2018-05-14 8:25 ` [bug#31442] [PATCH 4/5] profiles: Record fixed vulnerabilities as properties of entries Ludovic Courtès
2018-05-14 8:25 ` [bug#31442] [PATCH 5/5] DRAFT Add 'guix health' Ludovic Courtès
3 siblings, 0 replies; 7+ messages in thread
From: Ludovic Courtès @ 2018-05-14 8:25 UTC (permalink / raw)
To: 31442
* guix/profiles.scm (<manifest-entry>)[properties]: New field.
(manifest->gexp)[entry->gexp]: Serialize it.
(sexp->manifest)[sexp->manifest-entry]: Deserialize it.
---
guix/profiles.scm | 19 +++++++++++++------
1 file changed, 13 insertions(+), 6 deletions(-)
diff --git a/guix/profiles.scm b/guix/profiles.scm
index 3cdc3d2f1..02828e465 100644
--- a/guix/profiles.scm
+++ b/guix/profiles.scm
@@ -78,6 +78,7 @@
manifest-entry-dependencies
manifest-entry-search-paths
manifest-entry-parent
+ manifest-entry-properties
manifest-pattern
manifest-pattern?
@@ -181,7 +182,9 @@
(search-paths manifest-entry-search-paths ; search-path-specification*
(default '()))
(parent manifest-entry-parent ; promise (#f | <manifest-entry>)
- (default (delay #f))))
+ (default (delay #f)))
+ (properties manifest-entry-properties ; list of symbol/value pairs
+ (default '())))
(define-record-type* <manifest-pattern> manifest-pattern
make-manifest-pattern
@@ -320,18 +323,20 @@ denoting a specific output of a package."
(define (entry->gexp entry)
(match entry
(($ <manifest-entry> name version output (? string? path)
- (deps ...) (search-paths ...))
+ (deps ...) (search-paths ...) _ (properties ...))
#~(#$name #$version #$output #$path
(propagated-inputs #$(map entry->gexp deps))
(search-paths #$(map search-path-specification->sexp
- search-paths))))
+ search-paths))
+ (properties . #$properties)))
(($ <manifest-entry> name version output package
- (deps ...) (search-paths ...))
+ (deps ...) (search-paths ...) _ (properties ...))
#~(#$name #$version #$output
(ungexp package (or output "out"))
(propagated-inputs #$(map entry->gexp deps))
(search-paths #$(map search-path-specification->sexp
- search-paths))))))
+ search-paths))
+ (properties . #$properties)))))
(match manifest
(($ <manifest> (entries ...))
@@ -394,7 +399,9 @@ procedure is here for backward-compatibility and will eventually vanish."
(dependencies deps*)
(search-paths (map sexp->search-path-specification
search-paths))
- (parent parent))))
+ (parent parent)
+ (properties (or (assoc-ref extra-stuff 'properties)
+ '())))))
entry))))
(match sexp
--
2.17.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [bug#31442] [PATCH 4/5] profiles: Record fixed vulnerabilities as properties of entries.
2018-05-14 8:25 ` [bug#31442] [PATCH 1/5] profiles: Add '%current-profile', 'user-friendly-profile', & co Ludovic Courtès
2018-05-14 8:25 ` [bug#31442] [PATCH 2/5] packages: Add 'package-patched-vulnerabilities' Ludovic Courtès
2018-05-14 8:25 ` [bug#31442] [PATCH 3/5] profiles: Add 'properties' field to manifest entries Ludovic Courtès
@ 2018-05-14 8:25 ` Ludovic Courtès
2018-05-14 8:25 ` [bug#31442] [PATCH 5/5] DRAFT Add 'guix health' Ludovic Courtès
3 siblings, 0 replies; 7+ messages in thread
From: Ludovic Courtès @ 2018-05-14 8:25 UTC (permalink / raw)
To: 31442
* guix/profiles.scm (package->manifest-entry)[fixed, cpe-name]
[cpe-version]: New variables.
Populate the 'properties' field based on these.
* tests/profiles.scm ("manifest-entry-properties"): New test.
---
guix/profiles.scm | 23 ++++++++++++++++++++++-
tests/profiles.scm | 22 ++++++++++++++++++++++
2 files changed, 44 insertions(+), 1 deletion(-)
diff --git a/guix/profiles.scm b/guix/profiles.scm
index 02828e465..6656cf356 100644
--- a/guix/profiles.scm
+++ b/guix/profiles.scm
@@ -286,6 +286,17 @@ file name."
(define* (package->manifest-entry package #:optional (output "out")
#:key (parent (delay #f)))
"Return a manifest entry for the OUTPUT of package PACKAGE."
+ (define fixed
+ (append (package-patched-vulnerabilities package)
+ (or (assq-ref (package-properties package) 'lint-hidden-cve)
+ '())))
+
+ (define cpe-name
+ (assoc-ref (package-properties package) 'cpe-name))
+
+ (define cpe-version
+ (assoc-ref (package-properties package) 'cpe-version))
+
;; For each dependency, keep a promise pointing to its "parent" entry.
(letrec* ((deps (map (match-lambda
((label package)
@@ -303,7 +314,17 @@ file name."
(dependencies (delete-duplicates deps))
(search-paths
(package-transitive-native-search-paths package))
- (parent parent))))
+ (parent parent)
+ (properties `(,@(if cpe-name
+ `((cpe-name . ,cpe-name))
+ '())
+ ,@(if cpe-version
+ `((cpe-version . ,cpe-version))
+ '())
+ ,@(if (null? fixed)
+ '()
+ `((fixed-vulnerabilities
+ . ,fixed))))))))
entry))
(define (packages->manifest packages)
diff --git a/tests/profiles.scm b/tests/profiles.scm
index c668c2b83..8152e4b68 100644
--- a/tests/profiles.scm
+++ b/tests/profiles.scm
@@ -439,6 +439,28 @@
#:locales? #f)))
(return #f)))))
+(test-equal "manifest-entry-properties"
+ '(((fixed-vulnerabilities "CVE-2015-1234"))
+ ((fixed-vulnerabilities "CVE-2016-1234" "CVE-2018-4567"))
+ ((cpe-name . "Pi")
+ (fixed-vulnerabilities "CVE-2002-0001"))
+ ())
+ (let ((p1 (dummy-package "pi"
+ (source (dummy-origin
+ (patches (list "/a/b/pi-CVE-2015-1234.patch"))))))
+ (p2 (dummy-package "pi"
+ (source (dummy-origin
+ (patches
+ (list
+ "/a/b/pi-CVE-2016-1234-CVE-2018-4567.patch"))))))
+ (p3 (dummy-package "pi" (source (dummy-origin))
+ (properties
+ '((cpe-name . "Pi")
+ (lint-hidden-cve "CVE-2002-0001")))))
+ (p4 (dummy-package "pi" (source (dummy-origin)))))
+ (map (compose manifest-entry-properties package->manifest-entry)
+ (list p1 p2 p3 p4))))
+
(test-assertm "no collision"
;; Here we have an entry that is "lowered" (its 'item' field is a store file
;; name) and another entry (its 'item' field is a package) that is
--
2.17.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [bug#31442] [PATCH 5/5] DRAFT Add 'guix health'.
2018-05-14 8:25 ` [bug#31442] [PATCH 1/5] profiles: Add '%current-profile', 'user-friendly-profile', & co Ludovic Courtès
` (2 preceding siblings ...)
2018-05-14 8:25 ` [bug#31442] [PATCH 4/5] profiles: Record fixed vulnerabilities as properties of entries Ludovic Courtès
@ 2018-05-14 8:25 ` Ludovic Courtès
3 siblings, 0 replies; 7+ messages in thread
From: Ludovic Courtès @ 2018-05-14 8:25 UTC (permalink / raw)
To: 31442
DRAFT: Needs doc and tests, plus the FIXME noted inside.
* guix/scripts/health.scm: New file.
* Makefile.am (MODULES): Add it.
* po/guix/POTFILES.in: Add it.
---
Makefile.am | 1 +
guix/scripts/health.scm | 158 ++++++++++++++++++++++++++++++++++++++++
po/guix/POTFILES.in | 1 +
3 files changed, 160 insertions(+)
create mode 100644 guix/scripts/health.scm
diff --git a/Makefile.am b/Makefile.am
index 38bd54cf4..870ff6a89 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -194,6 +194,7 @@ MODULES = \
guix/scripts/package.scm \
guix/scripts/gc.scm \
guix/scripts/hash.scm \
+ guix/scripts/health.scm \
guix/scripts/pack.scm \
guix/scripts/pull.scm \
guix/scripts/substitute.scm \
diff --git a/guix/scripts/health.scm b/guix/scripts/health.scm
new file mode 100644
index 000000000..a991fcbe3
--- /dev/null
+++ b/guix/scripts/health.scm
@@ -0,0 +1,158 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2018 Ludovic Courtès <ludo@gnu.org>
+;;;
+;;; This file is part of GNU Guix.
+;;;
+;;; GNU Guix is free software; you can redistribute it and/or modify it
+;;; under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 3 of the License, or (at
+;;; your option) any later version.
+;;;
+;;; GNU Guix is distributed in the hope that it will be useful, but
+;;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;;; GNU General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
+
+(define-module (guix scripts health)
+ #:use-module (guix ui)
+ #:use-module (guix scripts)
+ #:use-module (guix profiles)
+ #:use-module (guix packages)
+ #:use-module (guix cve)
+ #:use-module (guix utils)
+ #:use-module (gnu packages)
+ #:use-module (srfi srfi-1)
+ #:use-module (srfi srfi-37)
+ #:use-module (ice-9 match)
+ #:export (guix-health))
+
+\f
+;;;
+;;; Reporting CVEs.
+;;;
+
+(define (same-package-entries? entry1 entry2)
+ "Return true if ENTRY1 and ENTRY2 refer to the same package and version."
+ (and (string=? (manifest-entry-name entry1)
+ (manifest-entry-name entry2))
+ (string=? (manifest-entry-version entry1)
+ (manifest-entry-version entry2))))
+
+(define (manifest-entry-vulnerabilities entry lookup-vulnerabilities)
+ "Return the list of vulnerabilities for ENTRY. Call LOOKUP-VULNERABILITIES
+to determine the list of vulnerabilities for a package/version."
+ (let* ((name (manifest-entry-name entry))
+ (cpe-name (or (assoc-ref (manifest-entry-properties entry)
+ 'cpe-name)
+ name))
+ (version (manifest-entry-version entry))
+ (cpe-version (or (assoc-ref (manifest-entry-properties entry)
+ 'cpe-version)
+ version))
+ (fixed (or (assoc-ref (manifest-entry-properties entry)
+ 'fixed-vulnerabilities)
+ '())))
+ (remove (lambda (vuln)
+ (member (vulnerability-id vuln) fixed))
+ (lookup-vulnerabilities cpe-name cpe-version))))
+
+(define (check-profile-cve profile)
+ "Check and report the CVEs of packages in PROFILE."
+ (define lookup-vulnerabilities
+ (vulnerabilities->lookup-proc (current-vulnerabilities)))
+
+ (define (report-entry-vulnerabilities entry)
+ (let ((name (manifest-entry-name entry))
+ (version (manifest-entry-version entry)))
+ (match (manifest-entry-vulnerabilities entry lookup-vulnerabilities)
+ (()
+ #t)
+ ((vulns ...)
+ (warning (G_ "~a@~a may be vulnerable to~{ ~a~}~%")
+ name version (map vulnerability-id vulns))
+ (match (find-best-packages-by-name name #f)
+ ((package . _)
+ (let ((vulns* (lookup-vulnerabilities name
+ (package-version package))))
+ (match (lset-difference string=?
+ (map vulnerability-id vulns)
+ (map vulnerability-id vulns*))
+ (()
+ (warning (G_ "~a@~a is available but does not \
+fix any of these~%")
+ name (package-version package))
+ (display-hint (format #f (G_ "Run @command{guix pull} and
+then re-run @command{guix health} to see if fixes are available. If none are
+available, please consider submitting a patch for the package definition of
+'~a'.") name)))
+ (fixed
+ (warning (G_ "~a@~a is available and fixes~{ ~a~}, \
+consider ugprading~%")
+ name (package-version package) fixed)))))
+ (()
+ (warning (G_ "'~a' is unavailable and thus \
+cannot be upgraded~%")
+ name)))))))
+
+ (let* ((manifest (profile-manifest profile))
+ (entries (manifest-transitive-entries manifest)))
+ ;; FIXME: We don't report vulnerabilities in dependencies of the entries.
+ ;; We could check the references and infer the package name/version for
+ ;; each of them, but then we wouldn't know their CPE name nor whether they
+ ;; already contain patches fixing known vulnerabilities.
+ (for-each report-entry-vulnerabilities
+ (delete-duplicates entries same-package-entries?))))
+
+\f
+;;;
+;;; Command-line options.
+;;;
+
+(define (show-help)
+ (display (G_ "Usage: guix health [OPTIONS]
+Report on the vulnerabilities of packages in a profile.\n"))
+ (display (G_ "
+ -p, --profile=PROFILE use PROFILE instead of the user's default profile"))
+ (newline)
+ (display (G_ "
+ -h, --help display this help and exit"))
+ (display (G_ "
+ -V, --version display version information and exit"))
+ (newline)
+ (show-bug-report-information))
+
+(define %options
+ (list (option '(#\h "help") #f #f
+ (lambda args
+ (show-help)
+ (exit 0)))
+ (option '(#\V "version") #f #f
+ (lambda args
+ (show-version-and-exit "guix package")))
+
+ (option '(#\p "profile") #t #f
+ (lambda (opt name arg result)
+ (values (alist-cons 'profile (canonicalize-profile arg)
+ result)
+ #f)))))
+
+(define %default-options
+ ;; Alist of default option values.
+ '())
+
+\f
+;;;
+;;; Entry point.
+;;;
+
+(define (guix-health . args)
+ (with-error-handling
+ (let* ((opts (parse-command-line args %options (list %default-options)
+ #:build-options? #f))
+ (profile (or (and=> (assoc-ref opts 'profile)
+ user-friendly-profile)
+ %user-profile-directory)))
+ (check-profile-cve profile))))
diff --git a/po/guix/POTFILES.in b/po/guix/POTFILES.in
index d11f408d4..76fdbe13b 100644
--- a/po/guix/POTFILES.in
+++ b/po/guix/POTFILES.in
@@ -31,6 +31,7 @@ guix/scripts/challenge.scm
guix/scripts/copy.scm
guix/scripts/pack.scm
guix/scripts/weather.scm
+guix/scripts/health.scm
guix/gnu-maintenance.scm
guix/scripts/container.scm
guix/scripts/container/exec.scm
--
2.17.0
^ permalink raw reply related [flat|nested] 7+ messages in thread