diff --git a/CHANGELOG.md b/CHANGELOG.md
index 07894799eed99888afd77e2430ddfeddba7b5352..e8f447a17cf324fe6cd7b7a6211283cb2fe05e37 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,21 @@
 documentation](doc/development/changelog.md) for instructions on adding your own
 entry.
 
+## 17.7.1 (2025-01-08)
+
+### Fixed (3 changes)
+
+- [Update acme-client to v2.0.19](https://gitlab.com/gitlab-org/security/gitlab/-/commit/17fb399704080e909a7ffd188dad4a0367a41cf6)
+- [Add a migration to regenerate CI job token signing key](https://gitlab.com/gitlab-org/security/gitlab/-/commit/1a69b334e54530b37ca623b8e0d5da6ed2961fb1)
+- [Fix CI job token signing key not always generated](https://gitlab.com/gitlab-org/security/gitlab/-/commit/6fd10c2117db61d7c894944462c29425038301c0)
+
+### Security (4 changes)
+
+- [Prevent cyclic reference in work item hierarchy widget from frontend](https://gitlab.com/gitlab-org/security/gitlab/-/commit/a545d1f48720d3d9908880d2a77ff0e76bcaa9a5) ([merge request](https://gitlab.com/gitlab-org/security/gitlab/-/merge_requests/4684))
+- [Allow external_provider config take precedence over external_groups](https://gitlab.com/gitlab-org/security/gitlab/-/commit/c74b00da4e644dc5628f805587b2ec492e8bd044) ([merge request](https://gitlab.com/gitlab-org/security/gitlab/-/merge_requests/4680))
+- [Filter out sensitive parameters on Auth logs](https://gitlab.com/gitlab-org/security/gitlab/-/commit/e6f661b6f3ec52e0e9d37b89d171e5b949346804) ([merge request](https://gitlab.com/gitlab-org/security/gitlab/-/merge_requests/4686))
+- [Don't allow unauthorized users to close issues automatically](https://gitlab.com/gitlab-org/security/gitlab/-/commit/b1ef837d0b61f997dacd3b299da17c96616ba275) ([merge request](https://gitlab.com/gitlab-org/security/gitlab/-/merge_requests/4674))
+
 ## 17.7.0 (2024-12-18)
 
 ### Added (178 changes)
diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION
index 50dcdf6c3d7b969f8367e1ff7e27b836eefffc2e..087aa4f2f1cd3f059d973f837519e6977cafbba5 100644
--- a/GITALY_SERVER_VERSION
+++ b/GITALY_SERVER_VERSION
@@ -1 +1 @@
-17.7.0
\ No newline at end of file
+17.7.1
\ No newline at end of file
diff --git a/GITLAB_KAS_VERSION b/GITLAB_KAS_VERSION
index 50dcdf6c3d7b969f8367e1ff7e27b836eefffc2e..087aa4f2f1cd3f059d973f837519e6977cafbba5 100644
--- a/GITLAB_KAS_VERSION
+++ b/GITLAB_KAS_VERSION
@@ -1 +1 @@
-17.7.0
\ No newline at end of file
+17.7.1
\ No newline at end of file
diff --git a/GITLAB_PAGES_VERSION b/GITLAB_PAGES_VERSION
index 50dcdf6c3d7b969f8367e1ff7e27b836eefffc2e..087aa4f2f1cd3f059d973f837519e6977cafbba5 100644
--- a/GITLAB_PAGES_VERSION
+++ b/GITLAB_PAGES_VERSION
@@ -1 +1 @@
-17.7.0
\ No newline at end of file
+17.7.1
\ No newline at end of file
diff --git a/Gemfile b/Gemfile
index 6220b2ece5cde91af95c05089e3b736a58964a44..4be9aed49bdeb3bb7b867ab7f77d176b3e2a7611 100644
--- a/Gemfile
+++ b/Gemfile
@@ -130,10 +130,10 @@ gem 'rqrcode', '~> 2.2', feature_category: :system_access
 gem 'attr_encrypted', '~> 3.2.4', path: 'vendor/gems/attr_encrypted' # rubocop:todo Gemfile/MissingFeatureCategory
 
 # GitLab Pages
-gem 'validates_hostname', '~> 1.0.13' # rubocop:todo Gemfile/MissingFeatureCategory
-gem 'rubyzip', '~> 2.3.2', require: 'zip' # rubocop:todo Gemfile/MissingFeatureCategory
+gem 'validates_hostname', '~> 1.0.13', feature_category: :pages
+gem 'rubyzip', '~> 2.3.2', require: 'zip', feature_category: :pages
 # GitLab Pages letsencrypt support
-gem 'acme-client', '~> 2.0.18' # rubocop:todo Gemfile/MissingFeatureCategory
+gem 'acme-client', '~> 2.0.19', feature_category: :pages
 
 # Browser detection
 gem 'browser', '~> 5.3.1' # rubocop:todo Gemfile/MissingFeatureCategory
diff --git a/Gemfile.checksum b/Gemfile.checksum
index 385500c1a5e94e5837cfb765efd6a0d0952649bf..b53647db0e91a2371c787d2f9da63bcca820c6d8 100644
--- a/Gemfile.checksum
+++ b/Gemfile.checksum
@@ -1,7 +1,7 @@
 [
 {"name":"CFPropertyList","version":"3.0.5","platform":"ruby","checksum":"a78551cd4768d78ebca98488c27e33652ef818be64697a54676d34e6434674a4"},
 {"name":"RedCloth","version":"4.3.4","platform":"ruby","checksum":"5231b2fdd91a933915cba330e5fd1a74025e77b56f57b7404c7191ebf2812297"},
-{"name":"acme-client","version":"2.0.18","platform":"ruby","checksum":"3feab341926ffc16eb65babe51ba4dad8180c13e21e774871344e0b3502ef275"},
+{"name":"acme-client","version":"2.0.19","platform":"ruby","checksum":"29647ab04cde309503cf553d3f9d0a25dcca6588caca57f55f12d6c8833244f8"},
 {"name":"actioncable","version":"7.0.8.6","platform":"ruby","checksum":"f48b0ff8414dfbb2b38e639bf49e86677dfd1b16580ce9e0a3ffa1bb4c0a4057"},
 {"name":"actionmailbox","version":"7.0.8.6","platform":"ruby","checksum":"8f9deed46e38fc97d86d34e9552ffd5aae81491b139f8df91ef2616ab5628857"},
 {"name":"actionmailer","version":"7.0.8.6","platform":"ruby","checksum":"e3f058a5e378e72b8a90577aaa325f23e1d718b437fe9430411eb0324ba8f71d"},
diff --git a/Gemfile.lock b/Gemfile.lock
index 1f664bb6a70bd6d8a335bf0161fb091441b3aa67..9991f04b2e0536c519156d21aca4d2236fdb5f46 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -229,7 +229,8 @@ GEM
     CFPropertyList (3.0.5)
       rexml
     RedCloth (4.3.4)
-    acme-client (2.0.18)
+    acme-client (2.0.19)
+      base64 (~> 0.2.0)
       faraday (>= 1.0, < 3.0.0)
       faraday-retry (>= 1.0, < 3.0.0)
     actioncable (7.0.8.6)
@@ -1970,7 +1971,7 @@ PLATFORMS
 DEPENDENCIES
   CFPropertyList (~> 3.0.0)
   RedCloth (~> 4.3.3)
-  acme-client (~> 2.0.18)
+  acme-client (~> 2.0.19)
   activerecord-explain-analyze (~> 0.1)
   activerecord-gitlab!
   addressable (~> 2.8)
diff --git a/Gemfile.next.checksum b/Gemfile.next.checksum
index a8f7e550bd0a8dfc5dd5930d8960e77e545c1b56..61ad7757317e1300d5ef80ccdbdaf4e43094811c 100644
--- a/Gemfile.next.checksum
+++ b/Gemfile.next.checksum
@@ -1,7 +1,7 @@
 [
 {"name":"CFPropertyList","version":"3.0.5","platform":"ruby","checksum":"a78551cd4768d78ebca98488c27e33652ef818be64697a54676d34e6434674a4"},
 {"name":"RedCloth","version":"4.3.4","platform":"ruby","checksum":"5231b2fdd91a933915cba330e5fd1a74025e77b56f57b7404c7191ebf2812297"},
-{"name":"acme-client","version":"2.0.18","platform":"ruby","checksum":"3feab341926ffc16eb65babe51ba4dad8180c13e21e774871344e0b3502ef275"},
+{"name":"acme-client","version":"2.0.19","platform":"ruby","checksum":"29647ab04cde309503cf553d3f9d0a25dcca6588caca57f55f12d6c8833244f8"},
 {"name":"actioncable","version":"7.1.4.2","platform":"ruby","checksum":"4235bd313a88a7ac594505247d3563603d4baaf080f5877c8bdf0af2f28354a3"},
 {"name":"actionmailbox","version":"7.1.4.2","platform":"ruby","checksum":"8e5c3ea53177588a63cb333602ac0bbdd0f9e380bece15ff889e22aa1da960dd"},
 {"name":"actionmailer","version":"7.1.4.2","platform":"ruby","checksum":"f5fe514a35034b7ccf34010bfe5f558dc3e22c7936be9b9b660f14dfd00f3c9e"},
@@ -344,7 +344,7 @@
 {"name":"io-event","version":"1.6.5","platform":"ruby","checksum":"5da4c044ac5f411563da1a4743d28c8d30d7802e29370db42139a52b807b4ce2"},
 {"name":"ipaddr","version":"1.2.5","platform":"ruby","checksum":"4e679c71d6d8ed99f925487082f70f9a958de155591caa0e7f6cef9aa160f17a"},
 {"name":"ipaddress","version":"0.8.3","platform":"ruby","checksum":"85640c4f9194c26937afc8c78e3074a8e7c97d5d1210358d1440f01034d006f5"},
-{"name":"irb","version":"1.14.2","platform":"ruby","checksum":"243040f6419115beb2404380a62e41861ddb03c0792c5873dec2a69a542723c6"},
+{"name":"irb","version":"1.14.3","platform":"ruby","checksum":"c457f1f2f1438ae9ce5c5be3981ae2138dec7fb894c7d73777eeeb0a6c0d0752"},
 {"name":"jaeger-client","version":"1.1.0","platform":"ruby","checksum":"cb5e9b9bbee6ee8d6a82d03d947a5b04543d8c0a949c22e484254f18d8a458a8"},
 {"name":"jaro_winkler","version":"1.5.6","platform":"java","checksum":"3262aea433861fec3179184e9adc1933cca8bc15665957a143b56816f1a22f74"},
 {"name":"jaro_winkler","version":"1.5.6","platform":"ruby","checksum":"007db7805527ada1cc12f2547676181d63b0a504ec4dd7a9a2eb2424521ccd81"},
@@ -533,8 +533,8 @@
 {"name":"pry-byebug","version":"3.10.1","platform":"ruby","checksum":"c8f975c32255bfdb29e151f5532130be64ff3d0042dc858d0907e849125581f8"},
 {"name":"pry-rails","version":"0.3.9","platform":"ruby","checksum":"468662575abb6b67f4a9831219f99290d5eae7bf186e64dd810d0a3e4a8cc4b1"},
 {"name":"pry-shell","version":"0.6.4","platform":"ruby","checksum":"ad024882d29912b071a7de65ebea538b242d2dc1498c60c7c2352ef94769f208"},
-{"name":"psych","version":"5.2.1","platform":"java","checksum":"ce33486096ad9518a479e7d95d715205266ea64655ae85ce01bec01035016128"},
-{"name":"psych","version":"5.2.1","platform":"ruby","checksum":"f6c8441d21e9df3f9790586eaf80ea472d7c98d1e89c92e68e1b6a85fb6eab66"},
+{"name":"psych","version":"5.2.2","platform":"java","checksum":"dae010e0bbc01e03da5c42cf538705759fb592f42eb9e06baf9bce932c2d7c4e"},
+{"name":"psych","version":"5.2.2","platform":"ruby","checksum":"a4a9477c85d3e858086c38cf64e7096abe40d1b1eed248b01020dec0ff9906ab"},
 {"name":"public_suffix","version":"6.0.1","platform":"ruby","checksum":"61d44e1cab5cbbbe5b31068481cf16976dd0dc1b6b07bd95617ef8c5e3e00c6f"},
 {"name":"puma","version":"6.5.0","platform":"java","checksum":"a58eea585d291aa33796add9884208bc1591da5d8e61886f8ac74d080b298c40"},
 {"name":"puma","version":"6.5.0","platform":"ruby","checksum":"94d1b75cab7f356d52e4f1b17b9040a090889b341dbeee6ee3703f441dc189f2"},
@@ -567,7 +567,7 @@
 {"name":"rbs","version":"3.6.1","platform":"ruby","checksum":"ed7273d018556844583d1785ac54194e67eec594d68e317d57fa90ad035532c0"},
 {"name":"rbtrace","version":"0.5.1","platform":"ruby","checksum":"e8cba64d462bfb8ba102d7be2ecaacc789247d52ac587d8003549d909cb9c5dc"},
 {"name":"rchardet","version":"1.8.0","platform":"ruby","checksum":"693acd5253d5ade81a51940697955f6dd4bb2f0d245bda76a8e23deec70a52c7"},
-{"name":"rdoc","version":"6.8.1","platform":"ruby","checksum":"0128002d1bfc4892bdd780940841e4ca41275f63781fd832d11bc8ba4461462c"},
+{"name":"rdoc","version":"6.10.0","platform":"ruby","checksum":"db665021883ac9df3ba29cdf71aece960749888db1bf9615b4a584cfa3fa3eda"},
 {"name":"re2","version":"2.7.0","platform":"aarch64-linux","checksum":"778921298b6e8aba26a6230dd298c9b361b92e45024f81fa6aee788060fa307c"},
 {"name":"re2","version":"2.7.0","platform":"arm-linux","checksum":"d328b5286d83ae265e13b855da8e348a976f80f91b748045b52073a570577954"},
 {"name":"re2","version":"2.7.0","platform":"arm64-darwin","checksum":"7d993f27a1afac4001c539a829e2af211ced62604930c90df32a307cf74cb4a4"},
@@ -592,7 +592,7 @@
 {"name":"regexp_parser","version":"2.6.0","platform":"ruby","checksum":"f163ba463a45ca2f2730e0902f2475bb0eefcd536dfc2f900a86d1e5a7d7a556"},
 {"name":"regexp_property_values","version":"1.0.0","platform":"java","checksum":"5e26782b01241616855c4ee7bb8a62fce9387e484f2d3eaf04f2a0633708222e"},
 {"name":"regexp_property_values","version":"1.0.0","platform":"ruby","checksum":"162499dc0bba1e66d334273a059f207a61981cc8cc69d2ca743594e7886d080f"},
-{"name":"reline","version":"0.5.12","platform":"ruby","checksum":"41ab36d3fd2aaa169e99f8b82a93b9585f51130529360e24388fcccc20a055a2"},
+{"name":"reline","version":"0.6.0","platform":"ruby","checksum":"57620375dcbe56ec09bac7192bfb7460c716bbf0054dc94345ecaa5438e539d2"},
 {"name":"representable","version":"3.2.0","platform":"ruby","checksum":"cc29bf7eebc31653586849371a43ffe36c60b54b0a6365b5f7d95ec34d1ebace"},
 {"name":"request_store","version":"1.5.1","platform":"ruby","checksum":"07a204d161590789f2b1d27f9f0eadcdecd6d868cb2f03240250e1bc747df78e"},
 {"name":"responders","version":"3.0.1","platform":"ruby","checksum":"613fe28e498987f4feaa3230aa6313ca4bd5f0563a3da83511b0dd6cd8f47292"},
@@ -737,7 +737,7 @@
 {"name":"thread_safe","version":"0.3.6","platform":"ruby","checksum":"9ed7072821b51c57e8d6b7011a8e282e25aeea3a4065eab326e43f66f063b05a"},
 {"name":"thrift","version":"0.16.0","platform":"ruby","checksum":"d023286ea89e30444c9f1c28dd76107f87d8aaf85fe1742da1d8cd3b5417dcce"},
 {"name":"tilt","version":"2.0.11","platform":"ruby","checksum":"7b180fc472cbdeb186c85d31c0f2d1e61a2c0d77e1d9fd0ca28482a9d972d6a0"},
-{"name":"timeout","version":"0.4.2","platform":"ruby","checksum":"8aca2d5ff98eb2f7a501c03f8c3622065932cc58bc58f725cd50a09e63b4cc19"},
+{"name":"timeout","version":"0.4.3","platform":"ruby","checksum":"9509f079b2b55fe4236d79633bd75e34c1c1e7e3fb4b56cb5fda61f80a0fe30e"},
 {"name":"timfel-krb5-auth","version":"0.8.3","platform":"ruby","checksum":"ab388c9d747fa3cd95baf2cc1c03253e372d8c680adcc543670f4f099854bb80"},
 {"name":"tins","version":"1.31.1","platform":"ruby","checksum":"51c4a347c25c630d310cbc2c040ffb84e266c8227f2ade881f1130ee4f9fbecf"},
 {"name":"toml-rb","version":"2.2.0","platform":"ruby","checksum":"a1e2c54ac3cc9d49861004f75f0648b3622ac03a76abe105358c31553227d9a6"},
diff --git a/Gemfile.next.lock b/Gemfile.next.lock
index 6df4dab08d318d2368ba19a059be3551c8d8f6ea..04c40b8e7283a2e9a4b963cff3396b8d048b64d2 100644
--- a/Gemfile.next.lock
+++ b/Gemfile.next.lock
@@ -229,7 +229,8 @@ GEM
     CFPropertyList (3.0.5)
       rexml
     RedCloth (4.3.4)
-    acme-client (2.0.18)
+    acme-client (2.0.19)
+      base64 (~> 0.2.0)
       faraday (>= 1.0, < 3.0.0)
       faraday-retry (>= 1.0, < 3.0.0)
     actioncable (7.1.4.2)
@@ -1032,7 +1033,7 @@ GEM
     io-event (1.6.5)
     ipaddr (1.2.5)
     ipaddress (0.8.3)
-    irb (1.14.2)
+    irb (1.14.3)
       rdoc (>= 4.0.0)
       reline (>= 0.4.2)
     jaeger-client (1.1.0)
@@ -1473,7 +1474,7 @@ GEM
       pry (>= 0.13.0)
       tty-markdown
       tty-prompt
-    psych (5.2.1)
+    psych (5.2.2)
       date
       stringio
     public_suffix (6.0.1)
@@ -1557,7 +1558,7 @@ GEM
       msgpack (>= 0.4.3)
       optimist (>= 3.0.0)
     rchardet (1.8.0)
-    rdoc (6.8.1)
+    rdoc (6.10.0)
       psych (>= 4.0.0)
     re2 (2.7.0)
       mini_portile2 (~> 2.8.5)
@@ -1587,7 +1588,7 @@ GEM
       redis (>= 4, < 6)
     regexp_parser (2.6.0)
     regexp_property_values (1.0.0)
-    reline (0.5.12)
+    reline (0.6.0)
       io-console (~> 0.5)
     representable (3.2.0)
       declarative (< 0.1.0)
@@ -1861,7 +1862,7 @@ GEM
     thread_safe (0.3.6)
     thrift (0.16.0)
     tilt (2.0.11)
-    timeout (0.4.2)
+    timeout (0.4.3)
     timfel-krb5-auth (0.8.3)
     tins (1.31.1)
       sync
@@ -1998,7 +1999,7 @@ PLATFORMS
 DEPENDENCIES
   CFPropertyList (~> 3.0.0)
   RedCloth (~> 4.3.3)
-  acme-client (~> 2.0.18)
+  acme-client (~> 2.0.19)
   activerecord-explain-analyze (~> 0.1)
   activerecord-gitlab!
   addressable (~> 2.8)
diff --git a/app/assets/javascripts/work_items/components/work_item_links/work_item_children_wrapper.vue b/app/assets/javascripts/work_items/components/work_item_links/work_item_children_wrapper.vue
index 847476a71563427f9f10bf45a786274045e8936c..23f466ee77297b29dff0e44de581a4dbc93d98f1 100644
--- a/app/assets/javascripts/work_items/components/work_item_links/work_item_children_wrapper.vue
+++ b/app/assets/javascripts/work_items/components/work_item_links/work_item_children_wrapper.vue
@@ -100,6 +100,11 @@ export default {
       required: false,
       default: null,
     },
+    parentId: {
+      type: String,
+      required: false,
+      default: null,
+    },
   },
   data() {
     return {
@@ -546,6 +551,7 @@ export default {
       :is-top-level="isTopLevel"
       :data-child-title="child.title"
       :data-child-type="child.workItemType.name"
+      :parent-id="parentId"
       class="!gl-border-x-0 !gl-border-b-1 !gl-border-t-0 !gl-border-solid !gl-pb-2 last:!gl-border-b-0 last:!gl-pb-0"
       @drag="$emit('drag', $event)"
       @drop="$emit('drop')"
diff --git a/app/assets/javascripts/work_items/components/work_item_links/work_item_link_child.vue b/app/assets/javascripts/work_items/components/work_item_links/work_item_link_child.vue
index 5fb755ac89f895f52e87e3dbefad8b7dc32d3482..26103d7948a2bab8b68aba4fd76ce49a6429201b 100644
--- a/app/assets/javascripts/work_items/components/work_item_links/work_item_link_child.vue
+++ b/app/assets/javascripts/work_items/components/work_item_links/work_item_link_child.vue
@@ -79,6 +79,11 @@ export default {
       required: false,
       default: () => {},
     },
+    parentId: {
+      type: String,
+      required: false,
+      default: '',
+    },
   },
   data() {
     return {
@@ -150,6 +155,12 @@ export default {
       );
     },
     shouldExpandChildren() {
+      // In case the parent is the same as the child,
+      // it is creating a cycle and recursively expanding the tree
+      // Issue details: https://gitlab.com/gitlab-org/gitlab/-/issues/498106
+      if (this.parentId === this.childItem.id) {
+        return false;
+      }
       const rolledUpCountsByType =
         findHierarchyWidgets(this.childItem.widgets)?.rolledUpCountsByType || [];
       const nrOpenChildren = rolledUpCountsByType
@@ -310,6 +321,7 @@ export default {
         :has-indirect-children="hasIndirectChildren"
         :dragged-item-type="draggedItemType"
         :allowed-children-by-type="allowedChildrenByType"
+        :parent-id="parentId"
         @drag="$emit('drag', $event)"
         @drop="$emit('drop')"
         @removeChild="$emit('removeChild', childItem)"
diff --git a/app/assets/javascripts/work_items/components/work_item_links/work_item_tree.vue b/app/assets/javascripts/work_items/components/work_item_links/work_item_tree.vue
index 7b7e190504a6f97ed1933a10c50af923c52fd3e5..cab33edc8092ef3bbb422d6a6e6c1c0bab0e7157 100644
--- a/app/assets/javascripts/work_items/components/work_item_links/work_item_tree.vue
+++ b/app/assets/javascripts/work_items/components/work_item_links/work_item_tree.vue
@@ -451,6 +451,7 @@ export default {
           :has-indirect-children="hasIndirectChildren"
           :allowed-children-by-type="allowedChildrenByType"
           :dragged-item-type="draggedItemType"
+          :parent-id="workItemId"
           @drag="draggedItemType = $event"
           @drop="draggedItemType = null"
           @error="error = $event"
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index 2f36f5a6b4edef7bd96a4362b8c9735eb08563a9..75433488ba36a560d02abcb238449d85e0b138f1 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -100,7 +100,7 @@ def self.endpoint_id_for_action(action_name)
       env: :blocklist,
       remote_ip: request.ip,
       request_method: request.request_method,
-      path: request.fullpath
+      path: request.filtered_path
     )
 
     render plain: e.message, status: :forbidden
diff --git a/app/controllers/concerns/invisible_captcha_on_signup.rb b/app/controllers/concerns/invisible_captcha_on_signup.rb
index a704ff251b3d29b487cf4755c75ec6a0f161543f..67c09e7b17bfb4bdfd1ab09b70a754e02b33bd77 100644
--- a/app/controllers/concerns/invisible_captcha_on_signup.rb
+++ b/app/controllers/concerns/invisible_captcha_on_signup.rb
@@ -45,7 +45,7 @@ def log_request(message)
       env: :invisible_captcha_signup_bot_detected,
       remote_ip: request.ip,
       request_method: request.request_method,
-      path: request.fullpath
+      path: request.filtered_path
     }
 
     Gitlab::AuthLogger.error(request_information)
diff --git a/app/models/group.rb b/app/models/group.rb
index d38971c83f34c731c6fa5fe05bffa9c1db665b91..5cc5e0ab6be6db7f7405ca3629febde5c89699fa 100644
--- a/app/models/group.rb
+++ b/app/models/group.rb
@@ -502,12 +502,6 @@ def human_name
     full_name
   end
 
-  def to_human_reference(from = nil)
-    return unless cross_namespace_reference?(from)
-
-    human_name
-  end
-
   def visibility_level_allowed_by_parent?(level = self.visibility_level)
     return true unless parent_id && parent_id.nonzero?
 
diff --git a/app/workers/issues/close_worker.rb b/app/workers/issues/close_worker.rb
index 343f50cd7cfe59695f010ca2964b792a611ec5d0..caf600fe8456cd925d5d614c34389457a9647957 100644
--- a/app/workers/issues/close_worker.rb
+++ b/app/workers/issues/close_worker.rb
@@ -37,7 +37,18 @@ def perform(project_id, issue_id, issue_type, params = {})
       author = User.find_by_id(params["closed_by"])
 
       unless author
-        logger.info(structured_payload(message: "User not found.", user_id: params["closed_by"]))
+        logger.info(structured_payload(message: "Author not found.", user_id: params["closed_by"]))
+        return
+      end
+
+      user = User.find_by_id(params["user_id"])
+
+      # Only authorizing if user is present for backwards compatibility.
+      # TODO: Remove with https://gitlab.com/gitlab-org/gitlab/-/work_items/509422
+      if user && !issue.is_a?(ExternalIssue) && !user.can?(:update_issue, issue)
+        logger.info(
+          structured_payload(message: "User cannot update issue.", user_id: params["user_id"], issue_id: issue_id)
+        )
         return
       end
 
diff --git a/app/workers/process_commit_worker.rb b/app/workers/process_commit_worker.rb
index 25392cb27a1023ddc0fd3912bdea90814034a8d5..0616cd4d476b22f08bbfd56b7ec2d83bed94c372 100644
--- a/app/workers/process_commit_worker.rb
+++ b/app/workers/process_commit_worker.rb
@@ -59,7 +59,12 @@ def close_issues(project, user, author, commit, issues)
     Issues::CloseWorker.bulk_perform_async_with_contexts(
       issues,
       arguments_proc: ->(issue) {
-        [project.id, issue.id, issue.class.to_s, { closed_by: author.id, commit_hash: commit.to_hash }]
+        [
+          project.id,
+          issue.id,
+          issue.class.to_s,
+          { closed_by: author.id, user_id: user.id, commit_hash: commit.to_hash }
+        ]
       },
       context_proc: ->(issue) { { project: project } }
     )
diff --git a/config/application.rb b/config/application.rb
index c3412f86480c9afc5832b69a084588773b18c9a5..476f0055d43bdfebdfc1b9bf0371bbed09a8585f 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -214,7 +214,7 @@ class Application < Rails::Application
     #       vulnerability:
     #       https://gitlab.com/gitlab-org/labkit/blob/master/mask/matchers.go
     config.filter_parameters += [
-      /token$/,
+      /token$/i,
       /password/,
       /secret/,
       /key$/,
diff --git a/db/migrate/20241017160504_generate_ci_job_token_signing_key.rb b/db/migrate/20241017160504_generate_ci_job_token_signing_key.rb
index 043f6414a6285c33c508166c626cae7e710e6435..5eb372af695b21a8d95cc6650c69cdf78f054774 100644
--- a/db/migrate/20241017160504_generate_ci_job_token_signing_key.rb
+++ b/db/migrate/20241017160504_generate_ci_job_token_signing_key.rb
@@ -15,12 +15,14 @@ class ApplicationSetting < MigrationRecord
   end
 
   def up
+    ApplicationSetting.reset_column_information
     ApplicationSetting.find_each do |application_setting|
       application_setting.update(ci_job_token_signing_key: OpenSSL::PKey::RSA.new(2048).to_pem)
     end
   end
 
   def down
+    ApplicationSetting.reset_column_information
     ApplicationSetting.find_each do |application_setting|
       application_setting.update_columns(
         encrypted_ci_job_token_signing_key: nil,
diff --git a/db/migrate/20241017160505_regenerate_ci_job_token_signing_key.rb b/db/migrate/20241017160505_regenerate_ci_job_token_signing_key.rb
new file mode 100644
index 0000000000000000000000000000000000000000..a0151b6be4bd5d0a5856522caee67a5bd84dc094
--- /dev/null
+++ b/db/migrate/20241017160505_regenerate_ci_job_token_signing_key.rb
@@ -0,0 +1,38 @@
+# frozen_string_literal: true
+
+# Due to a schema cache problem reported in https://gitlab.com/gitlab-org/gitlab-runner/-/issues/38397,
+# the previous migration did not always populate the application_settings table with a CI job token
+# signing key. Redo the migration if there is no key.
+class RegenerateCiJobTokenSigningKey < Gitlab::Database::Migration[2.2]
+  milestone '17.7'
+  restrict_gitlab_migration gitlab_schema: :gitlab_main
+
+  class ApplicationSetting < MigrationRecord
+    attr_encrypted :ci_job_token_signing_key, {
+      mode: :per_attribute_iv,
+      key: Settings.attr_encrypted_db_key_base_32,
+      algorithm: 'aes-256-gcm',
+      encode: false,
+      encode_iv: false
+    }
+  end
+
+  def up
+    ApplicationSetting.reset_column_information
+    ApplicationSetting.find_each do |application_setting|
+      next if application_setting.ci_job_token_signing_key.present?
+
+      application_setting.update(ci_job_token_signing_key: OpenSSL::PKey::RSA.new(2048).to_pem)
+    end
+  end
+
+  def down
+    ApplicationSetting.reset_column_information
+    ApplicationSetting.find_each do |application_setting|
+      application_setting.update_columns(
+        encrypted_ci_job_token_signing_key: nil,
+        encrypted_ci_job_token_signing_key_iv: nil
+      )
+    end
+  end
+end
diff --git a/db/schema_migrations/20241017160505 b/db/schema_migrations/20241017160505
new file mode 100644
index 0000000000000000000000000000000000000000..03379507ea8846826431445e24fdbc1ac252d6ea
--- /dev/null
+++ b/db/schema_migrations/20241017160505
@@ -0,0 +1 @@
+b72166c9449b798ee2d03a60aca42d300d172602678df65052fad564f98b4088
\ No newline at end of file
diff --git a/ee/lib/search/elastic/references/work_item.rb b/ee/lib/search/elastic/references/work_item.rb
index 2bf6964ed67f811714f9173cdb59da9858abbb7f..a2ea3641c407b43b8e664a0f2f9862cdd4f84d68 100644
--- a/ee/lib/search/elastic/references/work_item.rb
+++ b/ee/lib/search/elastic/references/work_item.rb
@@ -69,7 +69,7 @@ def model_klass
         private
 
         def populate_notes(target, data)
-          notes_internal, notes = target.notes.partition(&:internal)
+          notes_internal, notes = target.notes.user.partition(&:internal)
 
           internal_notes = notes_internal.sort_by(&:created_at).reverse.map(&:note).join("\n").presence
           data['notes_internal'] = internal_notes.truncate_bytes(NOTES_MAXIMUM_BYTES) if internal_notes
diff --git a/ee/spec/lib/gitlab/gfm/reference_rewriter_spec.rb b/ee/spec/lib/gitlab/gfm/reference_rewriter_spec.rb
deleted file mode 100644
index aeea856a4b5456311dc5689d3f90cc18c6a3e1d4..0000000000000000000000000000000000000000
--- a/ee/spec/lib/gitlab/gfm/reference_rewriter_spec.rb
+++ /dev/null
@@ -1,236 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Gitlab::Gfm::ReferenceRewriter, feature_category: :team_planning do
-  describe '#rewrite with table syntax' do
-    using RSpec::Parameterized::TableSyntax
-
-    let_it_be(:parent_group1) { create(:group, path: "parent-group-one") }
-    let_it_be(:parent_group2) { create(:group, path: "parent-group-two") }
-    let_it_be(:user) { create(:user) }
-
-    let_it_be(:source_project) { create(:project, path: 'old-project', group: parent_group1) }
-    let_it_be(:target_project1) { create(:project, path: 'new-project', group: parent_group1) }
-    let_it_be(:target_project2) { create(:project, path: 'new-project', group: parent_group2) }
-    let_it_be(:source_group) { create(:group, path: 'old-group', parent: parent_group1) }
-    let_it_be(:target_group1) { create(:group, path: 'new-group', parent: parent_group1) }
-    let_it_be(:target_group2) { create(:group, path: 'new-group', parent: parent_group2) }
-
-    let_it_be(:work_item_project_first) { create(:issue, project: source_project) }
-    let_it_be(:work_item_group_first) { create(:issue, :group_level, namespace: source_group) }
-
-    let_it_be(:merge_request) { create(:merge_request, source_project: source_project) }
-
-    let_it_be(:project_label) { create(:label, id: 123, name: 'pr label1', project: source_project) }
-    let_it_be(:parent_group_label) { create(:group_label, id: 321, name: 'gr label1', group: parent_group1) }
-
-    let_it_be(:project_milestone) { create(:milestone, title: 'project milestone', project: source_project) }
-    let_it_be(:parent_group_milestone) { create(:milestone, title: 'group milestone', group: parent_group1) }
-
-    before_all do
-      parent_group1.add_reporter(user)
-      parent_group2.add_reporter(user)
-    end
-
-    context 'with source as Project and target as Project within same parent group' do
-      let_it_be(:source_parent) { source_project }  # 'parent-group-one/old-project'
-      let_it_be(:target_parent) { target_project1 } # 'parent-group-one/new-project'
-
-      where(:source_text, :destination_text) do
-        # group level work item reference
-        'ref parent-group-one/old-group#1'   | 'ref parent-group-one/old-group#1'
-        'ref parent-group-one/old-group#1+'  | 'ref parent-group-one/old-group#1+'
-        'ref parent-group-one/old-group#1+s' | 'ref parent-group-one/old-group#1+s'
-      end
-
-      with_them do
-        it_behaves_like 'rewrites references correctly'
-      end
-    end
-
-    context 'with source as Project and target as Project within different parent groups' do
-      let_it_be(:source_parent) { source_project }  # 'parent-group-one/old-project'
-      let_it_be(:target_parent) { target_project2 } # 'parent-group-two/new-project'
-
-      where(:source_text, :destination_text) do
-        # group level work item reference
-        'ref parent-group-one/old-group#1'   | 'ref parent-group-one/old-group#1'
-        'ref parent-group-one/old-group#1+'  | 'ref parent-group-one/old-group#1+'
-        'ref parent-group-one/old-group#1+s' | 'ref parent-group-one/old-group#1+s'
-      end
-
-      with_them do
-        it_behaves_like 'rewrites references correctly'
-      end
-    end
-
-    context 'with source as Project and target as Group within same parent group' do
-      let_it_be(:source_parent) { source_project } # 'parent-group-one/old-project'
-      let_it_be(:target_parent) { target_group1 }  # 'parent-group-one/new-group'
-
-      where(:source_text, :destination_text) do
-        # group level work item reference
-        'ref parent-group-one/old-group#1'   | 'ref parent-group-one/old-group#1'
-        'ref parent-group-one/old-group#1+'  | 'ref parent-group-one/old-group#1+'
-        'ref parent-group-one/old-group#1+s' | 'ref parent-group-one/old-group#1+s'
-      end
-
-      with_them do
-        it_behaves_like 'rewrites references correctly'
-      end
-    end
-
-    context 'with source as Project and target as Group within different parent groups' do
-      let_it_be(:source_parent) { source_project } # 'parent-group-one/old-project'
-      let_it_be(:target_parent) { target_group2 }  # 'parent-group-two/new-group'
-
-      where(:source_text, :destination_text) do
-        # group level work item reference
-        'ref parent-group-one/old-group#1'   | 'ref parent-group-one/old-group#1'
-        'ref parent-group-one/old-group#1+'  | 'ref parent-group-one/old-group#1+'
-        'ref parent-group-one/old-group#1+s' | 'ref parent-group-one/old-group#1+s'
-      end
-
-      with_them do
-        it_behaves_like 'rewrites references correctly'
-      end
-    end
-
-    context 'with source as Group and target as Project within same parent groups' do
-      let_it_be(:source_parent) { source_group }    # 'parent-group-one/old-group'
-      let_it_be(:target_parent) { target_project1 } # 'parent-group-one/new-project'
-
-      where(:source_text, :destination_text) do
-        # project level work item reference
-        'ref parent-group-one/old-project#1'                   | 'ref parent-group-one/old-project#1'
-        'ref parent-group-one/old-project#1+'                  | 'ref parent-group-one/old-project#1+'
-        'ref parent-group-one/old-project#1+s'                 | 'ref parent-group-one/old-project#1+s'
-        # group level work item reference
-        'ref #1'                                               | 'ref parent-group-one/old-group#1'
-        'ref #1+'                                              | 'ref parent-group-one/old-group#1+'
-        'ref #1+s'                                             | 'ref parent-group-one/old-group#1+s'
-        # merge request reference
-        'ref parent-group-one/old-project!1'                   | 'ref parent-group-one/old-project!1'
-        'ref parent-group-one/old-project!1+'                  | 'ref parent-group-one/old-project!1+'
-        'ref parent-group-one/old-project!1+s'                 | 'ref parent-group-one/old-project!1+s'
-        # project label reference
-        'ref parent-group-one/old-project~123'                 | 'ref parent-group-one/old-project~123'
-        'ref parent-group-one/old-project~"pr label1"'         | 'ref parent-group-one/old-project~123'
-        # group level label reference
-        'ref ~321'                                             | 'ref parent-group-one/old-group~321'
-        'ref ~"gr label1"'                                     | 'ref parent-group-one/old-group~321'
-        # project level milestone reference
-        'ref parent-group-one/old-project%"project milestone"' | 'ref /parent-group-one/old-project%"project milestone"'
-        # group level milestone reference
-        'ref %"group milestone"'                               | 'ref /parent-group-one%"group milestone"'
-      end
-
-      with_them do
-        it_behaves_like 'rewrites references correctly'
-      end
-    end
-
-    context 'with source as Group and target as Project within different parent groups' do
-      let_it_be(:source_parent) { source_group }    # 'parent-group-one/old-group'
-      let_it_be(:target_parent) { target_project2 } # 'parent-group-two/new-project'
-
-      where(:source_text, :destination_text) do
-        # project level work item reference
-        'ref parent-group-one/old-project#1'                   | 'ref parent-group-one/old-project#1'
-        'ref parent-group-one/old-project#1+'                  | 'ref parent-group-one/old-project#1+'
-        'ref parent-group-one/old-project#1+s'                 | 'ref parent-group-one/old-project#1+s'
-        # group level work item reference
-        'ref #1'                                               | 'ref parent-group-one/old-group#1'
-        'ref #1+'                                              | 'ref parent-group-one/old-group#1+'
-        'ref #1+s'                                             | 'ref parent-group-one/old-group#1+s'
-        # merge request reference
-        'ref parent-group-one/old-project!1'                   | 'ref parent-group-one/old-project!1'
-        'ref parent-group-one/old-project!1+'                  | 'ref parent-group-one/old-project!1+'
-        'ref parent-group-one/old-project!1+s'                 | 'ref parent-group-one/old-project!1+s'
-        # project label reference
-        'ref parent-group-one/old-project~123'                 | 'ref parent-group-one/old-project~123'
-        'ref parent-group-one/old-project~"pr label1"'         | 'ref parent-group-one/old-project~123'
-        # group level label reference
-        'ref ~321'                                             | 'ref parent-group-one/old-group~321'
-        'ref ~"gr label1"'                                     | 'ref parent-group-one/old-group~321'
-        # project level milestone reference
-        'ref parent-group-one/old-project%"project milestone"' | 'ref /parent-group-one/old-project%"project milestone"'
-        # group level milestone reference
-        'ref %"group milestone"'                               | 'ref /parent-group-one%"group milestone"'
-      end
-
-      with_them do
-        it_behaves_like 'rewrites references correctly'
-      end
-    end
-
-    context 'with source as Group and target as Group within same parent groups' do
-      let_it_be(:source_parent) { source_group }  # 'parent-group-one/old-group'
-      let_it_be(:target_parent) { target_group1 } # 'parent-group-one/new-group'
-
-      where(:source_text, :destination_text) do
-        # project level work item reference
-        'ref parent-group-one/old-project#1'                   | 'ref parent-group-one/old-project#1'
-        'ref parent-group-one/old-project#1+'                  | 'ref parent-group-one/old-project#1+'
-        'ref parent-group-one/old-project#1+s'                 | 'ref parent-group-one/old-project#1+s'
-        # group level work item reference
-        'ref #1'                                               | 'ref parent-group-one/old-group#1'
-        'ref #1+'                                              | 'ref parent-group-one/old-group#1+'
-        'ref #1+s'                                             | 'ref parent-group-one/old-group#1+s'
-        # merge request reference
-        'ref parent-group-one/old-project!1'                   | 'ref parent-group-one/old-project!1'
-        'ref parent-group-one/old-project!1+'                  | 'ref parent-group-one/old-project!1+'
-        'ref parent-group-one/old-project!1+s'                 | 'ref parent-group-one/old-project!1+s'
-        # project label reference
-        'ref parent-group-one/old-project~123'                 | 'ref parent-group-one/old-project~123'
-        'ref parent-group-one/old-project~"pr label1"'         | 'ref parent-group-one/old-project~123'
-        # group level label reference
-        'ref ~321'                                             | 'ref parent-group-one/old-group~321'
-        'ref ~"gr label1"'                                     | 'ref parent-group-one/old-group~321'
-        # project level milestone reference
-        'ref parent-group-one/old-project%"project milestone"' | 'ref /parent-group-one/old-project%"project milestone"'
-        # group level milestone reference
-        'ref %"group milestone"'                               | 'ref /parent-group-one%"group milestone"'
-      end
-
-      with_them do
-        it_behaves_like 'rewrites references correctly'
-      end
-    end
-
-    context 'with source as Group and target as Group within different parent groups' do
-      let_it_be(:source_parent) { source_group }  # 'parent-group-one/old-group'
-      let_it_be(:target_parent) { target_group2 } # 'parent-group-two/new-group'
-
-      where(:source_text, :destination_text) do
-        # project level work item reference
-        'ref parent-group-one/old-project#1'                   | 'ref parent-group-one/old-project#1'
-        'ref parent-group-one/old-project#1+'                  | 'ref parent-group-one/old-project#1+'
-        'ref parent-group-one/old-project#1+s'                 | 'ref parent-group-one/old-project#1+s'
-        # group level work item reference
-        'ref #1'                                               | 'ref parent-group-one/old-group#1'
-        'ref #1+'                                              | 'ref parent-group-one/old-group#1+'
-        'ref #1+s'                                             | 'ref parent-group-one/old-group#1+s'
-        # merge request reference
-        'ref parent-group-one/old-project!1'                   | 'ref parent-group-one/old-project!1'
-        'ref parent-group-one/old-project!1+'                  | 'ref parent-group-one/old-project!1+'
-        'ref parent-group-one/old-project!1+s'                 | 'ref parent-group-one/old-project!1+s'
-        # project label reference
-        'ref parent-group-one/old-project~123'                 | 'ref parent-group-one/old-project~123'
-        'ref parent-group-one/old-project~"pr label1"'         | 'ref parent-group-one/old-project~123'
-        # group level label reference
-        'ref ~321'                                             | 'ref parent-group-one/old-group~321'
-        'ref ~"gr label1"'                                     | 'ref parent-group-one/old-group~321'
-        # project level milestone reference
-        'ref parent-group-one/old-project%"project milestone"' | 'ref /parent-group-one/old-project%"project milestone"'
-        # group level milestone reference
-        'ref %"group milestone"'                               | 'ref /parent-group-one%"group milestone"'
-      end
-
-      with_them do
-        it_behaves_like 'rewrites references correctly'
-      end
-    end
-  end
-end
diff --git a/ee/spec/lib/search/elastic/references/work_item_spec.rb b/ee/spec/lib/search/elastic/references/work_item_spec.rb
index 0541f60eebd09d7eab8bcf0eaced3df72931b40c..ebad7060f6b124ce6b97be1c8aa5af444114c623 100644
--- a/ee/spec/lib/search/elastic/references/work_item_spec.rb
+++ b/ee/spec/lib/search/elastic/references/work_item_spec.rb
@@ -95,7 +95,6 @@
       let_it_be(:note1) { create(:note_on_issue, noteable: project_work_item, project: project, note: 'Some pig') }
       let_it_be(:note2) { create(:note_on_issue, noteable: project_work_item, project: project, note: 'Terrific') }
       let_it_be(:note3) { create(:note, :internal, noteable: project_work_item, project: project, note: "Radiant") }
-
       let(:expected_hash) do
         base_work_item_hash.merge(
           root_namespace_id: project_work_item.namespace.root_ancestor.id,
@@ -121,6 +120,12 @@
         expect(indexed_json[:notes_internal]).to eq("Newest\n…")
       end
 
+      it 'does not include system notes' do
+        create(:note, :system, noteable: project_work_item, project: project, note: "Enchanting!")
+
+        expect(indexed_json[:notes]).not_to include('Enchanting!')
+      end
+
       context 'when add_work_item_type_correct_id migration is not complete' do
         before do
           set_elasticsearch_migration_to :add_work_item_type_correct_id, including: false
diff --git a/jh/Gemfile.checksum b/jh/Gemfile.checksum
index 9500bde80c37dd82bbf7e5cfa40f829a3b45b784..1281ace9fc3645451fbdebecc331035fe4ef4650 100644
--- a/jh/Gemfile.checksum
+++ b/jh/Gemfile.checksum
@@ -1,7 +1,7 @@
 [
 {"name":"CFPropertyList","version":"3.0.5","platform":"ruby","checksum":"a78551cd4768d78ebca98488c27e33652ef818be64697a54676d34e6434674a4"},
 {"name":"RedCloth","version":"4.3.4","platform":"ruby","checksum":"5231b2fdd91a933915cba330e5fd1a74025e77b56f57b7404c7191ebf2812297"},
-{"name":"acme-client","version":"2.0.18","platform":"ruby","checksum":"3feab341926ffc16eb65babe51ba4dad8180c13e21e774871344e0b3502ef275"},
+{"name":"acme-client","version":"2.0.19","platform":"ruby","checksum":"29647ab04cde309503cf553d3f9d0a25dcca6588caca57f55f12d6c8833244f8"},
 {"name":"actioncable","version":"7.0.8.6","platform":"ruby","checksum":"f48b0ff8414dfbb2b38e639bf49e86677dfd1b16580ce9e0a3ffa1bb4c0a4057"},
 {"name":"actionmailbox","version":"7.0.8.6","platform":"ruby","checksum":"8f9deed46e38fc97d86d34e9552ffd5aae81491b139f8df91ef2616ab5628857"},
 {"name":"actionmailer","version":"7.0.8.6","platform":"ruby","checksum":"e3f058a5e378e72b8a90577aaa325f23e1d718b437fe9430411eb0324ba8f71d"},
diff --git a/jh/Gemfile.lock b/jh/Gemfile.lock
index c0830bcd4b4c5553cf06e4a899fd34ba6354e051..4850246dc98fe96f50c89a2fb1be66c8b54b6d6a 100644
--- a/jh/Gemfile.lock
+++ b/jh/Gemfile.lock
@@ -273,7 +273,8 @@ GEM
     CFPropertyList (3.0.5)
       rexml
     RedCloth (4.3.4)
-    acme-client (2.0.18)
+    acme-client (2.0.19)
+      base64 (~> 0.2.0)
       faraday (>= 1.0, < 3.0.0)
       faraday-retry (>= 1.0, < 3.0.0)
     actioncable (7.0.8.6)
@@ -2026,7 +2027,7 @@ PLATFORMS
 DEPENDENCIES
   CFPropertyList (~> 3.0.0)
   RedCloth (~> 4.3.3)
-  acme-client (~> 2.0.18)
+  acme-client (~> 2.0.19)
   activerecord-explain-analyze (~> 0.1)
   activerecord-gitlab!
   addressable (~> 2.8)
diff --git a/lib/banzai/filter/references/abstract_reference_filter.rb b/lib/banzai/filter/references/abstract_reference_filter.rb
index 06ce1cb07ef4daa6a1b9b71c8adb8ae3a2134193..812475b944ca20abee06df6a2b31c6db0fe3541e 100644
--- a/lib/banzai/filter/references/abstract_reference_filter.rb
+++ b/lib/banzai/filter/references/abstract_reference_filter.rb
@@ -228,7 +228,7 @@ def object_link_filter(text, pattern, link_content: nil, link_reference: false)
 
               url.chomp!(matches[:format]) if matches.names.include?("format")
 
-              content = context[:link_text] || link_content || object_link_text(object, matches)
+              content = link_content || object_link_text(object, matches)
 
               link = %(<a href="#{url}" #{data}
                           title="#{escape_once(title)}"
diff --git a/lib/banzai/filter/references/issue_reference_filter.rb b/lib/banzai/filter/references/issue_reference_filter.rb
index 8b3cca320e4ffe084493f211bdbaf48a5f7844bd..9a6dd834e84132a28406ffcafeec9aaffd472fd5 100644
--- a/lib/banzai/filter/references/issue_reference_filter.rb
+++ b/lib/banzai/filter/references/issue_reference_filter.rb
@@ -22,10 +22,6 @@ def url_for_object(issue, _parent)
         end
 
         def parent_records(parent, ids)
-          # we are treating all group level issues as work items so those would be handled
-          # by the WorkItemReferenceFilter
-          return Issue.none if parent.is_a?(Group)
-
           parent.issues.where(iid: ids.to_a)
                 .includes(:project, :namespace, ::Gitlab::Issues::TypeAssociationGetter.call)
         end
diff --git a/lib/banzai/filter/references/milestone_reference_filter.rb b/lib/banzai/filter/references/milestone_reference_filter.rb
index 566552f77697913af0c2cfd99488bbb6878cc625..af8050cf8d001cffc2c62ec64786855d12d62f05 100644
--- a/lib/banzai/filter/references/milestone_reference_filter.rb
+++ b/lib/banzai/filter/references/milestone_reference_filter.rb
@@ -158,15 +158,6 @@ def parent
         def requires_unescaping?
           true
         end
-
-        def data_attributes_for(text, parent, object, link_content: false, link_reference: false)
-          object_parent = object.resource_parent
-
-          return super unless object_parent.is_a?(Group)
-          return super if object_parent.id == parent.id
-
-          super.merge({ group: object_parent.id, namespace: object_parent.id, project: nil })
-        end
       end
     end
   end
diff --git a/lib/banzai/filter/references/reference_cache.rb b/lib/banzai/filter/references/reference_cache.rb
index 2d4760ee3306c51c7f97b59e1f144eea5667d8d5..03af5062cb6881875894c949aadab5fd7b35ea77 100644
--- a/lib/banzai/filter/references/reference_cache.rb
+++ b/lib/banzai/filter/references/reference_cache.rb
@@ -195,7 +195,29 @@ def cached_objects_for_paths(paths, absolute_path)
         def objects_for_paths(paths, absolute_path)
           search_paths = absolute_path ? paths.pluck(1..-1) : paths
 
-          Route.by_paths(search_paths).preload(source: [:route, { namespace: :route }]).map(&:source)
+          klass = parent_type.to_s.camelize.constantize
+          result = if parent_type == :namespace
+                     klass.id_in(Route.by_paths(search_paths).select(:namespace_id))
+                   else
+                     klass.where_full_path_in(search_paths)
+                   end
+
+          return result if parent_type == :group || parent_type == :namespace
+          return unless parent_type == :project
+
+          projects = result.includes(namespace: :route)
+            .allow_cross_joins_across_databases(url: "https://gitlab.com/gitlab-org/gitlab/-/issues/420046")
+
+          return projects unless absolute_path
+
+          # If we make it to here, then we're handling absolute path(s).
+          # Which means we need to also search groups as well as projects.
+          # Possible future optimization might be to use Route along the lines of:
+          #   Routable.where_full_path_in(paths).includes(:source)
+          # See `routable.rb`
+          groups = Group.where_full_path_in(search_paths)
+
+          projects.to_a + groups.to_a
         end
 
         def refs_cache
diff --git a/lib/gitlab/application_rate_limiter.rb b/lib/gitlab/application_rate_limiter.rb
index c345ffa8351558189b4e7f293179795a3628c555..227d80f07fc8a5c905f0838acb87e7bec6f7e20a 100644
--- a/lib/gitlab/application_rate_limiter.rb
+++ b/lib/gitlab/application_rate_limiter.rb
@@ -11,6 +11,7 @@ module ApplicationRateLimiter
     LIMIT_USAGE_BUCKET = [0.25, 0.5, 0.75, 1].freeze
 
     class << self
+      include ::Gitlab::Utils::StrongMemoize
       # Application rate limits
       #
       # Threshold value can be either an Integer or a Proc
@@ -224,7 +225,7 @@ def log_request(request, type, current_user, logger = Gitlab::AuthLogger)
           env: type,
           remote_ip: request.ip,
           request_method: request.request_method,
-          path: request.fullpath
+          path: request_path(request)
         }
 
         if current_user
@@ -322,6 +323,31 @@ def scoped_user_in_allowlist?(scope, users_allowlist)
 
         scoped_user.username.downcase.in?(users_allowlist)
       end
+
+      def request_path(request)
+        # req is an ActionDispatch::Request
+        if request.respond_to?(:filtered_path)
+          request.filtered_path
+        else
+          # req is a Grape::Request < Rack::Request
+          other_filtered_path(request)
+        end
+      end
+
+      def other_filtered_path(request)
+        filtered_params = initialize_filtered_params.filter(request.GET)
+
+        if filtered_params.any?
+          "#{request.path}?#{filtered_params.to_query}"
+        else
+          request.fullpath
+        end
+      end
+
+      def initialize_filtered_params
+        ActiveSupport::ParameterFilter.new(Rails.application.config.filter_parameters)
+      end
+      strong_memoize_attr :initialize_filtered_params
     end
   end
 end
diff --git a/lib/gitlab/auth.rb b/lib/gitlab/auth.rb
index 4ac005f598b33b4a8373bf011d2e6898dafed55e..4fa771b447c7d5f56610ca56577da53cf74b693f 100644
--- a/lib/gitlab/auth.rb
+++ b/lib/gitlab/auth.rb
@@ -178,7 +178,7 @@ def rate_limit!(rate_limiter, success:, login:, request:)
             env: :blocklist,
             remote_ip: request.ip,
             request_method: request.request_method,
-            path: request.fullpath,
+            path: request.filtered_path,
             login: login
           )
         end
diff --git a/lib/gitlab/auth/saml/user.rb b/lib/gitlab/auth/saml/user.rb
index 6f72f185c8df5f72e70aa6ae0e792285f746e62c..135dc7c7dc417ab266abd4c1ae708e49681f23ac 100644
--- a/lib/gitlab/auth/saml/user.rb
+++ b/lib/gitlab/auth/saml/user.rb
@@ -18,9 +18,7 @@ def find_user
           user ||= find_or_build_ldap_user if auto_link_ldap_user?
           user ||= build_new_user if signup_enabled?
 
-          if user
-            user.external = !(auth_hash.groups & saml_config.external_groups).empty? if external_users_enabled?
-          end
+          user&.external = external_user?
 
           user
         end
@@ -57,6 +55,20 @@ def external_users_enabled?
         def auth_hash=(auth_hash)
           @auth_hash = Gitlab::Auth::Saml::AuthHash.new(auth_hash)
         end
+
+        private
+
+        def external_user?
+          if external_provider?
+            true
+          elsif external_users_enabled?
+            intersecting_external_groups?
+          end
+        end
+
+        def intersecting_external_groups?
+          !(auth_hash.groups & saml_config.external_groups).empty?
+        end
       end
     end
   end
diff --git a/lib/gitlab/gfm/reference_rewriter.rb b/lib/gitlab/gfm/reference_rewriter.rb
index eb4c6f1709670ebfed7ef57376c2326e80921ff9..de598879e206779c362d2091e94a6b284eaeb91e 100644
--- a/lib/gitlab/gfm/reference_rewriter.rb
+++ b/lib/gitlab/gfm/reference_rewriter.rb
@@ -72,7 +72,6 @@ def original_html
       end
 
       def unfold_reference(reference, match, target_parent)
-        format = match[:format].to_s
         before = @text[0...match.begin(0)]
         after = @text[match.end(0)..]
 
@@ -86,26 +85,22 @@ def unfold_reference(reference, match, target_parent)
           raise RewriteError, "Unspecified reference detected for #{referable.class.name}"
         end
 
-        cross_reference += format
         new_text = before + cross_reference + after
         substitution_valid?(new_text) ? cross_reference : reference
       end
 
       def find_referable(reference)
-        extractor = Gitlab::ReferenceExtractor.new(source_parent_param[:project], @current_user)
-        extractor.analyze(reference, **source_parent_param)
+        extractor = Gitlab::ReferenceExtractor.new(@source_parent, @current_user)
+        extractor.analyze(reference)
         extractor.all.first
       end
 
       def build_cross_reference(referable, target_parent)
-        class_name = referable.class.base_class.name
-
-        return referable.to_reference(target_parent) unless %w[Label Milestone].include?(class_name)
-        return referable.to_reference(@source_parent, target_container: target_parent) if referable.is_a?(GroupLabel)
-        return referable.to_reference(target_parent, full: true, absolute_path: true) if referable.is_a?(Milestone)
-
-        full = @source_parent.is_a?(Group) ? true : false
-        referable.to_reference(target_parent, full: full)
+        if referable.respond_to?(:project)
+          referable.to_reference(target_parent)
+        else
+          referable.to_reference(@source_parent, target_container: target_parent)
+        end
       end
 
       def substitution_valid?(substituted)
@@ -113,20 +108,8 @@ def substitution_valid?(substituted)
       end
 
       def markdown(text)
-        Banzai.render(text, **source_parent_param, no_original_data: true, no_sourcepos: true, link_text: 'placeholder')
-      end
-
-      def source_parent_param
-        case @source_parent
-        when Project
-          { project: @source_parent }
-        when Group
-          { group: @source_parent, project: nil }
-        when Namespaces::ProjectNamespace
-          { project: @source_parent.project }
-        end
+        Banzai.render(text, project: @source_parent, no_original_data: true, no_sourcepos: true)
       end
-      strong_memoize_attr :source_parent_param
     end
   end
 end
diff --git a/lib/gitlab/middleware/go.rb b/lib/gitlab/middleware/go.rb
index b312f209307fb23c7e45a1787b253206fcf42a4e..c18ea0fd2ba1380e1a13ad3c5f4117f29f0cf111 100644
--- a/lib/gitlab/middleware/go.rb
+++ b/lib/gitlab/middleware/go.rb
@@ -27,7 +27,7 @@ def call(env)
           env: :blocklist,
           remote_ip: request.ip,
           request_method: request.request_method,
-          path: request.fullpath
+          path: request.filtered_path
         )
         Rack::Response.new(e.message, 403).finish
       rescue Gitlab::Auth::MissingPersonalAccessTokenError
diff --git a/lib/gitlab/middleware/path_traversal_check.rb b/lib/gitlab/middleware/path_traversal_check.rb
index 7057acd0841e6684c0546036d239727a6541a75f..ac3dc8f88dd544a104acb2b5c7ce6e715542581a 100644
--- a/lib/gitlab/middleware/path_traversal_check.rb
+++ b/lib/gitlab/middleware/path_traversal_check.rb
@@ -34,7 +34,7 @@ def initialize(app)
       def call(env)
         return @app.call(env) unless Feature.enabled?(:check_path_traversal_middleware, Feature.current_request)
 
-        request = ::Rack::Request.new(env.dup)
+        request = ::ActionDispatch::Request.new(env.dup)
         log_params = {}
 
         return @app.call(env) unless path_traversal_attempt?(request, log_params)
@@ -56,7 +56,7 @@ def call(env)
 
       def path_traversal_attempt?(request, log_params)
         with_duration_metric do |metric_labels|
-          original_fullpath = request.fullpath
+          original_fullpath = request.filtered_path
           exclude_query_parameters(request)
 
           decoded_fullpath = CGI.unescape(request.fullpath)
diff --git a/qa/qa/runtime/user/store.rb b/qa/qa/runtime/user/store.rb
index 405c43eb8124d3063cc5cca1a50ab9e01e87270e..f5612e70d4994ab9c09cddffd2f9c27a0a22cd73 100644
--- a/qa/qa/runtime/user/store.rb
+++ b/qa/qa/runtime/user/store.rb
@@ -151,7 +151,7 @@ def reset_test_user!
           #
           # @return [Boolean]
           def create_unique_test_user?
-            !Env.running_on_live_env? && !Env.personal_access_tokens_disabled? && admin_api_client
+            !Env.running_on_dot_com? && !Env.personal_access_tokens_disabled? && admin_api_client
           end
 
           # Create api client with provided token with fallback to UI creation of token
diff --git a/qa/spec/runtime/user/store_spec.rb b/qa/spec/runtime/user/store_spec.rb
index fc796f9ff9bca9b20e373f745677af96c2b1488a..037b88efc44846abfa8fb15ddd7bf44ca9136cc2 100644
--- a/qa/spec/runtime/user/store_spec.rb
+++ b/qa/spec/runtime/user/store_spec.rb
@@ -294,7 +294,7 @@ def mock_user_get(token:, code: 200, body: { is_admin: true, id: 1, username: "r
           stub_env("GITLAB_PASSWORD", password)
           stub_env("GITLAB_QA_ACCESS_TOKEN", api_token)
 
-          allow(Runtime::Env).to receive(:running_on_live_env?).and_return(true)
+          allow(Runtime::Env).to receive(:running_on_dot_com?).and_return(true)
         end
 
         context "with personal access tokens disabled" do
@@ -375,7 +375,7 @@ def mock_user_get(token:, code: 200, body: { is_admin: true, id: 1, username: "r
         let(:user) { Resource::User.init { |usr| usr.api_client = instance_double(Runtime::API::Client) } }
 
         before do
-          allow(Runtime::Env).to receive(:running_on_live_env?).and_return(false)
+          allow(Runtime::Env).to receive(:running_on_dot_com?).and_return(false)
 
           described_class.instance_variable_set(:@admin_api_client, admin_api_client)
           described_class.instance_variable_set(:@test_user, user)
@@ -399,7 +399,7 @@ def mock_user_get(token:, code: 200, body: { is_admin: true, id: 1, username: "r
           stub_env("GITLAB_PASSWORD", password)
           stub_env("GITLAB_QA_ACCESS_TOKEN", nil)
 
-          allow(Runtime::Env).to receive(:running_on_live_env?).and_return(true)
+          allow(Runtime::Env).to receive(:running_on_dot_com?).and_return(true)
         end
 
         context "when api client has not been initialized" do
@@ -502,7 +502,7 @@ def mock_user_get(token:, code: 200, body: { is_admin: true, id: 1, username: "r
         let(:user) { Resource::User.new }
 
         before do
-          allow(Runtime::Env).to receive(:running_on_live_env?).and_return(false)
+          allow(Runtime::Env).to receive(:running_on_dot_com?).and_return(false)
           allow(Resource::User).to receive(:fabricate!).and_yield(user).and_return(user)
 
           described_class.instance_variable_set(:@admin_api_client, admin_api_client)
diff --git a/qa/spec/tools/ci/qa_changes_spec.rb b/qa/spec/tools/ci/qa_changes_spec.rb
index 8b5dbb473562e9870293af0d71074418681948f8..e052ecc15a96ec868af1c0727f9a65f83da99600 100644
--- a/qa/spec/tools/ci/qa_changes_spec.rb
+++ b/qa/spec/tools/ci/qa_changes_spec.rb
@@ -110,11 +110,12 @@
       before do
         stub_env('SELECTIVE_EXECUTION_IMPROVED', true)
         stub_env('QA_CODE_PATH_MAPPINGS_GCS_CREDENTIALS', gcs_creds)
-        allow(Fog::Storage::Google).to receive(:new)
-                                         .with(google_project: gcs_project_id,
-                                           google_json_key_string: gcs_creds)
-                                         .and_return(gcs_client)
+        stub_env('CI_MERGE_REQUEST_TARGET_BRANCH_NAME', "master")
+
         allow(QA::Tools::Ci::CodePathsMapping).to receive(:new).and_return(code_paths_mapping)
+        allow(Fog::Storage::Google).to receive(:new)
+          .with(google_project: gcs_project_id, google_json_key_string: gcs_creds)
+          .and_return(gcs_client)
       end
 
       describe '#qa_tests' do
diff --git a/spec/config/application_spec.rb b/spec/config/application_spec.rb
index be136c11b5c5ee5d1fee0f785c4bae7f22fc4958..8b02592093a605e9c9872aec7abdccce9db72136 100644
--- a/spec/config/application_spec.rb
+++ b/spec/config/application_spec.rb
@@ -20,7 +20,10 @@ def request_for_url(input_url)
         where(:input_url, :output_query) do
           '/'                                      | {}
           '/?safe=1'                               | { 'safe' => '1' }
+          '/?token=secret'                         | { 'token' => filtered }
+          '/?TOKEN=secret'                         | { 'TOKEN' => filtered }
           '/?private_token=secret'                 | { 'private_token' => filtered }
+          '/?PRIVATE_TOKEN=secret'                 | { 'PRIVATE_TOKEN' => filtered }
           '/?mixed=1&private_token=secret'         | { 'mixed' => '1', 'private_token' => filtered }
           '/?note=secret&noteable=1&prefix_note=2' | { 'note' => filtered, 'noteable' => '1', 'prefix_note' => '2' }
           '/?note[note]=secret&target_type=1'      | { 'note' => filtered, 'target_type' => '1' }
diff --git a/spec/frontend/work_items/components/work_item_links/work_item_link_child_spec.js b/spec/frontend/work_items/components/work_item_links/work_item_link_child_spec.js
index c5b48a2014e4b7d23286aba63bdb667810010f7a..795fec3ebed229d5ba99a4fc3e3d1dea71e16abc 100644
--- a/spec/frontend/work_items/components/work_item_links/work_item_link_child_spec.js
+++ b/spec/frontend/work_items/components/work_item_links/work_item_link_child_spec.js
@@ -29,6 +29,7 @@ import {
   workItemHierarchyTreeFailureResponse,
   workItemHierarchyNoChildrenTreeResponse,
   workItemHierarchyTreeSingleClosedItemResponse,
+  workItemWithParentAsChild,
 } from '../../mock_data';
 
 jest.mock('~/alert');
@@ -315,6 +316,7 @@ describe('WorkItemLinkChild', () => {
       });
     });
   });
+
   describe('drag & drop', () => {
     const allowedChildrenByType = { Issue: ['Task'], Epic: ['Epic', 'Issue'] };
     const getWorkItemTreeNoChildrenQueryHandler = jest
@@ -367,4 +369,17 @@ describe('WorkItemLinkChild', () => {
       },
     );
   });
+
+  describe('when parent is same as the grand child', () => {
+    it('hide the expand to avoid cyclic calls', () => {
+      createComponent({
+        childItem: workItemWithParentAsChild,
+        props: {
+          parentId: 'gid://gitlab/WorkItem/1',
+        },
+      });
+
+      expect(findExpandButton().exists()).toBe(false);
+    });
+  });
 });
diff --git a/spec/frontend/work_items/mock_data.js b/spec/frontend/work_items/mock_data.js
index fcce6b38b049e7536146ae3c57080fa7137df944..3fb36f3c5a6f0e796890faa529ca5239fe04eab4 100644
--- a/spec/frontend/work_items/mock_data.js
+++ b/spec/frontend/work_items/mock_data.js
@@ -2279,6 +2279,38 @@ export const workItemObjectiveWithChild = {
   __typename: 'WorkItem',
 };
 
+export const workItemWithParentAsChild = {
+  id: 'gid://gitlab/WorkItem/1',
+  iid: '1',
+  title: 'Cyclic parent 1',
+  description: 'Objective description',
+  state: 'OPEN',
+  confidential: false,
+  reference: 'test-project-path#12',
+  createdAt: '2022-08-03T12:41:54Z',
+  updatedAt: null,
+  closedAt: null,
+  workItemType: {
+    id: 'gid://gitlab/WorkItems::Type/2411',
+    name: 'Objective',
+    iconName: 'issue-type-objective',
+    __typename: 'WorkItemType',
+  },
+  widgets: [
+    {
+      type: 'HIERARCHY',
+      hasChildren: true,
+      parent: null,
+      rolledUpCountsByType: [],
+      children: {
+        nodes: [],
+      },
+      __typename: 'WorkItemWidgetHierarchy',
+    },
+  ],
+  __typename: 'WorkItem',
+};
+
 export const workItemObjectiveWithoutChild = {
   id: 'gid://gitlab/WorkItem/12',
   iid: '12',
diff --git a/spec/lib/banzai/filter/references/label_reference_filter_spec.rb b/spec/lib/banzai/filter/references/label_reference_filter_spec.rb
index ad07c361bf69ca4635cb2e3db58e15d02e0de346..d54c11f0b54fe2705f26b3a9ce786cb0cafb074f 100644
--- a/spec/lib/banzai/filter/references/label_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/references/label_reference_filter_spec.rb
@@ -793,34 +793,14 @@
       let_it_be(:context) { { project: nil, group: another_group } }
 
       it 'can not find the label' do
-        reference = "#{another_group.full_path}~#{group_label.name}"
+        reference = "#{group.full_path}~#{group_label.name}"
         result = reference_filter("See #{reference}", context)
 
         expect(result.to_html).to include "See #{reference}"
       end
 
-      it 'finds the label with relative reference' do
-        label_name = group_label.name
-        reference = "#{group.full_path}~#{label_name}"
-        result = reference_filter("See #{reference}", context)
-
-        if context[:label_url_method] == :group_url
-          expect(result.css('a').first.attr('href')).to eq(urls.group_url(group, label_name: label_name))
-        else
-          expect(result.css('a').first.attr('href')).to eq(urls.issues_group_url(group, label_name: label_name))
-        end
-      end
-
-      it 'finds label in ancestors' do
-        label_name = parent_group_label.name
-        reference = "#{group.full_path}~#{label_name}"
-        result = reference_filter("See #{reference}", context)
-
-        if context[:label_url_method] == :group_url
-          expect(result.css('a').first.attr('href')).to eq(urls.group_url(group, label_name: label_name))
-        else
-          expect(result.css('a').first.attr('href')).to eq(urls.issues_group_url(group, label_name: label_name))
-        end
+      it_behaves_like 'absolute group reference' do
+        let_it_be(:reference) { "#{group.full_path}~#{group_label.name}" }
       end
 
       it 'does not find label in ancestors' do
@@ -829,10 +809,6 @@
 
         expect(result.to_html).to include "See #{reference}"
       end
-
-      it_behaves_like 'absolute group reference' do
-        let_it_be(:reference) { "#{group.full_path}~#{group_label.name}" }
-      end
     end
   end
 
diff --git a/spec/lib/banzai/filter/references/milestone_reference_filter_spec.rb b/spec/lib/banzai/filter/references/milestone_reference_filter_spec.rb
index 8eaf629c24c651229fd7bed12ca27b6a16a59337..05dba4f774154a194552d34530355ee51fa34034 100644
--- a/spec/lib/banzai/filter/references/milestone_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/references/milestone_reference_filter_spec.rb
@@ -33,13 +33,8 @@
       doc = reference_filter("Milestone #{reference}")
       link = doc.css('a').first
 
-      if milestone.project.present?
-        expect(link).to have_attribute('data-project')
-        expect(link.attr('data-project')).to eq project.id.to_s
-      elsif milestone.group.present?
-        expect(link).to have_attribute('data-group')
-        expect(link.attr('data-group')).to eq milestone.group.id.to_s
-      end
+      expect(link).to have_attribute('data-project')
+      expect(link.attr('data-project')).to eq project.id.to_s
     end
 
     it 'includes a data-milestone attribute' do
@@ -158,13 +153,8 @@
       doc = reference_filter("Milestone #{link_reference}")
       link = doc.css('a').first
 
-      if milestone.project.present?
-        expect(link).to have_attribute('data-project')
-        expect(link.attr('data-project')).to eq project.id.to_s
-      elsif milestone.group.present?
-        expect(link).to have_attribute('data-group')
-        expect(link.attr('data-group')).to eq milestone.group.id.to_s
-      end
+      expect(link).to have_attribute('data-project')
+      expect(link.attr('data-project')).to eq project.id.to_s
     end
 
     it 'includes a data-milestone attribute' do
diff --git a/spec/lib/gitlab/application_rate_limiter_spec.rb b/spec/lib/gitlab/application_rate_limiter_spec.rb
index 52e068d2e84e987bdc60a696e510c89a22eb879d..b46e35f029cb865544b3cc3ddcaa68cbab4fcd5b 100644
--- a/spec/lib/gitlab/application_rate_limiter_spec.rb
+++ b/spec/lib/gitlab/application_rate_limiter_spec.rb
@@ -2,7 +2,9 @@
 
 require 'spec_helper'
 
-RSpec.describe Gitlab::ApplicationRateLimiter, :clean_gitlab_redis_rate_limiting do
+RSpec.describe Gitlab::ApplicationRateLimiter, :clean_gitlab_redis_rate_limiting, feature_category: :system_access do
+  include StubRequests
+
   let_it_be(:user) { create(:user) }
   let_it_be(:project) { create(:project) }
 
@@ -532,28 +534,27 @@
   end
 
   describe '.log_request' do
-    let(:file_path) { 'master/README.md' }
-    let(:type) { :raw_blob_request_limit }
-    let(:fullpath) { "/#{project.full_path}/raw/#{file_path}" }
+    let(:token_prefix) { Gitlab::ApplicationSettingFetcher.current_application_settings.personal_access_token_prefix }
+    let(:token_string) { "#{token_prefix}PAT1234" }
+    let(:relative_url) { "/#{project.full_path}/raw/?private_token=#{token_string}" }
 
-    let(:request) do
-      double('request', ip: '127.0.0.1', request_method: 'GET', fullpath: fullpath)
-    end
+    let(:type) { :raw_blob_request_limit }
+    let(:request) { request_for_url(relative_url) }
 
     let(:base_attributes) do
       {
         message: 'Application_Rate_Limiter_Request',
         env: type,
-        remote_ip: '127.0.0.1',
+        remote_ip: request.ip,
         request_method: 'GET',
-        path: fullpath
+        path: request.filtered_path
       }
     end
 
     context 'without a current user' do
       let(:current_user) { nil }
 
-      it 'logs information to auth.log' do
+      it 'logs filtered information to auth.log' do
         expect(Gitlab::AuthLogger).to receive(:error).with(base_attributes).once
 
         subject.log_request(request, type, current_user)
@@ -570,7 +571,7 @@
         })
       end
 
-      it 'logs information to auth.log' do
+      it 'logs filtered information to auth.log' do
         expect(Gitlab::AuthLogger).to receive(:error).with(attributes).once
 
         subject.log_request(request, type, current_user)
diff --git a/spec/lib/gitlab/auth/saml/user_spec.rb b/spec/lib/gitlab/auth/saml/user_spec.rb
index f96c7fe0bc438a81b6e8dc5199c2b0ba411e63c5..4b5cc355ad463328cd2c396df38044d6d29b0adb 100644
--- a/spec/lib/gitlab/auth/saml/user_spec.rb
+++ b/spec/lib/gitlab/auth/saml/user_spec.rb
@@ -2,7 +2,7 @@
 
 require 'spec_helper'
 
-RSpec.describe Gitlab::Auth::Saml::User do
+RSpec.describe Gitlab::Auth::Saml::User, feature_category: :system_access do
   include LdapHelpers
   include LoginHelpers
 
@@ -68,12 +68,22 @@
           end
         end
 
-        context 'user was external, now should not be' do
-          it 'makes user internal' do
-            existing_user.update_attribute('external', true)
-            saml_user.save # rubocop:disable Rails/SaveBang
-            expect(gl_user).to be_valid
-            expect(gl_user.external).to be_falsey
+        context 'when the external_provider config is set to saml' do
+          before do
+            stub_omniauth_saml_config(external_providers: [provider], block_auto_created_users: false)
+          end
+
+          context 'when an existing saml external_user is removed from their external_group' do
+            before do
+              stub_saml_group_config([])
+            end
+
+            it 'retains the external:true attribute', :aggregate_failures do
+              saml_user.save # rubocop:disable Rails/SaveBang -- Gitlab::Auth::OAuth::User#save is a custom method
+              expect(gl_user).to eq existing_user
+              expect(gl_user).to be_valid
+              expect(gl_user.external).to be_truthy
+            end
           end
         end
       end
@@ -418,6 +428,22 @@
         expect(saml_user.find_user.external).to be_falsy
       end
     end
+
+    context 'when the external_providers config includes saml' do
+      before do
+        stub_omniauth_saml_config(external_providers: [provider], block_auto_created_users: false)
+        stub_saml_group_config(%w[Freelancers])
+      end
+
+      it 'marks external:true for all users, regardless of the existence of external_groups', :aggregate_failures do
+        saml_user.find_user
+
+        saml_user.save # rubocop:disable Rails/SaveBang -- Gitlab::Auth::OAuth::User#save is a custom method
+        expect(gl_user).to be_valid
+        expect(gl_user).to be_truthy
+        expect(gl_user.external).to be_truthy
+      end
+    end
   end
 
   describe '#bypass_two_factor?' do
diff --git a/spec/lib/gitlab/auth_spec.rb b/spec/lib/gitlab/auth_spec.rb
index 333bdc22870070b9116f4452d8b0aac5696baf53..b1df7cf55d2d07b4a97fdf54e78e883b12571a02 100644
--- a/spec/lib/gitlab/auth_spec.rb
+++ b/spec/lib/gitlab/auth_spec.rb
@@ -3,6 +3,8 @@
 require 'spec_helper'
 
 RSpec.describe Gitlab::Auth, :use_clean_rails_memory_store_caching, feature_category: :system_access do
+  include StubRequests
+
   let_it_be(:project) { create(:project) }
 
   let(:auth_failure) { { actor: nil, project: nil, type: nil, authentication_abilities: nil } }
@@ -296,7 +298,10 @@
         end
 
         context 'when failure goes over threshold' do
-          let(:request) { instance_double(ActionDispatch::Request, fullpath: '/some/project.git/info/refs', request_method: 'GET', ip: 'ip', path: '/some_path/example') }
+          let(:token_prefix) { Gitlab::ApplicationSettingFetcher.current_application_settings.personal_access_token_prefix }
+          let(:token_string) { "#{token_prefix}PAT1234" }
+          let(:relative_url) { "/some/project.git/info/refs?private_token=#{token_string}" }
+          let(:request) { request_for_url(relative_url) }
 
           before do
             expect_next_instance_of(Gitlab::Auth::IpRateLimiter) do |rate_limiter|
@@ -304,13 +309,14 @@
             end
           end
 
-          it 'logs a message' do
+          it 'logs a message with a filtered path' do
             expect(Gitlab::AuthLogger).to receive(:error).with(
-              message: include('IP has been temporarily banned from Git auth'),
+              message: "Rack_Attack: Git auth failures has exceeded the threshold. " \
+                "IP has been temporarily banned from Git auth.",
               env: :blocklist,
               remote_ip: request.ip,
               request_method: request.request_method,
-              path: request.fullpath,
+              path: request.filtered_path,
               login: user.username
             )
 
diff --git a/spec/lib/gitlab/gfm/reference_rewriter_spec.rb b/spec/lib/gitlab/gfm/reference_rewriter_spec.rb
index 8b754c515b8a051dd090f43d173b64b2f148eb17..75427ac0402ee54f1bef2db139fd60aa4390e9e2 100644
--- a/spec/lib/gitlab/gfm/reference_rewriter_spec.rb
+++ b/spec/lib/gitlab/gfm/reference_rewriter_spec.rb
@@ -2,7 +2,7 @@
 
 require 'spec_helper'
 
-RSpec.describe Gitlab::Gfm::ReferenceRewriter, feature_category: :team_planning do
+RSpec.describe Gitlab::Gfm::ReferenceRewriter do
   let_it_be(:group) { create(:group) }
   let_it_be(:user) { create(:user) }
 
@@ -26,6 +26,14 @@
       let!(:issue_second) { create(:issue, project: old_project) }
       let!(:merge_request) { create(:merge_request, source_project: old_project) }
 
+      context 'plain text description' do
+        let(:text) { 'Description that references #1, #2 and !1' }
+
+        it { is_expected.to include issue_first.to_reference(new_project) }
+        it { is_expected.to include issue_second.to_reference(new_project) }
+        it { is_expected.to include merge_request.to_reference(new_project) }
+      end
+
       context 'description with ignored elements' do
         let(:text) do
           "Hi. This references #1, but not `#2`\n" \
@@ -63,9 +71,60 @@
 
           it { is_expected.to eq "#{ref}, `#1`, #{ref}, `#1`" }
         end
+
+        context 'description with project labels' do
+          let!(:label) { create(:label, id: 123, name: 'test', project: old_project) }
+
+          context 'label referenced by id' do
+            let(:text) { '#1 and ~123' }
+
+            it { is_expected.to eq %(#{old_project_ref}#1 and #{old_project_ref}~123) }
+          end
+
+          context 'label referenced by text' do
+            let(:text) { '#1 and ~"test"' }
+
+            it { is_expected.to eq %(#{old_project_ref}#1 and #{old_project_ref}~123) }
+          end
+        end
+
+        context 'description with group labels' do
+          let(:old_group) { create(:group) }
+          let!(:group_label) { create(:group_label, id: 321, name: 'group label', group: old_group) }
+
+          before do
+            old_project.update!(namespace: old_group)
+          end
+
+          context 'label referenced by id' do
+            let(:text) { '#1 and ~321' }
+
+            it { is_expected.to eq %(#{old_project_ref}#1 and #{old_project_ref}~321) }
+          end
+
+          context 'label referenced by text' do
+            let(:text) { '#1 and ~"group label"' }
+
+            it { is_expected.to eq %(#{old_project_ref}#1 and #{old_project_ref}~321) }
+          end
+        end
       end
     end
 
+    context 'when description contains a local reference' do
+      let(:local_issue) { create(:issue, project: old_project) }
+      let(:text) { "See ##{local_issue.iid}" }
+
+      it { is_expected.to eq("See #{old_project.path}##{local_issue.iid}") }
+    end
+
+    context 'when description contains a cross reference' do
+      let(:merge_request) { create(:merge_request) }
+      let(:text) { "See #{merge_request.project.full_path}!#{merge_request.iid}" }
+
+      it { is_expected.to eq(text) }
+    end
+
     context 'with a commit' do
       let(:old_project) { create(:project, :repository, name: 'old-project', group: group) }
       let(:commit) { old_project.commit }
@@ -83,6 +142,26 @@
       end
     end
 
+    context 'reference contains project milestone' do
+      let!(:milestone) do
+        create(:milestone, title: '9.0', project: old_project)
+      end
+
+      let(:text) { 'milestone: %"9.0"' }
+
+      it { is_expected.to eq %(milestone: #{old_project_ref}%"9.0") }
+    end
+
+    context 'when referring to group milestone' do
+      let!(:milestone) do
+        create(:milestone, title: '10.0', group: group)
+      end
+
+      let(:text) { 'milestone %"10.0"' }
+
+      it { is_expected.to eq text }
+    end
+
     context 'when referring to a group' do
       let(:text) { "group @#{group.full_path}" }
 
@@ -99,7 +178,9 @@
       before do
         create(:milestone, title: '9.0', project: old_project)
 
-        allow_any_instance_of(Milestone).to receive(:to_reference).and_return(nil)
+        allow_any_instance_of(Milestone)
+          .to receive(:to_reference)
+          .and_return(nil)
       end
 
       let(:text) { 'milestone: %"9.0"' }
@@ -112,153 +193,4 @@
       end
     end
   end
-
-  describe '#rewrite with table syntax' do
-    using RSpec::Parameterized::TableSyntax
-
-    let_it_be(:parent_group1) { create(:group, path: "parent-group-one") }
-    let_it_be(:parent_group2) { create(:group, path: "parent-group-two") }
-    let_it_be(:user) { create(:user) }
-
-    let_it_be(:source_project) { create(:project, path: 'old-project', group: parent_group1) }
-    let_it_be(:target_project1) { create(:project, path: 'new-project', group: parent_group1) }
-    let_it_be(:target_project2) { create(:project, path: 'new-project', group: parent_group2) }
-    let_it_be(:target_group1) { create(:group, path: 'new-group', parent: parent_group1) }
-    let_it_be(:target_group2) { create(:group, path: 'new-group', parent: parent_group2) }
-
-    let_it_be(:work_item_project_first) { create(:issue, project: source_project) }
-
-    let_it_be(:merge_request) { create(:merge_request, source_project: source_project) }
-
-    let_it_be(:project_label) { create(:label, id: 123, name: 'pr label1', project: source_project) }
-    let_it_be(:parent_group_label) { create(:group_label, id: 321, name: 'gr label1', group: parent_group1) }
-
-    let_it_be(:project_milestone) { create(:milestone, title: 'project milestone', project: source_project) }
-    let_it_be(:parent_group_milestone) { create(:milestone, title: 'group milestone', group: parent_group1) }
-
-    before_all do
-      parent_group1.add_reporter(user)
-      parent_group2.add_reporter(user)
-    end
-
-    context 'with source as Project and target as Project within same parent group' do
-      let_it_be(:source_parent) { source_project }  # 'parent-group-one/old-project'
-      let_it_be(:target_parent) { target_project1 } # 'parent-group-one/new-project'
-
-      where(:source_text, :destination_text) do
-        # project level work item reference
-        'ref #1'                             | 'ref old-project#1'
-        'ref #1+'                            | 'ref old-project#1+'
-        'ref #1+s'                           | 'ref old-project#1+s'
-        # merge request reference
-        'ref !1'                             | 'ref old-project!1'
-        'ref !1+'                            | 'ref old-project!1+'
-        'ref !1+s'                           | 'ref old-project!1+s'
-        # project label reference
-        'ref ~123'                           | 'ref old-project~123'
-        'ref ~"pr label1"'                   | 'ref old-project~123'
-        # group level label reference
-        'ref ~321'                           | 'ref old-project~321'
-        'ref ~"gr label1"'                   | 'ref old-project~321'
-        # project level milestone reference
-        'ref %"project milestone"'           | 'ref /parent-group-one/old-project%"project milestone"'
-        # group level milestone reference
-        'ref %"group milestone"'             | 'ref /parent-group-one%"group milestone"'
-      end
-
-      with_them do
-        it_behaves_like 'rewrites references correctly'
-      end
-    end
-
-    context 'with source as Project and target as Project within different parent groups' do
-      let_it_be(:source_parent) { source_project }  # 'parent-group-one/old-project'
-      let_it_be(:target_parent) { target_project2 } # 'parent-group-two/new-project'
-
-      where(:source_text, :destination_text) do
-        # project level work item reference
-        'ref #1'                             | 'ref parent-group-one/old-project#1'
-        'ref #1+'                            | 'ref parent-group-one/old-project#1+'
-        'ref #1+s'                           | 'ref parent-group-one/old-project#1+s'
-        # merge request reference
-        'ref !1'                             | 'ref parent-group-one/old-project!1'
-        'ref !1+'                            | 'ref parent-group-one/old-project!1+'
-        'ref !1+s'                           | 'ref parent-group-one/old-project!1+s'
-        # project label reference
-        'ref ~123'                           | 'ref parent-group-one/old-project~123'
-        'ref ~"pr label1"'                   | 'ref parent-group-one/old-project~123'
-        # group level label reference
-        'ref ~321'                           | 'ref parent-group-one/old-project~321'
-        'ref ~"gr label1"'                   | 'ref parent-group-one/old-project~321'
-        # project level milestone reference
-        'ref %"project milestone"'           | 'ref /parent-group-one/old-project%"project milestone"'
-        # group level milestone reference
-        'ref %"group milestone"'             | 'ref /parent-group-one%"group milestone"'
-      end
-
-      with_them do
-        it_behaves_like 'rewrites references correctly'
-      end
-    end
-
-    context 'with source as Project and target as Group within same parent group' do
-      let_it_be(:source_parent) { source_project } # 'parent-group-one/old-project'
-      let_it_be(:target_parent) { target_group1 }  # 'parent-group-one/new-group'
-
-      where(:source_text, :destination_text) do
-        # project level work item reference
-        'ref #1'                             | 'ref parent-group-one/old-project#1'
-        'ref #1+'                            | 'ref parent-group-one/old-project#1+'
-        'ref #1+s'                           | 'ref parent-group-one/old-project#1+s'
-        # merge request reference
-        'ref !1'                             | 'ref parent-group-one/old-project!1'
-        'ref !1+'                            | 'ref parent-group-one/old-project!1+'
-        'ref !1+s'                           | 'ref parent-group-one/old-project!1+s'
-        # project label reference
-        'ref ~123'                           | 'ref parent-group-one/old-project~123'
-        'ref ~"pr label1"'                   | 'ref parent-group-one/old-project~123'
-        # group level label reference
-        'ref ~321'                           | 'ref parent-group-one/old-project~321'
-        'ref ~"gr label1"'                   | 'ref parent-group-one/old-project~321'
-        # project level milestone reference
-        'ref %"project milestone"'           | 'ref /parent-group-one/old-project%"project milestone"'
-        # group level milestone reference
-        'ref %"group milestone"'             | 'ref /parent-group-one%"group milestone"'
-      end
-
-      with_them do
-        it_behaves_like 'rewrites references correctly'
-      end
-    end
-
-    context 'with source as Project and target as Group within different parent groups' do
-      let_it_be(:source_parent) { source_project } # 'parent-group-one/old-project'
-      let_it_be(:target_parent) { target_group2 }  # 'parent-group-two/new-group'
-
-      where(:source_text, :destination_text) do
-        # project level work item reference
-        'ref #1'                             | 'ref parent-group-one/old-project#1'
-        'ref #1+'                            | 'ref parent-group-one/old-project#1+'
-        'ref #1+s'                           | 'ref parent-group-one/old-project#1+s'
-        # merge request reference
-        'ref !1'                             | 'ref parent-group-one/old-project!1'
-        'ref !1+'                            | 'ref parent-group-one/old-project!1+'
-        'ref !1+s'                           | 'ref parent-group-one/old-project!1+s'
-        # project label reference
-        'ref ~123'                           | 'ref parent-group-one/old-project~123'
-        'ref ~"pr label1"'                   | 'ref parent-group-one/old-project~123'
-        # group level label reference
-        'ref ~321'                           | 'ref parent-group-one/old-project~321'
-        'ref ~"gr label1"'                   | 'ref parent-group-one/old-project~321'
-        # project level milestone reference
-        'ref %"project milestone"'           | 'ref /parent-group-one/old-project%"project milestone"'
-        # group level milestone reference
-        'ref %"group milestone"'             | 'ref /parent-group-one%"group milestone"'
-      end
-
-      with_them do
-        it_behaves_like 'rewrites references correctly'
-      end
-    end
-  end
 end
diff --git a/spec/lib/gitlab/middleware/go_spec.rb b/spec/lib/gitlab/middleware/go_spec.rb
index 2f38c83bb1fcdd55c3b4d1e8d598ba2d0f269cde..066218c86350aa31f912502c444f3abf5f593ef6 100644
--- a/spec/lib/gitlab/middleware/go_spec.rb
+++ b/spec/lib/gitlab/middleware/go_spec.rb
@@ -188,9 +188,23 @@
                   end
 
                   context 'with a denylisted ip' do
-                    it 'returns forbidden' do
+                    let(:request) { ActionDispatch::Request.new(env) }
+                    let(:attributes) do
+                      {
+                        message: 'Rack_Attack',
+                        status: 403,
+                        env: :blocklist,
+                        remote_ip: env['REMOTE_ADDR'],
+                        request_method: request.request_method,
+                        path: request.filtered_path
+                      }
+                    end
+
+                    it 'returns forbidden', :aggregate_failures do
                       err = Gitlab::Auth::IpBlocked.new
                       expect(Gitlab::Auth).to receive(:find_for_git_client).and_raise(err)
+                      expect(Gitlab::AuthLogger).to receive(:error).with(attributes)
+
                       response = go
 
                       expect(response[0]).to eq(403)
diff --git a/spec/lib/gitlab/middleware/path_traversal_check_spec.rb b/spec/lib/gitlab/middleware/path_traversal_check_spec.rb
index 5d9c9c86014c6b2de947214f9438b6a231fedac2..9cbe86130a73d6441532ca62f133c6e09f4c465f 100644
--- a/spec/lib/gitlab/middleware/path_traversal_check_spec.rb
+++ b/spec/lib/gitlab/middleware/path_traversal_check_spec.rb
@@ -10,7 +10,7 @@
   let(:fake_app) { ->(_) { fake_response } }
 
   describe '#call' do
-    let(:fullpath) { ::Rack::Request.new(env).fullpath }
+    let(:fullpath) { ::ActionDispatch::Request.new(env).filtered_path }
     let(:decoded_fullpath) { CGI.unescape(fullpath) }
 
     let(:graphql_query) do
diff --git a/spec/migrations/regenerate_ci_job_token_signing_key_spec.rb b/spec/migrations/regenerate_ci_job_token_signing_key_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..73acdabdd71acf61a00366bcd3969d78648d2ae9
--- /dev/null
+++ b/spec/migrations/regenerate_ci_job_token_signing_key_spec.rb
@@ -0,0 +1,61 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require_migration!
+
+RSpec.describe RegenerateCiJobTokenSigningKey, feature_category: :continuous_integration do
+  let(:application_settings) do
+    Class.new(ActiveRecord::Base) do
+      self.table_name = 'application_settings'
+
+      attr_encrypted :ci_job_token_signing_key, {
+        mode: :per_attribute_iv,
+        key: Settings.attr_encrypted_db_key_base_32,
+        algorithm: 'aes-256-gcm',
+        encode: false,
+        encode_iv: false
+      }
+    end
+  end
+
+  it 'generates a signing key' do
+    settings = application_settings.create!
+    settings.update!(ci_job_token_signing_key: nil)
+
+    reversible_migration do |migration|
+      migration.before -> {
+        settings = application_settings.first
+
+        expect(settings.ci_job_token_signing_key).to be_nil
+        expect(settings.encrypted_ci_job_token_signing_key).to be_nil
+        expect(settings.encrypted_ci_job_token_signing_key_iv).to be_nil
+      }
+
+      migration.after -> {
+        settings = application_settings.first
+
+        expect(settings.encrypted_ci_job_token_signing_key).to be_present
+        expect(settings.encrypted_ci_job_token_signing_key_iv).to be_present
+        expect { OpenSSL::PKey::RSA.new(settings.ci_job_token_signing_key) }.not_to raise_error
+      }
+    end
+  end
+
+  context 'with existing key' do
+    let(:key) { OpenSSL::PKey::RSA.new(2048).to_pem }
+
+    it 'does not touch existing keys' do
+      settings = application_settings.create!
+      settings.update!(ci_job_token_signing_key: key)
+
+      migrate!
+
+      settings = application_settings.first
+
+      expect(settings.ci_job_token_signing_key).to eq(key)
+      expect(settings.encrypted_ci_job_token_signing_key).to be_present
+      expect(settings.encrypted_ci_job_token_signing_key_iv).to be_present
+      expect { OpenSSL::PKey::RSA.new(settings.ci_job_token_signing_key) }.not_to raise_error
+    end
+  end
+end
diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb
index cb74824eb1cff59d97a1ab0b075ff5690df4bc93..e63b07b2f0808e95dce8494323465c6744fbd64d 100644
--- a/spec/models/group_spec.rb
+++ b/spec/models/group_spec.rb
@@ -1554,13 +1554,6 @@
     it { expect(group.human_name).to eq(group.name) }
   end
 
-  describe '#to_human_reference' do
-    let_it_be(:new_group) { create(:group) }
-
-    it { expect(group.to_human_reference).to be_nil }
-    it { expect(group.to_human_reference(new_group)).to eq(group.full_name) }
-  end
-
   describe '#add_user' do
     let(:user) { create(:user) }
 
diff --git a/spec/models/label_note_spec.rb b/spec/models/label_note_spec.rb
index e859d8a057f029abcc69f0c60b689a311bb3ed3f..a521c0051aac54ac907e5ced3557358e2891422f 100644
--- a/spec/models/label_note_spec.rb
+++ b/spec/models/label_note_spec.rb
@@ -2,7 +2,7 @@
 
 require 'spec_helper'
 
-RSpec.describe LabelNote, feature_category: :team_planning do
+RSpec.describe LabelNote do
   include Gitlab::Routing.url_helpers
 
   let_it_be(:project) { create(:project, :repository) }
diff --git a/spec/support/helpers/stub_requests.rb b/spec/support/helpers/stub_requests.rb
index f9fe12fbfbb29ec8c5d5f29826fc012063ceefea..877386e3dcbea996c4bb26407f65e11e5bbd1211 100644
--- a/spec/support/helpers/stub_requests.rb
+++ b/spec/support/helpers/stub_requests.rb
@@ -47,6 +47,13 @@ def stubbed_hostname(url, hostname: IP_ADDRESS_STUB)
     url.to_s
   end
 
+  def request_for_url(input_url)
+    env = Rack::MockRequest.env_for(input_url)
+    env['action_dispatch.parameter_filter'] = Gitlab::Application.config.filter_parameters
+
+    ActionDispatch::Request.new(env)
+  end
+
   private
 
   def parse_url(url)
diff --git a/spec/support/shared_examples/lib/gitlab/gfm/reference_rewriter_shared_examples.rb b/spec/support/shared_examples/lib/gitlab/gfm/reference_rewriter_shared_examples.rb
deleted file mode 100644
index 16a276ac9c06695d7124c13bf6d33c66fbccedc0..0000000000000000000000000000000000000000
--- a/spec/support/shared_examples/lib/gitlab/gfm/reference_rewriter_shared_examples.rb
+++ /dev/null
@@ -1,89 +0,0 @@
-# frozen_string_literal: true
-
-RSpec.shared_examples 'rewrites references correctly' do
-  let(:noteable) { source_parent.work_items.first }
-
-  let(:note_params) do
-    case source_parent
-    when ::Group
-      { namespace: source_parent, project: nil }
-    when ::Project
-      { project: source_parent }
-    when ::Namespaces::ProjectNamespace
-      { project: source_parent.project }
-    end
-  end
-
-  let(:note) { create(:note, note: source_text, noteable: noteable, **note_params) }
-
-  it 'checks source and target markdown text', :aggregate_failures do
-    new_text = described_class.new(note.note, note.note_html, source_parent, user).rewrite(target_parent)
-    source_text_html = generate_html_from_markdown(source_text, source_parent)
-    target_text_html = generate_html_from_markdown(destination_text, target_parent)
-    source_referable = find_referable(source_text, source_parent, user)
-    target_referable = find_referable(new_text, source_parent, user)
-
-    expect(new_text).to eq(destination_text)
-
-    # this checks that the rendered html actually did render, in contrary the result html would look smth like:
-    # <p dir="auto">ref #1</p> without an actual link to the referenced object
-    expect(source_text_html).to include("href=")
-    expect(target_text_html).to include("href=")
-    expect(referable_href(source_text_html)).to eq(referable_href(target_text_html))
-
-    # validate that expected referable can be extracted from source and destination texts
-    expect(source_referable.id).to eq(target_referable.id)
-
-    # test rewriter with target as project namespace
-    if target_parent.is_a?(Project)
-      project_namespace = target_parent.project_namespace
-      new_text = described_class.new(note.note, note.note_html, source_parent, user).rewrite(project_namespace)
-
-      expect(new_text).to eq(destination_text)
-    end
-
-    # test rewriter with source as project namespace
-    if source_parent.is_a?(Project)
-      project_namespace = source_parent.project_namespace
-      new_text = described_class.new(note.note, note.note_html, project_namespace, user).rewrite(target_parent)
-
-      expect(new_text).to eq(destination_text)
-    end
-
-    # test rewriter with source and target as project namespace
-    if target_parent.is_a?(Project) && source_parent.is_a?(Project)
-      target_namespace = target_parent.project_namespace
-      source_namespace = source_parent.project_namespace
-      new_text = described_class.new(note.note, note.note_html, source_namespace, user).rewrite(target_namespace)
-
-      expect(new_text).to eq(destination_text)
-    end
-  end
-end
-
-def generate_html_from_markdown(text, parent)
-  Banzai.render(text, **parent_argument(parent), no_original_data: true, no_sourcepos: true)
-end
-
-def parent_argument(parent)
-  case parent
-  when Project
-    { project: parent }
-  when Group
-    { group: parent, project: nil }
-  when Namespaces::ProjectNamespace
-    { project: parent.project }
-  end
-end
-
-def find_referable(reference, parent, user)
-  extractor = Gitlab::ReferenceExtractor.new(parent_argument(parent)[:project], user)
-  extractor.analyze(reference, **parent_argument(parent))
-  extractor.all.first
-end
-
-def referable_href(text_html)
-  css = 'a'
-  xpath = Gitlab::Utils::Nokogiri.css_to_xpath(css)
-  Nokogiri::HTML::DocumentFragment.parse(text_html).xpath(xpath).first.attribute('href').value
-end
diff --git a/spec/workers/issues/close_worker_spec.rb b/spec/workers/issues/close_worker_spec.rb
index 488be7eb067a44b8caa990f556cbebd73a7a1854..52bec47d29a62b6fd6aef2b0f089907c5784d07a 100644
--- a/spec/workers/issues/close_worker_spec.rb
+++ b/spec/workers/issues/close_worker_spec.rb
@@ -4,48 +4,66 @@
 
 RSpec.describe Issues::CloseWorker, feature_category: :team_planning do
   describe "#perform" do
-    let_it_be(:user) { create(:user) }
-    let_it_be(:project) { create(:project, :public, :repository) }
-    let_it_be(:issue) { create(:issue, project: project, author: user) }
-
+    let_it_be(:developer) { create(:user) }
+    let_it_be(:author) { create(:user) }
+    let_it_be(:project) { create(:project, :public, :repository, developers: [developer]) }
+    let_it_be_with_reload(:issue) { create(:issue, project: project, author: author) }
+
+    let(:project_id) { project.id }
+    let(:issue_id) { issue.id }
+    let(:current_user_id) { developer&.id }
+    let(:current_author_id) { author&.id }
     let(:commit) { project.commit }
     let(:opts) do
-      { "closed_by" => user&.id, "commit_hash" => commit.to_hash }
+      { "closed_by" => current_author_id, "user_id" => current_user_id, "commit_hash" => commit.to_hash }
     end
 
-    subject(:worker) { described_class.new }
+    subject(:perform_job) { described_class.new.perform(project_id, issue_id, issue.class.to_s, opts) }
 
     describe "#perform" do
       context "when the user can update the issues" do
         it "closes the issues" do
-          worker.perform(project.id, issue.id, issue.class.to_s, opts)
+          perform_job
 
           issue.reload
 
-          expect(issue.closed?).to eq(true)
+          expect(issue).to be_closed
         end
 
         it "closes external issues" do
           external_issue = ExternalIssue.new("foo", project)
           closer = instance_double(Issues::CloseService, execute: true)
 
-          expect(Issues::CloseService).to receive(:new).with(container: project, current_user: user).and_return(closer)
+          expect(Issues::CloseService).to receive(:new).with(container: project, current_user: author)
+                                                       .and_return(closer)
           expect(closer).to receive(:execute).with(external_issue, commit: commit)
 
-          worker.perform(project.id, external_issue.id, external_issue.class.to_s, opts)
+          described_class.new.perform(project.id, external_issue.id, external_issue.class.to_s, opts)
         end
       end
 
       context "when the user can not update the issues" do
-        it "does not close the issues" do
-          other_user = create(:user)
-          opts = { "closed_by" => other_user.id, "commit_hash" => commit.to_hash }
+        let(:current_user_id) { create(:user).id }
 
-          worker.perform(project.id, issue.id, issue.class.to_s, opts)
+        it 'does not close the issues' do
+          perform_job
 
           issue.reload
 
-          expect(issue.closed?).to eq(false)
+          expect(issue).not_to be_closed
+        end
+      end
+
+      # TODO: Remove with https://gitlab.com/gitlab-org/gitlab/-/work_items/509422
+      context "when user is not provided to the worker (backwards compatibility)" do
+        let(:current_user_id) { nil }
+
+        it 'does closes the issue' do
+          perform_job
+
+          issue.reload
+
+          expect(issue).to be_closed
         end
       end
     end
@@ -54,31 +72,24 @@
       it "does not call the close issue service" do
         expect(Issues::CloseService).not_to receive(:new)
 
-        expect { worker.perform(project.id, issue.id, issue.class.to_s, opts) }
-          .not_to raise_exception
+        expect { perform_job }.not_to raise_exception
       end
     end
 
     context "when the project does not exist" do
-      before do
-        allow(Project).to receive(:find_by_id).with(project.id).and_return(nil)
-      end
+      let(:project_id) { non_existing_record_id }
 
       it_behaves_like "when object does not exist"
     end
 
-    context "when the user does not exist" do
-      before do
-        allow(User).to receive(:find_by_id).with(user.id).and_return(nil)
-      end
+    context "when the author does not exist" do
+      let(:current_author_id) { non_existing_record_id }
 
       it_behaves_like "when object does not exist"
     end
 
     context "when the issue does not exist" do
-      before do
-        allow(Issue).to receive(:find_by_id).with(issue.id).and_return(nil)
-      end
+      let(:issue_id) { non_existing_record_id }
 
       it_behaves_like "when object does not exist"
     end
diff --git a/spec/workers/process_commit_worker_spec.rb b/spec/workers/process_commit_worker_spec.rb
index ae75fda72586728f4162bd7a066600ebe4ff2e79..92e7d0d0cee2585ed946d1e51a1ce57def0f4b63 100644
--- a/spec/workers/process_commit_worker_spec.rb
+++ b/spec/workers/process_commit_worker_spec.rb
@@ -4,6 +4,7 @@
 
 RSpec.describe ProcessCommitWorker, feature_category: :source_code_management do
   let_it_be(:user) { create(:user) }
+  let_it_be(:author) { create(:user) }
 
   let(:auto_close_issues) { true }
   let(:project) { create(:project, :public, :repository, autoclose_referenced_issues: auto_close_issues) }
@@ -84,15 +85,30 @@
       context 'when commit is not a merge request merge commit' do
         context 'when commit has issue reference' do
           before do
-            allow(commit).to receive(:safe_message).and_return("Closes #{issue.to_reference}")
+            allow(commit).to receive_messages(
+              safe_message: "Closes #{issue.to_reference}",
+              author: author
+            )
           end
 
           it 'closes issues that should be closed per the commit message' do
             expect { perform }.to change { Issues::CloseWorker.jobs.size }.by(1)
           end
 
+          it 'passes both author and user_id to CloseWorker' do
+            expect { perform }.to change { Issues::CloseWorker.jobs.size }.by(1)
+
+            last_job = Issues::CloseWorker.jobs.last
+            expect(last_job['args']).to include(
+              project.id,
+              issue.id,
+              issue.class.to_s,
+              hash_including('closed_by' => commit.author.id, 'user_id' => user.id)
+            )
+          end
+
           it 'creates cross references' do
-            expect(commit).to receive(:create_cross_references!).with(user, [issue])
+            expect(commit).to receive(:create_cross_references!).with(author, [issue])
 
             perform
           end
@@ -150,8 +166,12 @@
                 )
               end
 
-              it 'closes issues that should be closed per the commit message' do
-                expect { perform }.to change { Issues::CloseWorker.jobs.size }.by(1)
+              it 'closes issues that should be closed per the commit message', :sidekiq_inline do
+                expect_next_instance_of(Issues::CloseService) do |close_service|
+                  expect(close_service).to receive(:execute).with(issue, commit: commit)
+                end
+
+                perform
               end
             end
           end
diff --git a/yarn.lock b/yarn.lock
index ac98e75441d9f1742385d22143d0a079c6060e21..290ed987d1a9e28de51b09f453195e82afad7226 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -5061,9 +5061,9 @@ browserslist@^4.21.3, browserslist@^4.21.5, browserslist@^4.22.2, browserslist@^
     update-browserslist-db "^1.1.0"
 
 browserslist@^4.24.0:
-  version "4.24.3"
-  resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.24.3.tgz#5fc2725ca8fb3c1432e13dac278c7cc103e026d2"
-  integrity sha512-1CPmv8iobE2fyRMV97dAcMVegvvWKxmq94hkLiAkUGwKVTyDLw33K+ZxiFrREKmmps4rIw6grcCFCnTMSZ/YiA==
+  version "4.24.4"
+  resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.24.4.tgz#c6b2865a3f08bcb860a0e827389003b9fe686e4b"
+  integrity sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==
   dependencies:
     caniuse-lite "^1.0.30001688"
     electron-to-chromium "^1.5.73"
@@ -5250,9 +5250,9 @@ caniuse-lite@^1.0.30001464, caniuse-lite@^1.0.30001646:
   integrity sha512-fJegqZZ0ZX8HOWr6rcafGr72+xcgJKI9oWfDW5DrD7ExUtgZC7a7R7ZYmZqplh7XDocFdGeIFn7roAxhOeYrPQ==
 
 caniuse-lite@^1.0.30001688:
-  version "1.0.30001689"
-  resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001689.tgz#67ca960dd5f443903e19949aeacc9d28f6e10910"
-  integrity sha512-CmeR2VBycfa+5/jOfnp/NpWPGd06nf1XYiefUvhXFfZE4GkRc9jv+eGPS4nT558WS/8lYCzV8SlANCIPvbWP1g==
+  version "1.0.30001690"
+  resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001690.tgz#f2d15e3aaf8e18f76b2b8c1481abde063b8104c8"
+  integrity sha512-5ExiE3qQN6oF8Clf8ifIDcMRCRE/dMGcETG/XGMD8/XiXm6HXQgQTh1yZYLXXpSOsEUlJm1Xr7kGULZTuGtP/w==
 
 canvas-confetti@^1.4.0:
   version "1.4.0"
@@ -6960,9 +6960,9 @@ electron-to-chromium@^1.5.4:
   integrity sha512-orzA81VqLyIGUEA77YkVA1D+N+nNfl2isJVjjmOyrlxuooZ19ynb+dOlaDTqd/idKRS9lDCSBmtzM+kyCsMnkA==
 
 electron-to-chromium@^1.5.73:
-  version "1.5.74"
-  resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.74.tgz#cb886b504a6467e4c00bea3317edb38393c53413"
-  integrity sha512-ck3//9RC+6oss/1Bh9tiAVFy5vfSKbRHAFh7Z3/eTRkEqJeWgymloShB17Vg3Z4nmDNp35vAd1BZ6CMW4Wt6Iw==
+  version "1.5.79"
+  resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.79.tgz#4424f23f319db7a653cf9ee76102e4ac283e6b3e"
+  integrity sha512-nYOxJNxQ9Om4EC88BE4pPoNI8xwSFf8pU/BAeOl4Hh/b/i6V4biTAzwV7pXi3ARKeoYO5JZKMIXTryXSVer5RA==
 
 elkjs@^0.9.0:
   version "0.9.1"
@@ -12014,7 +12014,7 @@ picocolors@^1.0.0, picocolors@^1.0.1:
   resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.1.tgz#a8ad579b571952f0e5d25892de5445bcfe25aaa1"
   integrity sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==
 
-picocolors@^1.1.0:
+picocolors@^1.1.1:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b"
   integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==
@@ -14870,12 +14870,12 @@ update-browserslist-db@^1.1.0:
     picocolors "^1.0.1"
 
 update-browserslist-db@^1.1.1:
-  version "1.1.1"
-  resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz#80846fba1d79e82547fb661f8d141e0945755fe5"
-  integrity sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.2.tgz#97e9c96ab0ae7bcac08e9ae5151d26e6bc6b5580"
+  integrity sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg==
   dependencies:
     escalade "^3.2.0"
-    picocolors "^1.1.0"
+    picocolors "^1.1.1"
 
 uri-js@^4.2.2:
   version "4.4.1"