Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix ReDoS when parsing colors #3382

Merged
merged 1 commit into from
Jul 25, 2022

Conversation

matias-la
Copy link
Contributor

Description

The regular expression used to parse numbers could be abused to cause a denial of service if the color to be parsed is controlled by an attacker.

For information about this vulnerability, see https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS.

Changes

Changed the code so the new regex isn't vulnerable, since there's always only one possible path to take given any input.

Here is a proof-of-concept of how I made the existing regular expression cause a 100% CPU usage for several seconds, after being interrupted:

Welcome to Node.js v14.19.3.
Type ".help" for more information.
> (new RegExp('^[-+]?\\d*\\.?\\d+$')).test(`${'1'.repeat(1e6)}z`)
^CUncaught Error: Script execution was interrupted by `SIGINT`
    at Script.runInThisContext (vm.js:134:12)
    at REPLServer.defaultEval (repl.js:566:29)
    at bound (domain.js:421:15)
    at REPLServer.runBound [as eval] (domain.js:432:12)
    at REPLServer.onLine (repl.js:909:10)
    at REPLServer.emit (events.js:412:35)
    at REPLServer.emit (domain.js:475:12)
    at REPLServer.Interface._onLine (readline.js:434:10)
    at REPLServer.Interface._line (readline.js:791:8)
    at REPLServer.Interface._ttyWrite (readline.js:1136:14) {
  code: 'ERR_SCRIPT_EXECUTION_INTERRUPTED'
}
> (new RegExp('^[-+]?\\d*(?:\\.\\d*)?$')).test(`${'1'.repeat(1e6)}z`) // the new regex works fine
false
>

Checklist

  • Included code example that can be used to test this change
  • Updated TS types
  • Added TS types tests
  • Added unit / integration tests
  • Updated documentation
  • Ensured that CI passes

@piaskowyk piaskowyk self-requested a review July 25, 2022 09:45
@piaskowyk piaskowyk self-assigned this Jul 25, 2022
Copy link
Member

@piaskowyk piaskowyk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @matias-la, Thanks for your PR!

@piaskowyk piaskowyk merged commit 6025581 into software-mansion:main Jul 25, 2022
piaskowyk pushed a commit that referenced this pull request Jul 25, 2022
## Description

The regular expression used to parse numbers could be abused to cause a denial of service if the color to be parsed is controlled by an attacker.

For information about this vulnerability, see https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS.

## Changes

Changed the code so the new regex isn't vulnerable, since there's always only one possible path to take given any input.

Here is a proof-of-concept of how I made the existing regular expression cause a 100% CPU usage for several seconds, after being interrupted:

```
Welcome to Node.js v14.19.3.
Type ".help" for more information.
> (new RegExp('^[-+]?\\d*\\.?\\d+$')).test(`${'1'.repeat(1e6)}z`)
^CUncaught Error: Script execution was interrupted by `SIGINT`
    at Script.runInThisContext (vm.js:134:12)
    at REPLServer.defaultEval (repl.js:566:29)
    at bound (domain.js:421:15)
    at REPLServer.runBound [as eval] (domain.js:432:12)
    at REPLServer.onLine (repl.js:909:10)
    at REPLServer.emit (events.js:412:35)
    at REPLServer.emit (domain.js:475:12)
    at REPLServer.Interface._onLine (readline.js:434:10)
    at REPLServer.Interface._line (readline.js:791:8)
    at REPLServer.Interface._ttyWrite (readline.js:1136:14) {
  code: 'ERR_SCRIPT_EXECUTION_INTERRUPTED'
}
> (new RegExp('^[-+]?\\d*(?:\\.\\d*)?$')).test(`${'1'.repeat(1e6)}z`) // the new regex works fine
false
>
```

## Checklist

- [X] Included code example that can be used to test this change
- [ ] Updated TS types
- [ ] Added TS types tests
- [ ] Added unit / integration tests
- [ ] Updated documentation
- [ ] Ensured that CI passes
@petrusek
Copy link
Contributor

petrusek commented Jul 26, 2022

@matias-la @piaskowyk ... just to be sure, is it OK that the new regex accepts empty string and returns true?

OLD

> (new RegExp('^[-+]?\\d*\\.?\\d+$')).test('')
false

