@@ -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