diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-09-15 09:12:58 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-09-15 09:12:58 +0000 |
commit | b83c1bf23583e4d00f60fc4052b3a85693f506d5 (patch) | |
tree | 0b1d795738b4ad131e72d0f7172c5e61c56d31ef /vendor/gems/bundler-checksum/lib | |
parent | 9a0e0265e4fd6ef93e63c670adc66637dd6ff553 (diff) | |
download | gitlab-ce-b83c1bf23583e4d00f60fc4052b3a85693f506d5.tar.gz |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'vendor/gems/bundler-checksum/lib')
7 files changed, 291 insertions, 0 deletions
diff --git a/vendor/gems/bundler-checksum/lib/bundler-checksum.rb b/vendor/gems/bundler-checksum/lib/bundler-checksum.rb new file mode 100644 index 00000000000..600cd4f7107 --- /dev/null +++ b/vendor/gems/bundler-checksum/lib/bundler-checksum.rb @@ -0,0 +1 @@ +require 'bundler/checksum' diff --git a/vendor/gems/bundler-checksum/lib/bundler/checksum.rb b/vendor/gems/bundler-checksum/lib/bundler/checksum.rb new file mode 100644 index 00000000000..c8d78eba111 --- /dev/null +++ b/vendor/gems/bundler-checksum/lib/bundler/checksum.rb @@ -0,0 +1,109 @@ +# frozen_string_literal: true + +require 'bundler' +require 'bundler/checksum/version' +require 'json' + +module Bundler + module Patches + # This module monkey-patches Bundler to check Gemfile.checksum + # when installing gems that are from RubyGems + module RubyGemsInstallerPatch + def pre_install_checks + super && validate_local_package_checksum + end + + private + + def validate_local_package_checksum + cached_checksum = fetch_checksum_from_file(spec) + + if cached_checksum.nil? + raise SecurityError, "Cached checksum for #{spec.full_name} not found. Please (re-)generate Gemfile.checksum" + end + + validate_file_checksum(cached_checksum) + end + + def fetch_checksum_from_file(spec) + ::Bundler::Checksum.checksum_for(spec.name, spec.version.to_s, spec.platform.to_s) + end + + # Modified from + # https://github.com/rubygems/rubygems/blob/243173279e79a38f03e318eea8825d1c8824e119/bundler/lib/bundler/rubygems_gem_installer.rb#L116 + def validate_file_checksum(checksum) + return true if Bundler.settings[:disable_checksum_validation] + + source = @package.instance_variable_get(:@gem) + + # Contary to upstream, we raise instead of silently returning + raise "#{@package.inspect} does not have :@gem" unless source + raise "#{source.inspect} does not respond to :with_read_io" unless source.respond_to?(:with_read_io) + + digest = source.with_read_io do |io| + digest = SharedHelpers.digest(:SHA256).new + digest << io.read(16_384) until io.eof? + io.rewind + send(checksum_type(checksum), digest) + end + unless digest == checksum + raise SecurityError, <<-MESSAGE + Bundler cannot continue installing #{spec.name} (#{spec.version}). + The checksum for the downloaded `#{spec.full_name}.gem` does not match \ + the checksum from the checksum file. This means the contents of the downloaded \ + gem is different from what was recorded in the checksum file, and could be potential security issue. + gem is different from what was uploaded to the server, and could be a potential security issue. + + To resolve this issue: + 1. delete the downloaded gem located at: `#{spec.gem_dir}/#{spec.full_name}.gem` + 2. run `bundle install` + + If you wish to continue installing the downloaded gem, and are certain it does not pose a \ + security issue despite the mismatching checksum, do the following: + 1. run `bundle config set --local disable_checksum_validation true` to turn off checksum verification + 2. run `bundle install` + + (More info: The expected SHA256 checksum was #{checksum.inspect}, but the \ + checksum for the downloaded gem was #{digest.inspect}.) + MESSAGE + end + true + end + end + end +end + +module Bundler + module Checksum + class << self + def checksum_file + @checksum_file ||= File.join(File.dirname(Bundler.default_gemfile), 'Gemfile.checksum') + end + + def checksums_from_file + @checksums_from_file ||= JSON.parse(File.open(checksum_file).read, symbolize_names: true) + rescue JSON::ParserError => e + raise "Invalid checksum file: #{e.message}" + end + + def checksum_for(gem_name, gem_version, gem_platform) + item = checksums_from_file.detect do |item| + item[:name] == gem_name && + item[:platform] == gem_platform && + item[:version] == gem_version + end + + item&.fetch(:checksum) + end + + def patch! + return if defined?(@patched) && @patched + @patched = true + + Bundler.ui.info "Patching bundler with bundler-checksum..." + require 'bundler/rubygems_gem_installer' + ::Bundler::RubyGemsGemInstaller.prepend(Bundler::Patches::RubyGemsInstallerPatch) + end + end + end +end diff --git a/vendor/gems/bundler-checksum/lib/bundler/checksum/command.rb b/vendor/gems/bundler-checksum/lib/bundler/checksum/command.rb new file mode 100644 index 00000000000..438f41f6e69 --- /dev/null +++ b/vendor/gems/bundler-checksum/lib/bundler/checksum/command.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +module Bundler::Checksum + module Command + autoload :Init, File.expand_path("command/init", __dir__) + autoload :Verify, File.expand_path("command/verify", __dir__) + autoload :Helper, File.expand_path("command/helper", __dir__) + + def self.execute(args) + if args.empty? + $stderr.puts 'A command must be given [init,update,verify]' + end + + if args.first == 'init' + Init.execute + elsif args.first == 'update' + $stderr.puts 'Not implemented, please use init' + elsif args.first == 'verify' + verified = Verify.execute + + unless verified + exit 1 + end + end + end + end +end diff --git a/vendor/gems/bundler-checksum/lib/bundler/checksum/command/helper.rb b/vendor/gems/bundler-checksum/lib/bundler/checksum/command/helper.rb new file mode 100644 index 00000000000..fa06bfe3da4 --- /dev/null +++ b/vendor/gems/bundler-checksum/lib/bundler/checksum/command/helper.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +require 'json' +require 'net/http' + +module Bundler::Checksum::Command + module Helper + extend self + + def remote_checksums_for_gem(gem_name, gem_version) + response = Net::HTTP.get_response(URI( + "https://rubygems.org/api/v1/versions/#{gem_name}.json" + )) + + return [] unless response.code == '200' + + gem_candidates = JSON.parse(response.body, symbolize_names: true) + gem_candidates.select! { |g| g[:number] == gem_version.to_s } + + gem_candidates.map { + |g| {:name => gem_name, :version => gem_version, :platform => g[:platform], :checksum => g[:sha]} + } + + rescue JSON::ParserError + [] + end + end +end diff --git a/vendor/gems/bundler-checksum/lib/bundler/checksum/command/init.rb b/vendor/gems/bundler-checksum/lib/bundler/checksum/command/init.rb new file mode 100644 index 00000000000..fed0e11080f --- /dev/null +++ b/vendor/gems/bundler-checksum/lib/bundler/checksum/command/init.rb @@ -0,0 +1,66 @@ +# frozen_string_literal: true + +require 'openssl' + +module Bundler::Checksum::Command + module Init + extend self + + def execute + $stderr.puts "Initializing checksum file #{checksum_file}" + + checksums = [] + + compact_index_cache = Bundler::Fetcher::CompactIndex + .new(nil, Bundler::Source::Rubygems::Remote.new(Bundler::URI("https://rubygems.org")), nil) + .send(:compact_index_client) + .instance_variable_get(:@cache) + + seen = [] + Bundler.definition.resolve.sort_by(&:name).each do |spec| + next unless spec.source.is_a?(Bundler::Source::Rubygems) + + next if seen.include?(spec.name) + seen << spec.name + + $stderr.puts "Adding #{spec.name}==#{spec.version}" + + compact_index_dependencies = compact_index_cache.dependencies(spec.name).select { |item| item.first == spec.version.to_s } + + if !compact_index_dependencies.empty? + compact_index_checksums = compact_index_dependencies.map do |version, platform, dependencies, requirements| + { + name: spec.name, + version: spec.version.to_s, + platform: Gem::Platform.new(platform).to_s, + checksum: requirements.detect { |requirement| requirement.first == 'checksum' }.flatten[1] + } + end + + checksums += compact_index_checksums.sort_by { |hash| hash.values } + else + remote_checksum = Helper.remote_checksums_for_gem(spec.name, spec.version) + + if remote_checksum.empty? + raise "#{spec.name} #{spec.version} not found on Rubygems!" + end + + checksums += remote_checksum.sort_by { |hash| hash.values } + end + end + + File.write(checksum_file, JSON.generate(checksums, array_nl: "\n") + "\n") + end + + private + + def checksum_file + ::Bundler::Checksum.checksum_file + end + + def lockfile + lockfile_path = Bundler.default_lockfile + lockfile = Bundler::LockfileParser.new(Bundler.read_file(lockfile_path)) + end + end +end diff --git a/vendor/gems/bundler-checksum/lib/bundler/checksum/command/verify.rb b/vendor/gems/bundler-checksum/lib/bundler/checksum/command/verify.rb new file mode 100644 index 00000000000..ba2eea6ea0c --- /dev/null +++ b/vendor/gems/bundler-checksum/lib/bundler/checksum/command/verify.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +module Bundler::Checksum::Command + module Verify + extend self + + def execute + $stderr.puts 'Verifying bundle checksums' + + verified = true + + local_checksums.each do |gem| + name = gem.fetch(:name) + version = gem.fetch(:version) + platform = gem.fetch(:platform) + checksum = gem.fetch(:checksum) + + $stderr.puts "Verifying #{name}==#{version} #{platform}" + unless validate_gem_checksum(name, version, platform, checksum) + verified = false + end + end + + verified + end + + private + + def local_checksums + ::Bundler::Checksum.checksums_from_file + end + + def validate_gem_checksum(gem_name, gem_version, gem_platform, local_checksum) + remote_checksums = Helper.remote_checksums_for_gem(gem_name, gem_version) + if remote_checksums.empty? + $stderr.puts "#{gem_name} #{gem_version} not found on Rubygems, skipping" + return false + end + + remote_platform_checksum = remote_checksums.find { |g| g[:name] == gem_name && g[:platform] == gem_platform.to_s } + + if local_checksum == remote_platform_checksum[:checksum] + true + else + $stderr.puts "Gem #{gem_name} #{gem_version} #{gem_platform} failed checksum verification" + $stderr.puts "LOCAL: #{local_checksum}" + $stderr.puts "REMOTE: #{remote_platform_checksum[:checksum]}" + return false + end + end + end +end diff --git a/vendor/gems/bundler-checksum/lib/bundler/checksum/version.rb b/vendor/gems/bundler-checksum/lib/bundler/checksum/version.rb new file mode 100644 index 00000000000..41e958b2db9 --- /dev/null +++ b/vendor/gems/bundler-checksum/lib/bundler/checksum/version.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +module Bundler + module Checksum + # bundler-checksum version + VERSION = '0.1.0' + end +end |