Skip to content
Snippets Groups Projects
Commit 96c2c29a authored by GitLab Release Tools Bot's avatar GitLab Release Tools Bot
Browse files

Merge remote-tracking branch 'dev/12-2-stable' into 12-2-stable

parents 635e1578 2b354bdc
No related branches found
No related tags found
No related merge requests found
Showing
with 426 additions and 79 deletions
Loading
Loading
@@ -11,11 +11,14 @@ module Gitlab
# Validates the given url according to the constraints specified by arguments.
#
# ports - Raises error if the given URL port does is not between given ports.
# allow_localhost - Raises error if URL resolves to a localhost IP address and argument is true.
# allow_local_network - Raises error if URL resolves to a link-local address and argument is true.
# allow_localhost - Raises error if URL resolves to a localhost IP address and argument is false.
# allow_local_network - Raises error if URL resolves to a link-local address and argument is false.
# ascii_only - Raises error if URL has unicode characters and argument is true.
# enforce_user - Raises error if URL user doesn't start with alphanumeric characters and argument is true.
# enforce_sanitization - Raises error if URL includes any HTML/CSS/JS tags and argument is true.
# require_absolute - Raises error if URL is not absolute and argument is true.
# Allow relative URLs beginning with slash when argument is false
# Raises error if relative URL does not begin with slash and argument is false
#
# Returns an array with [<uri>, <original-hostname>].
# rubocop:disable Metrics/ParameterLists
Loading
Loading
@@ -28,7 +31,8 @@ module Gitlab
ascii_only: false,
enforce_user: false,
enforce_sanitization: false,
dns_rebind_protection: true)
dns_rebind_protection: true,
require_absolute: true)
# rubocop:enable Metrics/ParameterLists
 
return [nil, nil] if url.nil?
Loading
Loading
@@ -42,14 +46,15 @@ module Gitlab
ports: ports,
enforce_sanitization: enforce_sanitization,
enforce_user: enforce_user,
ascii_only: ascii_only
ascii_only: ascii_only,
require_absolute: require_absolute
)
 
normalized_hostname = uri.normalized_host
hostname = uri.hostname
port = get_port(uri)
 
address_info = get_address_info(hostname, port)
address_info = get_address_info(hostname, port) if require_absolute || uri.absolute?
return [uri, nil] unless address_info
 
ip_address = ip_address(address_info)
Loading
Loading
@@ -98,12 +103,14 @@ module Gitlab
address_info.first&.ip_address
end
 
def validate_uri(uri:, schemes:, ports:, enforce_sanitization:, enforce_user:, ascii_only:)
def validate_uri(uri:, schemes:, ports:, enforce_sanitization:, enforce_user:, ascii_only:, require_absolute:)
validate_html_tags(uri) if enforce_sanitization
validate_absolute(uri) if require_absolute
validate_relative(uri) unless require_absolute
 
return if internal?(uri)
 
validate_scheme(uri.scheme, schemes)
validate_scheme(uri.scheme, schemes, require_absolute)
validate_port(get_port(uri), ports) if ports.any?
validate_user(uri.user) if enforce_user
validate_hostname(uri.hostname)
Loading
Loading
@@ -183,8 +190,20 @@ module Gitlab
raise BlockedUrlError, "Only allowed ports are #{ports.join(', ')}, and any over 1024"
end
 
def validate_scheme(scheme, schemes)
if scheme.blank? || (schemes.any? && !schemes.include?(scheme))
def validate_absolute(uri)
return if uri.absolute?
raise BlockedUrlError, 'must be absolute'
end
def validate_relative(uri)
return if uri.absolute? || uri.path.starts_with?('/')
raise BlockedUrlError, 'relative path must begin with a / (slash)'
end
def validate_scheme(scheme, schemes, require_absolute)
if (require_absolute && scheme.blank?) || (schemes.any? && !schemes.include?(scheme))
raise BlockedUrlError, "Only allowed schemes are #{schemes.join(', ')}"
end
end
Loading
Loading
@@ -243,9 +262,10 @@ module Gitlab
end
 
def internal_web?(uri)
uri.scheme == config.gitlab.protocol &&
uri.hostname == config.gitlab.host &&
(uri.port.blank? || uri.port == config.gitlab.port)
(uri.scheme.blank? && uri.hostname.blank? && uri.port.blank? && uri.path.starts_with?('/')) ||
(uri.scheme == config.gitlab.protocol &&
uri.hostname == config.gitlab.host &&
(uri.port.blank? || uri.port == config.gitlab.port))
end
 
