Sinatra での HAML::Template.options の設定に悩む

Sinatra で以下のように書いたとき

Haml::Template::options[:escape_html] = true

このようなエラーが出ることがある

uninitialized constant Haml::Template (NameError)

ものによって出たり出なかったりなのでどういうタイミングでどう書いてあったら期待通りに動くかわからなくて、ざっと Sinatra::Base の set 呼んでるところあたり見て、以下で大丈夫な気がしたので書いてみたところ、問題ないようだ。

set :haml, :escape_html => true

結局、なんで上手く動いたのか、なんでダメだったのが全くわかってないし、やっぱり俺は Ruby 全然使えてないなあと改めて思った。

まだ、ソロでフレームワーク使えるレベルじゃないのかな。

文化だったり文脈だったりがもっとわかれば、突破口が開けるんじゃないかなとおもったりもするけど、言語仕様自体、なんとなくで使えちゃってるし、ちゃんとしたところわかってないよなあとおもったりする

Facebook ではてなスター

Facebookはてなスターを使いたかったんだけど、 SiteConfig 書いただけだと HatenaStar Everywhere が満足に動かなかったので、ちょこっと user.js 等を書いたりした。


いるもの


あと Star つけたときに like も押したかったのでこういうのも書いてみた。

今のところ Greasemonkey でしか動かないけど、はてなで siteconfig.json 取得する際に Access-Control-Allow-Origin ヘッダちゃんと吐いてくれれば Google Chrome とかでも動くようになるんじゃないかな。

Ciphers をサイト毎に管理する

SSH の平文回復できる脆弱性対応として、Ciphers ガチガチにしていたところ、仕事で使うサーバの一部の OpenSSH が古くて aes*-ctr だと通らなくて*1少し困った。
ちょっと試してみたところ Host 毎で Ciphers 設定できたので忘れないようにメモしておく

ServerAliveInterval 60
ServerAliveCountMax 60

Host oldssh
  hostname 192.0.2.1
  User example
  IdentityFile ~/.ssh/oldssh.pem
  Ciphers aes128-cbc,3des-cbc,blowfish-cbc
Host !oldssh
  Ciphers aes128-ctr,aes256-ctr,arcfour256,arcfour,aes128-cbc,aes256-cbc

*1:具体的には Ruby の Net::SSH でこける

安全な HTMLDocument の生成方法について

何が危ないのか

img.onerror や img.onload は src 属性の内容が評価された段階で実行されるので、外部ソースに対して HTMLDocument を構築する際などで、意図していないタイミングでスクリプトが実行されるケースがある。
具体的には、以下のような場合。

var source = '<img src="not_found.jpg" onerror="alert(1)">';
var range = document.createRange();
range.createContextualFragment(source); // onerror が実行される
var img = document.createElement('img');
img.setAttribute('onerror', 'alert(1)');
img.src = 'not_found.jpg'; // onerror が実行される

各ブラウザの状況

