Skip to content

fix(wrappers): guard against undefined Browser.cookies in getChatGptAccessToken#965

Open
octo-patch wants to merge 3 commits into
ChatGPTBox-dev:masterfrom
octo-patch:fix/issue-912-cookies-undefined-guard
Open

fix(wrappers): guard against undefined Browser.cookies in getChatGptAccessToken#965
octo-patch wants to merge 3 commits into
ChatGPTBox-dev:masterfrom
octo-patch:fix/issue-912-cookies-undefined-guard

Conversation

@octo-patch
Copy link
Copy Markdown
Contributor

@octo-patch octo-patch commented Apr 25, 2026

Fixes #912

Problem

In some browser contexts (e.g. Safari, or environments where the cookies permission is unavailable), Browser.cookies can be undefined. The current getChatGptAccessToken implementation calls Browser.cookies.getAll(...) without checking if Browser.cookies exists, causing:

Error: can't access property "getAll", a.cookies is undefined

Solution

Apply the same defensive guard pattern that already exists in chatgpt-web.mjs (line 255):

// chatgpt-web.mjs (existing pattern)
if (Browser.cookies && Browser.cookies.getAll) {
  cookie = (await Browser.cookies.getAll(...))...
}

In wrappers.mjs, wrap the Browser.cookies.getAll call in the same guard, defaulting cookie to an empty string when cookies are unavailable. The session endpoint at https://chatgpt.com/api/auth/session is still fetched; it will return an accessToken if the browser already has a valid session established via credentials: include or other means.

Testing

  • Verified that the existing unit tests in tests/unit/services/wrappers-register.test.mjs still pass with this change.
  • Confirmed the guard matches the existing pattern used elsewhere in the codebase.

Open in Devin Review

Summary by CodeRabbit

  • Refactor
    • Improved authentication token handling so the app gracefully retrieves tokens in environments with limited or missing browser cookie support; requests omit Cookie headers when cookies aren't available.
  • Tests
    • Added regression tests to ensure token retrieval still succeeds and that requests omit the Cookie header when cookie APIs are unavailable.

Review Change Stack

…ccessToken

In some browser contexts (e.g. Safari, or environments where the cookies
permission is unavailable), Browser.cookies can be undefined, causing:
  "can't access property 'getAll', a.cookies is undefined"

chatgpt-web.mjs already uses this guard pattern. Apply the same
defensive check before calling Browser.cookies.getAll in
getChatGptAccessToken, allowing the session endpoint to be reached
without cookies when the API is unavailable.

Fixes ChatGPTBox-dev#912

Co-Authored-By: Octopus <liyuan851277048@icloud.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 25, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0f749cad-4668-4f08-9203-c8d1539d8d16

📥 Commits

Reviewing files that changed from the base of the PR and between 9824d81 and b22f071.

📒 Files selected for processing (2)
  • src/services/wrappers.mjs
  • tests/unit/services/wrappers-register.test.mjs
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/services/wrappers.mjs

📝 Walkthrough

Walkthrough

getChatGptAccessToken now checks for Browser.cookies.getAll before reading cookies; if unavailable (or Browser.cookies is undefined) it omits the Cookie request header entirely while still fetching the session endpoint with credentials: 'include'. (26 words)

Changes

ChatGPT Cookie Handling

Layer / File(s) Summary
Behavior change / entry
src/services/wrappers.mjs
Guard cookie access: read and format cookies only when Browser.cookies.getAll exists; include Cookie header in the session fetch only when a non-empty cookie string is available.
Tests — shim & assertions
tests/unit/services/wrappers-register.test.mjs
Import Browser from webextension-polyfill to mutate cookie API availability and assert fetch uses credentials: 'include'.
Tests — regressions
tests/unit/services/wrappers-register.test.mjs (new cases)
Add two regression tests: one with Browser.cookies.getAll = undefined, one with Browser.cookies = undefined. Both assert the outgoing Cookie header is omitted and the mocked access token is returned.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I sniff the jar where crumbs might be,
If no lid's found, I dance carefree,
No crumbs to press upon my paw—
I hop, I fetch, and find the law. 🥕

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: guarding against undefined Browser.cookies in getChatGptAccessToken to fix the reported runtime error.
Linked Issues check ✅ Passed The PR successfully addresses issue #912 by guarding Browser.cookies.getAll calls, setting credentials: 'include', and conditionally including the Cookie header while ensuring the session fetch still proceeds.
Out of Scope Changes check ✅ Passed All changes are directly scoped to fixing issue #912: modifications to getChatGptAccessToken in wrappers.mjs and corresponding regression tests, with no unrelated changes.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@qodo-code-review
Copy link
Copy Markdown
Contributor

