Skip to content

Commit f625789

Browse files
committed
fix: Disable alias resolution with maxAliasCount:0 (#677)
1 parent e1a1a77 commit f625789

4 files changed

Lines changed: 48 additions & 25 deletions

File tree

src/nodes/Alias.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ export class Alias extends NodeBase {
4242
doc: Document,
4343
ctx?: ToJSContext
4444
): Scalar | YAMLMap | YAMLSeq | undefined {
45+
if (ctx?.maxAliasCount === 0)
46+
throw new ReferenceError('Alias resolution is disabled')
47+
4548
let nodes: Node[]
4649
if (ctx?.aliasResolveCache) {
4750
nodes = ctx.aliasResolveCache

src/schema/yaml-1.1/merge.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,19 +43,19 @@ export function addMergeToJSMap(
4343
map: MapLike,
4444
value: unknown
4545
) {
46-
value = ctx && isAlias(value) ? value.resolve(ctx.doc) : value
47-
if (isSeq(value)) for (const it of value.items) mergeValue(ctx, map, it)
48-
else if (Array.isArray(value))
49-
for (const it of value) mergeValue(ctx, map, it)
50-
else mergeValue(ctx, map, value)
46+
const source = resolveAliasValue(ctx, value)
47+
if (isSeq(source)) for (const it of source.items) mergeValue(ctx, map, it)
48+
else if (Array.isArray(source))
49+
for (const it of source) mergeValue(ctx, map, it)
50+
else mergeValue(ctx, map, source)
5151
}
5252

5353
function mergeValue(
5454
ctx: ToJSContext | undefined,
5555
map: MapLike,
5656
value: unknown
5757
) {
58-
const source = ctx && isAlias(value) ? value.resolve(ctx.doc) : value
58+
const source = resolveAliasValue(ctx, value)
5959
if (!isMap(source))
6060
throw new Error('Merge sources must be maps or map aliases')
6161
const srcMap = source.toJSON(null, ctx, Map)
@@ -75,3 +75,7 @@ function mergeValue(
7575
}
7676
return map
7777
}
78+
79+
function resolveAliasValue(ctx: ToJSContext | undefined, value: unknown) {
80+
return ctx && isAlias(value) ? value.resolve(ctx.doc, ctx) : value
81+
}

tests/doc/anchors.ts

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,14 @@ import { Document, parse, parseDocument } from 'yaml'
33

44
test('basic', () => {
55
const src = `- &a 1\n- *a\n`
6-
const doc = parseDocument<YAMLSeq>(src)
6+
const doc = parseDocument<YAMLSeq, false>(src)
77
expect(doc.errors).toHaveLength(0)
88
expect(doc.contents?.items).toMatchObject([
99
{ anchor: 'a', value: 1 },
1010
{ source: 'a' }
1111
])
1212
expect(String(doc)).toBe(src)
13+
expect(doc.get(1).resolve(doc)).toBe(doc.get(0, true))
1314
})
1415

1516
test('re-defined anchor', () => {
@@ -27,7 +28,7 @@ test('re-defined anchor', () => {
2728

2829
test('circular reference', () => {
2930
const src = '&a [ 1, *a ]\n'
30-
const doc = parseDocument(src)
31+
const doc = parseDocument<YAMLSeq, false>(src)
3132
expect(doc.errors).toHaveLength(0)
3233
expect(doc.warnings).toHaveLength(0)
3334
expect(doc.contents).toMatchObject({
@@ -37,6 +38,7 @@ test('circular reference', () => {
3738
const res = doc.toJS()
3839
expect(res[1]).toBe(res)
3940
expect(String(doc)).toBe(src)
41+
expect(doc.get(1).resolve(doc)).toBe(doc.contents)
4042
})
4143

4244
describe('anchor on tagged collection', () => {
@@ -146,6 +148,25 @@ describe('errors', () => {
146148
const doc = parseDocument('- &\n')
147149
expect(doc.errors).toMatchObject([{ code: 'BAD_ALIAS' }])
148150
})
151+
152+
test('resolution with maxAliasCount:0', () => {
153+
const src = `- &a 1\n- *a\n`
154+
const doc = parseDocument<YAMLSeq, false>(src)
155+
expect(doc.errors).toHaveLength(0)
156+
expect(() => doc.get(1).resolve(doc, { maxAliasCount: 0 })).toThrow(
157+
ReferenceError
158+
)
159+
})
160+
161+
test('circular reference', () => {
162+
const src = '&A { <<: *A, B: b }\n'
163+
const doc = parseDocument(src, { merge: true })
164+
expect(doc.errors).toHaveLength(0)
165+
expect(doc.warnings).toHaveLength(0)
166+
expect(() => doc.toJS()).toThrow('Maximum call stack size exceeded')
167+
expect(() => doc.toJS({ maxAliasCount: 0 })).toThrow(ReferenceError)
168+
expect(String(doc)).toBe(src)
169+
})
149170
})
150171

151172
describe('warnings', () => {
@@ -455,15 +476,6 @@ y:
455476
)
456477
})
457478

458-
test('circular reference', () => {
459-
const src = '&A { <<: *A, B: b }\n'
460-
const doc = parseDocument(src, { merge: true })
461-
expect(doc.errors).toHaveLength(0)
462-
expect(doc.warnings).toHaveLength(0)
463-
expect(() => doc.toJS()).toThrow('Maximum call stack size exceeded')
464-
expect(String(doc)).toBe(src)
465-
})
466-
467479
test('missing whitespace', () => {
468480
expect(() => parse('&a{}')).toThrow(
469481
'Tags and anchors must be separated from the next token by white space'

tests/doc/parse.ts

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -530,14 +530,18 @@ describe('Resource exhaustion attacks', () => {
530530
'e: [*d]'
531531
]
532532

533-
test(`depth 0: maxAliasCount 1 passes`, () => {
534-
expect(() => YAML.parse(rows[0], { maxAliasCount: 1 })).not.toThrow()
535-
})
533+
for (const maxAliasCount of [0, 1]) {
534+
test(`depth 0: maxAliasCount ${maxAliasCount} passes`, () => {
535+
expect(() => YAML.parse(rows[0], { maxAliasCount })).not.toThrow()
536+
})
536537

537-
test(`depth 1: maxAliasCount 1 fails on first alias`, () => {
538-
const src = `${rows[0]}\nb: *a`
539-
expect(() => YAML.parse(src, { maxAliasCount: 1 })).toThrow()
540-
})
538+
test(`depth 1: maxAliasCount ${maxAliasCount} fails on first alias`, () => {
539+
const src = `${rows[0]}\nb: *a`
540+
expect(() => YAML.parse(src, { maxAliasCount })).toThrow(
541+
ReferenceError
542+
)
543+
})
544+
}
541545

542546
const limits = [10, 50, 150, 300]
543547
for (let i = 0; i < 4; ++i) {
@@ -546,7 +550,7 @@ describe('Resource exhaustion attacks', () => {
546550
test(`depth ${i + 1}: maxAliasCount ${limits[i] - 1} fails`, () => {
547551
expect(() =>
548552
YAML.parse(src, { maxAliasCount: limits[i] - 1 })
549-
).toThrow()
553+
).toThrow(ReferenceError)
550554
})
551555

552556
test(`depth ${i + 1}: maxAliasCount ${limits[i]} passes`, () => {

0 commit comments

Comments
 (0)