summaryrefslogtreecommitdiff
path: root/spec/lib/gitlab/changelog/template/compiler_spec.rb
blob: 8b09bc90529dc30f3c3cf4c538d39281348d81ca (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
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