gem-guardian 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 75caa51bf7916d1feb83062445a665ca34d89b8e476cf015355b5d86566dbb76
4
- data.tar.gz: 4520cec08edf53406f26988cc5d48cd5794307fd12d59c4b661ee0e94dfc12db
3
+ metadata.gz: a18ee9f2f2111d0eca38def30302a7d66b9b6b9b96df06909f883be2978c1bcd
4
+ data.tar.gz: e6a421612c4c50423ef7fe29f74f2bc2503cb624b813befb114bc679940fcd5f
5
5
  SHA512:
6
- metadata.gz: 15c736bf8890ca30c0fef05508b14ed85b75bcb660c14843773348fa0c413213f6b79ac2ad39bbdd9edf3cc00c62d3f23667529b0f9b3f1231001b6c3804f020
7
- data.tar.gz: 5f50fc8de20b9691dc3ea84c9ef2eb542f4b58981552ee237ac1314e4860c7d47779dd078f29a936d632a6342b322237d73546dd139f8e6ddde94adadd8ff8b5
6
+ metadata.gz: 3577f45013b8bb9b94905e7f2c081501ef3b863f2913faee08a0604184e5181ed278bd75a57cee9f9d571d00395b26c3f666ab1c126abe251c88cc54f24cb8c3
7
+ data.tar.gz: faa68291c29be60c7b42d7cf1452dc8e5d29222ee8174e22ed7bbb6f913c5405a264555fe16c020e934d23863232aec74ee2befb537a7bb06192ab52d154bffb
@@ -25,7 +25,20 @@ jobs:
25
25
  with:
26
26
  ruby-version: "3.4"
27
27
  bundler-cache: true
28
+ - name: Build gem artifact
29
+ run: gem build gem-guardian.gemspec
30
+ - name: Generate checksum
31
+ run: |
32
+ gem_file=$(ls gem-guardian-*.gem)
33
+ shasum -a 256 "$gem_file" > "$gem_file.sha256"
28
34
  - name: Run tests
29
35
  run: bundle exec rake
30
36
  - name: Release gem to RubyGems.org
31
37
  uses: rubygems/release-gem@v1
38
+ - name: Create GitHub release
39
+ uses: softprops/action-gh-release@v2
40
+ with:
41
+ generate_release_notes: true
42
+ files: |
43
+ gem-guardian-*.gem
44
+ gem-guardian-*.gem.sha256
data/CHANGELOG.md CHANGED
@@ -2,6 +2,14 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [0.3.0] - 2026-06-12
6
+
7
+ - Discover GitHub Release checksum and signature assets.
8
+ - Verify signed Git tags and GitHub release attestations when provenance exposes a GitHub tag.
9
+ - Fall back to version-derived release tags when RubyGems provenance exposes only a commit SHA.
10
+ - Add GitHub release metadata to JSON and human-readable provenance output when available.
11
+ - Package the new GitHub verifier classes into the released gem.
12
+
5
13
  ## [0.2.0] - 2026-06-12
6
14
 
7
15
  - Add `--json` output for CI-friendly verification reports.
data/README.md CHANGED
@@ -8,7 +8,7 @@
8
8
 
9
9
  Consumer-side integrity verification for Ruby gems.
10
10
 
11
- `gem-guardian` audits Bundler checksum coverage, verifies `.gem` artifacts against RubyGems SHA256 data when needed, and can verify Trusted Publishing provenance for supported releases. It stays intentionally small: no Bundler monkeypatching, no install hooks, and no custom publishing flow required.
11
+ `gem-guardian` audits Bundler checksum coverage, verifies `.gem` artifacts against RubyGems SHA256 data when needed, and can verify Trusted Publishing provenance for supported releases, including GitHub release checksum/signature discovery and signed-tag attestation checks when the release data exposes them. It stays intentionally small: no Bundler monkeypatching, no install hooks, and no custom publishing flow required.
12
12
 
13
13
  ## Why
14
14
 
@@ -28,7 +28,7 @@ Trusted Publishing provenance verification when available
28
28
  Actionable report for CI or local review
