Skip to content

Commit ea717f4

Browse files
authored
feat(replay): Add "maxCanvasSize" option for replay canvases (#11617)
We do not want to allow users to capture super large canvases, enforce a max size limit Requires getsentry/rrweb#174 Closes #10498
1 parent 144a5f0 commit ea717f4

File tree

7 files changed

+96
-29
lines changed

7 files changed

+96
-29
lines changed

dev-packages/browser-integration-tests/suites/replay/canvas/manualSnapshot/test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ sentryTest('can manually snapshot canvas', async ({ getLocalTestUrl, page, brows
6363
},
6464
0,
6565
0,
66+
150,
67+
150,
6668
],
6769
property: 'drawImage',
6870
},

dev-packages/browser-integration-tests/suites/replay/canvas/records/test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ sentryTest('can record canvas', async ({ getLocalTestUrl, page, browserName }) =
6565
},
6666
0,
6767
0,
68+
150,
69+
150,
6870
],
6971
property: 'drawImage',
7072
},

packages/replay-canvas/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@
6969
"homepage": "https://docs.sentry.io/platforms/javascript/session-replay/",
7070
"devDependencies": {
7171
"@babel/core": "^7.17.5",
72-
"@sentry-internal/rrweb": "2.12.0"
72+
"@sentry-internal/rrweb": "2.13.0"
7373
},
7474
"dependencies": {
7575
"@sentry-internal/replay": "8.0.0-beta.3",

packages/replay-canvas/src/canvas.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,14 @@ import type { IntegrationFn } from '@sentry/types';
55

66
interface ReplayCanvasOptions {
77
enableManualSnapshot?: boolean;
8+
maxCanvasSize?: [width: number, height: number];
89
quality: 'low' | 'medium' | 'high';
910
}
1011

1112
type GetCanvasManager = (options: CanvasManagerOptions) => CanvasManagerInterface;
1213
export interface ReplayCanvasIntegrationOptions {
1314
enableManualSnapshot?: boolean;
15+
maxCanvasSize?: number;
1416
recordCanvas: true;
1517
getCanvasManager: GetCanvasManager;
1618
sampling: {
@@ -53,12 +55,18 @@ const CANVAS_QUALITY = {
5355
};
5456

5557
const INTEGRATION_NAME = 'ReplayCanvas';
58+
const DEFAULT_MAX_CANVAS_SIZE = 1280;
5659

5760
/** Exported only for type safe tests. */
5861
export const _replayCanvasIntegration = ((options: Partial<ReplayCanvasOptions> = {}) => {
62+
const [maxCanvasWidth, maxCanvasHeight] = options.maxCanvasSize || [];
5963
const _canvasOptions = {
6064
quality: options.quality || 'medium',
6165
enableManualSnapshot: options.enableManualSnapshot,
66+
maxCanvasSize: [
67+
maxCanvasWidth ? Math.min(maxCanvasWidth, DEFAULT_MAX_CANVAS_SIZE) : DEFAULT_MAX_CANVAS_SIZE,
68+
maxCanvasHeight ? Math.min(maxCanvasHeight, DEFAULT_MAX_CANVAS_SIZE) : DEFAULT_MAX_CANVAS_SIZE,
69+
] as [number, number],
6270
};
6371

6472
let canvasManagerResolve: (value: CanvasManager) => void;
@@ -67,15 +75,16 @@ export const _replayCanvasIntegration = ((options: Partial<ReplayCanvasOptions>
6775
return {
6876
name: INTEGRATION_NAME,
6977
getOptions(): ReplayCanvasIntegrationOptions {
70-
const { quality, enableManualSnapshot } = _canvasOptions;
78+
const { quality, enableManualSnapshot, maxCanvasSize } = _canvasOptions;
7179

7280
return {
7381
enableManualSnapshot,
7482
recordCanvas: true,
75-
getCanvasManager: (options: CanvasManagerOptions) => {
83+
getCanvasManager: (getCanvasManagerOptions: CanvasManagerOptions) => {
7684
const manager = new CanvasManager({
77-
...options,
85+
...getCanvasManagerOptions,
7886
enableManualSnapshot,
87+
maxCanvasSize,
7988
errorHandler: (err: unknown) => {
8089
try {
8190
if (typeof err === 'object') {

packages/replay-canvas/test/canvas.test.ts

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,17 @@
1+
import { CanvasManager } from '@sentry-internal/rrweb';
12
import { _replayCanvasIntegration } from '../src/canvas';
23

4+
jest.mock('@sentry-internal/rrweb');
5+
6+
beforeEach(() => {
7+
jest.clearAllMocks();
8+
});
9+
310
it('initializes with default options', () => {
411
const rc = _replayCanvasIntegration();
12+
const options = rc.getOptions();
513

6-
expect(rc.getOptions()).toEqual({
14+
expect(options).toEqual({
715
recordCanvas: true,
816
getCanvasManager: expect.any(Function),
917
sampling: {
@@ -14,12 +22,22 @@ it('initializes with default options', () => {
1422
quality: 0.4,
1523
},
1624
});
25+
26+
// @ts-expect-error don't care about the normal options we need to call this with, just want to test maxCanvasSize
27+
options.getCanvasManager({});
28+
29+
expect(CanvasManager).toHaveBeenCalledWith(
30+
expect.objectContaining({
31+
maxCanvasSize: [1280, 1280],
32+
}),
33+
);
1734
});
1835

1936
it('initializes with quality option and manual snapshot', () => {
2037
const rc = _replayCanvasIntegration({ enableManualSnapshot: true, quality: 'low' });
38+
const options = rc.getOptions();
2139

22-
expect(rc.getOptions()).toEqual({
40+
expect(options).toEqual({
2341
enableManualSnapshot: true,
2442
recordCanvas: true,
2543
getCanvasManager: expect.any(Function),
@@ -31,4 +49,40 @@ it('initializes with quality option and manual snapshot', () => {
3149
quality: 0.25,
3250
},
3351
});
52+
53+
// @ts-expect-error don't care about the normal options we need to call this with, just want to test maxCanvasSize
54+
options.getCanvasManager({});
55+
56+
expect(CanvasManager).toHaveBeenCalledWith(
57+
expect.objectContaining({
58+
maxCanvasSize: [1280, 1280],
59+
}),
60+
);
61+
});
62+
63+
it('enforces a max canvas size', () => {
64+
const rc = _replayCanvasIntegration({ enableManualSnapshot: true, quality: 'low', maxCanvasSize: [2000, 2000] });
65+
const options = rc.getOptions();
66+
67+
expect(options).toEqual({
68+
enableManualSnapshot: true,
69+
recordCanvas: true,
70+
getCanvasManager: expect.any(Function),
71+
sampling: {
72+
canvas: 1,
73+
},
74+
dataURLOptions: {
75+
type: 'image/webp',
76+
quality: 0.25,
77+
},
78+
});
79+
80+
// @ts-expect-error don't care about the normal options we need to call this with, just want to test maxCanvasSize
81+
options.getCanvasManager({});
82+
83+
expect(CanvasManager).toHaveBeenCalledWith(
84+
expect.objectContaining({
85+
maxCanvasSize: [1280, 1280],
86+
}),
87+
);
3488
});

packages/replay-internal/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,8 @@
7070
"devDependencies": {
7171
"@babel/core": "^7.17.5",
7272
"@sentry-internal/replay-worker": "8.0.0-beta.3",
73-
"@sentry-internal/rrweb": "2.12.0",
74-
"@sentry-internal/rrweb-snapshot": "2.12.0",
73+
"@sentry-internal/rrweb": "2.13.0",
74+
"@sentry-internal/rrweb-snapshot": "2.13.0",
7575
"fflate": "^0.8.1",
7676
"jsdom-worker": "^0.2.1"
7777
},

yarn.lock

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -6297,22 +6297,22 @@
62976297
dependencies:
62986298
"@sentry-internal/rrweb-snapshot" "2.11.0"
62996299

6300-
"@sentry-internal/rrdom@2.12.0":
6301-
version "2.12.0"
6302-
resolved "https://registry.yarnpkg.com/@sentry-internal/rrdom/-/rrdom-2.12.0.tgz#d3ca32b1e4b8c5d8cc9bdb44f933fe4b059573a0"
6303-
integrity sha512-EQ9vmhkTREdtzKp6SmD4GEkwr+RJcaEnbVcDZjbnQnxagskOpqvXjoPMONPf9hZhkULwnrnyFGGp0VpQOGBS0w==
6300+
"@sentry-internal/rrdom@2.13.0":
6301+
version "2.13.0"
6302+
resolved "https://registry.yarnpkg.com/@sentry-internal/rrdom/-/rrdom-2.13.0.tgz#3bb77fd67e72f743d33699431d8f66efb193e951"
6303+
integrity sha512-Idm+phUohY74mu9KxFX+lhBqHAN7qTMB6TZGf1hKBn8CusGm91jdEoBe4xqwesnthe8a37svkf8D3CIsiblZPA==
63046304
dependencies:
6305-
"@sentry-internal/rrweb-snapshot" "2.12.0"
6305+
"@sentry-internal/rrweb-snapshot" "2.13.0"
63066306

63076307
"@sentry-internal/[email protected]":
63086308
version "2.11.0"
63096309
resolved "https://registry.yarnpkg.com/@sentry-internal/rrweb-snapshot/-/rrweb-snapshot-2.11.0.tgz#1af79130604afea989d325465b209ac015b27c9a"
63106310
integrity sha512-1nP22QlplMNooSNvTh+L30NSZ+E3UcfaJyxXSMLxUjQHTGPyM1VkndxZMmxlKhyR5X+rLbxi/+RvuAcpM43VoA==
63116311

6312-
"@sentry-internal/rrweb-snapshot@2.12.0":
6313-
version "2.12.0"
6314-
resolved "https://registry.yarnpkg.com/@sentry-internal/rrweb-snapshot/-/rrweb-snapshot-2.12.0.tgz#2f1f6d4867a07ab757475fb4fa337d7f1aaa6b2d"
6315-
integrity sha512-AYo8CeDA7qDOKFG75E+bnxrS/qm7l5Ad0ftClA3VzoGV58bNNgv/aKiECtUPk0UPs4EqTQ8z8W/MZ9EYDF6vvA==
6312+
"@sentry-internal/rrweb-snapshot@2.13.0":
6313+
version "2.13.0"
6314+
resolved "https://registry.yarnpkg.com/@sentry-internal/rrweb-snapshot/-/rrweb-snapshot-2.13.0.tgz#47fb017b2031075cdd4f1b54c098c0bd8eb75e16"
6315+
integrity sha512-ftSybKlmddX9QsLXq02gMiWfuXEfyjysSJe0tvKxGMP2r1y4rS5h2qjJeKx+GYPhcGi1s48KkjphLNwHehqf4g==
63166316

63176317
"@sentry-internal/[email protected]":
63186318
version "2.11.0"
@@ -6321,12 +6321,12 @@
63216321
dependencies:
63226322
"@sentry-internal/rrweb-snapshot" "2.11.0"
63236323

6324-
"@sentry-internal/rrweb-types@2.12.0":
6325-
version "2.12.0"
6326-
resolved "https://registry.yarnpkg.com/@sentry-internal/rrweb-types/-/rrweb-types-2.12.0.tgz#f7c57eda7610882c71860437657ffbbcb788184d"
6327-
integrity sha512-W0iLlTx3HeapBTGjg/uLoKQr1/DGPbkANqwjf4mW0IS4jHAVcxFX/e769aHHKEmd68Lm3+A8b08xdA9UDBXW5w==
6324+
"@sentry-internal/rrweb-types@2.13.0":
6325+
version "2.13.0"
6326+
resolved "https://registry.yarnpkg.com/@sentry-internal/rrweb-types/-/rrweb-types-2.13.0.tgz#b68b9cf03c51626051bed9f8c41a40f1cf362991"
6327+
integrity sha512-noG66TvuN5xJuQAPfxHECW84XUsjYR95fytH6tSvPQQh7a0JUC+i5H76YSWdGhbu5eCMcrypzgATfw/GEN7bPA==
63286328
dependencies:
6329-
"@sentry-internal/rrweb-snapshot" "2.12.0"
6329+
"@sentry-internal/rrweb-snapshot" "2.13.0"
63306330

63316331
"@sentry-internal/[email protected]":
63326332
version "2.11.0"
@@ -6342,14 +6342,14 @@
63426342
fflate "^0.4.4"
63436343
mitt "^3.0.0"
63446344

6345-
"@sentry-internal/rrweb@2.12.0":
6346-
version "2.12.0"
6347-
resolved "https://registry.yarnpkg.com/@sentry-internal/rrweb/-/rrweb-2.12.0.tgz#4becbedf7315f4b4e0ebc35319a848ec6f082dce"
6348-
integrity sha512-NosAF5f8dXdj6linXpI+e38/eKVtwy3R2rzmMohBCwdhPXgTkTV/Laj/9OsRxARNRyz81mIEGcn/Ivp/De7RaA==
6345+
"@sentry-internal/rrweb@2.13.0":
6346+
version "2.13.0"
6347+
resolved "https://registry.yarnpkg.com/@sentry-internal/rrweb/-/rrweb-2.13.0.tgz#27f997a5922fa4af5990198b62a149bbf4c18833"
6348+
integrity sha512-Xi+Sg7T8+1UbIaW5l5zKNb+X7FlD7O0l8TZwZjMXVGQtfQcJTkPkrALspqZfUeaAtX+rQHQArcfpNhQWiDYezQ==
63496349
dependencies:
6350-
"@sentry-internal/rrdom" "2.12.0"
6351-
"@sentry-internal/rrweb-snapshot" "2.12.0"
6352-
"@sentry-internal/rrweb-types" "2.12.0"
6350+
"@sentry-internal/rrdom" "2.13.0"
6351+
"@sentry-internal/rrweb-snapshot" "2.13.0"
6352+
"@sentry-internal/rrweb-types" "2.13.0"
63536353
"@types/css-font-loading-module" "0.0.7"
63546354
"@xstate/fsm" "^1.4.0"
63556355
base64-arraybuffer "^1.0.1"

0 commit comments

Comments
 (0)