Merge ~litios/ubuntu-cve-tracker:customer_ppa_metadata into ubuntu-cve-tracker:master

Proposed by David Fernandez Gonzalez
Status: Rejected
Rejected by: David Fernandez Gonzalez
Proposed branch: ~litios/ubuntu-cve-tracker:customer_ppa_metadata
Merge into: ubuntu-cve-tracker:master
Diff against target: 228 lines (+102/-42)
8 files modified
scripts/cve_lib.py (+32/-42)
scripts/test_cve_lib.py (+36/-0)
test/subprojects/bar/focal/supported.txt (+1/-0)
test/subprojects/bar/priority-critical (+8/-0)
test/subprojects/bar/priority-invalid-value (+8/-0)
test/subprojects/foo/focal/supported.txt (+1/-0)
test/subprojects/foo/priority-critical (+8/-0)
test/subprojects/foo/priority-invalid-value (+8/-0)
Reviewer Review Type Date Requested Status
David Fernandez Gonzalez Disapprove
Emilia Torino Pending
Eduardo Barretto Pending
Review via email: mp+442260@code.launchpad.net

Commit message

Currently, only UCT CVEs could contain metadata fields. This commit
allows other project CVEs to have metadata, that will be loaded
into the main structure under the External-Metadata tag

Description of the change

The following fields have been considered for the external CVEs:

> 'Assigned-to', 'Notes', 'Priority', 'References'

The main motivation behind this change is so we can assign a different person to a subproject CVE as well as priority, as that may differ from the one designed in the Ubuntu CVE.

If metadata is present on the subproject CVE, it will be picked up when loading the main CVE file. If not, it will simply omit it.

Example: https://pastebin.canonical.com/p/dXnxqm3Yyg/
Example with error: https://pastebin.canonical.com/p/66S2Mp7JYJ/

To post a comment you must log in.
Revision history for this message
Eduardo Barretto (ebarretto) wrote :

hey, thanks for this changes, before checking it, could you please run and paste the output:
$ python3-coverage run -a -m pytest scripts/

I also wonder if we should move those tests hidden inside $UCT/scripts to $UCT/test. But this could be done in a separate PR.

Revision history for this message
Alex Murray (alexmurray) wrote :

I am a bit concerned that introducing a new field 'External-Metadata' may be a bit unmaintainable as we have to remember to always check this and process it separately.

I wonder if instead it would be better to introduce the concept of making these element first class, and so making the top-level fields of Assigned-to, Notes, Priority and References always be dictionaries, keyed on the package name - this would mean a lot more refactoring but I wonder if it may be better in the long-run?

Note this is just an idea, so feel free to ignore but I would be interested to know others thoughts on it.

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

To give a little bit of reasoning: my idea was to provide a new field to make this whole extra metadata completely optional and also because of potential new fields that could appear for special projects.

To treat that information as an appendix that may or may not exist and that we would use in customer-specific tooling instead of UCT.

But I like the idea of moving those fields to dictionaries. That would make the structure look cleaner. I was worried about the repercussions of those changes, as some of those fields are published and we would need to do some filtering over those fields.

One thing that I didn't understand is the keyed by package name. As of today, we are "bundling" those fields by the project (i.e. Ubuntu CVE and subproject CVE). Are you proposing to have those split by package? I was thinking about it as https://pastebin.canonical.com/p/M9QBWSwQpx/

I would be happy to come up with a proposal for the dictionary idea though.

Revision history for this message
Alex Murray (alexmurray) wrote :

Ah that seems like a better idea to have them keyed by projects rather than packages - since you are all in the room together in Prague, perhaps see what others think and if you can get some consensus, then go ahead with a more formal proposal?

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

Sure, will do! We will try to talk about it in the "All the tech debt" meeting.
Thanks for taking the time to review it

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

As discussed during the meeting, AlexM's idea with subprojects keys seems like the best approach.
We may want to also extend this for the pkgs, as right now everything is on the same level.