def internal_shell?(uri)
Loading
Loading
Loading
Loading
@@ -176,12 +176,6 @@ describe ApplicationController do
expect(controller).to receive(:not_found)
controller.send(:route_not_found)
end
it 'does redirect to login page via authenticate_user! if not authenticated' do
allow(controller).to receive(:current_user).and_return(nil)
expect(controller).to receive(:authenticate_user!)
controller.send(:route_not_found)
end
end
 
describe '#set_page_title_header' do
Loading
Loading
Loading
Loading
@@ -19,7 +19,8 @@ describe InternalRedirect do
[
'Hello world',
'//example.com/hello/world',
'https://example.com/hello/world'
'https://example.com/hello/world',
"not-starting-with-a-slash\n/starting/with/slash"
]
end
 
Loading
Loading
Loading
Loading
@@ -16,13 +16,17 @@ describe LfsRequest do
end
 
def project
@project ||= Project.find(params[:id])
@project ||= Project.find_by(id: params[:id])
end
 
def download_request?
true
end
 
def upload_request?
false
end
def ci?
false
end
Loading
Loading
@@ -49,4 +53,41 @@ describe LfsRequest do
expect(assigns(:storage_project)).to eq(project)
end
end
context 'user is authenticated without access to lfs' do
before do
allow(controller).to receive(:authenticate_user)
allow(controller).to receive(:authentication_result) do
Gitlab::Auth::Result.new
end
end
context 'with access to the project' do
it 'returns 403' do
get :show, params: { id: project.id }
expect(response.status).to eq(403)
end
end
context 'without access to the project' do
context 'project does not exist' do
it 'returns 404' do
get :show, params: { id: 'does not exist' }
expect(response.status).to eq(404)
end
end
context 'project is private' do
let(:project) { create(:project, :private) }
it 'returns 404' do
get :show, params: { id: project.id }
expect(response.status).to eq(404)
end
end
end
end
end
Loading
Loading
@@ -8,6 +8,10 @@ describe Projects::AutocompleteSourcesController do
set(:issue) { create(:issue, project: project) }
set(:user) { create(:user) }
 
def members_by_username(username)
json_response.find { |member| member['username'] == username }
end
describe 'GET members' do
before do
group.add_owner(user)
Loading
Loading
@@ -17,22 +21,21 @@ describe Projects::AutocompleteSourcesController do
it 'returns an array of member object' do
get :members, format: :json, params: { namespace_id: group.path, project_id: project.path, type: issue.class.name, type_id: issue.id }
 
all = json_response.find {|member| member["username"] == 'all'}
the_group = json_response.find {|member| member["username"] == group.full_path}
the_user = json_response.find {|member| member["username"] == user.username}
expect(all.symbolize_keys).to include(username: 'all',
name: 'All Project and Group Members',
count: 1)
expect(the_group.symbolize_keys).to include(type: group.class.name,
name: group.full_name,
avatar_url: group.avatar_url,
count: 1)
expect(the_user.symbolize_keys).to include(type: user.class.name,
name: user.name,
avatar_url: user.avatar_url)
expect(members_by_username('all').symbolize_keys).to include(
username: 'all',
name: 'All Project and Group Members',
count: 1)
expect(members_by_username(group.full_path).symbolize_keys).to include(
type: group.class.name,
name: group.full_name,
avatar_url: group.avatar_url,
count: 1)
expect(members_by_username(user.username).symbolize_keys).to include(
type: user.class.name,
name: user.name,
avatar_url: user.avatar_url)
end
end
 
Loading
Loading
Loading
Loading
@@ -142,7 +142,7 @@ describe Projects::CommitsController do
 
context 'token authentication' do
context 'public project' do
it_behaves_like 'authenticates sessionless user', :show, :atom, public: true do
it_behaves_like 'authenticates sessionless user', :show, :atom, { public: true, ignore_incrementing: true } do
before do
public_project = create(:project, :repository, :public)
 
Loading
Loading
@@ -152,7 +152,7 @@ describe Projects::CommitsController do
end
 
context 'private project' do
it_behaves_like 'authenticates sessionless user', :show, :atom, public: false 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)
Loading
Loading
Loading
Loading
@@ -146,7 +146,7 @@ describe Projects::ErrorTrackingController do
it 'redirects to sign-in page' do
post :list_projects, params: list_projects_params
 
