From 747c04cc1881f3c2922bd29d09818daea83339fe Mon Sep 17 00:00:00 2001 From: sayedtaqui Date: Tue, 12 Mar 2024 23:32:41 +0530 Subject: [PATCH 01/11] Add new permission for scripting --- packages/extension/src/manifest.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/extension/src/manifest.json b/packages/extension/src/manifest.json index 2c9b8ec04..38828cc0a 100644 --- a/packages/extension/src/manifest.json +++ b/packages/extension/src/manifest.json @@ -17,7 +17,8 @@ "cookies", "debugger", "management", - "contentSettings" + "contentSettings", + "scripting" ], "host_permissions": ["*://*/*"], "devtools_page": "devtools/devtools.html", From c9dadf4613067eee23ac54caa838438315f1b8a7 Mon Sep 17 00:00:00 2001 From: sayedtaqui Date: Tue, 12 Mar 2024 23:34:00 +0530 Subject: [PATCH 02/11] Remove catch console log from quick links --- .../design-system/src/components/landingPage/quickLinksList.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/design-system/src/components/landingPage/quickLinksList.tsx b/packages/design-system/src/components/landingPage/quickLinksList.tsx index fdf521f2f..c3e44f2fd 100644 --- a/packages/design-system/src/components/landingPage/quickLinksList.tsx +++ b/packages/design-system/src/components/landingPage/quickLinksList.tsx @@ -54,7 +54,7 @@ const QuickLinksList = () => { setNews(newsArray); } } catch (error) { - console.warn('Error fetching latest news', error); + // Fail silently. } })(); }, []); From df831ea0612a4f318daceb041fff41c0f0856be8 Mon Sep 17 00:00:00 2001 From: sayedtaqui Date: Tue, 12 Mar 2024 23:35:05 +0530 Subject: [PATCH 03/11] Make LibraryData type more generic --- packages/common/src/libraryDetection.types.ts | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/packages/common/src/libraryDetection.types.ts b/packages/common/src/libraryDetection.types.ts index 0d0374222..10f4157ce 100644 --- a/packages/common/src/libraryDetection.types.ts +++ b/packages/common/src/libraryDetection.types.ts @@ -32,14 +32,10 @@ export type DetectedSignature = { }; export type LibraryData = { - gis: { - signatureMatches: number; + [key: string]: { + signatureMatches?: number; moduleMatch?: number; - matches: DetectedSignature[]; - }; - gsiV2: { - signatureMatches: number; - moduleMatch?: number; - matches: DetectedSignature[]; + matches?: DetectedSignature[]; + domQuerymatches?: [string] | null; }; }; From f706558aba7860a10afb36103e2856383446b97d Mon Sep 17 00:00:00 2001 From: sayedtaqui Date: Tue, 12 Mar 2024 23:36:29 +0530 Subject: [PATCH 04/11] Refactor core --- .../src/core/detectMatchingSignatures.ts | 95 ++++---- .../core/filterMatchesBasedOnExceptions.ts | 67 ------ .../src/core/getInitialLibraryData.ts | 42 ++++ .../src/core/hooks/useLibraryDetection.ts | 70 +++++- packages/library-detection/src/core/index.ts | 1 - .../src/core/stateProvider/index.tsx | 73 +++--- .../src/core/sumUpDetectionResults.ts | 28 ++- .../tests/filterMatchesBasedOnExceptions.ts | 227 ------------------ 8 files changed, 190 insertions(+), 413 deletions(-) delete mode 100644 packages/library-detection/src/core/filterMatchesBasedOnExceptions.ts create mode 100644 packages/library-detection/src/core/getInitialLibraryData.ts delete mode 100644 packages/library-detection/src/core/tests/filterMatchesBasedOnExceptions.ts diff --git a/packages/library-detection/src/core/detectMatchingSignatures.ts b/packages/library-detection/src/core/detectMatchingSignatures.ts index 87bc5c737..732985324 100644 --- a/packages/library-detection/src/core/detectMatchingSignatures.ts +++ b/packages/library-detection/src/core/detectMatchingSignatures.ts @@ -19,79 +19,72 @@ import type { ScriptTagUnderCheck, LibraryData, - DetectionSubFunctions, - DetectionAuditFunctions, + DetectionFunctions, } from '../types'; +import LIBRARIES from '../config'; /** - * Checks if the origin of a script tag is Google-related. + * Filter origins of a script tag from skipped origins. * @param script - The script tag to check. - * @returns A boolean value indicating whether the origin is Google-related. + * @returns A boolean value indicating whether the origin is skipped */ -const originIsGoogle = (script: ScriptTagUnderCheck) => { - return !( - ( - script.origin?.includes('accounts.google.com') || - script.origin?.includes('gstatic.com') - ) // NOTE: intentionally removed apis.google.com from this list +const filterDomainsToSkip = (script: ScriptTagUnderCheck): boolean => { + const domainsToSkip = [ + ...new Set(LIBRARIES.flatMap((library) => library.domainsToSkip || [])), + ]; + + return ( + script.origin !== null && + !domainsToSkip.some((domain) => script?.origin?.includes(domain)) ); }; /** * Detects matching signatures of libraries in loaded scripts. - * @param librariesToDetect - An array of libraries to detect. - * @param loadedScripts - An array of loaded scripts to check. - * @param detectionSubFunctions - An object containing detection sub-functions for each library. - * @param detectionAuditFunctions - An object containing detection audit functions for each library. + * @param scripts - An array of loaded scripts to check. + * @param detectionFunctions - An object containing detection sub-functions for each library. * @returns An object containing the matching signatures and matches for each library. */ - const detectMatchingSignatures = ( - librariesToDetect: string[], - loadedScripts: ScriptTagUnderCheck[], - detectionSubFunctions: DetectionSubFunctions, - detectionAuditFunctions: DetectionAuditFunctions -) => { - const libraryMatches: LibraryData = librariesToDetect.reduce( - (acc, library) => { - acc[library as keyof LibraryData] = { - signatureMatches: 0, - matches: [], - moduleMatch: 0, - }; - return acc; - }, - {} as LibraryData + scripts: ScriptTagUnderCheck[], + detectionFunctions: DetectionFunctions +): LibraryData => { + const libraryMatches: LibraryData = Object.fromEntries( + LIBRARIES.filter(({ detectionFunction }) => detectionFunction).map( + ({ name }) => [ + name, + { + signatureMatches: 0, + matches: [], + moduleMatch: 0, + }, + ] + ) ); - for (const script of loadedScripts.filter(originIsGoogle)) { + for (const script of scripts.filter(filterDomainsToSkip)) { if (script.content === undefined) { continue; } - const detectionSubFunctionsKeys = Object.keys(detectionSubFunctions); + Object.entries(detectionFunctions).forEach(([key, detectionFunction]) => { + if (!detectionFunction) { + return; + } - for (let i = 0; i < detectionSubFunctionsKeys.length; i++) { - const key = detectionSubFunctionsKeys[i] as keyof LibraryData; - libraryMatches[key] = detectionSubFunctions[key]( + const { + signatureMatches, + matches, + moduleMatch = 0, + } = libraryMatches[key]; + + libraryMatches[key] = detectionFunction( script, - libraryMatches[key].matches, - libraryMatches[key].signatureMatches, - libraryMatches[key].moduleMatch as number + matches, + signatureMatches, + moduleMatch ); - } - } - - const detectionAuditFunctionsKeys = Object.keys(detectionAuditFunctions); - - for (let i = 0; i < detectionAuditFunctionsKeys.length; i++) { - const key = detectionAuditFunctionsKeys[i] as keyof DetectionAuditFunctions; - - libraryMatches[key].matches = detectionAuditFunctions[key]( - libraryMatches[key].signatureMatches, - libraryMatches[key].matches, - libraryMatches[key].moduleMatch as number - ); + }); } return libraryMatches; diff --git a/packages/library-detection/src/core/filterMatchesBasedOnExceptions.ts b/packages/library-detection/src/core/filterMatchesBasedOnExceptions.ts deleted file mode 100644 index ab3913445..000000000 --- a/packages/library-detection/src/core/filterMatchesBasedOnExceptions.ts +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2023 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * External dependencies. - */ -import { extractUrl } from '@ps-analysis-tool/common'; -/** - * Internal dependencies. - */ - -import type { DetectedSignature, ExceptionUrls } from '../types'; - -/** - * Filters the given matches based on the exceptions defined in the configuration. - * @param domain - The domain for which the matches are filtered. - * @param exceptions - The configuration object containing the exceptions. - * @param matches - The array of detected signatures to be filtered. - * @returns The filtered array of matches. - */ -const filterMatchesBasedOnExceptions = ( - domain: string, - exceptions: ExceptionUrls, - matches: DetectedSignature[] -) => { - const parsedUrl = extractUrl(domain); - const parsedTabDomain = parsedUrl?.domain; - const parsedSubDomain = parsedUrl?.subdomain; - let filteredMatches: DetectedSignature[] = [...matches]; - - const isCurrentDomainExceptionDomain = - exceptions?.[parsedTabDomain as string] && - exceptions?.[parsedTabDomain as string]?.signatures?.length > 0; - - if ( - (isCurrentDomainExceptionDomain && !parsedSubDomain) || - (isCurrentDomainExceptionDomain && - parsedSubDomain && - exceptions?.[parsedTabDomain as string]?.subDomains.includes( - parsedSubDomain - )) - ) { - filteredMatches = matches.filter( - (match: DetectedSignature) => - !exceptions?.[parsedTabDomain as string]?.signatures?.includes( - match.feature.text - ) - ); - } - - return filteredMatches; -}; - -export default filterMatchesBasedOnExceptions; diff --git a/packages/library-detection/src/core/getInitialLibraryData.ts b/packages/library-detection/src/core/getInitialLibraryData.ts new file mode 100644 index 000000000..8acc5ba2f --- /dev/null +++ b/packages/library-detection/src/core/getInitialLibraryData.ts @@ -0,0 +1,42 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * Internal dependencies. + */ +import LIBRARIES from '../config'; +import type { LibraryData } from '../types'; + +const getInitialLibraryData = (): LibraryData => { + return Object.fromEntries( + LIBRARIES.map(({ name, domQueryFunction }) => { + let initialData = { + signatureMatches: 0, + matches: [], + moduleMatch: 0, + }; + + if (domQueryFunction) { + initialData = { + domQueryMatches: null, + }; + } + + return [name, initialData]; + }) + ); +}; + +export default getInitialLibraryData; diff --git a/packages/library-detection/src/core/hooks/useLibraryDetection.ts b/packages/library-detection/src/core/hooks/useLibraryDetection.ts index 060f43de2..a57f14d6e 100644 --- a/packages/library-detection/src/core/hooks/useLibraryDetection.ts +++ b/packages/library-detection/src/core/hooks/useLibraryDetection.ts @@ -31,6 +31,7 @@ import { } from '../../utils'; import { sumUpDetectionResults, useLibraryDetectionContext } from '..'; import type { LibraryData, ResourceTreeItem } from '../../types'; +import LIBRARIES from '../../config'; // The delay after the page load, because some scripts arrive right after the page load. const LOADING_DELAY = 2000; @@ -41,18 +42,24 @@ const LOADING_DELAY = 2000; const useLibraryDetection = () => { const { isCurrentTabLoading, + isInitialDataUpdated, + setIsInitialDataUpdated, loadedBefore, showLoader, setLibraryMatches, setLoadedBeforeState, setShowLoader, + tabId, } = useLibraryDetectionContext(({ state, actions }) => ({ isCurrentTabLoading: state.isCurrentTabLoading, + isInitialDataUpdated: state.isInitialDataUpdated, + setIsInitialDataUpdated: actions.setIsInitialDataUpdated, loadedBefore: state.loadedBefore, showLoader: state.showLoader, setLoadedBeforeState: actions.setLoadedBeforeState, setLibraryMatches: actions.setLibraryMatches, setShowLoader: actions.setShowLoader, + tabId: state.tabId, })); const timeout = useRef(0); @@ -74,12 +81,18 @@ const useLibraryDetection = () => { LIBRARY_DETECTION_WORKER_TASK.DETECT_SIGNATURE_MATCHING, resourcesWithContent, (realtimeComputationResult: LibraryData) => { - if ( - realtimeComputationResult.gis.matches.length !== 0 || - realtimeComputationResult.gsiV2.matches.length !== 0 - ) { + const hasResults = Object.entries(realtimeComputationResult).find( + ([, result]) => result?.matches?.length + ); + + if (hasResults) { setLibraryMatches((matches) => { - return sumUpDetectionResults(matches, realtimeComputationResult); + const data = sumUpDetectionResults( + matches, + realtimeComputationResult + ); + + return data; }); } } @@ -102,19 +115,51 @@ const useLibraryDetection = () => { }, [listenerCallback, removeListener]); const updateInitialData = useCallback(async () => { + if (isInitialDataUpdated) { + return; + } + + setIsInitialDataUpdated(true); + // chrome.devtools.inspectedWindow.getResources updates whenever new items are added. const scripts = await getNetworkResourcesWithContent(); + const domQueryMatches: LibraryData = {}; + + LIBRARIES.forEach(async ({ name, domQueryFunction }) => { + if (domQueryFunction) { + const [queryResult] = await chrome.scripting.executeScript({ + target: { tabId: tabId, allFrames: false }, + func: domQueryFunction, + }); + + domQueryMatches[name] = { + domQuerymatches: queryResult?.result as [string], + }; + } + }); executeTaskInDevToolWorker( LIBRARY_DETECTION_WORKER_TASK.DETECT_SIGNATURE_MATCHING, scripts, (detectedMatchingSignatures: LibraryData) => { - setLibraryMatches(detectedMatchingSignatures); + const data = { + ...detectedMatchingSignatures, + ...domQueryMatches, + }; + + setLibraryMatches(data); attachListener(); setShowLoader(false); } ); - }, [setLibraryMatches, attachListener, setShowLoader]); + }, [ + setLibraryMatches, + attachListener, + setShowLoader, + setIsInitialDataUpdated, + tabId, + isInitialDataUpdated, + ]); useEffect(() => { if (showLoader) { @@ -124,7 +169,7 @@ const useLibraryDetection = () => { // Get the initial data and listen to new resources. useEffect(() => { - // Only show loader if the page has not loaded yet. + // Show loader while page is loading. if (isCurrentTabLoading) { return; } @@ -141,12 +186,10 @@ const useLibraryDetection = () => { timeout.current = 0; }, LOADING_DELAY); } - - return; + } else { + // When the user revisits the landing page we do not need to show loader. + updateInitialData(); } - - // When the user revisits the landing page we do not need to show loader. - updateInitialData(); }, [ isCurrentTabLoading, loadedBefore, @@ -154,6 +197,7 @@ const useLibraryDetection = () => { updateInitialData, ]); + // CLeanup on component unmount. useEffect(() => { return () => { removeListener(); diff --git a/packages/library-detection/src/core/index.ts b/packages/library-detection/src/core/index.ts index 87b5a600b..dc73f3c07 100644 --- a/packages/library-detection/src/core/index.ts +++ b/packages/library-detection/src/core/index.ts @@ -20,5 +20,4 @@ export { default as sumUpDetectionResults } from './sumUpDetectionResults'; export { default as filterResources } from './filterResources'; export { default as getInlineScriptContent } from './getInlineScriptContent'; export { default as useLibraryDetection } from './hooks/useLibraryDetection'; -export { default as filterMatchesBasedOnExceptions } from './filterMatchesBasedOnExceptions'; export * from './stateProvider'; diff --git a/packages/library-detection/src/core/stateProvider/index.tsx b/packages/library-detection/src/core/stateProvider/index.tsx index b6e74d9f1..e54b1be12 100644 --- a/packages/library-detection/src/core/stateProvider/index.tsx +++ b/packages/library-detection/src/core/stateProvider/index.tsx @@ -16,20 +16,23 @@ /** * External dependencies. */ -import { createContext, useContextSelector } from 'use-context-selector'; import React, { type PropsWithChildren, useState, useCallback, useEffect, - useRef, } from 'react'; -import { getDomainFromUrl, noop } from '@ps-analysis-tool/common'; +import { + noop, + useContextSelector, + createContext, +} from '@ps-analysis-tool/common'; /** * Internal dependencies. */ import type { LibraryData } from '../../types'; +import getInitialLibraryData from '../getInitialLibraryData'; /** * Represents the context for library detection state. @@ -38,90 +41,70 @@ export interface LibraryDetectionContext { state: { libraryMatches: LibraryData; isCurrentTabLoading: boolean; + isInitialDataUpdated: boolean; loadedBefore: boolean; showLoader: boolean; - tabDomain: string; + tabId: number; }; actions: { setLibraryMatches: React.Dispatch>; setIsCurrentTabLoading: React.Dispatch>; + setIsInitialDataUpdated: React.Dispatch>; setLoadedBeforeState: React.Dispatch>; setShowLoader: React.Dispatch>; - setTabDomain: React.Dispatch>; }; } -const INITIAL_STATE: LibraryData = { - gis: { - signatureMatches: 0, - matches: [], - }, - gsiV2: { - signatureMatches: 0, - moduleMatch: 0, - matches: [], - }, -}; +const initialLibraryMatches: LibraryData = getInitialLibraryData(); const initialState: LibraryDetectionContext = { state: { - libraryMatches: INITIAL_STATE, + libraryMatches: initialLibraryMatches, isCurrentTabLoading: false, + isInitialDataUpdated: false, loadedBefore: false, showLoader: true, - tabDomain: '', + tabId: -1, }, actions: { setLibraryMatches: noop, setIsCurrentTabLoading: noop, + setIsInitialDataUpdated: noop, setLoadedBeforeState: noop, setShowLoader: noop, - setTabDomain: noop, }, }; export const Context = createContext(initialState); export const LibraryDetectionProvider = ({ children }: PropsWithChildren) => { - const [libraryMatches, setLibraryMatches] = - useState(INITIAL_STATE); + const [libraryMatches, setLibraryMatches] = useState( + initialLibraryMatches + ); + const [isCurrentTabLoading, setIsCurrentTabLoading] = useState(false); // TODO: Use first/current tab loaded state instead. + const [isInitialDataUpdated, setIsInitialDataUpdated] = useState(false); const [loadedBefore, setLoadedBeforeState] = useState(false); const [showLoader, setShowLoader] = useState(true); - const [tabDomain, setTabDomain] = useState(''); - const tabId = useRef(-1); + const [tabId, setTabId] = useState(-1); useEffect(() => { - tabId.current = chrome.devtools.inspectedWindow.tabId; - chrome.devtools.inspectedWindow.eval( - 'window.location.href', - (result, isException) => { - if (!isException && typeof result === 'string') { - setTabDomain(getDomainFromUrl(result)); - } - } - ); + setTabId(chrome.devtools.inspectedWindow.tabId); }, []); // It is attached, next time the tab is updated or reloaded. const onTabUpdate = useCallback( - ( - changingTabId: number, - changeInfo: chrome.tabs.TabChangeInfo, - tab: chrome.tabs.Tab - ) => { - if (Number(changingTabId) === Number(tabId.current)) { - if (tab.url) { - setTabDomain(getDomainFromUrl(tab.url)); - } + (changingTabId: number, changeInfo: chrome.tabs.TabChangeInfo) => { + if (Number(changingTabId) === Number(tabId)) { if (changeInfo.status === 'complete') { setIsCurrentTabLoading(false); } else if (changeInfo.status === 'loading') { - setLibraryMatches(INITIAL_STATE); + setLibraryMatches(initialLibraryMatches); setIsCurrentTabLoading(true); setShowLoader(true); setLoadedBeforeState(false); + setIsInitialDataUpdated(false); } } }, @@ -130,6 +113,7 @@ export const LibraryDetectionProvider = ({ children }: PropsWithChildren) => { setLibraryMatches, setShowLoader, setLoadedBeforeState, + tabId, ] ); @@ -148,16 +132,17 @@ export const LibraryDetectionProvider = ({ children }: PropsWithChildren) => { state: { libraryMatches, isCurrentTabLoading, + isInitialDataUpdated, loadedBefore, showLoader, - tabDomain, + tabId, }, actions: { setLibraryMatches, setIsCurrentTabLoading, + setIsInitialDataUpdated, setLoadedBeforeState, setShowLoader, - setTabDomain, }, }} > diff --git a/packages/library-detection/src/core/sumUpDetectionResults.ts b/packages/library-detection/src/core/sumUpDetectionResults.ts index 0cd9bec5f..35f35473e 100644 --- a/packages/library-detection/src/core/sumUpDetectionResults.ts +++ b/packages/library-detection/src/core/sumUpDetectionResults.ts @@ -31,9 +31,13 @@ const sumUpDetectionResults = (obj1: LibraryData, obj2: LibraryData) => { for (let i = 0; i < libraryKeys.length; i++) { const key = libraryKeys[i] as keyof LibraryData; - for (let j = 0; j < resultObj[key].matches.length; j++) { - const featureText = resultObj[key].matches[j].feature.text; - const sameFeatureInOtherObject = obj2[key].matches.find( + + const resultObjMatches = resultObj[key] ? resultObj[key].matches ?? [] : []; + const obj2Matches = obj2[key] ? obj2[key].matches ?? [] : []; + + for (let j = 0; j < resultObjMatches.length; j++) { + const featureText = resultObjMatches[j].feature.text; + const sameFeatureInOtherObject = obj2Matches.find( (match) => match.feature.text === featureText ); @@ -41,22 +45,26 @@ const sumUpDetectionResults = (obj1: LibraryData, obj2: LibraryData) => { continue; } - const sameFeatureInOtherObjectIndex = obj2[key].matches.findIndex( + const sameFeatureInOtherObjectIndex = obj2Matches.findIndex( (match) => match.feature.text === featureText ); - obj2[key].matches.splice(sameFeatureInOtherObjectIndex, 1); + obj2Matches.splice(sameFeatureInOtherObjectIndex, 1); - resultObj[key].matches[j].subItems.items = [ - ...resultObj[key].matches[j].subItems.items, + resultObjMatches[j].subItems.items = [ + ...resultObjMatches[j].subItems.items, ...sameFeatureInOtherObject.subItems.items, ]; } - resultObj[key].matches = [...resultObj[key].matches, ...obj2[key].matches]; + resultObj[key].matches = [...resultObjMatches, ...obj2Matches]; + + const resultObjSignature = resultObj[key] + ? resultObj[key].signatureMatches ?? 0 + : 0; + const obj2Signature = obj2[key] ? obj2[key].signatureMatches ?? 0 : 0; - resultObj[key].signatureMatches = - resultObj[key].signatureMatches + obj2[key].signatureMatches; + resultObj[key].signatureMatches = resultObjSignature + obj2Signature; if (resultObj[key].moduleMatch && obj2[key].moduleMatch) { resultObj[key].moduleMatch = diff --git a/packages/library-detection/src/core/tests/filterMatchesBasedOnExceptions.ts b/packages/library-detection/src/core/tests/filterMatchesBasedOnExceptions.ts deleted file mode 100644 index 78f2cb27b..000000000 --- a/packages/library-detection/src/core/tests/filterMatchesBasedOnExceptions.ts +++ /dev/null @@ -1,227 +0,0 @@ -/* - * Copyright 2023 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * Internal dependencies. - */ -import filterMatchesBasedOnExceptions from '../filterMatchesBasedOnExceptions'; - -describe('Should filter signatures given in exception for the given domain only', () => { - it('Should filter only the given signature from the given main domain', () => { - const exceptions = { - 'linkedin.com': { - signatures: ['opt_out_or_no_session'], - subDomains: [], - }, - }; - const domain = '.linkedin.com'; - - const matches = [ - { - feature: { - type: 'link', - text: 'GoogleAuth', - url: 'https://developers.google.com/identity/gsi/web/guides/migration#object_migration_reference_for_user_sign-in', - }, - }, - { - feature: { - type: 'link', - text: 'opt_out_or_no_session', - url: 'https://developers.google.com/identity/gsi/web/guides/migration#object_migration_reference_for_user_sign-in', - }, - }, - ]; - - const expectedResult = [ - { - feature: { - type: 'link', - text: 'GoogleAuth', - url: 'https://developers.google.com/identity/gsi/web/guides/migration#object_migration_reference_for_user_sign-in', - }, - }, - ]; - - expect(filterMatchesBasedOnExceptions(domain, exceptions, matches)).toEqual( - expectedResult - ); - }); - - it("Shouldn't filter from the subdomain ", () => { - const exceptions = { - 'linkedin.com': { - signatures: ['opt_out_or_no_session'], - subDomains: [], - }, - }; - const domain = 'in.linkedin.com'; - - const matches = [ - { - feature: { - type: 'link', - text: 'GoogleAuth', - url: 'https://developers.google.com/identity/gsi/web/guides/migration#object_migration_reference_for_user_sign-in', - }, - }, - { - feature: { - type: 'link', - text: 'opt_out_or_no_session', - url: 'https://developers.google.com/identity/gsi/web/guides/migration#object_migration_reference_for_user_sign-in', - }, - }, - ]; - - expect(filterMatchesBasedOnExceptions(domain, exceptions, matches)).toEqual( - matches - ); - }); - - it('should filter from subdomain if subdomain is given in exception ', () => { - const exceptions = { - 'linkedin.com': { - signatures: ['opt_out_or_no_session'], - subDomains: ['in'], - }, - }; - const domain = 'in.linkedin.com'; - - const matches = [ - { - feature: { - type: 'link', - text: 'GoogleAuth', - url: 'https://developers.google.com/identity/gsi/web/guides/migration#object_migration_reference_for_user_sign-in', - }, - }, - { - feature: { - type: 'link', - text: 'opt_out_or_no_session', - url: 'https://developers.google.com/identity/gsi/web/guides/migration#object_migration_reference_for_user_sign-in', - }, - }, - ]; - - const expectedResult = [ - { - feature: { - type: 'link', - text: 'GoogleAuth', - url: 'https://developers.google.com/identity/gsi/web/guides/migration#object_migration_reference_for_user_sign-in', - }, - }, - ]; - - expect(filterMatchesBasedOnExceptions(domain, exceptions, matches)).toEqual( - expectedResult - ); - }); - - it('should filter all signatures from subdomain if subdomain is given in exception ', () => { - const exceptions = { - 'linkedin.com': { - signatures: ['opt_out_or_no_session', 'GoogleAuth'], - subDomains: ['in'], - }, - }; - const domain = 'in.linkedin.com'; - - const matches = [ - { - feature: { - type: 'link', - text: 'GoogleAuth', - url: 'https://developers.google.com/identity/gsi/web/guides/migration#object_migration_reference_for_user_sign-in', - }, - }, - { - feature: { - type: 'link', - text: 'opt_out_or_no_session', - url: 'https://developers.google.com/identity/gsi/web/guides/migration#object_migration_reference_for_user_sign-in', - }, - }, - ]; - - const expectedResult = []; - - expect(filterMatchesBasedOnExceptions(domain, exceptions, matches)).toEqual( - expectedResult - ); - }); - - it('should filter nothing if its not specified in the exception ', () => { - const exceptions = {}; - const domain = 'in.linkedin.com'; - - const matches = [ - { - feature: { - type: 'link', - text: 'GoogleAuth', - url: 'https://developers.google.com/identity/gsi/web/guides/migration#object_migration_reference_for_user_sign-in', - }, - }, - { - feature: { - type: 'link', - text: 'opt_out_or_no_session', - url: 'https://developers.google.com/identity/gsi/web/guides/migration#object_migration_reference_for_user_sign-in', - }, - }, - ]; - - expect(filterMatchesBasedOnExceptions(domain, exceptions, matches)).toEqual( - matches - ); - }); - - it('should from main domain even if subDomain is present and not given as input ', () => { - const exceptions = { - 'linkedin.com': { - signatures: ['opt_out_or_no_session', 'GoogleAuth'], - subDomains: ['in'], - }, - }; - const domain = 'linkedin.com'; - - const matches = [ - { - feature: { - type: 'link', - text: 'GoogleAuth', - url: 'https://developers.google.com/identity/gsi/web/guides/migration#object_migration_reference_for_user_sign-in', - }, - }, - { - feature: { - type: 'link', - text: 'opt_out_or_no_session', - url: 'https://developers.google.com/identity/gsi/web/guides/migration#object_migration_reference_for_user_sign-in', - }, - }, - ]; - - const expectedResult = []; - - expect(filterMatchesBasedOnExceptions(domain, exceptions, matches)).toEqual( - expectedResult - ); - }); -}); From 4ff83d8f60712ca769e4d799a37deacbbe406846 Mon Sep 17 00:00:00 2001 From: sayedtaqui Date: Tue, 12 Mar 2024 23:37:07 +0530 Subject: [PATCH 05/11] Create new library detection component and refactor code --- .../components/accordion/detectionMessage.tsx | 50 ++++++++++++++ .../src/components/accordion/index.tsx | 2 +- .../src/components/index.tsx | 1 + .../src/components/libraryDetection/index.tsx | 69 +++++++------------ 4 files changed, 76 insertions(+), 46 deletions(-) create mode 100644 packages/library-detection/src/components/accordion/detectionMessage.tsx diff --git a/packages/library-detection/src/components/accordion/detectionMessage.tsx b/packages/library-detection/src/components/accordion/detectionMessage.tsx new file mode 100644 index 000000000..8f0818928 --- /dev/null +++ b/packages/library-detection/src/components/accordion/detectionMessage.tsx @@ -0,0 +1,50 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * External dependencies. + */ +import React from 'react'; + +interface DetectionMessageProps { + libraryName: string; + provider: string; + supportURL: string; +} + +const DetectionMessage = ({ + libraryName, + provider, + supportURL, +}: DetectionMessageProps) => { + return ( +

+ {libraryName} functionality may not work properly due to the phaseout of + third-party cookies. For more information, please visit the + {provider + ' '} + + support forum + + . +

+ ); +}; + +export default DetectionMessage; diff --git a/packages/library-detection/src/components/accordion/index.tsx b/packages/library-detection/src/components/accordion/index.tsx index c5d064ac7..d34640a6e 100644 --- a/packages/library-detection/src/components/accordion/index.tsx +++ b/packages/library-detection/src/components/accordion/index.tsx @@ -46,7 +46,7 @@ const Accordion = ({ }); return ( -
+
({ libraryMatches: state.libraryMatches, showLoader: state.showLoader, - tabDomain: state.tabDomain, isCurrentTabLoading: state.isCurrentTabLoading, })); - LIBRARIES.map((config) => { - let matches = - libraryMatches && libraryMatches[config.name as keyof LibraryData] - ? libraryMatches[config.name as keyof LibraryData]?.matches - : []; - - const parsedUrl = extractUrl(tabDomain); - - const parsedTabDomain = parsedUrl?.domain; - - const isCurrentDomainExceptionDomain = - config?.exceptions?.[parsedTabDomain as string] && - config?.exceptions?.[parsedTabDomain as string]?.signatures?.length > 0; - - if (isCurrentDomainExceptionDomain) { - matches = filterMatchesBasedOnExceptions( - tabDomain, - config?.exceptions, - matches - ); - } - - libraryMatches[config.name as keyof LibraryData].matches = matches; - - return matches; - }); - const names = Object.keys(libraryMatches); const detectedLibraryNames = names.filter( - (name) => libraryMatches[name as keyof LibraryData]?.matches?.length + (name) => + libraryMatches[name as keyof LibraryData]?.matches?.length || + libraryMatches[name as keyof LibraryData]?.domQuerymatches?.length ); const dataMapping = [ @@ -90,16 +59,26 @@ const LibraryDetection = memo(function LibraryDetection() { const result = detectedLibraryNames.length > 0 ? ( <> - {LIBRARIES.map((config) => { - const Component = config.component as React.FC<{ - matches: DetectedSignature[]; - }>; + {LIBRARIES.map((library) => { + const Component = library.component as React.FC; + const matches = - libraryMatches && libraryMatches[config.name as keyof LibraryData] - ? libraryMatches[config.name as keyof LibraryData]?.matches + libraryMatches && libraryMatches[library.name as keyof LibraryData] + ? libraryMatches[library.name as keyof LibraryData]?.matches : []; - - return ; + const domQueryMatches = + libraryMatches && libraryMatches[library.name as keyof LibraryData] + ? libraryMatches[library.name as keyof LibraryData] + ?.domQuerymatches + : null; + + return ( + + ); })} ) : ( From 6d7291db9a5b8b61414c5dd7820836c534c14ab5 Mon Sep 17 00:00:00 2001 From: sayedtaqui Date: Tue, 12 Mar 2024 23:37:50 +0530 Subject: [PATCH 06/11] Add more libaries and refactor gis and gsi libraries --- .../libraries/disqus-comments/accordion.tsx | 50 +++++++++++ .../constants.ts} | 10 +-- .../disqus-comments/disqusCommentsDOMQuery.ts | 41 +++++++++ .../src/libraries/disqus-comments/index.ts | 18 ++++ .../disqus-comments/tests/accordion.tsx | 78 ++++++++++++++++ .../tests/disqusCommentsDOMQuery.ts | 70 +++++++++++++++ .../src/libraries/fb-comments/accordion.tsx | 50 +++++++++++ .../src/libraries/fb-comments/constants.ts | 17 ++++ .../fb-comments/fbCommentsDOMQuery.ts | 40 +++++++++ .../src/libraries/fb-comments/index.ts | 18 ++++ .../libraries/fb-comments/tests/accordion.tsx | 79 +++++++++++++++++ .../fb-comments/tests/fbCommentsDOMQuery.ts | 88 +++++++++++++++++++ .../src/libraries/fb-likes/accordion.tsx | 54 ++++++++++++ .../src/libraries/fb-likes/constants.ts | 17 ++++ .../src/libraries/fb-likes/fbLikesDOMQuery.ts | 42 +++++++++ .../src/libraries/fb-likes/index.ts | 18 ++++ .../libraries/fb-likes/tests/accordion.tsx | 79 +++++++++++++++++ .../fb-likes/tests/fbLikesDOMQuery.ts | 87 ++++++++++++++++++ .../src/libraries/gis/accordion.tsx | 6 +- .../src/libraries/gis/constants.ts | 26 +----- .../gis/{checkForGIS.ts => getGISMatches.ts} | 35 +++++--- .../src/libraries/gis/index.ts | 18 ++++ .../src/libraries/gsi/accordion.tsx | 6 +- .../src/libraries/gsi/constants.ts | 10 +-- .../src/libraries/gsi/generateGSIV2Matches.ts | 59 ------------- .../{checkForGSIv2.ts => getGSIV2Matches.ts} | 45 +++++++--- .../src/libraries/gsi/index.ts | 18 ++++ .../libraries/jetpack-comments/accordion.tsx | 50 +++++++++++ .../libraries/jetpack-comments/constants.ts | 17 ++++ .../src/libraries/jetpack-comments/index.ts | 18 ++++ .../jetpackCommentsDOMQuery.ts | 45 ++++++++++ .../jetpack-comments/tests/accordion.tsx | 75 ++++++++++++++++ .../tests/jetpackCommentsDOMQuery.ts | 59 +++++++++++++ .../tests/jetpackLikesDOMQuery.ts | 59 +++++++++++++ .../src/libraries/jetpack-likes/accordion.tsx | 50 +++++++++++ .../src/libraries/jetpack-likes/constants.ts | 17 ++++ .../src/libraries/jetpack-likes/index.ts | 18 ++++ .../jetpack-likes/jetpackLikesDOMQuery.ts | 40 +++++++++ .../src/libraries/reCaptcha/accordion.tsx | 50 +++++++++++ .../src/libraries/reCaptcha/constants.ts | 17 ++++ .../src/libraries/reCaptcha/index.ts | 18 ++++ .../libraries/reCaptcha/reCaptchaDOMQuery.ts | 40 +++++++++ .../libraries/reCaptcha/tests/accordion.tsx | 78 ++++++++++++++++ .../reCaptcha/tests/reCaptchaDOMQuery.ts | 82 +++++++++++++++++ 44 files changed, 1689 insertions(+), 123 deletions(-) create mode 100644 packages/library-detection/src/libraries/disqus-comments/accordion.tsx rename packages/library-detection/src/libraries/{index.tsx => disqus-comments/constants.ts} (59%) create mode 100644 packages/library-detection/src/libraries/disqus-comments/disqusCommentsDOMQuery.ts create mode 100644 packages/library-detection/src/libraries/disqus-comments/index.ts create mode 100644 packages/library-detection/src/libraries/disqus-comments/tests/accordion.tsx create mode 100644 packages/library-detection/src/libraries/disqus-comments/tests/disqusCommentsDOMQuery.ts create mode 100644 packages/library-detection/src/libraries/fb-comments/accordion.tsx create mode 100644 packages/library-detection/src/libraries/fb-comments/constants.ts create mode 100644 packages/library-detection/src/libraries/fb-comments/fbCommentsDOMQuery.ts create mode 100644 packages/library-detection/src/libraries/fb-comments/index.ts create mode 100644 packages/library-detection/src/libraries/fb-comments/tests/accordion.tsx create mode 100644 packages/library-detection/src/libraries/fb-comments/tests/fbCommentsDOMQuery.ts create mode 100644 packages/library-detection/src/libraries/fb-likes/accordion.tsx create mode 100644 packages/library-detection/src/libraries/fb-likes/constants.ts create mode 100644 packages/library-detection/src/libraries/fb-likes/fbLikesDOMQuery.ts create mode 100644 packages/library-detection/src/libraries/fb-likes/index.ts create mode 100644 packages/library-detection/src/libraries/fb-likes/tests/accordion.tsx create mode 100644 packages/library-detection/src/libraries/fb-likes/tests/fbLikesDOMQuery.ts rename packages/library-detection/src/libraries/gis/{checkForGIS.ts => getGISMatches.ts} (77%) create mode 100644 packages/library-detection/src/libraries/gis/index.ts delete mode 100644 packages/library-detection/src/libraries/gsi/generateGSIV2Matches.ts rename packages/library-detection/src/libraries/gsi/{checkForGSIv2.ts => getGSIV2Matches.ts} (77%) create mode 100644 packages/library-detection/src/libraries/gsi/index.ts create mode 100644 packages/library-detection/src/libraries/jetpack-comments/accordion.tsx create mode 100644 packages/library-detection/src/libraries/jetpack-comments/constants.ts create mode 100644 packages/library-detection/src/libraries/jetpack-comments/index.ts create mode 100644 packages/library-detection/src/libraries/jetpack-comments/jetpackCommentsDOMQuery.ts create mode 100644 packages/library-detection/src/libraries/jetpack-comments/tests/accordion.tsx create mode 100644 packages/library-detection/src/libraries/jetpack-comments/tests/jetpackCommentsDOMQuery.ts create mode 100644 packages/library-detection/src/libraries/jetpack-comments/tests/jetpackLikesDOMQuery.ts create mode 100644 packages/library-detection/src/libraries/jetpack-likes/accordion.tsx create mode 100644 packages/library-detection/src/libraries/jetpack-likes/constants.ts create mode 100644 packages/library-detection/src/libraries/jetpack-likes/index.ts create mode 100644 packages/library-detection/src/libraries/jetpack-likes/jetpackLikesDOMQuery.ts create mode 100644 packages/library-detection/src/libraries/reCaptcha/accordion.tsx create mode 100644 packages/library-detection/src/libraries/reCaptcha/constants.ts create mode 100644 packages/library-detection/src/libraries/reCaptcha/index.ts create mode 100644 packages/library-detection/src/libraries/reCaptcha/reCaptchaDOMQuery.ts create mode 100644 packages/library-detection/src/libraries/reCaptcha/tests/accordion.tsx create mode 100644 packages/library-detection/src/libraries/reCaptcha/tests/reCaptchaDOMQuery.ts diff --git a/packages/library-detection/src/libraries/disqus-comments/accordion.tsx b/packages/library-detection/src/libraries/disqus-comments/accordion.tsx new file mode 100644 index 000000000..3b6a7136d --- /dev/null +++ b/packages/library-detection/src/libraries/disqus-comments/accordion.tsx @@ -0,0 +1,50 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * External dependencies. + */ +import React from 'react'; + +/** + * Internal dependencies. + */ +import { Accordion, DetectionMessage } from '../../components'; +import type { AccordionProps } from '../../types'; +import { DISQUS_COMMENTS_HELP_URL } from './constants'; + +const DisqusCommentsAccordion = ({ domQueryMatches }: AccordionProps) => { + if (!domQueryMatches) { + return null; + } + + const featuresCount = domQueryMatches.length; + + if (!featuresCount) { + return null; + } + + return ( + + + + ); +}; + +export default DisqusCommentsAccordion; diff --git a/packages/library-detection/src/libraries/index.tsx b/packages/library-detection/src/libraries/disqus-comments/constants.ts similarity index 59% rename from packages/library-detection/src/libraries/index.tsx rename to packages/library-detection/src/libraries/disqus-comments/constants.ts index 829cd4181..5ee4cae07 100644 --- a/packages/library-detection/src/libraries/index.tsx +++ b/packages/library-detection/src/libraries/disqus-comments/constants.ts @@ -13,13 +13,5 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -// GSI -export { default as GSIAccordion } from './gsi/accordion'; -export { default as checkForGSIv2 } from './gsi/checkForGSIv2'; -export { default as generateGSIV2Matches } from './gsi/generateGSIV2Matches'; -export * from './gsi/constants'; -// GIS -export { default as GISAccordion } from './gis/accordion'; -export { default as checkForGIS } from './gis/checkForGIS'; -export * from './gis/constants'; +export const DISQUS_COMMENTS_HELP_URL = 'https://disqus.com/support/'; diff --git a/packages/library-detection/src/libraries/disqus-comments/disqusCommentsDOMQuery.ts b/packages/library-detection/src/libraries/disqus-comments/disqusCommentsDOMQuery.ts new file mode 100644 index 000000000..05e858805 --- /dev/null +++ b/packages/library-detection/src/libraries/disqus-comments/disqusCommentsDOMQuery.ts @@ -0,0 +1,41 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const disqusCommentsDOMQuery = () => { + const matchItems: string[] = []; + + const disqusThreadId = document.getElementById('disqus_thread'); + + const disqusCommentIframeSrcRegex = /^https:\/\/disqus\.com\/embed\/comments/; + + if (disqusThreadId) { + const iframes = document.querySelectorAll('iframe'); + + iframes.forEach((iframe) => { + if (iframe.src && disqusCommentIframeSrcRegex.test(iframe.src)) { + matchItems.push(`iframe[src]: ${iframe.src}`); + } + }); + + if (matchItems.length) { + matchItems.push('div[id]: disqus_thread'); + } + } + + return matchItems; +}; + +export default disqusCommentsDOMQuery; diff --git a/packages/library-detection/src/libraries/disqus-comments/index.ts b/packages/library-detection/src/libraries/disqus-comments/index.ts new file mode 100644 index 000000000..2f4afc47b --- /dev/null +++ b/packages/library-detection/src/libraries/disqus-comments/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export { default as DisqusCommentsAccordion } from './accordion'; +export { default as disqusCommentsDOMQuery } from './disqusCommentsDOMQuery'; +export * from './constants'; diff --git a/packages/library-detection/src/libraries/disqus-comments/tests/accordion.tsx b/packages/library-detection/src/libraries/disqus-comments/tests/accordion.tsx new file mode 100644 index 000000000..c4c908d4b --- /dev/null +++ b/packages/library-detection/src/libraries/disqus-comments/tests/accordion.tsx @@ -0,0 +1,78 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * External dependencies. + */ +import React from 'react'; +import { render, screen, fireEvent } from '@testing-library/react'; +import '@testing-library/jest-dom'; + +/** + * Internal dependencies. + */ +import DisqusCommentsAccordion from '../accordion'; + +describe('Disqus Comments Accordion', () => { + const accordionTitleText = 'Disqus Comments.'; + const accordionMessageText = + 'Disqus comments functionality may not work properly due to the phaseout of third-party cookies. For more information, please visit theDisqus support forum.'; + + it('should show accordion', () => { + const domQueryMatches = ['']; + render(); + + const accordion = screen.getByTestId('library-detection-accordion'); + const accordionTitle = screen.getByText(accordionTitleText); + + expect(accordion).toBeInTheDocument(); + expect(accordionTitle).toBeInTheDocument(); + }); + + it('should not show accordion', () => { + const domQueryMatches = null; + render(); + + const accordion = screen.queryByTestId('library-detection-accordion'); + const accordionTitle = screen.queryByText(accordionTitleText); + + expect(accordion).not.toBeInTheDocument(); + expect(accordionTitle).not.toBeInTheDocument(); + }); + + it('should toggle accordion message after clicking the accordion', () => { + const domQueryMatches = [ + 'div[id]: disqus_thread', + 'iframe[src]: https://disqus.com/embed/comments/123', + ]; + + render(); + + const accordion = screen.getByTestId('library-detection-accordion'); + const accordionTitle = screen.getByText(accordionTitleText); + + expect(accordion).not.toHaveTextContent(accordionMessageText); + + // Click the accordion + fireEvent.click(accordionTitle); + + expect(accordion).toHaveTextContent(accordionMessageText); + + // Click the accordion + fireEvent.click(accordionTitle); + + expect(accordion).not.toHaveTextContent(accordionMessageText); + }); +}); diff --git a/packages/library-detection/src/libraries/disqus-comments/tests/disqusCommentsDOMQuery.ts b/packages/library-detection/src/libraries/disqus-comments/tests/disqusCommentsDOMQuery.ts new file mode 100644 index 000000000..68b640290 --- /dev/null +++ b/packages/library-detection/src/libraries/disqus-comments/tests/disqusCommentsDOMQuery.ts @@ -0,0 +1,70 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * External dependencies. + */ +import '@testing-library/jest-dom'; + +/** + * Internal dependencies. + */ +import disqusCommentsDOMQuery from '../disqusCommentsDOMQuery'; + +describe('Disqus Comments DOM Query', () => { + beforeEach(() => { + // Clean up the DOM before each test + document.body.innerHTML = ''; + }); + + it('should return the matches found', () => { + const divWithId = document.createElement('div'); + divWithId.id = 'disqus_thread'; + + const iframe = document.createElement('iframe'); + iframe.src = 'https://disqus.com/embed/comments/abc'; + + document.body.appendChild(divWithId); + document.body.appendChild(iframe); + + const matches = disqusCommentsDOMQuery(); + + expect(matches).toHaveLength(2); + expect(Array.isArray(matches)).toBe(true); + expect(matches).toContain('div[id]: disqus_thread'); + expect(matches).toContain( + 'iframe[src]: https://disqus.com/embed/comments/abc' + ); + }); + + it('should return empty array if both signatures not found', () => { + const divWithId = document.createElement('div'); + divWithId.id = 'disqus_thread'; + + document.body.appendChild(divWithId); + + const matches = disqusCommentsDOMQuery(); + + expect(matches).toHaveLength(0); + expect(Array.isArray(matches)).toBe(true); + }); + + it('should return empty array if no signature found', () => { + const matches = disqusCommentsDOMQuery(); + + expect(matches).toHaveLength(0); + expect(Array.isArray(matches)).toBe(true); + }); +}); diff --git a/packages/library-detection/src/libraries/fb-comments/accordion.tsx b/packages/library-detection/src/libraries/fb-comments/accordion.tsx new file mode 100644 index 000000000..fc14b5f83 --- /dev/null +++ b/packages/library-detection/src/libraries/fb-comments/accordion.tsx @@ -0,0 +1,50 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * External dependencies. + */ +import React from 'react'; + +/** + * Internal dependencies. + */ +import { Accordion, DetectionMessage } from '../../components'; +import type { AccordionProps } from '../../types'; +import { FB_COMMENTS_HELP_URL } from './constants'; + +const FBCommentsAccordion = ({ domQueryMatches }: AccordionProps) => { + if (!domQueryMatches) { + return null; + } + + const featuresCount = domQueryMatches.length; + + if (!featuresCount) { + return null; + } + + return ( + + + + ); +}; + +export default FBCommentsAccordion; diff --git a/packages/library-detection/src/libraries/fb-comments/constants.ts b/packages/library-detection/src/libraries/fb-comments/constants.ts new file mode 100644 index 000000000..1e54876b4 --- /dev/null +++ b/packages/library-detection/src/libraries/fb-comments/constants.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export const FB_COMMENTS_HELP_URL = 'https://developers.facebook.com/support/'; diff --git a/packages/library-detection/src/libraries/fb-comments/fbCommentsDOMQuery.ts b/packages/library-detection/src/libraries/fb-comments/fbCommentsDOMQuery.ts new file mode 100644 index 000000000..cd553cfd4 --- /dev/null +++ b/packages/library-detection/src/libraries/fb-comments/fbCommentsDOMQuery.ts @@ -0,0 +1,40 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const fbCommentsDOMQuery = () => { + const matchItems: string[] = []; + const root = document.getElementById('fb-root'); + + const commentClass = document.querySelector('.fb-comments'); + const iframeRegex = + /^https:\/\/www\.facebook\.com\/v\d+\.\d+\/plugins\/comments\.php/; + + if (root && commentClass) { + matchItems.push('div[id]: fb-root'); + matchItems.push('div[class]: fb-comments'); + const iframes = document.querySelectorAll('iframe'); + + iframes.forEach((iframe) => { + if (iframe.src && iframeRegex.test(iframe.src)) { + matchItems.push(`iframe[src]: ${iframe.src}`); + } + }); + } + + return matchItems; +}; + +export default fbCommentsDOMQuery; diff --git a/packages/library-detection/src/libraries/fb-comments/index.ts b/packages/library-detection/src/libraries/fb-comments/index.ts new file mode 100644 index 000000000..5566a2611 --- /dev/null +++ b/packages/library-detection/src/libraries/fb-comments/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export { default as FBCommentsAccordion } from './accordion'; +export { default as fbCommentsDOMQuery } from './fbCommentsDOMQuery'; +export * from './constants'; diff --git a/packages/library-detection/src/libraries/fb-comments/tests/accordion.tsx b/packages/library-detection/src/libraries/fb-comments/tests/accordion.tsx new file mode 100644 index 000000000..d88fc3247 --- /dev/null +++ b/packages/library-detection/src/libraries/fb-comments/tests/accordion.tsx @@ -0,0 +1,79 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * External dependencies. + */ +import React from 'react'; +import { render, screen, fireEvent } from '@testing-library/react'; +import '@testing-library/jest-dom'; + +/** + * Internal dependencies. + */ +import FacebookCommentsAccordion from '../accordion'; + +describe('Facebook Comments Accordion', () => { + const accordionTitleText = 'Facebook Comments.'; + const accordionMessageText = + 'Facebook comments plugin functionality may not work properly due to the phaseout of third-party cookies. For more information, please visit theFacebook support forum.'; + + it('should show accordion', () => { + const domQueryMatches = ['']; + render(); + + const accordion = screen.getByTestId('library-detection-accordion'); + const accordionTitle = screen.getByText(accordionTitleText); + + expect(accordion).toBeInTheDocument(); + expect(accordionTitle).toBeInTheDocument(); + }); + + it('should not show accordion', () => { + const domQueryMatches = null; + render(); + + const accordion = screen.queryByTestId('library-detection-accordion'); + const accordionTitle = screen.queryByText(accordionTitleText); + + expect(accordion).not.toBeInTheDocument(); + expect(accordionTitle).not.toBeInTheDocument(); + }); + + it('should toggle accordion message after clicking the accordion', () => { + const domQueryMatches = [ + 'div[id]: fb-root', + 'div[class]: fb-comments', + 'iframe[src]: https://www.facebook.com/1.0/plugins/comments.php/123', + ]; + + render(); + + const accordion = screen.getByTestId('library-detection-accordion'); + const accordionTitle = screen.getByText(accordionTitleText); + + expect(accordion).not.toHaveTextContent(accordionMessageText); + + // Click the accordion + fireEvent.click(accordionTitle); + + expect(accordion).toHaveTextContent(accordionMessageText); + + // Click the accordion + fireEvent.click(accordionTitle); + + expect(accordion).not.toHaveTextContent(accordionMessageText); + }); +}); diff --git a/packages/library-detection/src/libraries/fb-comments/tests/fbCommentsDOMQuery.ts b/packages/library-detection/src/libraries/fb-comments/tests/fbCommentsDOMQuery.ts new file mode 100644 index 000000000..43dcf4992 --- /dev/null +++ b/packages/library-detection/src/libraries/fb-comments/tests/fbCommentsDOMQuery.ts @@ -0,0 +1,88 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * External dependencies. + */ +import '@testing-library/jest-dom'; + +/** + * Internal dependencies. + */ +import fbCommentsDOMQuery from '../fbCommentsDOMQuery'; + +describe('Facebook Comments DOM Query', () => { + beforeEach(() => { + // Clean up the DOM before each test + document.body.innerHTML = ''; + }); + + it('should return the matches found', () => { + const iframe = document.createElement('iframe'); + iframe.src = + 'https://www.facebook.com/v1.0/plugins/comments.php?app_id=123'; + + const divWithClass = document.createElement('div'); + divWithClass.classList.add('fb-comments'); + + const divWithId = document.createElement('div'); + divWithId.id = 'fb-root'; + + document.body.appendChild(divWithClass); + document.body.appendChild(divWithId); + document.body.appendChild(iframe); + + const matches = fbCommentsDOMQuery(); + + expect(matches).toHaveLength(3); + expect(Array.isArray(matches)).toBe(true); + expect(matches).toContain('div[id]: fb-root'); + expect(matches).toContain('div[class]: fb-comments'); + expect(matches).toContain( + 'iframe[src]: https://www.facebook.com/v1.0/plugins/comments.php?app_id=123' + ); + }); + + it('should return empty array if both id and class not found: 1', () => { + const divWithId = document.createElement('div'); + divWithId.id = 'fb-root'; + + document.body.appendChild(divWithId); + + const matches = fbCommentsDOMQuery(); + + expect(matches).toHaveLength(0); + expect(Array.isArray(matches)).toBe(true); + }); + + it('should return empty array if both id and class not found: 2', () => { + const divWithClass = document.createElement('div'); + divWithClass.classList.add('fb-comments'); + + document.body.appendChild(divWithClass); + + const matches = fbCommentsDOMQuery(); + + expect(matches).toHaveLength(0); + expect(Array.isArray(matches)).toBe(true); + }); + + it('should return empty array if no signature found', () => { + const matches = fbCommentsDOMQuery(); + + expect(matches).toHaveLength(0); + expect(Array.isArray(matches)).toBe(true); + }); +}); diff --git a/packages/library-detection/src/libraries/fb-likes/accordion.tsx b/packages/library-detection/src/libraries/fb-likes/accordion.tsx new file mode 100644 index 000000000..58f95637d --- /dev/null +++ b/packages/library-detection/src/libraries/fb-likes/accordion.tsx @@ -0,0 +1,54 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * External dependencies. + */ +import React from 'react'; + +/** + * Internal dependencies. + */ +import { Accordion, DetectionMessage } from '../../components'; +import type { AccordionProps } from '../../types'; +import { FB_LIKES_HELP_URL } from './constants'; + +const FBLikesAccordion = ({ domQueryMatches }: AccordionProps) => { + if (!domQueryMatches) { + return null; + } + + const featuresCount = domQueryMatches.length; + + if (!featuresCount) { + return null; + } + + return ( + + + + ); +}; + +export default FBLikesAccordion; diff --git a/packages/library-detection/src/libraries/fb-likes/constants.ts b/packages/library-detection/src/libraries/fb-likes/constants.ts new file mode 100644 index 000000000..724925d2a --- /dev/null +++ b/packages/library-detection/src/libraries/fb-likes/constants.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export const FB_LIKES_HELP_URL = 'https://developers.facebook.com/support/'; diff --git a/packages/library-detection/src/libraries/fb-likes/fbLikesDOMQuery.ts b/packages/library-detection/src/libraries/fb-likes/fbLikesDOMQuery.ts new file mode 100644 index 000000000..5eac5f515 --- /dev/null +++ b/packages/library-detection/src/libraries/fb-likes/fbLikesDOMQuery.ts @@ -0,0 +1,42 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const fbLikesDOMQuery = () => { + const matchItems: string[] = []; + + const root = document.getElementById('fb-root'); + const fbLikeClass = document.querySelector('.fb-like'); + + const fbLikeIframeSrcRegex = + /^https:\/\/www\.facebook\.com\/v\d+\.\d+\/plugins\/like\.php/; + + if (root && fbLikeClass) { + matchItems.push('div[id]: fb-root'); + matchItems.push('div[class]: fb-like'); + + const iframes = document.querySelectorAll('iframe'); + + iframes.forEach((iframe) => { + if (iframe.src && fbLikeIframeSrcRegex.test(iframe.src)) { + matchItems.push(`iframe[src]: ${iframe.src}`); + } + }); + } + + return matchItems; +}; + +export default fbLikesDOMQuery; diff --git a/packages/library-detection/src/libraries/fb-likes/index.ts b/packages/library-detection/src/libraries/fb-likes/index.ts new file mode 100644 index 000000000..15ba0f8bd --- /dev/null +++ b/packages/library-detection/src/libraries/fb-likes/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export { default as FBLikesAccordion } from './accordion'; +export { default as fbLikesDOMQuery } from './fbLikesDOMQuery'; +export * from './constants'; diff --git a/packages/library-detection/src/libraries/fb-likes/tests/accordion.tsx b/packages/library-detection/src/libraries/fb-likes/tests/accordion.tsx new file mode 100644 index 000000000..30b508190 --- /dev/null +++ b/packages/library-detection/src/libraries/fb-likes/tests/accordion.tsx @@ -0,0 +1,79 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * External dependencies. + */ +import React from 'react'; +import { render, screen, fireEvent } from '@testing-library/react'; +import '@testing-library/jest-dom'; + +/** + * Internal dependencies. + */ +import FacebookLikesAccordion from '../accordion'; + +describe('Facebook Likes Accordion', () => { + const accordionTitleText = 'Facebook Like Button.'; + const accordionMessageText = + 'Facebook like button functionality may not work properly due to the phaseout of third-party cookies. For more information, please visit theFacebook support forum.'; + + it('should show accordion', () => { + const domQueryMatches = ['']; + render(); + + const accordion = screen.getByTestId('library-detection-accordion'); + const accordionTitle = screen.getByText(accordionTitleText); + + expect(accordion).toBeInTheDocument(); + expect(accordionTitle).toBeInTheDocument(); + }); + + it('should not show accordion', () => { + const domQueryMatches = null; + render(); + + const accordion = screen.queryByTestId('library-detection-accordion'); + const accordionTitle = screen.queryByText(accordionTitleText); + + expect(accordion).not.toBeInTheDocument(); + expect(accordionTitle).not.toBeInTheDocument(); + }); + + it('should toggle accordion message after clicking the accordion', () => { + const domQueryMatches = [ + 'div[id]: fb-root', + 'div[class]: fb-like', + 'iframe[src]: https://www.facebook.com/1.0/plugins/like.php/123', + ]; + + render(); + + const accordion = screen.getByTestId('library-detection-accordion'); + const accordionTitle = screen.getByText(accordionTitleText); + + expect(accordion).not.toHaveTextContent(accordionMessageText); + + // Click the accordion + fireEvent.click(accordionTitle); + + expect(accordion).toHaveTextContent(accordionMessageText); + + // Click the accordion + fireEvent.click(accordionTitle); + + expect(accordion).not.toHaveTextContent(accordionMessageText); + }); +}); diff --git a/packages/library-detection/src/libraries/fb-likes/tests/fbLikesDOMQuery.ts b/packages/library-detection/src/libraries/fb-likes/tests/fbLikesDOMQuery.ts new file mode 100644 index 000000000..548d776c3 --- /dev/null +++ b/packages/library-detection/src/libraries/fb-likes/tests/fbLikesDOMQuery.ts @@ -0,0 +1,87 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * External dependencies. + */ +import '@testing-library/jest-dom'; + +/** + * Internal dependencies. + */ +import fbLikesDOMQuery from '../fbLikesDOMQuery'; + +describe('Facebook Likes DOM Query', () => { + beforeEach(() => { + // Clean up the DOM before each test + document.body.innerHTML = ''; + }); + + it('should return the matches found', () => { + const iframe = document.createElement('iframe'); + iframe.src = 'https://www.facebook.com/v1.0/plugins/like.php?app_id=123'; + + const divWithClass = document.createElement('div'); + divWithClass.classList.add('fb-like'); + + const divWithId = document.createElement('div'); + divWithId.id = 'fb-root'; + + document.body.appendChild(divWithClass); + document.body.appendChild(divWithId); + document.body.appendChild(iframe); + + const matches = fbLikesDOMQuery(); + + expect(matches).toHaveLength(3); + expect(Array.isArray(matches)).toBe(true); + expect(matches).toContain('div[id]: fb-root'); + expect(matches).toContain('div[class]: fb-like'); + expect(matches).toContain( + 'iframe[src]: https://www.facebook.com/v1.0/plugins/like.php?app_id=123' + ); + }); + + it('should return empty array if both id and class not found: 1', () => { + const divWithId = document.createElement('div'); + divWithId.id = 'fb-root'; + + document.body.appendChild(divWithId); + + const matches = fbLikesDOMQuery(); + + expect(matches).toHaveLength(0); + expect(Array.isArray(matches)).toBe(true); + }); + + it('should return empty array if both id and class not found: 2', () => { + const divWithClass = document.createElement('div'); + divWithClass.classList.add('fb-like'); + + document.body.appendChild(divWithClass); + + const matches = fbLikesDOMQuery(); + + expect(matches).toHaveLength(0); + expect(Array.isArray(matches)).toBe(true); + }); + + it('should return empty array if no signature found', () => { + const matches = fbLikesDOMQuery(); + + expect(matches).toHaveLength(0); + expect(Array.isArray(matches)).toBe(true); + }); +}); diff --git a/packages/library-detection/src/libraries/gis/accordion.tsx b/packages/library-detection/src/libraries/gis/accordion.tsx index dea20b30a..aa2183103 100644 --- a/packages/library-detection/src/libraries/gis/accordion.tsx +++ b/packages/library-detection/src/libraries/gis/accordion.tsx @@ -26,7 +26,11 @@ import { Accordion, FeatureList } from '../../components'; import type { AccordionProps } from '../../types'; const GSIAccordion = ({ matches }: AccordionProps) => { - const featuresCount = matches && matches.length ? matches.length : 0; + if (!matches) { + return null; + } + + const featuresCount = matches.length; if (!featuresCount) { return null; diff --git a/packages/library-detection/src/libraries/gis/constants.ts b/packages/library-detection/src/libraries/gis/constants.ts index 2d8e55c46..1b47288af 100644 --- a/packages/library-detection/src/libraries/gis/constants.ts +++ b/packages/library-detection/src/libraries/gis/constants.ts @@ -21,23 +21,8 @@ import { addUTMParams } from '@ps-analysis-tool/common'; /** * Internal dependencies. */ -import type { ExceptionUrls } from '../../types'; +import type { SignaturesConfigItem } from '../../types'; -/* - * Copyright 2023 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ export const GIS_SIGNATURE_WEAK_MATCHES = [ { signature: 'google.accounts.id.prompt(', // XXX add check for optional callback parameter @@ -69,17 +54,10 @@ export const GIS_SIGNATURE_WEAK_MATCHES = [ }, ]; -export const GIS_SIGNATURE_STRONG_MATCHES = []; +export const GIS_SIGNATURE_STRONG_MATCHES: SignaturesConfigItem[] = []; export const GIS_HELP_URL = addUTMParams( 'https://developers.google.com/identity/gsi/web/guides/migration' ); export const GIS_DOMAINS_TO_SKIP = ['accounts.google.com', 'gstatic.com']; - -export const GIS_EXCEPTIONS: ExceptionUrls = { - 'cnn.com': { - signatures: ['isDisplayed('], - subDomains: ['edition'], - }, -}; diff --git a/packages/library-detection/src/libraries/gis/checkForGIS.ts b/packages/library-detection/src/libraries/gis/getGISMatches.ts similarity index 77% rename from packages/library-detection/src/libraries/gis/checkForGIS.ts rename to packages/library-detection/src/libraries/gis/getGISMatches.ts index d5c53dda3..a36d9fdf4 100644 --- a/packages/library-detection/src/libraries/gis/checkForGIS.ts +++ b/packages/library-detection/src/libraries/gis/getGISMatches.ts @@ -32,18 +32,18 @@ import { /** * Checks for Google Identity Services api signatures. - * @param script - The script tag to check. - * @param existingItems - The existing items to check against. + * @param script - The script to parse for matching. + * @param matches - The existing items to check against. * @param signatureMatches - The number of signature matches. * @returns The number of signature matches and the items. */ -const checkForGIS = ( +const getGISMatches = ( script: ScriptTagUnderCheck, - existingItems: DetectedSignature[], + matches: DetectedSignature[], signatureMatches: number ) => { const content = script.content; - let items = existingItems; + let items = matches; if (!content) { // this case if no network request is present @@ -53,19 +53,23 @@ const checkForGIS = ( }; } - const gisSignatures = [ + const signatures = [ ...GIS_SIGNATURE_WEAK_MATCHES, ...GIS_SIGNATURE_STRONG_MATCHES, ].map((item) => item.signature); - const captureGroup = gisSignatures.map(escapeStringRegexp); + const captureGroup = signatures.map(escapeStringRegexp); + + const strongSignatures = GIS_SIGNATURE_STRONG_MATCHES.map( + (item) => item.signature + ); const allCaptureGroups = '(?:.{0,63}?)(?' + captureGroup.join('|') + ')(?:.{0,63}?)'; - const reSignatures = new RegExp(allCaptureGroups, 'dg'); + const signaturesRegex = new RegExp(allCaptureGroups, 'dg'); - for (const match of content.matchAll(reSignatures)) { + for (const match of content.matchAll(signaturesRegex)) { if (!match.groups || !('signature' in match.groups)) { continue; } @@ -111,8 +115,17 @@ const checkForGIS = ( } } + const signatureOfDetectedMatches = items.map((item) => item.feature.text); + + const isStrongSignatureFound = signatureOfDetectedMatches.some((signature) => + strongSignatures.includes(signature) + ); + // Audit step - if (signatureMatches === 0) { + if ( + signatureMatches === 0 || + (!isStrongSignatureFound && signatureMatches < 2) + ) { items = []; } @@ -122,4 +135,4 @@ const checkForGIS = ( }; }; -export default checkForGIS; +export default getGISMatches; diff --git a/packages/library-detection/src/libraries/gis/index.ts b/packages/library-detection/src/libraries/gis/index.ts new file mode 100644 index 000000000..b17f1804d --- /dev/null +++ b/packages/library-detection/src/libraries/gis/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export { default as GISAccordion } from './accordion'; +export { default as getGISMatches } from './getGISMatches'; +export * from './constants'; diff --git a/packages/library-detection/src/libraries/gsi/accordion.tsx b/packages/library-detection/src/libraries/gsi/accordion.tsx index 640ab08a7..72d85d588 100644 --- a/packages/library-detection/src/libraries/gsi/accordion.tsx +++ b/packages/library-detection/src/libraries/gsi/accordion.tsx @@ -26,7 +26,11 @@ import { Accordion, FeatureList } from '../../components'; import type { AccordionProps } from '../../types'; const GISAccordion = ({ matches }: AccordionProps) => { - const featuresCount = matches && matches.length ? matches.length : 0; + if (!matches) { + return null; + } + + const featuresCount = matches.length; if (!featuresCount) { return null; diff --git a/packages/library-detection/src/libraries/gsi/constants.ts b/packages/library-detection/src/libraries/gsi/constants.ts index 2edf1a536..e38425938 100644 --- a/packages/library-detection/src/libraries/gsi/constants.ts +++ b/packages/library-detection/src/libraries/gsi/constants.ts @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import type { ExceptionUrls } from '../../types'; +import type { SignaturesConfigItem } from '../../types'; /* * Copyright 2023 Google LLC @@ -30,7 +30,7 @@ import type { ExceptionUrls } from '../../types'; * See the License for the specific language governing permissions and * limitations under the License. */ -export const GSI_V2_SIGNATURE_WEAK_MATCHES = [ +export const GSI_V2_SIGNATURE_WEAK_MATCHES: SignaturesConfigItem[] = [ /* These signatures indicate use of OpenID Connect ID token for user sign-in, * link to the user authentication migration guide for user sign-in. */ { @@ -170,7 +170,7 @@ export const GSI_V2_SIGNATURE_WEAK_MATCHES = [ }, ]; -export const GSI_V2_SIGNATURE_STRONG_MATCHES = [ +export const GSI_V2_SIGNATURE_STRONG_MATCHES: SignaturesConfigItem[] = [ { signature: 'gapi.auth2', helpUrl: 'https://developers.google.com/identity/gsi/web/guides/migration', @@ -180,6 +180,4 @@ export const GSI_V2_SIGNATURE_STRONG_MATCHES = [ export const GSI_HELP_URL = 'https://developers.google.com/identity/gsi/web/guides/migration'; -export const GSI_DOMAINS_TO_SKIP = ['accounts.google.com', 'gstatic.com']; - -export const GSIv2_EXCEPTIONS: ExceptionUrls = {}; +export const GSI_V2_DOMAINS_TO_SKIP = ['accounts.google.com', 'gstatic.com']; diff --git a/packages/library-detection/src/libraries/gsi/generateGSIV2Matches.ts b/packages/library-detection/src/libraries/gsi/generateGSIV2Matches.ts deleted file mode 100644 index 3e4ea965a..000000000 --- a/packages/library-detection/src/libraries/gsi/generateGSIV2Matches.ts +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2023 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** - * Internal dependencies. - */ -import { GSI_V2_SIGNATURE_STRONG_MATCHES } from './constants'; -import type { DetectedSignature } from '../../types'; - -/** - * Generates the Google Sign-In v2 api matches. - * @param signatureMatches - The number of signature matches. - * @param matches - The existing matches. - * @param gsiV2ModuleMatch - The number of module matches. - * @returns The matches. - */ -const generateGSIV2Matches = ( - signatureMatches: number, - matches: DetectedSignature[], - gsiV2ModuleMatch: number -) => { - let strongMatch = false; - - const strongMatchObject = - matches.find((item) => - GSI_V2_SIGNATURE_STRONG_MATCHES.find( - (strongMatchItem) => item.feature.text === strongMatchItem.signature - ) - ) || {}; - - const moduleMatches = gsiV2ModuleMatch; // TODO: change this with network parsing - - if (Object.keys(strongMatchObject).length > 0) { - strongMatch = true; - } - - /* At least one JS object or method signature match along with a gapi.auth2 module match - * is necessary to report the audit as failed. - */ - if (!((signatureMatches > 0 && moduleMatches > 0) || strongMatch === true)) { - matches = []; - } - - return matches; -}; - -export default generateGSIV2Matches; diff --git a/packages/library-detection/src/libraries/gsi/checkForGSIv2.ts b/packages/library-detection/src/libraries/gsi/getGSIV2Matches.ts similarity index 77% rename from packages/library-detection/src/libraries/gsi/checkForGSIv2.ts rename to packages/library-detection/src/libraries/gsi/getGSIV2Matches.ts index c4ed95613..211165dce 100644 --- a/packages/library-detection/src/libraries/gsi/checkForGSIv2.ts +++ b/packages/library-detection/src/libraries/gsi/getGSIV2Matches.ts @@ -33,19 +33,19 @@ import { /** * Checks for Google Sign-In v2 api signatures. * @param script - The script tag to check. - * @param existingItems - The existing items to check against. + * @param matches - The existing items to check against. * @param signatureMatches - The number of signature matches. * @param gsi2ModuleMatch - The number of module matches. * @returns The number of signature matches and the items. */ -const checkForGSIv2 = ( +const getGSIV2Matches = ( script: ScriptTagUnderCheck, - existingItems: DetectedSignature[], + matches: DetectedSignature[], signatureMatches: number, gsi2ModuleMatch: number ) => { const content = script.content; - const items = existingItems; + let items = matches; const domainPaths = { 'apis.google.com': ['/js/platform.js', '/js/api:client.js', '/js/api.js'], @@ -67,19 +67,24 @@ const checkForGSIv2 = ( }; } - const gsiSignatures = [ - ...GSI_V2_SIGNATURE_WEAK_MATCHES.map((item) => item.signature), - ...GSI_V2_SIGNATURE_STRONG_MATCHES.map((item) => item.signature), - ]; + const signatures = [ + ...GSI_V2_SIGNATURE_WEAK_MATCHES, + ...GSI_V2_SIGNATURE_STRONG_MATCHES, + ].map((item) => item.signature); + + const strongSignatures = GSI_V2_SIGNATURE_STRONG_MATCHES.map( + (item) => item.signature + ); + /* Match all signatures in the capture group and return surrounding the text: * /(?:.{0,63}?)(signature1|signature2|signature3)(?:.{0,63}?)/g */ - const captureGroup = gsiSignatures.map(escapeStringRegexp); + const captureGroup = signatures.map(escapeStringRegexp); const allCaptureGroups = '(?:.{0,63}?)(?' + captureGroup.join('|') + ')(?:.{0,63}?)'; - const reSignatures = new RegExp(allCaptureGroups, 'dg'); + const signaturesRegex = new RegExp(allCaptureGroups, 'dg'); - for (const match of content.matchAll(reSignatures)) { + for (const match of content.matchAll(signaturesRegex)) { if (!match.groups || !('signature' in match.groups)) { continue; } @@ -125,6 +130,22 @@ const checkForGSIv2 = ( } } + const signatureOfDetectedMatches = items.map((item) => { + return item.feature.text; + }); + + const isStrongSignatureFound = signatureOfDetectedMatches.some( + (signature) => { + return strongSignatures.includes(signature); + } + ); + + // Reset if weak matches are less than one. + if (!isStrongSignatureFound && items.length < 2) { + // Clear array of signatures + items = []; + } + return { signatureMatches, matches: items, @@ -132,4 +153,4 @@ const checkForGSIv2 = ( }; }; -export default checkForGSIv2; +export default getGSIV2Matches; diff --git a/packages/library-detection/src/libraries/gsi/index.ts b/packages/library-detection/src/libraries/gsi/index.ts new file mode 100644 index 000000000..aa33daddc --- /dev/null +++ b/packages/library-detection/src/libraries/gsi/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export { default as GSIAccordion } from './accordion'; +export { default as getGSIV2Matches } from './getGSIV2Matches'; +export * from './constants'; diff --git a/packages/library-detection/src/libraries/jetpack-comments/accordion.tsx b/packages/library-detection/src/libraries/jetpack-comments/accordion.tsx new file mode 100644 index 000000000..599a5b43b --- /dev/null +++ b/packages/library-detection/src/libraries/jetpack-comments/accordion.tsx @@ -0,0 +1,50 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * External dependencies. + */ +import React from 'react'; + +/** + * Internal dependencies. + */ +import { Accordion, DetectionMessage } from '../../components'; +import type { AccordionProps } from '../../types'; +import { JETPACK_COMMENTS_HELP_URL } from './constants'; + +const JetpackCommentsAccordion = ({ domQueryMatches }: AccordionProps) => { + if (!domQueryMatches) { + return null; + } + + const featuresCount = domQueryMatches.length; + + if (!featuresCount) { + return null; + } + + return ( + + + + ); +}; + +export default JetpackCommentsAccordion; diff --git a/packages/library-detection/src/libraries/jetpack-comments/constants.ts b/packages/library-detection/src/libraries/jetpack-comments/constants.ts new file mode 100644 index 000000000..cfbfcabe0 --- /dev/null +++ b/packages/library-detection/src/libraries/jetpack-comments/constants.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export const JETPACK_COMMENTS_HELP_URL = 'https://jetpack.com/support/'; diff --git a/packages/library-detection/src/libraries/jetpack-comments/index.ts b/packages/library-detection/src/libraries/jetpack-comments/index.ts new file mode 100644 index 000000000..eea336760 --- /dev/null +++ b/packages/library-detection/src/libraries/jetpack-comments/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export { default as JetpackCommentsAccordion } from './accordion'; +export { default as jetpackCommentsDOMQuery } from './jetpackCommentsDOMQuery'; +export * from './constants'; diff --git a/packages/library-detection/src/libraries/jetpack-comments/jetpackCommentsDOMQuery.ts b/packages/library-detection/src/libraries/jetpack-comments/jetpackCommentsDOMQuery.ts new file mode 100644 index 000000000..83c8198ee --- /dev/null +++ b/packages/library-detection/src/libraries/jetpack-comments/jetpackCommentsDOMQuery.ts @@ -0,0 +1,45 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const jetpackCommentsDOMQuery = () => { + const matchItems: string[] = []; + + const jetpackIframeAttrValue = 'jetpack_remote_comment'; + + const jetpackCommentIframeSrcRegex = + /^https:\/\/jetpack\.wordpress\.com\/jetpack-comment/; + + const iframes = document.querySelectorAll('iframe'); + + iframes.forEach((iframe) => { + if ( + iframe.src && + jetpackCommentIframeSrcRegex.test(iframe.src) && + iframe.name === jetpackIframeAttrValue && + iframe.id === jetpackIframeAttrValue && + iframe.getAttribute('class') === jetpackIframeAttrValue + ) { + matchItems.push('iframe[class]: jetpack_remote_comment'); + matchItems.push('iframe[name]: jetpack_remote_comment'); + matchItems.push('iframe[id]: jetpack_remote_comment'); + matchItems.push(`iframe[src]: ${iframe.src}`); + } + }); + + return matchItems; +}; + +export default jetpackCommentsDOMQuery; diff --git a/packages/library-detection/src/libraries/jetpack-comments/tests/accordion.tsx b/packages/library-detection/src/libraries/jetpack-comments/tests/accordion.tsx new file mode 100644 index 000000000..2fd57ea13 --- /dev/null +++ b/packages/library-detection/src/libraries/jetpack-comments/tests/accordion.tsx @@ -0,0 +1,75 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * External dependencies. + */ +import React from 'react'; +import { render, screen, fireEvent } from '@testing-library/react'; +import '@testing-library/jest-dom'; + +/** + * Internal dependencies. + */ +import JetpackCommentsAccordion from '../accordion'; + +describe('Jetpack Comments Accordion', () => { + const accordionTitleText = 'Jetpack Comments.'; + const accordionMessageText = + 'Jetpack comments widget functionality may not work properly due to the phaseout of third-party cookies. For more information, please visit theJetpack support forum.'; + + it('should show accordion', () => { + const domQueryMatches = ['']; + render(); + + const accordion = screen.getByTestId('library-detection-accordion'); + const accordionTitle = screen.getByText(accordionTitleText); + + expect(accordion).toBeInTheDocument(); + expect(accordionTitle).toBeInTheDocument(); + }); + + it('should not show accordion', () => { + const domQueryMatches = null; + render(); + + const accordion = screen.queryByTestId('library-detection-accordion'); + const accordionTitle = screen.queryByText(accordionTitleText); + + expect(accordion).not.toBeInTheDocument(); + expect(accordionTitle).not.toBeInTheDocument(); + }); + + it('should toggle accordion message after clicking the accordion', () => { + const domQueryMatches = ['iframe[id]: jetpack_remote_comment']; + + render(); + + const accordion = screen.getByTestId('library-detection-accordion'); + const accordionTitle = screen.getByText(accordionTitleText); + + expect(accordion).not.toHaveTextContent(accordionMessageText); + + // Click the accordion + fireEvent.click(accordionTitle); + + expect(accordion).toHaveTextContent(accordionMessageText); + + // Click the accordion + fireEvent.click(accordionTitle); + + expect(accordion).not.toHaveTextContent(accordionMessageText); + }); +}); diff --git a/packages/library-detection/src/libraries/jetpack-comments/tests/jetpackCommentsDOMQuery.ts b/packages/library-detection/src/libraries/jetpack-comments/tests/jetpackCommentsDOMQuery.ts new file mode 100644 index 000000000..fe460782d --- /dev/null +++ b/packages/library-detection/src/libraries/jetpack-comments/tests/jetpackCommentsDOMQuery.ts @@ -0,0 +1,59 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * External dependencies. + */ +import '@testing-library/jest-dom'; + +/** + * Internal dependencies. + */ +import jetpackCommentsDOMQuery from '../jetpackCommentsDOMQuery'; + +describe('Jetpack Comments DOM Query', () => { + beforeEach(() => { + // Clean up the DOM before each test + document.body.innerHTML = ''; + }); + + it('should return the matches found', () => { + const iframe = document.createElement('iframe'); + iframe.id = 'jetpack_remote_comment'; + iframe.name = 'jetpack_remote_comment'; + iframe.classList.add('jetpack_remote_comment'); + iframe.src = 'https://jetpack.wordpress.com/jetpack-comment/abc'; + + document.body.appendChild(iframe); + + const matches = jetpackCommentsDOMQuery(); + + expect(matches).toHaveLength(4); + expect(Array.isArray(matches)).toBe(true); + expect(matches).toContain('iframe[id]: jetpack_remote_comment'); + expect(matches).toContain('iframe[name]: jetpack_remote_comment'); + expect(matches).toContain('iframe[class]: jetpack_remote_comment'); + expect(matches).toContain( + 'iframe[src]: https://jetpack.wordpress.com/jetpack-comment/abc' + ); + }); + + it('should return empty array if no signature found', () => { + const matches = jetpackCommentsDOMQuery(); + + expect(matches).toHaveLength(0); + expect(Array.isArray(matches)).toBe(true); + }); +}); diff --git a/packages/library-detection/src/libraries/jetpack-comments/tests/jetpackLikesDOMQuery.ts b/packages/library-detection/src/libraries/jetpack-comments/tests/jetpackLikesDOMQuery.ts new file mode 100644 index 000000000..fe460782d --- /dev/null +++ b/packages/library-detection/src/libraries/jetpack-comments/tests/jetpackLikesDOMQuery.ts @@ -0,0 +1,59 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * External dependencies. + */ +import '@testing-library/jest-dom'; + +/** + * Internal dependencies. + */ +import jetpackCommentsDOMQuery from '../jetpackCommentsDOMQuery'; + +describe('Jetpack Comments DOM Query', () => { + beforeEach(() => { + // Clean up the DOM before each test + document.body.innerHTML = ''; + }); + + it('should return the matches found', () => { + const iframe = document.createElement('iframe'); + iframe.id = 'jetpack_remote_comment'; + iframe.name = 'jetpack_remote_comment'; + iframe.classList.add('jetpack_remote_comment'); + iframe.src = 'https://jetpack.wordpress.com/jetpack-comment/abc'; + + document.body.appendChild(iframe); + + const matches = jetpackCommentsDOMQuery(); + + expect(matches).toHaveLength(4); + expect(Array.isArray(matches)).toBe(true); + expect(matches).toContain('iframe[id]: jetpack_remote_comment'); + expect(matches).toContain('iframe[name]: jetpack_remote_comment'); + expect(matches).toContain('iframe[class]: jetpack_remote_comment'); + expect(matches).toContain( + 'iframe[src]: https://jetpack.wordpress.com/jetpack-comment/abc' + ); + }); + + it('should return empty array if no signature found', () => { + const matches = jetpackCommentsDOMQuery(); + + expect(matches).toHaveLength(0); + expect(Array.isArray(matches)).toBe(true); + }); +}); diff --git a/packages/library-detection/src/libraries/jetpack-likes/accordion.tsx b/packages/library-detection/src/libraries/jetpack-likes/accordion.tsx new file mode 100644 index 000000000..e59618ea2 --- /dev/null +++ b/packages/library-detection/src/libraries/jetpack-likes/accordion.tsx @@ -0,0 +1,50 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * External dependencies. + */ +import React from 'react'; + +/** + * Internal dependencies. + */ +import { Accordion, DetectionMessage } from '../../components'; +import type { AccordionProps } from '../../types'; +import { JETPACK_LIKES_HELP_URL } from './constants'; + +const JetpackLikesAccordion = ({ domQueryMatches }: AccordionProps) => { + if (!domQueryMatches) { + return null; + } + + const featuresCount = domQueryMatches.length; + + if (!featuresCount) { + return null; + } + + return ( + + + + ); +}; + +export default JetpackLikesAccordion; diff --git a/packages/library-detection/src/libraries/jetpack-likes/constants.ts b/packages/library-detection/src/libraries/jetpack-likes/constants.ts new file mode 100644 index 000000000..6a2422b21 --- /dev/null +++ b/packages/library-detection/src/libraries/jetpack-likes/constants.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export const JETPACK_LIKES_HELP_URL = 'https://jetpack.com/support/'; diff --git a/packages/library-detection/src/libraries/jetpack-likes/index.ts b/packages/library-detection/src/libraries/jetpack-likes/index.ts new file mode 100644 index 000000000..3fc8f29cc --- /dev/null +++ b/packages/library-detection/src/libraries/jetpack-likes/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export { default as JetpackLikesAccordion } from './accordion'; +export { default as jetpackLikesDOMQuery } from './jetpackLikesDOMQuery'; +export * from './constants'; diff --git a/packages/library-detection/src/libraries/jetpack-likes/jetpackLikesDOMQuery.ts b/packages/library-detection/src/libraries/jetpack-likes/jetpackLikesDOMQuery.ts new file mode 100644 index 000000000..4ef5cf4df --- /dev/null +++ b/packages/library-detection/src/libraries/jetpack-likes/jetpackLikesDOMQuery.ts @@ -0,0 +1,40 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const jetpackLikesDOMQuery = () => { + const matchItems: string[] = []; + + const frameSrcRegex = /^https:\/\/widgets\.wp\.com\/likes/; + + const iframes = document.querySelectorAll('iframe'); + + iframes.forEach((iframe) => { + const name = iframe.getAttribute('name'); + + if ( + iframe.src && + frameSrcRegex.test(iframe.src) && + name?.startsWith('like-comment-frame') + ) { + matchItems.push(`iframe[name]: ${name}`); + matchItems.push(`iframe[src]: ${iframe.src}`); + } + }); + + return matchItems; +}; + +export default jetpackLikesDOMQuery; diff --git a/packages/library-detection/src/libraries/reCaptcha/accordion.tsx b/packages/library-detection/src/libraries/reCaptcha/accordion.tsx new file mode 100644 index 000000000..2cdd36940 --- /dev/null +++ b/packages/library-detection/src/libraries/reCaptcha/accordion.tsx @@ -0,0 +1,50 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * External dependencies. + */ +import React from 'react'; + +/** + * Internal dependencies. + */ +import { Accordion, DetectionMessage } from '../../components'; +import type { AccordionProps } from '../../types'; +import { RECAPTCHA_HELP_URL } from './constants'; + +const ReCaptchaAccordion = ({ domQueryMatches }: AccordionProps) => { + if (!domQueryMatches) { + return null; + } + + const featuresCount = domQueryMatches.length; + + if (!featuresCount) { + return null; + } + + return ( + + + + ); +}; + +export default ReCaptchaAccordion; diff --git a/packages/library-detection/src/libraries/reCaptcha/constants.ts b/packages/library-detection/src/libraries/reCaptcha/constants.ts new file mode 100644 index 000000000..a64f5c206 --- /dev/null +++ b/packages/library-detection/src/libraries/reCaptcha/constants.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export const RECAPTCHA_HELP_URL = 'https://support.google.com/recaptcha/'; diff --git a/packages/library-detection/src/libraries/reCaptcha/index.ts b/packages/library-detection/src/libraries/reCaptcha/index.ts new file mode 100644 index 000000000..b7f0169e7 --- /dev/null +++ b/packages/library-detection/src/libraries/reCaptcha/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export { default as ReCaptchaAccordion } from './accordion'; +export { default as reCaptchaDOMQuery } from './reCaptchaDOMQuery'; +export * from './constants'; diff --git a/packages/library-detection/src/libraries/reCaptcha/reCaptchaDOMQuery.ts b/packages/library-detection/src/libraries/reCaptcha/reCaptchaDOMQuery.ts new file mode 100644 index 000000000..ac1feecb8 --- /dev/null +++ b/packages/library-detection/src/libraries/reCaptcha/reCaptchaDOMQuery.ts @@ -0,0 +1,40 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const reCaptchaDOMQuery = () => { + const matchItems: string[] = []; + + const reCaptchaClass = document.querySelector('.g-recaptcha'); + + const reCaptchaScriptSrcRegex = /^https:\/\/www\.google\.com\/recaptcha/; + + const scripts = document.querySelectorAll('script'); + + scripts.forEach((script) => { + if (script.src && reCaptchaScriptSrcRegex.test(script.src)) { + matchItems.push(`script[src]: ${script.src}`); + } + }); + + if (matchItems.length && reCaptchaClass) { + const reCaptchaTag = reCaptchaClass.tagName.toLowerCase(); + matchItems.push(`${reCaptchaTag}[class]: g-recaptcha`); + } + + return matchItems; +}; + +export default reCaptchaDOMQuery; diff --git a/packages/library-detection/src/libraries/reCaptcha/tests/accordion.tsx b/packages/library-detection/src/libraries/reCaptcha/tests/accordion.tsx new file mode 100644 index 000000000..0196d0259 --- /dev/null +++ b/packages/library-detection/src/libraries/reCaptcha/tests/accordion.tsx @@ -0,0 +1,78 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * External dependencies. + */ +import React from 'react'; +import { render, screen, fireEvent } from '@testing-library/react'; +import '@testing-library/jest-dom'; + +/** + * Internal dependencies. + */ +import ReCaptchaAccordion from '../accordion'; + +describe('reCAPTCHA Accordion', () => { + const accordionTitleText = 'reCAPTCHA.'; + const accordionMessageText = + 'reCAPTCHA functionality may not work properly due to the phaseout of third-party cookies. For more information, please visit thereCAPTCHA support forum.'; + + it('should show accordion', () => { + const domQueryMatches = ['']; + render(); + + const accordion = screen.getByTestId('library-detection-accordion'); + const accordionTitle = screen.getByText(accordionTitleText); + + expect(accordion).toBeInTheDocument(); + expect(accordionTitle).toBeInTheDocument(); + }); + + it('should not show accordion', () => { + const domQueryMatches = null; + render(); + + const accordion = screen.queryByTestId('library-detection-accordion'); + const accordionTitle = screen.queryByText(accordionTitleText); + + expect(accordion).not.toBeInTheDocument(); + expect(accordionTitle).not.toBeInTheDocument(); + }); + + it('should toggle accordion message after clicking the accordion', () => { + const domQueryMatches = [ + 'div[class]: g-recaptcha', + 'script[src]: https://www.google.com/recaptcha/api.js', + ]; + + render(); + + const accordion = screen.getByTestId('library-detection-accordion'); + const accordionTitle = screen.getByText(accordionTitleText); + + expect(accordion).not.toHaveTextContent(accordionMessageText); + + // Click the accordion + fireEvent.click(accordionTitle); + + expect(accordion).toHaveTextContent(accordionMessageText); + + // Click the accordion + fireEvent.click(accordionTitle); + + expect(accordion).not.toHaveTextContent(accordionMessageText); + }); +}); diff --git a/packages/library-detection/src/libraries/reCaptcha/tests/reCaptchaDOMQuery.ts b/packages/library-detection/src/libraries/reCaptcha/tests/reCaptchaDOMQuery.ts new file mode 100644 index 000000000..8cf3671cb --- /dev/null +++ b/packages/library-detection/src/libraries/reCaptcha/tests/reCaptchaDOMQuery.ts @@ -0,0 +1,82 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * External dependencies. + */ +import '@testing-library/jest-dom'; + +/** + * Internal dependencies. + */ +import reCaptchaDOMQuery from '../reCaptchaDOMQuery'; + +describe('Disqus Comments DOM Query', () => { + beforeEach(() => { + // Clean up the DOM before each test + document.body.innerHTML = ''; + }); + + it('should return the matches found', () => { + const script = document.createElement('script'); + script.src = 'https://www.google.com/recaptcha/api.js'; + + const div = document.createElement('div'); + div.classList.add('g-recaptcha'); + + document.body.appendChild(script); + document.body.appendChild(div); + + const matches = reCaptchaDOMQuery(); + + expect(matches).toHaveLength(2); + expect(Array.isArray(matches)).toBe(true); + expect(matches).toContain('div[class]: g-recaptcha'); + expect(matches).toContain( + 'script[src]: https://www.google.com/recaptcha/api.js' + ); + }); + + it('should detect library if script signature is found', () => { + const script = document.createElement('script'); + script.src = 'https://www.google.com/recaptcha/api.js'; + + document.body.appendChild(script); + + const matches = reCaptchaDOMQuery(); + + expect(matches).toHaveLength(1); + expect(Array.isArray(matches)).toBe(true); + }); + + it('should return empty array if script signature is not found', () => { + const div = document.createElement('div'); + div.classList.add('g-recaptcha'); + + document.body.appendChild(div); + + const matches = reCaptchaDOMQuery(); + + expect(matches).toHaveLength(0); + expect(Array.isArray(matches)).toBe(true); + }); + + it('should return empty array if no signature found', () => { + const matches = reCaptchaDOMQuery(); + + expect(matches).toHaveLength(0); + expect(Array.isArray(matches)).toBe(true); + }); +}); From 648c2cfab389a417cc7ac2558c574c6c3cc7f6f9 Mon Sep 17 00:00:00 2001 From: sayedtaqui Date: Tue, 12 Mar 2024 23:38:15 +0530 Subject: [PATCH 07/11] Add libraries to config --- packages/library-detection/src/config.ts | 86 +++++++++++++++++++++--- 1 file changed, 77 insertions(+), 9 deletions(-) diff --git a/packages/library-detection/src/config.ts b/packages/library-detection/src/config.ts index 2bdead76c..60ebaa282 100644 --- a/packages/library-detection/src/config.ts +++ b/packages/library-detection/src/config.ts @@ -18,17 +18,49 @@ */ import { GSIAccordion, - GISAccordion, + getGSIV2Matches, GSI_V2_SIGNATURE_STRONG_MATCHES, GSI_V2_SIGNATURE_WEAK_MATCHES, + GSI_HELP_URL, + GSI_V2_DOMAINS_TO_SKIP, +} from './libraries/gsi'; +import { + GISAccordion, + getGISMatches, GIS_SIGNATURE_WEAK_MATCHES, GIS_HELP_URL, - GSI_HELP_URL, GIS_DOMAINS_TO_SKIP, - GSI_DOMAINS_TO_SKIP, - GSIv2_EXCEPTIONS, - GIS_EXCEPTIONS, -} from './libraries'; +} from './libraries/gis'; +import { + FBCommentsAccordion, + FB_COMMENTS_HELP_URL, + fbCommentsDOMQuery, +} from './libraries/fb-comments'; +import { + FBLikesAccordion, + fbLikesDOMQuery, + FB_LIKES_HELP_URL, +} from './libraries/fb-likes'; +import { + DisqusCommentsAccordion, + disqusCommentsDOMQuery, + DISQUS_COMMENTS_HELP_URL, +} from './libraries/disqus-comments'; +import { + JetpackCommentsAccordion, + jetpackCommentsDOMQuery, + JETPACK_COMMENTS_HELP_URL, +} from './libraries/jetpack-comments'; +import { + JetpackLikesAccordion, + jetpackLikesDOMQuery, + JETPACK_LIKES_HELP_URL, +} from './libraries/jetpack-likes'; +import { + ReCaptchaAccordion, + reCaptchaDOMQuery, + RECAPTCHA_HELP_URL, +} from './libraries/reCaptcha'; const LIBRARIES = [ { @@ -38,9 +70,9 @@ const LIBRARIES = [ strongMatches: GSI_V2_SIGNATURE_STRONG_MATCHES, weakMatches: GSI_V2_SIGNATURE_WEAK_MATCHES, }, - exceptions: GSIv2_EXCEPTIONS, - domainsToSkip: GSI_DOMAINS_TO_SKIP, + domainsToSkip: GSI_V2_DOMAINS_TO_SKIP, helpUrl: GSI_HELP_URL, + detectionFunction: getGSIV2Matches, }, { name: 'gis', @@ -49,9 +81,45 @@ const LIBRARIES = [ strongMatches: [], weakMatches: GIS_SIGNATURE_WEAK_MATCHES, }, - exceptions: GIS_EXCEPTIONS, domainsToSkip: GIS_DOMAINS_TO_SKIP, helpUrl: GIS_HELP_URL, + detectionFunction: getGISMatches, + }, + { + name: 'fb-comments', + component: FBCommentsAccordion, + helpUrl: FB_COMMENTS_HELP_URL, + domQueryFunction: fbCommentsDOMQuery, + }, + { + name: 'fb-likes', + component: FBLikesAccordion, + helpUrl: FB_LIKES_HELP_URL, + domQueryFunction: fbLikesDOMQuery, + }, + { + name: 'disqus-comments', + component: DisqusCommentsAccordion, + helpUrl: DISQUS_COMMENTS_HELP_URL, + domQueryFunction: disqusCommentsDOMQuery, + }, + { + name: 'jetpack-comments', + component: JetpackCommentsAccordion, + helpUrl: JETPACK_COMMENTS_HELP_URL, + domQueryFunction: jetpackCommentsDOMQuery, + }, + { + name: 'jetpack-likes', + component: JetpackLikesAccordion, + helpUrl: JETPACK_LIKES_HELP_URL, + domQueryFunction: jetpackLikesDOMQuery, + }, + { + name: 'reCaptcha', + component: ReCaptchaAccordion, + helpUrl: RECAPTCHA_HELP_URL, + domQueryFunction: reCaptchaDOMQuery, }, ]; From 7ab6ec2a71a3128e9dbc4b32a41e48f8b42e662c Mon Sep 17 00:00:00 2001 From: sayedtaqui Date: Tue, 12 Mar 2024 23:38:33 +0530 Subject: [PATCH 08/11] Update types --- packages/library-detection/src/types.ts | 30 ++++++++----------------- 1 file changed, 9 insertions(+), 21 deletions(-) diff --git a/packages/library-detection/src/types.ts b/packages/library-detection/src/types.ts index 00a243414..c4ee8981b 100644 --- a/packages/library-detection/src/types.ts +++ b/packages/library-detection/src/types.ts @@ -36,11 +36,6 @@ export type Config = { signatures: SignaturesConfig[]; domainsToSkip: string[]; helpUrl: string; - exceptions?: ExceptionUrls; -}; - -export type ExceptionUrls = { - [key: string]: { signatures: string[]; subDomains: string[] }; }; export type ScriptTagUnderCheck = { @@ -70,36 +65,29 @@ export type ResourceTreeItem = { }; export type AccordionProps = { - matches: DetectedSignature[]; + matches?: DetectedSignature[]; + domQueryMatches?: string[] | null; }; export type DomainPaths = { [domain: string]: string[]; }; -export type DetectionSubFunctions = { - gis: ( - arg0: ScriptTagUnderCheck, - arg1: DetectedSignature[], - arg2: number - ) => { - signatureMatches: number; - matches: DetectedSignature[]; - }; - gsiV2: ( +export type DetectionFunctions = { + [key: string]: ( arg0: ScriptTagUnderCheck, - arg1: DetectedSignature[], - arg2: number, - arg3: number + arg1: DetectedSignature[] | undefined, + arg2: number | undefined, + arg3?: number | undefined ) => { signatureMatches: number; matches: DetectedSignature[]; - moduleMatch: number; + moduleMatch?: number; }; }; export type DetectionAuditFunctions = { - gsiV2: ( + [key: string]: ( arg1: number, arg2: DetectedSignature[], arg3: number From e1754160f80a3d9bfbd466e81d3803cc3680a1d4 Mon Sep 17 00:00:00 2001 From: sayedtaqui Date: Tue, 12 Mar 2024 23:39:05 +0530 Subject: [PATCH 09/11] Refactor util exports --- packages/library-detection/src/utils/index.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/library-detection/src/utils/index.ts b/packages/library-detection/src/utils/index.ts index 3459cf4c8..ebdd20874 100644 --- a/packages/library-detection/src/utils/index.ts +++ b/packages/library-detection/src/utils/index.ts @@ -15,6 +15,5 @@ */ export * from './sourceMapGenerator'; -export * from '../libraries/gsi/generateGSIV2Matches'; export * from './getNetworkResourcesWithContent'; export * from './getResourcesWithContent'; From bfe07779dec07624b2e73d0676c02ef2a6b01c3b Mon Sep 17 00:00:00 2001 From: sayedtaqui Date: Tue, 12 Mar 2024 23:39:35 +0530 Subject: [PATCH 10/11] Refactor worker --- .../library-detection/src/worker/index.ts | 23 ++++++++----------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/packages/library-detection/src/worker/index.ts b/packages/library-detection/src/worker/index.ts index 350c6cdaa..300b6c0a2 100644 --- a/packages/library-detection/src/worker/index.ts +++ b/packages/library-detection/src/worker/index.ts @@ -22,8 +22,9 @@ import { LIBRARY_DETECTION_WORKER_TASK } from '@ps-analysis-tool/common'; * Internal dependencies. */ import detectMatchingSignatures from '../core/detectMatchingSignatures'; -import { checkForGIS, checkForGSIv2, generateGSIV2Matches } from '../libraries'; import { type PreDefinedLibraryWorkerTaskPayload } from './constants'; +import LIBRARIES from '../config'; +import type { DetectionFunctions } from '../types'; /** * Library Detection worker function that handles tasks related to library detection. @@ -31,24 +32,18 @@ import { type PreDefinedLibraryWorkerTaskPayload } from './constants'; */ export const ldWorkerOnMessageCallback = (event: MessageEvent): void => { const task: LIBRARY_DETECTION_WORKER_TASK = event.data.task; - const payload: PreDefinedLibraryWorkerTaskPayload[LIBRARY_DETECTION_WORKER_TASK] = + const scripts: PreDefinedLibraryWorkerTaskPayload[LIBRARY_DETECTION_WORKER_TASK] = event.data.payload; + const detectionFunctions = Object.fromEntries( + LIBRARIES.map((library) => [library.name, library.detectionFunction]) + ); + switch (task) { case LIBRARY_DETECTION_WORKER_TASK.DETECT_SIGNATURE_MATCHING: { - const detectionSubFunctions = { - gis: checkForGIS, - gsiV2: checkForGSIv2, - }; - const detectionAuditFunctions = { - gsiV2: generateGSIV2Matches, - }; - const librariesToDetect = ['gis', 'gsiV2']; const detectedMatchingSignatures = detectMatchingSignatures( - librariesToDetect, - payload, - detectionSubFunctions, - detectionAuditFunctions + scripts, + detectionFunctions as DetectionFunctions ); postMessage(detectedMatchingSignatures); break; From 2e088384d6b63def5eea9c137b9478a4c9a60e39 Mon Sep 17 00:00:00 2001 From: sayedtaqui Date: Tue, 12 Mar 2024 23:49:45 +0530 Subject: [PATCH 11/11] Get createContext and useContextSelector from use-context-selector as currently being done in main --- .../library-detection/src/core/stateProvider/index.tsx | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/packages/library-detection/src/core/stateProvider/index.tsx b/packages/library-detection/src/core/stateProvider/index.tsx index e54b1be12..56875c04b 100644 --- a/packages/library-detection/src/core/stateProvider/index.tsx +++ b/packages/library-detection/src/core/stateProvider/index.tsx @@ -22,11 +22,8 @@ import React, { useCallback, useEffect, } from 'react'; -import { - noop, - useContextSelector, - createContext, -} from '@ps-analysis-tool/common'; +import { createContext, useContextSelector } from 'use-context-selector'; +import { noop } from '@ps-analysis-tool/common'; /** * Internal dependencies.