Gentoo Archives: gentoo-commits

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