diff --git a/security.md b/security.md index 3d9b06a..100b26a 100644 --- a/security.md +++ b/security.md @@ -119,14 +119,85 @@ Add cert paths to your gemspec ------- -### Include checksum of released gems in your repository - - require 'digest/sha2' - built_gem_path = 'pkg/gemname-version.gem' - checksum = Digest::SHA512.new.hexdigest(File.read(built_gem_path)) - checksum_path = 'checksum/gemname-version.gem.sha512' - File.open(checksum_path, 'w' ) {|f| f.write(checksum) } - # add and commit 'checksum_path' +### Include SHA-256 and SHA-512 checksums of released gems in your repository + +Checksums can be created when you are ready to release a gem. + +Currently the rake task only creates an SHA-256 checksum. Run: + + rake build:checksum + +The checksum will be placed in the `checksums/` directory. If you track the +checksums in your source repository, others will be able to verify the +authenticity of a release. + +Alternatively, if you'd like a script that will create both SHA-256 and SHA-512 +checksums you might use something like the following: + +```ruby +#!/usr/bin/env ruby +# frozen_string_literal: true + +require "digest/sha2" +require "file" + +VERSION_REGEX = /\d+\.\d+\.\d+([-.].+)*/.freeze + +gem_path_parts = ARGV.first&.split("/") + +if gem_path_parts&.any? + gem_name = gem_path_parts.last + gem_pkg = File.join(gem_path_parts) + puts "Looking for: #{gem_pkg.inspect}" + gems = Dir[gem_pkg] + puts "Found: #{gems.inspect}" +else + gem_pkgs = File.join("pkg", "*.gem") + puts "Looking for: #{gem_pkgs.inspect}" + gems = Dir[gem_pkgs] + raise "Unable to find gems #{gem_pkgs}" if gems.empty? + + # Sort by newest last + # [ "my_gem-2.3.9.gem", "my_gem-2.3.11.pre.alpha.4.gem", "my_gem-2.3.15.gem", ... ] + gems.sort_by! { |gem| Gem::Version.new(gem[VERSION_REGEX]) } + gem_pkg = gems.last + gem_path_parts = gem_pkg.split("/") + gem_name = gem_path_parts.last + puts "Found: #{gems.length} gems; latest is #{gem_name}" +end + +checksum512 = Digest::SHA512.new.hexdigest(File.read(gem_pkg)) +checksum512_path = "checksums/#{gem_name}.sha512" +File.write(checksum512_path, checksum512) + +checksum256 = Digest::SHA256.new.hexdigest(File.read(gem_pkg)) +checksum256_path = "checksums/#{gem_name}.sha256" +File.write(checksum256_path, checksum256) + +version = File.basename(checksum256_path[VERSION_REGEX], ".gem") + +git_cmd = <<~GIT_MSG +git add checksums/* && \ +git commit -m "🔒️ Checksums for v#{version}" +GIT_MSG + +puts <<~RESULTS +[GEM: #{gem_name}] +[VERSION: #{version}] +[CHECKSUM SHA256 PATH: #{checksum256_path}] +[CHECKSUM SHA512 PATH: #{checksum512_path}] + +... Running ... + +#{git_cmd} +RESULTS + +# This will replace the current process with the git process, and exit. +# Any command placed after this will not be run: +# See: https://www.akshaykhot.com/call-shell-commands-in-ruby +exec(git_cmd) + +``` -------