gem-guardian 0.4.0 → 0.4.1

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: d5b22c1d6bd9bad2286db3b0bf061e14df64e544a7ec050b5c3ba883dc5dbaf9
4
- data.tar.gz: 98768de703f861207c355cdf9299428d9b70caeab628b8861e643ed8d38fa6ab
3
+ metadata.gz: 9d95cfd5ad19142df2c6c18d606f7c4badf96c11e1e7acc8fa048a5c8cd5d7ad
4
+ data.tar.gz: 31efbe8a66d14abe2abf8a73c34a080f4b9ca56a45e9e83ffd16194292ae5d61
5
5
  SHA512:
6
- metadata.gz: e167265b43bfb4311f73df8390579c9707034dfab202cd694fb0a43e3a0a75c68431217e7068de518bd75b14cb71ee1a08cffa0ccbe59e8dff869d35db958fb4
7
- data.tar.gz: 419cf82e3c70e1e34fd29e37e277899a96355b410a0b54dfb9830daebb8d5e3818f4016d835408802fad6ab588d37c97bab3836cff62c5455d28780476620c1b
6
+ metadata.gz: f010bbbfefcd4748b5695d20ddfc13b7e86fb5ad0501a0eecb8682638b8eee00c12950a7cc86600e315ffb371262fd48900cf6f70749a7e34d92e719e1f75953
7
+ data.tar.gz: 979b1cfd1e3ba1e692b03736361f345c5209bfbf2cb6978cb6b8c6a9ce48d009b7ce7e5fc3ef19016ab953fb9383b2059bcc26b4079bbd18cb5bc6c59b7c0d81
data/CHANGELOG.md CHANGED
@@ -2,6 +2,11 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## [0.4.1] - 2026-06-21
6
+
7
+ - Add curated RBS signatures for the public API.
8
+ - Restore `rbs:validate` to the default quality checks, load project signatures explicitly, and package them with the gem.
9
+
5
10
  ## [0.4.0]
6
11
 
7
12
  - Add `.gem-guardian.yml` project configuration for publisher checksum providers.
data/Gemfile CHANGED
@@ -7,6 +7,7 @@ gemspec
7
7
  gem "minitest", "~> 6.0"
8
8
  gem "pry", "~> 0.16.0"
9
9
  gem "rake", "~> 13.4"
10
+ gem "rbs", "~> 3.9"
10
11
  gem "rubocop", "~> 1.87"
11
12
  gem "rubocop-minitest", "~> 0.39.1"
12
13
  gem "rubocop-rake", "~> 0.7.1"
data/Rakefile CHANGED
@@ -19,6 +19,13 @@ end
19
19
 
20
20
  YARD::Rake::YardocTask.new(:yard)
21
21
 
22
+ namespace :rbs do
23
+ desc "Validate RBS signatures"
24
+ task :validate do
25
+ sh "bundle exec rbs -I sig validate"
26
+ end
27
+ end
28
+
22
29
  namespace :yard do
23
30
  desc "Validate YARD documentation coverage"
24
31
  task :validate do
@@ -40,4 +47,4 @@ namespace :yard do
40
47
  end
41
48
  end
42
49
 
43
- task default: %i[test rubocop yard:validate]
50
+ task default: %i[test rubocop rbs:validate yard:validate]
data/gem-guardian.gemspec CHANGED
@@ -27,12 +27,12 @@ Gem::Specification.new do |spec|
27
27
 
28
28
  spec.files = Dir.chdir(__dir__) do
29
29
  tracked_files = `git ls-files -z`.split("\x0")
30
- source_files = Dir["lib/**/*", "exe/*", "README.md", "LICENSE.txt", "CHANGELOG.md"]
30
+ source_files = Dir["lib/**/*", "sig/**/*", "exe/*", "README.md", "LICENSE.txt", "CHANGELOG.md"]
31
31
  (tracked_files + source_files).uniq.reject do |f|
32
32
  f.match(%r{\A(?:test|spec|features)/})
33
33
  end
34
34
  rescue StandardError
35
- Dir["lib/**/*", "exe/*", "README.md", "LICENSE.txt", "CHANGELOG.md"]
35
+ Dir["lib/**/*", "sig/**/*", "exe/*", "README.md", "LICENSE.txt", "CHANGELOG.md"]
36
36
  end
37
37
 
38
38
  spec.bindir = "exe"
@@ -3,6 +3,6 @@
3
3
  module Gem
4
4
  module Guardian
5
5
  # gem-guardian version.
6
- VERSION = "0.4.0"
6
+ VERSION = "0.4.1"
7
7
  end
