From cfedc0a9f4732afbf39fdf96e9b6a8598faeba5f Mon Sep 17 00:00:00 2001 From: Olivier Gonzalez Date: Thu, 27 Sep 2018 21:15:08 +0000 Subject: Extend reports to support security features --- lib/gitlab/ci/config/entry/reports.rb | 8 +++- lib/gitlab/ci/parsers.rb | 9 ----- lib/gitlab/ci/parsers/junit.rb | 66 --------------------------------- lib/gitlab/ci/parsers/test.rb | 21 +++++++++++ lib/gitlab/ci/parsers/test/junit.rb | 70 +++++++++++++++++++++++++++++++++++ 5 files changed, 98 insertions(+), 76 deletions(-) delete mode 100644 lib/gitlab/ci/parsers.rb delete mode 100644 lib/gitlab/ci/parsers/junit.rb create mode 100644 lib/gitlab/ci/parsers/test.rb create mode 100644 lib/gitlab/ci/parsers/test/junit.rb (limited to 'lib') diff --git a/lib/gitlab/ci/config/entry/reports.rb b/lib/gitlab/ci/config/entry/reports.rb index 5963f3eb90c..7bc482a6f48 100644 --- a/lib/gitlab/ci/config/entry/reports.rb +++ b/lib/gitlab/ci/config/entry/reports.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Gitlab module Ci class Config @@ -9,7 +11,7 @@ module Gitlab include Validatable include Attributable - ALLOWED_KEYS = %i[junit].freeze + ALLOWED_KEYS = %i[junit sast dependency_scanning container_scanning dast].freeze attributes ALLOWED_KEYS @@ -19,6 +21,10 @@ module Gitlab with_options allow_nil: true do validates :junit, array_of_strings_or_string: true + validates :sast, array_of_strings_or_string: true + validates :dependency_scanning, array_of_strings_or_string: true + validates :container_scanning, array_of_strings_or_string: true + validates :dast, array_of_strings_or_string: true end end diff --git a/lib/gitlab/ci/parsers.rb b/lib/gitlab/ci/parsers.rb deleted file mode 100644 index a4eccc08dfc..00000000000 --- a/lib/gitlab/ci/parsers.rb +++ /dev/null @@ -1,9 +0,0 @@ -module Gitlab - module Ci - module Parsers - def self.fabricate!(file_type) - "Gitlab::Ci::Parsers::#{file_type.classify}".constantize.new - end - end - end -end diff --git a/lib/gitlab/ci/parsers/junit.rb b/lib/gitlab/ci/parsers/junit.rb deleted file mode 100644 index d1c136f2009..00000000000 --- a/lib/gitlab/ci/parsers/junit.rb +++ /dev/null @@ -1,66 +0,0 @@ -module Gitlab - module Ci - module Parsers - class Junit - JunitParserError = Class.new(StandardError) - - def parse!(xml_data, test_suite) - root = Hash.from_xml(xml_data) - - all_cases(root) do |test_case| - test_case = create_test_case(test_case) - test_suite.add_test_case(test_case) - end - rescue REXML::ParseException => e - raise JunitParserError, "XML parsing failed: #{e.message}" - rescue => e - raise JunitParserError, "JUnit parsing failed: #{e.message}" - end - - private - - def all_cases(root, parent = nil, &blk) - return unless root.present? - - [root].flatten.compact.map do |node| - next unless node.is_a?(Hash) - - # we allow only one top-level 'testsuites' - all_cases(node['testsuites'], root, &blk) unless parent - - # we require at least one level of testsuites or testsuite - each_case(node['testcase'], &blk) if parent - - # we allow multiple nested 'testsuite' (eg. PHPUnit) - all_cases(node['testsuite'], root, &blk) - end - end - - def each_case(testcase, &blk) - return unless testcase.present? - - [testcase].flatten.compact.map(&blk) - end - - def create_test_case(data) - if data['failure'] - status = ::Gitlab::Ci::Reports::TestCase::STATUS_FAILED - system_output = data['failure'] - else - status = ::Gitlab::Ci::Reports::TestCase::STATUS_SUCCESS - system_output = nil - end - - ::Gitlab::Ci::Reports::TestCase.new( - classname: data['classname'], - name: data['name'], - file: data['file'], - execution_time: data['time'], - status: status, - system_output: system_output - ) - end - end - end - end -end diff --git a/lib/gitlab/ci/parsers/test.rb b/lib/gitlab/ci/parsers/test.rb new file mode 100644 index 00000000000..c6bc9662b07 --- /dev/null +++ b/lib/gitlab/ci/parsers/test.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +module Gitlab + module Ci + module Parsers + module Test + ParserNotFoundError = Class.new(StandardError) + + PARSERS = { + junit: ::Gitlab::Ci::Parsers::Test::Junit + }.freeze + + def self.fabricate!(file_type) + PARSERS.fetch(file_type.to_sym).new + rescue KeyError + raise ParserNotFoundError, "Cannot find any parser matching file type '#{file_type}'" + end + end + end + end +end diff --git a/lib/gitlab/ci/parsers/test/junit.rb b/lib/gitlab/ci/parsers/test/junit.rb new file mode 100644 index 00000000000..5d7d9a751d8 --- /dev/null +++ b/lib/gitlab/ci/parsers/test/junit.rb @@ -0,0 +1,70 @@ +# frozen_string_literal: true + +module Gitlab + module Ci + module Parsers + module Test + class Junit + JunitParserError = Class.new(StandardError) + + def parse!(xml_data, test_suite) + root = Hash.from_xml(xml_data) + + all_cases(root) do |test_case| + test_case = create_test_case(test_case) + test_suite.add_test_case(test_case) + end + rescue REXML::ParseException => e + raise JunitParserError, "XML parsing failed: #{e.message}" + rescue => e + raise JunitParserError, "JUnit parsing failed: #{e.message}" + end + + private + + def all_cases(root, parent = nil, &blk) + return unless root.present? + + [root].flatten.compact.map do |node| + next unless node.is_a?(Hash) + + # we allow only one top-level 'testsuites' + all_cases(node['testsuites'], root, &blk) unless parent + + # we require at least one level of testsuites or testsuite + each_case(node['testcase'], &blk) if parent + + # we allow multiple nested 'testsuite' (eg. PHPUnit) + all_cases(node['testsuite'], root, &blk) + end + end + + def each_case(testcase, &blk) + return unless testcase.present? + + [testcase].flatten.compact.map(&blk) + end + + def create_test_case(data) + if data['failure'] + status = ::Gitlab::Ci::Reports::TestCase::STATUS_FAILED + system_output = data['failure'] + else + status = ::Gitlab::Ci::Reports::TestCase::STATUS_SUCCESS + system_output = nil + end + + ::Gitlab::Ci::Reports::TestCase.new( + classname: data['classname'], + name: data['name'], + file: data['file'], + execution_time: data['time'], + status: status, + system_output: system_output + ) + end + end + end + end + end +end -- cgit v1.2.3