diff options
58 files changed, 383 insertions, 158 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index c7780abbaa4..57887b25881 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) @@ -1 +1 @@ -11.1.1 +11.1.2 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/ide/services/index.js b/app/assets/javascripts/ide/services/index.js index 49a481f25d5..cb93fba1665 100644 --- a/app/assets/javascripts/ide/services/index.js +++ b/app/assets/javascripts/ide/services/index.js @@ -18,7 +18,7 @@ export default { return axios .get(file.rawPath, { - params: { format: 'json' }, + transformResponse: [f => f], }) .then(({ data }) => data); }, @@ -33,7 +33,7 @@ export default { return axios .get(file.rawPath.replace(`/raw/${file.branchId}/${file.path}`, `/raw/${sha}/${file.path}`), { - params: { format: 'json' }, + transformResponse: [f => f], }) .then(({ data }) => data); }, 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/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss index c7b5e22c33d..ec4a0f378d0 100644 --- a/app/assets/stylesheets/framework/dropdowns.scss +++ b/app/assets/stylesheets/framework/dropdowns.scss @@ -822,7 +822,7 @@ header.header-content .dropdown-menu.frequent-items-dropdown-menu { display: flex; flex-direction: row; width: 500px; - height: 334px; + height: 354px; .frequent-items-dropdown-sidebar, .frequent-items-dropdown-content { @@ -868,6 +868,7 @@ header.header-content .dropdown-menu.frequent-items-dropdown-menu { } .frequent-items-list-container { + height: 304px; padding: 8px 0; overflow-y: auto; @@ -897,10 +898,6 @@ header.header-content .dropdown-menu.frequent-items-dropdown-menu { margin-top: 8px; } - .frequent-items-search-container { - height: 284px; - } - @include media-breakpoint-down(xs) { .frequent-items-list-container { width: auto; diff --git a/app/controllers/projects/labels_controller.rb b/app/controllers/projects/labels_controller.rb index 91016f6494e..83216c99561 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: 303) @@ -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 594563d1f6f..1d389c26654 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 c4b5dd2dc96..67e05cfaf93 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_attributes!( @@ -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 7cd600fec5b..8f05376ab9e 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -1032,7 +1032,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/4525-fix-project-indexes.yml b/changelogs/unreleased/4525-fix-project-indexes.yml new file mode 100644 index 00000000000..930e3b934c2 --- /dev/null +++ b/changelogs/unreleased/4525-fix-project-indexes.yml @@ -0,0 +1,5 @@ +--- +title: Rework some projects table indexes around repository_storage field +merge_request: 20377 +author: +type: fixed diff --git a/changelogs/unreleased/ide-edit-json-files.yml b/changelogs/unreleased/ide-edit-json-files.yml new file mode 100644 index 00000000000..2a6e6b80de1 --- /dev/null +++ b/changelogs/unreleased/ide-edit-json-files.yml @@ -0,0 +1,5 @@ +--- +title: Fixed IDE not opening JSON files +merge_request: 20798 +author: +type: fixed diff --git a/changelogs/unreleased/project-dropdown-list-overflow.yml b/changelogs/unreleased/project-dropdown-list-overflow.yml new file mode 100644 index 00000000000..9b74a68291b --- /dev/null +++ b/changelogs/unreleased/project-dropdown-list-overflow.yml @@ -0,0 +1,5 @@ +--- +title: Don't overflow project/group dropdown results +merge_request: 20704 +author: gfyoung +type: fixed diff --git a/changelogs/unreleased/zj-backup-timeout.yml b/changelogs/unreleased/zj-backup-timeout.yml new file mode 100644 index 00000000000..b2ad2ed8c63 --- /dev/null +++ b/changelogs/unreleased/zj-backup-timeout.yml @@ -0,0 +1,5 @@ +--- +title: Disable Gitaly timeouts when creating or restoring backups +merge_request: 20810 +author: +type: fixed 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/db/post_migrate/20180704145007_update_project_indexes.rb b/db/post_migrate/20180704145007_update_project_indexes.rb new file mode 100644 index 00000000000..193563b36db --- /dev/null +++ b/db/post_migrate/20180704145007_update_project_indexes.rb @@ -0,0 +1,23 @@ +# See http://doc.gitlab.com/ce/development/migration_style_guide.html +# for more information on how to write migrations for GitLab. + +class UpdateProjectIndexes < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + NEW_INDEX_NAME = 'idx_project_repository_check_partial' + + disable_ddl_transaction! + + def up + add_concurrent_index(:projects, + [:repository_storage, :created_at], + name: NEW_INDEX_NAME, + where: 'last_repository_check_at IS NULL' + ) + end + + def down + remove_concurrent_index_by_name(:projects, NEW_INDEX_NAME) + end +end diff --git a/db/schema.rb b/db/schema.rb index d2aa31fae30..1a5555fb3a4 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -1674,6 +1674,7 @@ ActiveRecord::Schema.define(version: 20180704204006) do add_index "projects", ["path"], name: "index_projects_on_path", using: :btree add_index "projects", ["path"], name: "index_projects_on_path_trigram", using: :gin, opclasses: {"path"=>"gin_trgm_ops"} add_index "projects", ["pending_delete"], name: "index_projects_on_pending_delete", using: :btree + add_index "projects", ["repository_storage", "created_at"], name: "idx_project_repository_check_partial", where: "(last_repository_check_at IS NULL)", using: :btree add_index "projects", ["repository_storage"], name: "index_projects_on_repository_storage", using: :btree add_index "projects", ["runners_token"], name: "index_projects_on_runners_token", using: :btree add_index "projects", ["star_count"], name: "index_projects_on_star_count", using: :btree diff --git a/doc/administration/pages/index.md b/doc/administration/pages/index.md index 9b1297ca4ba..f341e86aa22 100644 --- a/doc/administration/pages/index.md +++ b/doc/administration/pages/index.md @@ -124,11 +124,6 @@ The Pages daemon doesn't listen to the outside world. ``` 1. [Reconfigure GitLab][reconfigure] -1. Restart gitlab-pages by running the following command: - - ```shell - sudo gitlab-ctl restart gitlab-pages - ``` Watch the [video tutorial][video-admin] for this configuration. @@ -161,11 +156,6 @@ outside world. respectively. 1. [Reconfigure GitLab][reconfigure] -1. Restart gitlab-pages by running the following command: - - ```shell - sudo gitlab-ctl restart gitlab-pages - ``` ## Advanced configuration @@ -203,11 +193,6 @@ world. Custom domains are supported, but no TLS. listens on. If you don't have IPv6, you can omit the IPv6 address. 1. [Reconfigure GitLab][reconfigure] -1. Restart gitlab-pages by running the following command: - - ```shell - sudo gitlab-ctl restart gitlab-pages - ``` ### Custom domains with TLS support @@ -241,11 +226,6 @@ world. Custom domains and TLS are supported. listens on. If you don't have IPv6, you can omit the IPv6 address. 1. [Reconfigure GitLab][reconfigure] -1. Restart gitlab-pages by running the following command: - - ```shell - sudo gitlab-ctl restart gitlab-pages - ``` ### Custom domain verification @@ -273,11 +253,29 @@ are stored. ``` 1. [Reconfigure GitLab][reconfigure] -1. Restart gitlab-pages by running the following command: + +## Configure listener for reverse proxy requests + +Follow the steps below to configure the proxy listener of GitLab Pages. [Introduced](https://gitlab.com/gitlab-org/omnibus-gitlab/merge_requests/2533) in +Omnibus GitLab 11.1. + +1. By default the listener is configured to listen for requests on `localhost:8090`. + + If you wish to disable it you must configure this in + `/etc/gitlab/gitlab.rb`: ```shell - sudo gitlab-ctl restart gitlab-pages - ``` + gitlab_pages['listen_proxy'] = nil + ``` + + If you wish to make it listen on a different port you must configure this also in + `/etc/gitlab/gitlab.rb`: + + ```shell + gitlab_pages['listen_proxy'] = "localhost:10080" + ``` + +1. [Reconfigure GitLab][reconfigure] ## Set maximum pages size diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md index 096b64eb881..d95f8c7c8cc 100644 --- a/doc/ci/yaml/README.md +++ b/doc/ci/yaml/README.md @@ -1411,43 +1411,6 @@ variables: You can set it globally or per-job in the [`variables`](#variables) section. -### Custom build directories - -> [Introduced][gitlab-runner-876] in Gitlab Runner 11.1 - -NOTE: **Note:** -This can only be used when `custom_build_dir` is set to true in the [Runner's -configuration](https://docs.gitlab.com/runner/configuration/advanced-configuration.html). - -By default, GitLab Runner clones the repository in the `/builds` directory, -but sometimes your project might require to have the code in a specific -directory, like the GO projects for example. In that case, you can specify -the `CI_PROJECT_DIR` variable to tell the Runner in which directory to clone -the repository: - -```yml -image: golang:1.10-alpine3.7 - -variables: - CI_PROJECT_DIR: /go/src/gitlab.com/namespace/project-name - -stages: - - test - -dir: - stage: test - script: - - pwd # /go/src/gitlab.com/namespace/project-name -``` - -The following executors may use this feature only when -[concurrent](https://docs.gitlab.com/runner/configuration/advanced-configuration.html#the-global-section) -is set to `1`: - -- `shell` -- `ssh` -- `docker`, `docker+machine` when the job's working directory is mounted as a host volume. - ## Special YAML features It's possible to use special YAML features like anchors (`&`), aliases (`*`) @@ -1641,6 +1604,5 @@ CI with various languages. [ce-7983]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/7983 [ce-7447]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/7447 [ce-12909]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/12909 -[gitlab-runner-876]: https://gitlab.com/gitlab-org/gitlab-runner/merge_requests/876 [schedules]: ../../user/project/pipelines/schedules.md [variables-expressions]: ../variables/README.md#variables-expressions 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/banzai/filter/sanitization_filter.rb b/lib/banzai/filter/sanitization_filter.rb index 8275bb9e149..d28b12dce92 100644 --- a/lib/banzai/filter/sanitization_filter.rb +++ b/lib/banzai/filter/sanitization_filter.rb @@ -11,7 +11,7 @@ module Banzai def whitelist strong_memoize(:whitelist) do - customize_whitelist(super.dup) + customize_whitelist(super.deep_dup) end end 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/gitaly_client.rb b/lib/gitlab/gitaly_client.rb index 66e781a8e5b..01f05cd6037 100644 --- a/lib/gitlab/gitaly_client.rb +++ b/lib/gitlab/gitaly_client.rb @@ -407,7 +407,7 @@ module Gitlab # The default timeout on all Gitaly calls def self.default_timeout - return 0 if Sidekiq.server? + return no_timeout if Sidekiq.server? timeout(:gitaly_timeout_default) end @@ -420,6 +420,10 @@ module Gitlab timeout(:gitaly_timeout_medium) end + def self.no_timeout + 0 + end + def self.timeout(timeout_name) Gitlab::CurrentSettings.current_application_settings[timeout_name] end diff --git a/lib/gitlab/gitaly_client/repository_service.rb b/lib/gitlab/gitaly_client/repository_service.rb index 64b9af4d70c..2956ed4b911 100644 --- a/lib/gitlab/gitaly_client/repository_service.rb +++ b/lib/gitlab/gitaly_client/repository_service.rb @@ -202,7 +202,7 @@ module Gitlab save_path, :create_bundle, Gitaly::CreateBundleRequest, - GitalyClient.default_timeout + GitalyClient.no_timeout ) end @@ -220,7 +220,7 @@ module Gitlab bundle_path, :create_repository_from_bundle, Gitaly::CreateRepositoryFromBundleRequest, - GitalyClient.default_timeout + GitalyClient.no_timeout ) end @@ -245,7 +245,7 @@ module Gitlab :repository_service, :create_repository_from_snapshot, request, - timeout: GitalyClient.default_timeout + timeout: GitalyClient.no_timeout ) end 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/qa/qa/runtime/browser.rb b/qa/qa/runtime/browser.rb index 877864fb40c..ac1d07c66ef 100644 --- a/qa/qa/runtime/browser.rb +++ b/qa/qa/runtime/browser.rb @@ -72,6 +72,7 @@ module QA Capybara::Selenium::Driver.new( app, browser: :chrome, + clear_local_storage: true, desired_capabilities: capabilities, options: options ) diff --git a/spec/controllers/projects/labels_controller_spec.rb b/spec/controllers/projects/labels_controller_spec.rb index 452d7e23983..88955d879c9 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 b1d83246238..f02232700b0 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 c742eb79392..67b43741044 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/ide/stores/actions/file_spec.js b/spec/javascripts/ide/stores/actions/file_spec.js index 58d3ffc6d94..d8ef473355a 100644 --- a/spec/javascripts/ide/stores/actions/file_spec.js +++ b/spec/javascripts/ide/stores/actions/file_spec.js @@ -366,6 +366,23 @@ describe('IDE store file actions', () => { }); }); + describe('return JSON', () => { + beforeEach(() => { + mock.onGet(/(.*)/).replyOnce(200, JSON.stringify({ test: '123' })); + }); + + it('does not parse returned JSON', done => { + store + .dispatch('getRawFileData', { path: tmpFile.path }) + .then(() => { + expect(tmpFile.raw).toEqual('{"test":"123"}'); + + done(); + }) + .catch(done.fail); + }); + }); + describe('error', () => { beforeEach(() => { mock.onGet(/(.*)/).networkError(); 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 9c55a19ebc7..e71da9946ee 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 9e25a4b3fed..5b7a09a682d 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 f744f1af5e6..6b417bc0133 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/banzai/filter/sanitization_filter_spec.rb b/spec/lib/banzai/filter/sanitization_filter_spec.rb index d930c608b18..0b3c2390304 100644 --- a/spec/lib/banzai/filter/sanitization_filter_spec.rb +++ b/spec/lib/banzai/filter/sanitization_filter_spec.rb @@ -54,6 +54,18 @@ describe Banzai::Filter::SanitizationFilter do expect(instance.whitelist[:transformers].size).to eq control_count end + it 'customizes the whitelist only once for different instances' do + instance1 = described_class.new('Foo1') + instance2 = described_class.new('Foo2') + control_count = instance1.whitelist[:transformers].size + + instance1.whitelist + instance2.whitelist + + expect(instance1.whitelist[:transformers].size).to eq control_count + expect(instance2.whitelist[:transformers].size).to eq control_count + end + it 'sanitizes `class` attribute from all elements' do act = %q{<pre class="code highlight white c"><code><span class="k">def</span></code></pre>} exp = %q{<pre><code><span class="k">def</span></code></pre>} 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 51fad6c6838..53c99221fb4 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) |