Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2022-08-26 17:36:54 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2022-08-26 17:36:54 +0300
commitdaf5ae5bd439f1f32363d410129d5b9e73fbb539 (patch)
tree6d670487dc3dccf1a0c3e6b8337e5b4ab9da4ee9 /spec
parent6e8c2290dab8ae1612dff80e312911bc1147edaa (diff)
Add latest changes from gitlab-org/security/gitlab@15-3-stable-ee
Diffstat (limited to 'spec')
-rw-r--r--spec/frontend/ide/components/preview/clientside_spec.js36
-rw-r--r--spec/frontend/ide/components/preview/navigator_spec.js20
-rw-r--r--spec/helpers/commits_helper_spec.rb2
-rw-r--r--spec/lib/banzai/filter/image_link_filter_spec.rb45
-rw-r--r--spec/models/snippet_spec.rb39
-rw-r--r--spec/presenters/commit_presenter_spec.rb50
-rw-r--r--spec/requests/api/search_spec.rb90
-rw-r--r--spec/requests/git_http_spec.rb41
-rw-r--r--spec/requests/jwt_controller_spec.rb41
-rw-r--r--spec/support/shared_examples/requests/api/pypi_packages_shared_examples.rb51
-rw-r--r--spec/validators/bytesize_validator_spec.rb36
-rw-r--r--spec/views/projects/commits/_commit.html.haml_spec.rb37
12 files changed, 373 insertions, 115 deletions
diff --git a/spec/frontend/ide/components/preview/clientside_spec.js b/spec/frontend/ide/components/preview/clientside_spec.js
index cf768114e70..51e6a9d9034 100644
--- a/spec/frontend/ide/components/preview/clientside_spec.js
+++ b/spec/frontend/ide/components/preview/clientside_spec.js
@@ -2,15 +2,15 @@ import { GlLoadingIcon } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import Vue, { nextTick } from 'vue';
import { dispatch } from 'codesandbox-api';
-import smooshpack from 'smooshpack';
+import { SandpackClient } from '@codesandbox/sandpack-client';
import Vuex from 'vuex';
import waitForPromises from 'helpers/wait_for_promises';
import Clientside from '~/ide/components/preview/clientside.vue';
import { PING_USAGE_PREVIEW_KEY, PING_USAGE_PREVIEW_SUCCESS_KEY } from '~/ide/constants';
import eventHub from '~/ide/eventhub';
-jest.mock('smooshpack', () => ({
- Manager: jest.fn(),
+jest.mock('@codesandbox/sandpack-client', () => ({
+ SandpackClient: jest.fn(),
}));
Vue.use(Vuex);
@@ -78,8 +78,8 @@ describe('IDE clientside preview', () => {
// eslint-disable-next-line no-restricted-syntax
wrapper.setData({
sandpackReady: true,
- manager: {
- listener: jest.fn(),
+ client: {
+ cleanup: jest.fn(),
updatePreview: jest.fn(),
},
});
@@ -90,9 +90,9 @@ describe('IDE clientside preview', () => {
});
describe('without main entry', () => {
- it('creates sandpack manager', () => {
+ it('creates sandpack client', () => {
createComponent();
- expect(smooshpack.Manager).not.toHaveBeenCalled();
+ expect(SandpackClient).not.toHaveBeenCalled();
});
});
describe('with main entry', () => {
@@ -102,8 +102,8 @@ describe('IDE clientside preview', () => {
return waitForPromises();
});
- it('creates sandpack manager', () => {
- expect(smooshpack.Manager).toHaveBeenCalledWith(
+ it('creates sandpack client', () => {
+ expect(SandpackClient).toHaveBeenCalledWith(
'#ide-preview',
expectedSandpackOptions(),
expectedSandpackSettings(),
@@ -141,8 +141,8 @@ describe('IDE clientside preview', () => {
return waitForPromises();
});
- it('creates sandpack manager with bundlerURL', () => {
- expect(smooshpack.Manager).toHaveBeenCalledWith('#ide-preview', expectedSandpackOptions(), {
+ it('creates sandpack client with bundlerURL', () => {
+ expect(SandpackClient).toHaveBeenCalledWith('#ide-preview', expectedSandpackOptions(), {
...expectedSandpackSettings(),
bundlerURL: TEST_BUNDLER_URL,
});
@@ -156,8 +156,8 @@ describe('IDE clientside preview', () => {
return waitForPromises();
});
- it('creates sandpack manager', () => {
- expect(smooshpack.Manager).toHaveBeenCalledWith(
+ it('creates sandpack client', () => {
+ expect(SandpackClient).toHaveBeenCalledWith(
'#ide-preview',
{
files: {},
@@ -332,7 +332,7 @@ describe('IDE clientside preview', () => {
});
describe('update', () => {
- it('initializes manager if manager is empty', () => {
+ it('initializes client if client is empty', () => {
createComponent({ getters: { packageJson: dummyPackageJson } });
// setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
// eslint-disable-next-line no-restricted-syntax
@@ -340,7 +340,7 @@ describe('IDE clientside preview', () => {
wrapper.vm.update();
return waitForPromises().then(() => {
- expect(smooshpack.Manager).toHaveBeenCalled();
+ expect(SandpackClient).toHaveBeenCalled();
});
});
@@ -349,7 +349,7 @@ describe('IDE clientside preview', () => {
wrapper.vm.update();
- expect(wrapper.vm.manager.updatePreview).toHaveBeenCalledWith(wrapper.vm.sandboxOpts);
+ expect(wrapper.vm.client.updatePreview).toHaveBeenCalledWith(wrapper.vm.sandboxOpts);
});
});
@@ -361,7 +361,7 @@ describe('IDE clientside preview', () => {
});
it('calls updatePreview', () => {
- expect(wrapper.vm.manager.updatePreview).toHaveBeenCalledWith(wrapper.vm.sandboxOpts);
+ expect(wrapper.vm.client.updatePreview).toHaveBeenCalledWith(wrapper.vm.sandboxOpts);
});
});
});
@@ -405,7 +405,7 @@ describe('IDE clientside preview', () => {
beforeEach(() => {
createInitializedComponent();
- spy = wrapper.vm.manager.updatePreview;
+ spy = wrapper.vm.client.updatePreview;
wrapper.destroy();
});
diff --git a/spec/frontend/ide/components/preview/navigator_spec.js b/spec/frontend/ide/components/preview/navigator_spec.js
index 9c4f825ccf5..532cb6e795c 100644
--- a/spec/frontend/ide/components/preview/navigator_spec.js
+++ b/spec/frontend/ide/components/preview/navigator_spec.js
@@ -11,7 +11,7 @@ jest.mock('codesandbox-api', () => ({
describe('IDE clientside preview navigator', () => {
let wrapper;
- let manager;
+ let client;
let listenHandler;
const findBackButton = () => wrapper.findAll('button').at(0);
@@ -20,9 +20,9 @@ describe('IDE clientside preview navigator', () => {
beforeEach(() => {
listen.mockClear();
- manager = { bundlerURL: TEST_HOST, iframe: { src: '' } };
+ client = { bundlerURL: TEST_HOST, iframe: { src: '' } };
- wrapper = shallowMount(ClientsideNavigator, { propsData: { manager } });
+ wrapper = shallowMount(ClientsideNavigator, { propsData: { client } });
[[listenHandler]] = listen.mock.calls;
});
@@ -31,7 +31,7 @@ describe('IDE clientside preview navigator', () => {
});
it('renders readonly URL bar', async () => {
- listenHandler({ type: 'urlchange', url: manager.bundlerURL });
+ listenHandler({ type: 'urlchange', url: client.bundlerURL });
await nextTick();
expect(wrapper.find('input[readonly]').element.value).toBe('/');
});
@@ -89,13 +89,13 @@ describe('IDE clientside preview navigator', () => {
expect(findBackButton().attributes('disabled')).toBe('disabled');
});
- it('updates manager iframe src', async () => {
+ it('updates client iframe src', async () => {
listenHandler({ type: 'urlchange', url: `${TEST_HOST}/url1` });
listenHandler({ type: 'urlchange', url: `${TEST_HOST}/url2` });
await nextTick();
findBackButton().trigger('click');
- expect(manager.iframe.src).toBe(`${TEST_HOST}/url1`);
+ expect(client.iframe.src).toBe(`${TEST_HOST}/url1`);
});
});
@@ -133,13 +133,13 @@ describe('IDE clientside preview navigator', () => {
expect(findForwardButton().attributes('disabled')).toBe('disabled');
});
- it('updates manager iframe src', async () => {
+ it('updates client iframe src', async () => {
listenHandler({ type: 'urlchange', url: `${TEST_HOST}/url1` });
listenHandler({ type: 'urlchange', url: `${TEST_HOST}/url2` });
await nextTick();
findBackButton().trigger('click');
- expect(manager.iframe.src).toBe(`${TEST_HOST}/url1`);
+ expect(client.iframe.src).toBe(`${TEST_HOST}/url1`);
});
});
@@ -152,10 +152,10 @@ describe('IDE clientside preview navigator', () => {
});
it('calls refresh with current path', () => {
- manager.iframe.src = 'something-other';
+ client.iframe.src = 'something-other';
findRefreshButton().trigger('click');
- expect(manager.iframe.src).toBe(url);
+ expect(client.iframe.src).toBe(url);
});
});
});
diff --git a/spec/helpers/commits_helper_spec.rb b/spec/helpers/commits_helper_spec.rb
index b27954de0d4..010100769d4 100644
--- a/spec/helpers/commits_helper_spec.rb
+++ b/spec/helpers/commits_helper_spec.rb
@@ -320,7 +320,7 @@ RSpec.describe CommitsHelper do
let(:current_path) { "test" }
before do
- expect(commit).to receive(:status_for).with(ref).and_return(commit_status)
+ expect(commit).to receive(:detailed_status_for).with(ref).and_return(commit_status)
assign(:path, current_path)
end
diff --git a/spec/lib/banzai/filter/image_link_filter_spec.rb b/spec/lib/banzai/filter/image_link_filter_spec.rb
index 6326d894b08..78d68697ac7 100644
--- a/spec/lib/banzai/filter/image_link_filter_spec.rb
+++ b/spec/lib/banzai/filter/image_link_filter_spec.rb
@@ -92,5 +92,50 @@ RSpec.describe Banzai::Filter::ImageLinkFilter do
expect(doc.at_css('a')['class']).to match(%r{with-attachment-icon})
end
+
+ context 'when link attributes contain malicious code' do
+ let(:malicious_code) do
+ # rubocop:disable Layout/LineLength
+ %q(<a class='fixed-top fixed-bottom' data-create-path=/malicious-url><style> .tab-content>.tab-pane{display: block !important}</style>)
+ # rubocop:enable Layout/LineLength
+ end
+
+ context 'when image alt contains malicious code' do
+ it 'ignores image alt and uses image path as the link text', :aggregate_failures do
+ doc = filter(image(path, alt: malicious_code), context)
+
+ expect(doc.to_html).to match(%r{^<a[^>]*>#{path}</a>$})
+ expect(doc.at_css('a')['href']).to eq(path)
+ end
+ end
+
+ context 'when image src contains malicious code' do
+ it 'ignores image src and does not use it as the link text' do
+ doc = filter(image(malicious_code), context)
+
+ expect(doc.to_html).to match(%r{^<a[^>]*></a>$})
+ end
+
+ it 'keeps image src unchanged, malicious code does not execute as part of url' do
+ doc = filter(image(malicious_code), context)
+
+ expect(doc.at_css('a')['href']).to eq(malicious_code)
+ end
+ end
+
+ context 'when image data-src contains malicious code' do
+ it 'ignores data-src and uses image path as the link text', :aggregate_failures do
+ doc = filter(image(path, data_src: malicious_code), context)
+
+ expect(doc.to_html).to match(%r{^<a[^>]*>#{path}</a>$})
+ end
+
+ it 'uses image data-src, malicious code does not execute as part of url' do
+ doc = filter(image(path, data_src: malicious_code), context)
+
+ expect(doc.at_css('a')['href']).to eq(malicious_code)
+ end
+ end
+ end
end
end
diff --git a/spec/models/snippet_spec.rb b/spec/models/snippet_spec.rb
index 38bd189f6f4..da1f2653676 100644
--- a/spec/models/snippet_spec.rb
+++ b/spec/models/snippet_spec.rb
@@ -91,6 +91,45 @@ RSpec.describe Snippet do
end
end
end
+
+ context 'description validations' do
+ let_it_be(:invalid_description) { 'a' * (described_class::DESCRIPTION_LENGTH_MAX * 2) }
+
+ context 'with existing snippets' do
+ let(:snippet) { create(:personal_snippet, description: 'This is a valid content at the time of creation') }
+
+ it 'does not raise a validation error if the description is not changed' do
+ snippet.title = 'new title'
+
+ expect(snippet).to be_valid
+ end
+
+ it 'raises and error if the description is changed and the size is bigger than limit' do
+ expect(snippet).to be_valid
+
+ snippet.description = invalid_description
+
+ expect(snippet).not_to be_valid
+ end
+ end
+
+ context 'with new snippets' do
+ it 'is valid when description is smaller than the limit' do
+ snippet = build(:personal_snippet, description: 'Valid Desc')
+
+ expect(snippet).to be_valid
+ end
+
+ it 'raises error when description is bigger than setting limit' do
+ snippet = build(:personal_snippet, description: invalid_description)
+
+ aggregate_failures do
+ expect(snippet).not_to be_valid
+ expect(snippet.errors.messages_for(:description)).to include("is too long (2 MB). The maximum size is 1 MB.")
+ end
+ end
+ end
+ end
end
describe 'callbacks' do
diff --git a/spec/presenters/commit_presenter_spec.rb b/spec/presenters/commit_presenter_spec.rb
index b221c9ca8f7..df3ee69621b 100644
--- a/spec/presenters/commit_presenter_spec.rb
+++ b/spec/presenters/commit_presenter_spec.rb
@@ -12,29 +12,51 @@ RSpec.describe CommitPresenter do
it { expect(presenter.web_path).to eq("/#{project.full_path}/-/commit/#{commit.sha}") }
end
- describe '#status_for' do
- subject { presenter.status_for('ref') }
+ describe '#detailed_status_for' do
+ using RSpec::Parameterized::TableSyntax
+
+ let(:pipeline) { create(:ci_pipeline, :success, project: project, sha: commit.sha, ref: 'ref') }
- context 'when user can read_commit_status' do
+ subject { presenter.detailed_status_for('ref')&.text }
+
+ where(:read_commit_status, :read_pipeline, :expected_result) do
+ true | true | 'passed'
+ true | false | nil
+ false | true | nil
+ false | false | nil
+ end
+
+ with_them do
before do
- allow(presenter).to receive(:can?).with(user, :read_commit_status, project).and_return(true)
+ allow(presenter).to receive(:can?).with(user, :read_commit_status, project).and_return(read_commit_status)
+ allow(presenter).to receive(:can?).with(user, :read_pipeline, pipeline).and_return(read_pipeline)
end
- it 'returns commit status for ref' do
- pipeline = double
- status = double
+ it { is_expected.to eq expected_result }
+ end
+ end
- expect(commit).to receive(:latest_pipeline).with('ref').and_return(pipeline)
- expect(pipeline).to receive(:detailed_status).with(user).and_return(status)
+ describe '#status_for' do
+ using RSpec::Parameterized::TableSyntax
- expect(subject).to eq(status)
- end
+ let(:pipeline) { create(:ci_pipeline, :success, project: project, sha: commit.sha) }
+
+ subject { presenter.status_for }
+
+ where(:read_commit_status, :read_pipeline, :expected_result) do
+ true | true | 'success'
+ true | false | nil
+ false | true | nil
+ false | false | nil
end
- context 'when user can not read_commit_status' do
- it 'is nil' do
- is_expected.to eq(nil)
+ with_them do
+ before do
+ allow(presenter).to receive(:can?).with(user, :read_commit_status, project).and_return(read_commit_status)
+ allow(presenter).to receive(:can?).with(user, :read_pipeline, pipeline).and_return(read_pipeline)
end
+
+ it { is_expected.to eq expected_result }
end
end
diff --git a/spec/requests/api/search_spec.rb b/spec/requests/api/search_spec.rb
index 66b78829e0d..6034d26f1d2 100644
--- a/spec/requests/api/search_spec.rb
+++ b/spec/requests/api/search_spec.rb
@@ -763,6 +763,96 @@ RSpec.describe API::Search do
it_behaves_like 'pagination', scope: :commits, search: 'merge'
it_behaves_like 'ping counters', scope: :commits
+
+ describe 'pipeline visibility' do
+ shared_examples 'pipeline information visible' do
+ it 'contains status and last_pipeline' do
+ request
+
+ expect(json_response[0]['status']).to eq 'success'
+ expect(json_response[0]['last_pipeline']).not_to be_nil
+ end
+ end
+
+ shared_examples 'pipeline information not visible' do
+ it 'does not contain status and last_pipeline' do
+ request
+
+ expect(json_response[0]['status']).to be_nil
+ expect(json_response[0]['last_pipeline']).to be_nil
+ end
+ end
+
+ let(:request) { get api(endpoint, user), params: { scope: 'commits', search: repo_project.commit.sha } }
+
+ before do
+ create(:ci_pipeline, :success, project: repo_project, sha: repo_project.commit.sha)
+ end
+
+ context 'with non public pipeline' do
+ let_it_be(:repo_project) do
+ create(:project, :public, :repository, public_builds: false, group: group)
+ end
+
+ context 'user is project member with reporter role or above' do
+ before do
+ repo_project.add_reporter(user)
+ end
+
+ it_behaves_like 'pipeline information visible'
+ end
+
+ context 'user is project member with guest role' do
+ before do
+ repo_project.add_guest(user)
+ end
+
+ it_behaves_like 'pipeline information not visible'
+ end
+
+ context 'user is not project member' do
+ let_it_be(:user) { create(:user) }
+
+ it_behaves_like 'pipeline information not visible'
+ end
+ end
+
+ context 'with public pipeline' do
+ let_it_be(:repo_project) do
+ create(:project, :public, :repository, public_builds: true, group: group)
+ end
+
+ context 'user is project member with reporter role or above' do
+ before do
+ repo_project.add_reporter(user)
+ end
+
+ it_behaves_like 'pipeline information visible'
+ end
+
+ context 'user is project member with guest role' do
+ before do
+ repo_project.add_guest(user)
+ end
+
+ it_behaves_like 'pipeline information visible'
+ end
+
+ context 'user is not project member' do
+ let_it_be(:user) { create(:user) }
+
+ it_behaves_like 'pipeline information visible'
+
+ context 'when CI/CD is set to only project members' do
+ before do
+ repo_project.project_feature.update!(builds_access_level: ProjectFeature::PRIVATE)
+ end
+
+ it_behaves_like 'pipeline information not visible'
+ end
+ end
+ end
+ end
end
context 'for commits scope with project path as id' do
diff --git a/spec/requests/git_http_spec.rb b/spec/requests/git_http_spec.rb
index 3ffca7e3c62..77107d0b43c 100644
--- a/spec/requests/git_http_spec.rb
+++ b/spec/requests/git_http_spec.rb
@@ -643,17 +643,17 @@ RSpec.describe 'Git HTTP requests' do
end
context 'when username and password are provided' do
- it 'rejects pulls with personal access token error message' do
+ it 'rejects pulls with generic error message' do
download(path, user: user.username, password: user.password) do |response|
expect(response).to have_gitlab_http_status(:unauthorized)
- expect(response.body).to include('You must use a personal access token with \'read_repository\' or \'write_repository\' scope for Git over HTTP')
+ expect(response.body).to eq('HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password. See http://www.example.com/help/topics/git/troubleshooting_git#error-on-git-fetch-http-basic-access-denied')
end
end
- it 'rejects the push attempt with personal access token error message' do
+ it 'rejects the push attempt with generic error message' do
upload(path, user: user.username, password: user.password) do |response|
expect(response).to have_gitlab_http_status(:unauthorized)
- expect(response.body).to include('You must use a personal access token with \'read_repository\' or \'write_repository\' scope for Git over HTTP')
+ expect(response.body).to eq('HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password. See http://www.example.com/help/topics/git/troubleshooting_git#error-on-git-fetch-http-basic-access-denied')
end
end
end
@@ -750,17 +750,17 @@ RSpec.describe 'Git HTTP requests' do
allow_any_instance_of(ApplicationSetting).to receive(:password_authentication_enabled_for_git?) { false }
end
- it 'rejects pulls with personal access token error message' do
+ it 'rejects pulls with generic error message' do
download(path, user: 'foo', password: 'bar') do |response|
expect(response).to have_gitlab_http_status(:unauthorized)
- expect(response.body).to include('You must use a personal access token with \'read_repository\' or \'write_repository\' scope for Git over HTTP')
+ expect(response.body).to eq('HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password. See http://www.example.com/help/topics/git/troubleshooting_git#error-on-git-fetch-http-basic-access-denied')
end
end
- it 'rejects pushes with personal access token error message' do
+ it 'rejects pushes with generic error message' do
upload(path, user: 'foo', password: 'bar') do |response|
expect(response).to have_gitlab_http_status(:unauthorized)
- expect(response.body).to include('You must use a personal access token with \'read_repository\' or \'write_repository\' scope for Git over HTTP')
+ expect(response.body).to eq('HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password. See http://www.example.com/help/topics/git/troubleshooting_git#error-on-git-fetch-http-basic-access-denied')
end
end
@@ -771,10 +771,10 @@ RSpec.describe 'Git HTTP requests' do
.to receive(:login).and_return(nil)
end
- it 'does not display the personal access token error message' do
+ it 'displays the generic error message' do
upload(path, user: 'foo', password: 'bar') do |response|
expect(response).to have_gitlab_http_status(:unauthorized)
- expect(response.body).not_to include('You must use a personal access token with \'read_repository\' or \'write_repository\' scope for Git over HTTP')
+ expect(response.body).to eq('HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password. See http://www.example.com/help/topics/git/troubleshooting_git#error-on-git-fetch-http-basic-access-denied')
end
end
end
@@ -1300,17 +1300,18 @@ RSpec.describe 'Git HTTP requests' do
end
context 'when username and password are provided' do
- it 'rejects pulls with personal access token error message' do
+ it 'rejects pulls with generic error message' do
download(path, user: user.username, password: user.password) do |response|
expect(response).to have_gitlab_http_status(:unauthorized)
- expect(response.body).to include('You must use a personal access token with \'read_repository\' or \'write_repository\' scope for Git over HTTP')
+
+ expect(response.body).to eq('HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password. See http://www.example.com/help/topics/git/troubleshooting_git#error-on-git-fetch-http-basic-access-denied')
end
end
- it 'rejects the push attempt with personal access token error message' do
+ it 'rejects the push attempt with generic error message' do
upload(path, user: user.username, password: user.password) do |response|
expect(response).to have_gitlab_http_status(:unauthorized)
- expect(response.body).to include('You must use a personal access token with \'read_repository\' or \'write_repository\' scope for Git over HTTP')
+ expect(response.body).to eq('HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password. See http://www.example.com/help/topics/git/troubleshooting_git#error-on-git-fetch-http-basic-access-denied')
end
end
end
@@ -1381,17 +1382,17 @@ RSpec.describe 'Git HTTP requests' do
allow_any_instance_of(ApplicationSetting).to receive(:password_authentication_enabled_for_git?) { false }
end
- it 'rejects pulls with personal access token error message' do
+ it 'rejects pulls with generic error message' do
download(path, user: 'foo', password: 'bar') do |response|
expect(response).to have_gitlab_http_status(:unauthorized)
- expect(response.body).to include('You must use a personal access token with \'read_repository\' or \'write_repository\' scope for Git over HTTP')
+ expect(response.body).to eq('HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password. See http://www.example.com/help/topics/git/troubleshooting_git#error-on-git-fetch-http-basic-access-denied')
end
end
- it 'rejects pushes with personal access token error message' do
+ it 'rejects pushes with generic error message' do
upload(path, user: 'foo', password: 'bar') do |response|
expect(response).to have_gitlab_http_status(:unauthorized)
- expect(response.body).to include('You must use a personal access token with \'read_repository\' or \'write_repository\' scope for Git over HTTP')
+ expect(response.body).to eq('HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password. See http://www.example.com/help/topics/git/troubleshooting_git#error-on-git-fetch-http-basic-access-denied')
end
end
@@ -1402,10 +1403,10 @@ RSpec.describe 'Git HTTP requests' do
.to receive(:login).and_return(nil)
end
- it 'does not display the personal access token error message' do
+ it 'returns a generic error message' do
upload(path, user: 'foo', password: 'bar') do |response|
expect(response).to have_gitlab_http_status(:unauthorized)
- expect(response.body).not_to include('You must use a personal access token with \'read_repository\' or \'write_repository\' scope for Git over HTTP')
+ expect(response.body).to eq('HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password. See http://www.example.com/help/topics/git/troubleshooting_git#error-on-git-fetch-http-basic-access-denied')
end
end
end
diff --git a/spec/requests/jwt_controller_spec.rb b/spec/requests/jwt_controller_spec.rb
index db3be617a53..c9904ffa37b 100644
--- a/spec/requests/jwt_controller_spec.rb
+++ b/spec/requests/jwt_controller_spec.rb
@@ -33,6 +33,22 @@ RSpec.describe JwtController do
end
end
+ shared_examples "with invalid credentials" do
+ it "returns a generic error message" do
+ subject
+
+ expect(response).to have_gitlab_http_status(:unauthorized)
+ expect(json_response).to eq(
+ {
+ "errors" => [{
+ "code" => "UNAUTHORIZED",
+ "message" => "HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password. See http://www.example.com/help/user/profile/account/two_factor_authentication#troubleshooting"
+ }]
+ }
+ )
+ end
+ end
+
context 'authenticating against container registry' do
context 'existing service' do
subject! { get '/jwt/auth', params: parameters }
@@ -51,10 +67,7 @@ RSpec.describe JwtController do
context 'with blocked user' do
let(:user) { create(:user, :blocked) }
- it 'rejects the request as unauthorized' do
- expect(response).to have_gitlab_http_status(:unauthorized)
- expect(response.body).to include('HTTP Basic: Access denied')
- end
+ it_behaves_like 'with invalid credentials'
end
end
@@ -154,10 +167,7 @@ RSpec.describe JwtController do
let(:user) { create(:user, :two_factor) }
context 'without personal token' do
- it 'rejects the authorization attempt' do
- expect(response).to have_gitlab_http_status(:unauthorized)
- expect(response.body).to include('You must use a personal access token with \'api\' scope for Git over HTTP')
- end
+ it_behaves_like 'with invalid credentials'
end
context 'with personal token' do
@@ -181,14 +191,10 @@ RSpec.describe JwtController do
context 'using invalid login' do
let(:headers) { { authorization: credentials('invalid', 'password') } }
+ let(:subject) { get '/jwt/auth', params: parameters, headers: headers }
context 'when internal auth is enabled' do
- it 'rejects the authorization attempt' do
- get '/jwt/auth', params: parameters, headers: headers
-
- expect(response).to have_gitlab_http_status(:unauthorized)
- expect(response.body).not_to include('You must use a personal access token with \'api\' scope for Git over HTTP')
- end
+ it_behaves_like 'with invalid credentials'
end
context 'when internal auth is disabled' do
@@ -196,12 +202,7 @@ RSpec.describe JwtController do
stub_application_setting(password_authentication_enabled_for_git: false)
end
- it 'rejects the authorization attempt with personal access token message' do
- get '/jwt/auth', params: parameters, headers: headers
-
- expect(response).to have_gitlab_http_status(:unauthorized)
- expect(response.body).to include('You must use a personal access token with \'api\' scope for Git over HTTP')
- end
+ it_behaves_like 'with invalid credentials'
end
end
end
diff --git a/spec/support/shared_examples/requests/api/pypi_packages_shared_examples.rb b/spec/support/shared_examples/requests/api/pypi_packages_shared_examples.rb
index 1a248bb04e7..ba8311bf0be 100644
--- a/spec/support/shared_examples/requests/api/pypi_packages_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/pypi_packages_shared_examples.rb
@@ -170,6 +170,17 @@ RSpec.shared_examples 'PyPI package download' do |user_type, status, add_member
end
end
+RSpec.shared_examples 'rejected package download' do |user_type, status, add_member = true|
+ context "for user type #{user_type}" do
+ before do
+ project.send("add_#{user_type}", user) if add_member && user_type != :anonymous
+ group.send("add_#{user_type}", user) if add_member && user_type != :anonymous
+ end
+
+ it_behaves_like 'returning response status', status
+ end
+end
+
RSpec.shared_examples 'process PyPI api request' do |user_type, status, add_member = true|
context "for user type #{user_type}" do
before do
@@ -330,25 +341,25 @@ RSpec.shared_examples 'pypi file download endpoint' do
using RSpec::Parameterized::TableSyntax
context 'with valid project' do
- where(:visibility_level, :user_role, :member, :user_token) do
- :public | :developer | true | true
- :public | :guest | true | true
- :public | :developer | true | false
- :public | :guest | true | false
- :public | :developer | false | true
- :public | :guest | false | true
- :public | :developer | false | false
- :public | :guest | false | false
- :public | :anonymous | false | true
- :private | :developer | true | true
- :private | :guest | true | true
- :private | :developer | true | false
- :private | :guest | true | false
- :private | :developer | false | true
- :private | :guest | false | true
- :private | :developer | false | false
- :private | :guest | false | false
- :private | :anonymous | false | true
+ where(:visibility_level, :user_role, :member, :user_token, :shared_examples_name, :expected_status) do
+ :public | :developer | true | true | 'PyPI package download' | :success
+ :public | :guest | true | true | 'PyPI package download' | :success
+ :public | :developer | true | false | 'PyPI package download' | :success
+ :public | :guest | true | false | 'PyPI package download' | :success
+ :public | :developer | false | true | 'PyPI package download' | :success
+ :public | :guest | false | true | 'PyPI package download' | :success
+ :public | :developer | false | false | 'PyPI package download' | :success
+ :public | :guest | false | false | 'PyPI package download' | :success
+ :public | :anonymous | false | true | 'PyPI package download' | :success
+ :private | :developer | true | true | 'PyPI package download' | :success
+ :private | :guest | true | true | 'rejected package download' | :forbidden
+ :private | :developer | true | false | 'rejected package download' | :unauthorized
+ :private | :guest | true | false | 'rejected package download' | :unauthorized
+ :private | :developer | false | true | 'rejected package download' | :not_found
+ :private | :guest | false | true | 'rejected package download' | :not_found
+ :private | :developer | false | false | 'rejected package download' | :unauthorized
+ :private | :guest | false | false | 'rejected package download' | :unauthorized
+ :private | :anonymous | false | true | 'rejected package download' | :unauthorized
end
with_them do
@@ -360,7 +371,7 @@ RSpec.shared_examples 'pypi file download endpoint' do
group.update_column(:visibility_level, Gitlab::VisibilityLevel.level_value(visibility_level.to_s))
end
- it_behaves_like 'PyPI package download', params[:user_role], :success, params[:member]
+ it_behaves_like params[:shared_examples_name], params[:user_role], params[:expected_status], params[:member]
end
end
diff --git a/spec/validators/bytesize_validator_spec.rb b/spec/validators/bytesize_validator_spec.rb
new file mode 100644
index 00000000000..1914ccedd87
--- /dev/null
+++ b/spec/validators/bytesize_validator_spec.rb
@@ -0,0 +1,36 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe BytesizeValidator do
+ let(:model) do
+ Class.new do
+ include ActiveModel::Model
+ include ActiveModel::Validations
+
+ attr_accessor :content
+ alias_method :content_before_type_cast, :content
+
+ validates :content, bytesize: { maximum: -> { 7 } }
+ end.new
+ end
+
+ using RSpec::Parameterized::TableSyntax
+
+ where(:content, :validity, :errors) do
+ 'short' | true | {}
+ 'very long' | false | { content: ['is too long (9 Bytes). The maximum size is 7 Bytes.'] }
+ 'short😁' | false | { content: ['is too long (9 Bytes). The maximum size is 7 Bytes.'] }
+ 'short⇏' | false | { content: ['is too long (8 Bytes). The maximum size is 7 Bytes.'] }
+ end
+
+ with_them do
+ before do
+ model.content = content
+ model.validate
+ end
+
+ it { expect(model.valid?).to eq(validity) }
+ it { expect(model.errors.messages).to eq(errors) }
+ end
+end
diff --git a/spec/views/projects/commits/_commit.html.haml_spec.rb b/spec/views/projects/commits/_commit.html.haml_spec.rb
index 2ca23d4cb2d..4e8a680b6de 100644
--- a/spec/views/projects/commits/_commit.html.haml_spec.rb
+++ b/spec/views/projects/commits/_commit.html.haml_spec.rb
@@ -47,13 +47,12 @@ RSpec.describe 'projects/commits/_commit.html.haml' do
context 'with ci status' do
let(:ref) { 'master' }
- let(:user) { create(:user) }
+
+ let_it_be(:user) { create(:user) }
before do
allow(view).to receive(:current_user).and_return(user)
- project.add_developer(user)
-
create(
:ci_empty_pipeline,
ref: 'master',
@@ -80,18 +79,32 @@ RSpec.describe 'projects/commits/_commit.html.haml' do
end
context 'when pipelines are enabled' do
- before do
- allow(project).to receive(:builds_enabled?).and_return(true)
+ context 'when user has access' do
+ before do
+ project.add_developer(user)
+ end
+
+ it 'displays a ci status icon' do
+ render partial: template, formats: :html, locals: {
+ project: project,
+ ref: ref,
+ commit: commit
+ }
+
+ expect(rendered).to have_css('.ci-status-link')
+ end
end
- it 'does display a ci status icon when pipelines are enabled' do
- render partial: template, formats: :html, locals: {
- project: project,
- ref: ref,
- commit: commit
- }
+ context 'when user does not have access' do
+ it 'does not display a ci status icon' do
+ render partial: template, formats: :html, locals: {
+ project: project,
+ ref: ref,
+ commit: commit
+ }
- expect(rendered).to have_css('.ci-status-link')
+ expect(rendered).not_to have_css('.ci-status-link')
+ end
end
end
end