rails 5.1.6 のアプリをrails 5.2.0にアップデートしたときに困ったとこメモ

rails 5.1.6 のアプリをrails 5.2.0にアップデートしたときに、いろいろハマったのでメモ。

rails

rails側での作業メモ。

cannot load such file -- bootsnap/setup

とりあえず rails app:updateしてみる。 route.rbなどは上書きしたらダメだけど、boot.rbとか触った覚えのないファイルは上書きしていく。

$ rails app:update
conflict  config/boot.rb
Overwrite /Users/tkmru/code/old_app/config/boot.rb? (enter "h" for help) [Ynaqdh] Y
       force  config/boot.rb
       exist  config
    conflict  config/routes.rb
Overwrite /Users/tkmru/code/old_app/config/routes.rb? (enter "h" for help) [Ynaqdh] n
...

rails serverを起動するとbootsnapというrailsの起動を早くするgemがないとエラーが出た。railsが標準で使うようになったとのこと。

週刊Railsウォッチ(20170728)bootsnapがRailsで正式採用、Ruby Prizeの推薦開始、PostgreSQL配列の重複を除去ほか

gemファイルに追加してbundle installする。

$ bin/rails s
Traceback (most recent call last):
    3: from bin/rails:3:in `<main>'
    2: from bin/rails:3:in `require_relative'
    1: from /Users/tkmru/code/old_app/config/boot.rb:4:in `<top (required)>'
/Users/tkmru/code/old_app/config/boot.rb:4:in `require': cannot load such file -- bootsnap/setup (LoadError)

undefined method `halt_callback_chains_on_return_false=' for ActiveSupport:Module

halt_callback_chains_on_return_falseはRails 4との後方互換のためのメソッドで、rails serverを起動するとこれがないというエラーが出た。

$ bin/rails s
=> Booting Puma
=> Rails 5.2.0 application starting in development 
=> Run `rails server -h` for more startup options
Exiting
Traceback (most recent call last):
    73: from bin/rails:4:in `<main>'
    72: from /Users/tkmru/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/activesupport-5.2.0/lib/active_support/dependencies.rb:283:in `require'
    71: from /Users/tkmru/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/activesupport-5.2.0/lib/active_support/dependencies.rb:249:in `load_dependency'
    70: from /Users/tkmru/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/activesupport-5.2.0/lib/active_support/dependencies.rb:283:in `block in require'
...
    10: from /Users/tkmru/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/railties-5.2.0/lib/rails/engine.rb:613:in `each'
     9: from /Users/tkmru/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/railties-5.2.0/lib/rails/engine.rb:614:in `block (2 levels) in <class:Engine>'
     8: from /Users/tkmru/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/railties-5.2.0/lib/rails/engine.rb:656:in `load_config_initializer'
     7: from /Users/tkmru/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/activesupport-5.2.0/lib/active_support/notifications.rb:170:in `instrument'
     6: from /Users/tkmru/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/railties-5.2.0/lib/rails/engine.rb:657:in `block in load_config_initializer'
     5: from /Users/tkmru/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/activesupport-5.2.0/lib/active_support/dependencies.rb:277:in `load'
     4: from /Users/tkmru/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/activesupport-5.2.0/lib/active_support/dependencies.rb:249:in `load_dependency'
     3: from /Users/tkmru/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/activesupport-5.2.0/lib/active_support/dependencies.rb:277:in `block in load'
     2: from /Users/tkmru/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/bootsnap-1.3.0/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:50:in `load'
     1: from /Users/tkmru/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/bootsnap-1.3.0/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:50:in `load'
/Users/tkmru/code/old_app/config/initializers/new_framework_defaults.rb:23:in `<main>': undefined method `halt_callback_chains_on_return_false=' for ActiveSupport:Module (NoMethodError)

Ruby on Rails 5.2.0 Deprecations – Drifting Rubyによると、 5.2よりhalt_callback_chains_on_return_falseは削除されたみたい。 config/initializers/new_framework_defaults.rbの中のhalt_callback_chains_on_return_falseの行をコメントアウトした。

# Be sure to restart your server when you modify this file.
#
# This file contains migration options to ease your Rails 5.0 upgrade.
#
# Read the Guide for Upgrading Ruby on Rails for more info on each option.

