Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 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.Datad というローカル変数に格納しています。そして、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 を処理する部分にあります。

  1. d := p.tok.Data: まず、現在のトークン(p.tok)のテキストデータ (Data フィールド)を d という新しいローカル変数にコピーします。これにより、元のトークンデータを直接変更することなく処理を進めることができます。

  2. pre, listing, textarea 要素内の改行処理の統合:

    • 以前は、これらの要素の先頭にある改行を無視するロジックが、p.tok.Data を直接操作していました。
    • 変更後は、このロジックも d 変数に対して適用されるようになりました。これにより、コードの重複が避けられ、ヌルバイト除去と改行処理が同じ変数 d に対して順次適用される、より論理的なフローが実現されています。
  3. d = strings.Replace(d, "\x00", "", -1):

    • これが最も重要な変更点です。Go言語の strings.Replace 関数を使用して、変数 d 内に含まれるすべてのヌルバイト (\x00) を空文字列 ("") に置換しています。
    • 第三引数の -1 は、文字列内のすべての出現箇所を置換することを意味します。
    • この行により、HTML5の仕様に従って、テキストデータからヌルバイトが完全に除去されます。
  4. if d == "" { return true }:

    • ヌルバイト除去の結果、d が空文字列になった場合、それ以上テキストを追加する必要がないため、関数を早期に終了します。これは、無意味なテキストノードの追加を防ぐための最適化です。
  5. p.addText(d):

    • 最後に、ヌルバイトが除去され、必要に応じて改行が処理された後のクリーンなテキストデータ d を、パーサーの addText メソッドに渡して、DOMツリーにテキストノードとして追加します。

この一連の変更により、exp/html パーサーは、HTML5の仕様に厳密に準拠し、テキストデータ内のヌルバイトを適切に無視するようになりました。これにより、パーサーの堅牢性と互換性が向上しています。

関連リンク

参考にした情報源リンク