9月7日、GitHubは「GitHubのコードスキャンで開発者の幸福度を高める」を公開した。
Increasing developer happiness with GitHub code scanning
この記事では、GitHubのコードスキャンを活用した例や、コードスキャンのコード解析エンジンであるCodeQLを最大限に活用するための方法を紹介している。
GitHub社内でもCodeQLを活用していて、これにより、簡単にできるのに見つけるのが難しい、厄介なコーディングミスから身を守り、コードの品質を高く保つことができるという。
メモリリークの解消
Goのdeferステートメントは、周囲の関数が戻るまで、関数の実行を延期する。
これは後処理に便利である。例えば、ファイルハンドルのようなリソースを閉じたり、データベースのトランザクションを完了したりする。
既存コードを変更する際に、defer文をループの中に移動させてしまうことがある。
そうすると、関数の最後までクリーンアップを待たなければならず、ループの最後にはクリーンアップが行われない。
このミスにより、本番でメモリリークが発生することがある。
この間違いを指摘してもらえたら最高ではないかと、4行のCodeQLを用意した。
クレジット: GitHub
知らなかったでは済まされないエラー
私たちは、いくつかのコードベースでGORM(Go Object Relational Mapper)を使用している。
GORMでのエラー処理は、チェーン可能なAPIを持っているため、Goのイディオムとは異なる。
以下がその例である。
if err := db.Where("name = ?", "jinzhu").First(&user).Error; err !=
nil {
// error handling...
}
db.Where("name = ?", "jinzhu").First(&user)のようなコードを書いて、そのErrorフィールドをチェックしないのは簡単だ。
少なくとも、以前は簡単にできた。
現在、私たちは、関連するErrorフィールドをチェックしないGORM呼び出しを検出するCodeQLクエリを作成し、プルリクエストでこれらの呼び出しにフラグを立てている。
CodeQLのsecurity-and-quality query suiteには、ポインタを返す関数のエラーチェックをする同様のクエリがある。
パフォーマンス問題
エラーチェックの欠落を防ぐだけでなく、データベースへの問い合わせコードのパフォーマンスを維持したいと考えている。
"N+1クエリ"はよくあるパフォーマンス問題だ。
これは、高価な処理がセットの各メンバーに対して1回ずつ実行されるもので、アイテム数が増えるとコードが遅くなる。
ループ内のデータベース呼び出しが原因であることが多く、一般的には、ループ外でバッチクエリを実行した方がパフォーマンスは向上する。
カスタムCodeQLクエリを作成し、実際にクエリが実行された結果となるGORMメソッドの呼び出しを探す。
ループ内で発生した呼び出しのリストをフィルタリングし、それがあればCIを失敗させる。
CodeQLの優れた点は、ループ内で直接データベースを呼び出す場合に限らず、ループから直接または間接的に呼び出される関数内の呼び出しも検出されることだ。
これらのクエリを使用する
これらのクエリは実験的なもので、GitHubの標準suitesには含まれていない。
しかし、私たちが作成した特別なquery suiteを参照することで、これらを使用することができる。
まず、解析したいリポジトリに .github/codeql/go-developer-happiness.qls というファイルを作成する。
- import: codeql-suites/go-developer-happiness.qls
from: codeql-go
次に、CodeQLのワークフローを設定し(または既存のワークフローを編集し)、テンプレートの"Initialize CodeQL"の部分を以下のように修正する。
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: go
queries: ./.github/codeql/go-developer-happiness.qls
詳しい情報や設定例については、GitHubのコードスキャンでカスタムCodeQLクエリを実行するためのドキュメントを参照。
CodeQLの書き方については、ドキュメントやディスカッションで詳しく説明している。
また、クエリをコミュニティに還元する方法については、https://github.com/github/codeqlにあるCodeQLリポジトリで紹介している。