sdk-reforge 1.10.0 → 1.11.2

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: a8f8071f99c74c0f8b10bebefcb3a15042b2276797e3696be3e8dbf16b0ce446
4
- data.tar.gz: d51548a473a3fced6a34ca2f66453572c11c376c209178a5bdcfc3c3581a92b5
3
+ metadata.gz: de9767c4803726a6881d27e59b026d4a1e23d5592effae2f72908f44aed56e23
4
+ data.tar.gz: 16ab3c79d14b20136bb15cafd76b641f3dcced86c160a16681cf7666a564ec41
5
5
  SHA512:
6
- metadata.gz: 92a6bbc060c11f39c99c28d6b5577049a8b7a379d6edf29408bdd40a5d78c9c6527922bc62970127cff4d3ad6373109672e4305229f0edd155aaaf5b75debc78
7
- data.tar.gz: 4ce7e08d5789756af12148ef0921674f8c5d1bebca92b33100c888d0ee646a87de47fa5ca68a50b0543b76dab69a054eb81bc244bf64e70cda3197fae17544b9
6
+ metadata.gz: c5e8a05f5f01014d9605c68648de26784d9190fdb3ab61d2c3ea55687a35feea851c5f50fbe86a3d21dfd8a0795df4644a656db04098988f8f3e7e030b117ae8
7
+ data.tar.gz: fe44f02ea0fffa0982732a4c581b50440ce174e2cd3798208820ca96b4ee2cfebe90e866d8566588c2d795b39bd505234bbd56340a2155869f740cdc60b8b15f
data/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.11.2 - 2025-10-07
4
+
5
+ - Address OpenSSL issue with vulnerability to truncation attack
6
+
7
+ ## 1.11.1 - 2025-10-06
8
+
9
+ - quiet logging for SSE reconnections
10
+ - let the SSE::Client handle the Last-Event-ID header
11
+
3
12
  ## 1.10.0 - 2025-10-02
4
13
 
5
14
  - require `base64` for newest ruby versions
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.10.0
1
+ 1.11.2
@@ -4,6 +4,7 @@ module Reforge
4
4
  class Encryption
5
5
  CIPHER_TYPE = "aes-256-gcm" # 32/12
6
6
  SEPARATOR = "--"
7
+ AUTH_TAG_LENGTH = 16
7
8
 
8
9
  # Hexadecimal format ensures that generated keys are representable with
9
10
  # plain text
@@ -32,22 +33,30 @@ module Reforge
32
33
  encrypted = cipher.update(clear_text)
33
34
  encrypted << cipher.final
34
35
  tag = cipher.auth_tag
35
-
36
+
36
37
  # pack and join
37
38
  [encrypted, iv, tag].map { |p| p.unpack("H*")[0] }.join(SEPARATOR)
38
39
  end
39
40
 
40
41
  def decrypt(encrypted_string)
41
- unpacked_parts = encrypted_string.split(SEPARATOR).map { |p| [p].pack("H*") }
42
+ encrypted_data, iv, auth_tag = encrypted_string.split(SEPARATOR).map { |p| [p].pack("H*") }
43
+
44
+ # Currently the OpenSSL bindings do not raise an error if auth_tag is
45
+ # truncated, which would allow an attacker to easily forge it. See
46
+ # https://github.com/ruby/openssl/issues/63
47
+ if auth_tag.bytesize != AUTH_TAG_LENGTH
48
+ raise "truncated auth_tag"
49
+ end
42
50
 
43
51
  cipher = OpenSSL::Cipher.new(CIPHER_TYPE)
44
52
  cipher.decrypt
45
53
  cipher.key = @key
46
- cipher.iv = unpacked_parts[1]
47
- cipher.auth_tag = unpacked_parts[2]
48
-
54
+ cipher.iv = iv
55
+
56
+ cipher.auth_tag = auth_tag
57
+
49
58
  # and decrypt it
50
- decrypted = cipher.update(unpacked_parts[0])
59
+ decrypted = cipher.update(encrypted_data)
51
60
  decrypted << cipher.final
52
61
  decrypted
53
62
  end
@@ -72,6 +72,7 @@ module Reforge
72
72
  headers: headers,
73
73
  read_timeout: @options.sse_read_timeout,
74
74
  reconnect_time: @options.sse_default_reconnect_time,
75
+ last_event_id: (@config_loader.highwater_mark&.positive? ? @config_loader.highwater_mark.to_s : nil),
75
76
  logger: Reforge::InternalLogger.new(SSE::Client)) do |client|
76
77
  client.on_event do |event|
77
78
  if event.data.nil? || event.data.empty?
@@ -92,7 +93,12 @@ module Reforge
92
93
  end
93
94
 
94
95
  client.on_error do |error|
95
- @logger.error "SSE Streaming Error: #{error.inspect} for url #{url}"
96
+ # SSL "unexpected eof" is expected when SSE sessions timeout normally
97
+ if error.is_a?(OpenSSL::SSL::SSLError) && error.message.include?('unexpected eof')
98
+ @logger.debug "SSE Streaming: Connection closed (expected timeout) for url #{url}"
99
+ else
100
+ @logger.error "SSE Streaming Error: #{error.inspect} for url #{url}"
101
+ end
96
102
 
