Skip to content

Commit c3c6cf4

Browse files
fortmarekmotiz88
andauthored
Prevent LogBox from crashing on long messages (#38005) (#41989)
* Prevent LogBox from crashing on long messages (#38005) Summary: Pull Request resolved: #38005 Fixes #32093 by guarding the expensive `BABEL_CODE_FRAME_ERROR_FORMAT` regex with a cheaper initial scan. (Longer term, we should reduce our reliance on string parsing and propagate more structured errors.) Changelog: [General][Fixed] Prevent LogBox from crashing on very long messages Reviewed By: GijsWeterings Differential Revision: D46892454 fbshipit-source-id: 3afadcdd75969c2589bbb06f47d1c4c1c2690abd * Update RNTester Podfile.lock * Check for major ruby version --------- Co-authored-by: Moti Zilberman <moti@meta.com>
1 parent ed5a798 commit c3c6cf4

File tree

5 files changed

+529
-484
lines changed

5 files changed

+529
-484
lines changed

.circleci/config.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ commands:
202202
203203
# Set ruby dependencies
204204
rbenv global << parameters.ruby_version >>
205-
if [[ << parameters.ruby_version >> == "2.6.10" ]]; then
205+
if [[ $(echo << parameters.ruby_version >> | awk -F'.' '{print $1}') == "2" ]]; then
206206
gem install bundler -v 2.4.22
207207
else
208208
gem install bundler

packages/react-native/Libraries/LogBox/Data/parseLogBoxLog.js

Lines changed: 50 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,38 @@ import type {LogBoxLogData} from './LogBoxLog';
1414
import parseErrorStack from '../../Core/Devtools/parseErrorStack';
1515
import UTFSequence from '../../UTFSequence';
1616
import stringifySafe from '../../Utilities/stringifySafe';
17+
import ansiRegex from 'ansi-regex';
18+
19+
const ANSI_REGEX = ansiRegex().source;
1720

1821
const BABEL_TRANSFORM_ERROR_FORMAT =
1922
/^(?:TransformError )?(?:SyntaxError: |ReferenceError: )(.*): (.*) \((\d+):(\d+)\)\n\n([\s\S]+)/;
23+
24+
// https://github.com/babel/babel/blob/33dbb85e9e9fe36915273080ecc42aee62ed0ade/packages/babel-code-frame/src/index.ts#L183-L184
25+
const BABEL_CODE_FRAME_MARKER_PATTERN = new RegExp(
26+
[
27+
// Beginning of a line (per 'm' flag)
28+
'^',
29+
// Optional ANSI escapes for colors
30+
`(?:${ANSI_REGEX})*`,
31+
// Marker
32+
'>',
33+
// Optional ANSI escapes for colors
34+
`(?:${ANSI_REGEX})*`,
35+
// Left padding for line number
36+
' +',
37+
// Line number
38+
'[0-9]+',
39+
// Gutter
40+
' \\|',
41+
].join(''),
42+
'm',
43+
);
44+
2045
const BABEL_CODE_FRAME_ERROR_FORMAT =
2146
// eslint-disable-next-line no-control-regex
2247
/^(?:TransformError )?(?:.*):? (?:.*?)(\/.*): ([\s\S]+?)\n([ >]{2}[\d\s]+ \|[\s\S]+|\u{001b}[\s\S]+)/u;
48+
2349
const METRO_ERROR_FORMAT =
2450
/^(?:InternalError Metro has encountered an error:) (.*): (.*) \((\d+):(\d+)\)\n\n([\s\S]+)/u;
2551

@@ -241,27 +267,31 @@ export function parseLogBoxException(
241267
};
242268
}
243269

244-
const babelCodeFrameError = message.match(BABEL_CODE_FRAME_ERROR_FORMAT);
270+
// Perform a cheap match first before trying to parse the full message, which
271+
// can get expensive for arbitrary input.
272+
if (BABEL_CODE_FRAME_MARKER_PATTERN.test(message)) {
273+
const babelCodeFrameError = message.match(BABEL_CODE_FRAME_ERROR_FORMAT);
245274

246-
if (babelCodeFrameError) {
247-
// Codeframe errors are thrown from any use of buildCodeFrameError.
248-
const [fileName, content, codeFrame] = babelCodeFrameError.slice(1);
249-
return {
250-
level: 'syntax',
251-
stack: [],
252-
isComponentError: false,
253-
componentStack: [],
254-
codeFrame: {
255-
fileName,
256-
location: null, // We are not given the location.
257-
content: codeFrame,
258-
},
259-
message: {
260-
content,
261-
substitutions: [],
262-
},
263-
category: `${fileName}-${1}-${1}`,
264-
};
275+
if (babelCodeFrameError) {
276+
// Codeframe errors are thrown from any use of buildCodeFrameError.
277+
const [fileName, content, codeFrame] = babelCodeFrameError.slice(1);
278+
return {
279+
level: 'syntax',
280+
stack: [],
281+
isComponentError: false,
282+
componentStack: [],
283+
codeFrame: {
284+
fileName,
285+
location: null, // We are not given the location.
286+
content: codeFrame,
287+
},
288+
message: {
289+
content,
290+
substitutions: [],
291+
},
292+
category: `${fileName}-${1}-${1}`,
293+
};
294+
}
265295
}
266296

267297
if (message.match(/^TransformError /)) {
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/**
2+
* @flow strict
3+
* @format
4+
*/
5+
6+
declare module 'ansi-regex' {
7+
declare export type Options = {
8+
/**
9+
* Match only the first ANSI escape.
10+
*/
11+
+onlyFirst?: boolean,
12+
};
13+
declare export default function ansiRegex(options?: Options): RegExp;
14+
}

packages/react-native/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@
9292
"anser": "^1.4.9",
9393
"base64-js": "^1.1.2",
9494
"deprecated-react-native-prop-types": "^4.2.3",
95+
"ansi-regex": "^5.0.0",
9596
"event-target-shim": "^5.0.1",
9697
"flow-enums-runtime": "^0.0.5",
9798
"invariant": "^2.2.4",
@@ -133,4 +134,4 @@
133134
}
134135
]
135136
}
136-
}
137+
}

0 commit comments

Comments
 (0)