Commit a7fec177 authored by Grzegorz Bizon's avatar Grzegorz Bizon

Merge commit '68526805' into fix/gb/encrypt-runners-tokens

* commit '68526805': (57 commits)
parents 439d22b9 68526805
......@@ -2,6 +2,29 @@
documentation](doc/development/changelog.md) for instructions on adding your own
entry.
## 11.5.1 (2018-11-26)
### Security (17 changes)
- 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.
- 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 +287,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 +597,45 @@ entry.
- Check frozen string in style builds. (gfyoung)
## 11.3.11 (2018-11-26)
### Security (33 changes)
- Filter user sensitive data from discussions JSON. !2537
- Escape entity title while autocomplete template rendering to prevent XSS. !2557
- Restrict Personal Access Tokens to API scope on web requests.
- 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.
- 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.
- Redact personal tokens in unsubscribe links.
- Resolve reflected XSS in Ouath authorize window.
- 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)
......
......@@ -7,6 +7,11 @@ gem_versions = {}
gem_versions['activerecord_sane_schema_dumper'] = rails5? ? '1.0' : '0.2'
gem_versions['rails'] = rails5? ? '5.0.7' : '4.2.10'
gem_versions['rails-i18n'] = rails5? ? '~> 5.1' : '~> 4.0.9'
# The 2.0.6 version of rack requires monkeypatch to be present in
# `config.ru`. This can be removed once a new update for Rack
# is available that contains https://github.com/rack/rack/pull/1201.
gem_versions['rack'] = rails5? ? '2.0.6' : '1.6.11'
# --- The end of special code for migrating to Rails 5.0 ---
source 'https://rubygems.org'
......@@ -154,6 +159,8 @@ gem 'icalendar'
gem 'diffy', '~> 3.1.0'
# Application server
gem 'rack', gem_versions['rack']
group :unicorn do
gem 'unicorn', '~> 5.1.0'
gem 'unicorn-worker-killer', '~> 0.4.4'
......
......@@ -1088,6 +1088,7 @@ DEPENDENCIES
pry-rails (~> 0.3.4)
puma (~> 3.12)
puma_worker_killer
rack (= 2.0.6)
rack-attack (~> 4.4.1)
rack-cors (~> 1.0.0)
rack-oauth2 (~> 1.2.1)
......
......@@ -1079,6 +1079,7 @@ DEPENDENCIES
pry-rails (~> 0.3.4)
puma (~> 3.12)
puma_worker_killer
rack (= 1.6.11)
rack-attack (~> 4.4.1)
rack-cors (~> 1.0.0)
rack-oauth2 (~> 1.2.1)
......
......@@ -10,10 +10,10 @@ const Api = {
projectsPath: '/api/:version/projects.json',
projectPath: '/api/:version/projects/:id',
projectLabelsPath: '/:namespace_path/:project_path/labels',
mergeRequestPath: '/api/:version/projects/:id/merge_requests/:mrid',
projectMergeRequestPath: '/api/:version/projects/:id/merge_requests/:mrid',
projectMergeRequestChangesPath: '/api/:version/projects/:id/merge_requests/:mrid/changes',
projectMergeRequestVersionsPath: '/api/:version/projects/:id/merge_requests/:mrid/versions',
mergeRequestsPath: '/api/:version/merge_requests',
mergeRequestChangesPath: '/api/:version/projects/:id/merge_requests/:mrid/changes',
mergeRequestVersionsPath: '/api/:version/projects/:id/merge_requests/:mrid/versions',
groupLabelsPath: '/groups/:namespace_path/-/labels',
issuableTemplatePath: '/:namespace_path/:project_path/templates/:type/:key',
projectTemplatePath: '/api/:version/projects/:id/templates/:type/:key',
......@@ -99,36 +99,36 @@ const Api = {
},
// Return Merge Request for project
mergeRequest(projectPath, mergeRequestId, params = {}) {
const url = Api.buildUrl(Api.mergeRequestPath)
projectMergeRequest(projectPath, mergeRequestId, params = {}) {
const url = Api.buildUrl(Api.projectMergeRequestPath)
.replace(':id', encodeURIComponent(projectPath))
.replace(':mrid', mergeRequestId);
return axios.get(url, { params });
},
mergeRequests(params = {}) {
const url = Api.buildUrl(Api.mergeRequestsPath);
return axios.get(url, { params });
},
mergeRequestChanges(projectPath, mergeRequestId) {
const url = Api.buildUrl(Api.mergeRequestChangesPath)
projectMergeRequestChanges(projectPath, mergeRequestId) {
const url = Api.buildUrl(Api.projectMergeRequestChangesPath)
.replace(':id', encodeURIComponent(projectPath))
.replace(':mrid', mergeRequestId);
return axios.get(url);
},
mergeRequestVersions(projectPath, mergeRequestId) {
const url = Api.buildUrl(Api.mergeRequestVersionsPath)
projectMergeRequestVersions(projectPath, mergeRequestId) {
const url = Api.buildUrl(Api.projectMergeRequestVersionsPath)
.replace(':id', encodeURIComponent(projectPath))
.replace(':mrid', mergeRequestId);
return axios.get(url);
},
mergeRequests(params = {}) {
const url = Api.buildUrl(Api.mergeRequestsPath);
return axios.get(url, { params });
},
newLabel(namespacePath, projectPath, data, callback) {
let url;
......
......@@ -26,6 +26,9 @@ export default function renderMermaid($els) {
},
// mermaidAPI options
theme: 'neutral',
flowchart: {
htmlLabels: false,
},
});
$els.each((i, el) => {
......
......@@ -41,13 +41,13 @@ export default {
return Api.project(`${namespace}/${project}`);
},
getProjectMergeRequestData(projectId, mergeRequestId, params = {}) {
return Api.mergeRequest(projectId, mergeRequestId, params);
return Api.projectMergeRequest(projectId, mergeRequestId, params);
},
getProjectMergeRequestChanges(projectId, mergeRequestId) {
return Api.mergeRequestChanges(projectId, mergeRequestId);
return Api.projectMergeRequestChanges(projectId, mergeRequestId);
},
getProjectMergeRequestVersions(projectId, mergeRequestId) {
return Api.mergeRequestVersions(projectId, mergeRequestId);
return Api.projectMergeRequestVersions(projectId, mergeRequestId);
},
getBranchData(projectId, currentBranchId) {
return Api.branchSingle(projectId, currentBranchId);
......
......@@ -23,13 +23,19 @@ export const receiveMergeRequestsError = ({ commit, dispatch }, { type, search }
export const receiveMergeRequestsSuccess = ({ commit }, data) =>
commit(types.RECEIVE_MERGE_REQUESTS_SUCCESS, data);
export const fetchMergeRequests = ({ dispatch, state: { state } }, { type, search = '' }) => {
export const fetchMergeRequests = (
{ dispatch, state: { state }, rootState: { currentProjectId } },
{ type, search = '' },
) => {
dispatch('requestMergeRequests');
dispatch('resetMergeRequests');
const scope = type ? scopes[type] : 'all';
const scope = type && scopes[type];
const request = scope
? Api.mergeRequests({ scope, state, search })
: Api.projectMergeRequest(currentProjectId, '', { state, search });
return Api.mergeRequests({ scope, state, search })
return request
.then(({ data }) => dispatch('receiveMergeRequestsSuccess', data))
.catch(() => dispatch('receiveMergeRequestsError', { type, search }));
};
......
......@@ -128,7 +128,7 @@ export default {
};
</script>
<template>
<div class="prepend-top-default js-environment-container">
<div class="prepend-top-default append-bottom-default js-environment-container">
<div class="environment-information">
<ci-icon :status="iconStatus" />
<p class="inline append-bottom-0" v-html="environment"></p>
......
......@@ -28,20 +28,22 @@ export default {
<div class="bs-callout bs-callout-warning">
<p v-if="tags.length" class="js-stuck-with-tags append-bottom-0">
{{
s__(`This job is stuck, because you don't have
s__(`This job is stuck because you don't have
any active runners online with any of these tags assigned to them:`)
}}
<span v-for="(tag, index) in tags" :key="index" class="badge badge-primary"> {{ tag }} </span>
<span v-for="(tag, index) in tags" :key="index" class="badge badge-primary append-right-4">
{{ tag }}
</span>
</p>
<p v-else-if="hasNoRunnersForProject" class="js-stuck-no-runners append-bottom-0">
{{
s__(`Job|This job is stuck, because the project
s__(`Job|This job is stuck because the project
doesn't have any runners online assigned to it.`)
}}
</p>
<p v-else class="js-stuck-no-active-runner append-bottom-0">
{{
s__(`This job is stuck, because you don't
s__(`This job is stuck because you don't
have any active runners that can run this job.`)
}}
</p>
......
......@@ -12,11 +12,11 @@ class ApplicationController < ActionController::Base
include WorkhorseHelper
include EnforcesTwoFactorAuthentication
include WithPerformanceBar
include SessionlessAuthentication
# this can be removed after switching to rails 5
# https://gitlab.com/gitlab-org/gitlab-ce/issues/51908
include InvalidUTF8ErrorHandler unless Gitlab.rails5?
before_action :authenticate_sessionless_user!
before_action :authenticate_user!
before_action :enforce_terms!, if: :should_enforce_terms?
before_action :validate_user_service_ticket!
......@@ -153,13 +153,6 @@ class ApplicationController < ActionController::Base
end
end
# This filter handles personal access tokens, and atom requests with rss tokens
def authenticate_sessionless_user!
user = Gitlab::Auth::RequestAuthenticator.new(request).find_sessionless_user
sessionless_sign_in(user) if user
end
def log_exception(exception)
Raven.capture_exception(exception) if sentry_enabled?
......@@ -426,25 +419,11 @@ class ApplicationController < ActionController::Base
Gitlab::I18n.with_user_locale(current_user, &block)
end
def sessionless_sign_in(user)
if user && can?(user, :log_in)
# Notice we are passing store false, so the user is not
# actually stored in the session and a token is needed
# for every request. If you want the token to work as a
# sign in token, you can simply remove store: false.
sign_in(user, store: false, message: :sessionless_sign_in)
end
end
def set_page_title_header
# Per https://tools.ietf.org/html/rfc5987, headers need to be ISO-8859-1, not UTF-8
response.headers['Page-Title'] = URI.escape(page_title('GitLab'))
end
def sessionless_user?
current_user && !session.keys.include?('warden.user.user.key')
end
def peek_request?
request.path.start_with?('/-/peek')
end
......
......@@ -6,6 +6,7 @@ module NotesActions
extend ActiveSupport::Concern
included do
prepend_before_action :normalize_create_params, only: [:create]
before_action :set_polling_interval_header, only: [:index]
before_action :require_noteable!, only: [:index, :create]
before_action :authorize_admin_note!, only: [:update, :destroy]
......@@ -247,6 +248,15 @@ module NotesActions
DiscussionSerializer.new(project: project, noteable: noteable, current_user: current_user, note_entity: ProjectNoteEntity)
end
# Avoids checking permissions in the wrong object - this ensures that the object we checked permissions for
# is the object we're actually creating a note in.
def normalize_create_params
params[:note].try do |note|
note[:noteable_id] = params[:target_id]
note[:noteable_type] = params[:target_type].classify
end
end
def note_project
strong_memoize(:note_project) do
next nil unless project
......
# frozen_string_literal: true
# == SessionlessAuthentication
#
# Controller concern to handle PAT and RSS token authentication methods
#
module SessionlessAuthentication
# This filter handles personal access tokens, and atom requests with rss tokens
def authenticate_sessionless_user!(request_format)
user = Gitlab::Auth::RequestAuthenticator.new(request).find_sessionless_user(request_format)
sessionless_sign_in(user) if user
end
def sessionless_user?
current_user && !session.keys.include?('warden.user.user.key')
end
def sessionless_sign_in(user)
if user && can?(user, :log_in)
# Notice we are passing store false, so the user is not
# actually stored in the session and a token is needed
# for every request. If you want the token to work as a
# sign in token, you can simply remove store: false.
sign_in(user, store: false, message: :sessionless_sign_in)
end
end
end
......@@ -4,6 +4,7 @@ class Dashboard::ProjectsController < Dashboard::ApplicationController
include ParamsBackwardCompatibility
include RendersMemberAccess
prepend_before_action(only: [:index]) { authenticate_sessionless_user!(:rss) }
before_action :set_non_archived_param
before_action :default_sorting
skip_cross_project_access_check :index, :starred
......
......@@ -4,6 +4,7 @@ class Dashboard::TodosController < Dashboard::ApplicationController
include ActionView::Helpers::NumberHelper
before_action :authorize_read_project!, only: :index
before_action :authorize_read_group!, only: :index
before_action :find_todos, only: [:index, :destroy_all]
def index
......@@ -60,6 +61,15 @@ class Dashboard::TodosController < Dashboard::ApplicationController
end
end
def authorize_read_group!
group_id = params[:group_id]
if group_id.present?
group = Group.find(group_id)
render_404 unless can?(current_user, :read_group, group)
end
end
def find_todos
@todos ||= TodosFinder.new(current_user, todo_params).execute
end
......
......@@ -4,6 +4,9 @@ class DashboardController < Dashboard::ApplicationController
include IssuesAction
include MergeRequestsAction
prepend_before_action(only: [:issues]) { authenticate_sessionless_user!(:rss) }
prepend_before_action(only: [:issues_calendar]) { authenticate_sessionless_user!(:ics) }
before_action :event_filter, only: :activity
before_action :projects, only: [:issues, :merge_requests]
before_action :set_show_full_reference, only: [:issues, :merge_requests]
......
......@@ -3,6 +3,7 @@
class GraphqlController < ApplicationController
# Unauthenticated users have access to the API for public data
skip_before_action :authenticate_user!
prepend_before_action(only: [:execute]) { authenticate_sessionless_user!(:api) }
before_action :check_graphql_feature_flag!
......
......@@ -9,6 +9,9 @@ class GroupsController < Groups::ApplicationController
respond_to :html
prepend_before_action(only: [:show, :issues]) { authenticate_sessionless_user!(:rss) }
prepend_before_action(only: [:issues_calendar]) { authenticate_sessionless_user!(:ics) }
before_action :authenticate_user!, only: [:new, :create]
before_action :group, except: [:index, :new, :create]
......
......@@ -9,7 +9,7 @@ class Oauth::ApplicationsController < Doorkeeper::ApplicationsController
before_action :verify_user_oauth_applications_enabled, except: :index
before_action :authenticate_user!
before_action :add_gon_variables
before_action :load_scopes, only: [:index, :create, :edit]
before_action :load_scopes, only: [:index, :create, :edit, :update]
helper_method :can?
......
......@@ -6,6 +6,7 @@ class Projects::CommitsController < Projects::ApplicationController
include ExtractsPath
include RendersCommits
prepend_before_action(only: [:show]) { authenticate_sessionless_user!(:rss) }
before_action :whitelist_query_limiting, except: :commits_root
before_action :require_non_empty_project
before_action :assign_ref_vars, except: :commits_root
......
......@@ -9,10 +9,6 @@ class Projects::IssuesController < Projects::ApplicationController
include IssuesCalendar
include SpammableActions
def self.authenticate_user_only_actions
%i[new]
end
def self.issue_except_actions
%i[index calendar new create bulk_update]
end
......@@ -21,7 +17,10 @@ class Projects::IssuesController < Projects::ApplicationController
%i[index calendar]
end
prepend_before_action :authenticate_user!, only: authenticate_user_only_actions
prepend_before_action(only: [:index]) { authenticate_sessionless_user!(:rss) }
prepend_before_action(only: [:calendar]) { authenticate_sessionless_user!(:ics) }
prepend_before_action :authenticate_new_issue!, only: [:new]
prepend_before_action :store_uri, only: [:new, :show]
before_action :whitelist_query_limiting, only: [:create, :create_merge_request, :move, :bulk_update]
before_action :check_issues_available!
......@@ -232,16 +231,18 @@ class Projects::IssuesController < Projects::ApplicationController
] + [{ label_ids: [], assignee_ids: [] }]
end
def authenticate_user!
def authenticate_new_issue!
return if current_user
notice = "Please sign in to create the new issue."
redirect_to new_user_session_path, notice: notice
end
def store_uri
if request.get? && !request.xhr?
store_location_for :user, request.fullpath
end
redirect_to new_user_session_path, notice: notice
end
def serializer
......
......@@ -11,7 +11,10 @@ class Projects::MilestonesController < Projects::ApplicationController
before_action :authorize_read_milestone!
# Allow admin milestone
before_action :authorize_admin_milestone!, except: [:index, :show, :merge_requests, :participants, :labels, :promote]
before_action :authorize_admin_milestone!, except: [:index, :show, :merge_requests, :participants, :labels]
# Allow to promote milestone
before_action :authorize_promote_milestone!, only: :promote
respond_to :html
......@@ -78,7 +81,7 @@ 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] = flash_notice_for(promoted_milestone, project_group)
respond_to do |format|
format.html do
......@@ -109,6 +112,12 @@ class Projects::MilestonesController < Projects::ApplicationController
protected
def project_group
strong_memoize(:project_group) do
project.group
end
end
def milestones
strong_memoize(:milestones) do
MilestonesFinder.new(search_params).execute
......@@ -125,13 +134,17 @@ class Projects::MilestonesController < Projects::ApplicationController
return render_404 unless can?(current_user, :admin_milestone, @project)
end
def authorize_promote_milestone!
return render_404 unless can?(current_user, :admin_milestone, project_group)
end
def milestone_params
params.require(:milestone).permit(:title, :description, :start_date, :due_date, :state_event)
end
def search_params
if request.format.json? && @project.group && can?(current_user, :read_group, @project.group)
groups = @project.group.self_and_ancestors_ids
if request.format.json? && project_group && can?(current_user, :read_group, project_group)
groups = project_group.self_and_ancestors_ids
end
params.permit(:state).merge(project_ids: @project.id, group_ids: groups)
......
......@@ -3,6 +3,8 @@
class Projects::TagsController < Projects::ApplicationController
include SortingHelper
prepend_before_action(only: [:index]) { authenticate_sessionless_user!(:rss) }
# Authorize
before_action :require_non_empty_project
before_action :authorize_download_code!
......
......@@ -7,6 +7,8 @@ class ProjectsController < Projects::ApplicationController
include PreviewMarkdown
include SendFileUpload
prepend_before_action(only: [:show]) { authenticate_sessionless_user!(:rss) }
before_action :whitelist_query_limiting, only: [:create]
before_action :authenticate_user!, except: [:index, :show, :activity, :refs]
before_action :redirect_git_extension, only: [:show]
......
......@@ -14,6 +14,7 @@ class UsersController < ApplicationController
calendar_activities: true
skip_before_action :authenticate_user!
prepend_before_action(only: [:show]) { authenticate_sessionless_user!(:rss) }
before_action :user, except: [:exists]
before_action :authorize_read_user_profile!,
only: [:calendar, :calendar_activities, :groups, :projects, :contributed_projects, :snippets]
......
......@@ -2,6 +2,7 @@
module MilestonesHelper
include EntityDateHelper
include Gitlab::Utils::StrongMemoize
def milestones_filter_path(opts = {})
if @project
......@@ -243,4 +244,16 @@ module MilestonesHelper
dashboard_milestone_path(milestone.safe_title, title: milestone.title)
end
end
def can_admin_project_milestones?
strong_memoize(:can_admin_project_milestones) do
can?(current_user, :admin_milestone, @project)
end
end
def can_admin_group_milestones?
strong_memoize(:can_admin_group_milestones) do
can?(current_user, :admin_milestone, @project.group)
end
end
end
......@@ -26,7 +26,7 @@ module Emails
mail_answer_note_thread(@merge_request, @note, note_thread_options(recipient_id))
end
def note_snippet_email(recipient_id, note_id)
def note_project_snippet_email(recipient_id, note_id)
setup_note_mail(note_id, recipient_id)
@snippet = @note.noteable
......
......@@ -15,6 +15,8 @@ module Ci
WRITE_LOCK_SLEEP = 0.01.seconds
WRITE_LOCK_TTL = 1.minute
FailedToPersistDataError = Class.new(StandardError)
# Note: The ordering of this enum is related to the precedence of persist store.
# The bottom item takes the higest precedence, and the top item takes the lowest precedence.
enum data_store: {
......@@ -109,15 +111,18 @@ module Ci
def unsafe_persist_to!(new_store)
return if data_store == new_store.to_s
raise ArgumentError, 'Can not persist empty data' unless size > 0
current_data = get_data
unless current_data&.bytesize.to_i == CHUNK_SIZE
raise FailedToPersistDataError, 'Data is not fullfilled in a bucket'
end
old_store_class = self.class.get_store_class(data_store)
get_data.tap do |the_data|
self.raw_data = nil
self.data_store = new_store
unsafe_set_data!(the_data)
end
unsafe_set_data!(current_data)
old_store_class.delete_data(self)
end
......
......@@ -15,7 +15,7 @@ module CacheMarkdownField
# Increment this number every time the renderer changes its output
CACHE_REDCARPET_VERSION = 3
CACHE_COMMONMARK_VERSION_START = 10
CACHE_COMMONMARK_VERSION = 11
CACHE_COMMONMARK_VERSION = 12
# changes to these attributes cause the cache to be invalidates
INVALIDATED_BY = %w[author project].freeze
......
......@@ -8,6 +8,7 @@ class EnvironmentStatus
delegate :id, to: :environment
delegate :name, to: :environment
delegate :project, to: :environment
delegate :status, to: :deployment, allow_nil: true
delegate :deployed_at, to: :deployment, allow_nil: true
def self.for_merge_request(mr, user)
......@@ -43,22 +44,6 @@ class EnvironmentStatus
.merge_request_diff_files.where(deleted_file: false)
end
##
# Since frontend has not supported all statuses yet, BE has to
# proxy some status to a supported status.
def status
return unless deployment
case deployment.status
when 'created'
'running'
when 'canceled'
'failed'
else
deployment.status
end
end
private
PAGE_EXTENSIONS = /\A\.(s?html?|php|asp|cgi|pl)\z/i.freeze
......
......@@ -324,7 +324,7 @@ class Note < ActiveRecord::Base
end
def to_ability_name
for_personal_snippet? ? 'personal_snippet' : noteable_type.underscore
for_snippet? ? noteable.class.name.underscore : noteable_type.underscore
end
def can_be_discussion_note?
......
# frozen_string_literal: true
class PoolRepository < ActiveRecord::Base
POOL_PREFIX = '@pools'
belongs_to :shard
validates :shard, presence: true
# For now, only pool repositories are tracked in the database. However, we may
# want to add other repository types in the future
self.table_name = 'repositories'
has_many :member_projects, class_name: 'Project'
has_many :pool_member_projects, class_name: 'Project', foreign_key: :pool_repository_id
after_create :correct_disk_path
def shard_name
shard&.name
......@@ -19,4 +15,15 @@ class PoolRepository < ActiveRecord::Base
def shard_name=(name)
self.shard = Shard.by_name(name)
end
private
def correct_disk_path
update!(disk_path: storage.disk_path)
end
def storage
Storage::HashedProject
.new(self, prefix: Storage::HashedProject::POOL_PATH_PREFIX)
end
end
......@@ -878,9 +878,9 @@ class Project < ActiveRecord::Base
end
def readme_url
readme = repository.readme
if readme
Gitlab::Routing.url_helpers.project_blob_url(self, File.join(default_branch, readme.path))
readme_path = repository.readme_path
if readme_path
Gitlab::Routing.url_helpers.project_blob_url(self, File.join(default_branch, readme_path))
end
end
......
......@@ -71,7 +71,7 @@ class PrometheusService < MonitoringService
end
def prometheus_client
RestClient::Resource.new(api_url) if api_url && manual_configuration? && active?
RestClient::Resource.new(api_url, max_redirects: 0) if api_url && manual_configuration? && active?
end
def prometheus_available?
......
......@@ -35,7 +35,7 @@ class Repository
#
# For example, for entry `:commit_count` there's a method called `commit_count` which
# stores its data in the `commit_count` cache key.
CACHED_METHODS = %i(size commit_count rendered_readme contribution_guide
CACHED_METHODS = %i(size commit_count rendered_readme readme_path contribution_guide
changelog license_blob license_key gitignore
gitlab_ci_yml branch_names tag_names branch_count
tag_count avatar exists? root_ref has_visible_content?
......@@ -48,7 +48,7 @@ class Repository
# changed. This Hash maps file types (as returned by Gitlab::FileDetector) to
# the corresponding methods to call for refreshing caches.
METHOD_CACHES_FOR_FILE_TYPES = {
readme: :rendered_readme,
readme: %i(rendered_readme readme_path),
changelog: :changelog,
license: %i(license_blob license_key license),
contributing: :contribution_guide,
......@@ -591,6 +591,11 @@ class Repository
head_tree&.readme
end
def readme_path
readme&.path
end
cache_method :readme_path
def rendered_readme
return unless readme
......
......@@ -5,17 +5,19 @@ module Storage
attr_accessor :project
delegate :gitlab_shell, :repository_storage, to: :project
ROOT_PATH_PREFIX = '@hashed'.freeze
REPOSITORY_PATH_PREFIX = '@hashed'
POOL_PATH_PREFIX = '@pools'
def initialize(project)
def initialize(project, prefix: REPOSITORY_PATH_PREFIX)
@project = project
@prefix = prefix
end
# Base directory
#
# @return [String] directory where repository is stored
def base_dir
"#{ROOT_PATH_PREFIX}/#{disk_hash[0..1]}/#{disk_hash[2..3]}" if disk_hash
"#{@prefix}/#{disk_hash[0..1]}/#{disk_hash[2..3]}" if disk_hash
end
# Disk path is used to build repository and project's wiki path on disk
......
......@@ -2,4 +2,6 @@
class CommitPolicy < BasePolicy
delegate { @subject.project }
rule { can?(:download_code) }.enable :read_commit
end
......@@ -9,8 +9,17 @@ class NotePolicy < BasePolicy
condition(:editable, scope: :subject) { @subject.editable? }
condition(:can_read_noteable) { can?(:"read_#{@subject.to_ability_name}") }
rule { ~editable }.prevent :admin_note
# If user can't read the issue/MR/etc then they should not be allowed to do anything to their own notes
rule { ~can_read_noteable }.policy do
prevent :read_note
prevent :admin_note
prevent :resolve_note
end
rule { is_author }.policy do
enable :read_note
enable :admin_note
......
= email_default_heading("Hello, #{@resource.name}!")
- if @resource.try(:unconfirmed_email?)
%p
We're contacting you to notify you that your email is being changed to #{@resource.reload.unconfirmed_email}.
- else
%p
We're contacting you to notify you that your email has been changed to #{@resource.email}.
%p
If you did not initiate this change, please contact your administrator
immediately.
Hello, <%= @resource.name %>!
<% if @resource.try(:unconfirmed_email?) %>
We're contacting you to notify you that your email is being changed to <%= @resource.reload.unconfirmed_email %>.
<% else %>
We're contacting you to notify you that your email has been changed to <%= @resource.email %>.
<% end %>
If you did not initiate this change, please contact your administrator
immediately.
- page_title 'Edit', @label.name, 'Labels'
- add_to_breadcrumbs _("Labels"), group_labels_path(@group)
- breadcrumb_title _("Edit")
- page_title "Edit", @label.name, _("Labels")
%h3.page-title
Edit Label
......
- breadcrumb_title "Labels"
- page_title 'New Label'
- add_to_breadcrumbs _("Labels"), group_labels_path(@group)
- breadcrumb_title _("New")
- page_title _("New Label")
%h3.page-title
New Label
......
- page_title "Milestones"
- breadcrumb_title _("Edit")
- page_title _("Milestones")
- render "header_title"
%h3.page-title
Edit Milestone
%hr
= render "form"
- breadcrumb_title "Milestones"
- page_title "Milestones"
- @no_container = true
- add_to_breadcrumbs _("Milestones"), group_milestones_path(@group)
- breadcrumb_title _("New")
- page_title _("Milestones"), @milestone.name, _("Milestones")
%h3.page-title
%div{ class: container_class }
%h3.page-title
New Milestone
= render "form"
%hr
= render "form"
......@@ -8,19 +8,7 @@
- ref = local_assigns.fetch(:ref) { merge_request&.source_branch }
- link = commit_path(project, commit, merge_request: merge_request)
- cache_key = [project.full_path,
ref,
commit.id,
Gitlab::CurrentSettings.current_application_settings,
@path.presence,
current_controller?(:commits),
merge_request&.iid,
view_details,
commit.status(ref),
I18n.locale].compact
= cache(cache_key, expires_in: 1.day) do
%li.commit.flex-row.js-toggle-container{ id: "commit-#{commit.short_id}" }
%li.commit.flex-row.js-toggle-container{ id: "commit-#{commit.short_id}" }
.avatar-cell.d-none.d-sm-block
= author_avatar(commit, size: 36, has_tooltip: false)
......
- @no_container = true
- add_to_breadcrumbs "Labels", project_labels_path(@project)
- breadcrumb_title "Edit"
- page_title "Edit", @label.name, "Labels"
%div{ class: container_class }
......
- @no_container = true
- breadcrumb_title "Labels"
- add_to_breadcrumbs "Labels", project_labels_path(@project)
- breadcrumb_title "New"
- page_title "New Label"
%div{ class: container_class }
......
- @no_container = true
- breadcrumb_title "Edit"
- add_to_breadcrumbs "Milestones", project_milestones_path(@project)
- page_title "Edit", @milestone.title, "Milestones"
%div{ class: container_class }
%h3.page-title
......
- @no_container = true
- breadcrumb_title "Milestones"
- add_to_breadcrumbs "Milestones", project_milestones_path(@project)
- breadcrumb_title "New"
- page_title "New Milestone"
%div{ class: container_class }
......
- @content_class = "limit-container-width" unless fluid_layout
- page_title _("Edit"), @page.title.capitalize, _("Wiki")
- add_to_breadcrumbs _("Wiki"), project_wiki_path(@project, @page)
- breadcrumb_title @page.persisted? ? _("Edit") : _("New")
- page_title @page.persisted? ? _("Edit") : _("New"), @page.title.capitalize, _("Wiki")
= wiki_page_errors(@error)
......
......@@ -35,8 +35,8 @@
.col-sm-2
.milestone-actions.d-flex.justify-content-sm-start.justify-content-md-end
- if @project
- if can?(current_user, :admin_milestone, milestone.project) and milestone.active?
- if @project.group
- if can_admin_project_milestones? and milestone.active?
- if can_admin_group_milestones?
%button.js-promote-project-milestone-button.btn.btn-blank.btn-sm.btn-grouped.has-tooltip{ title: _('Promote to Group Milestone'),
disabled: true,
type: 'button',
......
......@@ -3,31 +3,31 @@
.d-none.d-sm-block
- if can?(current_user, :update_personal_snippet, @snippet)
= link_to edit_snippet_path(@snippet), class: "btn btn-grouped" do
Edit
= _("Edit")
- if can?(current_user, :admin_personal_snippet, @snippet)
= link_to snippet_path(@snippet), method: :delete, data: { confirm: "Are you sure?" }, class: "btn btn-grouped btn-inverted btn-remove", title: 'Delete Snippet' do
Delete
= link_to new_snippet_path, class: "btn btn-grouped btn-inverted btn-success", title: "New snippet" do
New snippet
= link_to snippet_path(@snippet), method: :delete, data: { confirm: _("Are you sure?") }, class: "btn btn-grouped btn-inverted btn-remove", title: _('Delete Snippet') do
= _("Delete")
= link_to new_snippet_path, class: "btn btn-grouped btn-inverted btn-create", title: _("New snippet") do
= _("New snippet")
- if @snippet.submittable_as_spam_by?(current_user)
= link_to 'Submit as spam', mark_as_spam_snippet_path(@snippet), method: :post, class: 'btn btn-grouped btn-spam', title: 'Submit as spam'
= link_to _('Submit as spam'), mark_as_spam_snippet_path(@snippet), method: :post, class: 'btn btn-grouped btn-spam', title: _('Submit as spam')
.d-block.d-sm-none.dropdown
%button.btn.btn-default.btn-block.append-bottom-0.prepend-top-5{ data: { toggle: "dropdown" } }
Options
= _("Options")
= icon('caret-down')
.dropdown-menu.dropdown-menu-full-width
%ul
%li
= link_to new_snippet_path, title: "New snippet" do
New snippet
= link_to new_snippet_path, title: _("New snippet") do
= _("New snippet")
- if can?(current_user, :admin_personal_snippet, @snippet)
%li
= link_to snippet_path(@snippet), method: :delete, data: { confirm: "Are you sure?" }, title: 'Delete Snippet' do
Delete
= link_to snippet_path(@snippet), method: :delete, data: { confirm: _("Are you sure?") }, title: _('Delete Snippet') do
= _("Delete")
- if can?(current_user, :update_personal_snippet, @snippet)
%li
= link_to edit_snippet_path(@snippet) do
Edit
= _("Edit")
- if @snippet.submittable_as_spam_by?(current_user)
%li
= link_to 'Submit as spam', mark_as_spam_snippet_path(@snippet), method: :post
= link_to _('Submit as spam'), mark_as_spam_snippet_path(@snippet), method: :post
......@@ -5,6 +5,6 @@
= render partial: 'shared/snippets/snippet', collection: @snippets, locals: { link_project: link_project }
- if @snippets.empty?
%li
.nothing-here-block Nothing here.
.nothing-here-block= _("Nothing here.")
= paginate @snippets, theme: 'gitlab'
......@@ -4,7 +4,7 @@
.nav-links.snippet-scope-menu.mobile-separator.nav.nav-tabs
%li{ class: active_when(params[:scope].nil?) }
= link_to subject_snippets_path(subject) do
All
= _("All")
%span.badge.badge-pill
- if include_private
= subject.snippets.count
......@@ -14,18 +14,18 @@
- if include_private
%li{ class: active_when(params[:scope] == "are_private") }
= link_to subject_snippets_path(subject, scope: 'are_private') do
Private
= _("Private")
%span.badge.badge-pill
= subject.snippets.are_private.count
%li{ class: active_when(params[:scope] == "are_internal") }
= link_to subject_snippets_path(subject, scope: 'are_internal') do
Internal
= _("Internal")
%span.badge.badge-pill
= subject.snippets.are_internal.count
%li{ class: active_when(params[:scope] == "are_public") }
= link_to subject_snippets_path(subject, scope: 'are_public') do
Public
= _("Public")
%span.badge.badge-pill
= subject.snippets.are_public.count
- page_title "Edit", "#{@snippet.title} (#{@snippet.to_reference})", "Snippets"
- page_title _("Edit"), "#{@snippet.title} (#{@snippet.to_reference})", _("Snippets")
%h3.page-title
Edit Snippet
= _("Edit Snippet")
%hr
= render 'shared/snippets/form', url: snippet_path(@snippet)
- page_title "By #{@user.name}", "Snippets"
- page_title _("By %{user_name}") % { user_name: @user.name }, _("Snippets")
%ol.breadcrumb
%li.breadcrumb-item
= link_to snippets_path do
Snippets
= _("Snippets")
%li.breadcrumb-item
= @user.name
.float-right.d-none.d-sm-block
= link_to user_path(@user) do
#{@user.name} profile page
= _("%{user_name} profile page") % { user_name: @user.name }
= render 'snippets'
- @hide_top_links = true
- @hide_breadcrumbs = true
- page_title "New Snippet"
- page_title _("New Snippet")
.page-title-holder
%h1.page-title= _('New Snippet')
......
- if current_user
- if note.emoji_awardable?
.note-actions-item
= link_to '#', title: 'Add reaction', class: "note-action-button note-emoji-button js-add-award js-note-emoji has-tooltip", data: { position: 'right' } do
= link_to '#', title: _('Add reaction'), class: "note-action-button note-emoji-button js-add-award js-note-emoji has-tooltip", data: { position: 'right' } do
= icon('spinner spin')
%span{ class: 'link-highlight award-control-icon-neutral' }= custom_icon('emoji_slightly_smiling_face')
%span{ class: 'link-highlight award-control-icon-positive' }= custom_icon('emoji_smiley')
......@@ -9,7 +9,7 @@
- if note_editable
.note-actions-item
= button_tag title: 'Edit comment', class: 'note-action-button js-note-edit has-tooltip btn btn-transparent', data: { container: 'body' } do
= button_tag title: _('Edit comment'), class: 'note-action-button js-note-edit has-tooltip btn btn-transparent', data: { container: 'body' } do
%span.link-highlight
= custom_icon('icon_pencil')
......
- @hide_top_links = true
- @content_class = "limit-container-width limited-inner-width-container" unless fluid_layout
- add_to_breadcrumbs "Snippets", dashboard_snippets_path
- add_to_breadcrumbs _("Snippets"), dashboard_snippets_path
- breadcrumb_title @snippet.to_reference
- page_title "#{@snippet.title} (#{@snippet.to_reference})", "Snippets"
- page_title "#{@snippet.title} (#{@snippet.to_reference})", _("Snippets")
= render 'shared/snippets/header'
......
......@@ -7,7 +7,7 @@
%li
%span.light
%i.fa.fa-clock-o
= event.created_at.strftime('%-I:%M%P')
= event.created_at.to_time.in_time_zone.strftime('%-I:%M%P')
- if event.visible_to_user?(current_user)
- if event.push?
#{event.action_name} #{event.ref_type}
......
---
title: Add a rebase API endpoint for merge requests
merge_request: 23296
author:
type: added
---
title: Show user contributions in correct timezone within user profile
merge_request: 23419
author:
type: changed
---
title: Scope default MR search in WebIDE dropdown to current project
merge_request: 23400
author:
type: changed
---
title: Improves performance of Project#readme_url by caching the README path
merge_request: 23357
author:
type: performance
---
title: Adds margins between tags when a job is stuck
merge_request:
author:
type: fixed
---
title: Validate chunk size when persist
merge_request: 23341
author:
type: fixed
---
title: Externalize strings from `/app/views/snippets`
merge_request: 23351
author: Tao Wang
type: other
---
title: Include new link in breadcrumb for issues, merge requests, milestones, and labels
merge_request: 18515
author: George Tsiolis
type: changed
---
title: Return real deployment status to frontend
merge_request: 23270
author:
type: fixed
---
title: Redact sensitive information on gitlab-workhorse log
merge_request:
author:
type: security
---
title: Do not follow redirects in Prometheus service when making http requests to the configured api url
merge_request:
author:
type: security
---
title: Don't expose confidential information in commit message list
merge_request:
author:
type: security
---
title: Provide email notification when a user changes their email address
merge_request:
author:
type: security
---
title: Restrict Personal Access Tokens to API scope on web requests
merge_request:
author:
type: security
---
title: Resolve reflected XSS in Ouath authorize window
merge_request:
author:
type: security
---
title: Fix SSRF in project integrations
merge_request:
author:
type: security
---
title: Fix CRLF vulnerability in Project hooks
merge_request:
author:
type: security
---
title: Fixed ability to comment on locked/confidential issues.
merge_request:
author:
type: security
---
title: Fixed ability of guest users to edit/delete comments on locked or confidential issues.
merge_request:
author:
type: security
---
title: Fix milestone promotion authorization check
merge_request:
author:
type: security
---
title: Configure mermaid to not render HTML content in diagrams
merge_request:
author:
type: security
---
title: Fix a possible symlink time of check to time of use race condition in GitLab
Pages
merge_request:
author:
type: security
---
title: Removed ability to see private group names when the group id is entered in
the url.
merge_request:
author:
type: security
---
title: Fix stored XSS for Environments
merge_request:
author:
type: security
---
title: Fix possible XSS attack in Markdown urls with spaces
merge_request: 2599
author:
type: security
---
title: Add monkey patch to unicorn to fix eof? problem
merge_request: 23385
author:
type: fixed
......@@ -13,6 +13,10 @@ if defined?(Unicorn)
# Max memory size (RSS) per worker
use Unicorn::WorkerKiller::Oom, min, max
end
# Monkey patch for fixing Rack 2.0.6 bug:
# https://gitlab.com/gitlab-org/gitlab-ee/issues/8539
Unicorn::StreamInput.send(:public, :eof?) # rubocop:disable GitlabSecurity/PublicSend
end
require ::File.expand_path('../config/environment', __FILE__)
......
......@@ -103,6 +103,9 @@ module Gitlab
# - Webhook URLs (:hook)
# - Sentry DSN (:sentry_dsn)
# - File content from Web Editor (:content)
#
# NOTE: It is **IMPORTANT** to also update gitlab-workhorse's filter when adding parameters here to not
# introduce another security vulnerability: https://gitlab.com/gitlab-org/gitlab-workhorse/issues/182
config.filter_parameters += [/token$/, /password/, /secret/, /key$/]
config.filter_parameters += %i(
certificate
......
......@@ -103,6 +103,9 @@ Devise.setup do |config|
# Send a notification email when the user's password is changed
config.send_password_change_notification = true
# Send a notification email when the user's email is changed
config.send_email_changed_notification = true
# ==> Configuration for :validatable
# Range for password length. Default is 6..128.
config.password_length = 8..128
......
......@@ -48,6 +48,13 @@ Doorkeeper.configure do
#
force_ssl_in_redirect_uri false
# Specify what redirect URI's you want to block during Application creation.
# Any redirect URI is whitelisted by default.
#
# You can use this option in order to forbid URI's with 'javascript' scheme
# for example.
forbid_redirect_uri { |uri| %w[data vbscript javascript].include?(uri.scheme.to_s.downcase) }
# Provide support for an owner to be assigned to each registered application (disabled by default)
# Optional parameter confirmation: true (default false) if you want to enforce ownership of
# a registered application
......
......@@ -33,22 +33,22 @@ class Rack::Attack
throttle('throttle_authenticated_api', Gitlab::Throttle.authenticated_api_options) do |req|
Gitlab::Throttle.settings.throttle_authenticated_api_enabled &&
req.api_request? &&
req.authenticated_user_id
req.authenticated_user_id([:api])
end
throttle('throttle_authenticated_web', Gitlab::Throttle.authenticated_web_options) do |req|
Gitlab::Throttle.settings.throttle_authenticated_web_enabled &&
req.web_request? &&
req.authenticated_user_id
req.authenticated_user_id([:api, :rss, :ics])
end
class Request
def unauthenticated?
!authenticated_user_id
!authenticated_user_id([:api, :rss, :ics])
end
def authenticated_user_id
Gitlab::Auth::RequestAuthenticator.new(self).user&.id
def authenticated_user_id(request_formats)
Gitlab::Auth::RequestAuthenticator.new(self).user(request_formats)&.id
end
def api_request?
......
# frozen_string_literal: true
class CleanupEnvironmentsExternalUrl < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
update_column_in_batches(:environments, :external_url, nil) do |table, query|
query.where(table[:external_url].matches('javascript://%'))
end
end
def down
end
end
class RenameRepositoriesPoolRepositories < ActiveRecord::Migration[5.0]
include Gitlab::Database::MigrationHelpers
# This change doesn't require downtime as the table is not in use, so we're
# free to change an empty table
DOWNTIME = false
def change
rename_table :repositories, :pool_repositories
end
end
# frozen_string_literal: true
class DropNotNullConstraintPoolRepositoryDiskPath < ActiveRecord::Migration[5.0]
DOWNTIME = false
def change
change_column_null :pool_repositories, :disk_path, true
end
end
# frozen_string_literal: true
class MigrateForbiddenRedirectUris < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
FORBIDDEN_SCHEMES = %w[data:// vbscript:// javascript://]
NEW_URI = 'http://forbidden-scheme-has-been-overwritten'
disable_ddl_transaction!
def up
update_forbidden_uris(:oauth_applications)
update_forbidden_uris(:oauth_access_grants)
end
def down
# noop
end
private
def update_forbidden_uris(table_name)
update_column_in_batches(table_name, :redirect_uri, NEW_URI) do |table, query|
where_clause = FORBIDDEN_SCHEMES.map do |scheme|
table[:redirect_uri].matches("#{scheme}%")
end.inject(&:or)
query.where(where_clause)
end
end
end
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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