[インデックス 10144] ファイルの概要
このコミットは、Go言語の標準ライブラリであるtext/template
(またはhtml/template
)パッケージにおける、エラー型がテンプレート内で文字列として扱われる挙動を修正するものです。具体的には、テストコード内でos.NewError
で作成されたエラーオブジェクトをbytes.NewBuffer
で作成されたバッファオブジェクトに置き換えることで、テンプレートエンジンがエラーオブジェクトを自動的に文字列化しないように変更しています。これにより、エラーメッセージが意図せずテンプレートに出力されることを防ぎ、セキュリティと堅牢性を向上させています。
コミット
- コミットハッシュ:
853c84631f1afda1672930e8e509beeb0e9d44f9
- Author: Russ Cox rsc@golang.org
- Date: Thu Oct 27 21:17:47 2011 -0700
- 変更ファイル:
src/pkg/template/exec_test.go
(1ファイル) - 変更行数: 2行 (1挿入, 1削除)
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/853c84631f1afda1672930e8e509beeb0e9d44f9
元コミット内容
template: do not use error as stringer
R=r
CC=golang-dev
https://golang.org/cl/5305069
変更の背景
Go言語のテンプレートパッケージ(text/template
やhtml/template
)は、テンプレート内で表示される値が特定のインターフェース(例えばfmt.Stringer
)を実装している場合、そのインターフェースのメソッド(String()
)を呼び出して値を文字列としてレンダリングする機能を持っています。
初期のGo言語の設計では、error
インターフェースを実装する型(つまり、Error()
メソッドを持つ型)が、テンプレート内で暗黙的にfmt.Stringer
のように扱われ、そのError()
メソッドの戻り値が直接テンプレートに出力される可能性がありました。
しかし、エラーメッセージには、ファイルパス、内部的な状態、スタックトレースなど、デバッグには有用でもエンドユーザーに公開すべきではない機密情報や内部情報が含まれている場合があります。このような情報が意図せずWebページやその他の出力に表示されてしまうと、情報漏洩やセキュリティ上の脆弱性につながる可能性があります。
このコミットは、このような潜在的なセキュリティリスクを軽減し、テンプレートの堅牢性を高めることを目的としています。エラーオブジェクトがテンプレート内で自動的に文字列化されることを防ぐことで、開発者はエラーを明示的に処理し、ユーザーに表示する内容を制御するよう促されます。これにより、不必要な情報が公開されることを防ぎ、アプリケーションのセキュリティが向上します。
前提知識の解説
Go言語のerror
インターフェース
Go言語では、エラーは組み込みのerror
インターフェースによって表現されます。このインターフェースは非常にシンプルで、Error() string
という単一のメソッドを定義しています。
type error interface {
Error() string
}
この設計により、Goの関数はエラーが発生した場合にerror
型の値を返すことができ、呼び出し元はそのエラーを処理することができます。
fmt.Stringer
インターフェース
fmt.Stringer
インターフェースは、Goのfmt
パッケージで定義されており、値を人間が読める文字列形式に変換するためのString() string
メソッドを定義しています。
type Stringer interface {
String() string
}
Goの多くの標準ライブラリ関数(例: fmt.Print
、fmt.Sprintf
)は、引数がfmt.Stringer
を実装している場合、そのString()
メソッドを呼び出して文字列表現を取得します。
text/template
およびhtml/template
パッケージの挙動
Goのテンプレートパッケージは、データ構造をテンプレートに渡してレンダリングする際に、渡された値の型を検査し、特定のインターフェースを実装している場合に特別な処理を行います。
特に、テンプレートエンジンは、表示しようとする値がfmt.Stringer
インターフェースを実装している場合、そのString()
メソッドを呼び出して得られた文字列をテンプレートに挿入します。
このコミット以前のGoのテンプレート実装では、error
インターフェースを実装する型が、暗黙的にfmt.Stringer
のように扱われることがありました。これは、error
インターフェースのError()
メソッドとfmt.Stringer
インターフェースのString()
メソッドが、どちらも() string
という同じシグネチャを持つため、Goのインターフェースの仕組み上、error
型がfmt.Stringer
としても振る舞うことが可能であったためです。
os.NewError
とbytes.NewBuffer
-
os.NewError(s string) error
: Go 1.0より前のバージョンで存在した関数で、指定された文字列s
をエラーメッセージとする新しいerror
オブジェクトを作成するために使用されていました。Go 1.0以降は非推奨となり、代わりにerrors.New(s string) error
が推奨されています。この関数が返すのは、error
インターフェースを実装する具体的な型です。 -
bytes.NewBuffer(buf []byte) *Buffer
:bytes
パッケージの関数で、バイトスライスbuf
を初期値とする新しいBuffer
オブジェクトを作成します。*bytes.Buffer
型は、io.Reader
、io.Writer
、fmt.Stringer
など、多くのインターフェースを実装しています。特に、String() string
メソッドを実装しており、バッファの内容を文字列として返します。これは、テンプレート内で安全に文字列データを扱うための一般的な方法です。
技術的詳細
このコミットの技術的な核心は、Goのテンプレートエンジンが、error
インターフェースを実装するオブジェクトを、もはやfmt.Stringer
として自動的に扱わないようにするという方針転換にあります。
以前の挙動では、例えばテンプレート内で.ErrorField
のようにエラーオブジェクトを参照した場合、そのエラーオブジェクトのError()
メソッドが呼び出され、その結果がテンプレートに直接埋め込まれていました。これは、開発者がエラーを明示的に処理する代わりに、エラーメッセージがそのままユーザーインターフェースに表示されてしまうという、意図しない結果を招く可能性がありました。
この変更により、テンプレートエンジンはerror
型を特別扱いし、そのError()
メソッドを自動的に呼び出して文字列化することを停止します。これにより、テンプレート内でエラーオブジェクトを直接参照しても、何も出力されないか、あるいはエラー処理が明示的に行われていない場合にはテンプレートの実行が失敗するようになります。
テストコードにおけるos.NewError("foozle")
からbytes.NewBuffer([]byte("foozle"))
への変更は、この新しい挙動を反映したものです。
os.NewError("foozle")
はerror
型を生成します。この型は、変更前はテンプレートによって文字列化されていました。bytes.NewBuffer([]byte("foozle"))
は*bytes.Buffer
型を生成します。この型はfmt.Stringer
を明示的に実装しており、そのString()
メソッドはバッファの内容(この場合は"foozle")を返します。したがって、このオブジェクトはテンプレートによって安全に文字列化されます。
この変更は、テンプレートのセキュリティモデルを強化し、開発者がエラー処理と情報公開についてより意識的な決定を下すことを強制します。これにより、アプリケーションがより堅牢になり、潜在的な情報漏洩のリスクが低減されます。
コアとなるコードの変更箇所
変更はsrc/pkg/template/exec_test.go
ファイル内のテストデータ定義にあります。
--- a/src/pkg/template/exec_test.go
+++ b/src/pkg/template/exec_test.go
@@ -98,7 +98,7 @@ var tVal = &T{
Empty3: []int{7, 8},\n Empty4: &U{"UinEmpty"},\n NonEmptyInterface: new(T),\n-\tStr: os.NewError("foozle"),\n+\tStr: bytes.NewBuffer([]byte("foozle")),\n PI: newInt(23),\n PSI: newIntSlice(21, 22, 23),\n Tmpl: Must(New("x").Parse("test template")), // "x" is the value of .X
具体的には、tVal
というテスト用の構造体T
の初期化において、Str
フィールドの値が変更されています。
- 変更前:
Str: os.NewError("foozle")
- 変更後:
Str: bytes.NewBuffer([]byte("foozle"))
コアとなるコードの解説
この変更は、template
パッケージのテストスイートの一部であるexec_test.go
ファイル内のtVal
というグローバル変数に定義されているテストデータT
構造体のStr
フィールドの値を修正しています。
tVal
は、テンプレートの実行テストにおいて、テンプレートに渡されるデータコンテキストとして使用されるものです。Str
フィールドは、テンプレートが文字列としてレンダリングすることを期待する値を保持するために使用されていました。
-
変更前 (
os.NewError("foozle")
): この行は、Str
フィールドにos.NewError
関数によって作成されたerror
型のオブジェクトを割り当てていました。このerror
オブジェクトは、Error()
メソッドを呼び出すと文字列"foozle"
を返します。変更前のテンプレートエンジンの挙動では、このerror
オブジェクトがテンプレート内で参照された際に、そのError()
メソッドが自動的に呼び出され、結果として"foozle"
という文字列がテンプレートに出力されることが期待されていました。これは、error
型がfmt.Stringer
のように扱われることをテストしていた、あるいはその挙動に依存していたことを示唆しています。 -
変更後 (
bytes.NewBuffer([]byte("foozle"))
): この行は、Str
フィールドにbytes.NewBuffer
関数によって作成された*bytes.Buffer
型のオブジェクトを割り当てています。*bytes.Buffer
型は、fmt.Stringer
インターフェースを明示的に実装しており、そのString()
メソッドはバッファの内容(この場合はバイトスライス[]byte("foozle")
が文字列に変換されたもの)を返します。 この変更は、テンプレートエンジンがerror
型を自動的に文字列化しないようになった新しい挙動に合わせて、テストデータを調整したものです。つまり、Str
フィールドがテンプレート内で文字列としてレンダリングされることを引き続きテストするために、error
型ではなく、fmt.Stringer
を安全に実装している*bytes.Buffer
型を使用するように変更されました。
このテストコードの変更は、template
パッケージの内部的な挙動変更(エラー型をstringer
として扱わない)が正しく機能していることを確認するためのものです。これにより、開発者はテンプレート内でエラーオブジェクトを直接表示するのではなく、エラーを適切に処理し、ユーザーに表示する内容を明示的に制御する必要があるという、より安全なプログラミングプラクティスが促進されます。
関連リンク
- Go CL 5305069: https://golang.org/cl/5305069 (このコミットに対応するGoのコードレビューシステム上の変更リスト)
- Go言語の
text/template
パッケージ公式ドキュメント: https://pkg.go.dev/text/template - Go言語の
html/template
パッケージ公式ドキュメント: https://pkg.go.dev/html/template - Go言語の
error
インターフェースに関する公式ドキュメント: https://pkg.go.dev/builtin#error - Go言語の
fmt.Stringer
インターフェースに関する公式ドキュメント: https://pkg.go.dev/fmt#Stringer - Go言語の
bytes.Buffer
に関する公式ドキュメント: https://pkg.go.dev/bytes#Buffer
参考にした情報源リンク
- Go言語の公式ドキュメント(上記「関連リンク」に記載の各パッケージドキュメント)
- Go言語のコミット履歴と関連するコードレビューディスカッション
- Go言語のテンプレートにおけるセキュリティに関する一般的な情報源(例: Goのテンプレートエスケープメカニズムに関する記事など)
[インデックス 10144] ファイルの概要
このコミットは、Go言語の標準ライブラリであるtext/template
(またはhtml/template
)パッケージにおいて、エラー型がテンプレート内で文字列として扱われる挙動を修正するものです。具体的には、テストコード内でos.NewError
で作成されたエラーオブジェクトをbytes.NewBuffer
で作成されたバッファオブジェクトに置き換えることで、テンプレートエンジンがエラーオブジェクトを自動的に文字列化しないように変更しています。これにより、エラーメッセージが意図せずテンプレートに出力されることを防ぎ、セキュリティと堅牢性を向上させています。
コミット
- コミットハッシュ:
853c84631f1afda1672930e8e509beeb0e9d44f9
- Author: Russ Cox rsc@golang.org
- Date: Thu Oct 27 21:17:47 2011 -0700
- 変更ファイル:
src/pkg/template/exec_test.go
(1ファイル) - 変更行数: 2行 (1挿入, 1削除)
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/853c84631f1afda1672930e8e509beeb0e9d44f9
元コミット内容
template: do not use error as stringer
R=r
CC=golang-dev
https://golang.org/cl/5305069
変更の背景
Go言語のテンプレートパッケージ(text/template
やhtml/template
)は、テンプレート内で表示される値が特定のインターフェース(例えばfmt.Stringer
)を実装している場合、そのインターフェースのメソッド(String()
)を呼び出して値を文字列としてレンダリングする機能を持っています。
初期のGo言語の設計では、error
インターフェースを実装する型(つまり、Error()
メソッドを持つ型)が、テンプレート内で暗黙的にfmt.Stringer
のように扱われ、そのError()
メソッドの戻り値が直接テンプレートに出力される可能性がありました。これは、error
インターフェースのError()
メソッドとfmt.Stringer
インターフェースのString()
メソッドが、どちらも() string
という同じシグネチャを持つため、Goのインターフェースの仕組み上、error
型がfmt.Stringer
としても振る舞うことが可能であったためです。
しかし、エラーメッセージには、ファイルパス、内部的な状態、スタックトレースなど、デバッグには有用でもエンドユーザーに公開すべきではない機密情報や内部情報が含まれている場合があります。このような情報が意図せずWebページやその他の出力に表示されてしまうと、情報漏洩やクロスサイトスクリプティング(XSS)などのセキュリティ上の脆弱性につながる可能性があります。特に、text/template
は自動的なエスケープを行わないため、悪意のあるスクリプトがエラーメッセージに含まれていた場合、XSS攻撃のリスクが高まります。
このコミットは、このような潜在的なセキュリティリスクを軽減し、テンプレートの堅牢性を高めることを目的としています。エラーオブジェクトがテンプレート内で自動的に文字列化されることを防ぐことで、開発者はエラーを明示的に処理し、ユーザーに表示する内容を制御するよう促されます。これにより、不必要な情報が公開されることを防ぎ、アプリケーションのセキュリティが向上します。
前提知識の解説
Go言語のerror
インターフェース
Go言語では、エラーは組み込みのerror
インターフェースによって表現されます。このインターフェースは非常にシンプルで、Error() string
という単一のメソッドを定義しています。
type error interface {
Error() string
}
この設計により、Goの関数はエラーが発生した場合にerror
型の値を返すことができ、呼び出し元はそのエラーを処理することができます。
fmt.Stringer
インターフェース
fmt.Stringer
インターフェースは、Goのfmt
パッケージで定義されており、値を人間が読める文字列形式に変換するためのString() string
メソッドを定義しています。
type Stringer interface {
String() string
}
Goの多くの標準ライブラリ関数(例: fmt.Print
、fmt.Sprintf
)は、引数がfmt.Stringer
を実装している場合、そのString()
メソッドを呼び出して文字列表現を取得します。
text/template
およびhtml/template
パッケージの挙動
Goのテンプレートパッケージは、データ構造をテンプレートに渡してレンダリングする際に、渡された値の型を検査し、特定のインターフェースを実装している場合に特別な処理を行います。
特に、テンプレートエンジンは、表示しようとする値がfmt.Stringer
インターフェースを実装している場合、そのString()
メソッドを呼び出して得られた文字列をテンプレートに挿入します。
このコミット以前のGoのテンプレート実装では、error
インターフェースを実装する型が、暗黙的にfmt.Stringer
のように扱われることがありました。これは、error
インターフェースのError()
メソッドとfmt.Stringer
インターフェースのString()
メソッドが、どちらも() string
という同じシグネチャを持つため、Goのインターフェースの仕組み上、error
型がfmt.Stringer
としても振る舞うことが可能であったためです。
os.NewError
とbytes.NewBuffer
-
os.NewError(s string) error
: Go 1.0より前のバージョンで存在した関数で、指定された文字列s
をエラーメッセージとする新しいerror
オブジェクトを作成するために使用されていました。Go 1.0以降は非推奨となり、代わりにerrors.New(s string) error
が推奨されています。この関数が返すのは、error
インターフェースを実装する具体的な型です。 -
bytes.NewBuffer(buf []byte) *Buffer
:bytes
パッケージの関数で、バイトスライスbuf
を初期値とする新しいBuffer
オブジェクトを作成します。*bytes.Buffer
型は、io.Reader
、io.Writer
、fmt.Stringer
など、多くのインターフェースを実装しています。特に、String() string
メソッドを実装しており、バッファの内容を文字列として返します。これは、テンプレート内で安全に文字列データを扱うための一般的な方法です。
技術的詳細
このコミットの技術的な核心は、Goのテンプレートエンジンが、error
インターフェースを実装するオブジェクトを、もはやfmt.Stringer
として自動的に扱わないようにするという方針転換にあります。
以前の挙動では、例えばテンプレート内で.ErrorField
のようにエラーオブジェクトを参照した場合、そのエラーオブジェクトのError()
メソッドが呼び出され、その結果がテンプレートに直接埋め込まれていました。これは、開発者がエラーを明示的に処理する代わりに、エラーメッセージがそのままユーザーインターフェースに表示されてしまうという、意図しない結果を招く可能性がありました。
この変更により、テンプレートエンジンはerror
型を特別扱いし、そのError()
メソッドを自動的に呼び出して文字列化することを停止します。これにより、テンプレート内でエラーオブジェクトを直接参照しても、何も出力されないか、あるいはエラー処理が明示的に行われていない場合にはテンプレートの実行が失敗するようになります。
テストコードにおけるos.NewError("foozle")
からbytes.NewBuffer([]byte("foozle"))
への変更は、この新しい挙動を反映したものです。
os.NewError("foozle")
はerror
型を生成します。この型は、変更前はテンプレートによって文字列化されていましたが、変更後は自動的に文字列化されなくなります。bytes.NewBuffer([]byte("foozle"))
は*bytes.Buffer
型を生成します。この型はfmt.Stringer
を明示的に実装しており、そのString()
メソッドはバッファの内容(この場合は"foozle")を返します。したがって、このオブジェクトはテンプレートによって安全に文字列化されます。
この変更は、テンプレートのセキュリティモデルを強化し、開発者がエラー処理と情報公開についてより意識的な決定を下すことを強制します。これにより、アプリケーションがより堅牢になり、潜在的な情報漏洩のリスクが低減されます。特に、Webアプリケーションにおいては、html/template
パッケージを常に使用し、String()
メソッドの実装が機密情報を公開しないように注意することが、XSSなどの脆弱性から保護するための重要なベストプラクティスとなります。
コアとなるコードの変更箇所
変更はsrc/pkg/template/exec_test.go
ファイル内のテストデータ定義にあります。
--- a/src/pkg/template/exec_test.go
+++ b/src/pkg/template/exec_test.go
@@ -98,7 +98,7 @@ var tVal = &T{
Empty3: []int{7, 8},\n Empty4: &U{"UinEmpty"},\n NonEmptyInterface: new(T),\n-\tStr: os.NewError("foozle"),\n+\tStr: bytes.NewBuffer([]byte("foozle")),\n PI: newInt(23),\n PSI: newIntSlice(21, 22, 23),\n Tmpl: Must(New("x").Parse("test template")), // "x" is the value of .X
具体的には、tVal
というテスト用の構造体T
の初期化において、Str
フィールドの値が変更されています。
- 変更前:
Str: os.NewError("foozle")
- 変更後:
Str: bytes.NewBuffer([]byte("foozle"))
コアとなるコードの解説
この変更は、template
パッケージのテストスイートの一部であるexec_test.go
ファイル内のtVal
というグローバル変数に定義されているテストデータT
構造体のStr
フィールドの値を修正しています。
tVal
は、テンプレートの実行テストにおいて、テンプレートに渡されるデータコンテキストとして使用されるものです。Str
フィールドは、テンプレートが文字列としてレンダリングすることを期待する値を保持するために使用されていました。
-
変更前 (
os.NewError("foozle")
): この行は、Str
フィールドにos.NewError
関数によって作成されたerror
型のオブジェクトを割り当てていました。このerror
オブジェクトは、Error()
メソッドを呼び出すと文字列"foozle"
を返します。変更前のテンプレートエンジンの挙動では、このerror
オブジェクトがテンプレート内で参照された際に、そのError()
メソッドが自動的に呼び出され、結果として"foozle"
という文字列がテンプレートに出力されることが期待されていました。これは、error
型がfmt.Stringer
のように扱われることをテストしていた、あるいはその挙動に依存していたことを示唆しています。 -
変更後 (
bytes.NewBuffer([]byte("foozle"))
): この行は、Str
フィールドにbytes.NewBuffer
関数によって作成された*bytes.Buffer
型のオブジェクトを割り当てています。*bytes.Buffer
型は、fmt.Stringer
インターフェースを明示的に実装しており、そのString()
メソッドはバッファの内容(この場合はバイトスライス[]byte("foozle")
が文字列に変換されたもの)を返します。 この変更は、テンプレートエンジンがerror
型を自動的に文字列化しないようになった新しい挙動に合わせて、テストデータを調整したものです。つまり、Str
フィールドがテンプレート内で文字列としてレンダリングされることを引き続きテストするために、error
型ではなく、fmt.Stringer
を安全に実装している*bytes.Buffer
型を使用するように変更されました。
このテストコードの変更は、template
パッケージの内部的な挙動変更(エラー型をstringer
として扱わない)が正しく機能していることを確認するためのものです。これにより、開発者はテンプレート内でエラーオブジェクトを直接表示するのではなく、エラーを適切に処理し、ユーザーに表示する内容を明示的に制御する必要があるという、より安全なプログラミングプラクティスが促進されます。
関連リンク
- Go CL 5305069: https://golang.org/cl/5305069 (このコミットに対応するGoのコードレビューシステム上の変更リスト)
- Go言語の
text/template
パッケージ公式ドキュメント: https://pkg.go.dev/text/template - Go言語の
html/template
パッケージ公式ドキュメント: https://pkg.go.dev/html/template - Go言語の
error
インターフェースに関する公式ドキュメント: https://pkg.go.dev/builtin#error - Go言語の
fmt.Stringer
インターフェースに関する公式ドキュメント: https://pkg.go.dev/fmt#Stringer - Go言語の
bytes.Buffer
に関する公式ドキュメント: https://pkg.go.dev/bytes#Buffer
参考にした情報源リンク
- Go言語の公式ドキュメント(上記「関連リンク」に記載の各パッケージドキュメント)
- Go言語のコミット履歴と関連するコードレビューディスカッション
- Web検索結果: "golang template error as stringer security"
- go.dev (https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQE4Mtnaot9PFGv11dtJjd-p_SlRpefqzANv6C9kX8OvKpI7L2ff4_g1mwembB7qdlntRpLWUFY1e4FT3zZB5W0lRyMkT0YyGMREntZRqwRrAj41CXaequg27jk=)
- last9.io (https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQGRbbCfRFtzWKAl1uBx7wYeAv5uv7WBFNqVkfaJBmg_9nbqR6qn1Eb-YlFOSsbFQ0Q8Ml38NY8A12na8X4hOOxkDQyg7hqJLfqNSbhYMgOwLsJaqphhi5xQqUeYlb-Rjo9WYZl9j-s=)
- go.dev (https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHJWTJf_ln9UUZJS-OkanJ2oTBCt5tt6RV7XLGrsPJdN4zJYDvYUGfCpy4kcUPE3zMZZfIWOZ8nCBNH3ndtPwoSC1K7F9qPUgWn9rTGnWiC1gMO1UsKlo2QeJE_0XyiWqojyP-x)
- medium.com (https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHkYIFSWHdUvOn3dWI0VUyOrPGhGyySbmvkT5EcS_t5374IkfWIfxRPPVzAPGPrZKNBBgOG_Rq3ILeSankdnpxDa4oNQ5IAW4-uWlsoO0iCPZ9tV8RojZSoJRWQ==)
- labex.io (https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQE6TXp5KAY0odyLwd-HC_SLSbsn66wlyY9iyAVbQwmpaosN8SoT9xnubcHFPmoa-a4Kh5GGAPBsGkUaqpJJ8xYbSrqgqg17RLd04392kj01BJ1Ormb0-9P80ZG8nn66rQvLe-mJcls6zQV0x3-R_nKd6N698H9BGsXdZ-81ltG8HfKHSu56wfgi4g==)
- studyraid.com (https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHFRaYpniH-vlhRmZPVMilk5IUAFrEST2LkoeBf7LkamDciU-PURTOUhk9N0X8RUTkuRHU_BzjxOCjfnLsSbMqG8YcGrY9p3Xowvs8q68DSv1klBi3Db5AX3jDbhHr0OhkiMMUXNElX-UgKFw1NVsVaGSZCbJahB6P1aiWNPH6F_OjDI4TtojdRwL0lWe17cKxTKg==)
- medium.com (https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQFeEdLREmwr-r3l3QdXXddVJMhzUt81ERD7OXSN4_qKZjlycHpfCN1E5RKhOHvspqswzJ-mVP64VYtqsq_vJ_2Xy90mUvd8U4iUYzfpF0bmPWTDpbS2ccVfaUmGu0DNjMsh0R9TyBpB5V0mlzj6Z_dd64UvVjgs79vy8XCTaIWVE45ueYGEwFheYNI=)