hexo-tag-google-photos-album をアップデートした

metascraperにおけるXSS対応と、その影響

自作のHexo向けプラグインであるhexo-tag-google-photos-albumでは、metascraperというnpmライブラリを利用しています。
Google Photosの共有リンクから辿って、いくつかのメタデータを抽出するためです。

そのmetascraperで、XSS脆弱性の報告がありました。(現時点では修正対応済)

その解決に伴い、hexo-tag-google-photos-albumでは、修正済みバージョンを利用するように、修正をしました。

  • metascraperのGitHubリポジトリにおけるイシュー
    • NPM Reporting as CSS Vulnerability #153
    • Add escape parameter to escape html data by default #168
  • npmのセキュリティ勧告
  • 報告の元になったレポート
  • isnot/hexo-tag-google-photos-album 修正の差分

修正後の確認

hexo-tag-google-photos-album$ npm audit

=== npm audit security report ===

found 0 vulnerabilities
in 2702 scanned packages

(snip)

blog$ npm view hexo-tag-google-photos-album version
1.0.5
blog$ hexo clean
(node:29044) [DEP0061] DeprecationWarning: fs.SyncWriteStream is deprecated.
INFO Deleted database.
INFO Deleted public folder.

特に問題なし。

東京都庁展望室

東京都庁 展望室のご利用案内

よく訪れている北展望室(半分がカフェ&レストランになっている方)が、来年1月まで改装工事中だそうで、珍しく南展望室でした。
南は、エレベーターのある側を除いたほぼ全周が見えるのが良いですね。

今回はあまり良い写真が撮れませんでした。
せっかく夕暮れ時だったのに、ちょうど日が落ちる間合いに、ボサッとして他のことに気を取られていたのが敗因。
あとは言い訳ですが、地平線付近の透明度があまり良くなかったのと、雲の具合があまり好みの感じでもなかったな😰。

Hexo 内部探訪 (6) DevToolsを使ったデバッグ

恥ずかしながらデバッカーを使い慣れておらず、こういった実行時コンテキストの解析に、手間取っていました。
今回は、DevToolsプロトコルを使って解析してみよう。

$ cd <path-to-your-blog-dir>
$ hexo clean
$ node --inspect-brk node_modules/hexo-cli/bin/hexo g --debug

このようにinspectを有効化してから、ブラウザ(Chromiumを利用)でchrome://inspectを開き、Remote Targetの中に現れるinspect thisから、デバッカーを開始する。

これだと、デバッカーに馴れていない私にとっては、見たいところにたどり着くまでに手間取ってしまった。

そこで、自身で開発中のプラグインの中に、任意のブレークポイントをあらかじめ挿入してしまう。

debugger;

そして

$ node --inspect node_modules/hexo-cli/bin/hexo g --debug

-brk の有無が異なる点に注意。

これで、ようやく調査がやりやすくなりました。
デバッカーの使い方は、これが詳しい。

コードをステップ実行する方法 | Tools for Web Developers | Google Developers

Hexo 内部探訪 (3) コンテキストとしての"hexo"変数

調査の開始にあたって

プラグイン(さしあたりTagプラグインを想定)の実装中にて参照できる変数やオブジェクトを、調べてまとめよう。

ドキュメント | Node.jsProcess 等もためになる。

ところで、Node.jsでは、グローバルな空間を参照するためのキーワードがある。
globalである。

また、次のようなやり方で参照する方法もあるようだ。

const { inspect } = require('util');
var g = Function('return this')();
console.log(inspect(g, { depth: 0 }));

これらを駆使しつつ、Hexoの場合には、私が見たところほとんど全ての場所で、hexoという、トップレベルの変数が見えるので、これを足がかりにしたい。
また、他にも使える要素がもしあるなら、確かめたい。

グローバル汚染はなかった

調査を進めてから理解できたことだが、Hexoではグローバル変数、またはグローバル・オブジェクトを使ってはいない。

