knqyf263's blog

自分のためのメモとして残しておくためのブログ。

Goのバイナリから依存するmodule情報を取り出す方法

概要

Goでビルドしたバイナリは色々な情報を含んでいます。例えばビルドに使用したGoのバージョンを取得できます。

$ go version ./test
./test: go1.15.2

そしてついこの間知ったのですが、 -m オプションを使うことで利用しているmoduleの情報も取得可能です。

$ go version -m /usr/local/bin/terraform
/usr/local/bin/terraform: go1.14.9
        path    github.com/hashicorp/terraform
        mod     github.com/hashicorp/terraform  (devel)
        dep     cloud.google.com/go     v0.45.1
        dep     github.com/Azure/azure-sdk-for-go       v45.0.0+incompatible
        dep     github.com/Azure/go-autorest/autorest   v0.11.3
        dep     github.com/Azure/go-autorest/autorest/adal      v0.9.0
        dep     github.com/Azure/go-autorest/autorest/azure/cli v0.4.0
        dep     github.com/Azure/go-autorest/autorest/date      v0.3.0
        dep     github.com/Azure/go-autorest/autorest/to        v0.4.0
        dep     github.com/Azure/go-autorest/autorest/validation        v0.3.0

Terraformは cloud.google.com/go のv0.45.1に依存していることが分かります。その他にも依存しているモジュール名とバージョンが取得できています。Terraformのgo.sumを見ると確かに一致していそうです。

github.com

知らなかったといいつつバイナリにmoduleの情報が含まれていることは以前から知っていて、どうやって取り出すのかなとずっと頭の片隅にはあったのですが、 go version で取り出せるということを今更ながら知りました。自分の開発しているOSSにとってこれは非常にありがたい情報で、面白い機能が実装できるようになります。これは後の余談で話します。

ということでこのブログではどうやって取り出すのか?という内部の挙動を説明します。いや内部知らなくても取り出せれば十分という人は上の2つのコマンドで終了しているのでこの先は不要です。

内部挙動

go version の実装がとてもシンプルで読みやすいので、気になる人は読んでみてください。

github.com

ELFバイナリの準備

ELFだと理解しやすいと思うので、Linux向けにビルドします。丁度よいサイズのバイナリということで拙作の utern を使います。

github.com

おもむろにビルドします。

$ GOOS=linux GOARCH=amd64 go build .

確かにELFになっています。

$ file utern
utern: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, Go BuildID=BBs_Ti-Xl6qateQn0t0S/4KAWgjZw9u6WdCn8glDp/DOFaRSFJ6gpI-0q_QngG/8Gn_fV3QzFw8O3fj4dI-, not stripped

.go.buildinfo section

まず readelf でセクションヘッダを見てみます。すると中に .go.buildinfo というセクションがあることがわかります。

$ readelf -S ./utern
There are 25 section headers, starting at offset 0x1c8:

