diff options
author | Robert Speicher <robert@gitlab.com> | 2017-05-10 18:26:02 +0000 |
---|---|---|
committer | Robert Speicher <robert@gitlab.com> | 2017-05-10 18:26:02 +0000 |
commit | 180ec7113e358a7f8388e1436dc0670a11ba68df (patch) | |
tree | fba74833a7e89d6c19160a0d7e02355c5215c8df /spec | |
parent | 09c4d27ae48ceb181f86657043af2a129c17dabf (diff) | |
parent | ebd8b7f60f41358df562625a4692f352b86b8c80 (diff) | |
download | gitlab-ce-180ec7113e358a7f8388e1436dc0670a11ba68df.tar.gz |
Merge branch 'bvl-security-patches' into 'master'
Security patches -> `master`
See merge request !11230
Diffstat (limited to 'spec')
-rw-r--r-- | spec/controllers/groups_controller_spec.rb | 35 | ||||
-rw-r--r-- | spec/controllers/snippets_controller_spec.rb | 34 | ||||
-rw-r--r-- | spec/features/dashboard/snippets_spec.rb | 47 | ||||
-rw-r--r-- | spec/features/projects/snippets_spec.rb | 24 | ||||
-rw-r--r-- | spec/features/snippets/explore_spec.rb | 25 | ||||
-rw-r--r-- | spec/features/snippets/internal_snippet_spec.rb | 23 | ||||
-rw-r--r-- | spec/features/users/snippets_spec.rb | 46 | ||||
-rw-r--r-- | spec/finders/groups_finder_spec.rb | 57 | ||||
-rw-r--r-- | spec/finders/snippets_finder_spec.rb | 125 | ||||
-rw-r--r-- | spec/helpers/submodule_helper_spec.rb | 12 | ||||
-rw-r--r-- | spec/lib/banzai/filter/external_link_filter_spec.rb | 85 | ||||
-rw-r--r-- | spec/lib/gitlab/asciidoc_spec.rb | 29 | ||||
-rw-r--r-- | spec/lib/gitlab/other_markup_spec.rb | 2 | ||||
-rw-r--r-- | spec/lib/gitlab/project_search_results_spec.rb | 75 | ||||
-rw-r--r-- | spec/models/snippet_spec.rb | 40 | ||||
-rw-r--r-- | spec/policies/project_snippet_policy_spec.rb | 80 | ||||
-rw-r--r-- | spec/views/projects/imports/new.html.haml_spec.rb | 22 |
17 files changed, 608 insertions, 153 deletions
diff --git a/spec/controllers/groups_controller_spec.rb b/spec/controllers/groups_controller_spec.rb index 073b87a1cb4..4c8d82a1677 100644 --- a/spec/controllers/groups_controller_spec.rb +++ b/spec/controllers/groups_controller_spec.rb @@ -26,6 +26,41 @@ describe GroupsController do end end + describe 'GET #subgroups' do + let!(:public_subgroup) { create(:group, :public, parent: group) } + let!(:private_subgroup) { create(:group, :private, parent: group) } + + context 'as a user' do + before do + sign_in(user) + end + + it 'shows the public subgroups' do + get :subgroups, id: group.to_param + + expect(assigns(:nested_groups)).to contain_exactly(public_subgroup) + end + + context 'being member' do + it 'shows public and private subgroups the user is member of' do + private_subgroup.add_guest(user) + + get :subgroups, id: group.to_param + + expect(assigns(:nested_groups)).to contain_exactly(public_subgroup, private_subgroup) + end + end + end + + context 'as a guest' do + it 'shows the public subgroups' do + get :subgroups, id: group.to_param + + expect(assigns(:nested_groups)).to contain_exactly(public_subgroup) + end + end + end + describe 'GET #issues' do let(:issue_1) { create(:issue, project: project) } let(:issue_2) { create(:issue, project: project) } diff --git a/spec/controllers/snippets_controller_spec.rb b/spec/controllers/snippets_controller_spec.rb index 41cd5bdcdd8..930415a4778 100644 --- a/spec/controllers/snippets_controller_spec.rb +++ b/spec/controllers/snippets_controller_spec.rb @@ -3,6 +3,34 @@ require 'spec_helper' describe SnippetsController do let(:user) { create(:user) } + describe 'GET #index' do + let(:user) { create(:user) } + + context 'when username parameter is present' do + it 'renders snippets of a user when username is present' do + get :index, username: user.username + + expect(response).to render_template(:index) + end + end + + context 'when username parameter is not present' do + it 'redirects to explore snippets page when user is not logged in' do + get :index + + expect(response).to redirect_to(explore_snippets_path) + end + + it 'redirects to snippets dashboard page when user is logged in' do + sign_in(user) + + get :index + + expect(response).to redirect_to(dashboard_snippets_path) + end + end + end + describe 'GET #new' do context 'when signed in' do before do @@ -132,7 +160,7 @@ describe SnippetsController do it 'responds with status 404' do get :show, id: 'doesntexist' - expect(response).to have_http_status(404) + expect(response).to redirect_to(new_user_session_path) end end end @@ -478,10 +506,10 @@ describe SnippetsController do end context 'when not signed in' do - it 'responds with status 404' do + it 'redirects to the sign in path' do get :raw, id: 'doesntexist' - expect(response).to have_http_status(404) + expect(response).to redirect_to(new_user_session_path) end end end diff --git a/spec/features/dashboard/snippets_spec.rb b/spec/features/dashboard/snippets_spec.rb index 62937688c22..c6ba118220a 100644 --- a/spec/features/dashboard/snippets_spec.rb +++ b/spec/features/dashboard/snippets_spec.rb @@ -12,4 +12,51 @@ describe 'Dashboard snippets', feature: true do it_behaves_like 'paginated snippets' end + + context 'filtering by visibility' do + let(:user) { create(:user) } + let!(:snippets) do + [ + create(:personal_snippet, :public, author: user), + create(:personal_snippet, :internal, author: user), + create(:personal_snippet, :private, author: user), + create(:personal_snippet, :public) + ] + end + + before do + login_as(user) + + visit dashboard_snippets_path + end + + it 'contains all snippets of logged user' do + expect(page).to have_selector('.snippet-row', count: 3) + + expect(page).to have_content(snippets[0].title) + expect(page).to have_content(snippets[1].title) + expect(page).to have_content(snippets[2].title) + end + + it 'contains all private snippets of logged user when clicking on private' do + click_link('Private') + + expect(page).to have_selector('.snippet-row', count: 1) + expect(page).to have_content(snippets[2].title) + end + + it 'contains all internal snippets of logged user when clicking on internal' do + click_link('Internal') + + expect(page).to have_selector('.snippet-row', count: 1) + expect(page).to have_content(snippets[1].title) + end + + it 'contains all public snippets of logged user when clicking on public' do + click_link('Public') + + expect(page).to have_selector('.snippet-row', count: 1) + expect(page).to have_content(snippets[0].title) + end + end end diff --git a/spec/features/projects/snippets_spec.rb b/spec/features/projects/snippets_spec.rb index d37e8ed4699..18689c17fe9 100644 --- a/spec/features/projects/snippets_spec.rb +++ b/spec/features/projects/snippets_spec.rb @@ -4,11 +4,27 @@ describe 'Project snippets', feature: true do context 'when the project has snippets' do let(:project) { create(:empty_project, :public) } let!(:snippets) { create_list(:project_snippet, 2, :public, author: project.owner, project: project) } - before do - allow(Snippet).to receive(:default_per_page).and_return(1) - visit namespace_project_snippets_path(project.namespace, project) + let!(:other_snippet) { create(:project_snippet) } + + context 'pagination' do + before do + allow(Snippet).to receive(:default_per_page).and_return(1) + + visit namespace_project_snippets_path(project.namespace, project) + end + + it_behaves_like 'paginated snippets' end - it_behaves_like 'paginated snippets' + context 'list content' do + it 'contains all project snippets' do + visit namespace_project_snippets_path(project.namespace, project) + + expect(page).to have_selector('.snippet-row', count: 2) + + expect(page).to have_content(snippets[0].title) + expect(page).to have_content(snippets[1].title) + end + end end end diff --git a/spec/features/snippets/explore_spec.rb b/spec/features/snippets/explore_spec.rb index 10a4597e467..fd097fe2e74 100644 --- a/spec/features/snippets/explore_spec.rb +++ b/spec/features/snippets/explore_spec.rb @@ -1,11 +1,11 @@ require 'rails_helper' feature 'Explore Snippets', feature: true do - scenario 'User should see snippets that are not private' do - public_snippet = create(:personal_snippet, :public) - internal_snippet = create(:personal_snippet, :internal) - private_snippet = create(:personal_snippet, :private) + let!(:public_snippet) { create(:personal_snippet, :public) } + let!(:internal_snippet) { create(:personal_snippet, :internal) } + let!(:private_snippet) { create(:personal_snippet, :private) } + scenario 'User should see snippets that are not private' do login_as create(:user) visit explore_snippets_path @@ -13,4 +13,21 @@ feature 'Explore Snippets', feature: true do expect(page).to have_content(internal_snippet.title) expect(page).not_to have_content(private_snippet.title) end + + scenario 'External user should see only public snippets' do + login_as create(:user, :external) + visit explore_snippets_path + + expect(page).to have_content(public_snippet.title) + expect(page).not_to have_content(internal_snippet.title) + expect(page).not_to have_content(private_snippet.title) + end + + scenario 'Not authenticated user should see only public snippets' do + visit explore_snippets_path + + expect(page).to have_content(public_snippet.title) + expect(page).not_to have_content(internal_snippet.title) + expect(page).not_to have_content(private_snippet.title) + end end diff --git a/spec/features/snippets/internal_snippet_spec.rb b/spec/features/snippets/internal_snippet_spec.rb new file mode 100644 index 00000000000..93382f4c359 --- /dev/null +++ b/spec/features/snippets/internal_snippet_spec.rb @@ -0,0 +1,23 @@ +require 'rails_helper' + +feature 'Internal Snippets', feature: true, js: true do + let(:internal_snippet) { create(:personal_snippet, :internal) } + + describe 'normal user' do + before do + login_as :user + end + + scenario 'sees internal snippets' do + visit snippet_path(internal_snippet) + + expect(page).to have_content(internal_snippet.content) + end + + scenario 'sees raw internal snippets' do + visit raw_snippet_path(internal_snippet) + + expect(page).to have_content(internal_snippet.content) + end + end +end diff --git a/spec/features/users/snippets_spec.rb b/spec/features/users/snippets_spec.rb index 1546a06b80c..4efbd672322 100644 --- a/spec/features/users/snippets_spec.rb +++ b/spec/features/users/snippets_spec.rb @@ -3,14 +3,46 @@ require 'spec_helper' describe 'Snippets tab on a user profile', feature: true, js: true do context 'when the user has snippets' do let(:user) { create(:user) } - let!(:snippets) { create_list(:snippet, 2, :public, author: user) } - before do - allow(Snippet).to receive(:default_per_page).and_return(1) - visit user_path(user) - page.within('.user-profile-nav') { click_link 'Snippets' } - wait_for_ajax + + context 'pagination' do + let!(:snippets) { create_list(:snippet, 2, :public, author: user) } + + before do + allow(Snippet).to receive(:default_per_page).and_return(1) + visit user_path(user) + page.within('.user-profile-nav') { click_link 'Snippets' } + wait_for_ajax + end + + it_behaves_like 'paginated snippets', remote: true end - it_behaves_like 'paginated snippets', remote: true + context 'list content' do + let!(:public_snippet) { create(:snippet, :public, author: user) } + let!(:internal_snippet) { create(:snippet, :internal, author: user) } + let!(:private_snippet) { create(:snippet, :private, author: user) } + let!(:other_snippet) { create(:snippet, :public) } + + it 'contains only internal and public snippets of a user when a user is logged in' do + login_as(:user) + visit user_path(user) + page.within('.user-profile-nav') { click_link 'Snippets' } + wait_for_ajax + + expect(page).to have_selector('.snippet-row', count: 2) + + expect(page).to have_content(public_snippet.title) + expect(page).to have_content(internal_snippet.title) + end + + it 'contains only public snippets of a user when a user is not logged in' do + visit user_path(user) + page.within('.user-profile-nav') { click_link 'Snippets' } + wait_for_ajax + + expect(page).to have_selector('.snippet-row', count: 1) + expect(page).to have_content(public_snippet.title) + end + end end end diff --git a/spec/finders/groups_finder_spec.rb b/spec/finders/groups_finder_spec.rb index d5d111e8d15..5b3591550c1 100644 --- a/spec/finders/groups_finder_spec.rb +++ b/spec/finders/groups_finder_spec.rb @@ -3,29 +3,64 @@ require 'spec_helper' describe GroupsFinder do describe '#execute' do let(:user) { create(:user) } - let!(:private_group) { create(:group, :private) } - let!(:internal_group) { create(:group, :internal) } - let!(:public_group) { create(:group, :public) } - let(:finder) { described_class.new } - describe 'execute' do - describe 'without a user' do - subject { finder.execute } + context 'root level groups' do + let!(:private_group) { create(:group, :private) } + let!(:internal_group) { create(:group, :internal) } + let!(:public_group) { create(:group, :public) } + + context 'without a user' do + subject { described_class.new.execute } it { is_expected.to eq([public_group]) } end - describe 'with a user' do - subject { finder.execute(user) } + context 'with a user' do + subject { described_class.new(user).execute } context 'normal user' do - it { is_expected.to eq([public_group, internal_group]) } + it { is_expected.to contain_exactly(public_group, internal_group) } end context 'external user' do let(:user) { create(:user, external: true) } - it { is_expected.to eq([public_group]) } + it { is_expected.to contain_exactly(public_group) } + end + + context 'user is member of the private group' do + before do + private_group.add_guest(user) + end + + it { is_expected.to contain_exactly(public_group, internal_group, private_group) } + end + end + end + + context 'subgroups' do + let!(:parent_group) { create(:group, :public) } + let!(:public_subgroup) { create(:group, :public, parent: parent_group) } + let!(:internal_subgroup) { create(:group, :internal, parent: parent_group) } + let!(:private_subgroup) { create(:group, :private, parent: parent_group) } + + context 'without a user' do + it 'only returns public subgroups' do + expect(described_class.new(nil, parent: parent_group).execute).to contain_exactly(public_subgroup) + end + end + + context 'with a user' do + it 'returns public and internal subgroups' do + expect(described_class.new(user, parent: parent_group).execute).to contain_exactly(public_subgroup, internal_subgroup) + end + + context 'being member' do + it 'returns public subgroups, internal subgroups, and private subgroups user is member of' do + private_subgroup.add_guest(user) + + expect(described_class.new(user, parent: parent_group).execute).to contain_exactly(public_subgroup, internal_subgroup, private_subgroup) + end end end end diff --git a/spec/finders/snippets_finder_spec.rb b/spec/finders/snippets_finder_spec.rb index cb6c80d1bd0..35f1683eef9 100644 --- a/spec/finders/snippets_finder_spec.rb +++ b/spec/finders/snippets_finder_spec.rb @@ -8,79 +8,145 @@ describe SnippetsFinder do let(:project1) { create(:empty_project, :public, group: group) } let(:project2) { create(:empty_project, :private, group: group) } - context ':all filter' do + context 'all snippets visible to a user' do let!(:snippet1) { create(:personal_snippet, :private) } let!(:snippet2) { create(:personal_snippet, :internal) } let!(:snippet3) { create(:personal_snippet, :public) } + let!(:project_snippet1) { create(:project_snippet, :private) } + let!(:project_snippet2) { create(:project_snippet, :internal) } + let!(:project_snippet3) { create(:project_snippet, :public) } it "returns all private and internal snippets" do - snippets = described_class.new.execute(user, filter: :all) - expect(snippets).to include(snippet2, snippet3) - expect(snippets).not_to include(snippet1) + snippets = described_class.new(user, scope: :all).execute + expect(snippets).to include(snippet2, snippet3, project_snippet2, project_snippet3) + expect(snippets).not_to include(snippet1, project_snippet1) end it "returns all public snippets" do - snippets = described_class.new.execute(nil, filter: :all) - expect(snippets).to include(snippet3) - expect(snippets).not_to include(snippet1, snippet2) + snippets = described_class.new(nil, scope: :all).execute + expect(snippets).to include(snippet3, project_snippet3) + expect(snippets).not_to include(snippet1, snippet2, project_snippet1, project_snippet2) + end + + it "returns all public and internal snippets for normal user" do + snippets = described_class.new(user).execute + + expect(snippets).to include(snippet2, snippet3, project_snippet2, project_snippet3) + expect(snippets).not_to include(snippet1, project_snippet1) + end + + it "returns all public snippets for non authorized user" do + snippets = described_class.new(nil).execute + + expect(snippets).to include(snippet3, project_snippet3) + expect(snippets).not_to include(snippet1, snippet2, project_snippet1, project_snippet2) + end + + it "returns all public and authored snippets for external user" do + external_user = create(:user, :external) + authored_snippet = create(:personal_snippet, :internal, author: external_user) + + snippets = described_class.new(external_user).execute + + expect(snippets).to include(snippet3, project_snippet3, authored_snippet) + expect(snippets).not_to include(snippet1, snippet2, project_snippet1, project_snippet2) end end - context ':public filter' do + context 'filter by visibility' do let!(:snippet1) { create(:personal_snippet, :private) } let!(:snippet2) { create(:personal_snippet, :internal) } let!(:snippet3) { create(:personal_snippet, :public) } - it "returns public public snippets" do - snippets = described_class.new.execute(nil, filter: :public) + it "returns public snippets when visibility is PUBLIC" do + snippets = described_class.new(nil, visibility: Snippet::PUBLIC).execute expect(snippets).to include(snippet3) expect(snippets).not_to include(snippet1, snippet2) end end - context ':by_user filter' do + context 'filter by scope' do + let!(:snippet1) { create(:personal_snippet, :private, author: user) } + let!(:snippet2) { create(:personal_snippet, :internal, author: user) } + let!(:snippet3) { create(:personal_snippet, :public, author: user) } + + it "returns all snippets for 'all' scope" do + snippets = described_class.new(user, scope: :all).execute + + expect(snippets).to include(snippet1, snippet2, snippet3) + end + + it "returns all snippets for 'are_private' scope" do + snippets = described_class.new(user, scope: :are_private).execute + + expect(snippets).to include(snippet1) + expect(snippets).not_to include(snippet2, snippet3) + end + + it "returns all snippets for 'are_interna;' scope" do + snippets = described_class.new(user, scope: :are_internal).execute + + expect(snippets).to include(snippet2) + expect(snippets).not_to include(snippet1, snippet3) + end + + it "returns all snippets for 'are_private' scope" do + snippets = described_class.new(user, scope: :are_public).execute + + expect(snippets).to include(snippet3) + expect(snippets).not_to include(snippet1, snippet2) + end + end + + context 'filter by author' do let!(:snippet1) { create(:personal_snippet, :private, author: user) } let!(:snippet2) { create(:personal_snippet, :internal, author: user) } let!(:snippet3) { create(:personal_snippet, :public, author: user) } it "returns all public and internal snippets" do - snippets = described_class.new.execute(user1, filter: :by_user, user: user) + snippets = described_class.new(user1, author: user).execute + expect(snippets).to include(snippet2, snippet3) expect(snippets).not_to include(snippet1) end it "returns internal snippets" do - snippets = described_class.new.execute(user, filter: :by_user, user: user, scope: "are_internal") + snippets = described_class.new(user, author: user, visibility: Snippet::INTERNAL).execute + expect(snippets).to include(snippet2) expect(snippets).not_to include(snippet1, snippet3) end it "returns private snippets" do - snippets = described_class.new.execute(user, filter: :by_user, user: user, scope: "are_private") + snippets = described_class.new(user, author: user, visibility: Snippet::PRIVATE).execute + expect(snippets).to include(snippet1) expect(snippets).not_to include(snippet2, snippet3) end it "returns public snippets" do - snippets = described_class.new.execute(user, filter: :by_user, user: user, scope: "are_public") + snippets = described_class.new(user, author: user, visibility: Snippet::PUBLIC).execute + expect(snippets).to include(snippet3) expect(snippets).not_to include(snippet1, snippet2) end it "returns all snippets" do - snippets = described_class.new.execute(user, filter: :by_user, user: user) + snippets = described_class.new(user, author: user).execute + expect(snippets).to include(snippet1, snippet2, snippet3) end it "returns only public snippets if unauthenticated user" do - snippets = described_class.new.execute(nil, filter: :by_user, user: user) + snippets = described_class.new(nil, author: user).execute + expect(snippets).to include(snippet3) expect(snippets).not_to include(snippet2, snippet1) end end - context 'by_project filter' do + context 'filter by project' do before do @snippet1 = create(:project_snippet, :private, project: project1) @snippet2 = create(:project_snippet, :internal, project: project1) @@ -88,43 +154,52 @@ describe SnippetsFinder do end it "returns public snippets for unauthorized user" do - snippets = described_class.new.execute(nil, filter: :by_project, project: project1) + snippets = described_class.new(nil, project: project1).execute + expect(snippets).to include(@snippet3) expect(snippets).not_to include(@snippet1, @snippet2) end it "returns public and internal snippets for non project members" do - snippets = described_class.new.execute(user, filter: :by_project, project: project1) + snippets = described_class.new(user, project: project1).execute + expect(snippets).to include(@snippet2, @snippet3) expect(snippets).not_to include(@snippet1) end it "returns public snippets for non project members" do - snippets = described_class.new.execute(user, filter: :by_project, project: project1, scope: "are_public") + snippets = described_class.new(user, project: project1, visibility: Snippet::PUBLIC).execute + expect(snippets).to include(@snippet3) expect(snippets).not_to include(@snippet1, @snippet2) end it "returns internal snippets for non project members" do - snippets = described_class.new.execute(user, filter: :by_project, project: project1, scope: "are_internal") + snippets = described_class.new(user, project: project1, visibility: Snippet::INTERNAL).execute + expect(snippets).to include(@snippet2) expect(snippets).not_to include(@snippet1, @snippet3) end it "does not return private snippets for non project members" do - snippets = described_class.new.execute(user, filter: :by_project, project: project1, scope: "are_private") + snippets = described_class.new(user, project: project1, visibility: Snippet::PRIVATE).execute + expect(snippets).not_to include(@snippet1, @snippet2, @snippet3) end it "returns all snippets for project members" do project1.team << [user, :developer] - snippets = described_class.new.execute(user, filter: :by_project, project: project1) + + snippets = described_class.new(user, project: project1).execute + expect(snippets).to include(@snippet1, @snippet2, @snippet3) end it "returns private snippets for project members" do project1.team << [user, :developer] - snippets = described_class.new.execute(user, filter: :by_project, project: project1, scope: "are_private") + + snippets = described_class.new(user, project: project1, visibility: Snippet::PRIVATE).execute + expect(snippets).to include(@snippet1) end end diff --git a/spec/helpers/submodule_helper_spec.rb b/spec/helpers/submodule_helper_spec.rb index 345bc33a67b..9da33792659 100644 --- a/spec/helpers/submodule_helper_spec.rb +++ b/spec/helpers/submodule_helper_spec.rb @@ -109,6 +109,18 @@ describe SubmoduleHelper do end context 'submodule on unsupported' do + it 'sanitizes unsupported protocols' do + stub_url('javascript:alert("XSS");') + + expect(helper.submodule_links(submodule_item)).to eq([nil, nil]) + end + + it 'sanitizes unsupported protocols disguised as a repository URL' do + stub_url('javascript:alert("XSS");foo/bar.git') + + expect(helper.submodule_links(submodule_item)).to eq([nil, nil]) + end + it 'returns original' do stub_url('http://mygitserver.com/gitlab-org/gitlab-ce') expect(submodule_links(submodule_item)).to eq([repo.submodule_url_for, nil]) diff --git a/spec/lib/banzai/filter/external_link_filter_spec.rb b/spec/lib/banzai/filter/external_link_filter_spec.rb index d9e4525cb28..6f6c215be87 100644 --- a/spec/lib/banzai/filter/external_link_filter_spec.rb +++ b/spec/lib/banzai/filter/external_link_filter_spec.rb @@ -1,5 +1,22 @@ require 'spec_helper' +shared_examples 'an external link with rel attribute' do + it 'adds rel="nofollow" to external links' do + expect(doc.at_css('a')).to have_attribute('rel') + expect(doc.at_css('a')['rel']).to include 'nofollow' + end + + it 'adds rel="noreferrer" to external links' do + expect(doc.at_css('a')).to have_attribute('rel') + expect(doc.at_css('a')['rel']).to include 'noreferrer' + end + + it 'adds rel="noopener" to external links' do + expect(doc.at_css('a')).to have_attribute('rel') + expect(doc.at_css('a')['rel']).to include 'noopener' + end +end + describe Banzai::Filter::ExternalLinkFilter, lib: true do include FilterSpecHelper @@ -22,49 +39,51 @@ describe Banzai::Filter::ExternalLinkFilter, lib: true do context 'for root links on document' do let(:doc) { filter %q(<a href="https://google.com/">Google</a>) } - it 'adds rel="nofollow" to external links' do - expect(doc.at_css('a')).to have_attribute('rel') - expect(doc.at_css('a')['rel']).to include 'nofollow' - end - - it 'adds rel="noreferrer" to external links' do - expect(doc.at_css('a')).to have_attribute('rel') - expect(doc.at_css('a')['rel']).to include 'noreferrer' - end + it_behaves_like 'an external link with rel attribute' end context 'for nested links on document' do let(:doc) { filter %q(<p><a href="https://google.com/">Google</a></p>) } - it 'adds rel="nofollow" to external links' do - expect(doc.at_css('a')).to have_attribute('rel') - expect(doc.at_css('a')['rel']).to include 'nofollow' + it_behaves_like 'an external link with rel attribute' + end + + context 'for invalid urls' do + it 'skips broken hrefs' do + doc = filter %q(<p><a href="don't crash on broken urls">Google</a></p>) + expected = %q(<p><a href="don't%20crash%20on%20broken%20urls">Google</a></p>) + + expect(doc.to_html).to eq(expected) end + end + + context 'for links with a username' do + context 'with a valid username' do + let(:doc) { filter %q(<a href="https://user@google.com/">Google</a>) } - it 'adds rel="noreferrer" to external links' do - expect(doc.at_css('a')).to have_attribute('rel') - expect(doc.at_css('a')['rel']).to include 'noreferrer' + it_behaves_like 'an external link with rel attribute' + end + + context 'with an impersonated username' do + let(:internal) { Gitlab.config.gitlab.url } + + let(:doc) { filter %Q(<a href="https://#{internal}@example.com" target="_blank">Reverse Tabnabbing</a>) } + + it_behaves_like 'an external link with rel attribute' end end context 'for non-lowercase scheme links' do - let(:doc_with_http) { filter %q(<p><a href="httP://google.com/">Google</a></p>) } - let(:doc_with_https) { filter %q(<p><a href="hTTpS://google.com/">Google</a></p>) } - - it 'adds rel="nofollow" to external links' do - expect(doc_with_http.at_css('a')).to have_attribute('rel') - expect(doc_with_https.at_css('a')).to have_attribute('rel') + context 'with http' do + let(:doc) { filter %q(<p><a href="httP://google.com/">Google</a></p>) } - expect(doc_with_http.at_css('a')['rel']).to include 'nofollow' - expect(doc_with_https.at_css('a')['rel']).to include 'nofollow' + it_behaves_like 'an external link with rel attribute' end - it 'adds rel="noreferrer" to external links' do - expect(doc_with_http.at_css('a')).to have_attribute('rel') - expect(doc_with_https.at_css('a')).to have_attribute('rel') + context 'with https' do + let(:doc) { filter %q(<p><a href="hTTpS://google.com/">Google</a></p>) } - expect(doc_with_http.at_css('a')['rel']).to include 'noreferrer' - expect(doc_with_https.at_css('a')['rel']).to include 'noreferrer' + it_behaves_like 'an external link with rel attribute' end it 'skips internal links' do @@ -84,14 +103,6 @@ describe Banzai::Filter::ExternalLinkFilter, lib: true do context 'for protocol-relative links' do let(:doc) { filter %q(<p><a href="//google.com/">Google</a></p>) } - it 'adds rel="nofollow" to external links' do - expect(doc.at_css('a')).to have_attribute('rel') - expect(doc.at_css('a')['rel']).to include 'nofollow' - end - - it 'adds rel="noreferrer" to external links' do - expect(doc.at_css('a')).to have_attribute('rel') - expect(doc.at_css('a')['rel']).to include 'noreferrer' - end + it_behaves_like 'an external link with rel attribute' end end diff --git a/spec/lib/gitlab/asciidoc_spec.rb b/spec/lib/gitlab/asciidoc_spec.rb index 0f47fb2fbd9..2c7ebb15fd7 100644 --- a/spec/lib/gitlab/asciidoc_spec.rb +++ b/spec/lib/gitlab/asciidoc_spec.rb @@ -22,7 +22,22 @@ module Gitlab expect(Asciidoctor).to receive(:convert) .with(input, expected_asciidoc_opts).and_return(html) - expect(render(input)).to eq(html) + expect(render(input, context)).to eq(html) + end + + context "with asciidoc_opts" do + it "merges the options with default ones" do + expected_asciidoc_opts = { + safe: :secure, + backend: :gitlab_html5, + attributes: described_class::DEFAULT_ADOC_ATTRS + } + + expect(Asciidoctor).to receive(:convert) + .with(input, expected_asciidoc_opts).and_return(html) + + render(input, context) + end end context "XSS" do @@ -33,7 +48,7 @@ module Gitlab }, 'images' => { input: 'image:https://localhost.com/image.png[Alt text" onerror="alert(7)]', - output: "<div>\n<p><span><img src=\"https://localhost.com/image.png\" alt=\"Alt text\"></span></p>\n</div>" + output: "<img src=\"https://localhost.com/image.png\" alt=\"Alt text\">" }, 'pre' => { input: '```mypre"><script>alert(3)</script>', @@ -43,10 +58,18 @@ module Gitlab links.each do |name, data| it "does not convert dangerous #{name} into HTML" do - expect(render(data[:input])).to eq(data[:output]) + expect(render(data[:input], context)).to include(data[:output]) end end end + + context 'external links' do + it 'adds the `rel` attribute to the link' do + output = render('link:https://google.com[Google]', context) + + expect(output).to include('rel="nofollow noreferrer noopener"') + end + end end def render(*args) diff --git a/spec/lib/gitlab/other_markup_spec.rb b/spec/lib/gitlab/other_markup_spec.rb index d6d53e8586c..c0f5fa9dc1f 100644 --- a/spec/lib/gitlab/other_markup_spec.rb +++ b/spec/lib/gitlab/other_markup_spec.rb @@ -13,7 +13,7 @@ describe Gitlab::OtherMarkup, lib: true do } links.each do |name, data| it "does not convert dangerous #{name} into HTML" do - expect(render(data[:file], data[:input])).to eq(data[:output]) + expect(render(data[:file], data[:input], context)).to eq(data[:output]) end end end diff --git a/spec/lib/gitlab/project_search_results_spec.rb b/spec/lib/gitlab/project_search_results_spec.rb index a7c8e7f1f57..6e0b1192706 100644 --- a/spec/lib/gitlab/project_search_results_spec.rb +++ b/spec/lib/gitlab/project_search_results_spec.rb @@ -22,8 +22,37 @@ describe Gitlab::ProjectSearchResults, lib: true do end describe 'blob search' do - let(:project) { create(:project, :repository) } - let(:results) { described_class.new(user, project, 'files').objects('blobs') } + let(:project) { create(:project, :public, :repository) } + + subject(:results) { described_class.new(user, project, 'files').objects('blobs') } + + context 'when repository is disabled' do + let(:project) { create(:project, :public, :repository, :repository_disabled) } + + it 'hides blobs from members' do + project.add_reporter(user) + + is_expected.to be_empty + end + + it 'hides blobs from non-members' do + is_expected.to be_empty + end + end + + context 'when repository is internal' do + let(:project) { create(:project, :public, :repository, :repository_private) } + + it 'finds blobs for members' do + project.add_reporter(user) + + is_expected.not_to be_empty + end + + it 'hides blobs from non-members' do + is_expected.to be_empty + end + end it 'finds by name' do expect(results).to include(["files/images/wm.svg", nil]) @@ -70,6 +99,46 @@ describe Gitlab::ProjectSearchResults, lib: true do end end + describe 'wiki search' do + let(:project) { create(:project, :public) } + let(:wiki) { build(:project_wiki, project: project) } + let!(:wiki_page) { wiki.create_page('Title', 'Content') } + + subject(:results) { described_class.new(user, project, 'Content').objects('wiki_blobs') } + + context 'when wiki is disabled' do + let(:project) { create(:project, :public, :wiki_disabled) } + + it 'hides wiki blobs from members' do + project.add_reporter(user) + + is_expected.to be_empty + end + + it 'hides wiki blobs from non-members' do + is_expected.to be_empty + end + end + + context 'when wiki is internal' do + let(:project) { create(:project, :public, :wiki_private) } + + it 'finds wiki blobs for members' do + project.add_reporter(user) + + is_expected.not_to be_empty + end + + it 'hides wiki blobs from non-members' do + is_expected.to be_empty + end + end + + it 'finds by content' do + expect(results).to include("master:Title.md:1:Content\n") + end + end + it 'does not list issues on private projects' do issue = create(:issue, project: project) @@ -79,7 +148,6 @@ describe Gitlab::ProjectSearchResults, lib: true do end describe 'confidential issues' do - let(:project) { create(:empty_project) } let(:query) { 'issue' } let(:author) { create(:user) } let(:assignee) { create(:user) } @@ -277,6 +345,7 @@ describe Gitlab::ProjectSearchResults, lib: true do context 'by commit hash' do let(:project) { create(:project, :public, :repository) } let(:commit) { project.repository.commit('0b4bc9a') } + commit_hashes = { short: '0b4bc9a', full: '0b4bc9a49b562e85de7cc9e834518ea6828729b9' } commit_hashes.each do |type, commit_hash| diff --git a/spec/models/snippet_spec.rb b/spec/models/snippet_spec.rb index 75b1fc7e216..1e5c96fe593 100644 --- a/spec/models/snippet_spec.rb +++ b/spec/models/snippet_spec.rb @@ -131,46 +131,6 @@ describe Snippet, models: true do end end - describe '.accessible_to' do - let(:author) { create(:author) } - let(:project) { create(:empty_project) } - - let!(:public_snippet) { create(:snippet, :public) } - let!(:internal_snippet) { create(:snippet, :internal) } - let!(:private_snippet) { create(:snippet, :private, author: author) } - - let!(:project_public_snippet) { create(:snippet, :public, project: project) } - let!(:project_internal_snippet) { create(:snippet, :internal, project: project) } - let!(:project_private_snippet) { create(:snippet, :private, project: project) } - - it 'returns only public snippets when user is blank' do - expect(described_class.accessible_to(nil)).to match_array [public_snippet, project_public_snippet] - end - - it 'returns only public, and internal snippets for regular users' do - user = create(:user) - - expect(described_class.accessible_to(user)).to match_array [public_snippet, internal_snippet, project_public_snippet, project_internal_snippet] - end - - it 'returns public, internal snippets and project private snippets for project members' do - member = create(:user) - project.team << [member, :developer] - - expect(described_class.accessible_to(member)).to match_array [public_snippet, internal_snippet, project_public_snippet, project_internal_snippet, project_private_snippet] - end - - it 'returns private snippets where the user is the author' do - expect(described_class.accessible_to(author)).to match_array [public_snippet, internal_snippet, private_snippet, project_public_snippet, project_internal_snippet] - end - - it 'returns all snippets when for admins' do - admin = create(:admin) - - expect(described_class.accessible_to(admin)).to match_array [public_snippet, internal_snippet, private_snippet, project_public_snippet, project_internal_snippet, project_private_snippet] - end - end - describe '#participants' do let(:project) { create(:empty_project, :public) } let(:snippet) { create(:snippet, content: 'foo', project: project) } diff --git a/spec/policies/project_snippet_policy_spec.rb b/spec/policies/project_snippet_policy_spec.rb index d0758af57dd..e1771b636b8 100644 --- a/spec/policies/project_snippet_policy_spec.rb +++ b/spec/policies/project_snippet_policy_spec.rb @@ -1,7 +1,9 @@ require 'spec_helper' describe ProjectSnippetPolicy, models: true do - let(:current_user) { create(:user) } + let(:regular_user) { create(:user) } + let(:external_user) { create(:user, :external) } + let(:project) { create(:empty_project) } let(:author_permissions) do [ @@ -10,13 +12,15 @@ describe ProjectSnippetPolicy, models: true do ] end - subject { described_class.abilities(current_user, project_snippet).to_set } + def abilities(user, snippet_visibility) + snippet = create(:project_snippet, snippet_visibility, project: project) - context 'public snippet' do - let(:project_snippet) { create(:project_snippet, :public) } + described_class.abilities(user, snippet).to_set + end + context 'public snippet' do context 'no user' do - let(:current_user) { nil } + subject { abilities(nil, :public) } it do is_expected.to include(:read_project_snippet) @@ -25,6 +29,17 @@ describe ProjectSnippetPolicy, models: true do end context 'regular user' do + subject { abilities(regular_user, :public) } + + it do + is_expected.to include(:read_project_snippet) + is_expected.not_to include(*author_permissions) + end + end + + context 'external user' do + subject { abilities(external_user, :public) } + it do is_expected.to include(:read_project_snippet) is_expected.not_to include(*author_permissions) @@ -33,10 +48,8 @@ describe ProjectSnippetPolicy, models: true do end context 'internal snippet' do - let(:project_snippet) { create(:project_snippet, :internal) } - context 'no user' do - let(:current_user) { nil } + subject { abilities(nil, :internal) } it do is_expected.not_to include(:read_project_snippet) @@ -45,6 +58,28 @@ describe ProjectSnippetPolicy, models: true do end context 'regular user' do + subject { abilities(regular_user, :internal) } + + it do + is_expected.to include(:read_project_snippet) + is_expected.not_to include(*author_permissions) + end + end + + context 'external user' do + subject { abilities(external_user, :internal) } + + it do + is_expected.not_to include(:read_project_snippet) + is_expected.not_to include(*author_permissions) + end + end + + context 'project team member external user' do + subject { abilities(external_user, :internal) } + + before { project.team << [external_user, :developer] } + it do is_expected.to include(:read_project_snippet) is_expected.not_to include(*author_permissions) @@ -53,10 +88,8 @@ describe ProjectSnippetPolicy, models: true do end context 'private snippet' do - let(:project_snippet) { create(:project_snippet, :private) } - context 'no user' do - let(:current_user) { nil } + subject { abilities(nil, :private) } it do is_expected.not_to include(:read_project_snippet) @@ -65,6 +98,8 @@ describe ProjectSnippetPolicy, models: true do end context 'regular user' do + subject { abilities(regular_user, :private) } + it do is_expected.not_to include(:read_project_snippet) is_expected.not_to include(*author_permissions) @@ -72,7 +107,9 @@ describe ProjectSnippetPolicy, models: true do end context 'snippet author' do - let(:project_snippet) { create(:project_snippet, :private, author: current_user) } + let(:snippet) { create(:project_snippet, :private, author: regular_user) } + + subject { described_class.abilities(regular_user, snippet).to_set } it do is_expected.to include(:read_project_snippet) @@ -80,8 +117,21 @@ describe ProjectSnippetPolicy, models: true do end end - context 'project team member' do - before { project_snippet.project.team << [current_user, :developer] } + context 'project team member normal user' do + subject { abilities(regular_user, :private) } + + before { project.team << [regular_user, :developer] } + + it do + is_expected.to include(:read_project_snippet) + is_expected.not_to include(*author_permissions) + end + end + + context 'project team member external user' do + subject { abilities(external_user, :private) } + + before { project.team << [external_user, :developer] } it do is_expected.to include(:read_project_snippet) @@ -90,7 +140,7 @@ describe ProjectSnippetPolicy, models: true do end context 'admin user' do - let(:current_user) { create(:admin) } + subject { abilities(create(:admin), :private) } it do is_expected.to include(:read_project_snippet) diff --git a/spec/views/projects/imports/new.html.haml_spec.rb b/spec/views/projects/imports/new.html.haml_spec.rb new file mode 100644 index 00000000000..9b293065797 --- /dev/null +++ b/spec/views/projects/imports/new.html.haml_spec.rb @@ -0,0 +1,22 @@ +require "spec_helper" + +describe "projects/imports/new.html.haml" do + let(:user) { create(:user) } + + context 'when import fails' do + let(:project) { create(:project_empty_repo, import_status: :failed, import_error: '<a href="http://googl.com">Foo</a>', import_type: :gitlab_project, import_source: '/var/opt/gitlab/gitlab-rails/shared/tmp/project_exports/uploads/t.tar.gz', import_url: nil) } + + before do + sign_in(user) + project.team << [user, :master] + end + + it "escapes HTML in import errors" do + assign(:project, project) + + render + + expect(rendered).not_to have_link('Foo', href: "http://googl.com") + end + end +end |