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
|
from debian import deb822
import os.path
import re
import sys
class issue(deb822.Deb822):
nonReleaseFields = ['Candidate',
'References',
'Description',
'Notes',
'Bugs']
reservedPrefixes = ['Ubuntu-']
def __init__(self, path):
self.file = open(path, 'r')
self.d = deb822.Deb822(self.file)
self.name = os.path.basename(path)
def status(self, release):
if release in self.d:
return self.d[release]
else:
return ""
def fieldIsRelease(self, field):
if field in self.nonReleaseFields:
return False
for p in self.reservedPrefixes:
if field[:len(p)] == p:
return False
return True
def get_releases(self):
releases = set()
for field in self.d.keys():
if self.fieldIsRelease(field):
releases.add(field)
return releases
_ignore_patterns = [re.compile('.*~$'),
re.compile('^#.*'),
re.compile('^00.*')]
def _ignore_issue_name(issue):
for p in _ignore_patterns:
if p.match(issue):
return True
return False
# Pad last part of CVE ID to 8 digits so string comparison keeps working
def _pad_cve_id(cve_id):
return re.sub(r'-(\d+)$', lambda m: '-%08d' % int(m.group(1)), cve_id)
def get_issues(dir):
issues = []
L = [f for f in os.listdir(dir)
if not os.path.isdir(os.path.join(dir, f))
and not _ignore_issue_name(f)]
L.sort(key=_pad_cve_id)
return [ issue(os.path.join(dir, f)) for f in L ]
_status_re = re.compile(
r'\s*(\S*)'
r'(?:\s*\([^\s)]+\))?' # optional version
r'(?:\s*\[[^]]+\])?' # optional changerefs
r'(?:\s*"[^"]+")?' # optional comment
)
_comma_re = re.compile(r'\s*,\s*')
def _coalesce_state(old_state, new_state):
if old_state is None:
return new_state
# "needed" overrides everything else
if 'needed' in [old_state, new_state]:
return 'needed'
# "pendng" overrides everything but "needed"
if 'pending' in [old_state, new_state]:
return 'pending'
# We don't expect to use more than one of "released", "ignored",
# or "N/A" and it's not clear which should override which
if old_state != new_state:
print(f'W: Not sure how to coalesce status {old_state} and {new_state}',
file=sys.stderr)
return old_state
def parse_status(s):
state = None
start = 0
while True:
m = _status_re.match(s[start:])
if not m:
raise SyntaxError(f'bad status {s[start:]}')
start += m.end()
# Coalesce with last status
state = _coalesce_state(state, m.group(1))
# End of field?
if start == len(s) or s[start:].isspace():
return {'state': state}
# No, must be followed by a comma separator
m = _comma_re.match(s[start:])
if not m:
raise SyntaxError(f'missing separator in{s[start:]}')
start += m.end()
|