NEW

> (new RegExp('^[-+]?\\d*(?:\\.\\d*)?$')).test(``)
true

@matias-la
Copy link
Contributor Author

Oops, nice catch! I hadn't notice that. The change in #3419 look good, as it prevents empty strings from matching while also not being vulnerable to ReDoS.

piaskowyk added a commit that referenced this pull request Aug 2, 2022
## Description

PR based on issue requested in comment: #3382 (comment)

I updated the regex to prevent empty string matches. 
The new regex `[-+]?(?:\d+(?:\.\d*)?|\.\d+)` - https://regex101.com/r/aHhlXG/1 is equivalent with the old version `[-+]?\d*\.?\d+` - https://regex101.com/r/Z0xMqR/1

I also tested ReDoS, and the new regex is safe.
```
(new RegExp('^[-+]?(?:\d+(?:\.\d*)?|\.\d+)$')).test(`${'1'.repeat(1e6)}z`)
```
piaskowyk added a commit that referenced this pull request Aug 2, 2022
## Description

PR based on issue requested in comment: #3382 (comment)

I updated the regex to prevent empty string matches. 
The new regex `[-+]?(?:\d+(?:\.\d*)?|\.\d+)` - https://regex101.com/r/aHhlXG/1 is equivalent with the old version `[-+]?\d*\.?\d+` - https://regex101.com/r/Z0xMqR/1

I also tested ReDoS, and the new regex is safe.
```
(new RegExp('^[-+]?(?:\d+(?:\.\d*)?|\.\d+)$')).test(`${'1'.repeat(1e6)}z`)
```
EvertEt added a commit to EvertEt/normalize-css-color that referenced this pull request Oct 27, 2022
fluiddot pushed a commit to wordpress-mobile/react-native-reanimated that referenced this pull request Jun 5, 2023
## Description

The regular expression used to parse numbers could be abused to cause a denial of service if the color to be parsed is controlled by an attacker.

For information about this vulnerability, see https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS.

## Changes

Changed the code so the new regex isn't vulnerable, since there's always only one possible path to take given any input.

Here is a proof-of-concept of how I made the existing regular expression cause a 100% CPU usage for several seconds, after being interrupted:

```
Welcome to Node.js v14.19.3.
Type ".help" for more information.
> (new RegExp('^[-+]?\\d*\\.?\\d+$')).test(`${'1'.repeat(1e6)}z`)
^CUncaught Error: Script execution was interrupted by `SIGINT`
    at Script.runInThisContext (vm.js:134:12)
    at REPLServer.defaultEval (repl.js:566:29)
    at bound (domain.js:421:15)
    at REPLServer.runBound [as eval] (domain.js:432:12)
    at REPLServer.onLine (repl.js:909:10)
    at REPLServer.emit (events.js:412:35)
    at REPLServer.emit (domain.js:475:12)
    at REPLServer.Interface._onLine (readline.js:434:10)
    at REPLServer.Interface._line (readline.js:791:8)
    at REPLServer.Interface._ttyWrite (readline.js:1136:14) {
  code: 'ERR_SCRIPT_EXECUTION_INTERRUPTED'
}
> (new RegExp('^[-+]?\\d*(?:\\.\\d*)?$')).test(`${'1'.repeat(1e6)}z`) // the new regex works fine
false
>
```

## Checklist

- [X] Included code example that can be used to test this change
- [ ] Updated TS types
- [ ] Added TS types tests
- [ ] Added unit / integration tests
- [ ] Updated documentation
- [ ] Ensured that CI passes
fluiddot pushed a commit to wordpress-mobile/react-native-reanimated that referenced this pull request Jun 5, 2023
## Description

PR based on issue requested in comment: software-mansion#3382 (comment)

I updated the regex to prevent empty string matches. 
The new regex `[-+]?(?:\d+(?:\.\d*)?|\.\d+)` - https://regex101.com/r/aHhlXG/1 is equivalent with the old version `[-+]?\d*\.?\d+` - https://regex101.com/r/Z0xMqR/1

I also tested ReDoS, and the new regex is safe.
```
(new RegExp('^[-+]?(?:\d+(?:\.\d*)?|\.\d+)$')).test(`${'1'.repeat(1e6)}z`)
```
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants