Merge ~sbeattie/ubuntu-cve-tracker/+git/ubuntu-cve-tracker:create_uct_module_refactoring into ubuntu-cve-tracker:master

Proposed by Steve Beattie
Status: Needs review
Proposed branch: ~sbeattie/ubuntu-cve-tracker/+git/ubuntu-cve-tracker:create_uct_module_refactoring
Merge into: ubuntu-cve-tracker:master
Diff against target: 1651 lines (+537/-339)
41 files modified
.coveragerc (+7/-0)
.gitignore (+2/-0)
.launchpad.yaml (+7/-5)
Makefile (+31/-2)
cache/.empty_dir (+0/-0)
scripts/boilerplate-to-json.py (+2/-1)
scripts/check-cves (+14/-234)
scripts/check-unreplaced-templates.py (+2/-1)
scripts/cve_lib.py (+7/-14)
scripts/debian-cve-update (+3/-2)
scripts/dump-features (+0/-2)
scripts/gen-source-map-cache (+2/-1)
scripts/generate-graphs.py (+0/-2)
scripts/plot-usns.py (+2/-4)
scripts/process_cves (+21/-18)
scripts/pull-in-progress.py (+3/-2)
scripts/report-bugs (+3/-2)
scripts/report-bugs-by-assignee (+0/-3)
scripts/report-bugs-by-team (+3/-2)
scripts/report-bugs-for-eol (+3/-2)
scripts/report-cve-age.py (+2/-1)
scripts/report-mistriaged-cves.py (+3/-2)
scripts/report-packages.py (+2/-1)
scripts/report-todo-mir (+0/-4)
scripts/report-todo-sponsoring (+0/-3)
scripts/report-updates.py (+2/-1)
scripts/report-version.py (+2/-1)
scripts/sis-changes (+3/-2)
scripts/sis-generate-usn (+2/-1)
scripts/source_map.py (+4/-22)
scripts/sync-bugs-kernel.py (+2/-1)
scripts/sync-from-usns.py (+2/-1)
scripts/test_uct_config.py (+48/-0)
scripts/test_uct_suggestions.py (+124/-0)
scripts/uct/config.py (+75/-0)
scripts/uct/suggestions.py (+131/-0)
scripts/usn_lib.py (+2/-2)
test/uct/config/no_plb_auth_entry.conf (+2/-0)
test/uct/config/plb_auth_dev_null.conf (+2/-0)
test/uct/config/plb_auth_does_not_exist.conf (+2/-0)
test/uct/config/uct.conf (+15/-0)
Reviewer Review Type Date Requested Status
Marc Deslauriers Approve
Review via email: mp+462102@code.launchpad.net

Commit message

[WIP] refactor UCT layout and add coverage and script syntax check reporting

This branch is motivated by a few different things:

- wanting to break up cve_lib.py into smaller components, as well as support moving things implemented in individual scripts into common importable places
- wanting to know about the deprecation warnings from newer versions of python (like the recent invalid escape sequence warnings that came from python 3.12)
- wanting to see test coverage reports

To do this, I made a couple of changes that we can bikeshed about:

- created a scripts/uct directory / module that can be imported and moved a few things in there.
- created an empty cache/ directory and adjusted process_cves to use it to store and look for external data files during triage

The motivation for the latter was to (a) reduce the number of things that we look at when identifying python or shell scripts to check for syntax or other errors, and (b) I would like to reduce the clutter from the top level of UCT. I have not done it yet, but would like to move the *supported.txt files to a subdirectory (likely the meta_lists/ directory but we can argue about the name/location).

Some results out of this series:

- there are new make targets:
    - check-python: invokes the same tests that the launchpad.yaml unit test job does (these should probably be unified)
    - check-syntax-scripts: calls specific targets for running the syntax checkers on python and shell scripts in the tree

These targets are added as dependencies to the 'check' target but that also runs `check-syntax` on the cve files, so they need to be individually invokable if we're going to keep the separate test jobs in lpci.

Also a `coverage` make target was added that generates an html reoprt of the python test coverage in the coverage_html subdirectory, which reports details about which lines in each python have or have not been exercised. Interestingly, it identifies that a couple of small portions of the existing tests that are supposed to look at the subprojects tree don't get exercised (despite the symlink to the external repo being present on my system); not sure what's going wrong there. In an ideal world the test coverage pages would get published somewhere.

One thing to think about is how to integrate the rest of the oval functional and semi-end-to-end tests that are in the toplevel test/ directory, and work around that some of the data sources may not be available from lpci.

Another note: the read_config() and read_config_file() implementation cannot yet be removed, as there are a number of scripts in repos outsde of UCT Tthat need to be adjusted first.

Description of the change

UCT layout changes and add test coverage and syntax warning reports

* [988c62cb224] uct module: create, reproduce cve_lib.read_config() functionality
* [3aa4f0a5186] UCT/scripts: remove unneeded calls to cve_lib.read_config()
* [a691753e5c9] UCT/scripts: convert most scripts to use uct.config.read_uct_config()
* [26fcf6446ec] cve_lib.py: convert read_config to use uct.config.read_uct_config()
* [657e0da2753] check-cves: move ignored_cache.py under scripts/uct
* [1ef963e634f] check-cves: move ignore suggestion reasons to separate file/class
* [4a803a179ca] launchpad.yaml: run pytest against scripts/test_*.py
* [6578c2f6e15] launchpad.yaml: drop calling check-cves --test
* [85aa9d5031e] check-cves: drop self unit test argument and implementation
* [1c539b24ed7] launchpad.yaml: generate a coverage report for the unit tests
* [6345465aed2] Makefile: add python check and coverage report targets
* [e62116181d9] Add empty cache dir
* [e01c4c53ac5] process_cves: use cache directory for triage
* [2161889cd62] Make: add targets for checking script syntax
* [196a1bff689] launchpad.yaml: add syntax warning to lpci unit test job

To post a comment you must log in.
Revision history for this message
Marc Deslauriers (mdeslaur) wrote :

Do you need to add the new cache directory to .gitignore?

34c2e14... by Steve Beattie

gitignore: ignore cache directory

Signed-off-by: Steve Beattie <email address hidden>

Revision history for this message
Steve Beattie (sbeattie) wrote :

On Sun, Mar 10, 2024 at 07:21:57PM -0000, Marc Deslauriers wrote:
> Do you need to add the new cache directory to .gitignore?

Not *strictly* necessary, but useful for not committing additional files
to that directory. Added a commit for that.

--
Steve Beattie
<email address hidden>

Revision history for this message
Marc Deslauriers (mdeslaur) wrote :

I agree with the reasoning behind this, and everything looks good to me. ACK

review: Approve

Unmerged commits

34c2e14... by Steve Beattie

gitignore: ignore cache directory

Signed-off-by: Steve Beattie <email address hidden>

Succeeded
[SUCCEEDED] unit-tests:0 (build)
[SUCCEEDED] check-cves:0 (build)
12 of 2 results
196a1bf... by Steve Beattie

launchpad.yaml: add syntax warning to lpci unit test job

Add needed dependencies and invoke make check-syntax-scripts

Signed-off-by: Steve Beattie <email address hidden>

Succeeded
[SUCCEEDED] unit-tests:0 (build)
[SUCCEEDED] check-cves:0 (build)
12 of 2 results
2161889... by Steve Beattie

Make: add targets for checking script syntax

Add targets for checking scripts syntax, currently using pyflakes and
shellcheck; I tried to use flake8 but it has an issue with the version
of pycodestyle in noble.

These invocations have their error codes suppressed, so that they are
informational only for now, as there are a large number of items
reported that need to be investigated.

This is the reason for the prior commit to try to move the nvd files
into a cache subdirectory to make pruning directories easier so that
e.g. file isn't invoked on all the individual nvd cve json files.

Signed-off-by: Steve Beattie <email address hidden>

e01c4c5... by Steve Beattie

process_cves: use cache directory for triage

Signed-off-by: Steve Beattie <email address hidden>

Succeeded
[SUCCEEDED] unit-tests:0 (build)
[SUCCEEDED] check-cves:0 (build)
12 of 2 results
e621161... by Steve Beattie

Add empty cache dir

Signed-off-by: Steve Beattie <email address hidden>

6345465... by Steve Beattie

Makefile: add python check and coverage report targets

Use the coverage module to generate an html coverage report. As part of
this:

- Adjust the .coveragerc config file to omit files that have not been
  covnerted to be python3 parseable.
- add the coverage_html created directory to .gitignore

Signed-off-by: Steve Beattie <email address hidden>

1c539b2... by Steve Beattie

launchpad.yaml: generate a coverage report for the unit tests

Signed-off-by: Steve Beattie <email address hidden>

85aa9d5... by Steve Beattie

check-cves: drop self unit test argument and implementation

Signed-off-by: Steve Beattie <email address hidden>

6578c2f... by Steve Beattie

launchpad.yaml: drop calling check-cves --test

As the same tests are now covered in the unit tests section, via
test_uct_suggestions.py

Signed-off-by: Steve Beattie <email address hidden>

4a803a1... by Steve Beattie

launchpad.yaml: run pytest against scripts/test_*.py

This way it will automatically pick up any new test scripts, including
the most recent scripts/test_uct_suggestions.py

