在上一篇我們了解了如何整合Lighthouse到CI
這篇我們會來會來了解一下CodeQL,以及如何用CodeQL做一個超簡單的檢查
CodeQL 是 GitHub 提供的一種靜態分析工具
,用來檢查
程式碼中的潛在安全漏洞
,支援JavaScript、TypeScript、Python、Java、Go、C#、Kotlin等語言
它會將C#、Java程式碼直接轉換成可查詢的CodeQL資料庫
,其他語言則需要自行建立CodeQL資料庫,有了CodeQL資料庫開發人員就能使用查詢語言來檢查
潛在的漏洞(e.g.: SQL injection、XSS)
不過只有符合以下條件的repo能使用CodeQL
1.公開repo
2.企業版用戶的私有repo
下載VSIX檔,然後把選左邊sidebar的...,然後選install from visx,之後選剛剛下載的檔案進行安裝
左邊sidebar選QL,然後點database區的github icon去import你的repo
按shift+ctrl+P叫出VS Code的command palette,選CodeQL: Quick Query,然後選JavaScript,最後輸入CodeQL檔案要放的路徑
接著就會看到資料夾中多了一些檔案
每個 QL pack 一定要有 codeql-pack.yml
,裡面包含了一些重要資訊
import javascript
from File f
select f, "Hello, world!"
先不管內容是甚麼,一樣打開選CodeQL: Run Query on Selected Database,或者點下方任一張圖黃圈處也可以在本地執行檢查
之後就會看到檢查的結果囉
.ql檔案中只能有一個query
,而一個query的結構跟SQL相當類似,由變數、select、where組成
引入library
/* 引入JavaScript、TypeScript的library */
import javascript
宣告變數
定義要用於query的變數,格式為from 型別 變數名
from Expr e
條件
格式為where 條件
、where 條件1 and 條件2
/* e.isPure()`檢查變數e沒有side effect */
/* e.getParent()`檢查變數e的父層是否為表達式 */
where e.isPure() and e.getParent() instanceof ExprStmt
另外需注意,如果條件是涉及值的判斷的,只能使用雙引號
where f.getName() = "foo"
定義檢查的項目、返回的message
格式為select 檢查的目標, "返回的訊息"
select e, "This expression has no effect."
import javascript
from Expr e
where e.isPure() and
e.getParent() instanceof ExprStmt
select e, "This expression has no effect."
整個合起來看的話整個query代表檢查所有表達式,如果發現沒有side effect且父層為表達式的表達式,就返回"This expression has no effect."
這邊只抽幾個出來大略介紹,更多內容可以看Data flow cheat sheet for JavaScript
.getName()
(多個使用對象)取得變數、function、class、屬性名
// 取得multiplier、personData
const multiplier = (a, b) => a * b;
const personData = {
name: 'Jasmine',
email: 'a123@gmail.com'
}
.getACall()
取得某個function被呼叫的地方
// 找multiplier、Object.keys()被呼叫的地方
multiplier(2, 3);
Object.keys(personData);
.getArgument(index)
取得傳給function的argument
// 視傳入的index取得2或3
multiplier(2, 3);
.getParameter(index)
取得傳給function的parameter
// 視傳入的index取得a或b
const multiplier = (a, b) => a * b;
.getRhs()
(多個使用對象)取得賦值表達式右側的內容
// 會取得100
tempNum = 100;
// 會取得365
constantNumber = 365;
.getAPropertyRead(propName)
取得讀取物件屬性值的地方
console.log(personData.userName);
.getAPropertyWrite(propName)
取得某物件屬性被賦予值的地方
personData.userName = 'Tempura';
.getAPropertySource(prop)
取得賦予給物件屬性的值
// 尋找賦值為"Tempura"的地方
personData.userName = 'Tempura';
.hasFlowPath(source, sink)、
檢查兩個變數或表達式之間是否存在data flow
// 以下兩個變數存在data flow
tempNum = constantNumber;
.hasFlow(source, sink)
檢查兩個表達式之間是否存在data flow
// 以下兩個變數存在data flow
let days = constantNumber;
.getAMethodCall(methodName)
和.getACall()類似,但只取得某個物件、class中的方法被呼叫的地方
Object.keys(bar);
[].push(1);
name: Try CodeQL
on:
push:
branches:
- master
# 如果Workflow permissions設為"Read repository contents and packages permissions"代表是restricted模式,
# restricted模式下GITHUB_TOKEN就只會有content和package的read權限,此時就需要在workflow內設定permission
# 關於restriced、permissive模式有的預設權限看https://docs.github.com/en/actions/security-for-github-actions/security-guides/automatic-token-authentication#permissions-for-the-github_token
permissions:
actions: read
security-events: write
jobs:
give-a-try:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
# Initializes CodeQL tools and creates a codebase for analysis.
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: JavaScript
# 如果是需要編譯的語言則要在定義build-mode,或自行寫build指令,JavaScript、TypeScript這兩者皆不需要
# build-mode: autobuild
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
with:
category: "/language:JavaScript"
output: check-results
upload: failure-only
完成後每次push就會到master就會做檢查
log內有個連結,點下去會導到tools/CodeQL,點黃圈處可以看最新的CodeQL report