Rails.application.config.raise_on_unfiltered_parameters = true

# Enable per-form CSRF tokens. Previous versions had false.
Rails.application.config.action_controller.per_form_csrf_tokens = true

# Enable origin-checking CSRF mitigation. Previous versions had false.
Rails.application.config.action_controller.forgery_protection_origin_check = true

# Make Ruby 2.4 preserve the timezone of the receiver when calling `to_time`.
# Previous versions had false.
ActiveSupport.to_time_preserves_timezone = true

# Require `belongs_to` associations by default. Previous versions had false.
Rails.application.config.active_record.belongs_to_required_by_default = true

# Do not halt callback chains when a callback returns false. Previous versions had true.
# ActiveSupport.halt_callback_chains_on_return_false = false # これをコメントアウトした!!

# Configure SSL options to enable HSTS with subdomains. Previous versions had false.
Rails.application.config.ssl_options = { hsts: { subdomains: true } }

これでrailsは起動するようになった。

frontend 側

フロントエンド側の作業メモ

can't find executable webpack for gem webpacker

webpackを実行してみると、gemのwebpackerから動かせるwebpackがないってエラーが出た。

$ bin/webpack
Traceback (most recent call last):
    2: from bin/webpack:17:in `<main>'
    1: from /Users/tkmru/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/bundler-1.16.1/lib/bundler/rubygems_integration.rb:489:in `block in replace_bin_path'
/Users/tkmru/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/bundler-1.16.1/lib/bundler/rubygems_integration.rb:458:in `block in replace_bin_path': can't find executable webpack for gem webpacker (Gem::Exception)

rails webpacker:binstubsでwebpackのバイナリを再インストールすると、webpackも動くようになった。

$ bin/rails webpacker:binstubs
Copying binstubs
       exist  bin
    conflict  bin/webpack
Overwrite /Users/tkmru/code/old_app/bin/webpack? (enter "h" for help) [Ynaqdh] Y
       force  bin/webpack
    conflict  bin/webpack-dev-server
Overwrite /Users/tkmru/code/old_app/bin/webpack-dev-server? (enter "h" for help) [Ynaqdh] Y
       force  bin/webpack-dev-server

$ bin/webpack
set is deprecated! Use append instead

Hash: 9c54321948fe9d42f554
Version: webpack 3.11.0
Time: 4625ms
                                                                                       Asset     Size  Chunks                    Chunk Names
                                                         application-07d7627f2dffd5e35ab1.js   607 kB       1  [emitted]  [big]  application
  _/node_modules/font-awesome/fonts/fontawesome-webfont-674f50d287a8c48dc19ba404d20fe713.eot   166 kB          [emitted]         
 _/node_modules/font-awesome/fonts/fontawesome-webfont-fee66e712a8a08eef5805a46892932ad.woff    98 kB          [emitted]         
...

技術書展4で TomoriNao vol.2 頒布します!!!

技術書展4で TomoriNao vol.2 頒布します!! 場所は「お16」です!!!

techbookfest.org

f:id:TAKEmaru:20180416133313j:plain

f:id:TAKEmaru:20180415220914j:plain

ぼくは、「アンチデバックのためのデッドコードをLLVM optimizerで緩和してみる」という題で書きました。 LLVMのパスを使った難読化/難読化緩和の研究がちょくちょくあるけど、 バイナリをLLVM IRにデコンパイルして、LLVMのデフォルトの最適化パスを使うだけで、 デッドコードくらいならまあまあ消えるのでは〜という実験をやってみたという内容です。

そのうち、自作パスによる難読化/難読化緩和もやってみたいですね!!

みなさま!ご購入の検討を!!よろしくお願いします!!!

JSONを扱いやすくするコマンド(jq, gron)のメモ

JSONを扱いやすくするコマンドのjqgronのメモ。

jq コマンド

jq コマンドはJSON向けのawkのようなもの。

インストール方法(on Mac

$ brew install jq

使い方

パイプで渡されたJSONデータにフィルターをかけることができる。

githubリポジトリ名を列挙する例

$ curl -s `curl -s https://api.github.com/users/tkmru | jq -r .repos_url`  | jq '.[].name'
"anarchy_proof"
"anti_debugging_test"
"aoj"
"awesome-linux-rootkits"
"a_and_d_web_nara"
"biwx"
...
"isucon4-qualifier-mokumoku"
"isucon6-qual-practice"
"isucon_pixv"
"ldap3"
"linux-driver-playground"
"linux-insides"
"linux-insides-ja"
"machine-learning-playground"

