ruby-saml 0.8.12 → 0.8.13

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of ruby-saml might be problematic. Click here for more details.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
- ---
2
- SHA512:
3
- metadata.gz: 8a2479b6725a5a9e7fdc76a4bec612e2f0c66cf53cbb79ff7c1dc0343d1cc56c09e9fd3b2d3490bdacbb09b16b473702d42fa42fdb6fedff6e7fa5a44fa421a2
4
- data.tar.gz: 43b1cfb12dc3fc14a2cbc139430e3ad15b975dca0d95d0d8c14363f362833de181a52ca10b8b607215c4b5aa23ffa62f2d8f3a3b2c9b5541e9387c635ca77150
5
- SHA256:
6
- metadata.gz: 694ade703ed05cc38aa2ca98cbfee57cc16223991ae6539422c136164cf29608
7
- data.tar.gz: ee07b69a9391b26c9af95d0cfdbaa57c8991fa187b869660e15c549fcbbe47e3
1
+ ---
2
+ SHA1:
3
+ metadata.gz: c3f3a436bf74c3342e13ed40b9d6d7c71e8b25f1
4
+ data.tar.gz: c39cb2b2fa7844d97cd83e2d6a34f7a5ab68151e
5
+ SHA512:
6
+ metadata.gz: 38e6e375700d52f5bd4300dc5a1e7b9b20e5283b00371418730b1857ffc9b98857e72066a9ea67b504953eddaefc8683a0d40a29156f614dc18f9aaea7e7e0e5
7
+ data.tar.gz: a93d2f2c35bed0a8c44db64e3672aa8e811883d37b0386618dd51d0d7a9f19ddd37c59381dfa2c94cc04a361f3ecce8cd9677dc9ab6f44dee4eb653fefedba91
@@ -1,7 +1,5 @@
1
1
  require "xml_security"
2
2
  require "time"
3
- require "base64"
4
- require "zlib"
5
3
 
6
4
  module OneLogin
7
5
  module RubySaml
@@ -30,7 +28,7 @@ module OneLogin
30
28
  self.settings = settings
31
29
 
32
30
  @options = options
33
- @response = decode_raw_response(response)
31
+ @response = OneLogin::RubySaml::Utils.decode_raw_saml(response)
34
32
  @document = XMLSecurity::SignedDocument.new(response)
35
33
  end
36
34
 
@@ -75,27 +73,6 @@ module OneLogin
75
73
 
76
74
  private
77
75
 
78
- def decode(encoded)
79
- Base64.decode64(encoded)
80
- end
81
-
82
- def inflate(deflated)
83
- zlib = Zlib::Inflate.new(-Zlib::MAX_WBITS)
84
- zlib.inflate(deflated)
85
- end
86
-
87
- def decode_raw_response(response)
88
- if response =~ /^</
89
- return response
90
- elsif (decoded = decode(response)) =~ /^</
91
- return decoded
92
- elsif (inflated = inflate(decoded)) =~ /^</
93
- return inflated
94
- end
95
-
96
- raise "Couldn't decode SAMLResponse"
97
- end
98
-
99
76
  def valid_saml?(soft = true)
100
77
  Dir.chdir(File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'schemas'))) do
101
78
  @schema = Nokogiri::XML::Schema(IO.read('saml20protocol_schema.xsd'))
@@ -1,6 +1,7 @@
1
1
  require "xml_security"
2
2
  require "time"
3
3
  require "nokogiri"
4
+ require "onelogin/ruby-saml/utils"
4
5
  require 'onelogin/ruby-saml/attributes'
5
6
 
6
7
  # Only supports SAML 2.0
@@ -22,7 +23,7 @@ module OneLogin
22
23
  def initialize(response, options = {})
23
24
  raise ArgumentError.new("Response cannot be nil") if response.nil?
24
25
  @options = options
25
- @response = (response =~ /^</) ? response : Base64.decode64(response)
26
+ @response = OneLogin::RubySaml::Utils.decode_raw_saml(response)
26
27
  @document = XMLSecurity::SignedDocument.new(@response)
27
28
  end
28
29
 
@@ -421,6 +422,7 @@ module OneLogin
421
422
 
422
423
  true
423
424
  end
425
+
424
426
  end
425
427
  end
426
428
  end
