safer_redis 1.0.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d0c31676a608f111b89e5de3b5ec1ba3cc46b276afb362eee6aebd1a43120cfb
4
- data.tar.gz: 0bc66dde14999d62732ade01b0cf36f645cff45911eff4c818ab0f696bbe9bee
3
+ metadata.gz: 790845a432c5b85fa5261a8cbca3170058adcc00352f79ae3a1d211e5027b7dc
4
+ data.tar.gz: 9bacdc397e449211800e19ccd0fb6e99089215db5fbd52593d452ec8b73f44d8
5
5
  SHA512:
6
- metadata.gz: a854dda2a09c6dd52330afc9de66895eba48447767da9e0cf53bc7a9a7c3dd7e64ca2c1ea4c590f15be29d4bfc53bf7751b9335a09150b85e34d932586f42de6
7
- data.tar.gz: 11d94efde9334fd9e242ebd2ec7dcd6ff1a320731cda4f1ec537ca333f642d933b9e87b2cbf0cc16171ecb1c34294570897fa191a60bda823de0c107d3622e5f
6
+ metadata.gz: f0fa1a49e44253388abaa4483ad9ceeae25b65386c1ed5ecff2820075905245d45684aabd0dfb475041739ec1b938ddcf125e3546d52b22d1380e30793b823f6
7
+ data.tar.gz: ee4c6847111eb19a6e2d836e557826a3cf9a4a14219dfcb9734e26b9728d9f8649960aa0071c2a2de68f83b421c339713e92291d52b796f8926e3e15cc118b54
data/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [1.2.0] - 2023-11-14
4
+
5
+ - Allow `DEL` (`@slow`) if `lazyfree-lazy-user-del` server option is enabled.
6
+
7
+ ## [1.1.0] - 2023-01-07
8
+
9
+ - Allow `@slow` if it's only `O(1)` complexity, e.g. the `SET` (string) command.
10
+
3
11
  ## [1.0.0] - 2022-12-02
4
12
 
5
13
  - Initial release
data/Gemfile.lock CHANGED
@@ -1,37 +1,39 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- safer_redis (0.1.0)
4
+ safer_redis (1.1.0)
5
5
  redis (>= 4.6.0)
6
6
  zeitwerk
7
7
 
8
8
  GEM
9
9
  remote: https://rubygems.org/
10
10
  specs:
11
- connection_pool (2.3.0)
11
+ connection_pool (2.4.1)
12
12
  diff-lcs (1.5.0)
13
- rake (13.0.6)
14
- redis (5.0.5)
15
- redis-client (>= 0.9.0)
16
- redis-client (0.11.2)
13
+ rake (13.1.0)
14
+ redis (5.0.8)
15
+ redis-client (>= 0.17.0)
16
+ redis-client (0.18.0)
17
17
  connection_pool
18
18
  rspec (3.12.0)
19
19
  rspec-core (~> 3.12.0)
20
20
  rspec-expectations (~> 3.12.0)
21
21
  rspec-mocks (~> 3.12.0)
22
- rspec-core (3.12.0)
22
+ rspec-core (3.12.2)
23
23
  rspec-support (~> 3.12.0)
24
- rspec-expectations (3.12.0)
24
+ rspec-expectations (3.12.3)
25
25
  diff-lcs (>= 1.2.0, < 2.0)
26
26
  rspec-support (~> 3.12.0)
27
- rspec-mocks (3.12.0)
27
+ rspec-mocks (3.12.6)
28
28
  diff-lcs (>= 1.2.0, < 2.0)
29
29
  rspec-support (~> 3.12.0)
30
- rspec-support (3.12.0)
31
- zeitwerk (2.6.6)
30
+ rspec-support (3.12.1)
31
+ zeitwerk (2.6.12)
32
32
 
33
33
  PLATFORMS
34
34
  x86_64-darwin-21
35
+ x86_64-darwin-22
36
+ x86_64-darwin-23
35
37
 
36
38
  DEPENDENCIES
37
39
  rake (~> 13.0)
data/README.md CHANGED
@@ -4,9 +4,16 @@ SaferRedis wraps a Redis connection, and warns you before letting through comman
4
4
 
