Skip to content
Snippets Groups Projects
Commit 2fd92f2d authored by GitLab Bot's avatar GitLab Bot
Browse files

Add latest changes from gitlab-org/gitlab@master

parent 42ca24aa
No related branches found
No related tags found
No related merge requests found
Showing
with 424 additions and 26 deletions
Loading
Loading
@@ -54,7 +54,7 @@ class AutomatedCleanup
end
 
def perform_gitlab_environment_cleanup!(days_for_stop:, days_for_delete:)
puts "Checking for review apps not updated in the last #{days_for_stop} days..."
puts "Checking for Review Apps not updated in the last #{days_for_stop} days..."
 
checked_environments = []
delete_threshold = threshold_time(days: days_for_delete)
Loading
Loading
@@ -84,7 +84,7 @@ class AutomatedCleanup
elsif deployed_at < stop_threshold
stop_environment(environment, deployment)
else
print_release_state(subject: 'Review app', release_name: environment.slug, release_date: last_deploy, action: 'leaving')
print_release_state(subject: 'Review App', release_name: environment.slug, release_date: last_deploy, action: 'leaving')
end
 
checked_environments << environment.slug
Loading
Loading
@@ -94,9 +94,9 @@ class AutomatedCleanup
end
 
def perform_helm_releases_cleanup!(days:)
puts "Checking for Helm releases not updated in the last #{days} days..."
puts "Checking for Helm releases that are FAILED or not updated in the last #{days} days..."
 
threshold_day = threshold_time(days: days)
threshold = threshold_time(days: days)
 
releases_to_delete = []
 
Loading
Loading
@@ -104,7 +104,7 @@ class AutomatedCleanup
# Prevents deleting `dns-gitlab-review-app` releases or other unrelated releases
next unless release.name.start_with?('review-')
 
if release.status == 'FAILED' || release.last_update < threshold_day
if release.status == 'FAILED' || release.last_update < threshold
releases_to_delete << release
else
print_release_state(subject: 'Release', release_name: release.name, release_date: release.last_update, action: 'leaving')
Loading
Loading
@@ -180,14 +180,14 @@ end
 
automated_cleanup = AutomatedCleanup.new
 
timed('Review apps cleanup') do
automated_cleanup.perform_gitlab_environment_cleanup!(days_for_stop: 2, days_for_delete: 3)
timed('Review Apps cleanup') do
automated_cleanup.perform_gitlab_environment_cleanup!(days_for_stop: 5, days_for_delete: 6)
end
 
puts
 
timed('Helm releases cleanup') do
automated_cleanup.perform_helm_releases_cleanup!(days: 3)
automated_cleanup.perform_helm_releases_cleanup!(days: 7)
end
 
exit(0)
Loading
Loading
@@ -6,9 +6,13 @@ describe Groups::GroupLinksController do
let(:shared_with_group) { create(:group, :private) }
let(:shared_group) { create(:group, :private) }
let(:user) { create(:user) }
let(:group_member) { create(:user) }
let!(:project) { create(:project, group: shared_group) }
 
before do
sign_in(user)
shared_with_group.add_developer(group_member)
end
 
describe '#create' do
Loading
Loading
@@ -40,13 +44,9 @@ describe Groups::GroupLinksController do
end
 
context 'when user has correct access to both groups' do
let(:group_member) { create(:user) }
before do
shared_with_group.add_developer(user)
shared_group.add_owner(user)
shared_with_group.add_developer(group_member)
end
 
context 'when default access level is requested' do
Loading
Loading
@@ -56,6 +56,10 @@ describe Groups::GroupLinksController do
context 'when owner access is requested' do
let(:shared_group_access) { Gitlab::Access::OWNER }
 
before do
shared_with_group.add_owner(group_member)
end
include_examples 'creates group group link'
 
it 'allows admin access for group member' do
Loading
Loading
@@ -64,6 +68,10 @@ describe Groups::GroupLinksController do
end
end
 
it 'updates project permissions' do
expect { subject }.to change { group_member.can?(:read_project, project) }.from(false).to(true)
end
context 'when shared with group id is not present' do
let(:shared_with_group_id) { nil }
 
Loading
Loading
@@ -149,6 +157,7 @@ describe Groups::GroupLinksController do
context 'when user has admin access to the shared group' do
before do
shared_group.add_owner(user)
shared_with_group.refresh_members_authorized_projects
end
 
it 'updates existing link' do
Loading
Loading
@@ -162,6 +171,10 @@ describe Groups::GroupLinksController do
expect(link.group_access).to eq(Gitlab::Access::GUEST)
expect(link.expires_at).to eq(expiry_date)
end
it 'updates project permissions' do
expect { subject }.to change { group_member.can?(:create_release, project) }.from(true).to(false)
end
end
 
