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

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

このコミットは、Go言語の標準ライブラリである html パッケージに、HTMLエスケープおよびアンエスケープ機能のテストを追加するものです。具体的には、UnescapeString 関数と EscapeString 関数の正確性を検証するための広範なテストケースが src/pkg/html/escape_test.go に追加されました。これにより、HTMLエンティティの処理、特殊文字のエスケープ、および様々なエッジケースにおける堅牢性が保証されます。

コミット

commit 4ca346795e9f1008878768214895c372838e7e48
Author: Andrew Gerrand <adg@golang.org>
Date:   Wed Jul 10 17:31:46 2013 +1000

    html: add escaping tests
    
    R=golang-dev, dsymonds, bradfitz
    CC=golang-dev
    https://golang.org/cl/11095043

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

https://github.com/golang/go/commit/4ca346795e9f1008878768214895c372838e7e48

元コミット内容

このコミットの目的は、Go言語の html パッケージにおけるHTMLエスケープ機能のテストを追加することです。これにより、HTMLコンテンツを安全に扱うための既存の関数(UnescapeString および EscapeString)が、様々な入力に対して正しく動作することを保証します。

変更の背景

Webアプリケーション開発において、ユーザーからの入力や外部データソースからの情報をHTMLとして表示する際には、セキュリティ上の脆弱性を防ぐために適切なエスケープ処理が不可欠です。特に、クロスサイトスクリプティング(XSS)攻撃を防ぐためには、HTMLの特殊文字(<, >, &, ', "など)をHTMLエンティティに変換するエスケープ処理が必須となります。また、HTMLエンティティを元の文字に戻すアンエスケープ処理も、正しく機能することが求められます。

このコミットが作成された2013年当時、Go言語の html パッケージは既にこれらのエスケープ/アンエスケープ機能を提供していましたが、その堅牢性をさらに高めるために、より包括的なテストスイートが必要とされていました。特に、様々な種類のHTMLエンティティ(名前付きエンティティ、数値エンティティ、16進数エンティティなど)や、文字列の終端、マルチバイト文字の扱いなど、多様なシナリオをカバーするテストが不足していたと考えられます。このコミットは、これらのテストを追加することで、html パッケージの信頼性と安全性を向上させることを目的としています。

前提知識の解説

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

HTMLエスケープとは、HTMLドキュメント内で特別な意味を持つ文字(例: < はタグの開始、& はエンティティの開始)を、その文字自体として表示するために、対応するHTMLエンティティに変換する処理です。これにより、悪意のあるスクリプトの挿入(XSS攻撃)を防いだり、意図しないHTML構造の崩壊を防いだりすることができます。

主要なHTML特殊文字とそのエンティティは以下の通りです。

  • < (小なり記号): &lt;
  • > (大なり記号): &gt;
  • & (アンパサンド): &amp;
  • " (二重引用符): &quot;
  • ' (一重引用符): &apos; (HTML5で導入、XMLでは以前から使用)

HTMLアンエスケープとは、エスケープされたHTMLエンティティを元の文字に戻す処理です。例えば、&lt;< に変換します。

HTMLエンティティの種類

HTMLエンティティには主に以下の種類があります。

  1. 名前付きエンティティ (Named Entities): &entity_name; の形式で、覚えやすい名前が付けられています。例: &amp;, &lt;, &gt;, &quot;, &copy; (著作権記号)。

  2. 数値エンティティ (Numeric Entities): 文字のUnicodeコードポイントを数値で指定します。

    • 10進数エンティティ: &#decimal_value; の形式。例: &#916; (ギリシャ文字のデルタ Δ)。
    • 16進数エンティティ: &#xhex_value; または &#Xhex_value; の形式。例: &#x3bb; (ギリシャ文字のラムダ λ)。

Go言語の html パッケージ

Go言語の標準ライブラリには、HTMLの解析や操作を行うための html パッケージが含まれています。このパッケージは、HTMLドキュメントのパース、HTMLエンティティの処理、およびHTMLテンプレートの安全なレンダリングなど、Webアプリケーション開発におけるHTML関連の様々な機能を提供します。特に、html.EscapeStringhtml.UnescapeString 関数は、文字列のエスケープとアンエスケープを担当します。

技術的詳細

このコミットは、html パッケージのテストカバレッジを向上させることに焦点を当てています。追加された escape_test.go ファイルは、UnescapeString 関数と EscapeString 関数の両方に対して、様々な入力パターンと期待される出力パターンを定義しています。

unescapeTest 構造体は、テストケースの記述を簡潔にするために導入されています。この構造体は、テストケースの説明 (desc)、入力HTML文字列 (html)、および期待されるアンエスケープ済み文字列 (unescaped) を保持します。

unescapeTests スライスには、以下のような多様なシナリオをカバーするテストケースが含まれています。

  • 基本的なコピー: エスケープが必要ない通常の文字列が正しくコピーされるか。
  • 単純な名前付きエンティティ: &amp;, &gt;, &lt; のような基本的な名前付きエンティティが正しくアンエスケープされるか。
  • 文字列の終端: エンティティが文字列の途中で不完全に終わる場合(例: &amp)に、正しく処理されるか。
  • マルチコードポイントエンティティ: &gesl; のように、単一のエンティティが複数のUnicodeコードポイントにマップされる場合に、正しくアンエスケープされるか。
  • 10進数数値エンティティ: &#916; のような10進数エンティティが正しくアンエスケープされるか。
  • 16進数数値エンティティ: &#x3bb;&#X3Bb のような16進数エンティティが正しくアンエスケープされるか。大文字・小文字の区別もテストされています。
  • 数値エンティティの早期終了: &#&#x のように数値エンティティが不完全に終わる場合や、数値の後に不正な文字が続く場合に、正しく処理されるか。
  • ISO-8859-1数値エンティティの置換: &#x87; のように、HTML5で定義された特定のISO-8859-1範囲の数値エンティティが、対応するUnicode文字に置換されるか。

TestUnescape 関数は、これらの unescapeTests をループし、UnescapeString 関数の結果を検証します。

TestUnescapeEscape 関数は、EscapeStringUnescapeString の両方を組み合わせたラウンドトリップテストを行います。これは、ある文字列をエスケープし、その結果を再度アンエスケープしたときに、元の文字列に戻ることを確認するものです。これにより、両関数の相互運用性と正確性が保証されます。テストケースには、空文字列、通常の文字列、特殊文字を含む文字列、既にエスケープされた文字列などが含まれています。

これらのテストの追加により、html パッケージのエスケープ/アンエスケープ機能が、より多様な現実世界のシナリオやエッジケースに対して堅牢であることが保証され、Webアプリケーションのセキュリティと信頼性が向上します。

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

このコミットでは、既存のコードの変更ではなく、新しいテストファイル src/pkg/html/escape_test.go が追加されています。

diff --git a/src/pkg/html/escape_test.go b/src/pkg/html/escape_test.go
new file mode 100644
index 0000000000..b405d4b4a7
--- /dev/null
+++ b/src/pkg/html/escape_test.go
@@ -0,0 +1,97 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package html
+
+import "testing"
+
+type unescapeTest struct {
+	// A short description of the test case.
+	desc string
+	// The HTML text.
+	html string
+	// The unescaped text.
+	unescaped string
+}
+
+var unescapeTests = []unescapeTest{
+	// Handle no entities.
+	{
+		"copy",
+		"A\ttext\nstring",
+		"A\ttext\nstring",
+	},
+	// Handle simple named entities.
+	{
+		"simple",
+		"&amp; &gt; &lt;",
+		"& > <",
+	},
+	// Handle hitting the end of the string.
+	{
+		"stringEnd",
+		"&amp &amp",
+		"& &",
+	},
+	// Handle entities with two codepoints.
+	{
+		"multiCodepoint",
+		"text &gesl; blah",
+		"text \u22db\ufe00 blah",
+	},
+	// Handle decimal numeric entities.
+	{
+		"decimalEntity",
+		"Delta = &#916; ",
+		"Delta = Δ ",
+	},
+	// Handle hexadecimal numeric entities.
+	{
+		"hexadecimalEntity",
+		"Lambda = &#x3bb; = &#X3Bb ",
+		"Lambda = λ = λ ",
+	},
+	// Handle numeric early termination.
+	{
+		"numericEnds",
+		"&# &#x &#128;43 &copy = &#169f = &#xa9",
+		"&# &#x €43 © = ©f = ©",
+	},
+	// Handle numeric ISO-8859-1 entity replacements.
+	{
+		"numericReplacements",
+		"Footnote&#x87;",
+		"Footnote‡",
+	},
+}
+
+func TestUnescape(t *testing.T) {
+	for _, tt := range unescapeTests {
+		unescaped := UnescapeString(tt.html)
+		if unescaped != tt.unescaped {
+			t.Errorf("TestUnescape %s: want %q, got %q", tt.desc, tt.unescaped, unescaped)
+		}
+	}
+}
+
+func TestUnescapeEscape(t *testing.T) {
+	ss := []string{
+		``,
+		`abc def`,
+		`a & b`,
+		`a&amp;b`,
+		`a &amp b`,
+		`&quot;`,
+		`"`,
+		`"<&>"`,
+		`&quot;&lt;&amp;&gt;&quot;`,
+		`3&5==1 && 0<1, "0&lt;1", a+acute=&aacute;`,
+		`The special characters are: <, >, &, ' and "`,
+	}
+	for _, s := range ss {
+		if got := UnescapeString(EscapeString(s)); got != s {
+			t.Errorf("got %q want %q", got, s)
+		}
+	}
+}

コアとなるコードの解説

追加された src/pkg/html/escape_test.go ファイルは、Go言語の testing パッケージを利用して、html パッケージのエスケープ/アンエスケープ機能の単体テストを実装しています。

  1. unescapeTest 構造体: この構造体は、UnescapeString 関数の個々のテストケースを定義するために使用されます。

    • desc: テストケースの簡単な説明。テスト失敗時にどのケースが失敗したかを特定しやすくします。
    • html: UnescapeString 関数に渡される入力HTML文字列。
    • unescaped: UnescapeString 関数が返すことが期待される、アンエスケープされた文字列。
  2. unescapeTests スライス: unescapeTest 型の構造体のスライスであり、UnescapeString 関数の様々なテストシナリオを網羅しています。これには、特殊文字、数値エンティティ、名前付きエンティティ、不完全なエンティティ、マルチバイト文字を含むエンティティなど、多様なケースが含まれています。

  3. TestUnescape(t *testing.T) 関数: この関数は、unescapeTests スライス内の各テストケースを反復処理します。

    • 各テストケースについて、UnescapeString(tt.html) を呼び出して実際のアンエスケープ処理を行います。
    • 結果 (unescaped) が期待される値 (tt.unescaped) と異なる場合、t.Errorf を使用してエラーを報告します。t.Errorf はテストを失敗としてマークし、詳細なエラーメッセージを出力します。
  4. TestUnescapeEscape(t *testing.T) 関数: この関数は、EscapeStringUnescapeString の両方の関数が連携して正しく動作するかを検証するラウンドトリップテストです。

    • ss スライスには、エスケープとアンエスケープの往復処理をテストするための様々な文字列が含まれています。これには、空文字列、通常のテキスト、HTML特殊文字を含む文字列、既にHTMLエンティティを含む文字列などが含まれます。
    • 各文字列 s について、まず EscapeString(s) を呼び出してエスケープし、その結果をさらに UnescapeString でアンエスケープします。
    • 最終的な結果 (got) が元の文字列 s と異なる場合、t.Errorf を使用してエラーを報告します。これにより、エスケープとアンエスケープの処理が可逆的であり、データが失われたり破損したりしないことが保証されます。

これらのテストは、html パッケージの堅牢性と正確性を保証するための重要な追加であり、将来の変更やバグ修正の際の回帰テストとしても機能します。

関連リンク

参考にした情報源リンク