Repository: GoogleChromeLabs/ps-analysis-tool (ts)

Issue:
Calculate Attributes from Cookie Data Objects for Cookies Table UI
## Feature Request

**Description:**
In order to display relevant information in the cookies table, we need to calculate certain attributes for each cookie data object. 
These attributes are crucial for users to understand the characteristics of the cookies they encounter.

**User Story:**
As a developer/website owner/web user concerned about privacy and cookie usage, I want the cookies table to display additional information about each cookie. So that I can make informed decisions and understand the implications of these cookies.

**Acceptance Criteria:**
Before presenting the data in the UI, the following attributes should be pre-calculated for each cookie data object:
- [ ] First Party / Third Party: Determine whether the cookie is set by the first-party website (the website the user is currently visiting) or a third-party domain.
- [ ] IBC Compliance: Check if the cookie complies with Incrementally Better Cookies (IBC) protocol.
- [ ] Cookie Set / Valid Cookie: Verify if the cookie is actively set inside Chrome's Cookie Store.


Pull request:
Refactor: Add cookie attributes to 'isFirstParty', 'isIbcCompliant' and 'isCookieSet' 
## Description

<!-- What do we want to achieve with this PR? -->

- Refactors extension data storage system to add `isFirstParty` flag.
- Refactors state provider of devtools to add flags `isIbcCompliant` and `isCookieSet`.
- Refactors state provider of devtools to react to changes in browser cookie store.
- Refactor `checkIbcCompliance` utility function. 

## Relevant Technical Choices

<!-- Please describe your changes. -->

## Testing Instructions

<!--
How can the changes in this PR be verified?
Please provide step-by-step instructions to test the changes.
-->

## Additional Information:
- Draft for Incrementally better cookies can be found [here](https://datatracker.ietf.org/doc/html/draft-west-cookie-incrementalism-00)
<!-- Any other information. -->

## Screenshot/Screencast

<!-- Please provide Screenshot/Screencast, if applicable -->

---

<!--
Example:

Fixes #123
Partially addresses #22
See #834
-->

Fixes #75
Partially addresses #76 


Patch:
diff --git a/packages/extension/src/localStore/cookieStore.ts b/packages/extension/src/localStore/cookieStore.ts
index e379f6e3..c599c6dc 100644
--- a/packages/extension/src/localStore/cookieStore.ts
+++ b/packages/extension/src/localStore/cookieStore.ts
@@ -76,6 +76,20 @@ const CookieStore = {
     });
   },
 
+  /**
+   * Deletes a cookie
+   * @param {string} cookieName Name of the cookie.
+   */
+  async deleteCookie(cookieName: string) {
+    const storage = await chrome.storage.local.get();
+
+    Object.values(storage).forEach((tabData) => {
+      delete tabData.cookies[cookieName];
+    });
+
+    await chrome.storage.local.set(storage);
+  },
+
   /**
    * Update the focusedAt timestamp for the tab.
    * @param {string} tabId The active tab id.
diff --git a/packages/extension/src/localStore/types.ts b/packages/extension/src/localStore/types.ts
index 386e86a2..d09d162e 100644
--- a/packages/extension/src/localStore/types.ts
+++ b/packages/extension/src/localStore/types.ts
@@ -28,6 +28,7 @@ export type CookieData = {
   analytics: CookieAnalytics | null;
   url: string;
   headerType: 'response' | 'request';
+  isFirstParty: boolean | null;
   frameIdList: number[];
 };
 
diff --git a/packages/extension/src/utils/checkIBCCompliance.ts b/packages/extension/src/utils/checkIbcCompliance.ts
similarity index 54%
rename from packages/extension/src/utils/checkIBCCompliance.ts
rename to packages/extension/src/utils/checkIbcCompliance.ts
index 195a74d8..71068946 100644
--- a/packages/extension/src/utils/checkIBCCompliance.ts
+++ b/packages/extension/src/utils/checkIbcCompliance.ts
@@ -16,19 +16,32 @@
 
 /**
  * Check if cookie is compliant with IBC(Incrementally Better Cookies) proposal.
- * @param samesite SameSite attribute value.
- * @param secure Secure attribute value.
- * @param chromeCookieStoreHasCookie True if cookie is stored in Chrome store.
+ * @param samesite Cookie SameSite attribute
+ * @param secure Cookie secure attribute
+ * @param name Cookie name
+ * @param url Cookie url
  * @returns {boolean} True if cookie is compliant with IBC proposal.
  */
