diff options
Diffstat (limited to 'spec/lib/banzai/filter/syntax_highlight_filter_spec.rb')
-rw-r--r-- | spec/lib/banzai/filter/syntax_highlight_filter_spec.rb | 238 |
1 files changed, 93 insertions, 145 deletions
diff --git a/spec/lib/banzai/filter/syntax_highlight_filter_spec.rb b/spec/lib/banzai/filter/syntax_highlight_filter_spec.rb index ef46fd62486..aee4bd93207 100644 --- a/spec/lib/banzai/filter/syntax_highlight_filter_spec.rb +++ b/spec/lib/banzai/filter/syntax_highlight_filter_spec.rb @@ -19,202 +19,150 @@ RSpec.describe Banzai::Filter::SyntaxHighlightFilter do end end - shared_examples_for 'renders correct markdown' do - context "when no language is specified" do - it "highlights as plaintext" do - result = filter('<pre><code>def fun end</code></pre>') + context "when no language is specified" do + it "highlights as plaintext" do + result = filter('<pre><code>def fun end</code></pre>') - expect(result.to_html.delete("\n")).to eq('<div class="gl-relative markdown-code-block js-markdown-code"><pre class="code highlight js-syntax-highlight language-plaintext" lang="plaintext" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">def fun end</span></code></pre><copy-code></copy-code></div>') - end - - include_examples "XSS prevention", "" + expect(result.to_html.delete("\n")).to eq('<div class="gl-relative markdown-code-block js-markdown-code"><pre class="code highlight js-syntax-highlight language-plaintext" lang="plaintext" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">def fun end</span></code></pre><copy-code></copy-code></div>') end - context "when contains mermaid diagrams" do - it "ignores mermaid blocks" do - result = filter('<pre data-mermaid-style="display"><code>mermaid code</code></pre>') + include_examples "XSS prevention", "" + end - expect(result.to_html).to eq('<pre data-mermaid-style="display"><code>mermaid code</code></pre>') - end + context "when contains mermaid diagrams" do + it "ignores mermaid blocks" do + result = filter('<pre data-mermaid-style="display"><code>mermaid code</code></pre>') + + expect(result.to_html).to eq('<pre data-mermaid-style="display"><code>mermaid code</code></pre>') end + end - context "when a valid language is specified" do - it "highlights as that language" do - result = if Feature.enabled?(:use_cmark_renderer, default_enabled: :yaml) - filter('<pre lang="ruby"><code>def fun end</code></pre>') - else - filter('<pre><code lang="ruby">def fun end</code></pre>') - end + context "when <pre> contains multiple <code> tags" do + it "ignores the block" do + result = filter('<pre><code>one</code> and <code>two</code></pre>') - expect(result.to_html.delete("\n")).to eq('<div class="gl-relative markdown-code-block js-markdown-code"><pre class="code highlight js-syntax-highlight language-ruby" lang="ruby" v-pre="true"><code><span id="LC1" class="line" lang="ruby"><span class="k">def</span> <span class="nf">fun</span> <span class="k">end</span></span></code></pre><copy-code></copy-code></div>') - end + expect(result.to_html).to eq('<pre><code>one</code> and <code>two</code></pre>') + end + end - include_examples "XSS prevention", "ruby" + context "when a valid language is specified" do + it "highlights as that language" do + result = filter('<pre lang="ruby"><code>def fun end</code></pre>') + + expect(result.to_html.delete("\n")).to eq('<div class="gl-relative markdown-code-block js-markdown-code"><pre class="code highlight js-syntax-highlight language-ruby" lang="ruby" v-pre="true"><code><span id="LC1" class="line" lang="ruby"><span class="k">def</span> <span class="nf">fun</span> <span class="k">end</span></span></code></pre><copy-code></copy-code></div>') end - context "when an invalid language is specified" do - it "highlights as plaintext" do - result = if Feature.enabled?(:use_cmark_renderer, default_enabled: :yaml) - filter('<pre lang="gnuplot"><code>This is a test</code></pre>') - else - filter('<pre><code lang="gnuplot">This is a test</code></pre>') - end + include_examples "XSS prevention", "ruby" + end - expect(result.to_html.delete("\n")).to eq('<div class="gl-relative markdown-code-block js-markdown-code"><pre class="code highlight js-syntax-highlight language-plaintext" lang="plaintext" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">This is a test</span></code></pre><copy-code></copy-code></div>') - end + context "when an invalid language is specified" do + it "highlights as plaintext" do + result = filter('<pre lang="gnuplot"><code>This is a test</code></pre>') - include_examples "XSS prevention", "gnuplot" + expect(result.to_html.delete("\n")).to eq('<div class="gl-relative markdown-code-block js-markdown-code"><pre class="code highlight js-syntax-highlight language-plaintext" lang="plaintext" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">This is a test</span></code></pre><copy-code></copy-code></div>') end - context "languages that should be passed through" do - let(:delimiter) { described_class::LANG_PARAMS_DELIMITER } - let(:data_attr) { described_class::LANG_PARAMS_ATTR } + include_examples "XSS prevention", "gnuplot" + end - %w(math mermaid plantuml suggestion).each do |lang| - context "when #{lang} is specified" do - it "highlights as plaintext but with the correct language attribute and class" do - result = if Feature.enabled?(:use_cmark_renderer, default_enabled: :yaml) - filter(%{<pre lang="#{lang}"><code>This is a test</code></pre>}) - else - filter(%{<pre><code lang="#{lang}">This is a test</code></pre>}) - end + context "languages that should be passed through" do + let(:delimiter) { described_class::LANG_PARAMS_DELIMITER } + let(:data_attr) { described_class::LANG_PARAMS_ATTR } - expect(result.to_html.delete("\n")).to eq(%{<div class="gl-relative markdown-code-block js-markdown-code"><pre class="code highlight js-syntax-highlight language-#{lang}" lang="#{lang}" v-pre="true"><code><span id="LC1" class="line" lang="#{lang}">This is a test</span></code></pre><copy-code></copy-code></div>}) - end + %w(math mermaid plantuml suggestion).each do |lang| + context "when #{lang} is specified" do + it "highlights as plaintext but with the correct language attribute and class" do + result = filter(%{<pre lang="#{lang}"><code>This is a test</code></pre>}) - include_examples "XSS prevention", lang + expect(result.to_html.delete("\n")).to eq(%{<div class="gl-relative markdown-code-block js-markdown-code"><pre class="code highlight js-syntax-highlight language-#{lang}" lang="#{lang}" v-pre="true"><code><span id="LC1" class="line" lang="#{lang}">This is a test</span></code></pre><copy-code></copy-code></div>}) end - context "when #{lang} has extra params" do - let(:lang_params) { 'foo-bar-kux' } - - let(:xss_lang) do - if Feature.enabled?(:use_cmark_renderer, default_enabled: :yaml) - "#{lang} data-meta=\"foo-bar-kux\"<script>alert(1)</script>" - else - "#{lang}#{described_class::LANG_PARAMS_DELIMITER}<script>alert(1)</script>" - end - end - - it "includes data-lang-params tag with extra information" do - result = if Feature.enabled?(:use_cmark_renderer, default_enabled: :yaml) - filter(%{<pre lang="#{lang}" data-meta="#{lang_params}"><code>This is a test</code></pre>}) - else - filter(%{<pre><code lang="#{lang}#{delimiter}#{lang_params}">This is a test</code></pre>}) - end - - expect(result.to_html.delete("\n")).to eq(%{<div class="gl-relative markdown-code-block js-markdown-code"><pre class="code highlight js-syntax-highlight language-#{lang}" lang="#{lang}" #{data_attr}="#{lang_params}" v-pre="true"><code><span id="LC1" class="line" lang="#{lang}">This is a test</span></code></pre><copy-code></copy-code></div>}) - end - - include_examples "XSS prevention", lang - - if Feature.enabled?(:use_cmark_renderer, default_enabled: :yaml) - include_examples "XSS prevention", - "#{lang} data-meta=\"foo-bar-kux\"<script>alert(1)</script>" - else - include_examples "XSS prevention", - "#{lang}#{described_class::LANG_PARAMS_DELIMITER}<script>alert(1)</script>" - end - - include_examples "XSS prevention", - "#{lang} data-meta=\"foo-bar-kux\"<script>alert(1)</script>" - end + include_examples "XSS prevention", lang end - context 'when multiple param delimiters are used' do - let(:lang) { 'suggestion' } - let(:lang_params) { '-1+10' } + context "when #{lang} has extra params" do + let(:lang_params) { 'foo-bar-kux' } + let(:xss_lang) { "#{lang} data-meta=\"foo-bar-kux\"<script>alert(1)</script>" } - let(:expected_result) do - %{<div class="gl-relative markdown-code-block js-markdown-code"><pre class="code highlight js-syntax-highlight language-#{lang}" lang="#{lang}" #{data_attr}="#{lang_params} more-things" v-pre="true"><code><span id="LC1" class="line" lang="#{lang}">This is a test</span></code></pre><copy-code></copy-code></div>} - end - - context 'when delimiter is space' do - it 'delimits on the first appearance' do - if Feature.enabled?(:use_cmark_renderer, default_enabled: :yaml) - result = filter(%{<pre lang="#{lang}" data-meta="#{lang_params} more-things"><code>This is a test</code></pre>}) + it "includes data-lang-params tag with extra information" do + result = filter(%{<pre lang="#{lang}" data-meta="#{lang_params}"><code>This is a test</code></pre>}) - expect(result.to_html.delete("\n")).to eq(expected_result) - else - result = filter(%{<pre><code lang="#{lang}#{delimiter}#{lang_params}#{delimiter}more-things">This is a test</code></pre>}) - - expect(result.to_html.delete("\n")).to eq(%{<div class="gl-relative markdown-code-block js-markdown-code"><pre class="code highlight js-syntax-highlight language-#{lang}" lang="#{lang}" #{data_attr}="#{lang_params}#{delimiter}more-things" v-pre="true"><code><span id="LC1" class="line" lang="#{lang}">This is a test</span></code></pre><copy-code></copy-code></div>}) - end - end + expect(result.to_html.delete("\n")).to eq(%{<div class="gl-relative markdown-code-block js-markdown-code"><pre class="code highlight js-syntax-highlight language-#{lang}" lang="#{lang}" #{data_attr}="#{lang_params}" v-pre="true"><code><span id="LC1" class="line" lang="#{lang}">This is a test</span></code></pre><copy-code></copy-code></div>}) end - context 'when delimiter is colon' do - it 'delimits on the first appearance' do - result = filter(%{<pre lang="#{lang}#{delimiter}#{lang_params} more-things"><code>This is a test</code></pre>}) + include_examples "XSS prevention", lang - if Feature.enabled?(:use_cmark_renderer, default_enabled: :yaml) - expect(result.to_html.delete("\n")).to eq(expected_result) - else - expect(result.to_html.delete("\n")).to eq(%{<div class="gl-relative markdown-code-block js-markdown-code"><pre class=\"code highlight js-syntax-highlight language-plaintext\" lang=\"plaintext\" v-pre=\"true\"><code><span id=\"LC1\" class=\"line\" lang=\"plaintext\">This is a test</span></code></pre><copy-code></copy-code></div>}) - end - end - end + include_examples "XSS prevention", + "#{lang} data-meta=\"foo-bar-kux\"<script>alert(1)</script>" + + include_examples "XSS prevention", + "#{lang} data-meta=\"foo-bar-kux\"<script>alert(1)</script>" end end - context "when sourcepos metadata is available" do - it "includes it in the highlighted code block" do - result = filter('<pre data-sourcepos="1:1-3:3"><code lang="plaintext">This is a test</code></pre>') + context 'when multiple param delimiters are used' do + let(:lang) { 'suggestion' } + let(:lang_params) { '-1+10' } - expect(result.to_html.delete("\n")).to eq('<div class="gl-relative markdown-code-block js-markdown-code"><pre data-sourcepos="1:1-3:3" class="code highlight js-syntax-highlight language-plaintext" lang="plaintext" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">This is a test</span></code></pre><copy-code></copy-code></div>') + let(:expected_result) do + %{<div class="gl-relative markdown-code-block js-markdown-code"><pre class="code highlight js-syntax-highlight language-#{lang}" lang="#{lang}" #{data_attr}="#{lang_params} more-things" v-pre="true"><code><span id="LC1" class="line" lang="#{lang}">This is a test</span></code></pre><copy-code></copy-code></div>} end - end - context "when Rouge lexing fails" do - before do - allow_next_instance_of(Rouge::Lexers::Ruby) do |instance| - allow(instance).to receive(:stream_tokens).and_raise(StandardError) + context 'when delimiter is space' do + it 'delimits on the first appearance' do + result = filter(%{<pre lang="#{lang}" data-meta="#{lang_params} more-things"><code>This is a test</code></pre>}) + + expect(result.to_html.delete("\n")).to eq(expected_result) end end - it "highlights as plaintext" do - result = if Feature.enabled?(:use_cmark_renderer, default_enabled: :yaml) - filter('<pre lang="ruby"><code>This is a test</code></pre>') - else - filter('<pre><code lang="ruby">This is a test</code></pre>') - end + context 'when delimiter is colon' do + it 'delimits on the first appearance' do + result = filter(%{<pre lang="#{lang}#{delimiter}#{lang_params} more-things"><code>This is a test</code></pre>}) - expect(result.to_html.delete("\n")).to eq('<div class="gl-relative markdown-code-block js-markdown-code"><pre class="code highlight js-syntax-highlight" lang="" v-pre="true"><code><span id="LC1" class="line" lang="">This is a test</span></code></pre><copy-code></copy-code></div>') + expect(result.to_html.delete("\n")).to eq(expected_result) + end end - - include_examples "XSS prevention", "ruby" end + end - context "when Rouge lexing fails after a retry" do - before do - allow_next_instance_of(Rouge::Lexers::PlainText) do |instance| - allow(instance).to receive(:stream_tokens).and_raise(StandardError) - end - end + context "when sourcepos metadata is available" do + it "includes it in the highlighted code block" do + result = filter('<pre data-sourcepos="1:1-3:3"><code lang="plaintext">This is a test</code></pre>') - it "does not add highlighting classes" do - result = filter('<pre><code>This is a test</code></pre>') + expect(result.to_html.delete("\n")).to eq('<div class="gl-relative markdown-code-block js-markdown-code"><pre data-sourcepos="1:1-3:3" class="code highlight js-syntax-highlight language-plaintext" lang="plaintext" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">This is a test</span></code></pre><copy-code></copy-code></div>') + end + end - expect(result.to_html).to eq('<pre><code>This is a test</code></pre>') + context "when Rouge lexing fails" do + before do + allow_next_instance_of(Rouge::Lexers::Ruby) do |instance| + allow(instance).to receive(:stream_tokens).and_raise(StandardError) end + end + + it "highlights as plaintext" do + result = filter('<pre lang="ruby"><code>This is a test</code></pre>') - include_examples "XSS prevention", "ruby" + expect(result.to_html.delete("\n")).to eq('<div class="gl-relative markdown-code-block js-markdown-code"><pre class="code highlight js-syntax-highlight" lang="" v-pre="true"><code><span id="LC1" class="line" lang="">This is a test</span></code></pre><copy-code></copy-code></div>') end + + include_examples "XSS prevention", "ruby" end - context 'using ruby-based HTML renderer' do + context "when Rouge lexing fails after a retry" do before do - stub_feature_flags(use_cmark_renderer: false) + allow_next_instance_of(Rouge::Lexers::PlainText) do |instance| + allow(instance).to receive(:stream_tokens).and_raise(StandardError) + end end - it_behaves_like 'renders correct markdown' - end + it "does not add highlighting classes" do + result = filter('<pre><code>This is a test</code></pre>') - context 'using c-based HTML renderer' do - before do - stub_feature_flags(use_cmark_renderer: true) + expect(result.to_html).to eq('<pre><code>This is a test</code></pre>') end - it_behaves_like 'renders correct markdown' + include_examples "XSS prevention", "ruby" end end |