OSVについて調べてみた
はじめに
Googleがosv-scannerというリポジトリを公開しています。どうやら脆弱性スキャナのようです。
これを使ってみる前に、OSVとかSBOMとか、知らなかった概念について先に掘り下げて置こうと思います。この記事ではOSVについて見ていきます。
OSV
トップページ曰く「A distributed vulnerability database for Open Source」とのこと。
Aboutでは、OSVは以下のものから構成されると説明しています。
- OSV Schemaという脆弱性を記述するためのデータフォーマット
- OSV Schemaで記述された脆弱性情報を集約、参照するためのインフラストラクチャー
- サイトそのもの
- API
- ツール(osv-scannerもここに含まれる?)
OSV Schema
OSV Schemaがどんな感じか見てみることにします。
Aboutによると、OSV Schema策定にあたっての課題意識のひとつとして、各エコシステムでのパッケージの命名およびバージョニングとCPEを照合するのって難しいよね、というのがあるようです。
Enforces version specification that precisely matches naming and versioning schemes used in actual open source package ecosystems. For instance, matching a vulnerability such as a CVE to a package name and set of versions in a package manager is difficult to do in an automated way using existing mechanisms such as CPEs.
CPEという仕組み自体が結構きついよね、という話を以前フォロワーさんとした記憶があって、掘り返してみたらやっぱり嘆いてました。もっとも、今見返すと無意識的にプロプライエタリな製品について言及していたと思うので、このOSVのスコープ外ですかね。なんてったって「Open Source Vulnerabilities」だし。
NISTのCPE Dictionaryを見ると、製品によって充足ぶりにばらつきがあるのはなんで……?やる気……?
— shinobe179 (@shinobe179) 2020年10月7日
OSV Schemaは、以下のような情報を含んでいます。
- 脆弱性情報の公開日や更新日
- 要約と詳細
- 影響を受けるパッケージ
- 参考URL
- 重大度
severity
重大度を示す severity[]
は type
というフィールドを持ち、これは執筆時点で CVSS_V2
もしくは CVSS_V3
の値をとることが想定されています。
そのうちEPSSとかも入ってくるんですかね?
また、このスキーマはありものの脆弱性情報の効率的な記述方法を提案するものであって、CVSSにとって代わるような新たな脆弱性評価手法を提案するものではないことが分かります。
affected
各パッケージへの影響範囲を示す affected[]
は、以下の情報から構成されます。
- package
- ranges
- versions
- ecosystem_specific
- database_specific
package
脆弱性の影響を受けるパッケージに関する情報です。以下の値を持ちます。
purl-specというのは初耳でした。これもエコシステムによってパッケージの命名規則を統一することで参照しやすくしようというのがねらいのようです。
ranges
以下の値を持ちます。
- type:
indtoduced
およびfixed
で使うバージョンの表現方法。SEMVER
: SemVer 2.0.0 に則ったバージョン表記。ECOSYSTEM
: 各エコシステムに則ったバージョン表記。GIT
: Gitのコミットハッシュ。
- repo: パッケージのコードリポジトリのURL。
affected[].ranges[].type
がGIT
の時は必須。 - events[]: 脆弱性の各イベント(発生、修正など)に関する情報。
versions
脆弱性があるパッケージバージョンのリストです。 affected[].ranges
と役割が重複している感じがしますが、脆弱性があるバージョンを列挙してあるとぱっと見分かりやすいよねぐらいのフィールドなんだと勝手に解釈しています。
例えば、Log4Shellに該当するJSONファイルを見てみると、 affected.version
がありません。
しかしosv.devで同じ脆弱性のページを見てみると、(JSONに affected.version
はないのに)Affected versionsとして値がマッピングされていることが分かります。
後述する参考実装でも、rangesとversionsの両方を評価する実装になっているので、やはり意味合い的に重複するものみたいです。
affected[] による脆弱性評価
OSVで記述された脆弱性情報を用いて脆弱性評価をするコードの参考実装が紹介されていました。メインルーチンである IsVulnerable()
だけ読んでみると、以下のようなロジックになっています。
- 検査対象であるパッケージが
affected.package
と同一だったら次のチェックをする- 検査対象バージョンが
affected.versions
に含まれているか、affected.ranges
に含まれていたら脆弱性あり
- 検査対象バージョンが
- いずれにも該当しなければ脆弱性なし
func IsVulnerable(pkg, v, osv) for affected in osv.affected if affected.package == pkg if IncludedInVersions(v, affected.versions) || IncludedInRanges(v, affected.ranges) return true return false
疑問点
参考実装では pkg
と osv.affected[].package
を直接比較していますが、 これを成立させるために pkg
をOSV形式に直すのって実は地道な努力が必要だったりしないか?と思うなどしました。それこそosv-scannerのソースを読めば解決しそうですが、それはまた次のお話ということで。