Section Headers:
  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .text             PROGBITS         0000000000401000  00001000
       00000000004f76b4  0000000000000000  AX       0     0     32
  [ 2] .rodata           PROGBITS         00000000008f9000  004f9000
       00000000001f3ca0  0000000000000000   A       0     0     32
  [ 3] .shstrtab         STRTAB           0000000000000000  006ecca0
       00000000000001bc  0000000000000000           0     0     1
  [ 4] .typelink         PROGBITS         0000000000aece60  006ece60
       00000000000032d0  0000000000000000   A       0     0     32
  [ 5] .itablink         PROGBITS         0000000000af0130  006f0130
       0000000000000d70  0000000000000000   A       0     0     8
  [ 6] .gosymtab         PROGBITS         0000000000af0ea0  006f0ea0
       0000000000000000  0000000000000000   A       0     0     1
  [ 7] .gopclntab        PROGBITS         0000000000af0ea0  006f0ea0
       000000000024751e  0000000000000000   A       0     0     32
  [ 8] .go.buildinfo     PROGBITS         0000000000d39000  00939000
       0000000000000020  0000000000000000  WA       0     0     16
  [ 9] .noptrdata        PROGBITS         0000000000d39020  00939020
       0000000000039e40  0000000000000000  WA       0     0     32
  [10] .data             PROGBITS         0000000000d72e60  00972e60
       000000000000ed00  0000000000000000  WA       0     0     32
  [11] .bss              NOBITS           0000000000d81b60  00981b60
       00000000000333f0  0000000000000000  WA       0     0     32
  [12] .noptrbss         NOBITS           0000000000db4f60  009b4f60
       0000000000003628  0000000000000000  WA       0     0     32
  [13] .zdebug_abbrev    PROGBITS         0000000000db9000  00982000
       0000000000000119  0000000000000000           0     0     1
  [14] .zdebug_line      PROGBITS         0000000000db9119  00982119
       000000000009a650  0000000000000000           0     0     1
  [15] .zdebug_frame     PROGBITS         0000000000e53769  00a1c769
       0000000000023f48  0000000000000000           0     0     1
  [16] .zdebug_pubnames  PROGBITS         0000000000e776b1  00a406b1
       000000000000422a  0000000000000000           0     0     1
  [17] .zdebug_pubtypes  PROGBITS         0000000000e7b8db  00a448db
       000000000000f06b  0000000000000000           0     0     1
  [18] .debug_gdb_s[...] PROGBITS         0000000000e8a946  00a53946
       0000000000000040  0000000000000000           0     0     1
  [19] .zdebug_info      PROGBITS         0000000000e8a986  00a53986
       00000000000f25c6  0000000000000000           0     0     1
  [20] .zdebug_loc       PROGBITS         0000000000f7cf4c  00b45f4c
       0000000000097da5  0000000000000000           0     0     1
  [21] .zdebug_ranges    PROGBITS         0000000001014cf1  00bddcf1
       0000000000032712  0000000000000000           0     0     1
  [22] .note.go.buildid  NOTE             0000000000400f9c  00000f9c
       0000000000000064  0000000000000000   A       0     0     4
  [23] .symtab           SYMTAB           0000000000000000  00c11000
       0000000000053d60  0000000000000018          24   672     8
  [24] .strtab           STRTAB           0000000000000000  00c64d60
       000000000008cf90  0000000000000000           0     0     1$ 

objdump でこの中身を見てみます。

$ objdump -s -j .go.buildinfo ./utern

./utern:     file format elf64-x86-64

Contents of section .go.buildinfo:
 d39000 ff20476f 20627569 6c64696e 663a0800  . Go buildinf:..
 d39010 303cd700 00000000 703cd700 00000000  0<......p<......

まず先頭16バイトについて見ていきます。

f:id:knqyf263:20210212160249p:plain

最初の14バイトはマジックバイトです。 \xff Go buildinf: でなければなりません。上の値を見ると確かにそうなっています。

15バイト目はポインタのサイズです。今回は 0x08 なので8バイトです。そして最後がエンディアンで、0の場合はリトルエンディアン、0以外ならビッグエンディアンになります。

Goのバージョン

次に17バイト目以降を見ていきますが、最初の8バイトがビルドに利用したGoバージョンへのポインタになっています。正確にはこの先もさらにポインタなのでポインタのポインタです。

f:id:knqyf263:20210212160335p:plain

上で見たようにリトルエンディアンなので、アドレスは 0xd73c30 になります。

ということで 0xd73c30 をstart-addressにして16バイトほど取り出してみます。

$ objdump -s --start-address 0x00d73c30 --stop-address 0x00d73c40 ./utern

./utern:     file format elf64-x86-64

Contents of section .data:
 d73c30 f0239e00 00000000 08000000 00000000  .#..............

この先頭8バイトがGoのバージョン情報へのポインタです。後半8バイトはそのバージョンのサイズです。

f:id:knqyf263:20210212160447p:plain

アドレスが 0x9e23f0 でサイズが8バイトであることが分かります。

ではその値を取り出します。サイズが8バイトなので stop-address0x9e23f8になります。

$ gobjdump -s --start-address 0x9e23f0 --stop-address 0x9e23f8 ./utern

./utern:     file format elf64-x86-64

Contents of section .rodata:
 9e23f0 676f312e 31352e32                    go1.15.2

ということでGoのバージョンを無事に取り出せました。このバイナリはGo 1.15.2でビルドされたことが分かります。

