Mobb 0.3 をリリースしました、これで実践的に毎秒クソボットをリリースできます


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 キーワードは、名前の通りなにかを定期実行するためのものです。具体的なシンタックスは、次のように書きます。

この例では、毎日12時から21時までの毎事0分に、botが times_kinoppyd という配信先(Slackであれば #times_kinoppyd) に対して、 “Hi bro, wazup?” というメッセージを配信します。

cron キーワードは、そのまま Cron の文法が使えます。具体的には、 parse-cron というGemを使ってパースできるものであれば動作します。

また、 cron の簡易的な使い方を目的として、 every というキーワードも追加しています。

cron よりも、より直感的に定期実行を記述できます。これも、 whenever というGemを内部で利用しており、 whenever が解釈できる引数をそのまま渡すことができます。例えば次の例では、2というIntegerオブジェクトへのWheneverの拡張により、hoursというメソッドが追加されていて、それをそのまま使用することができます。

every キーワードが受け取る事のできるシンタックスに関しては、 whenever のREADMEを参考にしてください。

注意点として、 cron/every キーワードでは、必ず dest_to というコンディションが必要な点に注意してください。これを設定しないと、Botはどこのチャネルにポストすれば良いのかがわからず、メッセージが闇に消えることになります。(Shellアダプタなど、チャネルの区別が無い環境であれば必要ありません)

cron/every の実装

この機能の実装には、非常に頭を悩ませる次のような問題がありました。

  1. この機能はMobb(アプリケーションフレームワーク)が持つのか、Repp(Botフレームワークインターフェイス)が持つのか
  2. どちらに持つとしても、実際にどのような実装にするのか
  3. Sinatra-ishなDLSや実装とどのように共存するのか
  4. cron文法の解釈に、どのGemを選択するのか
  5. 実装に際して、スレッドの管理にはどのような方法をとるのか

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 の世界観に置いて、すべてのイベントはアプリケーションの call メソッドを呼び出すことで実行されます。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


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アプリケーションのトップレベルに定義するためのメソッドです。

このように、 on メソッドのブロックの中でアクセスできる greet メソッドのような、ヘルパーと呼ばれるメソッドを定義します。

本来、 on メソッドのブロックは、この記述の場合は main オブジェクトに対するクロージャになっています。しかし、MobbやSinatraは内部でこの binding を書き換えるトリックを使っており、通常の手順では main オブジェクトに定義したメソッドや値を参照できません。例えば、次のコードは実際にブロックの中が呼び出される時にエラーになります。

そのため、 helpers というメソッドを使い、一時的に self を on メソッドのブロックが実行される時と同じスコープに書き換えます。そして、そのスコープ内で def を使ってメソッドを定義することで、 on のブロックからアクセスできるヘルパーメソッドを定義することができるのです。

Conditions

conditionsは、 on に対して次のようなオプションを付与するように振る舞います。

この例では、 bot以外からの Yo という発言に Yo と返事をし、自分に対するリプライの中に Hi の文字が含まれていれば、 Hi と返事をします。特に Yo の方は、 ignore_bot のコンディションを有効にしないと、自身の発言に対しても Yo と反応してしまい、無限ループが発生します。

現在、デフォルトで用意されている condition は、 ignore_bot と reply_to_me で、いずれも bool の値をとります。ですが、次のようにして独自の condition を定義することも可能です。

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のコードリーディング本を頒布します


技術書展5に当選しました

当選してしまったので、技術系の同人誌を書こうと思います。

最近の個人的な活動の成果として、SinatraのDSLっぽくBotが書けるMobbというプロダクトを作っています。そしてその過程で得たSinatraのコードリーディングの知見を広く共有するのが良いだろうと思っているので、同人誌にしようと思います。

たまたま、今日の夕方に会社のイベントでこういう発表をしたのですが、これを更に詳細に掘り下げた本になると思います。

スペースはどこ

◎貴サークル「トレイリア学園」は、 い22 に配置されました。

どういう本にするの

Sinatraの全コードの解説を目指します。一冊読めば、自分でSinatraライクのDSLを書けるようになるくらいの本にしたいと思っています。対象読者層は、メタプログラミングRubyを読んだことがあるRubyエンジニアを想定していますが、それではあまりにもリーチが狭い(気がする)ので、メタプログラミングRubyの本を読んでいなくても簡単なエッセンスくらいは理解できる感じに解説を入れようと思っています。

また、現在コミットをしているMobbというBotフレームワークのプロジェクトに関しても、簡単な解説本を書こうと思っています。


カテゴリー: Ruby, プログラミング | コメント / トラックバック: 0個

追伸:Rubyのサンドボックスを作って、evalするBotを作った


注意:安全じゃないです

あらすじ

  • 入力されたRubyのコード文字列を安全にEvalするBotを作ったと主張する
  • 次々と安全ではないことがわかる
  • ちょっとずつ安全に向けて改良したが、まだまだ安全じゃない

詳細はここ↓

Rubyのサンドボックスを作って、evalするBotを作った

たくさん届いた指摘

前回の最後の追伸から一夜明けて、またいくつかの指摘を頂いた。それぞれに関して対策を講じていく。

refine CleanRoomできる

こういう指摘がきたので実行してみたところ、確かに壊れた。

検証のためにこういうコードを書いてみると、確かにusingしているオブジェクトの中で自身をrefineすると、すでに効いているusingが無効になるようだった。

試しに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に触れる


なるほどって感じでした、グローバル変数もかなりマズイです。

RubyのObjectに定義されたグローバル関数は、モジュール定義内で上書きすることができます。よく考えたら、ENVとかもここで書き換えておけば安全(なはず)なので、ENVの文字列チェックをやめこっちに移行しました。

https://github.com/kinoppyd/ruby-eval-bot/commit/8f0d06bc19d4d30ceca68723589391b6868604b8

気軽にSandboxということの楽しさとつらさ

社内で気軽にRubyのコードを実行できるBotが欲しくて、ものすごいマズイことが起きなければいいかなくらいの気持ちで作った実装を公開したら、思った以上の反響と邪悪な人たちと素敵な人達に反応してもらいました。もらった指摘はとても役立つもので、原因や対応策を考えるのはとても楽しかったです。

しかし、気軽なSandboxは当然気軽なものでしかなく、無限に襲ってくる脆弱性に対応するのはやっぱり大変です。ちょっと仕事の時間に遊びすぎたと反省しました。

願わくば、このエントリに追記が増えていきませんように。


カテゴリー: Ruby | コメント / トラックバック: 0個

Rubyのサンドボックスを作って、evalするBotを作った


注意:安全じゃありません

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などのモジュールのクラスメソッドに対して、実行すると例外を投げるメソッドを定義し、すべてのメソッドのエイリアスとして設定します。

実際のコードは次のようなものになりました。

File, Dir, IO, FileTestの全メソッドに加えて、Kernelの使っても問題なさそうなメソッド以外を、すべて例外を投げるようにaliasします。

まず各モジュールに対してbannned_methodという、コールすると例外を投げるメソッドを用意し、各モジュールのメソッド一覧で得られたすべてのメソッドに対して、このbannned_methodへのエイリアスを張ります。そうすることで、ちょっとでも危険そうな動作をすると、例外を投げるようになります。

Kernelのコードも結構塞いでいるので心配になりますが、ちょっとevalするのにKernelのメソッドが必要になるケースの方がイレギュラーなので、無視します。

これを実際に利用するには、次のようにします。

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で動かしたところ、早速邪悪なコードを放り込んでくれた隣の席の人がいました。こういうコードです。

なるほど、実際CleanRoomの中に入力されたコード文字列をペタッと貼っているだけなので、SQLインジェクションみたいなことができるわけですね……あんまり深く考えていなかった。

対策として、入れられた文字列がRubyのシンタックスとして正当かどうかをチェックするようにしました。少なくともRubyのシンタックスとして正当であれば、周りをCleanRoomで囲えば安全なはずです。

https://github.com/kinoppyd/ruby-eval-bot/commit/a1ae3efaefc7cc9b1d57197a59ff07fc4e774c24

RubyVMを使って、渡された文字列からASTを作成できるかどうかをチェックしています。先程の例のような文字列が渡されるとSyntaxErrorがスローされるので、評価部分に入ることはありません。

ありがとう、隣の席の邪悪な人。

追記2

斜め後ろの席に座ってる邪悪なRubyコミッタの人がまたろくでもないコードを投げつけてくれました。こういうコードです。

MobbはENVに入っているSlackTokenを参照しているので、この一撃でTokenのRegenerateが必要になりました。Regenerateしたとはいえ、Tokenがいきなり公衆の面前にさらされるのは結構精神的ダメージでかいので、これはへこみました。

putsとかの副作用系は封じていたので平気だったと思っていましたが、よく考えたらinspectとかto_sとかの方法で出力は得られるので、盲点でした。

対策として、ENVにアクセスしようとするコードは一律排除することにしました。結構力技で排除しているので、これはなんかいろいろこねくり回したら回避できる気もしますが、一旦入れておきます。

https://github.com/kinoppyd/ruby-eval-bot/commit/c90a1d24125a816b2490b95375ce839b8a44e76b

ありがとう、斜め後ろの席の邪悪な人。

追記3

下のフロアで働いてる邪悪なセキュリティマニアが、邪悪なコードを優しく送ってくれました。こういうコードです。

なるほどね、そういえばASTはevalできるんだよね……という気持ちになりました。

https://github.com/kinoppyd/ruby-eval-bot/commit/5125b409ae22bd985c4c077e3a4900f90e0563cf

ありがとう、下のフロアの邪悪な人。

追記4

TD社で働いている素敵な人から、はてブのコメント経由で指摘をいただきました。Object空間のKernel系は塞いているけど、Kernelを直接呼ぶとダメじゃない? ということです。つまり、こういうことです。

完全にうっかりしていたので、慌てて塞ぎました。GitHubが落ちててPushできなくて辛かったです。

https://github.com/kinoppyd/ruby-eval-bot/commit/d35e1589616b50823d17c956c13e062871217168

ありがとう、TD社の素敵な人。

追記5

にょろにょろした素敵なアイコンの人から、TwitterでProcessが塞がれてないという指摘をいただきました。こういうことです。

うっかりです。というか、モジュールが多すぎて見逃しがたくさんあります。

https://github.com/kinoppyd/ruby-eval-bot/commit/977ab403cdff493c33101ed35426550c75e037d3

ありがとう、にょろにょろしたアイコンの素敵な人。


カテゴリー: Ruby | コメント: 2 件