diff --git a/src/yamler/yamler.ts b/src/yamler/yamler.ts index 98e19d7..ecf0d33 100644 --- a/src/yamler/yamler.ts +++ b/src/yamler/yamler.ts @@ -56,8 +56,13 @@ export default class YamlER { if (!this.parentFile) { // expressions can return an expression or reference an attribute wiith an expression later in the // object that hasn't been evaluated yet so we parse expressions in a loop until there are no more - while (await this.parseAllExpressions()) - ; + const maxIterations = 100 // prevent infinite loops + let iterations = 0 + + while (await this.parseAllExpressions()) { + if (++iterations >= maxIterations) + throw new Error(`Maximum expression parsing iterations (${maxIterations}) exceeded. Possible circular reference in expressions.`) + } } return this.attributes diff --git a/tests/fixtures/circular_expressions.staxfile b/tests/fixtures/circular_expressions.staxfile new file mode 100644 index 0000000..270c27e --- /dev/null +++ b/tests/fixtures/circular_expressions.staxfile @@ -0,0 +1,4 @@ +stax: + vars: + value1: ${{ get stax.vars.value2 }} + value2: ${{ get stax.vars.value1 }} diff --git a/tests/unit/yamler.test.js b/tests/unit/yamler.test.js index c14867e..3386276 100644 --- a/tests/unit/yamler.test.js +++ b/tests/unit/yamler.test.js @@ -52,4 +52,11 @@ describe('YamlER', () => { it('handles expressions that return an expression', () => { expect(yaml.stax.vars.value5).toBe('some_service') }) + + it('throws an error when expressions have circular references', async () => { + const circularYaml = resolve(fixturesDir, 'circular_expressions.staxfile') + const promise = loadFile(circularYaml, expressionCallback) + + await expect(promise).rejects.toThrow('Maximum expression parsing iterations (100) exceeded. Possible circular reference in expressions.') + }) })