Gentoo Archives: gentoo-portage-dev

From: Zac Medico <zmedico@g.o>
To: gentoo-portage-dev@l.g.o
Cc: Aaron Bauman <bman@g.o>, Zac Medico <zmedico@g.o>
Subject: [gentoo-portage-dev] [PATCH] glsa-check moved to portage repository (bug 463952)
Date: Tue, 13 Aug 2019 01:35:31
Message-Id: 20190813013509.14998-1-zmedico@gentoo.org
1 This copy of glsa-check is no longer need since it has
2 moved to the portage repository.
3
4 Bug: https://bugs.gentoo.org/463952
5 Signed-off-by: Zac Medico <zmedico@g.o>
6 ---
7 README | 1 -
8 bin/glsa-check | 418 ------------------
9 man/glsa-check.1 | 66 ---
10 pym/gentoolkit/glsa/__init__.py | 741 --------------------------------
11 setup.py | 2 -
12 5 files changed, 1228 deletions(-)
13 delete mode 100755 bin/glsa-check
14 delete mode 100644 man/glsa-check.1
15 delete mode 100644 pym/gentoolkit/glsa/__init__.py
16
17 diff --git a/README b/README
18 index e122086..4ec4fc9 100644
19 --- a/README
20 +++ b/README
21 @@ -29,7 +29,6 @@ equery - replacement for etcat and qpkg
22 eread - script to read portage log items from einfo, ewarn etc.
23 eshowkw - Display keywords for specified package(s)
24 euse - tool to manage USE flags
25 -glsa-check - tool to manage GLSA's (Gentoo Linux Security Advisory)
26 imlate - Displays candidates for keywords for an architecture...
27 qpkg - convient package query tool (deprecated)
28 revdep-rebuild - scans/fixes broken shared libs and binaries
29 diff --git a/bin/glsa-check b/bin/glsa-check
30 deleted file mode 100755
31 index 3f691b8..0000000
32 --- a/bin/glsa-check
33 +++ /dev/null
34 @@ -1,418 +0,0 @@
35 -#!/usr/bin/python
36 -
37 -# $Header: $
38 -# This program is licensed under the GPL, version 2
39 -
40 -import sys
41 -import os
42 -import codecs
43 -from functools import reduce
44 -
45 -import portage
46 -from portage.output import *
47 -
48 -from getopt import getopt, GetoptError
49 -
50 -__program__ = "glsa-check"
51 -__author__ = "Marius Mauch <genone@g.o>"
52 -__version__ = "git"
53 -
54 -optionmap = [
55 -["-l", "--list", "list the GLSAs"],
56 -["-d", "--dump", "--print", "show all information about the GLSAs"],
57 -["-t", "--test", "test if this system is affected by the GLSAs"],
58 -["-p", "--pretend", "show the necessary steps to apply the GLSAs"],
59 -["-f", "--fix", "try to auto-apply the GLSAs (experimental)"],
60 -["-i", "--inject", "inject the given GLSA into the glsa_injected file"],
61 -["-n", "--nocolor", "disable colors (option)"],
62 -["-e", "--emergelike", "upgrade to latest version (not least-change, option)"],
63 -["-h", "--help", "show this help message"],
64 -["-V", "--version", "some information about this tool"],
65 -["-v", "--verbose", "print more information (option)"],
66 -["-c", "--cve", "show CVE ids in listing mode (option)"],
67 -["-q", "--quiet", "be less verbose and do not send empty mail (option)"],
68 -["-m", "--mail", "send a mail with the given GLSAs to the administrator"],
69 -]
70 -
71 -# print a warning as this is beta code (but proven by now, so no more warning)
72 -#sys.stderr.write("WARNING: This tool is completely new and not very tested, so it should not be\n")
73 -#sys.stderr.write("used on production systems. It's mainly a test tool for the new GLSA release\n")
74 -#sys.stderr.write("and distribution system, it's functionality will later be merged into emerge\n")
75 -#sys.stderr.write("and equery.\n")
76 -#sys.stderr.write("Please read http://www.gentoo.org/proj/en/portage/glsa-integration.xml\n")
77 -#sys.stderr.write("before using this tool AND before reporting a bug.\n\n")
78 -
79 -# option parsing
80 -args = []
81 -params = []
82 -try:
83 - args, params = getopt(sys.argv[1:], "".join([o[0][1] for o in optionmap]), \
84 - [x[2:] for x in reduce(lambda x,y: x+y, [z[1:-1] for z in optionmap])])
85 - args = [a for a, b in args]
86 -
87 - for option in ["--nocolor", "-n"]:
88 - if option in args:
89 - nocolor()
90 - args.remove(option)
91 -
92 - verbose = False
93 - for option in ["--verbose", "-v"]:
94 - if option in args:
95 - verbose = True
96 - args.remove(option)
97 -
98 - list_cve = False
99 - for option in ["--cve", "-c"]:
100 - if option in args:
101 - list_cve = True
102 - args.remove(option)
103 -
104 - least_change = True
105 - for option in ["--emergelike", "-e"]:
106 - if option in args:
107 - least_change = False
108 - args.remove(option)
109 -
110 - quiet = False
111 - for option in ["--quiet", "-q"]:
112 - if option in args:
113 - quiet = True
114 - args.remove(option)
115 -
116 -
117 - # sanity checking
118 - if len(args) <= 0:
119 - sys.stderr.write("no option given: what should I do ?\n")
120 - mode = "HELP"
121 - elif len(args) > 1:
122 - sys.stderr.write("please use only one command per call\n")
123 - mode = "HELP"
124 - else:
125 - # in what mode are we ?
126 - args = args[0]
127 - for m in optionmap:
128 - if args in [o for o in m[:-1]]:
129 - mode = m[1][2:]
130 -
131 -except GetoptError as e:
132 - sys.stderr.write("unknown option given: ")
133 - sys.stderr.write(str(e)+"\n")
134 - mode = "HELP"
135 -
136 -# we need a set of glsa for most operation modes
137 -if len(params) <= 0 and mode in ["fix", "test", "pretend", "dump", "inject", "mail"]:
138 - sys.stderr.write("\nno GLSA given, so we'll do nothing for now. \n")
139 - sys.stderr.write("If you want to run on all GLSA please tell me so \n")
140 - sys.stderr.write("(specify \"all\" as parameter)\n\n")
141 - mode = "HELP"
142 -elif len(params) <= 0 and mode == "list":
143 - params.append("affected")
144 -
145 -# show help message
146 -if mode == "help" or mode == "HELP":
147 - msg = "Syntax: glsa-check <option> [glsa-list]\n\n"
148 - for m in optionmap:
149 - msg += m[0] + "\t" + m[1] + " \t: " + m[-1] + "\n"
150 - for o in m[2:-1]:
151 - msg += "\t" + o + "\n"
152 - msg += "\nglsa-list can contain an arbitrary number of GLSA ids, \n"
153 - msg += "filenames containing GLSAs or the special identifiers \n"
154 - msg += "'all' and 'affected'\n"
155 - if mode == "help":
156 - sys.stdout.write(msg)
157 - sys.exit(0)
158 - else:
159 - sys.stderr.write("\n" + msg)
160 - sys.exit(1)
161 -
162 -# we need root privileges for write access
163 -if mode in ["fix", "inject"] and os.geteuid() != 0:
164 - sys.stderr.write(__program__ + ": root access is needed for \""+mode+"\" mode\n")
165 - sys.exit(2)
166 -
167 -# show version and copyright information
168 -if mode == "version":
169 - sys.stderr.write("%(program)s (%(version)s)\n" % {
170 - "program": __program__,
171 - "version": __version__
172 - })
173 - sys.stderr.write("Author: %s\n" % __author__)
174 - sys.stderr.write("This program is licensed under the GPL, version 2\n")
175 - sys.exit(0)
176 -
177 -# delay this for speed increase
178 -from gentoolkit.glsa import *
179 -
180 -glsaconfig = checkconfig(portage.config(clone=portage.settings))
181 -
182 -if quiet:
183 - glsaconfig["EMERGE_OPTS"] += " --quiet"
184 -
185 -vardb = portage.db[portage.root]["vartree"].dbapi
186 -portdb = portage.db[portage.root]["porttree"].dbapi
187 -
188 -# Check that we really have a glsa dir to work on
189 -if not (os.path.exists(glsaconfig["GLSA_DIR"]) and os.path.isdir(glsaconfig["GLSA_DIR"])):
190 - sys.stderr.write(red("ERROR")+": GLSA_DIR %s doesn't exist. Please fix this.\n" % glsaconfig["GLSA_DIR"])
191 - sys.exit(1)
192 -
193 -# build glsa lists
194 -completelist = get_glsa_list(glsaconfig["GLSA_DIR"], glsaconfig)
195 -
196 -if os.access(glsaconfig["CHECKFILE"], os.R_OK):
197 - checklist = [line.strip() for line in open(glsaconfig["CHECKFILE"], "r").readlines()]
198 -else:
199 - checklist = []
200 -todolist = [e for e in completelist if e not in checklist]
201 -
202 -glsalist = []
203 -if "new" in params:
204 - params.remove("new")
205 - sys.stderr.write("Warning: The 'new' glsa-list target has been removed, using 'affected'.\n")
206 - params.append("affected")
207 -
208 -if "all" in params:
209 - glsalist = completelist
210 - params.remove("all")
211 -
212 -if "affected" in params:
213 - for x in todolist:
214 - try:
215 - myglsa = Glsa(x, glsaconfig)
216 - except (GlsaTypeException, GlsaFormatException) as e:
217 - if verbose:
218 - sys.stderr.write(("invalid GLSA: %s (error message was: %s)\n" % (x, e)))
219 - continue
220 - if myglsa.isVulnerable():
221 - glsalist.append(x)
222 - params.remove("affected")
223 -
224 -# remove invalid parameters
225 -for p in params[:]:
226 - if not (p in completelist or os.path.exists(p)):
227 - sys.stderr.write(("(removing %s from parameter list as it isn't a valid GLSA specification)\n" % p))
228 - params.remove(p)
229 -
230 -glsalist.extend([g for g in params if g not in glsalist])
231 -
232 -def summarylist(myglsalist, fd1=sys.stdout, fd2=sys.stderr, encoding="utf-8"):
233 - # Get to the raw streams in py3k before wrapping them with an encoded writer
234 - # to avoid writing bytes to a text stream (stdout/stderr are text streams
235 - # by default in py3k)
236 - if hasattr(fd1, "buffer"):
237 - fd1 = fd1.buffer
238 - if hasattr(fd2, "buffer"):
239 - fd2 = fd2.buffer
240 - fd1 = codecs.getwriter(encoding)(fd1)
241 - fd2 = codecs.getwriter(encoding)(fd2)
242 - if not quiet:
243 - fd2.write(white("[A]")+" means this GLSA was marked as applied (injected),\n")
244 - fd2.write(green("[U]")+" means the system is not affected and\n")
245 - fd2.write(red("[N]")+" indicates that the system might be affected.\n\n")
246 -
247 - myglsalist.sort()
248 - for myid in myglsalist:
249 - try:
250 - myglsa = Glsa(myid, glsaconfig)
251 - except (GlsaTypeException, GlsaFormatException) as e:
252 - if verbose:
253 - fd2.write(("invalid GLSA: %s (error message was: %s)\n" % (myid, e)))
254 - continue
255 - if myglsa.isInjected():
256 - status = "[A]"
257 - color = white
258 - elif myglsa.isVulnerable():
259 - status = "[N]"
260 - color = red
261 - else:
262 - status = "[U]"
263 - color = green
264 -
265 - if verbose:
266 - access = ("[%-8s] " % myglsa.access)
267 - else:
268 - access = ""
269 -
270 - fd1.write(color(myglsa.nr) + " " + color(status) + " " + color(access) + myglsa.title + " (")
271 - if not verbose:
272 - for pkg in list(myglsa.packages.keys())[:3]:
273 - fd1.write(" " + pkg + " ")
274 - if len(myglsa.packages) > 3:
275 - fd1.write("... ")
276 - else:
277 - for cpv in myglsa.packages.keys():
278 - pkg = myglsa.packages[cpv]
279 - for path in pkg:
280 - v_installed = reduce(operator.add, [match(v, "vartree") for v in path["vul_atoms"]], [])
281 - u_installed = reduce(operator.add, [match(u, "vartree") for u in path["unaff_atoms"]], [])
282 - mylist = sorted(set(v_installed).difference(set(u_installed)))
283 - if len(mylist) > 0:
284 - cpv = color(" ".join(mylist))
285 - fd1.write(" " + cpv + " ")
286 -
287 - fd1.write(")")
288 - if list_cve:
289 - fd1.write(" "+(",".join([r[:13] for r in myglsa.references if r[:4] in ["CAN-", "CVE-"]])))
290 - fd1.write("\n")
291 - return 0
292 -
293 -if mode == "list":
294 - sys.exit(summarylist(glsalist))
295 -
296 -# dump, fix, inject and fix are nearly the same code, only the glsa method call differs
297 -if mode in ["dump", "fix", "inject", "pretend"]:
298 - for myid in glsalist:
299 - try:
300 - myglsa = Glsa(myid, glsaconfig)
301 - except (GlsaTypeException, GlsaFormatException) as e:
302 - if verbose:
303 - sys.stderr.write(("invalid GLSA: %s (error message was: %s)\n" % (myid, e)))
304 - continue
305 - if mode == "dump":
306 - myglsa.dump()
307 - elif mode == "fix":
308 - if not quiet:
309 - sys.stdout.write("Fixing GLSA "+myid+"\n")
310 - if not myglsa.isVulnerable():
311 - if not quiet:
312 - sys.stdout.write(">>> no vulnerable packages installed\n")
313 - else:
314 - if quiet:
315 - sys.stdout.write("Fixing GLSA "+myid+"\n")
316 - mergelist = myglsa.getMergeList(least_change=least_change)
317 - if mergelist == []:
318 - sys.stdout.write(">>> cannot fix GLSA, no unaffected packages available\n")
319 - sys.exit(2)
320 - for pkg in mergelist:
321 - sys.stdout.write(">>> merging "+pkg+"\n")
322 - # using emerge for the actual merging as it contains the dependency
323 - # code and we want to be consistent in behaviour. Also this functionality
324 - # will be integrated in emerge later, so it shouldn't hurt much.
325 - emergecmd = "emerge --oneshot " + glsaconfig["EMERGE_OPTS"] + " =" + pkg
326 - if verbose:
327 - sys.stderr.write(emergecmd+"\n")
328 - exitcode = os.system(emergecmd)
329 - # system() returns the exitcode in the high byte of a 16bit integer
330 - if exitcode >= 1 << 8:
331 - exitcode >>= 8
332 - if exitcode:
333 - sys.exit(exitcode)
334 - if len(mergelist):
335 - sys.stdout.write("\n")
336 - elif mode == "pretend":
337 - if not quiet:
338 - sys.stdout.write("Checking GLSA "+myid+"\n")
339 - if not myglsa.isVulnerable():
340 - if not quiet:
341 - sys.stdout.write(">>> no vulnerable packages installed\n")
342 - else:
343 - if quiet:
344 - sys.stdout.write("Checking GLSA "+myid+"\n")
345 - mergedict = {}
346 - for (vuln, update) in myglsa.getAffectionTable(least_change=least_change):
347 - mergedict.setdefault(update, []).append(vuln)
348 -
349 - # first, extract the atoms that cannot be upgraded (where key == "")
350 - no_upgrades = []
351 - if "" in mergedict:
352 - no_upgrades = mergedict[""]
353 - del mergedict[""]
354 -
355 - # see if anything is left that can be upgraded
356 - if mergedict:
357 - sys.stdout.write(">>> Updates that will be performed:\n")
358 - for (upd, vuln) in mergedict.items():
359 - sys.stdout.write(" " + green(upd) + " (vulnerable: " + red(", ".join(vuln)) + ")\n")
360 -
361 - if no_upgrades:
362 - sys.stdout.write(">>> No upgrade path exists for these packages:\n")
363 - sys.stdout.write(" " + red(", ".join(no_upgrades)) + "\n")
364 - elif mode == "inject":
365 - sys.stdout.write("injecting " + myid + "\n")
366 - myglsa.inject()
367 - sys.exit(0)
368 -
369 -# test is a bit different as Glsa.test() produces no output
370 -if mode == "test":
371 - outputlist = []
372 - for myid in glsalist:
373 - try:
374 - myglsa = Glsa(myid, glsaconfig)
375 - except (GlsaTypeException, GlsaFormatException) as e:
376 - if verbose:
377 - sys.stderr.write(("invalid GLSA: %s (error message was: %s)\n" % (myid, e)))
378 - continue
379 - if myglsa.isVulnerable():
380 - outputlist.append(str(myglsa.nr))
381 - if len(outputlist) > 0:
382 - sys.stderr.write("This system is affected by the following GLSAs:\n")
383 - if verbose:
384 - summarylist(outputlist)
385 - else:
386 - sys.stdout.write("\n".join(outputlist)+"\n")
387 - else:
388 - sys.stderr.write("This system is not affected by any of the listed GLSAs\n")
389 - sys.exit(0)
390 -
391 -# mail mode as requested by solar
392 -if mode == "mail":
393 - try:
394 - import portage.mail as portage_mail
395 - except ImportError:
396 - import portage_mail
397 -
398 - import socket
399 - from io import BytesIO
400 - try:
401 - from email.mime.text import MIMEText
402 - except ImportError:
403 - from email.MIMEText import MIMEText
404 -
405 - # color doesn't make any sense for mail
406 - nocolor()
407 -
408 - if "PORTAGE_ELOG_MAILURI" in glsaconfig:
409 - myrecipient = glsaconfig["PORTAGE_ELOG_MAILURI"].split()[0]
410 - else:
411 - myrecipient = "root@localhost"
412 -
413 - if "PORTAGE_ELOG_MAILFROM" in glsaconfig:
414 - myfrom = glsaconfig["PORTAGE_ELOG_MAILFROM"]
415 - else:
416 - myfrom = "glsa-check"
417 -
418 - mysubject = "[glsa-check] Summary for %s" % socket.getfqdn()
419 -
420 - # need a file object for summarylist()
421 - myfd = BytesIO()
422 - line = "GLSA Summary report for host %s\n" % socket.getfqdn()
423 - myfd.write(line.encode("utf-8"))
424 - line = "(Command was: %s)\n\n" % " ".join(sys.argv)
425 - myfd.write(line.encode("utf-8"))
426 - summarylist(glsalist, fd1=myfd, fd2=myfd)
427 - summary = myfd.getvalue().decode("utf-8")
428 - myfd.close()
429 -
430 - myattachments = []
431 - for myid in glsalist:
432 - try:
433 - myglsa = Glsa(myid, glsaconfig)
434 - except (GlsaTypeException, GlsaFormatException) as e:
435 - if verbose:
436 - sys.stderr.write(("invalid GLSA: %s (error message was: %s)\n" % (myid, e)))
437 - continue
438 - myfd = BytesIO()
439 - myglsa.dump(outstream=myfd)
440 - attachment = myfd.getvalue().decode("utf-8")
441 - myattachments.append(MIMEText(attachment, _charset="utf8"))
442 - myfd.close()
443 -
444 - if glsalist or not quiet:
445 - mymessage = portage_mail.create_message(myfrom, myrecipient, mysubject, summary, myattachments)
446 - portage_mail.send_mail(glsaconfig, mymessage)
447 -
448 - sys.exit(0)
449 -
450 -# something wrong here, all valid paths are covered with sys.exit()
451 -sys.stderr.write("nothing more to do\n")
452 -sys.exit(2)
453 diff --git a/man/glsa-check.1 b/man/glsa-check.1
454 deleted file mode 100644
455 index 018bbd2..0000000
456 --- a/man/glsa-check.1
457 +++ /dev/null
458 @@ -1,66 +0,0 @@
459 -.TH "GLSA-CHECK" "1" "0.3" "Marius Mauch" "gentoolkit"
460 -.SH "NAME"
461 -.LP
462 -glsa\-check \- Gentoo: Tool to locally monitor and manage GLSAs
463 -.SH "SYNTAX"
464 -.LP
465 -glsa\-check <\fIoption\fP> [\fIglsa\-list\fP]
466 -
467 -[\fIglsa\-list\fR] can contain an arbitrary number of GLSA ids, filenames containing GLSAs or the special identifiers 'all' and 'affected'
468 -.SH "DESCRIPTION"
469 -.LP
470 -This tool is used to locally monitor and manage Gentoo Linux Security Advisories.
471 -Please read:
472 -.br
473 -http://www.gentoo.org/security
474 -.br
475 -before reporting a bug.
476 -.LP
477 -Note: In order for this tool to be effective, you must regularly sync your local portage tree.
478 -.SH "OPTIONS"
479 -.LP
480 -.TP
481 -.B \-l, \-\-list
482 -list the a summary for all GLSAs in glsa\-list and whether they affect the system
483 -.TP
484 -.B \-d, \-\-dump, \-\-print
485 -show all information about the GLSAs in glsa\-list
486 -.TP
487 -.B \-t, \-\-test
488 -test if this system is affected by the GLSAs in glsa\-list and output the GLSA IDs
489 -.TP
490 -.B \-p, \-\-pretend
491 -show the necessary steps to apply the GLSAs in glsa\-list
492 -.TP
493 -.B \-f, \-\-fix
494 -try to auto\-apply the GLSAs in in glsa\-list using emerge. This will only upgrade packages to later version, but not remove packages when no upgrade path exists (experimental)
495 -.TP
496 -.B \-i, \-\-inject
497 -inject the given GLSA into the glsa_injected file
498 -.TP
499 -.B \-n, \-\-nocolor
500 -disable colors (option)
501 -.TP
502 -.B \-h, \-\-help
503 -show this help message
504 -.TP
505 -.B \-V, \-\-version
506 -some information about this tool
507 -.TP
508 -.B \-v, \-\-verbose
509 -print more messages (option)
510 -.TP
511 -.B \-c, \-\-cve
512 -show CVE ids in listing mode (option)
513 -.TP
514 -.B \-q, \-\-quiet
515 -be less verbose and do not send empty mail (option)
516 -.TP
517 -.B \-m, \-\-mail
518 -send a mail with the given GLSAs to the administrator
519 -.SH "FILES"
520 -.LP
521 -.TP
522 -.B /var/lib/portage/glsa_injected
523 -List of GLSA ids that have been injected and will never show up as 'affected' on this system.
524 -The file must contain one GLSA id (e.g. '200804\-02') per line.
525 diff --git a/pym/gentoolkit/glsa/__init__.py b/pym/gentoolkit/glsa/__init__.py
526 deleted file mode 100644
527 index db71025..0000000
528 --- a/pym/gentoolkit/glsa/__init__.py
529 +++ /dev/null
530 @@ -1,741 +0,0 @@
531 -# $Header$
532 -
533 -# This program is licensed under the GPL, version 2
534 -
535 -# WARNING: this code is only tested by a few people and should NOT be used
536 -# on production systems at this stage. There are possible security holes and probably
537 -# bugs in this code. If you test it please report ANY success or failure to
538 -# me (genone@g.o).
539 -
540 -# The following planned features are currently on hold:
541 -# - getting GLSAs from http/ftp servers (not really useful without the fixed ebuilds)
542 -# - GPG signing/verification (until key policy is clear)
543 -
544 -from __future__ import unicode_literals
545 -
546 -__author__ = "Marius Mauch <genone@g.o>"
547 -
548 -
549 -import sys
550 -if sys.hexversion < 0x3000000:
551 - from io import open
552 -import os
553 -try:
554 - from urllib import urlopen
555 -except ImportError:
556 - from urllib.request import urlopen
557 -import codecs
558 -import re
559 -import operator
560 -import xml.dom.minidom
561 -from io import StringIO
562 -from functools import reduce
563 -
564 -if sys.version_info[0:2] < (2,3):
565 - raise NotImplementedError("Python versions below 2.3 have broken XML code " \
566 - +"and are not supported")
567 -
568 -try:
569 - import portage
570 - from portage import _encodings, _unicode_encode
571 -except ImportError:
572 - sys.path.insert(0, "/usr/lib/portage/pym")
573 - import portage
574 - from portage import _encodings, _unicode_encode
575 -
576 -
577 -# Note: the space for rgt and rlt is important !!
578 -opMapping = {"le": "<=", "lt": "<", "eq": "=", "gt": ">", "ge": ">=",
579 - "rge": ">=~", "rle": "<=~", "rgt": " >~", "rlt": " <~"}
580 -NEWLINE_ESCAPE = "!;\\n" # some random string to mark newlines that should be preserved
581 -SPACE_ESCAPE = "!;_" # some random string to mark spaces that should be preserved
582 -
583 -def center(text, width):
584 - """
585 - Returns a string containing I{text} that is padded with spaces on both
586 - sides. If C{len(text) >= width} I{text} is returned unchanged.
587 -
588 - @type text: String
589 - @param text: the text to be embedded
590 - @type width: Integer
591 - @param width: the minimum length of the returned string
592 - @rtype: String
593 - @return: the expanded string or I{text}
594 - """
595 - if len(text) >= width:
596 - return text
597 - margin = (width-len(text))//2
598 - rValue = " "*margin
599 - rValue += text
600 - if 2*margin + len(text) == width:
601 - rValue += " "*margin
602 - elif 2*margin + len(text) + 1 == width:
603 - rValue += " "*(margin+1)
604 - return rValue
605 -
606 -
607 -def wrap(text, width, caption=""):
608 - """
609 - Wraps the given text at column I{width}, optionally indenting
610 - it so that no text is under I{caption}. It's possible to encode
611 - hard linebreaks in I{text} with L{NEWLINE_ESCAPE}.
612 -
613 - @type text: String
614 - @param text: the text to be wrapped
615 - @type width: Integer
616 - @param width: the column at which the text should be wrapped
617 - @type caption: String
618 - @param caption: this string is inserted at the beginning of the
619 - return value and the paragraph is indented up to
620 - C{len(caption)}.
621 - @rtype: String
622 - @return: the wrapped and indented paragraph
623 - """
624 - rValue = ""
625 - line = caption
626 - text = text.replace(2*NEWLINE_ESCAPE, NEWLINE_ESCAPE+" "+NEWLINE_ESCAPE)
627 - words = text.split()
628 - indentLevel = len(caption)+1
629 -
630 - for w in words:
631 - if line[-1] == "\n":
632 - rValue += line
633 - line = " "*indentLevel
634 - if len(line)+len(w.replace(NEWLINE_ESCAPE, ""))+1 > width:
635 - rValue += line+"\n"
636 - line = " "*indentLevel+w.replace(NEWLINE_ESCAPE, "\n")
637 - elif w.find(NEWLINE_ESCAPE) >= 0:
638 - if len(line.strip()) > 0:
639 - rValue += line+" "+w.replace(NEWLINE_ESCAPE, "\n")
640 - else:
641 - rValue += line+w.replace(NEWLINE_ESCAPE, "\n")
642 - line = " "*indentLevel
643 - else:
644 - if len(line.strip()) > 0:
645 - line += " "+w
646 - else:
647 - line += w
648 - if len(line) > 0:
649 - rValue += line.replace(NEWLINE_ESCAPE, "\n")
650 - rValue = rValue.replace(SPACE_ESCAPE, " ")
651 - return rValue
652 -
653 -def checkconfig(myconfig):
654 - """
655 - takes a portage.config instance and adds GLSA specific keys if
656 - they are not present. TO-BE-REMOVED (should end up in make.*)
657 - """
658 - mysettings = {
659 - "GLSA_DIR": portage.settings["PORTDIR"]+"/metadata/glsa/",
660 - "GLSA_PREFIX": "glsa-",
661 - "GLSA_SUFFIX": ".xml",
662 - "CHECKFILE": "/var/lib/portage/glsa_injected",
663 - "GLSA_SERVER": "www.gentoo.org/security/en/glsa/", # not completely implemented yet
664 - "CHECKMODE": "local", # not completely implemented yet
665 - "PRINTWIDTH": "76"
666 - }
667 - for k in mysettings.keys():
668 - if k not in myconfig:
669 - myconfig[k] = mysettings[k]
670 - return myconfig
671 -
672 -def get_glsa_list(repository, myconfig):
673 - """
674 - Returns a list of all available GLSAs in the given repository
675 - by comparing the filelist there with the pattern described in
676 - the config.
677 -
678 - @type repository: String
679 - @param repository: The directory or an URL that contains GLSA files
680 - (Note: not implemented yet)
681 - @type myconfig: portage.config
682 - @param myconfig: a GLSA aware config instance (see L{checkconfig})
683 -
684 - @rtype: List of Strings
685 - @return: a list of GLSA IDs in this repository
686 - """
687 - # TODO: remote fetch code for listing
688 -
689 - rValue = []
690 -
691 - if not os.access(repository, os.R_OK):
692 - return []
693 - dirlist = os.listdir(repository)
694 - prefix = myconfig["GLSA_PREFIX"]
695 - suffix = myconfig["GLSA_SUFFIX"]
696 -
697 - for f in dirlist:
698 - try:
699 - if f[:len(prefix)] == prefix and f[-1*len(suffix):] == suffix:
700 - rValue.append(f[len(prefix):-1*len(suffix)])
701 - except IndexError:
702 - pass
703 - return rValue
704 -
705 -def getListElements(listnode):
706 - """
707 - Get all <li> elements for a given <ol> or <ul> node.
708 -
709 - @type listnode: xml.dom.Node
710 - @param listnode: <ul> or <ol> list to get the elements for
711 - @rtype: List of Strings
712 - @return: a list that contains the value of the <li> elements
713 - """
714 - if not listnode.nodeName in ["ul", "ol"]:
715 - raise GlsaFormatException("Invalid function call: listnode is not <ul> or <ol>")
716 - rValue = [getText(li, format="strip") \
717 - for li in listnode.childNodes \
718 - if li.nodeType == xml.dom.Node.ELEMENT_NODE]
719 - return rValue
720 -
721 -def getText(node, format, textfd = None):
722 - """
723 - This is the main parser function. It takes a node and traverses
724 - recursive over the subnodes, getting the text of each (and the
725 - I{link} attribute for <uri> and <mail>). Depending on the I{format}
726 - parameter the text might be formatted by adding/removing newlines,
727 - tabs and spaces. This function is only useful for the GLSA DTD,
728 - it's not applicable for other DTDs.
729 -
730 - @type node: xml.dom.Node
731 - @param node: the root node to start with the parsing
732 - @type format: String
733 - @param format: this should be either I{strip}, I{keep} or I{xml}
734 - I{keep} just gets the text and does no formatting.
735 - I{strip} replaces newlines and tabs with spaces and
736 - replaces multiple spaces with one space.
737 - I{xml} does some more formatting, depending on the
738 - type of the encountered nodes.
739 - @type textfd: writable file-like object
740 - @param textfd: the file-like object to write the output to
741 - @rtype: String
742 - @return: the (formatted) content of the node and its subnodes
743 - except if textfd was not none
744 - """
745 - if not textfd:
746 - textfd = StringIO()
747 - returnNone = False
748 - else:
749 - returnNone = True
750 - if format in ["strip", "keep"]:
751 - if node.nodeName in ["uri", "mail"]:
752 - textfd.write(node.childNodes[0].data+": "+node.getAttribute("link"))
753 - else:
754 - for subnode in node.childNodes:
755 - if subnode.nodeName == "#text":
756 - textfd.write(subnode.data)
757 - else:
758 - getText(subnode, format, textfd)
759 - else: # format = "xml"
760 - for subnode in node.childNodes:
761 - if subnode.nodeName == "p":
762 - for p_subnode in subnode.childNodes:
763 - if p_subnode.nodeName == "#text":
764 - textfd.write(p_subnode.data.strip())
765 - elif p_subnode.nodeName in ["uri", "mail"]:
766 - textfd.write(p_subnode.childNodes[0].data)
767 - textfd.write(" ( "+p_subnode.getAttribute("link")+" )")
768 - textfd.write(NEWLINE_ESCAPE)
769 - elif subnode.nodeName == "ul":
770 - for li in getListElements(subnode):
771 - textfd.write("-"+SPACE_ESCAPE+li+NEWLINE_ESCAPE+" ")
772 - elif subnode.nodeName == "ol":
773 - i = 0
774 - for li in getListElements(subnode):
775 - i = i+1
776 - textfd.write(str(i)+"."+SPACE_ESCAPE+li+NEWLINE_ESCAPE+" ")
777 - elif subnode.nodeName == "code":
778 - textfd.write(getText(subnode, format="keep").lstrip().replace("\n", NEWLINE_ESCAPE))
779 - textfd.write(NEWLINE_ESCAPE)
780 - elif subnode.nodeName == "#text":
781 - textfd.write(subnode.data)
782 - else:
783 - raise GlsaFormatException("Invalid Tag found: ", subnode.nodeName)
784 - if returnNone:
785 - return None
786 - rValue = textfd.getvalue()
787 - if format == "strip":
788 - rValue = rValue.strip(" \n\t")
789 - rValue = re.sub("[\s]{2,}", " ", rValue)
790 - return rValue
791 -
792 -def getMultiTagsText(rootnode, tagname, format):
793 - """
794 - Returns a list with the text of all subnodes of type I{tagname}
795 - under I{rootnode} (which itself is not parsed) using the given I{format}.
796 -
797 - @type rootnode: xml.dom.Node
798 - @param rootnode: the node to search for I{tagname}
799 - @type tagname: String
800 - @param tagname: the name of the tags to search for
801 - @type format: String
802 - @param format: see L{getText}
803 - @rtype: List of Strings
804 - @return: a list containing the text of all I{tagname} childnodes
805 - """
806 - rValue = [getText(e, format) \
807 - for e in rootnode.getElementsByTagName(tagname)]
808 - return rValue
809 -
810 -def makeAtom(pkgname, versionNode):
811 - """
812 - creates from the given package name and information in the
813 - I{versionNode} a (syntactical) valid portage atom.
814 -
815 - @type pkgname: String
816 - @param pkgname: the name of the package for this atom
817 - @type versionNode: xml.dom.Node
818 - @param versionNode: a <vulnerable> or <unaffected> Node that
819 - contains the version information for this atom
820 - @rtype: String
821 - @return: the portage atom
822 - """
823 - rValue = opMapping[versionNode.getAttribute("range")] \
824 - + pkgname \
825 - + "-" + getText(versionNode, format="strip")
826 - try:
827 - slot = versionNode.getAttribute("slot").strip()
828 - except KeyError:
829 - pass
830 - else:
831 - if slot and slot != "*":
832 - rValue += ":" + slot
833 - return str(rValue)
834 -
835 -def makeVersion(versionNode):
836 - """
837 - creates from the information in the I{versionNode} a
838 - version string (format <op><version>).
839 -
840 - @type versionNode: xml.dom.Node
841 - @param versionNode: a <vulnerable> or <unaffected> Node that
842 - contains the version information for this atom
843 - @rtype: String
844 - @return: the version string
845 - """
846 - rValue = opMapping[versionNode.getAttribute("range")] \
847 - +getText(versionNode, format="strip")
848 - try:
849 - slot = versionNode.getAttribute("slot").strip()
850 - except KeyError:
851 - pass
852 - else:
853 - if slot and slot != "*":
854 - rValue += ":" + slot
855 - return rValue
856 -
857 -def match(atom, portdbname, match_type="default"):
858 - """
859 - wrapper that calls revisionMatch() or portage.dbapi.match() depending on
860 - the given atom.
861 -
862 - @type atom: string
863 - @param atom: a <~ or >~ atom or a normal portage atom that contains the atom to match against
864 - @type portdb: portage.dbapi
865 - @param portdb: one of the portage databases to use as information source
866 - @type match_type: string
867 - @param match_type: if != "default" passed as first argument to dbapi.xmatch
868 - to apply the wanted visibility filters
869 -
870 - @rtype: list of strings
871 - @return: a list with the matching versions
872 - """
873 - db = portage.db[portage.root][portdbname].dbapi
874 - if atom[2] == "~":
875 - return revisionMatch(atom, db, match_type=match_type)
876 - elif match_type == "default" or not hasattr(db, "xmatch"):
877 - return db.match(atom)
878 - else:
879 - return db.xmatch(match_type, atom)
880 -
881 -def revisionMatch(revisionAtom, portdb, match_type="default"):
882 - """
883 - handler for the special >~, >=~, <=~ and <~ atoms that are supposed to behave
884 - as > and < except that they are limited to the same version, the range only
885 - applies to the revision part.
886 -
887 - @type revisionAtom: string
888 - @param revisionAtom: a <~ or >~ atom that contains the atom to match against
889 - @type portdb: portage.dbapi
890 - @param portdb: one of the portage databases to use as information source
891 - @type match_type: string
892 - @param match_type: if != "default" passed as first argument to portdb.xmatch
893 - to apply the wanted visibility filters
894 -
895 - @rtype: list of strings
896 - @return: a list with the matching versions
897 - """
898 - if match_type == "default" or not hasattr(portdb, "xmatch"):
899 - if ":" in revisionAtom:
900 - mylist = portdb.match(re.sub(r'-r[0-9]+(:[^ ]+)?$', r'\1', revisionAtom[2:]))
901 - else:
902 - mylist = portdb.match(re.sub("-r[0-9]+$", "", revisionAtom[2:]))
903 - else:
904 - if ":" in revisionAtom:
905 - mylist = portdb.xmatch(match_type, re.sub(r'-r[0-9]+(:[^ ]+)?$', r'\1', revisionAtom[2:]))
906 - else:
907 - mylist = portdb.xmatch(match_type, re.sub("-r[0-9]+$", "", revisionAtom[2:]))
908 - rValue = []
909 - for v in mylist:
910 - r1 = portage.pkgsplit(v)[-1][1:]
911 - r2 = portage.pkgsplit(revisionAtom[3:])[-1][1:]
912 - if eval(r1+" "+revisionAtom[0:2]+" "+r2):
913 - rValue.append(v)
914 - return rValue
915 -
916 -
917 -def getMinUpgrade(vulnerableList, unaffectedList, minimize=True):
918 - """
919 - Checks if the systemstate is matching an atom in
920 - I{vulnerableList} and returns string describing
921 - the lowest version for the package that matches an atom in
922 - I{unaffectedList} and is greater than the currently installed
923 - version. It will return an empty list if the system is affected,
924 - and no upgrade is possible or None if the system is not affected.
925 - Both I{vulnerableList} and I{unaffectedList} should have the
926 - same base package.
927 -
928 - @type vulnerableList: List of Strings
929 - @param vulnerableList: atoms matching vulnerable package versions
930 - @type unaffectedList: List of Strings
931 - @param unaffectedList: atoms matching unaffected package versions
932 - @type minimize: Boolean
933 - @param minimize: True for a least-change upgrade, False for emerge-like algorithm
934 -
935 - @rtype: String | None
936 - @return: the lowest unaffected version that is greater than
937 - the installed version.
938 - """
939 - v_installed = reduce(operator.add, [match(v, "vartree") for v in vulnerableList], [])
940 - u_installed = reduce(operator.add, [match(u, "vartree") for u in unaffectedList], [])
941 -
942 - # remove all unaffected atoms from vulnerable list
943 - v_installed = list(set(v_installed).difference(set(u_installed)))
944 -
945 - if not v_installed:
946 - return None
947 -
948 - # this tuple holds all vulnerable atoms, and the related upgrade atom
949 - vuln_update = []
950 - avail_updates = set()
951 - for u in unaffectedList:
952 - # TODO: This had match_type="match-all" before. I don't think it should
953 - # since we disregarded masked items later anyway (match(=rValue, "porttree"))
954 - avail_updates.update(match(u, "porttree"))
955 - # if an atom is already installed, we should not consider it for upgrades
956 - avail_updates.difference_update(u_installed)
957 -
958 - for vuln in v_installed:
959 - update = ""
960 - for c in avail_updates:
961 - c_pv = portage.catpkgsplit(c)
962 - i_pv = portage.catpkgsplit(vuln)
963 - if portage.pkgcmp(c_pv[1:], i_pv[1:]) > 0 \
964 - and (update == "" \
965 - or (minimize ^ (portage.pkgcmp(c_pv[1:], portage.catpkgsplit(update)[1:]) > 0))) \
966 - and portage.db[portage.root]["porttree"].dbapi.aux_get(c, ["SLOT"]) == portage.db[portage.root]["vartree"].dbapi.aux_get(vuln, ["SLOT"]):
967 - update = c_pv[0]+"/"+c_pv[1]+"-"+c_pv[2]
968 - if c_pv[3] != "r0": # we don't like -r0 for display
969 - update += "-"+c_pv[3]
970 - vuln_update.append([vuln, update])
971 -
972 - return vuln_update
973 -
974 -def format_date(datestr):
975 - """
976 - Takes a date (announced, revised) date from a GLSA and formats
977 - it as readable text (i.e. "January 1, 2008").
978 -
979 - @type date: String
980 - @param date: the date string to reformat
981 - @rtype: String
982 - @return: a reformatted string, or the original string
983 - if it cannot be reformatted.
984 - """
985 - splitdate = datestr.split("-", 2)
986 - if len(splitdate) != 3:
987 - return datestr
988 -
989 - # This cannot raise an error as we use () instead of []
990 - splitdate = (int(x) for x in splitdate)
991 -
992 - from datetime import date
993 - try:
994 - d = date(*splitdate)
995 - except ValueError:
996 - return datestr
997 -
998 - # TODO We could format to local date format '%x' here?
999 - return d.strftime("%B %d, %Y")
1000 -
1001 -# simple Exception classes to catch specific errors
1002 -class GlsaTypeException(Exception):
1003 - def __init__(self, doctype):
1004 - Exception.__init__(self, "wrong DOCTYPE: %s" % doctype)
1005 -
1006 -class GlsaFormatException(Exception):
1007 - pass
1008 -
1009 -class GlsaArgumentException(Exception):
1010 - pass
1011 -
1012 -# GLSA xml data wrapper class
1013 -class Glsa:
1014 - """
1015 - This class is a wrapper for the XML data and provides methods to access
1016 - and display the contained data.
1017 - """
1018 - def __init__(self, myid, myconfig):
1019 - """
1020 - Simple constructor to set the ID, store the config and gets the
1021 - XML data by calling C{self.read()}.
1022 -
1023 - @type myid: String
1024 - @param myid: String describing the id for the GLSA object (standard
1025 - GLSAs have an ID of the form YYYYMM-nn) or an existing
1026 - filename containing a GLSA.
1027 - @type myconfig: portage.config
1028 - @param myconfig: the config that should be used for this object.
1029 - """
1030 - if re.match(r'\d{6}-\d{2}', myid):
1031 - self.type = "id"
1032 - elif os.path.exists(myid):
1033 - self.type = "file"
1034 - else:
1035 - raise GlsaArgumentException("Given ID "+myid+" isn't a valid GLSA ID or filename.")
1036 - self.nr = myid
1037 - self.config = myconfig
1038 - self.read()
1039 -
1040 - def read(self):
1041 - """
1042 - Here we build the filename from the config and the ID and pass
1043 - it to urllib to fetch it from the filesystem or a remote server.
1044 -
1045 - @rtype: None
1046 - @return: None
1047 - """
1048 - if self.config["CHECKMODE"] == "local":
1049 - repository = "file://" + self.config["GLSA_DIR"]
1050 - else:
1051 - repository = self.config["GLSA_SERVER"]
1052 - if self.type == "file":
1053 - myurl = "file://"+self.nr
1054 - else:
1055 - myurl = repository + self.config["GLSA_PREFIX"] + str(self.nr) + self.config["GLSA_SUFFIX"]
1056 - self.parse(urlopen(myurl))
1057 - return None
1058 -
1059 - def parse(self, myfile):
1060 - """
1061 - This method parses the XML file and sets up the internal data
1062 - structures by calling the different helper functions in this
1063 - module.
1064 -
1065 - @type myfile: String
1066 - @param myfile: Filename to grab the XML data from
1067 - @rtype: None
1068 - @returns: None
1069 - """
1070 - self.DOM = xml.dom.minidom.parse(myfile)
1071 - if not self.DOM.doctype:
1072 - raise GlsaTypeException(None)
1073 - elif self.DOM.doctype.systemId == "http://www.gentoo.org/dtd/glsa.dtd":
1074 - self.dtdversion = 0
1075 - elif self.DOM.doctype.systemId == "http://www.gentoo.org/dtd/glsa-2.dtd":
1076 - self.dtdversion = 2
1077 - else:
1078 - raise GlsaTypeException(self.DOM.doctype.systemId)
1079 - myroot = self.DOM.getElementsByTagName("glsa")[0]
1080 - if self.type == "id" and myroot.getAttribute("id") != self.nr:
1081 - raise GlsaFormatException("filename and internal id don't match:" + myroot.getAttribute("id") + " != " + self.nr)
1082 -
1083 - # the simple (single, required, top-level, #PCDATA) tags first
1084 - self.title = getText(myroot.getElementsByTagName("title")[0], format="strip")
1085 - self.synopsis = getText(myroot.getElementsByTagName("synopsis")[0], format="strip")
1086 - self.announced = format_date(getText(myroot.getElementsByTagName("announced")[0], format="strip"))
1087 -
1088 - # Support both formats of revised:
1089 - # <revised>December 30, 2007: 02</revised>
1090 - # <revised count="2">2007-12-30</revised>
1091 - revisedEl = myroot.getElementsByTagName("revised")[0]
1092 - self.revised = getText(revisedEl, format="strip")
1093 - count = revisedEl.getAttribute("count")
1094 - if not count:
1095 - if self.revised.find(":") >= 0:
1096 - (self.revised, count) = self.revised.split(":")
1097 - else:
1098 - count = 1
1099 -
1100 - self.revised = format_date(self.revised)
1101 -
1102 - try:
1103 - self.count = int(count)
1104 - except ValueError:
1105 - # TODO should this rais a GlsaFormatException?
1106 - self.count = 1
1107 -
1108 - # now the optional and 0-n toplevel, #PCDATA tags and references
1109 - try:
1110 - self.access = getText(myroot.getElementsByTagName("access")[0], format="strip")
1111 - except IndexError:
1112 - self.access = ""
1113 - self.bugs = getMultiTagsText(myroot, "bug", format="strip")
1114 - self.references = getMultiTagsText(myroot.getElementsByTagName("references")[0], "uri", format="keep")
1115 -
1116 - # and now the formatted text elements
1117 - self.description = getText(myroot.getElementsByTagName("description")[0], format="xml")
1118 - self.workaround = getText(myroot.getElementsByTagName("workaround")[0], format="xml")
1119 - self.resolution = getText(myroot.getElementsByTagName("resolution")[0], format="xml")
1120 - self.impact_text = getText(myroot.getElementsByTagName("impact")[0], format="xml")
1121 - self.impact_type = myroot.getElementsByTagName("impact")[0].getAttribute("type")
1122 - try:
1123 - self.background = getText(myroot.getElementsByTagName("background")[0], format="xml")
1124 - except IndexError:
1125 - self.background = ""
1126 -
1127 - # finally the interesting tags (product, affected, package)
1128 - self.glsatype = myroot.getElementsByTagName("product")[0].getAttribute("type")
1129 - self.product = getText(myroot.getElementsByTagName("product")[0], format="strip")
1130 - self.affected = myroot.getElementsByTagName("affected")[0]
1131 - self.packages = {}
1132 - for p in self.affected.getElementsByTagName("package"):
1133 - name = p.getAttribute("name")
1134 - if name not in self.packages:
1135 - self.packages[name] = []
1136 - tmp = {}
1137 - tmp["arch"] = p.getAttribute("arch")
1138 - tmp["auto"] = (p.getAttribute("auto") == "yes")
1139 - tmp["vul_vers"] = [makeVersion(v) for v in p.getElementsByTagName("vulnerable")]
1140 - tmp["unaff_vers"] = [makeVersion(v) for v in p.getElementsByTagName("unaffected")]
1141 - tmp["vul_atoms"] = [makeAtom(name, v) for v in p.getElementsByTagName("vulnerable")]
1142 - tmp["unaff_atoms"] = [makeAtom(name, v) for v in p.getElementsByTagName("unaffected")]
1143 - self.packages[name].append(tmp)
1144 - # TODO: services aren't really used yet
1145 - self.services = self.affected.getElementsByTagName("service")
1146 - return None
1147 -
1148 - def dump(self, outstream=sys.stdout, encoding="utf-8"):
1149 - """
1150 - Dumps a plaintext representation of this GLSA to I{outfile} or
1151 - B{stdout} if it is ommitted. You can specify an alternate
1152 - I{encoding} if needed (default is utf-8).
1153 -
1154 - @type outstream: File
1155 - @param outfile: Stream that should be used for writing
1156 - (defaults to sys.stdout)
1157 - """
1158 - outstream = getattr(outstream, "buffer", outstream)
1159 - outstream = codecs.getwriter(encoding)(outstream)
1160 - width = int(self.config["PRINTWIDTH"])
1161 - outstream.write(center("GLSA %s: \n%s" % (self.nr, self.title), width)+"\n")
1162 - outstream.write((width*"=")+"\n")
1163 - outstream.write(wrap(self.synopsis, width, caption="Synopsis: ")+"\n")
1164 - outstream.write("Announced on: %s\n" % self.announced)
1165 - outstream.write("Last revised on: %s : %02d\n\n" % (self.revised, self.count))
1166 - if self.glsatype == "ebuild":
1167 - for k in self.packages.keys():
1168 - pkg = self.packages[k]
1169 - for path in pkg:
1170 - vul_vers = ", ".join(path["vul_vers"])
1171 - unaff_vers = ", ".join(path["unaff_vers"])
1172 - outstream.write("Affected package: %s\n" % k)
1173 - outstream.write("Affected archs: ")
1174 - if path["arch"] == "*":
1175 - outstream.write("All\n")
1176 - else:
1177 - outstream.write("%s\n" % path["arch"])
1178 - outstream.write("Vulnerable: %s\n" % vul_vers)
1179 - outstream.write("Unaffected: %s\n\n" % unaff_vers)
1180 - elif self.glsatype == "infrastructure":
1181 - pass
1182 - if len(self.bugs) > 0:
1183 - outstream.write("\nRelated bugs: ")
1184 - outstream.write(", ".join(self.bugs))
1185 - outstream.write("\n")
1186 - if self.background:
1187 - outstream.write("\n"+wrap(self.background, width, caption="Background: "))
1188 - outstream.write("\n"+wrap(self.description, width, caption="Description: "))
1189 - outstream.write("\n"+wrap(self.impact_text, width, caption="Impact: "))
1190 - outstream.write("\n"+wrap(self.workaround, width, caption="Workaround: "))
1191 - outstream.write("\n"+wrap(self.resolution, width, caption="Resolution: "))
1192 - myreferences = " ".join(r.replace(" ", SPACE_ESCAPE)+NEWLINE_ESCAPE for r in self.references)
1193 - outstream.write("\n"+wrap(myreferences, width, caption="References: "))
1194 - outstream.write("\n")
1195 -
1196 - def isVulnerable(self):
1197 - """
1198 - Tests if the system is affected by this GLSA by checking if any
1199 - vulnerable package versions are installed. Also checks for affected
1200 - architectures.
1201 -
1202 - @rtype: Boolean
1203 - @returns: True if the system is affected, False if not
1204 - """
1205 - rValue = False
1206 - for k in self.packages.keys():
1207 - pkg = self.packages[k]
1208 - for path in pkg:
1209 - if path["arch"] == "*" or self.config["ARCH"] in path["arch"].split():
1210 - for v in path["vul_atoms"]:
1211 - rValue = rValue \
1212 - or (None != getMinUpgrade([v,], path["unaff_atoms"]))
1213 - return rValue
1214 -
1215 - def isInjected(self):
1216 - """
1217 - Looks if the GLSA ID is in the GLSA checkfile to check if this
1218 - GLSA should be marked as applied.
1219 -
1220 - @rtype: Boolean
1221 - @returns: True if the GLSA is in the inject file, False if not
1222 - """
1223 - if not os.access(self.config["CHECKFILE"], os.R_OK):
1224 - return False
1225 - aList = portage.grabfile(self.config["CHECKFILE"])
1226 - return (self.nr in aList)
1227 -
1228 - def inject(self):
1229 - """
1230 - Puts the ID of this GLSA into the GLSA checkfile, so it won't
1231 - show up on future checks. Should be called after a GLSA is
1232 - applied or on explicit user request.
1233 -
1234 - @rtype: None
1235 - @returns: None
1236 - """
1237 - if not self.isInjected():
1238 - checkfile = open(_unicode_encode(self.config["CHECKFILE"],
1239 - encoding=_encodings['fs']), mode="a+",
1240 - encoding=_encodings['content'])
1241 - checkfile.write(self.nr+"\n")
1242 - checkfile.close()
1243 - return None
1244 -
1245 - def getMergeList(self, least_change=True):
1246 - """
1247 - Returns the list of package-versions that have to be merged to
1248 - apply this GLSA properly. The versions are as low as possible
1249 - while avoiding downgrades (see L{getMinUpgrade}).
1250 -
1251 - @type least_change: Boolean
1252 - @param least_change: True if the smallest possible upgrade should be selected,
1253 - False for an emerge-like algorithm
1254 - @rtype: List of Strings
1255 - @return: list of package-versions that have to be merged
1256 - """
1257 - return list(set(update for (vuln, update) in self.getAffectionTable(least_change) if update))
1258 -
1259 - def getAffectionTable(self, least_change=True):
1260 - """
1261 - Will initialize the self.systemAffection list of
1262 - atoms installed on the system that are affected
1263 - by this GLSA, and the atoms that are minimal upgrades.
1264 - """
1265 - systemAffection = []
1266 - for pkg in self.packages.keys():
1267 - for path in self.packages[pkg]:
1268 - update = getMinUpgrade(path["vul_atoms"], path["unaff_atoms"], minimize=least_change)
1269 - if update:
1270 - systemAffection.extend(update)
1271 - return systemAffection
1272 diff --git a/setup.py b/setup.py
1273 index 031afd3..777b7f3 100755
1274 --- a/setup.py
1275 +++ b/setup.py
1276 @@ -42,7 +42,6 @@ python_scripts = [(os.path.join(cwd, path), '__version__ = ') for path in (
1277 'bin/eclean-dist',
1278 'bin/eclean-pkg',
1279 'bin/epkginfo',
1280 - 'bin/glsa-check',
1281 'pym/gentoolkit/eclean/cli.py',
1282 'pym/gentoolkit/enalyze/__init__.py',
1283 'pym/gentoolkit/ekeyword/ekeyword.py',
1284 @@ -61,7 +60,6 @@ manpages = [(os.path.join(cwd, path[0]), path[1]) for path in (
1285 ('man/eread.1', 'EREAD'),
1286 ('man/eshowkw.1', 'ESHOWKW'),
1287 ('man/euse.1', 'EUSE'),
1288 - ('man/glsa-check.1', 'GLSA-CHECK'),
1289 ('man/imlate.1', 'IMLATE'),
1290 ('man/revdep-rebuild.1', 'REVDEP-REBUILD'),
1291 )]
1292 --
1293 2.21.0

Replies