From 9e3f5b980655817993682e409cbda72956d865cb Mon Sep 17 00:00:00 2001 From: Joel Drapper Date: Tue, 16 Apr 2024 22:31:35 +0100 Subject: [PATCH] Fix XSS --- lib/phlex/sgml.rb | 2 +- test/phlex/view/naughty_business.rb | 72 +++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 1 deletion(-) diff --git a/lib/phlex/sgml.rb b/lib/phlex/sgml.rb index 161be940..994adc38 100644 --- a/lib/phlex/sgml.rb +++ b/lib/phlex/sgml.rb @@ -382,7 +382,7 @@ def __attributes__(attributes, buffer = +"") end lower_name = name.downcase - next if lower_name == "href" && v.start_with?(/\s*javascript:/i) + next if lower_name == "href" && v.to_s.downcase.tr("\t \n", "").start_with?("javascript:") # Detect unsafe attribute names. Attribute names are considered unsafe if they match an event attribute or include unsafe characters. if HTML::EVENT_ATTRIBUTES.include?(lower_name) || name.match?(/[<>&"']/) diff --git a/test/phlex/view/naughty_business.rb b/test/phlex/view/naughty_business.rb index 6599191f..ad5d692e 100644 --- a/test/phlex/view/naughty_business.rb +++ b/test/phlex/view/naughty_business.rb @@ -85,6 +85,78 @@ def view_template end end + with "naughty javascript link protocol with a hidden tab character" do + view do + def view_template + a(href: "\tjavascript:alert(1)") { "XSS" } + a(href: "j\tavascript:alert(1)") { "XSS" } + a(href: "ja\tvascript:alert(1)") { "XSS" } + a(href: "jav\tascript:alert(1)") { "XSS" } + a(href: "java\tscript:alert(1)") { "XSS" } + a(href: "javas\tcript:alert(1)") { "XSS" } + a(href: "javasc\tript:alert(1)") { "XSS" } + a(href: "javascr\tipt:alert(1)") { "XSS" } + a(href: "javascri\tpt:alert(1)") { "XSS" } + a(href: "javascrip\tt:alert(1)") { "XSS" } + a(href: "javascript\t:alert(1)") { "XSS" } + a(href: "javascript:\talert(1)") { "XSS" } + end + end + + it "strips the javascript protocol" do + expect(output.scan("").size).to be == 12 + expect(output.scan("href").size).to be == 0 + end + end + + with "naughty javascript link protocol with a hidden newline character" do + view do + def view_template + a(href: "\njavascript:alert(1)") { "XSS" } + a(href: "j\navascript:alert(1)") { "XSS" } + a(href: "ja\nvascript:alert(1)") { "XSS" } + a(href: "jav\nascript:alert(1)") { "XSS" } + a(href: "java\nscript:alert(1)") { "XSS" } + a(href: "javas\ncript:alert(1)") { "XSS" } + a(href: "javasc\nript:alert(1)") { "XSS" } + a(href: "javascr\nipt:alert(1)") { "XSS" } + a(href: "javascri\npt:alert(1)") { "XSS" } + a(href: "javascrip\nt:alert(1)") { "XSS" } + a(href: "javascript\n:alert(1)") { "XSS" } + a(href: "javascript:\nalert(1)") { "XSS" } + end + end + + it "strips the javascript protocol" do + expect(output.scan("").size).to be == 12 + expect(output.scan("href").size).to be == 0 + end + end + + with "naughty javascript link protocol with a hidden whitespace character" do + view do + def view_template + a(href: " javascript:alert(1)") { "XSS" } + a(href: "j avascript:alert(1)") { "XSS" } + a(href: "ja vascript:alert(1)") { "XSS" } + a(href: "jav ascript:alert(1)") { "XSS" } + a(href: "java script:alert(1)") { "XSS" } + a(href: "javas cript:alert(1)") { "XSS" } + a(href: "javasc ript:alert(1)") { "XSS" } + a(href: "javascr ipt:alert(1)") { "XSS" } + a(href: "javascri pt:alert(1)") { "XSS" } + a(href: "javascrip t:alert(1)") { "XSS" } + a(href: "javascript :alert(1)") { "XSS" } + a(href: "javascript: alert(1)") { "XSS" } + end + end + + it "strips the javascript protocol" do + expect(output.scan("").size).to be == 12 + expect(output.scan("href").size).to be == 0 + end + end + Phlex::HTML::EVENT_ATTRIBUTES.each do |event_attribute| with "with naughty #{event_attribute} attribute" do naughty_attributes = { event_attribute => "alert(1);" }