module情報

先程は.go.buildinfo の17バイト目から24バイト目までを使ったので、今度は25バイト目から32バイト目を使います。最初の8バイトがポインタになっているので、 0xd73c70 になります。

f:id:knqyf263:20210212160557p:plain

先程と同様に16バイトほど取り出してみます。

$ gobjdump -s --start-address 0xd73c70 --stop-address 0xd73c80 ./utern

./utern:     file format elf64-x86-64

Contents of section .data:
 d73c70 f713a000 00000000 b9040000 00000000  ................

これもGoのバージョンを取り出したときと同じで、前半8バイトがポインタで後半8バイトがサイズです。つまりアドレスが 0xa013f7 でサイズが 0x04b9 になります。

stop-addressを計算すると 0xa013f7 + 0x04b9 = 0xa018b0です。

Goのバージョン同様に 0xa013f7 をstart-address、 0xa018b0 をend-addressに指定して取り出してみます。

$ gobjdump -s --start-address 0xa013f7 --stop-address 0xa018b0 ./utern

./utern:     file format elf64-x86-64

Contents of section .rodata:
 a013f7 30 77af0c92 74080241 e1c107e6 d618e6 0w...t..A.......
 a01407 70 61746809 67697468 75622e63 6f6d2f path.github.com/
 a01417 6b 6e717966 3236332f 75746572 6e0a6d knqyf263/utern.m
 a01427 6f 64096769 74687562 2e636f6d 2f6b6e od.github.com/kn
 a01437 71 79663236 332f7574 65726e09 286465 qyf263/utern.(de
 a01447 76 656c2909 0a646570 09676974 687562 vel)..dep.github
 a01457 2e 636f6d2f 6177732f 6177732d 73646b .com/aws/aws-sdk
 a01467 2d 676f0976 312e3235 2e333609 68313a -go.v1.25.36.h1:
 a01477 34 2b544c2f 59324735 68735231 7a6466 4+TL/Y2G5hsR1zdf
 a01487 48 6d6a4e47 316f7531 57457173 53576b HmjNG1ou1WEqsSWk
 a01497 38 76376d31 4761444b 796f3d0a 646570 8v7m1GaDKyo=.dep
 a014a7 09 67697468 75622e63 6f6d2f62 726961 .github.com/bria
 a014b7 6e 646f776e 732f7370 696e6e65 720976 ndowns/spinner.v
 a014c7 31 2e372e30 0968313a 61616e31 684242 1.7.0.h1:aan1hBB
 a014d7 4f 6f736372 79325458 416b6774 786b4a Ooscry2TXAkgtxkJ
 a014e7 69 71375365 302b3970 742b5455 576150 iq7Se0+9pt+TUWaP
...

確かにmodule情報が含まれていることが分かります。念の為 go version -m を見てみます。

$ go version -m ./utern
./utern: go1.15.2
        path    github.com/knqyf263/utern
        mod     github.com/knqyf263/utern       (devel)
        dep     github.com/aws/aws-sdk-go       v1.25.36        h1:4+TL/Y2G5hsR1zdfHmjNG1ou1WEqsSWk8v7m1GaDKyo=
        dep     github.com/briandowns/spinner   v1.7.0  h1:aan1hBBOoscry2TXAkgtxkJiq7Se0+9pt+TUWaPrB4g=
        dep     github.com/cpuguy83/go-md2man/v2        v2.0.0  h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM=
        dep     github.com/fatih/color  v1.7.0  h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
        dep     github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af      h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM=
        dep     github.com/mattn/go-colorable   v0.1.4  h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA=
        dep     github.com/mattn/go-isatty      v0.0.10 h1:qxFzApOv4WsAL965uUPIsXzAKCZxN2p9UqdhFS4ZW10=
        dep     github.com/pkg/errors   v0.8.1  h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
        dep     github.com/russross/blackfriday/v2      v2.0.1  h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
        dep     github.com/shurcooL/sanitized_anchor_name       v1.0.0  h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
        dep     github.com/urfave/cli   v1.22.1 h1:+mkCCcOFKPnCmVYVcURKps1Xe+3zP90gSYGNfRkjoIY=
        dep     golang.org/x/sys        v0.0.0-20191115151921-52ab43148777      h1:wejkGHRTr38uaKRqECZlsCsJ1/TGxIyFbH32x5zUdu4=

