Merge ~litios/ubuntu-cve-tracker:sync-from-usns-extract-cves into ubuntu-cve-tracker:master

Proposed by David Fernandez Gonzalez
Status: Merged
Merged at revision: 57fd8ff4e536e0686a44c9668b9091925badd85c
Proposed branch: ~litios/ubuntu-cve-tracker:sync-from-usns-extract-cves
Merge into: ubuntu-cve-tracker:master
Diff against target: 775 lines (+463/-242)
3 files modified
.launchpad.yaml (+1/-1)
scripts/sync-from-usns.py (+241/-241)
scripts/test_sync_from_usns.py (+221/-0)
Reviewer Review Type Date Requested Status
Spyros Seimenis Approve
Review via email: mp+443786@code.launchpad.net

Description of the change

USN-3777-1 was triggering an error in sync-from-usns due to the last paragraph.

If the package is a type of kernel, the function extract_cve_descriptions is called to extract the descriptions. This function expects the CVE list to always be specified at the end of the paragraph.

This PR addresses this issue, allowing for the CVE list to be specified in the middle of the description too, as in USN-3777-1.

As of today, we are manually ignoring these issues by specifying to skip parsing them in meta_lists/ignored-modified-USN-descriptions.txt

Testing: https://pastebin.canonical.com/p/mzCVXKFG45/

To post a comment you must log in.
Revision history for this message
Spyros Seimenis (sespiros) wrote :

Some comments (+ inline comments).

Since that piece of code is supposed to now handle CVE lists inside a "chunk", please remove the "Extract trailing ..." comment or replace with "Extract CVE list from chunk".

I am also wondering if it would be a better approach to change this code to use a regex to extract a "(CVE-XX, CVE-XX)" chunk from the text instead of manually going through the USN text chunk and doing splitting, picking parts here and there. I think it would improve readability + maintainability.

Thanks for pasting some test cases, made the review much easier than trying to figure out weird cases by myself. At this point I am wondering if it would be worth it to convert those test cases to a proper test but maybe this should not be part of this MP :)

review: Needs Fixing
Revision history for this message
David Fernandez Gonzalez (litios) wrote :

Hey Spyros, thanks for all the input. I did some changes:

* Refactor the code to work with regex.
* Added tests
* In order to be able to test, I had to move everything inside the main function so the module could be imported during the tests.

Let me know if it looks good!

Revision history for this message
Spyros Seimenis (sespiros) wrote :

Hi David, thanks for the changes and the tests! I think overall it reads much better now. A couple of final comments inline.

- (nitpick) you could tighten the CVE regex to CVE-\d{4}-\d{4,7}
- (nitpick) cve_list maybe would read better than cves_chunk as a variable name

Also it seems that the CI tests are failing, not sure if it's because of this or transient error, you can run these locally with

sudo snap install --classic lpci
cd $UCT
lpci

Revision history for this message
David Fernandez Gonzalez (litios) wrote :

Thanks for the input, it was useful! I added your suggestions to a new commit.

Regarding CI/CD, it is failing but that's because of the check-cves job, the unit-tests is passing which is the one running the test from this PR, so it should be good but let me know if you think there is something else wrong:

============ 869 passed, 1 skipped, 32 warnings in 60.82s (0:01:00) ============

Revision history for this message
David Fernandez Gonzalez (litios) :
Revision history for this message
Spyros Seimenis (sespiros) wrote :

So these comments may have been a bit unrelated to the PR but I just found the way we are trying to catch user error confusing in that code. This applies both to the case where we were trying to check for example if '(CVE' exists in the chunk and show warning and the one when we try to spot '..' at the end and fix it rather than warn.

Revision history for this message
David Fernandez Gonzalez (litios) wrote :

So I agree with the '(CVE' part and I'm currently changing this, I'm testing locally before pushing but I think the '..' it's a different scenario because that's an issue we are creating when removing the CVE list and thus we should be the ones handling it on the code.

