在 GitLab CI 中使用 Google osv-scanner

DevSecOps 議題持續熱絡,目前市面上已經有許多可以放進 CI Pipeline 中的掃描工具,既然 Google 也推出了一個新的 Vulnerability 掃描工具,我們當然一定要將它塞進 GitLab CI 試試看啊!

(本文首次發佈於 2023-01-01,於 2023-10-24 更新。)

根據 iThome 的報導,Google 釋出了一個新的免費開源軟體漏洞掃描工具 OSV-Scanner,可以幫助開發者自動掃描找出專案內的相依套件是否存有漏洞。看見這樣的好工具,我們第一時間當然要問一句,這玩意能夠放進我們的 CI Pipeline 嗎?是否能有助於我們及早發現程式碼中潛藏的資安問題呢?

因此本文,就讓我們試著在 GitLab CI Pipeline 中運用 OSV-Scanner 吧!

操作步驟

尋找可用的 Docker Image

老樣子,想要在 GitLab CI Pipeline 中試用一個新工具的第一步,當然是先找找看有沒有現成的 Docker image 可以使用。

在 2023/10/24 於 Docker Hub 上搜尋「osv-scanner」,目前都只能找到非 Google 官方建立的 Docker Image。

(截圖時間:2023/01/01)

不知為何這些 Docker Image 看起來都沒什麼人 Download 使用,稍微深入瀏覽後,可以發現只有 anmalkov/osv-scanner 的說明較完整,有興趣者可以嘗試看看。

anmalkov/osv-scanner 為例,我用它來嘗試掃描了手邊的一個專案,得到如下圖的結果,它依據 package-lock.json 的資訊,幫我找出許多的 Vulnerabilities,並且每一個 Vulnerability 都會提供一個 OSV URL,讓我可以了解更多的詳細資訊。

OSV URL 的網頁內容如下,會有完整的漏洞說明。

如果你想要自己 Build 一個 Image,則可以參考 GitHub Repo - osv-scanner ,在 Repo 中有提供 Dockerfile ,可以用來 Build image。

或者如果你想要用偷懶一點的方式 Build image,那你只需要如下的 Dockerfile 即可。

FROM alpine:3.18

RUN apk update && apk add osv-scanner

WORKDIR /root/

在本文我就是用偷懶的方式,自行 Build 了一個可以讓我在 GitLab CI 中使用的 Image,並上傳 Docker Hub - chengweisdocker/osv-scanner-for-ci

下面我們就快速的運用 gitlab.com 做一個簡單的 DEMO,如何在 GitLab CI 中使用 osv-scanner

準備一份乾淨的程式碼

第一步先利用 Create from template 功能,建立一個乾淨的 Project,Template 選擇 NodeJS Express

準備 GitLab Runner

既然前面我們尋找了合適的 Docker image,當然代表接下來在 CI Pipeline 中,我們打算用 Container 的方式來執行 osv-scanner。因此,老樣子請為我們剛才準備好的 Project 準備一個能夠操控 Container 的 GitLab Runner,或者你也可以直接使用 gitlab.com 提供的 Shared Runners。

(如果不打算自己架設 GitLab Runner,那就先用公用的 Shared runners 吧!但要記得這是有 Free limit 限制的喔!)

建立 .gitlab-ci.yml

接著就在我們的 Project 中新增 .gitlab-ci.yml

# .gitlab-ci.yml
osv-scanner:
  # 我當然是使用我自己做好的 image
  image: chengweisdocker/osv-scanner-for-ci:1.3.3
  script:
  ## set +e 是的特別的動作,下面會再說明
  - set +e
  ## 讓 osv-scanner 吐出 JSON 格式的結果,然後儲存為 .json 檔案
  ### 2023-10-24 更新:
  ### 由於 osv scanner 在 1.3.3 版本依然沒有 --output 這個參數可以使用,因此只能自己存成檔案
  ### 但 1.3.3 版已經有提供 -f --format,並提醒參數 --json 將被棄用。
  - osv-scanner -f json -r . > osv-report.json
  ## 印出來看看
  - cat osv-report.json
  ## 將 .json 上傳為 Artifats
  artifacts:
    paths:
    - osv-report.json

在上面的 script: 中,我們有使用了一個特別的動作 set +e,這是為什麼呢?

主要是因為 GitLab CI 在執行 Job 的過程中,會是以 set -e 的方式執行動作。也就是 GitLab CI 在執行 script: 內的各種指令時,只要任何一個指令的 Exit code / Return code 不等於 0 時,GitLab CI 就會立即判定 Job failed;而偏偏 osv-scanner 如果掃描到 Project 有使用不安全的套件時,就是會回傳 Exit code / Return code = 1(如果 osv-scanner 找不到可以掃描的檔案,則是回傳 Exit code / Return code = 128 )。

因此當 GitLab CI 在執行 osv-scanner -f json -r . > osv-report.json 時,就會因為 Exit code = 1 而直接 failed 中斷整個 CI Job,並不會繼續執行將 osv-report.json 上傳 Job Artifacts 的動作。

(Job log 明確的指出,是因為 exit code 1 所以 failed。)(截圖日期:2023/01/01,當時還是舊版本的 osv-scanner,因此圖中是使用參數 --json

為了避開上述的問題,我們只好在 script: 一開始先執行 set +e,取消 -e 模式,這樣我們的 CI Job 才能夠產出 osv-report.json 並完成上傳 Job Artifacts。

(有了 set +e,Job 就不會 failed 而會繼續執行下去。)(截圖日期:2023/01/01,當時還是舊版本的 osv-scanner,因此圖中是使用參數 --json

(當然也就能夠順利執行上傳 Job Artifacts 的動作。)

打開 osv-report.json 可以發現被掃出滿滿的 Vulnerabilities。

小結

在今天的文章中,我們簡單的試用了 osv-scanner,並且嘗試將它塞進 GitLab CI Pipeline 中。

osv-scanner 作為一個新興的 Vulnerability 掃描工具,不僅掃描速度快速,並且能跨多種程式語言一次掃描完畢,掃描結果也能輸出為常見的 JSON 格式。以這幾點來說 osv-scanner 的表現令人滿意,如果你還沒有為 Project 套用任何 Vulnerability 掃描工具,那麼不妨試試看 osv-scanner

另外今天也順便介紹了在 GitLab CI 中設置 set +e 的作法,如果你也有不得不讓 script: 持續執行下去的需求,那麼 set +e 就是你一定要學會的小技巧。但建議大家,可以更多的認識 set ,除了 -e+e 其實還有著不同的參數,有興趣者可以找個 Linux 環境試著用 set --help 查看一下。

最後,作為上面小技巧的延伸議題,如果我們換個角度想,其實 osv-scanner 在有發現 Vulnerability 時會回傳 exit code = 1 ,反而是一件好事;舉例來說假設你想要設置一條嚴格絕對不容任何 Vulnerability 的 CI Pipeline,那麼因為 exit code = 1 必然會導致 Job failed,意味著團隊非得要修正 Project 直到能通過掃描,不然 osv-scanner 可是不會回傳 exit code = 0 的。

參考資料

轉貼本文時禁止修改,禁止商業使用,並且必須註明來自「艦長,你有事嗎?」原創作者 Cheng Wei Chen,及附上原文連結。

工商服務

更多文章