Skip to content

Commit f9b1c21

Browse files
authored
Merge pull request #6 from chinmay29hub/v1.0.1-release
fix: all cookies not visible in dashboard
2 parents f313464 + cc58de8 commit f9b1c21

4 files changed

Lines changed: 225 additions & 9 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
44
[![Chrome Web Store](https://img.shields.io/badge/Chrome%20Web%20Store-CacheCat-4285F4?logo=google-chrome&logoColor=white)](https://chromewebstore.google.com/detail/cachecat/kkfchdmglcngekkddkeljllfgdpbodgd)
5+
[![Chrome Web Store Version](https://img.shields.io/chrome-web-store/v/kkfchdmglcngekkddkeljllfgdpbodgd)](https://chromewebstore.google.com/detail/cachecat/kkfchdmglcngekkddkeljllfgdpbodgd)
56
[![React](https://img.shields.io/badge/React-18.3-61DAFB?logo=react&logoColor=white)](https://reactjs.org/)
67
[![Vite](https://img.shields.io/badge/Vite-7.2-646CFF?logo=vite&logoColor=white)](https://vitejs.dev/)
78

manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"manifest_version": 3,
33
"name": "CacheCat",
44
"short_name": "CacheCat",
5-
"version": "1.0.0",
5+
"version": "1.0.1",
66
"description": "View, edit, and manage website storage. Supports Cookies, Local Storage, Session Storage, IndexedDB, and Cache Storage.",
77
"icons": {
88
"16": "icons/icon16.png",

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "cachecat",
3-
"version": "1.0.0",
3+
"version": "1.0.1",
44
"description": "CacheCat - Full-featured Chrome Extension to view, edit, and manage website storage (Cookies, Local Storage, Session Storage, IndexedDB, Cache Storage). Modern dashboard interface with light/dark themes.",
55
"type": "module",
66
"main": "dist/background.js",

src/background/background.js

Lines changed: 222 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -193,8 +193,7 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
193193
}
194194

195195
if (targetTabIdForCookies && ACTIVE_TABS.has(targetTabIdForCookies)) {
196-
const tabInfo = ACTIVE_TABS.get(targetTabIdForCookies);
197-
handleGetCookies(tabInfo.origin)
196+
handleGetCookies(targetTabIdForCookies)
198197
.then(sendResponse)
199198
.catch((error) => sendResponse({ error: error.message }));
200199
return true;
@@ -412,14 +411,230 @@ async function handleAttachToTab(payload, dashboardTabId) {
412411
}
413412
}
414413

414+
// Helper function to get performance entries (injected into page)
415+
function getPerformanceEntries() {
416+
return performance
417+
.getEntriesByType('resource')
418+
.map((r) => r.name);
419+
}
420+
415421
// Cookies API handlers
416-
async function handleGetCookies(url) {
417-
if (!url) {
418-
throw new Error('URL required');
422+
// Source: https://stackoverflow.com/a/76801557
423+
// Uses performance API to detect all resource origins, then fetches cookies for each
424+
async function handleGetCookies(tabId) {
425+
if (!tabId) {
426+
throw new Error('Tab ID required');
419427
}
428+
420429
try {
421-
const cookies = await chrome.cookies.getAll({ url });
422-
return { cookies: cookies.map(formatCookie) };
430+
// Step 1: Get all resource URLs from the page using performance API
431+
const results = await chrome.scripting.executeScript({
432+
target: { tabId },
433+
func: getPerformanceEntries,
434+
});
435+
436+
const resourceUrls = results[0]?.result || [];
437+
438+
// Step 2: Extract unique origins from resource URLs
439+
const origins = resourceUrls
440+
.map((url) => {
441+
try {
442+
return new URL(url).origin;
443+
} catch {
444+
return null;
445+
}
446+
})
447+
.filter((origin) => Boolean(origin) && origin !== 'null');
448+
449+
// Also include the main page origin
450+
let mainOrigin = null;
451+
try {
452+
const tab = await chrome.tabs.get(tabId);
453+
if (tab?.url) {
454+
mainOrigin = new URL(tab.url).origin;
455+
origins.push(mainOrigin);
456+
}
457+
} catch (e) {
458+
// Ignore if we can't get tab info
459+
}
460+
461+
const uniqueOrigins = new Set(origins);
462+
463+
// Step 3: Get the main tab URL to also query directly
464+
let mainTabUrl = null;
465+
try {
466+
const tab = await chrome.tabs.get(tabId);
467+
if (tab?.url) {
468+
mainTabUrl = tab.url;
469+
}
470+
} catch (e) {
471+
// Ignore if we can't get tab URL
472+
}
473+
474+
// Step 4: Get cookies for each origin AND the main tab URL
475+
// chrome.cookies.getAll({ url }) returns cookies that would be sent with requests
476+
// to that URL, which matches DevTools behavior
477+
// Note: Partitioned cookies might need special handling
478+
const getCookiesPromises = [];
479+
480+
// First, get cookies for the main tab URL directly (this is what DevTools shows)
481+
if (mainTabUrl) {
482+
const mainPromise = chrome.cookies
483+
.getAll({ url: mainTabUrl })
484+
.then(async (cookies) => {
485+
// Check for cf_clearance cookie specifically (partitioned cookies need special handling)
486+
let cfClearance = cookies.find(c => c.name === 'cf_clearance');
487+
if (!cfClearance) {
488+
// Try to get cf_clearance with partition key (for partitioned cookies)
489+
// IMPORTANT: Do NOT include 'url' parameter when querying partitioned cookies
490+
try {
491+
const mainUrlObj = new URL(mainTabUrl);
492+
const hostname = mainUrlObj.hostname;
493+
494+
// Extract top-level domain for partition key
495+
const domainParts = hostname.split('.');
496+
let topLevelDomain = hostname;
497+
if (domainParts.length >= 2) {
498+
topLevelDomain = domainParts.slice(-2).join('.');
499+
}
500+
const partitionKeyUrl = `https://${topLevelDomain}`;
501+
502+
// Try different domain formats (with and without leading dot, and parent domains)
503+
const domainVariants = [
504+
`.${hostname}`,
505+
hostname,
506+
`.${domainParts.slice(1).join('.')}`,
507+
domainParts.slice(1).join('.'),
508+
`.${topLevelDomain}`,
509+
topLevelDomain
510+
];
511+
512+
// Try querying with partition key for each domain variant
513+
let found = false;
514+
for (const domainVariant of domainVariants) {
515+
try {
516+
const partitionedCookies = await chrome.cookies.getAll({
517+
name: 'cf_clearance',
518+
domain: domainVariant,
519+
partitionKey: { topLevelSite: partitionKeyUrl }
520+
});
521+
if (partitionedCookies.length > 0) {
522+
cfClearance = partitionedCookies[0];
523+
cookies.push(cfClearance);
524+
found = true;
525+
break;
526+
}
527+
} catch (e) {
528+
continue;
529+
}
530+
}
531+
532+
// If partitionKey approach didn't work, try getAll and filter
533+
if (!found) {
534+
try {
535+
const allCookies = await chrome.cookies.getAll({});
536+
const cfClearanceAll = allCookies.find(c => {
537+
if (c.name !== 'cf_clearance') return false;
538+
return domainVariants.some(variant =>
539+
c.domain === variant ||
540+
c.domain === variant.replace(/^\./, '') ||
541+
variant.includes(c.domain.replace(/^\./, ''))
542+
);
543+
});
544+
if (cfClearanceAll) {
545+
cookies.push(cfClearanceAll);
546+
}
547+
} catch (e) {
548+
// Silently fail
549+
}
550+
}
551+
} catch (e) {
552+
// Silently fail
553+
}
554+
}
555+
return {
556+
url: mainTabUrl,
557+
cookies,
558+
isMainTab: true,
559+
};
560+
})
561+
.catch(() => {
562+
return { url: mainTabUrl, cookies: [], isMainTab: true };
563+
});
564+
getCookiesPromises.push(mainPromise);
565+
}
566+
567+
// Also get cookies for each origin (to catch third-party cookies)
568+
// For origins, we'll try both the origin URL and construct a full URL
569+
for (const originUrl of uniqueOrigins) {
570+
// Try with origin URL first
571+
const originPromise = chrome.cookies
572+
.getAll({ url: originUrl })
573+
.then((cookies) => {
574+
return {
575+
url: originUrl,
576+
cookies,
577+
isMainTab: false,
578+
};
579+
})
580+
.catch(() => {
581+
return { url: originUrl, cookies: [], isMainTab: false };
582+
});
583+
getCookiesPromises.push(originPromise);
584+
585+
// Also try with a constructed full URL for partitioned cookies
586+
// Partitioned cookies might need the full URL path, not just origin
587+
if (mainTabUrl && originUrl !== mainOrigin) {
588+
try {
589+
const originObj = new URL(originUrl);
590+
const mainUrlObj = new URL(mainTabUrl);
591+
// Construct a URL using the origin's protocol and host, with main tab's path
592+
const fullOriginUrl = `${originObj.protocol}//${originObj.host}${mainUrlObj.pathname}`;
593+
594+
const fullUrlPromise = chrome.cookies
595+
.getAll({ url: fullOriginUrl })
596+
.then((cookies) => {
597+
return {
598+
url: fullOriginUrl,
599+
cookies,
600+
isMainTab: false,
601+
};
602+
})
603+
.catch(() => {
604+
// Silently fail for full URL attempts
605+
return { url: fullOriginUrl, cookies: [], isMainTab: false };
606+
});
607+
getCookiesPromises.push(fullUrlPromise);
608+
} catch (e) {
609+
// Ignore URL construction errors
610+
}
611+
}
612+
}
613+
614+
const urlCookies = await Promise.all(getCookiesPromises);
615+
616+
// Step 5: Combine all cookies from all sources
617+
// We query both the main tab URL and all origins to ensure we catch all cookies
618+
// DevTools shows cookies that would be sent with requests to the page, which includes
619+
// first-party cookies and third-party cookies from domains that have resources on the page
620+
const cookieMap = new Map();
621+
622+
// Process all cookie sources (main tab URL + all origins)
623+
// This ensures we get all cookies, including third-party ones
624+
for (const { cookies } of urlCookies) {
625+
for (const cookie of cookies) {
626+
const key = `${cookie.name}|${cookie.domain}|${cookie.path}`;
627+
if (!cookieMap.has(key)) {
628+
cookieMap.set(key, cookie);
629+
}
630+
}
631+
}
632+
633+
634+
const allCookies = Array.from(cookieMap.values());
635+
const formattedCookies = allCookies.map(formatCookie);
636+
637+
return { cookies: formattedCookies };
423638
} catch (error) {
424639
throw new Error(`Failed to get cookies: ${error.message}`);
425640
}

0 commit comments

Comments
 (0)