railsでhtmlをsanitize gemでカスタマイズしてsanitizeしたい


## 前提

以下、[Sanitize gem: whitelistベースのsanitizerのインストール、設定と使い方](http://memo.yomukaku.net/entries/137)を参考にメモがてら記載しておきます。

## 概要
Sanitize gemを使用してwhitelistベースでhtmlタグをremoveしたり付け加えたりできます。
今回は、[rgrove / sanitize](https://github.com/rgrove/sanitize)を使っています。他に、[vjt / sanitize-rails](https://github.com/vjt/sanitize-rails)もあるみたいですが、こっちはわかりません。

>whitelistベースのサニタイズとは、「白（犯人ではない）」として特定のhtmlタグや属性を予め指定しておき、それらのタグのみのviewへの出力を許可する方式のサニタイズです。
>例えばp、a、ol、liのタグのみの出力を許可し、その他のタグは取り除いてしまうような方針です。
>Railsの世界では、このホワイトリストベースのサニタイズを行うpluginとしてその名もwhite_listというものがありましたが、Rails 3とともに使用するとdeprecation warningが多く出力されることや、1年以上メンテナンスされていないこと等で、Rails3では使いにくい状況になっています。



## インストール

    gem install sanitize

railsだったら、Gemfileに

    gem ‘sanitize’

を追加して

    bundle install

でいいです。

## このgemの特徴
>
* 設定が簡単。インラインまたは外部ファイルでオプション設定が可能です。
* きめ細かい設定が可能。許可する要素（htmlタグ: a、img等）、属性（class、id、src、style等）等を細かく指定することができます。
* View、model、controllerのどこでも使用可能。Railsの標準のエスケープのhelperは、基本的にviewの中のみで使用するもので、controllerやmodelの中ではその都度requireする必要があります。 一方sanitizeは一度ロードされればどこでも使用することができます。 最も一般的な用途としてviewのコードの中でのhtml escapeに使用できる他、modelの中で使用してユーザーが入力したhtmlを予め処理してからdbに永続化するような場合にも簡単に使用できます。
* Validなhtmlを出力してくれる。Sanitizeではnokogiriがhtml parserに使用されています。 このため、入力のhtmlが多少壊れたhtmlであったとしても、validなhtmlに修正して出力してくれます。
* 自由にフィルター（検索置換、正規化等々）を追加できる。単純にhtmlをescapeするだけではなく、目的に応じて入力を修正し、希望する出力をつくることができます。 例えばclass="foo"という属性と値の組を持つタグを全てspanタグに変更することや、一定の条件を満たすタグ全てに style="text-decoration: underline;" を追加して出力すること等々を容易に実現できます。 nokogiriベースであるため、難しい正規表現を書くことなくhtmlを扱うことができます。

## 使い方

### erbの場合

    <%=Sanitize.clean(対象のhtml文字列, オプションのhash) %>

### hamlの場合

    = Sanitize.clean(対象のhtml文字列, オプションのhash)

### hamlを使用していて、対象の文字列の中にpre等でコード等がある場合

    = find_and_preserve(Sanitize.clean(対象のhtml文字列, オプションのhash))
Sanitizeのcleanメソッドに対象となるhtml文字列（中身がhtmlのStringクラス）と、sanitizeの方針を指定したhashを与える方式です。

### 標準のオプション
RESTRICTED、BASIC、RELAXEDという3つの設定が用意されています。
オプションの指定方法としては、

* Sanitize::Config::RESTRICTED
* Sanitize::Config::BASIC
* Sanitize::Config::RELAXED

みたいに書けばいいです。

## 使用例

    html = '<b><a href="http://example.com/ class="foo" id="bar">example.com</a></b><img src="http://example.com/image.jpg />'

### Sanitize::Config::RESTRICTEDオプション

> b, em, i, strong, uタグの出力のみを許可し、その他のタグは取り除かれます（タグの子要素は出力されます）。

    $ html = '<b><a href="http://example.com/ class="foo" id="bar">example.com</a></b><img src="http://example.com/image.jpg />'
    $ Sanitize.clean(html, Sanitize::Config::RESTRICTED)
    # => '<b>example.com</b>'

> RESTRICTEDの設定では、リンク、イメージやブロック要素は出力されません。

### Sanitize::Config::BASICオプション

> 基本的なフォーマットタグ、リンク、リストのみを出力する設定です。

    $ html = '<b><a href="http://example.com/ class="foo" id="bar">example.com</a></b><img src="http://example.com/image.jpg />'
    $ Sanitize.clean(html, Sanitize::Config::BASIC)
    # => '<b><a href="http://example.com/ rel="nofollow">example.com</a></b>'

> リンクは出力されますが、rel="nofollow"が自動的に追加されます。 また、aタグのid属性とclass属性は値ごと削除されます。
> タグとしてはa, b, blockquote, br, cite, code, dd, dl, dt, em, i, li, ol, p, pre, q, small, strike, strong, sub, sup, u, ulが許可されます（詳しくは末尾の表を参照してください）。

### Sanitize::Config::RELAXEDオプション

> ほとんどのタグと属性が出力される緩やかなエスケープの設定です。

    $ html = '<b><a href="http://example.com/ class="foo" id="bar">example.com</a></b><img src="http://example.com/image.jpg />'
    $ Sanitize.clean(html, Sanitize::Config::BASIC)
    # => '<b><a href="http://example.com/>example.com</a></b><img src="http://example.com/image.jpg />'

> RESTRICTEDやBASICと異なり、imgタグも出力されます。また、BASICの場合はaタグにrel="nofollow"が追加されましたが、RELAXEDでは追加されません。

## カスタム設定
基本的に、

    Sanitize.clean(html, hash)

の形式でSanitizeのcleanメソッドに対象のhtml文字列とオプションのhashを指定して使用するのですが、最初から用意されている3つのオプションのデフォルト設定もsanitize gemの中のlib/configディレクトリから、それぞれ、

* restricted.rb
* basic.rb
* relaxed.rb

を読み込んでいます。
なので、

1. これらをいじるという方法
2. 別途新しくファイルを作って、optionとして引数に与える
3. まんまインラインでオプションを与える（非推奨）

という方法が考えられます。

### 1. これらをいじるという方法

Sanitizeのデフォルト設定として提供されているRESTRICTED、BASIC、RELAXEDの設定は、gemファイル中、

* lib/sanitize/config/restricted.rb
* lib/sanitize/config/basic.rb
* lib/sanitize/config/relaxed.rb

にあるので、例えば、Railsの環境でこれらをいじるには、アプリケーションルートディレクトリ以下にlib/sanitize/config/custom.rbのようなファイル名で設定ファイルを作成し、読みこみます。
例えば、こんな感じに書かれてるので、

    class Sanitize
      module Config
        RESTRICTED = {
          :elements => ['b', 'em', 'i', 'strong', 'u']
        }
      end
    end

これを、直接いじればいいです。


### 2. 別途新しくファイルを作って、optionとして引数に与える

引用元の記事からそのまんまです。

    # lib/sanitize/config/custom.rb

    class Sanitize
      module Config
        CUSTOM = {
          :elements => ['b', 'em', 'i', 'strong', ‘u’, ‘a’]
        }
      end
    end

これを使うには、

    Sanitize.clean(html, Sanitize::Config::CUSTOM)

こんな感じに書けばいいみたいです。

railsだと、上記pathに入れても読み込んでくれないと思うので、

    config/initializers/sanitize.rb

あたりに入れておけばいいのではないでしょうかね。

### 3. まんまインラインでオプションを与える（非推奨）

    Sanitize.clean(html, :elements => ['a', 'img', 'div'], :attributes => {'a' => ['href', 'title', 'target'], 'img' => ['src', 'alt', 'height', 'width', 'class'], 'div' => ['class', 'id'], :protocols => {'a' => {'href' => ['http', 'https', 'mailto', :relative]}})

こんな感じでオプションを与えることができます。
この例では、

* タグとしてa、img、div以外のものは許可しない
* タグの属性として、aタグにはhref、title、targetの属性のみを許可
* imgタグに対してsrc、alt、height、width、classの属性のみを許可
* divタグにはclassとid属性の出力を許可
* aタグのhrefに対するプロトコルとしてはhttp、https、mailtoのみを許可（例えばftp等は許可しない）。
=> :protocolの中にある:relativeは、相対的なパスの指定(http://example.com/foo/bar.html の代わりに /foo/bar.html という書き方) を許可するという意味

みたいな指定をしてます。

## Sanitizeの標準オプションのまとめ

    オプション名  キー  値
    RESTRICTED  elements   b, em, i, strong, u
    attributes  
    add_attributes  
    protocols 
    BASIC elements   a, b, blockquote, br, cite, code, dd, dl, dt, em, i, li, ol, p, pre, q, small, strike, strong, sub, sup, u, ul
    attributes   ‘a’ => [‘href’], 
    ‘blockquote’ => [‘cite’], 
    ‘q’ => [‘cite’]
    add_attributes   a’ => {’rel’ => ’nofollow’}
    protocols  ‘a’ => {’href’ => [‘ftp’, ‘http’, ‘https’, ‘mailto’, :relative]}, 
    ‘blockquote’ => {’cite’ => [‘http’, ‘https’, :relative]}, 
    ‘q’ => {’cite’ => [‘http’, ‘https’, :relative]}
    RELAXED elements   a, b, blockquote, br, caption, cite, code, col, colgroup, dd, dl, dt, em, h1, h2, h3, h4, h5, h6, i, img, li, ol, p, pre, q, small, strike, strong, sub, sup, table, tbody, td, tfoot, th, thead, tr, u, ul
    attributes   ‘a’ => [‘href’, ‘title’], 
    ‘blockquote’ => [‘cite’], 
    ‘col’ => [‘span’, ‘width’], 
    ‘colgroup’ => [‘span’, ‘width’], 
    ‘img’ => [‘align’, ‘alt’, ‘height’, ‘src’, ‘title’, ‘width’],
    ‘ol’ => [‘start’, ‘type’], 
    ‘q’ => [‘cite’], 
    ‘table’ => [‘summary’, ‘width’], 
    ‘td’ => [‘abbr’, ‘axis’, ‘colspan’, ‘rowspan’, ‘width’],
    ‘th’ => [‘abbr’, ‘axis’, ‘colspan’, ‘rowspan’, ‘scope’, ‘width’], 
    ‘ul’ => [‘type’]
    add_attributes  
    protocols  ‘a’ => {’href’ => [‘ftp’, ‘http’, ‘https’, ‘mailto’, :relative]}, 
    ‘blockquote’ => {’cite’ => [‘http’, ‘https’, :relative]}, 
    ‘img’ => {’src’ => [‘http’, ‘https’, :relative]}, 
    ‘q’ => {’cite’ => [‘http’, ‘https’, :relative]}

## 以上です

モトの記事、
[Sanitize gem: whitelistベースのsanitizerのインストール、設定と使い方](http://memo.yomukaku.net/entries/137)
も見てみてください :)