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

[インデックス 18053] ファイルの概要

このコミットは、Go言語の標準ライブラリhtmlパッケージ内のUnescapeString関数に対して、エッジケースをカバーする新しいテストケースを追加するものです。具体的には、単一のアンパサンド、アンパサンドの後にエンティティではない文字列が続く場合、および「&#」という文字列が続く場合の挙動を検証するためのテストが追加されています。これにより、UnescapeString関数の堅牢性と正確性が向上します。

コミット

commit a025e1caac516c967486f4644fbe4c647100b632
Author: Shawn Smith <shawn.p.smith@gmail.com>
Date:   Wed Dec 18 10:20:25 2013 -0800

    html: add tests for UnescapeString edge cases
    
    R=golang-dev, gobot, bradfitz
    CC=golang-dev
    https://golang.org/cl/40810044

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/a025e1caac516c967486f4644fbe4c647100b632

元コミット内容

このコミットの目的は、Go言語のhtmlパッケージにあるUnescapeString関数に、特定の「エッジケース」に対するテストを追加することです。これにより、この関数が予期せぬ入力に対しても正しく動作することを保証し、より堅牢なHTMLアンエスケープ処理を提供することを目指しています。

変更の背景

UnescapeString関数は、HTMLエンティティ(例: &lt;, &amp;)や数値文字参照(例: &#x27;, &#87;)を含む文字列を、対応する実際の文字に変換する役割を担っています。しかし、このような変換処理では、入力文字列が完全なエンティティや数値文字参照の形式ではない場合に、予期せぬ挙動を示す可能性があります。

このコミットが追加された背景には、以下のようなエッジケースに対するUnescapeString関数の挙動が十分にテストされていなかった、あるいは明確でなかったという問題意識があったと考えられます。

  1. 単一のアンパサンド (&): HTMLエンティティの開始文字であるアンパサンドが単独で出現した場合、それがエンティティの一部ではないことを正しく認識し、そのままの文字として扱う必要があります。
  2. アンパサンドの後にエンティティではない文字列が続く場合: 例えば &test のように、アンパサンドの後に有効なHTMLエンティティ名や数値文字参照の形式ではない文字列が続く場合、その全体をそのままの文字列として扱う必要があります。
  3. &# という文字列: 数値文字参照の開始を示す &# が出現した場合、その後に有効な数値が続かない限り、これもそのままの文字列として扱う必要があります。

これらのエッジケースに対するテストを追加することで、UnescapeString関数がこれらの曖昧な入力に対しても期待通りの(つまり、変更せずにそのまま返す)挙動をすることを保証し、ライブラリの信頼性を高めることが目的です。

前提知識の解説

HTMLエスケープとアンエスケープ

Web開発において、HTMLドキュメント内に特殊文字(例: <, >, &, ", ')を直接記述すると、ブラウザがそれらをHTMLタグやエンティティの開始と誤解釈し、意図しない表示やセキュリティ上の問題(XSSなど)を引き起こす可能性があります。これを避けるために、これらの特殊文字を「HTMLエンティティ」と呼ばれる形式に変換する処理を「HTMLエスケープ」と呼びます。

例:

  • <&lt; にエスケープされます。
  • >&gt; にエスケープされます。
  • &&amp; にエスケープされます。
  • "&quot; にエスケープされます。
  • '&apos; (HTML5) または &#39; (数値文字参照) にエスケープされます。

「HTMLアンエスケープ」は、この逆の処理であり、HTMLエンティティや数値文字参照を元の特殊文字に戻すことを指します。Go言語のhtmlパッケージのUnescapeString関数は、このアンエスケープ処理を行います。

HTMLエンティティと数値文字参照

HTMLエンティティには主に2種類あります。

  1. 名前付き文字参照 (Named Character References):

    • & で始まり、エンティティ名(例: lt, amp, quot)が続き、; で終わります。
    • 例: &lt; (less than), &amp; (ampersand), &quot; (double quotation mark)
  2. 数値文字参照 (Numeric Character References):

    • &# で始まり、文字のUnicodeコードポイントを10進数で記述し、; で終わります。
    • &#x で始まり、文字のUnicodeコードポイントを16進数で記述し、; で終わります。
    • 例: &#60; (<), &#x26; (&), &#87; ( - DOUBLE DAGGER)

UnescapeString関数は、これらの形式を認識し、対応する文字に変換します。しかし、入力がこれらの厳密な形式に従わない場合、例えば & の後に有効なエンティティ名や数値が続かない場合、関数はそれをエンティティとして解釈せず、元の文字列をそのまま返す必要があります。このコミットで追加されたテストケースは、まさにこのような「不完全な」または「無効な」エンティティ形式に対する関数の挙動を検証しています。

技術的詳細

html.UnescapeString関数は、入力文字列を走査し、& 文字を検出すると、その後に続く文字シーケンスが有効なHTMLエンティティまたは数値文字参照であるかどうかを解析します。

  • 有効なエンティティの場合: 例えば &amp; であれば & に、&#x87; であれば に変換します。
  • 無効なエンティティの場合: 例えば &foo; のように、& で始まり ; で終わるが、有効なエンティティ名や数値文字参照ではない場合、UnescapeStringは通常、そのシーケンスをそのままの文字列として扱います。
  • 不完全なエンティティの場合: 今回のコミットで追加されたテストケースが対象とするのは、この「不完全な」エンティティのパターンです。
    • & (単一のアンパサンド): これはエンティティの開始文字ですが、それ自体は完全なエンティティではありません。関数はこれを & としてそのまま返す必要があります。
    • text &test: & の後に続く test は有効なエンティティ名ではありません。関数は text &test をそのまま返す必要があります。
    • text &#: &# は数値文字参照の開始ですが、その後に数値が続きません。関数は text &# をそのまま返す必要があります。

これらのエッジケースは、UnescapeString関数の内部的な状態機械やパーシングロジックが、入力の曖昧さをどのように処理するかをテストする上で重要です。関数は、可能な限り多くの文字を消費してエンティティを形成しようとしますが、有効なエンティティを形成できない場合は、消費した文字を元の形式で出力ストリームに戻す必要があります。これらのテストは、この「フォールバック」または「非エンティティ」処理が正しく行われることを保証します。

コアとなるコードの変更箇所

このコミットでは、src/pkg/html/escape_test.go ファイルに以下のテストケースが追加されています。

--- a/src/pkg/html/escape_test.go
+++ b/src/pkg/html/escape_test.go
@@ -64,6 +64,24 @@ var unescapeTests = []unescapeTest{
 		"Footnote&#x87;",
 		"Footnote‡",
 	},
+	// Handle single ampersand.
+	{
+		"copySingleAmpersand",
+		"&",
+		"&",
+	},
+	// Handle ampersand followed by non-entity.
+	{
+		"copyAmpersandNonEntity",
+		"text &test",
+		"text &test",
+	},
+	// Handle "&#".
+	{
+		"copyAmpersandHash",
+		"text &#",
+		"text &#",
+	},
 }
 
 func TestUnescape(t *testing.T) {

コアとなるコードの解説

追加されたunescapeTest構造体の要素は、それぞれ特定のシナリオをテストしています。unescapeTest構造体は、テストケースの名前、入力文字列、期待される出力文字列の3つのフィールドを持つと推測されます。

  1. copySingleAmpersand:

    • 入力: &
    • 期待される出力: &
    • このテストケースは、単一のアンパサンドが入力として与えられた場合に、UnescapeString関数がそれをHTMLエンティティとして解釈せず、そのままの文字として返すことを検証します。これは、アンパサンドがエンティティの開始文字であるものの、それ自体は完全なエンティティではないため、変換されるべきではないという挙動を保証します。
  2. copyAmpersandNonEntity:

    • 入力: text &test
    • 期待される出力: text &test
    • このテストケースは、アンパサンドの後に有効なHTMLエンティティ名や数値文字参照の形式ではない文字列(この場合は test)が続く場合に、UnescapeString関数がそのシーケンス全体をエンティティとして解釈せず、元の文字列をそのまま返すことを検証します。これは、パーサーがエンティティを認識できない場合に、誤って一部を変換したり、エラーを発生させたりしないことを保証します。
  3. copyAmpersandHash:

    • 入力: text &#
    • 期待される出力: text &#
    • このテストケースは、数値文字参照の開始を示す &# が出現したものの、その後に有効な数値が続かない場合に、UnescapeString関数がそのシーケンス全体をエンティティとして解釈せず、元の文字列をそのまま返すことを検証します。これは、不完全な数値文字参照の形式に対しても、関数が正しくフォールバックし、元の文字列を保持することを保証します。

これらのテストケースは、UnescapeString関数がHTMLの仕様に厳密に従い、曖昧な入力や不完全なエンティティ形式に対しても、安全かつ予測可能な方法で動作することを保証するために不可欠です。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント (pkg.go.dev)
  • MDN Web Docs (developer.mozilla.org)
  • GitHubのgolang/goリポジトリのコミット履歴
  • Go言語のソースコード (src/pkg/html/escape_test.go)
  • 一般的なHTMLエスケープ/アンエスケープの概念に関する知識