Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ruby.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
strategy:
fail-fast: false
matrix:
ruby: [3.3, 3.2, 3.1, '3.0', 2.7]
ruby: [3.3, 3.2, 3.1, '3.0']
gemfile:
- Gemfile
- gemfiles/Gemfile-rails-6-0
Expand Down
2 changes: 1 addition & 1 deletion .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ inherit_gem:
rubocop-shopify: rubocop.yml

AllCops:
TargetRubyVersion: 2.7
TargetRubyVersion: 3.0
NewCops: disable
SuggestExtensions: false
102 changes: 59 additions & 43 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -12,80 +12,96 @@ PATH
GEM
remote: https://rubygems.org/
specs:
actionview (7.0.5)
activesupport (= 7.0.5)
actionview (7.1.3.2)
activesupport (= 7.1.3.2)
builder (~> 3.1)
erubi (~> 1.4)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.1, >= 1.2.0)
activesupport (7.0.5)
erubi (~> 1.11)
rails-dom-testing (~> 2.2)
rails-html-sanitizer (~> 1.6)
activesupport (7.1.3.2)
base64
bigdecimal
concurrent-ruby (~> 1.0, >= 1.0.2)
connection_pool (>= 2.2.5)
drb
i18n (>= 1.6, < 2)
minitest (>= 5.1)
mutex_m
tzinfo (~> 2.0)
ast (2.4.2)
base64 (0.2.0)
bigdecimal (3.1.7)
builder (3.2.4)
byebug (9.1.0)
coderay (1.1.2)
concurrent-ruby (1.2.2)
byebug (11.1.3)
coderay (1.1.3)
concurrent-ruby (1.2.3)
connection_pool (2.4.1)
crass (1.0.6)
drb (2.2.1)
erubi (1.12.0)
i18n (1.14.1)
i18n (1.14.4)
concurrent-ruby (~> 1.0)
json (2.6.2)
loofah (2.21.3)
json (2.7.1)
language_server-protocol (3.17.0.3)
loofah (2.22.0)
crass (~> 1.0.2)
nokogiri (>= 1.12.0)
method_source (0.9.0)
mini_portile2 (2.8.2)
minitest (5.16.2)
mocha (1.14.0)
nokogiri (1.15.2)
method_source (1.0.0)
mini_portile2 (2.8.5)
minitest (5.22.3)
mocha (2.1.0)
ruby2_keywords (>= 0.0.5)
mutex_m (0.2.0)
nokogiri (1.16.2)
mini_portile2 (~> 2.8.2)
racc (~> 1.4)
parallel (1.22.1)
parser (3.1.2.1)
parallel (1.24.0)
parser (3.3.0.5)
ast (~> 2.4.1)
pry (0.11.2)
coderay (~> 1.1.0)
method_source (~> 0.9.0)
pry-byebug (3.5.0)
byebug (~> 9.1)
pry (~> 0.10)
racc (1.7.0)
rails-dom-testing (2.0.3)
activesupport (>= 4.2.0)
racc
pry (0.14.2)
coderay (~> 1.1)
method_source (~> 1.0)
pry-byebug (3.10.1)
byebug (~> 11.0)
pry (>= 0.13, < 0.15)
racc (1.7.3)
rails-dom-testing (2.2.0)
activesupport (>= 5.0.0)
minitest
nokogiri (>= 1.6)
rails-html-sanitizer (1.6.0)
loofah (~> 2.21)
nokogiri (~> 1.14)
rainbow (3.1.1)
rake (13.0.6)
rake-compiler (1.2.0)
rake (13.1.0)
rake-compiler (1.2.7)
rake
regexp_parser (2.5.0)
rexml (3.2.5)
rubocop (1.35.0)
regexp_parser (2.9.0)
rexml (3.2.6)
rubocop (1.62.1)
json (~> 2.3)
language_server-protocol (>= 3.17.0)
parallel (~> 1.10)
parser (>= 3.1.2.1)
parser (>= 3.3.0.2)
rainbow (>= 2.2.2, < 4.0)
regexp_parser (>= 1.8, < 3.0)
rexml (>= 3.2.5, < 4.0)
rubocop-ast (>= 1.20.1, < 2.0)
rubocop-ast (>= 1.31.1, < 2.0)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 1.4.0, < 3.0)
rubocop-ast (1.21.0)
parser (>= 3.1.1.0)
rubocop-shopify (2.9.0)
rubocop (~> 1.33)
ruby-progressbar (1.11.0)
ruby_memcheck (1.3.2)
unicode-display_width (>= 2.4.0, < 3.0)
rubocop-ast (1.31.2)
parser (>= 3.3.0.4)
rubocop-shopify (2.15.1)
rubocop (~> 1.51)
ruby-progressbar (1.13.0)
ruby2_keywords (0.0.5)
ruby_memcheck (2.3.0)
nokogiri
smart_properties (1.17.0)
tzinfo (2.0.6)
concurrent-ruby (~> 1.0)
unicode-display_width (2.2.0)
unicode-display_width (2.5.0)

