From 34ab6dfa051c29d27353a9f555e713f36c7954a4 Mon Sep 17 00:00:00 2001 From: Brett Walker Date: Fri, 4 Jan 2019 20:54:02 -0600 Subject: [PATCH 1/6] Properly process footnotes in markdown All the ids and classes were stripped. Add them back in and make ids unique --- .../26375-markdown-footnotes-not-working.yml | 5 ++ lib/banzai/filter/footnote_filter.rb | 55 +++++++++++++++++++ lib/banzai/filter/sanitization_filter.rb | 27 ++++++++- lib/banzai/pipeline/gfm_pipeline.rb | 1 + .../lib/banzai/filter/footnote_filter_spec.rb | 48 ++++++++++++++++ .../banzai/filter/sanitization_filter_spec.rb | 38 ++++++++++++- 6 files changed, 171 insertions(+), 3 deletions(-) create mode 100644 changelogs/unreleased/26375-markdown-footnotes-not-working.yml create mode 100644 lib/banzai/filter/footnote_filter.rb create mode 100644 spec/lib/banzai/filter/footnote_filter_spec.rb diff --git a/changelogs/unreleased/26375-markdown-footnotes-not-working.yml b/changelogs/unreleased/26375-markdown-footnotes-not-working.yml new file mode 100644 index 00000000000..eb78c6556bd --- /dev/null +++ b/changelogs/unreleased/26375-markdown-footnotes-not-working.yml @@ -0,0 +1,5 @@ +--- +title: Footnotes now work render properly in markdown +merge_request: 24168 +author: +type: fixed diff --git a/lib/banzai/filter/footnote_filter.rb b/lib/banzai/filter/footnote_filter.rb new file mode 100644 index 00000000000..a7120fbd46e --- /dev/null +++ b/lib/banzai/filter/footnote_filter.rb @@ -0,0 +1,55 @@ +# frozen_string_literal: true + +module Banzai + module Filter + # HTML Filter for footnotes + # + # Footnotes are supported in CommonMark. However we were stripping + # the ids during sanitization. Those are now allowed. + # + # Footnotes are numbered the same - the first one has `id=fn1`, the + # second is `id=fn2`, etc. In order to allow footnotes when rendering + # multiple markdown blocks on a page, we need to make each footnote + # reference unique. + # + # This filter adds a random number to each footnote (the same number + # can be used for a single render). So you get `id=fn1-4335` and `id=fn2-4335`. + # + class FootnoteFilter < HTML::Pipeline::Filter + INTEGER_PATTERN = /\A\d+\Z/.freeze + + def call + return doc unless first_footnote = doc.at_css('ol > li[id=fn1]') + + # Sanitization stripped off the section wrapper - add it back in + first_footnote.parent.wrap('
') + + doc.css('sup > a[id]').each do |link_node| + ref_num = link_node[:id].delete_prefix('fnref') + footnote_node = doc.at_css("li[id=fn#{ref_num}]") + backref_node = doc.at_css("li[id=fn#{ref_num}] a[href=\"#fnref#{ref_num}\"]") + + if ref_num =~ INTEGER_PATTERN && footnote_node && backref_node + rand_ref_num = "#{ref_num}-#{random_number}" + link_node[:href] = "#fn#{rand_ref_num}" + link_node[:id] = "fnref#{rand_ref_num}" + footnote_node[:id] = "fn#{rand_ref_num}" + backref_node[:href] = "#fnref#{rand_ref_num}" + + # Sanitization stripped off class - add it back in + link_node.parent.append_class('footnote-ref') + backref_node.append_class('footnote-backref') + end + end + + doc + end + + private + + def random_number + @random_number ||= rand(10000) + end + end + end +end diff --git a/lib/banzai/filter/sanitization_filter.rb b/lib/banzai/filter/sanitization_filter.rb index 8ba09290e6d..d05518edcea 100644 --- a/lib/banzai/filter/sanitization_filter.rb +++ b/lib/banzai/filter/sanitization_filter.rb @@ -8,8 +8,10 @@ module Banzai class SanitizationFilter < HTML::Pipeline::SanitizationFilter include Gitlab::Utils::StrongMemoize - UNSAFE_PROTOCOLS = %w(data javascript vbscript).freeze - TABLE_ALIGNMENT_PATTERN = /text-align: (?center|left|right)/ + UNSAFE_PROTOCOLS = %w(data javascript vbscript).freeze + TABLE_ALIGNMENT_PATTERN = /text-align: (?center|left|right)/.freeze + FOOTNOTE_LINK_REFERENCE_PATTERN = /\Afnref\d\z/.freeze + FOOTNOTE_LI_REFERENCE_PATTERN = /\Afn\d\z/.freeze def whitelist strong_memoize(:whitelist) do @@ -57,6 +59,13 @@ module Banzai # Remove any `style` properties not required for table alignment whitelist[:transformers].push(self.class.remove_unsafe_table_style) + # Allow `id` in a and li elements for footnotes + whitelist[:attributes]['a'].push('id') + whitelist[:attributes]['li'] = %w(id) + + # ...but remove any `id` properties not matching for footnotes + whitelist[:transformers].push(self.class.remove_non_footnote_ids) + whitelist end @@ -112,6 +121,20 @@ module Banzai end end end + + def remove_non_footnote_ids + lambda do |env| + node = env[:node] + + return unless node.name == 'a' || node.name == 'li' + return unless node.has_attribute?('id') + + return if node.name == 'a' && node['id'] =~ FOOTNOTE_LINK_REFERENCE_PATTERN + return if node.name == 'li' && node['id'] =~ FOOTNOTE_LI_REFERENCE_PATTERN + + node.remove_attribute('id') + end + end end end end diff --git a/lib/banzai/pipeline/gfm_pipeline.rb b/lib/banzai/pipeline/gfm_pipeline.rb index 5f13a6d6cde..d860dad0b6c 100644 --- a/lib/banzai/pipeline/gfm_pipeline.rb +++ b/lib/banzai/pipeline/gfm_pipeline.rb @@ -30,6 +30,7 @@ module Banzai Filter::AutolinkFilter, Filter::ExternalLinkFilter, Filter::SuggestionFilter, + Filter::FootnoteFilter, *reference_filters, diff --git a/spec/lib/banzai/filter/footnote_filter_spec.rb b/spec/lib/banzai/filter/footnote_filter_spec.rb new file mode 100644 index 00000000000..0b7807b2c4c --- /dev/null +++ b/spec/lib/banzai/filter/footnote_filter_spec.rb @@ -0,0 +1,48 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Banzai::Filter::FootnoteFilter do + include FilterSpecHelper + + # first[^1] and second[^second] + # [^1]: one + # [^second]: two + let(:footnote) do + <<-EOF.strip_heredoc +

