Commit 21abe22f authored by Steve Azzopardi's avatar Steve Azzopardi

Merge branch 'master' into ce-to-ee-2018-11-29

parents 0cd09ba2 5db991b8
Please view this file on the master branch, on stable branches it's out of date.
## 11.5.1 (2018-11-26)
### Security (6 changes)
- Sanitize tracing external_urls before saving to DB and when displaying the URL to prevent XSS issues.
- Prevent reporter roles from viewing the Jaeger tracing settings page.
- Fix IDOR at /drafts/publish.
- Authorize users when listing board users and milestones.
- Resolve: Guest can set weight of a new issue.
- Fixes XSS with merge request approvers selection.
## 11.5.0 (2018-11-22)
### Security (2 changes)
......@@ -103,6 +115,17 @@ Please view this file on the master branch, on stable branches it's out of date.
- Geo: Clarify Geo HA documentation.
## 11.4.8 (2018-11-27)
### Security (5 changes)
- Escape entity title while autocomplete template rendering to prevent XSS. !707
- Authorize users when listing board users and milestones.
- Fix IDOR at /drafts/publish.
- Resolve: Guest can set weight of a new issue.
- Fixes XSS with merge request approvers selection.
## 11.4.7 (2018-11-20)
### Fixed (1 change)
......@@ -236,6 +259,19 @@ Please view this file on the master branch, on stable branches it's out of date.
- API: Allow issue weight parameter to be greater than or equal to zero.
## 11.3.11 (2018-11-26)
### Security (7 changes)
- Escape entity title while autocomplete template rendering to prevent XSS. !697
- Properly filter private references from system notes.
- Authorize users when listing board users and milestones.
- Project groups approvers no longer leak private groups info.
- Resolve: Guest can set weight of a new issue.
- Fixes XSS with merge request approvers selection.
- Protect against CSRF attacks when adding Slack app.
## 11.3.10 (2018-11-18)
- No changes.
......
......@@ -2,6 +2,28 @@
documentation](doc/development/changelog.md) for instructions on adding your own
entry.
## 11.5.1 (2018-11-26)
### Security (16 changes)
- Fix CRLF vulnerability in Project hooks.
- Fix possible XSS attack in Markdown urls with spaces.
- Redact sensitive information on gitlab-workhorse log.
- Do not follow redirects in Prometheus service when making http requests to the configured api url.
- Don't expose confidential information in commit message list.
- Provide email notification when a user changes their email address.
- Restrict Personal Access Tokens to API scope on web requests.
- Resolve reflected XSS in Ouath authorize window.
- Fix SSRF in project integrations.
- Fixed ability to comment on locked/confidential issues.
- Fixed ability of guest users to edit/delete comments on locked or confidential issues.
- Fix milestone promotion authorization check.
- Configure mermaid to not render HTML content in diagrams.
- Fix a possible symlink time of check to time of use race condition in GitLab Pages.
- Removed ability to see private group names when the group id is entered in the url.
- Fix stored XSS for Environments.
## 11.5.0 (2018-11-22)
### Security (10 changes, 1 of them is from the community)
......@@ -264,6 +286,36 @@ entry.
- Disables stop environment button while the deploy is in progress.
## 11.4.8 (2018-11-27)
### Security (24 changes)
- Escape entity title while autocomplete template rendering to prevent XSS. !2571
- Resolve reflected XSS in Ouath authorize window.
- Fix XSS in merge request source branch name.
- Escape user fullname while rendering autocomplete template to prevent XSS.
- Fix CRLF vulnerability in Project hooks.
- Fix possible XSS attack in Markdown urls with spaces.
- Redact sensitive information on gitlab-workhorse log.
- Do not follow redirects in Prometheus service when making http requests to the configured api url.
- Persist only SHA digest of PersonalAccessToken#token.
- Don't expose confidential information in commit message list.
- Provide email notification when a user changes their email address.
- Restrict Personal Access Tokens to API scope on web requests.
- Redact personal tokens in unsubscribe links.
- Fix SSRF in project integrations.
- Fixed ability to comment on locked/confidential issues.
- Fixed ability of guest users to edit/delete comments on locked or confidential issues.
- Fix milestone promotion authorization check.
- Monkey kubeclient to not follow any redirects.
- Configure mermaid to not render HTML content in diagrams.
- Fix a possible symlink time of check to time of use race condition in GitLab Pages.
- Removed ability to see private group names when the group id is entered in the url.
- Fix stored XSS for Environments.
- Prevent SSRF attacks in HipChat integration.
- Validate Wiki attachments are valid temporary files.
## 11.4.7 (2018-11-20)
- No changes.
......@@ -544,6 +596,44 @@ entry.
- Check frozen string in style builds. (gfyoung)
## 11.3.11 (2018-11-26)
### Security (32 changes)
- Filter user sensitive data from discussions JSON. !2537
- Escape entity title while autocomplete template rendering to prevent XSS. !2557
- Resolve reflected XSS in Ouath authorize window.
- Fix XSS in merge request source branch name.
- Fix CRLF vulnerability in Project hooks.
- Fix possible XSS attack in Markdown urls with spaces.
- Redact sensitive information on gitlab-workhorse log.
- Set timeout for syntax highlighting.
- Do not follow redirects in Prometheus service when making http requests to the configured api url.
- Persist only SHA digest of PersonalAccessToken#token.
- Sanitize JSON data properly to fix XSS on Issue details page.
- Don't expose confidential information in commit message list.
- Markdown API no longer displays confidential title references unless authorized.
- Provide email notification when a user changes their email address.
- Properly filter private references from system notes.
- Restrict Personal Access Tokens to API scope on web requests.
- Redact personal tokens in unsubscribe links.
- Fix SSRF in project integrations.
- Fix stored XSS in merge requests from imported repository.
- Fixed ability to comment on locked/confidential issues.
- Fixed ability of guest users to edit/delete comments on locked or confidential issues.
- Fix milestone promotion authorization check.
- Monkey kubeclient to not follow any redirects.
- Configure mermaid to not render HTML content in diagrams.
- Redact confidential events in the API.
- Fix xss vulnerability sourced from package.json.
- Fix a possible symlink time of check to time of use race condition in GitLab Pages.
- Removed ability to see private group names when the group id is entered in the url.
- Fix stored XSS for Environments.
- Block loopback addresses in UrlBlocker.
- Prevent SSRF attacks in HipChat integration.
- Validate Wiki attachments are valid temporary files.
## 11.3.10 (2018-11-18)
### Security (1 change)
......
......@@ -85,3 +85,5 @@ module BoardsResponses
end
end
end
BoardsResponses.prepend(EE::BoardsResponses)
......@@ -11,10 +11,6 @@ class Projects::IssuesController < Projects::ApplicationController
prepend ::EE::Projects::IssuesController
def self.authenticate_user_only_actions
%i[new]
end
def self.issue_except_actions
%i[index calendar new create bulk_update]
end
......
......@@ -14,7 +14,6 @@
# active: boolean
# blocked: boolean
# external: boolean
# skip_ldap: boolean
#
class UsersFinder
include CreatedAtFilter
......@@ -38,7 +37,6 @@ class UsersFinder
users = by_2fa(users)
users = by_created_at(users)
users = by_custom_attributes(users)
users = by_non_ldap(users)
users
end
......@@ -86,12 +84,6 @@ class UsersFinder
end
# rubocop: enable CodeReuse/ActiveRecord
def by_non_ldap(users)
return users unless params[:skip_ldap]
users.non_ldap
end
def by_2fa(users)
case params[:two_factor]
when 'enabled'
......@@ -103,3 +95,5 @@ class UsersFinder
end
end
end
UsersFinder.prepend(EE::UsersFinder)
---
title: Add a rebase API endpoint for merge requests
merge_request: 23296
author:
type: added
......@@ -3,6 +3,12 @@ comments: false
description: 'Learn how to use and administer GitLab, the most scalable Git-based fully integrated platform for software development.'
---
<div class="display-none">
<em>Visit <a href="https://docs.gitlab.com/ee/">docs.gitlab.com</a> for optimized
navigation, discoverability, and readability.</em>
</div>
<!-- the div above will not display on the docs site but will display on /help -->
# GitLab Documentation
Welcome to [GitLab](https://about.gitlab.com/) Documentation.
......
......@@ -411,6 +411,7 @@ Parameters:
- `merge_request_iid` (required) - The internal ID of the merge request
- `render_html` (optional) - If `true` response includes rendered HTML for title and description
- `include_diverged_commits_count` (optional) - If `true` response includes the commits behind the target branch
- `include_rebase_in_progress` (optional) - If `true` response includes whether a rebase operation is in progress
```json
{
......@@ -464,6 +465,7 @@ Parameters:
},
"merge_when_pipeline_succeeds": true,
"merge_status": "can_be_merged",
"merge_error": null,
"sha": "8888888888888888888888888888888888888888",
"merge_commit_sha": null,
"user_notes_count": 1,
......@@ -509,6 +511,7 @@ Parameters:
"start_sha": "c380d3acebd181f13629a25d2e2acca46ffe1e00"
},
"diverged_commits_count": 2,
"rebase_in_progress": false,
"approvals_before_merge": null
}
```
......@@ -786,6 +789,7 @@ order for it to take effect:
},
"merge_when_pipeline_succeeds": true,
"merge_status": "can_be_merged",
"merge_error": null,
"sha": "8888888888888888888888888888888888888888",
"merge_commit_sha": null,
"user_notes_count": 1,
......@@ -914,6 +918,7 @@ Must include at least one non-required attribute from above.
},
"merge_when_pipeline_succeeds": true,
"merge_status": "can_be_merged",
"merge_error": null,
"sha": "8888888888888888888888888888888888888888",
"merge_commit_sha": null,
"user_notes_count": 1,
......@@ -1058,6 +1063,7 @@ Parameters:
},
"merge_when_pipeline_succeeds": true,
"merge_status": "can_be_merged",
"merge_error": null,
"sha": "8888888888888888888888888888888888888888",
"merge_commit_sha": null,
"user_notes_count": 1,
......@@ -1174,6 +1180,7 @@ Parameters:
},
"merge_when_pipeline_succeeds": false,
"merge_status": "can_be_merged",
"merge_error": null,
"sha": "8888888888888888888888888888888888888888",
"merge_commit_sha": null,
"user_notes_count": 1,
......@@ -1223,6 +1230,62 @@ Parameters:
}
```
## Rebase a merge request
Automatically rebase the `source_branch` of the merge request against its
`target_branch`.
If you don't have permissions to push to the merge request's source branch -
you'll get a `403 Forbidden` response.
```
PUT /projects/:id/merge_requests/:merge_request_iid/rebase
```
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `merge_request_iid` | integer | yes | The internal ID of the merge request |
```bash
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/76/merge_requests/1/rebase
```
This is an asynchronous request. The API will return an empty `202 Accepted`
response if the request is enqueued successfully.
You can poll the [Get single MR](#get-single-mr) endpoint with the
`include_rebase_in_progress` parameter to check the status of the
asynchronous request.
If the rebase operation is ongoing, the response will include the following:
```json
{
"rebase_in_progress": true
"merge_error": null
}
```
Once the rebase operation has completed successfully, the response will include
the following:
```json
{
"rebase_in_progress": false,
"merge_error": null,
}
```
If the rebase operation fails, the response will include the following:
```json
{
"rebase_in_progress": false,
"merge_error": "Rebase failed. Please rebase locally",
}
```
## Comments on merge requests
Comments are done via the [notes](notes.md) resource.
......
......@@ -114,7 +114,7 @@ export default class ApproversSelect {
}
static formatSelection(group) {
return group.full_name || group.name;
return _.escape(group.full_name || group.name);
}
static formatResult({
......
......@@ -2,6 +2,10 @@
module Boards
class MilestonesController < Boards::ApplicationController
include BoardsResponses
before_action :authorize_read_milestone, only: [:index]
def index
milestones_finder = Boards::MilestonesFinder.new(board, current_user)
......
......@@ -7,6 +7,11 @@ module Boards
# If board parent is a group it enumerates all members of current group,
# ancestors, and descendants
# rubocop: disable CodeReuse/ActiveRecord
include BoardsResponses
before_action :authorize_read_parent, only: [:index]
def index
user_ids = user_finder.execute.select(:user_id)
......
module EE
module BoardsResponses
extend ActiveSupport::Concern
def authorize_read_parent
ability = board.group_board? ? :read_group : :read_project
authorize_action_for!(board.parent, ability)
end
def authorize_read_milestone
ability = board.group_board? ? :read_group : :read_milestone
authorize_action_for!(board.parent, ability)
end
end
end
......@@ -6,6 +6,7 @@ module EE
extend ActiveSupport::Concern
prepended do
before_action :authenticate_user!, only: [:export_csv]
before_action :check_export_issues_available!, only: [:export_csv]
before_action :check_service_desk_available!, only: [:service_desk]
before_action :whitelist_query_limiting_ee, only: [:update]
......@@ -14,11 +15,6 @@ module EE
class_methods do
extend ::Gitlab::Utils::Override
override :authenticate_user_only_actions
def authenticate_user_only_actions
super + %i[export_csv]
end
override :issue_except_actions
def issue_except_actions
super + %i[export_csv service_desk]
......
......@@ -5,6 +5,10 @@ class Groups::Epics::NotesController < Groups::ApplicationController
include NotesHelper
include ToggleAwardEmoji
# Re-defining the before_action set in NotesActions here without prepending pushes it farther down the callback stack
# and we do this here since we need variables instantiated in other before_actions
before_action :normalize_create_params, only: [:create]
before_action :epic
before_action :authorize_create_note!, only: [:create]
......@@ -41,4 +45,11 @@ class Groups::Epics::NotesController < Groups::ApplicationController
def note_serializer
EpicNoteSerializer.new(project: nil, noteable: noteable, current_user: current_user)
end
def normalize_create_params
params[:note].try do |note|
note[:noteable_id] = epic.id
note[:noteable_type] = 'Epic'
end
end
end
......@@ -8,6 +8,7 @@ class Projects::MergeRequests::DraftsController < Projects::MergeRequests::Appli
before_action :check_draft_notes_available!, except: [:index]
before_action :authorize_create_draft!, only: [:create]
before_action :authorize_admin_draft!, only: [:update, :destroy]
before_action :authorize_admin_draft!, only: [:publish], if: -> { params[:id].present? }
def index
drafts = prepare_notes_for_rendering(draft_notes)
......@@ -40,7 +41,7 @@ class Projects::MergeRequests::DraftsController < Projects::MergeRequests::Appli
end
def publish
DraftNotes::PublishService.new(merge_request, current_user).execute(params[:id])
DraftNotes::PublishService.new(merge_request, current_user).execute(draft_note(allow_nil: true))
head :ok
end
......@@ -53,10 +54,13 @@ class Projects::MergeRequests::DraftsController < Projects::MergeRequests::Appli
private
def draft_note
def draft_note(allow_nil: false)
strong_memoize(:draft_note) do
draft_notes.try(:find, params[:id])
draft_notes.find(params[:id])
end
rescue ActiveRecord::RecordNotFound => ex
# draft_note is allowed to be nil in #publish
raise ex unless allow_nil
end
def draft_notes
......
......@@ -4,8 +4,7 @@ module Projects
module Settings
class OperationsController < Projects::ApplicationController
before_action :check_license
before_action :authorize_update_environment!, only: [:create, :update]
before_action :authorize_read_environment!, only: [:show]
before_action :authorize_update_environment!
def show
@tracing_settings ||= ProjectTracingSetting.for_project(@project)
......
......@@ -2,7 +2,7 @@
class Projects::TracingsController < Projects::ApplicationController
before_action :check_license
before_action :authorize_read_environment!, only: [:show]
before_action :authorize_update_environment!
def show
end
......
# frozen_string_literal: true
module EE
module UsersFinder
extend ::Gitlab::Utils::Override
override :execute
def execute
by_non_ldap(super)
end
def by_non_ldap(users)
return users unless params[:skip_ldap]
users.non_ldap
end
end
end
......@@ -5,6 +5,8 @@ class ProjectTracingSetting < ActiveRecord::Base
validates :external_url, length: { maximum: 255 }, public_url: true
before_validation :sanitize_external_url
def self.create_or_update(project, params)
self.transaction(requires_new: true) do
tracing_setting = self.for_project(project)
......@@ -17,4 +19,10 @@ class ProjectTracingSetting < ActiveRecord::Base
def self.for_project(project)
self.where(project: project).first_or_initialize
end
private
def sanitize_external_url
self.external_url = ActionController::Base.helpers.sanitize(self.external_url, tags: [])
end
end
......@@ -2,9 +2,9 @@
module DraftNotes
class PublishService < DraftNotes::BaseService
def execute(draft_id = nil)
if draft_id
publish_draft_note(draft_id)
def execute(draft = nil)
if draft
publish_draft_note(draft)
else
publish_draft_notes
end
......@@ -12,9 +12,7 @@ module DraftNotes
private
def publish_draft_note(draft_id)
draft = DraftNote.find(draft_id)
def publish_draft_note(draft)
create_note_from_draft(draft)
draft.delete
......
......@@ -3,7 +3,13 @@ module EE
private
def filter_params(issuable)
params.delete(:weight) unless issuable.supports_weight?
# This security check is repeated here to avoid multiple backports,
# this should be refactored to be reused from the base class.
ability_name = :"admin_#{issuable.to_ability_name}"
unless issuable.supports_weight? && can?(current_user, ability_name, issuable)
params.delete(:weight)
end
super
end
......
......@@ -3,7 +3,7 @@
- if project_nav_tab? :settings
= nav_link(controller: :tracings, action: [:show]) do
- if @project.tracing_external_url.present?
= link_to @project.tracing_external_url, target: "_blank", rel: 'noopener noreferrer' do
= link_to sanitize(@project.tracing_external_url, tags: []), target: "_blank", rel: 'noopener noreferrer' do
%span
= _('Tracing')
%i.strong.ml-1.fa.fa-external-link
......
......@@ -8,12 +8,16 @@
%h4
= _("Jaeger tracing")
%p
- tracing_url = has_jaeger_url ? @project.tracing_external_url : project_tracing_path(@project)
- meta = has_jaeger_url ? 'rel="noopener noreferrer" target="_blank"' : ''
- icon = has_jaeger_url ? sprite_icon('external-link', size: 16, css_class: 'ml-1 vertical-align-middle') : ''
- tracing_start_tag = "<a href='#{tracing_url}' #{meta}>".html_safe
- tracing_end_tag = "#{icon}</a>".html_safe
= _("To open Jaeger and easily view tracing from GitLab, link the %{start_tag}Tracing%{end_tag} page to your server").html_safe % { start_tag: tracing_start_tag, end_tag: tracing_end_tag }
- if has_jaeger_url
- tracing_link = link_to sanitize(@project.tracing_external_url, tags: []), target: "_blank", rel: 'noopener noreferrer' do
%span
= _('Tracing')
= sprite_icon('external-link', size: 16, css_class: 'ml-1 vertical-align-middle')
- else
- tracing_link = link_to project_tracing_path(@project) do
%span
= _('Tracing')
= _("To open Jaeger and easily view tracing from GitLab, link the %{link} page to your server").html_safe % { link: tracing_link }
= form_for @tracing_settings, as: :tracing_settings, url: project_settings_operations_path(@project) do |f|
= form_errors(@tracing_settings)
.form-group
......
---
title: Move EE only differences for finders
merge_request: 8629
author: George Tsiolis
type: other
---
title: Sanitize tracing external_urls before saving to DB and when displaying the URL to prevent XSS issues
merge_request:
author:
type: security
---
title: Prevent reporter roles from viewing the Jaeger tracing settings page
merge_request:
author:
type: security
---
title: Fix IDOR at /drafts/publish
merge_request:
author:
type: security
---
title: Authorize users when listing board users and milestones.
merge_request:
author:
type: security
---
title: 'Resolve: Guest can set weight of a new issue'
merge_request:
author:
type: security
---
title: Fixes XSS with merge request approvers selection
merge_request:
author:
type: security
# frozen_string_literal: true
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class SanitizeTracingExternalUrl < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
# Set this constant to true if this migration requires downtime.
DOWNTIME = false
class ProjectTracingSetting < ActiveRecord::Base
include ::EachBatch
self.table_name = 'project_tracing_settings'
def sanitize_external_url
self.external_url = ActionController::Base.helpers.sanitize(self.external_url, tags: [])
end
end
def up
ProjectTracingSetting.each_batch(of: 50) do |batch|
batch.each do |rec|
rec.sanitize_external_url
rec.save! if rec.changed?
end
end
end
def down
# no-op
end
end
......@@ -5,23 +5,50 @@ describe Boards::MilestonesController do
let(:board) { create(:board, project: project) }
let(:user) { create(:user) }
before do
create(:milestone, project: project)
describe 'GET index' do
context 'with authorized user' do
before do
create(:milestone, project: project)
project.add_maintainer(user)
sign_in(user)
end
project.add_maintainer(user)
sign_in(user)
end
describe 'GET index' do
it 'returns a list of all milestones of board parent' do
get :index, board_id: board.to_param, format: :json
it 'returns a list of all milestones of board parent' do
get :index, board_id: board.to_param, format: :json
parsed_response = JSON.parse(response.body)
expect(response).to have_gitlab_http_status(200)
expect(response.content_type).to eq('application/json')
expect(parsed_response).to all(match_schema('entities/milestone', dir: 'ee'))
expect(parsed_response.size).to eq(1)
end
end
context 'with unauthorized user' do
before do
sign_in(user)
end
shared_examples 'unauthorized board milestone listing' do
it 'returns a forbidden 403 response' do
get :index, board_id: board.to_param, format: :json
expect(response).to have_gitlab_http_status(403)
end
end
context 'with private group board' do
let(:group) { create(:group, :private) }
let(:board) { create(:board, group: group) }
parsed_response = JSON.parse(response.body)
it_behaves_like 'unauthorized board milestone listing'
end
expect(response).to have_gitlab_http_status(200)
expect(response.content_type).to eq('application/json')
expect(parsed_response).to all(match_schema('entities/milestone', dir: 'ee'))
expect(parsed_response.size).to eq(1)
context 'with private project board' do
it_behaves_like 'unauthorized board milestone listing'
end
end
end
end
require 'spec_helper'
describe Boards::UsersController do
let(:group) { create(:group) }
let(:group) { create(:group, :private) }
let(:board) { create(:board, group: group) }
let(:guest) { create(:user) }
let(:user) { create(:user) }
before do
group.add_maintainer(user)
group.add_guest(guest)
describe 'GET index' do
context 'with authorized user' do
before do
group.add_maintainer(user)
group.add_guest(guest)
sign_in(user)
end
sign_in(user)
end
describe 'GET index' do
it 'returns a list of all members of board parent' do
get :index, namespace_id: group.to_param,
board_id: board.to_param,
format: :json
it 'returns a list of all members of board parent' do
get :index, namespace_id: group.to_param,
board_id: board.to_param,
format: :json
parsed_response = JSON.parse(response.body)
expect(response).to have_gitlab_http_status(200)
expect(response.content_type).to eq 'application/json'
expect(parsed_response).to all(match_schema('entities/user'))
expect(parsed_response.length).to eq 2
end
end
context 'with unauthorized user' do
before do
sign_in(user)
end
shared_examples 'unauthorized board user listing' do
it 'returns a forbidden 403 response' do
get :index, board_id: board.to_param, format: :json
expect(response).to have_gitlab_http_status(403)
end
end
context 'with private group board' do
it_behaves_like 'unauthorized board user listing'
end
parsed_response = JSON.parse(response.body)
context 'with private project board' do
let(:project) { create(:project) }
let(:board) { create(:board, project: project) }
expect(response).to have_gitlab_http_status(200)
expect(response.content_type).to eq 'application/json'
expect(parsed_response).to all(match_schema('entities/user'))
expect(parsed_response.length).to eq 2
it_behaves_like 'unauthorized board user listing'
end
end
end
end
......@@ -153,6 +153,7 @@ describe Projects::MergeRequests::DraftsController do
context 'without permissions' do
before do
sign_in(user2)
project.add_developer(user2)
end
it 'does not allow editing draft note belonging to someone else' do
......@@ -176,6 +177,22 @@ describe Projects::MergeRequests::DraftsController do
end
describe 'POST #publish' do
context 'without permissions' do
before do
sign_in(user2)
project.add_developer(user2)
end
it 'does not allow publishing draft note belonging to someone else' do
draft = create(:draft_note, merge_request: merge_request, author: user)
expect { post :publish, params.merge(id: draft.id) }.to change { Note.count }.by(0)
.and change { DraftNote.count }.by(0)
expect(response).to have_gitlab_http_status(404)
end
end
it 'publishes draft notes with position' do
diff_refs = project.commit(RepoHelpers.sample_commit.id).try(:diff_refs)
......
......@@ -10,21 +10,27 @@ describe Projects::Settings::OperationsController do
end
describe 'GET show' do
shared_examples 'user without access to project' do |project_visibility|
shared_examples 'user without read access' do |project_visibility|
let(:project) { create(:project, project_visibility) }
it 'returns 404' do
get :show, namespace_id: project.namespace, project_id: project
%w[guest reporter developer].each do |role|
before do
project.public_send("add_#{role}", user)
end
expect(response).to have_gitlab_http_status(:not_found)
it 'returns 404' do
get :show, namespace_id: project.namespace, project_id: project
expect(response).to have_gitlab_http_status(:not_found)
end
end
end
shared_examples 'user with access to project' do |project_visibility|
shared_examples 'user with read access' do |project_visibility|
let(:project) { create(:project, project_visibility) }
before do
project.add_reporter(user)
project.add_maintainer(user)
end
it 'renders ok' do
......@@ -50,16 +56,16 @@ describe Projects::Settings::OperationsController do
stub_licensed_features(tracing: true)
end
context 'when logged in with correct permission' do
it_behaves_like 'user with access to project', :public
it_behaves_like 'user with access to project', :private
it_behaves_like 'user with access to project', :internal
context 'with maintainer role' do
it_behaves_like 'user with read access', :public
it_behaves_like 'user with read access', :private
it_behaves_like 'user with read access', :internal
end
context 'when logged in without correct permission' do
it_behaves_like 'user without access to project', :public
it_behaves_like 'user without access to project', :private
it_behaves_like 'user without access to project', :internal
context 'without maintainer role' do
it_behaves_like 'user without read access', :public
it_behaves_like 'user without read access', :private
it_behaves_like 'user without read access', :internal
end
context 'when user not logged in' do
......@@ -67,7 +73,7 @@ describe Projects::Settings::OperationsController do
sign_out(user)
end
it_behaves_like 'user without access to project', :public
it_behaves_like 'user without read access', :public
it_behaves_like 'user needs to login', :private
it_behaves_like 'user needs to login', :internal
......@@ -79,9 +85,9 @@ describe Projects::Settings::OperationsController do
stub_licensed_features(tracing: false)
end
it_behaves_like 'user without access to project', :public
it_behaves_like 'user without access to project', :private
it_behaves_like 'user without access to project', :internal
it_behaves_like 'user without read access', :public
it_behaves_like 'user without read access', :private
it_behaves_like 'user without read access', :internal
end
end
......@@ -99,10 +105,16 @@ describe Projects::Settings::OperationsController do
shared_examples 'user without write access' do |project_visibility|
let(:project) { create(:project, project_visibility) }
it 'does not update tracing external_url' do
update_project(project, external_url: 'https://gitlab.com')
%w[guest reporter developer].each do |role|
before do
project.public_send("add_#{role}", user)
end
it 'does not update tracing external_url' do
update_project(project, external_url: 'https://gitlab.com')
expect(project.tracing_setting).to be_nil
expect(project.tracing_setting).to be_nil
end
end
end
......@@ -125,13 +137,13 @@ describe Projects::Settings::OperationsController do
end
end
context 'with authorized user' do
context 'with maintainer role' do
it_behaves_like 'user with write access', :public, 'https://gitlab.com', 'https://gitlab.com'
it_behaves_like 'user with write access', :private, 'https://gitlab.com', 'https://gitlab.com'
it_behaves_like 'user with write access', :internal, 'https://gitlab.com', 'https://gitlab.com'
end
context 'with unauthorized user' do
context 'with non maintainer roles' do
it_behaves_like 'user without write access', :public
it_behaves_like 'user without write access', :private
it_behaves_like 'user without write access', :internal
......
......@@ -6,36 +6,27 @@ describe Projects::TracingsController do
set(:user) { create(:user) }
describe 'GET show' do
describe 'with valid license' do
shared_examples 'user with read access' do |visibility_level|
let(:project) { create(:project, visibility_level) }
before do
stub_licensed_features(tracing: true)
project.add_maintainer(user)
end
shared_examples 'authorized user' do |visibility_level|
let(:project) { create(:project, visibility_level) }
before do
project.add_reporter(user)
sign_in(user)
end
it 'renders OK' do
get :show, namespace_id: project.namespace, project_id: project
it 'renders OK' do
get :show, namespace_id: project.namespace, project_id: project
expect(response).to have_gitlab_http_status(200)
expect(response).to render_template(:show)
end
expect(response).to have_gitlab_http_status(200)
expect(response).to render_template(:show)
end
end
it_behaves_like 'authorized user', :public
it_behaves_like 'authorized user', :internal
it_behaves_like 'authorized user', :private
shared_examples 'unauthorized user' do |visibility_level|
let(:project) { create(:project, visibility_level) }
shared_examples 'user without read access' do |visibility_level|
let(:project) { create(:project, visibility_level) }
%w[guest reporter developer].each do |role|
before do
sign_in(user)
project.public_send("add_#{role}", user)
end
it 'returns 404' do
......@@ -44,37 +35,36 @@ describe Projects::TracingsController do
expect(response).to have_gitlab_http_status(:not_found)
end
end
it_behaves_like 'unauthorized user', :public
it_behaves_like 'unauthorized user', :internal
it_behaves_like 'unauthorized user', :private
end
context 'with invalid license' do
describe 'with valid license' do
before do
stub_licensed_features(tracing: false)
stub_licensed_features(tracing: true)
sign_in(user)
end
shared_examples 'invalid license' do |visibility_level|
let(:project) { create(:project, visibility_level) }
before do
stub_licensed_features(tracing: false)
project.add_reporter(user)
sign_in(user)
end
context 'with maintainer role' do
it_behaves_like 'user with read access', :public
it_behaves_like 'user with read access', :internal
it_behaves_like 'user with read access', :private
end
it 'returns 404' do
get :show, namespace_id: project.namespace, project_id: project
context 'without maintainer role' do
it_behaves_like 'user without read access', :public
it_behaves_like 'user without read access', :internal
it_behaves_like 'user without read access', :private
end
end
expect(response).to have_gitlab_http_status(:not_found)
end
context 'with invalid license' do
before do
stub_licensed_features(tracing: false)
sign_in(user)
end
it_behaves_like 'invalid license', :public
it_behaves_like 'invalid license', :internal
it_behaves_like 'invalid license', :private
it_behaves_like 'user without read access', :public
it_behaves_like 'user without read access', :internal
it_behaves_like 'user without read access', :private
end
end
end
FactoryBot.define do
factory :project_tracing_setting do
project
external_url 'https://example.com'
end
end
......@@ -59,4 +59,22 @@ describe('ApproversSelect', () => {
expect(output).not.toContain('<script>alert("testing")</script>');
});
});
describe('formatSelection', () => {
it('escapes full name', () => {
expect(
ApproversSelect.formatSelection({
full_name: '<script>alert("testing")</script>',
}),
).not.toBe('<script>alert("testing")</script>');
});
it('escapes name', () => {
expect(
ApproversSelect.formatSelection({
name: '<script>alert("testing")</script>',
}),
).not.toBe('<script>alert("testing")</script>');
});
});
});
# frozen_string_literal: true
require 'spec_helper'
require Rails.root.join('ee', 'db', 'post_migrate', '20181116100917_sanitize_tracing_external_url.rb')
describe SanitizeTracingExternalUrl, :migration do
let(:migration) { described_class.new }
describe '#up' do
let(:projects) { table(:projects) }
let(:namespaces) { table(:namespaces) }
let(:project_tracing_settings) { table(:project_tracing_settings) }
let(:valid_url) { "https://replaceme.com/" }
let(:invalid_url) { "https://replaceme.com/'><script>alert(document.cookie)</script>" }
let(:cleaned_url) { "https://replaceme.com/'>" }
before do
namespaces.create(id: 1, name: 'gitlab-org', path: 'gitlab-org')
projects.create!(id: 123, name: 'gitlab1', path: 'gitlab1', namespace_id: 1)
projects.create!(id: 124, name: 'gitlab2', path: 'gitlab2', namespace_id: 1)
project_tracing_settings.create!(id: 2234, external_url: invalid_url, project_id: 123)
project_tracing_settings.create!(id: 2235, external_url: valid_url, project_id: 124)
end
it 'correctly sanitizes project_tracing_settings external_url' do
migrate!
expect(project_tracing_settings.order(:id).pluck(:external_url)).to match_array([cleaned_url, valid_url])
end
end
end
......@@ -24,5 +24,11 @@ describe ProjectTracingSetting do
tracing_setting.external_url = " "
expect(tracing_setting).not_to be_valid
end
it 'sanitizes the url' do
tracing_setting.external_url = "https://replaceme.com/'><script>alert(document.cookie)</script>"
expect(tracing_setting).to be_valid
expect(tracing_setting.external_url).to eq("https://replaceme.com/'>")
end
end
end
......@@ -6,14 +6,14 @@ describe DraftNotes::PublishService do
let(:project) { merge_request.target_project }
let(:user) { merge_request.author }
def publish(id: nil)
DraftNotes::PublishService.new(merge_request, user).execute(id)
def publish(draft: nil)
DraftNotes::PublishService.new(merge_request, user).execute(draft)
end
it 'publishes a single draft note' do
drafts = create_list(:draft_note, 2, merge_request: merge_request, author: user)
expect { publish(id: drafts.first.id) }.to change { DraftNote.count }.by(-1).and change { Note.count }.by(1)
expect { publish(draft: drafts.first) }.to change { DraftNote.count }.by(-1).and change { Note.count }.by(1)
expect(DraftNote.count).to eq(1)
end
......@@ -58,7 +58,7 @@ describe DraftNotes::PublishService do
let(:draft_note) { create(:draft_note, merge_request: merge_request, author: user, resolve_discussion: true, discussion_id: note.discussion.reply_id) }
it 'resolves the discussion' do
publish(id: draft_note.id)
publish(draft: draft_note)
expect(note.discussion.resolved?).to be true
end
......
require 'spec_helper'
describe Issues::CreateService do
let(:project) { create(:project) }
let(:opts) do
{
title: 'Awesome issue',
description: 'please fix',
weight: 9
}
end
context 'when current user cannot admin issues in the project' do
let(:guest) { create(:user) }
before do
project.add_guest(guest)
end
it 'filters out params that cannot be set without the :admin_issue permission' do
issue = described_class.new(project, guest, opts).execute
expect(issue).to be_persisted
expect(issue.weight).to be_nil
end
end
context 'when current user can admin issues in the project' do
let(:reporter) { create(:user) }
before do
project.add_reporter(reporter)
end
it 'sets permitted params correctly' do
issue = described_class.new(project, reporter, opts).execute
expect(issue).to be_persisted
expect(issue.weight).to eq(9)
end
end
end
......@@ -31,25 +31,43 @@ describe 'layouts/nav/sidebar/_project' do
context 'with project.tracing_external_url' do
let(:tracing_url) { 'https://tracing.url' }
let(:tracing_settings) { create(:project_tracing_setting, project: project, external_url: tracing_url) }
before do
allow(view).to receive(:can?).and_return(true)
allow(project).to receive(:tracing_external_url).and_return(tracing_url)
end
it 'links to project.tracing_external_url' do
expect(tracing_settings.external_url).to eq(tracing_url)
expect(project.tracing_external_url).to eq(tracing_url)
render
expect(rendered).to have_link('Tracing', href: tracing_url)
end
context 'with malicious external_url' do
let(:malicious_tracing_url) { "https://replaceme.com/'><script>alert(document.cookie)</script>" }
let(:cleaned_url) { "https://replaceme.com/'>" }
before do
tracing_settings.update_column(:external_url, malicious_tracing_url)
end
it 'sanitizes external_url' do
expect(project.tracing_external_url).to eq(malicious_tracing_url)
render
expect(tracing_settings.external_url).to eq(malicious_tracing_url)
expect(rendered).to have_link('Tracing', href: cleaned_url)
end
end
end
context 'without project.tracing_external_url' do
before do
allow(view).to receive(:can?).and_return(true)
allow(project).to receive(:tracing_external_url).and_return(nil)
end
it 'links to Tracing page' do
......
# frozen_string_literal: true
require 'spec_helper'
describe 'projects/settings/operations/show' do
let(:project) { create(:project, :repository) }
before do
assign(:project, project)
assign(:repository, project.repository)
allow(view).to receive(:current_ref).and_return('master')
stub_licensed_features(tracing: true)
end
describe 'Operations > Tracing' do
context 'with project.tracing_external_url' do
let(:tracing_url) { 'https://tracing.url' }
let(:tracing_settings) { create(:project_tracing_setting, project: project, external_url: tracing_url) }
before do
allow(view).to receive(:can?).and_return(true)
assign(:tracing_settings, tracing_settings)
end
it 'links to project.tracing_external_url' do
render
expect(rendered).to have_link('Tracing', href: tracing_url)
end
context 'with malicious external_url' do
let(:malicious_tracing_url) { "https://replaceme.com/'><script>alert(document.cookie)</script>" }
let(:cleaned_url) { "https://replaceme.com/'>" }
before do
tracing_settings.update_column(:external_url, malicious_tracing_url)
end
it 'sanitizes external_url' do
render
expect(tracing_settings.external_url).to eq(malicious_tracing_url)
expect(rendered).to have_link('Tracing', href: cleaned_url)
end
end
end
context 'without project.tracing_external_url' do
let(:tracing_settings) { build(:project_tracing_setting, project: project) }
before do
allow(view).to receive(:can?).and_return(true)
tracing_settings.external_url = nil
assign(:tracing_settings, tracing_settings)
end
it 'links to Tracing page' do
render
expect(rendered).to have_link('Tracing', href: project_tracing_path(project))
end
end
end
end
......@@ -717,6 +717,10 @@ module API
expose :diff_refs, using: Entities::DiffRefs
# Allow the status of a rebase to be determined
expose :merge_error
expose :rebase_in_progress?, as: :rebase_in_progress, if: -> (_, options) { options[:include_rebase_in_progress] }
expose :diverged_commits_count, as: :diverged_commits_count, if: -> (_, options) { options[:include_diverged_commits_count] }
def build_available?(options)
......
......@@ -76,6 +76,19 @@ module API
options
end
def authorize_push_to_merge_request!(merge_request)
forbidden!('Source branch does not exist') unless
merge_request.source_branch_exists?
user_access = Gitlab::UserAccess.new(
current_user,
project: merge_request.source_project
)
forbidden!('Cannot push to source branch') unless
user_access.can_push_to_branch?(merge_request.source_branch)
end
params :merge_requests_params do
optional :state, type: String, values: %w[opened closed locked merged all], default: 'all',
desc: 'Return opened, closed, locked, merged, or all merge requests'
......@@ -241,6 +254,7 @@ module API
requires :merge_request_iid, type: Integer, desc: 'The IID of a merge request'
optional :render_html, type: Boolean, desc: 'Returns the description and title rendered HTML'
optional :include_diverged_commits_count, type: Boolean, desc: 'Returns the commits count behind the target branch'
optional :include_rebase_in_progress, type: Boolean, desc: 'Returns whether a rebase operation is ongoing '
end
desc 'Get a single merge request' do
success Entities::MergeRequest
......@@ -248,7 +262,13 @@ module API
get ':id/merge_requests/:merge_request_iid' do
merge_request = find_merge_request_with_access(params[:merge_request_iid])
present merge_request, with: Entities::MergeRequest, current_user: current_user, project: user_project, render_html: params[:render_html], include_diverged_commits_count: params[:include_diverged_commits_count]
present merge_request,
with: Entities::MergeRequest,
current_user: current_user,
project: user_project,
render_html: params[:render_html],
include_diverged_commits_count: params[:include_diverged_commits_count],
include_rebase_in_progress: params[:include_rebase_in_progress]
end
desc 'Get the participants of a merge request' do
......@@ -380,6 +400,19 @@ module API
.cancel(merge_request)
end
desc 'Rebase the merge request against its target branch' do
detail 'This feature was added in GitLab 11.6'
end
put ':id/merge_requests/:merge_request_iid/rebase' do
merge_request = find_project_merge_request(params[:merge_request_iid])
authorize_push_to_merge_request!(merge_request)
RebaseWorker.perform_async(merge_request.id, current_user.id)
status :accepted
end
desc 'List issues that will be closed on merge' do
success Entities::MRNote
end
......
......@@ -8758,7 +8758,7 @@ msgstr ""
msgid "To only use CI/CD features for an external repository, choose <strong>CI/CD for external repo</strong>."
msgstr ""
msgid "To open Jaeger and easily view tracing from GitLab, link the %{start_tag}Tracing%{end_tag} page to your server"
msgid "To open Jaeger and easily view tracing from GitLab, link the %{link} page to your server"
msgstr ""
msgid "To preserve performance only <strong>%{display_size} of ${real_size}</strong> files are displayed."
......
......@@ -212,7 +212,7 @@ describe IssuablesHelper do
issuableRef: "&#{epic.iid}",
markdownPreviewPath: "/groups/#{@group.full_path}/preview_markdown",
markdownDocsPath: '/help/user/markdown',
markdownVersion: 11,
markdownVersion: CacheMarkdownField::CACHE_COMMONMARK_VERSION,
issuableTemplates: nil,
groupPath: @group.path,
initialTitleHtml: epic.title,
......
......@@ -359,6 +359,8 @@ describe API::MergeRequests do
expect(json_response['should_close_merge_request']).to be_falsy
expect(json_response['force_close_merge_request']).to be_falsy
expect(json_response['changes_count']).to eq(merge_request.merge_request_diff.real_size)
expect(json_response['merge_error']).to eq(merge_request.merge_error)
expect(json_response).not_to include('rebase_in_progress')
end
it 'exposes description and title html when render_html is true' do
......@@ -369,6 +371,14 @@ describe API::MergeRequests do
expect(json_response).to include('title_html', 'description_html')
end
it 'exposes rebase_in_progress when include_rebase_in_progress is true' do
get api("/projects/#{project.id}/merge_requests/#{merge_request.iid}", user), include_rebase_in_progress: true
expect(response).to have_gitlab_http_status(200)
expect(json_response).to include('rebase_in_progress')
end
context 'merge_request_metrics' do
before do
merge_request.metrics.update!(merged_by: user,
......@@ -1202,6 +1212,26 @@ describe API::MergeRequests do
end
end
describe 'PUT :id/merge_requests/:merge_request_iid/rebase' do
it 'enqueues a rebase of the merge request against the target branch' do
Sidekiq::Testing.fake! do
put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/rebase", user)
end
expect(response).to have_gitlab_http_status(202)
expect(RebaseWorker.jobs.size).to eq(1)
end
it 'returns 403 if the user cannot push to the branch' do
guest = create(:user)
project.add_guest(guest)
put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/rebase", guest)
expect(response).to have_gitlab_http_status(403)
end
end
describe 'Time tracking' do
let(:issuable) { merge_request }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment