...

パッケージ template

import "html/template"
概要
目次

概要 ▾

template (html/template) パッケージはコードインジェクション対策を施した安全な HTML を生成するデータドリブンのテンプレートを実装します。 text/template と同じインターフェースを提供するため,出力が HTML の場合はいつも,text/template のかわりに用いるべきです。

ここでのドキュメントはパッケージのセキュリティ機能に焦点を合わせています。 テンプレート自体をプログラムする方法については, text/template のドキュメントを参照してください。

前書き

このパッケージはパッケージの text/template をラップしているので,そのテンプレート API を共有して安全に HTML テンプレートを解析および実行することができます。

tmpl, err := template.New("name").Parse(...)
// エラーチェックの省略
err = tmpl.Execute(out, data)

成功すれば, tmpl はインジェクションセーフになります。 それ以外の場合, err は ErrorCode のドキュメントで定義されているエラーです。

HTML テンプレートは,データ値をプレーンテキストとして扱い, HTML ドキュメントに安全に埋め込めるようにエンコードする必要があります。 エスケープ処理はコンテキスト型であるため,アクションは JavaScript, CSS ,および URI コンテキスト内に表示される可能性があります。

このパッケージで使用されているセキュリティモデルは,テンプレート作成者が信頼されているのに対し, Execute の data パラメータは信頼されていないと仮定しています。 詳細は以下の通りです。

import "text/template"
...
t, err := template.New("foo").Parse(`{{define "T"}}Hello, {{.}}!{{end}}`)
err = t.ExecuteTemplate(out, "T", "<script>alert('you have been pwned')</script>")

は,以下を作り出します。

Hello, <script>alert('you have been pwned')</script>!

しかし, html/template でのコンテキスト自動エスケープによって,

import "html/template"
...
t, err := template.New("foo").Parse(`{{define "T"}}Hello, {{.}}!{{end}}`)
err = t.ExecuteTemplate(out, "T", "<script>alert('you have been pwned')</script>")

安全でエスケープされた HTML 出力を生成します

