moxml 0.5.92 → 0.5.93
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/base.rb +6 -0
- data/lib/moxml/adapter/customized_libxml/node.rb +8 -3
- data/lib/moxml/adapter/leptris.rb +7 -1
- data/lib/moxml/adapter/libxml.rb +4 -1
- data/lib/moxml/c14n/data_model.rb +153 -35
- data/lib/moxml/c14n.rb +17 -0
- data/lib/moxml/version.rb +1 -1
- data/spec/moxml/c14n/subset_spec.rb +109 -0
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 92d24efac79874e02cbfa26e20fc8135e2a714020a727896cdf2a460fa7d0508
|
|
4
|
+
data.tar.gz: 91f18637388f6983c2c75c606e95e93f15919b3099fbdc0c8e59ebf9aca93d5e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 91cd7d93def6717a7eedf675e871ff7d05d3dfb5378f8ddbe8f839daa68d0d1dbe0e1bdf6bdb939704bb03f673950c9faa858265321e2a209fbcb2042224a3cd
|
|
7
|
+
data.tar.gz: c973d0559eb5fcc42f45dfb824758cdbec8758ec0dc378a4ce4034d918b3b50fcdada1120cd9f18b47c39a42055d4cd810225588dbcf598346c920a2a510e0c7
|
data/lib/moxml/adapter/base.rb
CHANGED
|
@@ -184,6 +184,12 @@ module Moxml
|
|
|
184
184
|
one == other
|
|
185
185
|
end
|
|
186
186
|
|
|
187
|
+
# Owner element native for an attribute native, where the
|
|
188
|
+
# engine exposes it (nil elsewhere).
|
|
189
|
+
def attribute_element(_attribute)
|
|
190
|
+
nil
|
|
191
|
+
end
|
|
192
|
+
|
|
187
193
|
# Backends without a native subtree digest answer nil —
|
|
188
194
|
# the wrapper contract Node#digest gates on (issue #173).
|
|
189
195
|
def digest(*)
|
|
@@ -26,12 +26,17 @@ module Moxml
|
|
|
26
26
|
@native = fresh
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
-
# Compare wrappers based on their native nodes
|
|
29
|
+
# Compare wrappers based on their native nodes. Either side
|
|
30
|
+
# may arrive double-wrapped (a Customized node stored as
|
|
31
|
+
# another wrapper's native); LibXML's eql? raises on the
|
|
32
|
+
# class mismatch, so unwrap both before comparing.
|
|
30
33
|
def ==(other)
|
|
31
34
|
return false unless other
|
|
32
35
|
|
|
33
|
-
|
|
34
|
-
|
|
36
|
+
mine = @native.is_a?(CustomizedLibxml::Node) ? @native.native : @native
|
|
37
|
+
theirs = other.is_a?(self.class) ? other.native : other
|
|
38
|
+
theirs = theirs.native while theirs.is_a?(CustomizedLibxml::Node)
|
|
39
|
+
mine == theirs
|
|
35
40
|
end
|
|
36
41
|
|
|
37
42
|
alias eql? ==
|
|
@@ -1638,7 +1638,13 @@ module Moxml
|
|
|
1638
1638
|
end
|
|
1639
1639
|
|
|
1640
1640
|
def attribute_element(attr)
|
|
1641
|
-
|
|
1641
|
+
# XPath results mint ResultAttr (parent-backed); document
|
|
1642
|
+
# attributes are binding Attr (element-backed).
|
|
1643
|
+
if attr.is_a?(::Leptris::XML::ResultAttr)
|
|
1644
|
+
attr.parent
|
|
1645
|
+
else
|
|
1646
|
+
attr.element
|
|
1647
|
+
end
|
|
1642
1648
|
end
|
|
1643
1649
|
|
|
1644
1650
|
def attribute_name(attr)
|
data/lib/moxml/adapter/libxml.rb
CHANGED
|
@@ -421,7 +421,10 @@ module Moxml
|
|
|
421
421
|
end
|
|
422
422
|
|
|
423
423
|
def attribute_element(attr)
|
|
424
|
-
|
|
424
|
+
# Customized nodes wrap raw LibXML nodes one level deep;
|
|
425
|
+
# callers of this face expect the raw native.
|
|
426
|
+
parent = attr&.parent
|
|
427
|
+
parent.is_a?(::Moxml::Adapter::CustomizedLibxml::Node) ? parent.native : parent
|
|
425
428
|
end
|
|
426
429
|
|
|
427
430
|
def attribute_namespace(attr)
|
|
@@ -19,13 +19,85 @@ module Moxml
|
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
def self.from_node(moxml_node)
|
|
22
|
+
@source_map = {}
|
|
22
23
|
return build_from_document(moxml_node) if moxml_node.is_a?(::Moxml::Document)
|
|
23
24
|
|
|
24
25
|
root = Nodes::RootNode.new
|
|
25
|
-
|
|
26
|
+
built = build_node(moxml_node, [0])
|
|
27
|
+
root.add_child(built) if built
|
|
26
28
|
root
|
|
27
29
|
end
|
|
28
30
|
|
|
31
|
+
# Position keys for subset selection: adapters mint fresh
|
|
32
|
+
# wrappers per traversal, so XPath-matched wrappers cannot be
|
|
33
|
+
# keyed by object identity. Both sides instead compute the same
|
|
34
|
+
# child-index path — the build walks dm children in order, the
|
|
35
|
+
# match side climbs parents counting buildable siblings (the
|
|
36
|
+
# same types build_node answers non-nil for).
|
|
37
|
+
class << self
|
|
38
|
+
attr_reader :source_map
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
BUILDABLE_TYPES = [::Moxml::Element, ::Moxml::Text, ::Moxml::Cdata,
|
|
42
|
+
::Moxml::Comment,
|
|
43
|
+
::Moxml::ProcessingInstruction].freeze
|
|
44
|
+
|
|
45
|
+
def self.buildable?(node)
|
|
46
|
+
BUILDABLE_TYPES.any? { |type| node.is_a?(type) }
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Path key of a matched wrapper relative to the source the data
|
|
50
|
+
# model was built from; nil when the wrapper is outside it.
|
|
51
|
+
# Attributes extend their owner's path with [:attribute, index].
|
|
52
|
+
# The adapter comes from the caller: the Attribute contract
|
|
53
|
+
# keeps its own adapter hop protected.
|
|
54
|
+
def self.path_key_for(wrapper, source, adapter)
|
|
55
|
+
case wrapper
|
|
56
|
+
when ::Moxml::Attribute
|
|
57
|
+
owner = wrapper.parent
|
|
58
|
+
unless owner.is_a?(::Moxml::Element)
|
|
59
|
+
# XPath results are re-wrapped from natives: attribute
|
|
60
|
+
# wrappers carry no minted parent. The adapter answers
|
|
61
|
+
# the owner element instead.
|
|
62
|
+
owner_native = adapter.attribute_element(wrapper.native)
|
|
63
|
+
owner = owner_native && ::Moxml::Node.wrap(owner_native, wrapper.context)
|
|
64
|
+
end
|
|
65
|
+
return nil unless owner.is_a?(::Moxml::Element)
|
|
66
|
+
|
|
67
|
+
owner_path = node_path(owner, source)
|
|
68
|
+
return nil unless owner_path
|
|
69
|
+
|
|
70
|
+
idx = owner.attributes.index { |a| a == wrapper }
|
|
71
|
+
return nil unless idx
|
|
72
|
+
|
|
73
|
+
owner_path + [:attribute, idx]
|
|
74
|
+
when *BUILDABLE_TYPES
|
|
75
|
+
node_path(wrapper, source)
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def self.node_path(node, source)
|
|
80
|
+
parts = []
|
|
81
|
+
cur = node
|
|
82
|
+
loop do
|
|
83
|
+
if !source.is_a?(::Moxml::Document) && cur.equal?(source)
|
|
84
|
+
parts.unshift(0)
|
|
85
|
+
return parts
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
parent = cur.parent
|
|
89
|
+
return nil unless parent
|
|
90
|
+
|
|
91
|
+
idx = parent.children.select { |c| buildable?(c) }.index { |c| c == cur }
|
|
92
|
+
return nil unless idx
|
|
93
|
+
|
|
94
|
+
parts.unshift(idx)
|
|
95
|
+
return parts if source.is_a?(::Moxml::Document) && parent.is_a?(::Moxml::Document)
|
|
96
|
+
|
|
97
|
+
cur = parent
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
29
101
|
# Build from a Moxml::Document. Matches canon's Nokogiri path:
|
|
30
102
|
# the root element is added first, then all other document-level
|
|
31
103
|
# children (PIs, comments) in document order.
|
|
@@ -41,6 +113,7 @@ module Moxml
|
|
|
41
113
|
# adapters mint fresh wrappers per children call, so wrapper
|
|
42
114
|
# identity cannot locate it.
|
|
43
115
|
seen_root = false
|
|
116
|
+
idx = 0
|
|
44
117
|
document.children.each do |child|
|
|
45
118
|
if child.is_a?(::Moxml::Element)
|
|
46
119
|
next if seen_root
|
|
@@ -48,40 +121,48 @@ module Moxml
|
|
|
48
121
|
seen_root = true
|
|
49
122
|
end
|
|
50
123
|
|
|
51
|
-
built = build_node(child)
|
|
52
|
-
|
|
124
|
+
built = build_node(child, [idx])
|
|
125
|
+
next unless built
|
|
126
|
+
|
|
127
|
+
root.add_child(built)
|
|
128
|
+
idx += 1
|
|
53
129
|
end
|
|
54
|
-
root.add_child(build_element_node(document.root)) unless seen_root
|
|
130
|
+
root.add_child(build_element_node(document.root, [idx])) unless seen_root
|
|
55
131
|
end
|
|
56
132
|
|
|
57
133
|
root
|
|
58
134
|
end
|
|
59
135
|
|
|
60
|
-
def self.build_node(moxml_node)
|
|
136
|
+
def self.build_node(moxml_node, path)
|
|
61
137
|
case moxml_node
|
|
62
|
-
when ::Moxml::Element then build_element_node(moxml_node)
|
|
138
|
+
when ::Moxml::Element then build_element_node(moxml_node, path)
|
|
63
139
|
# CDATA sections are character data to canonicalization: their
|
|
64
140
|
# content is emitted as escaped text (spec §3.1 "text nodes").
|
|
65
|
-
when ::Moxml::Text, ::Moxml::Cdata then build_text_node(moxml_node)
|
|
66
|
-
when ::Moxml::Comment then build_comment_node(moxml_node)
|
|
67
|
-
when ::Moxml::ProcessingInstruction then build_pi_node(moxml_node)
|
|
141
|
+
when ::Moxml::Text, ::Moxml::Cdata then build_text_node(moxml_node, path)
|
|
142
|
+
when ::Moxml::Comment then build_comment_node(moxml_node, path)
|
|
143
|
+
when ::Moxml::ProcessingInstruction then build_pi_node(moxml_node, path)
|
|
68
144
|
end
|
|
69
145
|
end
|
|
70
146
|
|
|
71
|
-
def self.build_element_node(moxml_element)
|
|
147
|
+
def self.build_element_node(moxml_element, path)
|
|
72
148
|
ns = moxml_element.namespace
|
|
73
149
|
element = Nodes::ElementNode.new(
|
|
74
150
|
name: moxml_element.name,
|
|
75
151
|
namespace_uri: ns&.uri,
|
|
76
152
|
prefix: ns&.prefix,
|
|
77
153
|
)
|
|
154
|
+
source_map[path] = element
|
|
78
155
|
|
|
79
156
|
build_namespace_nodes(moxml_element, element)
|
|
80
|
-
build_attribute_nodes(moxml_element, element)
|
|
157
|
+
build_attribute_nodes(moxml_element, element, path)
|
|
81
158
|
|
|
159
|
+
idx = 0
|
|
82
160
|
moxml_element.children.each do |child|
|
|
83
|
-
built = build_node(child)
|
|
84
|
-
|
|
161
|
+
built = build_node(child, path + [idx])
|
|
162
|
+
next unless built
|
|
163
|
+
|
|
164
|
+
element.add_child(built)
|
|
165
|
+
idx += 1
|
|
85
166
|
end
|
|
86
167
|
|
|
87
168
|
element
|
|
@@ -101,8 +182,8 @@ module Moxml
|
|
|
101
182
|
)
|
|
102
183
|
end
|
|
103
184
|
|
|
104
|
-
def self.build_attribute_nodes(moxml_element, element)
|
|
105
|
-
moxml_element.attributes.
|
|
185
|
+
def self.build_attribute_nodes(moxml_element, element, path)
|
|
186
|
+
moxml_element.attributes.each_with_index do |attr, idx|
|
|
106
187
|
ns = attr.namespace
|
|
107
188
|
# The xml prefix is reserved: any attribute bound to the XML
|
|
108
189
|
# namespace URI renders (and sorts) as prefix "xml" regardless
|
|
@@ -113,30 +194,36 @@ module Moxml
|
|
|
113
194
|
prefix = "xml"
|
|
114
195
|
uri = XML_URI
|
|
115
196
|
end
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
prefix: prefix,
|
|
122
|
-
),
|
|
197
|
+
node = Nodes::AttributeNode.new(
|
|
198
|
+
name: attr.name,
|
|
199
|
+
value: attr.value,
|
|
200
|
+
namespace_uri: uri,
|
|
201
|
+
prefix: prefix,
|
|
123
202
|
)
|
|
203
|
+
source_map[path + [:attribute, idx]] = node
|
|
204
|
+
element.add_attribute(node)
|
|
124
205
|
end
|
|
125
206
|
end
|
|
126
207
|
|
|
127
|
-
def self.build_text_node(moxml_text)
|
|
128
|
-
Nodes::TextNode.new(value: moxml_text.content)
|
|
208
|
+
def self.build_text_node(moxml_text, path)
|
|
209
|
+
node = Nodes::TextNode.new(value: moxml_text.content)
|
|
210
|
+
source_map[path] = node
|
|
211
|
+
node
|
|
129
212
|
end
|
|
130
213
|
|
|
131
|
-
def self.build_comment_node(moxml_comment)
|
|
132
|
-
Nodes::CommentNode.new(value: moxml_comment.content)
|
|
214
|
+
def self.build_comment_node(moxml_comment, path)
|
|
215
|
+
node = Nodes::CommentNode.new(value: moxml_comment.content)
|
|
216
|
+
source_map[path] = node
|
|
217
|
+
node
|
|
133
218
|
end
|
|
134
219
|
|
|
135
|
-
def self.build_pi_node(moxml_pi)
|
|
136
|
-
Nodes::ProcessingInstructionNode.new(
|
|
220
|
+
def self.build_pi_node(moxml_pi, path)
|
|
221
|
+
node = Nodes::ProcessingInstructionNode.new(
|
|
137
222
|
target: moxml_pi.target || moxml_pi.name,
|
|
138
223
|
data: moxml_pi.content || "",
|
|
139
224
|
)
|
|
225
|
+
source_map[path] = node
|
|
226
|
+
node
|
|
140
227
|
end
|
|
141
228
|
|
|
142
229
|
def self.mark_all(node, value)
|
|
@@ -144,21 +231,52 @@ module Moxml
|
|
|
144
231
|
node.children.each { |child| mark_all(child, value) }
|
|
145
232
|
end
|
|
146
233
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
234
|
+
# Mark an XPath-selected subset: reset the whole model, then
|
|
235
|
+
# flag each matched node by position path. Node-set semantics
|
|
236
|
+
# follow the enveloped-signature interop (libxml2/xmlsec): an
|
|
237
|
+
# included element renders with its namespaces, attributes and
|
|
238
|
+
# DIRECT character data, but child ELEMENTS render only if
|
|
239
|
+
# matched too — an unmatched Signature subtree stays out even
|
|
240
|
+
# though its ancestors are matched.
|
|
241
|
+
def self.mark_subset_paths(root_node, paths)
|
|
242
|
+
mark_all(root_node, false)
|
|
243
|
+
paths.each do |path|
|
|
244
|
+
node = lookup_path(root_node, path)
|
|
245
|
+
next unless node
|
|
246
|
+
|
|
247
|
+
if node.is_a?(Nodes::ElementNode)
|
|
248
|
+
node.in_node_set = true
|
|
249
|
+
node.namespace_nodes.each { |ns| ns.in_node_set = true }
|
|
250
|
+
node.attribute_nodes.each { |attr| attr.in_node_set = true }
|
|
251
|
+
node.children.each do |child|
|
|
252
|
+
child.in_node_set = true unless child.is_a?(Nodes::ElementNode)
|
|
253
|
+
end
|
|
254
|
+
else
|
|
255
|
+
node.in_node_set = true
|
|
256
|
+
end
|
|
257
|
+
end
|
|
150
258
|
end
|
|
151
259
|
|
|
152
|
-
def self.
|
|
153
|
-
|
|
154
|
-
|
|
260
|
+
def self.lookup_path(root_node, path)
|
|
261
|
+
cur = root_node
|
|
262
|
+
i = 0
|
|
263
|
+
while i < path.length && path[i].is_a?(Integer)
|
|
264
|
+
cur = cur.children[path[i]]
|
|
265
|
+
return nil unless cur
|
|
266
|
+
|
|
267
|
+
i += 1
|
|
268
|
+
end
|
|
269
|
+
return cur if i >= path.length
|
|
270
|
+
return nil unless path[i] == :attribute
|
|
271
|
+
|
|
272
|
+
cur.attribute_nodes[path[i + 1]]
|
|
155
273
|
end
|
|
156
274
|
|
|
157
275
|
private_class_method :build_from_document, :build_node,
|
|
158
276
|
:build_element_node, :build_namespace_nodes,
|
|
159
277
|
:build_attribute_nodes, :build_text_node,
|
|
160
278
|
:build_comment_node, :build_pi_node,
|
|
161
|
-
:
|
|
279
|
+
:mark_all, :lookup_path
|
|
162
280
|
end
|
|
163
281
|
end
|
|
164
282
|
end
|
data/lib/moxml/c14n.rb
CHANGED
|
@@ -75,6 +75,23 @@ module Moxml
|
|
|
75
75
|
Inclusive10.new.canonicalize(node_or_xml, with_comments: with_comments)
|
|
76
76
|
end
|
|
77
77
|
|
|
78
|
+
# Canonicalize an XPath-selected subset of the document (spec §3).
|
|
79
|
+
# The node-set is whatever the expression matches (elements,
|
|
80
|
+
# attributes, text): matched elements render with their
|
|
81
|
+
# namespaces, attributes and descendants; matched attributes
|
|
82
|
+
# render on their owner element alone. Enveloped-signature flows
|
|
83
|
+
# use this to digest the document minus the Signature element
|
|
84
|
+
# without deep-copying.
|
|
85
|
+
def self.canonicalize_subset(node_or_xml, xpath, with_comments: false)
|
|
86
|
+
source = node_or_xml.is_a?(String) ? ::Moxml.parse(node_or_xml) : node_or_xml
|
|
87
|
+
root = DataModel.from_node(source)
|
|
88
|
+
adapter = source.context.config.adapter
|
|
89
|
+
matched = source.xpath(xpath).to_a
|
|
90
|
+
paths = matched.filter_map { |wrapper| DataModel.path_key_for(wrapper, source, adapter) }
|
|
91
|
+
DataModel.mark_subset_paths(root, paths)
|
|
92
|
+
Processor.new(with_comments: with_comments).process(root)
|
|
93
|
+
end
|
|
94
|
+
|
|
78
95
|
def self.canonicalize_inclusive11(node_or_xml, with_comments: false)
|
|
79
96
|
Inclusive11.new.canonicalize(node_or_xml, with_comments: with_comments)
|
|
80
97
|
end
|
data/lib/moxml/version.rb
CHANGED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
require "moxml/c14n"
|
|
5
|
+
|
|
6
|
+
SIG = "<Signature xmlns=\"http://www.w3.org/2000/09/xmldsig#\"><SignedInfo>" \
|
|
7
|
+
"<m xmlns=\"urn:m\">x</m></SignedInfo><Object>o</Object></Signature>"
|
|
8
|
+
ENVELOPED_DOC = %(<env><payload id="p1">data & more</payload>#{SIG}<tail/></env>).freeze
|
|
9
|
+
ENVELOPED_EXCLUDE = '//*[not(ancestor-or-self::*[local-name()="Signature"])]'
|
|
10
|
+
SIGNATURE_XPATH = '//*[local-name()="Signature"]'
|
|
11
|
+
|
|
12
|
+
CORPUS = [
|
|
13
|
+
["apex attraction + text",
|
|
14
|
+
%(<a xml:lang="en"><b id="1"><c>t</c></b><d/></a>), "//b",
|
|
15
|
+
%(<b id="1" xml:lang="en"></b>)],
|
|
16
|
+
["explicit descendant inclusion",
|
|
17
|
+
%(<a xml:lang="en"><b id="1"><c>t</c></b><d/></a>), "//b | //c",
|
|
18
|
+
%(<b id="1" xml:lang="en"><c>t</c></b>)],
|
|
19
|
+
["direct character data of a matched element",
|
|
20
|
+
%(<a><b>t</b><d/></a>), "//b", "<b>t</b>"],
|
|
21
|
+
["unmatched child element excluded",
|
|
22
|
+
%(<a><b><c>t</c></b></a>), "//b", "<b></b>"],
|
|
23
|
+
["excluded sibling subtree",
|
|
24
|
+
%(<a><b><c/></b><d/></a>), "//*[not(self::d)]",
|
|
25
|
+
%(<a><b><c></c></b></a>)],
|
|
26
|
+
["attribute-only selection renders nothing",
|
|
27
|
+
%(<a><b id="1"/></a>), "//@id", ""],
|
|
28
|
+
# C14N renders a text-only node-set without its omitted parents.
|
|
29
|
+
["text selection", %(<a><b>t1</b><b>t2</b></a>), "//b/text()", "t1t2"],
|
|
30
|
+
["empty result", %(<a><b/></a>), "//missing", ""],
|
|
31
|
+
].freeze
|
|
32
|
+
|
|
33
|
+
COMMENTS_CASE = [
|
|
34
|
+
"with_comments keeps matched comments only",
|
|
35
|
+
%(<a><!-- keep --><b><!-- in --><c/></b></a>), "//b",
|
|
36
|
+
%(<b><!-- in --></b>)
|
|
37
|
+
].freeze
|
|
38
|
+
|
|
39
|
+
COMMENTS_PADDING_LOSS = %i[ox headed_ox].freeze
|
|
40
|
+
|
|
41
|
+
RSpec.describe "Moxml::C14n subset canonicalization" do
|
|
42
|
+
# Node-set semantics follow the enveloped-signature interop
|
|
43
|
+
# (libxml2/xmlsec): a matched element renders with its namespaces,
|
|
44
|
+
# attributes and non-element children; child ELEMENTS need their
|
|
45
|
+
# own match, so an excluded subtree stays excluded under matched
|
|
46
|
+
# ancestors. Inheritable xml:* attributes of omitted ancestors and
|
|
47
|
+
# apex namespace attraction still render (spec §2.3/§2.4/§3).
|
|
48
|
+
describe "byte-identical subset canonical form across adapters" do
|
|
49
|
+
Moxml::Adapter::AVAILABLE_ADAPTERS.each do |adapter_name|
|
|
50
|
+
context "with the #{adapter_name} adapter" do
|
|
51
|
+
let(:ctx) { Moxml.new(adapter_name) }
|
|
52
|
+
|
|
53
|
+
CORPUS.each do |label, xml, xpath, expected|
|
|
54
|
+
next if label == "text selection" && adapter_name == :rexml
|
|
55
|
+
|
|
56
|
+
# rexml's adapter xpath does not return text nodes for
|
|
57
|
+
# text() node tests (pre-existing capability gap).
|
|
58
|
+
|
|
59
|
+
it "canonicalizes #{label}" do
|
|
60
|
+
doc = ctx.parse(xml)
|
|
61
|
+
expect(Moxml::C14n.canonicalize_subset(doc, xpath)).to eq(expected)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# ox/headed_ox strip comment content padding at the parse
|
|
66
|
+
# layer (pre-existing, same family as their PI content loss).
|
|
67
|
+
unless COMMENTS_PADDING_LOSS.include?(adapter_name)
|
|
68
|
+
it COMMENTS_CASE[0] do
|
|
69
|
+
doc = ctx.parse(COMMENTS_CASE[1])
|
|
70
|
+
expect(Moxml::C14n.canonicalize_subset(doc, COMMENTS_CASE[2],
|
|
71
|
+
with_comments: true))
|
|
72
|
+
.to eq(COMMENTS_CASE[3])
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
describe "enveloped-signature oracle" do
|
|
80
|
+
it "equals canonicalizing the DOM with the Signature removed" do
|
|
81
|
+
Moxml::Adapter::AVAILABLE_ADAPTERS.each do |adapter_name|
|
|
82
|
+
subset = Moxml::C14n.canonicalize_subset(
|
|
83
|
+
Moxml.new(adapter_name).parse(ENVELOPED_DOC), ENVELOPED_EXCLUDE
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
doc = Moxml.new(adapter_name).parse(ENVELOPED_DOC)
|
|
87
|
+
doc.root.xpath(SIGNATURE_XPATH).to_a[0].remove
|
|
88
|
+
removed = Moxml::C14n.canonicalize(doc.root)
|
|
89
|
+
|
|
90
|
+
expect(subset).to eq(removed)
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
describe "input forms" do
|
|
96
|
+
let(:ctx) { Moxml.new(:leptris) }
|
|
97
|
+
|
|
98
|
+
it "accepts an XML string" do
|
|
99
|
+
expect(Moxml::C14n.canonicalize_subset(%(<a><b>t</b></a>), "//b"))
|
|
100
|
+
.to eq("<b>t</b>")
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
it "accepts an element source, paths relative to it" do
|
|
104
|
+
doc = ctx.parse(%(<a><b><c>t</c></b></a>))
|
|
105
|
+
b = doc.root.xpath("//b").to_a[0]
|
|
106
|
+
expect(Moxml::C14n.canonicalize_subset(b, ".//c")).to eq("<c>t</c>")
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
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.
|
|
4
|
+
version: 0.5.93
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose Inc.
|
|
@@ -463,6 +463,7 @@ files:
|
|
|
463
463
|
- spec/moxml/c14n/comments_pis_spec.rb
|
|
464
464
|
- spec/moxml/c14n/inclusive10_spec.rb
|
|
465
465
|
- spec/moxml/c14n/namespace_edge_cases_spec.rb
|
|
466
|
+
- spec/moxml/c14n/subset_spec.rb
|
|
466
467
|
- spec/moxml/c14n/xml_attributes_spec.rb
|
|
467
468
|
- spec/moxml/cdata_spec.rb
|
|
468
469
|
- spec/moxml/comment_spec.rb
|