diff options
author | Felipe Artur <felipefac@gmail.com> | 2018-07-26 18:30:50 -0300 |
---|---|---|
committer | Felipe Artur <felipefac@gmail.com> | 2018-07-26 18:30:50 -0300 |
commit | 13ea4b387ddcee5f5e8a59ac90dc9e485f4242f5 (patch) | |
tree | ea588d7aecbaa8868f23addf50748a14d167e342 | |
parent | caeb4597a5b24e0eaa96b24901ce9208c2eef4bf (diff) | |
parent | 45c94aba1b90dbe86c5583c8782cc3f624249fa1 (diff) | |
download | gitlab-ce-13ea4b387ddcee5f5e8a59ac90dc9e485f4242f5.tar.gz |
Merge remote-tracking branch 'dev/master'
45 files changed, 303 insertions, 89 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 7cc047c40ed..8ba36bbad7b 100644 --- a/CHANGELOG.md +++ b/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) diff --git a/app/assets/javascripts/ide/components/commit_sidebar/actions.vue b/app/assets/javascripts/ide/components/commit_sidebar/actions.vue index eb7cb9745ec..a8b5c7a16d0 100644 --- a/app/assets/javascripts/ide/components/commit_sidebar/actions.vue +++ b/app/assets/javascripts/ide/components/commit_sidebar/actions.vue @@ -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, ); }, diff --git a/app/assets/javascripts/pipelines/components/graph/dropdown_job_component.vue b/app/assets/javascripts/pipelines/components/graph/dropdown_job_component.vue index c32dc83da8e..14518f86dc7 100644 --- a/app/assets/javascripts/pipelines/components/graph/dropdown_job_component.vue +++ b/app/assets/javascripts/pipelines/components/graph/dropdown_job_component.vue @@ -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}`); }, }, diff --git a/app/assets/javascripts/pipelines/components/graph/graph_component.vue b/app/assets/javascripts/pipelines/components/graph/graph_component.vue index 4ec67f6c01b..1952dd453f4 100644 --- a/app/assets/javascripts/pipelines/components/graph/graph_component.vue +++ b/app/assets/javascripts/pipelines/components/graph/graph_component.vue @@ -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) { diff --git a/app/assets/javascripts/pipelines/components/graph/job_component.vue b/app/assets/javascripts/pipelines/components/graph/job_component.vue index 8af984ef91a..84a3d58b770 100644 --- a/app/assets/javascripts/pipelines/components/graph/job_component.vue +++ b/app/assets/javascripts/pipelines/components/graph/job_component.vue @@ -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(' '); diff --git a/app/assets/javascripts/pipelines/components/graph/stage_column_component.vue b/app/assets/javascripts/pipelines/components/graph/stage_column_component.vue index 2c728582b7c..e7b2de52f76 100644 --- a/app/assets/javascripts/pipelines/components/graph/stage_column_component.vue +++ b/app/assets/javascripts/pipelines/components/graph/stage_column_component.vue @@ -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) { diff --git a/app/controllers/projects/labels_controller.rb b/app/controllers/projects/labels_controller.rb index 21d3c918581..ce03b2d8d1d 100644 --- a/app/controllers/projects/labels_controller.rb +++ b/app/controllers/projects/labels_controller.rb @@ -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 diff --git a/app/controllers/projects/milestones_controller.rb b/app/controllers/projects/milestones_controller.rb index 5e86ec93f34..b9b3dcd5a85 100644 --- a/app/controllers/projects/milestones_controller.rb +++ b/app/controllers/projects/milestones_controller.rb @@ -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) diff --git a/app/helpers/hooks_helper.rb b/app/helpers/hooks_helper.rb index 551b9cca6b1..0a356ba55d2 100644 --- a/app/helpers/hooks_helper.rb +++ b/app/helpers/hooks_helper.rb @@ -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 diff --git a/app/models/remote_mirror.rb b/app/models/remote_mirror.rb index 976b501e297..6172bb38881 100644 --- a/app/models/remote_mirror.rb +++ b/app/models/remote_mirror.rb @@ -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 diff --git a/app/models/repository.rb b/app/models/repository.rb index e248f94cbd8..9873d9a6327 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -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 diff --git a/app/views/projects/jobs/_sidebar.html.haml b/app/views/projects/jobs/_sidebar.html.haml index b88fe47726d..64c441492bc 100644 --- a/app/views/projects/jobs/_sidebar.html.haml +++ b/app/views/projects/jobs/_sidebar.html.haml @@ -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}" } diff --git a/app/workers/concerns/gitlab/github_import/object_importer.rb b/app/workers/concerns/gitlab/github_import/object_importer.rb index 100d86e38c8..eeeff6e93a0 100644 --- a/app/workers/concerns/gitlab/github_import/object_importer.rb +++ b/app/workers/concerns/gitlab/github_import/object_importer.rb @@ -22,7 +22,7 @@ module Gitlab importer_class.new(object, project, client).execute - counter.increment(project: project.full_path) + counter.increment end def counter diff --git a/app/workers/repository_fork_worker.rb b/app/workers/repository_fork_worker.rb index 5ef9b744db3..68ec66e8499 100644 --- a/app/workers/repository_fork_worker.rb +++ b/app/workers/repository_fork_worker.rb @@ -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) diff --git a/app/workers/repository_import_worker.rb b/app/workers/repository_import_worker.rb index 25fec542ac7..8c64c513c74 100644 --- a/app/workers/repository_import_worker.rb +++ b/app/workers/repository_import_worker.rb @@ -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 diff --git a/changelogs/unreleased/48617-promoting-milestone.yml b/changelogs/unreleased/48617-promoting-milestone.yml new file mode 100644 index 00000000000..7fbc15051cf --- /dev/null +++ b/changelogs/unreleased/48617-promoting-milestone.yml @@ -0,0 +1,5 @@ +--- +title: Escapes milestone and label's names on flash notice when promoting them +merge_request: +author: +type: fixed diff --git a/changelogs/unreleased/security-fj-missing-csrf-system-hooks.yml b/changelogs/unreleased/security-fj-missing-csrf-system-hooks.yml new file mode 100644 index 00000000000..fabf48acbbc --- /dev/null +++ b/changelogs/unreleased/security-fj-missing-csrf-system-hooks.yml @@ -0,0 +1,5 @@ +--- +title: Adding CSRF protection to Hooks test action +merge_request: +author: +type: security diff --git a/changelogs/unreleased/security-ide-branch-name-xss.yml b/changelogs/unreleased/security-ide-branch-name-xss.yml new file mode 100644 index 00000000000..51742ffa4e9 --- /dev/null +++ b/changelogs/unreleased/security-ide-branch-name-xss.yml @@ -0,0 +1,5 @@ +--- +title: Fixed XSS in branch name in Web IDE +merge_request: +author: +type: security diff --git a/changelogs/unreleased/sh-revert-markdown-changes.yml b/changelogs/unreleased/sh-revert-markdown-changes.yml deleted file mode 100644 index 72540f710a1..00000000000 --- a/changelogs/unreleased/sh-revert-markdown-changes.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix slow Markdown rendering -merge_request: 20820 -author: -type: performance diff --git a/config/routes/admin.rb b/config/routes/admin.rb index ff27ceb50dc..109f00631fb 100644 --- a/config/routes/admin.rb +++ b/config/routes/admin.rb @@ -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 diff --git a/config/routes/project.rb b/config/routes/project.rb index 5057e937941..8e019f8c8bb 100644 --- a/config/routes/project.rb +++ b/config/routes/project.rb @@ -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 diff --git a/lib/api/runner.rb b/lib/api/runner.rb index d0cc0945a5f..06c034444a1 100644 --- a/lib/api/runner.rb +++ b/lib/api/runner.rb @@ -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' diff --git a/lib/gitlab/email/handler/create_issue_handler.rb b/lib/gitlab/email/handler/create_issue_handler.rb index 764f93f6d3d..fc8615afcae 100644 --- a/lib/gitlab/email/handler/create_issue_handler.rb +++ b/lib/gitlab/email/handler/create_issue_handler.rb @@ -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 diff --git a/lib/gitlab/email/handler/create_merge_request_handler.rb b/lib/gitlab/email/handler/create_merge_request_handler.rb index 2f864f2082b..2316e58c3fc 100644 --- a/lib/gitlab/email/handler/create_merge_request_handler.rb +++ b/lib/gitlab/email/handler/create_merge_request_handler.rb @@ -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 diff --git a/lib/gitlab/email/handler/create_note_handler.rb b/lib/gitlab/email/handler/create_note_handler.rb index 5791dbd0484..379b114e957 100644 --- a/lib/gitlab/email/handler/create_note_handler.rb +++ b/lib/gitlab/email/handler/create_note_handler.rb @@ -28,10 +28,6 @@ module Gitlab record_name: 'comment') end - def metrics_params - super.merge(project: project&.full_path) - end - private def author diff --git a/lib/gitlab/email/handler/unsubscribe_handler.rb b/lib/gitlab/email/handler/unsubscribe_handler.rb index ea80e21532e..56751e4e41e 100644 --- a/lib/gitlab/email/handler/unsubscribe_handler.rb +++ b/lib/gitlab/email/handler/unsubscribe_handler.rb @@ -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 diff --git a/lib/gitlab/github_import/importer/pull_requests_importer.rb b/lib/gitlab/github_import/importer/pull_requests_importer.rb index e70361c163b..a52866c4b08 100644 --- a/lib/gitlab/github_import/importer/pull_requests_importer.rb +++ b/lib/gitlab/github_import/importer/pull_requests_importer.rb @@ -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) diff --git a/spec/controllers/projects/labels_controller_spec.rb b/spec/controllers/projects/labels_controller_spec.rb index 273702e6d21..e03d23bcdf6 100644 --- a/spec/controllers/projects/labels_controller_spec.rb +++ b/spec/controllers/projects/labels_controller_spec.rb @@ -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| diff --git a/spec/controllers/projects/milestones_controller_spec.rb b/spec/controllers/projects/milestones_controller_spec.rb index ea906cf7f32..6c2d1c7e92b 100644 --- a/spec/controllers/projects/milestones_controller_spec.rb +++ b/spec/controllers/projects/milestones_controller_spec.rb @@ -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 diff --git a/spec/features/projects/jobs_spec.rb b/spec/features/projects/jobs_spec.rb index 35b1c46ecf6..83293c0ca7d 100644 --- a/spec/features/projects/jobs_spec.rb +++ b/spec/features/projects/jobs_spec.rb @@ -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) } diff --git a/spec/javascripts/fixtures/graph.html.haml b/spec/javascripts/fixtures/graph.html.haml deleted file mode 100644 index 4fedb0f1ded..00000000000 --- a/spec/javascripts/fixtures/graph.html.haml +++ /dev/null @@ -1 +0,0 @@ -#js-pipeline-graph-vue{ data: { endpoint: "foo" } } diff --git a/spec/javascripts/ide/components/commit_sidebar/actions_spec.js b/spec/javascripts/ide/components/commit_sidebar/actions_spec.js index 27f10caccb1..3a5d6c8a90b 100644 --- a/spec/javascripts/ide/components/commit_sidebar/actions_spec.js +++ b/spec/javascripts/ide/components/commit_sidebar/actions_spec.js @@ -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" />'); + }); + }); }); diff --git a/spec/javascripts/pipelines/graph/dropdown_job_component_spec.js b/spec/javascripts/pipelines/graph/dropdown_job_component_spec.js new file mode 100644 index 00000000000..608a0d4be67 --- /dev/null +++ b/spec/javascripts/pipelines/graph/dropdown_job_component_spec.js @@ -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', + ); + }); +}); diff --git a/spec/javascripts/pipelines/graph/graph_component_spec.js b/spec/javascripts/pipelines/graph/graph_component_spec.js index 713baa65a17..b6fa4272c8b 100644 --- a/spec/javascripts/pipelines/graph/graph_component_spec.js +++ b/spec/javascripts/pipelines/graph/graph_component_spec.js @@ -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)>'); + }); + }); }); diff --git a/spec/javascripts/pipelines/graph/job_component_spec.js b/spec/javascripts/pipelines/graph/job_component_spec.js index 56476253ad0..59f18d9397d 100644 --- a/spec/javascripts/pipelines/graph/job_component_spec.js +++ b/spec/javascripts/pipelines/graph/job_component_spec.js @@ -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'); + }); + }); }); diff --git a/spec/javascripts/pipelines/graph/mock_data.js b/spec/javascripts/pipelines/graph/mock_data.js index b2161d54bce..a4a5d78f906 100644 --- a/spec/javascripts/pipelines/graph/mock_data.js +++ b/spec/javascripts/pipelines/graph/mock_data.js @@ -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: [ { diff --git a/spec/javascripts/pipelines/graph/stage_column_component_spec.js b/spec/javascripts/pipelines/graph/stage_column_component_spec.js index 9d1e71fd117..f6e6bd3132e 100644 --- a/spec/javascripts/pipelines/graph/stage_column_component_spec.js +++ b/spec/javascripts/pipelines/graph/stage_column_component_spec.js @@ -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)>'); + }); + }); }); diff --git a/spec/lib/gitlab/github_import/importer/pull_requests_importer_spec.rb b/spec/lib/gitlab/github_import/importer/pull_requests_importer_spec.rb index b2e544e6fed..c51985f00a2 100644 --- a/spec/lib/gitlab/github_import/importer/pull_requests_importer_spec.rb +++ b/spec/lib/gitlab/github_import/importer/pull_requests_importer_spec.rb @@ -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 diff --git a/spec/routing/admin_routing_spec.rb b/spec/routing/admin_routing_spec.rb index 179fc9733ad..98df5f787f7 100644 --- a/spec/routing/admin_routing_spec.rb +++ b/spec/routing/admin_routing_spec.rb @@ -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 diff --git a/spec/routing/project_routing_spec.rb b/spec/routing/project_routing_spec.rb index 56d93095a85..70a7707826e 100644 --- a/spec/routing/project_routing_spec.rb +++ b/spec/routing/project_routing_spec.rb @@ -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 diff --git a/spec/workers/concerns/gitlab/github_import/object_importer_spec.rb b/spec/workers/concerns/gitlab/github_import/object_importer_spec.rb index 615462380e0..9c187bead0a 100644 --- a/spec/workers/concerns/gitlab/github_import/object_importer_spec.rb +++ b/spec/workers/concerns/gitlab/github_import/object_importer_spec.rb @@ -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 }) diff --git a/spec/workers/gitlab/github_import/import_diff_note_worker_spec.rb b/spec/workers/gitlab/github_import/import_diff_note_worker_spec.rb index 48e7eaf32fc..5b1c6b6010a 100644 --- a/spec/workers/gitlab/github_import/import_diff_note_worker_spec.rb +++ b/spec/workers/gitlab/github_import/import_diff_note_worker_spec.rb @@ -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) diff --git a/spec/workers/gitlab/github_import/import_issue_worker_spec.rb b/spec/workers/gitlab/github_import/import_issue_worker_spec.rb index 8cf6ac15919..ab070d6d081 100644 --- a/spec/workers/gitlab/github_import/import_issue_worker_spec.rb +++ b/spec/workers/gitlab/github_import/import_issue_worker_spec.rb @@ -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) diff --git a/spec/workers/gitlab/github_import/import_note_worker_spec.rb b/spec/workers/gitlab/github_import/import_note_worker_spec.rb index 677697c02df..3a30f06bb2d 100644 --- a/spec/workers/gitlab/github_import/import_note_worker_spec.rb +++ b/spec/workers/gitlab/github_import/import_note_worker_spec.rb @@ -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) diff --git a/spec/workers/gitlab/github_import/import_pull_request_worker_spec.rb b/spec/workers/gitlab/github_import/import_pull_request_worker_spec.rb index e287ddbe0d7..3cccd7cab21 100644 --- a/spec/workers/gitlab/github_import/import_pull_request_worker_spec.rb +++ b/spec/workers/gitlab/github_import/import_pull_request_worker_spec.rb @@ -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) |