review: Disapprove

Unmerged commits

5ba83c0... by David Fernandez Gonzalez

cve_lib: add tests for external project metadata CVEs

Failed
[SUCCEEDED] unit-tests:0 (build)
[FAILED] check-cves:0 (build)
12 of 2 results
476f5b9... by David Fernandez Gonzalez

cve_lib: Allow metadata fields on external project CVEs

Currently, only UCT CVEs could contain metadata fields. This commit
allows other project CVEs to have metadata, that will be loaded
into the main structure under the External-Metadata tag

Failed
[SUCCEEDED] unit-tests:0 (build)
[FAILED] check-cves:0 (build)
12 of 2 results

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
diff --git a/scripts/cve_lib.py b/scripts/cve_lib.py
index d116f48..afd7749 100755
--- a/scripts/cve_lib.py
+++ b/scripts/cve_lib.py
@@ -1993,47 +1993,35 @@ class NotesParser(object):
19931993
19941994
19951995
1996def amend_external_subproject_pkg(cve, data, srcmap, amendments, code, msg):
1997 linenum = 0
1998 for line in amendments.splitlines():
1999 linenum += 1
2000 if len(line) == 0 or line.startswith('#') or line.startswith(' '):
2001 continue
2002 try:
2003 field, value = line.split(':', 1)
2004 field = field.strip()
2005 value = value.strip()
2006 except ValueError as e:
2007 msg += "%s: bad line '%s' (%s)\n" % (cve, line, e)
2008 code = EXIT_FAIL
2009 return code, msg
2010
2011 if '_' in field:
2012 success, pkg, release, state, details, code, msg = parse_cve_release_package_field(cve, field, data, value, code, msg, linenum)
2013 if not success:
2014 return code, msg
2015
2016 data.setdefault("pkgs", dict())
2017 data["pkgs"].setdefault(pkg, dict())
2018 srcmap["pkgs"].setdefault(pkg, dict())
2019 # override existing release info if it exists
2020 data["pkgs"][pkg][release] = [state, details]
2021 srcmap["pkgs"][pkg][release] = (cve, linenum)
2022
2023 return code, msg
2024
2025
2026def load_external_subproject_cve_data(cve, data, srcmap, code, msg):1996def load_external_subproject_cve_data(cve, data, srcmap, code, msg):
2027 cve_id = os.path.basename(cve)1997 cve_id = os.path.basename(cve)
1998 extra_fields = ['Assigned-to', 'Notes', 'Priority', 'References']
2028 for f in find_external_subproject_cves(cve_id):1999 for f in find_external_subproject_cves(cve_id):
2029 with codecs.open(f, 'r', encoding="utf-8") as fp:2000 subproject = f.rsplit('/', 2)[1]
2030 amendments = fp.read()2001 try:
2031 fp.close()2002 external_data = load_cve(f, srcmap=srcmap, is_external=True)
2032 code, msg = amend_external_subproject_pkg(f, data, srcmap, amendments, code, msg)2003 for pkg in external_data['pkgs']:
2004 for rel in external_data['pkgs'][pkg]:
2005 state, details = external_data['pkgs'][pkg][rel]
2006 data.setdefault("pkgs", dict())
2007 data["pkgs"].setdefault(pkg, dict())
2008 # override existing release info if it exists
2009 data["pkgs"][pkg][rel] = [state, details]
2010
2011 for field in extra_fields:
2012 if field in external_data and \
2013 external_data[field] != [] and external_data[field] != '' and \
2014 external_data[field] != 'untriaged':
2015 data.setdefault("External-Metadata", dict())
2016 data["External-Metadata"].setdefault(subproject, dict())
2017 data["External-Metadata"][subproject][field] = external_data[field]
2018
2019 except ValueError as e:
2020 return EXIT_FAIL, msg + str(e)
20332021
2034 return code, msg2022 return code, msg
20352023
2036def load_cve(cve, strict=False, srcmap=None):2024def load_cve(cve, strict=False, srcmap=None, is_external=False):
2037 '''Loads a given CVE into:2025 '''Loads a given CVE into:
2038 dict( fields...2026 dict( fields...
2039 'pkgs' -> dict( pkg -> dict( release -> (state, details) ) )2027 'pkgs' -> dict( pkg -> dict( release -> (state, details) ) )
@@ -2225,12 +2213,13 @@ def load_cve(cve, strict=False, srcmap=None):
2225 if strict:2213 if strict:
2226 nonempty += ['PublicDate']2214 nonempty += ['PublicDate']
22272215
2228 if field not in data or field not in fields_seen:2216 if not is_external:
2229 msg += "%s: %d: missing field '%s'\n" % (cve, linenum, field)2217 if field not in data or field not in fields_seen:
2230 code = EXIT_FAIL2218 msg += "%s: %d: missing field '%s'\n" % (cve, linenum, field)
2231 elif field in nonempty and data[field].strip() == "":2219 code = EXIT_FAIL
2232 msg += "%s: %d: required field '%s' is empty\n" % (cve, linenum, field)2220 elif field in nonempty and data[field].strip() == "":
2233 code = EXIT_FAIL2221 msg += "%s: %d: required field '%s' is empty\n" % (cve, linenum, field)
2222 code = EXIT_FAIL
22342223
2235 # Fill in defaults for missing fields2224 # Fill in defaults for missing fields
2236 if 'Priority' not in data:2225 if 'Priority' not in data:
@@ -2259,7 +2248,8 @@ def load_cve(cve, strict=False, srcmap=None):
22592248
2260 data['pkgs'] = affected2249 data['pkgs'] = affected
22612250
2262 code, msg = load_external_subproject_cve_data(cve, data, srcmap, code, msg)2251 if not is_external:
2252 code, msg = load_external_subproject_cve_data(cve, data, srcmap, code, msg)
22632253
2264 if code != EXIT_OKAY:2254 if code != EXIT_OKAY:
2265 raise ValueError(msg.strip())2255 raise ValueError(msg.strip())
diff --git a/scripts/test_cve_lib.py b/scripts/test_cve_lib.py
index f9bd1d2..0f1d78e 100755
--- a/scripts/test_cve_lib.py
+++ b/scripts/test_cve_lib.py
@@ -186,6 +186,10 @@ class TestParseCVEFiles:
186 assert cvss[0]['baseSeverity'] == js['baseMetricV3']['cvssV3']['baseSeverity']186 assert cvss[0]['baseSeverity'] == js['baseMetricV3']['cvssV3']['baseSeverity']
187187
188188
189MOCK_SUBPROJECTS = [
190 "foo","bar"
191]
192
189class TestSubprojects:193class TestSubprojects:
190194
191 def test_load_subprojects_loads_every_config_available(self):195 def test_load_subprojects_loads_every_config_available(self):
@@ -204,4 +208,36 @@ class TestSubprojects:
204 if project and "customer" in project:208 if project and "customer" in project:
205 assert cve_lib.subprojects[rel]["customer"] == project["customer"]209 assert cve_lib.subprojects[rel]["customer"] == project["customer"]
206210
211 @pytest.mark.parametrize("subproject", MOCK_SUBPROJECTS)
212 def test_external_subproject_metadata(self, subproject):
213 real_subproject_dir = cve_lib.subprojects_dir
214 cve_lib.subprojects_dir = os.path.join(TEST_DATA_DIR, 'subprojects')
215 cve_lib.load_external_subprojects()
216
217 cve = cve_lib.load_cve(os.path.join(TEST_DATA_DIR, 'okay/priority-critical'), strict=True)
218 assert cve['External-Metadata']
219 assert cve['External-Metadata'][subproject]
220
221 subproject_cve_data = cve_lib.load_cve(os.path.join(TEST_DATA_DIR, 'subprojects', subproject, 'priority-critical'), is_external=True)
222
223 for key in cve['External-Metadata'][subproject]:
224 assert cve['External-Metadata'][subproject][key] == subproject_cve_data[key]
225
226 cve_lib.subprojects_dir = real_subproject_dir
227 cve_lib.load_external_subprojects()
228
229 @pytest.mark.parametrize("subproject", MOCK_SUBPROJECTS)
230 def test_invalid_external_subproject_metadata(self, subproject):
231 real_subproject_dir = cve_lib.subprojects_dir
232 cve_lib.subprojects_dir = os.path.join(TEST_DATA_DIR, 'subprojects')
233 cve_lib.load_external_subprojects()
234 cve_lib.external_releases = [os.path.join(subproject, 'focal')]
235
236 error = f"test/bad/priority-invalid-value: 13: unknown Priority 'meduim'\n"
237 subproject_err = f"test/subprojects/{subproject}/priority-invalid-value: 5: unknown Priority"
238
239 with pytest.raises(ValueError, match=error+subproject_err):
240 cve_lib.load_cve(os.path.join(TEST_DATA_DIR, 'bad/priority-invalid-value'), strict=True)
207241
242 cve_lib.subprojects_dir = real_subproject_dir
243 cve_lib.load_external_subprojects()
208\ No newline at end of file244\ No newline at end of file
diff --git a/test/subprojects/bar/focal/supported.txt b/test/subprojects/bar/focal/supported.txt
209new file mode 100644245new file mode 100644
index 0000000..83b3780
--- /dev/null
+++ b/test/subprojects/bar/focal/supported.txt
@@ -0,0 +1 @@
1ppp
diff --git a/test/subprojects/bar/priority-critical b/test/subprojects/bar/priority-critical
0new file mode 1006442new file mode 100644
index 0000000..4b7736d
--- /dev/null
+++ b/test/subprojects/bar/priority-critical
@@ -0,0 +1,8 @@
1References:
2 https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-0001
3Notes:
4 bar> Test
5Priority: critical
6Assigned-to: bar
7
8bar/focal_ppp: released (2.4.7-2+4.1ubuntu8)
diff --git a/test/subprojects/bar/priority-invalid-value b/test/subprojects/bar/priority-invalid-value
0new file mode 1006449new file mode 100644
index 0000000..c826aa7
--- /dev/null
+++ b/test/subprojects/bar/priority-invalid-value
@@ -0,0 +1,8 @@
1References:
2 https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-0002
3Notes:
4 foo> Test
5Priority: Critical
6Assigned-to: foo
7
8bar/focal_ppp: released (2.4.7-2+4.1ubuntu8)
diff --git a/test/subprojects/foo/focal/supported.txt b/test/subprojects/foo/focal/supported.txt
0new file mode 1006449new file mode 100644
index 0000000..83b3780
--- /dev/null
+++ b/test/subprojects/foo/focal/supported.txt
@@ -0,0 +1 @@
1ppp
diff --git a/test/subprojects/foo/priority-critical b/test/subprojects/foo/priority-critical
0new file mode 1006442new file mode 100644
index 0000000..4035ba8
--- /dev/null
+++ b/test/subprojects/foo/priority-critical
@@ -0,0 +1,8 @@
1References:
2 https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-0002
3Notes:
4 foo> Test
5Priority: low
6Assigned-to: foo
7
8foo/focal_ppp: released (2.4.7-2+4.1ubuntu8)
diff --git a/test/subprojects/foo/priority-invalid-value b/test/subprojects/foo/priority-invalid-value
0new file mode 1006449new file mode 100644
index 0000000..6ac754d
--- /dev/null
+++ b/test/subprojects/foo/priority-invalid-value
@@ -0,0 +1,8 @@
1References:
2 https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-0002
3Notes:
4 foo> Test
5Priority: Low
6Assigned-to: foo
7
8foo/focal_ppp: released (2.4.7-2+4.1ubuntu8)

Subscribers

People subscribed via source and target branches