mirror of
https://github.com/chatwoot/chatwoot.git
synced 2026-06-04 21:02:35 +08:00
chore: update jwt and faraday (#14577)
This PR updates two dependencies — `faraday` (2.14.1 → 2.14.2) and `jwt` (2.10.1 → 2.10.3) — to pick up security patches flagged by `bundle-audit`. Both are bumped to the minimal patched release within their existing major lines to keep the blast radius small. ### Faraday `Faraday::Connection#build_exclusive_url` still allowed a protocol-relative host override when the request target was passed as a `URI` object (rather than a `String`), bypassing the earlier fix for the string-based variant (CVE-2026-25765 / GHSA-33mh-2634-fwr2). On a fixed-base connection this could redirect a request to an attacker-controlled host while still forwarding connection-scoped headers such as `Authorization` — i.e. off-host request forgery (CVE-2026-33637 / GHSA-5rv5-xj5j-3484). The fix is a clean patch bump to `2.14.2`, within Faraday's existing version range — no API changes and no other gems affected. ### JWT `jwt` 2.10.1 accepts an empty/`nil` HMAC key during verification: `JWT.decode(token, "", true, algorithm: 'HS256')` (and keyfinder paths returning `""`/`nil`) verify a forged token, because the empty-key HMAC digest is treated as valid and `enforce_hmac_key_length` defaults to `false` (CVE-2026-45363, High). The advisory offers two fixes — `~> 2.10.3` or `>= 3.2.0`. We chose **2.10.3** deliberately: jumping to 3.x cascaded into upgrading `oauth2`, `twilio-ruby`, `googleauth`, `web-push`, and `signet` (all pinned `jwt < 3.0`), and `jwt` is used directly in 8+ places here (token services, OAuth callbacks, integration helpers), so a major bump carries real breakage risk for no extra security benefit. The Gemfile is pinned `'~> 2.10', '>= 2.10.3'` to hold the 2.x line. **Spec changes.** 2.10.3 tightens key handling: HMAC sign/verify now raises on a `nil`, empty, or non-`String` key instead of silently coercing it. A few specs relied on the old lax behaviour and needed updating: - `microsoft` / `google` callback specs built unsigned ID tokens via `JWT.encode(payload, false)`. Replaced with the correct unsigned form, `JWT.encode(payload, nil, 'none')`. - `instagram` / `linear` / `shopify` helper specs have a "client secret not configured" context where `client_secret` is `nil`. Their shared `valid_token` `let` signed with that `nil` secret, which Ruby evaluates before the helper runs — now raising. Since the helper short-circuits on the blank secret and never decodes the token, those contexts now override `valid_token` with a throwaway string. **Production is unaffected.** Every production HMAC path uses a real, non-empty key — `Rails.application.secret_key_base` (`BaseTokenService`, `Widget::TokenService`) or a client secret guarded by `return if client_secret.blank?` (Instagram/TikTok/Shopify/Linear helpers). The one `nil`-key call, `JWT.decode(id_token, nil, false)` in `OauthCallbackController`, runs with verification disabled, so the key is never inspected. Twilio voice tokens use `Twilio::JWT::AccessToken` from `twilio-ruby`, not this gem. The specs failed precisely because they exercised the unsafe empty-key pattern the patch now blocks — production never did.
This commit is contained in:
parent
7422b656cd
commit
94daf26ead
2
Gemfile
2
Gemfile
@ -89,7 +89,7 @@ gem 'rails-i18n', '~> 7.0'
|
||||
# two-factor authentication
|
||||
gem 'devise-two-factor', '>= 5.0.0'
|
||||
# authorization
|
||||
gem 'jwt'
|
||||
gem 'jwt', '~> 2.10', '>= 2.10.3'
|
||||
gem 'pundit'
|
||||
|
||||
# super admin
|
||||
|
||||
@ -301,7 +301,7 @@ GEM
|
||||
railties (>= 5.0.0)
|
||||
faker (3.2.0)
|
||||
i18n (>= 1.8.11, < 2)
|
||||
faraday (2.14.1)
|
||||
faraday (2.14.2)
|
||||
faraday-net_http (>= 2.0, < 3.5)
|
||||
json
|
||||
logger
|
||||
@ -491,7 +491,7 @@ GEM
|
||||
judoscale-sidekiq (1.8.2)
|
||||
judoscale-ruby (= 1.8.2)
|
||||
sidekiq (>= 5.0)
|
||||
jwt (2.10.1)
|
||||
jwt (2.10.3)
|
||||
base64
|
||||
kaminari (1.2.2)
|
||||
activesupport (>= 4.1.0)
|
||||
@ -1102,7 +1102,7 @@ DEPENDENCIES
|
||||
json_schemer
|
||||
judoscale-rails
|
||||
judoscale-sidekiq
|
||||
jwt
|
||||
jwt (~> 2.10, >= 2.10.3)
|
||||
kaminari
|
||||
koala
|
||||
letter_opener
|
||||
|
||||
@ -8,12 +8,12 @@ RSpec.describe 'Google::CallbacksController', type: :request do
|
||||
|
||||
describe 'GET /google/callback' do
|
||||
let(:response_body_success) do
|
||||
{ id_token: JWT.encode({ email: email, name: 'test' }, false), access_token: SecureRandom.hex(10), token_type: 'Bearer',
|
||||
{ id_token: JWT.encode({ email: email, name: 'test' }, nil, 'none'), access_token: SecureRandom.hex(10), token_type: 'Bearer',
|
||||
refresh_token: SecureRandom.hex(10) }
|
||||
end
|
||||
|
||||
let(:response_body_success_without_name) do
|
||||
{ id_token: JWT.encode({ email: email }, false), access_token: SecureRandom.hex(10), token_type: 'Bearer',
|
||||
{ id_token: JWT.encode({ email: email }, nil, 'none'), access_token: SecureRandom.hex(10), token_type: 'Bearer',
|
||||
refresh_token: SecureRandom.hex(10) }
|
||||
end
|
||||
|
||||
|
||||
@ -8,12 +8,12 @@ RSpec.describe 'Microsoft::CallbacksController', type: :request do
|
||||
|
||||
describe 'GET /microsoft/callback' do
|
||||
let(:response_body_success) do
|
||||
{ id_token: JWT.encode({ email: email, name: 'test' }, false), access_token: SecureRandom.hex(10), token_type: 'Bearer',
|
||||
{ id_token: JWT.encode({ email: email, name: 'test' }, nil, 'none'), access_token: SecureRandom.hex(10), token_type: 'Bearer',
|
||||
refresh_token: SecureRandom.hex(10) }
|
||||
end
|
||||
|
||||
let(:response_body_success_without_name) do
|
||||
{ id_token: JWT.encode({ email: email }, false), access_token: SecureRandom.hex(10), token_type: 'Bearer',
|
||||
{ id_token: JWT.encode({ email: email }, nil, 'none'), access_token: SecureRandom.hex(10), token_type: 'Bearer',
|
||||
refresh_token: SecureRandom.hex(10) }
|
||||
end
|
||||
|
||||
|
||||
@ -82,6 +82,7 @@ RSpec.describe Instagram::IntegrationHelper do
|
||||
|
||||
context 'when client secret is not configured' do
|
||||
let(:client_secret) { nil }
|
||||
let(:valid_token) { 'any-token' }
|
||||
|
||||
it 'returns nil' do
|
||||
expect(verify_instagram_token(valid_token)).to be_nil
|
||||
|
||||
@ -65,6 +65,7 @@ RSpec.describe Linear::IntegrationHelper do
|
||||
|
||||
context 'when client secret is not configured' do
|
||||
let(:client_secret) { nil }
|
||||
let(:valid_token) { 'any-token' }
|
||||
|
||||
it 'returns nil' do
|
||||
expect(verify_linear_token(valid_token)).to be_nil
|
||||
|
||||
@ -65,6 +65,7 @@ RSpec.describe Shopify::IntegrationHelper do
|
||||
|
||||
context 'when client secret is not configured' do
|
||||
let(:client_secret) { nil }
|
||||
let(:valid_token) { 'any-token' }
|
||||
|
||||
it 'returns nil' do
|
||||
expect(verify_shopify_token(valid_token)).to be_nil
|
||||
|
||||
Loading…
Reference in New Issue
Block a user