Skip to content

Commit 3464937

Browse files
committed
add platform-specific base64 methods and attach them to the carrier
1 parent 442ccc7 commit 3464937

File tree

5 files changed

+99
-4
lines changed

5 files changed

+99
-4
lines changed

packages/browser/src/helpers.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,3 +213,40 @@ export function injectReportDialog(options: ReportDialogOptions = {}): void {
213213

214214
(document.head || document.body).appendChild(script);
215215
}
216+
217+
/**
218+
* Convert a base64-encoded string back to plaintext. Does not include error handling.
219+
*
220+
* (This is here (and in the node package) rather than in @sentry/utils to prevent webpack from bundling the entire
221+
* Buffer module when creating SDK users' vendor bundles for the browser. Used in @sentry/utils.string.)
222+
*
223+
* @param base64String The base64-encoded string
224+
* @returns A plaintext version of the string, in JS's native encoding (UTF-16)
225+
*/
226+
export function unsafeFromBase64(base64String: string): string {
227+
// `atob` returns a string rather than bytes, so we then need to encode using the native encoding (UTF-16)
228+
const bytesAsString = atob(base64String);
229+
const bytes = [...bytesAsString].map(char => char.charCodeAt(0));
230+
231+
// decode using UTF-8 (cast the `bytes` arry to a Uint8Array just because that's the format `decode()` expects)
232+
return new TextDecoder().decode(Uint8Array.from(bytes));
233+
}
234+
235+
/**
236+
* Convert a string to base64 encoding. Does not include error handling.
237+
*
238+
* (This is here (and in the node package) rather than in @sentry/utils to prevent webpack from bundling the entire
239+
* Buffer module when creating SDK users' vendor bundles for the browser. Used in @sentry/utils.string.)
240+
*
241+
* @param plaintext The original string, in JS's native encoding (UTF-16)
242+
* @returns A base64-encoded version of the string
243+
*/
244+
export function unsafeToBase64(plaintext: string): string {
245+
// encode using UTF-8
246+
const bytes = new TextEncoder().encode(plaintext);
247+
248+
// decode using UTF-16 (JS's native encoding) since `btoa` requires string input
249+
const bytesAsString = String.fromCharCode(...bytes);
250+
251+
return btoa(bytesAsString);
252+
}

packages/browser/src/index.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
export * from './exports';
22

33
import { Integrations as CoreIntegrations } from '@sentry/core';
4-
import { getGlobalObject } from '@sentry/utils';
4+
import { addBase64MethodsToCarrier, getGlobalObject } from '@sentry/utils';
55

6+
import { unsafeFromBase64, unsafeToBase64 } from './helpers';
67
import * as BrowserIntegrations from './integrations';
78
import * as Transports from './transports';
89

@@ -21,3 +22,8 @@ const INTEGRATIONS = {
2122
};
2223

2324
export { INTEGRATIONS as Integrations, Transports };
25+
26+
// We need to patch these string utils on the global __SENTRY__ object to make them work for both browser and node in
27+
// cross-platform packages like @sentry/utils. If we don't do this, browser bundlers will mistakenly include node's
28+
// Buffer module.
29+
addBase64MethodsToCarrier({ unsafeFromBase64, unsafeToBase64 });

packages/node/src/index.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,12 @@ export { defaultIntegrations, init, lastEventId, flush, close } from './sdk';
4444
export { SDK_NAME } from './version';
4545

4646
import { Integrations as CoreIntegrations } from '@sentry/core';
47-
import { getMainCarrier } from '@sentry/utils';
47+
import { addBase64MethodsToCarrier, getMainCarrier } from '@sentry/utils';
4848
import * as domain from 'domain';
4949

5050
import * as Handlers from './handlers';
5151
import * as NodeIntegrations from './integrations';
52+
import { unsafeFromBase64, unsafeToBase64 } from './string';
5253
import * as Transports from './transports';
5354

5455
const INTEGRATIONS = {
@@ -58,10 +59,12 @@ const INTEGRATIONS = {
5859

5960
export { INTEGRATIONS as Integrations, Transports, Handlers };
6061

61-
// We need to patch domain on the global __SENTRY__ object to make it work for node in cross-platform packages like
62-
// @sentry/hub. If we don't do this, browser bundlers will have troubles resolving `require('domain')`.
62+
// We need to patch domain and our string utils on the global __SENTRY__ object to make them work for node in
63+
// cross-platform packages like @sentry/hub. If we don't do this, browser bundlers will have troubles resolving
64+
// `require('domain')` and will mistakenly include node's Buffer module.
6365
const carrier = getMainCarrier();
6466
if (carrier.__SENTRY__) {
6567
carrier.__SENTRY__.extensions = carrier.__SENTRY__.extensions || {};
6668
carrier.__SENTRY__.extensions.domain = carrier.__SENTRY__.extensions.domain || domain;
6769
}
70+
addBase64MethodsToCarrier({ unsafeFromBase64, unsafeToBase64 });

packages/node/src/string.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/**
2+
* Convert a base64-encoded string back to plaintext. Does not include error handling.
3+
*
4+
* (This is here (and in the browser package) rather than in @sentry/utils to prevent webpack from bundling the entire
5+
* Buffer module when creating SDK users' vendor bundles for the browser. Used in @sentry/utils.string.)
6+
*
7+
* @param base64String The base64-encoded string
8+
* @returns A plaintext version of the string, in Node's native encoding (UTF-8)
9+
*/
10+
export function unsafeFromBase64(base64String: string): string {
11+
// unlike the browser, Node can go straight from base64 to bytes
12+
const bytes = Buffer.from(base64String, 'base64');
13+
14+
// decode using UTF-8
15+
return bytes.toString('utf-8');
16+
}
17+
/**
18+
* Convert a string to base64 encoding. Does not include error handling.
19+
*
20+
* (This is here (and in the browser package) rather than in @sentry/utils to prevent webpack from bundling the entire
21+
* Buffer module when creating SDK users' vendor bundles for the browser. Used in @sentry/utils.string.)
22+
*
23+
* @param plaintext The original string, in Node's native encoding (UTF-8)
24+
* @returns A base64-encoded version of the string
25+
*/
26+
export function unsafeToBase64(plaintext: string): string {
27+
// encode using UTF-8
28+
const bytes = Buffer.from(plaintext, 'utf-8');
29+
30+
// unlike the browser, Node can go straight from bytes to base64
31+
return bytes.toString('base64');
32+
}

packages/utils/src/compat.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,3 +52,20 @@ export function getMainCarrier(): Carrier {
5252
};
5353
return carrier;
5454
}
55+
56+
/**
57+
* Add base64 conversion methods to Sentry's internal extensions. This is necessary to do separately for each platform
58+
* so that bundlers don't include Node's `Buffer` package in SDK users' browser bundles.
59+
*/
60+
export function addBase64MethodsToCarrier(methods: {
61+
unsafeToBase64: (plaintext: string) => string;
62+
unsafeFromBase64: (plaintext: string) => string;
63+
}): void {
64+
const { unsafeToBase64, unsafeFromBase64 } = methods;
65+
const carrier = getMainCarrier();
66+
if (carrier.__SENTRY__) {
67+
carrier.__SENTRY__.extensions = carrier.__SENTRY__.extensions || {};
68+
carrier.__SENTRY__.extensions.unsafeFromBase64 = carrier.__SENTRY__.extensions.unsafeFromBase64 || unsafeFromBase64;
69+
carrier.__SENTRY__.extensions.unsafeToBase64 = carrier.__SENTRY__.extensions.unsafeToBase64 || unsafeToBase64;
70+
}
71+
}

0 commit comments

Comments
 (0)