Commit ff343fa0 authored by Sean McGivern's avatar Sean McGivern Committed by Mike Greiling

Merge branch 'fix/mirror-user' into 'security-9-3-ee'

Fix mirroring and CI exploit

See merge request !518
parent b56c3c6a
module SafeMirrorParams
extend ActiveSupport::Concern
included do
helper_method :default_mirror_users
end
private
def valid_mirror_user?(mirror_params)
return true unless mirror_params[:mirror_user_id].present?
default_mirror_users.map(&:id).include?(mirror_params[:mirror_user_id].to_i)
end
def default_mirror_users
[current_user, @project.mirror_user].compact.uniq
end
end
class Projects::ImportsController < Projects::ApplicationController class Projects::ImportsController < Projects::ApplicationController
include ContinueParams include ContinueParams
include SafeMirrorParams
# Authorize # Authorize
before_action :authorize_admin_project! before_action :authorize_admin_project!
...@@ -11,7 +12,7 @@ class Projects::ImportsController < Projects::ApplicationController ...@@ -11,7 +12,7 @@ class Projects::ImportsController < Projects::ApplicationController
end end
def create def create
if @project.update_attributes(import_params) if @project.update_attributes(safe_import_params)
@project.reload.import_schedule @project.reload.import_schedule
end end
...@@ -67,4 +68,10 @@ class Projects::ImportsController < Projects::ApplicationController ...@@ -67,4 +68,10 @@ class Projects::ImportsController < Projects::ApplicationController
def import_params def import_params
params.require(:project).permit(:import_url, :mirror, :mirror_user_id) params.require(:project).permit(:import_url, :mirror, :mirror_user_id)
end end
def safe_import_params
return import_params if valid_mirror_user?(import_params)
import_params.merge(mirror_user_id: current_user.id)
end
end end
class Projects::MirrorsController < Projects::ApplicationController class Projects::MirrorsController < Projects::ApplicationController
include RepositorySettingsRedirect include RepositorySettingsRedirect
include SafeMirrorParams
# Authorize # Authorize
before_action :authorize_admin_project!, except: [:update_now] before_action :authorize_admin_project!, except: [:update_now]
before_action :authorize_push_code!, only: [:update_now] before_action :authorize_push_code!, only: [:update_now]
...@@ -12,7 +13,7 @@ class Projects::MirrorsController < Projects::ApplicationController ...@@ -12,7 +13,7 @@ class Projects::MirrorsController < Projects::ApplicationController
end end
def update def update
if @project.update_attributes(mirror_params) if @project.update_attributes(safe_mirror_params)
if @project.mirror? if @project.mirror?
@project.force_import_job! @project.force_import_job!
...@@ -51,4 +52,10 @@ class Projects::MirrorsController < Projects::ApplicationController ...@@ -51,4 +52,10 @@ class Projects::MirrorsController < Projects::ApplicationController
params.require(:project).permit(:mirror, :import_url, :mirror_user_id, params.require(:project).permit(:mirror, :import_url, :mirror_user_id,
:mirror_trigger_builds, remote_mirrors_attributes: [:url, :id, :enabled]) :mirror_trigger_builds, remote_mirrors_attributes: [:url, :id, :enabled])
end end
def safe_mirror_params
return mirror_params if valid_mirror_user?(mirror_params)
mirror_params.merge(mirror_user_id: current_user.id)
end
end end
module Projects module Projects
module Settings module Settings
class RepositoryController < Projects::ApplicationController class RepositoryController < Projects::ApplicationController
include SafeMirrorParams
before_action :authorize_admin_project! before_action :authorize_admin_project!
prepend ::EE::Projects::Settings::RepositoryController prepend ::EE::Projects::Settings::RepositoryController
......
...@@ -4,4 +4,8 @@ module MirrorHelper ...@@ -4,4 +4,8 @@ module MirrorHelper
message << "<br>To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above." if can?(current_user, :push_code, @project) message << "<br>To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above." if can?(current_user, :push_code, @project)
message message
end end
def options_for_mirror_user
options_from_collection_for_select(default_mirror_users, :id, :name, @project.mirror_user_id || current_user.id)
end
end end
...@@ -36,12 +36,11 @@ ...@@ -36,12 +36,11 @@
= render "projects/mirrors/instructions" = render "projects/mirrors/instructions"
.form-group .form-group
= f.label :mirror_user_id, "Mirror user", class: "label-light" = f.label :mirror_user_id, "Mirror user", class: "label-light"
= users_select_tag("project[mirror_user_id]", class: 'input-large', selected: @project.mirror_user_id || current_user.id, = select_tag('project[mirror_user_id]', options_for_mirror_user, class: "select2 lg", required: true)
first_user: true, current_user: true, push_code_to_protected_branches: true)
.help-block .help-block
This user will be the author of all events in the activity feed that are the result of an update, This user will be the author of all events in the activity feed that are the result of an update,
like new branches being created or new commits being pushed to existing branches. like new branches being created or new commits being pushed to existing branches.
They need to have at least master access to this project. You can only assign yourself to be the mirror user.
- if @project.builds_enabled? - if @project.builds_enabled?
= render "shared/mirror_trigger_builds_setting", f: f = render "shared/mirror_trigger_builds_setting", f: f
= f.submit 'Save changes', class: 'btn btn-create', name: 'update_remote_mirror' = f.submit 'Save changes', class: 'btn btn-create', name: 'update_remote_mirror'
......
...@@ -6,3 +6,4 @@ ...@@ -6,3 +6,4 @@
Trigger pipelines when branches or tags are updated from the upstream repository. Trigger pipelines when branches or tags are updated from the upstream repository.
Depending on the activity of the upstream repository, this may greatly increase the load on your CI runners. Depending on the activity of the upstream repository, this may greatly increase the load on your CI runners.
Only enable this if you know they can handle the load. Only enable this if you know they can handle the load.
<strong>CI will run using the credentials assigned above.</strong>
---
title: Prevent mirror user to be assigned to users other than the current one
merge_request:
author:
...@@ -2,9 +2,6 @@ require 'spec_helper' ...@@ -2,9 +2,6 @@ require 'spec_helper'
describe Projects::ImportsController do describe Projects::ImportsController do
let(:user) { create(:user) } let(:user) { create(:user) }
describe 'GET #show' do
context 'when repository does not exists' do
let(:project) { create(:empty_project) } let(:project) { create(:empty_project) }
before do before do
...@@ -12,6 +9,8 @@ describe Projects::ImportsController do ...@@ -12,6 +9,8 @@ describe Projects::ImportsController do
project.team << [user, :master] project.team << [user, :master]
end end
describe 'GET #show' do
context 'when repository does not exists' do
it 'renders template' do it 'renders template' do
get :show, namespace_id: project.namespace.to_param, project_id: project get :show, namespace_id: project.namespace.to_param, project_id: project
...@@ -28,11 +27,6 @@ describe Projects::ImportsController do ...@@ -28,11 +27,6 @@ describe Projects::ImportsController do
context 'when repository exists' do context 'when repository exists' do
let(:project) { create(:project_empty_repo, import_url: 'https://github.com/vim/vim.git') } let(:project) { create(:project_empty_repo, import_url: 'https://github.com/vim/vim.git') }
before do
sign_in(user)
project.team << [user, :master]
end
context 'when import is in progress' do context 'when import is in progress' do
before do before do
project.update_attribute(:import_status, :started) project.update_attribute(:import_status, :started)
...@@ -125,4 +119,23 @@ describe Projects::ImportsController do ...@@ -125,4 +119,23 @@ describe Projects::ImportsController do
end end
end end
end end
context 'POST #create' do
context 'mirror user is not the current user' do
it 'should only assign the current user' do
allow_any_instance_of(EE::Project).to receive(:add_import_job)
new_user = create(:user)
project.add_master(new_user)
post :create, namespace_id: project.namespace.to_param,
project_id: project,
project: { mirror: true, mirror_user_id: new_user.id, import_url: 'http://local.dev' },
format: :json
expect(project.reload.mirror).to eq(true)
expect(project.reload.mirror_user.id).to eq(user.id)
end
end
end
end end
...@@ -71,6 +71,20 @@ describe Projects::MirrorsController do ...@@ -71,6 +71,20 @@ describe Projects::MirrorsController do
expect(project.reload.mirror).to eq(true) expect(project.reload.mirror).to eq(true)
expect(project.reload.import_url).to eq('http://local.dev') expect(project.reload.import_url).to eq('http://local.dev')
end end
context 'mirror user is not the current user' do
it 'should only assign the current user' do
expect_any_instance_of(EE::Project).to receive(:force_import_job!)
new_user = create(:user)
project.add_master(new_user)
do_put(project, mirror: true, mirror_user_id: new_user.id, import_url: 'http://local.dev')
expect(project.reload.mirror).to eq(true)
expect(project.reload.mirror_user.id).to eq(project.owner.id)
end
end
end end
end end
end end
......
...@@ -60,7 +60,7 @@ describe 'Project settings > [EE] repository', feature: true do ...@@ -60,7 +60,7 @@ describe 'Project settings > [EE] repository', feature: true do
click_button('Save changes') click_button('Save changes')
expect(find('.select2-chosen')).to have_content(user2.name) expect(find('.select2-chosen')).to have_content(user.name)
end end
end end
end end
......
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