Merge remote-tracking branch 'dev/master'
This commit is contained in:
commit
13ea4b387d
32
CHANGELOG.md
32
CHANGELOG.md
|
|
@ -2,6 +2,24 @@
|
|||
documentation](doc/development/changelog.md) for instructions on adding your own
|
||||
entry.
|
||||
|
||||
## 11.1.2 (2018-07-26)
|
||||
|
||||
### Security (4 changes)
|
||||
|
||||
- Adding CSRF protection to Hooks test action.
|
||||
- Don't expose project names in GitHub counters.
|
||||
- Don't expose project names in various counters.
|
||||
- Fixed XSS in branch name in Web IDE.
|
||||
|
||||
### Fixed (1 change)
|
||||
|
||||
- Escapes milestone and label's names on flash notice when promoting them.
|
||||
|
||||
### Performance (1 change)
|
||||
|
||||
- Fix slow Markdown rendering. !20820
|
||||
|
||||
|
||||
## 11.1.1 (2018-07-23)
|
||||
|
||||
### Fixed (2 changes)
|
||||
|
|
@ -253,6 +271,20 @@ entry.
|
|||
- Use monospaced font for MR diff commit link ref on GFM.
|
||||
|
||||
|
||||
## 11.0.5 (2018-07-26)
|
||||
|
||||
### Security (4 changes)
|
||||
|
||||
- Don't expose project names in various counters.
|
||||
- Don't expose project names in GitHub counters.
|
||||
- Adding CSRF protection to Hooks test action.
|
||||
- Fixed XSS in branch name in Web IDE.
|
||||
|
||||
### Fixed (1 change)
|
||||
|
||||
- Escapes milestone and label's names on flash notice when promoting them.
|
||||
|
||||
|
||||
## 11.0.4 (2018-07-17)
|
||||
|
||||
### Security (1 change)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<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,
|
||||
);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
<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}`);
|
||||
},
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<script>
|
||||
import _ from 'underscore';
|
||||
import LoadingIcon from '~/vue_shared/components/loading_icon.vue';
|
||||
import StageColumnComponent from './stage_column_component.vue';
|
||||
|
||||
|
|
@ -26,7 +27,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) {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<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(' ');
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<script>
|
||||
import _ from 'underscore';
|
||||
import JobComponent from './job_component.vue';
|
||||
import DropdownJobComponent from './dropdown_job_component.vue';
|
||||
|
||||
|
|
@ -37,7 +38,7 @@ export default {
|
|||
},
|
||||
|
||||
jobId(job) {
|
||||
return `ci-badge-${job.name}`;
|
||||
return `ci-badge-${_.escape(job.name)}`;
|
||||
},
|
||||
|
||||
buildConnnectorClass(index) {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ module HooksHelper
|
|||
|
||||
trigger_human_name = trigger.to_s.tr('_', ' ').camelize
|
||||
|
||||
link_to path, rel: 'nofollow' do
|
||||
link_to path, rel: 'nofollow', method: :post do
|
||||
content_tag(:span, trigger_human_name)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -48,13 +48,13 @@ class RemoteMirror < ActiveRecord::Base
|
|||
state :failed
|
||||
|
||||
after_transition any => :started do |remote_mirror, _|
|
||||
Gitlab::Metrics.add_event(:remote_mirrors_running, path: remote_mirror.project.full_path)
|
||||
Gitlab::Metrics.add_event(:remote_mirrors_running)
|
||||
|
||||
remote_mirror.update(last_update_started_at: Time.now)
|
||||
end
|
||||
|
||||
after_transition started: :finished do |remote_mirror, _|
|
||||
Gitlab::Metrics.add_event(:remote_mirrors_finished, path: remote_mirror.project.full_path)
|
||||
Gitlab::Metrics.add_event(:remote_mirrors_finished)
|
||||
|
||||
timestamp = Time.now
|
||||
remote_mirror.update!(
|
||||
|
|
@ -63,7 +63,7 @@ class RemoteMirror < ActiveRecord::Base
|
|||
end
|
||||
|
||||
after_transition started: :failed do |remote_mirror, _|
|
||||
Gitlab::Metrics.add_event(:remote_mirrors_failed, path: remote_mirror.project.full_path)
|
||||
Gitlab::Metrics.add_event(:remote_mirrors_failed)
|
||||
|
||||
remote_mirror.update(last_update_at: Time.now)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1029,7 +1029,7 @@ class Repository
|
|||
end
|
||||
|
||||
def repository_event(event, tags = {})
|
||||
Gitlab::Metrics.add_event(event, { path: full_path }.merge(tags))
|
||||
Gitlab::Metrics.add_event(event, tags)
|
||||
end
|
||||
|
||||
def initialize_raw_repository
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
= 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}" }
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ module Gitlab
|
|||
|
||||
importer_class.new(object, project, client).execute
|
||||
|
||||
counter.increment(project: project.full_path)
|
||||
counter.increment
|
||||
end
|
||||
|
||||
def counter
|
||||
|
|
|
|||
|
|
@ -23,9 +23,7 @@ class RepositoryForkWorker
|
|||
def fork_repository(target_project, source_repository_storage_name, source_disk_path)
|
||||
return unless start_fork(target_project)
|
||||
|
||||
Gitlab::Metrics.add_event(:fork_repository,
|
||||
source_path: source_disk_path,
|
||||
target_path: target_project.disk_path)
|
||||
Gitlab::Metrics.add_event(:fork_repository)
|
||||
|
||||
result = gitlab_shell.fork_repository(source_repository_storage_name, source_disk_path,
|
||||
target_project.repository_storage, target_project.disk_path)
|
||||
|
|
|
|||
|
|
@ -11,9 +11,7 @@ class RepositoryImportWorker
|
|||
|
||||
return unless start_import(project)
|
||||
|
||||
Gitlab::Metrics.add_event(:import_repository,
|
||||
import_url: project.import_url,
|
||||
path: project.full_path)
|
||||
Gitlab::Metrics.add_event(:import_repository)
|
||||
|
||||
service = Projects::ImportService.new(project, project.creator)
|
||||
result = service.execute
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Escapes milestone and label's names on flash notice when promoting them
|
||||
merge_request:
|
||||
author:
|
||||
type: fixed
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Adding CSRF protection to Hooks test action
|
||||
merge_request:
|
||||
author:
|
||||
type: security
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fixed XSS in branch name in Web IDE
|
||||
merge_request:
|
||||
author:
|
||||
type: security
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
---
|
||||
title: Fix slow Markdown rendering
|
||||
merge_request: 20820
|
||||
author:
|
||||
type: performance
|
||||
|
|
@ -54,7 +54,7 @@ namespace :admin do
|
|||
|
||||
resources :hooks, only: [:index, :create, :edit, :update, :destroy] do
|
||||
member do
|
||||
get :test
|
||||
post :test
|
||||
end
|
||||
|
||||
resources :hook_logs, only: [:show] do
|
||||
|
|
|
|||
|
|
@ -301,7 +301,7 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
|
|||
|
||||
resources :hooks, only: [:index, :create, :edit, :update, :destroy], constraints: { id: /\d+/ } do
|
||||
member do
|
||||
get :test
|
||||
post :test
|
||||
end
|
||||
|
||||
resources :hook_logs, only: [:show] do
|
||||
|
|
|
|||
|
|
@ -108,8 +108,7 @@ module API
|
|||
|
||||
if result.valid?
|
||||
if result.build
|
||||
Gitlab::Metrics.add_event(:build_found,
|
||||
project: result.build.project.full_path)
|
||||
Gitlab::Metrics.add_event(:build_found)
|
||||
present result.build, with: Entities::JobRequest::Response
|
||||
else
|
||||
Gitlab::Metrics.add_event(:build_not_found)
|
||||
|
|
@ -140,8 +139,7 @@ module API
|
|||
|
||||
job.trace.set(params[:trace]) if params[:trace]
|
||||
|
||||
Gitlab::Metrics.add_event(:update_build,
|
||||
project: job.project.full_path)
|
||||
Gitlab::Metrics.add_event(:update_build)
|
||||
|
||||
case params[:state].to_s
|
||||
when 'running'
|
||||
|
|
|
|||
|
|
@ -36,10 +36,6 @@ module Gitlab
|
|||
@project ||= Project.find_by_full_path(project_path)
|
||||
end
|
||||
|
||||
def metrics_params
|
||||
super.merge(project: project&.full_path)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def create_issue
|
||||
|
|
|
|||
|
|
@ -40,10 +40,6 @@ module Gitlab
|
|||
@project ||= Project.find_by_full_path(project_path)
|
||||
end
|
||||
|
||||
def metrics_params
|
||||
super.merge(project: project&.full_path)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def create_merge_request
|
||||
|
|
|
|||
|
|
@ -28,10 +28,6 @@ module Gitlab
|
|||
record_name: 'comment')
|
||||
end
|
||||
|
||||
def metrics_params
|
||||
super.merge(project: project&.full_path)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def author
|
||||
|
|
|
|||
|
|
@ -20,10 +20,6 @@ module Gitlab
|
|||
noteable.unsubscribe(sent_notification.recipient)
|
||||
end
|
||||
|
||||
def metrics_params
|
||||
super.merge(project: project&.full_path)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def sent_notification
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ module Gitlab
|
|||
Rails.logger
|
||||
.info("GitHub importer finished updating repository for #{pname}")
|
||||
|
||||
repository_updates_counter.increment(project: pname)
|
||||
repository_updates_counter.increment
|
||||
end
|
||||
|
||||
def update_repository?(pr)
|
||||
|
|
|
|||
|
|
@ -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<img src=x onerror=alert(document.domain)>')
|
||||
|
||||
post :promote, namespace_id: project.namespace.to_param, project_id: project, id: label_1.to_param
|
||||
|
||||
expect(flash[:notice]).to eq("CCC<img src=x onerror=alert(document.domain)> 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<img src=x onerror=alert(document.domain)>')
|
||||
|
||||
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) }
|
||||
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
#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" />');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -0,0 +1,93 @@
|
|||
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(
|
||||
'<img src=x onerror=alert(document.domain)> - passed',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
@ -1,37 +1,33 @@
|
|||
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';
|
||||
|
||||
describe('graph component', () => {
|
||||
preloadFixtures('static/graph.html.raw');
|
||||
const GraphComponent = Vue.extend(graphComponent);
|
||||
let component;
|
||||
|
||||
let GraphComponent;
|
||||
|
||||
beforeEach(() => {
|
||||
loadFixtures('static/graph.html.raw');
|
||||
GraphComponent = Vue.extend(graphComponent);
|
||||
afterEach(() => {
|
||||
component.$destroy();
|
||||
});
|
||||
|
||||
describe('while is loading', () => {
|
||||
it('should render a loading icon', () => {
|
||||
const component = new GraphComponent({
|
||||
propsData: {
|
||||
isLoading: true,
|
||||
pipeline: {},
|
||||
},
|
||||
}).$mount('#js-pipeline-graph-vue');
|
||||
component = mountComponent(GraphComponent, {
|
||||
isLoading: true,
|
||||
pipeline: {},
|
||||
});
|
||||
|
||||
expect(component.$el.querySelector('.loading-icon')).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('with data', () => {
|
||||
it('should render the graph', () => {
|
||||
const component = new GraphComponent({
|
||||
propsData: {
|
||||
isLoading: false,
|
||||
pipeline: graphJSON,
|
||||
},
|
||||
}).$mount('#js-pipeline-graph-vue');
|
||||
component = mountComponent(GraphComponent, {
|
||||
isLoading: false,
|
||||
pipeline: graphJSON,
|
||||
});
|
||||
|
||||
expect(component.$el.classList.contains('js-pipeline-graph')).toEqual(true);
|
||||
|
||||
|
|
@ -52,4 +48,15 @@ describe('graph component', () => {
|
|||
expect(component.$el.querySelector('.stage-column-list')).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
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 <img src=x onerror=alert(document.domain)>');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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('<img src=x onerror=alert(document.domain)> - 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: [
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,8 +1,11 @@
|
|||
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,12 +33,10 @@ describe('stage column component', () => {
|
|||
mockJobs.push(mockedJob);
|
||||
}
|
||||
|
||||
component = new StageColumnComponent({
|
||||
propsData: {
|
||||
title: 'foo',
|
||||
jobs: mockJobs,
|
||||
},
|
||||
}).$mount();
|
||||
component = mountComponent(StageColumnComponent, {
|
||||
title: 'foo',
|
||||
jobs: mockJobs,
|
||||
});
|
||||
});
|
||||
|
||||
it('should render provided title', () => {
|
||||
|
|
@ -46,4 +46,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-<img src=x onerror=alert(document.domain)>');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -158,7 +158,6 @@ describe Gitlab::GithubImport::Importer::PullRequestsImporter do
|
|||
|
||||
expect(importer.repository_updates_counter)
|
||||
.to receive(:increment)
|
||||
.with(project: project.path_with_namespace)
|
||||
.and_call_original
|
||||
|
||||
Timecop.freeze do
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ end
|
|||
# edit_admin_hook GET /admin/hooks/:id(.:format) admin/hooks#edit
|
||||
describe Admin::HooksController, "routing" do
|
||||
it "to #test" do
|
||||
expect(get("/admin/hooks/1/test")).to route_to('admin/hooks#test', id: '1')
|
||||
expect(post("/admin/hooks/1/test")).to route_to('admin/hooks#test', id: '1')
|
||||
end
|
||||
|
||||
it "to #index" do
|
||||
|
|
|
|||
|
|
@ -389,7 +389,7 @@ describe 'project routing' do
|
|||
# DELETE /:project_id/hooks/:id(.:format) hooks#destroy
|
||||
describe Projects::HooksController, 'routing' do
|
||||
it 'to #test' do
|
||||
expect(get('/gitlab/gitlabhq/hooks/1/test')).to route_to('projects/hooks#test', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1')
|
||||
expect(post('/gitlab/gitlabhq/hooks/1/test')).to route_to('projects/hooks#test', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1')
|
||||
end
|
||||
|
||||
it_behaves_like 'RESTful project resources' do
|
||||
|
|
|
|||
|
|
@ -51,7 +51,6 @@ describe Gitlab::GithubImport::ObjectImporter do
|
|||
|
||||
expect(worker.counter)
|
||||
.to receive(:increment)
|
||||
.with(project: 'foo/bar')
|
||||
.and_call_original
|
||||
|
||||
worker.import(project, client, { 'number' => 10 })
|
||||
|
|
|
|||
|
|
@ -33,7 +33,6 @@ describe Gitlab::GithubImport::ImportDiffNoteWorker do
|
|||
|
||||
expect(worker.counter)
|
||||
.to receive(:increment)
|
||||
.with(project: 'foo/bar')
|
||||
.and_call_original
|
||||
|
||||
worker.import(project, client, hash)
|
||||
|
|
|
|||
|
|
@ -36,7 +36,6 @@ describe Gitlab::GithubImport::ImportIssueWorker do
|
|||
|
||||
expect(worker.counter)
|
||||
.to receive(:increment)
|
||||
.with(project: 'foo/bar')
|
||||
.and_call_original
|
||||
|
||||
worker.import(project, client, hash)
|
||||
|
|
|
|||
|
|
@ -31,7 +31,6 @@ describe Gitlab::GithubImport::ImportNoteWorker do
|
|||
|
||||
expect(worker.counter)
|
||||
.to receive(:increment)
|
||||
.with(project: 'foo/bar')
|
||||
.and_call_original
|
||||
|
||||
worker.import(project, client, hash)
|
||||
|
|
|
|||
|
|
@ -42,7 +42,6 @@ describe Gitlab::GithubImport::ImportPullRequestWorker do
|
|||
|
||||
expect(worker.counter)
|
||||
.to receive(:increment)
|
||||
.with(project: 'foo/bar')
|
||||
.and_call_original
|
||||
|
||||
worker.import(project, client, hash)
|
||||
|
|
|
|||
Loading…
Reference in New Issue