[インデックス 12920] ファイルの概要
このコミットは、Go言語の実験的なHTMLパーサーパッケージ exp/html
内の parse.go
ファイルに対する変更と、それに関連するテストログファイル testlogs/pending-spec-changes-plain-text-unsafe.dat.log
の更新を含んでいます。parse.go
はHTMLドキュメントを解析し、DOMツリーを構築する主要なロジックを担っています。testlogs/pending-spec-changes-plain-text-unsafe.dat.log
は、HTML仕様の変更や未解決の問題に関連するテストケースの結果を記録しているログファイルです。
コミット
exp/html: テキスト内のヌルバイトを無視する
追加のテストを1つパスする
R=golang-dev, nigeltao CC=golang-dev https://golang.org/cl/6048051
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/6791057296ce6ff545f7a435aaebb371cb4191b9
元コミット内容
exp/html: ignore null bytes in text
pass one additional test
R=golang-dev, nigeltao
CC=golang-dev
https://golang.org/cl/6048051
変更の背景
このコミットの背景には、HTML5のパースアルゴリズムにおけるヌルバイト(\x00
)の扱いの標準化があります。HTML5の仕様では、テキストデータ内にヌルバイトが出現した場合、それを無視(削除)するように規定されています。これは、ヌルバイトがC言語などのプログラミング言語において文字列の終端を示すために使われることが多く、HTMLパーサーがこれを適切に処理しないと、セキュリティ上の問題(例: クロスサイトスクリプティング)や、予期せぬパース結果を引き起こす可能性があるためです。
具体的には、ブラウザ間の互換性を確保し、HTML5の仕様に厳密に準拠するために、exp/html
パッケージがテキストノード内のヌルバイトを正しく処理する必要がありました。この変更により、ヌルバイトを含む不正なHTML入力に対しても、仕様通りの堅牢なパース動作が保証されます。コミットメッセージにある「pass one additional test」は、このヌルバイトの処理に関する特定のテストケースが、変更によってパスするようになったことを示しています。
前提知識の解説
ヌルバイト (\x00
)
ヌルバイト(Null Byte)は、ASCIIコードで0x00(10進数で0)に相当する文字です。プログラミングにおいては、C言語などで文字列の終端を示すマーカーとして広く使われます。しかし、HTMLやXMLのようなマークアップ言語の文脈では、通常、テキストデータ内にヌルバイトが出現することは想定されていません。ヌルバイトは表示できない制御文字であり、パーサーがこれをどのように扱うかによって、セキュリティ上の脆弱性(例: 文字列の切り詰めによる意図しない解釈)や、異なるブラウザ間でのレンダリングの不一致を引き起こす可能性があります。HTML5の仕様では、このような潜在的な問題を回避するため、テキストデータ内のヌルバイトを無視するよう明確に指示しています。
HTML5のパースアルゴリズム
HTML5のパースアルゴリズムは、非常に詳細かつ厳格に定義されています。これは、異なるブラウザが同じHTMLドキュメントを常に同じように解釈し、レンダリングすることを保証するためです。このアルゴリズムは、エラー処理についても非常に寛容であり、不正なHTMLに対しても可能な限り意味のあるDOMツリーを構築しようとします。ヌルバイトの無視も、このエラー処理と互換性確保の原則の一部です。パーサーは、入力ストリームから文字を読み込み、トークン化し、そのトークンに基づいてDOMツリーを構築する一連の複雑な状態機械として動作します。
Go言語の exp/html
パッケージ
exp/html
は、Go言語の標準ライブラリの一部として提供されている html
パッケージの実験的な前身、または関連するパッケージです。このパッケージは、HTML5の仕様に準拠したHTMLパーサーを提供することを目的としています。ウェブスクレイピング、HTMLのサニタイズ、HTMLドキュメントの操作など、様々な用途で利用されます。このパッケージは、HTMLドキュメントをトークンに分割し、それらのトークンからDOM(Document Object Model)ツリーを構築する機能を提供します。
技術的詳細
このコミットの技術的詳細は、src/pkg/exp/html/parse.go
ファイル内の inBodyIM
関数におけるテキストトークンの処理に焦点を当てています。
変更前は、TextToken
の処理において、p.tok.Data
(現在のトークンのテキストデータ)を直接 p.addText
メソッドに渡していました。しかし、この方法では、p.tok.Data
にヌルバイトが含まれている場合に、それがそのままDOMツツリーのテキストノードに挿入されてしまう可能性がありました。
変更後、TextToken
の処理の冒頭で、現在のトークンのテキストデータ p.tok.Data
を d
というローカル変数に格納しています。そして、strings.Replace(d, "\x00", "", -1)
という行が追加されました。この関数呼び出しは、文字列 d
内のすべてのヌルバイト (\x00
) を空文字列 (""
) に置換し、その結果を再び d
に代入します。-1
は、すべての出現箇所を置換することを意味します。
この変更により、p.addText(d)
が呼び出される前に、ヌルバイトがテキストデータから確実に除去されるようになりました。これにより、HTML5の仕様に準拠し、ヌルバイトがDOMツリーに挿入されるのを防ぎます。
また、pre
, listing
, textarea
要素内の改行文字の処理ロジックも、ヌルバイト除去のロジックと統合されるように変更されています。以前は、これらの要素の先頭にある改行を無視する処理が p.tok.Data
を直接操作していましたが、変更後はヌルバイト除去後の d
変数に対して処理が行われるようになり、コードの重複が解消され、よりクリーンな実装になっています。
testlogs/pending-spec-changes-plain-text-unsafe.dat.log
の変更は、このヌルバイト処理の修正が、特定のテストケース(<body><table>\x00filler\x00text\x00
のようなヌルバイトを含むHTML)を FAIL
から PASS
に変更したことを示しています。これは、修正が意図通りに機能し、仕様に準拠した動作を実現したことの直接的な証拠です。
コアとなるコードの変更箇所
--- a/src/pkg/exp/html/parse.go
+++ b/src/pkg/exp/html/parse.go
@@ -616,25 +616,25 @@ func copyAttributes(dst *Node, src Token) {
func inBodyIM(p *parser) bool {
switch p.tok.Type {
case TextToken:
+ d := p.tok.Data
switch n := p.oe.top(); n.Data {
case "pre", "listing", "textarea":
if len(n.Child) == 0 {
// Ignore a newline at the start of a <pre> block.
- d := p.tok.Data
if d != "" && d[0] == '\r' {
d = d[1:]
}
if d != "" && d[0] == '\n' {
d = d[1:]
}
- if d == "" {
- return true
- }
- p.tok.Data = d
}
}
+ d = strings.Replace(d, "\x00", "", -1)
+ if d == "" {
+ return true
+ }
p.reconstructActiveFormattingElements()
- p.addText(p.tok.Data)
+ p.addText(d)
p.framesetOK = false
case StartTagToken:
switch p.tok.Data {
コアとなるコードの解説
この変更の核心は、inBodyIM
関数内の TextToken
を処理する部分にあります。
-
d := p.tok.Data
: まず、現在のトークン(p.tok
)のテキストデータ (Data
フィールド)をd
という新しいローカル変数にコピーします。これにより、元のトークンデータを直接変更することなく処理を進めることができます。 -
pre
,listing
,textarea
要素内の改行処理の統合:- 以前は、これらの要素の先頭にある改行を無視するロジックが、
p.tok.Data
を直接操作していました。 - 変更後は、このロジックも
d
変数に対して適用されるようになりました。これにより、コードの重複が避けられ、ヌルバイト除去と改行処理が同じ変数d
に対して順次適用される、より論理的なフローが実現されています。
- 以前は、これらの要素の先頭にある改行を無視するロジックが、
-
d = strings.Replace(d, "\x00", "", -1)
:- これが最も重要な変更点です。Go言語の
strings.Replace
関数を使用して、変数d
内に含まれるすべてのヌルバイト (\x00
) を空文字列 (""
) に置換しています。 - 第三引数の
-1
は、文字列内のすべての出現箇所を置換することを意味します。 - この行により、HTML5の仕様に従って、テキストデータからヌルバイトが完全に除去されます。
- これが最も重要な変更点です。Go言語の
-
if d == "" { return true }
:- ヌルバイト除去の結果、
d
が空文字列になった場合、それ以上テキストを追加する必要がないため、関数を早期に終了します。これは、無意味なテキストノードの追加を防ぐための最適化です。
- ヌルバイト除去の結果、
-
p.addText(d)
:- 最後に、ヌルバイトが除去され、必要に応じて改行が処理された後のクリーンなテキストデータ
d
を、パーサーのaddText
メソッドに渡して、DOMツリーにテキストノードとして追加します。
- 最後に、ヌルバイトが除去され、必要に応じて改行が処理された後のクリーンなテキストデータ
この一連の変更により、exp/html
パーサーは、HTML5の仕様に厳密に準拠し、テキストデータ内のヌルバイトを適切に無視するようになりました。これにより、パーサーの堅牢性と互換性が向上しています。
関連リンク
- Go CL 6048051: https://golang.org/cl/6048051
参考にした情報源リンク
- HTML Standard - 13.2.5.1 Data state: https://html.spec.whatwg.org/multipage/parsing.html#data-state (HTML5仕様におけるヌルバイトの扱いに関する記述)
- Go strings.Replace documentation: https://pkg.go.dev/strings#Replace (Go言語の
strings.Replace
関数の公式ドキュメント) - HTML5 Parsing Algorithm: https://www.w3.org/TR/html5/syntax.html#parsing (HTML5のパースアルゴリズムに関するW3Cのドキュメント)I have provided the comprehensive technical explanation as requested, following all the specified instructions and chapter structure. I have used the commit information and performed a conceptual web search to enrich the content. I believe the task is complete.