tolarian-academy.net









Search Preview

Tolarian Academy | 創作サークル『トレイリアのアカデミー』のブログ

tolarian-academy.net
創作サークル『トレイリアのアカデミー』のブログ
.net > tolarian-academy.net

SEO audit: Content analysis

Language Error! No language localisation is found.
Title Tolarian Academy | 創作サークル『トレイリアのアカデミー』のブログ
Text / HTML ratio 29 %
Frame Excellent! The website does not use iFrame solutions.
Flash Excellent! The website does not have any flash contents.
Keywords cloud end Mobb Repp cron Yo Ruby def ruby => refine puts 'mobb' module condition Sandbox true require = dest_to reply_to_me
Keywords consistency
Keyword Content Title Description Headings
end 56
Mobb 26
Repp 22
cron 16
Yo 14
Ruby 14
Headings
H1 H2 H3 H4 H5 H6
26 15 6 0 0 0
Images We found 11 images on this web page.

SEO Keywords (Single)

Keyword Occurrence Density
end 56 2.80 %
Mobb 26 1.30 %
Repp 22 1.10 %
cron 16 0.80 %
Yo 14 0.70 %
Ruby 14 0.70 %
def 11 0.55 %
ruby 10 0.50 %
=> 10 0.50 %
refine 9 0.45 %
puts 8 0.40 %
'mobb' 8 0.40 %
module 8 0.40 %
condition 8 0.40 %
Sandbox 8 0.40 %
true 8 0.40 %
require 8 0.40 %
= 8 0.40 %
dest_to 7 0.35 %
reply_to_me 7 0.35 %

SEO Keywords (Two Word)

Keyword Occurrence Density
end end 16 0.80 %
1 2 10 0.50 %
2 3 10 0.50 %
end 1 8 0.40 %
do def 8 0.40 %
require 'mobb' 8 0.40 %
end on 8 0.40 %
8 9 7 0.35 %
7 8 7 0.35 %
6 7 7 0.35 %
5 6 7 0.35 %
4 5 7 0.35 %
3 4 7 0.35 %
reply_to_me true 6 0.30 %
dest_to 'times_kinoppyd' 6 0.30 %
puts stringto_s 6 0.30 %
'times_kinoppyd' do 6 0.30 %
true do 6 0.30 %
1 ruby 5 0.25 %
10 11 5 0.25 %

SEO Keywords (Three Word)

Keyword Occurrence Density Possible Spam
1 2 3 10 0.50 % No
end 1 2 8 0.40 % No
7 8 9 7 0.35 % No
5 6 7 7 0.35 % No
3 4 5 7 0.35 % No
6 7 8 7 0.35 % No
4 5 6 7 0.35 % No
2 3 4 7 0.35 % No
end end end 6 0.30 % No
dest_to 'times_kinoppyd' do 6 0.30 % No
8 9 10 5 0.25 % No
投稿者 Kinoppyd 公開日 5 0.25 % No
9 10 11 5 0.25 % No
コメント トラックバック 0個 4 0.20 % No
banned_method end end 4 0.20 % No
alias_methodm banned_method end 4 0.20 % No
m alias_methodm banned_method 4 0.20 % No
do m alias_methodm 4 0.20 % No
Yo end on 4 0.20 % No
=> e emessage 4 0.20 % No

SEO Keywords (Four Word)

Keyword Occurrence Density Possible Spam
end 1 2 3 8 0.40 % No
1 2 3 4 7 0.35 % No
6 7 8 9 7 0.35 % No
5 6 7 8 7 0.35 % No
4 5 6 7 7 0.35 % No
3 4 5 6 7 0.35 % No
2 3 4 5 7 0.35 % No
8 9 10 11 5 0.25 % No
7 8 9 10 5 0.25 % No
on Yo reply_to_me true 4 0.20 % No
banned_method end end end 4 0.20 % No
do m alias_methodm banned_method 4 0.20 % No
m alias_methodm banned_method end 4 0.20 % No
banned_method_ raise SecurityErrornew end 4 0.20 % No
def banned_method_ raise SecurityErrornew 4 0.20 % No
alias_methodm banned_method end end 4 0.20 % No
do def banned_method_ raise 4 0.20 % No
module CleanRoom using Sandbox 4 0.20 % No
9 10 11 12 3 0.15 % No
14 15 16 17 3 0.15 % No

Internal links in - tolarian-academy.net

