# frozen_string_literal: true require 'spec_helper' RSpec.describe Banzai::Filter::SyntaxHighlightFilter, feature_category: :team_planning do include FilterSpecHelper shared_examples "XSS prevention" do |lang| it "escapes HTML tags" do # This is how a script tag inside a code block is presented to this filter # after Markdown rendering. result = filter(%{
<script>alert(1)</script>
}) # `(1)` symbols are wrapped by lexer tags. expect(result.to_html).not_to match(%r{}) # `<>` stands for lexer tags like , not <s above. expect(result.to_html).to match(%r{alert(<.*>)?\((<.*>)?1(<.*>)?\)}) end end context "when no language is specified" do it "highlights as plaintext" do result = filter('
def fun end
') expect(result.to_html.delete("\n")).to eq('
def fun end
') end include_examples "XSS prevention", "" end context "when contains mermaid diagrams" do it "ignores mermaid blocks" do result = filter('
mermaid code
') expect(result.to_html.delete("\n")).to eq('
mermaid code
') end end context "when
 contains multiple  tags" do
    it "ignores the block" do
      result = filter('
one and two
') expect(result.to_html).to eq('
one and two
') end end # This can happen with the following markdown # #
#

  # something
  #
  #     else
  # 
#
# # The blank line causes markdown to process ` else` as a code block. # Which could lead to an orphaned node being replaced and failing context "when
 is a child of 
 which is a child of a div " do
    it "captures all text and doesn't fail trying to replace a node with no parent" do
      text = "
\n
\nsomething\n
else\n
\n
" result = filter(text) expect(result.to_html.delete("\n")).to eq('
somethingelse
') end end context "when a valid language is specified" do it "highlights as that language" do result = filter('
def fun end
') expect(result.to_html.delete("\n")).to eq('
def fun end
') end include_examples "XSS prevention", "ruby" end context "when an invalid language is specified" do it "highlights as plaintext" do result = filter('
This is a test
') expect(result.to_html.delete("\n")).to eq('
This is a test
') end include_examples "XSS prevention", "gnuplot" end context "languages that should be passed through" do %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(%(
This is a test
)) copy_code_btn = '' unless lang == 'suggestion' expect(result.to_html.delete("\n")).to eq(%(
This is a test
#{copy_code_btn}
)) end include_examples "XSS prevention", lang end end end context "when sourcepos metadata is available" do it "includes it in the highlighted code block" do result = filter('
This is a test
') expect(result.to_html.delete("\n")).to eq('
This is a test
') end it "escape sourcepos metadata to prevent XSS" do result = filter('
') expect(result.to_html.delete("\n")).to eq('
') 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) end end it "highlights as plaintext" do result = filter('
This is a test
') expect(result.to_html.delete("\n")).to eq('
This is a test
') end include_examples "XSS prevention", "ruby" 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 it "does not add highlighting classes" do result = filter('
This is a test
') expect(result.to_html).to eq('
This is a test
') end include_examples "XSS prevention", "ruby" end it_behaves_like "html filter timeout" do let(:text) { '
def fun end
' } end end