では、どのようにすれば安全かということを nanto_vi さんの http://nanto.asablo.jp/blog/2009/10/29/4660197 で紹介されている各々の手法をもとに調べてみた。(関数名等はそちらのものを使っています)

  • ブラウザ先般
    • document から Range を生成して createContextualFragment を使用する際に doc.adoptNode を呼ぶのを忘れると img.onerror, img.onload が動く
    • document.createElement のとき img.onload, img.onerror が動く
  • Firefox
    • Range 生成時には document ではなく doc を使わないと createContextualFragment 実行時に img.onerror と img.onload が動くことがある
      • document の時の DocumentFragment に対する adoptNode の挙動がよくわからない
        • fragment 生成から adoptNode の間に重い処理(XMLHttpRequest を同期で走らせる等)を入れたらハンドラは実行されてしまう
        • Greasemonkey スクリプトだと重い処理がなくてもハンドラが実行されるときがある(eval が関係ある?)
    • createHTMLDocument_XSLT で作成した Document から作った img の addEventListener で付加した img.onerror, img.onload が動く
    • createHTMLDocument_cloneNode で作成した Document から作った img の addEventListener で付加した img.onerror が動く
  • Safari
    • createHTMLDocument_createDocument_DTD で Range 生成に doc 使うと NOT_SUPPORTED_ERR
    • createHTMLDocument_createDocument_DTD_NS のときだけ Range 生成に document ではなく doc を使わないと createContextualFragment 実行時に img.onerror と img.onload が動く
  • Chrome
    • createHTMLDocument_createDocument_DTD で Range 生成に doc 使うと NOT_SUPPORTED_ERR
  • Opera
    • DocumentFragment は adoptNode しなかった時点で Unhandled exception 発生
    • document.createRange を使って Range オブジェクトを生成しないとブラウザクラッシュ
    • createDocument を名前空間指定をせずに呼び出したとき以外は doc.createElement で img.onload, img.onerror が動く

まとめ

現象をざっと列挙したみたけど、すごくわかりづらかったので、ちょっと乱暴だけど簡単な落としどころにまとめてみる。

  • DocumentFragment 生成
    • DocumentFragment を生成したら doc.adoptNode を忘れない。
    • Opera 以外の Range 生成は document から直接作らない。
var range = doc.createRange();
range.selectNodeContents(doc.documentElement);
var range = document.createRange();
range.selectNodeContents(document.documentElement);
    • Firefox は createDocument, それ以外は createHTMLDocument 使う
  • Element 生成
    • img 生成は、onerror や onload の setAttribute や addEventListener をしない
      • createDocument(null, 'html', null) で作った Document から生成したりしてもたぶん大丈夫だけど、個別に要素作るケースだと入力値検証ちゃんとすべきだと思うので

雑感など

  • createContextualFragment 実行時に img.onerror が動くことに気付いて、まだ現象の整理も理解も出来てない状態で、これは Mozilla のバグなんじゃないのと Buzilla に投げてみたら、やっぱりあっさり WONTFIX で close でした。
    • https://bugzilla.mozilla.org/show_bug.cgi?id=557420
    • 英語力と技術力が足りなくて言いたいことが全然伝えられなかったけど、やっぱり Mozilla の挙動は不自然だと思う
    • Mozilla 勉強会のあとの飲み会でちょっと相談させて頂いたみなさん、投稿する際に英語ちょっとみてくれた @hajime, ありがとうございました。
  • adoptNode とか createContextualFragment での DocumentFragment が構築終了するタイミングとかどうなってるのかとかよくわからない
  • range 生成は doc 側に倒すべきなのか document 側に倒すべきなのかどっちなんだろう
  • img が Node 構築のタイミングで src の中身を取りに行くのはブラウザの仕様上理解出来る。でも、document に挿入される前に onload/onerror が走るのはちょっと怖いことだと思う。実装の都合だと思うけど。
    • 逆にそれを上手く使ってる JSDeferred などのライブラリは凄いと思った。
  • エントリ書くのにすごい時間掛かったけど全然わかりやすく書けなかった…

apply/call での継承の話

この件について。

継承というかスコープがわかりやすいというのもメリットだと思うけど、カプセル化しやすいのも大きなメリットかなと思う。

function Foo(){}
(function(){
  var bar = 'bar';
  this.bar = bar.toUpperCase();
  function baz () {
    console.log('baz');
  }
  this.baz = function() {
    baz();
    console.log('BAZ');
  }
}).call(Foo.prototype);

function Bar(){}
Bar.prototype = new Foo();
(function() {
  this.hoge = 'hoge';
  this.baz = function() {
    Foo.prototype.baz.call(this);
    baz(); // 実行時に ReferenceError
    console.log('Bar::baz');
  }
  this.bar = bar; // 評価時に ReferenceError
}).call(Bar.prototype);