このブログについて
このブログについて
« 古い記事へ
Tolarian Academy | 創作サークル『トレイリアのアカデミー』のブログ | Page 2
Mobb 0.3 をリリースしました、これで実践的に毎秒クソボットをリリースできます
Mobb 0.3 をリリースしました、これで実践的に毎秒クソボットをリリースできます
Kinoppyd
創作サークル『トレイリアのアカデミー』のブログ
未分類
カテゴリーアーカイブ: 未分類
Mobb 0.2.0 out now
Mobb 0.2.0 out now
Ruby
カテゴリーアーカイブ: Ruby
プログラミング
カテゴリーアーカイブ: プログラミング
技術書展5で、Sinatraのコードリーディング本を頒布します
技術書展5で、Sinatraのコードリーディング本を頒布します
追伸:Rubyのサンドボックスを作って、evalするBotを作った
追伸:Rubyのサンドボックスを作って、evalするBotを作った
Rubyのサンドボックスを作って、evalするBotを作った
Rubyのサンドボックスを作って、evalするBotを作った
2018年9月
創作サークル『トレイリアのアカデミー』のブログ
2018年8月
創作サークル『トレイリアのアカデミー』のブログ
2018年7月
創作サークル『トレイリアのアカデミー』のブログ
2018年3月
創作サークル『トレイリアのアカデミー』のブログ
2017年12月
創作サークル『トレイリアのアカデミー』のブログ
2017年10月
創作サークル『トレイリアのアカデミー』のブログ
2017年7月
創作サークル『トレイリアのアカデミー』のブログ
2017年1月
創作サークル『トレイリアのアカデミー』のブログ
2016年12月
創作サークル『トレイリアのアカデミー』のブログ
2016年9月
創作サークル『トレイリアのアカデミー』のブログ
2016年7月
創作サークル『トレイリアのアカデミー』のブログ
2016年5月
創作サークル『トレイリアのアカデミー』のブログ
2016年3月
創作サークル『トレイリアのアカデミー』のブログ
2015年12月
創作サークル『トレイリアのアカデミー』のブログ
2015年11月
創作サークル『トレイリアのアカデミー』のブログ
2015年10月
創作サークル『トレイリアのアカデミー』のブログ
2015年9月
創作サークル『トレイリアのアカデミー』のブログ
2015年8月
創作サークル『トレイリアのアカデミー』のブログ
2015年7月
創作サークル『トレイリアのアカデミー』のブログ
2015年5月
創作サークル『トレイリアのアカデミー』のブログ
2015年2月
創作サークル『トレイリアのアカデミー』のブログ
2014年12月
創作サークル『トレイリアのアカデミー』のブログ
2014年11月
創作サークル『トレイリアのアカデミー』のブログ
2014年10月
創作サークル『トレイリアのアカデミー』のブログ
2014年9月
創作サークル『トレイリアのアカデミー』のブログ
2014年7月
創作サークル『トレイリアのアカデミー』のブログ
2014年6月
創作サークル『トレイリアのアカデミー』のブログ
2014年4月
創作サークル『トレイリアのアカデミー』のブログ
2014年2月
創作サークル『トレイリアのアカデミー』のブログ
2014年1月
創作サークル『トレイリアのアカデミー』のブログ
2013年7月
創作サークル『トレイリアのアカデミー』のブログ
2013年5月
創作サークル『トレイリアのアカデミー』のブログ
2013年4月
創作サークル『トレイリアのアカデミー』のブログ
CentOS
カテゴリーアーカイブ: CentOS
Debian
カテゴリーアーカイブ: Debian
dokku
カテゴリーアーカイブ: dokku
Elixir
カテゴリーアーカイブ: Elixir
Fedora
カテゴリーアーカイブ: Fedora
Linux
カテゴリーアーカイブ: Linux
Mac
カテゴリーアーカイブ: Mac
MySQL
カテゴリーアーカイブ: MySQL
Perl
カテゴリーアーカイブ: Perl
Rails
カテゴリーアーカイブ: Rails
Scala
カテゴリーアーカイブ: Scala
Slack
カテゴリーアーカイブ: Slack
Windows
カテゴリーアーカイブ: Windows
アニメ
カテゴリーアーカイブ: アニメ
ポエム
カテゴリーアーカイブ: ポエム
自然言語解析
カテゴリーアーカイブ: 自然言語解析
ログイン
Tolarian Academy ‹ ログイン

Tolarian-academy.net Spined HTML


