summaryrefslogtreecommitdiffstats
path: root/bin/merge-cve-files
blob: 55f487e2d5f7726eb146a56b8f2e579600c8c33a (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
#!/usr/bin/python3
#
# Merge a separate CVE file (such as data/next-point-update.txt) back into
# the main one.
#
# Copyright © 2020 Emilio Pozuelo Monfort <pochu@debian.org>
# Copyright (c) 2021-2022 Neil Williams <codehelp@debian.org>

import os.path
import sys

import setup_paths  # noqa
from debian_support import internRelease
from sectracker.parsers import (
    Bug,
    cvelist,
    writecvelist,
    PackageAnnotation,
    FlagAnnotation,
    StringAnnotation,
    XrefAnnotation
)

def merge_notes(bug, notes):
    """
    Special support for StringAnnotations.

    notes is a dict containing a list of string annotations for
    each CVE in the file being merged. Pick out the string annotations
    for this bug, ignore if already exist, append if new.
    """
    new_notes = []
    cve = bug.header.name
    merge_list = notes.get(cve)  # list of notes to merge
    if not merge_list:
        # nothing to merge
        return bug
    tagged_notes = [note.description for note in merge_list]
    bug_notes = [ann.description for ann in bug.annotations if isinstance(ann, StringAnnotation)]
    # get the list items in tagged_notes which are not in bug_notes
    new_strings = list(set(tagged_notes) - set(bug_notes))
    if not new_strings:
        return bug
    for new_ann in merge_list:
        if new_ann.description in new_strings:
            new_notes.append(new_ann)
    bug_list = list(bug.annotations)
    bug_list.extend(new_notes)
    mod_bug = Bug(
        bug.file, bug.header, tuple(bug_list)
    )
    return mod_bug


def merge_annotations(annotations, new_annotation):
    if not isinstance(new_annotation, PackageAnnotation):
        raise NotImplementedError(f"unsupported annotation of type {new_annotation.type} (line {new_annotation.line})")

    annotations = list(annotations)

    annotations_for_pkg = [ann for ann in annotations \
                           if isinstance(ann, PackageAnnotation) \
                           and ann.package == new_annotation.package]
    if not annotations_for_pkg:
        if new_annotation.release:
            raise ValueError(f"new annotation for {new_annotation.package}/{new_annotation.release} "
                              "but there is no annotation for sid")
        # new package, add it at the top
        for idx, annotation in enumerate(annotations):
            if isinstance(annotation, FlagAnnotation) \
              or isinstance(annotation, XrefAnnotation):
                continue

            annotations.insert(idx, new_annotation)
            return annotations


    # append/substitute the new one at the right place
    for idx, annotation in enumerate(annotations):
        if not isinstance(annotation, PackageAnnotation) \
          or annotation.package != new_annotation.package:
            continue

        # if the annotation is for the same package/release, replace it
        if annotation.package == new_annotation.package \
          and annotation.release == new_annotation.release:
            annotations[idx] = new_annotation
            break

        # if the next annotation's release is the same, we continue to replace
        # it in the next iteration. otherwise if we found the right place, we
        # insert the new annotation
        next_annotation = annotations[idx + 1] if len(annotations) > (idx + 1) else None
        if next_annotation and isinstance(next_annotation, PackageAnnotation) \
          and next_annotation.package == new_annotation.package \
          and internRelease(new_annotation.release) <= internRelease(next_annotation.release):
            continue

        annotations.insert(idx + 1, new_annotation)
        break

    return annotations

def parse_list(path):
    data, messages = cvelist(path)

    for m in messages:
        sys.stderr.write(str(m) + "\n")

    return data

if len(sys.argv) not in (2, 3):
    print(f"Usage: {os.path.basename(sys.argv[0])} (CVE/list) extra-cve-list")
    sys.exit(1)

if len(sys.argv) == 3:
    main_list = sys.argv[1]
else:
    main_list = os.path.dirname(__file__) + '/../data/CVE/list'

extra_list = sys.argv[-1]

data = parse_list(main_list)
extra_data = parse_list(extra_list)

for extra_bug in extra_data:
    bug = next(bug for bug in data if bug.header.name == extra_bug.header.name)

    notes = {}
    new_annotations = bug.annotations
    for extra_annotation in extra_bug.annotations:
        if isinstance(extra_annotation, StringAnnotation):
            cve = f"{extra_bug.header.name}"
            note_tag = notes.setdefault(cve, [])
            note_tag.append(extra_annotation)
            continue
        new_annotations = merge_annotations(new_annotations, extra_annotation)

    bug = bug._replace(annotations=new_annotations)
    bug = merge_notes(bug, notes)
    data = [bug if bug.header.name == old_bug.header.name else old_bug for old_bug in data]

with open(main_list, 'w') as f:
    writecvelist(data, f)

© 2014-2024 Faster IT GmbH | imprint | privacy policy