Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add SafeValue to render content without escaping #71

Merged
merged 1 commit into from
Sep 14, 2024
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
21 changes: 21 additions & 0 deletions spec/blueprint/html/helpers_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ private class ExamplePage

private def blueprint
div("Tokens", class: ["a", "b", tokens(c?: "c", d?: "d", e?: "e")])

div(onclick: safe("<script>Attribute script</script>")) { safe("<script>Good Script</script>") }

span safe("<script>Another Good Script</script>")
end

private def c?
Expand All @@ -31,4 +35,21 @@ describe "helpers" do
page.to_s.should contain expected_html
end
end

describe "#safe" do
it "returns an object that Blueprint will understand as safe to render without escaping" do
page = ExamplePage.new
expected_html = normalize_html <<-HTML
<div onclick="<script>Attribute script</script>">
<script>Good Script</script>
</div>

<span>
<script>Another Good Script</script>
</span>
HTML

page.to_s.should contain expected_html
end
end
end
2 changes: 1 addition & 1 deletion spec/blueprint/html/utils_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ private class ExamplePage
comment "This is an html comment"

div do
unsafe_raw "<script>Dangerous script</script>"
raw safe("<script>Dangerous script</script>")
end
end
end
Expand Down
5 changes: 5 additions & 0 deletions src/blueprint/html.cr
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ require "html"

require "./html/attributes_handler"
require "./html/block_renderer"
require "./html/buffer_appender"
require "./html/builder"
require "./html/component_registrar"
require "./html/component_renderer"
Expand All @@ -13,9 +14,13 @@ require "./html/style_builder"
require "./html/svg"
require "./html/utils"

require "./safe_object"
require "./safe_value"

module Blueprint::HTML
include Blueprint::HTML::AttributesHandler
include Blueprint::HTML::BlockRenderer
include Blueprint::HTML::BufferAppender
include Blueprint::HTML::ComponentRegistrar
include Blueprint::HTML::ComponentRenderer
include Blueprint::HTML::ElementRegistrar
Expand Down
2 changes: 1 addition & 1 deletion src/blueprint/html/attributes_handler.cr
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ module Blueprint::HTML::AttributesHandler
@buffer << " "
@buffer << parse_attribute_name(attribute_name)
@buffer << %(=")
::HTML.escape(attribute_value.to_s, @buffer)
append_to_buffer(attribute_value)
@buffer << %(")
end

Expand Down
6 changes: 3 additions & 3 deletions src/blueprint/html/block_renderer.cr
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ module Blueprint::HTML::BlockRenderer
private def capture_content(&) : Nil
buffer_size_before_block_evaluation = @buffer.bytesize
content = yield
if buffer_size_before_block_evaluation == @buffer.bytesize
::HTML.escape(content.to_s, @buffer)
end
return if buffer_size_before_block_evaluation != @buffer.bytesize # return if something was written to buffer

append_to_buffer(content)
end
end
16 changes: 16 additions & 0 deletions src/blueprint/html/buffer_appender.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
module Blueprint::HTML::BufferAppender
private def append_to_buffer(content : String)
::HTML.escape(content, @buffer)
end

private def append_to_buffer(content : SafeObject)
content.to_s(@buffer)
end

private def append_to_buffer(content : Nil)
end

private def append_to_buffer(content)
::HTML.escape(content.to_s, @buffer)
end
end
2 changes: 1 addition & 1 deletion src/blueprint/html/element_registrar.cr
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ module Blueprint::HTML::ElementRegistrar
element({{tag}}, "", **attributes)
end

private def {{method_name.id}}(__content__ : String, **attributes) : Nil
private def {{method_name.id}}(__content__, **attributes) : Nil
element({{tag}}, __content__, **attributes)
end
end
Expand Down
4 changes: 2 additions & 2 deletions src/blueprint/html/element_renderer.cr
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ module Blueprint::HTML::ElementRenderer
@buffer << ">"
end

private def element(tag_name : String | Symbol, __content__ : String, **attributes) : Nil
private def element(tag_name : String | Symbol, __content__, **attributes) : Nil
@buffer << "<"
@buffer << tag_name
append_attributes(attributes)
@buffer << ">"
::HTML.escape(__content__, @buffer)
append_to_buffer(__content__)
@buffer << "</"
@buffer << tag_name
@buffer << ">"
Expand Down
4 changes: 4 additions & 0 deletions src/blueprint/html/helpers.cr
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
module Blueprint::HTML::Helpers
def safe(value) : SafeValue
Blueprint::SafeValue.new(value)
end

macro tokens(**conditions)
String.build do |io|
{% for key, value in conditions %}
Expand Down
6 changes: 3 additions & 3 deletions src/blueprint/html/utils.cr
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module Blueprint::HTML::Utils
private def plain(content : String) : Nil
::HTML.escape(content, @buffer)
append_to_buffer(content)
end

private def doctype : Nil
Expand All @@ -17,7 +17,7 @@ module Blueprint::HTML::Utils
@buffer << " "
end

def unsafe_raw(content : String) : Nil
@buffer << content
def raw(content : SafeObject) : Nil
append_to_buffer(content)
end
end
2 changes: 2 additions & 0 deletions src/blueprint/safe_object.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
module Blueprint::SafeObject
end
11 changes: 11 additions & 0 deletions src/blueprint/safe_value.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
class Blueprint::SafeValue(T)
include SafeObject

getter value : T

def initialize(@value : T); end

def to_s(io : String::Builder)
@value.to_s(io)
end
end
Loading