first1 and second2

+
    +
  1. +

    one

    +
  2. +
  3. +

    two

    +
  4. +
+ EOF + end + + context 'when footnotes exist' do + let(:doc) { filter(footnote) } + let(:link_node) { doc.css('sup > a').first } + let(:identifier) { link_node[:id].delete_prefix('fnref1-') } + + it 'adds identifier to footnotes' do + expect(link_node[:id]).to eq "fnref1-#{identifier}" + expect(link_node[:href]).to eq "#fn1-#{identifier}" + expect(doc.css("li[id=fn1-#{identifier}]")).not_to be_empty + expect(doc.css("li[id=fn1-#{identifier}] a[href=\"#fnref1-#{identifier}\"]")).not_to be_empty + end + + it 'uses the same identifier for all footnotes' do + expect(doc.css("li[id=fn2-#{identifier}]")).not_to be_empty + expect(doc.css("li[id=fn2-#{identifier}] a[href=\"#fnref2-#{identifier}\"]")).not_to be_empty + end + + it 'adds section and classes' do + expect(doc.css("section[class=footnotes]")).not_to be_empty + expect(doc.css("sup[class=footnote-ref]").count).to eq 2 + expect(doc.css("a[class=footnote-backref]").count).to eq 2 + end + end +end diff --git a/spec/lib/banzai/filter/sanitization_filter_spec.rb b/spec/lib/banzai/filter/sanitization_filter_spec.rb index 0b3c2390304..bfec7384443 100644 --- a/spec/lib/banzai/filter/sanitization_filter_spec.rb +++ b/spec/lib/banzai/filter/sanitization_filter_spec.rb @@ -246,7 +246,7 @@ describe Banzai::Filter::SanitizationFilter do 'protocol-based JS injection: spaces and entities' => { input: 'foo', - output: 'foo' + output: 'foo' }, 'protocol whitespace' => { @@ -300,5 +300,41 @@ describe Banzai::Filter::SanitizationFilter do expect(act.to_html).to eq exp end + + describe 'footnotes' do + it 'allows id property on links' do + exp = %q{foo/bar.md} + act = filter(exp) + + expect(act.to_html).to eq exp + end + + it 'allows id property on li element' do + exp = %q{
  1. footnote
} + act = filter(exp) + + expect(act.to_html).to eq exp + end + + it 'only allows valid footnote formats for links' do + exp = %q{link} + + %w[fnrefx test xfnref1].each do |id| + act = filter(%Q{link}) + + expect(act.to_html).to eq exp + end + end + + it 'only allows valid footnote formats for li' do + exp = %q{
  1. footnote
} + + %w[fnx test xfn1].each do |id| + act = filter(%Q{
  1. footnote
}) + + expect(act.to_html).to eq exp + end + end + end end end From 459d6f98126749cbd76cca20082c4ec6d4d57bb9 Mon Sep 17 00:00:00 2001 From: Brett Walker Date: Sun, 6 Jan 2019 20:54:19 -0600 Subject: [PATCH 2/6] Update nokogiri to 1.10.0 and truncato to 0.7.11 --- Gemfile | 4 ++-- Gemfile.lock | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Gemfile b/Gemfile index 1df4584afb7..ea33bb5c1e7 100644 --- a/Gemfile +++ b/Gemfile @@ -125,9 +125,9 @@ gem 'wikicloth', '0.8.1' gem 'asciidoctor', '~> 1.5.8' gem 'asciidoctor-plantuml', '0.0.8' gem 'rouge', '~> 3.1' -gem 'truncato', '~> 0.7.9' +gem 'truncato', '~> 0.7.11' gem 'bootstrap_form', '~> 2.7.0' -gem 'nokogiri', '~> 1.8.5' +gem 'nokogiri', '~> 1.10.0' gem 'escape_utils', '~> 1.1' # Calendar rendering diff --git a/Gemfile.lock b/Gemfile.lock index d708d0e9740..a180a459eba 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -462,7 +462,7 @@ GEM mimemagic (0.3.2) mini_magick (4.8.0) mini_mime (1.0.1) - mini_portile2 (2.3.0) + mini_portile2 (2.4.0) minitest (5.11.3) msgpack (1.2.4) multi_json (1.13.1) @@ -477,8 +477,8 @@ GEM net-ssh (5.0.1) netrc (0.11.0) nio4r (2.3.1) - nokogiri (1.8.5) - mini_portile2 (~> 2.3.0) + nokogiri (1.10.0) + mini_portile2 (~> 2.4.0) nokogumbo (1.5.0) nokogiri numerizer (0.1.1) @@ -876,9 +876,9 @@ GEM toml-rb (1.0.0) citrus (~> 3.0, > 3.0) trollop (2.1.3) - truncato (0.7.10) + truncato (0.7.11) htmlentities (~> 4.3.1) - nokogiri (~> 1.8.0, >= 1.7.0) + nokogiri (>= 1.7.0, <= 2.0) tzinfo (1.2.5) thread_safe (~> 0.1) u2f (0.2.1) @@ -1059,7 +1059,7 @@ DEPENDENCIES nakayoshi_fork (~> 0.0.4) net-ldap net-ssh (~> 5.0) - nokogiri (~> 1.8.5) + nokogiri (~> 1.10.0) oauth2 (~> 1.4) octokit (~> 4.9) omniauth (~> 1.8) @@ -1156,7 +1156,7 @@ DEPENDENCIES thin (~> 1.7.0) timecop (~> 0.8.0) toml-rb (~> 1.0.0) - truncato (~> 0.7.9) + truncato (~> 0.7.11) u2f (~> 0.2.1) uglifier (~> 2.7.2) unf (~> 0.1.4) From cc036417278111efc7e8e686339ab0191a324364 Mon Sep 17 00:00:00 2001 From: Brett Walker Date: Thu, 10 Jan 2019 17:28:44 -0600 Subject: [PATCH 3/6] Updates based on review comments --- lib/banzai/filter/footnote_filter.rb | 14 +++++++------- lib/banzai/filter/sanitization_filter.rb | 4 ++-- spec/lib/banzai/filter/sanitization_filter_spec.rb | 7 +++++++ 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/lib/banzai/filter/footnote_filter.rb b/lib/banzai/filter/footnote_filter.rb index a7120fbd46e..dc00ca73147 100644 --- a/lib/banzai/filter/footnote_filter.rb +++ b/lib/banzai/filter/footnote_filter.rb @@ -16,25 +16,25 @@ module Banzai # can be used for a single render). So you get `id=fn1-4335` and `id=fn2-4335`. # class FootnoteFilter < HTML::Pipeline::Filter - INTEGER_PATTERN = /\A\d+\Z/.freeze + INTEGER_PATTERN = /\A\d+\z/.freeze def call return doc unless first_footnote = doc.at_css('ol > li[id=fn1]') # Sanitization stripped off the section wrapper - add it back in first_footnote.parent.wrap('
') + rand_suffix = "-#{random_number}" doc.css('sup > a[id]').each do |link_node| ref_num = link_node[:id].delete_prefix('fnref') footnote_node = doc.at_css("li[id=fn#{ref_num}]") - backref_node = doc.at_css("li[id=fn#{ref_num}] a[href=\"#fnref#{ref_num}\"]") + backref_node = footnote_node.at_css("a[href=\"#fnref#{ref_num}\"]") if ref_num =~ INTEGER_PATTERN && footnote_node && backref_node - rand_ref_num = "#{ref_num}-#{random_number}" - link_node[:href] = "#fn#{rand_ref_num}" - link_node[:id] = "fnref#{rand_ref_num}" - footnote_node[:id] = "fn#{rand_ref_num}" - backref_node[:href] = "#fnref#{rand_ref_num}" + link_node[:href] += rand_suffix + link_node[:id] += rand_suffix + footnote_node[:id] += rand_suffix + backref_node[:href] += rand_suffix # Sanitization stripped off class - add it back in link_node.parent.append_class('footnote-ref') diff --git a/lib/banzai/filter/sanitization_filter.rb b/lib/banzai/filter/sanitization_filter.rb index d05518edcea..16accefa850 100644 --- a/lib/banzai/filter/sanitization_filter.rb +++ b/lib/banzai/filter/sanitization_filter.rb @@ -10,8 +10,8 @@ module Banzai UNSAFE_PROTOCOLS = %w(data javascript vbscript).freeze TABLE_ALIGNMENT_PATTERN = /text-align: (?center|left|right)/.freeze - FOOTNOTE_LINK_REFERENCE_PATTERN = /\Afnref\d\z/.freeze - FOOTNOTE_LI_REFERENCE_PATTERN = /\Afn\d\z/.freeze + FOOTNOTE_LINK_REFERENCE_PATTERN = /\Afnref\d+\z/.freeze + FOOTNOTE_LI_REFERENCE_PATTERN = /\Afn\d+\z/.freeze def whitelist strong_memoize(:whitelist) do diff --git a/spec/lib/banzai/filter/sanitization_filter_spec.rb b/spec/lib/banzai/filter/sanitization_filter_spec.rb index bfec7384443..0f9011cb655 100644 --- a/spec/lib/banzai/filter/sanitization_filter_spec.rb +++ b/spec/lib/banzai/filter/sanitization_filter_spec.rb @@ -335,6 +335,13 @@ describe Banzai::Filter::SanitizationFilter do expect(act.to_html).to eq exp end end + + it 'allows footnotes numbered higher than 9' do + exp = %q{link
  1. footnote
} + act = filter(exp) + + expect(act.to_html).to eq exp + end end end end From 7e900ed8569aff9fc549e95b6271c3d578acbd51 Mon Sep 17 00:00:00 2001 From: Brett Walker Date: Wed, 16 Jan 2019 21:13:44 -0600 Subject: [PATCH 4/6] Refactoring and addressing review comments and additional spec --- .../26375-markdown-footnotes-not-working.yml | 2 +- lib/banzai/filter/footnote_filter.rb | 23 ++++++-- lib/banzai/filter/sanitization_filter.rb | 18 +++---- .../lib/banzai/filter/footnote_filter_spec.rb | 52 +++++++++---------- .../banzai/filter/sanitization_filter_spec.rb | 8 +-- .../lib/banzai/pipeline/full_pipeline_spec.rb | 32 ++++++++++++ 6 files changed, 88 insertions(+), 47 deletions(-) diff --git a/changelogs/unreleased/26375-markdown-footnotes-not-working.yml b/changelogs/unreleased/26375-markdown-footnotes-not-working.yml index eb78c6556bd..86adef84a2a 100644 --- a/changelogs/unreleased/26375-markdown-footnotes-not-working.yml +++ b/changelogs/unreleased/26375-markdown-footnotes-not-working.yml @@ -1,5 +1,5 @@ --- -title: Footnotes now work render properly in markdown +title: Footnotes now render properly in markdown merge_request: 24168 author: type: fixed diff --git a/lib/banzai/filter/footnote_filter.rb b/lib/banzai/filter/footnote_filter.rb index dc00ca73147..97527976437 100644 --- a/lib/banzai/filter/footnote_filter.rb +++ b/lib/banzai/filter/footnote_filter.rb @@ -16,19 +16,24 @@ module Banzai # can be used for a single render). So you get `id=fn1-4335` and `id=fn2-4335`. # class FootnoteFilter < HTML::Pipeline::Filter - INTEGER_PATTERN = /\A\d+\z/.freeze + INTEGER_PATTERN = /\A\d+\z/.freeze + FOOTNOTE_ID_PREFIX = 'fn'.freeze + FOOTNOTE_LINK_ID_PREFIX = 'fnref'.freeze + FOOTNOTE_LI_REFERENCE_PATTERN = /\A#{FOOTNOTE_ID_PREFIX}\d+\z/.freeze + FOOTNOTE_LINK_REFERENCE_PATTERN = /\A#{FOOTNOTE_LINK_ID_PREFIX}\d+\z/.freeze + FOOTNOTE_START_NUMBER = 1 def call - return doc unless first_footnote = doc.at_css('ol > li[id=fn1]') + return doc unless first_footnote = doc.at_css("ol > li[id=#{fn_id(FOOTNOTE_START_NUMBER)}]") # Sanitization stripped off the section wrapper - add it back in first_footnote.parent.wrap('
') rand_suffix = "-#{random_number}" doc.css('sup > a[id]').each do |link_node| - ref_num = link_node[:id].delete_prefix('fnref') - footnote_node = doc.at_css("li[id=fn#{ref_num}]") - backref_node = footnote_node.at_css("a[href=\"#fnref#{ref_num}\"]") + ref_num = link_node[:id].delete_prefix(FOOTNOTE_LINK_ID_PREFIX) + footnote_node = doc.at_css("li[id=#{fn_id(ref_num)}]") + backref_node = footnote_node.at_css("a[href=\"##{fnref_id(ref_num)}\"]") if ref_num =~ INTEGER_PATTERN && footnote_node && backref_node link_node[:href] += rand_suffix @@ -50,6 +55,14 @@ module Banzai def random_number @random_number ||= rand(10000) end + + def fn_id(num) + "#{FOOTNOTE_ID_PREFIX}#{num}" + end + + def fnref_id(num) + "#{FOOTNOTE_LINK_ID_PREFIX}#{num}" + end end end end diff --git a/lib/banzai/filter/sanitization_filter.rb b/lib/banzai/filter/sanitization_filter.rb index 16accefa850..edc053638a8 100644 --- a/lib/banzai/filter/sanitization_filter.rb +++ b/lib/banzai/filter/sanitization_filter.rb @@ -8,10 +8,8 @@ module Banzai class SanitizationFilter < HTML::Pipeline::SanitizationFilter include Gitlab::Utils::StrongMemoize - UNSAFE_PROTOCOLS = %w(data javascript vbscript).freeze - TABLE_ALIGNMENT_PATTERN = /text-align: (?center|left|right)/.freeze - FOOTNOTE_LINK_REFERENCE_PATTERN = /\Afnref\d+\z/.freeze - FOOTNOTE_LI_REFERENCE_PATTERN = /\Afn\d+\z/.freeze + UNSAFE_PROTOCOLS = %w(data javascript vbscript).freeze + TABLE_ALIGNMENT_PATTERN = /text-align: (?center|left|right)/.freeze def whitelist strong_memoize(:whitelist) do @@ -47,10 +45,9 @@ module Banzai whitelist[:attributes][:all].delete('name') whitelist[:attributes]['a'].push('name') - # Allow any protocol in `a` elements... + # Allow any protocol in `a` elements + # and then remove links with unsafe protocols whitelist[:protocols].delete('a') - - # ...but then remove links with unsafe protocols whitelist[:transformers].push(self.class.remove_unsafe_links) # Remove `rel` attribute from `a` elements @@ -60,10 +57,9 @@ module Banzai whitelist[:transformers].push(self.class.remove_unsafe_table_style) # Allow `id` in a and li elements for footnotes + # and remove any `id` properties not matching for footnotes whitelist[:attributes]['a'].push('id') whitelist[:attributes]['li'] = %w(id) - - # ...but remove any `id` properties not matching for footnotes whitelist[:transformers].push(self.class.remove_non_footnote_ids) whitelist @@ -129,8 +125,8 @@ module Banzai return unless node.name == 'a' || node.name == 'li' return unless node.has_attribute?('id') - return if node.name == 'a' && node['id'] =~ FOOTNOTE_LINK_REFERENCE_PATTERN - return if node.name == 'li' && node['id'] =~ FOOTNOTE_LI_REFERENCE_PATTERN + return if node.name == 'a' && node['id'] =~ Banzai::Filter::FootnoteFilter::FOOTNOTE_LINK_REFERENCE_PATTERN + return if node.name == 'li' && node['id'] =~ Banzai::Filter::FootnoteFilter::FOOTNOTE_LI_REFERENCE_PATTERN node.remove_attribute('id') end diff --git a/spec/lib/banzai/filter/footnote_filter_spec.rb b/spec/lib/banzai/filter/footnote_filter_spec.rb index 0b7807b2c4c..2e50e4e2351 100644 --- a/spec/lib/banzai/filter/footnote_filter_spec.rb +++ b/spec/lib/banzai/filter/footnote_filter_spec.rb @@ -9,16 +9,30 @@ describe Banzai::Filter::FootnoteFilter do # [^1]: one # [^second]: two let(:footnote) do - <<-EOF.strip_heredoc -