Hello, &lt;script&gt;alert(&#39;you have been pwned&#39;)&lt;/script&gt;!

コンテキスト

このパッケージは HTML, CSS, JavaScript ,そして URI を理解します。 抜粋が与えられているので,それはサニタイズ関数をそれぞれの単純なアクションパイプラインに追加します。

<a href="/search?q={{.}}">{{.}}</a>

解析時に,必要に応じてエスケープ関数を追加するために各 {{.}} が上書きされます。 この場合は

<a href="/search?q={{. | urlescaper | attrescaper}}">{{. | htmlescaper}}</a>

urlescaper, attrescaper ,および htmlescaper は,内部エスケープ関数の別名です。

これらの内部エスケープ関数の場合,アクションパイプラインが nil インターフェース値に評価されると,空の文字列であるかのように扱われます。

エラー

詳細は ErrorCode のドキュメントを参照してください。

より詳細な全体像

このパッケージのコメントの残りの部分は最初に読む際にスキップしてもかまいません。 エスケープコンテキストとエラーメッセージを理解するのに必要な詳細を含みます。 ほとんどのユーザーはこれらの詳細を理解する必要はありません。

コンテキスト

{{.}} が `O'Reilly: How are <i>you</i>?` だとすると,左の文脈で {{.}} を使用したときの表示は次の表のとおりです。

コンテキスト                       {{.}} のあと
{{.}}                            O'Reilly: How are &lt;i&gt;you&lt;/i&gt;?
<a title='{{.}}'>                O&#39;Reilly: How are you?
<a href="/{{.}}">                O&#39;Reilly: How are %3ci%3eyou%3c/i%3e?
<a href="?q={{.}}">              O&#39;Reilly%3a%20How%20are%3ci%3e...%3f
<a onx='f("{{.}}")'>             O\x27Reilly: How are \x3ci\x3eyou...?
<a onx='f({{.}})'>               "O\x27Reilly: How are \x3ci\x3eyou...?"
<a onx='pattern = /{{.}}/;'>     O\x27Reilly: How are \x3ci\x3eyou...\x3f

安全でないコンテキストで使用された場合,値は除外される可能性があります。

コンテキスト                       {{.}} のあと
<a href="{{.}}">                 #ZgotmplZ

"O'Reilly:" は "http:" のような許可されたプロトコルではないため。

{{.}} が `left` のような無害な単語であるならば,より広く現れることができます,

Context                              {{.}} After
{{.}}                                left
<a title='{{.}}'>                    left
<a href='{{.}}'>                     left
<a href='/{{.}}'>                    left
<a href='?dir={{.}}'>                left
<a style="border-{{.}}: 4px">        left
<a style="align: {{.}}">             left
<a style="background: '{{.}}'>       left
<a style="background: url('{{.}}')>  left
<style>p.{{.}} {color:red}</style>   left

JavaScript のコンテキストでは,文字列以外の値を使用できます。 {{.}} が以下の場合,

struct{A,B string}{ "foo", "bar" }

エスケープテンプレート内

<script>var pair = {{.}};</script>

テンプレートの出力は以下となります。

<script>var pair = {"A": "foo", "B": "bar"};</script>

JavaScript のコンテキストに埋め込むために非文字列コンテンツがどのようにマーシャリングされるかを理解するには, json パッケージを参照してください。

型付き文字列

デフォルトでは,このパッケージはすべてのパイプラインがプレーンテキスト文字列を生成することを前提としています。 そのプレーンテキスト文字列を適切なコンテキストに正しく安全に埋め込むために必要なエスケープパイプラインステージを追加します。

データ値がプレーンテキストではない場合は,データ値をその型でマークすることで,エスケープされていないことを確認できます。

content.go の HTML, JS, URL などの型は,エスケープを免除された安全なコンテンツを伝送できます。

テンプレート

Hello, {{.}}!

を以下で呼び出すと,

tmpl.Execute(out, template.HTML(`<b>World</b>`))

以下を出力します。

Hello, <b>World</b>!

以下ではありません。

Hello, &lt;b&gt;World&lt;b&gt;!

{{.}} が通常の文字列であれば,後者が生成されます。

セキュリティモデル

https://rawgit.com/mikesamuel/sanitized-jquery-templates/trunk/safetemplate.html#problem_definition は,このパッケージで使用される "安全 (safe)" を定義します。

このパッケージは,テンプレート作成者が信頼されていること, Execute のデータパラメータが信頼されていないこと,および信頼できないデータに直面しても以下のプロパティを保持しようとすることを想定しています。

構造保持特徴: "... テンプレート作成者が安全なテンプレート言語で HTML タグを作成すると,ブラウザは信頼できないデータの値に関係なく,出力の対応する部分をタグとして解釈します。 属性の境界や JS, CSS の文字列の境界も同様です。"

コード有効性特徴: "... テンプレート作成者が指定したコードだけをページのテンプレート出力に挿入した結果として実行し,テンプレート作成者が指定したすべてのコードは同じ結果として実行する必要があります。"

最小サプライズ特徴: HTML, CSS, および JavaScript に精通している開発者(またはコードレビューア)は,コンテキスト自動エスケープが行われることを知っているので,{{.}} を見れば,サニタイズが行われることを正しく推測できるはずです。

コード:

const tpl = `
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>{{.Title}}</title>
    </head>
    <body>
        {{range .Items}}<div>{{ . }}</div>{{else}}<div><strong>no rows</strong></div>{{end}}
    </body>
</html>`

check := func(err error) {
    if err != nil {
        log.Fatal(err)
    }
}
t, err := template.New("webpage").Parse(tpl)
check(err)

data := struct {
    Title string
    Items []string
}{
    Title: "My page",
    Items: []string{
        "My photos",
        "My blog",
    },
}

err = t.Execute(os.Stdout, data)
check(err)

noItems := struct {
    Title string
    Items []string
}{
    Title: "My another page",
    Items: []string{},
}

err = t.Execute(os.Stdout, noItems)
check(err)

出力:

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8">
		<title>My page</title>
	</head>
	<body>
		<div>My photos</div><div>My blog</div>
	</body>
</html>
<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8">
		<title>My another page</title>
	</head>
	<body>
		<div><strong>no rows</strong></div>
	</body>
</html>

例 (Autoescaping)

コード:

check := func(err error) {
    if err != nil {
        log.Fatal(err)
    }
}
t, err := template.New("foo").Parse(`{{define "T"}}Hello, {{.}}!{{end}}`)
check(err)
err = t.ExecuteTemplate(os.Stdout, "T", "<script>alert('you have been pwned')</script>")
check(err)

出力:

Hello, &lt;script&gt;alert(&#39;you have been pwned&#39;)&lt;/script&gt;!

例 (Escape)

コード:

const s = `"Fran & Freddie's Diner" <tasty@example.com>`
v := []interface{}{`"Fran & Freddie's Diner"`, ' ', `<tasty@example.com>`}

fmt.Println(template.HTMLEscapeString(s))
template.HTMLEscape(os.Stdout, []byte(s))
fmt.Fprintln(os.Stdout, "")
fmt.Println(template.HTMLEscaper(v...))

fmt.Println(template.JSEscapeString(s))
template.JSEscape(os.Stdout, []byte(s))
fmt.Fprintln(os.Stdout, "")
fmt.Println(template.JSEscaper(v...))

fmt.Println(template.URLQueryEscaper(v...))

出力:

&#34;Fran &amp; Freddie&#39;s Diner&#34; &lt;tasty@example.com&gt;
&#34;Fran &amp; Freddie&#39;s Diner&#34; &lt;tasty@example.com&gt;
&#34;Fran &amp; Freddie&#39;s Diner&#34;32&lt;tasty@example.com&gt;
\"Fran & Freddie\'s Diner\" \x3Ctasty@example.com\x3E
\"Fran & Freddie\'s Diner\" \x3Ctasty@example.com\x3E
\"Fran & Freddie\'s Diner\"32\x3Ctasty@example.com\x3E
%22Fran+%26+Freddie%27s+Diner%2232%3Ctasty%40example.com%3E

目次 ▾

func HTMLEscape(w io.Writer, b []byte)
func HTMLEscapeString(s string) string
func HTMLEscaper(args ...interface{}) string
func IsTrue(val interface{}) (truth, ok bool)
func JSEscape(w io.Writer, b []byte)
func JSEscapeString(s string) string
func JSEscaper(args ...interface{}) string
func URLQueryEscaper(args ...interface{}) string
type CSS
type Error
    func (e *Error) Error() string
type ErrorCode
type FuncMap
type HTML
type HTMLAttr
type JS
type JSStr
type Srcset
type Template
    func Must(t *Template, err error) *Template
    func New(name string) *Template
    func ParseFiles(filenames ...string) (*Template, error)
    func ParseGlob(pattern string) (*Template, error)
    func (t *Template) AddParseTree(name string, tree *parse.Tree) (*Template, error)
    func (t *Template) Clone() (*Template, error)
    func (t *Template) DefinedTemplates() string
    func (t *Template) Delims(left, right string) *Template
    func (t *Template) Execute(wr io.Writer, data interface{}) error
    func (t *Template) ExecuteTemplate(wr io.Writer, name string, data interface{}) error
    func (t *Template) Funcs(funcMap FuncMap) *Template
    func (t *Template) Lookup(name string) *Template
    func (t *Template) Name() string
    func (t *Template) New(name string) *Template
    func (t *Template) Option(opt ...string) *Template
    func (t *Template) Parse(text string) (*Template, error)
    func (t *Template) ParseFiles(filenames ...string) (*Template, error)
    func (t *Template) ParseGlob(pattern string) (*Template, error)
    func (t *Template) Templates() []*Template
type URL

パッケージファイル

attr.go attr_string.go content.go context.go css.go delim_string.go doc.go element_string.go error.go escape.go html.go js.go jsctx_string.go state_string.go template.go transition.go url.go urlpart_string.go

func HTMLEscape

func HTMLEscape(w io.Writer, b []byte)

HTMLEscape は,プレーンテキストデータに相当するエスケープ HTML を w に書き込みます。

func HTMLEscapeString

func HTMLEscapeString(s string) string

HTMLEscapeString は,プレーンテキストデータに相当するエスケープ HTML を返します。

func HTMLEscaper

func HTMLEscaper(args ...interface{}) string

HTMLEscaper は,引数のテキスト表現に相当するエスケープ HTML を返します。

func IsTrue 1.6

func IsTrue(val interface{}) (truth, ok bool)

IsTrue は,値がその型のゼロではないという意味で 'true' であるかどうか,およびその値に意味のある真理値があるかどうかを報告します。 これは if や他のそのような行動によって使われる真理の定義です。

func JSEscape

func JSEscape(w io.Writer, b []byte)

JSEscape は,プレーンテキストデータと同等のエスケープされた JavaScript を w に書き込みます。

func JSEscapeString

func JSEscapeString(s string) string

JSEscapeString は,プレーンテキストデータに相当するエスケープされた JavaScript を返します。

func JSEscaper

func JSEscaper(args ...interface{}) string

JSEscaper は,引数のテキスト表現と同等の,エスケープされた JavaScript を返します。

func URLQueryEscaper

func URLQueryEscaper(args ...interface{}) string

URLQueryEscaper は,引数のテキスト表現のエスケープ値を URL クエリに埋め込むのに適した形式で返します。

type CSS

CSS は以下のいずれかに一致する既知の安全なコンテンツをカプセル化します。

1. CSS3 スタイルシートの生成。例えば `p { color: purple }` 。
2. CSS3 ルールの生成。例えば `a[href=~"https:"].foo#bar`。
3. CSS3 宣言の生成。例えば `color: red; margin: 2px`。
4. CSS3 の値生成。例えば `rgba(0, 0, 255, 127)`。

https://www.w3.org/TR/css3-syntax/#parsinghttps://web.archive.org/web/20090211114933/http://w3.org/TR/css3-syntax#style をご覧ください。

この型を使用するとセキュリティ上のリスクが生じます。 カプセル化されたコンテンツはテンプレートの出力にそのまま含まれるため,信頼できるソースから取得する必要があります。

type CSS string

type Error

Error は,テンプレートのエスケープ中に発生した問題を表します。

type Error struct {
    // ErrorCode はエラーの種類を表します。
    ErrorCode ErrorCode
    // Node は,既知の場合,問題の原因となったノードです。
    // nil でなければ, Name と Line を上書きします。
    Node parse.Node // Go 1.4
    // Name は,エラーが発生したテンプレートの名前です。
    Name string
    // Line はテンプレートソース内のエラーの行番号,または 0 です。
    Line int
    // Description は,人間が読める形式の問題の説明です。
    Description string
}

func (*Error) Error

func (e *Error) Error() string

type ErrorCode

ErrorCode は一種のエラーのコードです。

type ErrorCode int

テンプレートのエスケープ中に発生するエラーごとにコードを定義しますが,エスケープされたテンプレートも実行時に失敗する可能性があります。

出力: "ZgotmplZ" 例:

<img src="{{.X}}">
ここで, {{.X}} は `javascript:...` に評価されます。

解説:

"ZgotmplZ" は,実行時に CSS や URL コンテキストで,安全でないコンテンツであることを示す特別な値です。
例の出力は,以下となります。
  <img src="#ZgotmplZ">
データが信頼できるソースからのものである場合は,コンテンツタイプを使用してフィルタリングから除外します。
URL(`javascript:...`)
const (
    // OK はエラーがないことを示します。
    OK ErrorCode = iota

    // ErrAmbigContext: "... appears in an ambiguous context within a URL"
    // ("... は URL 内のあいまいなコンテキストで現れます")
    // 例:
    //   <a href="
    //      {{if .C}}
    //        /path/
    //      {{else}}
    //        /search?q=
    //      {{end}}
    //      {{.X}}
    //   ">
    // 解説:
    //   {{.C}} に応じて, URL サフィックスまたはクエリパラメータのいずれかになる可能性があるため, {{.X}} はあいまいな URL コンテキストになります。
    //   {{.X}} を条件の中に移動すると,あいまいさが解消されます。
    //   <a href="{{if .C}}/path/{{.X}}{{else}}/search?q={{.X}}">
    ErrAmbigContext

    // ErrBadHTML: "expected space, attr name, or end of tag, but got ...",
    //   "... in unquoted attr", "... in attribute name"
    //   ("スペース,属性名,またはタグの終わりが必要ですが, ... がありました" ,
    //   "... が引用符なしの属性にあります", "属性名に ... があります")
    // 例:
    //   <a href = /search?q=foo>
    //   <href=foo>
    //   <form na<e=...>
    //   <option selected<
    // 解説:
    //   これは多くの場合 HTML 要素の誤字によるものですが,ルーンによってはタグ名,属性名,引用符で囲まれていない属性値で禁止されているものがあります。
    //   パーサのあいまいさをつつくことができるからです。
    //   すべての属性を引用符で囲むことが最善の策です。
    ErrBadHTML

    // ErrBranchEnd: "{{if}} branches end in different contexts"
    //   ("{{if}} の分岐は異なるコンテキストで終わります")
    // 例:
    //   {{if .C}}<a href="{{end}}{{.X}}
    // 解説:
    //   パッケージ html/template は
    //   {{if}} ,{{range}},{{with}} の各パスを静的に調べ,
    //   次のパイプラインをエスケープします。
    //   例では, {{.X}} は HTML テキストノード,
    //   または HTML の URL プレフィックスである可能性があるため,あいまいです。
    //   {{.X}} のコンテキストを使ってエスケープする方法を解析しますが,
    //   コンテキストは実行時の値 {{.C}} に依存するため,
    //   静的に知ることができません。
    //
    //   通常,問題は引用符や山括弧がないことにあります。
    //   あるいは, 2 つのコンテキストを if, range ,または with の異なるブランチに入れるようにリファクタリングすることで回避できます。
    //   問題が,決して空にならないコレクション上の {{range}} にある場合は,ダミーの {{else}} を追加すると効果的です。
    ErrBranchEnd

    // ErrEndContext: "... ends in a non-text context: ..."
    //   ("... はテキストではないコンテキストで終わります: ...")
    // 例:
    //   <div
    //   <div title="no close quote>
    //   <script>f()
    // 解説:
    //   実行されたテンプレートは HTML の DocumentFragment を生成するはずです。
    //   タグを閉じずに終了するとこのエラーが発生します。
    //   HTML コンテキストで使用してはならないテンプレートや
    //   不完全なフラグメントを生成するテンプレートは直接実行してはなりません。
    //
    //   {{define "main"}} <script>{{template "helper"}}</script> {{end}}
    //   {{define "helper"}} document.write(' <div title=" ') {{end}}
    //
    //   "helper" は有効な文書の断片を生成しないので,直接実行してはなりません。
    ErrEndContext

    // ErrNoSuchTemplate: "no such template ..."
    //   ("テンプレート ... がありません")
    // Examples:
    //   {{define "main"}}<div {{template "attrs"}}>{{end}}
    //   {{define "attrs"}}href="{{.URL}}"{{end}}
    // 解説
    //   パッケージ html/template はテンプレート呼び出しを調べてコンテキストを計算します。
    //   ここで "attrs" の {{.URL}} は "main" から呼び出されたときに URL として扱われる必要がありますが, "main" が解析されるときに "attrs" が定義されていないとこのエラーになります。
    ErrNoSuchTemplate

    // ErrOutputContext: "cannot compute output context for template ..."
    //   ("テンプレート ... の出力コンテキストを計算できません")
    // Examples:
    //   {{define "t"}}{{if .T}}{{template "t" .T}}{{end}}{{.H}}",{{end}}
    // 解説
    //   再帰的テンプレートは,それが始まるのと同じコンテキストで終わらず,信頼できる出力コンテキストは計算できません。
    //   名前付きテンプレートの中でタイプミスを探してください。
    //   テンプレートが名前付きの開始コンテキストで呼び出されてはいけない場合,予期しないコンテキストでそのテンプレートへの呼び出しがないか探します。
    //   再帰的テンプレートを再帰的にならないようにリファクタリングするのがよいかもしれません。
    ErrOutputContext

    // ErrPartialCharset: "unfinished JS regexp charset in ..."
    //   ("... で未完成の JS 正規表現")
    // 例:
    //     <script>var pattern = /foo[{{.Chars}}]/</script>
    // 解説
    //   パッケージ html/template は正規表現リテラル文字への補間をサポートしていません。
    ErrPartialCharset

    // ErrPartialEscape: "unfinished escape sequence in ..."
    //   ("未完成のエスケープシーケンス ...")
    // 例:
    //   <script>alert("\{{.X}}")</script>
    // 解説
    //   パッケージ html/template はバックスラッシュに続く動作をサポートしていません。
    //   これは通常エラーであり,より良い解決策があります。たとえば,
    //     <script>alert("{{.X}}")</script>
    //   は動作します。
    //   {{.X}} が "xA0" のような部分的なエスケープシーケンスの場合は,
    //   シーケンス全体を安全なコンテンツとしてマークします。 JSStr(`\xA0`)
    ErrPartialEscape

    // ErrRangeLoopReentry: "on range loop re-entry: ..."
    // 例:
    //   <script>var x = [{{range .}}'{{.}},{{end}}]</script>
    // 解説
    //   range を反復すると,前のパスとは異なるコンテキストとなるため,
    //   1 つのコンテキストはありません。
    //   この例では,引用符が欠落しているため, {{.}} が JS 文字列内にあるのか JS 値コンテキスト内にあるのかは明確ではありません。
    //   2 回目の反復では,次のようになります。
    //
    //     <script>var x = ['firstValue,'secondValue]</script>
    ErrRangeLoopReentry

    // ErrSlashAmbig: '/' could start a division or regexp.
    // 例:
    //   <script>
    //     {{if .C}}var x = 1{{end}}
    //     /-{{.N}}/i.test(x) ? doThis : doThat();
    //   </script>
    // 解説
    //   上の例では,最初の '/' が数学的除算演算子である `var x = 1/-2/i.test(s)...` ,あるいは,最初の '/' が正規表現リテラルを開始する `/-2/i.test(s)` を生成します。
    //   分岐中に欠けているセミコロンを探し,どの解釈を意図しているかを明確にするために括弧を追加する必要があるかもしれません。
    ErrSlashAmbig

    // ErrPredefinedEscaper: "predefined escaper ... disallowed in template"
    // 例:
    //   <div class={{. | html}}>Hello<div>
    // 解説
    //   パッケージ html/template はすでにコードインジェクションに対して安全な HTML 出力を生成するためにすべてのパイプラインをコンテキスト的にエスケープします。
    //   定義済みエスケープ "html" または "urlquery" を使用して手動でパイプライン出力をエスケープすることは不要であり, Go 1.8 以前のエスケープパイプライン出力の正確性または安全性に影響を与える可能性があります。
    //
    //   例のような場合,このエラーはパイプラインから事前定義されたエスケープを削除し,コンテキストオートエスケープにパイプラインのエスケープを処理させることで解決できます。
    //   他の例として,事前定義されたエスケープがパイプラインの途中にあり,後続のコマンドがエスケープされた入力を期待する場合。たとえば,
    //     {{.X | html | makeALink}}
    //   ここで,makeALink が
    //     return `<a href="`+input+`">link</a>`
    //   である場合,コンテキストオートエスケープを利用するために周囲のテンプレートをリファクタリングすることを検討してください。つまり,
    //     <a href="{{.X}}">link</a>
    //   とします。
    //
    //   Go 1.9 以降への移行を容易にするために, "html" と "urlquery" はパイプラインの最後のコマンドとして引き続き許可されます。
    //   ただし,引用符で囲まれていない属性値のコンテキストでパイプラインが発生した場合, "html" は許可されません。
    //   新しいテンプレートでは, "html" と "urlquery" を完全に使用しないでください。
    ErrPredefinedEscaper
)

type FuncMap

FuncMap は,名前から関数へのマッピングを定義するマップの型です。 各関数は, 1 つの戻り値,または 2 つ目の戻り値に 2 つの型エラーがあるかのいずれかでなければなりません。 その場合, 2 番目の (エラー) 引数が実行中に nil 以外に評価されると,実行は終了し, Execute はそのエラーを返します。 FuncMap は "text/template" の FuncMap と同じ基本型を持ち,ここにコピーされるのでクライアントは "text/template" をインポートする必要はありません。

type FuncMap map[string]interface{}

type HTML

HTML は,既知の安全な HTML ドキュメントの一部をカプセル化したものです。 第三者からの HTML や,閉じられていないタグやコメントがある HTML には使用しないでください。 健全な HTML サニタイザーとこのパッケージによってエスケープされたテンプレートの出力は HTML で使用するのに問題ありません。

この型を使用するとセキュリティ上のリスクが生じます。 カプセル化されたコンテンツはテンプレートの出力にそのまま含まれるため,信頼できるソースから取得する必要があります。

type HTML string

type HTMLAttr

HTMLAttr は信頼できるソースからの HTML 属性,例えば `dir="ltr"` をカプセル化します。

この型を使用するとセキュリティ上のリスクが生じます。 カプセル化されたコンテンツはテンプレートの出力にそのまま含まれるため,信頼できるソースから取得する必要があります。

type HTMLAttr string

type JS

JS は既知の安全な EcmaScript5 式,例えば `(x + y * z())` をカプセル化します。 テンプレート作成者は,型付き式が意図した優先順位を破らないこと,および "{ foo: bar() }\n['foo']()" のような式を渡されたときに,ステートメント/式のあいまいさがないことに責任を負います。 こうした式は,有効な式でも有効なプログラムでもあり,まったく異なる意味を持ちます。

この型を使用するとセキュリティ上のリスクが生じます。 カプセル化されたコンテンツはテンプレートの出力にそのまま含まれるため,信頼できるソースから取得する必要があります。

JS を使用して有効だが信頼できない JSON を含めることは安全ではありません。 安全な代替策は, json.Unmarshal を使用して JSON を解析してから,結果のオブジェクトをテンプレートに渡すことです。 JavaScript のコンテキストで表示されると, JSON はサニタイズされた JSON に変換されます。

type JS string

type JSStr

JSStr は, JavaScript 式の引用符の間に埋め込まれることを意図した一連の文字をカプセル化します。 文字列は,一連の StringCharacter と一致する必要があります。

StringCharacter :: SourceCharacter but not `\` or LineTerminator
                 | EscapeSequence

LineContinuations は許可されていないことに注意してください。 JSStr("foo\\nbar") は問題ありませんが, JSStr("foo\\\nbar") は許可されていません。

この型を使用するとセキュリティ上のリスクが生じます。 カプセル化されたコンテンツはテンプレートの出力にそのまま含まれるため,信頼できるソースから取得する必要があります。

type JSStr string

type Srcset 1.10

Srcset は,既知の安全な srcset 属性をカプセル化したものです (https://w3c.github.io/html/semantics-embedded-content.html#element-attrdef-img-srcset を参照) 。

この型を使用するとセキュリティ上のリスクが生じます。 カプセル化されたコンテンツはテンプレートの出力にそのまま含まれるため,信頼できるソースから取得する必要があります。

type Srcset string

type Template

Template は安全な HTML 文書の断片を生成する "text/template" からの特殊なテンプレートです。

type Template struct {

    // 内部のテンプレートの解析ツリー。
    // HTML 対応になるように更新されました。
    Tree *parse.Tree // Go 1.2
    // エクスポートされていないフィールドがあります
}

例 (Block)

コード:

const (
    master  = `Names:{{block "list" .}}{{"\n"}}{{range .}}{{println "-" .}}{{end}}{{end}}`
    overlay = `{{define "list"}} {{join . ", "}}{{end}} `
)
var (
    funcs     = template.FuncMap{"join": strings.Join}
    guardians = []string{"Gamora", "Groot", "Nebula", "Rocket", "Star-Lord"}
)
masterTmpl, err := template.New("master").Funcs(funcs).Parse(master)
if err != nil {
    log.Fatal(err)
}
overlayTmpl, err := template.Must(masterTmpl.Clone()).Parse(overlay)
if err != nil {
    log.Fatal(err)
}
if err := masterTmpl.Execute(os.Stdout, guardians); err != nil {
    log.Fatal(err)
}
if err := overlayTmpl.Execute(os.Stdout, guardians); err != nil {
    log.Fatal(err)
}

出力:

Names:
- Gamora
- Groot
- Nebula
- Rocket
- Star-Lord
Names: Gamora, Groot, Nebula, Rocket, Star-Lord

例 (Glob)

Here we demonstrate loading a set of templates from a directory.

コード:

// Here we create a temporary directory and populate it with our sample
// template definition files; usually the template files would already
// exist in some location known to the program.
dir := createTestDir([]templateFile{
    // T0.tmpl is a plain template file that just invokes T1.
    {"T0.tmpl", `T0 invokes T1: ({{template "T1"}})`},
    // T1.tmpl defines a template, T1 that invokes T2.
    {"T1.tmpl", `{{define "T1"}}T1 invokes T2: ({{template "T2"}}){{end}}`},
    // T2.tmpl defines a template T2.
    {"T2.tmpl", `{{define "T2"}}This is T2{{end}}`},
})
// Clean up after the test; another quirk of running as an example.
defer os.RemoveAll(dir)

// pattern is the glob pattern used to find all the template files.
pattern := filepath.Join(dir, "*.tmpl")

// Here starts the example proper.
// T0.tmpl is the first name matched, so it becomes the starting template,
// the value returned by ParseGlob.
tmpl := template.Must(template.ParseGlob(pattern))

err := tmpl.Execute(os.Stdout, nil)
if err != nil {
    log.Fatalf("template execution: %s", err)
}

出力:

T0 invokes T1: (T1 invokes T2: (This is T2))

例 (Helpers)

This example demonstrates one way to share some templates and use them in different contexts. In this variant we add multiple driver templates by hand to an existing bundle of templates.

コード:

// Here we create a temporary directory and populate it with our sample
// template definition files; usually the template files would already
// exist in some location known to the program.
dir := createTestDir([]templateFile{
    // T1.tmpl defines a template, T1 that invokes T2.
    {"T1.tmpl", `{{define "T1"}}T1 invokes T2: ({{template "T2"}}){{end}}`},
    // T2.tmpl defines a template T2.
    {"T2.tmpl", `{{define "T2"}}This is T2{{end}}`},
})
// Clean up after the test; another quirk of running as an example.
defer os.RemoveAll(dir)

// pattern is the glob pattern used to find all the template files.
pattern := filepath.Join(dir, "*.tmpl")

// Here starts the example proper.
// Load the helpers.
templates := template.Must(template.ParseGlob(pattern))
// Add one driver template to the bunch; we do this with an explicit template definition.
_, err := templates.Parse("{{define `driver1`}}Driver 1 calls T1: ({{template `T1`}})\n{{end}}")
if err != nil {
    log.Fatal("parsing driver1: ", err)
}
// Add another driver template.
_, err = templates.Parse("{{define `driver2`}}Driver 2 calls T2: ({{template `T2`}})\n{{end}}")
if err != nil {
    log.Fatal("parsing driver2: ", err)
}
// We load all the templates before execution. This package does not require
// that behavior but html/template's escaping does, so it's a good habit.
err = templates.ExecuteTemplate(os.Stdout, "driver1", nil)
if err != nil {
    log.Fatalf("driver1 execution: %s", err)
}
err = templates.ExecuteTemplate(os.Stdout, "driver2", nil)
if err != nil {
    log.Fatalf("driver2 execution: %s", err)
}

出力:

Driver 1 calls T1: (T1 invokes T2: (This is T2))
Driver 2 calls T2: (This is T2)

例 (Parsefiles)

Here we demonstrate loading a set of templates from files in different directories

コード:

// Here we create different temporary directories and populate them with our sample
// template definition files; usually the template files would already
// exist in some location known to the program.
dir1 := createTestDir([]templateFile{
    // T1.tmpl is a plain template file that just invokes T2.
    {"T1.tmpl", `T1 invokes T2: ({{template "T2"}})`},
})

dir2 := createTestDir([]templateFile{
    // T2.tmpl defines a template T2.
    {"T2.tmpl", `{{define "T2"}}This is T2{{end}}`},
})

// Clean up after the test; another quirk of running as an example.
defer func(dirs ...string) {
    for _, dir := range dirs {
        os.RemoveAll(dir)
    }
}(dir1, dir2)

// Here starts the example proper.
// Let's just parse only dir1/T0 and dir2/T2
paths := []string{
    filepath.Join(dir1, "T1.tmpl"),
    filepath.Join(dir2, "T2.tmpl"),
}
tmpl := template.Must(template.ParseFiles(paths...))

err := tmpl.Execute(os.Stdout, nil)
if err != nil {
    log.Fatalf("template execution: %s", err)
}

出力:

T1 invokes T2: (This is T2)

例 (Share)

This example demonstrates how to use one group of driver templates with distinct sets of helper templates.

コード:

// Here we create a temporary directory and populate it with our sample
// template definition files; usually the template files would already
// exist in some location known to the program.
dir := createTestDir([]templateFile{
    // T0.tmpl is a plain template file that just invokes T1.
    {"T0.tmpl", "T0 ({{.}} version) invokes T1: ({{template `T1`}})\n"},
    // T1.tmpl defines a template, T1 that invokes T2. Note T2 is not defined
    {"T1.tmpl", `{{define "T1"}}T1 invokes T2: ({{template "T2"}}){{end}}`},
})
// Clean up after the test; another quirk of running as an example.
defer os.RemoveAll(dir)

// pattern is the glob pattern used to find all the template files.
pattern := filepath.Join(dir, "*.tmpl")

// Here starts the example proper.
// Load the drivers.
drivers := template.Must(template.ParseGlob(pattern))

// We must define an implementation of the T2 template. First we clone
// the drivers, then add a definition of T2 to the template name space.

// 1. Clone the helper set to create a new name space from which to run them.
first, err := drivers.Clone()
if err != nil {
    log.Fatal("cloning helpers: ", err)
}
// 2. Define T2, version A, and parse it.
_, err = first.Parse("{{define `T2`}}T2, version A{{end}}")
if err != nil {
    log.Fatal("parsing T2: ", err)
}

// Now repeat the whole thing, using a different version of T2.
// 1. Clone the drivers.
second, err := drivers.Clone()
if err != nil {
    log.Fatal("cloning drivers: ", err)
}
// 2. Define T2, version B, and parse it.
_, err = second.Parse("{{define `T2`}}T2, version B{{end}}")
if err != nil {
    log.Fatal("parsing T2: ", err)
}

// Execute the templates in the reverse order to verify the
// first is unaffected by the second.
err = second.ExecuteTemplate(os.Stdout, "T0.tmpl", "second")
if err != nil {
    log.Fatalf("second execution: %s", err)
}
err = first.ExecuteTemplate(os.Stdout, "T0.tmpl", "first")
if err != nil {
    log.Fatalf("first: execution: %s", err)
}

出力:

T0 (second version) invokes T1: (T1 invokes T2: (T2, version B))
T0 (first version) invokes T1: (T1 invokes T2: (T2, version A))

func Must

func Must(t *Template, err error) *Template

Must は,(*Template, error) を返す関数への呼び出しをラップするヘルパーで,エラーが nil 以外であればパニックします。 次のような変数の初期化での使用を目的としています。

var t = template.Must(template.New("name").Parse("html"))

func New

func New(name string) *Template

New は与えられた名前で新しい HTML テンプレートを割り当てます。

func ParseFiles

func ParseFiles(filenames ...string) (*Template, error)

ParseFiles は新しいテンプレートを作成し,指定されたファイルからテンプレート定義を解析します。 返されたテンプレートの名前は最初のファイルの (ベース) 名と (解析された) 内容を持ちます。 少なくとも 1 つのファイルがなければなりません。 エラーが発生した場合,解析は停止し,返された *Template は nil です。

異なるディレクトリにある同じ名前の複数のファイルを解析する場合,最後に挙げたものが結果として得られます。 たとえば, ParseFiles("a/foo", "b/foo") は "b/foo" を "foo" という名前のテンプレートとして格納しますが, "a/foo" は使用できません。

func ParseGlob

func ParseGlob(pattern string) (*Template, error)

ParseGlob creates a new Template and parses the template definitions from the files identified by the pattern. The files are matched according to the semantics of filepath.Match, and the pattern must match at least one file. The returned template will have the (base) name and (parsed) contents of the first file matched by the pattern. ParseGlob is equivalent to calling ParseFiles with the list of files matched by the pattern.

異なるディレクトリにある同じ名前の複数のファイルを解析する場合,最後に挙げたものが結果として得られます。

func (*Template) AddParseTree

func (t *Template) AddParseTree(name string, tree *parse.Tree) (*Template, error)

AddParseTree は名前と解析木で新しいテンプレートを作成し,それを t と関連付ける。

t または関連するテンプレートが既に実行されている場合はエラーを返します。

func (*Template) Clone

func (t *Template) Clone() (*Template, error)

Clone は,関連付けられているすべてのテンプレートを含むテンプレートの複製を返します。 実際の表現はコピーされませんが,関連付けられたテンプレートの名前空間はコピーされるため,コピー内でさらに Parse を呼び出すと,コピーにテンプレートが追加されますが,元のテンプレートには追加されません。 クローンは,クローン作成後にバリアントを追加することによって,一般的なテンプレートを準備し,それらを他のテンプレートのバリアント定義とともに使用するために使用できます。

t がすでに実行されている場合はエラーを返します。

func (*Template) DefinedTemplates 1.6

func (t *Template) DefinedTemplates() string

DefinedTemplates は,先頭に "; defined templates are: " という文字列を付けた,定義済みのテンプレートをリストした文字列を返します。 何もない場合は,空の文字列を返します。 エラーメッセージを生成するために使用されます。

func (*Template) Delims

func (t *Template) Delims(left, right string) *Template

Delims は,その後の Parse, ParseFiles, または ParseGlob の呼び出しで使用されるアクション区切り文字を指定された文字列に設定します。 ネストしたテンプレート定義は設定を継承します。 空の区切り文字は,対応するデフォルトの {{ または }} を表します。 戻り値はテンプレートなので,呼び出しは連鎖できます。

コード:

const text = "<<.Greeting>> {{.Name}}"

data := struct {
    Greeting string
    Name     string
}{
    Greeting: "Hello",
    Name:     "Joe",
}

t := template.Must(template.New("tpl").Delims("<<", ">>").Parse(text))

err := t.Execute(os.Stdout, data)
if err != nil {
    log.Fatal(err)
}

出力:

Hello {{.Name}}

func (*Template) Execute

func (t *Template) Execute(wr io.Writer, data interface{}) error

Execute は,解析されたテンプレートを指定されたデータオブジェクトに適用し,出力を wr に書き込みます。 テンプレートの実行中またはその出力の書き込み中にエラーが発生した場合,実行は停止しますが,部分的な結果はすでに出力ライターに書き込まれている可能性があります。 並列実行が Writer を共有する場合,出力は綴じ込まれますが,インターリーブされますが,テンプレートは安全に並列実行されます。

func (*Template) ExecuteTemplate

func (t *Template) ExecuteTemplate(wr io.Writer, name string, data interface{}) error

ExecuteTemplate は,指定された名前を持つ t に関連付けられたテンプレートを指定されたデータオブジェクトに適用し,出力を wr に書き込みます。 テンプレートの実行中またはその出力の書き込み中にエラーが発生した場合,実行は停止しますが,部分的な結果はすでに出力ライターに書き込まれている可能性があります。 並列実行が Writer を共有する場合,出力はインターリーブされますが,テンプレートは安全に並列実行されます。

func (*Template) Funcs

func (t *Template) Funcs(funcMap FuncMap) *Template

Funcs は引数マップの要素をテンプレートの関数マップに追加します。 テンプレートが解析される前に呼び出されなければなりません。 マップ内の値が適切な戻り型を持つ関数ではない場合はパニックします。 ただし,マップの要素を上書きすることは正当です。 戻り値はテンプレートなので,呼び出しは連鎖できます。

func (*Template) Lookup

func (t *Template) Lookup(name string) *Template

Lookup は, t に関連付けられた特定の名前のテンプレートを返します。 そのようなテンプレートがない場合は nil を返します。

func (*Template) Name

func (t *Template) Name() string

Name はテンプレートの名前を返します。

func (*Template) New

func (t *Template) New(name string) *Template

New は,与えられたものと同じ区切り文字を持つ新しい HTML テンプレートを割り当てます。 推移的な関連付けにより, 1 つのテンプレートが {{template}} アクションを使用して別のテンプレートを呼び出すことができます。

指定された名前のテンプレートが既に存在する場合は,新しい HTML テンプレートによって置き換えられます。 既存のテンプレートはリセットされ, t との関連付けが解除されます。

func (*Template) Option 1.5

func (t *Template) Option(opt ...string) *Template

Option はテンプレートのオプションを設定します。 Option は単純な文字列か "key=value" のどちらかの文字列で記述されます。 オプション文字列には,最大で 1 つの等号を含めることができます。 オプション文字列が認識されない,または無効な場合, Option はパニックします。

既知のオプション:

missingkey: マップがマップに存在しないキーでインデックス付けされている場合の実行中の動作を制御します。

"missingkey=default" または "missingkey=invalid"
	デフォルトの動作: 何もしないで実行を続行します。
	出力した場合,インデックス操作の結果は文字列 "<no value>" です。
"missingkey=zero"
	操作はマップ型の要素のゼロ値を返します。
"missingkey=error"
	実行はエラーで直ちに停止します。

func (*Template) Parse

func (t *Template) Parse(text string) (*Template, error)

Parse は,テキストを t のテンプレート本体として解析します。 テキスト内の名前付きテンプレート定義 ({{define ...}} または {{block ...}} ステートメント) は, t に関連する追加のテンプレートを定義し, t の定義から削除されます。

Template は,最初に t で Execute するか関連テンプレートを使用する前であれば, Parse をまた呼び出すときに再定義できます。 空白とコメントのみを含むボディを持つテンプレート定義は空と見なされ,既存のテンプレートのボディを置き換えることはありません。 これにより, Parse を使用して,メインのテンプレート本体を上書きせずに新しい名前付きテンプレート定義を追加できます。

func (*Template) ParseFiles

func (t *Template) ParseFiles(filenames ...string) (*Template, error)

ParseFiles は名前付きファイルを解析し,結果のテンプレートを t に関連付けます。 エラーが発生した場合,解析は停止し,返されたテンプレートは nil です。 それ以外の場合は t です。 少なくとも 1 つのファイルがなければなりません。

異なるディレクトリにある同じ名前の複数のファイルを解析する場合,最後に挙げたものが結果として得られます。

t または関連するテンプレートが既に実行されている場合, ParseFiles はエラーを返します。

func (*Template) ParseGlob

func (t *Template) ParseGlob(pattern string) (*Template, error)

ParseGlob parses the template definitions in the files identified by the pattern and associates the resulting templates with t. The files are matched according to the semantics of filepath.Match, and the pattern must match at least one file. ParseGlob is equivalent to calling t.ParseFiles with the list of files matched by the pattern.

異なるディレクトリにある同じ名前の複数のファイルを解析する場合,最後に挙げたものが結果として得られます。

t または関連するテンプレートが既に実行されている場合, ParseGlob はエラーを返します。

func (*Template) Templates

func (t *Template) Templates() []*Template

Templates は, t 自体を含む, t に関連付けられたテンプレートのスライスを返します。

type URL

URL は,既知の安全な URL または URL サブ文字列をカプセル化します (RFC 3986 を参照) 。 信頼できるソースからの `javascript:checkThatFormNotEditedBeforeLeavingPage()` のような URL はページに入るべきですが,デフォルトでは動的な `javascript:` URL は頻繁に悪用されるインジェクションベクトルなので除外されます。

この型を使用するとセキュリティ上のリスクが生じます。 カプセル化されたコンテンツはテンプレートの出力にそのまま含まれるため,信頼できるソースから取得する必要があります。

type URL string