From 0c9b9f93fcd16987a97e6e954043bba9aa2eae46 Mon Sep 17 00:00:00 2001 From: Ryan Faircloth <35384120+rfaircloth-splunk@users.noreply.github.com> Date: Mon, 1 Jul 2019 09:54:51 -0400 Subject: [PATCH] Feature/#6 add clair scanner to ci (#7) Fixes #6 adds vuln scanner and current approved vulns to CI --- .circleci/config.yml | 32 +++++++++++- .idea/modules.xml | 8 +++ .idea/splunk-connect-for-syslog.iml | 19 ++++++++ .idea/vcs.xml | 9 ++++ clair-whitelist.yml | 12 +++++ clair_to_junit_parser.py | 75 +++++++++++++++++++++++++++++ requirements.txt | 3 +- 7 files changed, 156 insertions(+), 2 deletions(-) create mode 100644 .idea/modules.xml create mode 100644 .idea/splunk-connect-for-syslog.iml create mode 100644 .idea/vcs.xml create mode 100644 clair-whitelist.yml create mode 100644 clair_to_junit_parser.py diff --git a/.circleci/config.yml b/.circleci/config.yml index d6d5b9b..419b1fe 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,4 +1,8 @@ version: 2.1 + +orbs: + clair_scanner: ovotech/clair-scanner@1 + jobs: build: environment: @@ -110,6 +114,27 @@ jobs: when: always - store_test_results: path: test-results + test-scan_images: + environment: + IMAGE_NAME: rfaircloth/scs + executor: clair_scanner/default + steps: + - clair_scanner/scan: + image: $IMAGE_NAME:$CIRCLE_SHA1 + whitelist: clair-whitelist.yml + - run: + command: | + mkdir -p /root/project/test-results + pip install -r requirements.txt + python clair_to_junit_parser.py "/clair-reports/$IMAGE_NAME:$CIRCLE_SHA1.json" --output test-results/results.xml + when: on_fail +# - store_test_results: +# path: test-results/results.xml + - store_artifacts: + path: test-results + - store_artifacts: + path: /clair-reports + publish: environment: IMAGE_NAME: rfaircloth/scs @@ -138,7 +163,12 @@ workflows: - dgoss: requires: - build - - test-unit + - test-unit: + requires: + - build + - test-scan_images: + requires: + - build - publish: requires: - dgoss diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..4e16e75 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/splunk-connect-for-syslog.iml b/.idea/splunk-connect-for-syslog.iml new file mode 100644 index 0000000..3ce5619 --- /dev/null +++ b/.idea/splunk-connect-for-syslog.iml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..53b20c8 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/clair-whitelist.yml b/clair-whitelist.yml new file mode 100644 index 0000000..036c034 --- /dev/null +++ b/clair-whitelist.yml @@ -0,0 +1,12 @@ +generalwhitelist: + RHSA-2019:1619: False Positive + RHSA-2018:0654: False Positive + RHSA-2018:1967: False Positive + RHSA-2017:0372: False Positive + RHSA-2018:0502: False Positive + RHSA-2018:2772: False Positive + RHSA-2018:1374: False Positive + RHSA-2018:0180: False Positive + +images: + scs: diff --git a/clair_to_junit_parser.py b/clair_to_junit_parser.py new file mode 100644 index 0000000..e7618cd --- /dev/null +++ b/clair_to_junit_parser.py @@ -0,0 +1,75 @@ +import json +from junit_xml import TestSuite, TestCase +import os +import argparse +import logging + +logger = logging.getLogger('clair_scanner_converter') +logger.setLevel(logging.WARN) +console_logger = logging.StreamHandler() +console_logger.setLevel(logging.WARN) +formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') +console_logger.setFormatter(formatter) +logger.addHandler(console_logger) + +def parse_args(): + parser = argparse.ArgumentParser(description="Process Json File") + parser.add_argument("clairfile", type=str, default=None, help="Location of clair scanner ouptut file to convert to cucumber.json") + parser.add_argument("--output", type=str, default=None, help="name of output file to store in new format. Defaults to clair inputfile") + args = parser.parse_args() + if not args.output: + logger.warning("No output file specified, replacing input file.") + args.output = args.clairfile + return args + +def main(): + cwd = os.getcwd() + args = parse_args() + try: + if os.path.exists(args.clairfile): + with open(args.clairfile) as clairfile: + clair_parsed_file = json.load(clairfile) + if os.path.exists(os.path.join("clair-scanner-logs", "/clair_setup_errors.log")): + with open(os.path.join("clair-scanner-logs", "/clair_setup_errors.log"), 'r') as clairfile_errors: + clair_parsed_error_file = clairfile_errors.readlines() + else: + clair_parsed_error_file = None + except: + logger.exception("Failed to parse clair / clair_error file. Exiting.") + + current_sorted_level = None + current_suite = None + test_suites = [] + if clair_parsed_error_file: + current_suite = TestSuite("SetupError") + new_step = TestCase(name="SetupError", classname="SetupError", status="unapproved", stderr=clair_parsed_error_file) + new_step.log = clair_parsed_error_file + new_step.category = "SetupError" + new_step.failure_type = "unapproved" + new_step.failure_message = "Please have the following security issue reviewed by Splunk: {}".format(vuln["link"]) + new_step.failure_output = clair_parsed_error_file + current_suite.test_cases.append(new_step) + test_suites.append(current_suite) + for vuln in clair_parsed_file["vulnerabilities"]: + if current_sorted_level != vuln["severity"]: + if current_suite: + test_suites.append(current_suite) + current_suite = TestSuite(name=vuln["severity"]) + current_sorted_level = vuln["severity"] + new_step = TestCase(name=vuln["vulnerability"], classname=vuln["severity"], status="unapproved", url=vuln["link"], stderr=vuln["description"]) + new_step.log = vuln + new_step.category = vuln["severity"] + new_step.failure_type = "unapproved" + new_step.failure_message = "Please have the following security issue reviewed by Splunk: {}".format(vuln["link"]) + new_step.failure_output = vuln["description"] + current_suite.test_cases.append(new_step) + # try to write new file + try: + with open(args.output, 'w') as outfile: + outfile.write(TestSuite.to_xml_string(test_suites)) + except: + logger.exception("Filed saving file.") + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 7b11f5e..866d712 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,3 @@ -r ./tests/requirements.txt - +junit_xml +argparse