97
103
  if @options.errors_to_close_connection.any? { |klass| error.is_a?(klass) }
98
104
  @logger.debug "Closing SSE connection for url #{url}"
@@ -106,7 +112,6 @@ module Reforge
106
112
  auth = "#{AUTH_USER}:#{@prefab_options.sdk_key}"
107
113
  auth_string = Base64.strict_encode64(auth)
108
114
  return {
109
- 'Last-Event-ID' => @config_loader.highwater_mark,
110
115
  'Authorization' => "Basic #{auth_string}",
111
116
  'Accept' => 'text/event-stream',
112
117
  'X-Reforge-SDK-Version' => "sdk-ruby-#{Reforge::VERSION}"
data/sdk-reforge.gemspec CHANGED
@@ -2,16 +2,16 @@
2
2
  # DO NOT EDIT THIS FILE DIRECTLY
3
3
  # Instead, edit Juwelier::Tasks in Rakefile, and run 'rake gemspec'
4
4
  # -*- encoding: utf-8 -*-
5
- # stub: sdk-reforge 1.10.0 ruby lib
5
+ # stub: sdk-reforge 1.11.2 ruby lib
6
6
 
7
7
  Gem::Specification.new do |s|
8
8
  s.name = "sdk-reforge".freeze
9
- s.version = "1.10.0"
9
+ s.version = "1.11.2"
10
10
 
11
11
  s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
12
12
  s.require_paths = ["lib".freeze]
13
13
  s.authors = ["Jeff Dwyer".freeze]
14
- s.date = "2025-10-02"
14
+ s.date = "2025-10-07"
15
15
  s.description = "Feature Flags, Live Config as a service".freeze
16
16
  s.email = "jeff.dwyer@reforge.com.cloud".freeze
17
17
  s.extra_rdoc_files = [
@@ -16,7 +16,6 @@ class TestSSEConfigClient < Minitest::Test
16
16
 
17
17
  client = Reforge::SSEConfigClient.new(options, config_loader)
18
18
 
19
- assert_equal 4, client.headers['Last-Event-ID']
20
19
  assert_equal "https://stream.goatsofreforge.com", client.source
21
20
 
22
21
  result = nil
@@ -48,8 +47,6 @@ class TestSSEConfigClient < Minitest::Test
48
47
 
49
48
  client = Reforge::SSEConfigClient.new(prefab_options, config_loader, sse_options)
50
49
 
51
- assert_equal 4, client.headers['Last-Event-ID']
52
-
53
50
  result = nil
54
51
 
55
52
  # fake our load_configs block
@@ -247,4 +244,41 @@ class TestSSEConfigClient < Minitest::Test
247
244
  'Expected to have logged an error about empty data for nil'
248
245
  mock_client.verify
249
246
  end
247
+
248
+ def test_last_event_id_initialization
249
+ # Test with positive highwater_mark
250
+ config_loader = OpenStruct.new(highwater_mark: 42)
251
+ prefab_options = OpenStruct.new(sse_sources: ['http://localhost:4567'], sdk_key: 'test')
252
+ client = Reforge::SSEConfigClient.new(prefab_options, config_loader)
253
+
254
+ # Mock SSE::Client.new to capture the last_event_id argument
255
+ SSE::Client.stub :new, ->(*args, **kwargs, &block) {
256
+ assert_equal '42', kwargs[:last_event_id], 'Expected last_event_id to be "42"'
257
+ OpenStruct.new(closed?: false, close: nil)
258
+ } do
259
+ client.connect { |_configs, _event, _source| }
260
+ end
261
+
262
+ # Test with nil highwater_mark
263
+ config_loader = OpenStruct.new(highwater_mark: nil)
264
+ client = Reforge::SSEConfigClient.new(prefab_options, config_loader)
265
+
266
+ SSE::Client.stub :new, ->(*args, **kwargs, &block) {
267
+ assert_nil kwargs[:last_event_id], 'Expected last_event_id to be nil when highwater_mark is nil'
268
+ OpenStruct.new(closed?: false, close: nil)
269
+ } do
270
+ client.connect { |_configs, _event, _source| }
271
+ end
272
+
273
+ # Test with zero highwater_mark
274
+ config_loader = OpenStruct.new(highwater_mark: 0)
275
+ client = Reforge::SSEConfigClient.new(prefab_options, config_loader)
276
+
277
+ SSE::Client.stub :new, ->(*args, **kwargs, &block) {
278
+ assert_nil kwargs[:last_event_id], 'Expected last_event_id to be nil when highwater_mark is 0'
279
+ OpenStruct.new(closed?: false, close: nil)
280
+ } do
281
+ client.connect { |_configs, _event, _source| }
282
+ end
283
+ end
250
284
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sdk-reforge
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.10.0
4
+ version: 1.11.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeff Dwyer
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-10-02 00:00:00.000000000 Z
10
+ date: 2025-10-07 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: concurrent-ruby