nodejs / nodejs/undici

make fetch compatible with Object.freeze(globalThis)

Open
#4,009 12 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

enhancement
Dominant language
JavaScript
Stars
7.7k
Forks
880
Avg merge
2d 16h
Merged PRs (30d)
68

Description

This would solve...

The NodeJS "security best practices" document suggests using Object.freeze(globalThis) to ensure no globals can be replaced, but this is unexpectedly not compatible with the built-in fetch:

# node
Welcome to Node.js v23.5.0.
Type ".help" for more information.
> Object.freeze(globalThis);
[...]
> fetch('https://example.com');
Uncaught:
TypeError: Cannot define property Symbol(undici.globalDispatcher.1), object is not extensible
    at Function.defineProperty (<anonymous>)
    at setGlobalDispatcher2 (node:internal/deps/undici/undici:8241:14)
    at lib/global.js (node:internal/deps/undici/undici:8235:7)
    at __require (node:internal/deps/undici/undici:6:50)
    at node:internal/deps/undici/undici:13478:52
    at BuiltinModule.compileForInternalLoader (node:internal/bootstrap/realm:402:7)
    at requireBuiltin (node:internal/bootstrap/realm:433:14)
    at fetch (node:internal/bootstrap/web/exposed-window-or-worker:78:28)

The implementation should look like...

The issue is that undici attempts to add a dispatcher to globalThis: https://github.com/nodejs/undici/blob/bd98a6303e45d5e0d44192a93731b1defdb415f3/lib/global.js#L17 (there also seems to be another one for globalOrigin)

This makes sense when undici is used as a userspace library since it avoids extra resource useage due to multiple versions coexisting, but makes less sense when it is part of NodeJS itself. The NodeJS integration should probably side-step this, or alternatively register the necessary globals before handing over to user code.

I have also considered...

This burden could be shifted to the user if there were some way to pre-initialise fetch; then the user could call this before freezing globalThis:

fetch.initialise();
Object.freeze(globalThis);

But this is an unexpected requirement and likely to catch developers out. Better for it to be automatic.

Another alternative would be to update the recommended security practices to suggest a method of freezing the existing properties of globalThis without preventing defining new properties, but I am not aware of a standard way to do this.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Reproduce the failure with Object.freeze(globalThis) followed by fetch in Node.js. Read lib/global.js and lib/web/fetch/global.js, especially their global dispatcher and globalOrigin initialization, then inspect the Node.js integration described in the issue. Done means built-in fetch works after globalThis is frozen without requiring users to pre-initialize it.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript, node.js
Domain
backend, networking
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.