-export function checkIBCCompliance(
+export async function checkIbcCompliance(
   samesite = 'lax',
   secure = false,
-  chromeCookieStoreHasCookie = false
-): boolean {
-  if (samesite.toLowerCase() === 'none') {
-    return secure ? chromeCookieStoreHasCookie : !chromeCookieStoreHasCookie;
-  }
+  name: string,
+  url: string
+): Promise<boolean> {
+  const chromeCookieStoreHasCookie = Boolean(
+    await chrome.cookies.get({
+      name: name,
+      url: url,
+    })
+  );
 
-  return chromeCookieStoreHasCookie;
+  if (samesite.toLowerCase() !== 'none' && chromeCookieStoreHasCookie) {
+    return true;
+  } else if (samesite.toLowerCase() === 'none' && secure) {
+    return chromeCookieStoreHasCookie;
+  } else if (samesite.toLowerCase() === 'none' && !secure) {
+    return !chromeCookieStoreHasCookie;
+  } else {
+    return true;
+  }
 }
diff --git a/packages/extension/src/view/devtools/components/tabContent/cookies/components/cookieDetails.tsx b/packages/extension/src/view/devtools/components/tabContent/cookies/components/cookieDetails.tsx
index 425d89f9..4f781b02 100644
--- a/packages/extension/src/view/devtools/components/tabContent/cookies/components/cookieDetails.tsx
+++ b/packages/extension/src/view/devtools/components/tabContent/cookies/components/cookieDetails.tsx
@@ -24,15 +24,20 @@ import { type Cookie as ParsedCookie } from 'simple-cookie';
  */
 import DomainIcon from '../../../../../../../icons/domain.svg';
 import { type CookieAnalytics } from '../../../../../../utils/fetchCookieDictionary';
-import { checkIBCCompliance } from '../../../../../../utils/checkIBCCompliance';
 
 interface CookieDetailsProps {
   data: ParsedCookie;
   analytics: CookieAnalytics | null;
-  url: string;
+  isIbcCompliant: boolean | null;
+  isCookieSet: boolean | null;
 }
 
-const CookieDetails = ({ data, analytics, url }: CookieDetailsProps) => {
+const CookieDetails = ({
+  data,
+  analytics,
+  isIbcCompliant,
+  isCookieSet,
+}: CookieDetailsProps) => {
   const descriptionRef = useRef<HTMLParagraphElement>(null);
   const valueRef = useRef<HTMLParagraphElement>(null);
 
@@ -44,33 +49,6 @@ const CookieDetails = ({ data, analytics, url }: CookieDetailsProps) => {
     useState<boolean>(false);
   const [showMoreValue, setShowMoreValue] = useState<boolean>(false);
 
-  const [chromeCookieStoreHasCookie, setChromeCookieStoreHasCookie] = useState<
-    boolean | null
-  >(null);
-  const [isCookieIBCCompliant, setisCookieIBCCompliant] = useState<
-    boolean | null
-  >(null);
-
-  useEffect(() => {
-    setChromeCookieStoreHasCookie(null);
-    setisCookieIBCCompliant(null);
-
-    (async () => {
-      const _chromeCookieStoreHasCookie = Boolean(
-        await chrome.cookies.get({ name: data.name, url })
-      );
-      setChromeCookieStoreHasCookie(_chromeCookieStoreHasCookie);
-
-      setisCookieIBCCompliant(
-        checkIBCCompliance(
-          data.samesite,
-          data.secure,
-          _chromeCookieStoreHasCookie
-        )
-      );
-    })();
-  }, [data.name, data.samesite, data.secure, url]);
-
   useEffect(() => {
     setShowMoreDescription(false);
     if (descriptionRef.current) {
@@ -162,12 +140,12 @@ const CookieDetails = ({ data, analytics, url }: CookieDetailsProps) => {
               IBC Compliant
             </abbr>
           </h1>
-          {isCookieIBCCompliant === null ? (
+          {isIbcCompliant === null ? (
             <>Loading...</>
           ) : (
             <>
               <p className="w-3/4 text-xs text-tertiary  truncate">
-                {isCookieIBCCompliant ? (
+                {isIbcCompliant ? (
                   <span className="text-green-500">Yes</span>
                 ) : (
                   <span className="text-red-500">
@@ -186,11 +164,11 @@ const CookieDetails = ({ data, analytics, url }: CookieDetailsProps) => {
               Cookie Set
             </abbr>
           </h1>
-          {chromeCookieStoreHasCookie === null ? (
+          {isCookieSet === null ? (
             <>Loading...</>
           ) : (
             <p className="w-3/4 text-xs text-tertiary truncate">
-              {chromeCookieStoreHasCookie ? (
+              {isCookieSet ? (
                 <span className="text-green-500">Yes</span>
               ) : (
                 <span className="text-red-500">No</span>
diff --git a/packages/extension/src/view/devtools/components/tabContent/cookies/index.tsx b/packages/extension/src/view/devtools/components/tabContent/cookies/index.tsx
index 320f2fc6..cd0d2fc0 100644
--- a/packages/extension/src/view/devtools/components/tabContent/cookies/index.tsx
+++ b/packages/extension/src/view/devtools/components/tabContent/cookies/index.tsx
@@ -32,7 +32,13 @@ const Cookies = () => {
   }));
 
   const [selectedKey, setSelectedKey] = useState<string | null>(null);
-  const [selectedCookie, setSelectedCookie] = useState<CookieData | null>(null);
+  const [selectedCookie, setSelectedCookie] = useState<
+    | (CookieData & {
+        isIbcCompliant: boolean | null;
+        isCookieSet: boolean | null;
+      })
+    | null
+  >(null);
 
   useEffect(() => {
     if (!selectedKey && cookies !== null && Object.keys(cookies).length !== 0) {
@@ -67,7 +73,8 @@ const Cookies = () => {
             <CookieDetails
               data={selectedCookie.parsedCookie}
               analytics={selectedCookie.analytics}
-              url={selectedCookie.url}
+              isIbcCompliant={selectedCookie.isIbcCompliant}
+              isCookieSet={selectedCookie.isCookieSet}
             />
           )}
         </div>
diff --git a/packages/extension/src/view/devtools/stateProviders/syncCookieStore/index.tsx b/packages/extension/src/view/devtools/stateProviders/syncCookieStore/index.tsx
index 7140d152..18c444b1 100644
--- a/packages/extension/src/view/devtools/stateProviders/syncCookieStore/index.tsx
+++ b/packages/extension/src/view/devtools/stateProviders/syncCookieStore/index.tsx
@@ -28,11 +28,15 @@ import React, {
  * Internal dependencies.
  */
 import type { CookieData } from '../../../../localStore';
+import { checkIbcCompliance } from '../../../../utils/checkIbcCompliance';
 
 export interface CookieStoreContext {
   state: {
     tabCookies: {
-      [key: string]: CookieData;
+      [key: string]: CookieData & {
+        isIbcCompliant: boolean | null;
+        isCookieSet: boolean | null;
+      };
     } | null;
     tabUrl: string | null;
   };
@@ -67,7 +71,31 @@ export const Provider = ({ children }: PropsWithChildren) => {
     ];
 
     if (tabData && tabData.cookies) {
-      setTabCookies(tabData.cookies);
+      const _cookies: NonNullable<CookieStoreContext['state']['tabCookies']> =
+        {};
+
+      await Promise.all(
+        Object.entries(tabData.cookies as { [key: string]: CookieData }).map(
+          async ([key, value]: [string, CookieData]) => {
+            const isIbcCompliant = await checkIbcCompliance(
+              value.parsedCookie.samesite,
+              value.parsedCookie.secure,
+              key,
+              value.url
+            );
+            const isCookieSet = Boolean(
+              await chrome.cookies.get({ name: key, url: value.url })
+            );
+            _cookies[key] = {
+              ...value,
+              isIbcCompliant,
+              isCookieSet,
+            };
+          }
+        )
+      );
+
+      setTabCookies(_cookies);
     }
 
     chrome.devtools.inspectedWindow.eval(
@@ -81,13 +109,39 @@ export const Provider = ({ children }: PropsWithChildren) => {
   }, []);
 
   const storeChangeListener = useCallback(
-    (changes: { [key: string]: chrome.storage.StorageChange }) => {
+    async (changes: { [key: string]: chrome.storage.StorageChange }) => {
       if (
         tabId &&
         Object.keys(changes).includes(tabId.toString()) &&
         changes[tabId.toString()]?.newValue?.cookies
       ) {
-        setTabCookies(changes[tabId.toString()].newValue.cookies);
+        const _cookies: NonNullable<CookieStoreContext['state']['tabCookies']> =
+          {};
+
+        await Promise.all(
+          Object.entries(
+            changes[tabId.toString()].newValue.cookies as {
+              [key: string]: CookieData;
+            }
+          ).map(async ([key, value]) => {
+            const isIbcCompliant = await checkIbcCompliance(
+              value.parsedCookie.samesite,
+              value.parsedCookie.secure,
+              key,
+              value.url
+            );
+            const isCookieSet = Boolean(
+              await chrome.cookies.get({ name: key, url: value.url })
+            );
+            _cookies[key] = {
+              ...value,
+              isIbcCompliant,
+              isCookieSet,
+            };
+          })
+        );
+
+        setTabCookies(_cookies);
       }
     },
     [tabId]
@@ -102,15 +156,57 @@ export const Provider = ({ children }: PropsWithChildren) => {
     [tabId]
   );
 
+  const cookieChangeListener = useCallback(
+    async (changeInfo: chrome.cookies.CookieChangeInfo) => {
+      if (
+        tabCookies &&
+        Object.keys(tabCookies).includes(changeInfo.cookie.name)
+      ) {
+        const isIbcCompliant = await checkIbcCompliance(
+          changeInfo.cookie.sameSite,
+          changeInfo.cookie.secure,
+          changeInfo.cookie.name,
+          tabCookies[changeInfo.cookie.name].url
+        );
+
+        const isCookieSet = Boolean(
+          await chrome.cookies.get({
+            name: changeInfo.cookie.name,
+            url: tabCookies[changeInfo.cookie.name].url,
+          })
+        );
+
+        const newCookieData = {
+          ...tabCookies[changeInfo.cookie.name],
+          isIbcCompliant,
+          isCookieSet,
+        };
+
+        setTabCookies({
+          ...tabCookies,
+          [changeInfo.cookie.name]: newCookieData,
+        });
+      }
+    },
+    [tabCookies]
+  );
+
   useEffect(() => {
     intitialSync();
     chrome.storage.local.onChanged.addListener(storeChangeListener);
     chrome.tabs.onUpdated.addListener(tabUpdateListener);
+    chrome.cookies.onChanged.addListener(cookieChangeListener);
     return () => {
       chrome.storage.local.onChanged.removeListener(storeChangeListener);
       chrome.tabs.onUpdated.removeListener(tabUpdateListener);
+      chrome.cookies.onChanged.removeListener(cookieChangeListener);
     };
-  }, [intitialSync, storeChangeListener, tabUpdateListener]);
+  }, [
+    cookieChangeListener,
+    intitialSync,
+    storeChangeListener,
+    tabUpdateListener,
+  ]);
 
   return (
     <Context.Provider value={{ state: { tabCookies, tabUrl }, actions: {} }}>
diff --git a/packages/extension/src/worker/parseRequestCookieHeader.ts b/packages/extension/src/worker/parseRequestCookieHeader.ts
index aa79afdb..d3587f03 100644
--- a/packages/extension/src/worker/parseRequestCookieHeader.ts
+++ b/packages/extension/src/worker/parseRequestCookieHeader.ts
@@ -29,6 +29,7 @@ import type {
 } from '../utils/fetchCookieDictionary';
 import { createCookieObject } from './createCookieObject';
 import findAnalyticsMatch from './findAnalyticsMatch';
+import isFirstParty from '../utils/isFirstParty';
 
 /**
  * Parse response cookies header.
@@ -36,6 +37,7 @@ import findAnalyticsMatch from './findAnalyticsMatch';
  * @param {string} url Cookie URL (URL of the server which is setting/updating cookies).
  * @param {string} value header value
  * @param {CookieDatabase} dictionary Dictionary from open cookie database
+ * @param {string} tabUrl top url of the tab from which the request originated.
  * @param {number} frameId Id of the frame the cookie is used in.
  * @returns {Promise<CookieData[]>} Parsed cookie object array.
  */
@@ -43,6 +45,7 @@ const parseRequestCookieHeader = async (
   url: string,
   value: string,
   dictionary: CookieDatabase,
+  tabUrl: string,
   frameId: number
 ): Promise<CookieData[]> => {
   try {
@@ -63,11 +66,14 @@ const parseRequestCookieHeader = async (
         } as ParsedCookie;
         parsedCookie = await createCookieObject(parsedCookie, url);
 
+        const _isFirstParty = isFirstParty(parsedCookie.domain || '', tabUrl);
+
         return {
           parsedCookie,
           analytics,
           headerType: 'request',
           url,
+          isFirstParty: _isFirstParty,
           frameIdList: [frameId],
         };
       })
diff --git a/packages/extension/src/worker/parseResponseCookieHeader.ts b/packages/extension/src/worker/parseResponseCookieHeader.ts
index 226c3136..967dfabe 100644
--- a/packages/extension/src/worker/parseResponseCookieHeader.ts
+++ b/packages/extension/src/worker/parseResponseCookieHeader.ts
@@ -28,6 +28,7 @@ import type {
 } from '../utils/fetchCookieDictionary';
 import findAnalyticsMatch from './findAnalyticsMatch';
 import { createCookieObject } from './createCookieObject';
+import isFirstParty from '../utils/isFirstParty';
 
 /**
  * Parse response cookies header.
@@ -35,6 +36,7 @@ import { createCookieObject } from './createCookieObject';
  * @param {string} url Cookie URL (URL of the server which is setting/updating cookies).
  * @param {string} value header value
  * @param {CookieDatabase} dictionary Dictionary from open cookie database
+ * @param {string} tabUrl top url of the tab from which the request originated.
  * @param {number} frameId Id of a frame in which this cookie is used.
  * @returns {Promise<CookieData>} Parsed cookie object.
  */
@@ -42,6 +44,7 @@ const parseResponseCookieHeader = async (
   url: string,
   value: string,
   dictionary: CookieDatabase,
+  tabUrl: string,
   frameId: number
 ): Promise<CookieData> => {
   let parsedCookie: ParsedCookie = cookie.parse(value);
@@ -52,11 +55,14 @@ const parseResponseCookieHeader = async (
     analytics = findAnalyticsMatch(parsedCookie.name, dictionary);
   }
 
+  const _isFirstParty = isFirstParty(parsedCookie.domain || '', tabUrl);
+
   return {
     parsedCookie,
     analytics,
     url,
     headerType: 'response',
+    isFirstParty: _isFirstParty,
     frameIdList: [frameId],
   };
 };
diff --git a/packages/extension/src/worker/service-worker.ts b/packages/extension/src/worker/service-worker.ts
index a87820ad..fd13da27 100644
--- a/packages/extension/src/worker/service-worker.ts
+++ b/packages/extension/src/worker/service-worker.ts
@@ -57,6 +57,7 @@ chrome.webRequest.onResponseStarted.addListener(
             url,
             header.value,
             cookieDB,
+            tab.url,
             frameId
           );
           return [...(await accumulator), cookie];
@@ -96,12 +97,14 @@ chrome.webRequest.onBeforeSendHeaders.addListener(
             header.name.toLowerCase() === 'cookie' &&
             header.value &&
             url &&
+            tab.url &&
             cookieDB
           ) {
             const cookieList = await parseRequestCookieHeader(
               url,
               header.value,
               cookieDB,
+              tab.url,
               frameId
             );
             return [...(await accumulator), ...cookieList];
@@ -164,3 +167,15 @@ chrome.windows.onRemoved.addListener(async (windowId) => {
 chrome.runtime.onInstalled.addListener(async () => {
   await chrome.storage.local.clear();
 });
+
+/**
+ * Listen to changes in cookie store and sync extension local store
+ * when any cookies are delted ( through application tab in devtools )
+ */
+chrome.cookies.onChanged.addListener(
+  async (changeInfo: chrome.cookies.CookieChangeInfo) => {
+    if (changeInfo.cause === 'explicit' && changeInfo.removed) {
+      await CookieStore.deleteCookie(changeInfo.cookie.name);
+    }
+  }
+);


Test patch:
diff --git a/packages/extension/src/localStore/tests/cookieStore.ts b/packages/extension/src/localStore/tests/cookieStore.ts
index 6ba403a9..cf027f6c 100644
--- a/packages/extension/src/localStore/tests/cookieStore.ts
+++ b/packages/extension/src/localStore/tests/cookieStore.ts
@@ -36,6 +36,7 @@ const cookieArray: CookieData[] = [
     analytics: null,
     url: 'https://example.com',
     headerType: 'response',
+    isFirstParty: false,
   },
   {
     parsedCookie: {
@@ -52,6 +53,7 @@ const cookieArray: CookieData[] = [
     analytics: null,
     url: 'https://example.com',
     headerType: 'response',
+    isFirstParty: false,
   },
 ];
 
@@ -121,6 +123,14 @@ describe('local store: CookieStore', () => {
     });
   });
 
+  it('should delte cookies', async () => {
+    await CookieStore.update('123', cookieArray);
+    await CookieStore.deleteCookie('countryCode1');
+    expect(storage['123'].cookies).toStrictEqual({
+      countryCode2: cookieArray[1],
+    });
+  });
+
   it('should add/update tab data and merge frame id list', async () => {
     await CookieStore.update('123', [
       cookieArray[0],
diff --git a/packages/extension/src/utils/tests/checkIbcCompliance.ts b/packages/extension/src/utils/tests/checkIbcCompliance.ts
new file mode 100644
index 00000000..998519ad
--- /dev/null
+++ b/packages/extension/src/utils/tests/checkIbcCompliance.ts
@@ -0,0 +1,66 @@
+/*
+ * 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 { checkIbcCompliance } from '../checkIbcCompliance';
+
+describe('checkIbcCompliance', () => {
+  beforeAll(() => {
+    //@ts-ignore
+    globalThis.chrome = {
+      cookies: {
+        //@ts-ignore
+        get: () => {
+          return true;
+        },
+      },
+    };
+  });
+  it('Should work with no samesite attribute', async () => {
+    const isIbcCompliant = await checkIbcCompliance(
+      undefined,
+      undefined,
+      'cookieName',
+      'https://example.com'
+    );
+
+    expect(isIbcCompliant).toBeTruthy();
+  });
+
+  it('Should work with no samesite set to none', async () => {
+    const isIbcCompliant = await checkIbcCompliance(
+      'none',
+      undefined,
+      'cookieName',
+      'https://example.com'
+    );
+
+    expect(isIbcCompliant).toBeFalsy();
+  });
+
+  it('Should work with no samesite set to none and secure attribute', async () => {
+    const isIbcCompliant = await checkIbcCompliance(
+      'none',
+      true,
+      'cookieName',
+      'https://example.com'
+    );
+
+    expect(isIbcCompliant).toBeTruthy();
+  });
+});
diff --git a/packages/extension/src/view/devtools/components/tabContent/cookies/tests/cookieTab.tsx b/packages/extension/src/view/devtools/components/tabContent/cookies/tests/cookieTab.tsx
index 2be7f24e..14ed8a54 100644
--- a/packages/extension/src/view/devtools/components/tabContent/cookies/tests/cookieTab.tsx
+++ b/packages/extension/src/view/devtools/components/tabContent/cookies/tests/cookieTab.tsx
@@ -62,6 +62,9 @@ const mockResponse: {
       analytics: null,
       url: 'https://edition.cnn.com/whatever/api',
       headerType: 'response',
+      isFirstParty: true,
+      isIbcCompliant: true,
+      isCookieSet: true,
       frameIdList: [1],
     },
     [uncategorised3pCookie.name]: {
@@ -69,6 +72,9 @@ const mockResponse: {
       analytics: null,
       url: 'https://api.pubmatic.com/whatever/api',
       headerType: 'response',
+      isFirstParty: false,
+      isIbcCompliant: true,
+      isCookieSet: true,
       frameIdList: [1],
     },
     [known1pCookie.name]: {
@@ -87,6 +93,9 @@ const mockResponse: {
       },
       url: 'https://edition.cnn.com/whatever/api',
       headerType: 'response',
+      isFirstParty: true,
+      isIbcCompliant: true,
+      isCookieSet: true,
       frameIdList: [1],
     },
     [known3pCookie.name]: {
@@ -105,6 +114,9 @@ const mockResponse: {
       },
       url: 'https://api.pubmatic.com/whatever/api',
       headerType: 'response',
+      isFirstParty: false,
+      isIbcCompliant: true,
+      isCookieSet: true,
       frameIdList: [1],
     },
   },
diff --git a/packages/extension/src/worker/tests/parseRequestCookieHeader.ts b/packages/extension/src/worker/tests/parseRequestCookieHeader.ts
index 5372989b..fedc2c34 100644
--- a/packages/extension/src/worker/tests/parseRequestCookieHeader.ts
+++ b/packages/extension/src/worker/tests/parseRequestCookieHeader.ts
@@ -38,6 +38,7 @@ const normalCookie1 = {
   analytics: null,
   url: 'https://example.com/public/api/alerts',
   headerType: 'request',
+  isFirstParty: false,
   frameIdList: [1],
 };
 
@@ -55,6 +56,7 @@ const normalCookie2 = {
   analytics: null,
   url: 'https://example.com/public/api/alerts',
   headerType: 'request',
+  isFirstParty: false,
   frameIdList: [1],
 };
 
@@ -72,6 +74,7 @@ const specialCookie = {
   analytics: null,
   url: 'https://example.com/public/api/alerts',
   headerType: 'request',
+  isFirstParty: false,
   frameIdList: [1],
 };
 
@@ -100,6 +103,7 @@ const wildcardCookie = {
   },
   url: 'https://example.com/public/api/alerts',
   headerType: 'request',
+  isFirstParty: false,
   frameIdList: [1],
 };
 
@@ -119,11 +123,13 @@ describe('parseRequestCookieHeader', () => {
       'https://example.com/public/api/alerts',
       header,
       {},
+      'https://docs.google.com/',
       1
     );
 
     expect(parsedCookie).toEqual([normalCookie1, normalCookie2, specialCookie]);
   });
+
   it('Should parse cookie header and add analytics', async () => {
     const header = `${normalCookie1Header}; ${normalCookie2Header}; ${specialCookieHeader}; ${wildcardCookieHeader}`;
     const parsedCookie = await parseRequestCookieHeader(
@@ -159,6 +165,7 @@ describe('parseRequestCookieHeader', () => {
           },
         ],
       },
+      'https://docs.google.com/',
       1
     );
 
diff --git a/packages/extension/src/worker/tests/parseResponseCookieHeader.ts b/packages/extension/src/worker/tests/parseResponseCookieHeader.ts
index 9d4e4a49..0f1c0fae 100644
--- a/packages/extension/src/worker/tests/parseResponseCookieHeader.ts
+++ b/packages/extension/src/worker/tests/parseResponseCookieHeader.ts
@@ -34,6 +34,7 @@ describe('parseResponseCookieHeader', () => {
       'https://example.com/public/api/alerts',
       'countryCode=IN; Domain=.example.com; Path=/; SameSite=None; Secure',
       {},
+      'https://docs.google.com/',
       1
     );
 
@@ -51,6 +52,7 @@ describe('parseResponseCookieHeader', () => {
       analytics: null,
       url: 'https://example.com/public/api/alerts',
       headerType: 'response',
+      isFirstParty: false,
       frameIdList: [1],
     });
   });
@@ -75,6 +77,7 @@ describe('parseResponseCookieHeader', () => {
           },
         ],
       },
+      'https://docs.google.com/',
       1
     );
 
@@ -103,6 +106,7 @@ describe('parseResponseCookieHeader', () => {
       },
       url: 'https://example.com/public/api/alerts',
       headerType: 'response',
+      isFirstParty: false,
       frameIdList: [1],
     });
   });
@@ -141,6 +145,7 @@ describe('parseResponseCookieHeader', () => {
           },
         ],
       },
+      'https://docs.google.com/',
       1
     );
 
@@ -169,6 +174,7 @@ describe('parseResponseCookieHeader', () => {
       },
       url: 'https://google.com/public/api/alerts',
       headerType: 'response',
+      isFirstParty: true,
       frameIdList: [1],
     });
   });