Skip to content

Commit 6cb05c0

Browse files
mykolaharmashjcger
authored andcommitted
[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.
1 parent 974dcb5 commit 6cb05c0

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
@@ -40,16 +40,19 @@ jest.mock('../package_list/package_list', () => ({
4040
),
4141
}));
4242

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

50-
const renderWithProviders = (children: React.ReactNode) => {
53+
const renderWithProviders = (children: React.ReactNode, initialEntries: string[] = ['/']) => {
5154
return render(
52-
<MemoryRouter>
55+
<MemoryRouter initialEntries={initialEntries}>
5356
<I18nProvider>{children}</I18nProvider>
5457
</MemoryRouter>
5558
);
@@ -177,5 +180,15 @@ describe('OnboardingFlowForm', () => {
177180
expect(screen.getByText(/Search through other ways of ingesting data/)).toBeInTheDocument();
178181
expect(screen.getByTestId('package-search-form')).toBeInTheDocument();
179182
});
183+
184+
it('should pass empty searchQuery to search form when URL search is invalid KQL', () => {
185+
mockUsePricingFeature.mockReturnValue(true);
186+
187+
renderWithProviders(<OnboardingFlowForm />, ['/?search=host:(']);
188+
189+
expect(mockPackageListSearchForm).toHaveBeenCalledWith(
190+
expect.objectContaining({ searchQuery: '' })
191+
);
192+
});
180193
});
181194
});

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)