summaryrefslogtreecommitdiff
path: root/spec/lib/gitlab/changelog/template/compiler_spec.rb
diff options
context:
space:
mode:
Diffstat (limited to 'spec/lib/gitlab/changelog/template/compiler_spec.rb')
-rw-r--r--spec/lib/gitlab/changelog/template/compiler_spec.rb136
1 files changed, 136 insertions, 0 deletions
diff --git a/spec/lib/gitlab/changelog/template/compiler_spec.rb b/spec/lib/gitlab/changelog/template/compiler_spec.rb
new file mode 100644
index 00000000000..8b09bc90529
--- /dev/null
+++ b/spec/lib/gitlab/changelog/template/compiler_spec.rb
@@ -0,0 +1,136 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Changelog::Template::Compiler do
+ def compile(template, data = {})
+ Gitlab::Changelog::Template::Compiler.new.compile(template).render(data)
+ end
+
+ describe '#compile' do
+ it 'compiles an empty template' do
+ expect(compile('')).to eq('')
+ end
+
+ it 'compiles a template with an undefined variable' do
+ expect(compile('{{number}}')).to eq('')
+ end
+
+ it 'compiles a template with a defined variable' do
+ expect(compile('{{number}}', 'number' => 42)).to eq('42')
+ end
+
+ it 'compiles a template with the special "it" variable' do
+ expect(compile('{{it}}', 'values' => 10)).to eq({ 'values' => 10 }.to_s)
+ end
+
+ it 'compiles a template containing an if statement' do
+ expect(compile('{% if foo %}yes{% end %}', 'foo' => true)).to eq('yes')
+ end
+
+ it 'compiles a template containing an if/else statement' do
+ expect(compile('{% if foo %}yes{% else %}no{% end %}', 'foo' => false))
+ .to eq('no')
+ end
+
+ it 'compiles a template that iterates over an Array' do
+ expect(compile('{% each numbers %}{{it}}{% end %}', 'numbers' => [1, 2, 3]))
+ .to eq('123')
+ end
+
+ it 'compiles a template that iterates over a Hash' do
+ output = compile(
+ '{% each pairs %}{{0}}={{1}}{% end %}',
+ 'pairs' => { 'key' => 'value' }
+ )
+
+ expect(output).to eq('key=value')
+ end
+
+ it 'compiles a template that iterates over a Hash of Arrays' do
+ output = compile(
+ '{% each values %}{{key}}{% end %}',
+ 'values' => [{ 'key' => 'value' }]
+ )
+
+ expect(output).to eq('value')
+ end
+
+ it 'compiles a template with a variable path' do
+ output = compile('{{foo.bar}}', 'foo' => { 'bar' => 10 })
+
+ expect(output).to eq('10')
+ end
+
+ it 'compiles a template with a variable path that uses an Array index' do
+ output = compile('{{foo.values.1}}', 'foo' => { 'values' => [10, 20] })
+
+ expect(output).to eq('20')
+ end
+
+ it 'compiles a template with a variable path that uses a Hash and a numeric index' do
+ output = compile('{{foo.1}}', 'foo' => { 'key' => 'value' })
+
+ expect(output).to eq('')
+ end
+
+ it 'compiles a template with a variable path that uses an Array and a String based index' do
+ output = compile('{{foo.numbers.bla}}', 'foo' => { 'numbers' => [10, 20] })
+
+ expect(output).to eq('')
+ end
+
+ it 'ignores ERB tags provided by the user' do
+ input = '<% exit %> <%= exit %> <%= foo -%>'
+
+ expect(compile(input)).to eq(input)
+ end
+
+ it 'removes newlines introduced by end statements on their own lines' do
+ output = compile(<<~TPL, 'foo' => true)
+ {% if foo %}
+ foo
+ {% end %}
+ TPL
+
+ expect(output).to eq("foo\n")
+ end
+
+ it 'supports escaping of trailing newlines' do
+ output = compile(<<~TPL)
+ foo \
+ bar\
+ baz
+ TPL
+
+ expect(output).to eq("foo barbaz\n")
+ end
+
+ # rubocop: disable Lint/InterpolationCheck
+ it 'ignores embedded Ruby expressions' do
+ input = '#{exit}'
+
+ expect(compile(input)).to eq(input)
+ end
+ # rubocop: enable Lint/InterpolationCheck
+
+ it 'ignores ERB tags inside variable tags' do
+ input = '{{<%= exit %>}}'
+
+ expect(compile(input)).to eq(input)
+ end
+
+ it 'ignores malicious code that tries to escape a variable' do
+ input = "{{') ::Kernel.exit # '}}"
+
+ expect(compile(input)).to eq(input)
+ end
+
+ it 'ignores malicious code that makes use of whitespace' do
+ input = "x<\\\n%::Kernel.system(\"id\")%>"
+
+ expect(Kernel).not_to receive(:system).with('id')
+ expect(compile(input)).to eq('x<%::Kernel.system("id")%>')
+ end
+ end
+end