本記事は2021年7月21日に公開した英語ブログSAST tools speed comparison: Snyk Code vs SonarQube and LGTMを日本語化した内容です。
Snyk Codeと一般的なSASTツール2種とのスキャン時間の比較について聞かれることが多くあります。LGTMとSonarQubeです。調査にあたって、いくつかの仮定をしましたが、透明性を確保するために詳細をこのブログで共有します。
TL;DR(結論)
静的アプリケーション・セキュリティ・テスト(SAST)は、ほぼリアルタイムのフィードバックを提供し、開発プロセスを遅らせることがない場合にのみ、開発者にとって「優しい」テストとなり得ます。Snyk CodeはLGTMの最大106倍高速です。平均して、Snyk CodeはSonarQubeの5倍、LGTMの14倍の速さです。要約すると、Snyk Codeは市場で最も高速なセマンティックスキャンニングエンジンの1つであることが証明されました。
検証方法
今回は、48のJavaScriptオープンソースリポジトリ(下記参照)を選定しました。これは、典型的な現代の開発者のコードセットを模倣することであり、JavaScriptは良い共通のデリミタであると思われました。我々は、現実に起こっている課題を再現するために、GitHub上のトップクラスのリポジトリからランダムにサンプルを選択しました。
まず1つ目のスキャナには、オープンソースの静的解析ツールとして広く利用されているSonarQubeのCommunity Editionを使用しました。ローカルで動作するため、そこそこのPCを用意する必要がありました。SonarCloudの無料版を使った以前のテストでは、「性能の良いPC上のSonarQubeは、無料のSonarCloudよりも高速である」という結果が出ていますので、クラウド版ではなく、ローカルエンジンを使うことは不公平ではありません。 既存の開発機(詳細は後述)を1台使わせてもらうことになりました。2つ目は、GitHubに買収されたSemmleという会社から生まれたLGTMです。LGTMは、CodeQLに基づく深いセマンティックコード検索を使用します。我々は、LGTMのSaaSサービスを利用しました。
最後に、Snyk Codeは、SnykのSASTソリューションです。旧DeepCode社のスキャンエンジンをベースにしており、Snykでの数ヶ月に及ぶさらなる開発を経てリリースされました。Snyk Codeの設計目標は、開発者に優しく、高精度であるだけでなく、非常に高速であることです。これを実現するために、独自の制約エンジンを使用しています。私たちは、(1)ライセンスによって実行と比較が可能であること、(2)これらはセマンティックエンジンであり(ESLintのような)リンターではないこと、(3)これらは一般的で広く使われているエンジンであること、(4)ローカルで実行中のものとSaaSがあること、からこの検証方法を選択しました。
SASTソリューションが開発者フレンドリーであるためには、スピードが重要です。よく知られていることですが、開発プロセス中にセキュリティ問題を特定し、コードがチェックインされる前に対処することができれば、最も効率的な開発が実現できます。開発者の頭の中には、コードが新鮮な状態で残っているのです。Snyk Codeは、開発者のワークフローにシームレスに組み込むことができるIDEプラグインを提供します。その上、Snyk Codeはわかりやすいデータフロー図と、同じ文脈のオープンソースライブラリで使用されている修正例などの豊富な解説を提供します。しかし、ここではテストの目的に合わせて、スピードに焦点を当てました。
テストでは、SonarQubeはローカルで、LGTMとSnyk CodeはSaaSとして各リポジトリに対してスキャンを実行しました。前述の通り、スキャン時間をネットワーク帯域幅に依存させたくなかったため、このことがリポジトリの選択につながりました。
また、実際の開発者が行っていることのシミレーションを目的としているため、ベンチマークではなく、実際のオープンソースリポジトリを選択しました。SASTツールの比較にベンチマークを使用することには問題があるので、私たちの目的には合わないでしょう。
テスト結果
以下、一般的な統計値(四捨五入)をスキャンタイム秒数で示します。(n=48、四捨五入して整数値で表示):
Average | Max | Min | 5-percentile | 95-percentile | Median | sd | |
---|---|---|---|---|---|---|---|
SonarQube | 110 | 895 | 63 | 63 | 257 | 77 | 127 |
LGTM | 312 | 1835 | 2 | 141 | 1046 | 189 | 352 |
Snyk Code | 22 | 162 | 5 | 5 | 72 | 12 | 28 |
その結果を散布図にしてみました(低い方が良い、対数スケール、R と ggplot2 で作成):
対数軸は、小さい値における広がりを強調しすぎています。実際のSnyk Codeの標準偏差は、例えばSonarQubeの標準偏差よりもはるかに小さいです。
以下は、値の箱ひげ図です(低いほど良い、すべての値をグラフに収めるために対数目盛りを使用、R と ggplot2 で実行、中央値と平均値が表示される):
このプロットは、ほとんどの場合において、Snykが他の2つのいずれよりも劇的に高速であることを明確に示しています。Snykの中央値は6.7倍(SonarQube)から最大16.4倍(LGTM)速く、この結果は一部の例外的なよい値に依存しているのではなく、一般的なものであることを示しています。
上図では、LGTMの欄の数値の広がりが注目されます。2分(なんとか大丈夫)から17分以上(許容できない)と大きな差があります。開発者は、仕事の合間に数分もかけて結果を待つようなことはしないでしょう。幸いなことに、LGTMの中央値は3分程度です。
最初に行ったスピードテスト(当時はDeepCode、今回はSnyk Code)と比較すると、パフォーマンスが大幅に向上していることに気づきました。ここ数ヶ月でクラウドサービスの負荷を最適化したことが理由であると考えられます。また、今回、SonarQubeが高速になったのは、最初のテストと比較して、並列度の高い強力なPCを使用したためと思われます。
もう一つ興味深いのは、SaaSとローカルインストールされたエンジンは、実はスピードの観点からはそれほど差がないことです。
前提条件と制約
- SonarQube (Community Edition 8.9.1)とLGTMは、ライセンス上比較可能であり、広く利用されているため、この2つを選びました。
- また、48の中規模なJavaScriptリポジトリを選択しました。この検証方法は、典型的な開発者の作業を反映していると思われるからです。GitHub の上位 20 万件のリポジトリからランダムに抽出しました。コードサイズは基準ではありません。
- フルスキャンを強制し、差分スキャンの仕組みは使いませんでした。例えば、IDEプラグインのSnyk Codeの場合、最初のスキャンの後、IDEプラグインが差分スキャンを行い、帯域幅を節約しています。
- Intel Xeon @ 2GHz、16コア、64GB RAMを使用しました。
生データ
また、生データを提供するので、みなさまも独自に集計してみてください(単位・秒):
epository | SonarQube | LGTM | Snyk Code |
---|---|---|---|
neumino/chateau | 101 | 259 | 9 |
ecomfe/etpl | 80 | 237 | 24 |
ustwo/ustwo.com-frontend | 77 | 278 | 20 |
node-inspector/node-inspector | 157 | 373 | 21 |
q-nick/npm-gui | 71 | 227 | 8 |
dojo/dojox | 317 | 662 | 53 |
majimboo/node-benchmarks | 63 | 141 | 5 |
prettier/prettier | 84 | 277 | 45 |
d3/d3.github.com | 895 | 1104 | 67 |
adobe/node-smb-server | 79 | 194 | 13 |
noble/bleno | 66 | 141 | 5 |
wagtail/wagtail | 115 | 1462 | 56 |
meteor/blaze | 83 | 194 | 12 |
dwyl/hapi-socketio-redis-chat-example | 68 | 141 | 5 |
chancancode/hn-reader | 64 | 165 | 20 |
shipshapecode/ember-shepherd | 82 | 152 | 30 |
draptik/angulardemorestful | 76 | 163 | 6 |
icebob/vue-express-mongo-boilerplate | 77 | 422 | 9 |
ngryman/ribs | 64 | 193 | 7 |
rjrodger/seneca-examples | 70 | 176 | 12 |
telepat-io/telepat-api | 68 | 173 | 8 |
sematext/logagent-js | 68 | 162 | 8 |
cookpad/elasticfox-ec2tag | 87 | 173 | 11 |
polonel/trudesk | 170 | 1835 | 77 |
saintedlama/passport-local-mongoose | 72 | 173 | 7 |
cholalabs/passport-localapikey | 63 | 3 | 5 |
mozilla/openbadger | 79 | 184 | 20 |
inbasic/turbo-download-manager | 94 | 206 | 31 |
pgherveou/gulp-awspublish | 65 | 152 | 5 |
swagger-api/swagger-socket | 93 | 746 | 7 |
tactivos/jquery-sew | 69 | 225 | 5 |
johansatge/jpeg-autorotate | 63 | 152 | 6 |
mqttjs/mqtt-packet | 65 | 173 | 12 |
fulcrum-agile/fulcrum | 80 | 152 | 18 |
microweber/microweber | 300 | 937 | 162 |
adamhalasz/uniqid | 68 | 151 | 5 |
componentjs/builder2.js | 71 | 142 | 8 |
frankyghost/projekktor | 92 | 195 | 9 |
nielsutrecht/jwt-angular-spring | 88 | 288 | 8 |
petereigenschink/steganography.js | 69 | 141 | 5 |
mixteam/mixsln | 75 | 205 | 48 |
borismus/webvr-boilerplate | 70 | 213 | 42 |
arrix/node-readability | 77 | 152 | 19 |
iamcal/js-emoji | 72 | 412 | 6 |
jbavari/ionic-socket.io-redis-chat | 149 | 384 | 19 |
cheeaun/steepless | 69 | 142 | 5 |
joewalker/devtools.html | 177 | 2 | 74 |
gaearon/react-hot-loader | 80 | 162 | 15 |