From 82dbed2860c8678ce244c773911b89c26261452b Mon Sep 17 00:00:00 2001 From: Ryan Sawhill Aroha Date: Tue, 27 Jun 2017 16:44:21 +0530 Subject: [PATCH 01/22] fix #66 --- rhsda.py | 34 ++++++++++++++++++---------------- rhsecapi.py | 6 +++--- 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/rhsda.py b/rhsda.py index 5064857..60c0ea8 100644 --- a/rhsda.py +++ b/rhsda.py @@ -384,7 +384,7 @@ def __stripjoin(self, input, oneLineEach=False): def __check_field(self, field, jsoninput): """Return True if field is desired and exists in jsoninput.""" - if field in self.cfg.desiredFields and jsoninput.has_key(field): + if field in self.cfg.desiredFields and field in jsoninput: return True return False @@ -460,7 +460,7 @@ def _get_and_parse_cve(self, cve): out.append(" CVSS3 : {0} ({1})".format(J['cvss3']['cvss3_base_score'], vector)) # BUGZILLA if 'bugzilla' in self.cfg.desiredFields: - if J.has_key('bugzilla'): + if 'bugzilla' in J: if self.cfg.urls: bug = J['bugzilla']['url'] else: @@ -506,7 +506,7 @@ def _get_and_parse_cve(self, cve): # If product doesn't match spotlight, go to next continue pkg = "" - if release.has_key('package'): + if 'package' in release: pkg = " [{0}]".format(release['package']) advisory = release['advisory'] if self.cfg.urls: @@ -534,7 +534,7 @@ def _get_and_parse_cve(self, cve): # If product doesn't match spotlight, go to next continue pkg = "" - if state.has_key('package_name'): + if 'package_name' in state: pkg = " [{0}]".format(state['package_name']) out.append(" {0}: {1}{2}".format(state['fix_state'], state['product_name'], pkg)) if self.cfg.product and not foundProduct_package_state: @@ -889,30 +889,32 @@ def cve_search_query(self, params, outFormat='list', urls=False): rows.append(["CVE ID", "PUB DATE", "BUGZILLA", "SEVERITY", "CVSS2", "CVSS3", "RHSAS", "PKGS"]) for i in result: date = "" - if i.has_key('public_date'): + if 'public_date' in i and i['public_date'] is not None: date = i['public_date'].split("T")[0] bz = "" if urls: cve = "https://access.redhat.com/security/cve/{0}".format(i['CVE']) - if i.has_key('bugzilla'): + if 'bugzilla' in i and i['bugzilla'] is not None: bz = "https://bugzilla.redhat.com/show_bug.cgi?id={0}".format(i['bugzilla']) else: cve = i['CVE'] - if i.has_key('bugzilla'): + if 'bugzilla' in i and i['bugzilla'] is not None: bz = i['bugzilla'] - severity = i['severity'] - rhsas = "" - if i.has_key('advisories'): - rhsas = "{0: >2}".format(len(i['advisories'])) - pkgs = "" - if i.has_key('affected_packages'): - pkgs = "{0: >2}".format(len(i['affected_packages'])) + severity = "" + if 'severity' in i and i['severity'] is not None: + severity = i['severity'] cvss2 = "" - if i.has_key('cvss_score'): + if 'cvss_score' in i and i['cvss_score'] is not None: cvss2 = str(i['cvss_score']) cvss3 = "" - if i.has_key('cvss3_score'): + if 'cvss3_score' in i and i['cvss3_score'] is not None: cvss3 = str(i['cvss3_score']) + rhsas = "" + if 'advisories' in i and i['advisories'] is not None: + rhsas = "{0: >2}".format(len(i['advisories'])) + pkgs = "" + if 'affected_packages' in i and i['affected_packages'] is not None: + pkgs = "{0: >2}".format(len(i['affected_packages'])) line = [cve, date, bz, severity, cvss2, cvss3, rhsas, pkgs] rows.append(line) return self._columnize(rows, sep=" ") diff --git a/rhsecapi.py b/rhsecapi.py index e396a28..03a463b 100755 --- a/rhsecapi.py +++ b/rhsecapi.py @@ -46,8 +46,8 @@ # Globals prog = 'rhsecapi' vers = {} -vers['version'] = '1.0.0_rc10' -vers['date'] = '2017/01/05' +vers['version'] = '1.0.1' +vers['date'] = '2017/06/27' # Logging @@ -179,7 +179,7 @@ def parse_args(): help="Narrow down results by severity rating (specify one of 'low', 'moderate', 'important', or 'critical')") g_listByAttr.add_argument( '--q-product', metavar="PRODUCT", - help="Narrow down results by product name via case-insensitive regex (e.g.: 'linux 7' or openstack platform [89]'); the API checks this against the 'FIXED_RELEASES' field so will only match CVEs where PRODUCT matches the 'product_name' of some released errata") + help="Narrow down results by product name via case-insensitive regex (e.g.: 'linux 7' or 'openstack platform [89]'); the API checks this against the 'FIXED_RELEASES' field so will only match CVEs where PRODUCT matches the 'product_name' of some released errata") g_listByAttr.add_argument( '--q-package', metavar="PKG", help="Narrow down results by package name (e.g.: 'samba' or 'thunderbird')") From f5225cf497383d6ec762d9fe76d2710c6d445339 Mon Sep 17 00:00:00 2001 From: Ryan Sawhill Aroha Date: Tue, 27 Jun 2017 16:55:08 +0530 Subject: [PATCH 02/22] readme update --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 693f401..3c0aa9e 100644 --- a/README.md +++ b/README.md @@ -552,7 +552,7 @@ FIND CVES BY ATTRIBUTE: --q-severity IMPACT Narrow down results by severity rating (specify one of 'low', 'moderate', 'important', or 'critical') --q-product PRODUCT Narrow down results by product name via case- - insensitive regex (e.g.: 'linux 7' or openstack + insensitive regex (e.g.: 'linux 7' or 'openstack platform [89]'); the API checks this against the 'FIXED_RELEASES' field so will only match CVEs where PRODUCT matches the 'product_name' of some released From 0c7a6f48304a1603a522412e525dd2f31964aa6d Mon Sep 17 00:00:00 2001 From: Kyle Walker Date: Tue, 23 Jul 2019 16:31:31 -0400 Subject: [PATCH 03/22] all: Packaging for setuptools and making python3 compatible Reorganizing the project. Multiple changes, but the intent of each alteration is to make the project: - Installable via setuptools - Compatible with python2 and python3 --- rhsecapi.py => bin/rhsecapi | 4 +++- rhsda.py => rhsda/__init__.py | 37 ++++++++++++++++++++++++--------- setup.py | 39 +++++++++++++++++++++++++++++++++++ 3 files changed, 69 insertions(+), 11 deletions(-) rename rhsecapi.py => bin/rhsecapi (99%) rename rhsda.py => rhsda/__init__.py (97%) create mode 100644 setup.py diff --git a/rhsecapi.py b/bin/rhsecapi similarity index 99% rename from rhsecapi.py rename to bin/rhsecapi index 03a463b..6079aaf 100755 --- a/rhsecapi.py +++ b/bin/rhsecapi @@ -346,9 +346,11 @@ def parse_args(): def main(opts): apiclient = rhsda.ApiClient(opts.loglevel) + from os import environ - if environ.has_key('RHSDA_URL') and environ['RHSDA_URL'].startswith('http'): + if environ.get('RHSDA_URL', '').startswith('http'): apiclient.cfg.apiUrl = environ['RHSDA_URL'] + searchOutput = "" iavaOutput = "" cveOutput = "" diff --git a/rhsda.py b/rhsda/__init__.py similarity index 97% rename from rhsda.py rename to rhsda/__init__.py index 60c0ea8..8d82a84 100644 --- a/rhsda.py +++ b/rhsda/__init__.py @@ -24,11 +24,15 @@ import textwrap, fcntl, termios, struct import json import signal -import copy_reg import types -import multiprocessing.dummy as multiprocessing +import multiprocessing as multiprocessing from argparse import Namespace +# Check to see if we are running Python 2 or 3 +try: + import copy_reg +except: + import copyreg as copy_reg # Logging logging.addLevelName(25, 'NOTICE') @@ -72,7 +76,6 @@ cveFields.most = list(cveFields.all) for f in cveFields.not_most: cveFields.most.remove(f) -del(f) # Simple set of most important fields cveFields.base = [ 'threat_severity', @@ -101,7 +104,6 @@ # A list of all fields + all aliases cveFields.all_plus_aliases = list(cveFields.all) cveFields.all_plus_aliases.extend([k for k in cveFields.aliases]) -del(k) # Regex to match a CVE id string @@ -122,10 +124,15 @@ def _reduce_method(m): # Set default number of worker threads -if multiprocessing.cpu_count() <= 2: +try: + hardwareCPUCount = multiprocessing.cpu_count() +except: + hardwareCPUCount = multiprocessing.dummy.cpu_count() + +if hardwareCPUCount <= 2: numThreadsDefault = 4 else: - numThreadsDefault = multiprocessing.cpu_count() * 2 + numThreadsDefault = hardwareCPUCount * 2 def jprint(jsoninput): @@ -728,10 +735,20 @@ def mget_cves(self, cves, numThreads=0, onlyCount=False, outFormat='plaintext', """ if outFormat not in ['plaintext', 'json', 'jsonpretty']: raise ValueError("Invalid outFormat ('{0}') requested; should be one of: 'plaintext', 'json', 'jsonpretty'".format(outFormat)) - if isinstance(cves, str) or isinstance(cves, file): - cves = extract_cves_from_input(cves) - elif not isinstance(cves, list): - raise ValueError("Invalid 'cves=' argument input; must be list, string, or file obj") + # This is necessary as Python3 doesn't have "file" types + try: + if isinstance(cves, (str, file)): + cves = extract_cves_from_input(cves) + elif not isinstance(cves, list): + raise ValueError("Invalid 'cves=' argument input; must be list, string, or file obj") + except: + from io import IOBase + + if isinstance(cves, (str, IOBase)): + cves = extract_cves_from_input(cves) + elif not isinstance(cves, list): + raise ValueError("Invalid 'cves=' argument input; must be list, string, or file obj") + if not len(cves): if outFormat in ['plaintext', 'jsonpretty']: return "" diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..5a3c8fe --- /dev/null +++ b/setup.py @@ -0,0 +1,39 @@ +import os +from setuptools import setup, find_packages + +def read(fname): + return open(os.path.join(os.path.dirname(__file__), fname)).read() + +DESCRIPTION = """ +Leverage Red Hat's Security Data API to find CVEs by various attributes +(date, severity, scores, package, IAVA, etc). Retrieve customizable details +about found CVEs or about specific CVE ids input on cmdline. Parse +arbitrary stdin for CVE ids and generate a customized report, optionally +sending it straight to pastebin. Searches are done via a single +instantaneous http request and CVE retrieval is parallelized, utilizing +multiple threads at once. Python requests is used for all remote +communication, so proxy support is baked right in. BASH intelligent +tab-completion is supported via optional Python argcomplete module. Python2 +tested on RHEL6, RHEL7, & Fedora and Python3 on Fedora but since it doesn't +integrate with RHN/RHSM/yum/Satellite, it can be used on any +internet-connected machine. Feedback, feature requests, and code +contributions welcome. +""" +setup( + name = 'rhsecapi', + version = '1.0.1', + author = 'Ryan Sawhill Aroha', + author_email = 'rsaw@redhat.com', + description = (DESCRIPTION), + license = 'GPL', + packages = find_packages(), + + scripts = ['bin/rhsecapi'], + install_requires = [ + 'requests', + ], + + long_description=read('README.md') +) + + From 581924e865626204eda69706c9525b22094107d4 Mon Sep 17 00:00:00 2001 From: Kyle Walker Date: Tue, 23 Jul 2019 16:37:46 -0400 Subject: [PATCH 04/22] setup: Change the formatting for RPM compatibility Thre previous revision was exceptionally verbose and didn't play well with the "python setup.py bdist_rpm" process. The new revision makes it more terse. --- setup.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/setup.py b/setup.py index 5a3c8fe..acdde2e 100644 --- a/setup.py +++ b/setup.py @@ -4,7 +4,7 @@ def read(fname): return open(os.path.join(os.path.dirname(__file__), fname)).read() -DESCRIPTION = """ +LONG_DESCRIPTION = """ Leverage Red Hat's Security Data API to find CVEs by various attributes (date, severity, scores, package, IAVA, etc). Retrieve customizable details about found CVEs or about specific CVE ids input on cmdline. Parse @@ -14,7 +14,7 @@ def read(fname): multiple threads at once. Python requests is used for all remote communication, so proxy support is baked right in. BASH intelligent tab-completion is supported via optional Python argcomplete module. Python2 -tested on RHEL6, RHEL7, & Fedora and Python3 on Fedora but since it doesn't +tested on RHEL6, RHEL7, & Fedora and Python3 on Fedora but since it doesnt integrate with RHN/RHSM/yum/Satellite, it can be used on any internet-connected machine. Feedback, feature requests, and code contributions welcome. @@ -24,7 +24,7 @@ def read(fname): version = '1.0.1', author = 'Ryan Sawhill Aroha', author_email = 'rsaw@redhat.com', - description = (DESCRIPTION), + description = 'Provides a simple interface for the Red Hat Security Data API', license = 'GPL', packages = find_packages(), @@ -33,7 +33,7 @@ def read(fname): 'requests', ], - long_description=read('README.md') + long_description=LONG_DESCRIPTION ) From 442ad19cd0d68f0ddffe34e5fce0b914976450e0 Mon Sep 17 00:00:00 2001 From: Kyle Walker Date: Tue, 23 Jul 2019 17:04:19 -0400 Subject: [PATCH 05/22] rhsecapi: Remove the "less" pager use There seems to be a problem within argparse for 3.7 which results in an error around file handling. The process of resolving it would be too arduous when compared to the alternative of avoiding calling "less" directly in the manner previously used. The recommended alternative is to simply pipe the help output to "less". --- bin/rhsecapi | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/bin/rhsecapi b/bin/rhsecapi index 6079aaf..c9b9d92 100755 --- a/bin/rhsecapi +++ b/bin/rhsecapi @@ -281,12 +281,7 @@ def parse_args(): argcomplete.autocomplete(p) o = p.parse_args() if o.showHelp: - from tempfile import NamedTemporaryFile - from subprocess import call - tmp = NamedTemporaryFile(prefix='{0}-help-'.format(prog), suffix='.txt') - p.print_help(file=tmp) - tmp.flush() - call(['less', tmp.name]) + p.print_help() sys.exit() # Add search params to dict o.searchParams = { From c58ae4e289c662c6e275dc3a4c66994befe402d4 Mon Sep 17 00:00:00 2001 From: Kyle Walker Date: Tue, 23 Jul 2019 17:07:08 -0400 Subject: [PATCH 06/22] all: Change the "See <>" URL to reflect the new location The previous location under "ryran" is no longer applicable. Moving mentions of that path over to the RedHatOfficial group. --- README.md | 8 ++++---- bin/rhsecapi | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 3c0aa9e..a6772fb 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # rhsecapi -`rhsecapi` makes it easy to interface with the [Red Hat Security Data API](https://access.redhat.com/documentation/en/red-hat-security-data-api/) -- even from [behind a proxy](https://github.com/ryran/rhsecapi/issues/29). From the rpm description: +`rhsecapi` makes it easy to interface with the [Red Hat Security Data API](https://access.redhat.com/documentation/en/red-hat-security-data-api/) -- even from [behind a proxy](https://github.com/RedHatOfficial/rhsecapi/issues/29). From the rpm description: > **Leverage Red Hat's Security Data API to find CVEs by various attributes (date, severity, scores, package, IAVA, etc). Retrieve customizable details about found CVEs or about specific CVE ids input on cmdline. Parse arbitrary stdin for CVE ids and generate a customized report, optionally sending it straight to pastebin. Searches are done via a single instantaneous http request and CVE retrieval is parallelized, utilizing multiple threads at once. Python requests is used for all remote communication, so proxy support is baked right in. BASH intelligent tab-completion is supported via optional Python argcomplete module. Python2 tested on RHEL6, RHEL7, & Fedora but since it doesn't integrate with RHN/RHSM/yum/Satellite, it can be used on any internet-connected machine. Feedback, feature requests, and code contributions welcome.** @@ -174,7 +174,7 @@ sys 0m0.055s 1. Execute: `rhsecapi` - **Option 2: Download latest release from github and run it** - 1. Go to [Releases](https://github.com/ryran/rhsecapi/releases) + 1. Go to [Releases](https://github.com/RedHatOfficial/rhsecapi/releases) 1. Download and extract the latest release 1. Optional: `mkdir -p ~/bin; ln -sv /PATH/TO/rhsecapi.py ~/bin/rhsecapi` 1. Execute: `rhsecapi` @@ -198,7 +198,7 @@ Run rhsecapi --help for full help page VERSION: rhsecapi v1.0.0_rc10 last mod 2017/01/05 - See to report bugs or RFEs + See to report bugs or RFEs ``` ## BASH intelligent tab-completion @@ -657,7 +657,7 @@ GENERAL OPTIONS: VERSION: rhsecapi v1.0.0_rc10 last mod 2017/01/05 - See to report bugs or RFEs + See to report bugs or RFEs ``` diff --git a/bin/rhsecapi b/bin/rhsecapi index c9b9d92..8b66a7b 100755 --- a/bin/rhsecapi +++ b/bin/rhsecapi @@ -151,7 +151,7 @@ def parse_args(): epilog = ( "VERSION:\n" " {0}\n" - " See to report bugs or RFEs").format(version) + " See to report bugs or RFEs").format(version) fmt = lambda prog: CustomFormatter(prog) p = argparse.ArgumentParser( prog=prog, From 059e69d5e0d82dd027d3f4c73482b4caab0e0333 Mon Sep 17 00:00:00 2001 From: Kyle Walker Date: Wed, 24 Jul 2019 09:17:06 -0400 Subject: [PATCH 07/22] rhsecapi: Set the default log level to WARNING The previous revision emitted a lot of informational output besides just the response. This is helpful if the utility is being investigated, but requiring a "-l warning" flag to omit those messages is not really necessary. Setting the default to WARNING to silence the NOTICE messages. --- bin/rhsecapi | 2 +- rhsda/__init__.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/rhsecapi b/bin/rhsecapi index 8b66a7b..d0ae13b 100755 --- a/bin/rhsecapi +++ b/bin/rhsecapi @@ -256,7 +256,7 @@ def parse_args(): '-c', '--count', action='store_true', help="Exit after printing CVE counts") g_general.add_argument( - '-l', '--loglevel', choices=['debug','info','notice','warning'], default='notice', + '-l', '--loglevel', choices=['debug','info','notice','warning'], default='warning', help="Configure logging level threshold; lower from the default of 'notice' to see extra details printed to stderr") g_general.add_argument( '-t', '--threads', metavar="THREDS", type=int, default=rhsda.numThreadsDefault, diff --git a/rhsda/__init__.py b/rhsda/__init__.py index 8d82a84..ee36c8a 100644 --- a/rhsda/__init__.py +++ b/rhsda/__init__.py @@ -40,7 +40,7 @@ consolehandler.setLevel('DEBUG') consolehandler.setFormatter(logging.Formatter("[%(levelname)-7s] %(name)s: %(message)s")) logger = logging.getLogger('rhsda') -logger.setLevel('NOTICE') +logger.setLevel('WARNING') logger.addHandler(consolehandler) From 251b71ed81a5503b7d4426a3dae2c0fa5a116b68 Mon Sep 17 00:00:00 2001 From: Kyle Walker Date: Wed, 24 Jul 2019 11:21:02 -0400 Subject: [PATCH 08/22] setup: Bump the version to 1.0.2 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index acdde2e..9760dcc 100644 --- a/setup.py +++ b/setup.py @@ -21,7 +21,7 @@ def read(fname): """ setup( name = 'rhsecapi', - version = '1.0.1', + version = '1.0.2', author = 'Ryan Sawhill Aroha', author_email = 'rsaw@redhat.com', description = 'Provides a simple interface for the Red Hat Security Data API', From 57f66ca161d1fdcddd426fda8ccaa27648c0ff1b Mon Sep 17 00:00:00 2001 From: Kyle Walker Date: Wed, 24 Jul 2019 12:39:53 -0400 Subject: [PATCH 09/22] build: Add a rpkg build configuration and specfile --- rhsecapi.spec | 136 ++++++++++++++++++++++++++++++++++++++++++++++++++ rpkg.macros | 11 ++++ 2 files changed, 147 insertions(+) create mode 100644 rhsecapi.spec create mode 100644 rpkg.macros diff --git a/rhsecapi.spec b/rhsecapi.spec new file mode 100644 index 0000000..dd81398 --- /dev/null +++ b/rhsecapi.spec @@ -0,0 +1,136 @@ +# Disable the debuginfo build operation +%global debug_package %{nil} + +%if 0%{?fedora} +%bcond_without python3 +%else +%if 0%{?rhel} >= 8 +%bcond_without python3 +%else +%bcond_with python3 +%endif +%endif + +Name: {{{ git_name }}} +Version: {{{ git_version }}} +Release: 1%{?dist} +Summary: Provides a simple interface for the Red Hat Security Data API + +License: GPL +URL: https://github.com/RedHatOfficial/rhsecapi +Source: {{{ git_pack }}} + +%if %{with python3} +BuildRequires: python3-devel python3-setuptools +Requires: python3-argparse python3-requests +%else +BuildRequires: python-devel python-setuptools +Requires: python-argparse python-requests +%endif + +%description +Leverage Red Hat's Security Data API to find CVEs by various attributes +(date, severity, scores, package, IAVA, etc). Retrieve customizable details +about found CVEs or about specific CVE ids input on cmdline. Parse +arbitrary stdin for CVE ids and generate a customized report, optionally +sending it straight to pastebin. Searches are done via a single +instantaneous http request and CVE retrieval is parallelized, utilizing +multiple threads at once. Python requests is used for all remote +communication, so proxy support is baked right in. BASH intelligent +tab-completion is supported via optional Python argcomplete module. Python2 +tested on RHEL6, RHEL7, & Fedora and Python3 on Fedora but since it doesnt +integrate with RHN/RHSM/yum/Satellite, it can be used on any +internet-connected machine. Feedback, feature requests, and code +contributions welcome. + +%if %{with python3} +%package -n python3-%{name} +Summary: Provides a simple interface for the Red Hat Security Data API + +%description -n python3-%{name} +Leverage Red Hat's Security Data API to find CVEs by various attributes +(date, severity, scores, package, IAVA, etc). Retrieve customizable details +about found CVEs or about specific CVE ids input on cmdline. Parse +arbitrary stdin for CVE ids and generate a customized report, optionally +sending it straight to pastebin. Searches are done via a single +instantaneous http request and CVE retrieval is parallelized, utilizing +multiple threads at once. Python requests is used for all remote +communication, so proxy support is baked right in. BASH intelligent +tab-completion is supported via optional Python argcomplete module. Python2 +tested on RHEL6, RHEL7, & Fedora and Python3 on Fedora but since it doesnt +integrate with RHN/RHSM/yum/Satellite, it can be used on any +internet-connected machine. Feedback, feature requests, and code +contributions welcome. +%else +%package -n python2-%{name} +Summary: Provides a simple interface for the Red Hat Security Data API + +%description -n python2-%{name} +Leverage Red Hat's Security Data API to find CVEs by various attributes +(date, severity, scores, package, IAVA, etc). Retrieve customizable details +about found CVEs or about specific CVE ids input on cmdline. Parse +arbitrary stdin for CVE ids and generate a customized report, optionally +sending it straight to pastebin. Searches are done via a single +instantaneous http request and CVE retrieval is parallelized, utilizing +multiple threads at once. Python requests is used for all remote +communication, so proxy support is baked right in. BASH intelligent +tab-completion is supported via optional Python argcomplete module. Python2 +tested on RHEL6, RHEL7, & Fedora and Python3 on Fedora but since it doesnt +integrate with RHN/RHSM/yum/Satellite, it can be used on any +internet-connected machine. Feedback, feature requests, and code +contributions welcome. +%endif + +%prep +%autosetup -c +%if %{with python3} +cp -a %{name}-%{version} python3 +%else +cp -a %{name}-%{version} python2 +%endif + +%build +rm -rf $RPM_BUILD_ROOT +mkdir $RPM_BUILD_ROOT + +%if %{with python3} +pushd python3 +# Remove CFLAGS=... for noarch packages (unneeded) +CFLAGS="$RPM_OPT_FLAGS" %{__python3} setup.py build +popd +%else +pushd python2 +# Remove CFLAGS=... for noarch packages (unneeded) +CFLAGS="$RPM_OPT_FLAGS" %{__python} setup.py build +popd +%endif + +%install +rm -rf $RPM_BUILD_ROOT + +%if %{with python3} +pushd python3 +%{__python3} setup.py install -O1 --root $RPM_BUILD_ROOT/ +popd +%else +pushd python2 +%{__python} setup.py install -O1 --root $RPM_BUILD_ROOT/ +popd +%endif + +%files +%{_bindir}/* +# For noarch packages: sitelib +%if %{with python3} +%files -n python3-%{name} +%{_bindir}/* +# For noarch packages: sitelib +%{python3_sitelib}/* +%else +%files -n python2-%{name} +%{python_sitelib}/* +%endif + +%changelog +{{{ git_changelog }}} + diff --git a/rpkg.macros b/rpkg.macros new file mode 100644 index 0000000..2185170 --- /dev/null +++ b/rpkg.macros @@ -0,0 +1,11 @@ +#!/bin/bash + +function git_version { + declare name="$(cached git_name)" "$@" + + latest_tag="$(git tag --list --sort=-taggerdate | tail -1)" + latest_tag_version="$(echo $latest_tag | sed "s/^v//g")" + + output "${latest_tag_version}" +} + From 7c9f7565f89ed10b16724c5b29c4c87e9eb53405 Mon Sep 17 00:00:00 2001 From: Kyle Walker Date: Thu, 25 Jul 2019 09:44:11 -0400 Subject: [PATCH 10/22] All: Remove IAVAs as a query subject The backend security API no longer supports IAVAs as a query subject. This was removed towards the end of 2018. This patch attempts to remove the corresponding client-side IAVA query code while preserving the remaining operations. --- README.md | 131 +++++--------------------------------- bin/rhsecapi | 34 ++-------- rhsda/__init__.py | 159 +--------------------------------------------- 3 files changed, 22 insertions(+), 302 deletions(-) diff --git a/README.md b/README.md index a6772fb..1da4a01 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ `rhsecapi` makes it easy to interface with the [Red Hat Security Data API](https://access.redhat.com/documentation/en/red-hat-security-data-api/) -- even from [behind a proxy](https://github.com/RedHatOfficial/rhsecapi/issues/29). From the rpm description: -> **Leverage Red Hat's Security Data API to find CVEs by various attributes (date, severity, scores, package, IAVA, etc). Retrieve customizable details about found CVEs or about specific CVE ids input on cmdline. Parse arbitrary stdin for CVE ids and generate a customized report, optionally sending it straight to pastebin. Searches are done via a single instantaneous http request and CVE retrieval is parallelized, utilizing multiple threads at once. Python requests is used for all remote communication, so proxy support is baked right in. BASH intelligent tab-completion is supported via optional Python argcomplete module. Python2 tested on RHEL6, RHEL7, & Fedora but since it doesn't integrate with RHN/RHSM/yum/Satellite, it can be used on any internet-connected machine. Feedback, feature requests, and code contributions welcome.** +> **Leverage Red Hat's Security Data API to find CVEs by various attributes (date, severity, scores, package, etc). Retrieve customizable details about found CVEs or about specific CVE ids input on cmdline. Parse arbitrary stdin for CVE ids and generate a customized report, optionally sending it straight to pastebin. Searches are done via a single instantaneous http request and CVE retrieval is parallelized, utilizing multiple threads at once. Python requests is used for all remote communication, so proxy support is baked right in. BASH intelligent tab-completion is supported via optional Python argcomplete module. Python2 tested on RHEL6, RHEL7, & Fedora but since it doesn't integrate with RHN/RHSM/yum/Satellite, it can be used on any internet-connected machine. Feedback, feature requests, and code contributions welcome.** If you don't have a GitHub account but do have a Red Hat Portal login, go here: [New cmdline tool using Red Hat's new Security Data API: rhsecapi](https://access.redhat.com/discussions/2713931). @@ -15,7 +15,6 @@ If you don't have a GitHub account but do have a Red Hat Portal login, go here: - [Find CVEs](#find-cves) - [Empty search: list CVEs by public-date](#empty-search-list-cves-by-public-date) - [Find CVEs by attributes](#find-cves-by-attributes) -- [Working with IAVAs](#working-with-iavas) - [Advanced: find unresolved CVEs for a specific package in a specific product](#advanced-find-unresolved-cves-for-a-specific-package-in-a-specific-product) - [Full help page](#full-help-page) - [Working with backend rhsda library](#working-with-backend-rhsda-library) @@ -211,7 +210,7 @@ $ rhsecapi --[TabTab] --extract-cves --pastebin --q-cvss --q-product --fields --pexpire --q-cvss3 --q-raw --help --product --q-cwe --q-severity ---iava --q-advisory --q-empty --stdin +--q-advisory --q-empty --stdin ``` ## Field display @@ -273,11 +272,11 @@ Note that there are also two presets: `--all-fields` and `--most-fields` ``` $ rhsecapi CVE-2016-6302 --loglevel debug --most-fields 2>&1 | grep fields [DEBUG ] rhsda: Requested fields string: 'MOST' -[DEBUG ] rhsda: Enabled fields: 'threat_severity, public_date, iava, cwe, cvss, cvss3, bugzilla, upstream_fix, affected_release, package_state' +[DEBUG ] rhsda: Enabled fields: 'threat_severity, public_date, cwe, cvss, cvss3, bugzilla, upstream_fix, affected_release, package_state' $ rhsecapi CVE-2016-6302 --loglevel debug --all-fields 2>&1 | grep fields [DEBUG ] rhsda: Requested fields string: 'ALL' -[DEBUG ] rhsda: Enabled fields: 'threat_severity, public_date, iava, cwe, cvss, cvss3, bugzilla, acknowledgement, details, statement, mitigation, upstream_fix, references, affected_release, package_state' +[DEBUG ] rhsda: Enabled fields: 'threat_severity, public_date, cwe, cvss, cvss3, bugzilla, acknowledgement, details, statement, mitigation, upstream_fix, references, affected_release, package_state' ``` ## Find CVEs @@ -379,68 +378,6 @@ CVE-2015-0235 RHEV Hypervisor for RHEL-6: [rhev-hypervisor6-6.6-20150123.1.el6ev] via RHSA-2015:0126 (2015-02-04) ``` - -### Working with IAVAs - -IAVAs can be retrieved instantly ... - -``` -$ rhsecapi --iava 2016-A-0287 -i 2016-A-0309 --urls -[NOTICE ] rhsda: Valid Red Hat IAVA results retrieved: 2 of 2 -[NOTICE ] rhsda: Number of CVEs mapped from retrieved IAVAs: 5 - -2016-A-0287 (https://access.redhat.com/labs/securitydataapi/iava?number=2016-A-0287) - TITLE : Multiple Vulnerabilities in Oracle Enterprise Manager - SEVERITY : CAT I - ID : 140611 - CVES : - CVE-2015-7940 (https://access.redhat.com/security/cve/CVE-2015-7940) - CVE-2016-2107 (https://access.redhat.com/security/cve/CVE-2016-2107) - CVE-2016-4979 (https://access.redhat.com/security/cve/CVE-2016-4979) - CVE-2016-5604 (https://access.redhat.com/security/cve/CVE-2016-5604) - -2016-A-0309 (https://access.redhat.com/labs/securitydataapi/iava?number=2016-A-0309) - TITLE : ISC BIND Remote Denial of Service Vulnerability - SEVERITY : CAT I - ID : 140634 - CVES : - CVE-2016-8864 (https://access.redhat.com/security/cve/CVE-2016-8864) -``` - -Each of the mapped CVEs can be looked up by simply adding the `-x`/`--extract-cves` option. (For brevity, the following example also uses `--product`.) - -``` -$ rhsecapi --iava 2016-A-0287 -i 2016-A-0309 --urls --extract-cves --product 'linux 6' -[NOTICE ] rhsda: Valid Red Hat IAVA results retrieved: 2 of 2 -[NOTICE ] rhsda: Number of CVEs mapped from retrieved IAVAs: 5 -[NOTICE ] rhsda: Valid Red Hat CVE results retrieved: 4 of 5 -[NOTICE ] rhsda: Results matching spotlight-product option: 3 of 5 - -CVE-2016-8864 (https://access.redhat.com/security/cve/CVE-2016-8864) - SEVERITY : Important Impact (https://access.redhat.com/security/updates/classification) - DATE : 2016-11-01 - BUGZILLA : https://bugzilla.redhat.com/show_bug.cgi?id=1389652 - FIXED_RELEASES matching 'linux 6' : - Red Hat Enterprise Linux 6: [bind-32:9.8.2-0.47.rc1.el6_8.3] via https://access.redhat.com/errata/RHSA-2016:2141 (2016-11-02) - -CVE-2016-2107 (https://access.redhat.com/security/cve/CVE-2016-2107) - SEVERITY : Moderate Impact (https://access.redhat.com/security/updates/classification) - DATE : 2016-05-03 - BUGZILLA : https://bugzilla.redhat.com/show_bug.cgi?id=1331426 - FIXED_RELEASES matching 'linux 6' : - Red Hat Enterprise Linux 6: [openssl-1.0.1e-48.el6_8.1] via https://access.redhat.com/errata/RHSA-2016:0996 (2016-05-10) - FIX_STATES matching 'linux 6' : - Not affected: Red Hat Enterprise Linux 6 [openssl098e] - -CVE-2016-4979 (https://access.redhat.com/security/cve/CVE-2016-4979) - SEVERITY : Moderate Impact (https://access.redhat.com/security/updates/classification) - DATE : 2016-07-05 - BUGZILLA : https://bugzilla.redhat.com/show_bug.cgi?id=1352476 - FIX_STATES matching 'linux 6' : - Not affected: Red Hat Enterprise Linux 6 [httpd] -``` - - ## Advanced: find unresolved CVEs for a specific package in a specific product - **Question:** @@ -532,9 +469,9 @@ usage: rhsecapi [--q-before YYYY-MM-DD] [--q-after YYYY-MM-DD] [--q-bug BZID] [--q-product PRODUCT] [--q-package PKG] [--q-cwe CWEID] [--q-cvss SCORE] [--q-cvss3 SCORE] [--q-empty] [--q-pagesize PAGESZ] [--q-pagenum PAGENUM] [--q-raw RAWQUERY] - [-i YYYY-?-NNNN] [-x] [-0] [-f FIELDS | -a | -m] [-p PRODUCT] - [-j] [-u] [-w [WIDTH]] [-c] [-l {debug,info,notice,warning}] - [-t THREDS] [-P] [-E [DAYS]] [--dryrun] [-h] [--help] + [-x] [-0] [-f FIELDS | -a | -m] [-p PRODUCT] [-j] [-u] + [-w [WIDTH]] [-c] [-l {debug,info,notice,warning}] [-t THREDS] + [-P] [-E [DAYS]] [--dryrun] [-h] [--help] [CVE-YYYY-NNNN [CVE-YYYY-NNNN ...]] Make queries against the Red Hat Security Data API @@ -575,20 +512,13 @@ FIND CVES BY ATTRIBUTE: --q-raw b=y'); this allows passing arbitrary params (e.g. something new that is unknown to rhsecapi) -RETRIEVE SPECIFIC IAVAS: - -i, --iava YYYY-?-NNNN - Retrieve notice details for an IAVA number; specify - option multiple times to retrieve multiple IAVAs at - once (use below --extract-cves option to lookup mapped - CVEs) - RETRIEVE SPECIFIC CVES: CVE-YYYY-NNNN Retrieve a CVE or list of CVEs (e.g.: 'CVE-2016-5387'); note that case-insensitive regex- matching is done -- extra characters & duplicate CVEs will be discarded -x, --extract-cves Extract CVEs from search query (as initiated by at - least one of the --q-xxx options or the --iava option) + least one of the --q-xxx options) -0, --stdin Extract CVEs from stdin (CVEs will be matched by case- insensitive regex 'CVE-[0-9]{4}-[0-9]{4,}' and duplicates will be discarded); note that terminal @@ -606,11 +536,11 @@ CVE DISPLAY OPTIONS: date, affected_release → fixed_releases or fixed or releases, package_state → fix_states or states; optionally prepend FIELDS with plus (+) sign to add - fields to the default (e.g., '-f +iava,cvss3') or a - caret (^) to remove fields from all-fields (e.g., '-f + fields to the default (e.g., '-f +cvss3') or a caret + (^) to remove fields from all-fields (e.g., '-f ^mitigation,severity') -a, --all-fields Display all supported fields (currently: - threat_severity, public_date, iava, cwe, cvss, cvss3, + threat_severity, public_date, cwe, cvss, cvss3, bugzilla, acknowledgement, details, statement, mitigation, upstream_fix, references, affected_release, package_state) @@ -642,7 +572,7 @@ GENERAL OPTIONS: default of 'notice' to see extra details printed to stderr -t, --threads THREDS Set number of concurrent worker threads to allow when - making CVE queries (default on this system: 8) + making CVE queries (default on this system: 48) -P, --pastebin Send output to Fedora Project Pastebin (paste.fedoraproject.org) and print only URL to stdout -E, --pexpire [DAYS] Set time in days after which paste will be deleted @@ -656,8 +586,8 @@ GENERAL OPTIONS: --help Show this help message and exit VERSION: - rhsecapi v1.0.0_rc10 last mod 2017/01/05 - See to report bugs or RFEs + rhsecapi v1.0.1 last mod 2017/06/27 + See to report bugs or RFEs ``` @@ -784,17 +714,6 @@ CLASSES | With *outFormat* of "xml", returns unformatted XML as string. | If *params* dict is passed, additional parameters are ignored. | - | find_iavas(self, params=None, outFormat='json', number=None, severity=None, page=None, per_page=None) - | Find IAVA notices by recent or attributes. - | - | Provides an index to recent IAVA notices when no parameters are passed. - | Each list item is a convenience object with minimal attributes. - | Use parameters to narrow down results. - | - | With *outFormat* of "json", returns JSON object. - | With *outFormat* of "xml", returns unformatted XML as string. - | If *params* dict is passed, additional parameters are ignored. - | | find_ovals(self, params=None, outFormat='json', before=None, after=None, bug=None, cve=None, severity=None, page=None, per_page=None) | Find OVAL definitions by recent or attributes. | @@ -815,9 +734,6 @@ CLASSES | get_cvrf_oval(self, rhsa, outFormat='json') | Retrieve CVRF-OVAL details for an RHSA. | - | get_iava(self, iava, outFormat='json') - | Retrieve notice details for an IAVA. - | | get_oval(self, rhsa, outFormat='json') | Retrieve OVAL details for an RHSA. | @@ -850,7 +766,7 @@ CLASSES | ON *FIELDS*: | | librhsecapi.cveFields.all is a list obj of supported fields, i.e.: - | threat_severity, public_date, iava, cwe, cvss, cvss3, bugzilla, + | threat_severity, public_date, cwe, cvss, cvss3, bugzilla, | acknowledgement, details, statement, mitigation, upstream_fix, references, | affected_release, package_state | @@ -877,23 +793,6 @@ CLASSES | fields="^releases,mitigation" | | Finally: *fields* is case-insensitive. - | - | mget_iavas(self, iavas, numThreads=0, onlyCount=False, outFormat='plaintext', urls=False, timeout=300) - | Use multi-threading to lookup a list of IAVAs and return text output. - | - | *iavas*: A list of IAVA ids - | *numThreads*: Number of concurrent worker threads; 0 == CPUs*2 - | *onlyCount*: Whether to exit after simply logging number of valid/invalid CVEs - | *outFormat*: Control output format ("list", "plaintext", "json", or "jsonpretty") - | *urls*: Whether to add extra URLs to certain fields - | *timeout*: Total ammount of time to wait for all CVEs to be retrieved - | - | ON *OUTFORMAT*: - | - | Setting to "list" returns list object containing ONLY CVE ids. - | Setting to "plaintext" returns str object containing formatted output. - | Setting to "json" returns list object (i.e., original JSON) - | Setting to "jsonpretty" returns str object containing prettified JSON FUNCTIONS extract_cves_from_input(obj, descriptiveNoun=None) diff --git a/bin/rhsecapi b/bin/rhsecapi index d0ae13b..51bb165 100755 --- a/bin/rhsecapi +++ b/bin/rhsecapi @@ -205,12 +205,6 @@ def parse_args(): '--q-raw', metavar="RAWQUERY", action='append', help="Narrow down results by RAWQUERY (e.g.: '--q-raw a=x --q-raw b=y'); this allows passing arbitrary params (e.g. something new that is unknown to {0})".format(prog)) # New group - g_listByIava = p.add_argument_group( - 'RETRIEVE SPECIFIC IAVAS') - g_listByIava.add_argument( - '-i', '--iava', dest='iavas', metavar='YYYY-?-NNNN', action='append', - help="Retrieve notice details for an IAVA number; specify option multiple times to retrieve multiple IAVAs at once (use below --extract-cves option to lookup mapped CVEs)") - # New group g_getCve = p.add_argument_group( 'RETRIEVE SPECIFIC CVES') g_getCve.add_argument( @@ -218,7 +212,7 @@ def parse_args(): help="Retrieve a CVE or list of CVEs (e.g.: 'CVE-2016-5387'); note that case-insensitive regex-matching is done -- extra characters & duplicate CVEs will be discarded") g_getCve.add_argument( '-x', '--extract-cves', action='store_true', - help="Extract CVEs from search query (as initiated by at least one of the --q-xxx options or the --iava option)") + help="Extract CVEs from search query (as initiated by at least one of the --q-xxx options)") g_getCve.add_argument( '-0', '--stdin', action='store_true', help="Extract CVEs from stdin (CVEs will be matched by case-insensitive regex '{0}' and duplicates will be discarded); note that terminal width auto-detection is not possible in this mode and WIDTH defaults to '70' (but can be overridden with '--width')".format(rhsda.cve_regex_string)) @@ -228,7 +222,7 @@ def parse_args(): g_cveDisplay0 = g_cveDisplay.add_mutually_exclusive_group() g_cveDisplay0.add_argument( '-f', '--fields', metavar="FIELDS", default='BASE', - help="Customize field display via comma-separated case-insensitive list (default: {0}); see --all-fields option for full list of official API-provided fields; shorter field aliases: {1}; optionally prepend FIELDS with plus (+) sign to add fields to the default (e.g., '-f +iava,cvss3') or a caret (^) to remove fields from all-fields (e.g., '-f ^mitigation,severity')".format(", ".join(rhsda.cveFields.base), ", ".join(rhsda.cveFields.aliases_printable))) + help="Customize field display via comma-separated case-insensitive list (default: {0}); see --all-fields option for full list of official API-provided fields; shorter field aliases: {1}; optionally prepend FIELDS with plus (+) sign to add fields to the default (e.g., '-f +cvss3') or a caret (^) to remove fields from all-fields (e.g., '-f ^mitigation,severity')".format(", ".join(rhsda.cveFields.base), ", ".join(rhsda.cveFields.aliases_printable))) g_cveDisplay0.add_argument( '-a', '--all-fields', dest='fields', action='store_const', const='ALL', @@ -307,9 +301,6 @@ def parse_args(): o.doSearch = False else: o.doSearch = True - if o.iavas: - print("{0}: error: --q-xxx options not allowed in concert with -i/--iava".format(prog), file=sys.stderr) - sys.exit(1) if o.cves or o.stdin: print("{0}: error: --q-xxx options not allowed in concert with CVE args".format(prog), file=sys.stderr) sys.exit(1) @@ -321,8 +312,8 @@ def parse_args(): found = rhsda.extract_cves_from_input(sys.stdin) o.cves.extend(found) # If no search (--q-xxx) and no CVEs mentioned - if not o.showUsage and not (o.doSearch or o.cves or o.iavas): - logger.error("Must specify CVEs/IAVAs to retrieve or a search to perform (--q-xxx opts)") + if not o.showUsage and not (o.doSearch or o.cves): + logger.error("Must specify CVEs to retrieve or a search to perform (--q-xxx opts)") o.showUsage = True if o.showUsage: p.print_usage() @@ -347,7 +338,6 @@ def main(opts): apiclient.cfg.apiUrl = environ['RHSDA_URL'] searchOutput = "" - iavaOutput = "" cveOutput = "" if opts.doSearch: if opts.extract_cves: @@ -363,18 +353,6 @@ def main(opts): if not opts.pastebin: print(file=sys.stderr) print(searchOutput, end="") - if opts.iavas: - logger.debug("IAVAs: {0}".format(opts.iavas)) - if opts.extract_cves: - result = apiclient.mget_iavas(iavas=opts.iavas, numThreads=opts.threads, onlyCount=opts.count, outFormat='list') - opts.cves.extend(result) - elif opts.count: - result = apiclient.mget_iavas(iavas=opts.iavas, numThreads=opts.threads, onlyCount=opts.count) - else: - iavaOutput = apiclient.mget_iavas(iavas=opts.iavas, numThreads=opts.threads, outFormat=opts.outFormat, urls=opts.printUrls) - if not opts.pastebin: - print(file=sys.stderr) - print(iavaOutput, end="") if opts.cves: originalCount = len(opts.cves) # Converting to a set removes duplicates @@ -386,8 +364,6 @@ def main(opts): logger.log(25, "Skipping CVE retrieval due to --dryrun; would have retrieved: {0}".format(len(opts.cves))) cveOutput = " ".join(opts.cves) + "\n" else: - if iavaOutput: - print(file=sys.stderr) cveOutput = apiclient.mget_cves(cves=opts.cves, numThreads=opts.threads, onlyCount=opts.count, outFormat=opts.outFormat, urls=opts.printUrls, fields=opts.fields, wrapWidth=opts.wrapWidth, product=opts.product) if opts.count: return @@ -395,7 +371,7 @@ def main(opts): opts.p_lang = 'text' if opts.json: opts.p_lang = 'Python' - data = searchOutput + iavaOutput + cveOutput + data = searchOutput + cveOutput try: response = fpaste_it(inputdata=data, author=prog, lang=opts.p_lang, expire=opts.pexpire) except ValueError as e: diff --git a/rhsda/__init__.py b/rhsda/__init__.py index ee36c8a..e703049 100644 --- a/rhsda/__init__.py +++ b/rhsda/__init__.py @@ -50,7 +50,6 @@ cveFields.all = [ 'threat_severity', 'public_date', - 'iava', 'cwe', 'cvss', 'cvss3', @@ -190,7 +189,7 @@ def _get_terminal_width(self): return w def __validate_data_type(self, dT): - dataTypes = ['cvrf', 'cve', 'oval', 'iava'] + dataTypes = ['cvrf', 'cve', 'oval'] if dT not in dataTypes: raise ValueError("Invalid data type ('{0}') requested; should be one of: {1}".format(dT, ", ".join(dataTypes))) @@ -328,28 +327,6 @@ def find_ovals(self, params=None, outFormat='json', } return self._find('oval', params, outFormat) - def find_iavas(self, params=None, outFormat='json', - number=None, severity=None, - page=None, per_page=None): - """Find IAVA notices by recent or attributes. - - Provides an index to recent IAVA notices when no parameters are passed. - Each list item is a convenience object with minimal attributes. - Use parameters to narrow down results. - - With *outFormat* of "json", returns JSON object. - With *outFormat* of "xml", returns unformatted XML as string. - If *params* dict is passed, additional parameters are ignored. - """ - if not params: - params = { - 'number': number, - 'severity': severity, - 'page': page, - 'per_page': per_page, - } - return self._find('iava', params, outFormat) - def get_cvrf(self, rhsa, outFormat='json'): """Retrieve CVRF details for an RHSA.""" return self._retrieve('cvrf', rhsa, outFormat) @@ -366,10 +343,6 @@ def get_oval(self, rhsa, outFormat='json'): """Retrieve OVAL details for an RHSA.""" return self._retrieve('oval', rhsa, outFormat) - def get_iava(self, iava, outFormat='json'): - """Retrieve notice details for an IAVA.""" - return self._retrieve('iava', iava, outFormat) - def __stripjoin(self, input, oneLineEach=False): """Strip whitespace from input or input list.""" text = "" @@ -440,9 +413,6 @@ def _get_and_parse_cve(self, cve): # PUBLIC_DATE if self.__check_field('public_date', J): out.append(" DATE : {0}".format(J['public_date'].split("T")[0])) - # IAVA - if self.__check_field('iava', J): - out.append(" IAVA : {0}".format(J['iava'])) # CWE ID if self.__check_field('cwe', J): out.append(" CWE : {0}".format(J['cwe'])) @@ -558,60 +528,6 @@ def _get_and_parse_cve(self, cve): out.append("") return True, "\n".join(out) - def _get_and_parse_iava(self, iava): - """Generate a plaintext representation of an IAVA. - - This is designed with only one argument in order to allow being used as a worker - with multiprocessing.Pool.map_async(). - - Various printing operations in this method are conditional upon (or are tweaked - by) the values in the self.cfg namespace as set in parent meth self.mget_iavas(). - """ - # Output array: - out = [] - try: - # Store json - J = self.get_iava(iava) - except requests.exceptions.HTTPError as e: - # IAVA not in RH IAVA DB - logger.info(e) - if self.cfg.onlyCount or self.cfg.outFormat in ['list', 'json', 'jsonpretty']: - return False, "", 0 - else: - return False, "{0}\n Not present in Red Hat IAVA database\n".format(iava), 0 - numCves = len(J['cvelist']) - # If json output requested - if self.cfg.outFormat.startswith('json'): - return True, J, numCves - # If CVE list output - elif self.cfg.outFormat == 'list': - return True, J['cvelist'], numCves - # If onlyCount requested - elif self.cfg.onlyCount: - return True, "", numCves - # IAVA NUMBER - u = "" - if self.cfg.urls: - u = " ({0}/iava?number={1})".format(self.cfg.apiUrl, iava) - out.append("{0}{1}".format(iava, u)) - # TITLE - out.append(" TITLE : {0}".format(J['title'])) - # SEVERITY - out.append(" SEVERITY : {0}".format(J['severity'])) - # ID - out.append(" ID : {0}".format(J['id'])) - # CVELIST - if J['cvelist']: - out.append(" CVES :") - for cve in J['cvelist']: - u = "" - if self.cfg.urls: - u = " (https://access.redhat.com/security/cve/{0})".format(cve) - out.append(" {0}{1}".format(cve, u)) - # Add one final newline to the end - out.append("") - return True, "\n".join(out), numCves - def _set_cve_plaintext_fields(self, desiredFields): logger.debug("Requested fields string: '{0}'".format(desiredFields)) if not desiredFields: @@ -705,7 +621,7 @@ def mget_cves(self, cves, numThreads=0, onlyCount=False, outFormat='plaintext', ON *FIELDS*: librhsecapi.cveFields.all is a list obj of supported fields, i.e.: - threat_severity, public_date, iava, cwe, cvss, cvss3, bugzilla, + threat_severity, public_date, cwe, cvss, cvss3, bugzilla, acknowledgement, details, statement, mitigation, upstream_fix, references, affected_release, package_state @@ -808,77 +724,6 @@ def mget_cves(self, cves, numThreads=0, onlyCount=False, outFormat='plaintext', elif outFormat == 'jsonpretty': return jprint(cveOutput) - def mget_iavas(self, iavas, numThreads=0, onlyCount=False, outFormat='plaintext', - urls=False, timeout=300): - """Use multi-threading to lookup a list of IAVAs and return text output. - - *iavas*: A list of IAVA ids - *numThreads*: Number of concurrent worker threads; 0 == CPUs*2 - *onlyCount*: Whether to exit after simply logging number of valid/invalid CVEs - *outFormat*: Control output format ("list", "plaintext", "json", or "jsonpretty") - *urls*: Whether to add extra URLs to certain fields - *timeout*: Total ammount of time to wait for all CVEs to be retrieved - - ON *OUTFORMAT*: - - Setting to "list" returns list object containing ONLY CVE ids. - Setting to "plaintext" returns str object containing formatted output. - Setting to "json" returns list object (i.e., original JSON) - Setting to "jsonpretty" returns str object containing prettified JSON - """ - if outFormat not in ['list', 'plaintext', 'json', 'jsonpretty']: - raise ValueError("Invalid outFormat ('{0}') requested; should be one of: 'list', 'plaintext', 'json', 'jsonpretty'".format(outFormat)) - if not isinstance(iavas, list): - raise ValueError("Invalid 'iavas=' argument input; must be list obj") - # Configure threads - if not numThreads: - numThreads = numThreadsDefault - # Lower threads for small work-loads - if numThreads > len(iavas): - numThreads = len(iavas) - logger.info("Using {0} worker threads".format(numThreads)) - # Set cfg directives for our worker - self.cfg.onlyCount = onlyCount - self.cfg.outFormat = outFormat - self.cfg.urls = urls - # Disable sigint before starting process pool - original_sigint_handler = signal.signal(signal.SIGINT, signal.SIG_IGN) - pool = multiprocessing.Pool(processes=numThreads) - # Re-enable receipt of sigint - signal.signal(signal.SIGINT, original_sigint_handler) - # Allow cancelling with Ctrl-c - try: - p = pool.map_async(self._get_and_parse_iava, iavas) - # Need to specify timeout; see: http://stackoverflow.com/a/35134329 - results = p.get(timeout=timeout) - except KeyboardInterrupt: - logger.error("Received KeyboardInterrupt; terminating worker threads") - pool.terminate() - raise - else: - pool.close() - pool.join() - successValues, iavaOutput, numCves = zip(*results) - n_total = len(iavas) - n_hidden = successValues.count(None) - n_valid = successValues.count(True) - logger.log(25, "Valid Red Hat IAVA results retrieved: {0} of {1}".format(n_valid + n_hidden, n_total)) - if sum(numCves): - logger.log(25, "Number of CVEs mapped from retrieved IAVAs: {0}".format(sum(numCves))) - if outFormat == 'list': - cves = [] - for cvelist in iavaOutput: - cves.extend(cvelist) - return cves - elif onlyCount: - return - if outFormat == 'plaintext': - return "\n".join(iavaOutput) - elif outFormat == 'json': - return iavaOutput - elif outFormat == 'jsonpretty': - return jprint(iavaOutput) - def cve_search_query(self, params, outFormat='list', urls=False): """Perform a CVE search query. From 8b91253951e3e9c0a086f6887db8d89b6733238d Mon Sep 17 00:00:00 2001 From: Kyle Walker Date: Thu, 25 Jul 2019 09:50:29 -0400 Subject: [PATCH 11/22] All: Remove the "python2" shebang callouts Since we are now relying on the setuptools mechanism to install to either python2 or python3, it no longer makes sense to include shebang callouts at the top of the script. --- bin/rhsecapi | 2 +- rhsda/__init__.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/bin/rhsecapi b/bin/rhsecapi index 51bb165..dcfab77 100755 --- a/bin/rhsecapi +++ b/bin/rhsecapi @@ -1,4 +1,4 @@ -#!/usr/bin/python2 +#!/usr/bin/env python # -*- coding: utf-8 -*- # PYTHON_ARGCOMPLETE_OK #------------------------------------------------------------------------------- diff --git a/rhsda/__init__.py b/rhsda/__init__.py index e703049..aadc8d7 100644 --- a/rhsda/__init__.py +++ b/rhsda/__init__.py @@ -1,4 +1,3 @@ -#!/usr/bin/python2 # -*- coding: utf-8 -*- #------------------------------------------------------------------------------- # Copyright 2016, 2017 From bed85528f45038f0b0ee704e1c3172c70fdd39f2 Mon Sep 17 00:00:00 2001 From: Kyle Walker Date: Wed, 18 Aug 2021 13:45:59 -0400 Subject: [PATCH 12/22] .gitignore; Omit the .vscode directory This avoids IDE configurations from being propagated into the project. --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 72364f9..7eb64d3 100644 --- a/.gitignore +++ b/.gitignore @@ -87,3 +87,6 @@ ENV/ # Rope project settings .ropeproject + +# +.vscode/launch.json From 4cbc406394f9681551f331b49dce1403205d0a1d Mon Sep 17 00:00:00 2001 From: Kyle Walker Date: Wed, 18 Aug 2021 13:47:34 -0400 Subject: [PATCH 13/22] rhsecapi: Bump the version to 1.0.3 This updates the version both in the setup.py and also within the in- CLI definition. --- bin/rhsecapi | 4 ++-- setup.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/bin/rhsecapi b/bin/rhsecapi index dcfab77..23ba9a0 100755 --- a/bin/rhsecapi +++ b/bin/rhsecapi @@ -46,8 +46,8 @@ if not (path.isfile(path.expanduser('~/.rhsecapi-no-argcomplete')) or path.isfil # Globals prog = 'rhsecapi' vers = {} -vers['version'] = '1.0.1' -vers['date'] = '2017/06/27' +vers['version'] = '1.0.3' +vers['date'] = '2021/08/18' # Logging diff --git a/setup.py b/setup.py index 9760dcc..5607d64 100644 --- a/setup.py +++ b/setup.py @@ -21,7 +21,7 @@ def read(fname): """ setup( name = 'rhsecapi', - version = '1.0.2', + version = '1.0.3', author = 'Ryan Sawhill Aroha', author_email = 'rsaw@redhat.com', description = 'Provides a simple interface for the Red Hat Security Data API', From b140adf02e3b83c50717d454210f2c547220eaf7 Mon Sep 17 00:00:00 2001 From: Kyle Walker Date: Wed, 18 Aug 2021 16:06:33 -0400 Subject: [PATCH 14/22] spec: Drop the python3-argparse Requires It isn't necessary, and causes installation issues for systems where it isn't available. --- rhsecapi.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rhsecapi.spec b/rhsecapi.spec index dd81398..b410af8 100644 --- a/rhsecapi.spec +++ b/rhsecapi.spec @@ -22,7 +22,7 @@ Source: {{{ git_pack }}} %if %{with python3} BuildRequires: python3-devel python3-setuptools -Requires: python3-argparse python3-requests +Requires: python3-requests %else BuildRequires: python-devel python-setuptools Requires: python-argparse python-requests From 33100bb4c34e57c4866cb3b6928345e44322020e Mon Sep 17 00:00:00 2001 From: Kyle Walker Date: Fri, 7 Jan 2022 09:01:19 -0500 Subject: [PATCH 15/22] spec: Require the lib when installing the cli The previous version didn't require the library, and so would fail with the following error. Traceback (most recent call last): File "/usr/bin/rhsecapi", line 25, in import rhsda ModuleNotFoundError: No module named 'rhsda' --- rhsecapi.spec | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rhsecapi.spec b/rhsecapi.spec index b410af8..e1b7e9b 100644 --- a/rhsecapi.spec +++ b/rhsecapi.spec @@ -22,10 +22,10 @@ Source: {{{ git_pack }}} %if %{with python3} BuildRequires: python3-devel python3-setuptools -Requires: python3-requests +Requires: python3-requests python3-%{name} %else BuildRequires: python-devel python-setuptools -Requires: python-argparse python-requests +Requires: python-argparse python-requests python-%{name} %endif %description From 3aff8af9aae9f261b61a7a21897b1b34ff011869 Mon Sep 17 00:00:00 2001 From: Kyle Walker Date: Fri, 7 Jan 2022 09:24:51 -0500 Subject: [PATCH 16/22] rhsecapi: Fix the version It wasn't updated with the last tag. --- bin/rhsecapi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/rhsecapi b/bin/rhsecapi index 23ba9a0..dee4618 100755 --- a/bin/rhsecapi +++ b/bin/rhsecapi @@ -46,8 +46,8 @@ if not (path.isfile(path.expanduser('~/.rhsecapi-no-argcomplete')) or path.isfil # Globals prog = 'rhsecapi' vers = {} -vers['version'] = '1.0.3' -vers['date'] = '2021/08/18' +vers['version'] = '1.0.4' +vers['date'] = '2022/01/07' # Logging From 37f7652dadde64724bda61f987a3f6f08416ace3 Mon Sep 17 00:00:00 2001 From: Kyle Walker Date: Wed, 3 Apr 2024 13:44:51 -0400 Subject: [PATCH 17/22] git: Add .venv to .gitignore Just making it simpler for those that use hidden cwd-venvs --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 7eb64d3..f77a5d4 100644 --- a/.gitignore +++ b/.gitignore @@ -79,6 +79,7 @@ celerybeat-schedule .env # virtualenv +.venv/ venv/ ENV/ From dd7fd49913574eb39a29c5be2105a28d75b1e355 Mon Sep 17 00:00:00 2001 From: Kyle Walker Date: Wed, 3 Apr 2024 13:45:29 -0400 Subject: [PATCH 18/22] packaging: Add a requirements.txt A similar quality-of-life improvement allowing: pip install -r requirements.txt --- requirements.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 requirements.txt diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..f229360 --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +requests From ac75afe1293c68d19fdbe56c30b073728cde4664 Mon Sep 17 00:00:00 2001 From: Kyle Walker Date: Wed, 3 Apr 2024 13:46:24 -0400 Subject: [PATCH 19/22] packaging: Bump the version to 1.0.5 --- bin/rhsecapi | 4 ++-- setup.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/bin/rhsecapi b/bin/rhsecapi index dee4618..b973ad8 100755 --- a/bin/rhsecapi +++ b/bin/rhsecapi @@ -46,8 +46,8 @@ if not (path.isfile(path.expanduser('~/.rhsecapi-no-argcomplete')) or path.isfil # Globals prog = 'rhsecapi' vers = {} -vers['version'] = '1.0.4' -vers['date'] = '2022/01/07' +vers['version'] = '1.0.5' +vers['date'] = '2024/04/03' # Logging diff --git a/setup.py b/setup.py index 5607d64..9744749 100644 --- a/setup.py +++ b/setup.py @@ -21,7 +21,7 @@ def read(fname): """ setup( name = 'rhsecapi', - version = '1.0.3', + version = '1.0.5', author = 'Ryan Sawhill Aroha', author_email = 'rsaw@redhat.com', description = 'Provides a simple interface for the Red Hat Security Data API', From 16434e30ec86c0ee9f175755e621b0d5b5384cda Mon Sep 17 00:00:00 2001 From: Kyle Walker Date: Wed, 3 Apr 2024 19:47:50 +0000 Subject: [PATCH 20/22] packaging: Remove rpkg entirely The project seems to be unmaintained upstream, so I'm dropping it here in favor of tito. --- rhsecapi.spec | 8 +++----- rpkg.macros | 11 ----------- 2 files changed, 3 insertions(+), 16 deletions(-) delete mode 100644 rpkg.macros diff --git a/rhsecapi.spec b/rhsecapi.spec index e1b7e9b..b91e30c 100644 --- a/rhsecapi.spec +++ b/rhsecapi.spec @@ -11,14 +11,14 @@ %endif %endif -Name: {{{ git_name }}} -Version: {{{ git_version }}} +Name: rhsecapi +Version: 1.0.5 Release: 1%{?dist} Summary: Provides a simple interface for the Red Hat Security Data API License: GPL URL: https://github.com/RedHatOfficial/rhsecapi -Source: {{{ git_pack }}} +Source: %{name}-%{version}.tar.gz %if %{with python3} BuildRequires: python3-devel python3-setuptools @@ -132,5 +132,3 @@ popd %endif %changelog -{{{ git_changelog }}} - diff --git a/rpkg.macros b/rpkg.macros deleted file mode 100644 index 2185170..0000000 --- a/rpkg.macros +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash - -function git_version { - declare name="$(cached git_name)" "$@" - - latest_tag="$(git tag --list --sort=-taggerdate | tail -1)" - latest_tag_version="$(echo $latest_tag | sed "s/^v//g")" - - output "${latest_tag_version}" -} - From c21874ad482901e153cff00ba202c121a7c76cc7 Mon Sep 17 00:00:00 2001 From: Kyle Walker Date: Wed, 3 Apr 2024 19:50:02 +0000 Subject: [PATCH 21/22] Initialized to use tito. --- .tito/packages/.readme | 3 +++ .tito/tito.props | 6 ++++++ 2 files changed, 9 insertions(+) create mode 100644 .tito/packages/.readme create mode 100644 .tito/tito.props diff --git a/.tito/packages/.readme b/.tito/packages/.readme new file mode 100644 index 0000000..b9411e2 --- /dev/null +++ b/.tito/packages/.readme @@ -0,0 +1,3 @@ +the .tito/packages directory contains metadata files +named after their packages. Each file has the latest tagged +version and the project's relative directory. diff --git a/.tito/tito.props b/.tito/tito.props new file mode 100644 index 0000000..9f6fd2b --- /dev/null +++ b/.tito/tito.props @@ -0,0 +1,6 @@ +[buildconfig] +builder = tito.builder.Builder +tagger = tito.tagger.VersionTagger +changelog_do_not_remove_cherrypick = 0 +changelog_format = %s (%ae) + From 658618a53bc4a710fe4c38c32fb631fbd63dbfda Mon Sep 17 00:00:00 2001 From: Kyle Walker Date: Wed, 3 Apr 2024 19:50:53 +0000 Subject: [PATCH 22/22] Automatic commit of package [rhsecapi] release [1.0.6-1]. Created by command: /usr/bin/tito tag --- .tito/packages/rhsecapi | 1 + rhsecapi.spec | 5 ++++- setup.py | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) create mode 100644 .tito/packages/rhsecapi diff --git a/.tito/packages/rhsecapi b/.tito/packages/rhsecapi new file mode 100644 index 0000000..4fc0073 --- /dev/null +++ b/.tito/packages/rhsecapi @@ -0,0 +1 @@ +1.0.6-1 ./ diff --git a/rhsecapi.spec b/rhsecapi.spec index b91e30c..ecc5cf8 100644 --- a/rhsecapi.spec +++ b/rhsecapi.spec @@ -12,7 +12,7 @@ %endif Name: rhsecapi -Version: 1.0.5 +Version: 1.0.6 Release: 1%{?dist} Summary: Provides a simple interface for the Red Hat Security Data API @@ -132,3 +132,6 @@ popd %endif %changelog +* Wed Apr 03 2024 Kyle Walker 1.0.6-1 +- new package built with tito + diff --git a/setup.py b/setup.py index 9744749..e3a6b9e 100644 --- a/setup.py +++ b/setup.py @@ -21,7 +21,7 @@ def read(fname): """ setup( name = 'rhsecapi', - version = '1.0.5', + version = '1.0.6', author = 'Ryan Sawhill Aroha', author_email = 'rsaw@redhat.com', description = 'Provides a simple interface for the Red Hat Security Data API',