summaryrefslogtreecommitdiffstats
path: root/bin/merge-cve-files
blob: 2f130a29fb6f8e2d308e5310c66fa63b0a444bfb (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
#!/usr/bin/python3
#
# Merge a separate CVE file (such as data/next-point-update.txt) back into
# the main one.
#
# Copyright © 2020-2023 Emilio Pozuelo Monfort <pochu@debian.org>
# Copyright (c) 2021-2022 Neil Williams <codehelp@debian.org>

import os
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(annotations, new_annotation):
    """
    Special support for StringAnnotations.

    Merges a note into the bug's annotations, taking care not to
    add duplicate notes.

    new_annotation is a new string annotation for this CVE (bug),
    """
    old_descriptions = [ann.description
                        for ann in annotations
                        if isinstance(ann, StringAnnotation)]

    # prevent adding duplicate notes
    if not new_annotation.description in old_descriptions:
        annotations.append(new_annotation)


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

    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


    # 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 we found an experimental annotation, it will be followed by a 'sid'
        # one, so next_annotation.release will be None in the next case. That
        # comparison will break, so we avoid it by continuing. If new_annotation
        # was for experimental, we would have already replaced it in the above check.
        if annotation.release == 'experimental':
            continue

        # 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


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 = cvelist(main_list)
extra_data = cvelist(extra_list)

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

    for extra_annotation in extra_bug.annotations:
        if isinstance(extra_annotation, FlagAnnotation):
            continue
        if isinstance(extra_annotation, StringAnnotation):
            merge_notes(bug.annotations, extra_annotation)
            continue

        merge_annotations(bug.annotations, extra_annotation)

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

# check for and erase an .xpck file built from the merge
xpck = f"{extra_list}.xpck"
if os.path.exists(xpck):
    os.unlink(xpck)

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