Skip to content

Commit

Permalink
Prevent LogBox from crashing on long messages (#38005) (#41989)
Browse files Browse the repository at this point in the history
* 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>
  • Loading branch information
fortmarek and motiz88 committed Dec 19, 2023
1 parent ed5a798 commit c3c6cf4
Show file tree
Hide file tree
Showing 5 changed files with 529 additions and 484 deletions.
2 changes: 1 addition & 1 deletion .circleci/config.yml
Expand Up @@ -202,7 +202,7 @@ commands:
# Set ruby dependencies
rbenv global << parameters.ruby_version >>
if [[ << parameters.ruby_version >> == "2.6.10" ]]; then
if [[ $(echo << parameters.ruby_version >> | awk -F'.' '{print $1}') == "2" ]]; then
gem install bundler -v 2.4.22
else
gem install bundler
Expand Down
70 changes: 50 additions & 20 deletions packages/react-native/Libraries/LogBox/Data/parseLogBoxLog.js
Expand Up @@ -14,12 +14,38 @@ import type {LogBoxLogData} from './LogBoxLog';
import parseErrorStack from '../../Core/Devtools/parseErrorStack';
import UTFSequence from '../../UTFSequence';
import stringifySafe from '../../Utilities/stringifySafe';
import ansiRegex from 'ansi-regex';

const ANSI_REGEX = ansiRegex().source;

const BABEL_TRANSFORM_ERROR_FORMAT =
/^(?:TransformError )?(?:SyntaxError: |ReferenceError: )(.*): (.*) \((\d+):(\d+)\)\n\n([\s\S]+)/;

// https://github.com/babel/babel/blob/33dbb85e9e9fe36915273080ecc42aee62ed0ade/packages/babel-code-frame/src/index.ts#L183-L184
const BABEL_CODE_FRAME_MARKER_PATTERN = new RegExp(
[
// Beginning of a line (per 'm' flag)
'^',
// Optional ANSI escapes for colors
`(?:${ANSI_REGEX})*`,
// Marker
'>',
// Optional ANSI escapes for colors
`(?:${ANSI_REGEX})*`,
// Left padding for line number
' +',
// Line number
'[0-9]+',
// Gutter
' \\|',
].join(''),
'm',
);

const BABEL_CODE_FRAME_ERROR_FORMAT =
// eslint-disable-next-line no-control-regex
/^(?:TransformError )?(?:.*):? (?:.*?)(\/.*): ([\s\S]+?)\n([ >]{2}[\d\s]+ \|[\s\S]+|\u{001b}[\s\S]+)/u;

const METRO_ERROR_FORMAT =
/^(?:InternalError Metro has encountered an error:) (.*): (.*) \((\d+):(\d+)\)\n\n([\s\S]+)/u;

Expand Down Expand Up @@ -241,27 +267,31 @@ export function parseLogBoxException(
};
}
const babelCodeFrameError = message.match(BABEL_CODE_FRAME_ERROR_FORMAT);
// Perform a cheap match first before trying to parse the full message, which
// can get expensive for arbitrary input.
if (BABEL_CODE_FRAME_MARKER_PATTERN.test(message)) {
const babelCodeFrameError = message.match(BABEL_CODE_FRAME_ERROR_FORMAT);
if (babelCodeFrameError) {
// Codeframe errors are thrown from any use of buildCodeFrameError.
const [fileName, content, codeFrame] = babelCodeFrameError.slice(1);
return {
level: 'syntax',
stack: [],
isComponentError: false,
componentStack: [],
codeFrame: {
fileName,
location: null, // We are not given the location.
content: codeFrame,
},
message: {
content,
substitutions: [],
},
category: `${fileName}-${1}-${1}`,
};
if (babelCodeFrameError) {
// Codeframe errors are thrown from any use of buildCodeFrameError.
const [fileName, content, codeFrame] = babelCodeFrameError.slice(1);
return {
level: 'syntax',
stack: [],
isComponentError: false,
componentStack: [],
codeFrame: {
fileName,
location: null, // We are not given the location.
content: codeFrame,
},
message: {
content,
substitutions: [],
},
category: `${fileName}-${1}-${1}`,
};
}
}
if (message.match(/^TransformError /)) {
Expand Down
14 changes: 14 additions & 0 deletions packages/react-native/flow-typed/npm/ansi-regex_v5.x.x.js
@@ -0,0 +1,14 @@
/**
* @flow strict
* @format
*/

declare module 'ansi-regex' {
declare export type Options = {
/**
* Match only the first ANSI escape.
*/
+onlyFirst?: boolean,
};
declare export default function ansiRegex(options?: Options): RegExp;
}
3 changes: 2 additions & 1 deletion packages/react-native/package.json
Expand Up @@ -92,6 +92,7 @@
"anser": "^1.4.9",
"base64-js": "^1.1.2",
"deprecated-react-native-prop-types": "^4.2.3",
"ansi-regex": "^5.0.0",
"event-target-shim": "^5.0.1",
"flow-enums-runtime": "^0.0.5",
"invariant": "^2.2.4",
Expand Down Expand Up @@ -133,4 +134,4 @@
}
]
}
}
}

0 comments on commit c3c6cf4

Please sign in to comment.