何らかのプラグインで、グローバルを意図して使うのならばともかくとして、少なくともHexoの本体と言える範囲や、純正/バンドルされた各プラグインでは、グローバル変数は使わないようだ。いわゆる「グローバル汚染」はない。

プラグイン含めて各所で見かけるhexoという変数が存在し、トップレベルの階層にあるし、自ら呼び出しているわけでもないので、私は当初これがグローバル変数なのかな?と勘違いしていました。

しかしこれは、実際には、module.exportsの仕組みによって与えられたもので、各モジュール(概ね各jsファイルに相当)毎にそれぞれエクスポートされていました。

私はこれまでmodule.exportsについて理解していないままでしたが、今回勉強になりました。

(調査中)

Hexoオブジェクト

{
_events: [Object: null prototype] {},
_eventsCount: 0,
_maxListeners: undefined,
base_dir: '/home/naoto/www/MyBlog/',
public_dir: '/home/naoto/www/MyBlog/public/',
source_dir: '/home/naoto/www/MyBlog/source/',
plugin_dir: '/home/naoto/www/MyBlog/node_modules/',
script_dir: '/home/naoto/www/MyBlog/scripts/',
scaffold_dir: '/home/naoto/www/MyBlog/scaffolds/',
theme_dir: '/home/naoto/www/MyBlog/themes/stage/',
theme_script_dir: '/home/naoto/www/MyBlog/themes/stage/scripts/',
env: [Object],
extend: [Object],
config: [Object],
log: [Logger],
render: [Render],
route: [Router],
post: [Post],
scaffold: [Scaffold],
_dbLoaded: true,
_isGenerating: true,
database: [Database],
config_path: '/home/naoto/www/MyBlog/_config.yml',
source: [Source],
theme: [Theme],
locals: [Locals]
}

(進捗途中)

Hexo 内部探訪 (2) 準備 npm linkメモ

npm linkを使いこなすには

npm linkという、ありがたい仕組みがあって、非公開のモジュール(または開発中のバージョン)を、node_modulesの中に配置することができる。
基本的には、シンボリック・リンクである。

ここまでは、すぐに把握できたけど、実はうまく動作させるための、さらなる条件があった。

モジュールを読み込む側と、読み込まれる側のモジュールが、ファイルシステム上で同階層になるように配置されている必要があるのだった。

私の場合だけど、開発中のリポジトリは、ある場所にまとめてある。
例:~/repository/hexo-tag-google-photos-album/
これが、Hexoプラグインを開発する場所。

一方で、ただ使うだけのものは、別の場所にある。
Hexoで構成しているブログの作業場所は、こんな感じ。
例:~/www/MyBlog/

つまり、自分のブログに自分のプラグインを組み込むとすると、
例:~/www/MyBlog/node_modules/hexo-tag-google-photos-album/
ということになる。

しかしながら、npm link的には、上記の2箇所を直接リンクすることはできないわけだ。

なので、さらに回りくどいが、以下のようにして、解決してみる。

naoto@MyComputer:~/www/MyBlog$ cd ..
naoto@MyComputer:~/www$ ln -s ../repository/hexo-tag-google-photos-album
naoto@MyComputer:~/www$ ls -l hexo-tag-google-photos-album
lrwxrwxrwx 1 naoto naoto 42 5月 6 01:46 hexo-tag-google-photos-album -> ../repository/hexo-tag-google-photos-album

こうすることで、開発中のプラグイン実体は従来通りのままとしつつ、link可能な場所にも配置できた。

呼び出される側のlinkは、以下の通り。

naoto@MyComputer:~/www$ cd hexo-tag-google-photos-album
naoto@MyComputer:~/www/hexo-tag-google-photos-album$ sudo npm link
(snip)
/usr/lib/node_modules/hexo-tag-google-photos-album -> /home/naoto/repository/hexo-tag-google-photos-album

