diff --git a/app/assets/javascripts/discourse/app/components/sidebar/api-sections.gjs b/app/assets/javascripts/discourse/app/components/sidebar/api-sections.gjs index 7d527ae53e2..7a7996ccbd9 100644 --- a/app/assets/javascripts/discourse/app/components/sidebar/api-sections.gjs +++ b/app/assets/javascripts/discourse/app/components/sidebar/api-sections.gjs @@ -76,7 +76,7 @@ function prepareSidebarSectionClass(Section) { .toLowerCase() .match(this.sidebarState.sanitizedFilter) || link.keywords.navigation.some((keyword) => - keyword.match(this.sidebarState.filter) + keyword.match(this.sidebarState.sanitizedFilter) ) ); }); diff --git a/app/assets/javascripts/discourse/app/lib/sidebar/admin-sidebar.js b/app/assets/javascripts/discourse/app/lib/sidebar/admin-sidebar.js index 99de2b99d88..596149aa1fa 100644 --- a/app/assets/javascripts/discourse/app/lib/sidebar/admin-sidebar.js +++ b/app/assets/javascripts/discourse/app/lib/sidebar/admin-sidebar.js @@ -355,10 +355,12 @@ export default class AdminSidebarPanel extends BaseCustomSidebarPanel { const params = { filter, settings_filter_url: getURL( - `/admin/site_settings/category/all_results?filter=${filter}` + `/admin/site_settings/category/all_results?filter=${encodeURIComponent( + filter + )}` ), user_list_filter_url: getURL( - `/admin/users/list/active?username=${filter}` + `/admin/users/list/active?username=${encodeURIComponent(filter)}` ), }; diff --git a/app/assets/javascripts/discourse/app/services/sidebar-state.js b/app/assets/javascripts/discourse/app/services/sidebar-state.js index ab67095ef7f..7271839506b 100644 --- a/app/assets/javascripts/discourse/app/services/sidebar-state.js +++ b/app/assets/javascripts/discourse/app/services/sidebar-state.js @@ -13,6 +13,7 @@ import { MAIN_PANEL, SEPARATED_MODE, } from "discourse/lib/sidebar/panels"; +import escapeRegExp from "discourse-common/utils/escape-regexp"; @disableImplicitInjections export default class SidebarState extends Service { @@ -138,7 +139,7 @@ export default class SidebarState extends Service { } get sanitizedFilter() { - return this.filter.toLowerCase().trim(); + return escapeRegExp(this.filter.toLowerCase().trim()); } clearFilter() { diff --git a/spec/system/admin_sidebar_navigation_spec.rb b/spec/system/admin_sidebar_navigation_spec.rb index 377d2eacbc5..0014b925fc2 100644 --- a/spec/system/admin_sidebar_navigation_spec.rb +++ b/spec/system/admin_sidebar_navigation_spec.rb @@ -118,6 +118,14 @@ describe "Admin Revamp | Sidebar Navigation", type: :system do expect(page).to have_no_css(".sidebar-no-results") end + it "escapes the filtered expression for regex expressions" do + visit("/admin") + + filter.filter(".*") # this shouldn't return any results if the expression was escaped + expect(page).to have_no_css(".sidebar-section-link-content-text") + expect(page).to have_css(".sidebar-no-results") + end + it "displays the no results description message correctly when the filter has no results" do visit("/admin") @@ -139,6 +147,27 @@ describe "Admin Revamp | Sidebar Navigation", type: :system do ) end + it "encodes the url param in the links when the filter has no results" do + visit("/admin") + + filter.filter("?") + expect(page).to have_no_css(".sidebar-section-link-content-text") + expect(page).to have_css(".sidebar-no-results") + + no_results_description = page.find(".sidebar-no-results__description") + expect(no_results_description.text).to eq( + "We couldn’t find anything matching ‘?’.\n\nDid you want to search site settings or the admin user list?", + ) + expect(no_results_description).to have_link( + "search site settings", + href: "/admin/site_settings/category/all_results?filter=%3F", + ) + expect(no_results_description).to have_link( + "admin user list?", + href: "/admin/users/list/active?username=%3F", + ) + end + it "temporarily expands section when filter" do visit("/admin") links = page.all(".sidebar-section-link-content-text")