Fix api leaking notes when user is not authorized to read noteable
This commit is contained in:
		
							parent
							
								
									ae25c19ee5
								
							
						
					
					
						commit
						e56e3cdc62
					
				|  | @ -15,6 +15,7 @@ v 8.8.0 (unreleased) | ||||||
|   - Bump mail_room to 0.7.0 to fix stuck IDLE connections |   - Bump mail_room to 0.7.0 to fix stuck IDLE connections | ||||||
|   - Remove future dates from contribution calendar graph. |   - Remove future dates from contribution calendar graph. | ||||||
|   - Support e-mail notifications for comments on project snippets |   - Support e-mail notifications for comments on project snippets | ||||||
|  |   - Fix API leak of notes of unauthorized issues, snippets and merge requests | ||||||
|   - Use ActionDispatch Remote IP for Akismet checking |   - Use ActionDispatch Remote IP for Akismet checking | ||||||
|   - Fix error when visiting commit builds page before build was updated |   - Fix error when visiting commit builds page before build was updated | ||||||
|   - Add 'l' shortcut to open Label dropdown on issuables and 'i' to create new issue on a project |   - Add 'l' shortcut to open Label dropdown on issuables and 'i' to create new issue on a project | ||||||
|  |  | ||||||
|  | @ -20,19 +20,24 @@ module API | ||||||
|         #   GET /projects/:id/snippets/:noteable_id/notes |         #   GET /projects/:id/snippets/:noteable_id/notes | ||||||
|         get ":id/#{noteables_str}/:#{noteable_id_str}/notes" do |         get ":id/#{noteables_str}/:#{noteable_id_str}/notes" do | ||||||
|           @noteable = user_project.send(:"#{noteables_str}").find(params[:"#{noteable_id_str}"]) |           @noteable = user_project.send(:"#{noteables_str}").find(params[:"#{noteable_id_str}"]) | ||||||
|  |           read_ability_name = "read_#{@noteable.class.to_s.underscore.downcase}".to_sym | ||||||
| 
 | 
 | ||||||
|           # We exclude notes that are cross-references and that cannot be viewed |           if can?(current_user, read_ability_name, @noteable) | ||||||
|           # by the current user. By doing this exclusion at this level and not |             # We exclude notes that are cross-references and that cannot be viewed | ||||||
|           # at the DB query level (which we cannot in that case), the current |             # by the current user. By doing this exclusion at this level and not | ||||||
|           # page can have less elements than :per_page even if |             # at the DB query level (which we cannot in that case), the current | ||||||
|           # there's more than one page. |             # page can have less elements than :per_page even if | ||||||
|           notes = |             # there's more than one page. | ||||||
|             # paginate() only works with a relation. This could lead to a |             notes = | ||||||
|             # mismatch between the pagination headers info and the actual notes |               # paginate() only works with a relation. This could lead to a | ||||||
|             # array returned, but this is really a edge-case. |               # mismatch between the pagination headers info and the actual notes | ||||||
|             paginate(@noteable.notes). |               # array returned, but this is really a edge-case. | ||||||
|             reject { |n| n.cross_reference_not_visible_for?(current_user) } |               paginate(@noteable.notes). | ||||||
|           present notes, with: Entities::Note |               reject { |n| n.cross_reference_not_visible_for?(current_user) } | ||||||
|  |             present notes, with: Entities::Note | ||||||
|  |           else | ||||||
|  |             render_api_error!("Not found.", 404) | ||||||
|  |           end | ||||||
|         end |         end | ||||||
| 
 | 
 | ||||||
|         # Get a single +noteable+ note |         # Get a single +noteable+ note | ||||||
|  |  | ||||||
|  | @ -57,6 +57,15 @@ describe API::API, api: true  do | ||||||
|           expect(json_response).to be_empty |           expect(json_response).to be_empty | ||||||
|         end |         end | ||||||
| 
 | 
 | ||||||
|  |         context "and issue is confidential" do | ||||||
|  |           before { ext_issue.update_attributes(confidential: true) } | ||||||
|  | 
 | ||||||
|  |           it "returns 404" do | ||||||
|  |             get api("/projects/#{ext_proj.id}/issues/#{ext_issue.id}/notes", user) | ||||||
|  |             expect(response.status).to eq(404) | ||||||
|  |           end | ||||||
|  |         end | ||||||
|  | 
 | ||||||
|         context "and current user can view the note" do |         context "and current user can view the note" do | ||||||
|           it "should return an empty array" do |           it "should return an empty array" do | ||||||
|             get api("/projects/#{ext_proj.id}/issues/#{ext_issue.id}/notes", private_user) |             get api("/projects/#{ext_proj.id}/issues/#{ext_issue.id}/notes", private_user) | ||||||
|  | @ -80,6 +89,11 @@ describe API::API, api: true  do | ||||||
|         get api("/projects/#{project.id}/snippets/42/notes", user) |         get api("/projects/#{project.id}/snippets/42/notes", user) | ||||||
|         expect(response.status).to eq(404) |         expect(response.status).to eq(404) | ||||||
|       end |       end | ||||||
|  | 
 | ||||||
|  |       it "returns 404 when not authorized" do | ||||||
|  |         get api("/projects/#{project.id}/snippets/#{snippet.id}/notes", private_user) | ||||||
|  |         expect(response.status).to eq(404) | ||||||
|  |       end | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|     context "when noteable is a Merge Request" do |     context "when noteable is a Merge Request" do | ||||||
|  | @ -94,6 +108,11 @@ describe API::API, api: true  do | ||||||
|         get api("/projects/#{project.id}/merge_requests/4444/notes", user) |         get api("/projects/#{project.id}/merge_requests/4444/notes", user) | ||||||
|         expect(response.status).to eq(404) |         expect(response.status).to eq(404) | ||||||
|       end |       end | ||||||
|  | 
 | ||||||
|  |       it "returns 404 when not authorized" do | ||||||
|  |         get api("/projects/#{project.id}/merge_requests/4444/notes", private_user) | ||||||
|  |         expect(response.status).to eq(404) | ||||||
|  |       end | ||||||
|     end |     end | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue