From e2b7ebd3c4adb0da16c5d8c4a594942c11120f3a Mon Sep 17 00:00:00 2001
From: stephann <3025661+stephannv@users.noreply.github.com>
Date: Sat, 12 Oct 2024 17:03:50 -0300
Subject: [PATCH] feat: Add escape_once helper
---
spec/blueprint/html/helpers_spec.cr | 30 +++++++++++++++++++++++++++++
spec/blueprint/html/utils_spec.cr | 8 --------
spec/spec_helper.cr | 8 ++++++++
src/blueprint/html/helpers.cr | 14 +++++++++++++-
4 files changed, 51 insertions(+), 9 deletions(-)
diff --git a/spec/blueprint/html/helpers_spec.cr b/spec/blueprint/html/helpers_spec.cr
index 95b7d9a..afde4d4 100644
--- a/spec/blueprint/html/helpers_spec.cr
+++ b/spec/blueprint/html/helpers_spec.cr
@@ -52,4 +52,34 @@ describe "helpers" do
page.to_s.should contain expected_html
end
end
+
+ describe "#escape_once" do
+ it "escapes HTML without affecting existing escaped entities" do
+ actual_html = Blueprint::HTML.build do
+ span escape_once("<< Accept & Checkout")
+
+ div escape_once(MarkdownLink.new("1 < 2 & 3", "example.com"))
+ end
+
+ expected_html = normalize_html <<-HTML
+ << Accept & Checkout
+
+
[1 < 2 & 3](example.com)
+ HTML
+
+ actual_html.to_s.should eq expected_html
+ end
+
+ it "escapes unsafe content" do
+ actual_html = Blueprint::HTML.build do
+ span escape_once("")
+ end
+
+ expected_html = normalize_html <<-HTML
+ <script>alert('content')</script>
+ HTML
+
+ actual_html.to_s.should eq expected_html
+ end
+ end
end
diff --git a/spec/blueprint/html/utils_spec.cr b/spec/blueprint/html/utils_spec.cr
index fbde51c..9e3df05 100644
--- a/spec/blueprint/html/utils_spec.cr
+++ b/spec/blueprint/html/utils_spec.cr
@@ -1,13 +1,5 @@
require "../../spec_helper"
-private class MarkdownLink
- def initialize(@text : String, @href : String); end
-
- def to_s
- "[#{@text}](#{@href})"
- end
-end
-
private class ExamplePage
include Blueprint::HTML
diff --git a/spec/spec_helper.cr b/spec/spec_helper.cr
index 10276a5..6550fbd 100644
--- a/spec/spec_helper.cr
+++ b/spec/spec_helper.cr
@@ -1,6 +1,14 @@
require "spec"
require "../src/blueprint/html"
+class MarkdownLink
+ def initialize(@text : String, @href : String); end
+
+ def to_s
+ "[#{@text}](#{@href})"
+ end
+end
+
def normalize_html(html : String) : String
html.strip.gsub(/\R\s+/, "")
end
diff --git a/src/blueprint/html/helpers.cr b/src/blueprint/html/helpers.cr
index a9f563f..16d8508 100644
--- a/src/blueprint/html/helpers.cr
+++ b/src/blueprint/html/helpers.cr
@@ -1,8 +1,20 @@
module Blueprint::HTML::Helpers
- def safe(value) : SafeValue
+ HTML_ESCAPE_ONCE_REGEXP = /["><']|&(?!([a-zA-Z]+|(#\d+)|(#[xX][\dA-Fa-f]+));)/
+
+ HTML_ESCAPE = {"&": "&", ">": ">", "<": "<", %("): """, "'": "'"}
+
+ private def safe(value) : SafeValue
Blueprint::SafeValue.new(value)
end
+ private def escape_once(value : String) : SafeValue
+ safe value.gsub(HTML_ESCAPE_ONCE_REGEXP, HTML_ESCAPE)
+ end
+
+ private def escape_once(value) : SafeValue
+ escape_once(value.to_s)
+ end
+
@[Experimental]
macro tokens(**conditions)
String.build do |io|