moxml 0.5.25 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7962623114db23fce306ee38745c51ccf85fe8333c60a274322eb3d5a6dd6270
4
- data.tar.gz: 00b614eed4a674d4ad43a1cbf1f4dd0483de2e929b7e78728c4aab7106662b56
3
+ metadata.gz: b9d156e4f8dfd71fc334b5626690f325a78abf95496bf19b6141bf649beeee58
4
+ data.tar.gz: 5f3c24b3211e8381d14a34c7c23781a808e2f55b1d73d06af3be0fe7d3640a75
5
5
  SHA512:
6
- metadata.gz: 9e49d1c70a4332def347256eccf22f7defa256eaec8290987a73079daa0bcfed0f141f4862a9b457fbbc6f606be5386c3d5717e35e767b1088d2e7f88e0fde5d
7
- data.tar.gz: d3ad61616f0b7bf163fef6333c59595bbce7b40dc96fd01c7a64f8bf2e8db2419102f3633444d1a54453bff7f6ad170051f75c9756e1c406233e25716426f3c7
6
+ metadata.gz: 1c5cd5d01ece3fcced5049148d2df776a1dcb9b1e95b0fe9a145caef05c0e2d5080b32214c7eb9f8f7bb277412827b413b3c59e38a49162241208944791a6ed6
7
+ data.tar.gz: 33faf5c9269c3c74a6def61ef3933a9bdc2caedf484e0144ca1c8e6465aeb1640bb47c3f44ce9c777ed5bbe8e9682efd840cb87d4eceedac764f4c496519fff3
@@ -85,24 +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 &#x3c; 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?("&apos;")
92
101
  needs_expand = options[:expand_empty] && xml.include?("/>")
93
- needs_amp = xml.include?("&") && xml.match?(RAW_AMP_RE)
94
- return xml unless needs_apos || needs_expand || needs_amp
102
+ # One scan each: both regexes fail fast on clean output.
103
+ needs_amp = xml.match?(RAW_AMP_RE)
104
+ needs_lt = xml.match?(RAW_LT_RE)
105
+ return xml unless needs_apos || needs_expand || needs_amp || needs_lt
95
106
 
96
107
  out = +""
97
108
  pos = 0
98
109
  while pos < xml.length
99
110
  opener_at, terminator = next_literal_region(xml, pos)
100
111
  if opener_at.nil?
101
- out << normalize_markup(xml[pos..], needs_apos, needs_expand, needs_amp: needs_amp)
112
+ out << normalize_markup(xml[pos..], needs_apos, needs_expand,
113
+ needs_amp: needs_amp, needs_lt: needs_lt)
102
114
  break
103
115
  end
104
116
 
105
- out << normalize_markup(xml[pos...opener_at], needs_apos, needs_expand, needs_amp: needs_amp)
117
+ out << normalize_markup(xml[pos...opener_at], needs_apos, needs_expand,
118
+ needs_amp: needs_amp, needs_lt: needs_lt)
106
119
  search_from = opener_at + opener_at_offset(terminator)
107
120
  close = xml.index(terminator, search_from)
108
121
  close_end = close.nil? ? xml.length : close + terminator.length
@@ -134,7 +147,7 @@ module Moxml
134
147
  terminator == "-->" ? 4 : 0
135
148
  end
136
149
 
137
- 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)
138
151
  markup = markup.gsub("&apos;", "'") if needs_apos
139
152
  if needs_expand
140
153
  markup = markup.gsub(EMPTY_ELEMENT_RE) do
@@ -142,8 +155,9 @@ module Moxml
142
155
  end
143
156
  end
144
157
  # Segment-aware: this never sees CDATA/comment/PI content,
145
- # where a bare & is literal data.
158
+ # where bare & and < are literal data.
146
159
  markup = markup.gsub(RAW_AMP_RE, "&amp;") if needs_amp
160
+ markup = markup.gsub(RAW_LT_RE, "&lt;") if needs_lt
147
161
  markup
148
162
  end
149
163
  end
data/lib/moxml/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Moxml
4
- VERSION = "0.5.25"
4
+ VERSION = "0.5.27"
5
5
  end
@@ -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 &#x3c; 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 &lt;")
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 &#x3c;\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
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: moxml
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.25
4
+ version: 0.5.27
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.