5
5
  Inspiration is taken from https://github.com/ankane/strong_migrations which provides similar guard-rails for potentially dangerous database migrations.
6
6
 
7
+ ## Special cases
8
+
9
+ Commands tagged as `@slow` but with only O(1) complexity are not considered dangerous. This includes
10
+ e.g. the extremely commonly used `SET` command.
11
+
12
+ The [`DEL`](https://redis.io/commands/del/) command (`@slow`) is treated as [`UNLINK`](https://redis.io/commands/unlink/) (`@fast`) when the `lazyfree-lazy-user-del` server option is set.
13
+
7
14
  ## Limitations
8
15
 
9
- Currently SaferRedis works with the [`redis` gem](https://rubygems.org/gems/redis) from https://github.com/redis/redis-rb (regardless of which connection adapter is being used, e.g. [`hiredis`](https://rubygems.org/gems/hiredis)) by hooking into the private `#send_command` method which was introduced in v4.6.0. This isn't a stable API, so other interception strategies will be considered and may be added in future. Some Redis connectors/drivers suport middleware, but others don't.
16
+ Currently SaferRedis works with the [`redis` gem](https://rubygems.org/gems/redis) from https://github.com/redis/redis-rb (regardless of which connection adapter is being used, e.g. [`hiredis`](https://rubygems.org/gems/hiredis)) by hooking into the private `#send_command` method which was introduced in v4.6.0. This isn't a stable API, so other interception strategies will be considered and may be added in future. Some Redis connectors/drivers support middleware, but others don't.
10
17
 
11
18
  Some commands are documented at the subcommand level as multiple words (e.g. `CLIENT LIST`). SaferRedis currently only recognises commands documented at their top-level single word (e.g. `DEL`). Support for multi-word commands will be considered and may be added in future.
12
19
 
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+
5
+ module SaferRedis
6
+ class Assessor
7
+ def self.assess!(doc, redis:)
8
+ # DEL becomes UNLINK if lazyfree-lazy-user-del is set
9
+ # https://github.com/redis/redis/pull/6243
10
+ # https://redis.io/docs/management/config-file/
11
+ if doc.name == "DEL" && config_get("lazyfree-lazy-user-del", redis:) == "yes"
12
+ doc = SaferRedis::CommandDoc.new("UNLINK")
13
+ end
14
+
15
+ if doc.dangerous?
16
+ # Anything tagged @dangerous is… dangerous
17
+ raise SaferRedis::Danger.new(doc)
18
+
19
+ elsif doc.slow? && doc.complexity != "O(1)"
20
+ # Anything tagged @slow might be dangerous, but we'll let through O(1)
21
+ # complexity commands e.g. SET
22
+ raise SaferRedis::Danger.new(doc)
23
+ end
24
+ end
25
+
26
+ def self.config_get(option, redis:)
27
+ SaferRedis.really { redis.config(:get, option)[option] }
28
+ end
29
+ end
30
+ end
@@ -52,6 +52,10 @@ module SaferRedis
52
52
  command.fetch("complexity", nil)
53
53
  end
54
54
 
55
+ def suggestion
56
+ Suggestion.for_command(name)
57
+ end
58
+
55
59
  private
56
60
 
57
61
  def command
@@ -15,6 +15,13 @@ module SaferRedis
15
15
  If you're sure this is okay, you can try again within `SaferRedis.really { ... }`
16
16
  MESSAGE
17
17
 
18
+ if doc.suggestion
19
+ message = <<~MESSAGE
20
+ #{message}
21
+ Suggestion: #{doc.suggestion.description}
22
+ MESSAGE
23
+ end
24
+
18
25
  super(message)
19
26
  end
20
27
  end
@@ -4,7 +4,10 @@ module SaferRedis
4
4
  module Interceptor
5
5
  def send_command(command, &block)
6
6
  if SaferRedis.active?
7
- SaferRedis.assess!(SaferRedis::CommandDoc.from_command_array(command))
7
+ SaferRedis::Assessor.assess!(
8
+ SaferRedis::CommandDoc.from_command_array(command),
9
+ redis: self,
10
+ )
8
11
  end
9
12
 
10
13
  super
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SaferRedis
4
+ class Suggestion
5
+ attr_reader :command, :description
6
+
7
+ def initialize(command, description)
8
+ @command = command
9
+ @description = description
10
+ end
11
+
12
+ def self.for_command(command)
13
+ case command
14
+ when "DEL"
15
+ new(command, <<~END)
16
+ Consider using the non-blocking UNLINK command instead to delete the key on a \
17
+ background thread, or set the lazyfree-lazy-user-del server option
18
+ END
19
+ else
20
+ nil
21
+ end
22
+ end
23
+ end
24
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SaferRedis
4
- VERSION = "1.0.0"
4
+ VERSION = "1.2.0"
5
5
  end
data/lib/safer_redis.rb CHANGED
@@ -26,10 +26,4 @@ module SaferRedis
26
26
  ensure
27
27
  @active = was
28
28
  end
29
-
30
- def self.assess!(doc)
31
- if doc.dangerous? || doc.slow?
32
- raise SaferRedis::Danger.new(doc)
33
- end
34
- end
35
29
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: safer_redis
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Paul Annesley
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-12-02 00:00:00.000000000 Z
11
+ date: 2023-11-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: redis
@@ -56,11 +56,12 @@ files:
56
56
  - Rakefile
57
57
  - data/redis-doc/commands.json
58
58
  - lib/safer_redis.rb
59
+ - lib/safer_redis/assessor.rb
59
60
  - lib/safer_redis/command_doc.rb
60
61
  - lib/safer_redis/danger.rb
61
62
  - lib/safer_redis/interceptor.rb
63
+ - lib/safer_redis/suggestion.rb
62
64
  - lib/safer_redis/version.rb
63
- - safer_redis.gemspec
64
65
  - sig/safer_redis.rbs
65
66
  homepage: https://github.com/buildkite/safer_redis
66
67
  licenses:
@@ -77,14 +78,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
77
78
  requirements:
78
79
  - - ">="
79
80
  - !ruby/object:Gem::Version
80
- version: 2.6.0
81
+ version: 3.2.0
81
82
  required_rubygems_version: !ruby/object:Gem::Requirement
82
83
  requirements:
83
84
  - - ">="
84
85
  - !ruby/object:Gem::Version
85
86
  version: '0'
86
87
  requirements: []
87
- rubygems_version: 3.1.6
88
+ rubygems_version: 3.4.22
88
89
  signing_key:
89
90
  specification_version: 4
90
91
  summary: Catch unsafe Redis commands in production
data/safer_redis.gemspec DELETED
@@ -1,35 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "lib/safer_redis/version"
4
-
5
- Gem::Specification.new do |spec|
6
- spec.name = "safer_redis"
7
- spec.version = SaferRedis::VERSION
8
- spec.authors = ["Paul Annesley"]
9
- spec.email = ["paul@annesley.cc"]
10
-
11
- spec.summary = "Catch unsafe Redis commands in production"
12
- spec.description = "SaferRedis warns you before letting through commands that could impact production availability by being marked `@slow` or `@dangerous` in the Redis documentation"
13
- spec.homepage = "https://github.com/buildkite/safer_redis"
14
- spec.license = "MIT"
15
- spec.required_ruby_version = ">= 2.6.0"
16
-
17
- spec.metadata["homepage_uri"] = spec.homepage
18
- spec.metadata["source_code_uri"] = "https://github.com/buildkite/safer_redis"
19
- spec.metadata["changelog_uri"] = "https://github.com/buildkite/safer_redis/blob/main/CHANGELOG.md"
20
-
21
- # Specify which files should be added to the gem when it is released.
22
- # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
23
- spec.files = Dir.chdir(__dir__) do
24
- `git ls-files -z`.split("\x0").reject do |f|
25
- (f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
26
- end
27
- end
28
- spec.require_paths = ["lib"]
29
-
30
- # The only strategy implemented so far is intercepting the private Redis#send_command method,
31
- # which was introduced in v4.6.0 via https://github.com/redis/redis-rb/pull/1058 and remains
32
- # in the latest release (v5.0.5) at time of writing.
33
- spec.add_dependency "redis", ">= 4.6.0"
34
- spec.add_dependency "zeitwerk"
35
- end