最新コミット情報を取って来る例

$ curl 'https://api.github.com/repos/tkmru/dotfiles/commits?per_page=2' | jq '.[0]'
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  7328  100  7328    0     0   9488      0 --:--:-- --:--:-- --:--:--  9492
{
  "sha": "c764b1baad95cb18cec14a1b7f84b460b6f0b3cc",
  "commit": {
    "author": {
      "name": "tkmru",
      "email": "i.am.tkmru@gmail.com",
      "date": "2018-04-10T14:33:34Z"
    },
    "committer": {
      "name": "tkmru",
      "email": "i.am.tkmru@gmail.com",
      "date": "2018-04-10T14:33:34Z"
    },
    "message": "Merge branch 'master' of github.com:tkmru/dotfiles",
  ...
  "parents": [
    {
      "sha": "495ddbfd8c19564a5c72260eb9c857f2d7f3a6d5",
      "url": "https://api.github.com/repos/tkmru/dotfiles/commits/495ddbfd8c19564a5c72260eb9c857f2d7f3a6d5",
      "html_url": "https://github.com/tkmru/dotfiles/commit/495ddbfd8c19564a5c72260eb9c857f2d7f3a6d5"
    },
    {
      "sha": "7f0f75610a2856c6cc97a3b1d949f5a4345a53df",
      "url": "https://api.github.com/repos/tkmru/dotfiles/commits/7f0f75610a2856c6cc97a3b1d949f5a4345a53df",
      "html_url": "https://github.com/tkmru/dotfiles/commit/7f0f75610a2856c6cc97a3b1d949f5a4345a53df"
    }
  ]
}

コミットメッセージ、名前を列挙する例

$ curl 'https://api.github.com/repos/tkmru/dotfiles/commits?per_page=2' | jq '[.[] | {message: .commit.message, name: .commit.committer.name}]'
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  7328  100  7328    0     0   9382      0 --:--:-- --:--:-- --:--:--  9382
[
  {
    "message": "Merge branch 'master' of github.com:tkmru/dotfiles",
    "name": "tkmru"
  },
  {
    "message": "add ungron",
    "name": "tkmru"
  }
]

gron コマンド

JSONgrepしやすい形式、JavaScriptで扱える形式に変換してくれるコマンド。 Golang製。

インストール方法(on Mac

$ brew install gron

使い方

URLもしくはJSONファイルを指定すると変換してくれる。

$ gron "https://api.github.com/repos/tkmru/dotfiles/commits?per_page=1"
json = [];
json[0] = {};
json[0].author = {};
json[0].author.avatar_url = "https://avatars3.githubusercontent.com/u/1628214?v=4";
json[0].author.events_url = "https://api.github.com/users/tkmru/events{/privacy}";
json[0].author.followers_url = "https://api.github.com/users/tkmru/followers";
json[0].author.following_url = "https://api.github.com/users/tkmru/following{/other_user}";
....
json[0].parents[1].html_url = "https://github.com/tkmru/dotfiles/commit/7f0f75610a2856c6cc97a3b1d949f5a4345a53df";
json[0].parents[1].sha = "7f0f75610a2856c6cc97a3b1d949f5a4345a53df";
json[0].parents[1].url = "https://api.github.com/repos/tkmru/dotfiles/commits/7f0f75610a2856c6cc97a3b1d949f5a4345a53df";
json[0].sha = "c764b1baad95cb18cec14a1b7f84b460b6f0b3cc";
json[0].url = "https://api.github.com/repos/tkmru/dotfiles/commits/c764b1baad95cb18cec14a1b7f84b460b6f0b3cc";

これによってgrepしやすくなる。

$ gron "https://api.github.com/repos/tkmru/dotfiles/commits?per_page=1" | grep commit.message
json[0].commit.message = "Merge branch 'master' of github.com:tkmru/dotfiles";

ungron

gronの出力結果をJSONに戻すには--ungronオプションを付ければいい。 以下のようにaliasを設定するよう、READMEで推奨されている。

alias norg="gron --ungron"
alias ungron="gron --ungron"

grepした結果をJSONで欲しいときに使える。

$ gron "https://api.github.com/repos/tkmru/dotfiles/commits?per_page=1" | grep commit.message | ungron
[
  {
    "commit": {
      "message": "Merge branch 'master' of github.com:tkmru/dotfiles"
    }
  }
]

diffもgronを使えば見やすい結果で取れる。

$ diff <(gron http://echo.jsontest.com/number/one) <(gron http://echo.jsontest.com/number/two)
2c2
< json.number = "one";
---
> json.number = "two";

JavaScriptから出力結果をさわる

出力されたテキストはJavaScriptのObjectとして扱える。

$ gron http://headers.jsontest.com/ > tmp.js
$ echo "console.log(json);" >> tmp.js
$ node tmp.js
{ Accept: 'application/json',
  Host: 'headers.jsontest.com',
  'User-Agent': 'gron/dev',
  'X-Cloud-Trace-Context': '0f152eb1d17dc08fd257b8b4383faaa6/9190834354357706108' }

株式会社はてなに入社しました

株式会社はてなに入社しました

株式会社はてなに入社しました - hitode909の日記

IDAプラグインの更新をサボっていたら割れIDAへのリンクを貼られた

IDA 7.0が昨年9月くらいに出たので、IDA 6.xのプラグインはIDA 7.xに対応するべく更新する必要がありました。 更新されたAPIへの差し替え、32bitアプリケーションだったのが64bitアプリケーションになったことによる変更などなど....

www.hex-rays.com

naoというプラグインを昔つくっていて、これをIDA 7.xに対応させる必要があり、 GitHubリポジトリにIssueをたてられたりしましたが、 IDA 7.0を当時は持ってなかったのでその旨を返信して放置していました。 すると、これを使えよ〜というノリで割れIDAへのリンクを貼られてしまった!!!! 治安が悪い!!!!!

f:id:TAKEmaru:20180214060412p:plain

社会性の塊なので、このスクショでは割れIDAへのURLの一部をマスクしています。偉い!

github.com

その裏でIDA 7.xに対応するプルリクを投げてくれた親切な人がおり、今ではIDA 7.xに対応しています。めでたしめでたし。

https://github.com/tkmru/nao/pull/39github.com

Rails appのコードを変更したときに、Guardに自動でRuboCop、RSpecを実行してもらう

Guardとは

Guardはファイルに変更があったときに、タスクを走らせてくれるgemで、コードを変更したときに、静的なコードチェックをしてくれるRuboCopやRSpecで書かれたテストを実行させることができる。RuboCopやテストコードを随時走らせてコーディングしていくと、問題を即座に把握できるので、プルリク前にあわててテストコードを走らせて修正....というハメに陥らなくて済む。

導入方法

Gemfile

Gemfileに以下のgemを追加。RSpec、RuboCopの導入はすでに終わっているものとする。

group :development do
  ...
  gem 'guard-rspec'
  gem 'guard-rubocop'
  ...
end

インストールも忘れずに。

$ bundle install

Guardfile

Guardの設定ファイルを作成する必要がある。以下のコマンドで生成できる。

$ bundle exec guard init rspec
$ bundle exec guard init rubocop

このようなGuardfileになる。このファイルを変更することで、監視対象とするファイルを変更したり、rspecが落ちた時の RuboCopの実行をスキップしたりできる。

guard :rubocop, cli: '--rails' do
  watch(/.+\.rb$/)
  watch(%r{(?:.+/)?\.rubocop\.yml$}) { |m| File.dirname(m[0]) }
end

guard :rspec, cmd: 'bundle exec rspec' do
  require 'guard/rspec/dsl'
  dsl = Guard::RSpec::Dsl.new(self)

  # RSpec files
  rspec = dsl.rspec
  watch(rspec.spec_helper) { rspec.spec_dir }
  watch(rspec.spec_support) { rspec.spec_dir }
  watch(rspec.spec_files)

  # Ruby files
  ruby = dsl.ruby
  dsl.watch_spec_files_for(ruby.lib_files)

  # Rails files
  rails = dsl.rails(view_extensions: %w(erb haml slim))
  dsl.watch_spec_files_for(rails.app_files)
  dsl.watch_spec_files_for(rails.views)

  watch(rails.controllers) do |m|
    [
      rspec.spec.call("routing/#{m[1]}_routing"),
      rspec.spec.call("controllers/#{m[1]}_controller"),
      rspec.spec.call("acceptance/#{m[1]}")
    ]
  end

  watch(%r{^app/api/(.+)\.rb$}) { |m| "spec/api/#{m[1]}_spec.rb" }

  # Rails config changes
  watch(rails.spec_helper)     { rspec.spec_dir }
  watch(rails.routes)          { "#{rspec.spec_dir}/routing" }
  watch(rails.app_controller)  { "#{rspec.spec_dir}/controllers" }

  # Turnip features and steps
  watch(%r{^spec/features/(.+)\.feature$})
  watch(%r{^spec/steps/(.+)_steps\.rb$}) do |m|
    Dir[File.join("**/#{m[1]}.feature")][0] || 'spec/features'
  end
end

rspecが落ちた時に RuboCopの実行をスキップしたい場合は、 以下のようにGuardfileを変更するとよい。

# This group allows to skip running RuboCop when RSpec failed.
group :red_green_refactor, halt_on_fail: true do
  guard :rspec do
    # ...
  end

  guard :rubocop do
    # ...
  end
end

Guardを実行してファイルを監視

実行するだけ。

$ bundle exec guard

おわりに

Guardを使って開発しているとプルリクする前にあわててテストを実行しなくてよくて便利。 快適railsライフを送りましょう。

LLVM bitcodeのCFGを生成する

LLVM bitcodeとは

LLVM内ではLLVM IRという中間言語表現が用いられる。 ソースコードLLVM IRコードに変換したあと、そのLLVM IRコードをターゲットのアーキテクチャのバイナリに変換...という流れでコンパイルは行われる。 LLVM bitcode は LLVM IR をバイナリファイルに収めるためのフォーマットで、LLVM IRとほぼ同等であり、相互変換もできる。 LLVM IRは.llファイル、LLVM bitcodeは.bcファイルでそれぞれ表される。

$ llvm-as sample.ll # LLVM IRをLLVM bitcodeに変換
$ llvm-dis sample.bc # LLVM bitcodeをLLVM IRに変換

LLVM bitcodeはclangに-emit-llvmオプションを指定することで生成できる。

$ clang -emit-llvm -c -g test.c

CFGを生成する

optコマンドにより、dot-cfg passを実行することでLLVM bitcodeからdotファイルを生成する。 passはLLVM内で機能を構成する1単位であり、 最適化機構などはpassとして実装され、各passはoptコマンドのオプションで指定することで実行できる。 dotファイルにはグラフ構造が書き込まれており、Graphviz内のdotコマンドを使うことで画像として出力することができる。 macだとGraphvizのインストールは脳死Homebrewでできる。

ソースコード

ここではたまたま手元にあったDynamic Opaque Predicateを施されたCのソースを使った。 Dynamic Opaque Predicateは難読化の一種で、どの分岐を通っても最終的に同じ処理を行うよう分岐を施す難読化である。 Opaque Predicateについては、 LOOPGeneralized Dynamic Opaque Predicatesがくわしい。

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[]) {
  if(argc != 2)
    exit(1);

  int x = atoi(argv[1]); 
  int y = 0;

  if (x % 2 == 0) {
    y = x + 2;
  } else {
    y = x + 100;
  }

  if (y % 2 == 0) {
    y = y + 100;
  } else {
    y = y + 2;
  }

  printf("y = %d\n", y);

  return 0;
}

コマンド

$ clang -emit-llvm -m32 -c -g test.c 
$ opt test.bc  -dot-cfg
$ dot -Tpng cfg.main.dot > test.png

生成されたCFG

f:id:TAKEmaru:20180124003448p:plain