@@ -4,6 +4,9 @@ else
4
4
  require 'securerandom'
5
5
  end
6
6
 
7
+ require "base64"
8
+ require "zlib"
9
+
7
10
  module OneLogin
8
11
  module RubySaml
9
12
 
@@ -12,6 +15,8 @@ module OneLogin
12
15
  class Utils
13
16
  @@uuid_generator = UUID.new if RUBY_VERSION < '1.9'
14
17
 
18
+ BASE64_FORMAT = %r(\A([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?\Z)
19
+
15
20
  # Given a REXML::Element instance, return the concatenation of all child text nodes. Assumes
16
21
  # that there all children other than text nodes can be ignored (e.g. comments). If nil is
17
22
  # passed, nil will be returned.
@@ -114,6 +119,66 @@ module OneLogin
114
119
 
115
120
  error_msg
116
121
  end
122
+
123
+ # Base64 decode and try also to inflate a SAML Message
124
+ # @param saml [String] The deflated and encoded SAML Message
125
+ # @return [String] The plain SAML Message
126
+ #
127
+ def self.decode_raw_saml(saml)
128
+ return saml unless base64_encoded?(saml)
129
+
130
+ decoded = decode(saml)
131
+ begin
132
+ inflate(decoded)
133
+ rescue
134
+ decoded
135
+ end
136
+ end
137
+
138
+ # Base 64 decode method
139
+ # @param string [String] The string message
140
+ # @return [String] The decoded string
141
+ #
142
+ def self.decode(string)
143
+ Base64.decode64(string)
144
+ end
145
+
146
+ # Base 64 encode method
147
+ # @param string [String] The string
148
+ # @return [String] The encoded string
149
+ #
150
+ def self.encode(string)
151
+ if Base64.respond_to?('strict_encode64')
152
+ Base64.strict_encode64(string)
153
+ else
154
+ Base64.encode64(string).gsub(/\n/, "")
155
+ end
156
+ end
157
+
158
+ # Check if a string is base64 encoded
159
+ # @param string [String] string to check the encoding of
160
+ # @return [true, false] whether or not the string is base64 encoded
161
+ #
162
+ def self.base64_encoded?(string)
163
+ !!string.gsub(/[\r\n]|\\r|\\n|\s/, "").match(BASE64_FORMAT)
164
+ end
165
+
166
+ # Inflate method
167
+ # @param deflated [String] The string
168
+ # @return [String] The inflated string
169
+ #
170
+ def self.inflate(deflated)
171
+ Zlib::Inflate.new(-Zlib::MAX_WBITS).inflate(deflated)
172
+ end
173
+
174
+ # Deflate method
175
+ # @param inflated [String] The string
176
+ # @return [String] The deflated string
177
+ #
178
+ def self.deflate(inflated)
179
+ Zlib::Deflate.deflate(inflated, 9)[2..-5]
180
+ end
181
+
117
182
  end
118
183
  end
119
184
  end
@@ -1,5 +1,5 @@
1
1
  module OneLogin
2
2
  module RubySaml
3
- VERSION = '0.8.12'
3
+ VERSION = '0.8.13'
4
4
  end
5
5
  end
@@ -42,40 +42,41 @@ module XMLSecurity
42
42
  NOKOGIRI_OPTIONS = Nokogiri::XML::ParseOptions::STRICT |
43
43
  Nokogiri::XML::ParseOptions::NONET
44
44
 
45
- def canon_algorithm(element)
46
- algorithm = element
47
- if algorithm.is_a?(REXML::Element)
48
- algorithm = element.attribute('Algorithm').value
49
- end
50
-
51
- case algorithm
52
- when "http://www.w3.org/TR/2001/REC-xml-c14n-20010315",
53
- "http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments"
54
- Nokogiri::XML::XML_C14N_1_0
55
- when "http://www.w3.org/2006/12/xml-c14n11",
56
- "http://www.w3.org/2006/12/xml-c14n11#WithComments"
57
- Nokogiri::XML::XML_C14N_1_1
58
- else
59
- Nokogiri::XML::XML_C14N_EXCLUSIVE_1_0
60
- end
61
- end
62
-
63
- def algorithm(element)
64
- algorithm = element
65
- if algorithm.is_a?(REXML::Element)
66
- algorithm = element.attribute("Algorithm").value
67
- end
68
-
69
- algorithm = algorithm && algorithm =~ /(rsa-)?sha(.*?)$/i && $2.to_i
70
-
71
- case algorithm
72
- when 256 then OpenSSL::Digest::SHA256
73
- when 384 then OpenSSL::Digest::SHA384
74
- when 512 then OpenSSL::Digest::SHA512
75
- else
76
- OpenSSL::Digest::SHA1
77
- end
78
- end
45
+ def canon_algorithm(element)
46
+ algorithm = element
47
+ if algorithm.is_a?(REXML::Element)
48
+ algorithm = element.attribute('Algorithm').value
49
+ end
50
+
51
+ case algorithm
52
+ when "http://www.w3.org/TR/2001/REC-xml-c14n-20010315",
53
+ "http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments"
54
+ Nokogiri::XML::XML_C14N_1_0
55
+ when "http://www.w3.org/2006/12/xml-c14n11",
56
+ "http://www.w3.org/2006/12/xml-c14n11#WithComments"
57
+ Nokogiri::XML::XML_C14N_1_1
58
+ else
59
+ Nokogiri::XML::XML_C14N_EXCLUSIVE_1_0
60
+ end
61
+ end
62
+
63
+ def algorithm(element)
64
+ algorithm = element
65
+ if algorithm.is_a?(REXML::Element)
66
+ algorithm = element.attribute("Algorithm").value
67
+ end
68
+
69
+ algorithm = algorithm && algorithm =~ /(rsa-)?sha(.*?)$/i && $2.to_i
70
+
71
+ case algorithm
72
+ when 256 then OpenSSL::Digest::SHA256
73
+ when 384 then OpenSSL::Digest::SHA384
74
+ when 512 then OpenSSL::Digest::SHA512
75
+ else
76
+ OpenSSL::Digest::SHA1
77
+ end
78
+ end
79
+
79
80
  end
80
81
 
81
82
  class Document < BaseDocument
@@ -98,15 +99,30 @@ module XMLSecurity
98
99
  end
99
100
  end
100
101
 
102
+ #<Signature>
103
+ #<SignedInfo>
104
+ #<CanonicalizationMethod />
105
+ #<SignatureMethod />
106
+ #<Reference>
107
+ #<Transforms>
108
+ #<DigestMethod>
109
+ #<DigestValue>
110
+ #</Reference>
111
+ #<Reference /> etc.
112
+ #</SignedInfo>
113
+ #<SignatureValue />
114
+ #<KeyInfo />
115
+ #<Object />
116
+ #</Signature>
101
117
  def sign_document(private_key, certificate, signature_method = RSA_SHA1, digest_method = SHA1)
102
118
  noko = Nokogiri::XML(self.to_s) do |config|
103
- config.options = NOKOGIRI_OPTIONS
119
+ config.options = XMLSecurity::BaseDocument::NOKOGIRI_OPTIONS
104
120
  end
105
121
 
106
122
  signature_element = REXML::Element.new("ds:Signature").add_namespace('ds', DSIG)
107
123
  signed_info_element = signature_element.add_element("ds:SignedInfo")
108
124
  signed_info_element.add_element("ds:CanonicalizationMethod", {"Algorithm" => C14N})
109
- signed_info_element.add_element("ds:SignatureMethod", {"Algorithm"=>signature_method})
125
+ signed_info_element.add_element("ds:SignatureMethod", {"Algorithm" => signature_method})
110
126
 
111
127
  # Add Reference
112
128
  reference_element = signed_info_element.add_element("ds:Reference", {"URI" => "##{uuid}"})
@@ -124,7 +140,7 @@ module XMLSecurity
124
140
 
125
141
  # add SignatureValue
126
142
  noko_sig_element = Nokogiri::XML(signature_element.to_s) do |config|
127
- config.options = NOKOGIRI_OPTIONS
143
+ config.options = XMLSecurity::BaseDocument::NOKOGIRI_OPTIONS
128
144
  end
129
145
 
130
146
  noko_signed_info_element = noko_sig_element.at_xpath('//ds:Signature/ds:SignedInfo', 'ds' => DSIG)
@@ -172,87 +188,169 @@ module XMLSecurity
172
188
 
173
189
  attr_writer :signed_element_id
174
190
 
175
- def initialize(response)
176
- super(response)
177
- extract_signed_element_id
178
- end
179
-
180
191
  def signed_element_id
181
192
  @signed_element_id ||= extract_signed_element_id
182
193
  end
183
194
 
184
- def validate_document(idp_cert_fingerprint, soft = true)
195
+ def validate_document(idp_cert_fingerprint, soft = true, options = {})
185
196
  # get cert from response
186
- cert_element = REXML::XPath.first(self, "//ds:X509Certificate", { "ds"=>DSIG })
187
- raise OneLogin::RubySaml::ValidationError.new("Certificate element missing in response (ds:X509Certificate)") unless cert_element
188
- base64_cert = OneLogin::RubySaml::Utils.element_text(cert_element)
189
- cert_text = Base64.decode64(base64_cert)
190
- cert = OpenSSL::X509::Certificate.new(cert_text)
197
+ cert_element = REXML::XPath.first(
198
+ self,
199
+ "//ds:X509Certificate",
200
+ { "ds"=>DSIG }
201
+ )
191
202
 
192
- # check cert matches registered idp cert
193
- fingerprint = Digest::SHA1.hexdigest(cert.to_der)
203
+ if cert_element
204
+ base64_cert = OneLogin::RubySaml::Utils.element_text(cert_element)
205
+ cert_text = Base64.decode64(base64_cert)
206
+ begin
207
+ cert = OpenSSL::X509::Certificate.new(cert_text)
208
+ rescue OpenSSL::X509::CertificateError => _e
209
+ return soft ? false : (raise OneLogin::RubySaml::ValidationError.new("Certificate Error"))
210
+ end
194
211
 
195
- if fingerprint != idp_cert_fingerprint.gsub(/[^a-zA-Z0-9]/,"").downcase
196
- return soft ? false : (raise OneLogin::RubySaml::ValidationError.new("Fingerprint mismatch"))
197
- end
212
+ if options[:fingerprint_alg]
213
+ fingerprint_alg = XMLSecurity::BaseDocument.new.algorithm(options[:fingerprint_alg]).new
214
+ else
215
+ fingerprint_alg = OpenSSL::Digest::SHA1.new
216
+ end
217
+ fingerprint = fingerprint_alg.hexdigest(cert.to_der)
198
218
 
219
+ # check cert matches registered idp cert
220
+ if fingerprint != idp_cert_fingerprint.gsub(/[^a-zA-Z0-9]/,"").downcase
221
+ return soft ? false : (raise OneLogin::RubySaml::ValidationError.new("Fingerprint mismatch"))
222
+ end
223
+ else
224
+ if options[:cert]
225
+ base64_cert = Base64.encode64(options[:cert].to_pem)
226
+ else
227
+ return soft ? false : (raise OneLogin::RubySaml::ValidationError.new("Certificate element missing in response (ds:X509Certificate) and not cert provided at settings"))
228
+ end
229
+ end
199
230
  validate_signature(base64_cert, soft)
200
231
  end
201
232
 
202
- def validate_signature(base64_cert, soft = true)
203
- # validate references
233
+ def validate_document_with_cert(idp_cert, soft = true)
234
+ # get cert from response
235
+ cert_element = REXML::XPath.first(
236
+ self,
237
+ "//ds:X509Certificate",
238
+ { "ds"=>DSIG }
239
+ )
204
240
 
205
- # check for inclusive namespaces
206
- inclusive_namespaces = extract_inclusive_namespaces
241
+ if cert_element
242
+ base64_cert = OneLogin::RubySaml::Utils.element_text(cert_element)
243
+ cert_text = Base64.decode64(base64_cert)
244
+ begin
245
+ cert = OpenSSL::X509::Certificate.new(cert_text)
246
+ rescue OpenSSL::X509::CertificateError => _e
247
+ return soft ? false : (raise OneLogin::RubySaml::ValidationError.new("Certificate Error"))
248
+ end
249
+
250
+ # check saml response cert matches provided idp cert
251
+ if idp_cert.to_pem != cert.to_pem
252
+ return false
253
+ end
254
+ else
255
+ base64_cert = Base64.encode64(idp_cert.to_pem)
256
+ end
257
+ validate_signature(base64_cert, true)
258
+ end
259
+
260
+ def validate_signature(base64_cert, soft = true)
207
261
 
208
262
  document = Nokogiri::XML(self.to_s) do |config|
209
- config.options = NOKOGIRI_OPTIONS
263
+ config.options = XMLSecurity::BaseDocument::NOKOGIRI_OPTIONS
210
264
  end
211
265
 
212
- # create a working copy so we don't modify the original
266
+ # create a rexml document
213
267
  @working_copy ||= REXML::Document.new(self.to_s).root
214
268
 
215
- # store signature node
216
- @sig_element ||= begin
217
- element = REXML::XPath.first(@working_copy, "//ds:Signature", {"ds"=>DSIG})
269
+ # get signature node
270
+ sig_element = REXML::XPath.first(
271
+ @working_copy,
272
+ "//ds:Signature",
273
+ {"ds"=>DSIG}
274
+ )
275
+
276
+ if sig_element.nil?
277
+ return soft ? false : (raise OneLogin::RubySaml::ValidationError.new("No Signature Node"))
218
278
  end
219
279
 
220
- # verify signature
221
- signed_info_element = REXML::XPath.first(@sig_element, "//ds:SignedInfo", {"ds"=>DSIG})
280
+ # signature method
281
+ sig_alg_value = REXML::XPath.first(
282
+ sig_element,
283
+ "./ds:SignedInfo/ds:SignatureMethod",
284
+ {"ds"=>DSIG}
285
+ )
286
+ signature_algorithm = algorithm(sig_alg_value)
287
+
288
+ # get signature
289
+ base64_signature = REXML::XPath.first(
290
+ sig_element,
291
+ "./ds:SignatureValue",
292
+ {"ds" => DSIG}
293
+ )
294
+
295
+ if base64_signature.nil?
296
+ return soft ? false : (raise OneLogin::RubySaml::ValidationError.new("SignatureValue not found"))
297
+ end
298
+
299
+ signature = Base64.decode64(OneLogin::RubySaml::Utils.element_text(base64_signature))
300
+
301
+ # canonicalization method
302
+ canon_algorithm = canon_algorithm REXML::XPath.first(
303
+ sig_element,
304
+ './ds:SignedInfo/ds:CanonicalizationMethod',
305
+ 'ds' => DSIG
306
+ )
307
+
222
308
  noko_sig_element = document.at_xpath('//ds:Signature', 'ds' => DSIG)
223
309
  noko_signed_info_element = noko_sig_element.at_xpath('./ds:SignedInfo', 'ds' => DSIG)
224
- canon_algorithm = canon_algorithm REXML::XPath.first(@sig_element, '//ds:CanonicalizationMethod', 'ds' => DSIG)
310
+
225
311
  canon_string = noko_signed_info_element.canonicalize(canon_algorithm)
226
312
  noko_sig_element.remove
227
313
 
314
+ # get inclusive namespaces
315
+ inclusive_namespaces = extract_inclusive_namespaces
316
+
228
317
  # check digests
229
- REXML::XPath.each(@sig_element, "//ds:Reference", {"ds"=>DSIG}) do |ref|
230
- uri = ref.attributes.get_attribute("URI").value
318
+ ref = REXML::XPath.first(sig_element, "//ds:Reference", {"ds"=>DSIG})
231
319
 
232
- hashed_element = document.at_xpath("//*[@ID='#{uri[1..-1]}']")
233
- canon_algorithm = canon_algorithm REXML::XPath.first(ref, '//ds:CanonicalizationMethod', 'ds' => DSIG)
234
- canon_hashed_element = hashed_element.canonicalize(canon_algorithm, inclusive_namespaces)
320
+ hashed_element = document.at_xpath("//*[@ID=$id]", nil, { 'id' => extract_signed_element_id })
235
321
 
236
- digest_algorithm = algorithm(REXML::XPath.first(ref, "//ds:DigestMethod", 'ds' => DSIG))
322
+ canon_algorithm = canon_algorithm REXML::XPath.first(
323
+ ref,
324
+ '//ds:CanonicalizationMethod',
325
+ { "ds" => DSIG }
326
+ )
237
327
 
238
- hash = digest_algorithm.digest(canon_hashed_element)
239
- digest_value = Base64.decode64(OneLogin::RubySaml::Utils.element_text(REXML::XPath.first(ref, "//ds:DigestValue", {"ds"=>DSIG})))
328
+ canon_algorithm = process_transforms(ref, canon_algorithm)
240
329
 
241
- unless digests_match?(hash, digest_value)
242
- return soft ? false : (raise OneLogin::RubySaml::ValidationError.new("Digest mismatch"))
243
- end
244
- end
330
+ canon_hashed_element = hashed_element.canonicalize(canon_algorithm, inclusive_namespaces)
245
331
 
246
- base64_signature = OneLogin::RubySaml::Utils.element_text(REXML::XPath.first(@sig_element, "//ds:SignatureValue", {"ds"=>DSIG}))
247
- signature = Base64.decode64(base64_signature)
332
+ digest_algorithm = algorithm(REXML::XPath.first(
333
+ ref,
334
+ "//ds:DigestMethod",
335
+ { "ds" => DSIG }
336
+ ))
337
+ hash = digest_algorithm.digest(canon_hashed_element)
338
+ encoded_digest_value = REXML::XPath.first(
339
+ ref,
340
+ "//ds:DigestValue",
341
+ { "ds" => DSIG }
342
+ )
343
+ digest_value = Base64.decode64(OneLogin::RubySaml::Utils.element_text(encoded_digest_value))
248
344
 
249
- # get certificate object
250
- cert_text = Base64.decode64(base64_cert)
251
- cert = OpenSSL::X509::Certificate.new(cert_text)
345
+ unless digests_match?(hash, digest_value)
346
+ return soft ? false : (raise OneLogin::RubySaml::ValidationError.new("Digest mismatch"))
347
+ end
252
348
 
253
- # signature method
254
- signature_algorithm = algorithm(REXML::XPath.first(signed_info_element, "//ds:SignatureMethod", {"ds"=>DSIG}))
349
+ # get certificate object
350
+ cert_text = Base64.decode64(base64_cert)
351
+ cert = OpenSSL::X509::Certificate.new(cert_text)
255
352
 
353
+ # verify signature
256
354
  unless cert.public_key.verify(signature_algorithm.new, signature, canon_string)
257
355
  return soft ? false : (raise OneLogin::RubySaml::ValidationError.new("Key validation error"))
258
356
  end
@@ -262,6 +360,33 @@ module XMLSecurity
262
360
 
263
361
  private
264
362
 
363
+ def process_transforms(ref, canon_algorithm)
364
+ transforms = REXML::XPath.match(
365
+ ref,
366
+ "//ds:Transforms/ds:Transform",
367
+ { "ds" => DSIG }
368
+ )
369
+
370
+ transforms.each do |transform_element|
371
+ if transform_element.attributes && transform_element.attributes["Algorithm"]
372
+ algorithm = transform_element.attributes["Algorithm"]
373
+ case algorithm
374
+ when "http://www.w3.org/TR/2001/REC-xml-c14n-20010315",
375
+ "http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments"
376
+ canon_algorithm = Nokogiri::XML::XML_C14N_1_0
377
+ when "http://www.w3.org/2006/12/xml-c14n11",
378
+ "http://www.w3.org/2006/12/xml-c14n11#WithComments"
379
+ canon_algorithm = Nokogiri::XML::XML_C14N_1_1
380
+ when "http://www.w3.org/2001/10/xml-exc-c14n#",
381
+ "http://www.w3.org/2001/10/xml-exc-c14n#WithComments"
382
+ canon_algorithm = Nokogiri::XML::XML_C14N_EXCLUSIVE_1_0
383
+ end
384
+ end
385
+ end
386
+
387
+ canon_algorithm
388
+ end
389
+
265
390
  def digests_match?(hash, digest_value)
266
391
  hash == digest_value
267
392
  end
@@ -280,11 +405,16 @@ module XMLSecurity
280
405
  end
281
406
 
282
407
  def extract_inclusive_namespaces
283
- if element = REXML::XPath.first(self, "//ec:InclusiveNamespaces", { "ec" => C14N })
408
+ element = REXML::XPath.first(
409
+ self,
410
+ "//ec:InclusiveNamespaces",
411
+ { "ec" => C14N }
412
+ )
413
+ if element
284
414
  prefix_list = element.attributes.get_attribute("PrefixList").value
285
415
  prefix_list.split(" ")
286
416
  else
287
- []
417
+ nil
288
418
  end
289
419
  end
290
420