Code QL
現在DevOps関係の仕事をしているが、そこで各種ツールをCI/CDパイプラインに組み込み、自動化する環境の構築をしている。
ここでは、コードの脆弱性を検出することができる、CodeQLについて実際に使ってみたので備忘録として残すことにする。
CodeQLを学ぶ上でのリファレンス
CodeQLを学ぶ上でどこを参照すればよいか、初めはよくわからない。
というのもCodeQLについて専門で書かれている書籍は見当たらなかったからだ。
そこで、ネットで検索してみるとCodeQLの公式documentがgithubから提供されていることがわかった。
・CodeQL documentation
一応ここにCodeQLについてのすべてが記載されている。
ただ、私のような新米エンジニアにとってはなかなかハードルが高く、読んでもイマイチピンとこないことが多い。(ベースが英語なので、英語に苦手意識がある人はさらにきついかも。googleの翻訳機能を使ってもイマイチ理解できないところが多々ある。)
ネットで探すと「CodeQLを使ってみた」的なブログがあり、それはそれで私にもわかりやすい形で書かれていて理解を助けてくれるが、網羅的ではないのと、本当に正しいか不安に思うこともある。
最近見つけたのが、Microsoftが公式に無料提供している学習サイトで、こちらは日本語にも対応していてかつわかりやすい表現で書かれている。
・GitHub CodeQLを使用したコードスキャン
・CodeQLを使用してコードベースのセキュリティの脆弱性を特定する
今回はこちらをベースに学んだことを記載していきたいと思う。
CodeQLとは
ソフトウェアを開発する上で、品質を担保するためにコーディングルール等が定められている。自動車や飛行機などに搭載されるソフトウェアでは、特にその安全性への要求が高いことから多くの標準ルールがあり、メーカーはそのルールをベースにした社内ルール等(以下、コーディング規約と呼ぶ)を作成している。
CodeQLは、ソースコードがそのようなコーディング規約に準拠しているかどうかを解析することができるツールである。
CodeQLを使用した解析では、まずソースコードからクエリ可能なCodeQLデータベースを抽出する。
そして、コーディング規約のルールに則ったクエリをデータベースに対して実行し、ソースコードのチェックを行う。
毎回手動で実行することは、ソースコードの量やコーディングルールの量からみて現実的ではない。Githubには、CodeQLとの連携がしやすくなる機能が備わっており、GitHubActionsと併用することで自動化の仕組みを構築することができる。
GitHub構成
解析対象
Bazelのビルドシステムを利用して今回は解析をしたいと思う。
以下リンク先に、Bazelの公式ページにC++のサンプルが公開されているので、こちらを今回は用いることにする。
https://docs.bazel.build/versions/main/tutorial/cpp.html
GitHub構成
GitHubの構成とファイルの中身は以下の通りである。
|--.github
| |--workflows
| | |--codeql-task.yml
|--main
| |--BUILD
| |--hello-world.cc
|--README.md
|--WORKSPACE
各ファイル中身
---
name: codeQL-task
on:
workflow_dispatch:
push:
branches:
- "develop"
- "main"
jobs:
codeql-job:
name: codeql-job
runs-on: ubuntu-latest
steps:
- name: Ceckout repository
uses: actions/checkout@v3
- name: Setup CodeQL
run: |
wget https://github.com/github/codeql-cli-binaries/releases/download/v2.11.4/codeql-linux64.zip
unzip codeql-linux64.zip
rm -f codeql-linux64.zip
git clone -b codeql-cli/v2.11.4 https://github.com/github/codeql.git ql
- name: Setup Bazel
run: |
sudo apt-get install openjdk-8-jdk
sudo add-apt-repository ppa:webupd8team/java
sudo apt-get update && sudo apt-get install oracle-java8-installer
echo "deb [arch=amd64] http://storage.googleapis.com/bazel-apt stable jdk1.8" | sudo tee /etc/apt/sources.list.d/bazel.list
curl https://bazel.build/bazel-release.pub.gpg | sudo apt-key add -
sudo apt-get update && sudo apt-get install bazel
sudo apt-get upgrade bazel
- name: Run codeQL-task
run: |
rm -rf codeql_work/codeql-database
git clone ${codeql-rulepack-path}/codeql-rule-packs.git
mkdir -p codeql_work
codeql/codeql database create codeql_work/codeql-database --language=cpp --command='bazel build //main:hello-world'
codeql/codeql database analyze codeql_work/codeql-database codeql-rule-packs/qls/test.qls \
--format=sarif-latest \
--output=codeql_work/codeql-analysis.sarif
echo "success"
- name: Upload SARIF file
uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: codeql_work/codeql-analysis.sarif
- name: Upload artifact
uses: actions/upload-artifact@v3
with:
name: SARIF file
path: codeql_work/codeql-analysis.sarif
- name: Clean workspace
if: ${{ always() }}
run: |
chmod +w ${{github.workspace}} -Rf
rm -rf ${{github.workspace}}/*
load("@rules_cc//cc:defs.bzl", "cc_binary")
cc_binary(
name = "hello-world",
srcs = ["hello-world.cc"],
)
#include <ctime>
#include <string>
#include <iostream>
std::string get_greet(const std::string& who) {
return "Hello " + who;
}
void print_localtime() {
std::time_t result = std::time(nullptr);
std::cout << std::asctime(std::localtime(&result));
}
int main(int argc, char** argv) {
std::string who = "world";
if (argc > 1) {
who = argv[1];
}
std::cout << get_greet(who) << std::endl;
print_localtime();
return 0;
}
WORKSPACEファイルは空でよい。
CodeQlルールパック
クエリを実行する前に、どのルールをチェックするかのルールパックを取得する必要がある。
共通で使用できるルールから、カスタマイズして作成できるルールもある。
ルールパックのファイル構成は以下の通りである。
|--ql
| |--commentout.ql
| |--cyclomatic-complexity.ql
| |--source-line-number.ql
|--qlpack.yml
|--qls
| |--test.qls
|--README.md
各ファイル中身
qlディレクトリ配下には、クエリのルールが格納されている。今回はAV ruleという公開されている3つのクエリを配置した。
/**
* @name AV Rule 127
* @description Code that is not used (commented out) shall be deleted.
* @kind problem
* @id cpp/jsf/av-rule-127
* @problem.severity recommendation
* @tags maintainability
* external/jsf
*/
import cpp
import external.ExternalArtifact
from DefectExternalData d
where d.getQueryPath() = "jsf/4.14 Comments/AV Rule 127.ql"
select d, d.getMessage()
/**
* @name cycloatic complexity
* @description All functions shall have a cyclomatic complexity number of 10 or less
* @kind problem
* @id cpp/jsf/av-rule-3
* @problem.severity error
* @tags maintainability
*/
import cpp
from Function f, int c
where
c = f.getMetrics().getCyclomaticComplexity() and
c >= 0
select f, "All functions shall have a cyclomatic complexity number of 10 or less"
/**
* @name AV Rule 1
* @description Any one function (or method) will contain no more than 200 logical source lines of code.
* @kind problem
* @id cpp/jsf/av-rule-1
* @problem.severity warning
* @tags maintainability
* external/jsf
*/
import cpp
from Function f, int n
where
n = f.getMetrics().getNumberOfLinesOfCode() and
n > 5
select f,
"AV Rule 1: any one function (or method) will contain no more than 200 logical source lines of code. Function '"
+ f.toString() + "' contains " + n.toString() + " lines of code."
rule-packのrootディレクトリには、以下のようなqlpack.ymlファイルを置く必要があり、このファイルにCodeQL分析で使用するメタデータを記載する。
name: test-local
version: 0.0.0
libraryPathDependencies: codeql-cpp
qlsディレクトリ配下には、CodeQLのクエリスイートを定義する。クエリースイートを定義すると、どのルール(今回の例では、qlディレクトリ配下に置いてあるどのルールか)を適用するかを定義することができる。qlpackとしては、qlpack.ymlで定義した名前を用いて、この下に3つのルールのうちのどのルールを適用するかも選択し記載することができる。また、別のqlpackからもルールを指定することが可能である。
クエリ実行時に、下記のtest.qlsを指定すると、ql配下の3つのクエリが実行される。
- description: test-suite
- qlpack: test-local
実行の流れ
ワークフローの定義ファイル(codeql-task.yml)を見ていただくとわかるが、基本的な流れとしては、環境Setup(CodeQL、Bazel)、CodeQLの実行、結果のUploadという流れで行われている。
CodeQLの実行は、以下の二つのステップで行える。
①DataBaseの作成
codeql database create codeql_work/codeql-database --language=cpp --command='bazel build //main:hello-world'
②クエリの実行
codeql database analyze codeql_work/codeql-database codeql-rule-packs/qls/test.qls \
--format=sarif-latest \
--output=codeql_work/codeql-analysis.sarif
動作結果
クエリが実行されているログがでているが、期待する動作が得られていないため、引き続き検証を行う。
ディスカッション
コメント一覧
まだ、コメントがありません