From aa7282b74f0aa3b431fe999397b735125bee624c Mon Sep 17 00:00:00 2001 From: Neil Williams Date: Fri, 17 Dec 2021 14:11:48 +0000 Subject: Add initial update-vuln script --- bin/update-vuln | 182 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 182 insertions(+) create mode 100755 bin/update-vuln diff --git a/bin/update-vuln b/bin/update-vuln new file mode 100755 index 0000000000..9e3d710b5a --- /dev/null +++ b/bin/update-vuln @@ -0,0 +1,182 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" + update-vuln - #1001453 + + - mark a given released suite (stable/oldstable/LTS) as + for a specific CVE ID + - add a bug number to an existing CVE entry + - add a NOTE: entry to an existing CVE + +""" +# Copyright 2021 Neil Williams +# +# 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 os +import argparse +import sys + +import setup_paths # noqa # pylint: disable=unused-import +from sectracker.parsers import ( + sourcepackages, + PackageAnnotation, + StringAnnotation, + Bug, + cvelist, + writecvelist, +) + + +class ParseUpdates: + """ + Update a CVE with requested changes and produce a file for + manual review and use with merge-cve-files. + """ + + def __init__(self): + self.cves = [] + self.bugs = {} + + def _read_cvelist(self): + os.chdir(os.path.dirname(os.path.dirname(os.path.realpath(__file__)))) + data, _ = cvelist("data/CVE/list") + for cve in self.cves: + for bug in data: + if bug.header.name == cve: + self.bugs[cve] = bug + + def _add_annotation_after_line(self, cve, line, annotation): + bug_list = list(self.bugs[cve].annotations) + bug_list.append(annotation) + mod_bug = Bug(self.bugs[cve].file, self.bugs[cve].header, tuple(bug_list)) + return mod_bug + + def _replace_annotation_on_line(self, cve, line, mod_line): + index = self.bugs[cve].annotations.index(line) + bug_list = list(self.bugs[cve].annotations) + bug_list[index] = mod_line + mod_bug = Bug(self.bugs[cve].file, self.bugs[cve].header, tuple(bug_list)) + return mod_bug + + def write_modified(self, modified, cve_file): + if not modified: + return + if not isinstance(modified, list): + return + if os.path.exists(cve_file): + raise OSError("%s already exists" % cve_file) + mods = [] + for cve in modified: + print(f"Writing to ./{cve_file} with update for {cve.header.name}.") + with open(cve_file, "a") as snippet: + writecvelist(modified, snippet) + + def mark_not_affected(self, suite, src): + """ + Writes out a CVE file snippet with the filename: + ./.list + Fails if the file already exists. + """ + release = suite + if suite == "unstable" or suite == "sid": + # special handling for unstable + suite = None + release = "unstable" + modified = [] + cve = self.cves[0] + cve_file = f"{cve}.list" + for line in self.bugs[cve].annotations: + if not isinstance(line, PackageAnnotation): + continue # skip notes etc. + if line.release != suite: + continue + if line.package != src: + continue + # need to define the allowed changes + # if fixed, version would need to be undone too. + if line.kind == "not-affected": + print(f"Nothing to do for {cve} in {suite}.") + return + mod_line = line._replace(kind="not-affected") + print(f"Modified {cve} for {src} in {release} to ") + if mod_line.version: + print(f"Removing version {line.version}") + ver_line = mod_line + mod_line = ver_line._replace(version=None) + if mod_line.description: + print(f"Removing description {line.description}") + desc_line = mod_line + mod_line = desc_line._replace(description=None) + # removing a bug annotation is not covered, yet. + mod_bug = self._replace_annotation_on_line(cve, line, mod_line) + modified.append(mod_bug) + self.write_modified(modified, cve_file) + + def add_note(self, note): + """ + Writes out a CVE file snippet with the filename: + ./.list + Fails if the file already exists. + """ + # use _add_annotation_after_line to add a line + pass + + def add_bug_number(self, bug): + """ + Writes out a CVE file snippet with the filename: + ./.list + Fails if the file already exists. + """ + # need to work out how to manipulate releases + pass + + def load_cve(self, cve): + print(f"Loading data for {cve}...") + self.cves.append(cve) + self._read_cvelist() + + +def main(): + parser = argparse.ArgumentParser( + description="Update a specified CVE data as not-affected, add bug number or add a note", + epilog="Data is written to a new .list " + "file which can be used with './bin/merge-cve-files'", + ) + required = parser.add_argument_group("Required arguments") + required.add_argument("--cve", required=True, help="The CVE ID to update") + affected = parser.add_argument_group("Marking a CVE as not-affected") + # needs to specify the src_package as well as suite to cope with removed etc. + affected.add_argument("--src", help="Source package name in SUITE") + affected.add_argument( + "--suite", default="unstable", help="Mark the CVE as in SUITE" + ) + buggy = parser.add_argument_group("Add a bug number to the CVE") + buggy.add_argument("--number", help="Debian BTS bug number") + notes = parser.add_argument_group("Add a NOTE: entry to the CVE") + notes.add_argument("--note", help="Content of the NOTE: entry to add to the CVE") + args = parser.parse_args() + parser = ParseUpdates() + parser.load_cve(args.cve) + if not parser.bugs: + raise ValueError("Unable to parse CVE ID %s" % args.cve) + # print(parser.bugs[args.cve].header.description) + parser.mark_not_affected(args.suite, args.src) + return 0 + + +if __name__ == "__main__": + sys.exit(main()) -- cgit v1.2.3