first1 and second2

-
    -
  1. -

    one

    -
  2. -
  3. -

    two

    -
  4. -
+ <<~EOF +

first1 and second2

+
    +
  1. +

    one

    +
  2. +
  3. +

    two

    +
  4. +
+ EOF + end + + let(:filtered_footnote) do + <<~EOF +

first1 and second2

+
    +
  1. +

    one

    +
  2. +
  3. +

    two

    +
  4. +
EOF end @@ -27,22 +41,8 @@ describe Banzai::Filter::FootnoteFilter do let(:link_node) { doc.css('sup > a').first } let(:identifier) { link_node[:id].delete_prefix('fnref1-') } - it 'adds identifier to footnotes' do - expect(link_node[:id]).to eq "fnref1-#{identifier}" - expect(link_node[:href]).to eq "#fn1-#{identifier}" - expect(doc.css("li[id=fn1-#{identifier}]")).not_to be_empty - expect(doc.css("li[id=fn1-#{identifier}] a[href=\"#fnref1-#{identifier}\"]")).not_to be_empty - end - - it 'uses the same identifier for all footnotes' do - expect(doc.css("li[id=fn2-#{identifier}]")).not_to be_empty - expect(doc.css("li[id=fn2-#{identifier}] a[href=\"#fnref2-#{identifier}\"]")).not_to be_empty - end - - it 'adds section and classes' do - expect(doc.css("section[class=footnotes]")).not_to be_empty - expect(doc.css("sup[class=footnote-ref]").count).to eq 2 - expect(doc.css("a[class=footnote-backref]").count).to eq 2 + it 'properly adds the necessary ids and classes' do + expect(doc.to_html).to eq filtered_footnote end end end diff --git a/spec/lib/banzai/filter/sanitization_filter_spec.rb b/spec/lib/banzai/filter/sanitization_filter_spec.rb index 0f9011cb655..836af18e0b6 100644 --- a/spec/lib/banzai/filter/sanitization_filter_spec.rb +++ b/spec/lib/banzai/filter/sanitization_filter_spec.rb @@ -302,21 +302,21 @@ describe Banzai::Filter::SanitizationFilter do end describe 'footnotes' do - it 'allows id property on links' do + it 'allows correct footnote id property on links' do exp = %q{foo/bar.md} act = filter(exp) expect(act.to_html).to eq exp end - it 'allows id property on li element' do + it 'allows correct footnote id property on li element' do exp = %q{
  1. footnote
} act = filter(exp) expect(act.to_html).to eq exp end - it 'only allows valid footnote formats for links' do + it 'removes invalid id for footnote links' do exp = %q{link} %w[fnrefx test xfnref1].each do |id| @@ -326,7 +326,7 @@ describe Banzai::Filter::SanitizationFilter do end end - it 'only allows valid footnote formats for li' do + it 'removes invalid id for footnote li' do exp = %q{
  1. footnote
} %w[fnx test xfn1].each do |id| diff --git a/spec/lib/banzai/pipeline/full_pipeline_spec.rb b/spec/lib/banzai/pipeline/full_pipeline_spec.rb index e9c7a2f352e..3634655c6a5 100644 --- a/spec/lib/banzai/pipeline/full_pipeline_spec.rb +++ b/spec/lib/banzai/pipeline/full_pipeline_spec.rb @@ -25,4 +25,36 @@ describe Banzai::Pipeline::FullPipeline do expect(result).to include(%{data-original='\">bad things'}) end end + + describe 'footnotes' do + let(:project) { create(:project, :public) } + let(:html) { described_class.to_html(footnote_markdown, project: project) } + let(:identifier) { html[/.*fnref1-(\d+).*/, 1] } + let(:footnote_markdown) do + <<~EOF + first[^1] and second[^second] + [^1]: one + [^second]: two + EOF + end + + let(:filtered_footnote) do + <<~EOF +

