From 5dfe245f2d1fbae482b74e1605e09fcbd720e42e Mon Sep 17 00:00:00 2001 From: Vikrant Gupta Date: Tue, 24 Dec 2024 19:07:20 +0530 Subject: [PATCH 1/3] chore: fix cross spawn vulnerability (#6709) --- frontend/package.json | 3 ++- frontend/yarn.lock | 50 +++++-------------------------------------- 2 files changed, 7 insertions(+), 46 deletions(-) diff --git a/frontend/package.json b/frontend/package.json index bbfd1c8564..0379ac9bff 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -242,6 +242,7 @@ "xml2js": "0.5.0", "phin": "^3.7.1", "body-parser": "1.20.3", - "http-proxy-middleware": "3.0.3" + "http-proxy-middleware": "3.0.3", + "cross-spawn": "7.0.5" } } diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 8f1214a21a..15e4b72c0f 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -6740,21 +6740,10 @@ cross-fetch@3.1.5: dependencies: node-fetch "2.6.7" -cross-spawn@^6.0.5: - version "6.0.5" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" - integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== - dependencies: - nice-try "^1.0.4" - path-key "^2.0.1" - semver "^5.5.0" - shebang-command "^1.2.0" - which "^1.2.9" - -cross-spawn@^7.0.1, cross-spawn@^7.0.2, cross-spawn@^7.0.3: - version "7.0.3" - resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz" - integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== +cross-spawn@7.0.5, cross-spawn@^6.0.5, cross-spawn@^7.0.1, cross-spawn@^7.0.2, cross-spawn@^7.0.3: + version "7.0.5" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.5.tgz#910aac880ff5243da96b728bc6521a5f6c2f2f82" + integrity sha512-ZVJrKKYunU38/76t0RMOulHOnUcbU9GbpWKAOZ0mhjr7CX6FVrH+4FrAapSOekrgFQ3f/8gwMEuIft0aKq6Hug== dependencies: path-key "^3.1.0" shebang-command "^2.0.0" @@ -12120,11 +12109,6 @@ nice-color-palettes@^1.0.1: new-array "^1.0.0" xhr-request "^1.0.1" -nice-try@^1.0.4: - version "1.0.5" - resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" - integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== - no-case@^3.0.4: version "3.0.4" resolved "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz" @@ -12682,11 +12666,6 @@ path-is-absolute@^1.0.0: resolved "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== -path-key@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" - integrity sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw== - path-key@^3.0.0, path-key@^3.1.0: version "3.1.1" resolved "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz" @@ -14758,7 +14737,7 @@ selfsigned@^2.1.1: dependencies: node-forge "^1" -"semver@2 || 3 || 4 || 5", semver@7.3.7, semver@7.5.4, semver@7.x, semver@^5.5.0, semver@^5.6.0, semver@^6.0.0, semver@^6.1.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0, semver@^6.3.1, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7: +"semver@2 || 3 || 4 || 5", semver@7.3.7, semver@7.5.4, semver@7.x, semver@^5.6.0, semver@^6.0.0, semver@^6.1.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0, semver@^6.3.1, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7: version "7.5.4" resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== @@ -14870,13 +14849,6 @@ shallowequal@^1.1.0: resolved "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz" integrity sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ== -shebang-command@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" - integrity sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg== - dependencies: - shebang-regex "^1.0.0" - shebang-command@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz" @@ -14884,11 +14856,6 @@ shebang-command@^2.0.0: dependencies: shebang-regex "^3.0.0" -shebang-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" - integrity sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ== - shebang-regex@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz" @@ -16765,13 +16732,6 @@ which-typed-array@^1.1.9: has-tostringtag "^1.0.0" is-typed-array "^1.1.10" -which@^1.2.9: - version "1.3.1" - resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" - integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== - dependencies: - isexe "^2.0.0" - which@^2.0.1, which@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" From 838192cf5c621715769917132f57e265a5b343fe Mon Sep 17 00:00:00 2001 From: Shaheer Kochai Date: Tue, 24 Dec 2024 19:55:07 +0430 Subject: [PATCH 2/3] fix(Traces Explorer): prevent duplicate API calls to query_range in traces explorer (#6677) * fix(Traces Explorer): prevent duplicate API calls to query_range in traces explorer * fix(QueryBuilder): fix the race condition causing duplicate triggering of initQueryBuilderData * chore: address review comments * fix: fix the failing tests --------- Co-authored-by: Vikrant Gupta --- .../TracesExplorer/ListView/index.tsx | 64 +++++++++++++------ .../pages/TracesExplorer/Filter/Filter.tsx | 4 ++ .../__test__/TracesExplorer.test.tsx | 7 ++ frontend/src/providers/QueryBuilder.tsx | 7 +- 4 files changed, 60 insertions(+), 22 deletions(-) diff --git a/frontend/src/container/TracesExplorer/ListView/index.tsx b/frontend/src/container/TracesExplorer/ListView/index.tsx index e59906c075..1c8209f709 100644 --- a/frontend/src/container/TracesExplorer/ListView/index.tsx +++ b/frontend/src/container/TracesExplorer/ListView/index.tsx @@ -7,10 +7,12 @@ import { REACT_QUERY_KEY } from 'constants/reactQueryKeys'; import EmptyLogsSearch from 'container/EmptyLogsSearch/EmptyLogsSearch'; import NoLogs from 'container/NoLogs/NoLogs'; import { useOptionsMenu } from 'container/OptionsMenu'; +import { CustomTimeType } from 'container/TopNav/DateTimeSelectionV2/config'; import TraceExplorerControls from 'container/TracesExplorer/Controls'; import { useGetQueryRange } from 'hooks/queryBuilder/useGetQueryRange'; import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder'; import { Pagination } from 'hooks/queryPagination'; +import { getDefaultPaginationConfig } from 'hooks/queryPagination/utils'; import useDragColumns from 'hooks/useDragColumns'; import { getDraggedColumns } from 'hooks/useDragColumns/utils'; import useUrlQueryData from 'hooks/useUrlQueryData'; @@ -32,12 +34,19 @@ interface ListViewProps { } function ListView({ isFilterApplied }: ListViewProps): JSX.Element { - const { stagedQuery, panelType } = useQueryBuilder(); + const { + stagedQuery, + panelType: panelTypeFromQueryBuilder, + } = useQueryBuilder(); - const { selectedTime: globalSelectedTime, maxTime, minTime } = useSelector< - AppState, - GlobalReducer - >((state) => state.globalTime); + const panelType = panelTypeFromQueryBuilder || PANEL_TYPES.LIST; + + const { + selectedTime: globalSelectedTime, + maxTime, + minTime, + loading: timeRangeUpdateLoading, + } = useSelector((state) => state.globalTime); const { options, config } = useOptionsMenu({ storageKey: LOCALSTORAGE.TRACES_LIST_OPTIONS, @@ -55,34 +64,51 @@ function ListView({ isFilterApplied }: ListViewProps): JSX.Element { const { queryData: paginationQueryData } = useUrlQueryData( QueryParams.pagination, ); + const paginationConfig = + paginationQueryData ?? getDefaultPaginationConfig(PER_PAGE_OPTIONS); + + const queryKey = useMemo( + () => [ + REACT_QUERY_KEY.GET_QUERY_RANGE, + globalSelectedTime, + maxTime, + minTime, + stagedQuery, + panelType, + paginationConfig, + options?.selectColumns, + ], + [ + stagedQuery, + panelType, + globalSelectedTime, + paginationConfig, + options?.selectColumns, + maxTime, + minTime, + ], + ); const { data, isFetching, isLoading, isError } = useGetQueryRange( { query: stagedQuery || initialQueriesMap.traces, - graphType: panelType || PANEL_TYPES.LIST, - selectedTime: 'GLOBAL_TIME', - globalSelectedInterval: globalSelectedTime, + graphType: panelType, + selectedTime: 'GLOBAL_TIME' as const, + globalSelectedInterval: globalSelectedTime as CustomTimeType, params: { dataSource: 'traces', }, tableParams: { - pagination: paginationQueryData, + pagination: paginationConfig, selectColumns: options?.selectColumns, }, }, DEFAULT_ENTITY_VERSION, { - queryKey: [ - REACT_QUERY_KEY.GET_QUERY_RANGE, - globalSelectedTime, - maxTime, - minTime, - stagedQuery, - panelType, - paginationQueryData, - options?.selectColumns, - ], + queryKey, enabled: + // don't make api call while the time range state in redux is loading + !timeRangeUpdateLoading && !!stagedQuery && panelType === PANEL_TYPES.LIST && !!options?.selectColumns?.length, diff --git a/frontend/src/pages/TracesExplorer/Filter/Filter.tsx b/frontend/src/pages/TracesExplorer/Filter/Filter.tsx index 27c3b65950..c4ee2dba5a 100644 --- a/frontend/src/pages/TracesExplorer/Filter/Filter.tsx +++ b/frontend/src/pages/TracesExplorer/Filter/Filter.tsx @@ -203,6 +203,10 @@ export function Filter(props: FilterProps): JSX.Element { selectedFilters, }); } + + if (isEqual(currentQuery, preparedQuery) && !props?.resetAll) { + return; + } redirectWithQueryBuilderData(preparedQuery); }, [currentQuery, redirectWithQueryBuilderData, selectedFilters], diff --git a/frontend/src/pages/TracesExplorer/__test__/TracesExplorer.test.tsx b/frontend/src/pages/TracesExplorer/__test__/TracesExplorer.test.tsx index c4fdf588de..d5e89feb20 100644 --- a/frontend/src/pages/TracesExplorer/__test__/TracesExplorer.test.tsx +++ b/frontend/src/pages/TracesExplorer/__test__/TracesExplorer.test.tsx @@ -109,6 +109,13 @@ jest.mock('container/OptionsMenu/useOptionsMenu', () => ({ default: (): any => optionMenuReturn, })); +jest.mock('react-redux', () => ({ + ...jest.requireActual('react-redux'), + useSelector: (): any => ({ + loading: false, + }), +})); + describe('TracesExplorer - Filters', () => { // Initial filter panel rendering // Test the initial state like which filters section are opened, default state of duration slider, etc. diff --git a/frontend/src/providers/QueryBuilder.tsx b/frontend/src/providers/QueryBuilder.tsx index b79538cb45..9db10d2767 100644 --- a/frontend/src/providers/QueryBuilder.tsx +++ b/frontend/src/providers/QueryBuilder.tsx @@ -95,7 +95,8 @@ export function QueryBuilderProvider({ const urlQuery = useUrlQuery(); const history = useHistory(); const location = useLocation(); - const currentPathnameRef = useRef(null); + + const currentPathnameRef = useRef(location.pathname); const { maxTime, minTime } = useSelector( (state) => state.globalTime, @@ -814,14 +815,14 @@ export function QueryBuilderProvider({ }; useEffect(() => { - if (stagedQuery && location.pathname !== currentPathnameRef.current) { + if (location.pathname !== currentPathnameRef.current) { currentPathnameRef.current = location.pathname; setStagedQuery(null); // reset the last used query to 0 when navigating away from the page setLastUsedQuery(0); } - }, [location, stagedQuery, currentQuery]); + }, [location.pathname]); const handleOnUnitsChange = useCallback( (unit: string) => { From d2aa1cf06e4b0a83bc70f6e0f5416ab6c68af3ed Mon Sep 17 00:00:00 2001 From: Shaheer Kochai Date: Tue, 24 Dec 2024 20:09:48 +0430 Subject: [PATCH 3/3] fix: fix the issue of saved view overriding query for logs and traces (#6678) * fix: fix the issue of saved view overriding query for logs and traces for builder type query * chore: refactored isDefaultQuery to use a function to extract relevant keys and use lodash's isEqual * fix: add check for multiple queries in isDefaultQuery logic * chore: moved extractRelevantKeys outside isDefaultQuery * fix: fix the failing tests --- .../ExplorerOptions/ExplorerOptions.tsx | 14 ++- .../__tests__/LogsExplorer.test.tsx | 1 + .../TracesExplorer/__test__/testUtils.ts | 1 + frontend/src/providers/QueryBuilder.tsx | 88 ++++++++++++++++++- frontend/src/types/common/queryBuilder.ts | 6 ++ 5 files changed, 108 insertions(+), 2 deletions(-) diff --git a/frontend/src/container/ExplorerOptions/ExplorerOptions.tsx b/frontend/src/container/ExplorerOptions/ExplorerOptions.tsx index 8bccc41c67..85ce464db4 100644 --- a/frontend/src/container/ExplorerOptions/ExplorerOptions.tsx +++ b/frontend/src/container/ExplorerOptions/ExplorerOptions.tsx @@ -112,6 +112,7 @@ function ExplorerOptions({ panelType, isStagedQueryUpdated, redirectWithQueryBuilderData, + isDefaultQuery, } = useQueryBuilder(); const handleSaveViewModalToggle = (): void => { @@ -478,6 +479,11 @@ function ExplorerOptions({ ] = useState(false); useEffect(() => { + // If the query is not the default query, don't set the recently used saved view + if (!isDefaultQuery({ currentQuery, sourcePage: sourcepage })) { + return; + } + const parsedPreservedView = JSON.parse( localStorage.getItem(PRESERVED_VIEW_LOCAL_STORAGE_KEY) || '{}', ); @@ -499,12 +505,18 @@ function ExplorerOptions({ setIsRecentlyUsedSavedViewSelected(false); } - return (): void => clearTimeout(timeoutId); + // eslint-disable-next-line consistent-return + return (): void => { + clearTimeout(timeoutId); + }; }, [ PRESERVED_VIEW_LOCAL_STORAGE_KEY, PRESERVED_VIEW_TYPE, + currentQuery, + isDefaultQuery, isRecentlyUsedSavedViewSelected, onMenuItemSelectHandler, + sourcepage, viewKey, viewName, viewsData?.data?.data, diff --git a/frontend/src/pages/LogsExplorer/__tests__/LogsExplorer.test.tsx b/frontend/src/pages/LogsExplorer/__tests__/LogsExplorer.test.tsx index f76bdce610..f22bfbac80 100644 --- a/frontend/src/pages/LogsExplorer/__tests__/LogsExplorer.test.tsx +++ b/frontend/src/pages/LogsExplorer/__tests__/LogsExplorer.test.tsx @@ -155,6 +155,7 @@ describe('Logs Explorer Tests', () => { const { queryAllByText } = render( false, currentQuery: { ...initialQueriesMap.metrics, builder: { diff --git a/frontend/src/pages/TracesExplorer/__test__/testUtils.ts b/frontend/src/pages/TracesExplorer/__test__/testUtils.ts index 8a46740e6a..22817103e5 100644 --- a/frontend/src/pages/TracesExplorer/__test__/testUtils.ts +++ b/frontend/src/pages/TracesExplorer/__test__/testUtils.ts @@ -195,6 +195,7 @@ export const compositeQuery: Query = { export const redirectWithQueryBuilderData = jest.fn(); export const qbProviderValue = { + isDefaultQuery: jest.fn(() => false), currentQuery: { ...initialQueriesMap.traces, builder: { diff --git a/frontend/src/providers/QueryBuilder.tsx b/frontend/src/providers/QueryBuilder.tsx index 9db10d2767..031ca9841f 100644 --- a/frontend/src/providers/QueryBuilder.tsx +++ b/frontend/src/providers/QueryBuilder.tsx @@ -27,7 +27,7 @@ import { createIdFromObjectFields } from 'lib/createIdFromObjectFields'; import { createNewBuilderItemName } from 'lib/newQueryBuilder/createNewBuilderItemName'; import { getOperatorsBySourceAndPanelType } from 'lib/newQueryBuilder/getOperatorsBySourceAndPanelType'; import { replaceIncorrectObjectFields } from 'lib/replaceIncorrectObjectFields'; -import { cloneDeep, get, merge, set } from 'lodash-es'; +import { cloneDeep, get, isEqual, merge, set } from 'lodash-es'; import { createContext, PropsWithChildren, @@ -53,6 +53,7 @@ import { ViewProps } from 'types/api/saveViews/types'; import { EQueryType } from 'types/common/dashboard'; import { DataSource, + IsDefaultQueryProps, QueryBuilderContextType, QueryBuilderData, } from 'types/common/queryBuilder'; @@ -87,6 +88,7 @@ export const QueryBuilderContext = createContext({ initQueryBuilderData: () => {}, handleOnUnitsChange: () => {}, isStagedQueryUpdated: () => false, + isDefaultQuery: () => false, }); export function QueryBuilderProvider({ @@ -250,6 +252,88 @@ export function QueryBuilderProvider({ [getElementWithActualOperator], ); + const extractRelevantKeys = useCallback( + (queryData: IBuilderQuery): IBuilderQuery => { + const { + dataSource, + queryName, + aggregateOperator, + aggregateAttribute, + timeAggregation, + spaceAggregation, + functions, + filters, + expression, + disabled, + stepInterval, + having, + groupBy, + legend, + } = queryData; + + return { + dataSource, + queryName, + aggregateOperator, + // remove id from aggregateAttribute + aggregateAttribute: { + ...aggregateAttribute, + id: '', + }, + timeAggregation, + spaceAggregation, + functions, + filters, + expression, + disabled, + stepInterval, + having, + groupBy, + legend, + // set to default values + orderBy: [], + limit: null, + reduceTo: 'avg', + }; + }, + [], + ); + + const isDefaultQuery = useCallback( + ({ currentQuery, sourcePage }: IsDefaultQueryProps): boolean => { + // Get default query with updated operators + const defaultQuery = updateAllQueriesOperators( + initialQueriesMap[sourcePage], + PANEL_TYPES.LIST, + sourcePage, + ); + + // Early return if query types don't match + if (currentQuery.queryType !== defaultQuery.queryType) { + return false; + } + + // Only compare builder queries + if (currentQuery.queryType !== EQueryType.QUERY_BUILDER) { + return false; + } + + // If there is more than one query, then it is not a default query + if (currentQuery.builder.queryData.length > 1) { + return false; + } + + const currentBuilderData = extractRelevantKeys( + currentQuery.builder.queryData[0], + ); + const defaultBuilderData = extractRelevantKeys( + defaultQuery.builder.queryData[0], + ); + + return isEqual(currentBuilderData, defaultBuilderData); + }, + [updateAllQueriesOperators, extractRelevantKeys], + ); const updateQueriesData = useCallback( ( query: Query, @@ -884,6 +968,7 @@ export function QueryBuilderProvider({ handleRunQuery, resetQuery, updateAllQueriesOperators, + isDefaultQuery, updateQueriesData, initQueryBuilderData, handleOnUnitsChange, @@ -910,6 +995,7 @@ export function QueryBuilderProvider({ redirectWithQueryBuilderData, handleRunQuery, updateAllQueriesOperators, + isDefaultQuery, updateQueriesData, initQueryBuilderData, handleOnUnitsChange, diff --git a/frontend/src/types/common/queryBuilder.ts b/frontend/src/types/common/queryBuilder.ts index 0987347fd5..fe77e1ad68 100644 --- a/frontend/src/types/common/queryBuilder.ts +++ b/frontend/src/types/common/queryBuilder.ts @@ -247,9 +247,15 @@ export type QueryBuilderContextType = { viewData: ViewProps[] | undefined, viewKey: string, ) => boolean; + isDefaultQuery: (props: IsDefaultQueryProps) => boolean; }; export type QueryAdditionalFilter = { field: keyof IBuilderQuery; text: string; }; + +export type IsDefaultQueryProps = { + currentQuery: Query; + sourcePage: DataSource; +};