SECURITY: fix XSS in excerpt parser
This commit is contained in:
parent
fbbd4999b6
commit
6e9f5f5584
|
@ -23,8 +23,15 @@ class ExcerptParser < Nokogiri::XML::SAX::Document
|
||||||
me.excerpt
|
me.excerpt
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def escape_attribute(v)
|
||||||
|
v.gsub("&", "&")
|
||||||
|
.gsub("\"", """)
|
||||||
|
.gsub("<", "<")
|
||||||
|
.gsub(">", ">")
|
||||||
|
end
|
||||||
|
|
||||||
def include_tag(name, attributes)
|
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
|
end
|
||||||
|
|
||||||
def start_element(name, attributes=[])
|
def start_element(name, attributes=[])
|
||||||
|
|
|
@ -75,6 +75,15 @@ describe PrettyText do
|
||||||
|
|
||||||
describe "Excerpt" do
|
describe "Excerpt" do
|
||||||
|
|
||||||
|
it "sanitizes attempts to inject invalid attributes" do
|
||||||
|
|
||||||
|
spinner = "<a href=\"http://thedailywtf.com/\" data-bbcode=\"' class='fa fa-spin\">WTF</a>"
|
||||||
|
PrettyText.excerpt(spinner, 20).should match_html spinner
|
||||||
|
|
||||||
|
spinner = %q{<a href="http://thedailywtf.com/" title="' class="fa fa-spin"><img src='http://thedailywtf.com/Resources/Images/Primary/logo.gif"></a>}
|
||||||
|
PrettyText.excerpt(spinner, 20).should match_html spinner
|
||||||
|
end
|
||||||
|
|
||||||
context "images" do
|
context "images" do
|
||||||
|
|
||||||
it "should dump images" do
|
it "should dump images" do
|
||||||
|
@ -94,8 +103,8 @@ describe PrettyText do
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should keep spoilers" do
|
it "should keep spoilers" do
|
||||||
PrettyText.excerpt("<div class='spoiler'><img src='http://cnn.com/a.gif'></div>", 100).should == "<span class='spoiler'>[image]</span>"
|
PrettyText.excerpt("<div class='spoiler'><img src='http://cnn.com/a.gif'></div>", 100).should match_html "<span class='spoiler'>[image]</span>"
|
||||||
PrettyText.excerpt("<span class='spoiler'>spoiler</div>", 100).should == "<span class='spoiler'>spoiler</span>"
|
PrettyText.excerpt("<span class='spoiler'>spoiler</div>", 100).should match_html "<span class='spoiler'>spoiler</span>"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -104,7 +113,7 @@ describe PrettyText do
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should preserve links" do
|
it "should preserve links" do
|
||||||
PrettyText.excerpt("<a href='http://cnn.com'>cnn</a>",100).should == "<a href='http://cnn.com'>cnn</a>"
|
PrettyText.excerpt("<a href='http://cnn.com'>cnn</a>",100).should match_html "<a href='http://cnn.com'>cnn</a>"
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should deal with special keys properly" do
|
it "should deal with special keys properly" do
|
||||||
|
@ -125,15 +134,15 @@ describe PrettyText do
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should not count the surrounds of a link" do
|
it "should not count the surrounds of a link" do
|
||||||
PrettyText.excerpt("<a href='http://cnn.com'>cnn</a>",3).should == "<a href='http://cnn.com'>cnn</a>"
|
PrettyText.excerpt("<a href='http://cnn.com'>cnn</a>",3).should match_html "<a href='http://cnn.com'>cnn</a>"
|
||||||
end
|
end
|
||||||
|
|
||||||
it "uses an ellipsis instead of html entities if provided with the option" do
|
it "uses an ellipsis instead of html entities if provided with the option" do
|
||||||
PrettyText.excerpt("<a href='http://cnn.com'>cnn</a>", 2, text_entities: true).should == "<a href='http://cnn.com'>cn...</a>"
|
PrettyText.excerpt("<a href='http://cnn.com'>cnn</a>", 2, text_entities: true).should match_html "<a href='http://cnn.com'>cn...</a>"
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should truncate links" do
|
it "should truncate links" do
|
||||||
PrettyText.excerpt("<a href='http://cnn.com'>cnn</a>",2).should == "<a href='http://cnn.com'>cn…</a>"
|
PrettyText.excerpt("<a href='http://cnn.com'>cnn</a>",2).should match_html "<a href='http://cnn.com'>cn…</a>"
|
||||||
end
|
end
|
||||||
|
|
||||||
it "doesn't extract empty quotes as links" do
|
it "doesn't extract empty quotes as links" do
|
||||||
|
@ -294,9 +303,6 @@ describe PrettyText do
|
||||||
PrettyText.cook("**你hello**").should match_html "<p><strong>你hello</strong></p>"
|
PrettyText.cook("**你hello**").should match_html "<p><strong>你hello</strong></p>"
|
||||||
end
|
end
|
||||||
|
|
||||||
it "sanitizes attempts to inject invalid attributes" do
|
|
||||||
PrettyText.cook("<a href=\"http://thedailywtf.com/\" data-bbcode=\"' class='fa fa-spin\">WTF</a>").should == "<p><a href=\"http://thedailywtf.com/\" rel=\"nofollow\">WTF</a></p>"
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -57,7 +57,7 @@ describe UserProfile do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with a user that has a link in their bio' do
|
context 'with a user that has a link in their bio' do
|
||||||
let(:user_profile) { Fabricate.build(:user_profile, bio_raw: "im sissy and i love http://ponycorns.com") }
|
let(:user_profile) { Fabricate.build(:user_profile, bio_raw: "I love http://discourse.org") }
|
||||||
let(:user) do
|
let(:user) do
|
||||||
user = Fabricate.build(:user, user_profile: user_profile)
|
user = Fabricate.build(:user, user_profile: user_profile)
|
||||||
user_profile.user = user
|
user_profile.user = user
|
||||||
|
@ -66,22 +66,22 @@ describe UserProfile do
|
||||||
|
|
||||||
let(:created_user) do
|
let(:created_user) do
|
||||||
user = Fabricate(:user)
|
user = Fabricate(:user)
|
||||||
user.user_profile.bio_raw = 'im sissy and i love http://ponycorns.com'
|
user.user_profile.bio_raw = 'I love http://discourse.org'
|
||||||
user.user_profile.save!
|
user.user_profile.save!
|
||||||
user
|
user
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'includes the link as nofollow if the user is not new' do
|
it 'includes the link as nofollow if the user is not new' do
|
||||||
user.user_profile.send(:cook)
|
user.user_profile.send(:cook)
|
||||||
expect(user_profile.bio_excerpt).to eq("im sissy and i love <a href='http://ponycorns.com' rel='nofollow'>http://ponycorns.com</a>")
|
expect(user_profile.bio_excerpt).to match_html("I love <a href='http://discourse.org' rel='nofollow'>http://discourse.org</a>")
|
||||||
expect(user_profile.bio_processed).to eq("<p>im sissy and i love <a href=\"http://ponycorns.com\" rel=\"nofollow\">http://ponycorns.com</a></p>")
|
expect(user_profile.bio_processed).to match_html("<p>I love <a href=\"http://discourse.org\" rel=\"nofollow\">http://discourse.org</a></p>")
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'removes the link if the user is new' do
|
it 'removes the link if the user is new' do
|
||||||
user.trust_level = TrustLevel.levels[:newuser]
|
user.trust_level = TrustLevel.levels[:newuser]
|
||||||
user_profile.send(:cook)
|
user_profile.send(:cook)
|
||||||
expect(user_profile.bio_excerpt).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("<p>im sissy and i love http://ponycorns.com</p>")
|
expect(user_profile.bio_processed).to eq("<p>I love http://discourse.org</p>")
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'leader_links_no_follow is false' do
|
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
|
it 'includes the link without nofollow if the user is trust level 3 or higher' do
|
||||||
user.trust_level = TrustLevel.levels[:leader]
|
user.trust_level = TrustLevel.levels[:leader]
|
||||||
user_profile.send(:cook)
|
user_profile.send(:cook)
|
||||||
expect(user_profile.bio_excerpt).to eq("im sissy and i love <a href='http://ponycorns.com'>http://ponycorns.com</a>")
|
expect(user_profile.bio_excerpt).to match_html("I love <a href='http://discourse.org'>http://discourse.org</a>")
|
||||||
expect(user_profile.bio_processed).to eq("<p>im sissy and i love <a href=\"http://ponycorns.com\">http://ponycorns.com</a></p>")
|
expect(user_profile.bio_processed).to match_html("<p>I love <a href=\"http://discourse.org\">http://discourse.org</a></p>")
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'removes nofollow from links in bio when trust level is increased' do
|
it 'removes nofollow from links in bio when trust level is increased' do
|
||||||
created_user.change_trust_level!(:leader)
|
created_user.change_trust_level!(:leader)
|
||||||
expect(created_user.user_profile.bio_excerpt).to eq("im sissy and i love <a href='http://ponycorns.com'>http://ponycorns.com</a>")
|
expect(created_user.user_profile.bio_excerpt).to match_html("I love <a href='http://discourse.org'>http://discourse.org</a>")
|
||||||
expect(created_user.user_profile.bio_processed).to eq("<p>im sissy and i love <a href=\"http://ponycorns.com\">http://ponycorns.com</a></p>")
|
expect(created_user.user_profile.bio_processed).to match_html("<p>I love <a href=\"http://discourse.org\">http://discourse.org</a></p>")
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'adds nofollow to links in bio when trust level is decreased' do
|
it 'adds nofollow to links in bio when trust level is decreased' do
|
||||||
created_user.trust_level = TrustLevel.levels[:leader]
|
created_user.trust_level = TrustLevel.levels[:leader]
|
||||||
created_user.save
|
created_user.save
|
||||||
created_user.change_trust_level!(:regular)
|
created_user.change_trust_level!(:regular)
|
||||||
expect(created_user.user_profile.bio_excerpt).to eq("im sissy and i love <a href='http://ponycorns.com' rel='nofollow'>http://ponycorns.com</a>")
|
expect(created_user.user_profile.bio_excerpt).to match_html("I love <a href='http://discourse.org' rel='nofollow'>http://discourse.org</a>")
|
||||||
expect(created_user.user_profile.bio_processed).to eq("<p>im sissy and i love <a href=\"http://ponycorns.com\" rel=\"nofollow\">http://ponycorns.com</a></p>")
|
expect(created_user.user_profile.bio_processed).to match_html("<p>I love <a href=\"http://discourse.org\" rel=\"nofollow\">http://discourse.org</a></p>")
|
||||||
end
|
end
|
||||||
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
|
it 'includes the link with nofollow if the user is trust level 3 or higher' do
|
||||||
user.trust_level = TrustLevel.levels[:leader]
|
user.trust_level = TrustLevel.levels[:leader]
|
||||||
user_profile.send(:cook)
|
user_profile.send(:cook)
|
||||||
expect(user_profile.bio_excerpt).to eq("im sissy and i love <a href='http://ponycorns.com' rel='nofollow'>http://ponycorns.com</a>")
|
expect(user_profile.bio_excerpt).to match_html("I love <a href='http://discourse.org' rel='nofollow'>http://discourse.org</a>")
|
||||||
expect(user_profile.bio_processed).to eq("<p>im sissy and i love <a href=\"http://ponycorns.com\" rel=\"nofollow\">http://ponycorns.com</a></p>")
|
expect(user_profile.bio_processed).to match_html("<p>I love <a href=\"http://discourse.org\" rel=\"nofollow\">http://discourse.org</a></p>")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue