Commit 2d5d31cc authored by Kerri Miller's avatar Kerri Miller

Return error when user lacks read access for project

There are a number of places where we were not checking for user access
rights (or assuming that authors automatically have them) so we were
potentially in situations where a user could create a merge request,
have their access rights revoked, and they would still be able to access
information or take actions related to their MR. This is potentially a
security issue, so we need to block this potential leak.
parent 8e4cdb89
---
title: Check user access on API merge request read actions
merge_request:
author:
type: security
...@@ -26,6 +26,8 @@ module API ...@@ -26,6 +26,8 @@ module API
# GET /projects/:id/merge_requests/:merge_request_iid/approvals # GET /projects/:id/merge_requests/:merge_request_iid/approvals
desc 'List approvals for merge request' desc 'List approvals for merge request'
get 'approvals' do get 'approvals' do
not_found!("Merge Request") unless can?(current_user, :read_merge_request, user_project)
merge_request = find_merge_request_with_access(params[:merge_request_iid]) merge_request = find_merge_request_with_access(params[:merge_request_iid])
present_approval(merge_request) present_approval(merge_request)
......
...@@ -23,6 +23,8 @@ module API ...@@ -23,6 +23,8 @@ module API
use :pagination use :pagination
end end
get ":id/merge_requests/:merge_request_iid/versions" do get ":id/merge_requests/:merge_request_iid/versions" do
not_found!("Merge Request") unless can?(current_user, :read_merge_request, user_project)
merge_request = find_merge_request_with_access(params[:merge_request_iid]) merge_request = find_merge_request_with_access(params[:merge_request_iid])
present paginate(merge_request.merge_request_diffs.order_id_desc), with: Entities::MergeRequestDiff present paginate(merge_request.merge_request_diffs.order_id_desc), with: Entities::MergeRequestDiff
...@@ -39,6 +41,8 @@ module API ...@@ -39,6 +41,8 @@ module API
end end
get ":id/merge_requests/:merge_request_iid/versions/:version_id" do get ":id/merge_requests/:merge_request_iid/versions/:version_id" do
not_found!("Merge Request") unless can?(current_user, :read_merge_request, user_project)
merge_request = find_merge_request_with_access(params[:merge_request_iid]) merge_request = find_merge_request_with_access(params[:merge_request_iid])
present merge_request.merge_request_diffs.find(params[:version_id]), with: Entities::MergeRequestDiffFull present merge_request.merge_request_diffs.find(params[:version_id]), with: Entities::MergeRequestDiffFull
......
...@@ -248,6 +248,8 @@ module API ...@@ -248,6 +248,8 @@ module API
success Entities::MergeRequest success Entities::MergeRequest
end end
get ':id/merge_requests/:merge_request_iid' do get ':id/merge_requests/:merge_request_iid' do
not_found!("Merge Request") unless can?(current_user, :read_merge_request, user_project)
merge_request = find_merge_request_with_access(params[:merge_request_iid]) merge_request = find_merge_request_with_access(params[:merge_request_iid])
present merge_request, present merge_request,
...@@ -264,7 +266,10 @@ module API ...@@ -264,7 +266,10 @@ module API
success Entities::UserBasic success Entities::UserBasic
end end
get ':id/merge_requests/:merge_request_iid/participants' do get ':id/merge_requests/:merge_request_iid/participants' do
not_found!("Merge Request") unless can?(current_user, :read_merge_request, user_project)
merge_request = find_merge_request_with_access(params[:merge_request_iid]) merge_request = find_merge_request_with_access(params[:merge_request_iid])
participants = ::Kaminari.paginate_array(merge_request.participants) participants = ::Kaminari.paginate_array(merge_request.participants)
present paginate(participants), with: Entities::UserBasic present paginate(participants), with: Entities::UserBasic
...@@ -274,6 +279,8 @@ module API ...@@ -274,6 +279,8 @@ module API
success Entities::Commit success Entities::Commit
end end
get ':id/merge_requests/:merge_request_iid/commits' do get ':id/merge_requests/:merge_request_iid/commits' do
not_found!("Merge Request") unless can?(current_user, :read_merge_request, user_project)
merge_request = find_merge_request_with_access(params[:merge_request_iid]) merge_request = find_merge_request_with_access(params[:merge_request_iid])
commits = commits =
...@@ -355,6 +362,8 @@ module API ...@@ -355,6 +362,8 @@ module API
success Entities::MergeRequestChanges success Entities::MergeRequestChanges
end end
get ':id/merge_requests/:merge_request_iid/changes' do get ':id/merge_requests/:merge_request_iid/changes' do
not_found!("Merge Request") unless can?(current_user, :read_merge_request, user_project)
merge_request = find_merge_request_with_access(params[:merge_request_iid]) merge_request = find_merge_request_with_access(params[:merge_request_iid])
present merge_request, present merge_request,
...@@ -370,6 +379,8 @@ module API ...@@ -370,6 +379,8 @@ module API
get ':id/merge_requests/:merge_request_iid/pipelines' do get ':id/merge_requests/:merge_request_iid/pipelines' do
pipelines = merge_request_pipelines_with_access pipelines = merge_request_pipelines_with_access
not_found!("Merge Request") unless can?(current_user, :read_merge_request, user_project)
present paginate(pipelines), with: Entities::Ci::PipelineBasic present paginate(pipelines), with: Entities::Ci::PipelineBasic
end end
......
...@@ -28,6 +28,11 @@ module API ...@@ -28,6 +28,11 @@ module API
end end
post ":id/#{type}/:#{type_id_str}/todo" do post ":id/#{type}/:#{type_id_str}/todo" do
issuable = instance_exec(params[type_id_str], &finder) issuable = instance_exec(params[type_id_str], &finder)
unless can?(current_user, :read_merge_request, issuable.project)
not_found!(type.split("_").map(&:capitalize).join(" "))
end
todo = TodoService.new.mark_todo(issuable, current_user).first todo = TodoService.new.mark_todo(issuable, current_user).first
if todo if todo
......
...@@ -21,6 +21,12 @@ RSpec.describe API::MergeRequestApprovals do ...@@ -21,6 +21,12 @@ RSpec.describe API::MergeRequestApprovals do
expect(response).to have_gitlab_http_status(:ok) expect(response).to have_gitlab_http_status(:ok)
end end
context 'when merge request author has only guest access' do
it_behaves_like 'rejects user from accessing merge request info' do
let(:url) { "/projects/#{project.id}/merge_requests/#{merge_request.iid}/approvals" }
end
end
end end
describe 'POST :id/merge_requests/:merge_request_iid/approve' do describe 'POST :id/merge_requests/:merge_request_iid/approve' do
......
...@@ -35,6 +35,12 @@ RSpec.describe API::MergeRequestDiffs, 'MergeRequestDiffs' do ...@@ -35,6 +35,12 @@ RSpec.describe API::MergeRequestDiffs, 'MergeRequestDiffs' do
get api("/projects/#{project.id}/merge_requests/0/versions", user) get api("/projects/#{project.id}/merge_requests/0/versions", user)
expect(response).to have_gitlab_http_status(:not_found) expect(response).to have_gitlab_http_status(:not_found)
end end
context 'when merge request author has only guest access' do
it_behaves_like 'rejects user from accessing merge request info' do
let(:url) { "/projects/#{project.id}/merge_requests/#{merge_request.iid}/versions" }
end
end
end end
describe 'GET /projects/:id/merge_requests/:merge_request_iid/versions/:version_id' do describe 'GET /projects/:id/merge_requests/:merge_request_iid/versions/:version_id' do
...@@ -63,5 +69,11 @@ RSpec.describe API::MergeRequestDiffs, 'MergeRequestDiffs' do ...@@ -63,5 +69,11 @@ RSpec.describe API::MergeRequestDiffs, 'MergeRequestDiffs' do
get api("/projects/#{project.id}/merge_requests/#{non_existing_record_iid}/versions/#{merge_request_diff.id}", user) get api("/projects/#{project.id}/merge_requests/#{non_existing_record_iid}/versions/#{merge_request_diff.id}", user)
expect(response).to have_gitlab_http_status(:not_found) expect(response).to have_gitlab_http_status(:not_found)
end end
context 'when merge request author has only guest access' do
it_behaves_like 'rejects user from accessing merge request info' do
let(:url) { "/projects/#{project.id}/merge_requests/#{merge_request.iid}/versions/#{merge_request_diff.id}" }
end
end
end end
end end
...@@ -1226,6 +1226,12 @@ RSpec.describe API::MergeRequests do ...@@ -1226,6 +1226,12 @@ RSpec.describe API::MergeRequests do
end end
end end
context 'when merge request author has only guest access' do
it_behaves_like 'rejects user from accessing merge request info' do
let(:url) { "/projects/#{project.id}/merge_requests/#{merge_request.iid}" }
end
end
context 'merge_request_metrics' do context 'merge_request_metrics' do
let(:pipeline) { create(:ci_empty_pipeline) } let(:pipeline) { create(:ci_empty_pipeline) }
...@@ -1402,6 +1408,12 @@ RSpec.describe API::MergeRequests do ...@@ -1402,6 +1408,12 @@ RSpec.describe API::MergeRequests do
it_behaves_like 'issuable participants endpoint' do it_behaves_like 'issuable participants endpoint' do
let(:entity) { create(:merge_request, :simple, milestone: milestone1, author: user, assignees: [user], source_project: project, target_project: project, source_branch: 'markdown', title: "Test", created_at: base_time) } let(:entity) { create(:merge_request, :simple, milestone: milestone1, author: user, assignees: [user], source_project: project, target_project: project, source_branch: 'markdown', title: "Test", created_at: base_time) }
end end
context 'when merge request author has only guest access' do
it_behaves_like 'rejects user from accessing merge request info' do
let(:url) { "/projects/#{project.id}/merge_requests/#{merge_request.iid}/participants" }
end
end
end end
describe 'GET /projects/:id/merge_requests/:merge_request_iid/commits' do describe 'GET /projects/:id/merge_requests/:merge_request_iid/commits' do
...@@ -1427,6 +1439,12 @@ RSpec.describe API::MergeRequests do ...@@ -1427,6 +1439,12 @@ RSpec.describe API::MergeRequests do
expect(response).to have_gitlab_http_status(:not_found) expect(response).to have_gitlab_http_status(:not_found)
end end
context 'when merge request author has only guest access' do
it_behaves_like 'rejects user from accessing merge request info' do
let(:url) { "/projects/#{project.id}/merge_requests/#{merge_request.iid}/commits" }
end
end
end end
describe 'GET /projects/:id/merge_requests/:merge_request_iid/:context_commits' do describe 'GET /projects/:id/merge_requests/:merge_request_iid/:context_commits' do
...@@ -1502,6 +1520,12 @@ RSpec.describe API::MergeRequests do ...@@ -1502,6 +1520,12 @@ RSpec.describe API::MergeRequests do
expect(response).to have_gitlab_http_status(:not_found) expect(response).to have_gitlab_http_status(:not_found)
end end
context 'when merge request author has only guest access' do
it_behaves_like 'rejects user from accessing merge request info' do
let(:url) { "/projects/#{project.id}/merge_requests/#{merge_request.iid}/changes" }
end
end
it_behaves_like 'find an existing merge request' it_behaves_like 'find an existing merge request'
it_behaves_like 'accesses diffs via raw_diffs' it_behaves_like 'accesses diffs via raw_diffs'
...@@ -1591,6 +1615,12 @@ RSpec.describe API::MergeRequests do ...@@ -1591,6 +1615,12 @@ RSpec.describe API::MergeRequests do
expect(response).to have_gitlab_http_status(:forbidden) expect(response).to have_gitlab_http_status(:forbidden)
end end
end end
context 'when merge request author has only guest access' do
it_behaves_like 'rejects user from accessing merge request info' do
let(:url) { "/projects/#{project.id}/merge_requests/#{merge_request.iid}/pipelines" }
end
end
end end
describe 'POST /projects/:id/merge_requests/:merge_request_iid/pipelines' do describe 'POST /projects/:id/merge_requests/:merge_request_iid/pipelines' do
......
...@@ -331,6 +331,14 @@ RSpec.describe API::Todos do ...@@ -331,6 +331,14 @@ RSpec.describe API::Todos do
expect(response).to have_gitlab_http_status(:not_found) expect(response).to have_gitlab_http_status(:not_found)
end end
end end
it 'returns an error if the issuable author does not have access' do
project_1.add_guest(issuable.author)
post api("/projects/#{project_1.id}/#{issuable_type}/#{issuable.iid}/todo", issuable.author)
expect(response).to have_gitlab_http_status(:not_found)
end
end end
describe 'POST :id/issuable_type/:issueable_id/todo' do describe 'POST :id/issuable_type/:issueable_id/todo' do
......
# frozen_string_literal: true
RSpec.shared_examples 'rejects user from accessing merge request info' do
let(:project) { create(:project, :private) }
let(:merge_request) do
create(:merge_request,
author: user,
source_project: project,
target_project: project
)
end
before do
project.add_guest(user)
end
it 'returns a 404 error' do
get api(url, user)
expect(response).to have_gitlab_http_status(:not_found)
expect(json_response['message']).to eq('404 Merge Request Not Found')
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