expect(response).to have_gitlab_http_status(:unauthorized)
expect(response).to have_gitlab_http_status(:redirect)
end
end
 
Loading
Loading
Loading
Loading
@@ -1349,7 +1349,7 @@ describe Projects::IssuesController do
context 'private project with token authentication' do
let(:private_project) { create(:project, :private) }
 
it_behaves_like 'authenticates sessionless user', :index, :atom do
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)
 
Loading
Loading
@@ -1357,7 +1357,7 @@ describe Projects::IssuesController do
end
end
 
it_behaves_like 'authenticates sessionless user', :calendar, :ics do
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)
 
Loading
Loading
Loading
Loading
@@ -41,7 +41,7 @@ describe Projects::TagsController do
context 'private project with token authentication' do
let(:private_project) { create(:project, :repository, :private) }
 
it_behaves_like 'authenticates sessionless user', :index, :atom do
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)
 
Loading
Loading
Loading
Loading
@@ -1053,7 +1053,7 @@ describe ProjectsController do
context 'private project with token authentication' do
let(:private_project) { create(:project, :private) }
 
it_behaves_like 'authenticates sessionless user', :show, :atom do
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)
 
Loading
Loading
Loading
Loading
@@ -827,7 +827,10 @@ describe 'Pipelines', :js do
context 'when project is private' do
let(:project) { create(:project, :private, :repository) }
 
it { expect(page).to have_content 'You need to sign in' }
it 'redirects the user to sign_in and displays the flash alert' do
expect(page).to have_content 'You need to sign in'
expect(page.current_path).to eq("/users/sign_in")
end
end
end
 
Loading
Loading
Loading
Loading
@@ -15,7 +15,7 @@ describe 'User views tags', :feature do
it do
visit project_tags_path(project, format: :atom)
 
expect(page).to have_gitlab_http_status(401)
expect(page.current_path).to eq("/users/sign_in")
end
end
 
Loading
Loading
Loading
Loading
@@ -127,6 +127,88 @@ describe LabelsFinder do
end
end
end
context 'when including labels from group projects with limited visibility' do
let(:finder) { described_class.new(user, group_id: group_4.id) }
let(:group_4) { create(:group) }
let(:limited_visibility_project) { create(:project, :public, group: group_4) }
let(:visible_project) { create(:project, :public, group: group_4) }
let!(:group_label_1) { create(:group_label, group: group_4) }
let!(:limited_visibility_label) { create(:label, project: limited_visibility_project) }
let!(:visible_label) { create(:label, project: visible_project) }
shared_examples 'with full visibility' do
it 'returns all projects labels' do
expect(finder.execute).to eq [group_label_1, limited_visibility_label, visible_label]
end
end
shared_examples 'with limited visibility' do
it 'returns only authorized projects labels' do
expect(finder.execute).to eq [group_label_1, visible_label]
end
end
context 'when merge requests and issues are not visible for non members' do
before do
limited_visibility_project.project_feature.update!(
merge_requests_access_level: ProjectFeature::PRIVATE,
issues_access_level: ProjectFeature::PRIVATE
)
end
context 'when user is not a group member' do
it_behaves_like 'with limited visibility'
end
context 'when user is a group member' do
before do
group_4.add_developer(user)
end
it_behaves_like 'with full visibility'
end
end
context 'when merge requests are not visible for non members' do
before do
limited_visibility_project.project_feature.update!(
merge_requests_access_level: ProjectFeature::PRIVATE
)
end
context 'when user is not a group member' do
it_behaves_like 'with full visibility'
end
context 'when user is a group member' do
before do
group_4.add_developer(user)
end
it_behaves_like 'with full visibility'
end
end
context 'when issues are not visible for non members' do
before do
limited_visibility_project.project_feature.update!(
issues_access_level: ProjectFeature::PRIVATE
)
end
context 'when user is not a group member' do
it_behaves_like 'with full visibility'
end
context 'when user is a group member' do
before do
group_4.add_developer(user)
end
it_behaves_like 'with full visibility'
end
end
end
 
