evalとFunctionを使わず文字列をJSコードとして実行する

以下のコードは古のスーパーハカーが書いたと言われる、SNSでシェアしただけで行政機関の執行を受けるほどの危険なコードらしい。

for(;;) alert('何回閉じても無駄ですよ~');

なぜこのコードが危険なのか私には説明できないのだが、そんなコードを実行可能なスクリプトのまま扱うのは良くない。文字列にしてしまおう。

const src = "for(;;) alert('何回閉じても無駄ですよ~')";

とりあえずこれで安心と言いたいところだが、ご存知のようにJavaScriptにはevalやFunctionという文字列をスクリプトとして動的に実行する仕組みがある。

eval(src);

(Function(src))();

実はこれ以外にも文字列を実行できる第3の方法がある。文字列をObject URLまたはData URIに変換し、これを動的生成したscript要素のsrcにセットして、DOMツリーに追加するという方法だ。

Object URLの場合

const src = "for(;;) alert('何回閉じても無駄ですよ~')";
const objURL = URL.createObjectURL(new Blob([src], {type: 'application/javascript'}));

const s = document.createElement('script');
s.src = objURL;
document.querySelector("head").appendChild(s);

Data URIの場合

const src = "for(;;) alert('何回閉じても無駄ですよ~')";
const fr = new FileReader();
fr.onload = (e) => {
    const dataURI = e.target.result;
    const s = document.createElement('script');
    s.src = dataURI;
    document.querySelector("head").appendChild(s);
};
fr.readAsDataURL(new Blob([src], {type: 'application/javascript'}));

Node.jsのテストツールAVA

最近のNode界隈ではAVAというテストツールがイケているという噂なので試した。

GitHub - avajs/ava: 🚀 Testing can be a drag. AVA helps you get it done.

import test from 'ava';

test('foo', t => {
    t.pass();
});

test('bar', async t => {
    const bar = Promise.resolve('bar');
    t.is(await bar, 'bar');
});

import、ES Moduleだ。

ES Moduleはtc39のstage 3で、Nodeではv11で試験的に採用されているのみ。AVAは最新のJS文法で書けるようにするためbabelでトランスパイルして実行いるらしい。そんな事をしているにも関わらず実行は高速。テストファイル毎にNodeプロセスを立て、並列、非同期で実するため高速なテスト実行が可能になっているらしい。

直列化することも一応できる。

test.serial('passes serially', t => {
    t.pass();
});

async function や promise に対応しているのも嬉しい。テスト関数をアロー関数で書けるのもマル(mochaはmochaオブジェクトのコンテキストでテスト関数を実行するため、コンテキストを変更できないアロー関数は非推奨)。そしてアサーションはfailするとPower Assertのような詳細なエラーレポートが出る。AVAの文法をeslintに認識させるためのプラグインも用意されており隙がない。

promise や async function 対応ばかりに注目してしまうが、既存のcallbackにも対応している。

test.cb('data.txt can be read', t => {
    fs.readFile('data.txt', t.end);
});

なかなか良さそうなのでしばらくこれを使おうと思う。