@@ -2,11 +2,14 @@ import type { StackFrame } from '@sentry/core';
22
33import { afterEach , beforeEach , describe , expect , it , jest } from '@jest/globals' ;
44import * as fs from 'fs' ;
5+ import * as path from 'path' ;
56
67import * as openUrlMiddlewareModule from '../../src/js/metro/openUrlMiddleware' ;
78import * as metroMiddleware from '../../src/js/tools/metroMiddleware' ;
89
9- const { withSentryMiddleware, createSentryMetroMiddleware, stackFramesContextMiddleware } = metroMiddleware ;
10+ const { withSentryMiddleware, createSentryMetroMiddleware, createStackFramesContextMiddleware } = metroMiddleware ;
11+
12+ const TEST_PROJECT_ROOT = path . resolve ( '/tmp/sentry-rn-test-project' ) ;
1013
1114jest . mock ( '../../src/js/tools/metroMiddleware' , ( ) => jest . requireActual ( '../../src/js/tools/metroMiddleware' ) ) ;
1215jest . mock ( 'fs' , ( ) => {
@@ -60,65 +63,69 @@ describe('metroMiddleware', () => {
6063 const next = jest . fn ( ) ;
6164 const response = { } as any ;
6265
63- let spiedStackFramesContextMiddleware : jest . Spied < typeof stackFramesContextMiddleware > ;
66+ let spiedCreateStackFramesContextMiddleware : jest . Spied < typeof createStackFramesContextMiddleware > ;
67+ let mockedStackFramesContextMiddleware : jest . Mock ;
6468
6569 beforeEach ( ( ) => {
6670 jest . clearAllMocks ( ) ;
67- spiedStackFramesContextMiddleware = jest
68- . spyOn ( metroMiddleware , 'stackFramesContextMiddleware' )
69- . mockReturnValue ( undefined ) ;
71+ mockedStackFramesContextMiddleware = jest . fn ( ) ;
72+ spiedCreateStackFramesContextMiddleware = jest
73+ . spyOn ( metroMiddleware , 'createStackFramesContextMiddleware' )
74+ . mockReturnValue ( mockedStackFramesContextMiddleware ) ;
7075 } ) ;
7176
7277 afterEach ( ( ) => {
7378 jest . resetAllMocks ( ) ;
7479 } ) ;
7580
7681 it ( 'should call stackFramesContextMiddleware for sentry context requests' , ( ) => {
77- const testedMiddleware = createSentryMetroMiddleware ( defaultMiddleware ) ;
82+ const testedMiddleware = createSentryMetroMiddleware ( defaultMiddleware , TEST_PROJECT_ROOT ) ;
7883
7984 const sentryRequest = {
8085 url : '/__sentry/context' ,
8186 } as any ;
8287 testedMiddleware ( sentryRequest , response , next ) ;
8388 expect ( defaultMiddleware ) . not . toHaveBeenCalled ( ) ;
84- expect ( spiedStackFramesContextMiddleware ) . toHaveBeenCalledWith ( sentryRequest , response , next ) ;
89+ expect ( spiedCreateStackFramesContextMiddleware ) . toHaveBeenCalledWith ( TEST_PROJECT_ROOT ) ;
90+ expect ( mockedStackFramesContextMiddleware ) . toHaveBeenCalledWith ( sentryRequest , response , next ) ;
8591 } ) ;
8692
8793 it ( 'should call openURLMiddleware for sentry open-url requests' , ( ) => {
8894 const spiedOpenURLMiddleware = jest
8995 . spyOn ( openUrlMiddlewareModule , 'openURLMiddleware' )
9096 . mockReturnValue ( undefined as any ) ;
9197
92- const testedMiddleware = createSentryMetroMiddleware ( defaultMiddleware ) ;
98+ const testedMiddleware = createSentryMetroMiddleware ( defaultMiddleware , TEST_PROJECT_ROOT ) ;
9399
94100 const openUrlRequest = {
95101 url : '/__sentry/open-url' ,
96102 } as any ;
97103 testedMiddleware ( openUrlRequest , response , next ) ;
98104 expect ( defaultMiddleware ) . not . toHaveBeenCalled ( ) ;
99- expect ( spiedStackFramesContextMiddleware ) . not . toHaveBeenCalled ( ) ;
105+ expect ( mockedStackFramesContextMiddleware ) . not . toHaveBeenCalled ( ) ;
100106 expect ( spiedOpenURLMiddleware ) . toHaveBeenCalledWith ( openUrlRequest , response ) ;
101107
102108 spiedOpenURLMiddleware . mockRestore ( ) ;
103109 } ) ;
104110
105111 it ( 'should call default middleware for non-sentry requests' , ( ) => {
106- const testedMiddleware = createSentryMetroMiddleware ( defaultMiddleware ) ;
112+ const testedMiddleware = createSentryMetroMiddleware ( defaultMiddleware , TEST_PROJECT_ROOT ) ;
107113
108114 const regularRequest = {
109115 url : '/regular/path' ,
110116 } as any ;
111117 testedMiddleware ( regularRequest , response , next ) ;
112118 expect ( defaultMiddleware ) . toHaveBeenCalledWith ( regularRequest , response , next ) ;
113119 expect ( defaultMiddleware ) . toHaveBeenCalledTimes ( 1 ) ;
114- expect ( spiedStackFramesContextMiddleware ) . not . toHaveBeenCalled ( ) ;
120+ expect ( mockedStackFramesContextMiddleware ) . not . toHaveBeenCalled ( ) ;
115121 } ) ;
116122 } ) ;
117123
118124 describe ( 'stackFramesContextMiddleware' , ( ) => {
119125 let request : any ;
120126 let response : any ;
121127 const next = jest . fn ( ) ;
128+ const stackFramesContextMiddleware = createStackFramesContextMiddleware ( TEST_PROJECT_ROOT ) ;
122129
123130 let testData : string = '' ;
124131
@@ -201,7 +208,7 @@ describe('metroMiddleware', () => {
201208 ] ,
202209 } satisfies { stack : StackFrame [ ] } ) ;
203210
204- mockReadFileOnce ( readFileSpy , 'test.js' , 'line1\nline2\nline3\nline4\nline5' ) ;
211+ mockReadFileOnce ( readFileSpy , path . join ( TEST_PROJECT_ROOT , 'test.js' ) , 'line1\nline2\nline3\nline4\nline5' ) ;
205212
206213 await stackFramesContextMiddleware ( request , response , next ) ;
207214
@@ -282,10 +289,55 @@ describe('metroMiddleware', () => {
282289 } ) ;
283290 } ) ;
284291
292+ it ( 'should skip frames whose filename escapes the project root' , async ( ) => {
293+ const readFileSpy = jest . spyOn ( fs , 'readFile' ) ;
294+ testData = JSON . stringify ( {
295+ stack : [
296+ {
297+ in_app : true ,
298+ filename : '/etc/passwd' ,
299+ function : 'testFunction' ,
300+ lineno : 1 ,
301+ colno : 1 ,
302+ } ,
303+ {
304+ in_app : true ,
305+ filename : '../outside.js' ,
306+ function : 'testFunction' ,
307+ lineno : 1 ,
308+ colno : 1 ,
309+ } ,
310+ ] ,
311+ } satisfies { stack : StackFrame [ ] } ) ;
312+
313+ await stackFramesContextMiddleware ( request , response , next ) ;
314+
315+ expect ( readFileSpy ) . not . toHaveBeenCalled ( ) ;
316+ expect ( response . statusCode ) . toBe ( 200 ) ;
317+ expect ( JSON . parse ( response . end . mock . calls [ 0 ] [ 0 ] ) ) . toEqual ( {
318+ stack : [
319+ {
320+ in_app : true ,
321+ filename : '/etc/passwd' ,
322+ function : 'testFunction' ,
323+ lineno : 1 ,
324+ colno : 1 ,
325+ } ,
326+ {
327+ in_app : true ,
328+ filename : '../outside.js' ,
329+ function : 'testFunction' ,
330+ lineno : 1 ,
331+ colno : 1 ,
332+ } ,
333+ ] ,
334+ } ) ;
335+ } ) ;
336+
285337 it ( 'should handle mixed frame types correctly' , async ( ) => {
286338 const readFileSpy = jest . spyOn ( fs , 'readFile' ) ;
287- mockReadFileOnce ( readFileSpy , 'app1.js' , 'line1\nline2\nline3\nline4\nline5' ) ;
288- mockReadFileOnce ( readFileSpy , 'app2.js' , 'code1\ncode2\ncode3\ncode4\ncode5' ) ;
339+ mockReadFileOnce ( readFileSpy , path . join ( TEST_PROJECT_ROOT , 'app1.js' ) , 'line1\nline2\nline3\nline4\nline5' ) ;
340+ mockReadFileOnce ( readFileSpy , path . join ( TEST_PROJECT_ROOT , 'app2.js' ) , 'code1\ncode2\ncode3\ncode4\ncode5' ) ;
289341
290342 testData = JSON . stringify ( {
291343 stack : [
0 commit comments