diff --git a/app/controllers/concerns/notes_actions.rb b/app/controllers/concerns/notes_actions.rb
index 777b147e2dd24a3cd95a7cdae8563bef160f9152..0319948a12f26ba2ea3aa147d0dd8388392bfc76 100644
--- a/app/controllers/concerns/notes_actions.rb
+++ b/app/controllers/concerns/notes_actions.rb
@@ -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 @@ def discussion_serializer
     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
diff --git a/app/mailers/emails/notes.rb b/app/mailers/emails/notes.rb
index d3284e90568ca63a3dc75f15f20d1cd03975fbcf..1b3c1f9a8a9076990f4e64888f83e7df5eac24c8 100644
--- a/app/mailers/emails/notes.rb
+++ b/app/mailers/emails/notes.rb
@@ -26,7 +26,7 @@ def note_merge_request_email(recipient_id, note_id)
       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
diff --git a/app/models/note.rb b/app/models/note.rb
index 592efb714f327e6df544cc0fca2c35c38b2adadd..a6ae4f58ac48cb0ef9390054ec8797fc93fbd1f9 100644
--- a/app/models/note.rb
+++ b/app/models/note.rb
@@ -324,7 +324,7 @@ def contains_emoji_only?
   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?
diff --git a/app/policies/commit_policy.rb b/app/policies/commit_policy.rb
index 67e9bc12804555e001d3e81793c40335e1423ecf..4d4f0ba92674f4e2d24d398605772d2abd3fb15e 100644
--- a/app/policies/commit_policy.rb
+++ b/app/policies/commit_policy.rb
@@ -2,4 +2,6 @@
 
 class CommitPolicy < BasePolicy
   delegate { @subject.project }
+
+  rule { can?(:download_code) }.enable :read_commit
 end
diff --git a/app/policies/note_policy.rb b/app/policies/note_policy.rb
index bbc2b48b8567e24813d0305c871df7a22d96b545..f22843b6463761f4ea40e99b2e6b563ff5624cd8 100644
--- a/app/policies/note_policy.rb
+++ b/app/policies/note_policy.rb
@@ -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
diff --git a/app/views/notify/note_snippet_email.html.haml b/app/views/notify/note_project_snippet_email.html.haml
similarity index 100%
rename from app/views/notify/note_snippet_email.html.haml
rename to app/views/notify/note_project_snippet_email.html.haml
diff --git a/app/views/notify/note_snippet_email.text.erb b/app/views/notify/note_project_snippet_email.text.erb
similarity index 100%
rename from app/views/notify/note_snippet_email.text.erb
rename to app/views/notify/note_project_snippet_email.text.erb
diff --git a/changelogs/unreleased/security-guest-comments.yml b/changelogs/unreleased/security-guest-comments.yml
new file mode 100644
index 0000000000000000000000000000000000000000..2c99512433ba5d7d46517eb800381c8985fda963
--- /dev/null
+++ b/changelogs/unreleased/security-guest-comments.yml
@@ -0,0 +1,5 @@
+---
+title: Fixed ability to comment on locked/confidential issues.
+merge_request:
+author:
+type: security
diff --git a/changelogs/unreleased/security-guest-comments_2.yml b/changelogs/unreleased/security-guest-comments_2.yml
new file mode 100644
index 0000000000000000000000000000000000000000..be6f2d6a49070986464b81b8a3c15974b5d53bae
--- /dev/null
+++ b/changelogs/unreleased/security-guest-comments_2.yml
@@ -0,0 +1,5 @@
+---
+title: Fixed ability of guest users to edit/delete comments on locked or confidential issues.
+merge_request:
+author:
+type: security
diff --git a/spec/controllers/projects/notes_controller_spec.rb b/spec/controllers/projects/notes_controller_spec.rb
index 9ac7b8ee8a83f04ed00e565db50f84f422c12c43..d2a260683621c1fa611ff07dc91e4c0e065c1125 100644
--- a/spec/controllers/projects/notes_controller_spec.rb
+++ b/spec/controllers/projects/notes_controller_spec.rb
@@ -283,14 +283,14 @@
 
       def post_create(extra_params = {})
         post :create, {
-               note: { note: 'some other note' },
-               namespace_id: project.namespace,
-               project_id: project,
-               target_type: 'merge_request',
-               target_id: merge_request.id,
-               note_project_id: forked_project.id,
-               in_reply_to_discussion_id: existing_comment.discussion_id
-             }.merge(extra_params)
+          note: { note: 'some other note', noteable_id: merge_request.id },
+          namespace_id: project.namespace,
+          project_id: project,
+          target_type: 'merge_request',
+          target_id: merge_request.id,
+          note_project_id: forked_project.id,
+          in_reply_to_discussion_id: existing_comment.discussion_id
+        }.merge(extra_params)
       end
 
       context 'when the note_project_id is not correct' do
