Skip to content

Commit cffe651

Browse files
committed
[Obs Onboarding] Fix app crash when parsing invalid syntax from search URL param (#269741)
Closes elastic/observability-error-backlog#808 Adds guard for invalid syntax inside the `search` URL param. The app doesn't write an invalid search query into the URL when user enters it in the field, but it's still possible to modify the URL directly, which will crash the app when trying to read the parameter. (cherry picked from commit d1f70d8)
1 parent 935f76c commit cffe651

2 files changed

Lines changed: 37 additions & 8 deletions

File tree

x-pack/solutions/observability/plugins/observability_onboarding/public/application/onboarding_flow_form/onboarding_flow_form.test.tsx

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,16 +38,19 @@ jest.mock('../package_list/package_list', () => ({
3838
),
3939
}));
4040

41-
jest.mock('../package_list_search_form/package_list_search_form', () => ({
42-
PackageListSearchForm: () => <div data-test-subj="package-search-form">Search Form</div>,
43-
}));
44-
4541
const mockUseKibana = useKibana as jest.MockedFunction<typeof useKibana>;
4642
const mockUsePricingFeature = usePricingFeature as jest.MockedFunction<typeof usePricingFeature>;
43+
const mockPackageListSearchForm = jest.fn(({ searchQuery }: { searchQuery: string }) => (
44+
<div data-test-subj="package-search-form">Search Form: {searchQuery}</div>
45+
));
46+
47+
jest.mock('../package_list_search_form/package_list_search_form', () => ({
48+
PackageListSearchForm: (props: { searchQuery: string }) => mockPackageListSearchForm(props),
49+
}));
4750

48-
const renderWithProviders = (children: React.ReactNode) => {
51+
const renderWithProviders = (children: React.ReactNode, initialEntries: string[] = ['/']) => {
4952
return render(
50-
<MemoryRouter>
53+
<MemoryRouter initialEntries={initialEntries}>
5154
<I18nProvider>{children}</I18nProvider>
5255
</MemoryRouter>
5356
);
@@ -175,5 +178,15 @@ describe('OnboardingFlowForm', () => {
175178
expect(screen.getByText(/Search through other ways of ingesting data/)).toBeInTheDocument();
176179
expect(screen.getByTestId('package-search-form')).toBeInTheDocument();
177180
});
181+
182+
it('should pass empty searchQuery to search form when URL search is invalid KQL', () => {
183+
mockUsePricingFeature.mockReturnValue(true);
184+
185+
renderWithProviders(<OnboardingFlowForm />, ['/?search=host:(']);
186+
187+
expect(mockPackageListSearchForm).toHaveBeenCalledWith(
188+
expect.objectContaining({ searchQuery: '' })
189+
);
190+
});
178191
});
179192
});

x-pack/solutions/observability/plugins/observability_onboarding/public/application/onboarding_flow_form/onboarding_flow_form.tsx

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import {
2121
useEuiTheme,
2222
EuiBadge,
2323
EuiFlexGrid,
24+
EuiSearchBar,
2425
} from '@elastic/eui';
2526
import { css } from '@emotion/react';
2627

@@ -143,11 +144,13 @@ export const OnboardingFlowForm: FunctionComponent = () => {
143144

144145
const suggestedPackagesRef = useRef<HTMLDivElement | null>(null);
145146
const searchResultsRef = useRef<HTMLDivElement | null>(null);
146-
const [integrationSearch, setIntegrationSearch] = useState(searchParams.get('search') ?? '');
147+
const [integrationSearch, setIntegrationSearch] = useState(
148+
parseSearchQuery(searchParams.get('search'))
149+
);
147150
const { euiTheme } = useEuiTheme();
148151

149152
useEffect(() => {
150-
const searchParam = searchParams.get('search') ?? '';
153+
const searchParam = parseSearchQuery(searchParams.get('search'));
151154
if (integrationSearch === searchParam) return;
152155
const entries: Record<string, string> = Object.fromEntries(searchParams.entries());
153156
if (integrationSearch) {
@@ -400,3 +403,16 @@ function scrollIntoViewWithOffset(element: HTMLElement, offset = 0) {
400403
top: element.getBoundingClientRect().top - document.body.getBoundingClientRect().top - offset,
401404
});
402405
}
406+
407+
function parseSearchQuery(searchQuery: string | null) {
408+
if (searchQuery === null) {
409+
return '';
410+
}
411+
412+
try {
413+
EuiSearchBar.Query.parse(searchQuery ?? '');
414+
return searchQuery;
415+
} catch {
416+
return '';
417+
}
418+
}

0 commit comments

Comments
 (0)