diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2024-01-10 23:27:55 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2024-01-10 23:27:55 +0300 |
commit | 8e6173652867fad53710575c794e50dba6819cb7 (patch) | |
tree | b652a63603188f2d380c567080d57845539bd042 /spec/models | |
parent | a2421d9b73f934f82f1eea995b2933d6eec37135 (diff) |
Add latest changes from gitlab-org/security/gitlab@16-6-stable-ee
Diffstat (limited to 'spec/models')
-rw-r--r-- | spec/models/concerns/recoverable_by_any_email_spec.rb | 133 | ||||
-rw-r--r-- | spec/models/integrations/mattermost_slash_commands_spec.rb | 34 | ||||
-rw-r--r-- | spec/models/integrations/slack_slash_commands_spec.rb | 23 | ||||
-rw-r--r-- | spec/models/merge_request_spec.rb | 30 |
4 files changed, 190 insertions, 30 deletions
diff --git a/spec/models/concerns/recoverable_by_any_email_spec.rb b/spec/models/concerns/recoverable_by_any_email_spec.rb index c17507cae83..ba0bb99effb 100644 --- a/spec/models/concerns/recoverable_by_any_email_spec.rb +++ b/spec/models/concerns/recoverable_by_any_email_spec.rb @@ -4,67 +4,140 @@ require 'spec_helper' RSpec.describe RecoverableByAnyEmail, feature_category: :system_access do describe '.send_reset_password_instructions' do - let_it_be(:user) { create(:user, email: 'test@example.com') } - let_it_be(:verified_email) { create(:email, :confirmed, user: user) } - let_it_be(:unverified_email) { create(:email, user: user) } + include EmailHelpers subject(:send_reset_password_instructions) do User.send_reset_password_instructions(email: email) end - shared_examples 'sends the password reset email' do + let_it_be(:user) { create(:user) } + let_it_be(:user_confirmed_primary_email) { user.email } + + let_it_be(:user_confirmed_secondary_email) do + create(:email, :confirmed, user: user, email: 'confirmed-secondary-email@example.com').email + end + + let_it_be(:user_unconfirmed_secondary_email) do + create(:email, user: user, email: 'unconfirmed-secondary-email@example.com').email + end + + let_it_be(:unknown_email) { 'attacker@example.com' } + let_it_be(:invalid_email) { 'invalid_email' } + let_it_be(:sql_injection_email) { 'sql-injection-email@example.com OR 1=1' } + + let_it_be(:another_user_confirmed_primary_email) { create(:user).email } + + let_it_be(:another_user) { create(:user, :unconfirmed) } + let_it_be(:another_user_unconfirmed_primary_email) { another_user.email } + + shared_examples "sends 'Reset password instructions' email" do it 'finds the user' do - expect(send_reset_password_instructions).to eq(user) + expect(send_reset_password_instructions).to eq(expected_user) end it 'sends the email' do + reset_delivered_emails! + expect { send_reset_password_instructions }.to have_enqueued_mail(DeviseMailer, :reset_password_instructions) + + perform_enqueued_jobs + + expect_only_one_email_to_be_sent(subject: 'Reset password instructions', to: [email]) end end - shared_examples 'does not send the password reset email' do + shared_examples "does not send 'Reset password instructions' email" do + # If user is not found, returns a new user with errors. + # See https://github.com/heartcombo/devise/blob/main/lib/devise/models/recoverable.rb it 'does not find the user' do - expect(subject.id).to be_nil - expect(subject.errors).not_to be_empty + expect(send_reset_password_instructions).to be_instance_of User + expect(send_reset_password_instructions).to be_new_record + expect(send_reset_password_instructions.errors).not_to be_empty end - it 'does not send any email' do - subject + it 'does not send email to anyone' do + reset_delivered_emails! + + expect { send_reset_password_instructions } + .not_to have_enqueued_mail(DeviseMailer, :reset_password_instructions) + + perform_enqueued_jobs - expect { subject }.not_to have_enqueued_mail(DeviseMailer, :reset_password_instructions) + should_not_email_anyone end end - context 'with user primary email' do - let(:email) { user.email } + context "when email param matches user's confirmed primary email" do + let(:expected_user) { user } + let(:email) { user_confirmed_primary_email } - it_behaves_like 'sends the password reset email' + it_behaves_like "sends 'Reset password instructions' email" end - context 'with user verified email' do - let(:email) { verified_email.email } + context "when email param matches user's unconfirmed primary email" do + let(:expected_user) { another_user } + let(:email) { another_user_unconfirmed_primary_email } - it_behaves_like 'sends the password reset email' + it_behaves_like "sends 'Reset password instructions' email" end - context 'with user unverified email' do - let(:email) { unverified_email.email } + context "when email param matches user's confirmed secondary email" do + let(:expected_user) { user } + let(:email) { user_confirmed_secondary_email } - it_behaves_like 'does not send the password reset email' + it_behaves_like "sends 'Reset password instructions' email" end - context 'with one email matching user and one not matching' do - let(:email) { [verified_email.email, 'other_email@example.com'] } + context "when email param matches user's unconfirmed secondary email" do + let(:email) { user_unconfirmed_secondary_email } - it 'sends an email only to the user verified email' do - expect { send_reset_password_instructions } - .to have_enqueued_mail(DeviseMailer, :reset_password_instructions) - .with( - user, - anything, # reset token - to: user.verified_emails(include_private_email: false) - ) + it_behaves_like "does not send 'Reset password instructions' email" + end + + context 'when email param is unknown email' do + let(:email) { unknown_email } + + it_behaves_like "does not send 'Reset password instructions' email" + end + + context 'when email param is invalid email' do + let(:email) { invalid_email } + + it_behaves_like "does not send 'Reset password instructions' email" + end + + context 'when email param with attempt to cause SQL injection' do + let(:email) { sql_injection_email } + + it_behaves_like "does not send 'Reset password instructions' email" + end + + context 'when email param is nil' do + let(:email) { nil } + + it_behaves_like "does not send 'Reset password instructions' email" + end + + context 'when email param is empty string' do + let(:email) { '' } + + it_behaves_like "does not send 'Reset password instructions' email" + end + + # See https://gitlab.com/gitlab-org/gitlab/-/issues/436084 + context 'when email param with multiple emails' do + let(:email) do + [ + user_confirmed_primary_email, + user_confirmed_secondary_email, + user_unconfirmed_secondary_email, + unknown_email, + another_user_confirmed_primary_email, + another_user_unconfirmed_primary_email + ] end + + it_behaves_like "does not send 'Reset password instructions' email" end end end diff --git a/spec/models/integrations/mattermost_slash_commands_spec.rb b/spec/models/integrations/mattermost_slash_commands_spec.rb index 3dee8737067..43316e164ed 100644 --- a/spec/models/integrations/mattermost_slash_commands_spec.rb +++ b/spec/models/integrations/mattermost_slash_commands_spec.rb @@ -125,5 +125,39 @@ RSpec.describe Integrations::MattermostSlashCommands, feature_category: :integra end end end + + describe '#redirect_url' do + let(:url) { 'http://www.mattermost.com/hooks' } + + subject { integration.redirect_url('team', 'channel', url) } + + it { is_expected.to eq("http://www.mattermost.com/team/channels/channel") } + + context 'with invalid URL scheme' do + let(:url) { 'javascript://www.mattermost.com/hooks' } + + it { is_expected.to be_nil } + end + + context 'with unsafe URL' do + let(:url) { "https://replaceme.com/'><script>alert(document.cookie)</script>" } + + it { is_expected.to be_nil } + end + end + + describe '#confirmation_url' do + let(:params) do + { + team_domain: 'gitlab', + channel_name: 'test-channel', + response_url: 'http://mattermost.gitlab.com/hooks/commands/my123command' + } + end + + subject { integration.confirmation_url('command-id', params) } + + it { is_expected.to be_present } + end end end diff --git a/spec/models/integrations/slack_slash_commands_spec.rb b/spec/models/integrations/slack_slash_commands_spec.rb index f373fc2a2de..5d6f214a5d5 100644 --- a/spec/models/integrations/slack_slash_commands_spec.rb +++ b/spec/models/integrations/slack_slash_commands_spec.rb @@ -40,4 +40,27 @@ RSpec.describe Integrations::SlackSlashCommands, feature_category: :integrations end end end + + describe '#redirect_url' do + let(:integration) { build(:slack_slash_commands_integration) } + + subject { integration.redirect_url('team', 'channel', 'www.example.com') } + + it { is_expected.to eq('slack://channel?team=team&id=channel') } + end + + describe '#confirmation_url' do + let(:integration) { build(:slack_slash_commands_integration) } + let(:params) do + { + team_id: 'T123456', + channel_id: 'C654321', + response_url: 'https://hooks.slack.com/services/T123456/C654321' + } + end + + subject { integration.confirmation_url('command-id', params) } + + it { is_expected.to be_present } + end end diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index 1c6a29f065f..b826475621a 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -6138,4 +6138,34 @@ RSpec.describe MergeRequest, factory_default: :keep, feature_category: :code_rev it { is_expected.to eq(false) } end end + + describe '#previous_diff' do + let(:merge_request) { create(:merge_request, :skip_diff_creation) } + + subject { merge_request.previous_diff } + + context 'when there is are no merge_request_diffs' do + it { is_expected.to be_nil } + end + + context 'when there is one merge request_diff' do + let(:merge_request) { create(:merge_request) } + + it { is_expected.to be_nil } + end + + context 'when there are multiple merge_request_diffs' do + let(:oldest_merge_request_diff) { create(:merge_request_diff, merge_request: merge_request) } + let(:second_to_last_merge_request_diff) { create(:merge_request_diff, merge_request: merge_request) } + let(:most_recent_merge_request_diff) { create(:merge_request_diff, merge_request: merge_request) } + + before do + oldest_merge_request_diff + second_to_last_merge_request_diff + most_recent_merge_request_diff + end + + it { is_expected.to eq(second_to_last_merge_request_diff) } + end + end end |