diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-01-10 23:36:29 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-01-10 23:36:29 +0300 |
commit | 1eef146c2d1de19d4e995d421e5787053e50db80 (patch) | |
tree | 2761efabea712248557826977a849e31e3fdb961 /spec | |
parent | 661d663ab2b7c69977ba8a0db02ef4afc2427e39 (diff) |
Add latest changes from gitlab-org/security/gitlab@14-6-stable-ee
Diffstat (limited to 'spec')
32 files changed, 625 insertions, 358 deletions
diff --git a/spec/controllers/dashboard/projects_controller_spec.rb b/spec/controllers/dashboard/projects_controller_spec.rb index 9b13025cbe3..743759d5023 100644 --- a/spec/controllers/dashboard/projects_controller_spec.rb +++ b/spec/controllers/dashboard/projects_controller_spec.rb @@ -8,10 +8,6 @@ RSpec.describe Dashboard::ProjectsController, :aggregate_failures do let_it_be(:user) { create(:user) } describe '#index' do - context 'user not logged in' do - it_behaves_like 'authenticates sessionless user', :index, :atom - end - context 'user logged in' do let_it_be(:project) { create(:project, name: 'Project 1') } let_it_be(:project2) { create(:project, name: 'Project Two') } diff --git a/spec/controllers/dashboard_controller_spec.rb b/spec/controllers/dashboard_controller_spec.rb index c838affa239..8fae617ea65 100644 --- a/spec/controllers/dashboard_controller_spec.rb +++ b/spec/controllers/dashboard_controller_spec.rb @@ -72,9 +72,6 @@ RSpec.describe DashboardController do end end - it_behaves_like 'authenticates sessionless user', :issues, :atom, author_id: User.first - it_behaves_like 'authenticates sessionless user', :issues_calendar, :ics - describe "#check_filters_presence!" do let(:user) { create(:user) } diff --git a/spec/controllers/groups_controller_spec.rb b/spec/controllers/groups_controller_spec.rb index a7625e65603..62171528695 100644 --- a/spec/controllers/groups_controller_spec.rb +++ b/spec/controllers/groups_controller_spec.rb @@ -1209,26 +1209,6 @@ RSpec.describe GroupsController, factory_default: :keep do end end - context 'token authentication' do - it_behaves_like 'authenticates sessionless user', :show, :atom, public: true do - before do - default_params.merge!(id: group) - end - end - - it_behaves_like 'authenticates sessionless user', :issues, :atom, public: true do - before do - default_params.merge!(id: group, author_id: user.id) - end - end - - it_behaves_like 'authenticates sessionless user', :issues_calendar, :ics, public: true do - before do - default_params.merge!(id: group) - end - end - end - describe 'external authorization' do before do group.add_owner(user) diff --git a/spec/controllers/projects/commits_controller_spec.rb b/spec/controllers/projects/commits_controller_spec.rb index a8e71d73beb..fd840fafa61 100644 --- a/spec/controllers/projects/commits_controller_spec.rb +++ b/spec/controllers/projects/commits_controller_spec.rb @@ -162,27 +162,4 @@ RSpec.describe Projects::CommitsController do end end end - - context 'token authentication' do - context 'public project' do - it_behaves_like 'authenticates sessionless user', :show, :atom, { public: true, ignore_incrementing: true } do - before do - public_project = create(:project, :repository, :public) - - default_params.merge!(namespace_id: public_project.namespace, project_id: public_project, id: "master.atom") - end - end - end - - context 'private project' do - it_behaves_like 'authenticates sessionless user', :show, :atom, { public: false, ignore_incrementing: true } do - before do - private_project = create(:project, :repository, :private) - private_project.add_maintainer(user) - - default_params.merge!(namespace_id: private_project.namespace, project_id: private_project, id: "master.atom") - end - end - end - end end diff --git a/spec/controllers/projects/issues_controller_spec.rb b/spec/controllers/projects/issues_controller_spec.rb index 763c3e43e27..d91c1b0d29a 100644 --- a/spec/controllers/projects/issues_controller_spec.rb +++ b/spec/controllers/projects/issues_controller_spec.rb @@ -1948,40 +1948,4 @@ RSpec.describe Projects::IssuesController do end end end - - context 'private project with token authentication' do - let_it_be(:private_project) { create(:project, :private) } - - it_behaves_like 'authenticates sessionless user', :index, :atom, ignore_incrementing: true do - before do - default_params.merge!(project_id: private_project, namespace_id: private_project.namespace) - - private_project.add_maintainer(user) - end - end - - it_behaves_like 'authenticates sessionless user', :calendar, :ics, ignore_incrementing: true do - before do - default_params.merge!(project_id: private_project, namespace_id: private_project.namespace) - - private_project.add_maintainer(user) - end - end - end - - context 'public project with token authentication' do - let_it_be(:public_project) { create(:project, :public) } - - it_behaves_like 'authenticates sessionless user', :index, :atom, public: true do - before do - default_params.merge!(project_id: public_project, namespace_id: public_project.namespace) - end - end - - it_behaves_like 'authenticates sessionless user', :calendar, :ics, public: true do - before do - default_params.merge!(project_id: public_project, namespace_id: public_project.namespace) - end - end - end end diff --git a/spec/controllers/projects/raw_controller_spec.rb b/spec/controllers/projects/raw_controller_spec.rb index 79da18f2d6d..4d99afb6b1f 100644 --- a/spec/controllers/projects/raw_controller_spec.rb +++ b/spec/controllers/projects/raw_controller_spec.rb @@ -193,6 +193,8 @@ RSpec.describe Projects::RawController do let_it_be(:user) { create(:user, static_object_token: 'very-secure-token') } let_it_be(:file_path) { 'master/README.md' } + let(:token) { user.static_object_token } + before do project.add_developer(user) end @@ -207,17 +209,46 @@ RSpec.describe Projects::RawController do end context 'when a token param is present' do + subject(:execute_raw_request_with_token_in_params) do + execute_raw_requests(requests: 1, project: project, file_path: file_path, token: token) + end + context 'when token is correct' do it 'calls the action normally' do - execute_raw_requests(requests: 1, project: project, file_path: file_path, token: user.static_object_token) + execute_raw_request_with_token_in_params expect(response).to have_gitlab_http_status(:ok) end + + context 'when user with expired password' do + let_it_be(:user) { create(:user, password_expires_at: 2.minutes.ago) } + + it 'redirects to sign in page' do + execute_raw_request_with_token_in_params + + expect(response).to have_gitlab_http_status(:found) + expect(response.location).to end_with('/users/sign_in') + end + end + + context 'when password expiration is not applicable' do + context 'when ldap user' do + let_it_be(:user) { create(:omniauth_user, provider: 'ldap', password_expires_at: 2.minutes.ago) } + + it 'calls the action normally' do + execute_raw_request_with_token_in_params + + expect(response).to have_gitlab_http_status(:ok) + end + end + end end context 'when token is incorrect' do + let(:token) { 'foobar' } + it 'redirects to sign in page' do - execute_raw_requests(requests: 1, project: project, file_path: file_path, token: 'foobar') + execute_raw_request_with_token_in_params expect(response).to have_gitlab_http_status(:found) expect(response.location).to end_with('/users/sign_in') @@ -226,19 +257,47 @@ RSpec.describe Projects::RawController do end context 'when a token header is present' do + subject(:execute_raw_request_with_token_in_headers) do + request.headers['X-Gitlab-Static-Object-Token'] = token + execute_raw_requests(requests: 1, project: project, file_path: file_path) + end + context 'when token is correct' do it 'calls the action normally' do - request.headers['X-Gitlab-Static-Object-Token'] = user.static_object_token - execute_raw_requests(requests: 1, project: project, file_path: file_path) + execute_raw_request_with_token_in_headers expect(response).to have_gitlab_http_status(:ok) end + + context 'when user with expired password' do + let_it_be(:user) { create(:user, password_expires_at: 2.minutes.ago) } + + it 'redirects to sign in page' do + execute_raw_request_with_token_in_headers + + expect(response).to have_gitlab_http_status(:found) + expect(response.location).to end_with('/users/sign_in') + end + end + + context 'when password expiration is not applicable' do + context 'when ldap user' do + let_it_be(:user) { create(:omniauth_user, provider: 'ldap', password_expires_at: 2.minutes.ago) } + + it 'calls the action normally' do + execute_raw_request_with_token_in_headers + + expect(response).to have_gitlab_http_status(:ok) + end + end + end end context 'when token is incorrect' do + let(:token) { 'foobar' } + it 'redirects to sign in page' do - request.headers['X-Gitlab-Static-Object-Token'] = 'foobar' - execute_raw_requests(requests: 1, project: project, file_path: file_path) + execute_raw_request_with_token_in_headers expect(response).to have_gitlab_http_status(:found) expect(response.location).to end_with('/users/sign_in') diff --git a/spec/controllers/projects/repositories_controller_spec.rb b/spec/controllers/projects/repositories_controller_spec.rb index b7eef3812a4..f7cf55d8a95 100644 --- a/spec/controllers/projects/repositories_controller_spec.rb +++ b/spec/controllers/projects/repositories_controller_spec.rb @@ -178,6 +178,29 @@ RSpec.describe Projects::RepositoriesController do expect(response).to have_gitlab_http_status(:ok) end + + context 'when user with expired password' do + let_it_be(:user) { create(:user, password_expires_at: 2.minutes.ago) } + + it 'redirects to sign in page' do + get :archive, params: { namespace_id: project.namespace, project_id: project, id: 'master', token: user.static_object_token }, format: 'zip' + + expect(response).to have_gitlab_http_status(:found) + expect(response.location).to end_with('/users/sign_in') + end + end + + context 'when password expiration is not applicable' do + context 'when ldap user' do + let_it_be(:user) { create(:omniauth_user, provider: 'ldap', password_expires_at: 2.minutes.ago) } + + it 'calls the action normally' do + get :archive, params: { namespace_id: project.namespace, project_id: project, id: 'master', token: user.static_object_token }, format: 'zip' + + expect(response).to have_gitlab_http_status(:ok) + end + end + end end context 'when token is incorrect' do @@ -197,6 +220,31 @@ RSpec.describe Projects::RepositoriesController do expect(response).to have_gitlab_http_status(:ok) end + + context 'when user with expired password' do + let_it_be(:user) { create(:user, password_expires_at: 2.minutes.ago) } + + it 'redirects to sign in page' do + request.headers['X-Gitlab-Static-Object-Token'] = user.static_object_token + get :archive, params: { namespace_id: project.namespace, project_id: project, id: 'master' }, format: 'zip' + + expect(response).to have_gitlab_http_status(:found) + expect(response.location).to end_with('/users/sign_in') + end + end + + context 'when password expiration is not applicable' do + context 'when ldap user' do + let_it_be(:user) { create(:omniauth_user, provider: 'ldap', password_expires_at: 2.minutes.ago) } + + it 'calls the action normally' do + request.headers['X-Gitlab-Static-Object-Token'] = user.static_object_token + get :archive, params: { namespace_id: project.namespace, project_id: project, id: 'master' }, format: 'zip' + + expect(response).to have_gitlab_http_status(:ok) + end + end + end end context 'when token is incorrect' do diff --git a/spec/controllers/projects/tags_controller_spec.rb b/spec/controllers/projects/tags_controller_spec.rb index 0045c0a484b..9823c36cb86 100644 --- a/spec/controllers/projects/tags_controller_spec.rb +++ b/spec/controllers/projects/tags_controller_spec.rb @@ -117,28 +117,6 @@ RSpec.describe Projects::TagsController do end end - context 'private project with token authentication' do - let(:private_project) { create(:project, :repository, :private) } - - it_behaves_like 'authenticates sessionless user', :index, :atom, ignore_incrementing: true do - before do - default_params.merge!(project_id: private_project, namespace_id: private_project.namespace) - - private_project.add_maintainer(user) - end - end - end - - context 'public project with token authentication' do - let(:public_project) { create(:project, :repository, :public) } - - it_behaves_like 'authenticates sessionless user', :index, :atom, public: true do - before do - default_params.merge!(project_id: public_project, namespace_id: public_project.namespace) - end - end - end - describe 'POST #create' do before do project.add_developer(user) diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb index fd0f9985392..7ebd86640ad 100644 --- a/spec/controllers/projects_controller_spec.rb +++ b/spec/controllers/projects_controller_spec.rb @@ -1568,28 +1568,6 @@ RSpec.describe ProjectsController do end end - context 'private project with token authentication' do - let_it_be(:private_project) { create(:project, :private) } - - it_behaves_like 'authenticates sessionless user', :show, :atom, ignore_incrementing: true do - before do - default_params.merge!(id: private_project, namespace_id: private_project.namespace) - - private_project.add_maintainer(user) - end - end - end - - context 'public project with token authentication' do - let_it_be(:public_project) { create(:project, :public) } - - it_behaves_like 'authenticates sessionless user', :show, :atom, public: true do - before do - default_params.merge!(id: public_project, namespace_id: public_project.namespace) - end - end - end - context 'GET show.atom' do let_it_be(:public_project) { create(:project, :public) } let_it_be(:event) { create(:event, :commented, project: public_project, target: create(:note, project: public_project)) } diff --git a/spec/frontend/behaviors/gl_emoji_spec.js b/spec/frontend/behaviors/gl_emoji_spec.js index 0f4e2e08dbd..cac1ea67cf5 100644 --- a/spec/frontend/behaviors/gl_emoji_spec.js +++ b/spec/frontend/behaviors/gl_emoji_spec.js @@ -97,6 +97,18 @@ describe('gl_emoji', () => { }); }); + it('escapes gl-emoji name', async () => { + const glEmojiElement = markupToDomElement( + "<gl-emoji data-name='"x="y" onload="alert(document.location.href)"' data-unicode-version='x'>abc</gl-emoji>", + ); + + await waitForPromises(); + + expect(glEmojiElement.outerHTML).toBe( + '<gl-emoji data-name=""x="y" onload="alert(document.location.href)"" data-unicode-version="x"><img class="emoji" title=":"x="y" onload="alert(document.location.href)":" alt=":"x="y" onload="alert(document.location.href)":" src="/-/emojis/2/grey_question.png" width="20" height="20" align="absmiddle"></gl-emoji>', + ); + }); + it('Adds sprite CSS if emojis are not supported', async () => { const testPath = '/test-path.css'; jest.spyOn(EmojiUnicodeSupport, 'default').mockReturnValue(false); diff --git a/spec/models/integrations/chat_message/alert_message_spec.rb b/spec/models/integrations/chat_message/alert_message_spec.rb index 9866b2d9185..162df1a774c 100644 --- a/spec/models/integrations/chat_message/alert_message_spec.rb +++ b/spec/models/integrations/chat_message/alert_message_spec.rb @@ -16,6 +16,8 @@ RSpec.describe Integrations::ChatMessage::AlertMessage do }.merge(Gitlab::DataBuilder::Alert.build(alert)) end + it_behaves_like Integrations::ChatMessage + describe '#message' do it 'returns the correct message' do expect(subject.message).to eq("Alert firing in #{args[:project_name]}") diff --git a/spec/models/integrations/chat_message/base_message_spec.rb b/spec/models/integrations/chat_message/base_message_spec.rb index eada5d1031d..0f0ab11f2ac 100644 --- a/spec/models/integrations/chat_message/base_message_spec.rb +++ b/spec/models/integrations/chat_message/base_message_spec.rb @@ -31,4 +31,22 @@ RSpec.describe Integrations::ChatMessage::BaseMessage do it { is_expected.to eq('Check this out https://gitlab-domain.com/uploads/Screenshot1.png. And this https://gitlab-domain.com/uploads/Screenshot2.png') } end end + + describe '#strip_markup' do + using RSpec::Parameterized::TableSyntax + + where(:input, :output) do + nil | nil + '' | '' + '[label](url)' | 'label(url)' + '<url|label>' | 'urllabel' + '<a href="url">label</a>' | 'a href="url"label/a' + end + + with_them do + it 'returns the expected output' do + expect(base_message.send(:strip_markup, input)).to eq(output) + end + end + end end diff --git a/spec/models/integrations/chat_message/deployment_message_spec.rb b/spec/models/integrations/chat_message/deployment_message_spec.rb index ff255af11a3..6bcd29c0a00 100644 --- a/spec/models/integrations/chat_message/deployment_message_spec.rb +++ b/spec/models/integrations/chat_message/deployment_message_spec.rb @@ -3,83 +3,79 @@ require 'spec_helper' RSpec.describe Integrations::ChatMessage::DeploymentMessage do - describe '#pretext' do - it 'returns a message with the data returned by the deployment data builder' do - environment = create(:environment, name: "myenvironment") - project = create(:project, :repository) - commit = project.commit('HEAD') - deployment = create(:deployment, status: :success, environment: environment, project: project, sha: commit.sha) - data = Gitlab::DataBuilder::Deployment.build(deployment, Time.current) + subject { described_class.new(args) } + + let_it_be(:user) { create(:user, name: 'John Smith', username: 'smith') } + let_it_be(:namespace) { create(:namespace, name: 'myspace') } + let_it_be(:project) { create(:project, :repository, namespace: namespace, name: 'myproject') } + let_it_be(:commit) { project.commit('HEAD') } + let_it_be(:ci_build) { create(:ci_build, project: project) } + let_it_be(:environment) { create(:environment, name: 'myenvironment', project: project) } + let_it_be(:deployment) { create(:deployment, status: :success, deployable: ci_build, environment: environment, project: project, user: user, sha: commit.sha) } + + let(:args) do + Gitlab::DataBuilder::Deployment.build(deployment, Time.current) + end - message = described_class.new(data) + it_behaves_like Integrations::ChatMessage - expect(message.pretext).to eq("Deploy to myenvironment succeeded") + describe '#pretext' do + it 'returns a message with the data returned by the deployment data builder' do + expect(subject.pretext).to eq("Deploy to myenvironment succeeded") end it 'returns a message for a successful deployment' do - data = { + args.merge!( status: 'success', environment: 'production' - } + ) - message = described_class.new(data) - - expect(message.pretext).to eq('Deploy to production succeeded') + expect(subject.pretext).to eq('Deploy to production succeeded') end it 'returns a message for a failed deployment' do - data = { + args.merge!( status: 'failed', environment: 'production' - } + ) - message = described_class.new(data) - - expect(message.pretext).to eq('Deploy to production failed') + expect(subject.pretext).to eq('Deploy to production failed') end it 'returns a message for a canceled deployment' do - data = { + args.merge!( status: 'canceled', environment: 'production' - } - - message = described_class.new(data) + ) - expect(message.pretext).to eq('Deploy to production canceled') + expect(subject.pretext).to eq('Deploy to production canceled') end it 'returns a message for a deployment to another environment' do - data = { + args.merge!( status: 'success', environment: 'staging' - } - - message = described_class.new(data) + ) - expect(message.pretext).to eq('Deploy to staging succeeded') + expect(subject.pretext).to eq('Deploy to staging succeeded') end it 'returns a message for a deployment with any other status' do - data = { + args.merge!( status: 'unknown', environment: 'staging' - } + ) - message = described_class.new(data) - - expect(message.pretext).to eq('Deploy to staging unknown') + expect(subject.pretext).to eq('Deploy to staging unknown') end it 'returns a message for a running deployment' do - data = { - status: 'running', - environment: 'production' - } - - message = described_class.new(data) + args.merge!( + status: 'running', + environment: 'production' + ) - expect(message.pretext).to eq('Starting deploy to production') + expect(subject.pretext).to eq('Starting deploy to production') end end @@ -108,21 +104,11 @@ RSpec.describe Integrations::ChatMessage::DeploymentMessage do end it 'returns attachments with the data returned by the deployment data builder' do - user = create(:user, name: "John Smith", username: "smith") - namespace = create(:namespace, name: "myspace") - project = create(:project, :repository, namespace: namespace, name: "myproject") - commit = project.commit('HEAD') - environment = create(:environment, name: "myenvironment", project: project) - ci_build = create(:ci_build, project: project) - deployment = create(:deployment, :success, deployable: ci_build, environment: environment, project: project, user: user, sha: commit.sha) job_url = Gitlab::Routing.url_helpers.project_job_url(project, ci_build) commit_url = Gitlab::UrlBuilder.build(deployment.commit) user_url = Gitlab::Routing.url_helpers.user_url(user) - data = Gitlab::DataBuilder::Deployment.build(deployment, Time.current) - message = described_class.new(data) - - expect(message.attachments).to eq([{ + expect(subject.attachments).to eq([{ text: "[myspace/myproject](#{project.web_url}) with job [##{ci_build.id}](#{job_url}) by [John Smith (smith)](#{user_url})\n[#{deployment.short_sha}](#{commit_url}): #{commit.title}", color: "good" }]) diff --git a/spec/models/integrations/chat_message/issue_message_spec.rb b/spec/models/integrations/chat_message/issue_message_spec.rb index 31b80ad3169..7026a314b78 100644 --- a/spec/models/integrations/chat_message/issue_message_spec.rb +++ b/spec/models/integrations/chat_message/issue_message_spec.rb @@ -28,6 +28,8 @@ RSpec.describe Integrations::ChatMessage::IssueMessage do } end + it_behaves_like Integrations::ChatMessage + context 'without markdown' do let(:color) { '#C95823' } diff --git a/spec/models/integrations/chat_message/merge_message_spec.rb b/spec/models/integrations/chat_message/merge_message_spec.rb index ed1ad6837e2..52f15667b03 100644 --- a/spec/models/integrations/chat_message/merge_message_spec.rb +++ b/spec/models/integrations/chat_message/merge_message_spec.rb @@ -29,6 +29,8 @@ RSpec.describe Integrations::ChatMessage::MergeMessage do } end + it_behaves_like Integrations::ChatMessage + context 'without markdown' do let(:color) { '#345' } diff --git a/spec/models/integrations/chat_message/note_message_spec.rb b/spec/models/integrations/chat_message/note_message_spec.rb index 668c0da26ae..197df216814 100644 --- a/spec/models/integrations/chat_message/note_message_spec.rb +++ b/spec/models/integrations/chat_message/note_message_spec.rb @@ -19,6 +19,10 @@ RSpec.describe Integrations::ChatMessage::NoteMessage do name: 'project_name', url: 'http://somewhere.com' }, + commit: { + id: '5f163b2b95e6f53cbd428f5f0b103702a52b9a23', + message: "Added a commit message\ndetails\n123\n" + }, object_attributes: { id: 10, note: 'comment on a commit', @@ -28,16 +32,9 @@ RSpec.describe Integrations::ChatMessage::NoteMessage do } end - context 'commit notes' do - before do - args[:object_attributes][:note] = 'comment on a commit' - args[:object_attributes][:noteable_type] = 'Commit' - args[:commit] = { - id: '5f163b2b95e6f53cbd428f5f0b103702a52b9a23', - message: "Added a commit message\ndetails\n123\n" - } - end + it_behaves_like Integrations::ChatMessage + context 'commit notes' do context 'without markdown' do it 'returns a message regarding notes on commits' do expect(subject.pretext).to eq("Test User (test.user) <http://url.com|commented on " \ diff --git a/spec/models/integrations/chat_message/pipeline_message_spec.rb b/spec/models/integrations/chat_message/pipeline_message_spec.rb index a80d13d7f5d..68ef0ccb2e4 100644 --- a/spec/models/integrations/chat_message/pipeline_message_spec.rb +++ b/spec/models/integrations/chat_message/pipeline_message_spec.rb @@ -40,6 +40,8 @@ RSpec.describe Integrations::ChatMessage::PipelineMessage do let(:has_yaml_errors) { false } + it_behaves_like Integrations::ChatMessage + before do test_commit = double("A test commit", committer: args[:user], title: "A test commit message") test_project = double("A test project", commit_by: test_commit, name: args[:project][:name], web_url: args[:project][:web_url]) diff --git a/spec/models/integrations/chat_message/push_message_spec.rb b/spec/models/integrations/chat_message/push_message_spec.rb index 167487449c3..8d2d0f9f9a8 100644 --- a/spec/models/integrations/chat_message/push_message_spec.rb +++ b/spec/models/integrations/chat_message/push_message_spec.rb @@ -19,6 +19,8 @@ RSpec.describe Integrations::ChatMessage::PushMessage do let(:color) { '#345' } + it_behaves_like Integrations::ChatMessage + context 'push' do before do args[:commits] = [ diff --git a/spec/models/integrations/chat_message/wiki_page_message_spec.rb b/spec/models/integrations/chat_message/wiki_page_message_spec.rb index ded467dc910..4aa96c7e031 100644 --- a/spec/models/integrations/chat_message/wiki_page_message_spec.rb +++ b/spec/models/integrations/chat_message/wiki_page_message_spec.rb @@ -33,6 +33,8 @@ RSpec.describe Integrations::ChatMessage::WikiPageMessage do } end + it_behaves_like Integrations::ChatMessage + context 'without markdown' do describe '#pretext' do context 'when :action == "create"' do diff --git a/spec/requests/api/graphql_spec.rb b/spec/requests/api/graphql_spec.rb index b8f7af29a9f..da0c87fcefe 100644 --- a/spec/requests/api/graphql_spec.rb +++ b/spec/requests/api/graphql_spec.rb @@ -253,7 +253,7 @@ RSpec.describe 'GraphQL' do end context 'with token authentication' do - let(:token) { create(:personal_access_token) } + let(:token) { create(:personal_access_token, user: user) } it 'authenticates users with a PAT' do stub_authentication_activity_metrics(debug: false) @@ -276,6 +276,32 @@ RSpec.describe 'GraphQL' do expect(graphql_errors).to include({ 'message' => /API not accessible/ }) end + context 'when user with expired password' do + let_it_be(:user) { create(:user, password_expires_at: 2.minutes.ago) } + + it 'does not authenticate user' do + post_graphql(query, headers: { 'PRIVATE-TOKEN' => token.token }) + + expect(response).to have_gitlab_http_status(:ok) + + expect(graphql_data['echo']).to eq('nil says: Hello world') + end + end + + context 'when password expiration is not applicable' do + context 'when ldap user' do + let_it_be(:user) { create(:omniauth_user, provider: 'ldap', password_expires_at: 2.minutes.ago) } + + it 'authenticates user' do + post_graphql(query, headers: { 'PRIVATE-TOKEN' => token.token }) + + expect(response).to have_gitlab_http_status(:ok) + + expect(graphql_data['echo']).to eq("\"#{token.user.username}\" says: Hello world") + end + end + end + context 'when the personal access token has no api scope' do it 'does not log the user in' do token.update!(scopes: [:read_user]) diff --git a/spec/requests/dashboard/projects_controller_spec.rb b/spec/requests/dashboard/projects_controller_spec.rb new file mode 100644 index 00000000000..4cd3b6c4f9e --- /dev/null +++ b/spec/requests/dashboard/projects_controller_spec.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Dashboard::ProjectsController do + context 'token authentication' do + it_behaves_like 'authenticates sessionless user for the request spec', 'index atom', public_resource: false do + let(:url) { dashboard_projects_url(:atom) } + end + end +end diff --git a/spec/requests/dashboard_controller_spec.rb b/spec/requests/dashboard_controller_spec.rb new file mode 100644 index 00000000000..62655d720c5 --- /dev/null +++ b/spec/requests/dashboard_controller_spec.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe DashboardController do + context 'token authentication' do + it_behaves_like 'authenticates sessionless user for the request spec', 'issues atom', public_resource: false do + let(:url) { issues_dashboard_url(:atom, assignee_username: user.username) } + end + + it_behaves_like 'authenticates sessionless user for the request spec', 'issues_calendar ics', public_resource: false do + let(:url) { issues_dashboard_url(:ics, assignee_username: user.username) } + end + end +end diff --git a/spec/requests/groups_controller_spec.rb b/spec/requests/groups_controller_spec.rb new file mode 100644 index 00000000000..422c108f2ad --- /dev/null +++ b/spec/requests/groups_controller_spec.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe GroupsController do + context 'token authentication' do + context 'when public group' do + let_it_be(:public_group) { create(:group, :public) } + + it_behaves_like 'authenticates sessionless user for the request spec', 'show atom', public_resource: true do + let(:url) { group_path(public_group, format: :atom) } + end + + it_behaves_like 'authenticates sessionless user for the request spec', 'issues atom', public_resource: true do + let(:url) { issues_group_path(public_group, format: :atom) } + end + + it_behaves_like 'authenticates sessionless user for the request spec', 'issues_calendar ics', public_resource: true do + let(:url) { issues_group_calendar_url(public_group, format: :ics) } + end + end + + context 'when private project' do + let_it_be(:private_group) { create(:group, :private) } + + it_behaves_like 'authenticates sessionless user for the request spec', 'show atom', public_resource: false, ignore_metrics: true do + let(:url) { group_path(private_group, format: :atom) } + + before do + private_group.add_maintainer(user) + end + end + + it_behaves_like 'authenticates sessionless user for the request spec', 'issues atom', public_resource: false, ignore_metrics: true do + let(:url) { issues_group_path(private_group, format: :atom) } + + before do + private_group.add_maintainer(user) + end + end + + it_behaves_like 'authenticates sessionless user for the request spec', 'issues_calendar ics', public_resource: false, ignore_metrics: true do + let(:url) { issues_group_calendar_url(private_group, format: :ics) } + + before do + private_group.add_maintainer(user) + end + end + end + end +end diff --git a/spec/requests/projects/commits_controller_spec.rb b/spec/requests/projects/commits_controller_spec.rb new file mode 100644 index 00000000000..158902c0ffd --- /dev/null +++ b/spec/requests/projects/commits_controller_spec.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Projects::CommitsController do + context 'token authentication' do + context 'when public project' do + let_it_be(:public_project) { create(:project, :repository, :public) } + + it_behaves_like 'authenticates sessionless user for the request spec', 'show atom', public_resource: true do + let(:url) { project_commits_url(public_project, public_project.default_branch, format: :atom) } + end + end + + context 'when private project' do + let_it_be(:private_project) { create(:project, :repository, :private) } + + it_behaves_like 'authenticates sessionless user for the request spec', 'show atom', public_resource: false, ignore_metrics: true do + let(:url) { project_commits_url(private_project, private_project.default_branch, format: :atom) } + + before do + private_project.add_maintainer(user) + end + end + end + end +end diff --git a/spec/requests/projects/issues_controller_spec.rb b/spec/requests/projects/issues_controller_spec.rb index f44b1f4d502..248e3e3a92b 100644 --- a/spec/requests/projects/issues_controller_spec.rb +++ b/spec/requests/projects/issues_controller_spec.rb @@ -8,11 +8,11 @@ RSpec.describe Projects::IssuesController do let_it_be(:project) { issue.project } let_it_be(:user) { issue.author } - before do - login_as(user) - end - describe 'GET #discussions' do + before do + login_as(user) + end + let_it_be(:discussion) { create(:discussion_note_on_issue, noteable: issue, project: issue.project) } let_it_be(:discussion_reply) { create(:discussion_note_on_issue, noteable: issue, project: issue.project, in_reply_to: discussion) } let_it_be(:state_event) { create(:resource_state_event, issue: issue) } @@ -68,4 +68,38 @@ RSpec.describe Projects::IssuesController do end end end + + context 'token authentication' do + context 'when public project' do + let_it_be(:public_project) { create(:project, :public) } + + it_behaves_like 'authenticates sessionless user for the request spec', 'index atom', public_resource: true do + let(:url) { project_issues_url(public_project, format: :atom) } + end + + it_behaves_like 'authenticates sessionless user for the request spec', 'calendar ics', public_resource: true do + let(:url) { project_issues_url(public_project, format: :ics) } + end + end + + context 'when private project' do + let_it_be(:private_project) { create(:project, :private) } + + it_behaves_like 'authenticates sessionless user for the request spec', 'index atom', public_resource: false, ignore_metrics: true do + let(:url) { project_issues_url(private_project, format: :atom) } + + before do + private_project.add_maintainer(user) + end + end + + it_behaves_like 'authenticates sessionless user for the request spec', 'calendar ics', public_resource: false, ignore_metrics: true do + let(:url) { project_issues_url(private_project, format: :ics) } + + before do + private_project.add_maintainer(user) + end + end + end + end end diff --git a/spec/requests/projects/merge_requests_controller_spec.rb b/spec/requests/projects/merge_requests_controller_spec.rb new file mode 100644 index 00000000000..3b1ce569033 --- /dev/null +++ b/spec/requests/projects/merge_requests_controller_spec.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Projects::MergeRequestsController do + context 'token authentication' do + context 'when public project' do + let_it_be(:public_project) { create(:project, :public) } + + it_behaves_like 'authenticates sessionless user for the request spec', 'index atom', public_resource: true do + let(:url) { project_merge_requests_url(public_project, format: :atom) } + end + end + + context 'when private project' do + let_it_be(:private_project) { create(:project, :private) } + + it_behaves_like 'authenticates sessionless user for the request spec', 'index atom', public_resource: false, ignore_metrics: true do + let(:url) { project_merge_requests_url(private_project, format: :atom) } + + before do + private_project.add_maintainer(user) + end + end + end + end +end diff --git a/spec/requests/projects/tags_controller_spec.rb b/spec/requests/projects/tags_controller_spec.rb new file mode 100644 index 00000000000..b9531a2739c --- /dev/null +++ b/spec/requests/projects/tags_controller_spec.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Projects::TagsController do + context 'token authentication' do + context 'when public project' do + let_it_be(:public_project) { create(:project, :repository, :public) } + + it_behaves_like 'authenticates sessionless user for the request spec', 'index atom', public_resource: true do + let(:url) { project_tags_url(public_project, format: :atom) } + end + end + + context 'when private project' do + let_it_be(:private_project) { create(:project, :repository, :private) } + + it_behaves_like 'authenticates sessionless user for the request spec', 'index atom', public_resource: false, ignore_metrics: true do + let(:url) { project_tags_url(private_project, format: :atom) } + + before do + private_project.add_maintainer(user) + end + end + end + end +end diff --git a/spec/requests/projects_controller_spec.rb b/spec/requests/projects_controller_spec.rb new file mode 100644 index 00000000000..d2200d5a4ec --- /dev/null +++ b/spec/requests/projects_controller_spec.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe ProjectsController do + context 'token authentication' do + context 'when public project' do + let_it_be(:public_project) { create(:project, :public) } + + it_behaves_like 'authenticates sessionless user for the request spec', 'show atom', public_resource: true do + let(:url) { project_url(public_project, format: :atom) } + end + end + + context 'when private project' do + let_it_be(:private_project) { create(:project, :private) } + + it_behaves_like 'authenticates sessionless user for the request spec', 'show atom', public_resource: false, ignore_metrics: true do + let(:url) { project_url(private_project, format: :atom) } + + before do + private_project.add_maintainer(user) + end + end + end + end +end diff --git a/spec/requests/users_controller_spec.rb b/spec/requests/users_controller_spec.rb index 701a73761fd..eefc24f7824 100644 --- a/spec/requests/users_controller_spec.rb +++ b/spec/requests/users_controller_spec.rb @@ -792,9 +792,9 @@ RSpec.describe UsersController do end context 'token authentication' do - let(:url) { user_url(user.username, format: :atom) } - - it_behaves_like 'authenticates sessionless user for the request spec', public: true + it_behaves_like 'authenticates sessionless user for the request spec', 'show atom', public_resource: true do + let(:url) { user_url(user, format: :atom) } + end end def user_moved_message(redirect_route, user) diff --git a/spec/support/shared_examples/controllers/sessionless_auth_controller_shared_examples.rb b/spec/support/shared_examples/controllers/sessionless_auth_controller_shared_examples.rb deleted file mode 100644 index 041695d8111..00000000000 --- a/spec/support/shared_examples/controllers/sessionless_auth_controller_shared_examples.rb +++ /dev/null @@ -1,112 +0,0 @@ -# frozen_string_literal: true - -# This controller shared examples will be migrated to -# spec/support/shared_examples/requests/sessionless_auth_request_shared_examples.rb -# See also https://gitlab.com/groups/gitlab-org/-/epics/5076 - -RSpec.shared_examples 'authenticates sessionless user' do |path, format, params| - params ||= {} - - before do - stub_authentication_activity_metrics(debug: false) - end - - let(:user) { create(:user) } - let(:personal_access_token) { create(:personal_access_token, user: user) } - let(:default_params) { { format: format }.merge(params.except(:public) || {}) } - - context "when the 'personal_access_token' param is populated with the personal access token" do - it 'logs the user in' do - expect(authentication_metrics) - .to increment(:user_authenticated_counter) - .and increment(:user_session_override_counter) - .and increment(:user_sessionless_authentication_counter) - - get path, params: default_params.merge(private_token: personal_access_token.token) - - expect(response).to have_gitlab_http_status(:ok) - expect(controller.current_user).to eq(user) - end - - it 'does not log the user in if page is public', if: params[:public] do - get path, params: default_params - - expect(response).to have_gitlab_http_status(:ok) - expect(controller.current_user).to be_nil - end - end - - context 'when the personal access token has no api scope', unless: params[:public] do - it 'does not log the user in' do - # Several instances of where these specs are shared route the request - # through ApplicationController#route_not_found which does not involve - # the usual auth code from Devise, so does not increment the - # :user_unauthenticated_counter - # - unless params[:ignore_incrementing] - expect(authentication_metrics) - .to increment(:user_unauthenticated_counter) - end - - personal_access_token.update!(scopes: [:read_user]) - - get path, params: default_params.merge(private_token: personal_access_token.token) - - expect(response).not_to have_gitlab_http_status(:ok) - end - end - - context "when the 'PERSONAL_ACCESS_TOKEN' header is populated with the personal access token" do - it 'logs the user in' do - expect(authentication_metrics) - .to increment(:user_authenticated_counter) - .and increment(:user_session_override_counter) - .and increment(:user_sessionless_authentication_counter) - - @request.headers['PRIVATE-TOKEN'] = personal_access_token.token - get path, params: default_params - - expect(response).to have_gitlab_http_status(:ok) - end - end - - context "when the 'feed_token' param is populated with the feed token", if: format == :rss do - it "logs the user in" do - expect(authentication_metrics) - .to increment(:user_authenticated_counter) - .and increment(:user_session_override_counter) - .and increment(:user_sessionless_authentication_counter) - - get path, params: default_params.merge(feed_token: user.feed_token) - - expect(response).to have_gitlab_http_status(:ok) - end - end - - context "when the 'feed_token' param is populated with an invalid feed token", if: format == :rss, unless: params[:public] do - it "logs the user" do - expect(authentication_metrics) - .to increment(:user_unauthenticated_counter) - - get path, params: default_params.merge(feed_token: 'token') - - expect(response).not_to have_gitlab_http_status(:ok) - end - end - - it "doesn't log the user in otherwise", unless: params[:public] do - # Several instances of where these specs are shared route the request - # through ApplicationController#route_not_found which does not involve - # the usual auth code from Devise, so does not increment the - # :user_unauthenticated_counter - # - unless params[:ignore_incrementing] - expect(authentication_metrics) - .to increment(:user_unauthenticated_counter) - end - - get path, params: default_params.merge(private_token: 'token') - - expect(response).not_to have_gitlab_http_status(:ok) - end -end diff --git a/spec/support/shared_examples/models/integrations/chat_message_shared_examples.rb b/spec/support/shared_examples/models/integrations/chat_message_shared_examples.rb new file mode 100644 index 00000000000..2665f249ded --- /dev/null +++ b/spec/support/shared_examples/models/integrations/chat_message_shared_examples.rb @@ -0,0 +1,56 @@ +# frozen_string_literal: true + +RSpec.shared_examples Integrations::ChatMessage do + context 'when input contains link markup' do + let(:evil_input) { '[Markdown](http://evil.com) <a href="http://evil.com">HTML</a> <http://evil.com|Slack>' } + + # Attributes returned from #activity and #attributes which should be sanitized. + let(:sanitized_attributes) do + %i[title subtitle text fallback author_name] + end + + # Attributes passed to #initialize which can contain user input. + before do + args.deep_merge!( + project_name: evil_input, + user_name: evil_input, + user_full_name: evil_input, + commit_title: evil_input, + environment: evil_input, + project: { + name: evil_input + }, + user: { + name: evil_input, + username: evil_input + }, + object_attributes: { + title: evil_input + } + ) + end + + # NOTE: The `include` matcher is used here so the RSpec error messages will tell us + # which method or attribute is failing, even though it makes the spec a bit less readable. + it 'strips all link markup characters', :aggregate_failures do + expect(subject).not_to have_attributes( + pretext: include(evil_input), + summary: include(evil_input) + ) + + begin + sanitized_attributes.each do |attribute| + expect(subject.activity).not_to include(attribute => include(evil_input)) + end + rescue NotImplementedError + end + + begin + sanitized_attributes.each do |attribute| + expect(subject.attachments).not_to include(include(attribute => include(evil_input))) + end + rescue NotImplementedError + end + end + end +end diff --git a/spec/support/shared_examples/requests/sessionless_auth_request_shared_examples.rb b/spec/support/shared_examples/requests/sessionless_auth_request_shared_examples.rb index d82da1b01e1..56e90a6ec34 100644 --- a/spec/support/shared_examples/requests/sessionless_auth_request_shared_examples.rb +++ b/spec/support/shared_examples/requests/sessionless_auth_request_shared_examples.rb @@ -1,84 +1,160 @@ # frozen_string_literal: true -RSpec.shared_examples 'authenticates sessionless user for the request spec' do |params| - params ||= {} - +RSpec.shared_examples 'authenticates sessionless user for the request spec' do |name, public_resource:, ignore_metrics: false, params: {}| before do stub_authentication_activity_metrics(debug: false) end - let(:user) { create(:user) } + let_it_be(:user) { create(:user) } let(:personal_access_token) { create(:personal_access_token, user: user) } - let(:default_params) { params.except(:public) || {} } - context "when the 'personal_access_token' param is populated with the personal access token" do - it 'logs the user in' do + shared_examples 'authenticates user and returns response with ok status' do + it 'authenticates user and returns response with ok status' do expect(authentication_metrics) .to increment(:user_authenticated_counter) - .and increment(:user_session_override_counter) - .and increment(:user_sessionless_authentication_counter) + .and increment(:user_session_override_counter) + .and increment(:user_sessionless_authentication_counter) - get url, params: default_params.merge(private_token: personal_access_token.token) + subject - expect(response).to have_gitlab_http_status(:ok) expect(controller.current_user).to eq(user) + expect(response).to have_gitlab_http_status(:ok) end + end - it 'does not log the user in if page is public', if: params[:public] do - get url, params: default_params + shared_examples 'does not authenticate user and returns response with ok status' do + it 'does not authenticate user and returns response with ok status' do + subject - expect(response).to have_gitlab_http_status(:ok) expect(controller.current_user).to be_nil + expect(response).to have_gitlab_http_status(:ok) end end - context 'when the personal access token has no api scope', unless: params[:public] do - it 'does not log the user in' do + shared_examples 'does not return response with ok status' do + it 'does not return response with ok status' do # Several instances of where these specs are shared route the request # through ApplicationController#route_not_found which does not involve # the usual auth code from Devise, so does not increment the # :user_unauthenticated_counter - # - unless params[:ignore_incrementing] + unless ignore_metrics expect(authentication_metrics) .to increment(:user_unauthenticated_counter) end - personal_access_token.update!(scopes: [:read_user]) - - get url, params: default_params.merge(private_token: personal_access_token.token) + subject expect(response).not_to have_gitlab_http_status(:ok) end end - context "when the 'PERSONAL_ACCESS_TOKEN' header is populated with the personal access token" do - it 'logs the user in' do - expect(authentication_metrics) - .to increment(:user_authenticated_counter) - .and increment(:user_session_override_counter) - .and increment(:user_sessionless_authentication_counter) + shared_examples 'using valid token' do + context 'when resource is private', unless: public_resource do + include_examples 'authenticates user and returns response with ok status' - headers = { 'PRIVATE-TOKEN': personal_access_token.token } - get url, params: default_params, headers: headers + context 'when user with expired password' do + let_it_be(:user) { create(:user, password_expires_at: 2.minutes.ago) } - expect(response).to have_gitlab_http_status(:ok) + include_examples 'does not return response with ok status' + end + + context 'when password expiration is not applicable' do + context 'when ldap user' do + let_it_be(:user) { create(:omniauth_user, provider: 'ldap', password_expires_at: 2.minutes.ago) } + + include_examples 'authenticates user and returns response with ok status' + end + end + end + + context 'when resource is public', if: public_resource do + include_examples 'authenticates user and returns response with ok status' + + context 'when user with expired password' do + let_it_be(:user) { create(:user, password_expires_at: 2.minutes.ago) } + + include_examples 'does not authenticate user and returns response with ok status' + end end end - it "doesn't log the user in otherwise", unless: params[:public] do - # Several instances of where these specs are shared route the request - # through ApplicationController#route_not_found which does not involve - # the usual auth code from Devise, so does not increment the - # :user_unauthenticated_counter - # - unless params[:ignore_incrementing] - expect(authentication_metrics) - .to increment(:user_unauthenticated_counter) + shared_examples 'using invalid token' do + context 'when resource is private', unless: public_resource do + include_examples 'does not return response with ok status' + end + + context 'when resource is public', if: public_resource do + include_examples 'does not authenticate user and returns response with ok status' + end + end + + shared_examples 'personal access token has no api scope' do + context 'when the personal access token has no api scope' do + before do + personal_access_token.update!(scopes: [:read_user]) + end + + context 'when resource is private', unless: public_resource do + include_examples 'does not return response with ok status' + end + + context 'when resource is public', if: public_resource do + include_examples 'does not authenticate user and returns response with ok status' + end + end + end + + describe name do + context "when the 'private_token' param is populated with the personal access token" do + context 'when valid token' do + subject { get url, params: params.merge(private_token: personal_access_token.token) } + + include_examples 'using valid token' + + include_examples 'personal access token has no api scope' + end + + context 'when invalid token' do + subject { get url, params: params.merge(private_token: 'invalid token') } + + include_examples 'using invalid token' + end end - get url, params: default_params.merge(private_token: 'token') + context "when the 'PRIVATE-TOKEN' header is populated with the personal access token" do + context 'when valid token' do + subject do + headers = { 'PRIVATE-TOKEN': personal_access_token.token } + get url, params: params, headers: headers + end - expect(response).not_to have_gitlab_http_status(:ok) + include_examples 'using valid token' + + include_examples 'personal access token has no api scope' + end + + context 'when invalid token' do + subject do + headers = { 'PRIVATE-TOKEN': 'invalid token' } + get url, params: params, headers: headers + end + + include_examples 'using invalid token' + end + end + + context "when the 'feed_token' param is populated with the feed token" do + context 'when valid token' do + subject { get url, params: params.merge(feed_token: user.feed_token) } + + include_examples 'using valid token' + end + + context 'when invalid token' do + subject { get url, params: params.merge(feed_token: 'invalid token') } + + include_examples 'using invalid token' + end + end end end |