Skip to content

Commit

Permalink
fix(lockfile): prevent infinite loop when parsing a corrupted lockfile (
Browse files Browse the repository at this point in the history
#4965)

* refactor(lockfile): remove string concatenation from parsing code

Replace all instances of building strings incrementally using += with more efficient methods.

* fix(lockfile): prevent infinite loop when parsing a 

Fix bug where yarn could get into an infinite loop when parsing a corrupted lockfile with an
unterminated string.
  • Loading branch information
rhendric authored and Gudahtt committed Oct 11, 2018
1 parent f2206d2 commit b3d136f
Show file tree
Hide file tree
Showing 2 changed files with 26 additions and 27 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Expand Up @@ -4,6 +4,10 @@ Please add one entry in this file for each change in Yarn's behavior. Use the sa

## Master

- Prevent infinite loop when parsing corrupted lockfile with unterminated string

[#4965](https://github.com/yarnpkg/yarn/pull/4965) - [**Ryan Hendrickson**](https://github.com/rhendric)

- Environment variables now have to **start** with `YARN_` (instead of just contain it) to be considered

[#6518](https://github.com/yarnpkg/yarn/pull/6518) - [**Michael Gmelin**](https://blog.grem.de)
Expand Down
49 changes: 22 additions & 27 deletions src/lockfile/parse.js
Expand Up @@ -69,44 +69,43 @@ function* tokenise(input: string): Iterator<Token> {
} else if (input[0] === '#') {
chop++;

let val = '';
while (input[chop] !== '\n') {
val += input[chop];
chop++;
let nextNewline = input.indexOf('\n', chop);
if (nextNewline === -1) {
nextNewline = input.length;
}
const val = input.substring(chop, nextNewline);
chop = nextNewline;
yield buildToken(TOKEN_TYPES.comment, val);
} else if (input[0] === ' ') {
if (lastNewline) {
let indent = '';
for (let i = 0; input[i] === ' '; i++) {
indent += input[i];
let indentSize = 1;
for (let i = 1; input[i] === ' '; i++) {
indentSize++;
}

if (indent.length % 2) {
if (indentSize % 2) {
throw new TypeError('Invalid number of spaces');
} else {
chop = indent.length;
yield buildToken(TOKEN_TYPES.indent, indent.length / 2);
chop = indentSize;
yield buildToken(TOKEN_TYPES.indent, indentSize / 2);
}
} else {
chop++;
}
} else if (input[0] === '"') {
let val = '';

for (let i = 0; ; i++) {
const currentChar = input[i];
val += currentChar;

if (i > 0 && currentChar === '"') {
let i = 1;
for (; i < input.length; i++) {
if (input[i] === '"') {
const isEscaped = input[i - 1] === '\\' && input[i - 2] !== '\\';
if (!isEscaped) {
i++;
break;
}
}
}
const val = input.substring(0, i);

chop = val.length;
chop = i;

try {
yield buildToken(TOKEN_TYPES.string, JSON.parse(val));
Expand All @@ -118,10 +117,7 @@ function* tokenise(input: string): Iterator<Token> {
}
}
} else if (/^[0-9]/.test(input)) {
let val = '';
for (let i = 0; /^[0-9]$/.test(input[i]); i++) {
val += input[i];
}
const val = /^[0-9]+/.exec(input)[0];
chop = val.length;

yield buildToken(TOKEN_TYPES.number, +val);
Expand All @@ -138,16 +134,15 @@ function* tokenise(input: string): Iterator<Token> {
yield buildToken(TOKEN_TYPES.comma);
chop++;
} else if (/^[a-zA-Z\/-]/g.test(input)) {
let name = '';
for (let i = 0; i < input.length; i++) {
let i = 0;
for (; i < input.length; i++) {
const char = input[i];
if (char === ':' || char === ' ' || char === '\n' || char === '\r' || char === ',') {
break;
} else {
name += char;
}
}
chop = name.length;
const name = input.substring(0, i);
chop = i;

yield buildToken(TOKEN_TYPES.string, name);
} else {
Expand Down

0 comments on commit b3d136f

Please sign in to comment.