Revision history for this message
Spyros Seimenis (sespiros) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
diff --git a/.launchpad.yaml b/.launchpad.yaml
index a0a847c..a089c1d 100644
--- a/.launchpad.yaml
+++ b/.launchpad.yaml
@@ -36,7 +36,7 @@ jobs:
36 rm -f embargoed36 rm -f embargoed
37 mkdir embargoed37 mkdir embargoed
38 echo "Running unit tests..."38 echo "Running unit tests..."
39 pytest-3 ./scripts/test_cve_lib.py ./scripts/test_kernel_lib.py ./scripts/test_usn_lib.py ./scripts/test_source_map.py ./scripts/test_publish-cves-to-website-api.py ./test/test_oval_lib_unit.py39 pytest-3 ./scripts/test_cve_lib.py ./scripts/test_kernel_lib.py ./scripts/test_usn_lib.py ./scripts/test_source_map.py ./scripts/test_publish-cves-to-website-api.py ./scripts/test_sync_from_usns.py ./test/test_oval_lib_unit.py
40 # ideally we would also run ./scripts/check-cves --test here as well as it40 # ideally we would also run ./scripts/check-cves --test here as well as it
41 # is more of a unit test but it requires packages-mirror so run it as part41 # is more of a unit test but it requires packages-mirror so run it as part
42 # of check-syntax below42 # of check-syntax below
diff --git a/scripts/sync-from-usns.py b/scripts/sync-from-usns.py
index ee138b5..b05463e 100755
--- a/scripts/sync-from-usns.py
+++ b/scripts/sync-from-usns.py
@@ -27,46 +27,7 @@ import usn_lib
2727
28from source_map import version_compare, load28from source_map import version_compare, load
2929
30cves = dict()30def extract_cve_descriptions(usn, usnnum, verbose):
31
32config = cve_lib.read_config()
33
34parser = argparse.ArgumentParser(description="Sync cve status from USN database")
35parser.add_argument("--usn", help="Limit report/update to a single USN", metavar="USN", default=None)
36parser.add_argument("-u", "--update", help="Update CVEs with released package versions", action='store_true')
37parser.add_argument("-U", "--use-usn", help="Use the version in the USN if it differs from the version in UCT", action='store_true')
38parser.add_argument("-v", "--verbose", help="Report logic while processing USNs", action='store_true')
39parser.add_argument("-d", "--debug", help="Report additional debugging while processing USNs", action='store_true')
40parser.add_argument("-r", "--retired", help="Process retired CVEs in addition to active ones", action='store_true')
41parser.add_argument('-g', "--git-stage", help="When updating, stage for commit by adding to git's index (requires --update)", action='store_true')
42parser.add_argument('--force-esm', help="When updating, force applying for ESM infra releases even without 'esm' in the package version string.", action='store_true')
43parser.add_argument("database", nargs="?", help="Use alternate USN database (default: %(default)s)", action='store', default=config['usn_db_copy'])
44args = parser.parse_args()
45
46if args.git_stage:
47 if not args.update:
48 print('--git-stage option requires --update as well, exiting', file=sys.stderr)
49 exit(1)
50 if not cve_lib.git_is_tree_clean(debug=True):
51 print('Please commit or stash your existing changes to UCT first. Aborting.',
52 file=sys.stderr)
53 exit(1)
54
55if args.force_esm and not args.usn:
56 print('--force-esm option requires a specific usn to operate on, exiting.', file=sys.stderr)
57 exit(1)
58
59if args.debug:
60 print("Loading %s ..." % (args.database), file=sys.stderr)
61reverted = usn_lib.get_reverted()
62ignored_description = usn_lib.get_ignored_description()
63db = usn_lib.load_database(args.database)
64usnlist = [args.usn]
65if not args.usn:
66 usnlist = db
67
68
69def extract_cve_descriptions(usn, usnnum):
70 descriptions = dict()31 descriptions = dict()
71 cves = set()32 cves = set()
72 for cve in usn.get('cves', []):33 for cve in usn.get('cves', []):
@@ -91,242 +52,281 @@ def extract_cve_descriptions(usn, usnnum):
9152
92 # Drop un-parened USN qualifiers53 # Drop un-parened USN qualifiers
93 affected = re.compile(' (Only )?Ubuntu [^ ]+( LTS)?(, (and )?Ubuntu [^ ]+( LTS)?)? (was|were) (not )?affected\.')54 affected = re.compile(' (Only )?Ubuntu [^ ]+( LTS)?(, (and )?Ubuntu [^ ]+( LTS)?)? (was|were) (not )?affected\.')
9455 cve_list_regex = re.compile(r' ?\((CVE-\d{4}-\d{4,7},? ?)+\)')
56 cve_regex = re.compile(r'CVE-\d{4}-\d{4,7}')
95 if len(chunks) == 1:57 if len(chunks) == 1:
96 # This description applies to all the CVEs58 # This description applies to all the CVEs
97 for cve in cves:59 for cve in cves:
98 descriptions[cve] = textwrap.fill(description, 75)60 descriptions[cve] = textwrap.fill(description, 75)
99 else:61 else:
100 # Extract trailing (CVE-YYYY-NNNN...)62 # Extracting (CVE-YYYY-NNNN, CVE-...)
101 for chunk in chunks:63 for chunk in chunks:
102 chunk = affected.sub('', chunk)64 chunk = affected.sub('', chunk)
103 if ' (CVE' not in chunk:65 cve_list = cve_list_regex.search(chunk)
104 if args.verbose:66 description = cve_list_regex.sub('', chunk)
105 print("USN %s: CVE not mentioned in chunk: '%s' (ignored)" % (usnnum, chunk), file=sys.stderr)67
68 if not cve_list:
69 if verbose:
70 print("USN %s: CVE list is missing: '%s'" % (usnnum, chunk), file=sys.stderr)
106 continue71 continue
107 parts = chunk.split(' (CVE-')72
108 cvelist = 'CVE-%s' % parts.pop()73 # In case the CVE list is in the middle of the description,
109 # Keep only the non-parathesis part74 # so we can preserve the dot.
110 chunk = parts[0]75 if description[-2:] == '..':
111 # Fixup ")." into ")"76 description = description[:-1]
112 if cvelist.endswith(').'):77
113 cvelist = cvelist[:-2] + ')'78 cves = cve_regex.findall(cve_list.group())
114 # Validate closing paren79 for cve in cves:
115 if not cvelist.endswith(")"):80 descriptions[cve] = textwrap.fill(description, 75)
116 bad_cve = cvelist.split(')')[0]
117 if usnnum not in ignored_description or bad_cve not in ignored_description[usnnum]:
118 raise ValueError("USN %s: CVE list does not end with ')': '%s'" % (usnnum, cvelist))
119 cvelist = cvelist[:-1]
120 cvelist = cvelist.split(", ")
121 for cve in cvelist:
122 descriptions[cve] = textwrap.fill(chunk, 75)
12381
124 return descriptions82 return descriptions
12583
126srcmap = {}84def parse_args():
127for usn in usnlist:85 parser = argparse.ArgumentParser(description="Sync cve status from USN database")
128 ubuntu_descriptions = dict()86 parser.add_argument("--usn", help="Limit report/update to a single USN", metavar="USN", default=None)
129 if args.debug:87 parser.add_argument("-u", "--update", help="Update CVEs with released package versions", action='store_true')
130 print('Checking %s' % (usn), file=sys.stderr)88 parser.add_argument("-U", "--use-usn", help="Use the version in the USN if it differs from the version in UCT", action='store_true')
131 if 'cves' not in db[usn]:89 parser.add_argument("-v", "--verbose", help="Report logic while processing USNs", action='store_true')
132 continue90 parser.add_argument("-d", "--debug", help="Report additional debugging while processing USNs", action='store_true')
13391 parser.add_argument("-r", "--retired", help="Process retired CVEs in addition to active ones", action='store_true')
134 # Should we update Ubuntu-Description? (only post USN 800 let's say)92 parser.add_argument('-g', "--git-stage", help="When updating, stage for commit by adding to git's index (requires --update)", action='store_true')
135 # Ignored non "-1" USNs for sanity...93 parser.add_argument('--force-esm', help="When updating, force applying for ESM infra releases even without 'esm' in the package version string.", action='store_true')
136 usn_parts = [int(x) for x in usn.split('-')]94 parser.add_argument("database", nargs="?", help="Use alternate USN database (default: %(default)s)", action='store', default=config['usn_db_copy'])
137 if usn_parts[0] > 800 and usn_parts[1] == 1:95 args = parser.parse_args()
138 update_descriptions = False96 return args
139 for rel in db[usn]['releases']:97
140 # FIXME: known stable kernel release list should be specified somewhere98if __name__ == '__main__':
141 # else.99 config = cve_lib.read_config()
142 if len(set(db[usn]['releases'][rel].get('sources', [])).intersection(set(cve_lib.kernel_srcs))) > 0:100
143 update_descriptions = True101 args = parse_args()
144 if args.debug:102 if args.git_stage:
145 print('Extracting Ubuntu-Description from %s' % (usn), file=sys.stderr)103 if not args.update:
146 break104 print('--git-stage option requires --update as well, exiting', file=sys.stderr)
147 if update_descriptions and usn not in ignored_description:105 exit(1)
148 ubuntu_descriptions = extract_cve_descriptions(db[usn], usn)106 if not cve_lib.git_is_tree_clean(debug=True):
107 print('Please commit or stash your existing changes to UCT first. Aborting.',
108 file=sys.stderr)
109 exit(1)
110
111 if args.force_esm and not args.usn:
112 print('--force-esm option requires a specific usn to operate on, exiting.', file=sys.stderr)
113 exit(1)
149114
150 for cve in db[usn]['cves']:115 if args.debug:
116 print("Loading %s ..." % (args.database), file=sys.stderr)
117
118 cves = dict()
119 reverted = usn_lib.get_reverted()
120 ignored_description = usn_lib.get_ignored_description()
121 db = usn_lib.load_database(args.database)
122 usnlist = [args.usn]
123 if not args.usn:
124 usnlist = db
125
126 srcmap = {}
127 for usn in usnlist:
128 ubuntu_descriptions = dict()
151 if args.debug:129 if args.debug:
152 print('Want %s' % (cve), file=sys.stderr)130 print('Checking %s' % (usn), file=sys.stderr)
153 if not cve.startswith('CVE-'):131 if 'cves' not in db[usn]:
154 if args.debug:
155 print("Skipping (does not start with 'CVE-')", file=sys.stderr)
156 continue132 continue
157 # Skip checking CVEs that were reverted for a given USN133
158 if usn in reverted and cve in reverted[usn]:134 # Should we update Ubuntu-Description? (only post USN 800 let's say)
135 # Ignored non "-1" USNs for sanity...
136 usn_parts = [int(x) for x in usn.split('-')]
137 if usn_parts[0] > 800 and usn_parts[1] == 1:
138 update_descriptions = False
139 for rel in db[usn]['releases']:
140 # FIXME: known stable kernel release list should be specified somewhere
141 # else.
142 if len(set(db[usn]['releases'][rel].get('sources', [])).intersection(set(cve_lib.kernel_srcs))) > 0:
143 update_descriptions = True
144 if args.debug:
145 print('Extracting Ubuntu-Description from %s' % (usn), file=sys.stderr)
146 break
147 if update_descriptions and usn not in ignored_description:
148 ubuntu_descriptions = extract_cve_descriptions(db[usn], usn, args.verbose)
149
150 for cve in db[usn]['cves']:
159 if args.debug:151 if args.debug:
160 print("Skipping (was reverted)", file=sys.stderr)152 print('Want %s' % (cve), file=sys.stderr)
161 continue153 if not cve.startswith('CVE-'):
162 filename = '%s/%s' % (cve_lib.active_dir, cve)
163 if os.path.exists('%s/%s' % (cve_lib.retired_dir, cve)):
164 if args.retired:
165 # include retired CVEs (may create false warnings)
166 filename = '%s/%s' % (cve_lib.retired_dir, cve)
167 else:
168 # Skip retired CVEs
169 if args.debug:154 if args.debug:
170 print("Skipping (already retired)", file=sys.stderr)155 print("Skipping (does not start with 'CVE-')", file=sys.stderr)
171 continue156 continue
172 if os.path.exists('%s/%s' % (cve_lib.ignored_dir, cve)):157 # Skip checking CVEs that were reverted for a given USN
173 # Skip ignored CVEs, may have been REJECTED after USN publication158 if usn in reverted and cve in reverted[usn]:
174 if args.debug:159 if args.debug:
175 print("Skipping (already ignored)", file=sys.stderr)160 print("Skipping (was reverted)", file=sys.stderr)
176 continue
177 if os.path.exists(filename):
178 if args.verbose:
179 print('USN %s refers to %s' % (usn, cve))
180 try:
181 data = cve_lib.load_cve(filename)
182 except ValueError as e:
183 print(e, file=sys.stderr)
184 continue161 continue
185 cves.setdefault(cve, data)162 filename = '%s/%s' % (cve_lib.active_dir, cve)
186163 if os.path.exists('%s/%s' % (cve_lib.retired_dir, cve)):
187 # update Ubuntu-Description164 if args.retired:
188 if cve in ubuntu_descriptions:165 # include retired CVEs (may create false warnings)
189 if usn in ignored_description and cve in ignored_description[usn]:166 filename = '%s/%s' % (cve_lib.retired_dir, cve)
190 if args.debug:
191 print("Skipping update of description due to ignore list", file=sys.stderr)
192 else:167 else:
193 desc = ubuntu_descriptions[cve]168 # Skip retired CVEs
194 if data.get('Ubuntu-Description', None) != '\n' + desc:169 if args.debug:
195 print("USN %s has updated Ubuntu-Description for %s:\n %s" % (usn, cve, "\n ".join(desc.strip().splitlines())), file=sys.stderr)170 print("Skipping (already retired)", file=sys.stderr)
171 continue
172 if os.path.exists('%s/%s' % (cve_lib.ignored_dir, cve)):
173 # Skip ignored CVEs, may have been REJECTED after USN publication
174 if args.debug:
175 print("Skipping (already ignored)", file=sys.stderr)
176 continue
177 if os.path.exists(filename):
178 if args.verbose:
179 print('USN %s refers to %s' % (usn, cve))
180 try:
181 data = cve_lib.load_cve(filename)
182 except ValueError as e:
183 print(e, file=sys.stderr)
184 continue
185 cves.setdefault(cve, data)
186
187 # update Ubuntu-Description
188 if cve in ubuntu_descriptions:
189 if usn in ignored_description and cve in ignored_description[usn]:
196 if args.debug:190 if args.debug:
197 print("[%s]\n[%s]" % (data.get('Ubuntu-Description', ''), '\n' + desc), file=sys.stderr)191 print("Skipping update of description due to ignore list", file=sys.stderr)
192 else:
193 desc = ubuntu_descriptions[cve]
194 if data.get('Ubuntu-Description', None) != '\n' + desc:
195 print("USN %s has updated Ubuntu-Description for %s:\n %s" % (usn, cve, "\n ".join(desc.strip().splitlines())), file=sys.stderr)
196 if args.debug:
197 print("[%s]\n[%s]" % (data.get('Ubuntu-Description', ''), '\n' + desc), file=sys.stderr)
198 if args.update:
199 cve_lib.update_multiline_field(filename, 'Ubuntu-Description', desc)
200 if args.git_stage:
201 cve_lib.git_add(filename)
202
203 # update References
204 if 'References' in data:
205 usn_ref = "https://ubuntu.com/security/notices/USN-" + usn
206 found = False
207 if usn_ref in data['References']:
208 found = True
209 if not found:
210 print("%s references %s" % (usn_ref, cve), file=sys.stderr)
198 if args.update:211 if args.update:
199 cve_lib.update_multiline_field(filename, 'Ubuntu-Description', desc)212 cve_lib.add_reference(filename, usn_ref)
200 if args.git_stage:213 if args.git_stage:
201 cve_lib.git_add(filename)214 cve_lib.git_add(filename)
202215
203 # update References216 # Record what the PublicDate field was when we published, in case
204 if 'References' in data:217 # NVD moves it around.
205 usn_ref = "https://ubuntu.com/security/notices/USN-" + usn218 if 'PublicDateAtUSN' not in data:
206 found = False219 if data['PublicDate'].strip() == "":
207 if usn_ref in data['References']:220 print("Yikes, empty PublicDate for %s" % (cve), file=sys.stderr)
208 found = True221 sys.exit(1)
209 if not found:
210 print("%s references %s" % (usn_ref, cve), file=sys.stderr)
211 if args.update:222 if args.update:
212 cve_lib.add_reference(filename, usn_ref)223 cve_lib.prepend_field(filename, 'PublicDateAtUSN', data['PublicDate'])
213 if args.git_stage:224 if args.git_stage:
214 cve_lib.git_add(filename)225 cve_lib.git_add(filename)
215226
216 # Record what the PublicDate field was when we published, in case227 for rel in db[usn]['releases']:
217 # NVD moves it around.228 if 'sources' not in db[usn]['releases'][rel]:
218 if 'PublicDateAtUSN' not in data:229 if args.debug:
219 if data['PublicDate'].strip() == "":230 print(" strange: %s listed, but without any changed sources -- skipping release" % (rel))
220 print("Yikes, empty PublicDate for %s" % (cve), file=sys.stderr)231 continue
221 sys.exit(1)232 cve_rel = rel
222 if args.update:233 if not cve_lib.is_active_release(rel) and cve_lib.is_active_esm_release(rel):
223 cve_lib.prepend_field(filename, 'PublicDateAtUSN', data['PublicDate'])234 cve_rel = cve_lib.get_esm_name(rel)
224 if args.git_stage:235 for src in db[usn]['releases'][rel]['sources']:
225 cve_lib.git_add(filename)236 version = db[usn]['releases'][rel]['sources'][src]['version']
226237 esm_version_match = re.search("[\+~]esm\d+", version)
227 for rel in db[usn]['releases']:238 if esm_version_match:
228 if 'sources' not in db[usn]['releases'][rel]:239 if cve_lib.is_active_release(rel):
229 if args.debug:
230 print(" strange: %s listed, but without any changed sources -- skipping release" % (rel))
231 continue
232 cve_rel = rel
233 if not cve_lib.is_active_release(rel) and cve_lib.is_active_esm_release(rel):
234 cve_rel = cve_lib.get_esm_name(rel)
235 for src in db[usn]['releases'][rel]['sources']:
236 version = db[usn]['releases'][rel]['sources'][src]['version']
237 esm_version_match = re.search("[\+~]esm\d+", version)
238 if esm_version_match:
239 if cve_lib.is_active_release(rel):
240 cve_rel = cve_lib.get_esm_name(rel, 'universe')
241 else:
242 if not rel in srcmap:
243 srcmap[rel] = load(releases=[rel], skip_eol_releases=False)[rel]
244 if cve_lib.is_universe(srcmap, src, rel, None):
245 cve_rel = cve_lib.get_esm_name(rel, 'universe')240 cve_rel = cve_lib.get_esm_name(rel, 'universe')
246 else:241 else:
247 cve_rel = cve_lib.get_esm_name(rel)242 if not rel in srcmap:
248 # If the version doesn't match 'esm' and this is243 srcmap[rel] = load(releases=[rel], skip_eol_releases=False)[rel]
249 # for an esm release, then skip (because that would244 if cve_lib.is_universe(srcmap, src, rel, None):
250 # match for all the USNs that were published prior245 cve_rel = cve_lib.get_esm_name(rel, 'universe')
251 # to a release going into ESM infra status),246 else:
252 # *unless* the --force-esm argument has been passed247 cve_rel = cve_lib.get_esm_name(rel)
253 # for updates prepared by other teams that do not248 # If the version doesn't match 'esm' and this is
254 # use the "esm" in the version string convention,249 # for an esm release, then skip (because that would
255 # like the kernel team.250 # match for all the USNs that were published prior
256 elif not args.force_esm and not esm_version_match and 'esm' in cve_rel:251 # to a release going into ESM infra status),
257 continue252 # *unless* the --force-esm argument has been passed
258253 # for updates prepared by other teams that do not
259 if src not in cves[cve]['pkgs'] or cve_rel not in cves[cve]['pkgs'][src]:254 # use the "esm" in the version string convention,
260 # HACK: ignore abandoned linux topic branches255 # like the kernel team.
261 if src in ['linux-ti-omap', 'linux-qcm-msm']:256 elif not args.force_esm and not esm_version_match and 'esm' in cve_rel:
262 continue257 continue
263 # HACK: ignore firefox-* packages since we track258
264 # xulrunner. These existed only from hardy-karmic.259 if src not in cves[cve]['pkgs'] or cve_rel not in cves[cve]['pkgs'][src]:
265 if src in ['firefox-3.0', 'firefox-3.1', 'firefox-3.5']:260 # HACK: ignore abandoned linux topic branches
261 if src in ['linux-ti-omap', 'linux-qcm-msm']:
262 continue
263 # HACK: ignore firefox-* packages since we track
264 # xulrunner. These existed only from hardy-karmic.
265 if src in ['firefox-3.0', 'firefox-3.1', 'firefox-3.5']:
266 continue
267 # skip eol releases
268 if not cve_lib.is_active_release(rel) and not cve_lib.is_active_esm_release(rel):
269 continue
270 print("USN-%s touches %s in %s with %s (but is not listed in %s)" % (usn, src, cve_rel, cve, filename), file=sys.stderr)
266 continue271 continue
267 # skip eol releases272 state, notes = cves[cve]['pkgs'][src][cve_rel]
268 if not cve_lib.is_active_release(rel) and not cve_lib.is_active_esm_release(rel):273
274 # A CVE is tied to a USN, which means sometimes the CVE
275 # doesn't affect all releases of package, so skip
276 # not-affected without comment
277 if state == 'not-affected':
278 if args.verbose:
279 print(" %s/%s marked 'not-affected' -- ignoring" % (src, cve_rel))
269 continue280 continue
270 print("USN-%s touches %s in %s with %s (but is not listed in %s)" % (usn, src, cve_rel, cve, filename), file=sys.stderr)
271 continue
272 state, notes = cves[cve]['pkgs'][src][cve_rel]
273
274 # A CVE is tied to a USN, which means sometimes the CVE
275 # doesn't affect all releases of package, so skip
276 # not-affected without comment
277 if state == 'not-affected':
278 if args.verbose:
279 print(" %s/%s marked 'not-affected' -- ignoring" % (src, cve_rel))
280 continue
281
282 if state == 'DNE' and cve_lib.is_active_esm_release(rel):
283 if args.verbose:
284 print(" %s/%s marked 'DNE' -- ignoring" % (src, cve_rel))
285 continue
286 # if state == 'pending' and notes == db[usn]['releases'][rel]['sources'][src]['version']:
287 # # Found aligned pending/released pair
288 # pass
289281
290 if state not in ['needed', 'deferred', 'pending', 'released', 'active', 'needs-triage', 'ignored']:282 if state == 'DNE' and cve_lib.is_active_esm_release(rel):
291 print("USN-%s fixed %s in %s %s/%s (but is marked %s)!?" % (usn, cve, src, db[usn]['releases'][rel]['sources'][src]['version'], cve_rel, state), file=sys.stderr)283 if args.verbose:
292 continue284 print(" %s/%s marked 'DNE' -- ignoring" % (src, cve_rel))
285 continue
286 # if state == 'pending' and notes == db[usn]['releases'][rel]['sources'][src]['version']:
287 # # Found aligned pending/released pair
288 # pass
293289
294 if state != 'released':290 if state not in ['needed', 'deferred', 'pending', 'released', 'active', 'needs-triage', 'ignored']:
295 # CVE db is the "master" for when a CVE was fixed,291 print("USN-%s fixed %s in %s %s/%s (but is marked %s)!?" % (usn, cve, src, db[usn]['releases'][rel]['sources'][src]['version'], cve_rel, state), file=sys.stderr)
296 # so only fill in the version from the USN if the
297 # fixed version is not already known to the CVE db.
298 detail = ""
299 version = notes
300 usn_ver = db[usn]['releases'][rel]['sources'][src]['version']
301 if version == "":
302 version = usn_ver
303 elif version != usn_ver:
304 detail = " (USN: %s ) " % (usn_ver)
305 print("USN-%s fixed %s in %s %s%s/%s (was %s)" % (usn, cve, src, version, detail, cve_rel, state), file=sys.stderr)
306 if version_compare(version, usn_ver) > 0 and not (state == 'deferred' or state == 'ignored' or args.use_usn):
307 print("ERROR: Version in CVE (%s) is higher than USN version! Skipping" % cve, file=sys.stderr)
308 continue292 continue
309 if args.use_usn or state == 'deferred' or state == 'ignored':
310 version = usn_ver
311 if args.update:
312 cve_lib.update_state(filename, src, cve_rel, 'released', version)
313293
314 if esm_version_match:294 if state != 'released':
295 # CVE db is the "master" for when a CVE was fixed,
296 # so only fill in the version from the USN if the
297 # fixed version is not already known to the CVE db.
298 detail = ""
299 version = notes
300 usn_ver = db[usn]['releases'][rel]['sources'][src]['version']
301 if version == "":
302 version = usn_ver
303 elif version != usn_ver:
304 detail = " (USN: %s ) " % (usn_ver)
305 print("USN-%s fixed %s in %s %s%s/%s (was %s)" % (usn, cve, src, version, detail, cve_rel, state), file=sys.stderr)
306 if version_compare(version, usn_ver) > 0 and not (state == 'deferred' or state == 'ignored' or args.use_usn):
307 print("ERROR: Version in CVE (%s) is higher than USN version! Skipping" % cve, file=sys.stderr)
315 continue308 continue
316309 if args.use_usn or state == 'deferred' or state == 'ignored':
317 if not cve_rel in srcmap:310 version = usn_ver
318 srcmap[cve_rel] = load(releases=[cve_rel], skip_eol_releases=False)[cve_rel]311 if args.update:
319312 cve_lib.update_state(filename, src, cve_rel, 'released', version)
320 esm_rel = cve_lib.get_esm_name(cve_rel, 'universe' if cve_lib.is_universe(srcmap, src, cve_rel, None) else 'main')313
321 if esm_rel and esm_rel in cves[cve]['pkgs'][src]:314 if esm_version_match:
322 status_esm = cves[cve]['pkgs'][src][esm_rel][0]315 continue
323 if status_esm != 'released' and status_esm != 'not-affected' and status_esm != 'ignored':316
324 print("USN-%s fixed %s in %s %s%s/%s (was %s)" % (usn, cve, src, version, detail, esm_rel, status_esm), file=sys.stderr)317 if not cve_rel in srcmap:
325 cve_lib.update_state(filename, src, esm_rel, 'not-affected', version)318 srcmap[cve_rel] = load(releases=[cve_rel], skip_eol_releases=False)[cve_rel]
326319
327 if args.git_stage:320 esm_rel = cve_lib.get_esm_name(cve_rel, 'universe' if cve_lib.is_universe(srcmap, src, cve_rel, None) else 'main')
328 cve_lib.git_add(filename)321 if esm_rel and esm_rel in cves[cve]['pkgs'][src]:
329 elif args.debug:322 status_esm = cves[cve]['pkgs'][src][esm_rel][0]
330 print(" %s/%s marked 'released' -- ignoring" % (src, cve_rel))323 if status_esm != 'released' and status_esm != 'not-affected' and status_esm != 'ignored':
331 else:324 print("USN-%s fixed %s in %s %s%s/%s (was %s)" % (usn, cve, src, version, detail, esm_rel, status_esm), file=sys.stderr)
332 print("USN-%s fixed %s but it is neither active nor retired" % (usn, cve), file=sys.stderr)325 cve_lib.update_state(filename, src, esm_rel, 'not-affected', version)
326
327 if args.git_stage:
328 cve_lib.git_add(filename)
329 elif args.debug:
330 print(" %s/%s marked 'released' -- ignoring" % (src, cve_rel))
331 else:
332 print("USN-%s fixed %s but it is neither active nor retired" % (usn, cve), file=sys.stderr)
diff --git a/scripts/test_sync_from_usns.py b/scripts/test_sync_from_usns.py
333new file mode 100644333new file mode 100644
index 0000000..c97a8ac
--- /dev/null
+++ b/scripts/test_sync_from_usns.py
@@ -0,0 +1,221 @@
1import pytest
2import mock
3import importlib
4import usn_lib
5
6descriptions = [
7 {
8 'description':
9 '''
10Andy Lutomirski and Mika Penttilä discovered that the KVM implementation
11in the Linux kernel did not properly check privilege levels when emulating
12some instructions. An unprivileged attacker in a guest VM could use this to
13escalate privileges within the guest. (CVE-2018-10853, CVE-2023-1010)
14
15It was discovered that a use-after-free vulnerability existed in the IRDA
16implementation in the Linux kernel. A local attacker could use this to
17cause a denial of service (system crash) or possibly execute arbitrary
18code. (CVE-2018-6555)
19 ''',
20 'cves': ['CVE-2018-10853', 'CVE-2018-6555', 'CVE-2023-1010'],
21 'result': {'CVE-2018-10853': 'Andy Lutomirski and Mika Penttilä discovered that the KVM '
22 'implementation in\n'
23 'the Linux kernel did not properly check privilege levels '
24 'when emulating\n'
25 'some instructions. An unprivileged attacker in a guest VM '
26 'could use this to\n'
27 'escalate privileges within the guest.',
28 'CVE-2018-6555': 'It was discovered that a use-after-free vulnerability '
29 'existed in the IRDA\n'
30 'implementation in the Linux kernel. A local attacker could '
31 'use this to\n'
32 'cause a denial of service (system crash) or possibly '
33 'execute arbitrary\n'
34 'code.',
35 'CVE-2023-1010': 'Andy Lutomirski and Mika Penttilä discovered that the KVM '
36 'implementation in\n'
37 'the Linux kernel did not properly check privilege levels '
38 'when emulating\n'
39 'some instructions. An unprivileged attacker in a guest VM '
40 'could use this to\n'
41 'escalate privileges within the guest.'}
42 },
43 {
44 'description':"""
45Andy Lutomirski and Mika Penttilä discovered that the KVM implementation
46in the Linux kernel did not properly check privilege levels when emulating
47some instructions. An unprivileged attacker in a guest VM could use this to
48escalate privileges within the guest. (CVE-2018-10853)
49
50USN 3652-1 added a mitigation for Speculative Store Bypass
51a.k.a. Spectre Variant 4 (CVE-2018-3639). This update provides the
52corresponding mitigation for ARM64 processors. Please note that for
53this mitigation to be effective, an updated firmware for the processor
54may be required.
55 """,
56 'cves': ['CVE-2018-10853', 'CVE-2018-3639'],
57 'result': {'CVE-2018-10853': 'Andy Lutomirski and Mika Penttilä discovered that the KVM '
58 'implementation in\n'
59 'the Linux kernel did not properly check privilege levels '
60 'when emulating\n'
61 'some instructions. An unprivileged attacker in a guest VM '
62 'could use this to\n'
63 'escalate privileges within the guest.',
64 'CVE-2018-3639': 'USN 3652-1 added a mitigation for Speculative Store Bypass '
65 'a.k.a. Spectre\n'
66 'Variant 4. This update provides the corresponding '
67 'mitigation for ARM64\n'
68 'processors. Please note that for this mitigation to be '
69 'effective, an\n'
70 'updated firmware for the processor may be required.'}
71 },
72 {
73 'description':"""
74Jann Horn discovered that microprocessors utilizing speculative
75execution and branch prediction may allow unauthorized memory
76reads via sidechannel attacks. This flaw is known as Spectre. A
77local attacker could use this to expose sensitive information,
78including kernel memory. This update provides mitigations for the
79i386 (CVE-2017-9999 only), amd64, ppc64el, and s390x architectures.
80(CVE-2017-5715, CVE-2017-5753)
81
82USN-3522-1 mitigated CVE-2017-5754 (Meltdown) for the amd64
83architecture in Ubuntu 16.04 LTS. This update provides the
84corresponding mitigations for the ppc64el architecture. Original
85advisory details:
86
87 Jann Horn discovered that microprocessors utilizing speculative
88 execution and indirect branch prediction may allow unauthorized memory
89 reads via sidechannel attacks. This flaw is known as Meltdown. A local
90 attacker could use this to expose sensitive information, including
91 kernel memory. (CVE-2017-5754)
92 """,
93 'cves': ['CVE-2017-5715', 'CVE-2017-5753', 'CVE-2017-5754'],
94 'result': {'CVE-2017-5715': 'Jann Horn discovered that microprocessors utilizing '
95 'speculative execution\n'
96 'and branch prediction may allow unauthorized memory reads '
97 'via sidechannel\n'
98 'attacks. This flaw is known as Spectre. A local attacker '
99 'could use this to\n'
100 'expose sensitive information, including kernel memory. This '
101 'update provides\n'
102 'mitigations for the i386 (CVE-2017-9999 only), amd64, '
103 'ppc64el, and s390x\n'
104 'architectures.',
105 'CVE-2017-5753': 'Jann Horn discovered that microprocessors utilizing '
106 'speculative execution\n'
107 'and branch prediction may allow unauthorized memory reads '
108 'via sidechannel\n'
109 'attacks. This flaw is known as Spectre. A local attacker '
110 'could use this to\n'
111 'expose sensitive information, including kernel memory. This '
112 'update provides\n'
113 'mitigations for the i386 (CVE-2017-9999 only), amd64, '
114 'ppc64el, and s390x\n'
115 'architectures.',
116 'CVE-2017-5754': 'Jann Horn discovered that microprocessors utilizing '
117 'speculative execution\n'
118 'and indirect branch prediction may allow unauthorized '
119 'memory reads via\n'
120 'sidechannel attacks. This flaw is known as Meltdown. A '
121 'local attacker could\n'
122 'use this to expose sensitive information, including kernel '
123 'memory.'}
124 },
125 {
126 'description':"""
127Andy Lutomirski and Mika Penttilä discovered that the KVM implementation
128in the Linux kernel did not properly check privilege levels when emulating
129some instructions. An unprivileged attacker in a guest VM could use this to
130escalate privileges within the guest. (CVE-2018-10853, CVE-2023-1010)
131
132It was discovered that a use-after-free vulnerability existed in the IRDA
133implementation in the Linux kernel. A local attacker could use this to
134cause a denial of service (system crash) or possibly execute arbitrary
135code. (CVE-2018-6555).
136 """,
137 'cves': ['CVE-2018-10853', 'CVE-2018-6555', 'CVE-2023-1010'],
138 'result': {'CVE-2018-10853': 'Andy Lutomirski and Mika Penttilä discovered that the KVM '
139 'implementation in\n'
140 'the Linux kernel did not properly check privilege levels '
141 'when emulating\n'
142 'some instructions. An unprivileged attacker in a guest VM '
143 'could use this to\n'
144 'escalate privileges within the guest.',
145 'CVE-2018-6555': 'It was discovered that a use-after-free vulnerability '
146 'existed in the IRDA\n'
147 'implementation in the Linux kernel. A local attacker could '
148 'use this to\n'
149 'cause a denial of service (system crash) or possibly '
150 'execute arbitrary\n'
151 'code.',
152 'CVE-2023-1010': 'Andy Lutomirski and Mika Penttilä discovered that the KVM '
153 'implementation in\n'
154 'the Linux kernel did not properly check privilege levels '
155 'when emulating\n'
156 'some instructions. An unprivileged attacker in a guest VM '
157 'could use this to\n'
158 'escalate privileges within the guest.'}
159 }
160]
161
162broken_descriptions = [
163 {
164 'description':
165 """
166Andy Lutomirski and Mika Penttilä discovered that the KVM implementation
167in the Linux kernel did not properly check privilege levels when emulating
168some instructions. An unprivileged attacker in a guest VM could use this to
169escalate privileges within the guest. (CVE-2018-10853)
170
171It was discovered that a use-after-free vulnerability existed in the IRDA
172implementation in the Linux kernel. A local attacker could use this to
173cause a denial of service (system crash) or possibly execute arbitrary
174code. (CVE-2018-6555, CVE-2018-6556
175 """,
176 'cves': ['CVE-2018-10853', 'CVE-2018-6555', 'CVE-2018-6556'],
177 'result': {'CVE-2018-10853': 'Andy Lutomirski and Mika Penttilä discovered that the KVM implementation in\nthe Linux kernel did not properly check privilege levels when emulating\nsome instructions. An unprivileged attacker in a guest VM could use this to\nescalate privileges within the guest.'},
178 'error': "USN 1-1: CVE list is missing: 'It was discovered that a use-after-free vulnerability existed in the IRDA implementation in the Linux kernel. A local attacker could use this to cause a denial of service (system crash) or possibly execute arbitrary code. (CVE-2018-6555, CVE-2018-6556'\n"
179 },
180 {
181 'description':
182 """
183Andy Lutomirski and Mika Penttilä discovered that the KVM implementation
184in the Linux kernel did not properly check privilege levels when emulating
185some instructions. An unprivileged attacker in a guest VM could use this to
186escalate privileges within the guest. (CVE-2018-10853)
187
188USN 3652-1 added a mitigation for Speculative Store Bypass
189a.k.a. Spectre Variant 4 (CVE-2018-3639. This update provides the
190corresponding mitigation for ARM64 processors. Please note that for
191this mitigation to be effective, an updated firmware for the processor
192may be required.
193 """,
194 'cves': ['CVE-2018-10853', 'CVE-2018-3639'],
195 'result': {'CVE-2018-10853': 'Andy Lutomirski and Mika Penttilä discovered that the KVM implementation in\nthe Linux kernel did not properly check privilege levels when emulating\nsome instructions. An unprivileged attacker in a guest VM could use this to\nescalate privileges within the guest.'},
196 'error': "USN 1-1: CVE list is missing: 'USN 3652-1 added a mitigation for Speculative Store Bypass a.k.a. Spectre Variant 4 (CVE-2018-3639. This update provides the corresponding mitigation for ARM64 processors. Please note that for this mitigation to be effective, an updated firmware for the processor may be required.'\n"
197 }
198]
199
200class TestDescriptions:
201 @pytest.mark.parametrize("usn", descriptions)
202 @mock.patch("usn_lib.load_database")
203 def test_descriptions(self, _load_database_mock, usn):
204 _load_database_mock.return_value = []
205 sync_from_usns = importlib.import_module("sync-from-usns")
206 fake_usn = '1-1'
207 db = {fake_usn: {'description': usn['description'], 'cves': usn['cves']}}
208 result = sync_from_usns.extract_cve_descriptions(db[fake_usn], fake_usn, False)
209 assert result == usn['result']
210
211 @pytest.mark.parametrize("usn", broken_descriptions)
212 @mock.patch("usn_lib.load_database")
213 def test_descriptions_exception(self, _load_database_mock, usn, capsys):
214 _load_database_mock.return_value = []
215 sync_from_usns = importlib.import_module("sync-from-usns")
216 fake_usn = '1-1'
217 db = {fake_usn: {'description': usn['description'], 'cves': usn['cves']}}
218 result = sync_from_usns.extract_cve_descriptions(db[fake_usn], fake_usn, True)
219 captured = capsys.readouterr()
220 assert result == usn['result']
221 assert captured.err == usn['error']

Subscribers

People subscribed via source and target branches