context 'filtering by project_id' do
context 'when include_ancestor_groups is true' do
Loading
Loading
query allSchemaTypes {
__schema {
types {
fields {
type{
fields {
type {
fields {
type {
fields {
type {
fields {
type {
fields {
type {
fields {
type {
fields {
type {
fields {
type {
fields {
type {
fields {
type {
fields {
type {
fields {
name
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
\ No newline at end of file
{
project(fullPath: "gitlab-org/gitlab-ce") {
group {
projects {
edges {
node {
group {
projects {
edges {
node {
group {
projects {
edges {
node {
group {
projects {
edges {
node {
group {
projects {
edges {
node {
group {
description
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
query allSchemaTypes {
__schema {
types {
fields {
type {
fields {
type {
name
}
}
}
}
}
}
}
require 'spec_helper'
 
describe MarkupHelper do
let!(:project) { create(:project, :repository) }
let(:user) { create(:user, username: 'gfm') }
let(:commit) { project.commit }
let(:issue) { create(:issue, project: project) }
let(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
let(:snippet) { create(:project_snippet, project: project) }
before do
# Ensure the generated reference links aren't redacted
set(:project) { create(:project, :repository) }
set(:user) do
user = create(:user, username: 'gfm')
project.add_maintainer(user)
user
end
set(:issue) { create(:issue, project: project) }
set(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
set(:snippet) { create(:project_snippet, project: project) }
let(:commit) { project.commit }
 
before do
# Helper expects a @project instance variable
helper.instance_variable_set(:@project, project)
 
Loading
Loading
@@ -42,8 +42,8 @@ describe MarkupHelper do
 
describe "override default project" do
let(:actual) { issue.to_reference }
let(:second_project) { create(:project, :public) }
let(:second_issue) { create(:issue, project: second_project) }
set(:second_project) { create(:project, :public) }
set(:second_issue) { create(:issue, project: second_project) }
 
it 'links to the issue' do
expected = urls.project_issue_path(second_project, second_issue)
Loading
Loading
@@ -53,7 +53,7 @@ describe MarkupHelper do
 
describe 'uploads' do
let(:text) { "![ImageTest](/uploads/test.png)" }
let(:group) { create(:group) }
set(:group) { create(:group) }
 
subject { helper.markdown(text) }
 
Loading
Loading
@@ -75,7 +75,7 @@ describe MarkupHelper do
end
 
describe "with a group in the context" do
let(:project_in_group) { create(:project, group: group) }
set(:project_in_group) { create(:project, group: group) }
 
before do
helper.instance_variable_set(:@group, group)
Loading
Loading
@@ -233,38 +233,48 @@ describe MarkupHelper do
end
 
describe '#render_wiki_content' do
let(:wiki) { double('WikiPage', path: "file.#{extension}") }
let(:context) do
{
pipeline: :wiki, project: project, project_wiki: wiki,
page_slug: 'nested/page', issuable_state_filter_enabled: true
}
end
before do
@wiki = double('WikiPage')
allow(@wiki).to receive(:content).and_return('wiki content')
allow(@wiki).to receive(:slug).and_return('nested/page')
helper.instance_variable_set(:@project_wiki, @wiki)
expect(wiki).to receive(:content).and_return('wiki content')
expect(wiki).to receive(:slug).and_return('nested/page')
helper.instance_variable_set(:@project_wiki, wiki)
end
 
it "uses Wiki pipeline for markdown files" do
allow(@wiki).to receive(:format).and_return(:markdown)
context 'when file is Markdown' do
let(:extension) { 'md' }
 
expect(helper).to receive(:markdown_unsafe).with('wiki content',
pipeline: :wiki, project: project, project_wiki: @wiki, page_slug: "nested/page",
issuable_state_filter_enabled: true)
it 'renders using #markdown_unsafe helper method' do
expect(helper).to receive(:markdown_unsafe).with('wiki content', context)
 
helper.render_wiki_content(@wiki)
helper.render_wiki_content(wiki)
end
end
 
it "uses Asciidoctor for asciidoc files" do
allow(@wiki).to receive(:format).and_return(:asciidoc)
context 'when file is Asciidoc' do
let(:extension) { 'adoc' }
 
expect(helper).to receive(:asciidoc_unsafe).with('wiki content')
it 'renders using Gitlab::Asciidoc' do
expect(Gitlab::Asciidoc).to receive(:render)
 
helper.render_wiki_content(@wiki)
helper.render_wiki_content(wiki)
end
end
 
it "uses the Gollum renderer for all other file types" do
allow(@wiki).to receive(:format).and_return(:rdoc)
formatted_content_stub = double('formatted_content')
expect(formatted_content_stub).to receive(:html_safe)
allow(@wiki).to receive(:formatted_content).and_return(formatted_content_stub)
context 'any other format' do
let(:extension) { 'foo' }
 
helper.render_wiki_content(@wiki)
it 'renders all other formats using Gitlab::OtherMarkup' do
expect(Gitlab::OtherMarkup).to receive(:render)
helper.render_wiki_content(wiki)
end
end
end
 
Loading
Loading
Loading
Loading
@@ -2,7 +2,18 @@
require 'spec_helper'
 
describe Gitlab::UrlBlocker do
include StubRequests
describe '#validate!' do
shared_examples 'validates URI and hostname' do
it 'runs the url validations' do
uri, hostname = subject
expect(uri).to eq(Addressable::URI.parse(expected_uri))
expect(hostname).to eq(expected_hostname)
end
end
context 'when URI is nil' do
let(:import_url) { nil }
 
Loading
Loading
@@ -34,6 +45,14 @@ describe Gitlab::UrlBlocker do
expect(uri).to eq(Addressable::URI.parse('https://93.184.216.34'))
expect(hostname).to eq('example.org')
end
context 'when scheme is missing' do
let(:import_url) { '//example.org/path' }
it 'raises and error' do
expect { described_class.validate!(import_url) }.to raise_error(described_class::BlockedUrlError)
end
end
end
 
context 'when the URL hostname is an IP address' do
Loading
Loading
@@ -47,6 +66,32 @@ describe Gitlab::UrlBlocker do
end
end
 
context 'disabled require_absolute' do
subject { described_class.validate!(import_url, require_absolute: false) }
context 'with scheme and hostname' do
let(:import_url) { 'https://example.org/path' }
before do
stub_dns(import_url, ip_address: '93.184.216.34')
end
it_behaves_like 'validates URI and hostname' do
let(:expected_uri) { 'https://93.184.216.34/path' }
let(:expected_hostname) { 'example.org' }
end
end
context 'without scheme' do
let(:import_url) { '//93.184.216.34/path' }
it_behaves_like 'validates URI and hostname' do
let(:expected_uri) { import_url }
let(:expected_hostname) { nil }
end
end
end
context 'disabled DNS rebinding protection' do
context 'when URI is internal' do
let(:import_url) { 'http://localhost' }
Loading
Loading
@@ -520,6 +565,21 @@ describe Gitlab::UrlBlocker do
end
end
 
context 'when require_absolute is false' do
it 'allows paths' do
expect(described_class.blocked_url?('/foo/foo.bar', require_absolute: false)).to be false
end
it 'allows absolute urls without paths' do
expect(described_class.blocked_url?('http://example.com', require_absolute: false)).to be false
end
it 'paths must begin with a slash' do
expect(described_class.blocked_url?('foo/foo.bar', require_absolute: false)).to be true
expect(described_class.blocked_url?('', require_absolute: false)).to be true
end
end
it 'blocks urls with invalid ip address' do
stub_env('RSPEC_ALLOW_INVALID_URLS', 'false')
 
Loading
Loading
Loading
Loading
@@ -17,6 +17,7 @@ describe ApplicationSetting do
let(:http) { 'http://example.com' }
let(:https) { 'https://example.com' }
let(:ftp) { 'ftp://example.com' }
let(:javascript) { 'javascript:alert(window.opener.document.location)' }
 
it { is_expected.to allow_value(nil).for(:home_page_url) }
it { is_expected.to allow_value(http).for(:home_page_url) }
Loading
Loading
@@ -48,6 +49,11 @@ describe ApplicationSetting do
it { is_expected.not_to allow_value(nil).for(:outbound_local_requests_whitelist) }
it { is_expected.to allow_value([]).for(:outbound_local_requests_whitelist) }
 
it { is_expected.to allow_value(http).for(:grafana_url) }
it { is_expected.to allow_value(https).for(:grafana_url) }
it { is_expected.not_to allow_value(ftp).for(:grafana_url) }
it { is_expected.not_to allow_value(javascript).for(:grafana_url) }
context "when user accepted let's encrypt terms of service" do
before do
setting.update(lets_encrypt_terms_of_service_accepted: true)
Loading
Loading
Loading
Loading
@@ -206,6 +206,14 @@ describe Milestone do
end
end
 
describe '#to_ability_name' do
it 'returns milestone' do
milestone = build(:milestone)
expect(milestone.to_ability_name).to eq('milestone')
end
end
describe '.search' do
let(:milestone) { create(:milestone, title: 'foo', description: 'bar') }
 
Loading
Loading
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment