moxml 0.5.26 → 0.5.27
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/moxml/adapter/leptris/serialize.rb +19 -7
- data/lib/moxml/version.rb +1 -1
- data/spec/moxml/adapter/leptris_spec.rb +30 -0
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b9d156e4f8dfd71fc334b5626690f325a78abf95496bf19b6141bf649beeee58
|
|
4
|
+
data.tar.gz: 5f3c24b3211e8381d14a34c7c23781a808e2f55b1d73d06af3be0fe7d3640a75
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1c5cd5d01ece3fcced5049148d2df776a1dcb9b1e95b0fe9a145caef05c0e2d5080b32214c7eb9f8f7bb277412827b413b3c59e38a49162241208944791a6ed6
|
|
7
|
+
data.tar.gz: 33faf5c9269c3c74a6def61ef3933a9bdc2caedf484e0144ca1c8e6465aeb1640bb47c3f44ce9c777ed5bbe8e9682efd840cb87d4eceedac764f4c496519fff3
|
|
@@ -85,26 +85,37 @@ module Moxml
|
|
|
85
85
|
# detection below is a no-op on correct builds.
|
|
86
86
|
RAW_AMP_RE = /&(?!#{Entity::NAME_PATTERN};|#\d+;|#x[0-9A-Fa-f]+;)/
|
|
87
87
|
|
|
88
|
+
# A raw "<" in text position — the engine intermittently
|
|
89
|
+
# loses the escape when parsing under allocation pressure
|
|
90
|
+
# (leptris-ruby#131), so a decoded < serializes bare and
|
|
91
|
+
# reparsing truncates at it. Valid markup NEVER puts "<"
|
|
92
|
+
# before a non-name, non-delimiter character, so this is
|
|
93
|
+
# deterministic in markup segments (CDATA is literal and
|
|
94
|
+
# excluded by the segment walker).
|
|
95
|
+
RAW_LT_RE = /<(?![A-Za-z_:\/?!])/
|
|
96
|
+
|
|
88
97
|
def normalize_serialization(xml, options)
|
|
89
98
|
# The libxml2-layout serializer (>= 1.9.42) keeps attribute
|
|
90
99
|
# apostrophes literal; older engines escaped them.
|
|
91
100
|
needs_apos = !LIBXML2_LAYOUT_PARITY && xml.include?("'")
|
|
92
101
|
needs_expand = options[:expand_empty] && xml.include?("/>")
|
|
93
|
-
# One scan:
|
|
94
|
-
# ampersand-free output — no include? pre-filter needed.
|
|
102
|
+
# One scan each: both regexes fail fast on clean output.
|
|
95
103
|
needs_amp = xml.match?(RAW_AMP_RE)
|
|
96
|
-
|
|
104
|
+
needs_lt = xml.match?(RAW_LT_RE)
|
|
105
|
+
return xml unless needs_apos || needs_expand || needs_amp || needs_lt
|
|
97
106
|
|
|
98
107
|
out = +""
|
|
99
108
|
pos = 0
|
|
100
109
|
while pos < xml.length
|
|
101
110
|
opener_at, terminator = next_literal_region(xml, pos)
|
|
102
111
|
if opener_at.nil?
|
|
103
|
-
out << normalize_markup(xml[pos..], needs_apos, needs_expand,
|
|
112
|
+
out << normalize_markup(xml[pos..], needs_apos, needs_expand,
|
|
113
|
+
needs_amp: needs_amp, needs_lt: needs_lt)
|
|
104
114
|
break
|
|
105
115
|
end
|
|
106
116
|
|
|
107
|
-
out << normalize_markup(xml[pos...opener_at], needs_apos, needs_expand,
|
|
117
|
+
out << normalize_markup(xml[pos...opener_at], needs_apos, needs_expand,
|
|
118
|
+
needs_amp: needs_amp, needs_lt: needs_lt)
|
|
108
119
|
search_from = opener_at + opener_at_offset(terminator)
|
|
109
120
|
close = xml.index(terminator, search_from)
|
|
110
121
|
close_end = close.nil? ? xml.length : close + terminator.length
|
|
@@ -136,7 +147,7 @@ module Moxml
|
|
|
136
147
|
terminator == "-->" ? 4 : 0
|
|
137
148
|
end
|
|
138
149
|
|
|
139
|
-
def normalize_markup(markup, needs_apos, needs_expand, needs_amp: false)
|
|
150
|
+
def normalize_markup(markup, needs_apos, needs_expand, needs_amp: false, needs_lt: false)
|
|
140
151
|
markup = markup.gsub("'", "'") if needs_apos
|
|
141
152
|
if needs_expand
|
|
142
153
|
markup = markup.gsub(EMPTY_ELEMENT_RE) do
|
|
@@ -144,8 +155,9 @@ module Moxml
|
|
|
144
155
|
end
|
|
145
156
|
end
|
|
146
157
|
# Segment-aware: this never sees CDATA/comment/PI content,
|
|
147
|
-
# where
|
|
158
|
+
# where bare & and < are literal data.
|
|
148
159
|
markup = markup.gsub(RAW_AMP_RE, "&") if needs_amp
|
|
160
|
+
markup = markup.gsub(RAW_LT_RE, "<") if needs_lt
|
|
149
161
|
markup
|
|
150
162
|
end
|
|
151
163
|
end
|
data/lib/moxml/version.rb
CHANGED
|
@@ -243,6 +243,36 @@ RSpec.describe Moxml::Adapter::Leptris do
|
|
|
243
243
|
expect(doc.root.children.to_a.select(&:text?)).to be_empty
|
|
244
244
|
end
|
|
245
245
|
|
|
246
|
+
it "repairs a raw < in text position (issue #167 / leptris-ruby#131)" do
|
|
247
|
+
# The engine intermittently drops the escape when parsing under
|
|
248
|
+
# allocation pressure: a decoded < serializes bare and the
|
|
249
|
+
# output fails to reparse. Valid markup never puts < before a
|
|
250
|
+
# non-name character, so the damage is deterministically
|
|
251
|
+
# repairable in markup segments.
|
|
252
|
+
repaired = described_class.normalize_serialization(
|
|
253
|
+
%(<?xml version="1.0"?><r><pre>A <\n B</pre></r>\n), {}
|
|
254
|
+
)
|
|
255
|
+
expect(repaired).to include("A <")
|
|
256
|
+
expect(repaired).not_to match("A <")
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
it "keeps a literal < inside CDATA untouched by the repair" do
|
|
260
|
+
doc = ctx.parse(%(<r><![CDATA[A < B]]></r>))
|
|
261
|
+
expect(doc.to_xml).to include("<![CDATA[A < B]]>")
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
it "survives parse-under-allocation-pressure without emitting raw < (issue #167)" do
|
|
265
|
+
xml = "<?xml version=\"1.0\"?><doc>#{%(<s><pre alt="A B">A <\n B</pre></s>) * 10}</doc>"
|
|
266
|
+
30.times do
|
|
267
|
+
100.times { |j| "pressure#{j}" * 20 }
|
|
268
|
+
out = ctx.parse(xml, noblanks: true).to_xml(
|
|
269
|
+
declaration: true, encoding: "UTF-8", indent: 2, expand_empty: false,
|
|
270
|
+
)
|
|
271
|
+
expect(out).not_to match("A <"), "raw unescaped < leaked into text"
|
|
272
|
+
expect(Nokogiri::XML(out, &:strict)).to be_a(Nokogiri::XML::Document)
|
|
273
|
+
end
|
|
274
|
+
end
|
|
275
|
+
|
|
246
276
|
it "refuses noblanks on readonly parses while the strip path is active" do
|
|
247
277
|
# The strip mutates the tree; readonly freezes it at parse. On
|
|
248
278
|
# bindings whose engine flag is libxml2-safe (probe), the flag
|