Skip to content

Commit 79de55f

Browse files
rubennortereact-native-bot
authored andcommitted
Harden window and navigator global property descriptors (#57447)
Summary: Pull Request resolved: #57447 The `window` and `navigator` globals were installed with plain property assignments, which produced writable and configurable data properties. This makes the global definitions safer and more spec-compliant: `window` is now non-writable and non-configurable (still enumerable), matching how browsers expose it, and `navigator` is now non-writable (still configurable and enumerable). `self` is unchanged and remains writable, configurable and enumerable. `window` and `self` are now installed on `globalThis`. Also adds Fantom tests that assert the property descriptors of the `window`, `self`, `navigator`, `Infinity`, `NaN` and `undefined` globals so these guarantees don't regress. Changelog: [General][Changed] - Make the `window` global non-writable and non-configurable, and the `navigator` global non-writable Reviewed By: huntie Differential Revision: D110759948 fbshipit-source-id: 7cd79d9ba5a7dc4b6ef9fb61fede0d25723516b5
1 parent ce057d6 commit 79de55f

3 files changed

Lines changed: 84 additions & 5 deletions

File tree

packages/react-native/Libraries/Core/setUpGlobals.js

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,19 @@
1515
* Sets up global variables for React Native.
1616
* You can use this module directly, or just require InitializeCore.
1717
*/
18-
if (global.window === undefined) {
18+
if (globalThis.window === undefined) {
1919
// $FlowExpectedError[cannot-write] The global isn't writable anywhere but here, where we define it.
20-
global.window = global;
20+
Object.defineProperty(globalThis, 'window', {
21+
value: globalThis,
22+
configurable: false,
23+
enumerable: true,
24+
writable: false,
25+
});
2126
}
2227

23-
if (global.self === undefined) {
28+
if (globalThis.self === undefined) {
2429
// $FlowExpectedError[cannot-write] The global isn't writable anywhere but here, where we define it.
25-
global.self = global;
30+
globalThis.self = globalThis;
2631
}
2732

2833
// Set up process

packages/react-native/Libraries/Core/setUpNavigator.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,12 @@ const {polyfillObjectProperty} = require('../Utilities/PolyfillFunctions');
1515
const navigator = global.navigator;
1616
if (navigator === undefined) {
1717
// $FlowExpectedError[cannot-write] The global isn't writable anywhere but here, where we define it.
18-
global.navigator = {product: 'ReactNative'};
18+
Object.defineProperty(global, 'navigator', {
19+
value: {product: 'ReactNative'},
20+
configurable: true,
21+
enumerable: true,
22+
writable: false,
23+
});
1924
} else {
2025
// see https://github.com/facebook/react-native/issues/10881
2126
polyfillObjectProperty(navigator, 'product', () => 'ReactNative');

packages/react-native/src/private/setup/__tests__/setUpDefaultReactNativeEnvironment-Globals-itest.js

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,25 @@ import '@react-native/fantom/src/setUpDefaultReactNativeEnvironment';
1414
declare var PerformanceObserverEntryList: unknown;
1515
declare var EventCounts: unknown;
1616

17+
function expectGlobalDescriptorToMatch(
18+
name: string,
19+
expected: {
20+
enumerable: boolean,
21+
configurable: boolean,
22+
writable: boolean,
23+
},
24+
) {
25+
const descriptor = Object.getOwnPropertyDescriptor(globalThis, name);
26+
if (descriptor == null) {
27+
throw new Error(`Expected globalThis.${name} to be defined`);
28+
}
29+
expect({
30+
enumerable: descriptor.enumerable,
31+
configurable: descriptor.configurable,
32+
writable: descriptor.writable,
33+
}).toEqual(expected);
34+
}
35+
1736
describe('setUpDefaultReactNativeEnvironment (globals)', () => {
1837
describe('global object', () => {
1938
it('should be exposed as globalThis, global, window and self', () => {
@@ -23,6 +42,56 @@ describe('setUpDefaultReactNativeEnvironment (globals)', () => {
2342
});
2443
});
2544

45+
describe('property descriptors', () => {
46+
it('should define window as enumerable but not configurable or writable', () => {
47+
expectGlobalDescriptorToMatch('window', {
48+
enumerable: true,
49+
configurable: false,
50+
writable: false,
51+
});
52+
});
53+
54+
it('should define self as configurable, enumerable and writable', () => {
55+
expectGlobalDescriptorToMatch('self', {
56+
enumerable: true,
57+
configurable: true,
58+
writable: true,
59+
});
60+
});
61+
62+
it('should define navigator as configurable and enumerable but not writable', () => {
63+
expectGlobalDescriptorToMatch('navigator', {
64+
enumerable: true,
65+
configurable: true,
66+
writable: false,
67+
});
68+
});
69+
70+
it('should define Infinity as not configurable, writable or enumerable', () => {
71+
expectGlobalDescriptorToMatch('Infinity', {
72+
enumerable: false,
73+
configurable: false,
74+
writable: false,
75+
});
76+
});
77+
78+
it('should define NaN as not configurable, writable or enumerable', () => {
79+
expectGlobalDescriptorToMatch('NaN', {
80+
enumerable: false,
81+
configurable: false,
82+
writable: false,
83+
});
84+
});
85+
86+
it('should define undefined as not configurable, writable or enumerable', () => {
87+
expectGlobalDescriptorToMatch('undefined', {
88+
enumerable: false,
89+
configurable: false,
90+
writable: false,
91+
});
92+
});
93+
});
94+
2695
describe('environment', () => {
2796
it('should provide process.env.NODE_ENV', () => {
2897
expect(process.env.NODE_ENV).toBe('development');

0 commit comments

Comments
 (0)