Security News
Research
Data Theft Repackaged: A Case Study in Malicious Wrapper Packages on npm
The Socket Research Team breaks down a malicious wrapper package that uses obfuscation to harvest credentials and exfiltrate sensitive data.
SpeedLimiter is a gem that limits the number of executions per unit of time. By default, it achieves throttling through sleep.
You can also use the on_throttled
event to raise an exception instead of sleeping, or to re-enqueue the task.
Add this line to your application's Gemfile:
gem 'speed_limiter'
And then execute:
$ bundle install
Or install it yourself as:
$ gem install speed_limiter
Limit the number of executions to 10 times per second.
SpeedLimiter.throttle('server_name/method_name', limit: 10, period: 1) do |state|
puts state #=> <SpeedLimiter::State key=server_name/method_name count=1 ttl=0>
http.get(path)
end
# or
throttle_limit_10_par_sec = SpeedLimiter.throttle('server_name/method_name', limit: 10, period: 1)
throttle_limit_10_par_sec.call do |state|
puts state #=> <SpeedLimiter::State key=server_name/method_name count=1 ttl=0>
http.get(path)
end
It returns the result of the block execution.
result = SpeedLimiter.throttle('server_name/method_name', limit: 10, period: 1) do
http.get(path)
end
puts result.code #=> 200
Specify the process when the limit is exceeded.
on_throttled = proc { |state| logger.info("limit exceeded #{state.key} #{state.ttl}") }
SpeedLimiter.throttle('server_name/method_name', limit: 10, period: 1, on_throttled: on_throttled) do
http.get(path)
end
It raises an exception when the limit is exceeded.
begin
SpeedLimiter.throttle('server_name/method_name', limit: 10, period: 1, raise_on_throttled: true) do
http.get(path)
end
rescue SpeedLimiter::ThrottledError => e
logger.info(e.message) #=> "server_name/method_name rate limit exceeded. Retry after 0.9 seconds. limit=10, count=11, period=1"
e.state #=> <SpeedLimiter::State key=server_name/method_name count=11 ttl=0.9>
end
Reinitialize the queue instead of sleeping when the limit is reached in ActiveJob.
class CreateSlackChannelJob < ApplicationJob
rescue_from(SpeedLimiter::ThrottledError) do |e|
Rails.logger.warn("[#{e.class}] #{self.class} retry job. #{e.message}")
retry_job(wait: e.ttl, queue: 'low')
end
def perform(*args)
SpeedLimiter.throttle("slack", limit: 20, period: 1.minute, raise_on_throttled: true) do
create_slack_channel(*args)
end
end
end
To use the retry:
option, you need to introduce the Retryable gem.
By specifying the options of the Retryable gem, you can retry when an exception occurs.
# Gemfile
gem 'retryable'
SpeedLimiter.throttle('server_name/method_name', limit: 10, period: 1, retry: { tries: 3, on: OpenURI::HTTPError }) do
http.get(path)
end
# equivalent to
SpeedLimiter.throttle('server_name/method_name', limit: 10, period: 1) do
Retryable.retryable(tries: 3, on: OpenURI::HTTPError) do
http.get(path)
end
end
retry: true
or retry: {}
is default use of Retryable.configure
.
SpeedLimiter.throttle('server_name/method_name', limit: 10, period: 1, retry: true) do
http.get(path)
end
Redis can be specified as follows
redis://localhost:6379/0
SPEED_LIMITER_REDIS_URL
environment variableSpeedLimiter.configure
# config/initializers/speed_limiter.rb
SpeedLimiter.configure do |config|
config.redis_url = ENV.fetch('SPEED_LIMITER_REDIS_URL', 'redis://localhost:6379/2')
end
or Use Redis instance
# config/initializers/speed_limiter.rb
SpeedLimiter.configure do |config|
config.redis = Redis.new(host: 'localhost', port: 6379, db: 2)
end
SpeedLimiter.configure do |config|
config.redis_url = ENV.fetch("SPEED_LIMITER_REDIS_URL", "redis://localhost:6379/0")
config.redis = nil
config.no_limit = false # If true, it will not be throttled
config.prefix = "speed_limiter" # Redis key prefix
config.on_throttled = nil # Proc to be executed when the limit is exceeded
end
If you do not want to impose a limit in the test environment, please set it as follows.
# spec/support/speed_limiter.rb
RSpec.configure do |config|
config.before(:suite) do
SpeedLimiter.configure do |config|
config.no_limit = true
end
end
end
If you want to detect the limit in the test environment, please set it as follows.
Rspec.describe do
around do |example|
SpeedLimiter.config.on_throttled = proc { |state| raise "limit exceeded #{state.key} #{state.ttl}" }
example.run
SpeedLimiter.config.on_throttled = nil
end
it do
expect { over_limit_method }.to raise_error('limit exceeded key_name [\d.]+')
end
end
SpeedLimiter officially supports the following Ruby implementations and Redis :
After checking out the repo, run bin/setup
to install dependencies.
Please run rspec referring to the following.
Before committing, run bundle exec rubocop to perform a style check. You can also run bin/console for an interactive prompt that will allow you to experiment.
Start a test web server and Redis for testing with the following command.
$ rake throttle_server:start_daemon # or rake throttle_server:start
$ docker compose up -d
After that, please run the test with the following command.
$ bundle exec rspec -fd
The gem is available as open source under the terms of the MIT License.
FAQs
Unknown package
We found that speed_limiter demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Research
The Socket Research Team breaks down a malicious wrapper package that uses obfuscation to harvest credentials and exfiltrate sensitive data.
Research
Security News
Attackers used a malicious npm package typosquatting a popular ESLint plugin to steal sensitive data, execute commands, and exploit developer systems.
Security News
The Ultralytics' PyPI Package was compromised four times in one weekend through GitHub Actions cache poisoning and failure to rotate previously compromised API tokens.