context 'when user does not have admin access to the shared group' do
Loading
Loading
@@ -199,11 +212,16 @@ describe Groups::GroupLinksController do
context 'when user has admin access to the shared group' do
before do
shared_group.add_owner(user)
shared_with_group.refresh_members_authorized_projects
end
 
it 'deletes existing link' do
expect { subject }.to change(GroupGroupLink, :count).by(-1)
end
it 'updates project permissions' do
expect { subject }.to change { group_member.can?(:create_release, project) }.from(true).to(false)
end
end
 
context 'when user does not have admin access to the shared group' do
Loading
Loading
Loading
Loading
@@ -260,8 +260,10 @@ describe SnippetsController do
context 'when the snippet description contains a file' do
include FileMoverHelpers
 
let(:picture_file) { "/-/system/user/#{user.id}/secret56/picture.jpg" }
let(:text_file) { "/-/system/user/#{user.id}/secret78/text.txt" }
let(:picture_secret) { SecureRandom.hex }
let(:text_secret) { SecureRandom.hex }
let(:picture_file) { "/-/system/user/#{user.id}/#{picture_secret}/picture.jpg" }
let(:text_file) { "/-/system/user/#{user.id}/#{text_secret}/text.txt" }
let(:description) do
"Description with picture: ![picture](/uploads#{picture_file}) and "\
"text: [text.txt](/uploads#{text_file})"
Loading
Loading
@@ -284,8 +286,8 @@ describe SnippetsController do
snippet = subject
 
expected_description = "Description with picture: "\
"![picture](/uploads/-/system/personal_snippet/#{snippet.id}/secret56/picture.jpg) and "\
"text: [text.txt](/uploads/-/system/personal_snippet/#{snippet.id}/secret78/text.txt)"
"![picture](/uploads/-/system/personal_snippet/#{snippet.id}/#{picture_secret}/picture.jpg) and "\
"text: [text.txt](/uploads/-/system/personal_snippet/#{snippet.id}/#{text_secret}/text.txt)"
 
expect(snippet.description).to eq(expected_description)
end
Loading
Loading
Loading
Loading
@@ -28,7 +28,7 @@ describe 'Issue Detail', :js do
visit project_issue_path(project, issue)
end
 
it 'encodes the description to prevent xss issues' do
it 'encodes the description to prevent xss issues', quarantine: 'https://gitlab.com/gitlab-org/gitlab/issues/207951' do
page.within('.issuable-details .detail-page-description') do
image = find('img.js-lazy-loaded')
 
Loading
Loading
Loading
Loading
@@ -5,9 +5,9 @@ require "spec_helper"
describe "User creates a merge request", :js do
include ProjectForksHelper
 
let_it_be(:project) { create(:project, :repository) }
let_it_be(:user) { create(:user) }
let(:title) { "Some feature" }
let(:project) { create(:project, :repository) }
let(:user) { create(:user) }
 
before do
project.add_maintainer(user)
Loading
Loading
@@ -38,6 +38,26 @@ describe "User creates a merge request", :js do
end
end
 
context "XSS branch name exists" do
before do
project.repository.create_branch("<img/src='x'/onerror=alert('oops')>", "master")
end
it "doesn't execute the dodgy branch name" do
visit(project_new_merge_request_path(project))
find(".js-source-branch").click
click_link("<img/src='x'/onerror=alert('oops')>")
find(".js-target-branch").click
click_link("feature")
click_button("Compare branches")
expect { page.driver.browser.switch_to.alert }.to raise_error(Selenium::WebDriver::Error::NoSuchAlertError)
end
end
context "to a forked project" do
let(:forked_project) { fork_project(project, user, namespace: user.namespace, repository: true) }
 
Loading
Loading
Loading
Loading
@@ -3,6 +3,8 @@
require 'spec_helper'
 
describe 'User updates wiki page' do
include WikiHelpers
let(:user) { create(:user) }
 
before do
Loading
Loading
@@ -11,8 +13,11 @@ describe 'User updates wiki page' do
end
 
context 'when wiki is empty' do
before do
before do |example|
visit(project_wikis_path(project))
wait_for_svg_to_be_loaded(example)
click_link "Create your first page"
end
 