Signed-off-by: Steve Beattie <email address hidden>

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
diff --git a/.coveragerc b/.coveragerc
index 3ad560c..d59ccbe 100644
--- a/.coveragerc
+++ b/.coveragerc
@@ -3,3 +3,10 @@ branch = True
3include =3include =
4 scripts/generate-oval4 scripts/generate-oval
5 scripts/oval_lib.py5 scripts/oval_lib.py
6# the follow scripts have not been converted to be parseable by a
7# python3 intepreter
8omit =
9 scripts/pull-in-progress.py
10 scripts/report-date.py
11 scripts/report-mismatched-cve-fixes.py
12 scripts/report_universe_cves.py
diff --git a/.gitignore b/.gitignore
index 551e1e4..d8a74d4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -70,7 +70,9 @@ graphing/cosmic.data
70ignore_package_regexes.txt70ignore_package_regexes.txt
71.login71.login
72.coverage72.coverage
73coverage_html
73/subprojects74/subprojects
74/pkg_cache.json75/pkg_cache.json
75.vscode/76.vscode/
76/.venv77/.venv
78/cache/
diff --git a/.launchpad.yaml b/.launchpad.yaml
index a089c1d..9bcdb5d 100644
--- a/.launchpad.yaml
+++ b/.launchpad.yaml
@@ -13,7 +13,9 @@ jobs:
13 architectures: amd6413 architectures: amd64
14 packages:14 packages:
15 - distro-info15 - distro-info
16 - file
16 - lsb-release17 - lsb-release
18 - pyflakes3
17 - python319 - python3
18 - python3-apt20 - python3-apt
19 - python3-configobj21 - python3-configobj
@@ -21,7 +23,9 @@ jobs:
21 - python3-mock23 - python3-mock
22 - python3-progressbar24 - python3-progressbar
23 - python3-pytest25 - python3-pytest
26 - python3-pytest-cov
24 - python3-yaml27 - python3-yaml
28 - shellcheck
25 run-before: |29 run-before: |
26 # configure a basic ~/.ubuntu-cve-tracker.conf30 # configure a basic ~/.ubuntu-cve-tracker.conf
27 echo plb_authentication=/dev/null > ~/.ubuntu-cve-tracker.conf31 echo plb_authentication=/dev/null > ~/.ubuntu-cve-tracker.conf
@@ -35,11 +39,10 @@ jobs:
35 # new one39 # new one
36 rm -f embargoed40 rm -f embargoed
37 mkdir embargoed41 mkdir embargoed
42 echo "reporting syntax issues on scripts"
43 make check-syntax-scripts
38 echo "Running unit tests..."44 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 ./scripts/test_sync_from_usns.py ./test/test_oval_lib_unit.py45 pytest-3 --cov=scripts ./scripts/test_*.py ./test/test_oval_lib_unit.py
40 # 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 part
42 # of check-syntax below
43 check-cves:46 check-cves:
44 series: jammy47 series: jammy
45 architectures: amd6448 architectures: amd64
@@ -75,6 +78,5 @@ jobs:
75 rm -f embargoed78 rm -f embargoed
76 mkdir embargoed79 mkdir embargoed
7780
78 ./scripts/check-cves --test
79 echo "Checking syntax..."81 echo "Checking syntax..."
80 ./scripts/check-syntax82 ./scripts/check-syntax
diff --git a/Makefile b/Makefile
index 2c0abb7..254b074 100644
--- a/Makefile
+++ b/Makefile
@@ -13,6 +13,9 @@ ifneq ($(UCT_REVIEWED)$(SCRIPTS_RELDIR),$(SCRIPTS_RELDIR))
13endif13endif
14export SCRIPTS=$(UCT_SCRIPTS)14export SCRIPTS=$(UCT_SCRIPTS)
15export ACTIVE=$(shell pwd)/active15export ACTIVE=$(shell pwd)/active
16PYTEST=pytest-3
17PYTHON=python3
18PYFLAKES=pyflakes3
1619
17all: cves pkgs tables20all: cves pkgs tables
1821
@@ -79,7 +82,33 @@ dev_setup:
79 echo '*** $$UQT is not set, unable to find location of lpl_common.py ***' ; \82 echo '*** $$UQT is not set, unable to find location of lpl_common.py ***' ; \
80 fi83 fi
8184
82check:85syntax-shell:
86 # run shellcheck on the specific types of shell scripts we have
87 # this does not propagate the error code yet because we have a
88 # a bunch of warnings, some of which because of how things are
89 # used, just need to be silenced.
90 find -type d \( -name cache -o -name active -o -name retired -o -name ignored -o -name nvd-database -o -name .git \) -prune -o -exec file {} \+ | grep "Bourne-Again shell script" | cut -d ":" -f 1 | xargs shellcheck -s bash || true
91 find -type d \( -name cache -o -name active -o -name retired -o -name ignored -o -name nvd-database -o -name .git \) -prune -o -exec file {} \+ | grep -i "posix shell script" | cut -d ":" -f 1 | xargs shellcheck -s dash || true
92
93syntax-python:
94 # run pyflakes on the python scripts we have
95 # this does not propagate the error code yet because there are
96 # some scripts that need to be made python3 compliant and some
97 # imports cleaned up
98 find -type d \( -name cache -o -name active -o -name retired -o -name ignored -o -name nvd-database -o -name .git \) -prune -o -exec file {} \+ | grep "Python script" | cut -d ":" -f 1 | xargs $(PYFLAKES) || true
99
100check-syntax-scripts: syntax-shell syntax-python
101
102coverage-python: check-python .coverage
103 $(PYTHON) -m coverage html --dir coverage_html
104
105.coverage: check-python
106
107check-python:
108 $(PYTEST) --cov=scripts ./scripts/test_*.py ./test/test_oval_lib_unit.py
109
110check: check-syntax-scripts check-python
83 $(SCRIPTS)/check-syntax111 $(SCRIPTS)/check-syntax
84112
85.PHONY: dev_setup check113.PHONY: dev_setup check check-python coverage-python check-syntax-scripts \
114syntax-shell syntax-python
diff --git a/cache/.empty_dir b/cache/.empty_dir
86new file mode 100644115new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/cache/.empty_dir
diff --git a/scripts/boilerplate-to-json.py b/scripts/boilerplate-to-json.py
index 06d800f..4066116 100755
--- a/scripts/boilerplate-to-json.py
+++ b/scripts/boilerplate-to-json.py
@@ -6,6 +6,7 @@ import re
6import sys6import sys
77
8import cve_lib8import cve_lib
9from uct.config import read_uct_config
910
10def parse_boilerplate(filepath):11def parse_boilerplate(filepath):
11 cve_data = cve_lib.load_cve(filepath)12 cve_data = cve_lib.load_cve(filepath)
@@ -131,7 +132,7 @@ def parse_embedded_code_copies(filepath):
131 return data132 return data
132133
133# also parse debian's embedded-code-copies and amalgate that into data134# also parse debian's embedded-code-copies and amalgate that into data
134config = cve_lib.read_config()135config = read_uct_config()
135debian_embedded_copies = os.path.join(config['secure_testing_path'], "data", "embedded-code-copies")136debian_embedded_copies = os.path.join(config['secure_testing_path'], "data", "embedded-code-copies")
136code_copies = parse_embedded_code_copies(debian_embedded_copies)137code_copies = parse_embedded_code_copies(debian_embedded_copies)
137for pkg in code_copies["pkgs"]:138for pkg in code_copies["pkgs"]:
diff --git a/scripts/check-cves b/scripts/check-cves
index 8a9e8fc..020693f 100755
--- a/scripts/check-cves
+++ b/scripts/check-cves
@@ -28,7 +28,6 @@ import subprocess
28import sys28import sys
29import tempfile29import tempfile
30import time30import time
31import unittest
32import urllib.request31import urllib.request
33import xml.sax32import xml.sax
34import xml.sax.handler33import xml.sax.handler
@@ -38,10 +37,12 @@ import progressbar
3837
39import cve_lib38import cve_lib
40import source_map39import source_map
41from check_cves.ignored_cache import IgnoredCache40from uct.ignored_cache import IgnoredCache
41from uct.suggestions import IgnoreSuggestions
42from uct.config import read_uct_config
4243
43# load settings, if any44# load settings, if any
44cve_lib.read_config()45uct_config = read_uct_config()
4546
46parser = optparse.OptionParser()47parser = optparse.OptionParser()
47parser.add_option("-r", "--report", help="Just report CVEs that need checking", action="store_true")48parser.add_option("-r", "--report", help="Just report CVEs that need checking", action="store_true")
@@ -50,7 +51,7 @@ parser.add_option("-k", "--known", help="Only report CVEs already known", action
50parser.add_option("-N", "--skip-nfu", help="Skip any CVEs marked as NFU (used with -k)", action="store_true")51parser.add_option("-N", "--skip-nfu", help="Skip any CVEs marked as NFU (used with -k)", action="store_true")
51parser.add_option("-R", "--refresh", help="Refresh CVE descriptions", action="store_true")52parser.add_option("-R", "--refresh", help="Refresh CVE descriptions", action="store_true")
52parser.add_option("-S", "--score-refresh", help="Refresh CVSS scores values only", action="store_true")53parser.add_option("-S", "--score-refresh", help="Refresh CVSS scores values only", action="store_true")
53parser.add_option("", "--test", help="Run regression tests", action="store_true")54parser.add_option("", "--test", help="NO LONGER SUPPORTED, see test_uct_suggestions.py", action="help")
54parser.add_option("--untriaged", help="Process untriaged CVEs from output of locate_cves.py", metavar="FILE")55parser.add_option("--untriaged", help="Process untriaged CVEs from output of locate_cves.py", metavar="FILE")
55parser.add_option("--mbox", help="Process untriaged CVEs from mbox file", metavar="FILE")56parser.add_option("--mbox", help="Process untriaged CVEs from mbox file", metavar="FILE")
56parser.add_option("--rhel8oval", help="Process untriaged RHEL8 CVEs", metavar="FILE")57parser.add_option("--rhel8oval", help="Process untriaged RHEL8 CVEs", metavar="FILE")
@@ -242,7 +243,7 @@ def import_debian(handler):
242 return False243 return False
243244
244 # pull in CVEs from data/DSA/list245 # pull in CVEs from data/DSA/list
245 dsas = cve_lib.load_debian_dsas(cve_lib.config['secure_testing_path'] + '/data/DSA/list', opt.verbose)246 dsas = cve_lib.load_debian_dsas(uct_config['secure_testing_path'] + '/data/DSA/list', opt.verbose)
246 for dsa in dsas:247 for dsa in dsas:
247 for cve in dsas[dsa]['cves']:248 for cve in dsas[dsa]['cves']:
248 if not cve_lib.CVE_RE.match(cve):249 if not cve_lib.CVE_RE.match(cve):
@@ -581,12 +582,14 @@ class CVEHandler(xml.sax.handler.ContentHandler):
581 self.saved_cve = ""582 self.saved_cve = ""
582 self.debian = None583 self.debian = None
583584
585 # get ignore suggestion
586 self.ignore_suggestion = IgnoreSuggestions()
584 # File-type detection587 # File-type detection
585588
586589
587 # Load debian CVE states, if configured590 # Load debian CVE states, if configured
588 if 'secure_testing_path' in cve_lib.config:591 if 'secure_testing_path' in uct_config:
589 self.debian = cve_lib.load_debian_cves(cve_lib.config['secure_testing_path'] + '/data/CVE/list')592 self.debian = cve_lib.load_debian_cves(uct_config['secure_testing_path'] + '/data/CVE/list')
590593
591 def updateTimestamp(self):594 def updateTimestamp(self):
592 # Get UTC time595 # Get UTC time
@@ -792,117 +795,6 @@ class CVEHandler(xml.sax.handler.ContentHandler):
792 def cves(self):795 def cves(self):
793 return self.cve_list796 return self.cve_list
794797
795 def get_ignore_suggestion(self, text):
796 '''Try to find a reasonable suggestion for the user.'''
797 suggestion = ""
798
799 # remove any whitespace
800 rev_text = text.strip()
801 # strip out the added mailing list stuff (locate_cves.py importing)
802 rev_text = re.sub(r'^ML-Date: .* ML-Subject: ', '', rev_text)
803 rev_text = re.sub(r'^(|Re: )CVE (r|R)equest: ', '', rev_text)
804
805 first_sentence = re.split(r'\. ', rev_text)[0]
806
807 # hunt for module/component
808 nouns = ["library", "templates?", "components?", "modules?", "[Pp]lug-?ins?", "extension", "application", "[Tt]hemes?"]
809 products = ["Joomla!", "Drupal", "WordPress", "TYPO3", "Mambo", "Android", "jQuery"]
810 match = re.search(r'(?: in t|^T)he (.*) (%s) (?:.* )?for (%s)' % ("|".join(nouns), "|".join(products)),
811 first_sentence)
812 preposition = "for"
813 if not match:
814 # try swapping order of product and noun in which case there is no preposition
815 match = re.search(r'(?: in t|^T)he (.*) (%s) (%s) (?:.* )' % ("|".join(products), "|".join(nouns)),
816 first_sentence)
817 preposition = ""
818 if match:
819 module = match.group(1)
820 noun = match.group(2)
821 product = match.group(3)
822 for marker in [" module", " ("]:
823 if marker in module:
824 module = module.split(marker)[0]
825 return " ".join(filter(lambda x: len(x) > 0, [module, noun, preposition, product]))
826
827 # hunt for a description of a thing written in a language or for creating something else
828 match = re.search(r'(.*) is a (?:.*) (written in|for creating) (?:.*)', first_sentence)
829 if match:
830 return match.group(1)
831
832 # hunt for a product on a platform
833 match = re.search(r'.* for (Windows|Linux|Mac OS|iOS|Android|iPhone|iPad|BlackBerry|Symbian)', first_sentence)
834 if match:
835 return match.group(0)
836
837 # look for common pattern
838 match = re.search(r'A vulnerability(?:, which was)? classified as (?:.*),? was found in (.*)', first_sentence)
839 if match:
840 return match.group(1)
841
842 # drop commas-extensions
843 if ',' in first_sentence:
844 first_sentence = re.split(r',', first_sentence)[0]
845 phrases = re.split(r' [io]n ', first_sentence)
846
847 # default to the last phrase
848 suggestion = phrases[-1]
849 # move to earlier phrase if suggestion starts with "a"
850 if suggestion.startswith('a ') and len(phrases) > 1:
851 suggestion = phrases[-2]
852
853 version_preps = r'(\s+(before|through|prior to|versions?|[<>]=?))+\s*'
854 version_regex = r'\s+([a-zA-Z\._\-]*[0-9]+[a-zA-Z\._\-]*)+'
855 # prefer 'Apple iOS before <version>' or 'Apple Mac OS X through
856 # <version' in the last phrase over other suggestions
857 if not re.search(r'' + version_preps + version_regex, suggestion):
858 # grab the first phrase with something that may be a version number
859 for p in phrases:
860 if re.search(r'' + version_regex, p):
861 suggestion = p
862 break
863 if re.search(r'^[^,]+\s+for\s+', p):
864 suggestion = p
865 break
866
867 # try to find a good suggestion from the phrase (ie suggest 'Linux
868 # kernel' from 'the Linux kernel before 2.6.27')
869 suggestion = re.split(r'\s+([a-zA-Z\._\-]*[0-9]+[a-zA-Z\._\-]*)+', suggestion)[0]
870 # "blah in component for Software"
871 if re.search(r'^[^,]+\s+for\s+', suggestion):
872 suggestion = re.split(r'[^,]+\s+for\s+', suggestion)[1]
873
874 # Chop off action verbs
875 cleanup_regexes = [
876 # clean up leading "in" or "the"
877 r'^\s*([tT]he|[iI]n)\s+',
878 # clean up trailing version prepositions like "before" or "through"
879 # from version details
880 r'' + version_preps + '$',
881 # clean up trailing parens
882 r'\s+\([^\)]+\)\s*$',
883 # action verbs
884 r'\s+(has|creates|allows|could|contains)($|\s+.*)',
885 # "vulnerbale installations of"
886 r'(^|\s+)vulnerable\sinstallations?\sof($|\s+)',
887 # This affects all versions of package
888 r'^This affects all versions of package\s+',
889 # This affects the package
890 r'^This affects the package\s+',
891 ]
892
893 for regex in cleanup_regexes:
894 if re.search(regex, suggestion):
895 suggestion = re.sub(regex, '', suggestion)
896
897 # if the phrase is too long, truncate it to max_length, but make
898 # sure we don't have a partial word at the end
899 max_length = 64
900 if len(suggestion) > max_length:
901 suggestion = suggestion[:max_length]
902 suggestion = re.sub(r'\s+\w+$', '', suggestion)
903
904 return suggestion
905
906 def display_command_file_usage(self, f, line_prefix=''):798 def display_command_file_usage(self, f, line_prefix=''):
907 f.write('%sThe following commands can be used in this file:\n' % (line_prefix))799 f.write('%sThe following commands can be used in this file:\n' % (line_prefix))
908 f.write('%s\n' % (line_prefix))800 f.write('%s\n' % (line_prefix))
@@ -1000,13 +892,13 @@ class CVEHandler(xml.sax.handler.ContentHandler):
1000 # no debian info, display possible commented ignore command when892 # no debian info, display possible commented ignore command when
1001 # using command file (i.e. wrap_desc is true)893 # using command file (i.e. wrap_desc is true)
1002 if (self.debian[cve]['state'] == 'RESERVED' or self.debian[cve]['state'] == None) and wrap_desc:894 if (self.debian[cve]['state'] == 'RESERVED' or self.debian[cve]['state'] == None) and wrap_desc:
1003 proposed_ignore = self.get_ignore_suggestion(self.cve_data[cve]['desc'])895 proposed_ignore = self.ignore_suggestion.get_ignore_suggestion(self.cve_data[cve]['desc'])
1004 print('%s ignore "%s"' % (cve, proposed_ignore))896 print('%s ignore "%s"' % (cve, proposed_ignore))
1005 # debian rejected, so offer to reject by ignoring when using command file (i.e. wrap_desc is true)897 # debian rejected, so offer to reject by ignoring when using command file (i.e. wrap_desc is true)
1006 if (self.debian[cve]['state'] == 'REJECTED' and wrap_desc):898 if (self.debian[cve]['state'] == 'REJECTED' and wrap_desc):
1007 print('%s ignore "%s"' % (cve, "REJECTED"))899 print('%s ignore "%s"' % (cve, "REJECTED"))
1008 else:900 else:
1009 proposed_ignore = self.get_ignore_suggestion(self.cve_data[cve]['desc'])901 proposed_ignore = self.ignore_suggestion.get_ignore_suggestion(self.cve_data[cve]['desc'])
1010 print('%s ignore %s' % (cve, proposed_ignore))902 print('%s ignore %s' % (cve, proposed_ignore))
1011903
1012 software_hints_from_cve_desc = self.get_software_hints_from_cve_description(self.cve_data[cve]["desc"])904 software_hints_from_cve_desc = self.get_software_hints_from_cve_description(self.cve_data[cve]["desc"])
@@ -1096,7 +988,7 @@ class CVEHandler(xml.sax.handler.ContentHandler):
1096 return (action, reason, packages)988 return (action, reason, packages)
1097989
1098 def get_software_hints_from_cve_description(self, cve_description):990 def get_software_hints_from_cve_description(self, cve_description):
1099 desc = self.get_ignore_suggestion(cve_description)991 desc = self.ignore_suggestion.get_ignore_suggestion(cve_description)
1100 # split based on brackets and other chars which aren't going to be992 # split based on brackets and other chars which aren't going to be
1101 # helpful in matching a package name etc993 # helpful in matching a package name etc
1102 words = set(filter(None, re.split("[\\s\\(\\){}\\[\\]!\\?\\\\/]+", desc.lower())))994 words = set(filter(None, re.split("[\\s\\(\\){}\\[\\]!\\?\\\\/]+", desc.lower())))
@@ -1166,7 +1058,7 @@ class CVEHandler(xml.sax.handler.ContentHandler):
1166 # Show debian reason first, then automatically determined1058 # Show debian reason first, then automatically determined
1167 # reason, then saved reasons. This makes more sense1059 # reason, then saved reasons. This makes more sense
1168 # than sorting the reasons and is more predictable1060 # than sorting the reasons and is more predictable
1169 choices = [reason, self.get_ignore_suggestion(self.cve_data[cve]['desc'])]1061 choices = [reason, self.ignore_suggestion.get_ignore_suggestion(self.cve_data[cve]['desc'])]
1170 choices.extend(self.saved_ignore_cache.get())1062 choices.extend(self.saved_ignore_cache.get())
1171 for choice in choices:1063 for choice in choices:
1172 if choice != "" and choice not in prompts:1064 if choice != "" and choice not in prompts:
@@ -1478,113 +1370,7 @@ class CVEHandler(xml.sax.handler.ContentHandler):
1478 def skip_cve(self):1370 def skip_cve(self):
1479 self.num_skipped += 11371 self.num_skipped += 1
14801372
1481class CheckCVETest(unittest.TestCase):
1482 def test_get_ignore_suggestion(self):
1483 '''"Ignore" suggestion text extraction'''
1484
1485 # Re-use the global handler
1486 h = handler
1487
1488 self.assertEqual("Courier-Authlib", h.get_ignore_suggestion('''SQL injection vulnerability in authpgsqllib.c in Courier-Authlib before 0.62.0, when a non-Latin locale Postgres database is used, allows remote attackers to execute arbitrary SQL commands via query parameters containing apostrophes.'''))
1489
1490 self.assertEqual("Apple Mac OS X", h.get_ignore_suggestion('''Buffer overflow in the DirectoryService Proxy in DirectoryService in Apple Mac OS X through 10.6.8 allows remote attackers to execute arbitrary code or cause a denial of service (application crash) via unspecified vectors.'''))
1491
1492 self.assertEqual("KDE", h.get_ignore_suggestion('''HTMLTokenizer::scriptHandler in Konqueror in KDE 3.5.9 and 3.5.10 allows remote attackers to cause a denial of service (application crash) via an invalid document.load call that triggers use of a deleted object. NOTE: some of these details are obtained from third party information.'''))
1493
1494 self.assertEqual("Sun Solaris", h.get_ignore_suggestion('''The name service cache daemon (nscd) in Sun Solaris 10 and OpenSolaris snv_50 through snv_104 does not properly check permissions, which allows local users to gain privileges and obtain sensitive information via unspecified vectors.'''))
1495
1496 self.assertEqual("Linux kernel", h.get_ignore_suggestion('''libata in the Linux kernel before 2.6.27.9 does not set minimum timeouts for SG_IO requests, which allows local users to cause a denial of service (Programmed I/O mode on drives) via multiple simultaneous invocations of an unspecified test program.'''))
1497
1498 self.assertEqual("iGaming", h.get_ignore_suggestion('''Multiple SQL injection vulnerabilities in iGaming 1.5 and earlier allow remote attackers to execute arbitrary SQL commands via the browse parameter to (1) previews.php and (2) reviews.php, and the (3) id parameter to index.php in a viewarticle action.'''))
1499
1500 self.assertEqual("PHP iCalendar", h.get_ignore_suggestion('''PHP iCalendar 2.24 and earlier allows remote attackers to bypass authentication by setting the phpicalendar and phpicalendar_login cookies to 1.'
1501'''))
1502
1503 # Test length truncation, tweaked to avoid "has" matcher
1504 self.assertEqual("** TEST CVE ** This candidate HAS been reserved by an", h.get_ignore_suggestion('''** TEST CVE ** This candidate HAS been reserved by an organization or individual that will use it when announcing a new security problem. When the candidate has been publicized, the details for this candidate will be provided.'''))
1505
1506 self.assertEqual("Sun OpenSolaris", h.get_ignore_suggestion('''Unspecified vulnerability in the root/boot archive tool in Sun OpenSolaris has unknown impact and local attack vectors, related to a "Temporary file vulnerability," aka Bug ID 6653455.'''))
1507
1508 self.assertEqual("Red Hat Certificate System", h.get_ignore_suggestion('''Red Hat Certificate System 7.2 uses world-readable permissions for password.conf and unspecified other configuration files, which allows local users to discover passwords by reading these files.'''))
1509
1510 self.assertEqual("Microsoft Internet Explorer", h.get_ignore_suggestion('''An unspecified function in the JavaScript implementation in Microsoft Internet Explorer creates and exposes a "temporary footprint" when there is a current login to a web site, which makes it easier for remote attackers to trick a user into acting upon a spoofed pop-up message, aka an "in-session phishing attack." NOTE: as of 20090116, the only disclosure is a vague pre-advisory with no actionable information. However, because it is from a well-known researcher, it is being assigned a CVE identifier for tracking purposes.'''))
1511
1512 self.assertEqual("Umer Inc Songs Portal", h.get_ignore_suggestion('''SQL injection vulnerability in albums.php in Umer Inc Songs Portal allows remote attackers to execute arbitrary SQL commands via the id parameter.'''))
1513
1514 self.assertEqual("Limbo CMS", h.get_ignore_suggestion('''SQL injection vulnerability in open.php in the Private Messaging (com_privmsg) component for Limbo CMS allows remote attackers to execute arbitrary SQL commands via the id parameter in a pms action to index.php.'''))
1515
1516 self.assertEqual("phpscripts Ranking Script", h.get_ignore_suggestion('''phpscripts Ranking Script allows remote attackers to bypass authentication and gain administrative access by sending an admin=ja cookie.'''))
1517
1518 self.assertEqual("A4Desk Event Calendar", h.get_ignore_suggestion('''PHP remote file inclusion vulnerability in index.php in A4Desk Event Calendar, when magic_quotes_gpc is disabled, allows remote attackers to execute arbitrary PHP code via a URL in the v parameter.'''))
1519
1520 self.assertEqual("Galatolo WebManager", h.get_ignore_suggestion('''Cross-site scripting (XSS) vulnerability in result.php in Galatolo WebManager (GWM) 1.0 allows remote attackers to inject arbitrary web script or HTML via the key parameter.'''))
1521
1522 self.assertEqual("Sun OpenSolaris", h.get_ignore_suggestion('''Unspecified vulnerability in the process (aka proc) filesystem in Sun OpenSolaris snv_85 through snv_100 allows local users to gain privileges via vectors related to the contract filesystem.'''))
1523
1524 self.assertEqual("Dreampics Gallery Builder", h.get_ignore_suggestion('''SQL injection vulnerability in index.php in Dreampics Gallery Builder allows remote attackers to execute arbitrary SQL commands via the exhibition_id parameter in a gallery.viewPhotos action.'''))
1525
1526 self.assertEqual("Omilen Photo Gallery component for Joomla!", h.get_ignore_suggestion('''Directory traversal vulnerability in the Omilen Photo Gallery (com_omphotogallery) component Beta 0.5 for Joomla! allows remote attackers to include and execute arbitrary local files via directory traversal sequences in the controller parameter to index.php.'''))
1527
1528 self.assertEqual("Webform module for Drupal", h.get_ignore_suggestion('''Cross-site scripting (XSS) vulnerability in the Webform module 5.x before 5.x-2.7 and 6.x before 6.x-2.7, a module for Drupal, allows remote attackers to inject arbitrary web script or HTML via a submission.'''))
1529
1530 self.assertEqual("Itamar Elharar MusicGallery component for Joomla!", h.get_ignore_suggestion('''SQL injection vulnerability in the Itamar Elharar MusicGallery (com_musicgallery) component for Joomla! allows remote attackers to execute arbitrary SQL commands via the id parameter in an itempage action to index.php. NOTE: the provenance of this information is unknown; the details are obtained solely from third party information.'''))
1531
1532 self.assertEqual("Kide Shoutbox component for Joomla!", h.get_ignore_suggestion('''The Kide Shoutbox (com_kide) component 0.4.6 for Joomla! does not properly perform authentication, which allows remote attackers to post messages with an arbitrary account name via an insertar action to index.php. NOTE: the provenance of this information is unknown; the details are obtained solely from third party information.'''))
15331373
1534 self.assertEqual("WP-Forum plugin for WordPress", h.get_ignore_suggestion('''Multiple SQL injection vulnerabilities in the WP-Forum plugin before 2.4 for WordPress allow remote attackers to execute arbitrary SQL commands via (1) the search_max parameter in a search action to the default URI, related to wpf.class.php; (2) the forum parameter to an unspecified component, related to wpf.class.php; (3) the topic parameter in a viewforum action to the default URI, related to the remove_topic function in wpf.class.php; or the id parameter in a (4) editpost or (5) viewtopic action to the default URI, related to wpf-post.php.'''))
1535
1536 self.assertEqual("ListMan extension for TYPO3", h.get_ignore_suggestion('''Cross-site scripting (XSS) vulnerability in the ListMan (nl_listman) extension 1.2.1 for TYPO3 allows remote attackers to inject arbitrary web script or HTML via unspecified vectors.'''))
1537
1538 self.assertEqual("multiple status.net issues", h.get_ignore_suggestion('''ML-Date: 2011-01-25 12:08:05, ML-Subject: Re: CVE request: multiple status.net issues'''))
1539 self.assertEqual("multiple status.net issues", h.get_ignore_suggestion('''ML-Date: 2011-01-25 12:08:05, ML-Subject: CVE request: multiple status.net issues'''))
1540 self.assertEqual("multiple status.net issues", h.get_ignore_suggestion('''ML-Date: 2011-01-25 12:08:05, ML-Subject: multiple status.net issues'''))
1541 self.assertEqual("Mambo CMS", h.get_ignore_suggestion('''ML-Date: 2011-06-28 16:24:28, ML-Subject: Re: CVE Request: Mambo CMS 4.6.x | Multiple Cross Site Scripting Vulnerabilities'''))
1542
1543 self.assertEqual("Apple iOS", h.get_ignore_suggestion('''The DNAv4 protocol implementation in the DHCP component in Apple iOS before 6 sends Wi-Fi packets containing a MAC address of a host on a previously used network, which might allow remote attackers to obtain sensitive information about previous device locations by sniffing an unencrypted Wi-Fi network for these packets.'''))
1544
1545 self.assertEqual("Conceptronic", h.get_ignore_suggestion('''Multiple open redirect vulnerabilities on the Conceptronic C54APM access point with runtime code 1.26 allow remote attackers to redirect users to arbitrary web sites and conduct phishing attacks via (1) the submit-url parameter in a Refresh action to goform/formWlSiteSurvey or (2) the wlan-url parameter to goform/formWlanSetup.'''))
1546
1547 self.assertEqual("Tapjoy library for Android", h.get_ignore_suggestion('''The Tapjoy library for Android does not verify X.509 certificates from SSL servers, which allows man-in-the-middle attackers to spoof servers and obtain sensitive information via a crafted certificate.'''))
1548
1549 self.assertEqual("kamkomesan application for Android", h.get_ignore_suggestion('''The kamkomesan (aka com.anek.kamkomesan) application 1.0 for Android does not verify X.509 certificates from SSL servers, which allows man-in-the-middle attackers to spoof servers and obtain sensitive information via a crafted certificate.'''))
1550
1551 self.assertEqual("DataTables plugin for jQuery", h.get_ignore_suggestion('''Cross-site scripting (XSS) vulnerability in the DataTables plugin 1.10.8 and earlier for jQuery allows remote attackers to inject arbitrary web script or HTML via the scripts parameter to media/unit_testing/templates/6776.php.'''))
1552
1553 self.assertEqual("Cisco Application Policy Infrastructure Controller (APIC)", h.get_ignore_suggestion('''A vulnerability in Cisco Application Policy Infrastructure Controller (APIC) could allow an authenticated, remote attacker to gain higher privileges than the account is assigned.'''))
1554
1555 self.assertEqual("Redirection", h.get_ignore_suggestion('''Redirection version 2.7.3 contains a ACE via file inclusion vulnerability in Pass-through mode that can result in allows admins to execute any PHP file in the filesystem.'''))
1556
1557 self.assertEqual("topydo", h.get_ignore_suggestion('''topydo contains a CWE-20: Improper Input Validation vulnerability in ListFormatParser::parse, file topydo/lib/ListFormat.py line 292 as of d4f843dac71308b2f29a7c2cdc76f055c3841523 that can result in Injection of arbitrary bytes to the terminal,'''))
1558
1559 self.assertEqual("Joplin", h.get_ignore_suggestion('''Joplin version prior to 1.0.90 contains a XSS evolving into code execution due to enabled nodeIntegration.'''))
1560
1561 self.assertEqual("Foxit Reader", h.get_ignore_suggestion('''This vulnerability allows remote attackers to execute arbitrary code on vulnerable installations of Foxit Reader 9.2.0.9297.'''))
1562
1563 self.assertEqual("Exquisite Ultimate Newspaper theme for WordPress", h.get_ignore_suggestion('''The Exquisite Ultimate Newspaper theme 1.3.3 for WordPress has XSS via the anchor identifier to assets/js/jquery.foundation.plugins.js.'''))
1564 self.assertEqual("Weaver Xtreme Theme for WordPress", h.get_ignore_suggestion('''The Weaver Xtreme Theme for WordPress is vulnerable to stored Cross-Site Scripting due to insufficient escaping of the profile display name in versions up to, and including, 5.0.7.'''))
1565 self.assertEqual("Weaver Show Posts Plugin for WordPress", h.get_ignore_suggestion('''The Weaver Show Posts Plugin for WordPress is vulnerable to stored Cross-Site Scripting due to insufficient escaping of the profile display name in versions up to, and including, 1.6.'''))
1566 self.assertEqual("WordPress File Upload and WordPress File Upload Pro plugins for WordPress", h.get_ignore_suggestion('''The WordPress File Upload and WordPress File Upload Pro plugins for WordPress are vulnerable to Path Traversal in versions up to, and including, 4.19.1 via the vulnerable parameter wfu_newpath.'''))
1567 self.assertEqual("browserless-chrome", h.get_ignore_suggestion('''This affects all versions of package browserless-chrome. User input flowing from the workspace endpoint gets used to create a file path filePath and this is fetched and then sent back to a user. This can be escaped to fetch arbitrary files from a server.'''))
1568 self.assertEqual("@absolunet/kafe", h.get_ignore_suggestion('''This affects the package @absolunet/kafe before 3.2.10. It allows cause a denial of service when validating crafted invalid emails.'''))
1569 self.assertEqual("Magneticlab Sàrl Homepage Pop-up plugin", h.get_ignore_suggestion('''Cross-Site Request Forgery (CSRF) vulnerability in Magneticlab Sàrl Homepage Pop-up plugin <= 1.2.5 versions.'''))
1570
1571 # handle odd whitespace
1572 self.assertEqual("PTC Vuforia Studio does not require a token.", h.get_ignore_suggestion('''
1573
1574
1575
1576
1577 PTC Vuforia Studio does not require a token.
1578 '''))
1579 self.assertEqual("zxcvbn-ts", h.get_ignore_suggestion('''zxcvbn-ts is a password strength estimator written in typescript.'''))
1580 self.assertEqual("VMware Tools for Windows", h.get_ignore_suggestion('''VMware Tools for Windows (10.x.y prior to 10.3.10, 11.x.y prior to 11.1.5) contains a denial-of-service vulnerability due to NULL pointer dereference in vm3dmp driver. Successful exploitation of this issue may allow attackers with normal user privileges to create a denial-of-service condition on the guest machine where VMware Tools is installed.'''))
1581
1582 self.assertEqual("3DPrint WordPress plugin", h.get_ignore_suggestion("The 3DPrint WordPress plugin before 3.5.6.9 does not protect against CSRF attacks in the modified version of Tiny File Manager included with the plugin, allowing an attacker to craft a malicious request that will create an archive of any files or directories on the target server by tricking a logged in admin into submitting a form."))
1583
1584 self.assertEqual("Feathersjs", h.get_ignore_suggestion('''Feathersjs is a framework for creating web APIs and real-time applications with TypeScript or JavaScript.'''))
1585
1586 self.assertEqual("PaulPrinting CMS 2018", h.get_ignore_suggestion("A vulnerability, which was classified as problematic, was found in PaulPrinting CMS 2018"))
1587
1588ignored_notforus_path = 'ignored/not-for-us.txt'1374ignored_notforus_path = 'ignored/not-for-us.txt'
1589if destdir != './' and destdir != '.':1375if destdir != './' and destdir != '.':
1590 ignored_notforus_path = os.path.join(destdir, ignored_notforus_path)1376 ignored_notforus_path = os.path.join(destdir, ignored_notforus_path)
@@ -1625,12 +1411,6 @@ parser = xml.sax.make_parser()
1625handler = CVEHandler(CVEIgnoreList)1411handler = CVEHandler(CVEIgnoreList)
1626parser.setContentHandler(handler)1412parser.setContentHandler(handler)
16271413
1628if opt.test:
1629 suite = unittest.TestSuite()
1630 suite.addTest(unittest.TestLoader().loadTestsFromTestCase(CheckCVETest))
1631 unittest.TextTestRunner(verbosity=2).run(suite)
1632 sys.exit(0)
1633
1634# if has specified to triage only specific CVEs, check these are not1414# if has specified to triage only specific CVEs, check these are not
1635# ignored1415# ignored
1636specific_cves = None1416specific_cves = None
diff --git a/scripts/check-unreplaced-templates.py b/scripts/check-unreplaced-templates.py
index 4723688..3e438d9 100755
--- a/scripts/check-unreplaced-templates.py
+++ b/scripts/check-unreplaced-templates.py
@@ -16,6 +16,7 @@ import cve_lib
16import json16import json
17import optparse17import optparse
18import usn_lib18import usn_lib
19from uct.config import read_uct_config
1920
20parser = optparse.OptionParser()21parser = optparse.OptionParser()
21parser.add_option("-d", "--debug", help="Report additional debugging while loading USNs", action='store_true')22parser.add_option("-d", "--debug", help="Report additional debugging while loading USNs", action='store_true')
@@ -26,7 +27,7 @@ parser.add_option("-j", "--json", help="Check json instead of default pickle dat
2627
27cves = dict()28cves = dict()
2829
29config = cve_lib.read_config()30config = read_uct_config()
3031
31changed = False32changed = False
32if opt.json:33if opt.json:
diff --git a/scripts/cve_lib.py b/scripts/cve_lib.py
index a3104fa..e982b55 100755
--- a/scripts/cve_lib.py
+++ b/scripts/cve_lib.py
@@ -24,6 +24,7 @@ import json
24import yaml24import yaml
25import urllib.error25import urllib.error
26import urllib.request26import urllib.request
27from uct.config import read_uct_config
2728
28from functools import reduce29from functools import reduce
29from functools import lru_cache30from functools import lru_cache
@@ -1698,24 +1699,16 @@ def read_config_file(config_file):
16981699
1699 return ConfigObj(config_file)1700 return ConfigObj(config_file)
17001701
1701def read_config():
1702 config_file = os.path.join(os.path.expanduser("~"), ".ubuntu-cve-tracker.conf")
1703
1704 if not os.path.exists(config_file):
1705 raise ValueError("Could not find '%s'" % (config_file))
17061702
1707 # FIXME: Why does this need to be defined as "global" when other globals1703# this function has been deprecated in favor of
1708 # like "releases" and "EXIT_OKAY" don't need it??1704# uct.config.read_uct_config() and callers should be converted to use
1705# that directly
1706def read_config():
1709 global config1707 global config
1710 config = read_config_file(config_file)1708 config = read_uct_config()
1711
1712 # Validate required arguments
1713 if "plb_authentication" not in config:
1714 raise ValueError(("Could not find 'plb_authentication' entry in %s." % (config_file)))
1715 if not os.path.exists(config["plb_authentication"]):
1716 raise ValueError(("Could not find file specified by 'plb_authentication' in %s." % (config_file)))
1717 return config1709 return config
17181710
1711
1719def drop_dup_release(cve, rel):1712def drop_dup_release(cve, rel):
1720 output = codecs.open(cve + ".new", 'w', encoding="utf-8")1713 output = codecs.open(cve + ".new", 'w', encoding="utf-8")
1721 saw = set()1714 saw = set()
diff --git a/scripts/debian-cve-update b/scripts/debian-cve-update
index 07c1bea..37856fa 100755
--- a/scripts/debian-cve-update
+++ b/scripts/debian-cve-update
@@ -10,12 +10,13 @@ from __future__ import print_function
1010
11from cve_lib import (11from cve_lib import (
12 check_mirror_timestamp, get_cve_list, load_debian_cves,12 check_mirror_timestamp, get_cve_list, load_debian_cves,
13 load_ignored_reasons, prepend_debian_cve, read_config,13 load_ignored_reasons, prepend_debian_cve,
14 update_debian_todo_cves,14 update_debian_todo_cves,
15)15)
16import source_map16import source_map
17import optparse17import optparse
18import os18import os
19from uct.config import read_uct_config
1920
20parser = optparse.OptionParser()21parser = optparse.OptionParser()
21parser.add_option("-p", "--packages", help="Include known packages in the updates", action='store_true')22parser.add_option("-p", "--packages", help="Include known packages in the updates", action='store_true')
@@ -23,7 +24,7 @@ parser.add_option("-n", "--dry-run", help="Do not actually make changes", action
23parser.add_option("-q", "--quiet", help="Report actions verbosely", dest="verbose", default=True, action='store_false')24parser.add_option("-q", "--quiet", help="Report actions verbosely", dest="verbose", default=True, action='store_false')
24(opt, args) = parser.parse_args()25(opt, args) = parser.parse_args()
2526
26config = read_config()27config = read_uct_config()
27if len(args) == 1:28if len(args) == 1:
28 debian_cve_list = args[0]29 debian_cve_list = args[0]
29else:30else:
diff --git a/scripts/dump-features b/scripts/dump-features
index 30ca715..5dccc03 100755
--- a/scripts/dump-features
+++ b/scripts/dump-features
@@ -29,8 +29,6 @@ if opt.editmoin:
29 else:29 else:
30 sys.exit(1)30 sys.exit(1)
3131
32config = cve_lib.read_config()
33
34features = dict()32features = dict()
3533
36UNIMPLEMENTED = 034UNIMPLEMENTED = 0
diff --git a/scripts/gen-source-map-cache b/scripts/gen-source-map-cache
index 1c84cc6..d6fd4cf 100755
--- a/scripts/gen-source-map-cache
+++ b/scripts/gen-source-map-cache
@@ -23,10 +23,11 @@ if not "UCT" in os.environ and not os.path.islink('subprojects'):
2323
24import cve_lib24import cve_lib
25import source_map25import source_map
26from uct.config import read_uct_config
2627
27def main():28def main():
2829
29 config = source_map.read_config_file(os.path.expanduser("~/.ubuntu-cve-tracker.conf"))30 config = read_uct_config()
30 if 'packages_mirror' not in config:31 if 'packages_mirror' not in config:
31 sys.stderr.write("\rERROR: Could not find packages_mirror in config file!\n")32 sys.stderr.write("\rERROR: Could not find packages_mirror in config file!\n")
32 sys.exit(1)33 sys.exit(1)
diff --git a/scripts/generate-graphs.py b/scripts/generate-graphs.py
index e699f04..cceb55c 100755
--- a/scripts/generate-graphs.py
+++ b/scripts/generate-graphs.py
@@ -61,8 +61,6 @@ if opt.target == None:
61 print("Must specify --target", file=sys.stderr)61 print("Must specify --target", file=sys.stderr)
62 sys.exit(1)62 sys.exit(1)
6363
64config = cve_lib.read_config()
65
66if not os.path.exists(opt.database):64if not os.path.exists(opt.database):
67 print("Cannot find '%s'" % opt.database, file=sys.stderr)65 print("Cannot find '%s'" % opt.database, file=sys.stderr)
68 sys.exit(1)66 sys.exit(1)
diff --git a/scripts/plot-usns.py b/scripts/plot-usns.py
index b6e8e2b..527bb22 100755
--- a/scripts/plot-usns.py
+++ b/scripts/plot-usns.py
@@ -8,6 +8,7 @@
8# License: GPLv38# License: GPLv3
9import sys, time, usn_lib, cve_lib9import sys, time, usn_lib, cve_lib
10import optparse10import optparse
11from uct.config import read_uct_config
1112
12import pprint13import pprint
13pp = pprint.PrettyPrinter(indent=4)14pp = pprint.PrettyPrinter(indent=4)
@@ -23,7 +24,7 @@ if opt.target not in ['usn','src','bin','cve']:
23 print("Unknown target '%s'" % (opt.target), file=sys.stderr)24 print("Unknown target '%s'" % (opt.target), file=sys.stderr)
24 sys.exit(1)25 sys.exit(1)
2526
26config = cve_lib.read_config()27config = read_uct_config()
27db = None28db = None
28db_filename = config['usn_db_copy']29db_filename = config['usn_db_copy']
29if len(args) > 0:30if len(args) > 0:
@@ -33,9 +34,6 @@ db = usn_lib.load_database(db_filename)
33columns = ['total', 'untriaged'] + cve_lib.priorities34columns = ['total', 'untriaged'] + cve_lib.priorities
3435
35cves = dict()36cves = dict()
36if opt.target == 'cve':
37 cve_lib.read_config()
38
39months = dict()37months = dict()
40month_cves = dict()38month_cves = dict()
41for usn in sorted(db.keys()):39for usn in sorted(db.keys()):
diff --git a/scripts/process_cves b/scripts/process_cves
index 97d15cc..334ae9b 100755
--- a/scripts/process_cves
+++ b/scripts/process_cves
@@ -11,6 +11,7 @@
11set -e11set -e
1212
13loc=$(readlink -f "$(dirname "$0")/..")13loc=$(readlink -f "$(dirname "$0")/..")
14cache_dir="${loc}/cache/"
1415
15uctconf="$HOME/.ubuntu-cve-tracker.conf"16uctconf="$HOME/.ubuntu-cve-tracker.conf"
16if [ -s "$uctconf" ]; then17if [ -s "$uctconf" ]; then
@@ -26,6 +27,8 @@ is_http_url() {
26}27}
2728
28download() {29download() {
30 local url
31
29 url="$1"32 url="$1"
30 # if http, then download it with wget, otherwise, try to rsync it33 # if http, then download it with wget, otherwise, try to rsync it
31 if is_http_url "$url" ; then34 if is_http_url "$url" ; then
@@ -35,11 +38,11 @@ download() {
35 file=$(basename "$url")38 file=$(basename "$url")
3639
37 echo "wget -N \"$url\""40 echo "wget -N \"$url\""
38 wget -N "$url" ||:41 wget -N "$url" -P "${cache_dir}" ||:
39 gunzip -f "$file"42 gunzip -f "${cache_dir}/$file"
40 else43 else
41 echo "rsync -avz --progress \"$url\" ."44 echo "rsync -avz --progress \"$url\" ${cache_dir}"
42 rsync -avz --progress "$url" .45 rsync -avz --progress "$url" "${cache_dir}"
43 fi46 fi
44 echo "---"47 echo "---"
45}48}
@@ -107,18 +110,18 @@ update_files() {
107110
108full_refresh() {111full_refresh() {
109 # get PubDate112 # get PubDate
110 echo "update PubDate (check-cves --refresh nvdcve-*.json) ..."113 echo "update PubDate (check-cves --refresh ${cache_dir}/nvdcve-*.json) ..."
111 ./scripts/check-cves --refresh nvdcve-*.json114 ./scripts/check-cves --refresh "${cache_dir}"/nvdcve-*.json
112115
113 # get authoritative descriptions116 # get authoritative descriptions
114 echo "get authoritative descriptions (check-cves --refresh ./allitems.xml) ..."117 echo "get authoritative descriptions (check-cves --refresh ${cache_dir}/allitems.xml) ..."
115 ./scripts/check-cves --refresh ./allitems.xml118 ./scripts/check-cves --refresh "${cache_dir}/allitems.xml"
116}119}
117120
118refresh_cvss_score() {121refresh_cvss_score() {
119 # get CVSS score from NVD122 # get CVSS score from NVD
120 echo "update CVSS score (check-cves --score-refresh nvdcve-*.json) ..."123 echo "update CVSS score (check-cves --score-refresh ${cache_dir}/nvdcve-*.json) ..."
121 ./scripts/check-cves --score-refresh nvdcve-*.json124 ./scripts/check-cves --score-refresh "${cache_dir}"/nvdcve-*.json
122}125}
123126
124kernel_team_merge() {127kernel_team_merge() {
@@ -303,7 +306,7 @@ case "$action" in
303 # kernel_team_merge306 # kernel_team_merge
304 echo "Skipping merge from kernel team"307 echo "Skipping merge from kernel team"
305 echo "check-cves nvdcve-*.json"308 echo "check-cves nvdcve-*.json"
306 ./scripts/check-cves nvdcve-*.json309 ./scripts/check-cves "${cache_dir}"/nvdcve-*.json
307 # this will also do missing debian310 # this will also do missing debian
308 mistriaged311 mistriaged
309312
@@ -321,10 +324,10 @@ case "$action" in
321 echo "Skipping merge from kernel team"324 echo "Skipping merge from kernel team"
322325
323 # process new ones326 # process new ones
324 echo "check-cves ./nvdcve-1.1-recent.json"327 echo "check-cves ${cache_dir}/nvdcve-1.1-recent.json"
325 ./scripts/check-cves ./nvdcve-1.1-recent.json328 ./scripts/check-cves "${cache_dir}/nvdcve-1.1-recent.json"
326 echo "check-cves ./allitems.xml"329 echo "check-cves ${cache_dir}/allitems.xml"
327 ./scripts/check-cves ./allitems.xml330 ./scripts/check-cves "${cache_dir}/allitems.xml"
328331
329 process_missing_debian332 process_missing_debian
330 # Disabling this due to https://listman.redhat.com/archives/rhsa-announce/2023-October/012854.html333 # Disabling this due to https://listman.redhat.com/archives/rhsa-announce/2023-October/012854.html
@@ -396,7 +399,7 @@ case "$action" in
396 update_files399 update_files
397 echo "check-cves --cve ${action}"400 echo "check-cves --cve ${action}"
398 # add all NVD details as well so we can get date etc for it401 # add all NVD details as well so we can get date etc for it
399 ./scripts/check-cves --cve ${action} nvdcve-*.json402 ./scripts/check-cves --cve ${action} "${cache_dir}"/nvdcve-*.json
400 check_syntax403 check_syntax
401 ;;404 ;;
402 *.json)405 *.json)
@@ -420,8 +423,8 @@ case "$action" in
420 update_files423 update_files
421 # kernel_team_merge424 # kernel_team_merge
422 echo "Skipping merge from kernel team"425 echo "Skipping merge from kernel team"
423 echo "check-cves ./nvdcve-1.1-recent.json"426 echo "check-cves ${cache_dir}/nvdcve-1.1-recent.json"
424 ./scripts/check-cves ./nvdcve-1.1-recent.json427 ./scripts/check-cves "${cache_dir}/nvdcve-1.1-recent.json"
425 process_missing_debian428 process_missing_debian
426 refresh_cvss_score429 refresh_cvss_score
427 check_syntax430 check_syntax
diff --git a/scripts/pull-in-progress.py b/scripts/pull-in-progress.py
index a4a57d9..0426e74 100755
--- a/scripts/pull-in-progress.py
+++ b/scripts/pull-in-progress.py
@@ -7,6 +7,7 @@
7import sys, os, os.path, re, urllib, tempfile7import sys, os, os.path, re, urllib, tempfile
8import optparse, glob8import optparse, glob
9import cve_lib9import cve_lib
10from utc.config import read_uct_config
1011
11from launchpadlib.launchpad import Launchpad, EDGE_SERVICE_ROOT12from launchpadlib.launchpad import Launchpad, EDGE_SERVICE_ROOT
12from launchpadlib.credentials import Credentials13from launchpadlib.credentials import Credentials
@@ -57,8 +58,8 @@ def get_cves(bug):
57#58#
58from launchpadbugs.connector import ConnectBugList59from launchpadbugs.connector import ConnectBugList
59BugList = ConnectBugList()60BugList = ConnectBugList()
60cve_lib.read_config()61config = read_uct_config()
61BugList.authentication = cve_lib.config["plb_authentication"]62BugList.authentication = config["plb_authentication"]
6263
63url="https://bugs.launchpad.net/~ubuntu-security/+bugs?field.searchtext=&orderby=-importance&field.status%3Alist=INPROGRESS&assignee_option=any&field.assignee=&field.bug_reporter=&field.bug_supervisor=&field.bug_commenter=&field.subscriber=&field.status_upstream-empty-marker=1&field.omit_dupes.used=&field.has_patch.used=&field.has_patch=on&field.has_cve.used=&field.tag=&field.tags_combinator=ANY&search=Search"64url="https://bugs.launchpad.net/~ubuntu-security/+bugs?field.searchtext=&orderby=-importance&field.status%3Alist=INPROGRESS&assignee_option=any&field.assignee=&field.bug_reporter=&field.bug_supervisor=&field.bug_commenter=&field.subscriber=&field.status_upstream-empty-marker=1&field.omit_dupes.used=&field.has_patch.used=&field.has_patch=on&field.has_cve.used=&field.tag=&field.tags_combinator=ANY&search=Search"
64if opt.any:65if opt.any:
diff --git a/scripts/report-bugs b/scripts/report-bugs
index bb2d78c..de6684a 100755
--- a/scripts/report-bugs
+++ b/scripts/report-bugs
@@ -14,6 +14,7 @@ import re
14import subprocess14import subprocess
15import sys15import sys
16import time16import time
17from uct.config import read_uct_config
1718
18parser = optparse.OptionParser()19parser = optparse.OptionParser()
19parser.add_option("--debug", help="Verbose processing output", action='store_true')20parser.add_option("--debug", help="Verbose processing output", action='store_true')
@@ -43,14 +44,14 @@ except:
43 sys.exit(1)44 sys.exit(1)
4445
45# Load configuration46# Load configuration
46cve_lib.read_config()47config = read_uct_config()
4748
48# API interface49# API interface
49debug("Connecting to LP ...\n")50debug("Connecting to LP ...\n")
50lp = lpl_common.connect()51lp = lpl_common.connect()
5152
52# Get authenticated URL fetcher53# Get authenticated URL fetcher
53opener = lpl_common.opener_with_cookie(cve_lib.config["plb_authentication"])54opener = lpl_common.opener_with_cookie(config["plb_authentication"])
54if not opener:55if not opener:
55 raise ValueError("Could not open cookies")56 raise ValueError("Could not open cookies")
5657
diff --git a/scripts/report-bugs-by-assignee b/scripts/report-bugs-by-assignee
index e34ba4b..2fc5d5e 100755
--- a/scripts/report-bugs-by-assignee
+++ b/scripts/report-bugs-by-assignee
@@ -30,9 +30,6 @@ def debug(s):
30 if opt.debug:30 if opt.debug:
31 print( "DEBUG: %s" % s, file=sys.stderr)31 print( "DEBUG: %s" % s, file=sys.stderr)
3232
33# Load configuration
34cve_lib.read_config()
35
36# API interface33# API interface
37debug("Connecting to LP ...")34debug("Connecting to LP ...")
38lp = UCTLaunchpad(opt)35lp = UCTLaunchpad(opt)
diff --git a/scripts/report-bugs-by-team b/scripts/report-bugs-by-team
index e3b71ec..da7ae74 100755
--- a/scripts/report-bugs-by-team
+++ b/scripts/report-bugs-by-team
@@ -19,6 +19,7 @@ import shutil
19import subprocess19import subprocess
20import sys20import sys
21import time21import time
22from uct.config import read_uct_config
2223
23try:24try:
24 import pickle25 import pickle
@@ -377,7 +378,7 @@ if all_bugs == None:
377 sys.exit(1)378 sys.exit(1)
378379
379 # Load configuration380 # Load configuration
380 cve_lib.read_config()381 config = read_uct_config()
381382
382 # API interface383 # API interface
383 debug("Team: %s" % opt.team)384 debug("Team: %s" % opt.team)
@@ -385,7 +386,7 @@ if all_bugs == None:
385 lp = lpl_common.connect()386 lp = lpl_common.connect()
386387
387 # Get authenticated URL fetcher388 # Get authenticated URL fetcher
388 opener = lpl_common.opener_with_cookie(cve_lib.config["plb_authentication"])389 opener = lpl_common.opener_with_cookie(config["plb_authentication"])
389 if not opener:390 if not opener:
390 raise ValueError("Could not open cookies")391 raise ValueError("Could not open cookies")
391392
diff --git a/scripts/report-bugs-for-eol b/scripts/report-bugs-for-eol
index 2cc2895..81e3397 100755
--- a/scripts/report-bugs-for-eol
+++ b/scripts/report-bugs-for-eol
@@ -17,6 +17,7 @@ import optparse
17import os17import os
18import re18import re
19import sys19import sys
20from uct.config import read_uct_config
2021
21parser = optparse.OptionParser()22parser = optparse.OptionParser()
22parser.add_option("--debug", help="Verbose processing output", action='store_true')23parser.add_option("--debug", help="Verbose processing output", action='store_true')
@@ -138,14 +139,14 @@ if bugs == None:
138 sys.exit(1)139 sys.exit(1)
139140
140 # Load configuration141 # Load configuration
141 cve_lib.read_config()142 config = read_uct_config()
142143
143 # API interface144 # API interface
144 print("Connecting to LP ...", end=' ', file=sys.stderr)145 print("Connecting to LP ...", end=' ', file=sys.stderr)
145 lp = lpl_common.connect()146 lp = lpl_common.connect()
146147
147 # Get authenticated URL fetcher148 # Get authenticated URL fetcher
148 opener = lpl_common.opener_with_cookie(cve_lib.config["plb_authentication"])149 opener = lpl_common.opener_with_cookie(config["plb_authentication"])
149 if not opener:150 if not opener:
150 raise ValueError("Could not open cookies")151 raise ValueError("Could not open cookies")
151152
diff --git a/scripts/report-cve-age.py b/scripts/report-cve-age.py
index 26c2925..7d28820 100755
--- a/scripts/report-cve-age.py
+++ b/scripts/report-cve-age.py
@@ -18,11 +18,12 @@ import os
18import sys18import sys
19import optparse19import optparse
20import cve_lib, usn_lib20import cve_lib, usn_lib
21from uct.config import read_uct_config
2122
22import source_map23import source_map
23source_map = source_map.load()24source_map = source_map.load()
24releases = cve_lib.releases25releases = cve_lib.releases
25config = cve_lib.read_config()26config = read_uct_config()
2627
27default_db = config['usn_db_copy']28default_db = config['usn_db_copy']
28if '-all' not in default_db:29if '-all' not in default_db:
diff --git a/scripts/report-mistriaged-cves.py b/scripts/report-mistriaged-cves.py
index 352a104..90e717f 100755
--- a/scripts/report-mistriaged-cves.py
+++ b/scripts/report-mistriaged-cves.py
@@ -9,6 +9,7 @@
9# Public License, Version 3 or later. See http://www.gnu.org/copyleft/gpl.html9# Public License, Version 3 or later. See http://www.gnu.org/copyleft/gpl.html
10# for details.10# for details.
11import sys, os, os.path, optparse, cve_lib, re, subprocess, signal11import sys, os, os.path, optparse, cve_lib, re, subprocess, signal
12from uct.config import read_uct_config
1213
13def subprocess_setup():14def subprocess_setup():
14 # Python installs a SIGPIPE handler by default. This is usually not what15 # Python installs a SIGPIPE handler by default. This is usually not what
@@ -63,7 +64,7 @@ def check_apt_cache(package):
6364
6465
65# get base config for path to debian security tracker etc66# get base config for path to debian security tracker etc
66config = cve_lib.read_config()67config = read_uct_config()
6768
68parser = optparse.OptionParser()69parser = optparse.OptionParser()
69parser.add_option("-v", "--verbose", dest="verbose",70parser.add_option("-v", "--verbose", dest="verbose",
@@ -139,7 +140,7 @@ if len(dups) > 0:
139 print('Duplicate entries in not-for-us.txt: ' + ' '.join(dups))140 print('Duplicate entries in not-for-us.txt: ' + ' '.join(dups))
140141
141# get the list of CVEs which Debian knows about142# get the list of CVEs which Debian knows about
142debian = cve_lib.load_debian_cves(cve_lib.config['secure_testing_path'] +143debian = cve_lib.load_debian_cves(config['secure_testing_path'] +
143 '/data/CVE/list')144 '/data/CVE/list')
144145
145cache = {}146cache = {}
diff --git a/scripts/report-packages.py b/scripts/report-packages.py
index bcc3406..33eb8a8 100755
--- a/scripts/report-packages.py
+++ b/scripts/report-packages.py
@@ -23,9 +23,10 @@ import cve_lib
23import usn_lib23import usn_lib
2424
25import source_map25import source_map
26from uct.config import read_uct_config
26source_map = source_map.load()27source_map = source_map.load()
27releases = cve_lib.releases28releases = cve_lib.releases
28config = cve_lib.read_config()29config = read_uct_config()
2930
30parser = optparse.OptionParser()31parser = optparse.OptionParser()
31parser.add_option("-S", "--skip-devel", help="Show only those CVEs *not* in the current devel release", action="store_true")32parser.add_option("-S", "--skip-devel", help="Show only those CVEs *not* in the current devel release", action="store_true")
diff --git a/scripts/report-todo-mir b/scripts/report-todo-mir
index d598fb6..e820a43 100755
--- a/scripts/report-todo-mir
+++ b/scripts/report-todo-mir
@@ -14,7 +14,6 @@ import argparse
14import re14import re
15import sys15import sys
16from launchpadlib.launchpad import Launchpad16from launchpadlib.launchpad import Launchpad
17import cve_lib
1817
19# by default we want all not in Fix Released, Fix Committed, Opinion, Invalid,18# by default we want all not in Fix Released, Fix Committed, Opinion, Invalid,
20# Won't Fix & Expired which leaves the following19# Won't Fix & Expired which leaves the following
@@ -225,9 +224,6 @@ def lookup_tasks_for_assignees(taskdb, assignees):
225 taskdb.add(mir_task)224 taskdb.add(mir_task)
226225
227226
228# Load configuration
229cve_lib.read_config()
230
231# API interface227# API interface
232debug("Connecting to LP ...")228debug("Connecting to LP ...")
233lp = Launchpad.login_anonymously("report-todo-mir", "production", version="devel")229lp = Launchpad.login_anonymously("report-todo-mir", "production", version="devel")
diff --git a/scripts/report-todo-sponsoring b/scripts/report-todo-sponsoring
index 14c87d3..9294a40 100755
--- a/scripts/report-todo-sponsoring
+++ b/scripts/report-todo-sponsoring
@@ -151,9 +151,6 @@ except:
151 print("lpl_common.py seems to be missing. Please create a symlink from $UQT/common/lpl_common.py to $UCT/scripts/", file=sys.stderr)151 print("lpl_common.py seems to be missing. Please create a symlink from $UQT/common/lpl_common.py to $UCT/scripts/", file=sys.stderr)
152 sys.exit(1)152 sys.exit(1)
153153
154# Load configuration
155cve_lib.read_config()
156
157# API interface154# API interface
158debug("Connecting to LP ...")155debug("Connecting to LP ...")
159lp = lpl_common.connect()156lp = lpl_common.connect()
diff --git a/scripts/report-updates.py b/scripts/report-updates.py
index ed69604..4d939ca 100755
--- a/scripts/report-updates.py
+++ b/scripts/report-updates.py
@@ -13,11 +13,12 @@ import os
13import sys13import sys
14import time14import time
15import usn_lib15import usn_lib
16from uct.config import read_uct_config
1617
17import source_map18import source_map
18source_map = source_map.load()19source_map = source_map.load()
1920
20config = cve_lib.read_config()21config = read_uct_config()
21default_db = config['usn_db_copy']22default_db = config['usn_db_copy']
22if '-all' not in default_db:23if '-all' not in default_db:
23 tmp = os.path.splitext(default_db)24 tmp = os.path.splitext(default_db)
diff --git a/scripts/report-version.py b/scripts/report-version.py
index 381c724..c1fdb7d 100755
--- a/scripts/report-version.py
+++ b/scripts/report-version.py
@@ -18,13 +18,14 @@ import sys
18import optparse18import optparse
19import cve_lib19import cve_lib
20import usn_lib20import usn_lib
21from uct.config import read_uct_config
2122
22import apt_pkg23import apt_pkg
23apt_pkg.init_system()24apt_pkg.init_system()
2425
25releases = cve_lib.releases26releases = cve_lib.releases
26esm_releases = cve_lib.esm_releases + cve_lib.esm_infra_releases27esm_releases = cve_lib.esm_releases + cve_lib.esm_infra_releases
27config = cve_lib.read_config()28config = read_uct_config()
2829
29parser = optparse.OptionParser()30parser = optparse.OptionParser()
30parser.add_option("--db", help="Specify the USN database to load", metavar="FILENAME", default=config['usn_db_copy'])31parser.add_option("--db", help="Specify the USN database to load", metavar="FILENAME", default=config['usn_db_copy'])
diff --git a/scripts/sis-changes b/scripts/sis-changes
index d33da15..fc398f4 100755
--- a/scripts/sis-changes
+++ b/scripts/sis-changes
@@ -23,6 +23,7 @@ import urllib.request, urllib.error, urllib.parse
23import cve_lib23import cve_lib
24import json24import json
25from source_map import version_compare, load25from source_map import version_compare, load
26from uct.config import read_uct_config
2627
27try:28try:
28 import lpl_common29 import lpl_common
@@ -148,13 +149,13 @@ parser.add_option("--distribution", help="Distribution to use (eg, 'ubuntu-rtm')
148(opt, args) = parser.parse_args()149(opt, args) = parser.parse_args()
149150
150# Load configuration151# Load configuration
151cve_lib.read_config()152config = read_uct_config()
152153
153# API interface154# API interface
154lp = lpl_common.connect(beta=opt.beta, uri=opt.uri)155lp = lpl_common.connect(beta=opt.beta, uri=opt.uri)
155156
156# Get authenticated URL fetcher157# Get authenticated URL fetcher
157opener = lpl_common.opener_with_cookie(cve_lib.config["plb_authentication"])158opener = lpl_common.opener_with_cookie(config["plb_authentication"])
158if not opener:159if not opener:
159 raise ValueError("Could not open cookies")160 raise ValueError("Could not open cookies")
160161
diff --git a/scripts/sis-generate-usn b/scripts/sis-generate-usn
index 8947779..9ae7a8b 100755
--- a/scripts/sis-generate-usn
+++ b/scripts/sis-generate-usn
@@ -23,6 +23,7 @@ import subprocess
23import sys23import sys
24import tempfile24import tempfile
25import cve_lib25import cve_lib
26from uct.config import read_uct_config
2627
27opter = optparse.OptionParser()28opter = optparse.OptionParser()
28opter.add_option("--debug", help="Verbose processing output", action='store_true')29opter.add_option("--debug", help="Verbose processing output", action='store_true')
@@ -60,7 +61,7 @@ for rel in cve_lib.releases:
60# components (-updates has gotten wrecked by things in -proposed before).61# components (-updates has gotten wrecked by things in -proposed before).
61release_suffixes = ['','-security','-updates']62release_suffixes = ['','-security','-updates']
6263
63config = cve_lib.read_config()64config = read_uct_config()
64archive = config['packages_mirror']65archive = config['packages_mirror']
65cve_lib.check_mirror_timestamp(config, mirror='packages_mirror')66cve_lib.check_mirror_timestamp(config, mirror='packages_mirror')
6667
diff --git a/scripts/source_map.py b/scripts/source_map.py
index c5ec9ad..b6377fb 100755
--- a/scripts/source_map.py
+++ b/scripts/source_map.py
@@ -19,6 +19,7 @@ import sys
19import cve_lib19import cve_lib
20import yaml20import yaml
21import pickle21import pickle
22from uct.config import read_uct_config
2223
23built_using_tags = ["Built-Using", "Static-Built-Using", "X-Cargo-Built-Using"]24built_using_tags = ["Built-Using", "Static-Built-Using", "X-Cargo-Built-Using"]
24apt_pkg.init_system()25apt_pkg.init_system()
@@ -27,28 +28,9 @@ apt_pkg.init_system()
27def version_compare(one, two):28def version_compare(one, two):
28 return apt_pkg.version_compare(one, two)29 return apt_pkg.version_compare(one, two)
2930
30def read_config_file(config_file):
31 '''Read in and do basic validation on config file'''
32 try:
33 from configobj import ConfigObj
34 except ImportError:
35 # Some releases lack this class, so reimplement it quickly
36 class ConfigObj(dict):
37 def __init__(self, filepath):
38 for line in open(filepath).readlines():
39 line = line.strip()
40 if line.startswith('#') or len(line) == 0:
41 continue
42 name, stuff = line.strip().split('=', 1)
43 self[name] = eval(stuff)
44
45 def __attr__(self, name):
46 return self.stuff[name]
47
48 return ConfigObj(config_file)
4931
50def _find_sources(pockets=None, releases=None, skip_eol_releases=True, arch='amd64'):32def _find_sources(pockets=None, releases=None, skip_eol_releases=True, arch='amd64'):
51 config = read_config_file(os.path.expanduser("~/.ubuntu-cve-tracker.conf"))33 config = read_uct_config()
52 if 'packages_mirror' in config:34 if 'packages_mirror' in config:
53 cve_lib.check_mirror_timestamp(config)35 cve_lib.check_mirror_timestamp(config)
54 return _find_from_mirror(config['packages_mirror'],36 return _find_from_mirror(config['packages_mirror'],
@@ -62,7 +44,7 @@ def _find_sources(pockets=None, releases=None, skip_eol_releases=True, arch='amd
6244
6345
64def _find_packages(pockets=None, releases=None, skip_eol_releases=True, arch='amd64'):46def _find_packages(pockets=None, releases=None, skip_eol_releases=True, arch='amd64'):
65 config = read_config_file(os.path.expanduser("~/.ubuntu-cve-tracker.conf"))47 config = read_uct_config()
66 if 'packages_mirror' in config:48 if 'packages_mirror' in config:
67 cve_lib.check_mirror_timestamp(config)49 cve_lib.check_mirror_timestamp(config)
68 return _find_from_mirror(config['packages_mirror'],50 return _find_from_mirror(config['packages_mirror'],
@@ -218,7 +200,7 @@ def load(data_type='sources', pockets=None, releases=None, skip_eol_releases=Tru
218 raise ValueError("'data_type' should be either 'sources' or 'packages'")200 raise ValueError("'data_type' should be either 'sources' or 'packages'")
219201
220 # Attempt to load from cached pickle files202 # Attempt to load from cached pickle files
221 config = read_config_file(os.path.expanduser("~/.ubuntu-cve-tracker.conf"))203 config = read_uct_config()
222 if 'packages_mirror' in config:204 if 'packages_mirror' in config:
223 mirror = config['packages_mirror']205 mirror = config['packages_mirror']
224206
diff --git a/scripts/sync-bugs-kernel.py b/scripts/sync-bugs-kernel.py
index 67b1bf9..21e1c32 100755
--- a/scripts/sync-bugs-kernel.py
+++ b/scripts/sync-bugs-kernel.py
@@ -29,6 +29,7 @@ import sys
29import cve_lib29import cve_lib
30import urlparse30import urlparse
31from lp_lib import UCTLaunchpad31from lp_lib import UCTLaunchpad
32from uct.config import read_uct_config
3233
33priority_to_importance = {34priority_to_importance = {
34 'critical': 'Critical',35 'critical': 'Critical',
@@ -61,7 +62,7 @@ uctlp = UCTLaunchpad(opt)
61# Use devel version so we get the bugtask delete API62# Use devel version so we get the bugtask delete API
62uctlp.lp_version = "devel"63uctlp.lp_version = "devel"
6364
64config = cve_lib.read_config()65config = read_uct_config()
65ktools = config.get('kernel_team_tools_path', None)66ktools = config.get('kernel_team_tools_path', None)
66if not ktools:67if not ktools:
67 raise ValueError("'kernel_team_tools_path' missing in ~/.ubuntu-cve-tracker.conf")68 raise ValueError("'kernel_team_tools_path' missing in ~/.ubuntu-cve-tracker.conf")
diff --git a/scripts/sync-from-usns.py b/scripts/sync-from-usns.py
index c2a2a27..91a2ef3 100755
--- a/scripts/sync-from-usns.py
+++ b/scripts/sync-from-usns.py
@@ -24,6 +24,7 @@ import textwrap
2424
25import cve_lib25import cve_lib
26import usn_lib26import usn_lib
27from uct.config import read_uct_config
2728
28from source_map import version_compare, load29from source_map import version_compare, load
2930
@@ -96,7 +97,7 @@ def parse_args():
96 return args97 return args
9798
98if __name__ == '__main__':99if __name__ == '__main__':
99 config = cve_lib.read_config()100 config = read_uct_config()
100101
101 args = parse_args()102 args = parse_args()
102 if args.git_stage:103 if args.git_stage:
diff --git a/scripts/test_uct_config.py b/scripts/test_uct_config.py
103new file mode 100755104new file mode 100755
index 0000000..c69fbda
--- /dev/null
+++ b/scripts/test_uct_config.py
@@ -0,0 +1,48 @@
1#!/usr/bin/env pytest-3
2# -*- coding: utf-8 -*-
3#
4# Test functions for scripts/uct/config.py
5#
6# Author: Steve Beattie <sbeattie@ubuntu.com>
7# Copyright (C) 2005-2024 Canonical Ltd.
8#
9# This script is distributed under the terms and conditions of the GNU General
10# Public License, Version 2 or later. See http://www.gnu.org/copyleft/gpl.html
11# for details.
12
13import os
14import pytest
15
16# _read_config and _read_config_cached are private functions in
17# uct/config.py for testing
18# users of uct/config.py should only use read_config()
19from uct.config import _read_config, _read_config_cached
20
21TEST_DATA_DIR = "test/uct/config/"
22
23
24class TestUCTConfig:
25
26 def test_config_file_not_exist(self):
27 with pytest.raises(ValueError):
28 _read_config(os.path.join(TEST_DATA_DIR, "does-not-exist.conf"))
29
30 def test_plb_auth_config_not_exist(self):
31 with pytest.raises(ValueError):
32 _read_config(os.path.join(TEST_DATA_DIR, "plb_auth_does_not_exist.conf"))
33
34 def test_no_plb_auth_entry(self):
35 with pytest.raises(ValueError):
36 _read_config(os.path.join(TEST_DATA_DIR, "no_plb_auth_entry.conf"))
37
38 def test_plb_auth_config_simple(self):
39 expected = {
40 'plb_authentication': '/dev/null'
41 }
42 config = _read_config(os.path.join(TEST_DATA_DIR, "plb_auth_dev_null.conf"))
43 assert config == expected
44
45 def test_returned_config_is_singleton(self):
46 first_config = _read_config_cached(os.path.join(TEST_DATA_DIR, "uct.conf"))
47 second_config = _read_config_cached(os.path.join(TEST_DATA_DIR, "uct.conf"))
48 assert first_config is second_config
diff --git a/scripts/test_uct_suggestions.py b/scripts/test_uct_suggestions.py
0new file mode 10075549new file mode 100755
index 0000000..43cfb75
--- /dev/null
+++ b/scripts/test_uct_suggestions.py
@@ -0,0 +1,124 @@
1#!/usr/bin/env pytest-3
2# -*- coding: utf-8 -*-
3
4# Author: Martin Pitt <martin.pitt@ubuntu.com>
5# Author: Kees Cook <kees@ubuntu.com>
6# Author: Jamie Strandboge <jamie@ubuntu.com>
7# Author: Marc Deslauriers <marc.deslauriers@ubuntu.com>
8# Author: Steve Beattie <sbeattie@ubuntu.com>
9# Copyright (C) 2005-2024 Canonical Ltd.
10#
11# This script is distributed under the terms and conditions of the GNU General
12# Public License, Version 2 or later. See http://www.gnu.org/copyleft/gpl.html
13# for details.
14
15import unittest
16from uct.suggestions import IgnoreSuggestions
17
18class CheckCVETest(unittest.TestCase):
19 def test_get_ignore_suggestion(self):
20 '''"Ignore" suggestion text extraction'''
21
22 # instantiate the suggestion handler
23 h = IgnoreSuggestions()
24
25 self.assertEqual("Courier-Authlib", h.get_ignore_suggestion('''SQL injection vulnerability in authpgsqllib.c in Courier-Authlib before 0.62.0, when a non-Latin locale Postgres database is used, allows remote attackers to execute arbitrary SQL commands via query parameters containing apostrophes.'''))
26
27 self.assertEqual("Apple Mac OS X", h.get_ignore_suggestion('''Buffer overflow in the DirectoryService Proxy in DirectoryService in Apple Mac OS X through 10.6.8 allows remote attackers to execute arbitrary code or cause a denial of service (application crash) via unspecified vectors.'''))
28
29 self.assertEqual("KDE", h.get_ignore_suggestion('''HTMLTokenizer::scriptHandler in Konqueror in KDE 3.5.9 and 3.5.10 allows remote attackers to cause a denial of service (application crash) via an invalid document.load call that triggers use of a deleted object. NOTE: some of these details are obtained from third party information.'''))
30
31 self.assertEqual("Sun Solaris", h.get_ignore_suggestion('''The name service cache daemon (nscd) in Sun Solaris 10 and OpenSolaris snv_50 through snv_104 does not properly check permissions, which allows local users to gain privileges and obtain sensitive information via unspecified vectors.'''))
32
33 self.assertEqual("Linux kernel", h.get_ignore_suggestion('''libata in the Linux kernel before 2.6.27.9 does not set minimum timeouts for SG_IO requests, which allows local users to cause a denial of service (Programmed I/O mode on drives) via multiple simultaneous invocations of an unspecified test program.'''))
34
35 self.assertEqual("iGaming", h.get_ignore_suggestion('''Multiple SQL injection vulnerabilities in iGaming 1.5 and earlier allow remote attackers to execute arbitrary SQL commands via the browse parameter to (1) previews.php and (2) reviews.php, and the (3) id parameter to index.php in a viewarticle action.'''))
36
37 self.assertEqual("PHP iCalendar", h.get_ignore_suggestion('''PHP iCalendar 2.24 and earlier allows remote attackers to bypass authentication by setting the phpicalendar and phpicalendar_login cookies to 1.'
38'''))
39
40 # Test length truncation, tweaked to avoid "has" matcher
41 self.assertEqual("** TEST CVE ** This candidate HAS been reserved by an", h.get_ignore_suggestion('''** TEST CVE ** This candidate HAS been reserved by an organization or individual that will use it when announcing a new security problem. When the candidate has been publicized, the details for this candidate will be provided.'''))
42
43 self.assertEqual("Sun OpenSolaris", h.get_ignore_suggestion('''Unspecified vulnerability in the root/boot archive tool in Sun OpenSolaris has unknown impact and local attack vectors, related to a "Temporary file vulnerability," aka Bug ID 6653455.'''))
44
45 self.assertEqual("Red Hat Certificate System", h.get_ignore_suggestion('''Red Hat Certificate System 7.2 uses world-readable permissions for password.conf and unspecified other configuration files, which allows local users to discover passwords by reading these files.'''))
46
47 self.assertEqual("Microsoft Internet Explorer", h.get_ignore_suggestion('''An unspecified function in the JavaScript implementation in Microsoft Internet Explorer creates and exposes a "temporary footprint" when there is a current login to a web site, which makes it easier for remote attackers to trick a user into acting upon a spoofed pop-up message, aka an "in-session phishing attack." NOTE: as of 20090116, the only disclosure is a vague pre-advisory with no actionable information. However, because it is from a well-known researcher, it is being assigned a CVE identifier for tracking purposes.'''))
48
49 self.assertEqual("Umer Inc Songs Portal", h.get_ignore_suggestion('''SQL injection vulnerability in albums.php in Umer Inc Songs Portal allows remote attackers to execute arbitrary SQL commands via the id parameter.'''))
50
51 self.assertEqual("Limbo CMS", h.get_ignore_suggestion('''SQL injection vulnerability in open.php in the Private Messaging (com_privmsg) component for Limbo CMS allows remote attackers to execute arbitrary SQL commands via the id parameter in a pms action to index.php.'''))
52
53 self.assertEqual("phpscripts Ranking Script", h.get_ignore_suggestion('''phpscripts Ranking Script allows remote attackers to bypass authentication and gain administrative access by sending an admin=ja cookie.'''))
54
55 self.assertEqual("A4Desk Event Calendar", h.get_ignore_suggestion('''PHP remote file inclusion vulnerability in index.php in A4Desk Event Calendar, when magic_quotes_gpc is disabled, allows remote attackers to execute arbitrary PHP code via a URL in the v parameter.'''))
56
57 self.assertEqual("Galatolo WebManager", h.get_ignore_suggestion('''Cross-site scripting (XSS) vulnerability in result.php in Galatolo WebManager (GWM) 1.0 allows remote attackers to inject arbitrary web script or HTML via the key parameter.'''))
58
59 self.assertEqual("Sun OpenSolaris", h.get_ignore_suggestion('''Unspecified vulnerability in the process (aka proc) filesystem in Sun OpenSolaris snv_85 through snv_100 allows local users to gain privileges via vectors related to the contract filesystem.'''))
60
61 self.assertEqual("Dreampics Gallery Builder", h.get_ignore_suggestion('''SQL injection vulnerability in index.php in Dreampics Gallery Builder allows remote attackers to execute arbitrary SQL commands via the exhibition_id parameter in a gallery.viewPhotos action.'''))
62
63 self.assertEqual("Omilen Photo Gallery component for Joomla!", h.get_ignore_suggestion('''Directory traversal vulnerability in the Omilen Photo Gallery (com_omphotogallery) component Beta 0.5 for Joomla! allows remote attackers to include and execute arbitrary local files via directory traversal sequences in the controller parameter to index.php.'''))
64
65 self.assertEqual("Webform module for Drupal", h.get_ignore_suggestion('''Cross-site scripting (XSS) vulnerability in the Webform module 5.x before 5.x-2.7 and 6.x before 6.x-2.7, a module for Drupal, allows remote attackers to inject arbitrary web script or HTML via a submission.'''))
66
67 self.assertEqual("Itamar Elharar MusicGallery component for Joomla!", h.get_ignore_suggestion('''SQL injection vulnerability in the Itamar Elharar MusicGallery (com_musicgallery) component for Joomla! allows remote attackers to execute arbitrary SQL commands via the id parameter in an itempage action to index.php. NOTE: the provenance of this information is unknown; the details are obtained solely from third party information.'''))
68
69 self.assertEqual("Kide Shoutbox component for Joomla!", h.get_ignore_suggestion('''The Kide Shoutbox (com_kide) component 0.4.6 for Joomla! does not properly perform authentication, which allows remote attackers to post messages with an arbitrary account name via an insertar action to index.php. NOTE: the provenance of this information is unknown; the details are obtained solely from third party information.'''))
70
71 self.assertEqual("WP-Forum plugin for WordPress", h.get_ignore_suggestion('''Multiple SQL injection vulnerabilities in the WP-Forum plugin before 2.4 for WordPress allow remote attackers to execute arbitrary SQL commands via (1) the search_max parameter in a search action to the default URI, related to wpf.class.php; (2) the forum parameter to an unspecified component, related to wpf.class.php; (3) the topic parameter in a viewforum action to the default URI, related to the remove_topic function in wpf.class.php; or the id parameter in a (4) editpost or (5) viewtopic action to the default URI, related to wpf-post.php.'''))
72
73 self.assertEqual("ListMan extension for TYPO3", h.get_ignore_suggestion('''Cross-site scripting (XSS) vulnerability in the ListMan (nl_listman) extension 1.2.1 for TYPO3 allows remote attackers to inject arbitrary web script or HTML via unspecified vectors.'''))
74
75 self.assertEqual("multiple status.net issues", h.get_ignore_suggestion('''ML-Date: 2011-01-25 12:08:05, ML-Subject: Re: CVE request: multiple status.net issues'''))
76 self.assertEqual("multiple status.net issues", h.get_ignore_suggestion('''ML-Date: 2011-01-25 12:08:05, ML-Subject: CVE request: multiple status.net issues'''))
77 self.assertEqual("multiple status.net issues", h.get_ignore_suggestion('''ML-Date: 2011-01-25 12:08:05, ML-Subject: multiple status.net issues'''))
78 self.assertEqual("Mambo CMS", h.get_ignore_suggestion('''ML-Date: 2011-06-28 16:24:28, ML-Subject: Re: CVE Request: Mambo CMS 4.6.x | Multiple Cross Site Scripting Vulnerabilities'''))
79
80 self.assertEqual("Apple iOS", h.get_ignore_suggestion('''The DNAv4 protocol implementation in the DHCP component in Apple iOS before 6 sends Wi-Fi packets containing a MAC address of a host on a previously used network, which might allow remote attackers to obtain sensitive information about previous device locations by sniffing an unencrypted Wi-Fi network for these packets.'''))
81
82 self.assertEqual("Conceptronic", h.get_ignore_suggestion('''Multiple open redirect vulnerabilities on the Conceptronic C54APM access point with runtime code 1.26 allow remote attackers to redirect users to arbitrary web sites and conduct phishing attacks via (1) the submit-url parameter in a Refresh action to goform/formWlSiteSurvey or (2) the wlan-url parameter to goform/formWlanSetup.'''))
83
84 self.assertEqual("Tapjoy library for Android", h.get_ignore_suggestion('''The Tapjoy library for Android does not verify X.509 certificates from SSL servers, which allows man-in-the-middle attackers to spoof servers and obtain sensitive information via a crafted certificate.'''))
85
86 self.assertEqual("kamkomesan application for Android", h.get_ignore_suggestion('''The kamkomesan (aka com.anek.kamkomesan) application 1.0 for Android does not verify X.509 certificates from SSL servers, which allows man-in-the-middle attackers to spoof servers and obtain sensitive information via a crafted certificate.'''))
87
88 self.assertEqual("DataTables plugin for jQuery", h.get_ignore_suggestion('''Cross-site scripting (XSS) vulnerability in the DataTables plugin 1.10.8 and earlier for jQuery allows remote attackers to inject arbitrary web script or HTML via the scripts parameter to media/unit_testing/templates/6776.php.'''))
89
90 self.assertEqual("Cisco Application Policy Infrastructure Controller (APIC)", h.get_ignore_suggestion('''A vulnerability in Cisco Application Policy Infrastructure Controller (APIC) could allow an authenticated, remote attacker to gain higher privileges than the account is assigned.'''))
91
92 self.assertEqual("Redirection", h.get_ignore_suggestion('''Redirection version 2.7.3 contains a ACE via file inclusion vulnerability in Pass-through mode that can result in allows admins to execute any PHP file in the filesystem.'''))
93
94 self.assertEqual("topydo", h.get_ignore_suggestion('''topydo contains a CWE-20: Improper Input Validation vulnerability in ListFormatParser::parse, file topydo/lib/ListFormat.py line 292 as of d4f843dac71308b2f29a7c2cdc76f055c3841523 that can result in Injection of arbitrary bytes to the terminal,'''))
95
96 self.assertEqual("Joplin", h.get_ignore_suggestion('''Joplin version prior to 1.0.90 contains a XSS evolving into code execution due to enabled nodeIntegration.'''))
97
98 self.assertEqual("Foxit Reader", h.get_ignore_suggestion('''This vulnerability allows remote attackers to execute arbitrary code on vulnerable installations of Foxit Reader 9.2.0.9297.'''))
99
100 self.assertEqual("Exquisite Ultimate Newspaper theme for WordPress", h.get_ignore_suggestion('''The Exquisite Ultimate Newspaper theme 1.3.3 for WordPress has XSS via the anchor identifier to assets/js/jquery.foundation.plugins.js.'''))
101 self.assertEqual("Weaver Xtreme Theme for WordPress", h.get_ignore_suggestion('''The Weaver Xtreme Theme for WordPress is vulnerable to stored Cross-Site Scripting due to insufficient escaping of the profile display name in versions up to, and including, 5.0.7.'''))
102 self.assertEqual("Weaver Show Posts Plugin for WordPress", h.get_ignore_suggestion('''The Weaver Show Posts Plugin for WordPress is vulnerable to stored Cross-Site Scripting due to insufficient escaping of the profile display name in versions up to, and including, 1.6.'''))
103 self.assertEqual("WordPress File Upload and WordPress File Upload Pro plugins for WordPress", h.get_ignore_suggestion('''The WordPress File Upload and WordPress File Upload Pro plugins for WordPress are vulnerable to Path Traversal in versions up to, and including, 4.19.1 via the vulnerable parameter wfu_newpath.'''))
104 self.assertEqual("browserless-chrome", h.get_ignore_suggestion('''This affects all versions of package browserless-chrome. User input flowing from the workspace endpoint gets used to create a file path filePath and this is fetched and then sent back to a user. This can be escaped to fetch arbitrary files from a server.'''))
105 self.assertEqual("@absolunet/kafe", h.get_ignore_suggestion('''This affects the package @absolunet/kafe before 3.2.10. It allows cause a denial of service when validating crafted invalid emails.'''))
106 self.assertEqual("Magneticlab Sàrl Homepage Pop-up plugin", h.get_ignore_suggestion('''Cross-Site Request Forgery (CSRF) vulnerability in Magneticlab Sàrl Homepage Pop-up plugin <= 1.2.5 versions.'''))
107
108 # handle odd whitespace
109 self.assertEqual("PTC Vuforia Studio does not require a token.", h.get_ignore_suggestion('''
110
111
112
113
114 PTC Vuforia Studio does not require a token.
115 '''))
116 self.assertEqual("zxcvbn-ts", h.get_ignore_suggestion('''zxcvbn-ts is a password strength estimator written in typescript.'''))
117 self.assertEqual("VMware Tools for Windows", h.get_ignore_suggestion('''VMware Tools for Windows (10.x.y prior to 10.3.10, 11.x.y prior to 11.1.5) contains a denial-of-service vulnerability due to NULL pointer dereference in vm3dmp driver. Successful exploitation of this issue may allow attackers with normal user privileges to create a denial-of-service condition on the guest machine where VMware Tools is installed.'''))
118
119 self.assertEqual("3DPrint WordPress plugin", h.get_ignore_suggestion("The 3DPrint WordPress plugin before 3.5.6.9 does not protect against CSRF attacks in the modified version of Tiny File Manager included with the plugin, allowing an attacker to craft a malicious request that will create an archive of any files or directories on the target server by tricking a logged in admin into submitting a form."))
120
121 self.assertEqual("Feathersjs", h.get_ignore_suggestion('''Feathersjs is a framework for creating web APIs and real-time applications with TypeScript or JavaScript.'''))
122
123 self.assertEqual("PaulPrinting CMS 2018", h.get_ignore_suggestion("A vulnerability, which was classified as problematic, was found in PaulPrinting CMS 2018"))
124
diff --git a/scripts/uct/config.py b/scripts/uct/config.py
0new file mode 100644125new file mode 100644
index 0000000..9476199
--- /dev/null
+++ b/scripts/uct/config.py
@@ -0,0 +1,75 @@
1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3#
4# Copyright (C) 2008-2024 Canonical Ltd.
5# Author: Jamie Strandboge <jamie@ubuntu.com>
6# Author: Kees Cook <kees@ubuntu.com>
7# Author: Steve Beattie <steve.beattie@canonical.com>
8#
9# This script is distributed under the terms and conditions of the GNU General
10# Public License, Version 3 or later. See http://www.gnu.org/copyleft/gpl.html
11# for details.
12
13from __future__ import print_function
14
15import os
16
17# nobody should be using ubuntu 14.04 LTS with this code, so we don't
18# need a fallback implementation to ConfigObj
19from configobj import ConfigObj
20
21# private singleton object, use read_config() to access
22_config = None
23
24
25# _read_config: does the actual work without writing to the global
26# singleton. This can be used in tests where read_config() will always
27# return the same thing.
28def _read_config(_config_file=None):
29
30 if _config_file:
31 config_file = _config_file
32 else:
33 config_file = os.path.join(
34 os.path.expanduser("~"), ".ubuntu-cve-tracker.conf"
35 )
36
37 if not os.path.exists(config_file):
38 raise ValueError("Could not find '%s'" % (config_file))
39
40 config = ConfigObj(config_file)
41
42 validate_config(config, config_file)
43
44 return config
45
46
47# _read_config_cached: if the results of parsing the config file have
48# not already been cached, save it; otherwise return the singleton.
49# Takes an optional config file path as an argument. This can be used in tests
50# where read_config() will only use ~/.ubuntu-cve-tracker.conf
51def _read_config_cached(_config_file=None):
52 global _config
53
54 # if we've already parsed the config file, return what we already
55 # have
56 if not _config:
57 _config = _read_config()
58
59 return _config
60
61
62# read_uct_config: call with no arguments to get the contents of
63# ~/.ubuntu-cve-tracker.conf
64def read_uct_config():
65 return _read_config_cached()
66
67
68# XXX-FIXME this should use configobj's Validator stuff
69def validate_config(config, config_file):
70
71 # Validate required arguments
72 if "plb_authentication" not in config:
73 raise ValueError(("Could not find 'plb_authentication' entry in %s." % (config_file)))
74 if not os.path.exists(config["plb_authentication"]):
75 raise ValueError(("Could not find file specified by 'plb_authentication' in %s." % (config_file)))
diff --git a/scripts/check_cves/ignored_cache.py b/scripts/uct/ignored_cache.py
0similarity index 100%76similarity index 100%
1rename from scripts/check_cves/ignored_cache.py77rename from scripts/check_cves/ignored_cache.py
2rename to scripts/uct/ignored_cache.py78rename to scripts/uct/ignored_cache.py
diff --git a/scripts/uct/suggestions.py b/scripts/uct/suggestions.py
3new file mode 10075579new file mode 100755
index 0000000..fe496fc
--- /dev/null
+++ b/scripts/uct/suggestions.py
@@ -0,0 +1,131 @@
1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3
4# suggestions module
5#
6# Author: Martin Pitt <martin.pitt@ubuntu.com>
7# Author: Kees Cook <kees@ubuntu.com>
8# Author: Jamie Strandboge <jamie@ubuntu.com>
9# Author: Marc Deslauriers <marc.deslauriers@ubuntu.com>
10# Author: Steve Beattie <sbeattie@ubuntu.com>
11# Copyright (C) 2005-2024 Canonical Ltd.
12#
13# This script is distributed under the terms and conditions of the GNU General
14# Public License, Version 2 or later. See http://www.gnu.org/copyleft/gpl.html
15# for details.
16
17import re
18
19class IgnoreSuggestions:
20
21 def get_ignore_suggestion(self, text):
22 '''Try to find a reasonable suggestion for the user.'''
23 suggestion = ""
24
25 # remove any whitespace
26 rev_text = text.strip()
27 # strip out the added mailing list stuff (locate_cves.py importing)
28 rev_text = re.sub(r'^ML-Date: .* ML-Subject: ', '', rev_text)
29 rev_text = re.sub(r'^(|Re: )CVE (r|R)equest: ', '', rev_text)
30
31 first_sentence = re.split(r'\. ', rev_text)[0]
32
33 # hunt for module/component
34 nouns = ["library", "templates?", "components?", "modules?", "[Pp]lug-?ins?", "extension", "application", "[Tt]hemes?"]
35 products = ["Joomla!", "Drupal", "WordPress", "TYPO3", "Mambo", "Android", "jQuery"]
36 match = re.search(r'(?: in t|^T)he (.*) (%s) (?:.* )?for (%s)' % ("|".join(nouns), "|".join(products)),
37 first_sentence)
38 preposition = "for"
39 if not match:
40 # try swapping order of product and noun in which case there is no preposition
41 match = re.search(r'(?: in t|^T)he (.*) (%s) (%s) (?:.* )' % ("|".join(products), "|".join(nouns)),
42 first_sentence)
43 preposition = ""
44 if match:
45 module = match.group(1)
46 noun = match.group(2)
47 product = match.group(3)
48 for marker in [" module", " ("]:
49 if marker in module:
50 module = module.split(marker)[0]
51 return " ".join(filter(lambda x: len(x) > 0, [module, noun, preposition, product]))
52
53 # hunt for a description of a thing written in a language or for creating something else
54 match = re.search(r'(.*) is a (?:.*) (written in|for creating) (?:.*)', first_sentence)
55 if match:
56 return match.group(1)
57
58 # hunt for a product on a platform
59 match = re.search(r'.* for (Windows|Linux|Mac OS|iOS|Android|iPhone|iPad|BlackBerry|Symbian)', first_sentence)
60 if match:
61 return match.group(0)
62
63 # look for common pattern
64 match = re.search(r'A vulnerability(?:, which was)? classified as (?:.*),? was found in (.*)', first_sentence)
65 if match:
66 return match.group(1)
67
68 # drop commas-extensions
69 if ',' in first_sentence:
70 first_sentence = re.split(r',', first_sentence)[0]
71 phrases = re.split(r' [io]n ', first_sentence)
72
73 # default to the last phrase
74 suggestion = phrases[-1]
75 # move to earlier phrase if suggestion starts with "a"
76 if suggestion.startswith('a ') and len(phrases) > 1:
77 suggestion = phrases[-2]
78
79 version_preps = r'(\s+(before|through|prior to|versions?|[<>]=?))+\s*'
80 version_regex = r'\s+([a-zA-Z\._\-]*[0-9]+[a-zA-Z\._\-]*)+'
81 # prefer 'Apple iOS before <version>' or 'Apple Mac OS X through
82 # <version' in the last phrase over other suggestions
83 if not re.search(r'' + version_preps + version_regex, suggestion):
84 # grab the first phrase with something that may be a version number
85 for p in phrases:
86 if re.search(r'' + version_regex, p):
87 suggestion = p
88 break
89 if re.search(r'^[^,]+\s+for\s+', p):
90 suggestion = p
91 break
92
93 # try to find a good suggestion from the phrase (ie suggest 'Linux
94 # kernel' from 'the Linux kernel before 2.6.27')
95 suggestion = re.split(r'\s+([a-zA-Z\._\-]*[0-9]+[a-zA-Z\._\-]*)+', suggestion)[0]
96 # "blah in component for Software"
97 if re.search(r'^[^,]+\s+for\s+', suggestion):
98 suggestion = re.split(r'[^,]+\s+for\s+', suggestion)[1]
99
100 # Chop off action verbs
101 cleanup_regexes = [
102 # clean up leading "in" or "the"
103 r'^\s*([tT]he|[iI]n)\s+',
104 # clean up trailing version prepositions like "before" or "through"
105 # from version details
106 r'' + version_preps + '$',
107 # clean up trailing parens
108 r'\s+\([^\)]+\)\s*$',
109 # action verbs
110 r'\s+(has|creates|allows|could|contains)($|\s+.*)',
111 # "vulnerbale installations of"
112 r'(^|\s+)vulnerable\sinstallations?\sof($|\s+)',
113 # This affects all versions of package
114 r'^This affects all versions of package\s+',
115 # This affects the package
116 r'^This affects the package\s+',
117 ]
118
119 for regex in cleanup_regexes:
120 if re.search(regex, suggestion):
121 suggestion = re.sub(regex, '', suggestion)
122
123 # if the phrase is too long, truncate it to max_length, but make
124 # sure we don't have a partial word at the end
125 max_length = 64
126 if len(suggestion) > max_length:
127 suggestion = suggestion[:max_length]
128 suggestion = re.sub(r'\s+\w+$', '', suggestion)
129
130 return suggestion
131
diff --git a/scripts/usn_lib.py b/scripts/usn_lib.py
index 3e45239..d8c7622 100644
--- a/scripts/usn_lib.py
+++ b/scripts/usn_lib.py
@@ -16,7 +16,7 @@ import subprocess
16import sys16import sys
17import urllib17import urllib
18from source_map import version_compare18from source_map import version_compare
19import cve_lib19from uct.config import read_uct_config
2020
21try:21try:
22 import cPickle22 import cPickle
@@ -164,7 +164,7 @@ class USNdb(object):
164 if db:164 if db:
165 db_name = db165 db_name = db
166 else:166 else:
167 config = cve_lib.read_config()167 config = read_uct_config()
168 if config['usn_db_copy']:168 if config['usn_db_copy']:
169 db_name = config['usn_db_copy']169 db_name = config['usn_db_copy']
170170
diff --git a/test/uct/config/no_plb_auth_entry.conf b/test/uct/config/no_plb_auth_entry.conf
171new file mode 100644171new file mode 100644
index 0000000..755ad8c
--- /dev/null
+++ b/test/uct/config/no_plb_auth_entry.conf
@@ -0,0 +1,2 @@
1# python-launchpad-bugs authentication cookies file (sis-changes)
2not_plb_authentication="/bin/true"
diff --git a/test/uct/config/plb_auth_dev_null.conf b/test/uct/config/plb_auth_dev_null.conf
0new file mode 1006443new file mode 100644
index 0000000..1e33174
--- /dev/null
+++ b/test/uct/config/plb_auth_dev_null.conf
@@ -0,0 +1,2 @@
1# python-launchpad-bugs authentication cookies file (sis-changes)
2plb_authentication="/dev/null"
diff --git a/test/uct/config/plb_auth_does_not_exist.conf b/test/uct/config/plb_auth_does_not_exist.conf
0new file mode 1006443new file mode 100644
index 0000000..9a81f39
--- /dev/null
+++ b/test/uct/config/plb_auth_does_not_exist.conf
@@ -0,0 +1,2 @@
1# python-launchpad-bugs authentication cookies file (sis-changes)
2plb_authentication="/does/not/exist"
diff --git a/test/uct/config/uct.conf b/test/uct/config/uct.conf
0new file mode 1006443new file mode 100644
index 0000000..8bf6334
--- /dev/null
+++ b/test/uct/config/uct.conf
@@ -0,0 +1,15 @@
1# python-launchpad-bugs authentication cookies file (sis-changes)
2plb_authentication="/dev/null"
3
4# path to Debian "security-tracker" GIT tree (check-cves)
5secure_testing_path='/home/ubuntu-security/git/cve_trackers/debian-security-tracker'
6
7# path to archive-layout mirror of supported architectures
8# sis-generate-usn, packages-mirror)
9packages_mirror='/home/ubuntu-security/archive-metadata/ubuntu-archive-metadata'
10
11# same as packages_mirror, but for Debian testing repository
12debian_mirror='/home/ubuntu-security/archive-metadata/debian-testing-archive-metadata'
13
14# path to usn-tool bzr tree, used to manipulate USN databases
15usn_tool='/home/ubuntu-security/git/usn-tool'

Subscribers

People subscribed via source and target branches