phlex 2.1.1 → 2.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/phlex/csv.rb +9 -3
- data/lib/phlex/html.rb +1 -1
- data/lib/phlex/sgml/state.rb +12 -15
- data/lib/phlex/sgml.rb +41 -15
- data/lib/phlex/svg.rb +1 -1
- data/lib/phlex/version.rb +1 -1
- data/lib/phlex.rb +0 -1
- metadata +3 -4
- data/lib/phlex/vanish.rb +0 -22
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 58ef5e091c9feed2677149231356e32305898a8d2bb271a0e603bcc208ead0fd
|
|
4
|
+
data.tar.gz: bab9a697db8f529f0221afb1fd491e30d2f4b0c702a253f7a0446f8a075ddc82
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1e92858e2c2a177a120579e7cbb4c7489456a8faa0a40ecbce9fe6070f7ef01ace1e4f13baf72a9f9baf841ed57ea9b74f9cf2aa262fc774eaa95178a9248b63
|
|
7
|
+
data.tar.gz: 8040edc5a9640fb638280bccb4e879ab97d855b739f5eab04fc7ffcf7fcb7d7ca50a06ae808c31e67f9fdb7c286062c35ff40483abfaf42ac9637e2abe32c67e
|
data/lib/phlex/csv.rb
CHANGED
|
@@ -168,7 +168,9 @@ class Phlex::CSV
|
|
|
168
168
|
value = value.strip
|
|
169
169
|
|
|
170
170
|
if escape_csv_injection
|
|
171
|
-
if
|
|
171
|
+
if value.empty?
|
|
172
|
+
buffer << value
|
|
173
|
+
elsif FORMULA_PREFIXES_MAP[value.getbyte(0)]
|
|
172
174
|
value.gsub!('"', '""')
|
|
173
175
|
buffer << '"\'' << value << '"'
|
|
174
176
|
elsif value.match?(escape_regex)
|
|
@@ -184,7 +186,9 @@ class Phlex::CSV
|
|
|
184
186
|
if escape_csv_injection
|
|
185
187
|
first_byte = value.getbyte(0)
|
|
186
188
|
|
|
187
|
-
if
|
|
189
|
+
if value.empty?
|
|
190
|
+
buffer << '""'
|
|
191
|
+
elsif FORMULA_PREFIXES_MAP[first_byte]
|
|
188
192
|
buffer << '"\'' << value.gsub('"', '""') << '"'
|
|
189
193
|
elsif value.match?(escape_regex)
|
|
190
194
|
buffer << '"' << value.gsub('"', '""') << '"'
|
|
@@ -192,7 +196,9 @@ class Phlex::CSV
|
|
|
192
196
|
buffer << value
|
|
193
197
|
end
|
|
194
198
|
else # not escaping CSV injection
|
|
195
|
-
if value.
|
|
199
|
+
if value.empty?
|
|
200
|
+
buffer << '""'
|
|
201
|
+
elsif value.match?(escape_regex)
|
|
196
202
|
buffer << '"' << value.gsub('"', '""') << '"'
|
|
197
203
|
else
|
|
198
204
|
buffer << value
|
data/lib/phlex/html.rb
CHANGED
|
@@ -58,7 +58,7 @@ class Phlex::HTML < Phlex::SGML
|
|
|
58
58
|
raise Phlex::ArgumentError.new("Expected the tag name to be a Symbol.")
|
|
59
59
|
end
|
|
60
60
|
|
|
61
|
-
if (tag = StandardElements.__registered_elements__[name]) || (tag = name.name.tr("_", "-")).include?("-")
|
|
61
|
+
if (tag = StandardElements.__registered_elements__[name]) || ((tag = name.name.tr("_", "-")).include?("-") && tag.match?(/\A[a-z0-9-]+\z/))
|
|
62
62
|
if attributes.length > 0 # with attributes
|
|
63
63
|
if block_given # with content block
|
|
64
64
|
buffer << "<#{tag}" << (Phlex::ATTRIBUTE_CACHE[attributes] ||= __attributes__(attributes)) << ">"
|
data/lib/phlex/sgml/state.rb
CHANGED
|
@@ -14,16 +14,7 @@ class Phlex::SGML::State
|
|
|
14
14
|
|
|
15
15
|
attr_accessor :capturing, :user_context
|
|
16
16
|
|
|
17
|
-
attr_reader :fragments, :fragment_depth, :output_buffer
|
|
18
|
-
|
|
19
|
-
def buffer
|
|
20
|
-
case @buffer
|
|
21
|
-
when Proc
|
|
22
|
-
@buffer.call
|
|
23
|
-
else
|
|
24
|
-
@buffer
|
|
25
|
-
end
|
|
26
|
-
end
|
|
17
|
+
attr_reader :fragments, :fragment_depth, :output_buffer, :buffer
|
|
27
18
|
|
|
28
19
|
def around_render(component)
|
|
29
20
|
stack = @stack
|
|
@@ -85,17 +76,23 @@ class Phlex::SGML::State
|
|
|
85
76
|
end
|
|
86
77
|
|
|
87
78
|
def caching(&)
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
79
|
+
result = nil
|
|
80
|
+
|
|
81
|
+
capture do
|
|
82
|
+
@cache_stack.push([buffer, {}].freeze)
|
|
83
|
+
yield
|
|
84
|
+
result = @cache_stack.pop
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
result
|
|
92
88
|
end
|
|
93
89
|
|
|
94
90
|
def caching?
|
|
95
91
|
@cache_stack.length > 0
|
|
96
92
|
end
|
|
97
93
|
|
|
98
|
-
def
|
|
94
|
+
def capture
|
|
95
|
+
new_buffer = +""
|
|
99
96
|
original_buffer = @buffer
|
|
100
97
|
original_capturing = @capturing
|
|
101
98
|
original_fragments = @fragments
|
data/lib/phlex/sgml.rb
CHANGED
|
@@ -3,7 +3,13 @@
|
|
|
3
3
|
# **Standard Generalized Markup Language** for behaviour common to {HTML} and {SVG}.
|
|
4
4
|
class Phlex::SGML
|
|
5
5
|
UNSAFE_ATTRIBUTES = Set.new(%w[srcdoc sandbox http-equiv]).freeze
|
|
6
|
-
REF_ATTRIBUTES = Set.new(%w[href src action formaction lowsrc dynsrc background ping]).freeze
|
|
6
|
+
REF_ATTRIBUTES = Set.new(%w[href src action formaction lowsrc dynsrc background ping xlinkhref]).freeze
|
|
7
|
+
NAMED_CHARACTER_REFERENCES = {
|
|
8
|
+
"colon" => ":",
|
|
9
|
+
"tab" => "\t",
|
|
10
|
+
"newline" => "\n",
|
|
11
|
+
}.freeze
|
|
12
|
+
UNSAFE_ATTRIBUTE_NAME_CHARS = %r([<>&"'/=\s\x00])
|
|
7
13
|
|
|
8
14
|
ERBCompiler = ERB::Compiler.new("<>").tap do |compiler|
|
|
9
15
|
compiler.pre_cmd = [""]
|
|
@@ -217,9 +223,9 @@ class Phlex::SGML
|
|
|
217
223
|
return "" unless block
|
|
218
224
|
|
|
219
225
|
if args.length > 0
|
|
220
|
-
@_state.
|
|
226
|
+
@_state.capture { __yield_content_with_args__(*args, &block) }
|
|
221
227
|
else
|
|
222
|
-
@_state.
|
|
228
|
+
@_state.capture { __yield_content__(&block) }
|
|
223
229
|
end
|
|
224
230
|
end
|
|
225
231
|
|
|
@@ -298,6 +304,8 @@ class Phlex::SGML
|
|
|
298
304
|
].freeze
|
|
299
305
|
|
|
300
306
|
low_level_cache(full_key, **, &content)
|
|
307
|
+
|
|
308
|
+
nil
|
|
301
309
|
end
|
|
302
310
|
|
|
303
311
|
# Cache a block of content where you control the entire cache key.
|
|
@@ -331,6 +339,8 @@ class Phlex::SGML
|
|
|
331
339
|
end
|
|
332
340
|
end
|
|
333
341
|
end
|
|
342
|
+
|
|
343
|
+
nil
|
|
334
344
|
end
|
|
335
345
|
|
|
336
346
|
def json_escape(string)
|
|
@@ -353,15 +363,8 @@ class Phlex::SGML
|
|
|
353
363
|
false
|
|
354
364
|
end
|
|
355
365
|
|
|
356
|
-
def vanish(
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
if args.length > 0
|
|
360
|
-
@_state.capturing_into(Phlex::Vanish) { yield(*args) }
|
|
361
|
-
else
|
|
362
|
-
@_state.capturing_into(Phlex::Vanish) { yield(self) }
|
|
363
|
-
end
|
|
364
|
-
|
|
366
|
+
def vanish(...)
|
|
367
|
+
capture(...)
|
|
365
368
|
nil
|
|
366
369
|
end
|
|
367
370
|
|
|
@@ -526,7 +529,9 @@ class Phlex::SGML
|
|
|
526
529
|
if value != true && REF_ATTRIBUTES.include?(normalized_name)
|
|
527
530
|
case value
|
|
528
531
|
when String
|
|
529
|
-
|
|
532
|
+
decoded_value = decode_html_character_references(value)
|
|
533
|
+
|
|
534
|
+
if decoded_value.downcase.delete("^a-z:").start_with?("javascript:")
|
|
530
535
|
# We just ignore these because they were likely not specified by the developer.
|
|
531
536
|
next
|
|
532
537
|
end
|
|
@@ -544,7 +549,7 @@ class Phlex::SGML
|
|
|
544
549
|
end
|
|
545
550
|
end
|
|
546
551
|
|
|
547
|
-
if name.match?(
|
|
552
|
+
if name.match?(UNSAFE_ATTRIBUTE_NAME_CHARS)
|
|
548
553
|
raise Phlex::ArgumentError.new("Unsafe attribute name detected: #{k}.")
|
|
549
554
|
end
|
|
550
555
|
|
|
@@ -575,7 +580,7 @@ class Phlex::SGML
|
|
|
575
580
|
else raise Phlex::ArgumentError.new("Attribute keys should be Strings or Symbols")
|
|
576
581
|
end
|
|
577
582
|
|
|
578
|
-
if name.match?(
|
|
583
|
+
if name.match?(UNSAFE_ATTRIBUTE_NAME_CHARS)
|
|
579
584
|
raise Phlex::ArgumentError.new("Unsafe attribute name detected: #{k}.")
|
|
580
585
|
end
|
|
581
586
|
|
|
@@ -651,6 +656,27 @@ class Phlex::SGML
|
|
|
651
656
|
buffer.gsub('"', """)
|
|
652
657
|
end
|
|
653
658
|
|
|
659
|
+
private def decode_html_character_references(value)
|
|
660
|
+
value
|
|
661
|
+
.gsub(/&#x([0-9a-f]+);?/i) {
|
|
662
|
+
begin
|
|
663
|
+
[$1.to_i(16)].pack("U*")
|
|
664
|
+
rescue
|
|
665
|
+
""
|
|
666
|
+
end
|
|
667
|
+
}
|
|
668
|
+
.gsub(/&#(\d+);?/) {
|
|
669
|
+
begin
|
|
670
|
+
[$1.to_i].pack("U*")
|
|
671
|
+
rescue
|
|
672
|
+
""
|
|
673
|
+
end
|
|
674
|
+
}
|
|
675
|
+
.gsub(/&([a-z][a-z0-9]+);?/i) {
|
|
676
|
+
NAMED_CHARACTER_REFERENCES[$1.downcase] || ""
|
|
677
|
+
}
|
|
678
|
+
end
|
|
679
|
+
|
|
654
680
|
# Result is **unsafe**, so it should be escaped!
|
|
655
681
|
def __styles__(styles)
|
|
656
682
|
case styles
|
data/lib/phlex/svg.rb
CHANGED
|
@@ -43,7 +43,7 @@ class Phlex::SVG < Phlex::SGML
|
|
|
43
43
|
raise Phlex::ArgumentError.new("Expected the tag name to be a Symbol.")
|
|
44
44
|
end
|
|
45
45
|
|
|
46
|
-
if (tag = StandardElements.__registered_elements__[name]) || (tag = name.name.tr("_", "-")).include?("-")
|
|
46
|
+
if (tag = StandardElements.__registered_elements__[name]) || ((tag = name.name.tr("_", "-")).include?("-") && tag.match?(/\A[a-z0-9-]+\z/))
|
|
47
47
|
if attributes.length > 0 # with attributes
|
|
48
48
|
if block_given # with content block
|
|
49
49
|
buffer << "<#{tag}" << (Phlex::ATTRIBUTE_CACHE[attributes] ||= __attributes__(attributes)) << ">"
|
data/lib/phlex/version.rb
CHANGED
data/lib/phlex.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: phlex
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.1.
|
|
4
|
+
version: 2.1.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Joel Drapper
|
|
8
8
|
- Will Cosgrove
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2026-02-06 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description: Build HTML, SVG and CSV views with Ruby classes.
|
|
14
14
|
email:
|
|
@@ -40,7 +40,6 @@ files:
|
|
|
40
40
|
- lib/phlex/sgml/state.rb
|
|
41
41
|
- lib/phlex/svg.rb
|
|
42
42
|
- lib/phlex/svg/standard_elements.rb
|
|
43
|
-
- lib/phlex/vanish.rb
|
|
44
43
|
- lib/phlex/version.rb
|
|
45
44
|
homepage: https://www.phlex.fun
|
|
46
45
|
licenses:
|
|
@@ -65,7 +64,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
65
64
|
- !ruby/object:Gem::Version
|
|
66
65
|
version: '0'
|
|
67
66
|
requirements: []
|
|
68
|
-
rubygems_version: 3.6.
|
|
67
|
+
rubygems_version: 3.6.6
|
|
69
68
|
specification_version: 4
|
|
70
69
|
summary: Object-oriented views in Ruby.
|
|
71
70
|
test_files: []
|
data/lib/phlex/vanish.rb
DELETED