上に含まれているデータと同じになっているようです。何かシリアライズされていて入っていて、 go version が整形して出しているのかと思いきや普通に上の表示のまま入っています。つまり改行やタブなどがそのままです。プログラムで使いたい場合は逆に整形する必要があります。

ちなみにgo.modの中でreplaceを使っている場合などは dep のところが => になったりします。

ということで簡単にGoバージョンとmodule情報を取得することが出来ました。 go version はELFだけでなくPEやMach-Oにも対応しています。

ldflagsについて

バイナリサイズ削減のため、シンボルテーブルを生成しないようにすることがあるかと思います。よく見る以下のようなオプションですね。

-ldflags '-w -s'

ありがたいことにこの場合でも .go.buildinfo は残りました。

$ GOOS=linux GOARCH=amd64 go build -ldflags '-w -s' .
$ go version -m ./utern
./utern: go1.15.2
        path    github.com/knqyf263/utern
        mod     github.com/knqyf263/utern       (devel)
        dep     github.com/aws/aws-sdk-go       v1.25.36        h1:4+TL/Y2G5hsR1zdfHmjNG1ou1WEqsSWk8v7m1GaDKyo=
        dep     github.com/briandowns/spinner   v1.7.0  h1:aan1hBBOoscry2TXAkgtxkJiq7Se0+9pt+TUWaPrB4g=
        ...

strip などしても変わらずでした。 upx を使うとさすがに消えました。

Goのソースコード

上は readelfobjdump などのコマンドラインを使って取り出す方法を説明しましたが、せっかくなのでGoのコードも少しここで追ってみます。簡単なので各自で読んだほうが早いとは思いますが、自分があとで読み返すメモとして残しておきます。

.go.buildinfo

Goのコードを見るとセクション全体を見て .go.buildinfo のアドレスを取り出しています。

   for _, s := range x.f.Sections {
        if s.Name == ".go.buildinfo" {
            return s.Addr
        }
    }

https://github.com/golang/go/blob/e9c96835971044aa4ace37c7787de231bbde05d9/src/cmd/go/internal/version/exe.go#L107

そして .go.builinfo には先程何回も見た以下のデータが入っています。

$ objdump -s -j .go.buildinfo ./utern

./utern:     file format elf64-x86-64

Contents of section .go.buildinfo:
 d39000 ff20476f 20627569 6c64696e 663a0800  . Go buildinf:..
 d39010 303cd700 00000000 703cd700 00000000  0<......p<......

マジックバイト

この先頭14バイトはマジックバイトになっていてその値は \xff Go buildinf: でなければなりませんでしたが、Goにそのマジックバイトが定義されています。

var buildInfoMagic = []byte("\xff Go buildinf:")

https://github.com/golang/go/blob/e9c96835971044aa4ace37c7787de231bbde05d9/src/cmd/go/internal/version/version.go#L165)

そして15バイト目はこのあと出てくるポインタのサイズです。今回は 0x08 なので8バイトです。

ptrSize := int(data[14])

https://github.com/golang/go/blob/e9c96835971044aa4ace37c7787de231bbde05d9/src/cmd/go/internal/version/version.go#L165

4バイトの場合はuint32として取り出してuint64に変換し、8バイトの場合はそのままint64で取り出します。ここで出てくる bo は後述するエンディアンです。今回のバイナリの場合は8バイトなので bo.Uint64 になります。

var readPtr func([]byte) uint64
if ptrSize == 4 {
    readPtr = func(b []byte) uint64 { return uint64(bo.Uint32(b)) }
} else {
    readPtr = bo.Uint64
}

https://github.com/golang/go/blob/e9c96835971044aa4ace37c7787de231bbde05d9/src/cmd/go/internal/version/version.go#L191-L196

16バイト目はビッグエンディアンかリトルエンディアンかを表しています。0の場合はリトルエンディアンになります。ここで先程の bo に代入しています。

