aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'lib/portage/glsa.py')
-rw-r--r--lib/portage/glsa.py206
1 files changed, 107 insertions, 99 deletions
diff --git a/lib/portage/glsa.py b/lib/portage/glsa.py
index 19f226db1..648159ad8 100644
--- a/lib/portage/glsa.py
+++ b/lib/portage/glsa.py
@@ -1,4 +1,4 @@
-# Copyright 2003-2020 Gentoo Authors
+# Copyright 2003-2022 Gentoo Authors
# Distributed under the terms of the GNU General Public License v2
import codecs
@@ -10,7 +10,6 @@ import xml.dom.minidom
from functools import reduce
-import io
from io import StringIO
from portage import _encodings, _unicode_decode, _unicode_encode
@@ -38,6 +37,8 @@ opMapping = {
}
NEWLINE_ESCAPE = "!;\\n" # some random string to mark newlines that should be preserved
SPACE_ESCAPE = "!;_" # some random string to mark spaces that should be preserved
+# See PMS 3.1.7 "Keyword names"
+ARCH_REGEX = re.compile(r"^\*$|^[-_a-z0-9 ]+$")
def get_applied_glsas(settings):
@@ -76,26 +77,29 @@ def wrap(text, width, caption=""):
words = text.split()
indentLevel = len(caption) + 1
- for w in words:
- if line != "" and line[-1] == "\n":
- rValue += line
+ for word in words:
+ if line and line[-1] == "\n":
+ rValue = f"{rValue}{line}"
line = " " * indentLevel
- if len(line) + len(w.replace(NEWLINE_ESCAPE, "")) + 1 > width:
- rValue += line + "\n"
- line = " " * indentLevel + w.replace(NEWLINE_ESCAPE, "\n")
- elif w.find(NEWLINE_ESCAPE) >= 0:
+ if len(line) + len(word.replace(NEWLINE_ESCAPE, "")) + 1 > width:
+ rValue = f"{rValue}{line}\n"
+ escaped_word = word.replace(NEWLINE_ESCAPE, "\n")
+ line = f"{' ' * indentLevel}{escaped_word}"
+ elif word.find(NEWLINE_ESCAPE) >= 0:
+ escaped_word = word.replace(NEWLINE_ESCAPE, "\n")
+ whitespace = ""
if len(line.strip()) > 0:
- rValue += line + " " + w.replace(NEWLINE_ESCAPE, "\n")
- else:
- rValue += line + w.replace(NEWLINE_ESCAPE, "\n")
+ whitespace = " "
+ rValue = f"{rValue}{line}{whitespace}{escaped_word}"
line = " " * indentLevel
else:
+ whitespace = ""
if len(line.strip()) > 0:
- line += " " + w
- else:
- line += w
+ whitespace = " "
+ line = f"{line}{whitespace}{word}"
if len(line) > 0:
- rValue += line.replace(NEWLINE_ESCAPE, "\n")
+ escaped_line = line.replace(NEWLINE_ESCAPE, "\n")
+ rValue = f"{rValue}{escaped_line}"
rValue = rValue.replace(SPACE_ESCAPE, " ")
return rValue
@@ -112,25 +116,29 @@ def get_glsa_list(myconfig):
@rtype: List of Strings
@return: a list of GLSA IDs in this repository
"""
- rValue = []
+ repository = os.path.join(myconfig["PORTDIR"], "metadata", "glsa")
if "GLSA_DIR" in myconfig:
repository = myconfig["GLSA_DIR"]
- else:
- repository = os.path.join(myconfig["PORTDIR"], "metadata", "glsa")
if not os.access(repository, os.R_OK):
return []
dirlist = os.listdir(repository)
prefix = "glsa-"
+ prefix_size = len(prefix)
suffix = ".xml"
+ suffix_size = len(suffix)
- for f in dirlist:
+ def check(value):
try:
- if f[: len(prefix)] == prefix and f[-1 * len(suffix) :] == suffix:
- rValue.append(f[len(prefix) : -1 * len(suffix)])
+ if value[:prefix_size] == prefix and value[-suffix_size:] == suffix:
+ return value[prefix_size:-suffix_size]
except IndexError:
- pass
+ return None
+ return None
+
+ checked_dirlist = (check(f) for f in dirlist)
+ rValue = [f for f in checked_dirlist if f]
return rValue
@@ -143,7 +151,7 @@ def getListElements(listnode):
@rtype: List of Strings
@return: a list that contains the value of the <li> elements
"""
- if not listnode.nodeName in ["ul", "ol"]:
+ if not listnode.nodeName in ("ul", "ol"):
raise GlsaFormatException("Invalid function call: listnode is not <ul> or <ol>")
rValue = [
getText(li, format="strip")
@@ -182,9 +190,9 @@ def getText(node, format, textfd=None): # pylint: disable=redefined-builtin
returnNone = False
else:
returnNone = True
- if format in ["strip", "keep"]:
- if node.nodeName in ["uri", "mail"]:
- textfd.write(node.childNodes[0].data + ": " + node.getAttribute("link"))
+ if format in ("strip", "keep"):
+ if node.nodeName in ("uri", "mail"):
+ textfd.write(f"{node.childNodes[0].data}:{node.getAttribute('link')}")
else:
for subnode in node.childNodes:
if subnode.nodeName == "#text":
@@ -197,20 +205,18 @@ def getText(node, format, textfd=None): # pylint: disable=redefined-builtin
for p_subnode in subnode.childNodes:
if p_subnode.nodeName == "#text":
textfd.write(p_subnode.data.strip())
- elif p_subnode.nodeName in ["uri", "mail"]:
+ elif p_subnode.nodeName in ("uri", "mail"):
textfd.write(p_subnode.childNodes[0].data)
textfd.write(" ( " + p_subnode.getAttribute("link") + " )")
textfd.write(NEWLINE_ESCAPE)
elif subnode.nodeName == "ul":
for li in getListElements(subnode):
- textfd.write("-" + SPACE_ESCAPE + li + NEWLINE_ESCAPE + " ")
+ textfd.write(f"-{SPACE_ESCAPE}{li}{NEWLINE_ESCAPE} ")
elif subnode.nodeName == "ol":
i = 0
for li in getListElements(subnode):
i = i + 1
- textfd.write(
- str(i) + "." + SPACE_ESCAPE + li + NEWLINE_ESCAPE + " "
- )
+ textfd.write(f"{i}.{SPACE_ESCAPE}{li}{NEWLINE_ESCAPE} ")
elif subnode.nodeName == "code":
textfd.write(
getText(subnode, format="keep")
@@ -262,19 +268,22 @@ def makeAtom(pkgname, versionNode):
@rtype: String
@return: the portage atom
"""
- rValue = (
- opMapping[versionNode.getAttribute("range")]
- + pkgname
- + "-"
- + getText(versionNode, format="strip")
- )
+ rangetype = versionNode.getAttribute("range")
+ if rangetype in opMapping:
+ op = opMapping[rangetype]
+ else:
+ raise GlsaFormatException(
+ _(f"Invalid range found for '{pkgname}': {rangetype}")
+ )
+ version = getText(versionNode, format="strip")
+ rValue = f"{op}{pkgname}-{version}"
try:
slot = versionNode.getAttribute("slot").strip()
except KeyError:
pass
else:
if slot and slot != "*":
- rValue += _slot_separator + slot
+ rValue = f"{rValue}{_slot_separator}{slot}"
return str(rValue)
@@ -289,16 +298,20 @@ def makeVersion(versionNode):
@rtype: String
@return: the version string
"""
- rValue = opMapping[versionNode.getAttribute("range")] + getText(
- versionNode, format="strip"
- )
+ rangetype = versionNode.getAttribute("range")
+ if rangetype in opMapping:
+ op = opMapping[rangetype]
+ else:
+ raise GlsaFormatException(_(f"Invalid range found: {rangetype}"))
+ version = getText(versionNode, format="strip")
+ rValue = f"{op}{version}"
try:
slot = versionNode.getAttribute("slot").strip()
except KeyError:
pass
else:
if slot and slot != "*":
- rValue += _slot_separator + slot
+ rValue = f"{rValue}{_slot_separator}{slot}"
return rValue
@@ -421,9 +434,9 @@ def getMinUpgrade(vulnerableList, unaffectedList, portdbapi, vardbapi, minimize=
and portdbapi._pkg_str(c, None).slot
== vardbapi._pkg_str(vuln, None).slot
):
- update = c_pv[0] + "/" + c_pv[1] + "-" + c_pv[2]
+ update = f"{c_pv[0]}/{c_pv[1]}-{c_pv[2]}"
if c_pv[3] != "r0": # we don't like -r0 for display
- update += "-" + c_pv[3]
+ update = f"{update}-{c_pv[3]}"
update = portdbapi._pkg_str(update, None)
vuln_update.append([vuln, update])
@@ -466,7 +479,7 @@ def format_date(datestr):
class GlsaTypeException(Exception):
def __init__(self, doctype):
- Exception.__init__(self, "wrong DOCTYPE: %s" % doctype)
+ Exception.__init__(self, f"wrong DOCTYPE: {doctype}")
class GlsaFormatException(Exception):
@@ -509,7 +522,7 @@ class Glsa:
self.type = "file"
else:
raise GlsaArgumentException(
- _("Given ID %s isn't a valid GLSA ID or filename.") % myid
+ _(f"Given ID {myid} isn't a valid GLSA ID or filename.")
)
self.nr = myid
self.config = myconfig
@@ -526,13 +539,13 @@ class Glsa:
@return: None
"""
if "GLSA_DIR" in self.config:
- repository = "file://" + self.config["GLSA_DIR"] + "/"
+ repository = f"file://{self.config['GLSA_DIR']}/"
else:
- repository = "file://" + self.config["PORTDIR"] + "/metadata/glsa/"
+ repository = f"file://{self.config['PORTDIR']}/metadata/glsa/"
if self.type == "file":
- myurl = "file://" + self.nr
+ myurl = f"file://{self.nr}"
else:
- myurl = repository + "glsa-%s.xml" % str(self.nr)
+ myurl = f"{repository}glsa-{self.nr}.xml"
f = urllib_request_urlopen(myurl)
try:
@@ -563,10 +576,9 @@ class Glsa:
myroot = self.DOM.getElementsByTagName("glsa")[0]
if self.type == "id" and myroot.getAttribute("id") != self.nr:
raise GlsaFormatException(
- _("filename and internal id don't match:")
- + myroot.getAttribute("id")
- + " != "
- + self.nr
+ _(
+ f"filename and internal id don't match: {myroot.getAttribute('id')} != {self.nr}"
+ )
)
# the simple (single, required, top-level, #PCDATA) tags first
@@ -585,17 +597,14 @@ class Glsa:
count = revisedEl.getAttribute("count")
if not count:
raise GlsaFormatException(
- "Count attribute is missing or blank in GLSA: "
- + myroot.getAttribute("id")
+ f"Count attribute is missing or blank in GLSA: {myroot.getAttribute('id')}"
)
try:
self.count = int(count)
except ValueError:
raise GlsaFormatException(
- "Revision attribute in GLSA: "
- + myroot.getAttribute("id")
- + " is not an integer"
+ f"Revision attribute in GLSA: {myroot.getAttribute('id')} is not an integer"
)
self.revised = format_date(self.revised)
@@ -645,9 +654,9 @@ class Glsa:
try:
name = portage.dep.Atom(name)
except portage.exception.InvalidAtom:
- raise GlsaFormatException(_("invalid package name: %s") % name)
+ raise GlsaFormatException(_(f"invalid package name: {name}"))
if name != name.cp:
- raise GlsaFormatException(_("invalid package name: %s") % name)
+ raise GlsaFormatException(_(f"invalid package name: {name}"))
name = name.cp
if name not in self.packages:
self.packages[name] = []
@@ -683,58 +692,51 @@ class Glsa:
outstream = getattr(outstream, "buffer", outstream)
outstream = codecs.getwriter(encoding)(outstream)
width = 76
- outstream.write(("GLSA %s: \n%s" % (self.nr, self.title)).center(width) + "\n")
- outstream.write((width * "=") + "\n")
- outstream.write(
- wrap(self.synopsis, width, caption=_("Synopsis: ")) + "\n"
- )
- outstream.write(_("Announced on: %s\n") % self.announced)
- outstream.write(
- _("Last revised on: %s : %02d\n\n") % (self.revised, self.count)
+ buffer = "\n".join(
+ (
+ f"GLSA {self.nr}: ",
+ f"{self.title}".center(width),
+ "=" * width,
+ wrap(self.synopsis, width, caption=_("Synopsis: ")),
+ _(f"Announced on: {self.announced}"),
+ _(f"Last revised on: {self.revised} : %{self.count}\n"),
+ )
)
+ outstream.write(buffer)
if self.glsatype == "ebuild":
for k in self.packages:
pkg = self.packages[k]
for path in pkg:
vul_vers = ", ".join(path["vul_vers"])
unaff_vers = ", ".join(path["unaff_vers"])
- outstream.write(_("Affected package: %s\n") % k)
+ outstream.write(_(f"Affected package: {k}\n"))
outstream.write(_("Affected archs: "))
if path["arch"] == "*":
outstream.write(_("All\n"))
else:
- outstream.write("%s\n" % path["arch"])
- outstream.write(_("Vulnerable: %s\n") % vul_vers)
- outstream.write(_("Unaffected: %s\n\n") % unaff_vers)
+ outstream.write(f"{path['arch']}\n")
+ outstream.write(_(f"Vulnerable: {vul_vers}\n"))
+ outstream.write(_(f"Unaffected: {unaff_vers}\n\n"))
elif self.glsatype == "infrastructure":
pass
if len(self.bugs) > 0:
- outstream.write(_("\nRelated bugs: "))
- outstream.write(", ".join(self.bugs))
- outstream.write("\n")
+ outstream.write(_(f"\nRelated bugs: {', '.join(self.bugs)}\n"))
if self.background:
- outstream.write(
- "\n" + wrap(self.background, width, caption=_("Background: "))
- )
- outstream.write(
- "\n" + wrap(self.description, width, caption=_("Description: "))
- )
- outstream.write(
- "\n" + wrap(self.impact_text, width, caption=_("Impact: "))
- )
- outstream.write(
- "\n" + wrap(self.workaround, width, caption=_("Workaround: "))
- )
- outstream.write(
- "\n" + wrap(self.resolution, width, caption=_("Resolution: "))
- )
+ bg = wrap(self.background, width, caption=_("Background: "))
+ outstream.write(f"\n{bg}")
myreferences = " ".join(
r.replace(" ", SPACE_ESCAPE) + NEWLINE_ESCAPE for r in self.references
)
- outstream.write(
- "\n" + wrap(myreferences, width, caption=_("References: "))
+ buffer = "\n".join(
+ (
+ wrap(self.description, width, caption=_("Description: ")),
+ wrap(self.impact_text, width, caption=_("Impact: ")),
+ wrap(self.workaround, width, caption=_("Workaround: ")),
+ wrap(self.resolution, width, caption=_("Resolution: ")),
+ wrap(myreferences, width, caption=_("References: ")),
+ )
)
- outstream.write("\n")
+ outstream.write(f"\n{buffer}\n")
def isVulnerable(self):
"""
@@ -749,7 +751,13 @@ class Glsa:
for k in self.packages:
pkg = self.packages[k]
for path in pkg:
- if path["arch"] == "*" or self.config["ARCH"] in path["arch"].split():
+ if not ARCH_REGEX.match(path["arch"]):
+ raise GlsaFormatException(
+ f"Unrecognized arch list in {self.nr} (wrong delimiter?): {path['arch']}"
+ )
+
+ arches = path["arch"].split()
+ if path["arch"] == "*" or self.config["ARCH"] in arches:
for v in path["vul_atoms"]:
rValue = rValue or (
len(match(v, self.vardbapi)) > 0
@@ -787,7 +795,7 @@ class Glsa:
@return: None
"""
if not self.isInjected():
- checkfile = io.open(
+ checkfile = open(
_unicode_encode(
os.path.join(self.config["EROOT"], PRIVATE_PATH, "glsa_injected"),
encoding=_encodings["fs"],
@@ -813,11 +821,11 @@ class Glsa:
@return: list of package-versions that have to be merged
"""
return list(
- set(
+ {
update
for (vuln, update) in self.getAffectionTable(least_change)
if update
- )
+ }
)
def getAffectionTable(self, least_change=True):