8
8
  end
@@ -0,0 +1,57 @@
1
+ module Gem
2
+ module Guardian
3
+ module ChecksumProvider
4
+ interface _Provider
5
+ def checksum_for: (Dependency dependency, client: RubygemsClient) -> Result?
6
+ end
7
+
8
+ class Result < Data
9
+ attr_reader sha256: String
10
+ attr_reader source: Symbol
11
+ attr_reader provider: String
12
+ attr_reader verification_uri: String?
13
+
14
+ def self.new: (String sha256, Symbol source, String provider, String? verification_uri) -> instance
15
+ | (sha256: String, source: Symbol, provider: String, verification_uri: String?) -> instance
16
+
17
+ def to_h: () -> Hash[Symbol, String | Symbol | nil]
18
+ end
19
+
20
+ class RubyGemsApi
21
+ include _Provider
22
+ end
23
+
24
+ class CompactIndex
25
+ include _Provider
26
+ end
27
+
28
+ class SourceScoped
29
+ include _Provider
30
+
31
+ def initialize: (source: String, provider: _Provider) -> void
32
+ end
33
+
34
+ class Url
35
+ include _Provider
36
+
37
+ SHA256_PATTERN: Regexp
38
+ OPEN_TIMEOUT: Integer
39
+ READ_TIMEOUT: Integer
40
+
41
+ def initialize: (template: String, ?http: untyped, ?provider_name: String) -> void
42
+ end
43
+ end
44
+
45
+ class Configuration
46
+ DEFAULT_PATH: String
47
+
48
+ attr_reader path: String?
49
+ attr_reader checksum_providers: Array[ChecksumProvider::_Provider]
50
+
51
+ def self.load: (?path: String?, ?cwd: String) -> Configuration
52
+
53
+ def initialize: (?path: String?, ?checksum_providers: Array[ChecksumProvider::_Provider]) -> void
54
+ def checksum_providers?: () -> bool
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,33 @@
1
+ module Gem
2
+ module Guardian
3
+ class CLI
4
+ class LockfileDataView < Data
5
+ attr_reader dependencies: Array[Dependency]
6
+ attr_reader checksums: checksum_map
7
+ attr_reader checksums_section_present: bool
8
+
9
+ def self.new: (Array[Dependency] dependencies, checksum_map checksums, bool checksums_section_present) -> instance
10
+ | (dependencies: Array[Dependency], checksums: checksum_map, checksums_section_present: bool) -> instance
11
+
12
+ def checksum_for: (Dependency dependency, ?String algorithm) -> String?
13
+ def sha256_checksums: () -> Hash[Dependency, String]
14
+ def missing_checksum_dependencies: () -> Array[Dependency]
15
+ def checksums_present?: () -> bool
16
+ end
17
+
18
+ def self.start: (Array[String] argv) -> Integer
19
+
20
+ def initialize: (
21
+ Array[String] argv,
22
+ ?stdout: untyped,
23
+ ?stderr: untyped,
24
+ ?verifier_class: untyped,
25
+ ?lockfile_parser_class: untyped,
26
+ ?provenance_verifier_class: untyped,
27
+ ?report_builder_class: untyped
28
+ ) -> void
29
+
30
+ def run: () -> Integer
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,80 @@
1
+ module Gem
2
+ module Guardian
3
+ class GitHubClient
4
+ DEFAULT_HOST: String
5
+
6
+ def initialize: (?host: String, ?http: untyped) -> void
7
+ def release: (String repository, String tag) -> Hash[String, untyped]?
8
+ def tag_verification: (String repository, String tag) -> Hash[String, untyped]?
9
+ end
10
+
11
+ class RubygemsClient
12
+ class TrustedPublishingProvenance < Data
13
+ attr_reader trusted_publishing: bool
14
+ attr_reader repository: String?
15
+ attr_reader ref: String?
16
+ attr_reader workflow: String?
17
+ attr_reader issuer: String?
18
+ attr_reader subject: String?
19
+ attr_reader sha256: String?
20
+ attr_reader attestation_url: String?
21
+
22
+ def self.new: (
23
+ bool trusted_publishing,
24
+ String? repository,
25
+ String? ref,
26
+ String? workflow,
27
+ String? issuer,
28
+ String? subject,
29
+ String? sha256,
30
+ String? attestation_url
31
+ ) -> instance
32
+ | (
33
+ trusted_publishing: bool,
34
+ repository: String?,
35
+ ref: String?,
36
+ workflow: String?,
37
+ issuer: String?,
38
+ subject: String?,
39
+ sha256: String?,
40
+ attestation_url: String?
41
+ ) -> instance
42
+ end
43
+
44
+ SOURCE_COMMIT_PATTERN: Regexp
45
+ BUILD_FILE_PATTERN: Regexp
46
+ LOG_ENTRY_PATTERN: Regexp
47
+ SHA256_PATTERN: Regexp
48
+ WORKFLOW_PATTERN: Regexp
49
+ DEFAULT_HOST: String
50
+ MAX_REDIRECTS: Integer
51
+ OPEN_TIMEOUT: Integer
52
+ READ_TIMEOUT: Integer
53
+
54
+ def self.default_checksum_providers: () -> Array[ChecksumProvider::_Provider]
55
+
56
+ def initialize: (
57
+ ?host: String,
58
+ ?http: untyped,
59
+ ?credentials: untyped,
60
+ ?spec_fetcher: untyped,
61
+ ?sources: untyped,
62
+ ?checksum_providers: Array[ChecksumProvider::_Provider]?
63
+ ) -> void
64
+
65
+ def resolve_dependency: (Dependency dependency) -> Dependency
66
+ def expected_sha256: (Dependency dependency) -> String
67
+ def registry_checksum: (Dependency dependency) -> ChecksumProvider::Result?
68
+ def sanitize_uri: (untyped uri) -> String
69
+ def rubygems_api_checksum: (Dependency dependency) -> ChecksumProvider::Result?
70
+ def compact_index_registry_checksum: (Dependency dependency) -> ChecksumProvider::Result?
71
+ def trusted_publishing_provenance: (Dependency dependency) -> TrustedPublishingProvenance?
72
+ def download_gem: (Dependency dependency, String destination) -> String
73
+ end
74
+
75
+ class ArtifactStore
76
+ def initialize: (client: RubygemsClient, ?cache_dir: String) -> void
77
+ def path_for: (Dependency dependency) -> String
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,39 @@
1
+ module Gem
2
+ module Guardian
3
+ VERSION: String
4
+
5
+ class Error < StandardError
6
+ end
7
+
8
+ class ChecksumNotFound < Error
9
+ end
10
+
11
+ class ArtifactFetchError < Error
12
+ end
13
+
14
+ class LockfileError < Error
15
+ end
16
+
17
+ class Dependency < Data
18
+ attr_reader name: String
19
+ attr_reader version: String
20
+ attr_reader platform: String?
21
+ attr_reader source: String?
22
+
23
+ def self.new: (name: String, version: String, platform: String?, ?source: String?) -> instance
24
+
25
+ def initialize: (name: String, version: String, platform: String?, ?source: String?) -> void
26
+ def gem_filename: () -> String
27
+ end
28
+
29
+ module Checksum
30
+ def self.sha256_file: (String path) -> String
31
+ end
32
+
33
+ module Progress
34
+ def self.update: (untyped message, ?io: untyped, ?force: bool) -> void
35
+ def self.finish: (?untyped message, ?io: untyped, ?force: bool) -> void
36
+ def self.enabled?: (?untyped io, ?force: bool) -> bool
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,30 @@
1
+ module Gem
2
+ module Guardian
3
+ type checksum_map = Hash[Dependency, Hash[String, String]]
4
+
5
+ class LockfileParser
6
+ GEM_LINE: Regexp
7
+ REMOTE_LINE: Regexp
8
+ CHECKSUM_LINE: Regexp
9
+
10
+ class LockfileData < Data
11
+ attr_reader dependencies: Array[Dependency]
12
+ attr_reader checksums: checksum_map
13
+ attr_reader checksums_section_present: bool
14
+
15
+ def self.new: (Array[Dependency] dependencies, checksum_map checksums, bool checksums_section_present) -> instance
16
+ | (dependencies: Array[Dependency], checksums: checksum_map, checksums_section_present: bool) -> instance
17
+
18
+ def checksum_for: (Dependency dependency, ?String algorithm) -> String?
19
+ def sha256_checksums: () -> Hash[Dependency, String]
20
+ def missing_checksum_dependencies: () -> Array[Dependency]
21
+ def checksums_present?: () -> bool
22
+ end
23
+
24
+ def initialize: (?String path) -> void
25
+ def parse: () -> LockfileData
26
+ def dependencies: () -> Array[Dependency]
27
+ def checksums: () -> checksum_map
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,51 @@
1
+ module Gem
2
+ module Guardian
3
+ class Registry
4
+ class Entry < Data
5
+ attr_reader name: String
6
+ attr_reader version: String
7
+ attr_reader platform: String
8
+ attr_reader source: String
9
+
10
+ def self.new: (name: String, version: String, platform: String, source: String) -> instance
11
+ | (String name, String version, String platform, String source) -> instance
12
+
13
+ def dependency: () -> Dependency
14
+ end
15
+
16
+ def initialize: (?sources: untyped, ?spec_fetcher: untyped) -> void
17
+ def each_latest_spec: (?limit: Integer?) { (Entry) -> void } -> untyped
18
+ | (?limit: Integer?) -> Enumerator[Entry, untyped]
19
+ def latest_specs: (?limit: Integer?) -> Array[Entry]
20
+ end
21
+
22
+ class RegistryAudit
23
+ class EntryResult < Data
24
+ attr_reader entry: Registry::Entry
25
+ attr_reader provenance: ProvenanceResult
26
+
27
+ def self.new: (entry: Registry::Entry, provenance: ProvenanceResult) -> instance
28
+ | (Registry::Entry entry, ProvenanceResult provenance) -> instance
29
+
30
+ def dependency: () -> Dependency
31
+ end
32
+
33
+ class Result < Data
34
+ attr_reader results: Array[EntryResult]
35
+
36
+ def self.new: (Array[EntryResult] results) -> instance
37
+ | (results: Array[EntryResult]) -> instance
38
+
39
+ def counts: () -> Hash[provenance_status, Integer]
40
+ def verified: () -> Array[EntryResult]
41
+ def unsupported: () -> Array[EntryResult]
42
+ def errors: () -> Array[EntryResult]
43
+ def mismatches: () -> Array[EntryResult]
44
+ def total: () -> Integer
45
+ end
46
+
47
+ def initialize: (?registry: Registry, ?provenance_verifier: ProvenanceVerifier) -> void
48
+ def run: (?limit: Integer?) -> Result
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,32 @@
1
+ module Gem
2
+ module Guardian
3
+ class ReportBuilder
4
+ def initialize: (version: String) -> void
5
+
6
+ def build: (
7
+ Enumerable[VerificationResult] results,
8
+ lockfile_data: LockfileParser::LockfileData | CLI::LockfileDataView | nil,
9
+ ?provenance_results: Array[ProvenanceResult],
10
+ ?lockfile_path: String?
11
+ ) -> Hash[Symbol, untyped]
12
+ end
13
+
14
+ class ResultPrinter
15
+ USAGE: String
16
+
17
+ def initialize: (stdout: untyped) -> void
18
+ def print_results: (Enumerable[VerificationResult] results, lockfile_mode: bool) -> void
19
+ def print_result: (VerificationResult result, lockfile_mode: bool) -> void
20
+ def print_ok_result: (VerificationResult result, String label, bool lockfile_mode) -> void
21
+ def print_mismatch_result: (VerificationResult result, String label) -> void
22
+ def print_error_result: (VerificationResult result, String label) -> void
23
+ def print_lockfile_coverage: (LockfileParser::LockfileData | CLI::LockfileDataView lockfile_data) -> void
24
+ def print_provenance_results: (Enumerable[ProvenanceResult] results) -> void
25
+ def print_provenance_result: (ProvenanceResult result) -> void
26
+ def print_verified_provenance_result: (ProvenanceResult result, String label) -> void
27
+ def print_mismatched_provenance_result: (ProvenanceResult result, String label) -> void
28
+ def print_unsupported_provenance_result: (ProvenanceResult result, String label) -> void
29
+ def usage: () -> void
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,125 @@
1
+ module Gem
2
+ module Guardian
3
+ type verification_status = :ok | :mismatch | :error
4
+ type checksum_source = :lockfile | :registry | :artifact
5
+ type provenance_status = :verified | :unsupported | :mismatch | :error
6
+
7
+ class VerificationResult < Data
8
+ attr_reader dependency: Dependency
9
+ attr_reader expected_sha256: String?
10
+ attr_reader actual_sha256: String?
11
+ attr_reader artifact_path: String?
12
+ attr_reader status: verification_status
13
+ attr_reader error: Exception?
14
+ attr_reader checksum_source: checksum_source?
15
+ attr_reader registry_sha256: String?
16
+ attr_reader registry_checksum_provider: String?
17
+ attr_reader registry_checksum_uri: String?
18
+
19
+ def self.new: (
20
+ dependency: Dependency,
21
+ expected_sha256: String?,
22
+ actual_sha256: String?,
23
+ artifact_path: String?,
24
+ status: verification_status,
25
+ error: Exception?,
26
+ checksum_source: checksum_source?,
27
+ registry_sha256: String?,
28
+ registry_checksum_provider: String?,
29
+ registry_checksum_uri: String?
30
+ ) -> instance
31
+
32
+ def ok?: () -> bool
33
+ end
34
+
35
+ class Verifier
36
+ def initialize: (
37
+ ?client: RubygemsClient,
38
+ ?artifact_store: ArtifactStore?,
39
+ ?expected_checksums: Hash[Dependency, String]
40
+ ) -> void
41
+
42
+ def verify: (Dependency dependency) -> VerificationResult
43
+ def verify_all: (Enumerable[Dependency] dependencies) -> Array[VerificationResult]
44
+ end
45
+
46
+ class GitHubReleaseResult < Data
47
+ attr_reader dependency: Dependency
48
+ attr_reader status: provenance_status
49
+ attr_reader repository: String?
50
+ attr_reader tag: String?
51
+ attr_reader checksum_assets: Array[String]
52
+ attr_reader signature_assets: Array[String]
53
+ attr_reader signed_tag: bool?
54
+ attr_reader signed_tag_reason: String?
55
+ attr_reader release_attestation: bool?
56
+ attr_reader release_url: String?
57
+ attr_reader error: Exception?
58
+
59
+ def self.new: (
60
+ dependency: Dependency,
61
+ status: provenance_status,
62
+ repository: String?,
63
+ tag: String?,
64
+ checksum_assets: Array[String],
65
+ signature_assets: Array[String],
66
+ signed_tag: bool?,
67
+ signed_tag_reason: String?,
68
+ release_attestation: bool?,
69
+ release_url: String?,
70
+ error: Exception?
71
+ ) -> instance
72
+
73
+ def verified?: () -> bool
74
+ end
75
+
76
+ class GitHubReleaseVerifier
77
+ def initialize: (?client: GitHubClient) -> void
78
+ def verify: (RubygemsClient::TrustedPublishingProvenance provenance) -> GitHubReleaseResult
79
+ end
80
+
81
+ class ProvenanceResult < Data
82
+ attr_reader dependency: Dependency
83
+ attr_reader status: provenance_status
84
+ attr_reader trusted_publishing: bool?
85
+ attr_reader repository: String?
86
+ attr_reader ref: String?
87
+ attr_reader workflow: String?
88
+ attr_reader issuer: String?
89
+ attr_reader subject: String?
90
+ attr_reader expected_sha256: String?
91
+ attr_reader actual_sha256: String?
92
+ attr_reader error: Exception?
93
+ attr_reader attestation_url: String?
94
+ attr_reader github_release: GitHubReleaseResult?
95
+
96
+ def self.new: (
97
+ dependency: Dependency,
98
+ status: provenance_status,
99
+ trusted_publishing: bool?,
100
+ repository: String?,
101
+ ref: String?,
102
+ workflow: String?,
103
+ issuer: String?,
104
+ subject: String?,
105
+ expected_sha256: String?,
106
+ actual_sha256: String?,
107
+ error: Exception?,
108
+ attestation_url: String?,
109
+ github_release: GitHubReleaseResult?
110
+ ) -> instance
111
+
112
+ def verified?: () -> bool
113
+ end
114
+
115
+ class ProvenanceVerifier
116
+ def initialize: (
117
+ ?client: RubygemsClient,
118
+ ?github_release_verifier: GitHubReleaseVerifier
119
+ ) -> void
120
+
121
+ def verify: (Dependency dependency, ?artifact_sha256: String?) -> ProvenanceResult
122
+ def verify_all: (Enumerable[VerificationResult] results) -> Array[ProvenanceResult]
123
+ end
124
+ end
125
+ end
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.4.0
4
+ version: 0.4.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kenneth Demanawa
@@ -59,6 +59,14 @@ files:
59
59
  - lib/gem/guardian/version.rb
60
60
  - mise.toml
61
61
  - script/registry_provenance_audit.rb
62
+ - sig/gem/guardian/checksum_provider.rbs
63
+ - sig/gem/guardian/cli.rbs
64
+ - sig/gem/guardian/clients.rbs
65
+ - sig/gem/guardian/core.rbs
66
+ - sig/gem/guardian/lockfile.rbs
67
+ - sig/gem/guardian/registry.rbs
68
+ - sig/gem/guardian/reporting.rbs
69
+ - sig/gem/guardian/verification.rbs
62
70
  homepage: https://kanutocd.github.io/gem-guardian
63
71
  licenses:
64
72
  - MIT