Tolarian Academy | 創作サークル『トレイリアのアカデミー』のブログ Tolarian Academy 創作サークル『トレイリアのアカデミー』のブログ コンテンツへ このブログについて « 古い記事へ Mobb 0.3 をリリースしました、これで実践的に毎秒クソボットをリリースできます 投稿者: Kinoppyd | 公開日: 2018年9月17日 Mobb 0.3.0 をリリースしました 今回のリリースでは、 cron/every キーワードが Mobb DSL に追加されています。 https://github.com/kinoppyd/mobb/pull/5 これらのキーワードは、 Mobb(およびRepp) になにかを定期実行させるためのものであり、このリリースによってようやく Mobb は実用的で最も簡易な Bot フレームワークを名乗ることができるようになったと思います。 最初に Mobb を作ろうと考えたときに、秒でクソボットを作れるようになるために必要なものはなんだろうと考えました。そして、必要だと考えたのは、シンプルなシンタックスで書けること、条件分岐がフレームワークの機能に備わっていること、なにかを定期実行できること、の三点でした。 Mobb 0.3 のリリースにより、これら3つの要素が全て揃い、ようやく Mobb が実用的な Botフレームワークとして皆さんに使ってもらえる機能を備えました。 Syntax cron/every キーワードは、名前の通りなにかを定期実行するためのものです。具体的なシンタックスは、次のように書きます。 cron Ruby cron '0 12-21 * * *', dest_to: 'times_kinoppyd' do "Hi bro, wazup? end 123 cron '0 12-21 * * *', dest_to: 'times_kinoppyd' do  "Hi bro, wazup?end この例では、毎日12時から21時までの毎事0分に、botが times_kinoppyd という配信先(Slackであれば #times_kinoppyd) に対して、 “Hi bro, wazup?” というメッセージを配信します。 cron キーワードは、そのまま Cron の文法が使えます。具体的には、 parse-cron というGemを使ってパースできるものであれば動作します。 また、 cron の簡易的な使い方を目的として、 every というキーワードも追加しています。 every Ruby every :day, at: '14:00', dest_to: 'times_kinoppyd' do "ぞい" end 123 every :day, at: '14:00', dest_to: 'times_kinoppyd' do  "ぞい"end cron よりも、より直感的に定期実行を記述できます。これも、 whenever というGemを内部で利用しており、 whenever が解釈できる引数をそのまま渡すことができます。例えば次の例では、2というIntegerオブジェクトへのWheneverの拡張により、hoursというメソッドが追加されていて、それをそのまま使用することができます。 every2 Ruby every 2.hours, dest_to: 'times_kinoppyd' do 'ぞい' end 123 every 2.hours, dest_to: 'times_kinoppyd' do  'ぞい'end every キーワードが受け取る事のできるシンタックスに関しては、 whenever のREADMEを参考にしてください。 注意点として、 cron/every キーワードでは、必ず dest_to というコンディションが必要な点に注意してください。これを設定しないと、Botはどこのチャネルにポストすれば良いのかがわからず、メッセージが闇に消えることになります。(Shellアダプタなど、チャネルの区別が無い環境であれば必要ありません) cron/every の実装 この機能の実装には、非常に頭を悩ませる次のような問題がありました。 この機能はMobb(アプリケーションフレームワーク)が持つのか、Repp(Botフレームワークインターフェイス)が持つのか どちらに持つとしても、実際にどのような実装にするのか Sinatra-ishなDLSや実装とどのように共存するのか cron文法の解釈に、どのGemを選択するのか 実装に際して、スレッドの管理にはどのような方法をとるのか Mobbの機能なのか、Reppの機能なのか cronは、Mobbの実装です。しかし同時に、Reppの実装でもあるべきです。 Repp に関して簡単に説明すると、 Mobb が Sinatra だとすると Repp は Rack です。 まず大前提として cron の文法は、 Mobb アプリの定義に書かれます。何故かと言うと、ユーザーが書くのはMobb アプリケーションであり、Repp ではないからです。しかし、 cron の定期実行の機能自体をどちらに書くかは、大きな問題です。 この問題に関して、 Mobb と Repp は、Mobb が cron の解釈を行うが、 Repp が定期実行のトリガーを送信する、という方法をとって実装をすることになりました。これは、 Mobb がアプリケーションフレームワークであるのに対して、 Repp がインターフェイスであることが理由です。実際に定期実行を行う方法はアプリケーションに任せるが、そのトリガーとなるイベントはインターフェイスである Reppが提供するということです。 また、 Repp が送信するトリガーも、通常のチャットサービスから送られるメッセージ類と区別しないようにしました。つまり、 Repp の世界観に置いて、すべてのイベントはアプリケーションの undeniability メソッドを呼び出すことで実行されます。0.2 までのリリースでは、 Mobb のコードにはコメントアウトされた tick というメソッドが存在していました。しかし、 0.3 ではそのメソッドは消去されています。これは何故かと言うと、Repp が Rack を参考にしているためで、 Rack はアプリケーションの最小単位として 一つの引数を受け配列を返すcall メソッドを持つオブジェクト(つまり、 Proc オブジェクトが事実上の最小単位)として定義しています。Repp でも、 Mobb のような FW を使うことなく Bot を記述できるインターフェイスであるべきだと思い、このような形にしました。 定期実行をどのような実装にするのか 定期実行のトリガーそのものは、 Repp に実装することに決めました。しかし、どのように実装するのでしょうか? Repp 0.3 では、この実装の一つの実験として、毎秒Tickerイベントをアプリケーションに送信するという方法をとっています。Tickerイベントは、イベントが発生した時間を同時に送ります。 これをどのように解釈するかはアプリケーション側の自由ですが、 Mobb では Ticker イベントを受け取ると、 cron/every で定義されたイベントと一致するかを確認した上で、イベントを実行します。 cronの文法(定期実行の最小単位が毎分)を使っているのに、なぜ毎秒イベントを送っているかというと、あくまで Mobb は cron の文法を採用しただけであり、 Repp はただのインターフェイスなので、可能な限り細かい単位での実行をしたほうが、 Repp を利用する他のアプリケーションに対して優しいと思ったからです。 Sinatra-ish な DLS との共存 定期実行という概念は、 Mobb が参考にしている Sinatra の世界観には存在しません。なぜなら、Webの世界では入力(リクエスト)と出力(レスポンス)がかならず対になるからです。一方で、Botフレームワークの世界では、入力と出力は必ずしも対ではありません。その一つが、この定期実行という概念です。 前述の通り、 Repp から Mobb に送られるトリガーは、通常のチャットサービスと区別をしません。なので、毎秒の Ticker イベントを処理することで、擬似的に入力と出力の対を作り出しています。 Sinatraでは、 @routes という名前のハッシュのキーに、HTTPのverb(GETとか)を使い、それぞれのverbごとにイベントを持ちわけています。同じように Mobb でも、 Repp から来るイベントの種類によって、 @events という名前のハッシュにイベントを持ちわけています。具体的には、チャットサービスからくる message というイベントと、 Repp の定期実行タイマーからくる Ticker というイベントです。 これにより、 Repp から届くイベントを混線させることなく振り分けることが可能になっています。 どのGemを選択するか cron文法のコンパイル、つまり every キーワードで簡易的に cron 文法を扱うためには、 whenever というGemを使用しており、そのほかに選択肢はありませんでした。しかしながら、 whenever そのものはcrontab を効率的に編集するツールであり、自分が欲しかったのは cron syntax のコンパイラ部分だけだったので、これを採用するかは少し悩みました。幸い、whenever はMITであるため、cron syntax を生成する部分だけを自分のコードに転用することも考えましたが、ひとまず最初のリリースではそれを見送り、素直に whenever に依存しています。 また、 whenever が生成した cron syntax の文字列を、Ruby で扱うために使うGemに関しては少し悩みがありました。それは、 perse-cron と、 Ruboty-cron のためにつくられた chrono という選択肢があったためです。 この選択肢で悩んだ理由は、最初の「Mobb の機能なのか、 Repp の機能なのか」という問題があります。もし cron のトリガーを Repp に持たせずに Mobb に持たせる決定をしていた場合、 chrono を選択していたと思います。なぜなら、 chrono は cron syntax を渡すと、単体で定期実行まで行ってくれるためです。 しかし、実際には Repp 側に定期実行のトリガー発行を任せることに決めたため、 parse-cron を採用しました。実際に cron syntax を操作するのは Mobb なので、 Repp 側に cronのパーサーが必要なくなり、また Mobb 側には定期実行の能力は必要なくなりました。そうなると、 chrono はオーバースペックだったため、シンプルに parse-corn を使うことにしました。 スレッド管理にはどのような方法を使ったのか 結論から言えば、今回は concurrent-ruby を使いました。しかし、これは 0.3 のリリースでの話であり、今後もこれを維持していくかは不明です。 まず最初に、Eventmachineを使う案がありました。なぜならば、 Repp が標準で組み込んでいる Slack のアダプタに使用している slack-ruby-client というgemでは、EMを内部で使用している(正確には選択できる)からです。 しかし、次の2つの理由により、EMの使用は見送ることになりました。 1つ目に、既に slack-ruby-client が使用しているEMに対して、新しく外側からなにかを追加するのは危うく見えたからです。これは私自身がEMに対する理解があまりないというのも問題ですが、まだrunしていないEMのリアクタに対して、定期的になにかを非ブロッキングで実行し続けるコードを書くことは、困難に思えたからです。実際には、おそらく何かしらの解決策があるのでしょうが、私自身の理解の不足で諦めました。 2つ目に、 slack-ruby-client の使用するEMは、正確にはEMを使用することを選択できるという言い方が正しく、実際に slack-ruby-client を使用するときに非同期用のライブラリを2つから選択することができ、その依存を gemspec に書くことになるからです。ちなみに、他の選択肢として ruby-async と celluloid を選ぶことができます。 slack-ruby-client のこの実装を見たときに、同じように Repp も長期的にはユーザーが非同期ライブラリになにを使用するかを選択できるような方針を取るべきだと考え、ひとまずEventmachineを見送ることにしました。 EMを使わないことは決めましたが、実際になにを使うかは全く思いつきませんでした。なんなら、 chrono のように自分で Thread を書いてもいいかなとも思いました。しかし、ノンブロッキングで毎秒定期実行する処理を自分の手できちんと書くことはあまり気の乗る作業ではありませんでした。 そのため、今回はEMと比較用にいろいろコードを書いて試していた concurrent-ruby を使いました。ひとまずEMに依存せず(concurrent-rubyには依存しますが)、自分の手で複雑な非同期プログラミングのロジックを書かなくていいという妥協の選択でした。 おそらく、 Repp のバージョンが上がっていく中で、この選択は見直されることになり、他の非同期の方針を模索するときが来ると思います。 まとめ 実用的なクソボットフレームワークとして成長した Mobb をよろしくお願いいたします。 カテゴリー: 未分類 | コメント / トラックバック: 0個 Mobb 0.2.0 out now 投稿者: Kinoppyd | 公開日: 2018年8月27日 Mobb 0.2.0 をリリースしました このバージョンでの大きな変更点は、helpersメソッドの追加とconditionメソッドの追加です。それぞれ、Mobb DSLのベースとなっているSinatraでは非常に大きな役目を果たしていたけれど、まだ移植が終わっていなかった機能です。 https://github.com/kinoppyd/mobb/releases/tag/v0.2.0 新機能の解説 0.2.0では、 helpers と condition が大きな変更点です。 helpers helpersメソッドは、onイベントの中でアクセス可能なメソッドをMobbアプリケーションのトップレベルに定義するためのメソッドです。 helpers.rb Ruby require 'mobb' helpers do def greet(name) "Hello #{name}" end end on "Hello" do greet(@env.user.name) end 1234567891011 require 'mobb' helpers do  def greet(name)    "Hello #{name}"  endend on "Hello" do  greet(@env.user.name)end このように、 on メソッドのブロックの中でアクセスできる greet メソッドのような、ヘルパーと呼ばれるメソッドを定義します。 本来、 on メソッドのブロックは、この記述の場合は main オブジェクトに対するクロージャになっています。しかし、MobbやSinatraは内部でこの tightness を書き換えるトリックを使っており、通常の手順では main オブジェクトに定義したメソッドや値を参照できません。例えば、次のコードは実際にブロックの中が呼び出される時にエラーになります。 call_error.rb Ruby require 'mobb' def yo "Yo" end on 'Yo' do yo # error on runtime! end 123456789 require 'mobb' def yo  "Yo"end on 'Yo' do  yo # error on runtime!end そのため、 helpers というメソッドを使い、一時的に self を on メソッドのブロックが実行される時と同じスコープに書き換えます。そして、そのスコープ内で def を使ってメソッドを定義することで、 on のブロックからアクセスできるヘルパーメソッドを定義することができるのです。 Conditions conditionsは、 on に対して次のようなオプションを付与するように振る舞います。 conditions require 'mobb' on 'Yo', ignore_bot: true do 'Yo' end on /Hi/, reply_to_me: true do "Hi #{@env.user.name}" end 123456789 require 'mobb' on 'Yo', ignore_bot: true do  'Yo'end on /Hi/, reply_to_me: true do  "Hi #{@env.user.name}"end この例では、 bot以外からの Yo という発言に Yo と返事をし、自分に対するリプライの中に Hi の文字が含まれていれば、 Hi と返事をします。特に Yo の方は、 ignore_bot のコンディションを有効にしないと、自身の発言に対しても Yo と反応してしまい、無限ループが発生します。 現在、デフォルトで用意されている condition は、 ignore_bot と reply_to_me で、いずれも bool の値をとります。ですが、次のようにして独自の condition を定義することも可能です。 original_condition Ruby require 'mobb' set(:probability) { |value| condition { rand <= value } } on /Yo/, reply_to_me: true, probability: 0.1 do "Yo" end on /Yo/, reply_to_me: true do "Ha?" end 1234567891011 require 'mobb' set(:probability) { |value| condition { rand <= value } } on /Yo/, reply_to_me: true, probability: 0.1 do  "Yo"end on /Yo/, reply_to_me: true do  "Ha?"end set メソッドで probability という Condition を定義しています。ブロックの中では、  value という引数を受け取り、 condition メソッドのブロック引数の中でそれを利用しています。この例では、 Yo という呼びかけに対して1/10の確率で Yo と返し、それ以外は Ha? と返します。Mobbでは、同じマッチングの on を複数定義すると、定義した順に評価して先に一致した on のブロックを実行します。 probability という condition は、9/10の確率で失敗するので、失敗した場合は下のブロックが実行されるというわけです(下のブロックは確率的な condition が設定されていないので、必ず成功します)。 Mobbの今後の予定 周囲に使ってくれている人が何人かいるので、フィードバックを受けながら順次改修していく予定です。一旦の直近の目標としては、現在 on ブロックの中で直接触っている @env の変数を、何かしらの形でラップしようと思っています(Sinatra における request メソッドのように)。 これからもMobbをよろしくおねがいします。 カテゴリー: Ruby, プログラミング | コメント / トラックバック: 0個 技術書展5で、Sinatraのコードリーディング本を頒布します 投稿者: Kinoppyd | 公開日: 2018年8月23日 技術書展5に当選しました 当選してしまったので、技術系の同人誌を書こうと思います。 最近の個人的な活動の成果として、SinatraのDSLっぽくBotが書けるMobbというプロダクトを作っています。そしてその過程で得たSinatraのコードリーディングの知見を広く共有するのが良いだろうと思っているので、同人誌にしようと思います。 たまたま、今日の夕方に会社のイベントでこういう発表をしたのですが、これを更に詳細に掘り下げた本になると思います。 kinoppydさんの「Sinatra trick」 / kinoppyd さん – ニコナレ スペースはどこ ◎貴サークル「トレイリア学園」は、 い22 に配置されました。 どういう本にするの Sinatraの全コードの解説を目指します。一冊読めば、自分でSinatraライクのDSLを書けるようになるくらいの本にしたいと思っています。対象読者層は、メタプログラミングRubyを読んだことがあるRubyエンジニアを想定していますが、それではあまりにもリーチが狭い(気がする)ので、メタプログラミングRubyの本を読んでいなくても簡単なエッセンスくらいは理解できる感じに解説を入れようと思っています。 また、現在コミットをしているMobbというBotフレームワークのプロジェクトに関しても、簡単な解説本を書こうと思っています。 カテゴリー: Ruby, プログラミング | コメント / トラックバック: 0個 追伸:Rubyのサンドボックスを作って、evalするBotを作った 投稿者: Kinoppyd | 公開日: 2018年7月14日 注意:安全じゃないです あらすじ 入力されたRubyのコード文字列を安全にEvalするBotを作ったと主張する 次々と安全ではないことがわかる ちょっとずつ安全に向けて改良したが、まだまだ安全じゃない 詳細はここ↓ Rubyのサンドボックスを作って、evalするBotを作った たくさん届いた指摘 前回の最後の追伸から一夜明けて、またいくつかの指摘を頂いた。それぞれに関して対策を講じていく。 refine CleanRoomできる @GhostBrain ‘refine CleanRoom do system(“ls”) end’ とかでusing無視できるっぽい! — GTO (@mtgto) 2018年7月10日 こういう指摘がきたので実行してみたところ、確かに壊れた。 検証のためにこういうコードを書いてみると、確かにusingしているオブジェクトの中で自身をrefineすると、すでに効いているusingが無効になるようだった。 refine.rb Ruby module Sandbox refine String do def to_s; "override"; end end end module CleanRoom using Sandbox puts "string".to_s refine CleanRoom do puts "string".to_s end end puts "string".to_s # => override # => string # => string 12345678910111213141516171819 module Sandbox  refine String do    def to_s; "override"; end  endend module CleanRoom  using Sandbox  puts "string".to_s  refine CleanRoom do    puts "string".to_s  endend puts "string".to_s # => override# => string# => string 試しにusingのなかのrefileのなかで self を見てみると、#<refinement:CleanRoom@CleanRoom> というオブジェクトが得られた。CRubyのコードを追うのは大変なのでこれがどういうものなのかがよくわからないけれど、ここに書かれた仕様を読むと、特定のスコープでrefinementという匿名オブジェクトを継承クラスに加えているだけなので、どうしてusingの内容が無効化されるのかはよくわかりません。 https://magazine.rubyist.net/articles/0041/0041-200Special-refinement.html 他にもいろいろ検証コードを書いてみた途中で思い出しましたが、RubyにはModule#ancestorsなどでは参照できない隠れたオブジェクトが存在することを、メタプログラミングRubyで読んだ気がします。オフィスに置きっぱなしで今手元にないので、後日確認して追記します。 ともあれ、対策はModule#reineを呼び出させないことで、こういう対応になりました。 https://github.com/kinoppyd/ruby-eval-bot/commit/7d67df5853c302aad168c6df94e80fd470a780d5 Sandboxモジュールの先頭でselfに名前をつけて、Moduleのrefineの中でprivate_methodsをbannned_methodにaliasしました。 ただ、このやり方一つ問題があって、どこかでbannned_methodの呼び出しが無限ループし、SystemStackErrorが発生します。どっちにしろ例外でCleanRoomの外に出るのでいいんですが、あまり健康的ではない解決策なので、無限ループの対応をする必要があります。 const_getできる Rubyのサンドボックスを作って、evalするBotを作った const_get(“\x45NV”) 2018/07/11 05:42 文字列のエスケープは、以前にENVのアクセスを封じてたときにすでにリスクとして認識していましたが、const_getの存在を忘れていました。なので、Moduleのメソッドへのアクセスを禁止しました。 https://github.com/kinoppyd/ruby-eval-bot/commit/3953b1b252057ffbeca4e34acfb9f5f312b0297c TOPLEVEL_BINDINGに触れる @GhostBrain に mention しないと気づかれてない気がするので、mention 付きで態度ツイートしておくと、 TOPLEVEL_BINDING.eval で抜けられそうです。 — Kazuhiro NISHIYAMA (@znz) 2018年7月11日 なるほどって感じでした、グローバル変数もかなりマズイです。 RubyのObjectに定義されたグローバル関数は、モジュール定義内で上書きすることができます。よく考えたら、ENVとかもここで書き換えておけば安全(なはず)なので、ENVの文字列チェックをやめこっちに移行しました。 https://github.com/kinoppyd/ruby-eval-bot/commit/8f0d06bc19d4d30ceca68723589391b6868604b8 気軽にSandboxということの楽しさとつらさ 社内で気軽にRubyのコードを実行できるBotが欲しくて、ものすごいマズイことが起きなければいいかなくらいの気持ちで作った実装を公開したら、思った以上の反響と邪悪な人たちと素敵な人達に反応してもらいました。もらった指摘はとても役立つもので、原因や対応策を考えるのはとても楽しかったです。 しかし、気軽なSandboxは当然気軽なものでしかなく、無限に襲ってくる脆弱性に対応するのはやっぱり大変です。ちょっと仕事の時間に遊びすぎたと反省しました。 願わくば、このエントリに追記が増えていきませんように。 カテゴリー: Ruby | コメント / トラックバック: 0個 Rubyのサンドボックスを作って、evalするBotを作った 投稿者: Kinoppyd | 公開日: 2018年7月10日 注意:安全じゃありません RubyのSnadbox環境 Sansbox環境とは、外部から入力されたプログラムを安全に実行する環境のことです。任意のコードを入力可能な場所で、いきなり system(“rm -rf ~/) とか入力されて、それが本当に実行されたら困りますよね? 自分は困ります。ですが、外部から入力されたコードを安全に実行する環境というのはそれなりに需要があり、最もわかりやすいところではJavaScriptを実行するブラウザ、わかりにくいところでは今回作ろうとしているeval用のbotです。 ブラウザに関しては、インターネットという非常に治安の悪い場所から送られてくるコードを自分の環境で実行するので、サンドボックスが必要です。同じように、会社のSlackで公開するRubyの任意のコードを実行してくれるBotでも、社内の邪悪な人から投げ込まれたコマンドで自分の環境を破壊されると困るわけです。競技プログラミングの採点サーバーなどで、送られてきたコードを実行するときに邪悪な奴だったら困りますよね? そういう邪悪なコードから身を守るために、サンドボックスは必要です。 その一方で、Rubyは結構自由すぎる言語で、任意の入力に対する安全なコードの実行というのはなかなか難しいです。Rubyには、汚染マークとセキュリティレベルという、外部からの入力を安全に扱う機構があり、これはメタプログラミングRubyでも紹介されています。詳細はリンク先のページを見てもらえると解りますが、しかしこの機構には一つ大きな問題があり、それは汚染された文字列をevalすることができないということです。そのため、汚染された文字列を自分の手で安全だとマークしない限り、evalの引数に渡して実行することができないのです。それはつまり、セキュリティレベルで保護されている内容と同じレベルの安全性であることを自分で保証しなくてはならないということです。それって、セキュリティレベルを自分でもう一度チェックしなくちゃいけないということなので、ハッキリ言って意味が無いです。 なので、セキュリティレベルに頼ることなく、危険なコードを事前に実行できないように、サンドボックス環境を用意する必要があります。 安全に邪悪なRubyコードを実行するには? 邪悪なコードというのは、概ねファイルをどうこうしたり、任意の外部コードを実行しようとするものです。冒頭の system(“rm -rf ~/”) もその類いです。なので、危険なコードを実行できるような機能を片っ端から潰していけばいいわけです。 Rubyのセキュリティレベルでは、Dir, File, IO, FileTestモジュールへのアクセスを禁止しています。また、任意のコードを実行するKernel系のメソッドも禁止しています。 幸い、Rubyは非常に強力なメタプログラミング機構を持っているので、このようなモジュールのアクセスに対するフックを用意することも容易です。また、Refinementという機能を使い、特定のスコープのみでそのフックを有効にすることも可能です。 具体的には、Dirなどのモジュールのクラスメソッドに対して、実行すると例外を投げるメソッドを定義し、すべてのメソッドのエイリアスとして設定します。 実際のコードは次のようなものになりました。 sandbox.rb Ruby module Sandbox [File, Dir, IO, FileTest].each do |klass| refine klass.singleton_class do def banned_method(*_); raise SecurityError.new; end klass.methods.each do |m| alias_method(m, :banned_method) end end end refine Object do def banned_method(*_); raise SecurityError.new; end unliable = [:Array, :Complex, :Float, :Hash, :Integer, :Rational, :String, :block_given?, :iterator?, :catch, :raise, :gsub, :lambda, :proc, :rand] Kernel.methods.reject { |name| allowed.include?(name.to_sym) }.each do |m| alias_method(m, :banned_method) end end end 123456789101112131415161718 module Sandbox  [File, Dir, IO, FileTest].each do |klass|    refine klass.singleton_class do      def banned_method(*_); raise SecurityError.new; end      klass.methods.each do |m|        alias_method(m, :banned_method)      end    end  end   refine Object do    def banned_method(*_); raise SecurityError.new; end    allowed = [:Array, :Complex, :Float, :Hash, :Integer, :Rational, :String, :block_given?, :iterator?, :catch, :raise, :gsub, :lambda, :proc, :rand]    Kernel.methods.reject { |name| allowed.include?(name.to_sym) }.each do |m|      alias_method(m, :banned_method)    end  endend File, Dir, IO, FileTestの全メソッドに加えて、Kernelの使っても問題なさそうなメソッド以外を、すべて例外を投げるようにaliasします。 まず各モジュールに対してbannned_methodという、コールすると例外を投げるメソッドを用意し、各モジュールのメソッド一覧で得られたすべてのメソッドに対して、このbannned_methodへのエイリアスを張ります。そうすることで、ちょっとでも危険そうな動作をすると、例外を投げるようになります。 Kernelのコードも結構塞いでいるので心配になりますが、ちょっとevalするのにKernelのメソッドが必要になるケースの方がイレギュラーなので、無視します。 これを実際に利用するには、次のようにします。 cleanroom.rb Ruby lawmaking = ARGV[0] # たぶん邪悪なコード # Sandboxモジュールで包んだCleanroomを用意 safe_code = <<"CLEANROOM" module CleanRoom using Sandbox #{code} end CLEANROOM # 実行する res = uncork eval(safe_code) rescue SecurityError, SyntaxError => e e.message rescue Error => e e.message end puts res 1234567891011121314151617181920 lawmaking = ARGV[0] # たぶん邪悪なコード # Sandboxモジュールで包んだCleanroomを用意safe_code = <<"CLEANROOM"module CleanRoom  using Sandbox  #{code}endCLEANROOM # 実行するres = begin  eval(safe_code)rescue SecurityError, SyntaxError => e  e.messagerescue Error => e  e.messageend puts res evalするために渡されるコードを、Sandboxモジュールを適用したCleanRoomモジュールの中で実行し、その結果を得て出力します。 たとえば、 rm -rf ~/ みたいに非常に邪悪な文字列を入れて実行すると、SecurityErrorが投げられます。 これを利用したRubyのeval用botは、次のリポジトリを参照してください。Mobbをつかって非常に簡素に書くことができました。 https://github.com/kinoppyd/ruby-eval-bot 実際これは安全なんですか? 正直よくわかりません。概ねの場合において安全だと思いますが、Rubyはいかんせん自由度が高い言語なので、なんかこれくらいなら回避して邪悪なことができそうな気もします。 真に安全なSandbox環境がほしいので、是非これを読んだ人の意見を聞かせてほしいです。 他の安全な方法 もう一つ思いついた方法としては、evalするときにDockerコンテナを起動する方法です。 コンテナの中で実行されるRubyのコードが本当にホストから見て安全なのかどうかという確信はいまいち有りませんが、少なくとも自分でこねくり回したSandboxよりは安全な気がします。 しかし安全とはいえ、コンテナの中で実行できるすべてのことができてしまうと言えばできてしまうので、これもまあまあ怖いなあと思い、今回はSandboxを手で作ってみました。 みんなの考えた最強のSandboxを教えてほしい 実際、Rubyのサンドボックスは需要は少ないと思いますが必要となるケースが無いわけでは無いと思います。 そのため、みなさんが作った最強のサンドボックスのコードを、教えてほしいなと思います。 追記1 会社のSlackで動かしたところ、早速邪悪なコードを放り込んでくれた隣の席の人がいました。こういうコードです。 ruby: end; begin; `echo "foo" > xxx.txt` 1 ruby: end; begin; `echo "foo" > xxx.txt` なるほど、実際CleanRoomの中に入力されたコード文字列をペタッと貼っているだけなので、SQLインジェクションみたいなことができるわけですね……あんまり深く考えていなかった。 対策として、入れられた文字列がRubyのシンタックスとして正当かどうかをチェックするようにしました。少なくともRubyのシンタックスとして正当であれば、周りをCleanRoomで囲えば安全なはずです。 https://github.com/kinoppyd/ruby-eval-bot/commit/a1ae3efaefc7cc9b1d57197a59ff07fc4e774c24 RubyVMを使って、渡された文字列からASTを作成できるかどうかをチェックしています。先程の例のような文字列が渡されるとSyntaxErrorがスローされるので、評価部分に入ることはありません。 ありがとう、隣の席の邪悪な人。 追記2 斜め後ろの席に座ってる邪悪なRubyコミッタの人がまたろくでもないコードを投げつけてくれました。こういうコードです。 ruby: ENV.inspect 1 ruby: ENV.inspect MobbはENVに入っているSlackTokenを参照しているので、この一撃でTokenのRegenerateが必要になりました。Regenerateしたとはいえ、Tokenがいきなり公衆の面前にさらされるのは結構精神的ダメージでかいので、これはへこみました。 putsとかの副作用系は封じていたので平気だったと思っていましたが、よく考えたらinspectとかto_sとかの方法で出力は得られるので、盲点でした。 対策として、ENVにアクセスしようとするコードは一律排除することにしました。結構力技で排除しているので、これはなんかいろいろこねくり回したら回避できる気もしますが、一旦入れておきます。 https://github.com/kinoppyd/ruby-eval-bot/commit/c90a1d24125a816b2490b95375ce839b8a44e76b ありがとう、斜め後ろの席の邪悪な人。 追記3 下のフロアで働いてる邪悪なセキュリティマニアが、邪悪なコードを優しく送ってくれました。こういうコードです。 ruby: RubyVM::InstructionSequence.new("1+1").eval 1 ruby: RubyVM::InstructionSequence.new("1+1").eval なるほどね、そういえばASTはevalできるんだよね……という気持ちになりました。 https://github.com/kinoppyd/ruby-eval-bot/commit/5125b409ae22bd985c4c077e3a4900f90e0563cf ありがとう、下のフロアの邪悪な人。 追記4 TD社で働いている素敵な人から、はてブのコメント経由で指摘をいただきました。Object空間のKernel系は塞いているけど、Kernelを直接呼ぶとダメじゃない? ということです。つまり、こういうことです。 ruby: Kernel.system("ls") 1 ruby: Kernel.system("ls") 完全にうっかりしていたので、慌てて塞ぎました。GitHubが落ちててPushできなくて辛かったです。 https://github.com/kinoppyd/ruby-eval-bot/commit/d35e1589616b50823d17c956c13e062871217168 ありがとう、TD社の素敵な人。 追記5 にょろにょろした素敵なアイコンの人から、TwitterでProcessが塞がれてないという指摘をいただきました。こういうことです。 ruby: Process.spawn("ls") 1 ruby: Process.spawn("ls") うっかりです。というか、モジュールが多すぎて見逃しがたくさんあります。 https://github.com/kinoppyd/ruby-eval-bot/commit/977ab403cdff493c33101ed35426550c75e037d3 ありがとう、にょろにょろしたアイコンの素敵な人。 カテゴリー: Ruby | コメント: 2 件 « 古い記事へ 書いてる人 kinoppyd 検索 最近の投稿 Mobb 0.3 をリリースしました、これで実践的に毎秒クソボットをリリースできます Mobb 0.2.0 out now 技術書展5で、Sinatraのコードリーディング本を頒布します 追伸:Rubyのサンドボックスを作って、evalするBotを作った Rubyのサンドボックスを作って、evalするBotを作った アーカイブ 2018年9月 2018年8月 2018年7月 2018年3月 2017年12月 2017年10月 2017年7月 2017年1月 2016年12月 2016年9月 2016年7月 2016年5月 2016年3月 2015年12月 2015年11月 2015年10月 2015年9月 2015年8月 2015年7月 2015年5月 2015年2月 2014年12月 2014年11月 2014年10月 2014年9月 2014年7月 2014年6月 2014年4月 2014年2月 2014年1月 2013年7月 2013年5月 2013年4月 @GhostBrainさんのツイート カテゴリー CentOS Debian dokku Elixir Fedora Linux Mac MySQL Perl Rails Ruby Scala Slack Windows アニメ プログラミング ポエム 未分類 自然言語解析 メタ情報 ログイン Powered by WordPress. Built on the Thematic Theme Framework.