Florian Weimer
2010-May-08 09:16 UTC
[Secure-testing-commits] r14638 - in lib/python: . sectracker sectracker_test
Author: fw Date: 2010-05-08 09:16:30 +0000 (Sat, 08 May 2010) New Revision: 14638 Added: lib/python/sectracker/parsers.py lib/python/sectracker_test/test_parsers.py Removed: lib/python/parsers.py Log: sectracker.parsers: rename from parsers Deleted: lib/python/parsers.py ==================================================================--- lib/python/parsers.py 2010-05-08 09:15:06 UTC (rev 14637) +++ lib/python/parsers.py 2010-05-08 09:16:30 UTC (rev 14638) @@ -1,351 +0,0 @@ -# parsers -- various text file parsers -# Copyright (C) 2010 Florian Weimer <fw at deneb.enyo.de> -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -import operator -import re - -import debian_support -import sectracker.regexpcase as _regexpcase -from sectracker.xcollections import namedtuple as _namedtuple -import sectracker.xpickle as _xpickle - -import sectracker.diagnostics - -FORMAT = "1" - - at _xpickle.loader("BINARY" + FORMAT) -def binarypackages(name, f): - """Returns a sequence of binary package names""" - obj = set(v for p in debian_support.PackageFile(name, f) - for k, v in p if k == "Package") - obj = list(obj) - obj.sort() - return tuple(obj) - - - at _xpickle.loader("SOURCE" + FORMAT) -def sourcepackages(name, f): - """Returns a dictionary of source package objects. - - The keys are strings, containing the source package name, the - values are corresponding source package versions.""" - - data = {} - for p in debian_support.PackageFile(name, f): - pkg_name, pkg_version = (None, None) - for name, contents in p: - if name == "Package": - pkg_name = intern(contents) - elif name == "Version": - pkg_version = contents - if pkg_name is None: - raise SyntaxError("package record does not contain package name") - if pkg_version is None: - raise SyntaxError("package record for %s does not contain version" - % pkg_name) - if pkg_name in data: - oversion = debian_support.Version(data[pkg_name]) - if oversion >= debian_support.Version(pkg_version): - continue - data[pkg_name] = pkg_version - return data - -def _sortedtuple(seq): - l = list(seq) - l.sort() - return tuple(l) - -FlagAnnotation = _namedtuple("FlagAnnotation", "line type") -StringAnnotation = _namedtuple("StringAnnotation", - "line type description") -XrefAnnotation = _namedtuple("XrefAnnotation", "line type bugs") -PackageAnnotation = _namedtuple( - "PackageAnnotation", - "line type release package kind version description " - + "urgency debian_bugs bug_filed") - -def _annotationdispatcher(): - # Parser for inner annotations, like (bug #1345; low) - urgencies=set("unimportant low medium high".split()) - @_regexpcase.rule(''(bug filed|%s)'' % ''|''.join(urgencies)) - def innerflag(groups, diag, flags, bugs): - f = groups[0] - if f in flags: - diag.error("duplicate flag: " + repr(f)) - else: - flags.add(f) - @_regexpcase.rule(r''bug #(\d+)'') - def innerbug(groups, diag, flags, bugs): - no = int(groups[0]) - if no in bugs: - diag.error("duplicate bug number: " + groups[0]) - else: - bugs.add(no) - def innerdefault(text, diag, flags, bugs): - diag.error("invalid inner annotation: " + repr(text)) - innerdispatch = _regexpcase.RegexpCase((innerflag, innerbug), - default=innerdefault) - - def parseinner(diag, inner): - if not inner: - return (None, (), False) - flags = set() - bugs = set() - for innerann in inner.split(";"): - innerdispatch(innerann.strip(), diag, flags, bugs) - - urgency = urgencies.intersection(flags) - if urgency: - if len(urgency) > 1: - diag.error("multiple urgencies: " + ", ".join(urgency)) - else: - urgency = urgency.pop() - else: - urgency = None - - bug_filed = "bug filed" in flags - if bugs and bug_filed: - diag.error("''bug filed'' and bug numbers listed") - bug_filed = False - - return (urgency, _sortedtuple(bugs), bug_filed) - - # Parsers for indented annotations (NOT-FOR-US:, " - foo <unfixed>" etc.) - - @_regexpcase.rule(r''(?:\[([a-z]+)\]\s)?-\s([A-Za-z0-9:.+-]+)\s*'' - + r''(?:\s([A-Za-z0-9:.+~-]+)\s*)?(?:\s\((.*)\))?'') - def package_version(groups, diag, anns): - release, package, version, inner = groups - inner = parseinner(diag, inner) - if version is None: - kind = "unfixed" - else: - kind = "fixed" - anns.append(PackageAnnotation( - *((diag.line(), "package", release, package, kind, - version, None) + inner))) - - pseudo_freetext = "no-dsa not-affected end-of-life".split() - pseudo_struct = set("unfixed removed itp undetermined".split()) - @_regexpcase.rule(r''(?:\[([a-z]+)\]\s)?-\s([A-Za-z0-9:.+-]+)'' - + r''\s+<([a-z-]+)>\s*(?:\s\((.*)\))?'') - def package_pseudo(groups, diag, anns): - release, package, version, inner = groups - if version in pseudo_freetext: - anns.append(PackageAnnotation( - diag.line(), "package", release, package, version, - None, inner, None, (), False)) - elif version in pseudo_struct: - inner = parseinner(diag, inner) - if version == "itp" and not inner[1]: - diag.error("<itp> needs Debian bug reference") - anns.append(PackageAnnotation( - *((diag.line(), "package", release, package, version, - None, None) + inner))) - else: - diag.error("invalid pseudo-version: " + repr(version)) - - @_regexpcase.rule(r''\{(.*)\}'') - def xref(groups, diag, anns): - x = _sortedtuple(groups[0].strip().split()) - if x: - anns.append(XrefAnnotation(diag.line(), "xref", x)) - else: - diag.error("empty cross-reference") - - return _regexpcase.RegexpCase( - ((r''(RESERVED|REJECTED)'', - lambda groups, diag, anns: - anns.append(FlagAnnotation(diag.line(), groups[0]))), - (r''(NOT-FOR-US|NOTE|TODO):\s+(\S.*)'', - lambda groups, diag, anns: - anns.append(StringAnnotation(diag.line(), *groups))), - package_version, package_pseudo, xref), - prefix=r"\s+", suffix=r"\s*", - default=lambda text, diag, anns: - diag.error("invalid annotation")) -_annotationdispatcher = _annotationdispatcher() - -List = _namedtuple("List", "list messages") -Bug = _namedtuple("Bug", "file header annotations") -Header = _namedtuple("Header", "line name description") - -def _cveuniquename(line, anns): - bug = 0 - for ann in anns: - if ann.type == "package" and ann.debian_bugs: - bug = ann.debian_bugs[0] - break - return "TEMP-%07d-%06d" % (bug, line) - -_re_cve_header = re.compile(r''^(CVE-\d{4}-(?:\d{4}|XXXX))\s+(.*?)\s*$'') - at _xpickle.loader("CVE" + FORMAT) -def cvelist(path, f): - lineno = 0 - headerlineno = None - bugs = [] - diag = sectracker.diagnostics.Diagnostics() - name = desc = None - anns = [] - - def emit(): - if name is None: - return - - if name[-1] == "X": - name1 = _cveuniquename(headerlineno, anns) - else: - name1 = name - bugs.append(Bug(path, Header(headerlineno, name1, desc), tuple(anns))) - del anns[:] - - for line in f.readlines(): - lineno += 1 - diag.setlocation(path, lineno) - - if line[:1] in " \t": - if name is None: - diag.error("header expected") - continue - _annotationdispatcher(line, diag, anns) - else: - emit() - headerlineno = lineno - - match = _re_cve_header.match(line) - if match is None: - diag.error("malformed header") - name = desc = None - continue - name, desc = match.groups() - if desc: - if desc[0] == ''('': - if desc[-1] <> '')'': - diag.error("error", "missing '')''") - else: - desc = desc[1:-1] - elif desc[0] == ''['': - if desc[-1] <> '']'': - diag.error("missing '']''") - else: - desc = desc[1:-1] - - emit() - return List(tuple(bugs), diag.messages()) - -def _test(): - o = binarypackages("../../data/packages/sid__main_i386_Packages") - assert type(o) == type(()) - assert "bash" in o - - o = sourcepackages("../../data/packages/sid__main_Sources") - assert type(o) == type({}) - assert "bash" in o - - _xpickle.safeunlink("../../data/CVE/list" + _xpickle.EXTENSION) - o = cvelist("../../data/CVE/list") - for err in o.messages: - print "%s:%d: %s: %s" % (err.file, err.line, err.level, err.message) - - Message = sectracker.diagnostics.Message - for (line, res, xmsgs) in [ - ('' - foo <unfixed>'', - PackageAnnotation(17, "package", None, "foo", "unfixed", None, - None, None, (), False), ()), - ('' - foo'', - PackageAnnotation(17, "package", None, "foo", "unfixed", None, - None, None, (), False), ()), - ('' [lenny] - foo <unfixed>'', - PackageAnnotation(17, "package", "lenny", "foo", "unfixed", None, - None, None, (), False), ()), - ('' [lenny] - foo <undetermined> (bug #1234)'', - PackageAnnotation(17, "package", "lenny", "foo", "undetermined", - None, None, None, (1234,), False), ()), - ('' [lenny] - foo <itp> (bug #1234)'', - PackageAnnotation(17, "package", "lenny", "foo", "itp", None, - None, None, (1234,), False), ()), - ('' [lenny] - foo <itp>'', - PackageAnnotation(17, "package", "lenny", "foo", "itp", None, - None, None, (), False), - (Message("CVE", 17, "error", - "<itp> needs Debian bug reference"),)), - ('' [lenny] - foo 1.0'', - PackageAnnotation(17, "package", "lenny", "foo", "fixed", "1.0" , - None, None, (), False), ()), - ('' [lenny] - foo <unfixed> (bug filed)'', - PackageAnnotation(17, "package", "lenny", "foo", "unfixed", None, - None, None, (), True), ()), - ('' [lenny] - foo <unfixed> (bug filed; bug #1234)'', - PackageAnnotation(17, "package", "lenny", "foo", "unfixed", None, - None, None, (1234,), False), - (Message("CVE", 17, "error", - "''bug filed'' and bug numbers listed"),)), - ('' [lenny] - foo <unfixed> (low)'', - PackageAnnotation(17, "package", "lenny", "foo", "unfixed", None, - None, "low", (), False), ()), - ('' [lenny] - foo <unfixed> (low; low)'', - PackageAnnotation(17, "package", "lenny", "foo", "unfixed", None, - None, "low", (), False), - (Message("CVE", 17, "error", "duplicate flag: ''low''"),)), - ('' [lenny] - foo <unfixed> (bug #1234; garbled)'', - PackageAnnotation(17, "package", "lenny", "foo", "unfixed", None, - None, None, (1234,), False), - (Message("CVE", 17, "error", - "invalid inner annotation: ''garbled''"),)), - ('' [lenny] - foo <no-dsa> (explanation goes here)'', - PackageAnnotation(17, "package", "lenny", "foo", "no-dsa", None, - "explanation goes here", None, (), False), ()), - ('' [lenny] - foo <end-of-life> (explanation goes here)'', - PackageAnnotation(17, "package", "lenny", "foo", "end-of-life", - None, "explanation goes here", None, (), False), - ()), - ('' [lenny] - foo <not-affected> (explanation goes here)'', - PackageAnnotation(17, "package", "lenny", "foo", "not-affected", - None, - "explanation goes here", None, (), False), ()), - (''\t{CVE-2009-1234 CVE-2009-1235}'', - XrefAnnotation(17, "xref", - tuple("CVE-2009-1234 CVE-2009-1235".split())), - ()), - (''\t{}'', None, - (Message("CVE", 17, "error", "empty cross-reference"),)), - ('' NOT-FOR-US: Plan 9'', - StringAnnotation(17, "NOT-FOR-US", "Plan 9"), ()), - ('' TODO: to-do'', StringAnnotation(17, "TODO", "to-do"), ()), - ('' NOTE: note'', StringAnnotation(17, "NOTE", "note"), ()), - ('' RESERVED'', FlagAnnotation(17, ''RESERVED''), ()), - ('' REJECTED'', FlagAnnotation(17, ''REJECTED''), ()), - ('' garbled'', None, - (Message("CVE", 17, "error", "invalid annotation"),)), - ('' [lenny] - foo <garbled> (bug #1234)'', None, - (Message("CVE", 17, "error", - "invalid pseudo-version: ''garbled''"),)), - ]: - anns = [] - diag = sectracker.diagnostics.Diagnostics() - diag.setlocation("CVE", 17) - _annotationdispatcher(line, diag, anns) - msgs = diag.messages() - assert tuple(msgs) == xmsgs, repr(msgs) - if anns: - r = anns[0] - else: - r = None - assert r == res, repr(anns) - -if __name__ == "__main__": - _test() Copied: lib/python/sectracker/parsers.py (from rev 14633, lib/python/parsers.py) ==================================================================--- lib/python/sectracker/parsers.py (rev 0) +++ lib/python/sectracker/parsers.py 2010-05-08 09:16:30 UTC (rev 14638) @@ -0,0 +1,246 @@ +# sectracker.parsers -- various text file parsers +# Copyright (C) 2010 Florian Weimer <fw at deneb.enyo.de> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +import re + +import debian_support +import sectracker.regexpcase as _regexpcase +from sectracker.xcollections import namedtuple as _namedtuple +import sectracker.xpickle as _xpickle +import sectracker.diagnostics + +FORMAT = "1" + + at _xpickle.loader("BINARY" + FORMAT) +def binarypackages(name, f): + """Returns a sequence of binary package names""" + obj = set(v for p in debian_support.PackageFile(name, f) + for k, v in p if k == "Package") + obj = list(obj) + obj.sort() + return tuple(obj) + + + at _xpickle.loader("SOURCE" + FORMAT) +def sourcepackages(name, f): + """Returns a dictionary of source package objects. + + The keys are strings, containing the source package name, the + values are corresponding source package versions.""" + + data = {} + for p in debian_support.PackageFile(name, f): + pkg_name, pkg_version = (None, None) + for name, contents in p: + if name == "Package": + pkg_name = intern(contents) + elif name == "Version": + pkg_version = contents + if pkg_name is None: + raise SyntaxError("package record does not contain package name") + if pkg_version is None: + raise SyntaxError("package record for %s does not contain version" + % pkg_name) + if pkg_name in data: + oversion = debian_support.Version(data[pkg_name]) + if oversion >= debian_support.Version(pkg_version): + continue + data[pkg_name] = pkg_version + return data + +def _sortedtuple(seq): + l = list(seq) + l.sort() + return tuple(l) + +FlagAnnotation = _namedtuple("FlagAnnotation", "line type") +StringAnnotation = _namedtuple("StringAnnotation", + "line type description") +XrefAnnotation = _namedtuple("XrefAnnotation", "line type bugs") +PackageAnnotation = _namedtuple( + "PackageAnnotation", + "line type release package kind version description " + + "urgency debian_bugs bug_filed") + +def _annotationdispatcher(): + # Parser for inner annotations, like (bug #1345; low) + urgencies=set("unimportant low medium high".split()) + @_regexpcase.rule(''(bug filed|%s)'' % ''|''.join(urgencies)) + def innerflag(groups, diag, flags, bugs): + f = groups[0] + if f in flags: + diag.error("duplicate flag: " + repr(f)) + else: + flags.add(f) + @_regexpcase.rule(r''bug #(\d+)'') + def innerbug(groups, diag, flags, bugs): + no = int(groups[0]) + if no in bugs: + diag.error("duplicate bug number: " + groups[0]) + else: + bugs.add(no) + def innerdefault(text, diag, flags, bugs): + diag.error("invalid inner annotation: " + repr(text)) + innerdispatch = _regexpcase.RegexpCase((innerflag, innerbug), + default=innerdefault) + + def parseinner(diag, inner): + if not inner: + return (None, (), False) + flags = set() + bugs = set() + for innerann in inner.split(";"): + innerdispatch(innerann.strip(), diag, flags, bugs) + + urgency = urgencies.intersection(flags) + if urgency: + if len(urgency) > 1: + diag.error("multiple urgencies: " + ", ".join(urgency)) + else: + urgency = urgency.pop() + else: + urgency = None + + bug_filed = "bug filed" in flags + if bugs and bug_filed: + diag.error("''bug filed'' and bug numbers listed") + bug_filed = False + + return (urgency, _sortedtuple(bugs), bug_filed) + + # Parsers for indented annotations (NOT-FOR-US:, " - foo <unfixed>" etc.) + + @_regexpcase.rule(r''(?:\[([a-z]+)\]\s)?-\s([A-Za-z0-9:.+-]+)\s*'' + + r''(?:\s([A-Za-z0-9:.+~-]+)\s*)?(?:\s\((.*)\))?'') + def package_version(groups, diag, anns): + release, package, version, inner = groups + inner = parseinner(diag, inner) + if version is None: + kind = "unfixed" + else: + kind = "fixed" + anns.append(PackageAnnotation( + *((diag.line(), "package", release, package, kind, + version, None) + inner))) + + pseudo_freetext = "no-dsa not-affected end-of-life".split() + pseudo_struct = set("unfixed removed itp undetermined".split()) + @_regexpcase.rule(r''(?:\[([a-z]+)\]\s)?-\s([A-Za-z0-9:.+-]+)'' + + r''\s+<([a-z-]+)>\s*(?:\s\((.*)\))?'') + def package_pseudo(groups, diag, anns): + release, package, version, inner = groups + if version in pseudo_freetext: + anns.append(PackageAnnotation( + diag.line(), "package", release, package, version, + None, inner, None, (), False)) + elif version in pseudo_struct: + inner = parseinner(diag, inner) + if version == "itp" and not inner[1]: + diag.error("<itp> needs Debian bug reference") + anns.append(PackageAnnotation( + *((diag.line(), "package", release, package, version, + None, None) + inner))) + else: + diag.error("invalid pseudo-version: " + repr(version)) + + @_regexpcase.rule(r''\{(.*)\}'') + def xref(groups, diag, anns): + x = _sortedtuple(groups[0].strip().split()) + if x: + anns.append(XrefAnnotation(diag.line(), "xref", x)) + else: + diag.error("empty cross-reference") + + return _regexpcase.RegexpCase( + ((r''(RESERVED|REJECTED)'', + lambda groups, diag, anns: + anns.append(FlagAnnotation(diag.line(), groups[0]))), + (r''(NOT-FOR-US|NOTE|TODO):\s+(\S.*)'', + lambda groups, diag, anns: + anns.append(StringAnnotation(diag.line(), *groups))), + package_version, package_pseudo, xref), + prefix=r"\s+", suffix=r"\s*", + default=lambda text, diag, anns: + diag.error("invalid annotation")) +_annotationdispatcher = _annotationdispatcher() + +List = _namedtuple("List", "list messages") +Bug = _namedtuple("Bug", "file header annotations") +Header = _namedtuple("Header", "line name description") + +def _cveuniquename(line, anns): + bug = 0 + for ann in anns: + if ann.type == "package" and ann.debian_bugs: + bug = ann.debian_bugs[0] + break + return "TEMP-%07d-%06d" % (bug, line) + +_re_cve_header = re.compile(r''^(CVE-\d{4}-(?:\d{4}|XXXX))\s+(.*?)\s*$'') + at _xpickle.loader("CVE" + FORMAT) +def cvelist(path, f): + lineno = 0 + headerlineno = None + bugs = [] + diag = sectracker.diagnostics.Diagnostics() + name = desc = None + anns = [] + + def emit(): + if name is None: + return + + if name[-1] == "X": + name1 = _cveuniquename(headerlineno, anns) + else: + name1 = name + bugs.append(Bug(path, Header(headerlineno, name1, desc), tuple(anns))) + del anns[:] + + for line in f.readlines(): + lineno += 1 + diag.setlocation(path, lineno) + + if line[:1] in " \t": + if name is None: + diag.error("header expected") + continue + _annotationdispatcher(line, diag, anns) + else: + emit() + headerlineno = lineno + + match = _re_cve_header.match(line) + if match is None: + diag.error("malformed header") + name = desc = None + continue + name, desc = match.groups() + if desc: + if desc[0] == ''('': + if desc[-1] <> '')'': + diag.error("error", "missing '')''") + else: + desc = desc[1:-1] + elif desc[0] == ''['': + if desc[-1] <> '']'': + diag.error("missing '']''") + else: + desc = desc[1:-1] + + emit() + return List(tuple(bugs), diag.messages()) Added: lib/python/sectracker_test/test_parsers.py ==================================================================--- lib/python/sectracker_test/test_parsers.py (rev 0) +++ lib/python/sectracker_test/test_parsers.py 2010-05-08 09:16:30 UTC (rev 14638) @@ -0,0 +1,119 @@ +# Test for sectracker.parsers +# Copyright (C) 2010 Florian Weimer <fw at deneb.enyo.de> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +from sectracker.parsers import * +import sectracker.parsers as p +from sectracker.xpickle import safeunlink, EXTENSION + +o = binarypackages("../../data/packages/sid__main_i386_Packages") +assert type(o) == type(()) +assert "bash" in o + +o = sourcepackages("../../data/packages/sid__main_Sources") +assert type(o) == type({}) +assert "bash" in o + +safeunlink("../../data/CVE/list" + EXTENSION) +o = cvelist("../../data/CVE/list") +for err in o.messages: + print "%s:%d: %s: %s" % (err.file, err.line, err.level, err.message) + +Message = sectracker.diagnostics.Message +for (line, res, xmsgs) in [ + ('' - foo <unfixed>'', + PackageAnnotation(17, "package", None, "foo", "unfixed", None, + None, None, (), False), ()), + ('' - foo'', + PackageAnnotation(17, "package", None, "foo", "unfixed", None, + None, None, (), False), ()), + ('' [lenny] - foo <unfixed>'', + PackageAnnotation(17, "package", "lenny", "foo", "unfixed", None, + None, None, (), False), ()), + ('' [lenny] - foo <undetermined> (bug #1234)'', + PackageAnnotation(17, "package", "lenny", "foo", "undetermined", + None, None, None, (1234,), False), ()), + ('' [lenny] - foo <itp> (bug #1234)'', + PackageAnnotation(17, "package", "lenny", "foo", "itp", None, + None, None, (1234,), False), ()), + ('' [lenny] - foo <itp>'', + PackageAnnotation(17, "package", "lenny", "foo", "itp", None, + None, None, (), False), + (Message("CVE", 17, "error", + "<itp> needs Debian bug reference"),)), + ('' [lenny] - foo 1.0'', + PackageAnnotation(17, "package", "lenny", "foo", "fixed", "1.0" , + None, None, (), False), ()), + ('' [lenny] - foo <unfixed> (bug filed)'', + PackageAnnotation(17, "package", "lenny", "foo", "unfixed", None, + None, None, (), True), ()), + ('' [lenny] - foo <unfixed> (bug filed; bug #1234)'', + PackageAnnotation(17, "package", "lenny", "foo", "unfixed", None, + None, None, (1234,), False), + (Message("CVE", 17, "error", + "''bug filed'' and bug numbers listed"),)), + ('' [lenny] - foo <unfixed> (low)'', + PackageAnnotation(17, "package", "lenny", "foo", "unfixed", None, + None, "low", (), False), ()), + ('' [lenny] - foo <unfixed> (low; low)'', + PackageAnnotation(17, "package", "lenny", "foo", "unfixed", None, + None, "low", (), False), + (Message("CVE", 17, "error", "duplicate flag: ''low''"),)), + ('' [lenny] - foo <unfixed> (bug #1234; garbled)'', + PackageAnnotation(17, "package", "lenny", "foo", "unfixed", None, + None, None, (1234,), False), + (Message("CVE", 17, "error", + "invalid inner annotation: ''garbled''"),)), + ('' [lenny] - foo <no-dsa> (explanation goes here)'', + PackageAnnotation(17, "package", "lenny", "foo", "no-dsa", None, + "explanation goes here", None, (), False), ()), + ('' [lenny] - foo <end-of-life> (explanation goes here)'', + PackageAnnotation(17, "package", "lenny", "foo", "end-of-life", + None, "explanation goes here", None, (), False), + ()), + ('' [lenny] - foo <not-affected> (explanation goes here)'', + PackageAnnotation(17, "package", "lenny", "foo", "not-affected", + None, + "explanation goes here", None, (), False), ()), + (''\t{CVE-2009-1234 CVE-2009-1235}'', + XrefAnnotation(17, "xref", + tuple("CVE-2009-1234 CVE-2009-1235".split())), + ()), + (''\t{}'', None, + (Message("CVE", 17, "error", "empty cross-reference"),)), + ('' NOT-FOR-US: Plan 9'', + StringAnnotation(17, "NOT-FOR-US", "Plan 9"), ()), + ('' TODO: to-do'', StringAnnotation(17, "TODO", "to-do"), ()), + ('' NOTE: note'', StringAnnotation(17, "NOTE", "note"), ()), + ('' RESERVED'', FlagAnnotation(17, ''RESERVED''), ()), + ('' REJECTED'', FlagAnnotation(17, ''REJECTED''), ()), + ('' garbled'', None, + (Message("CVE", 17, "error", "invalid annotation"),)), + ('' [lenny] - foo <garbled> (bug #1234)'', None, + (Message("CVE", 17, "error", + "invalid pseudo-version: ''garbled''"),)), + ]: + anns = [] + diag = sectracker.diagnostics.Diagnostics() + diag.setlocation("CVE", 17) + p._annotationdispatcher(line, diag, anns) + msgs = diag.messages() + assert tuple(msgs) == xmsgs, repr(msgs) + if anns: + r = anns[0] + else: + r = None + assert r == res, repr(anns)