From 4bbd710f3ec8008bd1c52fda6a3a2440f3b35cd1 Mon Sep 17 00:00:00 2001 From: Lex Alexander Date: Tue, 17 Dec 2024 20:07:37 -0800 Subject: [PATCH] Prevent Opening JSON Key Path unless It's an IO Object --- lib/fcm.rb | 22 ++++++++++++++++++---- spec/fcm_spec.rb | 43 ++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 58 insertions(+), 7 deletions(-) diff --git a/lib/fcm.rb b/lib/fcm.rb index 3622c77..1bd781f 100644 --- a/lib/fcm.rb +++ b/lib/fcm.rb @@ -4,6 +4,8 @@ require "googleauth" class FCM + class InvalidCredentialError < StandardError; end + BASE_URI = "https://fcm.googleapis.com" BASE_URI_V1 = "https://fcm.googleapis.com/v1/projects/" DEFAULT_TIMEOUT = 30 @@ -291,11 +293,23 @@ def jwt_token token["access_token"] end + def credentials_error_msg(json_key_path) + param_klass = if @json_key_path.nil? + 'nil' + else + "a #{@json_key_path.class.name}" + end + + "credentials must be an IO-like object or a path. You passed #{param_klass}" + end + def json_key @json_key ||= if @json_key_path.respond_to?(:read) - @json_key_path - else - File.open(@json_key_path) - end + @json_key_path + elsif (@json_key_path.is_a?(String) || @json_key_path.respond_to?(:open)) && File.file?(@json_key_path) + File.open(@json_key_path) + else + raise InvalidCredentialError, credentials_error_msg(@json_key_path) + end end end diff --git a/spec/fcm_spec.rb b/spec/fcm_spec.rb index a3f24e1..e87bda3 100644 --- a/spec/fcm_spec.rb +++ b/spec/fcm_spec.rb @@ -13,15 +13,31 @@ } end + let(:json_credentials) do + { + "type": 'service_account', + "project_id": 'example', + "private_key_id": 'c09c4593eee53707ca9f4208fbd6fe72b29fc7ab', + "private_key": OpenSSL::PKey::RSA.new(2048), + "client_email": '83315528762cf7e0-7bbcc3aad87e0083391bc7f234d487c8@developer.gserviceaccount.com', + "client_id": 'acedc3c0a63b3562376386f0-f3b94aafbecd0e7d60563bf7bb8bb47f.apps.googleusercontent.com', + "auth_uri": 'https://accounts.google.com/o/oauth2/auth', + "token_uri": 'https://oauth2.googleapis.com/token', + "auth_provider_x509_cert_url": 'https://www.googleapis.com/oauth2/v1/certs', + "client_x509_cert_url": 'https://www.googleapis.com/robot/v1/metadata/x509/fd6b61037dd2bb8585527679-7bbcc3aad87e0083391bc7f234d487c8%40developer.gserviceaccount.com', + "universe_domain": 'googleapis.com' + }.to_json + end + before do allow(client).to receive(:json_key) # Mock the Google::Auth::ServiceAccountCredentials - allow(Google::Auth::ServiceAccountCredentials).to receive(:make_creds). - and_return(double(fetch_access_token!: { 'access_token' => mock_token })) + allow(Google::Auth::ServiceAccountCredentials).to receive(:make_creds) + .and_return(double(fetch_access_token!: { 'access_token' => mock_token })) end - it "should initialize" do + it 'should initialize' do expect { client }.not_to raise_error end @@ -35,6 +51,27 @@ fcm = FCM.new(StringIO.new("hey")) expect(fcm.__send__(:json_key).class).to eq(StringIO) end + + it "raises an error when passed a non-existent credentials file path" do + fcm = FCM.new('spec/fake_credentials.json', '', {}) + expect { fcm.__send__(:json_key).class }.to raise_error(FCM::InvalidCredentialError) + end + + it "raises an error when passed a string of a file that does not exist" do + fcm = FCM.new("fake_credentials.json", '', {}) + expect { fcm.__send__(:json_key).class }.to raise_error(FCM::InvalidCredentialError) + end + + it 'raises an error when passed a non IO-like object' do + fcm_with_non_io_objects = [ + fcm_with_nil_creds = FCM.new(nil, '', {}), + fcm_with_hash_creds = FCM.new({}, '', {}), + fcm_with_json = FCM.new(json_credentials, '', {}) + ] + fcm_with_non_io_objects.each do |fcm_with_non_io_object| + expect { fcm_with_non_io_object.__send__(:json_key).class }.to raise_error(FCM::InvalidCredentialError) + end + end end describe "#send_v1 or #send_notification_v1" do