@@ -324,6 +324,30 @@ def post_create(extra_params = {})
       end
     end
 
+    context 'when target_id and noteable_id do not match' do
+      let(:locked_issue) { create(:issue, :locked, project: project) }
+      let(:issue) {create(:issue, project: project)}
+
+      before do
+        project.update_attribute(:visibility_level, Gitlab::VisibilityLevel::PUBLIC)
+        project.project_member(user).destroy
+      end
+
+      it 'uses target_id and ignores noteable_id' do
+        request_params = {
+          note: { note: 'some note', noteable_type: 'Issue', noteable_id: locked_issue.id },
+          target_type: 'issue',
+          target_id: issue.id,
+          project_id: project,
+          namespace_id: project.namespace
+        }
+
+        expect { post :create, request_params }.to change { issue.notes.count }.by(1)
+          .and change { locked_issue.notes.count }.by(0)
+        expect(response).to have_gitlab_http_status(302)
+      end
+    end
+
     context 'when the merge request discussion is locked' do
       before do
         project.update_attribute(:visibility_level, Gitlab::VisibilityLevel::PUBLIC)
@@ -376,35 +400,60 @@ def post_create(extra_params = {})
   end
 
   describe 'PUT update' do
-    let(:request_params) do
-      {
-        namespace_id: project.namespace,
-        project_id: project,
-        id: note,
-        format: :json,
-        note: {
-          note: "New comment"
+    context "should update the note with a valid issue" do
+      let(:request_params) do
+        {
+          namespace_id: project.namespace,
+          project_id: project,
+          id: note,
+          format: :json,
+          note: {
+            note: "New comment"
+          }
         }
-      }
-    end
+      end
 
-    before do
-      sign_in(note.author)
-      project.add_developer(note.author)
+      before do
+        sign_in(note.author)
+        project.add_developer(note.author)
+      end
+
+      it "updates the note" do
+        expect { put :update, request_params }.to change { note.reload.note }
+      end
     end
+    context "doesnt update the note" do
+      let(:issue)   { create(:issue, :confidential, project: project) }
+      let(:note)    { create(:note, noteable: issue, project: project) }
 
-    it "updates the note" do
-      expect { put :update, request_params }.to change { note.reload.note }
+      before do
+        sign_in(user)
+        project.add_guest(user)
+      end
+
+      it "disallows edits when the issue is confidential and the user has guest permissions" do
+        request_params = {
+          namespace_id: project.namespace,
+          project_id: project,
+          id: note,
+          format: :json,
+          note: {
+            note: "New comment"
+          }
+        }
+        expect { put :update, request_params }.not_to change { note.reload.note }
+        expect(response).to have_gitlab_http_status(404)
+      end
     end
   end
 
   describe 'DELETE destroy' do
     let(:request_params) do
       {
-        namespace_id: project.namespace,
-        project_id: project,
-        id: note,
-        format: :js
+          namespace_id: project.namespace,
+          project_id: project,
+          id: note,
+          format: :js
       }
     end
 
diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb
index ff1a5aa2536ce90387c5bc8b9354f941c3fde7e5..150c00e4bfe809d4b487d0d812bb5c4a20024475 100644
--- a/spec/mailers/notify_spec.rb
+++ b/spec/mailers/notify_spec.rb
@@ -522,7 +522,7 @@ def have_referable_subject(referable, reply: false)
       let(:project_snippet) { create(:project_snippet, project: project) }
       let(:project_snippet_note) { create(:note_on_project_snippet, project: project, noteable: project_snippet) }
 
-      subject { described_class.note_snippet_email(project_snippet_note.author_id, project_snippet_note.id) }
+      subject { described_class.note_project_snippet_email(project_snippet_note.author_id, project_snippet_note.id) }
 
       it_behaves_like 'an answer to an existing thread with reply-by-email enabled' do
         let(:model) { project_snippet }
diff --git a/spec/models/note_spec.rb b/spec/models/note_spec.rb
index f9be61e4768907c1fe13afef1eb422e1a3ee3593..bcdfe3cf1eb14e50e1257f1573b6b3e8cb06b3b5 100644
--- a/spec/models/note_spec.rb
+++ b/spec/models/note_spec.rb
@@ -517,7 +517,7 @@ def retrieve_participants
 
   describe '#to_ability_name' do
     it 'returns snippet for a project snippet note' do
-      expect(build(:note_on_project_snippet).to_ability_name).to eq('snippet')
+      expect(build(:note_on_project_snippet).to_ability_name).to eq('project_snippet')
     end
 
     it 'returns personal_snippet for a personal snippet note' do
diff --git a/spec/policies/note_policy_spec.rb b/spec/policies/note_policy_spec.rb
index e8096358f7df2e21e60dcde7db5a755e048db051..7e25c53e77c30ca35d141afbd54460639124c2cc 100644
--- a/spec/policies/note_policy_spec.rb
+++ b/spec/policies/note_policy_spec.rb
@@ -10,11 +10,50 @@ def policies(noteable = nil)
       return @policies if @policies
 
       noteable ||= issue
-      note = create(:note, noteable: noteable, author: user, project: project)
+      note = if noteable.is_a?(Commit)
+               create(:note_on_commit, commit_id: noteable.id, author: user, project: project)
+             else
+               create(:note, noteable: noteable, author: user, project: project)
+             end
 
       @policies = described_class.new(user, note)
     end
 
+    shared_examples_for 'a discussion with a private noteable' do
+      let(:noteable) { issue }
+      let(:policy) { policies(noteable) }
+
+      context 'when the note author can no longer see the noteable' do
+        it 'can not edit nor read the note' do
+          expect(policy).to be_disallowed(:admin_note)
+          expect(policy).to be_disallowed(:resolve_note)
+          expect(policy).to be_disallowed(:read_note)
+        end
+      end
+
+      context 'when the note author can still see the noteable' do
+        before do
+          project.add_developer(user)
+        end
+
+        it 'can edit the note' do
+          expect(policy).to be_allowed(:admin_note)
+          expect(policy).to be_allowed(:resolve_note)
+          expect(policy).to be_allowed(:read_note)
+        end
+      end
+    end
+
+    context 'when the project is private' do
+      let(:project) { create(:project, :private, :repository) }
+
+      context 'when the noteable is a commit' do
+        it_behaves_like 'a discussion with a private noteable' do
+          let(:noteable) { project.repository.head_commit }
+        end
+      end
+    end
+
     context 'when the project is public' do
       context 'when the note author is not a project member' do
         it 'can edit a note' do
@@ -24,14 +63,48 @@ def policies(noteable = nil)
         end
       end
 
-      context 'when the noteable is a snippet' do
+      context 'when the noteable is a project snippet' do
+        it 'can edit note' do
+          policies = policies(create(:project_snippet, :public, project: project))
+
+          expect(policies).to be_allowed(:admin_note)
+          expect(policies).to be_allowed(:resolve_note)
+          expect(policies).to be_allowed(:read_note)
+        end
+
+        context 'when it is private' do
+          it_behaves_like 'a discussion with a private noteable' do
+            let(:noteable) { create(:project_snippet, :private, project: project) }
+          end
+        end
+      end
+
+      context 'when the noteable is a personal snippet' do
         it 'can edit note' do
-          policies = policies(create(:project_snippet, project: project))
+          policies = policies(create(:personal_snippet, :public))
 
           expect(policies).to be_allowed(:admin_note)
           expect(policies).to be_allowed(:resolve_note)
           expect(policies).to be_allowed(:read_note)
         end
+
+        context 'when it is private' do
+          it 'can not edit nor read the note' do
+            policies = policies(create(:personal_snippet, :private))
+
+            expect(policies).to be_disallowed(:admin_note)
+            expect(policies).to be_disallowed(:resolve_note)
+            expect(policies).to be_disallowed(:read_note)
+          end
+        end
+      end
+
+      context 'when a discussion is confidential' do
+        before do
+          issue.update_attribute(:confidential, true)
+        end
+
+        it_behaves_like 'a discussion with a private noteable'
       end
 
       context 'when a discussion is locked' do