Review Summary by Qodo

Guard against undefined Browser.cookies in getChatGptAccessToken

🐞 Bug fix

Grey Divider

Walkthroughs

Description
• Guard against undefined Browser.cookies in getChatGptAccessToken
• Prevents runtime error in Safari and restricted permission contexts
• Matches existing defensive pattern used in chatgpt-web.mjs
• Session endpoint still accessible via credentials when cookies unavailable
Diagram
flowchart LR
  A["getChatGptAccessToken called"] --> B{"Browser.cookies exists?"}
  B -->|Yes| C["Get cookies from Browser.cookies.getAll"]
  B -->|No| D["Use empty cookie string"]
  C --> E["Fetch session endpoint"]
  D --> E
  E --> F["Return accessToken"]
Loading

Grey Divider

File Changes

1. src/services/wrappers.mjs 🐞 Bug fix +8/-5

Add defensive guard for Browser.cookies access

• Added defensive guard check for Browser.cookies and Browser.cookies.getAll before accessing
• Initialize cookie variable as empty string to handle undefined cookies case
• Wraps Browser.cookies.getAll call in conditional block
• Allows session endpoint fetch to proceed even when cookies API unavailable

src/services/wrappers.mjs


Grey Divider

Qodo Logo

@qodo-code-review
Copy link
Copy Markdown
Contributor

qodo-code-review Bot commented Apr 25, 2026

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0)

Grey Divider


Action required

1. Missing session-cookie fallback ✓ Resolved 🐞 Bug ≡ Correctness
Description
When Browser.cookies is unavailable, getChatGptAccessToken still sends an always-present (often
empty) Cookie header and does not set credentials: 'include', so the /api/auth/session request may
not carry any session cookies and will keep returning no accessToken (throwing UNAUTHORIZED). This
makes the new guard avoid the crash but still breaks token retrieval in the target environments.
Code

src/services/wrappers.mjs[R18-28]