bigEndian := data[15] != 0
var bo binary.ByteOrder
if bigEndian {
    bo = binary.BigEndian
} else {
    bo = binary.LittleEndian
}

https://github.com/golang/go/blob/e9c96835971044aa4ace37c7787de231bbde05d9/src/cmd/go/internal/version/version.go#L165

Goのバージョン情報へのポインタ

そして17バイト目から上の ptrSize 分(今回は8バイト)を readPtr で取り出し、そこの値を読みに行きます。今回の例では 303cd700 00000000 なので readPtr した結果は 0xd73c30 になり、そのアドレスの値を読みに行く処理が以下の readString になります。

vers = readString(x, ptrSize, readPtr, readPtr(data[16:]))

https://github.com/golang/go/blob/e9c96835971044aa4ace37c7787de231bbde05d9/src/cmd/go/internal/version/version.go#L197

そしてポインタサイズの2倍分を読み込みます。今回は8バイトなので16バイト読み込んでいます。

hdr, err := x.ReadData(addr, uint64(2*ptrSize))
if err != nil || len(hdr) < 2*ptrSize {
    return ""
}

https://github.com/golang/go/blob/e9c96835971044aa4ace37c7787de231bbde05d9/src/cmd/go/internal/version/version.go#L213-L216

そして前半8バイトが実際のデータへのアドレス、後半8バイトがデータサイズになっています。

dataAddr := readPtr(hdr)
dataLen := readPtr(hdr[ptrSize:])

https://github.com/golang/go/blob/e9c96835971044aa4ace37c7787de231bbde05d9/src/cmd/go/internal/version/version.go#L217-L218

言葉だとわかりにくいと思うので図を再掲しておきます。

f:id:knqyf263:20210212160447p:plain

前半8バイトがアドレス( 0x9e23f0 )で、後半8バイトがサイズ( 0x08 )になります。

これがそれぞれ dataAddrdataLen に入ります。

あとはそのアドレス( dataAddr )にサイズ分( dataLen)アクセスするだけです。

data, err := x.ReadData(dataAddr, dataLen)
if err != nil || uint64(len(data)) < dataLen {
    return ""
}

これで以下のように go1.15.2 が得られます。

$ gobjdump -s --start-address 0x009e23f0 --stop-address 0x009e23f8 ./utern

./utern:     file format elf64-x86-64

Contents of section .rodata:
 9e23f0 676f312e 31352e32                    go1.15.2

module情報へのポインタ

先程は前半8バイトのポインタを使ったので、後半8バイトのポインタを使うだけです。実際には ptrSize をずらすので、ポインタサイズが違えば8バイトではなくなります。

mod = readString(x, ptrSize, readPtr, readPtr(data[16+ptrSize:]))

あとの流れは上と同じで、ポインタのポインタを辿っていくとmoduleデータが手に入ります。同じ readString が呼ばれているだけなので割愛します。

EXEファイルの処理

ELFだけじゃなくPEやMach-Oにも対応していると前述しましたが、それは openExe() 周りで吸収されています。バイナリの先頭を見て判断しています。あとは debug/elfNewFile を呼んでよしなにやってもらうという感じです。

  if bytes.HasPrefix(data, []byte("\x7FELF")) {
        e, err := elf.NewFile(f)
        if err != nil {
            f.Close()
            return nil, err
        }
        return &elfExe{f, e}, nil
    }

https://github.com/golang/go/blob/e9c96835971044aa4ace37c7787de231bbde05d9/src/cmd/go/internal/version/exe.go#L41-L48

PEやMach-Oの場合は debug/pedebug/macho を呼んでいます。

余談

最近DNS脆弱性についてブログに書いていて何かそういうセキュリティ関係者と思われている可能性があるのですが、そういった分析は本業と何も関係なくて本業ではTrivyというOSSを開発しています。これは脆弱性スキャンツールで、コンテナイメージなどを渡すと内部の脆弱性をスキャンします。何か未知の新たな脆弱性を見つけるタイプのものではなくて、CVE-IDなどが採番されている既知の脆弱性を見つけるツールになっています。OSパッケージの古いバージョンやプログラミング言語で使われるライブラリの古いバージョンなどに存在する脆弱性を見つけるのが主な役割です。

コンテナイメージ用のツールとして始めたのですが、今ではファイルシステム上のファイルだったりDockerfileに埋め込んで使えたりGitHubリポジトリのスキャンだったりにも対応しています。

github.com

Trivyは自分が趣味で開発したOSSで気づけば業務としてやることになっているのですが、Clairというこの界隈における絶対的王者を倒すために作り始めて気付けばGitHubスター数が1000差程度まで来ています。作り始めた時点でClairはスター数が5000以上もあって無理だろうと諦めつつあったのですがここに来て現実味を増してきたので、もしよろしければ応援していただけると嬉しいです(宣伝)。

話がそれましたが、Trivyは現在PythonRubyなどの言語にも対応しています。どのように検知するかと言うと各言語のパッケージマネージャが生成するロックファイルを使っています。npmでいう package-lock.json 、bundlerでいうところの Gemfile.lock です。

ですがGoには対応していません。それには2つの理由があって、Goの脆弱性データベースで使えるものがなかったためと、 go.sum はコンテナイメージ内に残さない可能性が高いためです。関係ないですが go.sum はロックファイルではないということもユーザの方から教わりました。GoはシングルバイナリにビルドできるのでDockerのマルチステージビルドなどと相性がよく、バイナリだけ置いて動かす事が多いです。スキャンしたかったら go.sum 置いてね、という運用も出来なくはないのですが美しくないですしそもそも良い脆弱性DBもないので後回しにしていました。

そこで改めて公開されているDBを見てみたところ以前はほとんど脆弱性がなかったGitLab Advisory Databaseがかなり充実してきていました。

gitlab.com

これなら十分有用だなということで go.sum を使わない方法を探していたところ、バイナリからmodule情報取り出せるということを1年前にIssueで教えてもらっていたことを思い出しました。

github.com

それで色々調べた結果、 go version が既に対応していることを知り、自分でも実装することが出来ました。以前ブログにも書いたのですが、Issueでユーザの方から教えてもらうことがとても多くていつも助かっています。情報の価値が高い現代において欲しい情報が勝手に集まってくるOSSは良いものだなと思います。

knqyf263.hatenablog.com

一方でこないだ放置していたOSSを久しぶりにメンテナンスしたら煽られたりしてOSS最悪だわとなったり、感情を揺さぶられがちなので情緒不安定にならないように気をつけましょう。

ということで脱線して長くなりましたが、結論を言うとGoでビルドしたバイナリをコンテナイメージ内に置いておくだけで脆弱性スキャンが出来るようになります!(まだ実装終わってないから出来るか断定はできないですが多分大丈夫)

コンテナに限らず手元にあるバイナリでも何でも .go.buildinfo が落とされていなければスキャンできます。個人的にはこういう機能ずっと実装したかったので嬉しい限りです。

このbuildinfoを取り出す処理はGo本体で実装済みなので関数呼ぶだけで済んで便利だな〜と思いきやinternal/の下にあって呼べませんでした。なので仕方なくコピペしつつ移植しました。コンテナイメージ内のファイルに対して使えるように多少の修正が必要だったので結果的には良かったのですが、internal/はやはりコピペを生むので微妙だな派閥になりました。上記のTrivyでもinternal/使ってるのですがexportしてくれ〜と苦情が来たりしていてあまり良い思い出がないです。

さらに余談ですがJavaの対応ももうすぐ入ります。 pom.xml は依存の依存とかは書いてなくて自前でMavenリポジトリから取得するように実装しました。単にGoでMavenの再実装をしただけになってしまいとても大変でした。それでも全部のプラグインには対応できないので完璧にはトレース出来ませんが、多くのケースでは幸せになるかと思います。他にも pom.xml ではなくてWARやJARを見てスキャンする機能も作っているので、 war として固めてコンテナイメージ内に置いただけで pom.xml はない、みたいなパターンでもスキャンできます。つまりMavenじゃなくてGradleでもOKです。この辺りの詳細はいずれ元気があれば書きます。

まとめ

TrivyがGoのバイナリに対して脆弱性スキャンかけられるようになる!!!(多分)