Skip to content

Commit 7abcf45

Browse files
committed
fix: Catch stack overflow during CST composition
1 parent a0252f8 commit 7abcf45

3 files changed

Lines changed: 32 additions & 6 deletions

File tree

src/cst/ParseContext.js

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -196,13 +196,20 @@ export class ParseContext {
196196
parent: context.parent.type
197197
}
198198
const node = createNewNode(type, props)
199-
let offset = node.parse(context, valueStart)
199+
let offset = start
200+
try {
201+
offset = node.parse(context, valueStart)
202+
} catch (error) {
203+
const msg = error instanceof Error ? error.message : String(error)
204+
if (!node.error) node.error = new YAMLSyntaxError(node, msg)
205+
}
200206
node.range = new Range(start, offset)
201207
/* istanbul ignore if */
202208
if (offset <= start) {
203209
// This should never happen, but if it does, let's make sure to at least
204210
// step one character forward to avoid a busy loop.
205-
node.error = new Error(`Node#parse consumed no characters`)
211+
if (!node.error)
212+
node.error = new Error(`Node#parse consumed no characters`)
206213
node.error.parseEnd = offset
207214
node.error.source = node
208215
node.range.end = start + 1

src/resolve/resolveTag.js

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
import { Collection } from '../ast/Collection.js'
22
import { Scalar } from '../ast/Scalar.js'
33
import { Type, defaultTags } from '../constants.js'
4-
import { YAMLReferenceError, YAMLWarning } from '../errors.js'
4+
import {
5+
YAMLError,
6+
YAMLReferenceError,
7+
YAMLSemanticError,
8+
YAMLWarning
9+
} from '../errors.js'
510
import { resolveScalar } from './resolveScalar.js'
611
import { resolveString } from './resolveString.js'
712

@@ -46,9 +51,13 @@ export function resolveTag(doc, node, tagName) {
4651
return res
4752
}
4853
} catch (error) {
49-
/* istanbul ignore if */
50-
if (!error.source) error.source = node
51-
doc.errors.push(error)
54+
if (error instanceof YAMLError) {
55+
if (!error.source) error.source = node
56+
doc.errors.push(error)
57+
} else {
58+
const msg = error instanceof Error ? error.message : String(error)
59+
doc.errors.push(new YAMLSemanticError(node, msg))
60+
}
5261
return null
5362
}
5463

tests/doc/parse.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import fs from 'fs'
22
import path from 'path'
33
import YAML from '../../index.js'
4+
import { YAMLError } from '../../util.js'
45

56
describe('tags', () => {
67
describe('implicit tags', () => {
@@ -616,6 +617,15 @@ describe('Excessive entity expansion attacks', () => {
616617
})
617618
})
618619

620+
test('Excessive recursion', () => {
621+
const depth = 5000
622+
const src = '['.repeat(depth) + '1' + ']'.repeat(depth)
623+
const doc = YAML.parseDocument(src)
624+
for (const error of doc.errors) {
625+
expect(error).toBeInstanceOf(YAMLError)
626+
}
627+
})
628+
619629
test('Anchor for empty node (6KGN)', () => {
620630
const src = `a: &anchor\nb: *anchor`
621631
expect(YAML.parse(src)).toMatchObject({ a: null, b: null })

0 commit comments

Comments
 (0)