PLATFORMS
ruby
Expand Down
2 changes: 1 addition & 1 deletion better_html.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Gem::Specification.new do |s|
s.description = "Better HTML for Rails. Provides sane html helpers that make it easier to do the right thing."
s.license = "MIT"

s.required_ruby_version = ">= 2.7.0"
s.required_ruby_version = ">= 3.0.0"

s.metadata = {
"bug_tracker_uri" => "https://github.com/Shopify/better-html/issues",
Expand Down
2 changes: 1 addition & 1 deletion ext/better_html_ext/html_tokenizer.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

static VALUE mHtmlTokenizer = Qnil;

void Init_better_html_ext()
void Init_better_html_ext(void)
{
mHtmlTokenizer = rb_define_module("HtmlTokenizer");
Init_html_tokenizer_tokenizer(mHtmlTokenizer);
Expand Down
2 changes: 1 addition & 1 deletion lib/better_html/better_erb.rb
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ def generate(template, source)

generator = klass.new(
erb,
**options
**options,
)
generator.validate! if generator.respond_to?(:validate!)
generator.src
Expand Down
4 changes: 2 additions & 2 deletions lib/better_html/better_erb/runtime_checks.rb
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ def check_tag_name(type, start, stop, line, column)
return if text.upcase == "!DOCTYPE"
return if @config.partial_tag_name_pattern.match?(text)

s = +"Invalid tag name #{text.inspect} does not match "\
s = +"Invalid tag name #{text.inspect} does not match " \
"regular expression #{@config.partial_tag_name_pattern.inspect}\n"
s << build_location(line, column, text.size)
raise BetterHtml::HtmlError, s
Expand All @@ -129,7 +129,7 @@ def check_attribute_name(type, start, stop, line, column)
text = @parser.document[start...stop]
return if @config.partial_attribute_name_pattern.match?(text)

s = +"Invalid attribute name #{text.inspect} does not match "\
s = +"Invalid attribute name #{text.inspect} does not match " \
"regular expression #{@config.partial_attribute_name_pattern.inspect}\n"
s << build_location(line, column, text.size)
raise BetterHtml::HtmlError, s
Expand Down
34 changes: 17 additions & 17 deletions lib/better_html/better_erb/validated_output_buffer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,23 +17,23 @@ def safe_quoted_value_append=(value)
value = properly_escaped(value)

if value.include?(@context[:quote_character])
raise UnsafeHtmlError, "Detected invalid characters as part of the interpolation "\
raise UnsafeHtmlError, "Detected invalid characters as part of the interpolation " \
"into a quoted attribute value. The value cannot contain the character #{@context[:quote_character]}."
end

@output.safe_append = value
end

def safe_unquoted_value_append=(value)
raise DontInterpolateHere, "Do not interpolate without quotes around this "\
"attribute value. Instead of "\
"<#{@context[:tag_name]} #{@context[:attribute_name]}=#{@context[:attribute_value]}<%=#{@code}%>> "\
raise DontInterpolateHere, "Do not interpolate without quotes around this " \
"attribute value. Instead of " \
"<#{@context[:tag_name]} #{@context[:attribute_name]}=#{@context[:attribute_value]}<%=#{@code}%>> " \
"try <#{@context[:tag_name]} #{@context[:attribute_name]}=\"#{@context[:attribute_value]}<%=#{@code}%>\">."
end

def safe_space_after_attribute_append=(value)
raise DontInterpolateHere, "Add a space after this attribute value. Instead of "\
"<#{@context[:tag_name]} #{@context[:attribute_name]}=\"#{@context[:attribute_value]}\"<%=#{@code}%>> "\
raise DontInterpolateHere, "Add a space after this attribute value. Instead of " \
"<#{@context[:tag_name]} #{@context[:attribute_name]}=\"#{@context[:attribute_value]}\"<%=#{@code}%>> " \
"try <#{@context[:tag_name]} #{@context[:attribute_name]}=\"#{@context[:attribute_value]}\" <%=#{@code}%>>."
end

Expand All @@ -43,7 +43,7 @@ def safe_attribute_name_append=(value)
value = value.to_s

unless value =~ /\A[a-z0-9\-]*\z/
raise UnsafeHtmlError, "Detected invalid characters as part of the interpolation "\
raise UnsafeHtmlError, "Detected invalid characters as part of the interpolation " \
"into a attribute name around '#{@context[:attribute_name]}<%=#{@code}%>'."
end

Expand All @@ -54,25 +54,25 @@ def safe_after_attribute_name_append=(value)
return if value.nil?

unless value.is_a?(BetterHtml::HtmlAttributes)
raise DontInterpolateHere, "Do not interpolate #{value.class} in a tag. "\
"Instead of <#{@context[:tag_name]} <%=#{@code}%>> please "\
raise DontInterpolateHere, "Do not interpolate #{value.class} in a tag. " \
"Instead of <#{@context[:tag_name]} <%=#{@code}%>> please " \
"try <#{@context[:tag_name]} <%= html_attributes(attr: value) %>>."
end

@output.safe_append = value.to_s
end

def safe_after_equal_append=(value)
raise DontInterpolateHere, "Do not interpolate without quotes after "\
raise DontInterpolateHere, "Do not interpolate without quotes after " \
"attribute around '#{@context[:attribute_name]}=<%=#{@code}%>'."
end

def safe_tag_append=(value)
return if value.nil?

unless value.is_a?(BetterHtml::HtmlAttributes)
raise DontInterpolateHere, "Do not interpolate #{value.class} in a tag. "\
"Instead of <#{@context[:tag_name]} <%=#{@code}%>> please "\
raise DontInterpolateHere, "Do not interpolate #{value.class} in a tag. " \
"Instead of <#{@context[:tag_name]} <%=#{@code}%>> please " \
"try <#{@context[:tag_name]} <%= html_attributes(attr: value) %>>."
end

Expand All @@ -85,7 +85,7 @@ def safe_tag_name_append=(value)
value = value.to_s

unless value =~ /\A[a-z0-9\:\-]*\z/
raise UnsafeHtmlError, "Detected invalid characters as part of the interpolation "\
raise UnsafeHtmlError, "Detected invalid characters as part of the interpolation " \
"into a tag name around: <#{@context[:tag_name]}<%=#{@code}%>>."
end

Expand All @@ -100,12 +100,12 @@ def safe_rawtext_append=(value)
if @context[:tag_name].downcase == "script" &&
(value =~ /<script/i || value =~ %r{</script}i)
# https://www.w3.org/TR/html5/scripting-1.html#restrictions-for-contents-of-script-elements
raise UnsafeHtmlError, "Detected invalid characters as part of the interpolation "\
"into a script tag around: <#{@context[:tag_name]}>#{@context[:rawtext_text]}<%=#{@code}%>. "\
raise UnsafeHtmlError, "Detected invalid characters as part of the interpolation " \
"into a script tag around: <#{@context[:tag_name]}>#{@context[:rawtext_text]}<%=#{@code}%>. " \
"A script tag cannot contain <script or </script anywhere inside of it."
elsif value =~ /<#{Regexp.escape(@context[:tag_name].downcase)}/i ||
value =~ %r{</#{Regexp.escape(@context[:tag_name].downcase)}}i
raise UnsafeHtmlError, "Detected invalid characters as part of the interpolation "\
raise UnsafeHtmlError, "Detected invalid characters as part of the interpolation " \
"into a #{@context[:tag_name].downcase} tag around: " \
"<#{@context[:tag_name]}>#{@context[:rawtext_text]}<%=#{@code}%>."
end
Expand All @@ -120,7 +120,7 @@ def safe_comment_append=(value)

# in a <!-- ...here --> we disallow -->
if value =~ /-->/
raise UnsafeHtmlError, "Detected invalid characters as part of the interpolation "\
raise UnsafeHtmlError, "Detected invalid characters as part of the interpolation " \
"into a html comment around: <!--#{@context[:comment_text]}<%=#{@code}%>."
end

Expand Down
2 changes: 1 addition & 1 deletion lib/better_html/html_attributes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ def to_s
value = value.to_s
escaped_value = value.html_safe? ? value : CGI.escapeHTML(value)
if escaped_value.include?('"')
raise ArgumentError, "The value provided for attribute '#{key}' contains a `\"` "\
raise ArgumentError, "The value provided for attribute '#{key}' contains a `\"` " \
"character which is not allowed. Did you call .html_safe without properly escaping this data?"
end
"#{key}=\"#{escaped_value}\""
Expand Down
23 changes: 16 additions & 7 deletions lib/better_html/parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ def parser_errors
@erb.parser.errors.map do |error|
Error.new(
error.message,
location: Tokenizer::Location.new(@buffer, error.position, error.position + 1)
location: Tokenizer::Location.new(@buffer, error.position, error.position + 1),
)
end
end
Expand Down Expand Up @@ -183,9 +183,13 @@ def build_attribute_name_node(tokens)
end

def build_attribute_value_node(tokens)
children = shift_all_with_interpolation(tokens,
:attribute_quoted_value_start, :attribute_quoted_value,
:attribute_quoted_value_end, :attribute_unquoted_value)
children = shift_all_with_interpolation(
tokens,
:attribute_quoted_value_start,
:attribute_quoted_value,
:attribute_quoted_value_end,
:attribute_unquoted_value,
)

build_node(:attribute_value, children)
end
Expand All @@ -199,7 +203,7 @@ def build_node(type, tokens, pre: nil, post: nil)
BetterHtml::AST::Node.new(
type,
tokens.present? ? wrap_tokens(tokens) : [],
loc: tokens.present? ? build_location([pre, *tokens, post]) : empty_location
loc: tokens.present? ? build_location([pre, *tokens, post]) : empty_location,
)
end

Expand Down Expand Up @@ -298,8 +302,13 @@ def wrap_token(object)

if object.is_a?(::AST::Node)
object
elsif [:text, :tag_name, :attribute_name, :attribute_quoted_value,
:attribute_unquoted_value,].include?(object.type)
elsif [
:text,
:tag_name,
:attribute_name,
:attribute_quoted_value,
:attribute_unquoted_value,
].include?(object.type)
object.loc.source
elsif [:attribute_quoted_value_start, :attribute_quoted_value_end].include?(object.type)
BetterHtml::AST::Node.new(:quote, [object.loc.source], loc: object.loc)
Expand Down
2 changes: 0 additions & 2 deletions lib/better_html/railtie.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@ module BetterHtml
class Railtie < Rails::Railtie
initializer "better_html.better_erb.initialization" do
BetterHtml::BetterErb.prepend!
end

config.after_initialize do
ActiveSupport.on_load(:action_view) do
BetterHtml.config.annotate_rendered_view_with_filenames = ActionView::Base.annotate_rendered_view_with_filenames
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def validate_type(tag)

add_error(
"#{type_attribute.value} is not a valid type, valid types are #{VALID_JAVASCRIPT_TAG_TYPES.join(", ")}",
location: type_attribute.loc
location: type_attribute.loc,
)
end
end
Expand Down
Loading