gitlab-security_report_schemas 0.1.0.min0.0.0.max0.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 48bb6ab237a896e1a713f7384008e510307fc383135450eca126ce5c912250bd
4
+ data.tar.gz: b1b4f46ea3c0e83ee3967a418b71a93039ab4227b0f6beae3487b8ed568a4116
5
+ SHA512:
6
+ metadata.gz: d92c6d99fa5605d114247a642e386a0876de1396a080868b55ce05951e51b30eb7768fc0c3846b311360a8f6c44f930224a9f7ef2c2f96af1881eed14e2b82f1
7
+ data.tar.gz: 3e21bfff0c8c1a3dfae2831b92546a513a89b85475f20d5c68303711580881a174e2160f91e23f4457a689063e6dc16ea28e5050201f6a4fbeb7d6af7d483377
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,23 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.7
3
+
4
+ Style/Alias:
5
+ EnforcedStyle: prefer_alias_method
6
+
7
+ Style/StringLiterals:
8
+ Enabled: true
9
+ EnforcedStyle: double_quotes
10
+
11
+ Style/StringLiteralsInInterpolation:
12
+ Enabled: true
13
+ EnforcedStyle: double_quotes
14
+
15
+ Layout/LineLength:
16
+ Max: 120
17
+
18
+ Metrics/BlockLength:
19
+ Exclude:
20
+ - "spec/**/*"
21
+
22
+ Naming/VariableNumber:
23
+ EnforcedStyle: snake_case
data/CODEOWNERS ADDED
@@ -0,0 +1,7 @@
1
+ # default to maintainers
2
+ [Maintainers]
3
+ * @gitlab-org/maintainers/security-report-schemas @minac
4
+
5
+ # Specific reviewers
6
+ [Reviewers]
7
+ *.rb @gitlab-org/maintainers/security-report-schemas @gitlab-org/govern/threat-insights-backend-team
data/CONTRIBUTING.md ADDED
@@ -0,0 +1,26 @@
1
+ ## Contributor License Agreement and Developer Certificate of Origin
2
+
3
+ Contributions to this repository are subject to the [Developer Certificate of Origin](https://docs.gitlab.com/ee/legal/developer_certificate_of_origin.html#developer-certificate-of-origin-version-11), or the [Individual](https://docs.gitlab.com/ee/legal/individual_contributor_license_agreement.html) or [Corporate](https://docs.gitlab.com/ee/legal/corporate_contributor_license_agreement.html) Contributor License Agreement, depending on where the contribution is made and on whose behalf:
4
+
5
+ - By submitting code contributions as an individual to the [`/ee` subdirectory](/ee) of this repository, you agree to the [Individual Contributor License Agreement](https://docs.gitlab.com/ee/legal/individual_contributor_license_agreement.html).
6
+
7
+ - By submitting code contributions on behalf of a corporation to the [`/ee` subdirectory](/ee) of this repository, you agree to the [Corporate Contributor License Agreement](https://docs.gitlab.com/ee/legal/corporate_contributor_license_agreement.html).
8
+
9
+ - By submitting code contributions as an individual or on behalf of a corporation to any directory in this repository outside of the [`/ee` subdirectory](/ee), you agree to the [Developer Certificate of Origin](https://docs.gitlab.com/ee/legal/developer_certificate_of_origin.html#developer-certificate-of-origin-version-11).
10
+
11
+ All Documentation content that resides under the [`doc/` directory](/doc) of this repository is licensed under Creative Commons:
12
+ [CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/).
13
+
14
+ _This notice must stay as the first item in the `CONTRIBUTING.md` file._
15
+
16
+ ## Contributing Documentation
17
+
18
+ Documentation for contributing to the GitLab project can be found at https://about.gitlab.com/community/contribute/.
19
+
20
+ ## Code of Conduct
21
+
22
+ See https://about.gitlab.com/contributing/code-of-conduct/.
23
+
24
+ ## Issue tracker
25
+
26
+ Use the issue tracker at https://gitlab.com/gitlab-org/gitlab/-/issues/.
data/Gemfile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gemspec
6
+
7
+ gem "git", "~> 1.3"
8
+ gem "pry"
9
+ gem "rake", "~> 13.0"
10
+ gem "rspec", "~> 3.0"
11
+ gem "rubocop", "~> 1.21"
12
+ gem "shoulda-matchers", "~> 5.0"
data/Gemfile.lock ADDED
@@ -0,0 +1,94 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ gitlab-security_report_schemas (0.1.0.min0.0.0.max0.0.0)
5
+ activesupport (>= 5)
6
+ json_schemer (~> 0.2.18)
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ activesupport (7.0.3.1)
12
+ concurrent-ruby (~> 1.0, >= 1.0.2)
13
+ i18n (>= 1.6, < 2)
14
+ minitest (>= 5.1)
15
+ tzinfo (~> 2.0)
16
+ ast (2.4.2)
17
+ coderay (1.1.3)
18
+ concurrent-ruby (1.1.10)
19
+ diff-lcs (1.5.0)
20
+ ecma-re-validator (0.4.0)
21
+ regexp_parser (~> 2.2)
22
+ git (1.11.0)
23
+ rchardet (~> 1.8)
24
+ hana (1.3.7)
25
+ i18n (1.12.0)
26
+ concurrent-ruby (~> 1.0)
27
+ json (2.6.2)
28
+ json_schemer (0.2.21)
29
+ ecma-re-validator (~> 0.3)
30
+ hana (~> 1.3)
31
+ regexp_parser (~> 2.0)
32
+ uri_template (~> 0.7)
33
+ method_source (1.0.0)
34
+ minitest (5.16.2)
35
+ parallel (1.22.1)
36
+ parser (3.1.2.0)
37
+ ast (~> 2.4.1)
38
+ pry (0.14.1)
39
+ coderay (~> 1.1)
40
+ method_source (~> 1.0)
41
+ rainbow (3.1.1)
42
+ rake (13.0.6)
43
+ rchardet (1.8.0)
44
+ regexp_parser (2.5.0)
45
+ rexml (3.2.5)
46
+ rspec (3.11.0)
47
+ rspec-core (~> 3.11.0)
48
+ rspec-expectations (~> 3.11.0)
49
+ rspec-mocks (~> 3.11.0)
50
+ rspec-core (3.11.0)
51
+ rspec-support (~> 3.11.0)
52
+ rspec-expectations (3.11.0)
53
+ diff-lcs (>= 1.2.0, < 2.0)
54
+ rspec-support (~> 3.11.0)
55
+ rspec-mocks (3.11.1)
56
+ diff-lcs (>= 1.2.0, < 2.0)
57
+ rspec-support (~> 3.11.0)
58
+ rspec-support (3.11.0)
59
+ rubocop (1.32.0)
60
+ json (~> 2.3)
61
+ parallel (~> 1.10)
62
+ parser (>= 3.1.0.0)
63
+ rainbow (>= 2.2.2, < 4.0)
64
+ regexp_parser (>= 1.8, < 3.0)
65
+ rexml (>= 3.2.5, < 4.0)
66
+ rubocop-ast (>= 1.19.1, < 2.0)
67
+ ruby-progressbar (~> 1.7)
68
+ unicode-display_width (>= 1.4.0, < 3.0)
69
+ rubocop-ast (1.19.1)
70
+ parser (>= 3.1.1.0)
71
+ ruby-progressbar (1.11.0)
72
+ shoulda-matchers (5.1.0)
73
+ activesupport (>= 5.2.0)
74
+ tzinfo (2.0.5)
75
+ concurrent-ruby (~> 1.0)
76
+ unicode-display_width (2.2.0)
77
+ uri_template (0.7.0)
78
+
79
+ PLATFORMS
80
+ arm64-darwin-21
81
+ ruby
82
+ x86_64-darwin-19
83
+
84
+ DEPENDENCIES
85
+ git (~> 1.3)
86
+ gitlab-security_report_schemas!
87
+ pry
88
+ rake (~> 13.0)
89
+ rspec (~> 3.0)
90
+ rubocop (~> 1.21)
91
+ shoulda-matchers (~> 5.0)
92
+
93
+ BUNDLED WITH
94
+ 2.3.15
data/LICENSE.txt ADDED
@@ -0,0 +1,28 @@
1
+ Copyright (c) 2011-present GitLab B.V.
2
+
3
+ Portions of this software are licensed as follows:
4
+
5
+ * All content residing under the "doc/" directory of this repository is licensed under "Creative Commons: CC BY-SA 4.0 license".
6
+ * All content that resides under the "ee/" directory of this repository, if that directory exists, is licensed under the license defined in "ee/LICENSE".
7
+ * All content that resides under the "jh/" directory of this repository, if that directory exists, is licensed under the license defined in "jh/LICENSE".
8
+ * All client-side JavaScript (when served directly or after being compiled, arranged, augmented, or combined), is licensed under the "MIT Expat" license.
9
+ * All third party components incorporated into the GitLab Software are licensed under the original license provided by the owner of the applicable component.
10
+ * Content outside of the above mentioned directories or restrictions above is available under the "MIT Expat" license as defined below.
11
+
12
+ Permission is hereby granted, free of charge, to any person obtaining a copy
13
+ of this software and associated documentation files (the "Software"), to deal
14
+ in the Software without restriction, including without limitation the rights
15
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16
+ copies of the Software, and to permit persons to whom the Software is
17
+ furnished to do so, subject to the following conditions:
18
+
19
+ The above copyright notice and this permission notice shall be included in all
20
+ copies or substantial portions of the Software.
21
+
22
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,66 @@
1
+ [![pipeline status](https://gitlab.com/gitlab-org/security-products/security-report-schemas-ruby/badges/main/pipeline.svg)](https://gitlab.com/gitlab-org/security-products/security-report-schemas-ruby/-/commits/main)
2
+ [![coverage report](https://gitlab.com/gitlab-org/security-products/security-report-schemas-ruby/badges/main/coverage.svg)](https://gitlab.com/gitlab-org/security-products/security-report-schemas-ruby/-/commits/main)
3
+ [![Latest Release](https://gitlab.com/gitlab-org/security-products/security-report-schemas-ruby/-/badges/release.svg)](https://gitlab.com/gitlab-org/security-products/security-report-schemas-ruby/-/releases)
4
+
5
+ # Gitlab::SecurityReportSchemas
6
+
7
+ Rubygem for https://gitlab.com/gitlab-org/security-products/security-reports-schema
8
+
9
+ This gem provides a Ruby and command line interface to validate the report artifact generated by the security analyzers.
10
+
11
+ ## Installation
12
+
13
+ Install the gem and add to the application's Gemfile by executing:
14
+
15
+ $ bundle add gitlab-security_report_schemas
16
+
17
+ If bundler is not being used to manage dependencies, install the gem by executing:
18
+
19
+ $ gem install gitlab-security_report_schemas
20
+
21
+ ## Usage
22
+
23
+ ### Ruby interface
24
+
25
+ ```ruby
26
+ require "security_report_schemas"
27
+
28
+ file_path = "path_of_the_report_file"
29
+ report_content = File.read(file_path)
30
+ data = JSON.parse(report_content)
31
+ validator = Gitlab::SecurityReportSchemas::Validator.new(data, "sast", "15.0.0")
32
+
33
+ validator.valid? # true/false
34
+ validator.errors # An array of error messages
35
+ validator.warnings # An array of warning messages
36
+ ```
37
+
38
+ ### CLI
39
+
40
+ You can use the executable to check validity of your report artifact, like so;
41
+
42
+ ```sh
43
+ bundle exec security-reports-schemas $FILE_PATH
44
+ ```
45
+
46
+ ## Development
47
+
48
+ ### Updating the schemas
49
+
50
+ You can use the `rake prepare_schemas[versions]` rake task to update the schemas, like so;
51
+
52
+ ```sh
53
+ rake "prepare_schemas[14.0.0 14.0.1 14.0.2 14.0.3 14.0.4]"
54
+ ```
55
+
56
+ This will download the following schema versions: "14.0.0", "14.0.1", "14.0.2", "14.0.3", "14.0.4".
57
+
58
+ Check `rake -T` for other available tasks
59
+
60
+ ## Contributing
61
+
62
+ See [`CONTRIBUTING.md`](CONTRIBUTING.md).
63
+
64
+ ## License
65
+
66
+ See [`LICENSE.txt`](LICENSE.txt).
data/Rakefile ADDED
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/setup"
4
+ require "bundler/gem_tasks"
5
+ require "rspec/core/rake_task"
6
+
7
+ RSpec::Core::RakeTask.new(:spec)
8
+
9
+ require "rubocop/rake_task"
10
+
11
+ RuboCop::RakeTask.new
12
+
13
+ task default: %i[spec rubocop]
14
+
15
+ desc "Bundles the Security Report Schemas into the project"
16
+ task :prepare_schemas, [:versions] do |_, args|
17
+ require "gitlab/security_report_schemas"
18
+ require "gitlab/security_report_schemas/cli/schema_downloader"
19
+
20
+ requested_versions = args[:versions]&.split.to_a
21
+
22
+ cleanup_schema_dir.then { schemas_to_prepare(requested_versions) }
23
+ .then { copy_new_schemas(_1) }
24
+ .then { update_gem_version }
25
+ end
26
+
27
+ desc "Bundles the Security Report Schemas into the project and builds the gem"
28
+ task :prepare, %i[versions] => %i[prepare_schemas build]
29
+
30
+ desc "Checks the integrity of the schema files with upstream"
31
+ task :integrity_check do
32
+ require "gitlab/security_report_schemas"
33
+ require "gitlab/security_report_schemas/cli/integrity_checker"
34
+
35
+ Gitlab::SecurityReportSchemas.supported_versions.each do |version|
36
+ puts "Checking the integrity of #{version} schemas"
37
+
38
+ Gitlab::SecurityReportSchemas::CLI::IntegrityChecker.check!(version)
39
+ end
40
+ end
41
+
42
+ def cleanup_schema_dir
43
+ puts "Cleaning the schemas directory..."
44
+
45
+ Gitlab::SecurityReportSchemas.schema_directories.each(&:rmtree)
46
+ end
47
+
48
+ def versions_file
49
+ @versions_file ||= Gitlab::SecurityReportSchemas.root_path
50
+ .join("supported_versions")
51
+ .then { |path| File.open(path, "r+") }
52
+ end
53
+
54
+ def schemas_to_prepare(requested_versions)
55
+ supported_versions = versions_file.readlines.map(&:chomp)
56
+
57
+ if requested_versions.empty?
58
+ puts "Preparing schemas from `supported_versions' file..."
59
+ else
60
+ puts "Preparing new schemas #{requested_versions}"
61
+ end
62
+
63
+ supported_versions + requested_versions
64
+ end
65
+
66
+ def copy_new_schemas(versions)
67
+ versions_file.seek(0)
68
+
69
+ versions.uniq.each do |version|
70
+ puts "Preparing the schemas for #{version}"
71
+
72
+ versions_file.puts(version)
73
+ Gitlab::SecurityReportSchemas::CLI::SchemaDownloader.download(version)
74
+ end
75
+
76
+ versions_file.close
77
+ end
78
+
79
+ def update_gem_version
80
+ # Writing the gemversion file so the gemspec can be updated
81
+ new_gem_version = Gitlab::SecurityReportSchemas::Version.to_s
82
+ gem_version_file = Gitlab::SecurityReportSchemas.root_path.join("gem_version")
83
+
84
+ gem_version_file.write(new_gem_version)
85
+
86
+ puts "Gem version is updated as `#{new_gem_version}'"
87
+ end
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "gitlab/security_report_schemas"
5
+ require "gitlab/security_report_schemas/cli/validator"
6
+
7
+ Gitlab::SecurityReportSchemas::CLI::Validator.new(ARGV.dup).run
data/gem_version ADDED
@@ -0,0 +1 @@
1
+ 0.1.0.min0.0.0.max0.0.0
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "schema_checker/abstract_file"
4
+ require_relative "schema_checker/local_file"
5
+ require_relative "schema_checker/remote_file"
6
+
7
+ module Gitlab
8
+ module SecurityReportSchemas
9
+ module CLI
10
+ # Checks the integrity of the schemas
11
+ class IntegrityChecker
12
+ def self.check!(version)
13
+ new(version).check!
14
+ end
15
+
16
+ def initialize(version)
17
+ @version = version
18
+ end
19
+
20
+ def check!
21
+ local_schema_files.each do |schema_file|
22
+ check_integrity_of!(schema_file)
23
+ end
24
+ end
25
+
26
+ private
27
+
28
+ attr_reader :version
29
+
30
+ def local_schema_files
31
+ schema_dir.children
32
+ end
33
+
34
+ def schema_dir
35
+ SecurityReportSchemas.schemas_path.join(version)
36
+ end
37
+
38
+ def check_integrity_of!(schema_file)
39
+ return if LocalFile.new(schema_file) == RemoteFile.new(schema_file)
40
+
41
+ raise "Integrity of `#{schema_file}' is broken!"
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gitlab
4
+ module SecurityReportSchemas
5
+ module CLI
6
+ class IntegrityChecker
7
+ # Encapsulates common schema file logic
8
+ class AbstractFile
9
+ def initialize(schema_file)
10
+ @schema_file = schema_file
11
+ end
12
+
13
+ def ==(other)
14
+ checksum == other.checksum
15
+ end
16
+
17
+ protected
18
+
19
+ def checksum
20
+ Digest::MD5.hexdigest(content)
21
+ end
22
+
23
+ private
24
+
25
+ attr_reader :schema_file
26
+
27
+ def content
28
+ raise "Must be implemented by the subclass"
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gitlab
4
+ module SecurityReportSchemas
5
+ module CLI
6
+ class IntegrityChecker
7
+ # Represents the local schema file
8
+ class LocalFile < AbstractFile
9
+ private
10
+
11
+ def content
12
+ File.read(schema_file)
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "net/http"
4
+
5
+ module Gitlab
6
+ module SecurityReportSchemas
7
+ module CLI
8
+ class IntegrityChecker
9
+ # Represents the schema file located on GitLab.com
10
+ class RemoteFile < AbstractFile
11
+ SCHEMA_PROJECT_RAW_URL = "https://gitlab.com/gitlab-org/security-products/security-report-schemas/-/raw/v%<version>s/dist/%<schema_file_name>s"
12
+ SCHEMA_FILE_NAME_REGEX = %r{./+(?<version>\d+\.\d+\.\d+)/(?<file_name>.+-report-format\.json)$}.freeze
13
+
14
+ private
15
+
16
+ def content
17
+ Net::HTTP.get(uri)
18
+ end
19
+
20
+ def uri
21
+ URI(schema_url)
22
+ end
23
+
24
+ def schema_url
25
+ format(SCHEMA_PROJECT_RAW_URL, version: version, schema_file_name: schema_file_name)
26
+ end
27
+
28
+ def version
29
+ schema_file_components["version"]
30
+ end
31
+
32
+ def schema_file_name
33
+ schema_file_components["file_name"]
34
+ end
35
+
36
+ def schema_file_components
37
+ @schema_file_components ||= schema_file.to_s.match(SCHEMA_FILE_NAME_REGEX).named_captures
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "git"
4
+ require "fileutils"
5
+
6
+ module Gitlab
7
+ module SecurityReportSchemas
8
+ module CLI
9
+ # Copies the schema for the given version to the project
10
+ class SchemaDownloader
11
+ SCHEMA_PROJECT_REPO = "git@gitlab.com:gitlab-org/security-products/security-report-schemas.git"
12
+
13
+ def self.download(version)
14
+ new(version).download
15
+ end
16
+
17
+ def initialize(version)
18
+ @version = version
19
+ end
20
+
21
+ def download
22
+ checkout_version
23
+ create_target_directory
24
+ copy_schemas
25
+ end
26
+
27
+ private
28
+
29
+ attr_reader :version
30
+
31
+ def checkout_version
32
+ git_project.checkout("v#{version}")
33
+ end
34
+
35
+ def create_target_directory
36
+ FileUtils.mkdir_p(target_directory) unless File.exist?(target_directory)
37
+ end
38
+
39
+ def copy_schemas
40
+ FileUtils.cp(dist_path.children, target_directory)
41
+ end
42
+
43
+ def target_directory
44
+ @target_directory ||= SecurityReportSchemas.schemas_path.join(version)
45
+ end
46
+
47
+ def dist_path
48
+ SecurityReportSchemas.root_path.join("tmp", "security-report-schemas", "dist")
49
+ end
50
+
51
+ def git_project
52
+ @git_project ||= existing_repository || clone_repository
53
+ end
54
+
55
+ def existing_repository
56
+ return unless File.exist?(git_project_path)
57
+
58
+ Git.open(git_project_path)
59
+ end
60
+
61
+ def clone_repository
62
+ Git.clone(SCHEMA_PROJECT_REPO, git_project_path)
63
+ end
64
+
65
+ def git_project_path
66
+ @git_project_path ||= SecurityReportSchemas.root_path.join("tmp", "security-report-schemas")
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,122 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "optparse"
4
+ require_relative "../utils/string_refinements"
5
+
6
+ module Gitlab
7
+ module SecurityReportSchemas
8
+ module CLI
9
+ # Validation command line interface
10
+ class Validator
11
+ using Utils::StringRefinements
12
+
13
+ def initialize(argv)
14
+ @argv = argv
15
+ end
16
+
17
+ def run
18
+ configure
19
+
20
+ run_validation
21
+ print_warnings
22
+ end
23
+
24
+ private
25
+
26
+ attr_reader :argv, :warnings
27
+
28
+ def file_path
29
+ @file_path ||= argv.shift
30
+ end
31
+
32
+ def configure
33
+ opt_parser.parse!(argv)
34
+
35
+ exit_with_usage_message unless file_path
36
+ exit_with_unknown_report_type_message unless report_type
37
+ rescue OptionParser::InvalidOption => e
38
+ exit_with_usage_message(e.message)
39
+ end
40
+
41
+ def exit_with_usage_message(extra_message = nil)
42
+ puts extra_message if extra_message
43
+ puts opt_parser.help
44
+
45
+ exit
46
+ end
47
+
48
+ def exit_with_unknown_report_type_message
49
+ puts "Can not find report type! Consider providing the report type."
50
+
51
+ exit
52
+ end
53
+
54
+ def run_validation
55
+ puts "Validating #{report_type} v#{version} against schema v#{validator.schema_ver_version}"
56
+
57
+ if validator.valid?
58
+ puts "Content is valid".green
59
+ else
60
+ puts "Content is invalid".red
61
+ puts make_string(validator.errors)
62
+ end
63
+ end
64
+
65
+ def print_warnings
66
+ return unless warnings && validator.warnings.present?
67
+
68
+ puts make_string(validator.warnings).brown
69
+ end
70
+
71
+ def make_string(array)
72
+ array.map { |message| "* #{message}" }
73
+ .join("\n")
74
+ end
75
+
76
+ def validator
77
+ @validator ||= SecurityReportSchemas::Validator.new(report_data, report_type, version)
78
+ end
79
+
80
+ def version
81
+ report_data["version"]
82
+ end
83
+
84
+ def report_type
85
+ @report_type ||= report_data.dig("scan", "type")
86
+ end
87
+
88
+ def report_data
89
+ @report_data ||= JSON.parse(report_content)
90
+ end
91
+
92
+ def report_content
93
+ File.read(file_path)
94
+ end
95
+
96
+ def opt_parser
97
+ @opt_parser ||= OptionParser.new do |parser|
98
+ parser.banner = banner_message
99
+
100
+ parser.on("-r", "--report_type=REPORT_TYPE", "Override the report type") do |arg|
101
+ @report_type = arg
102
+ end
103
+
104
+ parser.on("-w", "--warnings", "Prints the warning messages") do
105
+ @warnings = true
106
+ end
107
+ end
108
+ end
109
+
110
+ def banner_message
111
+ "SecurityReportSchemas #{SecurityReportSchemas::Version}.\n" \
112
+ "Supported schema versions: #{supported_versions.to_s.green}\n\n" \
113
+ "Usage: security-report-schemas REPORT_FILE_PATH [options]"
114
+ end
115
+
116
+ def supported_versions
117
+ SecurityReportSchemas.supported_versions.map(&:version)
118
+ end
119
+ end
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gitlab
4
+ module SecurityReportSchemas
5
+ # Holds the configuration of the gem
6
+ class Configuration
7
+ OPTIONS = {
8
+ schemas_path: -> { SecurityReportSchemas.root_path.join("schemas") },
9
+ deprecated_versions: -> { [] }
10
+ }.freeze
11
+
12
+ OPTIONS.each do |option, default_value|
13
+ define_method(option) do
14
+ instance_variable_get("@#{option}") || default_value.call
15
+ end
16
+
17
+ attr_writer option
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json_schemer"
4
+
5
+ module Gitlab
6
+ module SecurityReportSchemas
7
+ # Schema related logic
8
+ class Schema
9
+ attr_reader :report_type, :version
10
+
11
+ CROSS_COMPLIANT_SCHEMAS = { api_fuzzing: :dast }.freeze
12
+
13
+ def initialize(report_type, version)
14
+ @report_type = report_type
15
+ @version = version
16
+ end
17
+
18
+ delegate :validate, to: :schemer
19
+ delegate :supported?, :deprecated?, :fallback?, to: :schema_ver
20
+ delegate :version, to: :schema_ver, prefix: true
21
+
22
+ private
23
+
24
+ delegate :schemas_path, to: SecurityReportSchemas, private: true
25
+
26
+ def schemer
27
+ @schemer ||= JSONSchemer.schema(pathname)
28
+ end
29
+
30
+ def pathname
31
+ Pathname.new(schema_file_path)
32
+ end
33
+
34
+ def schema_file_path
35
+ schemas_path.join(schema_ver, schema_file_name)
36
+ end
37
+
38
+ def schema_file_name
39
+ "#{schema_name.to_s.dasherize}-report-format.json"
40
+ end
41
+
42
+ def schema_name
43
+ CROSS_COMPLIANT_SCHEMAS.fetch(report_type.to_sym, report_type)
44
+ end
45
+
46
+ def schema_ver
47
+ @schema_ver ||= SchemaVer.new!(version)
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,102 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gitlab
4
+ module SecurityReportSchemas
5
+ # Value class to encapsulate schemaVer related logic
6
+ class SchemaVer
7
+ VALID_SCHEMA_FORMAT = /\d+\.\d+\.\d+/.freeze
8
+
9
+ class InvalidSchemaVersion < StandardError; end
10
+
11
+ include Comparable
12
+
13
+ def self.new!(version)
14
+ return SecurityReportSchemas.supported_versions.last if version.blank?
15
+ raise InvalidSchemaVersion, version unless version.match(VALID_SCHEMA_FORMAT)
16
+
17
+ new(version).itself_or_fallback
18
+ end
19
+
20
+ attr_reader :version
21
+
22
+ def initialize(version)
23
+ @version = version
24
+ end
25
+
26
+ def itself_or_fallback
27
+ supported? ? self : (fallback || self)
28
+ end
29
+
30
+ def supported?
31
+ supported_versions.include?(self)
32
+ end
33
+
34
+ def deprecated?
35
+ deprecated_versions.include?(self)
36
+ end
37
+
38
+ def fallback?
39
+ @fallback_to.present?
40
+ end
41
+
42
+ attr_accessor :fallback_to
43
+
44
+ # The below public methods are commonly used by the Ruby's standard library
45
+
46
+ delegate :hash, to: :version
47
+
48
+ def eql?(other)
49
+ other.is_a?(self.class) && version == other.version
50
+ end
51
+
52
+ def <=>(other)
53
+ segments <=> other.segments
54
+ end
55
+
56
+ def to_s
57
+ version
58
+ end
59
+
60
+ # Enables implicit conversion to string
61
+ alias_method :to_str, :to_s
62
+
63
+ protected
64
+
65
+ def segments
66
+ @segments ||= version.split(".").map(&:to_i)
67
+ end
68
+
69
+ def compatible?(other)
70
+ model == other.model &&
71
+ revision == other.revision &&
72
+ addition > other.addition
73
+ end
74
+
75
+ def model
76
+ segments[0]
77
+ end
78
+
79
+ def revision
80
+ segments[1]
81
+ end
82
+
83
+ def addition
84
+ segments[2]
85
+ end
86
+
87
+ def as_fallback_to!(primary)
88
+ dup.tap { |duplicate| duplicate.fallback_to = primary }
89
+ end
90
+
91
+ private
92
+
93
+ delegate :supported_versions, :deprecated_versions, to: SecurityReportSchemas, private: true
94
+
95
+ def fallback
96
+ supported_versions.select { |version| compatible?(version) }
97
+ .max
98
+ &.as_fallback_to!(self)
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gitlab
4
+ module SecurityReportSchemas
5
+ module Utils
6
+ # Contains utility methods for the String class
7
+ module StringRefinements
8
+ refine String do
9
+ def red
10
+ "\e[31m#{self}\e[0m"
11
+ end
12
+
13
+ def green
14
+ "\e[32m#{self}\e[0m"
15
+ end
16
+
17
+ def brown
18
+ "\e[33m#{self}\e[0m"
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json_schemer"
4
+
5
+ require_relative "schema"
6
+
7
+ module Gitlab
8
+ module SecurityReportSchemas
9
+ # Takes the data to be validated along with the
10
+ # report type and version.
11
+ class Validator
12
+ UNSUPPORTED_SCHEMA_TEMPLATE = "Version %<version>s for report type %<report_type>s is unsupported, " \
13
+ "supported versions for this report type are: %<supported_versions>s"
14
+
15
+ DEPRECATED_SCHEMA_TEMPLATE = "Version %<version>s for report type %<report_type>s has been deprecated, " \
16
+ "supported versions for this report type are: %<supported_versions>s"
17
+
18
+ FALLBACK_USED_TEMPLATE = "This report uses a supported MAJOR.MINOR version but the PATCH doesn't match " \
19
+ "any vendored schema version. Validation is done against version %<fallback>s"
20
+
21
+ def initialize(data, report_type, version)
22
+ @data = data
23
+ @report_type = report_type
24
+ @version = version
25
+ end
26
+
27
+ def valid?
28
+ errors.empty?
29
+ end
30
+
31
+ def errors
32
+ @errors ||= schema.supported? ? schema_errors : [unsupported_schema_error]
33
+ end
34
+
35
+ def warnings
36
+ [].tap do |warn|
37
+ warn << deprecated_schema_warning_message if schema.deprecated?
38
+ warn << fallback_schema_used_warning_message if schema.fallback?
39
+ end
40
+ end
41
+
42
+ delegate :schema_ver_version, to: :schema
43
+
44
+ private
45
+
46
+ attr_reader :data, :report_type, :version
47
+
48
+ def schema_errors
49
+ validation_result.map { |error| JSONSchemer::Errors.pretty(error) }
50
+ end
51
+
52
+ def unsupported_schema_error
53
+ formatted_message(UNSUPPORTED_SCHEMA_TEMPLATE, SecurityReportSchemas.supported_versions)
54
+ end
55
+
56
+ def deprecated_schema_warning_message
57
+ formatted_message(DEPRECATED_SCHEMA_TEMPLATE, SecurityReportSchemas.maintained_versions)
58
+ end
59
+
60
+ def fallback_schema_used_warning_message
61
+ format(FALLBACK_USED_TEMPLATE, fallback: schema_ver_version)
62
+ end
63
+
64
+ def formatted_message(template, supported_versions)
65
+ format(template,
66
+ version: schema_ver_version,
67
+ report_type: report_type,
68
+ supported_versions: supported_versions.map(&:to_s))
69
+ end
70
+
71
+ def validation_result
72
+ @validation_result ||= schema.validate(data)
73
+ end
74
+
75
+ def schema
76
+ @schema ||= Schema.new(report_type, version)
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gitlab
4
+ module SecurityReportSchemas
5
+ # Represents the version of the gem
6
+ class Version
7
+ VERSION_SPEC = "%<gem_version>s.min%<min_schema>s.max%<max_schema>s"
8
+ GEM_VERSION = "0.1.0"
9
+ MISSING_SCHEMA_VERSION = "0.0.0"
10
+
11
+ class << self
12
+ def to_s
13
+ format(VERSION_SPEC,
14
+ gem_version: GEM_VERSION,
15
+ min_schema: min_schema,
16
+ max_schema: max_schema)
17
+ end
18
+
19
+ private
20
+
21
+ def min_schema
22
+ SecurityReportSchemas.supported_versions.first || MISSING_SCHEMA_VERSION
23
+ end
24
+
25
+ def max_schema
26
+ SecurityReportSchemas.supported_versions.last || MISSING_SCHEMA_VERSION
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "pathname"
4
+ require "active_support/all"
5
+ require_relative "security_report_schemas/configuration"
6
+ require_relative "security_report_schemas/schema_ver"
7
+ require_relative "security_report_schemas/version"
8
+ require_relative "security_report_schemas/validator"
9
+
10
+ module Gitlab
11
+ # I will document this later
12
+ module SecurityReportSchemas
13
+ class Error < StandardError; end
14
+
15
+ SCHEMA_PATH_REGEX = %r{.+schemas/(\d+\.\d+\.\d+)$}.freeze
16
+
17
+ class << self
18
+ # Returns the list of schema versions available including the deprecated ones.
19
+ def supported_versions
20
+ @supported_versions ||= schema_directories.map { |path_name| path_name_to_version(path_name) }
21
+ .sort
22
+ end
23
+
24
+ # Returns the list of actively maintained schemas excluding the ones marked as deprecated.
25
+ def maintained_versions
26
+ @maintained_versions ||= supported_versions - deprecated_versions
27
+ end
28
+
29
+ def deprecated_versions
30
+ @deprecated_versions ||= configuration.deprecated_versions.map { |version| SchemaVer.new(version) }
31
+ end
32
+
33
+ def schema_files
34
+ schema_directories.flat_map { |directory| directory.children.select(&:file?) }
35
+ .map { |schema_path| schema_path.relative_path_from(root_path) }
36
+ end
37
+
38
+ def schema_directories
39
+ schemas_path.children.select(&:directory?)
40
+ end
41
+
42
+ def root_path
43
+ @root_path ||= Pathname.new(__dir__).join("..", "..")
44
+ end
45
+
46
+ def configure(&block)
47
+ flush_memoized_methods!
48
+
49
+ block.call(configuration)
50
+ end
51
+
52
+ def configuration
53
+ @configuration ||= Configuration.new
54
+ end
55
+
56
+ delegate :schemas_path, to: :configuration
57
+
58
+ private
59
+
60
+ def flush_memoized_methods!
61
+ @deprecated_versions = @maintained_versions = nil
62
+ end
63
+
64
+ def path_name_to_version(path_name)
65
+ version = path_name.to_s.match(SCHEMA_PATH_REGEX).captures.first
66
+
67
+ SchemaVer.new(version)
68
+ end
69
+ end
70
+ end
71
+ end
data/schemas/.keep ADDED
File without changes
File without changes
metadata ADDED
@@ -0,0 +1,98 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: gitlab-security_report_schemas
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0.min0.0.0.max0.0.0
5
+ platform: ruby
6
+ authors:
7
+ - GitLab
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2022-12-19 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '5'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '5'
27
+ - !ruby/object:Gem::Dependency
28
+ name: json_schemer
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 0.2.18
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 0.2.18
41
+ description:
42
+ email:
43
+ - gitlab_rubygems@gitlab.com
44
+ executables:
45
+ - security-report-schemas
46
+ extensions: []
47
+ extra_rdoc_files: []
48
+ files:
49
+ - ".rspec"
50
+ - ".rubocop.yml"
51
+ - CODEOWNERS
52
+ - CONTRIBUTING.md
53
+ - Gemfile
54
+ - Gemfile.lock
55
+ - LICENSE.txt
56
+ - README.md
57
+ - Rakefile
58
+ - exe/security-report-schemas
59
+ - gem_version
60
+ - lib/gitlab/security_report_schemas.rb
61
+ - lib/gitlab/security_report_schemas/cli/integrity_checker.rb
62
+ - lib/gitlab/security_report_schemas/cli/schema_checker/abstract_file.rb
63
+ - lib/gitlab/security_report_schemas/cli/schema_checker/local_file.rb
64
+ - lib/gitlab/security_report_schemas/cli/schema_checker/remote_file.rb
65
+ - lib/gitlab/security_report_schemas/cli/schema_downloader.rb
66
+ - lib/gitlab/security_report_schemas/cli/validator.rb
67
+ - lib/gitlab/security_report_schemas/configuration.rb
68
+ - lib/gitlab/security_report_schemas/schema.rb
69
+ - lib/gitlab/security_report_schemas/schema_ver.rb
70
+ - lib/gitlab/security_report_schemas/utils/string_refinements.rb
71
+ - lib/gitlab/security_report_schemas/validator.rb
72
+ - lib/gitlab/security_report_schemas/version.rb
73
+ - schemas/.keep
74
+ - supported_versions
75
+ homepage: https://gitlab.com/gitlab-org/security-products/security-reports-schema-ruby
76
+ licenses: []
77
+ metadata:
78
+ homepage_uri: https://gitlab.com/gitlab-org/security-products/security-reports-schema-ruby
79
+ post_install_message:
80
+ rdoc_options: []
81
+ require_paths:
82
+ - lib
83
+ required_ruby_version: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ version: 2.7.0
88
+ required_rubygems_version: !ruby/object:Gem::Requirement
89
+ requirements:
90
+ - - ">"
91
+ - !ruby/object:Gem::Version
92
+ version: 1.3.1
93
+ requirements: []
94
+ rubygems_version: 3.3.26
95
+ signing_key:
96
+ specification_version: 4
97
+ summary: Ruby gem for GitLab security report JSON schemas
98
+ test_files: []