[ruby-dev:50631] [Ruby trunk Bug#15057] REXML::Text#value returns a double unescaped string in non-raw mode

From: rnanba@...
Date: 2018-09-02 17:29:56 UTC
List: ruby-dev #50631
Issue #15057 has been updated by rna (Ryosuke Nanba).


> DOMでもnodeValueの値は実体参照を解決した値になるので今の挙動で問題ないように思うんですが、

JavaScript の DOM でも `Text` のコンストラクタ引数で渡した文字列はパースの対象にならずにそのままテキストノードが表す文字列になります。

~~~
<p id="p"></p>
<script>
t = new Text("&lt; <");
console.log(t.nodeValue); // コンソールに「&lt; <」が出力
p.appendChild(t);         // 画面に「&lt; <」と表示。
</script>
~~~

実体参照が解決されるのは XML 文書実体(ソースコード)からパースする段階の話で、DOM API としては通常パース済みの文字列しか扱いません。

DOM はともかくとして、REXMLの仕様として(非rawモードで) `Text` のコンストラクタ引数はパースされることになっているのだ、ということであれば、以下の挙動はバグということになってしまいます。
~~~
t = REXML::Text.new("&lt; <", false, nil, false)
t.to_s   # => "&amp;lt; &lt;" (expected?: "&lt; &lt;" (or Error?))
~~~

> どんなユースケースで使おうとして今の挙動は間違っていると考えたか教えてもらえますか?

atomutil 0.1.4 でテキストノードを要素から要素にコピーするユースケースでエスケープが解除されてしまってコピーにならないという現象が発生していました。

https://github.com/lyokato/ruby-atomutil/blob/954c5d1b387bff63db3462d19d3f7abf196d2b1f/lib/atomutil.rb#L526 (`element` と `value.elem` が `REXML::Element` です):
~~~
        element.text = value.elem.text unless value.elem.text.nil?
~~~


----------------------------------------
Bug #15057: REXML::Text#value returns a double unescaped string in non-raw mode
https://bugs.ruby-lang.org/issues/15057#change-73853

* Author: rna (Ryosuke Nanba)
* Status: Feedback
* Priority: Normal
* Assignee: kou (Kouhei Sutou)
* Target version: 
* ruby -v: ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-linux-gnu]
* Backport: 2.3: UNKNOWN, 2.4: UNKNOWN, 2.5: UNKNOWN
----------------------------------------
`REXML::Text` オブジェクトが非rawモードの場合、`REXML::Text#value` がエスケープ済みのテキストを二重にエスケープ解除された文字列を返します。

例:
~~~
require 'rexml/document'
t = REXML::Text.new("&lt; <", false, nil, false)
t.to_s   # => "&amp;lt; &lt;"
t.value  # => "< <" (expected: "&lt; <")
~~~

`REXML::Text#value` のコメントに以下のような記述があるため、上の挙動は期待通りのように見えますが、このコメントそのものが誤りだと思います。

~~~
    #   t = Text.new( "< & &s; russell", false, nil, false )
    #   t.value   #-> "< & sean russell"
~~~
非rawモードではコンストラクタの第一引数に渡された文字列はテキストノードが表す文字列そのものを意味するはずです。上で渡された文字列中の "&s;" は実体参照ではなく単なる3文字のテキストを意味します。`t.value` は "< & &s; russell" であるべきだと思います。




-- 
https://bugs.ruby-lang.org/

In This Thread

Prev Next