From 6e9f5f5584dfad284121ab868118772a2f077d87 Mon Sep 17 00:00:00 2001 From: Sam Date: Fri, 25 Jul 2014 12:15:43 +1000 Subject: [PATCH] SECURITY: fix XSS in excerpt parser --- lib/excerpt_parser.rb | 9 ++++++++- spec/components/pretty_text_spec.rb | 24 +++++++++++++++--------- spec/models/user_profile_spec.rb | 28 ++++++++++++++-------------- 3 files changed, 37 insertions(+), 24 deletions(-) diff --git a/lib/excerpt_parser.rb b/lib/excerpt_parser.rb index 9afa2f2d991..03485ad812e 100644 --- a/lib/excerpt_parser.rb +++ b/lib/excerpt_parser.rb @@ -23,8 +23,15 @@ class ExcerptParser < Nokogiri::XML::SAX::Document me.excerpt end + def escape_attribute(v) + v.gsub("&", "&") + .gsub("\"", """) + .gsub("<", "<") + .gsub(">", ">") + end + def include_tag(name, attributes) - characters("<#{name} #{attributes.map{|k,v| "#{k}='#{v}'"}.join(' ')}>", false, false, false) + characters("<#{name} #{attributes.map{|k,v| "#{k}=\"#{escape_attribute(v)}\""}.join(' ')}>", false, false, false) end def start_element(name, attributes=[]) diff --git a/spec/components/pretty_text_spec.rb b/spec/components/pretty_text_spec.rb index add42031657..3dc0e9fbb43 100644 --- a/spec/components/pretty_text_spec.rb +++ b/spec/components/pretty_text_spec.rb @@ -75,6 +75,15 @@ describe PrettyText do describe "Excerpt" do + it "sanitizes attempts to inject invalid attributes" do + + spinner = "", 100).should == "[image]" - PrettyText.excerpt("spoiler", 100).should == "spoiler" + PrettyText.excerpt("
", 100).should match_html "[image]" + PrettyText.excerpt("spoiler", 100).should match_html "spoiler" end end @@ -104,7 +113,7 @@ describe PrettyText do end it "should preserve links" do - PrettyText.excerpt("
cnn",100).should == "cnn" + PrettyText.excerpt("cnn",100).should match_html "cnn" end it "should deal with special keys properly" do @@ -125,15 +134,15 @@ describe PrettyText do end it "should not count the surrounds of a link" do - PrettyText.excerpt("cnn",3).should == "cnn" + PrettyText.excerpt("cnn",3).should match_html "cnn" end it "uses an ellipsis instead of html entities if provided with the option" do - PrettyText.excerpt("cnn", 2, text_entities: true).should == "cn..." + PrettyText.excerpt("cnn", 2, text_entities: true).should match_html "cn..." end it "should truncate links" do - PrettyText.excerpt("cnn",2).should == "cn…" + PrettyText.excerpt("cnn",2).should match_html "cn…" end it "doesn't extract empty quotes as links" do @@ -294,9 +303,6 @@ describe PrettyText do PrettyText.cook("**你hello**").should match_html "

你hello

" end - it "sanitizes attempts to inject invalid attributes" do - PrettyText.cook("http://ponycorns.com") - expect(user_profile.bio_processed).to eq("

im sissy and i love http://ponycorns.com

") + expect(user_profile.bio_excerpt).to match_html("I love http://discourse.org") + expect(user_profile.bio_processed).to match_html("

I love http://discourse.org

") end it 'removes the link if the user is new' do user.trust_level = TrustLevel.levels[:newuser] user_profile.send(:cook) - expect(user_profile.bio_excerpt).to eq("im sissy and i love http://ponycorns.com") - expect(user_profile.bio_processed).to eq("

im sissy and i love http://ponycorns.com

") + expect(user_profile.bio_excerpt).to match_html("I love http://discourse.org") + expect(user_profile.bio_processed).to eq("

I love http://discourse.org

") end context 'leader_links_no_follow is false' do @@ -90,22 +90,22 @@ describe UserProfile do it 'includes the link without nofollow if the user is trust level 3 or higher' do user.trust_level = TrustLevel.levels[:leader] user_profile.send(:cook) - expect(user_profile.bio_excerpt).to eq("im sissy and i love http://ponycorns.com") - expect(user_profile.bio_processed).to eq("

im sissy and i love http://ponycorns.com

") + expect(user_profile.bio_excerpt).to match_html("I love http://discourse.org") + expect(user_profile.bio_processed).to match_html("

I love http://discourse.org

") end it 'removes nofollow from links in bio when trust level is increased' do created_user.change_trust_level!(:leader) - expect(created_user.user_profile.bio_excerpt).to eq("im sissy and i love http://ponycorns.com") - expect(created_user.user_profile.bio_processed).to eq("

im sissy and i love http://ponycorns.com

") + expect(created_user.user_profile.bio_excerpt).to match_html("I love http://discourse.org") + expect(created_user.user_profile.bio_processed).to match_html("

I love http://discourse.org

") end it 'adds nofollow to links in bio when trust level is decreased' do created_user.trust_level = TrustLevel.levels[:leader] created_user.save created_user.change_trust_level!(:regular) - expect(created_user.user_profile.bio_excerpt).to eq("im sissy and i love http://ponycorns.com") - expect(created_user.user_profile.bio_processed).to eq("

im sissy and i love http://ponycorns.com

") + expect(created_user.user_profile.bio_excerpt).to match_html("I love http://discourse.org") + expect(created_user.user_profile.bio_processed).to match_html("

I love http://discourse.org

") end end @@ -115,8 +115,8 @@ describe UserProfile do it 'includes the link with nofollow if the user is trust level 3 or higher' do user.trust_level = TrustLevel.levels[:leader] user_profile.send(:cook) - expect(user_profile.bio_excerpt).to eq("im sissy and i love http://ponycorns.com") - expect(user_profile.bio_processed).to eq("

im sissy and i love http://ponycorns.com

") + expect(user_profile.bio_excerpt).to match_html("I love http://discourse.org") + expect(user_profile.bio_processed).to match_html("

I love http://discourse.org

") end end end