💮

Go 公式の脆弱性管理システム

2022/08/11に公開

Go Vulnerability Management

Twitter の TL を眺めていて Go 公式の脆弱性レポートのページがあることを知る。

https://pkg.go.dev/vuln/list

で,これについて

https://twitter.com/spiegel_2007/status/1557187183072935936

とか呟いたら

https://twitter.com/mattn_jp/status/1557233641356271616

と情報を頂いた。ありがとうございます 🙇

件のページを覗いてみると,こんな感じで脆弱性管理を行っているらしい。


via “Go Vulnerability Management - The Go Programming Language

各番号の説明は以下。

  1. A data pipeline that populates the vulnerability database. Data about new vulnerabilities come directly from Go package maintainers or sources such as MITRE and GitHub. Reports are curated by the Go Security team.
  2. A vulnerability database that stores all information presented by govulncheck and can be consumed by other clients.
  3. A client library (golang.org/x/vuln/client), which reads data from the Go vulnerability database. This is also used by pkg.go.dev to surface vulnerabilities.
  4. A vulncheck API (golang.org/x/vuln/vulncheck), which is used to find vulnerabilities affecting Go packages and perform static analysis. This API is made available for clients that do not want to run the govulncheck binary, such as VS Code Go.
  5. The govulncheck command (golang.org/x/vuln/cmd/govulncheck, a wrapper around the vulncheck library for use on the command line.
  6. A web portal that presents information about vulnerabilities, hosted at pkg.go.dev/vuln.

Go の静的解析をやっておられる方なら client libraryvulncheck API あたりは興味が湧くのではないかと推察するが,今回はお手軽にコマンドライン・ツール govulncheck を試してみたいと思う。

govulncheck を試す

govulncheck のインストールは,まず Go のビルド環境を用意し

$ go install golang.org/x/vuln/cmd/govulncheck@latest
go: downloading golang.org/x/vuln v0.0.0-20220810233855-e1dd057bf2a3
...

でビルド結果が $GOPATH/bin または $GOBIN ディレクトリに出力される[1]。とりあえず -h オプションでヘルプを表示してみよう。

$ govulncheck -h
govulncheck: identify known vulnerabilities by call graph traversal.

Usage:

    govulncheck [flags] {package pattern...}

    govulncheck [flags] {binary path} (if built with Go 1.18 or higher)

Flags:

    -json    Print vulnerability findings in JSON format.

    -html    Generate HTML with the vulnerability findings.

    -tags    Comma-separated list of build tags.

    -tests    Boolean flag indicating if test files should be analyzed too.

govulncheck can be used with either one or more package patterns (i.e. golang.org/x/crypto/...
or ./...) or with a single path to a Go binary. In the latter case module and symbol
information will be extracted from the binary to detect vulnerable symbols.

The environment variable GOVULNDB can be set to a comma-separated list of vulnerability
database URLs, with http://, https://, or file:// protocols. Entries from multiple
databases are merged.

ふむふむ。 JSON 形式でも出力できるんだな。さっそく試してみよう。まずは手元の depm の開発環境で試してみる。

$ cd ~/path/to/depm

$ govulncheck ./...
govulncheck is an experimental tool. Share feedback at https://go.dev/s/govulncheck-feedback.

Scanning for dependencies with known vulnerabilities...
No vulnerabilities found.

よし。問題なさそう。次に JSON 形式で出力してみる。

$ govulncheck -json ./...
{
    "Calls": {
        "Functions": {},
        "Entries": null
    },
    "Imports": {
        "Packages": {},
        "Entries": null
    },
    "Requires": {
        "Modules": {},
        "Entries": null
    },
    "Vulns": null,
    "Modules": [
        {
            "Path": "github.com/BurntSushi/toml",
            "Version": "v1.2.0",
            "Dir": "",
            "Replace": null
        },
        {
            "Path": "github.com/davecgh/go-spew",
            "Version": "v1.1.1",
            "Dir": "",
            "Replace": null
        },
        {
            "Path": "github.com/emicklei/dot",
            "Version": "v1.0.0",
            "Dir": "",
            "Replace": null
        },
        {
            "Path": "github.com/goark/depm",
            "Version": "",
            "Dir": "",
            "Replace": null
        },
        {
            "Path": "github.com/goark/errs",
            "Version": "v1.1.0",
            "Dir": "",
            "Replace": null
        },
        {
            "Path": "github.com/goark/gocli",
            "Version": "v0.12.0",
            "Dir": "",
            "Replace": null
        },
        {
            "Path": "github.com/google/licenseclassifier/v2",
            "Version": "v2.0.0-pre6",
            "Dir": "",
            "Replace": null
        },
        {
            "Path": "github.com/sergi/go-diff",
            "Version": "v1.2.0",
            "Dir": "",
            "Replace": null
        },
        {
            "Path": "github.com/spf13/cobra",
            "Version": "v1.5.0",
            "Dir": "",
            "Replace": null
        },
        {
            "Path": "github.com/spf13/pflag",
            "Version": "v1.0.5",
            "Dir": "",
            "Replace": null
        },
        {
            "Path": "golang.org/x/mod",
            "Version": "v0.6.0-dev.0.20220419223038-86c51ed26bb4",
            "Dir": "",
            "Replace": null
        },
        {
            "Path": "golang.org/x/sys",
            "Version": "v0.0.0-20220804214406-8e32c043e418",
            "Dir": "",
            "Replace": null
        },
        {
            "Path": "golang.org/x/tools",
            "Version": "v0.1.12",
            "Dir": "",
            "Replace": null
        },
        {
            "Path": "stdlib",
            "Version": "v1.19.0",
            "Dir": "",
            "Replace": null
        }
    ]
}

んー? 依存関係が空だけどいいのかな? ちょっと思いついて Go 1.18 にバージョンダウンして試してみた。

$ go version
go version go1.18 linux/amd64

$ govulncheck -json ./...
{
    "Calls": {
        "Functions": {},
        "Entries": null
    },
    "Imports": {
        "Packages": {
            "1": {
                "ID": 1,
                "Name": "syscall",
                "Path": "syscall",
                "Module": 1,
                "ImportedBy": [
                    2,
                    3,
                    4,
                    5,
                    7,
                    12,
                    13,
                    35,
                    69,
                    74,
                    81
                ]
            },
            ...
            "9": {
                "ID": 9,
                "Name": "json",
                "Path": "encoding/json",
                "Module": 1,
                "ImportedBy": [
                    10,
                    15,
                    49,
                    61,
                    63,
                    66,
                    67
                ]
            }
        },
        "Entries": [
            14,
            15,
            51,
            58,
            59,
            60,
            61,
            65,
            66,
            67,
            68,
            81,
            82
        ]
    },
    "Requires": {
        "Modules": {
            "1": {
                "ID": 1,
                "Path": "stdlib",
                "Version": "v1.18.0",
                "Replace": 0,
                "RequiredBy": [
                    4,
                    5,
                    7,
                    8,
                    9,
                    11,
                    13,
                    14,
                    2,
                    3,
                    6,
                    10,
                    12
                ]
            },
            ...
            "9": {
                "ID": 9,
                "Path": "github.com/google/licenseclassifier/v2",
                "Version": "v2.0.0-pre6",
                "Replace": 0,
                "RequiredBy": [
                    3
                ]
            }
        },
        "Entries": [
            3
        ]
    },
    "Vulns": null,
    "Modules": [
        ...
    ]
}

(あまりに長いの途中端折ってるがご容赦)

出るぢゃん,ちゃんと。んー。この辺は今後の課題だろうか。

と,とにかく最低限は使えるようだし,おそらく CI に組み込むのも難しくないだろう。お試しあれ。

gopls との連携

Go 公式の Language Server である goplsgovulncheck が連携できるらしい。

https://twitter.com/gorilla0513/status/1557699235482861568

情報感謝です 🙇

たとえば VS CodeGo の拡張機能を導入し Language Server として gopls を有効にしていれば setting.json で以下の項目を追加すれば govulncheck が連携できるようになるそうだ。

setting.json
{
  "gopls": {
    "ui.codelenses": {
      "run_vulncheck_exp": true
    }
  }
}

試しに自分の環境に入れてみたんだけど,今のところ警告が出るような状況に出くわさないのでちゃんと機能しているのか自信がない(笑)

RESTFul Web API

あらかじめ問題のあるモジュールや Go 脆弱性ID (GO-2021-1234 など) が分かっていれば,特にツールを使わなくても簡単な Web API で情報を取得することができる。

Path Description
https://vuln.go.dev/index.json List of module paths in the database mapped to its last modified timestamp (link).
https://vuln.go.dev/$module.json List of vulnerability entries for that module (example).
https://vuln.go.dev/ID/index.json List of all the vulnerability entries in the database.
https://vuln.go.dev/ID/$vuln.json An individual Go vulnerability report.

ここで $module はモジュール名(golang.org/x/crypto など[2])で $vuln は脆弱性IDを指す。

たとえば Go 1.18.5 で修正された脆弱性の ID は GO-2022-0537 なので

$ curl -L "https://vuln.go.dev/ID/GO-2022-0537.json" | jq .
{
  "id": "GO-2022-0537",
  "published": "2022-08-01T22:21:06Z",
  "modified": "2022-08-01T22:21:06Z",
  "aliases": [
    "CVE-2022-32189"
  ],
  "details": "Decoding big.Float and big.Rat types can panic if the encoded message is\ntoo short, potentially allowing a denial of service.\n",
  "affected": [
    {
      "package": {
        "name": "math/big",
        "ecosystem": "Go"
      },
      "ranges": [
        {
          "type": "SEMVER",
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "1.17.13"
            },
            {
              "introduced": "1.18.0"
            },
            {
              "fixed": "1.18.5"
            }
          ]
        }
      ],
      "database_specific": {
        "url": "https://pkg.go.dev/vuln/GO-2022-0537"
      },
      "ecosystem_specific": {
        "symbols": [
          "Float.GobDecode",
          "Rat.GobDecode"
        ]
      }
    }
  ],
  "references": [
    {
      "type": "FIX",
      "url": "https://go.dev/cl/417774"
    },
    {
      "type": "FIX",
      "url": "https://go.googlesource.com/go/+/055113ef364337607e3e72ed7d48df67fde6fc66"
    },
    {
      "type": "WEB",
      "url": "https://go.dev/issue/53871"
    },
    {
      "type": "WEB",
      "url": "https://groups.google.com/g/golang-announce/c/YqYYG87xB10"
    }
  ]
}

という感じに情報を取り出せる。

正直な話 CVE ベースの情報ページは更新が遅くて使い勝手がよくない。 Go 公式で脆弱性IDを振って運用をしてくれるのであれば,今後はそちらを頼りにすることにしよう。

参考

https://zenn.dev/spiegel/articles/20220402-how-go-mitigates-supply-chain-attacks
https://text.baldanders.info/golang/check-for-vulns-in-golang-dependencies/
https://yamdas.hatenablog.com/entry/20220808/open-source-security

以下の記事に今回紹介した脆弱性管理システムについて少し解説があります。

https://gihyo.jp/article/2022/08/tukinami-go-01

Go Conference mini 2022 Autumn IN SENDAI」の最初のセッションのスライドです。

https://docs.google.com/presentation/d/1jtuK52vSPLNxzuSJ8LdPrJ2n1-UyY7bNXW7Irr1GEsA/edit

脚注
  1. Go の環境変数については「GOPATH に(可能な限り)依存しない Go 開発環境(Go 1.15 版)」あたりを参考にどうぞ。 ↩︎

  2. 標準パッケージのモジュール名は stdlib で指定するようだ。 ↩︎

GitHubで編集を提案

Discussion