そして、呼び出す方で使う側の、つまりブログ側の(しつこい)linkはこの通り。

naoto@MyComputer:~/www/hexo-tag-google-photos-album$ cd ../MyBlog
naoto@MyComputer:~/www/MyBlog$ npm link hexo-tag-google-photos-album
/home/naoto/www/MyBlog/node_modules/hexo-tag-google-photos-album -> /usr/lib/node_modules/hexo-tag-google-photos-album -> /home/naoto/repository/hexo-tag-google-photos-album
naoto@MyComputer:~/www/MyBlog$ grep photos package.json
"hexo-tag-google-photos-album": "git+https://github.com/isnot/hexo-tag-google-photos-album.git",
naoto@MyComputer:~/www/MyBlog$ ls -l node_modules/hexo-tag-google-photos-album
lrwxrwxrwx 1 naoto naoto 64 5月 6 01:50 node_modules/hexo-tag-google-photos-album -> ../../../../../usr/lib/node_modules/hexo-tag-google-photos-album

これで、毎度git pushとnpm upを繰り返すこと無く、作業ができるようになる(はず)。
確かめてみよう。

開発中のプラグインの中に、以下を書き加える。

~/repository/hexo-tag-google-photos-album/index.js
const { inspect } = require('util');
var g = Function('return this')();
console.log(inspect(g, { depth: 0 }));
console.log(inspect(hexo, { depth: 0 }));

これを、コミットもせず、コピーもせず、動かしたい。

naoto@MyComputer:~/www/MyBlog$ hexo list page
Object [global] {
global: [Circular],
process: [process],
Buffer: [Function],
clearImmediate: [Function: clearImmediate],
clearInterval: [Function: clearInterval],
clearTimeout: [Function: clearTimeout],
setImmediate: [Function],
setInterval: [Function: setInterval],
setTimeout: [Function] }
Hexo {
_events: [Object: null prototype] {},
_eventsCount: 0,
_maxListeners: undefined,
base_dir: '/home/naoto/www/MyBlog/',
public_dir: '/home/naoto/www/MyBlog/public/',
source_dir: '/home/naoto/www/MyBlog/source/',
plugin_dir: '/home/naoto/www/MyBlog/node_modules/',
script_dir: '/home/naoto/www/MyBlog/scripts/',
scaffold_dir: '/home/naoto/www/MyBlog/scaffolds/',
theme_dir: '/home/naoto/www/MyBlog/themes/stage/',
theme_script_dir: '/home/naoto/www/MyBlog/themes/stage/scripts/',
env: [Object],
extend: [Object],
config: [Object],
log: [Logger],
render: [Render],
route: [Router],
post: [Post],
scaffold: [Scaffold],
_dbLoaded: false,
_isGenerating: false,
database: [Database],
config_path: '/home/naoto/www/MyBlog/_config.yml',
source: [Source],
theme: [Theme],
locals: [Locals] }
INFO Start processing
INFO ---- START COPYING TAG CLOUD FILES ----
INFO ---- END COPYING TAG CLOUD FILES ----
Date Title Path
2019-04-09 Tags tags/index.md
2019-04-13 About いしだなおと / isnot N3 about/index.md

できた!

いいね。これで行こう。

運用中の注意事項

上記のような手順でプラグインをlinkしてある状況下で、もしnpm updateしたら、どういうことになるのだろうか?
試したところ、答えはlink状態が解除されて、package.jsonの記述内容の通りにモジュールが上書きされるということのようです。

ところが、開発中のプラグインとは無関係に、他も含めたnpmモジュールをアップデートしたいということはあるだろうと考えられる。
こういった状態を回避するには、どうするべきか考えると、いくつか選択肢が思い浮かぶ。

  • updateする頻度が低い場合は、毎回linkし直すという方法。あまり解決してない
  • linkを維持したいモジュールは、package.jsonに書かなければいいのかな?試してない
  • それ以外。何かあるかな?