Loading
Loading
Loading
Loading
@@ -6452,7 +6452,7 @@
]
},
{
"id": 37,
"id": 26,
"project_id": 5,
"ref": "master",
"sha": "048721d90c449b244b7b4c53a9186b04330174ec",
Loading
Loading
@@ -6744,7 +6744,7 @@
]
},
{
"id": 40,
"id": 19,
"project_id": 5,
"ref": "master",
"sha": "2ea1f3dec713d940208fb5ce4a38765ecb5d3f73",
Loading
Loading
@@ -6851,7 +6851,7 @@
]
},
{
"id": 42,
"id": 20,
"project_id": 5,
"ref": "master",
"sha": "ce84140e8b878ce6e7c4d298c7202ff38170e3ac",
Loading
Loading
Loading
Loading
@@ -137,6 +137,28 @@ describe('ErrorDetails', () => {
expect(wrapper.findAll(GlButton).length).toBe(3);
});
 
describe('unsafe chars for culprit field', () => {
const findReportedText = () => wrapper.find('[data-qa-selector="reported_text"]');
const culprit = '<script>console.log("surprise!")</script>';
beforeEach(() => {
store.state.details.loadingStacktrace = false;
wrapper.setData({
error: {
culprit,
},
});
});
it('should not convert interpolated text to html entities', () => {
expect(findReportedText().findAll('script').length).toEqual(0);
expect(findReportedText().findAll('strong').length).toEqual(1);
});
it('should render text instead of converting to html entities', () => {
expect(findReportedText().text()).toContain(culprit);
});
});
describe('Badges', () => {
it('should show language and error level badges', () => {
wrapper.setData({
Loading
Loading
Loading
Loading
@@ -5,5 +5,9 @@ require 'spec_helper'
describe GitlabSchema.types['DiffRefs'] do
it { expect(described_class.graphql_name).to eq('DiffRefs') }
 
it { expect(described_class).to have_graphql_fields(:base_sha, :head_sha, :start_sha) }
it { is_expected.to have_graphql_fields(:head_sha, :base_sha, :start_sha).only }
it { expect(described_class.fields['headSha'].type).to be_non_null }
it { expect(described_class.fields['baseSha'].type).not_to be_non_null }
it { expect(described_class.fields['startSha'].type).to be_non_null }
end
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::AssetProxy do
context 'when asset proxy is disabled' do
before do
stub_asset_proxy_setting(enabled: false)
end
it 'returns the original URL' do
url = 'http://example.com/test.png'
expect(described_class.proxy_url(url)).to eq(url)
end
end
context 'when asset proxy is enabled' do
before do
stub_asset_proxy_setting(whitelist: %w(gitlab.com *.mydomain.com))
stub_asset_proxy_setting(
enabled: true,
url: 'https://assets.example.com',
secret_key: 'shared-secret',
domain_regexp: Banzai::Filter::AssetProxyFilter.compile_whitelist(Gitlab.config.asset_proxy.whitelist)
)
end
it 'returns a proxied URL' do
url = 'http://example.com/test.png'
proxied_url = 'https://assets.example.com/08df250eeeef1a8cf2c761475ac74c5065105612/687474703a2f2f6578616d706c652e636f6d2f746573742e706e67'
expect(described_class.proxy_url(url)).to eq(proxied_url)
end
context 'whitelisted domain' do
it 'returns original URL for single domain whitelist' do
url = 'http://gitlab.com/test.png'
expect(described_class.proxy_url(url)).to eq(url)
end
it 'returns original URL for wildcard subdomain whitelist' do
url = 'http://test.mydomain.com/test.png'
expect(described_class.proxy_url(url)).to eq(url)
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::BackgroundMigration::RecalculateProjectAuthorizationsWithMinMaxUserId, :migration, schema: 20200204113224 do
let(:users_table) { table(:users) }
let(:min) { 1 }
let(:max) { 5 }
before do
min.upto(max) do |i|
users_table.create!(id: i, email: "user#{i}@example.com", projects_limit: 10)
end
end
describe '#perform' do
it 'initializes Users::RefreshAuthorizedProjectsService with correct users' do
min.upto(max) do |i|
user = User.find(i)
expect(Users::RefreshAuthorizedProjectsService).to(
receive(:new).with(user, any_args).and_call_original)
end
described_class.new.perform(min, max)
end
it 'executes Users::RefreshAuthorizedProjectsService' do
expected_call_counts = max - min + 1
service = instance_double(Users::RefreshAuthorizedProjectsService)
expect(Users::RefreshAuthorizedProjectsService).to(
receive(:new).exactly(expected_call_counts).times.and_return(service))
expect(service).to receive(:execute).exactly(expected_call_counts).times
described_class.new.perform(min, max)
end
end
end
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::DependencyLinker::BaseLinker do
let(:linker_class) do
Class.new(described_class) do
def link_dependencies
link_regex(%r{^(?<name>https?://[^ ]+)}, &:itself)
end
end
end
let(:plain_content) do
<<~CONTENT
http://\\njavascript:alert(1)
https://gitlab.com/gitlab-org/gitlab
CONTENT
end
let(:highlighted_content) do
<<~CONTENT
<span><span>http://</span><span>\\n</span><span>javascript:alert(1)</span></span>
<span><span>https://gitlab.com/gitlab-org/gitlab</span></span>
CONTENT
end
let(:linker) { linker_class.new(plain_content, highlighted_content) }
describe '#link' do
subject { linker.link }
it 'only converts valid links' do
expect(subject).to eq(
<<~CONTENT
<span><span>#{link('http://')}</span><span>#{link('\n', url: '%5Cn')}</span><span>#{link('javascript:alert(1)', url: nil)}</span></span>
<span><span>#{link('https://gitlab.com/gitlab-org/gitlab')}</span></span>
CONTENT
)
end
end
def link(text, url: text)
attrs = [
'rel="nofollow noreferrer noopener"',
'target="_blank"'
]
attrs.unshift(%{href="#{url}"}) if url
%{<a #{attrs.join(' ')}>#{text}</a>}
end
end
Loading
Loading
@@ -104,6 +104,24 @@ describe Gitlab::ImportExport::Project::TreeRestorer do
expect(pipeline.merge_request.source_branch).to eq('feature_conflict')
end
 
it 'restores pipelines based on ascending id order' do
expected_ordered_shas = %w[
2ea1f3dec713d940208fb5ce4a38765ecb5d3f73
ce84140e8b878ce6e7c4d298c7202ff38170e3ac
048721d90c449b244b7b4c53a9186b04330174ec
sha-notes
5f923865dde3436854e9ceb9cdb7815618d4e849
d2d430676773caa88cdaf7c55944073b2fd5561a
2ea1f3dec713d940208fb5ce4a38765ecb5d3f73
]
project = Project.find_by_path('project')
project.ci_pipelines.order(:id).each_with_index do |pipeline, i|
expect(pipeline['sha']).to eq expected_ordered_shas[i]
end
end
it 'preserves updated_at on issues' do
issue = Issue.where(description: 'Aliquam enim illo et possimus.').first
 
Loading
Loading
@@ -385,7 +403,7 @@ describe Gitlab::ImportExport::Project::TreeRestorer do
it 'has the correct number of pipelines and statuses' do
expect(@project.ci_pipelines.size).to eq(7)
 
@project.ci_pipelines.order(:id).zip([2, 2, 2, 2, 2, 0, 0])
@project.ci_pipelines.order(:id).zip([2, 0, 2, 2, 2, 2, 0])
.each do |(pipeline, expected_status_size)|
expect(pipeline.statuses.size).to eq(expected_status_size)
end
Loading
Loading
@@ -422,7 +440,7 @@ describe Gitlab::ImportExport::Project::TreeRestorer do
end
 
it 'restores external pull request for the restored pipeline' do
pipeline_with_external_pr = @project.ci_pipelines.order(:id).last
pipeline_with_external_pr = @project.ci_pipelines.where(source: 'external_pull_request_event').first
 
expect(pipeline_with_external_pr.external_pull_request).to be_persisted
end
Loading
Loading
Loading
Loading
@@ -109,6 +109,20 @@ describe Gitlab::ProjectAuthorizations do
end
end
 
context 'with lower group access level than max access level for share' do
let(:user) { create(:user) }
it 'creates proper authorizations' do
group.add_reporter(user)
mapping = map_access_levels(authorizations)
expect(mapping[project_parent.id]).to be_nil
expect(mapping[project.id]).to eq(Gitlab::Access::REPORTER)
expect(mapping[project_child.id]).to eq(Gitlab::Access::REPORTER)
end
end
context 'parent group user' do
let(:user) { parent_group_user }
 
Loading
Loading
Loading
Loading
@@ -30,6 +30,17 @@ describe Gitlab::UserAccess do
end
end
 
describe 'push to branch in an internal project' do
it 'will not infinitely loop when a project is internal' do
project.visibility_level = Gitlab::VisibilityLevel::INTERNAL
project.save!
expect(project).not_to receive(:branch_allows_collaboration?)
access.can_push_to_branch?('master')
end
end
describe 'push to empty project' do
let(:empty_project) { create(:project_empty_repo) }
let(:project_access) { described_class.new(user, project: empty_project) }
Loading
Loading
Loading
Loading
@@ -291,4 +291,18 @@ describe Gitlab::Utils do
expect(described_class.string_to_ip_object('1:0:0:0:0:0:0:0/124')).to eq(IPAddr.new('1:0:0:0:0:0:0:0/124'))
end
end
describe '.parse_url' do
it 'returns Addressable::URI object' do
expect(described_class.parse_url('http://gitlab.com')).to be_instance_of(Addressable::URI)
end
it 'returns nil when URI cannot be parsed' do
expect(described_class.parse_url('://gitlab.com')).to be nil
end
it 'returns nil with invalid parameter' do
expect(described_class.parse_url(1)).to be nil
end
end
end
# frozen_string_literal: true
require 'spec_helper'
require Rails.root.join('db', 'migrate', '20200214085940_clean_grafana_url.rb')
describe CleanGrafanaUrl, :migration do
let(:application_settings_table) { table(:application_settings) }
[
'javascript:alert(window.opener.document.location)',
' javascript:alert(window.opener.document.location)'
].each do |grafana_url|
it "sets grafana_url back to its default value when grafana_url is '#{grafana_url}'" do
application_settings = application_settings_table.create!(grafana_url: grafana_url)
migrate!
expect(application_settings.reload.grafana_url).to eq('/-/grafana')
end
end
['/-/grafana', '/some/relative/url', 'http://localhost:9000'].each do |grafana_url|
it "does not modify grafana_url when grafana_url is '#{grafana_url}'" do
application_settings = application_settings_table.create!(grafana_url: grafana_url)
migrate!
expect(application_settings.reload.grafana_url).to eq(grafana_url)
end
end
context 'when application_settings table has no rows' do
it 'does not fail' do
migrate!
end
end
end
# frozen_string_literal: true
require 'spec_helper'
require Rails.root.join('db', 'post_migrate', '20200204113224_schedule_recalculate_project_authorizations_second_run.rb')
describe ScheduleRecalculateProjectAuthorizationsSecondRun, :migration do
let(:users_table) { table(:users) }
before do
stub_const("#{described_class}::BATCH_SIZE", 2)
1.upto(4) do |i|
users_table.create!(id: i, name: "user#{i}", email: "user#{i}@example.com", projects_limit: 1)
end
end
it 'schedules background migration' do
Sidekiq::Testing.fake! do
Timecop.freeze do
migrate!
expect(BackgroundMigrationWorker.jobs.size).to eq(2)
expect(described_class::MIGRATION).to be_scheduled_migration(1, 2)
expect(described_class::MIGRATION).to be_scheduled_migration(3, 4)
end
end
end
end
Loading
Loading
@@ -19,6 +19,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
@@ -81,6 +82,53 @@ describe ApplicationSetting do
it { is_expected.not_to allow_value('abc').for(:minimum_password_length) }
it { is_expected.to allow_value(10).for(:minimum_password_length) }
 
context 'grafana_url validations' do
before do
subject.instance_variable_set(:@parsed_grafana_url, nil)
end
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) }
it { is_expected.to allow_value('/-/grafana').for(:grafana_url) }
it { is_expected.to allow_value('http://localhost:9000').for(:grafana_url) }
context 'when local URLs are not allowed in system hooks' do
before do
stub_application_setting(allow_local_requests_from_system_hooks: false)
end
it { is_expected.not_to allow_value('http://localhost:9000').for(:grafana_url) }
end
context 'with invalid grafana URL' do
it 'adds an error' do
subject.grafana_url = ' ' + http
expect(subject.save).to be false
expect(subject.errors[:grafana_url]).to eq([
'must be a valid relative or absolute URL. ' \
'Please check your Grafana URL setting in ' \
'Admin Area > Settings > Metrics and profiling > Metrics - Grafana'
])
end
end
context 'with blocked grafana URL' do
it 'adds an error' do
subject.grafana_url = javascript
expect(subject.save).to be false
expect(subject.errors[:grafana_url]).to eq([
'is blocked: Only allowed schemes are http, https. Please check your ' \
'Grafana URL setting in ' \
'Admin Area > Settings > Metrics and profiling > Metrics - Grafana'
])
end
end
end
context 'when snowplow is enabled' do
before do
setting.snowplow_enabled = true
Loading
Loading
Loading
Loading
@@ -91,6 +91,22 @@ describe Badge do
let(:method) { :image_url }
 
it_behaves_like 'rendered_links'
context 'when asset proxy is enabled' do
let(:placeholder_url) { 'http://www.example.com/image' }
before do
stub_asset_proxy_setting(
enabled: true,
url: 'https://assets.example.com',
secret_key: 'shared-secret'
)
end
it 'returns a proxied URL' do
expect(badge.rendered_image_url).to start_with('https://assets.example.com')
end
end
end
end
end
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