Skip to content

Commit 8c77bb6

Browse files
authored
misc: Disallow direct usage of globals (#3999)
1 parent ba50656 commit 8c77bb6

File tree

11 files changed

+61
-15
lines changed

11 files changed

+61
-15
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@
44

55
- "You miss 100 percent of the chances you don't take. — Wayne Gretzky" — Michael Scott
66

7+
## 6.13.2
8+
9+
- fix(browser): Use getGlobalObject for document check (#3996)
10+
711
## 6.13.1
812

913
- fix(browser): Check for document when sending outcomes (#3993)

packages/angular/src/tracing.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { AfterViewInit, Directive, Injectable, Input, NgModule, OnDestroy, OnIni
22
import { Event, NavigationEnd, NavigationStart, Router } from '@angular/router';
33
import { getCurrentHub } from '@sentry/browser';
44
import { Span, Transaction, TransactionContext } from '@sentry/types';
5-
import { logger, stripUrlQueryAndFragment, timestampWithMs } from '@sentry/utils';
5+
import { getGlobalObject, logger, stripUrlQueryAndFragment, timestampWithMs } from '@sentry/utils';
66
import { Observable, Subscription } from 'rxjs';
77
import { filter, tap } from 'rxjs/operators';
88

@@ -12,6 +12,8 @@ let instrumentationInitialized: boolean;
1212
let stashedStartTransaction: (context: TransactionContext) => Transaction | undefined;
1313
let stashedStartTransactionOnLocationChange: boolean;
1414

15+
const global = getGlobalObject<Window>();
16+
1517
/**
1618
* Creates routing instrumentation for Angular Router.
1719
*/
@@ -26,7 +28,7 @@ export function routingInstrumentation(
2628

2729
if (startTransactionOnPageLoad) {
2830
customStartTransaction({
29-
name: window.location.pathname,
31+
name: global.location.pathname,
3032
op: 'pageload',
3133
});
3234
}

packages/browser/src/helpers.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import { API, captureException, withScope } from '@sentry/core';
22
import { DsnLike, Event as SentryEvent, Mechanism, Scope, WrappedFunction } from '@sentry/types';
3-
import { addExceptionMechanism, addExceptionTypeValue, logger } from '@sentry/utils';
3+
import { addExceptionMechanism, addExceptionTypeValue, getGlobalObject, logger } from '@sentry/utils';
44

5+
const global = getGlobalObject<Window>();
56
let ignoreOnError: number = 0;
67

78
/**
@@ -193,16 +194,21 @@ export interface ReportDialogOptions {
193194
* @hidden
194195
*/
195196
export function injectReportDialog(options: ReportDialogOptions = {}): void {
197+
if (!global.document) {
198+
return;
199+
}
200+
196201
if (!options.eventId) {
197202
logger.error(`Missing eventId option in showReportDialog call`);
198203
return;
199204
}
205+
200206
if (!options.dsn) {
201207
logger.error(`Missing dsn option in showReportDialog call`);
202208
return;
203209
}
204210

205-
const script = document.createElement('script');
211+
const script = global.document.createElement('script');
206212
script.async = true;
207213
script.src = new API(options.dsn).getReportDialogEndpoint(options);
208214

@@ -211,7 +217,7 @@ export function injectReportDialog(options: ReportDialogOptions = {}): void {
211217
script.onload = options.onLoad;
212218
}
213219

214-
const injectionPoint = document.head || document.body;
220+
const injectionPoint = global.document.head || global.document.body;
215221

216222
if (injectionPoint) {
217223
injectionPoint.appendChild(script);

packages/browser/src/transports/base.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ export abstract class BaseTransport implements Transport {
5151
// eslint-disable-next-line deprecation/deprecation
5252
this.url = this._api.getStoreEndpointWithUrlEncodedAuth();
5353

54-
if (this.options.sendClientReports && global && global.document) {
54+
if (this.options.sendClientReports && global.document) {
5555
global.document.addEventListener('visibilitychange', () => {
5656
if (global.document.visibilityState === 'hidden') {
5757
this._flushOutcomes();
@@ -99,7 +99,7 @@ export abstract class BaseTransport implements Transport {
9999
return;
100100
}
101101

102-
if (!navigator || typeof navigator.sendBeacon !== 'function') {
102+
if (!global.navigator || typeof global.navigator.sendBeacon !== 'function') {
103103
logger.warn('Beacon API not available, skipping sending outcomes.');
104104
return;
105105
}
@@ -134,7 +134,7 @@ export abstract class BaseTransport implements Transport {
134134
});
135135
const envelope = `${envelopeHeader}\n${itemHeaders}\n${item}`;
136136

137-
navigator.sendBeacon(url, envelope);
137+
global.navigator.sendBeacon(url, envelope);
138138
}
139139

140140
/**

packages/eslint-config-sdk/src/index.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,30 @@ module.exports = {
117117
// Configuration for files under src
118118
files: ['src/**/*'],
119119
rules: {
120+
'no-restricted-globals': [
121+
'error',
122+
{
123+
name: 'window',
124+
message:
125+
'Some global variables are not available in environments like WebWorker or Node.js. Use getGlobalObject() instead.',
126+
},
127+
{
128+
name: 'document',
129+
message:
130+
'Some global variables are not available in environments like WebWorker or Node.js. Use getGlobalObject() instead.',
131+
},
132+
{
133+
name: 'location',
134+
message:
135+
'Some global variables are not available in environments like WebWorker or Node.js. Use getGlobalObject() instead.',
136+
},
137+
{
138+
name: 'navigator',
139+
message:
140+
'Some global variables are not available in environments like WebWorker or Node.js. Use getGlobalObject() instead.',
141+
},
142+
],
143+
120144
// We want to prevent async await usage in our files to prevent uncessary bundle size.
121145
'@sentry-internal/sdk/no-async-await': 'error',
122146

packages/tracing/src/browser/browsertracing.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,7 @@ export function getHeaderContext(): Partial<TransactionContext> | undefined {
256256

257257
/** Returns the value of a meta tag */
258258
export function getMetaContent(metaName: string): string | null {
259-
const el = document.querySelector(`meta[name=${metaName}]`);
259+
const el = getGlobalObject<Window>().document.querySelector(`meta[name=${metaName}]`);
260260
return el ? el.getAttribute('content') : null;
261261
}
262262

packages/tracing/src/browser/metrics.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ export class MetricsInstrumentation {
122122
break;
123123
}
124124
case 'resource': {
125-
const resourceName = (entry.name as string).replace(window.location.origin, '');
125+
const resourceName = (entry.name as string).replace(global.location.origin, '');
126126
const endTimestamp = addResourceSpans(transaction, entry, resourceName, startTime, duration, timeOrigin);
127127
// We remember the entry script end time to calculate the difference to the first init mark
128128
if (entryScriptStartTimestamp === undefined && (entryScriptSrc || '').indexOf(resourceName) > -1) {

packages/tracing/src/browser/web-vitals/lib/getVisibilityWatcher.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,14 @@
1414
* limitations under the License.
1515
*/
1616

17+
import { getGlobalObject } from '@sentry/utils';
18+
1719
import { onHidden } from './onHidden';
1820

1921
let firstHiddenTime = -1;
2022

2123
const initHiddenTime = (): number => {
22-
return document.visibilityState === 'hidden' ? 0 : Infinity;
24+
return getGlobalObject<Window>().document.visibilityState === 'hidden' ? 0 : Infinity;
2325
};
2426

2527
const trackChanges = (): void => {

packages/tracing/src/browser/web-vitals/lib/onHidden.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,15 @@
1414
* limitations under the License.
1515
*/
1616

17+
import { getGlobalObject } from '@sentry/utils';
18+
1719
export interface OnHiddenCallback {
1820
(event: Event): void;
1921
}
2022

2123
export const onHidden = (cb: OnHiddenCallback, once?: boolean): void => {
2224
const onHiddenOrPageHide = (event: Event): void => {
23-
if (event.type === 'pagehide' || document.visibilityState === 'hidden') {
25+
if (event.type === 'pagehide' || getGlobalObject<Window>().document.visibilityState === 'hidden') {
2426
cb(event);
2527
if (once) {
2628
removeEventListener('visibilitychange', onHiddenOrPageHide, true);

packages/utils/src/misc.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ const fallbackGlobalObject = {};
3131
export function getGlobalObject<T>(): T & SentryGlobal {
3232
return (isNodeEnv()
3333
? global
34-
: typeof window !== 'undefined'
35-
? window
34+
: typeof window !== 'undefined' // eslint-disable-line no-restricted-globals
35+
? window // eslint-disable-line no-restricted-globals
3636
: typeof self !== 'undefined'
3737
? self
3838
: fallbackGlobalObject) as T & SentryGlobal;
@@ -227,8 +227,9 @@ export function addExceptionMechanism(
227227
* A safe form of location.href
228228
*/
229229
export function getLocationHref(): string {
230+
const global = getGlobalObject<Window>();
230231
try {
231-
return document.location.href;
232+
return global.document.location.href;
232233
} catch (oO) {
233234
return '';
234235
}

packages/utils/src/object.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,10 +224,15 @@ function normalizeValue<T>(value: T, key?: any): T | string {
224224
return '[Global]';
225225
}
226226

227+
// It's safe to use `window` and `document` here in this manner, as we are asserting using `typeof` first
228+
// which won't throw if they are not present.
229+
230+
// eslint-disable-next-line no-restricted-globals
227231
if (typeof (window as any) !== 'undefined' && (value as unknown) === window) {
228232
return '[Window]';
229233
}
230234

235+
// eslint-disable-next-line no-restricted-globals
231236
if (typeof (document as any) !== 'undefined' && (value as unknown) === document) {
232237
return '[Document]';
233238
}

0 commit comments

Comments
 (0)