Repository: intercom/intercom-rails Branch: master Commit: 7a4012274746 Files: 40 Total size: 349.3 KB Directory structure: gitextract_dc2i27xd/ ├── .circleci/ │ └── config.yml ├── .github/ │ ├── ISSUE_TEMPLATE.md │ ├── PULL_REQUEST_TEMPLATE.md │ └── workflows/ │ └── label-ai-generated-prs.yml ├── .gitignore ├── Gemfile ├── MIT-LICENSE ├── README.md ├── RELEASING.md ├── Rakefile ├── intercom-rails.gemspec ├── lib/ │ ├── data/ │ │ └── cacert.pem │ ├── intercom-rails/ │ │ ├── auto_include_filter.rb │ │ ├── config.rb │ │ ├── custom_data_helper.rb │ │ ├── date_helper.rb │ │ ├── encrypted_mode.rb │ │ ├── exceptions.rb │ │ ├── proxy/ │ │ │ ├── company.rb │ │ │ └── user.rb │ │ ├── proxy.rb │ │ ├── railtie.rb │ │ ├── script_tag.rb │ │ ├── script_tag_helper.rb │ │ ├── shutdown_helper.rb │ │ └── version.rb │ ├── intercom-rails.rb │ └── rails/ │ └── generators/ │ └── intercom/ │ └── config/ │ ├── config_generator.rb │ └── intercom.rb.erb └── spec/ ├── action_controller_spec_helper.rb ├── auto_include_filter_spec.rb ├── auto_include_filter_spec_csp_helper.rb ├── config_spec.rb ├── encrypted_mode_spec.rb ├── proxy/ │ ├── company_spec.rb │ └── user_spec.rb ├── script_tag_helper_spec.rb ├── script_tag_spec.rb ├── shutdown_helper_spec.rb └── spec_helper.rb ================================================ FILE CONTENTS ================================================ ================================================ FILE: .circleci/config.yml ================================================ version: 2 jobs: build: docker: - image: cimg/ruby:2.7 working_directory: ~/intercom-rails steps: - checkout - run: bundle install - run: bundle exec rake ================================================ FILE: .github/ISSUE_TEMPLATE.md ================================================ Please use the following template to submit your issue. Following this template will allow us to quickly investigate and help you with your issue. Please be aware that issues which do not conform to this template may be closed. For feature requests please contact us at team@intercom.io ## Version info - intercom-rails version: - Rails version: ## Expected behavior ## Actual behavior ## Steps to reproduce 1. 2. 3. ## Logs ================================================ FILE: .github/PULL_REQUEST_TEMPLATE.md ================================================ #### Why? Why are you making this change? #### How? Technical details on your change ================================================ FILE: .github/workflows/label-ai-generated-prs.yml ================================================ # .github/workflows/label-ai-generated-prs.yml name: Label AI-generated PRs on: pull_request: types: [opened, edited, synchronize] # run when the body changes too jobs: call-label-ai-prs: uses: intercom/github-action-workflows/.github/workflows/label-ai-prs.yml@main secrets: inherit ================================================ FILE: .gitignore ================================================ doc *.gem *.iml .bundle Gemfile.lock pkg/* .rakeTasks .yardoc spike.rb html .idea .ruby-version vendor/* gemfiles/*.lock ================================================ FILE: Gemfile ================================================ source "http://rubygems.org" gemspec ================================================ FILE: MIT-LICENSE ================================================ The MIT License Copyright (c) 2011- Intercom, Inc. (https://www.intercom.io) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # IntercomRails The easiest way to install Intercom in a rails app. For interacting with the Intercom REST API, use the `intercom` gem (https://github.com/intercom/intercom-ruby) Requires Ruby 2.0 or higher. ## Installation Add this to your Gemfile: ```ruby gem "intercom-rails" ``` Then run: ``` bundle install ``` Take note of your `app_id` from [here](https://app.intercom.com/a/apps/_/settings/web) and generate a config file: ``` rails generate intercom:config YOUR-APP-ID ``` To make installing Intercom easy, where possible a `\n") end it 'includes custom data' do get :with_user_instance_variable_and_custom_data expect(response.body).to include("testing_stuff") end it 'finds user from current_user method' do get :with_current_user_method expect(response.body).to include('\n") end context 'content security policy support' do before do require 'auto_include_filter_spec_csp_helper' end it 'injects nonce if csp_nonce_hook is defined' do IntercomRails.config.api_secret = 'abcd' get :with_current_user_method expect(response.body).to include('nonce="aaaa"') end end end ================================================ FILE: spec/auto_include_filter_spec_csp_helper.rb ================================================ module CoreExtensions module IntercomRails module AutoInclude def self.csp_nonce_hook(controller) 'aaaa' end end end end ================================================ FILE: spec/config_spec.rb ================================================ require 'active_support/time' require 'spec_helper' describe IntercomRails do it 'gets/sets app_id' do IntercomRails.config.app_id = "1234" expect(IntercomRails.config.app_id).to eq("1234") end it 'gets/sets session_duration' do IntercomRails.config.session_duration = 60000 expect(IntercomRails.config.session_duration).to eq(60000) end it 'gets/sets current_user' do current_user = Proc.new { @blah } IntercomRails.config.user.current = current_user expect(IntercomRails.config.user.current).to eq(current_user) end it 'gets/sets several current_user' do current_user = [ Proc.new { @blah }, Proc.new { @bloh }, Proc.new{ @bleh } ] IntercomRails.config.user.current = current_user expect(IntercomRails.config.user.current).to eq(current_user) end it 'gets/sets custom_data' do custom_data_config = { 'foo' => Proc.new {}, 'bar' => :method_name } IntercomRails.config.user.custom_data = custom_data_config expect(IntercomRails.config.user.custom_data).to eq(custom_data_config) end it 'gets/sets company custom_data' do custom_data_config = { 'the_local' => Proc.new { 'club 93' } } IntercomRails.config.company.custom_data = custom_data_config expect(IntercomRails.config.company.custom_data).to eq(custom_data_config) end it 'gets/sets inbox style' do IntercomRails.config.inbox.style = :custom expect(IntercomRails.config.inbox.style).to eq(:custom) end it 'gets/sets custom_activator' do IntercomRails.config.inbox.custom_activator = '.intercom' expect(IntercomRails.config.inbox.custom_activator).to eq('.intercom') end it 'gets/sets hide_default_launcher' do IntercomRails.config.hide_default_launcher = true expect(IntercomRails.config.hide_default_launcher).to eq(true) end it 'gets/sets api_base' do IntercomRails.config.api_base = "https://abcde1.intercom-messenger.com" expect(IntercomRails.config.api_base).to eq("https://abcde1.intercom-messenger.com") end it 'gets/sets Encrypted Mode' do IntercomRails.config.encrypted_mode = true expect(IntercomRails.config.encrypted_mode).to eq(true) end it 'raises error if current user not a proc' do expect { IntercomRails.config.user.current = 1 }.to raise_error(ArgumentError) end it 'allows config in block form' do IntercomRails.config do |config| config.app_id = "4567" end expect(IntercomRails.config.app_id).to eq("4567") end it 'rejects non proc/symbol attributes' do expect { IntercomRails.config.user.custom_data = {'bar' => 'heyheyhey!'} }.to raise_error(ArgumentError) do |error| expect(error.message).to eq("all custom_data attributes should be either a Proc or a symbol") end end it 'setting custom data with proc' do custom_data = { 'foo' => 'bar', 'bar' => 'baz' } custom_data_config = Proc.new { custom_data } IntercomRails.config.user.custom_data = custom_data_config expect(custom_data).to eql(IntercomRails.config.user.custom_data.call) end it 'can be reset!' do IntercomRails.config.inbox.style = :custom IntercomRails.config.user.custom_data = {'muffin' => :muffin} IntercomRails.config.reset! expect(IntercomRails.config.user.custom_data).to eq(nil) expect(IntercomRails.config.inbox.style).to eq(nil) end it 'prints a deprecation warning when #api_key= is used' do expect do IntercomRails.config.api_key = "foo" end.to output(/no longer supported/).to_stderr end it 'prints a deprecation warning when user.company_association= is used' do expect do IntercomRails.config.user.company_association = Proc.new { [] } end.to output(/no longer supported/).to_stderr end context 'jwt configuration' do it 'gets/sets jwt_enabled' do IntercomRails.config.jwt.enabled = true expect(IntercomRails.config.jwt.enabled).to eq(true) end it 'defaults jwt_enabled to nil' do IntercomRails.config.reset! expect(IntercomRails.config.jwt.enabled).to eq(nil) end it 'allows jwt_enabled in block form' do IntercomRails.config do |config| config.jwt.enabled = true end expect(IntercomRails.config.jwt.enabled).to eq(true) end\ it 'gets/sets signed_user_fields' do IntercomRails.config.jwt.signed_user_fields = [:email, :name] expect(IntercomRails.config.jwt.signed_user_fields).to eq([:email, :name]) end it 'validates signed_user_fields is an array of symbols or strings' do expect { IntercomRails.config.jwt.signed_user_fields = "not_an_array" }.to raise_error(ArgumentError) expect { IntercomRails.config.jwt.signed_user_fields = [1, 2, 3] }.to raise_error(ArgumentError) expect { IntercomRails.config.jwt.signed_user_fields = [:email, "name", :custom_field] }.not_to raise_error end it 'allows nil signed_user_fields' do expect { IntercomRails.config.jwt.signed_user_fields = nil }.not_to raise_error end end end ================================================ FILE: spec/encrypted_mode_spec.rb ================================================ require 'spec_helper' describe IntercomRails::EncryptedMode do it 'whitelists certain attributes' do encrypted_mode = IntercomRails::EncryptedMode.new("foo", nil, {:enabled => true}) expect(encrypted_mode.plaintext_part({:app_id => "bar", :baz => "bang"})).to eq({:app_id => "bar"}) end it "encrypts correctly" do encrypted_mode = IntercomRails::EncryptedMode.new("foo", "a"*12, {:enabled => true}) encrypted = encrypted_mode.encrypt({"baz" => "bang"}) decoded = Base64.decode64(encrypted) cipher = OpenSSL::Cipher.new('aes-256-gcm') cipher.decrypt cipher.key = Digest::SHA256.digest("foo") cipher.iv = decoded[0, 12] auth_tag_index = decoded.length - 16 cipher.auth_tag = decoded[auth_tag_index, 16] ciphertext = decoded[12, decoded.length - 16 - 12] result = cipher.update(ciphertext) + cipher.final original = JSON.parse(result) expect(original).to eq({"baz" => "bang"}) end end ================================================ FILE: spec/proxy/company_spec.rb ================================================ require 'spec_helper' describe IntercomRails::Proxy::Company do ProxyCompany = IntercomRails::Proxy::Company DUMMY_COMPANY = dummy_company it 'finds current company' do IntercomRails.config.company.current = Proc.new { @app } object_with_app_instance_var = Object.new object_with_app_instance_var.instance_variable_set(:@app, DUMMY_COMPANY) c = ProxyCompany.current_in_context(object_with_app_instance_var) expect(c.valid?).to eq(true) expected_hash = {:id => '6', :name => 'Intercom'} expect(c.to_hash).to eq(expected_hash) end it 'is invalid if whiny nil' do NilClass.class_eval do def id raise ArgumentError, "boo" end end search_object = nil expect(ProxyCompany.new(search_object).valid?).to eq(false) end end ================================================ FILE: spec/proxy/user_spec.rb ================================================ require 'spec_helper' describe IntercomRails::Proxy::User do ProxyUser = IntercomRails::Proxy::User DUMMY_USER = dummy_user(:email => 'ciaran@intercom.io', :name => 'Ciaran Lee') it 'raises error if no user found' do expect { ProxyUser.current_in_context(Object.new) }.to raise_error(IntercomRails::NoUserFoundError) end it 'finds current_user' do object_with_current_user_method = Object.new object_with_current_user_method.instance_eval do def current_user DUMMY_USER end end @user_proxy = ProxyUser.current_in_context(object_with_current_user_method) expect(@user_proxy.user).to eq(DUMMY_USER) end it 'finds user instance variable' do object_with_instance_variable = Object.new object_with_instance_variable.instance_eval do @user = DUMMY_USER end @user_proxy = ProxyUser.current_in_context(object_with_instance_variable) expect(@user_proxy.user).to eq(DUMMY_USER) end it 'finds config user' do object_from_config = Object.new object_from_config.instance_eval do def something_esoteric DUMMY_USER end end IntercomRails.config.user.current = Proc.new { something_esoteric } @user_proxy = ProxyUser.current_in_context(object_from_config) expect(@user_proxy.user).to eq(DUMMY_USER) end it 'raises error if config.user.current is set but does not resolve' do IntercomRails.config.user.current = Proc.new { something_esoteric } object_with_instance_variable = Object.new object_with_instance_variable.instance_eval do @user = DUMMY_USER end expect { ProxyUser.current_in_context(object_with_instance_variable) }.to raise_error(IntercomRails::NoUserFoundError) end it 'includes custom_data' do plan_dummy_user = DUMMY_USER.dup plan_dummy_user.instance_eval do def plan 'pro' end end IntercomRails.config.user.custom_data = { 'plan' => :plan } @user_proxy = ProxyUser.new(plan_dummy_user) expect(@user_proxy.to_hash['plan']).to eql('pro') end it 'can work with proc custom data' do plan_dummy_user = DUMMY_USER.dup plan_dummy_user.instance_eval do def custom_data { 'plan' => 'pro', 'registered_at' => Time.at(5), } end end IntercomRails.config.user.custom_data = Proc.new { |u| u.custom_data } @user_proxy = ProxyUser.new(plan_dummy_user) expect(@user_proxy.to_hash['plan']).to eql('pro') end it 'test can work with symbol custom data' do plan_dummy_user = DUMMY_USER.dup plan_dummy_user.instance_eval do def custom_data { 'plan' => 'pro', 'registered_at' => Time.at(5), } end end IntercomRails.config.user.custom_data = :custom_data @user_proxy = ProxyUser.new(plan_dummy_user) expect(@user_proxy.to_hash['plan']).to eql('pro') end it 'converts dates to timestamps' do plan_dummy_user = DUMMY_USER.dup plan_dummy_user.instance_eval do def some_date Time.at(5) end end IntercomRails.config.user.custom_data = { 'some_date' => :some_date } @user_proxy = ProxyUser.new(plan_dummy_user) expect(@user_proxy.to_hash['some_date']).to eq(5) end it 'is considered valid if user_id or email' do expect(ProxyUser.new(DUMMY_USER).valid?).to be(true) end it 'works with hashes' do user = { email: 'hash@foo.com' } expect(ProxyUser.new(user).valid?).to be(true) end it 'considers new records to be invalid' do new_record_user = dummy_user(:email => 'not-saved@intercom.io', :name => 'New Record') def new_record_user.new_record? true end expect(ProxyUser.new(new_record_user).valid?).to be(false) end it 'includes custom data from intercom custom data' do object_with_intercom_custom_data = Object.new object_with_intercom_custom_data.instance_eval do def intercom_custom_data Object.new.tap do |o| o.instance_eval do def user {:ponies => :rainbows} end end end end end @user_proxy = ProxyUser.new(DUMMY_USER, object_with_intercom_custom_data) expect(@user_proxy.to_hash[:ponies]).to eql(:rainbows) end it 'is invalid if whiny nil' do NilClass.class_eval do def id raise ArgumentError, "boo" end end search_object = nil expect(ProxyUser.new(search_object).valid?).to eq(false) end it 'gets/sets lead_attributes' do IntercomRails.config.user.lead_attributes = %w(utm_source ref_data) expect(IntercomRails.config.user.lead_attributes).to eq(["utm_source", "ref_data"]) end it 'gets/sets lead_attributes with valid types' do expect { IntercomRails.config.user.lead_attributes = "utm_source" }.to raise_error(ArgumentError) end end ================================================ FILE: spec/script_tag_helper_spec.rb ================================================ require 'active_support/time' require 'spec_helper' describe IntercomRails::ScriptTagHelper do include IntercomRails::ScriptTagHelper it 'delegates to script tag ' do expect(IntercomRails::ScriptTag).to receive(:new) intercom_script_tag({}) end it 'does not use dummy data if app_id is set in development' do allow(Rails).to receive(:development?).and_return true output = intercom_script_tag({app_id: 'thisismyappid', email:'foo'}).to_s expect(output).to include("/widget/thisismyappid") end it 'sets instance variable to record that it was called' do fake_action_view = fake_action_view_class.new obj = Object.new fake_action_view.instance_variable_set(:@controller, obj) fake_action_view.intercom_script_tag({}) expect(obj.instance_variable_get(IntercomRails::SCRIPT_TAG_HELPER_CALLED_INSTANCE_VARIABLE)).to eq(true) end context 'content security policy support' do it 'returns a valid sha256 hash for the CSP header' do # # See also spec/script_tag_spec.rb # script_tag = intercom_script_tag({ :app_id => 'csp_sha_test', :email => 'marco@intercom.io', :user_id => 'marco', }) expect(script_tag.csp_sha256).to eq("'sha256-/0mStQPBID1jSuXAoW0YtDqu8JmWUJJ5SdBB2u7Fy90='") end it 'inserts a valid nonce if present' do script_tag = intercom_script_tag({ :app_id => 'csp_sha_test', :email => 'marco@intercom.io', :user_id => 'marco', }, { :nonce => 'pJwtLVnwiMaPCxpb41KZguOcC5mGUYD+8RNGcJSlR94=', }) expect(script_tag.to_s).to include('nonce="pJwtLVnwiMaPCxpb41KZguOcC5mGUYD+8RNGcJSlR94="') end end context 'JWT authentication' do before(:each) do allow(Rails).to receive(:env).and_return(ActiveSupport::StringInquirer.new("test")) end before(:each) do IntercomRails.config.api_secret = 'super-secret' end it 'enables JWT when configured' do IntercomRails.config.jwt.enabled = true output = intercom_script_tag({ user_id: '1234', email: 'test@example.com' }).to_s expect(output).to include('intercom_user_jwt') expect(output).not_to include('user_hash') end it 'falls back to user_hash when JWT is disabled' do IntercomRails.config.jwt.enabled = false output = intercom_script_tag({ user_id: '1234', email: 'test@example.com' }).to_s expect(output).not_to include('intercom_user_jwt') expect(output).to include('user_hash') end end end ================================================ FILE: spec/script_tag_spec.rb ================================================ require 'active_support/time' require 'spec_helper' require 'jwt' describe IntercomRails::ScriptTag do ScriptTag = IntercomRails::ScriptTag before(:each) do IntercomRails.config.app_id = 'script_tag_test' end it 'should output html_safe?' do expect(ScriptTag.new({}).to_s.html_safe?).to be(true) end it 'should convert times to unix timestamps' do time = Time.new(1993, 02, 13) top_level_time = ScriptTag.new(:user_details => {:created_at => time}) expect(top_level_time.intercom_settings[:created_at]).to eq(time.to_i) now = Time.now nested_time = ScriptTag.new(:user_details => {:custom_data => {"something" => now}}) expect(nested_time.intercom_settings[:custom_data]["something"]).to eq(now.to_i) utc_time = Time.utc(2013, 04, 03) time_zone = ActiveSupport::TimeZone.new('London') time_with_zone = ActiveSupport::TimeWithZone.new(utc_time, time_zone) time_from_time_with_zone = ScriptTag.new(:user_details => {:created_at => time_with_zone}) expect(time_from_time_with_zone.intercom_settings[:created_at]).to eq(utc_time.to_i) end context 'session' do before do IntercomRails.config.session_duration = 60000 end it 'displays session_duration' do script_tag = ScriptTag.new() expect(script_tag.intercom_settings[:session_duration]).to eq(60000) end end context 'integration type' do it 'should be rails' do script_tag = ScriptTag.new() expect(script_tag.intercom_settings[:installation_type]).to eq('rails') end end it 'strips out nil entries for standard attributes' do %w(name email user_id).each do |standard_attribute| with_value = ScriptTag.new(:user_details => {standard_attribute => 'value'}) expect(with_value.intercom_settings[standard_attribute]).to eq('value') with_nil_value = ScriptTag.new(:user_details => {standard_attribute.to_sym => nil}) expect(with_nil_value.intercom_settings.has_key?(standard_attribute.to_sym)).to be(false) with_nil_value = ScriptTag.new(:user_details => {standard_attribute => nil}) expect(with_nil_value.intercom_settings.has_key?(standard_attribute.to_sym)).to be(false) end end it 'should escape html attributes' do nasty_email = "" script_tag = ScriptTag.new(:user_details => {:email => nasty_email}) expect(script_tag.to_s).not_to include(nasty_email) end it 'should escape html attributes in app_id' do email = "bob@foo.com" before = IntercomRails.config.app_id nasty_app_id = "" IntercomRails.config.app_id = nasty_app_id script_tag = ScriptTag.new(:user_details => {:email => email}) expect(script_tag.to_s).not_to include(nasty_app_id) IntercomRails.config.app_id = before end context 'Encrypted Mode' do it 'sets an encrypted payload' do iv = Base64.decode64("2X0G4PoOBn9+wdf8") script_tag = ScriptTag.new(:user_details => {:email => 'ciaran@intercom.io'}, :secret => 'abcdefgh', :encrypted_mode => true, :initialization_vector => iv) result = script_tag.to_s expect(result).to_not include("ciaran@intercom.io") expect(result).to match(/window\.intercomEncryptedPayload = \"[^\"\n]+\"/) end it "#plaintext_settings" do script_tag = ScriptTag.new(:user_details => {:email => 'ciaran@intercom.io'}, :secret => 'abcdefgh', :encrypted_mode => true) expect(script_tag.plaintext_settings).to_not include(:email) script_tag = ScriptTag.new(:user_details => {:email => 'ciaran@intercom.io'}, :secret => 'abcdefgh', :encrypted_mode => false) expect(script_tag.plaintext_settings).to include(:email) end it "#encrypted_settings" do script_tag = ScriptTag.new(:user_details => {:email => 'ciaran@intercom.io'}, :secret => 'abcdefgh', :encrypted_mode => true) expect(script_tag.encrypted_settings).to match(/[^\"\n]+/) end end context 'Identity Verification - user_hash' do it 'computes user_hash using email when email present, and user_id blank' do script_tag = ScriptTag.new(:user_details => {:email => 'ciaran@intercom.io'}, :secret => 'abcdefgh') expect(script_tag.intercom_settings[:user_hash]).to eq(sha256_hmac('abcdefgh', 'ciaran@intercom.io')) script_tag = ScriptTag.new(:user_details => {:email => 'ciaran@intercom.io', :user_id => nil}, :secret => 'abcdefgh') expect(script_tag.intercom_settings[:user_hash]).to eq(sha256_hmac('abcdefgh', 'ciaran@intercom.io')) # script_tag = ScriptTag.new(:user_details => {:email => 'ciaran@intercom.io', :user_id => ''}, :secret => 'abcdefgh') # expect(script_tag.intercom_settings[:user_hash]).to eq(sha256_hmac('abcdefgh', 'ciaran@intercom.io')) # todo - test what server behavior is... end it 'computes user_hash using user_id when user_id present' do script_tag = ScriptTag.new(:user_details => {:user_id => '1234'}, :secret => 'abcdefgh') expect(script_tag.intercom_settings[:user_hash]).to eq(sha256_hmac('abcdefgh', '1234')) script_tag = ScriptTag.new(:user_details => {:user_id => '1234', :email => nil}, :secret => 'abcdefgh') expect(script_tag.intercom_settings[:user_hash]).to eq(sha256_hmac('abcdefgh', '1234')) script_tag = ScriptTag.new(:user_details => {:user_id => '1234', :email => ''}, :secret => 'abcdefgh') expect(script_tag.intercom_settings[:user_hash]).to eq(sha256_hmac('abcdefgh', '1234')) end it 'computes user_hash using user_id when both present' do script_tag = ScriptTag.new(:user_details => {:user_id => '1234', :email => 'ciaran@intercom.io'}, :secret => 'abcdefgh') expect(script_tag.intercom_settings[:user_hash]).to eq(sha256_hmac('abcdefgh', '1234')) end it 'emits user_hash when api_secret set on config' do IntercomRails.config.api_secret = 'abcdefgh' script_tag = ScriptTag.new(:user_details => {:email => 'ben@intercom.io'}) expect(script_tag.intercom_settings[:user_hash]).to eq(sha256_hmac('abcdefgh', 'ben@intercom.io')) end it 'favors passed seret over config api_secret' do IntercomRails.config.api_secret = 'abcd' script_tag = ScriptTag.new(:user_details => {:email => 'ben@intercom.io'}, :secret => '1234') expect(script_tag.intercom_settings[:user_hash]).to eq(sha256_hmac('1234', 'ben@intercom.io')) script_tag = ScriptTag.new(:user_details => {:user_id => 5678}, :secret => '1234') expect(script_tag.intercom_settings[:user_hash]).to eq(sha256_hmac('1234', '5678')) end def sha256_hmac(secret, input) OpenSSL::HMAC.hexdigest("sha256", secret, input) end end context 'inbox style' do it 'knows about :default' do IntercomRails.config.inbox.style = :default expect(ScriptTag.new.intercom_settings['widget']).to eq({'activator' => '#IntercomDefaultWidget'}) end it 'knows about :custom' do IntercomRails.config.inbox.style = :custom expect(ScriptTag.new.intercom_settings['widget']).to eq({'activator' => '#Intercom'}) end it 'knows about :custom_activator' do IntercomRails.config.inbox.style = :custom IntercomRails.config.inbox.custom_activator = '.intercom' expect(ScriptTag.new.intercom_settings['widget']).to eq({'activator' => '.intercom'}) end it 'knows about :hide_default_launcher' do IntercomRails.config.hide_default_launcher = true expect(ScriptTag.new.intercom_settings['hide_default_launcher']).to eq(true) end it 'knows about :api_base' do IntercomRails.config.api_base = "https://abcde1.intercom-messenger.com" expect(ScriptTag.new.intercom_settings['api_base']).to eq("https://abcde1.intercom-messenger.com") end end context 'company' do let(:company) { dummy_company } it 'discovers and includes company' do IntercomRails.config.company.current = Proc.new { @app } controller_with_app_variable = Object.new controller_with_app_variable.instance_eval do @app = dummy_company end script_tag = ScriptTag.new(:controller => controller_with_app_variable, :find_current_company_details => true) expect(script_tag.intercom_settings[:company]).to eq({'id' => '6', 'name' => 'Intercom'}) end end context 'without user details' do it 'should be valid when show_everywhere is set' do script_tag = ScriptTag.new(:show_everywhere => true) expect(script_tag.valid?).to eq(true) end it 'should not be valid when show_everywhere is not set' do script_tag = ScriptTag.new() expect(script_tag.valid?).to eq(false) end end context 'content security policy support' do it 'returns a valid sha256 hash for the CSP header' do # # If default values change, re-generate the string below using this one # liner: # echo "sha256-$(echo -n "js code" | openssl dgst -sha256 -binary | openssl base64)" # or an online service like https://report-uri.io/home/hash/ # # For instance: # echo "sha256-$(echo -n "alert('hello');" | openssl dgst -sha256 -binary | openssl base64)" # sha256-gj4FLpwFgWrJxA7NLcFCWSwEF/PMnmWidszB6OONAAo= # script_tag = ScriptTag.new(:user_details => { :app_id => 'csp_sha_test', :email => 'marco@intercom.io', :user_id => 'marco', }) expect(script_tag.csp_sha256).to eq("'sha256-/0mStQPBID1jSuXAoW0YtDqu8JmWUJJ5SdBB2u7Fy90='") end it 'inserts a valid nonce if present' do script_tag = ScriptTag.new(:user_details => { :app_id => 'csp_sha_test', :email => 'marco@intercom.io', :user_id => 'marco', }, :nonce => 'pJwtLVnwiMaPCxpb41KZguOcC5mGUYD+8RNGcJSlR94=') expect(script_tag.to_s).to include('nonce="pJwtLVnwiMaPCxpb41KZguOcC5mGUYD+8RNGcJSlR94="') end it 'does not insert a nasty nonce if present' do script_tag = ScriptTag.new(:user_details => { :app_id => 'csp_sha_test', :email => 'marco@intercom.io', :user_id => 'marco', }, :nonce => '>alert(1)