29
29
  ```
30
30
 
31
- This reports whether your lockfile is using Bundler checksum protection, whether any locked gems are missing expected checksum data, and whether RubyGems exposes Trusted Publishing provenance for the gem being verified. It does **not** yet prove source provenance for releases that do not publish attestation data.
31
+ This reports whether your lockfile is using Bundler checksum protection, whether any locked gems are missing expected checksum data, whether RubyGems exposes Trusted Publishing provenance for the gem being verified, and whether GitHub release assets and tag attestations are available for the release being inspected.
32
32
 
33
33
  ## Installation
34
34
 
@@ -111,12 +111,11 @@ Use `--provenance` to inspect Trusted Publishing metadata when RubyGems exposes
111
111
  - Downloads artifacts from RubyGems.org `/downloads/<gem-file>.gem` only when verification is needed.
112
112
  - Caches downloaded artifacts under the system temp directory.
113
113
  - Does not integrate into Bundler install hooks.
114
- - Does not yet verify Sigstore, SLSA, GitHub Actions provenance, or signed git tags.
114
+ - GitHub Release checksum/signature discovery and signed tag/release attestation checks are supported when the release metadata is available.
115
115
 
116
116
  ## Roadmap
117
117
 
118
- - GitHub Release checksum/signature discovery.
119
- - Signed tag and release attestation checks.
118
+ - Expand release provenance checks to additional publishing workflows beyond GitHub release provenance.
120
119
 
121
120
 
122
121
  ## License
data/gem-guardian.gemspec CHANGED
@@ -24,7 +24,9 @@ Gem::Specification.new do |spec|
24
24
  }
25
25
 
26
26
  spec.files = Dir.chdir(__dir__) do
27
- `git ls-files -z`.split("\x0").reject do |f|
27
+ tracked_files = `git ls-files -z`.split("\x0")
28
+ source_files = Dir["lib/**/*", "exe/*", "README.md", "LICENSE.txt", "CHANGELOG.md"]
29
+ (tracked_files + source_files).uniq.reject do |f|
28
30
  f.match(%r{\A(?:test|spec|features)/})
29
31
  end
30
32
  rescue StandardError
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+ require "net/http"
5
+ require "uri"
6
+
7
+ module Gem
8
+ module Guardian
9
+ # Reads GitHub release and tag metadata for provenance checks.
10
+ class GitHubClient
11
+ # Default GitHub API endpoint used by the client.
12
+ DEFAULT_HOST = "https://api.github.com"
13
+
14
+ def initialize(host: DEFAULT_HOST, http: Net::HTTP)
15
+ @host = host.delete_suffix("/")
16
+ @http = http
17
+ end
18
+
19
+ # Returns the release payload for +repository+ and +tag+.
20
+ def release(repository, tag)
21
+ fetch_json("/repos/#{repository}/releases/tags/#{tag}")
22
+ rescue StandardError
23
+ nil
24
+ end
25
+
26
+ # Returns the tag verification payload for +repository+ and +tag+.
27
+ # rubocop:disable Metrics/CyclomaticComplexity
28
+ def tag_verification(repository, tag)
29
+ ref = fetch_json("/repos/#{repository}/git/ref/tags/#{tag}")
30
+ return unless ref.is_a?(Hash)
31
+
32
+ object = ref["object"]
33
+ return unless object.is_a?(Hash)
34
+ return object["verification"] if object["type"] == "tag" && object["verification"].is_a?(Hash)
35
+
36
+ commit = fetch_json("/repos/#{repository}/commits/#{object["sha"]}")
37
+ commit.is_a?(Hash) ? commit["commit"]&.fetch("verification", nil) : nil
38
+ rescue StandardError
39
+ nil
40
+ end
41
+ # rubocop:enable Metrics/CyclomaticComplexity
42
+
43
+ private
44
+
45
+ def fetch_json(path)
46
+ JSON.parse(get(path))
47
+ end
48
+
49
+ def get(path)
50
+ uri = URI("#{@host}#{path}")
51
+ response = @http.get_response(uri)
52
+ return response.body if response.is_a?(Net::HTTPSuccess)
53
+
54
+ raise Error, "GET #{uri} failed with #{response.code} #{response.message}"
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,200 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gem
4
+ module Guardian
5
+ # Result object for GitHub release provenance checks.
6
+ GitHubReleaseResult = Data.define(
7
+ :dependency, :status, :repository, :tag, :checksum_assets, :signature_assets, :signed_tag,
8
+ :signed_tag_reason, :release_attestation, :release_url, :error
9
+ ) do
10
+ # Returns true when the GitHub release checks succeeded.
11
+ def verified?
12
+ status == :verified
13
+ end
14
+ end
15
+
16
+ # Verifies GitHub release checksum, signature, and attestation metadata.
17
+ # rubocop:disable Metrics/ClassLength, Metrics/MethodLength, Metrics/ParameterLists, Metrics/CyclomaticComplexity
18
+ class GitHubReleaseVerifier
19
+ def initialize(client: GitHubClient.new)
20
+ @client = client
21
+ end
22
+
23
+ # Verifies GitHub release metadata for +provenance+.
24
+ # rubocop:disable Metrics/AbcSize
25
+ def verify(provenance)
26
+ repository = github_repository(provenance.repository)
27
+ tag_candidates = github_tag_candidates(provenance)
28
+ return unsupported_result(provenance, repository, tag_candidates.first) unless repository && tag_candidates.any?
29
+
30
+ release, tag = release_for(repository, tag_candidates)
31
+ return unsupported_result(provenance, repository, tag) unless release
32
+
33
+ checksum_assets = discovered_assets(release, checksum_asset_name?)
34
+ signature_assets = discovered_assets(release, signature_asset_name?)
35
+ tag_verification = @client.tag_verification(repository, tag)
36
+ build_release_result(provenance, repository, tag, checksum_assets, signature_assets, tag_verification, release)
37
+ rescue StandardError => e
38
+ error_result(provenance, repository, tag, e)
39
+ end
40
+ # rubocop:enable Metrics/AbcSize
41
+
42
+ private
43
+
44
+ def release_for(repository, tag_candidates)
45
+ tag_candidates.each do |candidate|
46
+ release = @client.release(repository, candidate)
47
+ return [release, candidate] if release
48
+ end
49
+
50
+ [nil, tag_candidates.first]
51
+ end
52
+
53
+ def build_release_result(provenance, repository, tag, checksum_assets, signature_assets, tag_verification,
54
+ release)
55
+ signed_tag = signed_tag?(tag_verification)
56
+ attestation = release_attestation(release)
57
+ status = release_status(signed_tag, attestation, tag_verification)
58
+ GitHubReleaseResult.new(
59
+ dependency: provenance.dependency,
60
+ status: status,
61
+ repository:,
62
+ tag:,
63
+ checksum_assets:,
64
+ signature_assets:,
65
+ signed_tag:,
66
+ signed_tag_reason: verification_reason(tag_verification),
67
+ release_attestation: attestation,
68
+ release_url: release["html_url"],
69
+ error: nil
70
+ )
71
+ end
72
+
73
+ def unsupported_result(provenance, repository, tag)
74
+ GitHubReleaseResult.new(
75
+ dependency: provenance.dependency,
76
+ status: :unsupported,
77
+ repository: repository,
78
+ tag: tag,
79
+ checksum_assets: [],
80
+ signature_assets: [],
81
+ signed_tag: nil,
82
+ signed_tag_reason: nil,
83
+ release_attestation: nil,
84
+ release_url: nil,
85
+ error: nil
86
+ )
87
+ end
88
+
89
+ def error_result(provenance, repository, tag, error)
90
+ GitHubReleaseResult.new(
91
+ dependency: provenance.dependency,
92
+ status: :error,
93
+ repository: repository,
94
+ tag: tag,
95
+ checksum_assets: [],
96
+ signature_assets: [],
97
+ signed_tag: nil,
98
+ signed_tag_reason: nil,
99
+ release_attestation: nil,
100
+ release_url: nil,
101
+ error: error
102
+ )
103
+ end
104
+
105
+ def github_repository(repository)
106
+ return if repository.nil?
107
+
108
+ value = repository.to_s
109
+ return unless value.match?(%r{\Ahttps://github\.com/[^/]+/[^/]+\z}) || value.match?(%r{\A[^/]+/[^/]+\z})
110
+
111
+ value.delete_prefix("https://github.com/")
112
+ end
113
+
114
+ def github_tag(provenance)
115
+ github_tag_candidates(provenance).first
116
+ end
117
+
118
+ def github_tag_candidates(provenance)
119
+ [
120
+ tag_from_ref(provenance.ref),
121
+ tag_from_subject(provenance.subject),
122
+ tag_from_version(provenance.dependency.version),
123
+ provenance.dependency.version.to_s
124
+ ].compact.uniq
125
+ end
126
+
127
+ def tag_from_ref(ref)
128
+ ref = ref.to_s
129
+ return unless ref.start_with?("refs/tags/")
130
+
131
+ ref.delete_prefix("refs/tags/")
132
+ end
133
+
134
+ def tag_from_subject(subject)
135
+ match = subject.to_s.match(%r{:ref:refs/tags/([^:]+)\z})
136
+ match && match[1]
137
+ end
138
+
139
+ def tag_from_version(version)
140
+ version = version.to_s
141
+ return if version.empty?
142
+
143
+ "v#{version}"
144
+ end
145
+
146
+ def discovered_assets(release, matcher)
147
+ Array(release["assets"]).filter_map do |asset|
148
+ name = asset.is_a?(Hash) ? asset["name"].to_s : asset.to_s
149
+ name if matcher.call(name)
150
+ end
151
+ end
152
+
153
+ def checksum_asset_name?
154
+ @checksum_asset_name ||= lambda do |name|
155
+ name.match?(/\.(?:sha256|sha256sum|checksum|checksums|sha)\z/i) ||
156
+ name.match?(/\ASHA256SUMS(?:\.txt)?\z/i) ||
157
+ name.match?(/\Achecksums(?:\.txt)?\z/i)
158
+ end
159
+ end
160
+
161
+ def signature_asset_name?
162
+ @signature_asset_name ||= lambda do |name|
163
+ name.match?(/\.(?:sig|asc)\z/i) || name.match?(/\.(?:bundle|intoto\.jsonl)\z/i)
164
+ end
165
+ end
166
+
167
+ def signed_tag?(verification)
168
+ return nil unless verification.is_a?(Hash)
169
+
170
+ verification["verified"] == true || verification["verified"].to_s.casecmp("true").zero?
171
+ end
172
+
173
+ def verification_reason(verification)
174
+ return unless verification.is_a?(Hash)
175
+
176
+ verification["reason"]
177
+ end
178
+
179
+ def release_attestation(release)
180
+ value = release["attestations"] || release["attestation"] || release["provenance"] ||
181
+ release["artifact_attestations"]
182
+ return nil if value.nil?
183
+
184
+ value.is_a?(TrueClass) || value.is_a?(FalseClass) ? value : true
185
+ end
186
+
187
+ def release_status(signed_tag, attestation, verification)
188
+ return :error if verification.is_a?(Hash) &&
189
+ verification["verified"] == true &&
190
+ verification["reason"] == "invalid"
191
+ return :verified if signed_tag == true && attestation == true
192
+ return :mismatch if signed_tag == false
193
+ return :mismatch if attestation == false
194
+
195
+ :unsupported
196
+ end
197
+ end
198
+ # rubocop:enable Metrics/ClassLength, Metrics/MethodLength, Metrics/ParameterLists, Metrics/CyclomaticComplexity
199
+ end
200
+ end
@@ -5,7 +5,7 @@ module Gem
5
5
  # Result object for a provenance verification attempt.
6
6
  ProvenanceResult = Data.define(
7
7
  :dependency, :status, :trusted_publishing, :repository, :ref, :workflow, :issuer, :subject,
8
- :expected_sha256, :actual_sha256, :error, :attestation_url
8
+ :expected_sha256, :actual_sha256, :error, :attestation_url, :github_release
9
9
  ) do
10
10
  # Returns true when provenance verification succeeded.
11
11
  def verified?
@@ -15,8 +15,9 @@ module Gem
15
15
 
16
16
  # Verifies RubyGems Trusted Publishing provenance metadata.
17
17
  class ProvenanceVerifier
18
- def initialize(client: RubygemsClient.new)
18
+ def initialize(client: RubygemsClient.new, github_release_verifier: GitHubReleaseVerifier.new)
19
19
  @client = client
20
+ @github_release_verifier = github_release_verifier
20
21
  end
21
22
 
22
23
  # Verifies Trusted Publishing provenance for +dependency+.
@@ -38,20 +39,21 @@ module Gem
38
39
 
39
40
  # rubocop:disable Metrics/MethodLength
40
41
  def build_result(dependency, provenance, artifact_sha256)
41
- ProvenanceResult.new(**result_attributes(
42
- dependency, provenance, artifact_sha256, provenance_status(provenance, artifact_sha256)
43
- ))
42
+ github_release = github_release_result(provenance)
43
+ status = combine_status(provenance_status(provenance, artifact_sha256), github_release&.status)
44
+ ProvenanceResult.new(**result_attributes(dependency, provenance, artifact_sha256, status, github_release))
44
45
  end
45
46
 
46
47
  def unsupported_result(dependency)
47
- ProvenanceResult.new(**result_attributes(dependency, nil, nil, :unsupported))
48
+ ProvenanceResult.new(**result_attributes(dependency, nil, nil, :unsupported, nil))
48
49
  end
49
50
 
50
51
  def error_result(dependency, artifact_sha256, error)
51
- ProvenanceResult.new(**result_attributes(dependency, nil, artifact_sha256, :error, error))
52
+ ProvenanceResult.new(**result_attributes(dependency, nil, artifact_sha256, :error, nil, error))
52
53
  end
53
54
 
54
- def result_attributes(dependency, provenance, artifact_sha256, status, error = nil)
55
+ # rubocop:disable Metrics/ParameterLists
56
+ def result_attributes(dependency, provenance, artifact_sha256, status, github_release = nil, error = nil)
55
57
  {
56
58
  dependency:,
57
59
  status:,
@@ -64,9 +66,11 @@ module Gem
64
66
  expected_sha256: provenance&.sha256,
65
67
  actual_sha256: artifact_sha256,
66
68
  error:,
67
- attestation_url: provenance&.attestation_url
69
+ attestation_url: provenance&.attestation_url,
70
+ github_release:
68
71
  }
69
72
  end
73
+ # rubocop:enable Metrics/ParameterLists
70
74
  # rubocop:enable Metrics/MethodLength
71
75
 
72
76
  def provenance_status(provenance, artifact_sha256)
@@ -76,6 +80,19 @@ module Gem
76
80
  secure_compare(provenance.sha256, artifact_sha256) ? :verified : :mismatch
77
81
  end
78
82
 
83
+ def github_release_result(provenance)
84
+ @github_release_verifier.verify(provenance)
85
+ rescue StandardError
86
+ nil
87
+ end
88
+
89
+ def combine_status(provenance_status, github_release_status)
90
+ return github_release_status if %i[mismatch error].include?(github_release_status)
91
+ return provenance_status if github_release_status.nil? || github_release_status == :unsupported
92
+
93
+ provenance_status == :unsupported ? github_release_status : provenance_status
94
+ end
95
+
79
96
  def secure_compare(left, right)
80
97
  left = left.to_s
81
98
  right = right.to_s
@@ -55,7 +55,8 @@ module Gem
55
55
 
56
56
  def provenance_hash(result)
57
57
  provenance_fields(result).merge(
58
- error: error_hash(result.error)
58
+ error: error_hash(result.error),
59
+ github_release: github_release_hash(result.github_release)
59
60
  )
60
61
  end
61
62
 
@@ -77,6 +78,26 @@ module Gem
77
78
  end
78
79
  # rubocop:enable Metrics/MethodLength
79
80
 
81
+ # Returns the GitHub release details for a provenance result.
82
+ # rubocop:disable Metrics/MethodLength
83
+ def github_release_hash(result)
84
+ return nil unless result
85
+
86
+ {
87
+ status: result.status,
88
+ repository: result.repository,
89
+ tag: result.tag,
90
+ checksum_assets: result.checksum_assets,
91
+ signature_assets: result.signature_assets,
92
+ signed_tag: result.signed_tag,
93
+ signed_tag_reason: result.signed_tag_reason,
94
+ release_attestation: result.release_attestation,
95
+ release_url: result.release_url,
96
+ error: error_hash(result.error)
97
+ }
98
+ end
99
+ # rubocop:enable Metrics/MethodLength
100
+
80
101
  # Returns the checksum payload for a verification result.
81
102
  def checksum_hash(result)
82
103
  {
@@ -83,6 +83,7 @@ module Gem
83
83
  provenance_fields(result).each do |label_name, value|
84
84
  @stdout.puts format_provenance_field(label_name, value) if value
85
85
  end
86
+ print_github_release_result(result.github_release) if result.github_release
86
87
  end
87
88
 
88
89
  # Prints a provenance checksum mismatch.
@@ -140,6 +141,31 @@ module Gem
140
141
  ]
141
142
  end
142
143
 
144
+ # Returns the GitHub release fields to render for a provenance result.
145
+ # rubocop:disable Metrics/MethodLength
146
+ def github_release_fields(result)
147
+ [
148
+ ["github release", result.status],
149
+ ["release repo", result.repository],
150
+ ["release tag", result.tag],
151
+ ["checksum assets", result.checksum_assets.join(", ")],
152
+ ["signature assets", result.signature_assets.join(", ")],
153
+ ["signed tag", result.signed_tag],
154
+ ["tag reason", result.signed_tag_reason],
155
+ ["attestation", result.release_attestation],
156
+ ["release url", result.release_url]
157
+ ]
158
+ end
159
+ # rubocop:enable Metrics/MethodLength
160
+
161
+ # Prints a GitHub release provenance result.
162
+ def print_github_release_result(result)
163
+ @stdout.puts "GITHUB RELEASE #{result.status.to_s.upcase}"
164
+ github_release_fields(result).each do |label_name, value|
165
+ @stdout.puts format_provenance_field(label_name, value) if value
166
+ end
167
+ end
168
+
143
169
  # Formats one provenance field line.
144
170
  def format_provenance_field(label, value)
145
171
  format("%<label>11s %<value>s", label:, value:)
@@ -15,10 +15,15 @@ module Gem
15
15
  :trusted_publishing, :repository, :ref, :workflow, :issuer, :subject, :sha256, :attestation_url
16
16
  )
17
17
 
18
+ # Matches the `Source Commit` field on the RubyGems provenance page.
18
19
  SOURCE_COMMIT_PATTERN = %r{Source Commit\s+([A-Za-z0-9._/-]+@[A-Za-z0-9._-]+)}i
20
+ # Matches the `Build File` field on the RubyGems provenance page.
19
21
  BUILD_FILE_PATTERN = /Build File\s+([^\s]+)/i
22
+ # Matches the transparency log URL shown on the RubyGems provenance page.
20
23
  LOG_ENTRY_PATTERN = %r{transparency log entry\s*(https?://[^\s]+)}i
24
+ # Matches the SHA256 checksum shown on the RubyGems provenance page.
21
25
  SHA256_PATTERN = /SHA 256 checksum\s*([a-f0-9]{64})/i
26
+ # Matches the provenance workflow label shown on the RubyGems provenance page.
22
27
  WORKFLOW_PATTERN = /
23
28
  Built and signed on\s+
24
29
  ([A-Za-z0-9 ._-]+?)
@@ -3,6 +3,6 @@
3
3
  module Gem
4
4
  module Guardian
5
5
  # gem-guardian version.
6
- VERSION = "0.2.0"
6
+ VERSION = "0.3.0"
7
7
  end
8
8
  end
data/lib/gem/guardian.rb CHANGED
@@ -10,6 +10,8 @@ require_relative "guardian/checksum"
10
10
  require_relative "guardian/dependency"
11
11
  require_relative "guardian/lockfile_parser"
12
12
  require_relative "guardian/rubygems_client"
13
+ require_relative "guardian/github_client"
14
+ require_relative "guardian/github_release_verifier"
13
15
  require_relative "guardian/artifact_store"
14
16
  require_relative "guardian/verifier"
15
17
  require_relative "guardian/provenance_verifier"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gem-guardian
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kenneth Demanawa
@@ -43,6 +43,8 @@ files:
43
43
  - lib/gem/guardian/cli.rb
44
44
  - lib/gem/guardian/dependency.rb
45
45
  - lib/gem/guardian/error.rb
46
+ - lib/gem/guardian/github_client.rb
47
+ - lib/gem/guardian/github_release_verifier.rb
46
48
  - lib/gem/guardian/lockfile_parser.rb
47
49
  - lib/gem/guardian/provenance_verifier.rb
48
50
  - lib/gem/guardian/report_builder.rb