Node.js を Proxy サーバー兼レイアウト変更ツールとして使用すると、毎回同じ問題に遭遇するのでそれの回避方法のまとめ。
Node.js-v0.6.2 で確認。いずれ解決されてバッドノウハウになるだろうと思われる。
作成 2011.11.27
更新 2011.11.27
更新 2011.11.27
Node.js の jsdom に読み込ませる HTML を安全化するノウハウ
サンプルコード全体
管理していないサイトから取得した html を jsdom へそのまま突っ込むと、未対応コードの問題で例外が発生する場合があり、期待通りの動作をする保証がない。
現段階のバージョンでは以下の問題が判明している。
また、// ~~~ の行を無差別に削除すると、スクリプトコメント以外の文字列も削除してしまうため、これらの問題を回避するようにしている。
現段階のバージョンでは以下の問題が判明している。
- 正規表現の複数行マッチに対応していない。
- javascript コードを含んだ HTML 文字列を jsdom に読み込ませると、読み込んだ時点で文字列内の javascript コードが実行される。
- 改行コードを無害な文字列へ変換する
- javascript コードを無害な文字列へ変換し、退避する
- jsdom で処理
- jsdom の処理結果の文字列に元の javascript コードを埋め戻す
- 改行コードをもとに戻す
また、// ~~~ の行を無差別に削除すると、スクリプトコメント以外の文字列も削除してしまうため、これらの問題を回避するようにしている。
// サンプル HTML データ
var str = '<html><head><script type="a"><!--\n'
+'a;\n'
+'//test\n'
+'aa;\n'
+'//---></script>\n'
+'<Script type="b">b;</script>\n'
+'</head><body></body></html>';
console.log('== 処理前 ==');
console.log(str);
var counter = 0;
var scrs = new Array();
var matches;
// 何度も同じ正規表現を使いまわすのでオブジェクトとして設定
var re = new RegExp('<script.+?</script>','i');
// 無害な文字列へ変換
// 現時点では RegExp.multiline の効果が無いので改行コードを消す必要がある
str = str.replace(/[ \t]*[\r\n]+/g,'<!--BR-->');
while(matches = str.match(re)){
scrs[counter] = matches[0];
str = str.replace(re,'<!--SCRIPT'+counter+'-->');
counter++;
}
console.log('\n== 無害化処理後 ==');
console.log(str);
// jsdom でレイアウト変更などを処理
var jsdom = require('jsdom');
var document = jsdom.jsdom(str);
var res = document.innerHTML;
// 元のコードを埋め戻す。
for(var k in scrs){
res = res.replace('<!--SCRIPT'+k+'-->', scrs[k]);
}
res = res.replace(/<!--BR-->/g,'\n');
console.log('\n== 埋戻し後 ==');
console.log(res);
実行結果
$ node sample.js == 処理前 == <html><head><script type="a"><!-- a; //test aa; //---></script> <Script type="b">b;</script> </head><body></body></html> == 無害化処理後 == <html><head><!--SCRIPT0--><!--BR--><!--SCRIPT1--><!--BR--></head><body></body></html> == 埋戻し後 == <html><head><script type="a"><!-- a; //test aa; //---></script> <Script type="b">b;</script> </head><body></body></html>
タグ: Node.js