first1 and second2

+ +
    +
  1. +

    one

    +
  2. +
  3. +

    two

    +
  4. +
+ EOF + end + + it 'properly adds the necessary ids and classes' do + expect(html.lines.map(&:strip).join("\n")).to eq filtered_footnote + end + end end From 800220bd72be92cae212ab86cd1fc7abe6fa23e1 Mon Sep 17 00:00:00 2001 From: Brett Walker Date: Thu, 17 Jan 2019 10:35:18 -0600 Subject: [PATCH 5/6] Update to nokogiri 1.10.1 --- Gemfile | 2 +- Gemfile.lock | 4 ++-- qa/Gemfile | 2 +- qa/Gemfile.lock | 8 ++++---- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Gemfile b/Gemfile index ea33bb5c1e7..ea28ea3e2e2 100644 --- a/Gemfile +++ b/Gemfile @@ -127,7 +127,7 @@ gem 'asciidoctor-plantuml', '0.0.8' gem 'rouge', '~> 3.1' gem 'truncato', '~> 0.7.11' gem 'bootstrap_form', '~> 2.7.0' -gem 'nokogiri', '~> 1.10.0' +gem 'nokogiri', '~> 1.10.1' gem 'escape_utils', '~> 1.1' # Calendar rendering diff --git a/Gemfile.lock b/Gemfile.lock index a180a459eba..aed88b5de47 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -477,7 +477,7 @@ GEM net-ssh (5.0.1) netrc (0.11.0) nio4r (2.3.1) - nokogiri (1.10.0) + nokogiri (1.10.1) mini_portile2 (~> 2.4.0) nokogumbo (1.5.0) nokogiri @@ -1059,7 +1059,7 @@ DEPENDENCIES nakayoshi_fork (~> 0.0.4) net-ldap net-ssh (~> 5.0) - nokogiri (~> 1.10.0) + nokogiri (~> 1.10.1) oauth2 (~> 1.4) octokit (~> 4.9) omniauth (~> 1.8) diff --git a/qa/Gemfile b/qa/Gemfile index 75ad7bd07af..873eac1013f 100644 --- a/qa/Gemfile +++ b/qa/Gemfile @@ -7,4 +7,4 @@ gem 'rake', '~> 12.3.0' gem 'rspec', '~> 3.7' gem 'selenium-webdriver', '~> 3.12' gem 'airborne', '~> 0.2.13' -gem 'nokogiri', '~> 1.8.5' +gem 'nokogiri', '~> 1.10.1' diff --git a/qa/Gemfile.lock b/qa/Gemfile.lock index 55f3211482b..419cacdb2af 100644 --- a/qa/Gemfile.lock +++ b/qa/Gemfile.lock @@ -44,11 +44,11 @@ GEM mime-types-data (~> 3.2015) mime-types-data (3.2016.0521) mini_mime (1.0.0) - mini_portile2 (2.3.0) + mini_portile2 (2.4.0) minitest (5.11.1) netrc (0.11.0) - nokogiri (1.8.5) - mini_portile2 (~> 2.3.0) + nokogiri (1.10.1) + mini_portile2 (~> 2.4.0) pry (0.11.3) coderay (~> 1.1.0) method_source (~> 0.9.0) @@ -97,7 +97,7 @@ DEPENDENCIES airborne (~> 0.2.13) capybara (~> 2.16.1) capybara-screenshot (~> 1.0.18) - nokogiri (~> 1.8.5) + nokogiri (~> 1.10.0) pry-byebug (~> 3.5.1) rake (~> 12.3.0) rspec (~> 3.7) From 8bf78e3911962626aea6399afabe630e93ac85e9 Mon Sep 17 00:00:00 2001 From: Brett Walker Date: Thu, 17 Jan 2019 10:51:06 -0600 Subject: [PATCH 6/6] Bumping the CACHE_COMMONMARK_VERSION Since the rendering of footnotes is changed, the cached html need to be updated --- app/models/concerns/cache_markdown_field.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/concerns/cache_markdown_field.rb b/app/models/concerns/cache_markdown_field.rb index a8c9e54f00c..73a27326f6c 100644 --- a/app/models/concerns/cache_markdown_field.rb +++ b/app/models/concerns/cache_markdown_field.rb @@ -15,7 +15,7 @@ module CacheMarkdownField # Increment this number every time the renderer changes its output CACHE_REDCARPET_VERSION = 3 CACHE_COMMONMARK_VERSION_START = 10 - CACHE_COMMONMARK_VERSION = 12 + CACHE_COMMONMARK_VERSION = 13 # changes to these attributes cause the cache to be invalidates INVALIDATED_BY = %w[author project].freeze