+    let cookie = ''
+    if (Browser.cookies && Browser.cookies.getAll) {
+      cookie = (await Browser.cookies.getAll({ url: 'https://chatgpt.com/' }))
+        .map((cookie) => {
+          return `${cookie.name}=${cookie.value}`
+        })
+        .join('; ')
+    }
   const resp = await fetch('https://chatgpt.com/api/auth/session', {
     headers: {
       Cookie: cookie,
Evidence
wrappers.mjs now defaults cookie to '' and still unconditionally sets the Cookie header, while not
enabling credentials-based cookie sending. Elsewhere (chatgpt-web.mjs), the code uses credentials:
'include' and only attaches the Cookie header when a non-empty cookie string exists, indicating the
intended robust pattern for authenticated ChatGPT web requests. getChatGptAccessToken is invoked
from the background flow, so this fetch is not guaranteed to be same-origin with chatgpt.com and
needs an explicit cookie-sending strategy when the cookies API is missing.

src/services/wrappers.mjs[12-40]
src/services/apis/chatgpt-web.mjs[275-288]
src/background/index.mjs[485-505]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`getChatGptAccessToken()` now avoids a crash when `Browser.cookies` is missing, but the fallback still makes a `fetch('https://chatgpt.com/api/auth/session')` request that may not include any session cookies (it always sets `headers.Cookie`, often to `''`, and does not set `credentials: 'include'`). This can cause the endpoint to return no `accessToken`, resulting in persistent `UNAUTHORIZED` errors in the very environments this PR targets.
### Issue Context
A more robust pattern already exists in `chatgpt-web.mjs`: always use `credentials: 'include'`, and only set a manual `Cookie` header when you actually have a non-empty cookie string.
### Fix Focus Areas
- src/services/wrappers.mjs[18-30]
### Suggested change
- Build `headers` so `Cookie` is only included when `cookie` is non-empty (e.g. `...(cookie && { Cookie: cookie })`).
- Add `credentials: 'include'` to the `fetch` options so environments without `Browser.cookies` still have a chance to send existing session cookies automatically.
- (Optional but recommended) Add/extend a unit test to cover the `Browser.cookies` undefined case and assert that the request either omits `Cookie` when empty and/or sets `credentials: 'include'`.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request adds a safety check for the Browser.cookies API in the getChatGptAccessToken function to prevent potential errors when the API is unavailable. The review feedback suggests refactoring the implementation to use optional chaining and a ternary operator for better conciseness and to avoid variable shadowing within the map callback.

Comment thread src/services/wrappers.mjs
Comment on lines +18 to +25
let cookie = ''
if (Browser.cookies && Browser.cookies.getAll) {
cookie = (await Browser.cookies.getAll({ url: 'https://chatgpt.com/' }))
.map((cookie) => {
return `${cookie.name}=${cookie.value}`
})
.join('; ')
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The variable cookie is shadowed by the parameter in the .map() callback, which is a poor practice that can lead to confusion and potential bugs. Additionally, the logic can be simplified by using a const declaration with a ternary operator and optional chaining, making the code more concise and idiomatic while avoiding the let declaration.

    const cookie = (Browser.cookies?.getAll
      ? await Browser.cookies.getAll({ url: 'https://chatgpt.com/' })
      : [])
      .map((c) => `${c.name}=${c.value}`)
      .join('; ')

Copy link
Copy Markdown

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no potential bugs to report.

View in Devin Review to see 2 additional findings.

Open in Devin Review

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
src/services/wrappers.mjs (1)

18-25: Guard correctly fixes the runtime error; minor polish and a heads-up on sibling functions.

The added check resolves issue #912 and matches the pattern used in chatgpt-web.mjs. A couple of small notes:

  1. The map callback parameter cookie (line 21) shadows the outer let cookie (line 18). Renaming to c (or similar) avoids the shadowing and keeps the inner reference unambiguous.
  2. Optional chaining would express the same intent more concisely: if (Browser.cookies?.getAll).
  3. Heads-up (out of scope for this PR): getBingAccessToken (line 44), getBardCookies (line 48), and getClaudeSessionKey (line 54) still call Browser.cookies.get(...) unconditionally and will throw the same a.cookies is undefined error in browser contexts where Browser.cookies is unavailable. Consider applying the same guard there in a follow-up.
♻️ Optional refactor for the changed block
-    let cookie = ''
-    if (Browser.cookies && Browser.cookies.getAll) {
-      cookie = (await Browser.cookies.getAll({ url: 'https://chatgpt.com/' }))
-        .map((cookie) => {
-          return `${cookie.name}=${cookie.value}`
-        })
-        .join('; ')
-    }
+    let cookie = ''
+    if (Browser.cookies?.getAll) {
+      cookie = (await Browser.cookies.getAll({ url: 'https://chatgpt.com/' }))
+        .map((c) => `${c.name}=${c.value}`)
+        .join('; ')
+    }

Want me to open a follow-up issue to apply the same guard to getBingAccessToken, getBardCookies, and getClaudeSessionKey?

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/services/wrappers.mjs` around lines 18 - 25, The current block declares
let cookie then shadows it in the map callback and uses a verbose guard; rename
the map parameter (e.g., c) to avoid shadowing, replace the guard with optional
chaining for clarity (check Browser.cookies?.getAll), and ensure the result
assignment still builds the cookie string from Browser.cookies?.getAll({ url:
'https://chatgpt.com/' }) mapping each entry; also note the sibling functions
getBingAccessToken, getBardCookies, and getClaudeSessionKey call
Browser.cookies.get(...) without a guard—plan a follow-up to add similar
optional chaining guards there to prevent undefined access errors.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/services/wrappers.mjs`:
- Around line 18-25: The current block declares let cookie then shadows it in
the map callback and uses a verbose guard; rename the map parameter (e.g., c) to
avoid shadowing, replace the guard with optional chaining for clarity (check
Browser.cookies?.getAll), and ensure the result assignment still builds the
cookie string from Browser.cookies?.getAll({ url: 'https://chatgpt.com/' })
mapping each entry; also note the sibling functions getBingAccessToken,
getBardCookies, and getClaudeSessionKey call Browser.cookies.get(...) without a
guard—plan a follow-up to add similar optional chaining guards there to prevent
undefined access errors.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: bd2bd3d3-4fa5-4752-b912-098bda865469

📥 Commits

Reviewing files that changed from the base of the PR and between c236a4b and 5e3329e.

📒 Files selected for processing (1)
  • src/services/wrappers.mjs

Comment thread src/services/wrappers.mjs Outdated
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Guards getChatGptAccessToken() against environments where the WebExtension cookies API is unavailable, preventing runtime crashes when attempting to read ChatGPT cookies.

Changes:

  • Add a defensive check for Browser.cookies?.getAll before reading cookies.
  • Default the Cookie header value to an empty string when cookies cannot be read.
Comments suppressed due to low confidence (1)

src/services/wrappers.mjs:30

  • When Browser.cookies is unavailable, this fetch currently runs without credentials: 'include', so the browser’s existing session cookies likely won’t be sent and the session endpoint will always return UNAUTHORIZED even if the user is logged in. Consider adding credentials: 'include' and only setting the Cookie header when cookie is non-empty (aligning with the pattern in src/services/apis/chatgpt-web.mjs).
    const resp = await fetch('https://chatgpt.com/api/auth/session', {
      headers: {
        Cookie: cookie,
      },
    })

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/services/wrappers.mjs Outdated
Comment on lines +21 to +22
.map((cookie) => {
return `${cookie.name}=${cookie.value}`
Comment thread src/services/wrappers.mjs
Comment on lines +19 to 26
if (Browser.cookies && Browser.cookies.getAll) {
cookie = (await Browser.cookies.getAll({ url: 'https://chatgpt.com/' }))
.map((cookie) => {
return `${cookie.name}=${cookie.value}`
})
.join('; ')
}
const resp = await fetch('https://chatgpt.com/api/auth/session', {
@PeterDaveHello
Copy link
Copy Markdown
Member

Please also add a unit test for the Browser.cookies unavailable path, since that is the core branch introduced by this PR.

…ccessToken

Adds two unit tests for the new defensive branch introduced in
5e3329e (issue ChatGPTBox-dev#912):

- getAll missing on Browser.cookies (e.g. restricted Firefox contexts)
- Browser.cookies entirely undefined

Both cases assert that getChatGptAccessToken does not throw before the
fetch and that the request goes out with an empty Cookie header. The
polyfill state is restored via t.after so other tests are unaffected.
@octo-patch
Copy link
Copy Markdown
Contributor Author

Thanks @PeterDaveHello! Pushed commit 9824d81 that adds two unit tests for the new branch in tests/unit/services/wrappers-register.test.mjs:

  • Browser.cookies.getAll unavailable — overrides getAll to undefined and asserts getChatGptAccessToken does not throw and sends Cookie: "" to the session endpoint.
  • Browser.cookies entirely undefined — same assertions for the case where the whole cookies API is missing.

Both tests use t.after to restore the polyfill state so they don't leak into the other tests in the file. Local npm test passes 22/22.

@PeterDaveHello
Copy link
Copy Markdown
Member

@codex review

@chatgpt-codex-connector
Copy link
Copy Markdown

Codex Review: Didn't find any major issues. 🚀

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (1)

src/services/wrappers.mjs:29

  • getChatGptAccessToken always sets a Cookie header (even when cookies are unavailable and cookie is ''). This diverges from the existing pattern in src/services/apis/chatgpt-web.mjs where the Cookie header is conditionally included only when a non-empty cookie string is available. Consider omitting the Cookie header entirely when Browser.cookies/getAll is unavailable (e.g., build headers with a conditional spread) to avoid sending an explicit empty Cookie header and to keep behavior consistent across the codebase.
    let cookie = ''
    if (Browser.cookies && Browser.cookies.getAll) {
      cookie = (await Browser.cookies.getAll({ url: 'https://chatgpt.com/' }))
        .map((cookie) => {
          return `${cookie.name}=${cookie.value}`
        })
        .join('; ')
    }
    const resp = await fetch('https://chatgpt.com/api/auth/session', {
      headers: {
        Cookie: cookie,
      },

Comment on lines +352 to +386
test('getChatGptAccessToken sends empty Cookie header when Browser.cookies.getAll is unavailable', async (t) => {
t.mock.method(console, 'debug', () => {})
setStorage({ tokenSavedOn: Date.now() })

// Simulate environments (e.g. some Firefox / restricted contexts) where
// chrome.cookies.getAll is not exposed. Override on the polyfill object
// and restore afterwards so other tests are unaffected.
const originalGetAll = Browser.cookies.getAll
Object.defineProperty(Browser.cookies, 'getAll', {
value: undefined,
writable: true,
configurable: true,
})
t.after(() => {
Object.defineProperty(Browser.cookies, 'getAll', {
value: originalGetAll,
writable: true,
configurable: true,
})
})

let observedCookieHeader
t.mock.method(globalThis, 'fetch', async (url, init) => {
assert.equal(url, 'https://chatgpt.com/api/auth/session')
observedCookieHeader = init.headers.Cookie
return {
status: 200,
json: async () => ({ accessToken: 'token-without-cookies' }),
}
})

const token = await getChatGptAccessToken()
assert.equal(token, 'token-without-cookies')
assert.equal(observedCookieHeader, '')
})
Copy link
Copy Markdown
Member

Thanks for adding the regression tests for the Browser.cookies unavailable path.

I think this still needs one small follow-up before merging. Right now getChatGptAccessToken() avoids the crash, but when Browser.cookies is unavailable it still sends Cookie: '' and does not set credentials: 'include'. That means the session request may still fail to carry existing browser session cookies in the target environments.

Could you please align this with the existing chatgpt-web.mjs pattern by setting credentials: 'include', only adding the Cookie header when the cookie string is non-empty, and updating the new tests accordingly? While touching that block, please also rename the .map((cookie) => ...) callback parameter to avoid shadowing the outer cookie variable.

After that and a green CI run, this should be good to merge.

- set credentials: 'include' so the browser attaches existing chatgpt.com
  session cookies in environments where Browser.cookies.getAll cannot
  build the Cookie header manually
- only emit the Cookie header when the assembled cookie string is
  non-empty, matching the pattern already used in
  src/services/apis/chatgpt-web.mjs
- rename the .map((cookie) => ...) callback parameter so it does not
  shadow the outer cookie string
- update the regression tests to assert the absent Cookie header and
  presence of credentials: 'include'
@octo-patch
Copy link
Copy Markdown
Contributor Author

Thanks @PeterDaveHello! Pushed b22f071 aligning with the chatgpt-web.mjs pattern:

  • fetch(...) now sets credentials: 'include' so the browser attaches existing chatgpt.com session cookies in environments where Browser.cookies.getAll cannot build the header manually.
  • The Cookie header is only emitted when the assembled cookie string is non-empty (...(cookie && { Cookie: cookie })), instead of always sending an empty header.
  • The .map((cookie) => ...) callback now destructures { name, value } so there's no shadowing of the outer cookie string.
  • Updated the two regression tests to assert the absent Cookie header (instead of '') and the presence of credentials: 'include'.

npm test tests/unit/services/wrappers-register.test.mjs → 22/22 pass; npm run lint and prettier --check are clean. Ready for another pass.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Error: can't access property "getAll", a.cookies is undefined

3 participants