diff options
| author | genone <genone@gentoo.org> | 2004-03-17 11:56:00 +0000 |
|---|---|---|
| committer | genone <genone@gentoo.org> | 2004-03-17 11:56:00 +0000 |
| commit | 92cdf5ce4a48233f681a45871ff3644292013245 (patch) | |
| tree | c14e3e111a66a02bb227998002f2cfa03826b2d0 /trunk/src | |
| parent | 97d0ca56c28b81fc8534dc09a4d6b25775218ec0 (diff) | |
| download | gentoolkit-92cdf5ce4a48233f681a45871ff3644292013245.tar.gz | |
oops, forgot to commit
svn path=/; revision=92
Diffstat (limited to 'trunk/src')
| -rw-r--r-- | trunk/src/glsa-check/Makefile | 18 | ||||
| -rw-r--r-- | trunk/src/glsa-check/glsa-check | 205 | ||||
| -rw-r--r-- | trunk/src/glsa-check/glsa.py | 520 |
3 files changed, 743 insertions, 0 deletions
diff --git a/trunk/src/glsa-check/Makefile b/trunk/src/glsa-check/Makefile new file mode 100644 index 0000000..126425c --- /dev/null +++ b/trunk/src/glsa-check/Makefile @@ -0,0 +1,18 @@ +# Copyright 2003 Karl Trygve Kalleberg <karltk@gentoo.org> +# Copyright 2003 Gentoo Technologies, Inc. +# Distributed under the terms of the GNU General Public License v2 +# +# $Header$ + +include ../../makedefs.mak + +all: + echo "YADDLETHORPE (vb.) (Of offended pooves.) To exit huffily from a boutique." + +dist: + mkdir -p ../../$(distdir)/src/glsa-check/ + cp {Makefile,glsa.py,glsa-check} ../../$(distdir)/src/glsa-check/ + +install: + install -m 0755 glsa-check $(bindir)/ + install -m 0644 glsa.py $(DESTDIR)/usr/lib/gentoolkit/pym/ diff --git a/trunk/src/glsa-check/glsa-check b/trunk/src/glsa-check/glsa-check new file mode 100644 index 0000000..d59ec08 --- /dev/null +++ b/trunk/src/glsa-check/glsa-check @@ -0,0 +1,205 @@ +#!/usr/bin/python + +# $Header$ +# This program is licensed under the GPL, version 2 + +import os,string,sys +from getopt import getopt,GetoptError + +__program__ = "glsa-check" +__author__ = "Marius Mauch <genone@gentoo.org>" +__version__ = "0.4" + +optionmap = [ +["-l", "--list", "list all unapplied GLSA"], +["-d", "--dump", "--print", "show all information about the given GLSA"], +["-t", "--test", "test if this system is affected by the given GLSA"], +["-p", "--pretend", "show the necessary commands to apply this GLSA"], +["-f", "--fix", "try to auto-apply this GLSA (experimental)"], +["-i", "--inject", "inject the given GLSA into the checkfile"], +["-h", "--help", "show this help message"], +["-v", "--version", "some information about this tool"] +] + +# print a warning as this is beta code +sys.stderr.write("WARNING: This tool is completely new and not very tested, so it should not be\n") +sys.stderr.write("used on production systems. It's mainly a test tool for the new GLSA release\n") +sys.stderr.write("and distribution system, it's functionality will later be merged into emerge\n") +sys.stderr.write("and equery.\n") +sys.stderr.write("Please read http://www.gentoo.org/proj/en/portage/glsa-integration.xml\n") +sys.stderr.write("before using this tool AND before reporting a bug.\n\n") + +# option parsing +args = [] +params = [] +try: + args, params = getopt(sys.argv[1:], "dplfchivt", \ + ["dump", "print", "list", "pretend", "fix", "inject", "help", "info", "version", "test"]) + args = [a for a,b in args] + + # sanity checking + if len(args) <= 0: + print "no option given: what should I do ?" + mode="help" + elif len(args) > 1: + print "please use only one option per call" + mode = "help" + else: + # in what mode are we ? + args = args[0] + for m in optionmap: + if args in [o for o in m[:-1]]: + mode = m[1][2:] +except GetoptError: + print "unknown option given" + mode = "help" + +# we need a set of glsa for most operation modes +if len(params) <= 0 and mode in ["fix", "test", "pretend", "dump", "inject"]: + print + print "no GLSA given, so we'll do nothing for now. " + print "If you want to run on all GLSA please tell me so " + print "(specify \"all\" as parameter)" + print + mode = "help" +elif len(params) <= 0 and mode == "list": + params.append("new") + +# show help message +if mode == "help": + print + print "Syntax: glsa-check <option> [glsa-list]" + print + for m in optionmap: + print m[0] + "\t" + m[1] + " \t: " + m[-1] + for o in m[2:-1]: + print "\t" + o + print + print "glsa-list can contain an arbitrary number of GLSA ids " + print "or the special identifiers 'all' and 'new'" + print + sys.exit(1) + +# we need root priviledges for write access +if mode in ["fix", "inject"] and os.geteuid() != 0: + print + print "This tool needs root access to "+mode+" this GLSA" + print + sys.exit(2) + +# show version and copyright information +if mode == "version": + print + print __program__ + ", version " + __version__ + print "Author: " + __author__ + print "This program is licensed under the GPL, version 2" + print + sys.exit(0) + +# delay this for speed increase +sys.path.insert(0, "/usr/lib/gentoolkit/pym") +from glsa import * + +glsaconfig = checkconfig(portage.config(clone=portage.settings)) + +# build glsa lists +completelist = get_glsa_list(glsaconfig["GLSA_DIR"], glsaconfig) + +if os.access(glsaconfig["CHECKFILE"], os.R_OK): + checklist = [line.strip() for line in open(glsaconfig["CHECKFILE"], "r").readlines()] +else: + checklist = [] +todolist = [e for e in completelist if e not in checklist] + +glsalist = [] +if "new" in params: + glsalist = todolist + params.remove("new") +if "all" in params: + glsalist = completelist + params.remove("all") +glsalist.extend([g for g in params if g not in glsalist]) + +# list short information for given or new GLSA +if mode == "list": + print "[A] means this GLSA was already applied," + print "[U] means the system is not affected and" + print "[N] indicates that the system might be affected." + print + for myid in glsalist: + try: + myglsa = Glsa(myid, glsaconfig) + except GlsaTypeException, e: + #print "invalid GLSA: %s (error message was: %s)" % (myid, e) + continue + if myglsa.isApplied(): + status = "[A]" + elif myglsa.isVulnerable(): + status = "[N]" + else: + status = "[U]" + print myglsa.nr, status, myglsa.title, "(", + for pkg in myglsa.packages.keys()[:3]: + print pkg, + if len(myglsa.packages) > 3: + print "...", + print ")" + sys.exit(0) + +# dump, fix, inject and fix are nearly the same code, only the glsa method call differs +if mode in ["dump", "fix", "inject", "pretend"]: + for myid in glsalist: + myglsa = Glsa(myid, glsaconfig) + if mode == "dump": + myglsa.dump() + elif mode == "fix": + print "fixing "+myid + mergelist = myglsa.getMergeList() + for pkg in mergelist: + print ">>> merging "+pkg + # using emerge for the actual merging as it contains the dependency + # code and we want to be consistent in behaviour. Also this functionality + # will be integrated in emerge later, so it shouldn't hurt much. + exitcode = os.system("emerge ="+pkg) + if exitcode: + sys.exit(exitcode) + myglsa.inject() + elif mode == "pretend": + mergelist = myglsa.getMergeList() + print "The following updates will be performed for this GLSA:" + for pkg in mergelist: + # we simplify a bit here + oldver = portage.db["/"]["vartree"].dbapi.match(portage.dep_getkey(pkg))[-1] + oldver = oldver[len(portage.dep_getkey(oldver))+1:] + print " ", pkg, "("+oldver+")" + elif mode == "inject": + print "injecting " + myid + myglsa.inject() + if glsalist[-1] != myid: + print + print 70*'*' + print + sys.exit(0) + +# test is a bit different as Glsa.test() produces no output +if mode == "test": + outputlist = [] + for myid in glsalist: + try: + myglsa = Glsa(myid, glsaconfig) + except GlsaTypeException, e: + #print "invalid GLSA: %s (error message was: %s)" % (myid, e) + continue + if myglsa.isVulnerable(): + outputlist.append(myglsa.nr) + if len(outputlist) > 0: + print "This system is affected by the following GLSA:" + for g in outputlist: + print g + else: + print "This system is not affected by any of the listed GLSA" + sys.exit(0) + +# something wrong here, all valid paths are covered with sys.exit() +print "nothing more to do" +sys.exit(2) diff --git a/trunk/src/glsa-check/glsa.py b/trunk/src/glsa-check/glsa.py new file mode 100644 index 0000000..4848a89 --- /dev/null +++ b/trunk/src/glsa-check/glsa.py @@ -0,0 +1,520 @@ +# $Header$ + +# This program is licensed under the GPL, version 2 + +# WARNING: this code is only tested by a few people and should NOT be used +# on production systems at this stage. There are possible security holes and probably +# bugs in this code. If you test it please report ANY success or failure to +# me (genone@gentoo.org). + +# The following planned features are currently on hold: +# - getting GLSAs from http/ftp servers (not really useful without the fixed ebuilds) +# - GPG signing/verification (until key policy is clear) + +__author__ = "Marius Mauch <genone@gentoo.org>" + +import os, sys, urllib, time, string, portage, codecs, re +import xml.dom.minidom + +sys.path.insert(0, "/usr/lib/portage/pym") # to find portage.py + +opMapping = {"le": "<=", "lt": "<", "eq": "=", "gt": ">", "ge": ">="} +NEWLINE_ESCAPE = "!;\\n" # some random string to mark newlines that should be preserved + +def center(text, width): + """ + Returns a string containing I{text} that is padded with spaces on both + sides. If C{len(text) >= width} I{text} is returned unchanged. + + @type text: String + @param text: the text to be embedded + @type width: Integer + @param width: the minimum length of the returned string + @rtype: String + @return: the expanded string or I{text} + """ + if len(text) >= width: + return text + margin = (width-len(text))/2 + rValue = " "*margin + rValue += text + if 2*margin + len(text) == width: + rValue += " "*margin + elif 2*margin + len(text) + 1 == width: + rValue += " "*(margin+1) + return rValue + + +def wrap(text, width, caption=""): + """ + Wraps the given text at column I{width}, optionally indenting + it so that no text is under I{caption}. It's possible to encode + hard linebreaks in I{text} with L{NEWLINE_ESCAPE}. + + @type text: String + @param text: the text to be wrapped + @type width: Integer + @param width: the column at which the text should be wrapped + @type caption: String + @param caption: this string is inserted at the beginning of the + return value and the paragraph is indented up to + C{len(caption)}. + @rtype: String + @return: the wrapped and indented paragraph + """ + rValue = "" + line = caption + words = text.split() + indentLevel = len(caption)+1 + for w in words: + if len(line)+len(w)+1 > width: + rValue += line+"\n" + line = " "*indentLevel+w + elif w.find(NEWLINE_ESCAPE) >= 0: + if len(line.strip()) > 0: + rValue += line+" "+w.replace(NEWLINE_ESCAPE, "\n") + else: + rValue += line+w.replace(NEWLINE_ESCAPE, "\n") + line = " "*indentLevel + else: + if len(line.strip()) > 0: + line += " "+w + else: + line += w + if len(line) > 0: + rValue += line.replace(NEWLINE_ESCAPE, "\n") + return rValue + +def checkconfig(myconfig): + """ + takes a portage.config instance and adds GLSA specific keys if + they are not present. TO-BE-REMOVED (should end up in make.*) + """ + mysettings = { + "GLSA_DIR": portage.settings["PORTDIR"]+"/metadata/glsa/", + "GLSA_PREFIX": "glsa-", + "GLSA_SUFFIX": ".xml", + "CHECKFILE": "/var/cache/edb/glsa", + "GLSA_SERVER": "www.gentoo.org/security/en/glsa/", # not completely implemented yet + "CHECKMODE": "local", # not completely implemented yet + "PRINTWIDTH": "76" + } + for k in mysettings.keys(): + if not myconfig.has_key(k): + myconfig[k] = mysettings[k] + return myconfig + +def get_glsa_list(repository, myconfig): + """ + Returns a list of all available GLSAs in the given repository + by comparing the filelist there with the pattern described in + the config. + + @type repository: String + @param repository: The directory or an URL that contains GLSA files + (Note: not implemented yet) + @type myconfig: portage.config + @param myconfig: a GLSA aware config instance (see L{checkconfig}) + + @rtype: List of Strings + @return: a list of GLSA IDs in this repository + """ + # TODO: remote fetch code for listing + + rValue = [] + + if not os.access(repository, os.R_OK): + return [] + dirlist = os.listdir(repository) + prefix = myconfig["GLSA_PREFIX"] + suffix = myconfig["GLSA_SUFFIX"] + + for f in dirlist: + try: + if f[:len(prefix)] == prefix: + rValue.append(f[len(prefix):-1*len(suffix)]) + except IndexError: + pass + return rValue + +def getListElements(listnode): + """ + Get all <li> elements for a given <ol> or <ul> node. + + @type listnode: xml.dom.Node + @param listnode: <ul> or <ol> list to get the elements for + @rtype: List of Strings + @return: a list that contains the value of the <li> elements + """ + rValue = [] + if not listnode.nodeName in ["ul", "ol"]: + raise GlsaFormatException("Invalid function call: listnode is not <ul> or <ol>") + for li in listnode.childNodes: + rValue.append(getText(li, format="strip")) + return rValue + +def getText(node, format): + """ + This is the main parser function. It takes a node and traverses + recursive over the subnodes, getting the text of each (and the + I{link} attribute for <uri> and <mail>). Depending on the I{format} + parameter the text might be formatted by adding/removing newlines, + tabs and spaces. This function is only useful for the GLSA DTD, + it's not applicable for other DTDs. + + @type node: xml.dom.Node + @param node: the root node to start with the parsing + @type format: String + @param format: this should be either I{strip}, I{keep} or I{xml} + I{keep} just gets the text and does no formatting. + I{strip} replaces newlines and tabs with spaces and + replaces multiple spaces with one space. + I{xml} does some more formatting, depending on the + type of the encountered nodes. + @rtype: String + @return: the (formatted) content of the node and its subnodes + """ + rValue = "" + if format in ["strip", "keep"]: + if node.nodeName in ["uri", "mail"]: + rValue += node.childNodes[0].data+": "+node.getAttribute("link") + else: + for subnode in node.childNodes: + if subnode.nodeName == "#text": + rValue += subnode.data + else: + rValue += getText(subnode, format) + else: + for subnode in node.childNodes: + if subnode.nodeName == "p": + for p_subnode in subnode.childNodes: + if p_subnode.nodeName == "#text": + rValue += p_subnode.data + elif p_subnode.nodeName in ["uri", "mail"]: + rValue += p_subnode.childNodes[0].data + rValue += " ( "+p_subnode.getAttribute("link")+" )" + rValue += NEWLINE_ESCAPE + elif subnode.nodeName == "ul": + for li in getListElements(subnode): + rValue += "- "+li+NEWLINE_ESCAPE+" " + elif subnode.nodeName == "ol": + i = 0 + for li in getListElements(subnode): + i = i+1 + rValue += str(i)+". "+li+NEWLINE_ESCAPE+" " + elif subnode.nodeName == "code": + rValue += getText(subnode, format="keep").replace("\n", NEWLINE_ESCAPE) + elif subnode.nodeName == "#text": + rValue += subnode.data + else: + raise GlsaFormatException("Invalid Tag found: ", subnode.nodeName) + if format == "strip": + rValue = rValue.strip(" \n\t") + rValue = re.sub("[\s]{2,}", " ", rValue) + return rValue + +def getMultiTagsText(rootnode, tagname, format): + """ + Returns a list with the text of all subnodes of type I{tagname} + under I{rootnode} (which itself is not parsed) using the given I{format}. + + @type rootnode: xml.dom.Node + @param rootnode: the node to search for I{tagname} + @type tagname: String + @param tagname: the name of the tags to search for + @type format: String + @param format: see L{getText} + @rtype: List of Strings + @return: a list containing the text of all I{tagname} childnodes + """ + rValue = [] + for e in rootnode.getElementsByTagName(tagname): + rValue.append(getText(e, format)) + return rValue + +def makeAtom(pkgname, versionNode): + """ + creates from the given package name and information in the + I{versionNode} a (syntactical) valid portage atom. + + @type pkgname: String + @param pkgname: the name of the package for this atom + @type versionNode: xml.dom.Node + @param versionNode: a <vulnerable> or <unaffected> Node that + contains the version information for this atom + @rtype: String + @return: the portage atom + """ + return opMapping[versionNode.getAttribute("range")] \ + +pkgname \ + +"-"+getText(versionNode, format="strip") + +def makeVersion(versionNode): + """ + creates from the information in the I{versionNode} a + version string (format <op><version>). + + @type versionNode: xml.dom.Node + @param versionNode: a <vulnerable> or <unaffected> Node that + contains the version information for this atom + @rtype: String + @return: the version string + """ + return opMapping[versionNode.getAttribute("range")] \ + +getText(versionNode, format="strip") + +def getMinUpgrade(vulnerableList, unaffectedList): + """ + Checks if the systemstate is matching an atom in + I{vulnerableList} and returns string describing + the lowest version for the package that matches an atom in + I{unaffectedList} and is greater than the currently installed + version or None if the system is not affected. Both + I{vulnerableList} and I{unaffectedList} should have the + same base package. + + @type vulnerableList: List of Strings + @param vulnerableList: atoms matching vulnerable package versions + @type unaffectedList: List of Strings + @param unaffectedList: atoms matching unaffected package versions + @rtype: String | None + @return: the lowest unaffected version that is greater than + the installed version. + """ + rValue = None + for v in vulnerableList: + installed = portage.db["/"]["vartree"].dbapi.match(v) + if not installed: + continue + for u in unaffectedList: + for c in portage.db["/"]["porttree"].dbapi.match(u): + c_pv = portage.catpkgsplit(c) + i_pv = portage.catpkgsplit(portage.best(installed)) + if portage.pkgcmp(c_pv[1:], i_pv[1:]) > 0 and (rValue == None or portage.pkgcmp(c_pv[1:], rValue) < 0): + rValue = c_pv[0]+"/"+c_pv[1]+"-"+c_pv[2] + if c_pv[3] != "r0": # we don't like -r0 for display + rValue += "-"+c_pv[3] + return rValue + +# simple Exception classes to catch specific errors +class GlsaTypeException(Exception): + def __init__(self, doctype): + Exception.__init__(self, "wrong DOCTYPE: %s" % doctype) + +class GlsaFormatExceptio(Exception): + pass + +# GLSA xml data wrapper class +class Glsa: + """ + This class is a wrapper for the XML data and provides methods to access + and display the contained data. + """ + def __init__(self, myid, myconfig): + """ + Simple constructor to set the ID, store the config and gets the + XML data by calling C{self.read()}. + + @type myid: String + @param myid: String describing the id for the GLSA object (standard + GLSAs have an ID of the form YYYYMM-nn) + @type myconfig: portage.config + @param myconfig: the config that should be used for this object. + """ + self.nr = myid + self.config = myconfig + self.read() + + def read(self): + """ + Here we build the filename from the config and the ID and pass + it to urllib to fetch it from the filesystem or a remote server. + + @rtype: None + @return: None + """ + if self.config["CHECKMODE"] == "local": + repository = "file://" + self.config["GLSA_DIR"] + else: + repository = self.config["GLSA_SERVER"] + myurl = repository + self.config["GLSA_PREFIX"] + str(self.nr) + self.config["GLSA_SUFFIX"] + self.parse(urllib.urlopen(myurl)) + return None + + def parse(self, myfile): + """ + This method parses the XML file and sets up the internal data + structures by calling the different helper functions in this + module. + + @type myfile: String + @param myfile: Filename to grab the XML data from + @rtype: None + @returns: None + """ + self.DOM = xml.dom.minidom.parse(myfile) + if not self.DOM.doctype: + raise GlsaTypeException(None) + elif self.DOM.doctype.systemId != "http://www.gentoo.org/dtd/glsa.dtd": + raise GlsaTypeException(self.DOM.doctype.systemId) + myroot = self.DOM.getElementsByTagName("glsa")[0] + if myroot.getAttribute("id") != self.nr: + raise GlsaFormatException("filename and internal id don't match:" + myroot.getAttribute("id") + " != " + self.nr) + + # the simple (single, required, top-level, #PCDATA) tags first + self.title = getText(myroot.getElementsByTagName("title")[0], format="strip") + self.synopsis = getText(myroot.getElementsByTagName("synopsis")[0], format="strip") + self.announced = getText(myroot.getElementsByTagName("announced")[0], format="strip") + self.revised = getText(myroot.getElementsByTagName("revised")[0], format="strip") + + # now the optional and 0-n toplevel, #PCDATA tags and references + try: + self.access = getText(myroot.getElementsByTagName("access")[0], format="strip") + except IndexError: + self.access = "" + self.bugs = getMultiTagsText(myroot, "bug", format="strip") + self.references = getMultiTagsText(myroot.getElementsByTagName("references")[0], "uri", format="keep") + + # and now the formatted text elements + self.description = getText(myroot.getElementsByTagName("description")[0], format="xml") + self.workaround = getText(myroot.getElementsByTagName("workaround")[0], format="xml") + self.resolution = getText(myroot.getElementsByTagName("resolution")[0], format="xml") + self.impact_text = getText(myroot.getElementsByTagName("impact")[0], format="xml") + self.impact_type = myroot.getElementsByTagName("impact")[0].getAttribute("type") + try: + self.background = getText(myroot.getElementsByTagName("background")[0], format="xml") + except IndexError: + self.background = "" + + # finally the interesting tags (product, affected, package) + self.glsatype = myroot.getElementsByTagName("product")[0].getAttribute("type") + self.product = getText(myroot.getElementsByTagName("product")[0], format="strip") + self.affected = myroot.getElementsByTagName("affected")[0] + self.packages = {} + for p in self.affected.getElementsByTagName("package"): + name = p.getAttribute("name") + self.packages[name] = {} + self.packages[name]["arch"] = p.getAttribute("arch") + self.packages[name]["auto"] = (p.getAttribute("auto") == "yes") + self.packages[name]["vul_vers"] = [makeVersion(v) for v in p.getElementsByTagName("vulnerable")] + self.packages[name]["unaff_vers"] = [makeVersion(v) for v in p.getElementsByTagName("unaffected")] + self.packages[name]["vul_atoms"] = [makeAtom(name, v) for v in p.getElementsByTagName("vulnerable")] + self.packages[name]["unaff_atoms"] = [makeAtom(name, v) for v in p.getElementsByTagName("unaffected")] + # TODO: services aren't really used yet + self.services = self.affected.getElementsByTagName("service") + return None + + def dump(self, outfile="/dev/stdout", encoding="latin1"): + """ + Dumps a plaintext representation of this GLSA to I{outfile} or + B{stdout} if it is ommitted. You can specify an alternate + I{encoding} if needed (default is latin1). + + @type outfile: String + @param outfile: Filename to dump the output in + (defaults to "/dev/stdout") + @type encoding: The encoding that should be used when writing + to I{outfile}. + """ + myfile = codecs.open(outfile, "w", encoding) + width = int(self.config["PRINTWIDTH"]) + myfile.write(center("GLSA %s: %s" % (self.nr, self.title), width)+"\n") + myfile.write((width*"=")+"\n") + myfile.write(wrap(self.synopsis, width, caption="Synopsis: ")+"\n") + myfile.write("Announced on: %s\n" % self.announced) + myfile.write("Last revised on: %s\n\n" % self.revised) + if self.glsatype == "ebuild": + for pkg in self.packages.keys(): + vul_vers = string.join(self.packages[pkg]["vul_vers"]) + unaff_vers = string.join(self.packages[pkg]["unaff_vers"]) + myfile.write("Affected package: %s\n" % pkg) + myfile.write("Affected archs: ") + if self.packages[pkg]["arch"] == "*": + myfile.write("All\n") + else: + myfile.write("%s\n" % self.packages[pkg]["arch"]) + myfile.write("Vulnerable: %s\n" % vul_vers) + myfile.write("Unaffected: %s\n\n" % unaff_vers) + elif self.glsatype == "infrastructure": + pass + if len(self.bugs) > 0: + myfile.write("\nRelated bugs: ") + for i in range(0, len(self.bugs)): + myfile.write(self.bugs[i]) + if i < len(self.bugs)-1: + myfile.write(", ") + else: + myfile.write("\n") + if self.background: + myfile.write("\n"+wrap(self.background, width, caption="Background: ")) + myfile.write("\n"+wrap(self.description, width, caption="Description: ")) + myfile.write("\n"+wrap(self.impact_text, width, caption="Impact: ")) + myfile.write("\n"+wrap(self.workaround, width, caption="Workaround: ")) + myfile.write("\n"+wrap(self.resolution, width, caption="Resolution: ")) + myfile.write("\nReferences: ") + for r in self.references: + myfile.write(r+"\n"+19*" ") + myfile.write("\n") + myfile.close() + + def isVulnerable(self): + """ + Tests if the system is affected by this GLSA by checking if any + vulnerable package versions are installed. Also checks for affected + architectures. + + @rtype: Boolean + @returns: True if the system is affected, False if not + """ + vList = [] + rValue = False + for k in self.packages.keys(): + pkg = self.packages[k] + if pkg["arch"] == "*" or self.config["ARCH"] in pkg["arch"].split(): + vList += pkg["vul_atoms"] + for v in vList: + rValue = rValue or len(portage.db["/"]["vartree"].dbapi.match(v)) > 0 + return rValue + + def isApplied(self): + """ + Looks if the GLSA IDis in the GLSA checkfile to check if this + GLSA was already applied. + + @rtype: Boolean + @returns: True if the GLSA was applied, False if not + """ + aList = portage.grabfile(self.config["CHECKFILE"]) + return (self.nr in aList) + + def inject(self): + """ + Puts the ID of this GLSA into the GLSA checkfile, so it won't + show up on future checks. Should be called after a GLSA is + applied or on explicit user request. + + @rtype: None + @returns: None + """ + if not self.isApplied(): + checkfile = open(self.config["CHECKFILE"], "a+") + checkfile.write(self.nr+"\n") + checkfile.close() + return None + + def getMergeList(self): + """ + Returns the list of package-versions that have to be merged to + apply this GLSA properly. The versions are as low as possible + while avoiding downgrades (see L{getMinUpgrade}). + + @rtype: List of Strings + @return: list of package-versions that have to be merged + """ + rValue = [] + for pkg in self.packages.keys(): + update = getMinUpgrade(self.packages[pkg]["vul_atoms"], + self.packages[pkg]["unaff_atoms"]) + if update: + rValue.append(update) + return rValue |
