Commit a8ee7285 authored by GitLab Bot's avatar GitLab Bot

Merge remote-tracking branch 'upstream/master' into ce-to-ee-2018-07-27

# Conflicts:
#	Gemfile.rails5.lock
#	app/policies/concerns/policy_actor.rb
#	app/serializers/pipeline_serializer.rb
#	spec/javascripts/pipelines/graph/graph_component_spec.js
#	spec/javascripts/pipelines/graph/stage_column_component_spec.js
#	spec/serializers/pipeline_serializer_spec.rb

[ci skip]
parents 63e4129d 35ce06ad
......@@ -389,7 +389,7 @@ GEM
grape-entity (0.7.1)
activesupport (>= 4.0)
multi_json (>= 1.3.2)
grape-path-helpers (1.0.5)
grape-path-helpers (1.0.6)
activesupport (>= 4, < 5.1)
grape (~> 1.0)
rake (~> 12)
......@@ -403,9 +403,14 @@ GEM
google-protobuf (~> 3.1)
googleapis-common-protos-types (~> 1.0.0)
googleauth (>= 0.5.1, < 0.7)
<<<<<<< HEAD
gssapi (1.2.0)
ffi (>= 1.0.1)
haml (4.0.7)
=======
haml (5.0.4)
temple (>= 0.8.0)
>>>>>>> upstream/master
tilt
haml_lint (0.26.0)
haml (>= 4.0, < 5.1)
......@@ -427,7 +432,7 @@ GEM
hipchat (1.5.2)
httparty
mimemagic
html-pipeline (2.8.3)
html-pipeline (2.8.4)
activesupport (>= 2)
nokogiri (>= 1.4)
html2text (0.2.0)
......@@ -547,7 +552,7 @@ GEM
net-ssh (5.0.1)
netrc (0.11.0)
nio4r (2.3.1)
nokogiri (1.8.3)
nokogiri (1.8.4)
mini_portile2 (~> 2.3.0)
nokogumbo (1.5.0)
nokogiri
......@@ -850,7 +855,7 @@ GEM
et-orbi (~> 1.0)
rugged (0.27.2)
safe_yaml (1.0.4)
sanitize (4.6.5)
sanitize (4.6.6)
crass (~> 1.0.2)
nokogiri (>= 1.4.4)
nokogumbo (~> 1.4)
......@@ -944,7 +949,7 @@ GEM
rack (>= 1, < 3)
thor (0.19.4)
thread_safe (0.3.6)
tilt (2.0.6)
tilt (2.0.8)
timecop (0.8.1)
timfel-krb5-auth (0.8.3)
toml (0.1.2)
......
<script>
import _ from 'underscore';
import { mapActions, mapState, mapGetters } from 'vuex';
import { sprintf, __ } from '~/locale';
import * as consts from '../../stores/modules/commit/constants';
......@@ -14,7 +15,7 @@ export default {
commitToCurrentBranchText() {
return sprintf(
__('Commit to %{branchName} branch'),
{ branchName: `<strong class="monospace">${this.currentBranchId}</strong>` },
{ branchName: `<strong class="monospace">${_.escape(this.currentBranchId)}</strong>` },
false,
);
},
......
<script>
import $ from 'jquery';
import _ from 'underscore';
import JobNameComponent from './job_name_component.vue';
import JobComponent from './job_component.vue';
import tooltip from '../../../vue_shared/directives/tooltip';
......@@ -46,7 +47,7 @@ export default {
computed: {
tooltipText() {
return `${this.job.name} - ${this.job.status.label}`;
return _.escape(`${this.job.name} - ${this.job.status.label}`);
},
},
......
<script>
import _ from 'underscore';
import LoadingIcon from '~/vue_shared/components/loading_icon.vue';
import StageColumnComponent from './stage_column_component.vue';
import LinkedPipelinesColumn from 'ee/pipelines/components/graph/linked_pipelines_column.vue'; // eslint-disable-line import/first
......@@ -41,7 +42,8 @@ export default {
methods: {
capitalizeStageName(name) {
return name.charAt(0).toUpperCase() + name.slice(1);
const escapedName = _.escape(name);
return escapedName.charAt(0).toUpperCase() + escapedName.slice(1);
},
isFirstColumn(index) {
......
<script>
import _ from 'underscore';
import ActionComponent from './action_component.vue';
import JobNameComponent from './job_name_component.vue';
import tooltip from '../../../vue_shared/directives/tooltip';
......@@ -61,7 +62,7 @@ export default {
const textBuilder = [];
if (this.job.name) {
textBuilder.push(this.job.name);
textBuilder.push(_.escape(this.job.name));
}
if (this.job.name && this.status.tooltip) {
......@@ -69,7 +70,7 @@ export default {
}
if (this.status.tooltip) {
textBuilder.push(`${this.job.status.tooltip}`);
textBuilder.push(this.job.status.tooltip);
}
return textBuilder.join(' ');
......
<script>
import _ from 'underscore';
import JobComponent from './job_component.vue';
import DropdownJobComponent from './dropdown_job_component.vue';
......@@ -42,7 +43,7 @@ export default {
},
jobId(job) {
return `ci-badge-${job.name}`;
return `ci-badge-${_.escape(job.name)}`;
},
buildConnnectorClass(index) {
......
......@@ -179,6 +179,10 @@
font-weight: inherit;
}
a > code {
color: $gl-link-color;
}
dd {
margin-left: $gl-padding;
}
......
......@@ -112,7 +112,7 @@ class Projects::LabelsController < Projects::ApplicationController
begin
return render_404 unless promote_service.execute(@label)
flash[:notice] = "#{@label.title} promoted to <a href=\"#{group_labels_path(@project.group)}\">group label</a>.".html_safe
flash[:notice] = flash_notice_for(@label, @project.group)
respond_to do |format|
format.html do
redirect_to(project_labels_path(@project), status: :see_other)
......@@ -135,6 +135,15 @@ class Projects::LabelsController < Projects::ApplicationController
end
end
def flash_notice_for(label, group)
notice = ''.html_safe
notice << label.title
notice << ' promoted to '
notice << view_context.link_to('<u>group label</u>'.html_safe, group_labels_path(group))
notice << '.'
notice
end
protected
def label_params
......
......@@ -76,8 +76,8 @@ class Projects::MilestonesController < Projects::ApplicationController
def promote
promoted_milestone = Milestones::PromoteService.new(project, current_user).execute(milestone)
flash[:notice] = flash_notice_for(promoted_milestone, project.group)
flash[:notice] = "#{milestone.title} promoted to <a href=\"#{group_milestone_path(project.group, promoted_milestone.iid)}\"><u>group milestone</u></a>.".html_safe
respond_to do |format|
format.html do
redirect_to project_milestones_path(project)
......@@ -90,6 +90,15 @@ class Projects::MilestonesController < Projects::ApplicationController
redirect_to milestone, alert: error.message
end
def flash_notice_for(milestone, group)
notice = ''.html_safe
notice << milestone.title
notice << ' promoted to '
notice << view_context.link_to('<u>group milestone</u>'.html_safe, group_milestone_path(group, milestone.iid))
notice << '.'
notice
end
def destroy
return access_denied! unless can?(current_user, :admin_milestone, @project)
......
......@@ -126,7 +126,7 @@ class Notify < BaseMailer
fallback_reply_message_id = "<reply-#{reply_key}@#{Gitlab.config.gitlab.host}>".freeze
headers['References'] ||= []
headers['References'] << fallback_reply_message_id
headers['References'].unshift(fallback_reply_message_id)
@reply_by_email = true
end
......@@ -160,7 +160,7 @@ class Notify < BaseMailer
def mail_answer_thread(model, headers = {})
headers['Message-ID'] = "<#{SecureRandom.hex}@#{Gitlab.config.gitlab.host}>"
headers['In-Reply-To'] = message_id(model)
headers['References'] = message_id(model)
headers['References'] = [message_id(model)]
headers[:subject]&.prepend('Re: ')
......
......@@ -32,13 +32,12 @@ module Storage
begin
send_update_instructions
write_projects_repository_config
true
rescue
# Returning false does not rollback after_* transaction but gives
# us information about failing some of tasks
false
rescue => e
# Raise if development/test environment, else just notify Sentry
Gitlab::Sentry.track_exception(e, extra: { full_path_was: full_path_was, full_path: full_path, action: 'move_dir' })
end
true # false would cancel later callbacks but not rollback
end
# Hooks
......
......@@ -6,8 +6,11 @@
module PolicyActor
extend ActiveSupport::Concern
<<<<<<< HEAD
prepend EE::PolicyActor
=======
>>>>>>> upstream/master
def blocked?
false
end
......
......@@ -11,8 +11,11 @@ class PipelineSerializer < BaseSerializer
:retryable_builds,
:cancelable_statuses,
:trigger_requests,
<<<<<<< HEAD
{ triggered_by_pipeline: [:project, :user] },
{ triggered_pipelines: [:project, :user] },
=======
>>>>>>> upstream/master
:manual_actions,
:artifacts,
{
......
......@@ -86,7 +86,7 @@
- HasStatus::ORDERED_STATUSES.each do |build_status|
- builds.select{|build| build.status == build_status}.each do |build|
.build-job{ class: sidebar_build_class(build, @build), data: { stage: build.stage } }
- tooltip = build.tooltip_message
- tooltip = sanitize(build.tooltip_message.dup)
= link_to(project_job_path(@project, build), data: { toggle: 'tooltip', html: 'true', title: tooltip, container: 'body' }) do
= sprite_icon('arrow-right', size:16, css_class: 'icon-arrow-right')
%span{ class: "ci-status-icon-#{build.status}" }
......
---
title: Fix link color in markdown code brackets
merge_request: 20841
author:
type: fixed
---
title: Escapes milestone and label's names on flash notice when promoting them
merge_request:
author:
type: fixed
---
title: Fix namespace move callback behavior, especially to fix Geo replication of namespace moves during certain exceptions.
merge_request: 19297
author:
type: fixed
---
title: Rails5 update Gemfile.rails5.lock
merge_request: 20858
author: Jasper Maes
type: fixed
---
title: Fixed XSS in branch name in Web IDE
merge_request:
author:
type: security
---
title: Put fallback reply-key address first in the References header
merge_request: 20871
author:
type: changed
......@@ -17,7 +17,7 @@ module Gitlab
end
rescue GRPC::FailedPrecondition => e
raise Gitlab::Git::Conflict::Resolver::ConflictSideMissing.new(e.message)
rescue Rugged::ReferenceError, Rugged::OdbError, GRPC::BadStatus => e
rescue GRPC::BadStatus => e
raise Gitlab::Git::CommandError.new(e)
end
......
......@@ -558,7 +558,9 @@ module Gitlab
if is_enabled
gitaly_operation_client.user_update_branch(branch_name, user, newrev, oldrev)
else
OperationService.new(user, self).update_branch(branch_name, newrev, oldrev)
Gitlab::GitalyClient::StorageSettings.allow_disk_access do
OperationService.new(user, self).update_branch(branch_name, newrev, oldrev)
end
end
end
end
......@@ -832,18 +834,9 @@ module Gitlab
Gitlab::Git.check_namespace!(source_repository)
source_repository = RemoteRepository.new(source_repository) unless source_repository.is_a?(RemoteRepository)
message, status = GitalyClient.migrate(:fetch_ref) do |is_enabled|
if is_enabled
gitaly_fetch_ref(source_repository, source_ref: source_ref, target_ref: target_ref)
else
# When removing this code, also remove source_repository#path
# to remove deprecated method calls
local_fetch_ref(source_repository.path, source_ref: source_ref, target_ref: target_ref)
end
end
# Make sure ref was created, and raise Rugged::ReferenceError when not
raise Rugged::ReferenceError, message if status != 0
args = %W(fetch --no-tags -f #{GITALY_INTERNAL_URL} #{source_ref}:#{target_ref})
message, status = run_git(args, env: source_repository.fetch_env)
raise Gitlab::Git::CommandError, message if status != 0
target_ref
end
......@@ -1244,17 +1237,6 @@ module Gitlab
gitaly_repository_client.apply_gitattributes(revision)
end
def local_fetch_ref(source_path, source_ref:, target_ref:)
args = %W(fetch --no-tags -f #{source_path} #{source_ref}:#{target_ref})
run_git(args)
end
def gitaly_fetch_ref(source_repository, source_ref:, target_ref:)
args = %W(fetch --no-tags -f #{GITALY_INTERNAL_URL} #{source_ref}:#{target_ref})
run_git(args, env: source_repository.fetch_env)
end
def gitaly_delete_refs(*ref_names)
gitaly_ref_client.delete_refs(refs: ref_names) if ref_names.any?
end
......
......@@ -6,7 +6,9 @@ module Gitlab
if is_enabled
gitaly_ref_client.remote_branches(remote_name)
else
rugged_remote_branches(remote_name)
Gitlab::GitalyClient::StorageSettings.allow_disk_access do
rugged_remote_branches(remote_name)
end
end
end
end
......
......@@ -27,7 +27,11 @@ module Gitlab
# Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/1295
def fetch_ref
@project.repository.fetch_ref(@project.repository, source_ref: @diff_head_sha, target_ref: @merge_request.source_branch)
target_ref = Gitlab::Git::BRANCH_REF_PREFIX + @merge_request.source_branch
unless @project.repository.fetch_source_branch!(@project.repository, @diff_head_sha, target_ref)
Rails.logger.warn("Import/Export warning: Failed to create #{target_ref} for MR: #{@merge_request.iid}")
end
end
def branch_exists?(branch_name)
......
......@@ -143,6 +143,14 @@ describe Projects::LabelsController do
expect(GroupLabel.find_by(title: promoted_label_name)).not_to be_nil
end
it 'renders label name without parsing it as HTML' do
label_1.update!(name: 'CCC&lt;img src=x onerror=alert(document.domain)&gt;')
post :promote, namespace_id: project.namespace.to_param, project_id: project, id: label_1.to_param
expect(flash[:notice]).to eq("CCC&lt;img src=x onerror=alert(document.domain)&gt; promoted to <a href=\"#{group_labels_path(project.group)}\"><u>group label</u></a>.")
end
context 'service raising InvalidRecord' do
before do
expect_any_instance_of(Labels::PromoteService).to receive(:execute) do |label|
......
......@@ -127,6 +127,14 @@ describe Projects::MilestonesController do
expect(flash[:notice]).to eq("#{milestone.title} promoted to <a href=\"#{group_milestone_path(project.group, milestone.iid)}\"><u>group milestone</u></a>.")
expect(response).to redirect_to(project_milestones_path(project))
end
it 'renders milestone name without parsing it as HTML' do
milestone.update!(name: 'CCC&lt;img src=x onerror=alert(document.domain)&gt;')
post :promote, namespace_id: project.namespace.id, project_id: project.id, id: milestone.iid
expect(flash[:notice]).to eq("CCC promoted to <a href=\"#{group_milestone_path(project.group, milestone.iid)}\"><u>group milestone</u></a>.")
end
end
context 'promotion fails' do
......
......@@ -135,6 +135,20 @@ describe 'Jobs', :clean_gitlab_redis_shared_state do
end
end
context 'sidebar' do
let(:job) { create(:ci_build, :success, :trace_live, pipeline: pipeline, name: '<img src=x onerror=alert(document.domain)>') }
before do
visit project_job_path(project, job)
end
it 'renders escaped tooltip name' do
page.within('aside.right-sidebar') do
expect(find('.active.build-job a')['data-title']).to eq('<img src="x"> - passed')
end
end
end
context 'when job is not running', :js do
let(:job) { create(:ci_build, :success, :trace_artifact, pipeline: pipeline) }
......
......@@ -8,7 +8,7 @@ From: Jake the Dog <jake@adventuretime.ooo>
To: reply+59d8df8370b7e95c5a49fbf86aeb2c93@appmail.adventuretime.ooo
Message-ID: <CADkmRc+rNGAGGbV2iE5p918UVy4UyJqVcXRO2=otppgzduJSg@mail.gmail.com>
In-Reply-To: <issue_1@localhost>
References: <issue_1@localhost> <reply-59d8df8370b7e95c5a49fbf86aeb2c93@localhost>
References: <reply-59d8df8370b7e95c5a49fbf86aeb2c93@localhost> <issue_1@localhost>
Subject: re: [Discourse Meta] eviltrout posted in 'Adventure Time Sux'
Mime-Version: 1.0
Content-Type: text/plain;
......
......@@ -8,7 +8,7 @@ From: Jake the Dog <jake@adventuretime.ooo>
To: reply+59d8df8370b7e95c5a49fbf86aeb2c93@appmail.adventuretime.ooo
Message-ID: <CADkmRc+rNGAGGbV2iE5p918UVy4UyJqVcXRO2=otppgzduJSg@mail.gmail.com>
In-Reply-To: <issue_1@localhost>
References: <issue_1@localhost> <reply-59d8df8370b7e95c5a49fbf86aeb2c93@localhost>
References: <reply-59d8df8370b7e95c5a49fbf86aeb2c93@localhost> <issue_1@localhost>
Subject: re: [Discourse Meta] eviltrout posted in 'Adventure Time Sux'
Mime-Version: 1.0
Content-Type: text/plain;
......
......@@ -8,7 +8,7 @@ From: Jake the Dog <jake@adventuretime.ooo>
To: reply@appmail.adventuretime.ooo
Message-ID: <CADkmRc+rNGAGGbV2iE5p918UVy4UyJqVcXRO2=otppgzduJSg@mail.gmail.com>
In-Reply-To: <issue_1@localhost>
References: <issue_1@localhost> <reply-59d8df8370b7e95c5a49fbf86aeb2c93@localhost>
References: <reply-59d8df8370b7e95c5a49fbf86aeb2c93@localhost> <issue_1@localhost>
Subject: re: [Discourse Meta] eviltrout posted in 'Adventure Time Sux'
Mime-Version: 1.0
Content-Type: text/plain;
......
......@@ -8,7 +8,7 @@ From: Jake the Dog <jake@adventuretime.ooo>
To: reply@appmail.adventuretime.ooo
Message-ID: <CADkmRc+rNGAGGbV2iE5p918UVy4UyJqVcXRO2=otppgzduJSg@mail.gmail.com>
In-Reply-To: <issue_1@localhost>
References: <issue_1@localhost> <reply-59d8df8370b7e95c5a49fbf86aeb2c93@localhost>,<exchange@microsoft.com>
References: <reply-59d8df8370b7e95c5a49fbf86aeb2c93@localhost> <issue_1@localhost>,<exchange@microsoft.com>
Subject: re: [Discourse Meta] eviltrout posted in 'Adventure Time Sux'
Mime-Version: 1.0
Content-Type: text/plain;
......
......@@ -8,7 +8,7 @@ From: Jake the Dog <jake@adventuretime.ooo>
To: reply+59d8df8370b7e95c5a49fbf86aeb2c93@appmail.adventuretime.ooo
Message-ID: <CADkmRc+rNGAGGbV2iE5p918UVy4UyJqVcXRO2=otppgzduJSg@mail.gmail.com>
In-Reply-To: <issue_1@localhost>
References: <issue_1@localhost> <reply-59d8df8370b7e95c5a49fbf86aeb2c93@localhost>
References: <reply-59d8df8370b7e95c5a49fbf86aeb2c93@localhost> <issue_1@localhost>
Subject: re: [Discourse Meta] eviltrout posted in 'Adventure Time Sux'
Mime-Version: 1.0
Content-Type: text/plain;
......
......@@ -8,7 +8,7 @@ From: Jake the Dog <jake@adventuretime.ooo>
To: reply+59d8df8370b7e95c5a49fbf86aeb2c93@appmail.adventuretime.ooo
Message-ID: <CADkmRc+rNGAGGbV2iE5p918UVy4UyJqVcXRO2=otppgzduJSg@mail.gmail.com>
In-Reply-To: <issue_1@localhost>
References: <issue_1@localhost> <reply-59d8df8370b7e95c5a49fbf86aeb2c93@localhost>
References: <reply-59d8df8370b7e95c5a49fbf86aeb2c93@localhost> <issue_1@localhost>
Subject: re: [Discourse Meta] eviltrout posted in 'Adventure Time Sux'
Mime-Version: 1.0
Content-Type: text/plain;
......
#js-pipeline-graph-vue{ data: { endpoint: "foo" } }
......@@ -46,4 +46,12 @@ describe('IDE commit sidebar actions', () => {
done();
});
});
describe('commitToCurrentBranchText', () => {
it('escapes current branch', () => {
vm.$store.state.currentBranchId = '<img src="x" />';
expect(vm.commitToCurrentBranchText).not.toContain('<img src="x" />');
});
});
});
import Vue from 'vue';
import component from '~/pipelines/components/graph/dropdown_job_component.vue';
import mountComponent from 'spec/helpers/vue_mount_component_helper';
describe('dropdown job component', () => {
const Component = Vue.extend(component);
let vm;
const mock = {
jobs: [
{
id: 4256,
name: '<img src=x onerror=alert(document.domain)>',
status: {
icon: 'icon_status_success',
text: 'passed',
label: 'passed',
tooltip: 'passed',
group: 'success',
details_path: '/root/ci-mock/builds/4256',
has_details: true,
action: {
icon: 'retry',
title: 'Retry',
path: '/root/ci-mock/builds/4256/retry',
method: 'post',
},
},
},
{
id: 4299,
name: 'test',
status: {
icon: 'icon_status_success',
text: 'passed',
label: 'passed',
tooltip: 'passed',
group: 'success',
details_path: '/root/ci-mock/builds/4299',
has_details: true,
action: {
icon: 'retry',
title: 'Retry',
path: '/root/ci-mock/builds/4299/retry',
method: 'post',
},
},
},
],
name: 'rspec:linux',
size: 2,
status: {
icon: 'icon_status_success',
text: 'passed',
label: 'passed',
tooltip: 'passed',
group: 'success',
details_path: '/root/ci-mock/builds/4256',
has_details: true,
action: {
icon: 'retry',
title: 'Retry',
path: '/root/ci-mock/builds/4256/retry',
method: 'post',
},
},
};
afterEach(() => {
vm.$destroy();
});
beforeEach(() => {
vm = mountComponent(Component, { job: mock });
});
it('renders button with job name and size', () => {
expect(vm.$el.querySelector('button').textContent).toContain(mock.name);
expect(vm.$el.querySelector('button').textContent).toContain(mock.size);
});
it('renders dropdown with jobs', () => {
expect(vm.$el.querySelectorAll('.scrollable-menu>ul>li').length).toEqual(mock.jobs.length);
});
it('escapes tooltip title', () => {
expect(
vm.$el.querySelector('.js-pipeline-graph-job-link').getAttribute('data-original-title'),
).toEqual(
'&lt;img src=x onerror=alert(document.domain)&gt; - passed',
);
});
});
import Vue from 'vue';
import mountComponent from 'spec/helpers/vue_mount_component_helper';
import graphComponent from '~/pipelines/components/graph/graph_component.vue';
import graphJSON from './mock_data';
import linkedPipelineJSON from './linked_pipelines_mock_data';
<<<<<<< HEAD
const GraphComponent = Vue.extend(graphComponent);
const pipelineJSON = Object.assign(graphJSON, {
......@@ -61,6 +63,32 @@ describe('graph component', function () {
it('should include the left-margin class on the second child', function () {
const firstStageColumnElement = this.component.$el.querySelector('.stage-column-list .stage-column:last-child');
expect(firstStageColumnElement.classList.contains('left-margin')).toEqual(true);
=======
describe('graph component', () => {
const GraphComponent = Vue.extend(graphComponent);
let component;
afterEach(() => {
component.$destroy();
});
describe('while is loading', () => {
it('should render a loading icon', () => {
component = mountComponent(GraphComponent, {
isLoading: true,
pipeline: {},
});
expect(component.$el.querySelector('.loading-icon')).toBeDefined();
});
});
describe('with data', () => {
it('should render the graph', () => {
component = mountComponent(GraphComponent, {
isLoading: false,
pipeline: graphJSON,
>>>>>>> upstream/master
});
it('should include the has-linked-pipelines flag', function () {
......@@ -128,4 +156,15 @@ describe('graph component', function () {
});
});
});
describe('capitalizeStageName', () => {
it('capitalizes and escapes stage name', () => {
component = mountComponent(GraphComponent, {
isLoading: false,
pipeline: graphJSON,
});
expect(component.$el.querySelector('.stage-column:nth-child(2) .stage-name').textContent.trim()).toEqual('Deploy &lt;img src=x onerror=alert(document.domain)&gt;');
});
});
});
......@@ -3,7 +3,7 @@ import jobComponent from '~/pipelines/components/graph/job_component.vue';
import mountComponent from 'spec/helpers/vue_mount_component_helper';
describe('pipeline graph job component', () => {
let JobComponent;
const JobComponent = Vue.extend(jobComponent);
let component;
const mockJob = {
......@@ -26,10 +26,6 @@ describe('pipeline graph job component', () => {
},
};
beforeEach(() => {
JobComponent = Vue.extend(jobComponent);
});
afterEach(() => {
component.$destroy();
});
......@@ -165,4 +161,24 @@ describe('pipeline graph job component', () => {
expect(component.$el.querySelector(tooltipBoundary)).toBeNull();
});
});
describe('tooltipText', () => {
it('escapes job name', () => {
component = mountComponent(JobComponent, {
job: {
id: 4259,
name: '<img src=x onerror=alert(document.domain)>',
status: {
icon: 'icon_status_success',
label: 'success',
tooltip: 'failed',
},
},
});
expect(
component.$el.querySelector('.js-job-component-tooltip').getAttribute('data-original-title'),
).toEqual('&lt;img src=x onerror=alert(document.domain)&gt; - failed');
});
});
});
......@@ -91,7 +91,7 @@ export default {
dropdown_path: '/root/ci-mock/pipelines/123/stage.json?stage=test',
},
{
name: 'deploy',
name: 'deploy <img src=x onerror=alert(document.domain)>',
title: 'deploy: passed',
groups: [
{
......
import Vue from 'vue';
import stageColumnComponent from '~/pipelines/components/graph/stage_column_component.vue';
import mountComponent from 'spec/helpers/vue_mount_component_helper';
describe('stage column component', () => {
let component;
const StageColumnComponent = Vue.extend(stageColumnComponent);
const mockJob = {
id: 4250,
name: 'test',
......@@ -22,7 +25,6 @@ describe('stage column component', () => {
};
beforeEach(() => {
const StageColumnComponent = Vue.extend(stageColumnComponent);
const mockJobs = [];
for (let i = 0; i < 3; i += 1) {
......@@ -31,6 +33,7 @@ describe('stage column component', () => {
mockJobs.push(mockedJob);
}
<<<<<<< HEAD
component = new StageColumnComponent({
propsData: {
title: 'foo',
......@@ -38,6 +41,12 @@ describe('stage column component', () => {
hasTriggeredBy: false,
},
}).$mount();
=======
component = mountComponent(StageColumnComponent, {
title: 'foo',
jobs: mockJobs,
});
>>>>>>> upstream/master
});
it('should render provided title', () => {
......@@ -47,4 +56,27 @@ describe('stage column component', () => {
it('should render the provided jobs', () => {
expect(component.$el.querySelectorAll('.builds-container > ul > li').length).toEqual(3);
});
describe('jobId', () => {
it('escapes job name', () => {
component = mountComponent(StageColumnComponent, {
jobs: [
{
id: 4259,
name: '<img src=x onerror=alert(document.domain)>',
status: {
icon: 'icon_status_success',
label: 'success',
tooltip: '<img src=x onerror=alert(document.domain)>',
},
},
],
title: 'test',
});
expect(
component.$el.querySelector('.builds-container li').getAttribute('id'),
).toEqual('ci-badge-&lt;img src=x onerror=alert(document.domain)&gt;');
});
});
});
......@@ -16,7 +16,7 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do
@shared = @project.import_export_shared
allow(@shared).to receive(:export_path).and_return('spec/lib/gitlab/import_export/')
allow_any_instance_of(Repository).to receive(:fetch_ref).and_return(true)
allow_any_instance_of(Repository).to receive(:fetch_source_branch!).and_return(true)
allow_any_instance_of(Gitlab::Git::Repository).to receive(:branch_exists?).and_return(false)
expect_any_instance_of(Gitlab::Git::Repository).to receive(:create_branch).with('feature', 'DCBA')
......
......@@ -205,6 +205,34 @@ describe Namespace do
expect(gitlab_shell.exists?(project.repository_storage, "#{namespace.path}/#{project.path}.git")).to be_truthy
end
context 'when #write_projects_repository_config raises an error' do
context 'in test environment' do
it 'raises an exception' do
expect(namespace).to receive(:write_projects_repository_config).and_raise('foo')
expect do
namespace.update(path: namespace.full_path + '_new')
end.to raise_error('foo')
end
end
context 'in production environment' do
it 'does not cancel later callbacks' do
expect(namespace).to receive(:write_projects_repository_config).and_raise('foo')
expect(namespace).to receive(:move_dir).and_wrap_original do |m, *args|
move_dir_result = m.call(*args)
expect(move_dir_result).to be_truthy # Must be truthy, or else later callbacks would be canceled
move_dir_result
end
expect(Gitlab::Sentry).to receive(:should_raise?).and_return(false) # like prod
namespace.update(path: namespace.full_path + '_new')
end
end
end
context 'with subgroups', :nested_groups do
let(:parent) { create(:group, name: 'parent', path: 'parent') }
let(:new_parent) { create(:group, name: 'new_parent', path: 'new_parent') }
......
......@@ -1129,16 +1129,12 @@ describe Repository do
end
it 'raises Rugged::ReferenceError' do
raise_reference_error = raise_error(Rugged::ReferenceError) do |err|
expect(err.cause).to be_nil
end
expect do
Gitlab::Git::OperationService.new(git_user, target_project.repository.raw_repository)
.with_branch('feature',
start_repository: project.repository.raw_repository,
&:itself)
end.to raise_reference_error
end.to raise_error(Gitlab::Git::CommandError)
end
end
......
......@@ -125,7 +125,11 @@ describe PipelineSerializer do
it 'verifies number of queries', :request_store do
recorded = ActiveRecord::QueryRecorder.new { subject }
<<<<<<< HEAD
expect(recorded.count).to be_within(2).of(35)
=======
expect(recorded.count).to be_within(2).of(31)
>>>>>>> upstream/master
expect(recorded.cached_count).to eq(0)
end
end
......@@ -144,7 +148,11 @@ describe PipelineSerializer do
# pipeline. With the same ref this check is cached but if refs are
# different then there is an extra query per ref
# https://gitlab.com/gitlab-org/gitlab-ce/issues/46368
<<<<<<< HEAD
expect(recorded.count).to be_within(2).of(40)
=======
expect(recorded.count).to be_within(2).of(34)
>>>>>>> upstream/master
expect(recorded.cached_count).to eq(0)
end
end
......
......@@ -77,7 +77,7 @@ shared_examples 'a thread answer email with reply-by-email enabled' do
aggregate_failures do
is_expected.to have_header('Message-ID', /\A<.*@#{host}>\Z/)
is_expected.to have_header('In-Reply-To', "<#{route_key}@#{host}>")
is_expected.to have_header('References', /\A<#{route_key}@#{host}> <reply\-.*@#{host}>\Z/ )
is_expected.to have_header('References', /\A<reply\-.*@#{host}> <#{route_key}@#{host}>\Z/ )
is_expected.to have_subject(/^Re: /)
end
end
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment