mirror of
https://github.com/trufflesecurity/trufflehog.git
synced 2024-11-10 07:04:24 +00:00
Initial CLI w/ partially implemented Git source and demo detector (#1)
This commit is contained in:
parent
9edeb164f4
commit
4218c39d99
43 changed files with 22428 additions and 0 deletions
0
Dockerfile
Normal file
0
Dockerfile
Normal file
32
Makefile
Normal file
32
Makefile
Normal file
|
@ -0,0 +1,32 @@
|
|||
PROTOS_IMAGE=us-docker.pkg.dev/thog-artifacts/public/go-ci-1.17-1
|
||||
|
||||
.PHONY: check
|
||||
.PHONY: test
|
||||
.PHONY: test-race
|
||||
.PHONY: run
|
||||
.PHONY: install
|
||||
.PHONY: protos
|
||||
.PHONY: protos-windows
|
||||
.PHONY: vendor
|
||||
|
||||
install:
|
||||
CGO_ENABLED=0 go install .
|
||||
|
||||
check:
|
||||
go fmt $(shell go list ./... | grep -v /vendor/)
|
||||
go vet $(shell go list ./... | grep -v /vendor/)
|
||||
|
||||
test:
|
||||
CGO_ENABLED=0 go test $(shell go list ./... | grep -v /vendor/)
|
||||
|
||||
test-race:
|
||||
CGO_ENABLED=1 go test -race $(shell go list ./... | grep -v /vendor/)
|
||||
|
||||
bench:
|
||||
CGO_ENABLED=0 go test $(shell go list ./pkg/secrets/... | grep -v /vendor/) -benchmem -run=xxx -bench .
|
||||
|
||||
run:
|
||||
CGO_ENABLED=0 go run . git file://.
|
||||
|
||||
protos:
|
||||
docker run -u "$(shell id -u)" -v "$(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))":/pwd "${PROTOS_IMAGE}" bash -c "cd /pwd; /pwd/scripts/gen_proto.sh"
|
0
README.md
Normal file
0
README.md
Normal file
67
go.mod
Normal file
67
go.mod
Normal file
|
@ -0,0 +1,67 @@
|
|||
module github.com/trufflesecurity/trufflehog
|
||||
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
cloud.google.com/go/secretmanager v1.0.0
|
||||
github.com/bradleyfalzon/ghinstallation v1.1.1
|
||||
github.com/envoyproxy/protoc-gen-validate v0.6.2
|
||||
github.com/go-errors/errors v1.4.1
|
||||
github.com/go-git/go-git/v5 v5.4.2
|
||||
github.com/google/go-github/v41 v41.0.0
|
||||
github.com/h2non/filetype v1.1.3
|
||||
github.com/hashicorp/go-retryablehttp v0.7.0
|
||||
github.com/joho/godotenv v1.4.0
|
||||
github.com/kylelemons/godebug v1.1.0
|
||||
github.com/mattn/go-colorable v0.1.12
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/sirupsen/logrus v1.8.1
|
||||
github.com/tailscale/depaware v0.0.0-20210622194025-720c4b409502
|
||||
github.com/xanzy/go-gitlab v0.54.3
|
||||
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
|
||||
google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368
|
||||
google.golang.org/protobuf v1.27.1
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6
|
||||
)
|
||||
|
||||
require (
|
||||
cloud.google.com/go v0.94.1 // indirect
|
||||
github.com/Microsoft/go-winio v0.4.16 // indirect
|
||||
github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 // indirect
|
||||
github.com/acomagu/bufpipe v1.0.3 // indirect
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 // indirect
|
||||
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect
|
||||
github.com/emirpasic/gods v1.12.0 // indirect
|
||||
github.com/go-git/gcfg v1.5.0 // indirect
|
||||
github.com/go-git/go-billy/v5 v5.3.1 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/google/go-cmp v0.5.6 // indirect
|
||||
github.com/google/go-github/v29 v29.0.2 // indirect
|
||||
github.com/google/go-querystring v1.1.0 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.1.0 // indirect
|
||||
github.com/hashicorp/go-cleanhttp v0.5.1 // indirect
|
||||
github.com/imdario/mergo v0.3.12 // indirect
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
||||
github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 // indirect
|
||||
github.com/mattn/go-isatty v0.0.14 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
github.com/pkg/diff v0.0.0-20200914180035-5b29258ca4f7 // indirect
|
||||
github.com/sergi/go-diff v1.1.0 // indirect
|
||||
github.com/xanzy/ssh-agent v0.3.0 // indirect
|
||||
go.opencensus.io v0.23.0 // indirect
|
||||
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 // indirect
|
||||
golang.org/x/mod v0.5.0 // indirect
|
||||
golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d // indirect
|
||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6 // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect
|
||||
golang.org/x/tools v0.1.5 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
||||
google.golang.org/api v0.57.0 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/grpc v1.40.0 // indirect
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
)
|
697
go.sum
Normal file
697
go.sum
Normal file
|
@ -0,0 +1,697 @@
|
|||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
|
||||
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
||||
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
|
||||
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
|
||||
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
|
||||
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
|
||||
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
|
||||
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
|
||||
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
|
||||
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
|
||||
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
|
||||
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
|
||||
cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
|
||||
cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
|
||||
cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg=
|
||||
cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8=
|
||||
cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0=
|
||||
cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY=
|
||||
cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM=
|
||||
cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY=
|
||||
cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ=
|
||||
cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI=
|
||||
cloud.google.com/go v0.94.1 h1:DwuSvDZ1pTYGbXo8yOJevCTr3BoBlE+OVkHAKiYQUXc=
|
||||
cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4=
|
||||
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
|
||||
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
|
||||
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
|
||||
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
|
||||
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
|
||||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
|
||||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
|
||||
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
|
||||
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
|
||||
cloud.google.com/go/secretmanager v1.0.0 h1:Wbw6lsRrpatsE8GVpuwYqImn+sY5DmRjaEImYPwcSMY=
|
||||
cloud.google.com/go/secretmanager v1.0.0/go.mod h1:+Qkm5qxIJ5mk74xxIXA+87fseaY1JLYBcFPQoc/GQxg=
|
||||
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
||||
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
|
||||
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
|
||||
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
|
||||
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
|
||||
github.com/Microsoft/go-winio v0.4.16 h1:FtSW/jqD+l4ba5iPBj9CODVtgfYAD8w2wS923g/cFDk=
|
||||
github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 h1:YoJbenK9C67SkzkDfmQuVln04ygHj3vjZfd9FL+GmQQ=
|
||||
github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo=
|
||||
github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk=
|
||||
github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4=
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM=
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 h1:s6gZFSlWYmbqAuRjVTiNNhvNRfY2Wxp9nhfyel4rklc=
|
||||
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
|
||||
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
||||
github.com/bradleyfalzon/ghinstallation v1.1.1 h1:pmBXkxgM1WeF8QYvDLT5kuQiHMcmf+X015GI0KM/E3I=
|
||||
github.com/bradleyfalzon/ghinstallation v1.1.1/go.mod h1:vyCmHTciHx/uuyN82Zc3rXN3X2KTK8nUTCrTMwAhcug=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg=
|
||||
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.6.2 h1:JiO+kJTpmYGjEodY7O1Zk8oZcNz1+f30UtwtXoFUPzE=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.6.2/go.mod h1:2t7qjJNvHPx8IjnBOzl9E9/baC+qXE/TeeyBRzgJDws=
|
||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0=
|
||||
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
||||
github.com/go-errors/errors v1.4.1 h1:IvVlgbzSsaUNudsw5dcXSzF3EWyXTi5XrAdngnuhRyg=
|
||||
github.com/go-errors/errors v1.4.1/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
|
||||
github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4=
|
||||
github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E=
|
||||
github.com/go-git/go-billy/v5 v5.2.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
|
||||
github.com/go-git/go-billy/v5 v5.3.1 h1:CPiOUAzKtMRvolEKw+bG1PLRpT7D3LIs3/3ey4Aiu34=
|
||||
github.com/go-git/go-billy/v5 v5.3.1/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.2.1 h1:n9gGL1Ct/yIw+nfsfr8s4+sbhT+Ncu2SubfXjIWgci8=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.2.1/go.mod h1:K8zd3kDUAykwTdDCr+I0per6Y6vMiRR/nnVTBtavnB0=
|
||||
github.com/go-git/go-git/v5 v5.4.2 h1:BXyZu9t0VkbiHtqrsvdq39UDhGJTl1h55VW6CSC4aY4=
|
||||
github.com/go-git/go-git/v5 v5.4.2/go.mod h1:gQ1kArt6d+n+BGd+/B/I74HwRTLhth2+zti4ihgckDc=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
||||
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
|
||||
github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
|
||||
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
|
||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-github/v29 v29.0.2 h1:opYN6Wc7DOz7Ku3Oh4l7prmkOMwEcQxpFtxdU8N8Pts=
|
||||
github.com/google/go-github/v29 v29.0.2/go.mod h1:CHKiKKPHJ0REzfwc14QMklvtHwCveD0PxlMjLlzAM5E=
|
||||
github.com/google/go-github/v41 v41.0.0 h1:HseJrM2JFf2vfiZJ8anY2hqBjdfY1Vlj/K27ueww4gg=
|
||||
github.com/google/go-github/v41 v41.0.0/go.mod h1:XgmCA5H323A9rtgExdTcnDkcqp6S30AVACCBDOonIxg=
|
||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
|
||||
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||
github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/googleapis/gax-go/v2 v2.1.0 h1:6DWmvNpomjL1+3liNSZbVns3zsYzzCjm6pRBO1tLeso=
|
||||
github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
||||
github.com/h2non/filetype v1.1.3 h1:FKkx9QbD7HR/zjK1Ia5XiBsq9zdLi5Kf3zGyFTAFkGg=
|
||||
github.com/h2non/filetype v1.1.3/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
github.com/hashicorp/go-hclog v0.9.2 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxCsHI=
|
||||
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
|
||||
github.com/hashicorp/go-retryablehttp v0.6.8/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.0 h1:eu1EI/mbirUgP5C8hVsTNaGZreBDlYiwC1FZWkvQPQ4=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.0/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=
|
||||
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
|
||||
github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4=
|
||||
github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg=
|
||||
github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||
github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 h1:DowS9hvgyYSX4TO5NpyC606/Z4SxnNYbT+WX27or6Ck=
|
||||
github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc87/1qhoTACD8w=
|
||||
github.com/matryer/is v1.2.0 h1:92UTHpy8CDwaJ08GqLDzhhuixiBUUD1p3AU6PHddz4A=
|
||||
github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA=
|
||||
github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=
|
||||
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
|
||||
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/pkg/diff v0.0.0-20200914180035-5b29258ca4f7 h1:+/+DxvQaYifJ+grD4klzrS5y+KJXldn/2YTl5JG+vZ8=
|
||||
github.com/pkg/diff v0.0.0-20200914180035-5b29258ca4f7/go.mod h1:zO8QMzTeZd5cpnIkz/Gn6iK0jDfGicM1nynOkkPIl28=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
|
||||
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
||||
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
|
||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4=
|
||||
github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/tailscale/depaware v0.0.0-20210622194025-720c4b409502 h1:34icjjmqJ2HPjrSuJYEkdZ+0ItmGQAQ75cRHIiftIyE=
|
||||
github.com/tailscale/depaware v0.0.0-20210622194025-720c4b409502/go.mod h1:p9lPsd+cx33L3H9nNoecRRxPssFKUwwI50I3pZ0yT+8=
|
||||
github.com/xanzy/go-gitlab v0.54.3 h1:fPfZ3Jcu5dPc3xyIYtAALZsEgoyKNFNuULD+TdJ7Zvk=
|
||||
github.com/xanzy/go-gitlab v0.54.3/go.mod h1:F0QEXwmqiBUxCgJm8fE9S+1veX4XC9Z4cfaAbqwk4YM=
|
||||
github.com/xanzy/ssh-agent v0.3.0 h1:wUMzuKtKilRgBAD1sUb8gOwwRr2FGoBVumcjoOACClI=
|
||||
github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0=
|
||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
|
||||
go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M=
|
||||
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
|
||||
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
||||
golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ=
|
||||
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
|
||||
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
|
||||
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
|
||||
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.5.0 h1:UG21uOlmZabA4fW5i7ZX6bjw1xELEGg/ZLgZq9auk/Q=
|
||||
golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
|
||||
golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d h1:LO7XpTYMwTqxjLcGWPijK3vRXg1aWdlNOVOHRq45d7c=
|
||||
golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 h1:RerP+noqYHUQ8CMRcPlC2nvTa4dcBIjegkuWdcUDuqg=
|
||||
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210502180810-71e4cd670f79/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6 h1:foEbQz/B0Oz6YIqu/69kfXPYeFQAuuMYFkjaqXzl5Wo=
|
||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||
golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
|
||||
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
|
||||
golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20201211185031-d93e913c1a58/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.5 h1:ouewzE6p+/VEB31YYnTbEJdi8pFqKp4P4n85vwo3DHA=
|
||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
||||
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
||||
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
|
||||
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
|
||||
google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
|
||||
google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
|
||||
google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
|
||||
google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU=
|
||||
google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94=
|
||||
google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo=
|
||||
google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4=
|
||||
google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw=
|
||||
google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU=
|
||||
google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k=
|
||||
google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE=
|
||||
google.golang.org/api v0.57.0 h1:4t9zuDlHLcIx0ZEhmXEeFVCRsiOgpgn2QOH9N0MNjPI=
|
||||
google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
|
||||
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
|
||||
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
|
||||
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
|
||||
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
|
||||
google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A=
|
||||
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
|
||||
google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
|
||||
google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
|
||||
google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24=
|
||||
google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=
|
||||
google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=
|
||||
google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
|
||||
google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
|
||||
google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w=
|
||||
google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
|
||||
google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
|
||||
google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
|
||||
google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
|
||||
google.golang.org/genproto v0.0.0-20210921142501-181ce0d877f6/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||
google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368 h1:Et6SkiuvnBn+SgrSYXs/BrUpGB4mbdwt4R3vaPIlicA=
|
||||
google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
|
||||
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
|
||||
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
|
||||
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
||||
google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
|
||||
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
|
||||
google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
|
||||
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
|
||||
google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
|
||||
google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
|
||||
google.golang.org/grpc v1.40.0 h1:AGJ0Ih4mHjSeibYkFGh1dD9KJ/eOtZ93I6hoHhukQ5Q=
|
||||
google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
|
||||
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
|
||||
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
|
||||
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
101
main.go
Normal file
101
main.go
Normal file
|
@ -0,0 +1,101 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"runtime"
|
||||
"strconv"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/trufflesecurity/trufflehog/pkg/decoders"
|
||||
"github.com/trufflesecurity/trufflehog/pkg/engine"
|
||||
kingpin "gopkg.in/alecthomas/kingpin.v2"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
cli := kingpin.New("TruffleHog", "TruffleHog is a tool for finding credentials.")
|
||||
debug := cli.Flag("debug", "Run in debug mode").Bool()
|
||||
jsonOut := cli.Flag("json", "Output in JSON format.").Bool()
|
||||
concurrency := cli.Flag("concurrency", "Number of concurrent workers.").Default(strconv.Itoa(runtime.NumCPU())).Int()
|
||||
verification := cli.Flag("verification", "Verify the results.").Bool()
|
||||
// rules := cli.Flag("rules", "Path to file with custom rules.").String()
|
||||
|
||||
gitScan := cli.Command("git", "Find credentials in git repositories.")
|
||||
gitScanURI := gitScan.Arg("uri", "Git repository URL. https:// or file:// schema expected.").Required().String()
|
||||
// gitScanIncludePaths := gitScan.Flag("include_paths", "Path to file with newline separated regexes for files to include in scan.").Short('i').String()
|
||||
// gitScanExcludePaths := gitScan.Flag("exclude_paths", "Path to file with newline separated regexes for files to exclude in scan.").Short('x').String()
|
||||
// gitScanSinceCommit := gitScan.Flag("since_commit", "Commit to start scan from.").String()
|
||||
gitScanBranch := gitScan.Flag("branch", "Branch to scan.").String()
|
||||
// gitScanMaxDepth := gitScan.Flag("max_depth", "Maximum depth of commits to scan.").Int()
|
||||
gitScan.Flag("allow", "No-op flag for backwards compat.").Bool()
|
||||
gitScan.Flag("entropy", "No-op flag for backwards compat.").Bool()
|
||||
gitScan.Flag("regex", "No-op flag for backwards compat.").Bool()
|
||||
|
||||
githubScan := cli.Command("github", "Find credentials in GitHub repositories.")
|
||||
// githubScanTarget := githubScan.Arg("target", "GitHub target. Can be a repository, user or organization.").Required().String()
|
||||
// githubScanToken := githubScan.Flag("token", "GitHub token.").String()
|
||||
|
||||
gitlabScan := cli.Command("gitlab", "Find credentials in GitLab repositories.")
|
||||
// gitlabScanTarget := gitlabScan.Arg("target", "GitLab target. Can be a repository, user or organization.").Required().String()
|
||||
// gitlabScanToken := gitlabScan.Flag("token", "GitLab token.").String()
|
||||
|
||||
// bitbucketScan := cli.Command("bitbucket", "Find credentials in Bitbucket repositories.")
|
||||
// bitbucketScanTarget := bitbucketScan.Arg("target", "Bitbucket target. Can be a repository, user or organization.").Required().String()
|
||||
// bitbucketScanToken := bitbucketScan.Flag("token", "Bitbucket token.").String()
|
||||
|
||||
// filesystemScan := cli.Command("filesystem", "Find credentials in filesystem.")
|
||||
// filesystemScanPath := filesystemScan.Arg("path", "Path to scan.").Required().String()
|
||||
// filesystemScanRecursive := filesystemScan.Flag("recursive", "Scan recursively.").Short('r').Bool()
|
||||
// filesystemScanIncludePaths := filesystemScan.Flag("include_paths", "Path to file with newline separated regexes for files to include in scan.").Short('i').String()
|
||||
// filesystemScanExcludePaths := filesystemScan.Flag("exclude_paths", "Path to file with newline separated regexes for files to exclude in scan.").Short('x').String()
|
||||
|
||||
cmd := kingpin.MustParse(cli.Parse(os.Args[1:]))
|
||||
|
||||
if *jsonOut {
|
||||
logrus.SetFormatter(&logrus.JSONFormatter{})
|
||||
}
|
||||
if *debug {
|
||||
logrus.SetLevel(logrus.DebugLevel)
|
||||
} else {
|
||||
logrus.SetLevel(logrus.InfoLevel)
|
||||
}
|
||||
|
||||
ctx := context.TODO()
|
||||
e := engine.Start(ctx,
|
||||
engine.WithConcurrency(*concurrency),
|
||||
engine.WithDecoders(decoders.DefaultDecoders()...),
|
||||
engine.WithDetectors(*verification, engine.DefaultDetectors()...),
|
||||
)
|
||||
|
||||
switch cmd {
|
||||
case gitScan.FullCommand():
|
||||
err := e.ScanGit(ctx, *gitScanURI, *gitScanBranch, "HEAD")
|
||||
if err != nil {
|
||||
logrus.WithError(err).Fatal("Failed to scan git.")
|
||||
}
|
||||
case githubScan.FullCommand():
|
||||
log.Fatal("github not implemented")
|
||||
case gitlabScan.FullCommand():
|
||||
log.Fatal("gitlab not implemented")
|
||||
}
|
||||
|
||||
// deal with the results from e.ResultsChan()
|
||||
for r := range e.ResultsChan() {
|
||||
if *jsonOut {
|
||||
// todo - add parity to trufflehog's existing output for git
|
||||
// source
|
||||
out, err := json.Marshal(r)
|
||||
if err != nil {
|
||||
logrus.WithError(err).Fatal("could not marshal result")
|
||||
}
|
||||
fmt.Println(string(out))
|
||||
} else {
|
||||
fmt.Printf("%+v\n", r)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
5
pkg/common/depaware.go
Normal file
5
pkg/common/depaware.go
Normal file
|
@ -0,0 +1,5 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
_ "github.com/tailscale/depaware/depaware"
|
||||
)
|
130
pkg/common/http.go
Normal file
130
pkg/common/http.go
Normal file
|
@ -0,0 +1,130 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
retryablehttp "github.com/hashicorp/go-retryablehttp"
|
||||
)
|
||||
|
||||
var caCerts = []string{
|
||||
// CN = ISRG Root X1
|
||||
// TODO: Expires Monday, June 4, 2035 at 4:04:38 AM Pacific
|
||||
`
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw
|
||||
TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
|
||||
cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4
|
||||
WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu
|
||||
ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY
|
||||
MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc
|
||||
h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+
|
||||
0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U
|
||||
A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW
|
||||
T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH
|
||||
B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC
|
||||
B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv
|
||||
KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn
|
||||
OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn
|
||||
jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw
|
||||
qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI
|
||||
rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV
|
||||
HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq
|
||||
hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL
|
||||
ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ
|
||||
3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK
|
||||
NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5
|
||||
ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur
|
||||
TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC
|
||||
jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc
|
||||
oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq
|
||||
4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA
|
||||
mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d
|
||||
emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=
|
||||
-----END CERTIFICATE-----
|
||||
`,
|
||||
// CN = ISRG Root X2
|
||||
// TODO: Expires September 17, 2040 at 9:00:00 AM Pacific Daylight Time
|
||||
`
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICGzCCAaGgAwIBAgIQQdKd0XLq7qeAwSxs6S+HUjAKBggqhkjOPQQDAzBPMQsw
|
||||
CQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2gg
|
||||
R3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMjAeFw0yMDA5MDQwMDAwMDBaFw00
|
||||
MDA5MTcxNjAwMDBaME8xCzAJBgNVBAYTAlVTMSkwJwYDVQQKEyBJbnRlcm5ldCBT
|
||||
ZWN1cml0eSBSZXNlYXJjaCBHcm91cDEVMBMGA1UEAxMMSVNSRyBSb290IFgyMHYw
|
||||
EAYHKoZIzj0CAQYFK4EEACIDYgAEzZvVn4CDCuwJSvMWSj5cz3es3mcFDR0HttwW
|
||||
+1qLFNvicWDEukWVEYmO6gbf9yoWHKS5xcUy4APgHoIYOIvXRdgKam7mAHf7AlF9
|
||||
ItgKbppbd9/w+kHsOdx1ymgHDB/qo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0T
|
||||
AQH/BAUwAwEB/zAdBgNVHQ4EFgQUfEKWrt5LSDv6kviejM9ti6lyN5UwCgYIKoZI
|
||||
zj0EAwMDaAAwZQIwe3lORlCEwkSHRhtFcP9Ymd70/aTSVaYgLXTWNLxBo1BfASdW
|
||||
tL4ndQavEi51mI38AjEAi/V3bNTIZargCyzuFJ0nN6T5U6VR5CmD1/iQMVtCnwr1
|
||||
/q4AaOeMSQ+2b1tbFfLn
|
||||
-----END CERTIFICATE-----
|
||||
`,
|
||||
}
|
||||
|
||||
func PinnedCertPool() *x509.CertPool {
|
||||
trustedCerts := x509.NewCertPool()
|
||||
for _, cert := range caCerts {
|
||||
trustedCerts.AppendCertsFromPEM([]byte(strings.TrimSpace(cert)))
|
||||
}
|
||||
return trustedCerts
|
||||
}
|
||||
|
||||
type CustomTransport struct {
|
||||
T http.RoundTripper
|
||||
}
|
||||
|
||||
func (t *CustomTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
req.Header.Add("User-Agent", "TruffleHog")
|
||||
return t.T.RoundTrip(req)
|
||||
}
|
||||
|
||||
func NewCustomTransport(T http.RoundTripper) *CustomTransport {
|
||||
if T == nil {
|
||||
T = http.DefaultTransport
|
||||
}
|
||||
return &CustomTransport{T}
|
||||
}
|
||||
|
||||
func PinnedRetryableHttpClient() *http.Client {
|
||||
httpClient := retryablehttp.NewClient()
|
||||
httpClient.Logger = nil
|
||||
httpClient.HTTPClient.Transport = NewCustomTransport(&http.Transport{
|
||||
TLSClientConfig: &tls.Config{
|
||||
RootCAs: PinnedCertPool(),
|
||||
},
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
DialContext: (&net.Dialer{
|
||||
Timeout: 30 * time.Second,
|
||||
KeepAlive: 30 * time.Second,
|
||||
DualStack: true,
|
||||
}).DialContext,
|
||||
ForceAttemptHTTP2: true,
|
||||
MaxIdleConns: 100,
|
||||
IdleConnTimeout: 90 * time.Second,
|
||||
TLSHandshakeTimeout: 10 * time.Second,
|
||||
ExpectContinueTimeout: 1 * time.Second,
|
||||
})
|
||||
return httpClient.StandardClient()
|
||||
}
|
||||
|
||||
func RetryableHttpClient() *http.Client {
|
||||
httpClient := retryablehttp.NewClient()
|
||||
httpClient.RetryMax = 3
|
||||
httpClient.Logger = nil
|
||||
httpClient.HTTPClient.Timeout = 3 * time.Second
|
||||
httpClient.HTTPClient.Transport = NewCustomTransport(nil)
|
||||
return httpClient.StandardClient()
|
||||
}
|
||||
|
||||
func SaneHttpClient() *http.Client {
|
||||
httpClient := &http.Client{}
|
||||
httpClient.Timeout = time.Second * 2
|
||||
httpClient.Transport = NewCustomTransport(nil)
|
||||
return httpClient
|
||||
}
|
55
pkg/common/secrets.go
Normal file
55
pkg/common/secrets.go
Normal file
|
@ -0,0 +1,55 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
secretmanager "cloud.google.com/go/secretmanager/apiv1"
|
||||
"github.com/joho/godotenv"
|
||||
"github.com/pkg/errors"
|
||||
secretmanagerpb "google.golang.org/genproto/googleapis/cloud/secretmanager/v1"
|
||||
)
|
||||
|
||||
type Secret struct{ kv map[string]string }
|
||||
|
||||
func (s *Secret) MustGetField(name string) string {
|
||||
val, ok := s.kv[name]
|
||||
if !ok {
|
||||
panic(errors.Errorf("field %s not found", name))
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
func GetTestSecret(ctx context.Context) (secret *Secret, err error) {
|
||||
return GetSecret(ctx, "trufflehog-testing", "test")
|
||||
}
|
||||
|
||||
func GetSecret(ctx context.Context, gcpProject, name string) (secret *Secret, err error) {
|
||||
ctx, cancel := context.WithTimeout(ctx, time.Second*10)
|
||||
defer cancel()
|
||||
|
||||
parent := fmt.Sprintf("projects/%s/secrets/%s/versions/latest", gcpProject, name)
|
||||
|
||||
client, err := secretmanager.NewClient(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Errorf("failed to create secretmanager client: %v", err)
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
req := &secretmanagerpb.AccessSecretVersionRequest{
|
||||
Name: parent,
|
||||
}
|
||||
|
||||
result, err := client.AccessSecretVersion(ctx, req)
|
||||
if err != nil {
|
||||
return nil, errors.Errorf("failed to access secret version: %v", err)
|
||||
}
|
||||
|
||||
data, err := godotenv.Unmarshal(string(result.Payload.Data))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Secret{kv: data}, nil
|
||||
}
|
27
pkg/common/vars.go
Normal file
27
pkg/common/vars.go
Normal file
|
@ -0,0 +1,27 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
|
||||
"github.com/h2non/filetype"
|
||||
)
|
||||
|
||||
var (
|
||||
KB, MB, GB, TB, PB = 1e3, 1e6, 1e9, 1e12, 1e15
|
||||
IGNORED_EXTENSIONS = []string{"pdf", "mp4", "avi", "mpeg", "mpg", "mov", "wmv", "m4p", "swf", "mp2", "flv", "vob", "webm", "hdv", "3gp", "ogg", "mp3", "wav", "flac", "tif", "tiff", "jpg", "jpeg", "png", "gif", "zip", "webp"}
|
||||
)
|
||||
|
||||
func SkipFile(filename string, data []byte) bool {
|
||||
if filepath.Ext(filename) == "" {
|
||||
//no sepcified extension, check mimetype
|
||||
if filetype.IsArchive(data[:256]) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
for _, ext := range IGNORED_EXTENSIONS {
|
||||
if filepath.Ext(filename) == ext {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
36
pkg/decoders/decoders.go
Normal file
36
pkg/decoders/decoders.go
Normal file
|
@ -0,0 +1,36 @@
|
|||
package decoders
|
||||
|
||||
import (
|
||||
"github.com/trufflesecurity/trufflehog/pkg/sources"
|
||||
)
|
||||
|
||||
func DefaultDecoders() []Decoder {
|
||||
return []Decoder{
|
||||
&Plain{},
|
||||
}
|
||||
}
|
||||
|
||||
type Decoder interface {
|
||||
FromChunk(chunk *sources.Chunk) *sources.Chunk
|
||||
}
|
||||
|
||||
// Fuzz is an entrypoint for go-fuzz, which is an AFL-style fuzzing tool.
|
||||
// This one attempts to uncover any panics during decoding.
|
||||
func Fuzz(data []byte) int {
|
||||
decoded := false
|
||||
for i, decoder := range DefaultDecoders() {
|
||||
// Skip the first decoder (plain), because it will always decode and give
|
||||
// priority to the input (return 1)
|
||||
if i == 0 {
|
||||
continue
|
||||
}
|
||||
chunk := decoder.FromChunk(&sources.Chunk{Data: data})
|
||||
if chunk != nil {
|
||||
decoded = true
|
||||
}
|
||||
}
|
||||
if decoded {
|
||||
return 1 // prioritize the input
|
||||
}
|
||||
return -1 // don't add input to the corpus
|
||||
}
|
14
pkg/decoders/plain.go
Normal file
14
pkg/decoders/plain.go
Normal file
|
@ -0,0 +1,14 @@
|
|||
package decoders
|
||||
|
||||
import (
|
||||
"github.com/trufflesecurity/trufflehog/pkg/sources"
|
||||
)
|
||||
|
||||
// Ensure the Decoder satisfies the interface at compile time
|
||||
var _ Decoder = (*Plain)(nil)
|
||||
|
||||
type Plain struct{}
|
||||
|
||||
func (d *Plain) FromChunk(chunk *sources.Chunk) *sources.Chunk {
|
||||
return chunk
|
||||
}
|
77
pkg/detectors/detectors.go
Normal file
77
pkg/detectors/detectors.go
Normal file
|
@ -0,0 +1,77 @@
|
|||
package detectors
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/trufflesecurity/trufflehog/pkg/pb/detectorspb"
|
||||
"github.com/trufflesecurity/trufflehog/pkg/pb/source_metadatapb"
|
||||
"github.com/trufflesecurity/trufflehog/pkg/pb/sourcespb"
|
||||
|
||||
"github.com/trufflesecurity/trufflehog/pkg/sources"
|
||||
)
|
||||
|
||||
// Detector defines and interface for scanning for and verifying secrets.
|
||||
type Detector interface {
|
||||
// FromData will scan bytes for results, and optionally verify them.
|
||||
FromData(ctx context.Context, verify bool, data []byte) ([]Result, error)
|
||||
// Keywords are used for efficiently pre-filtering chunks using substring operations.
|
||||
// Use unique identifiers that are part of the secret if you can, or the provider name.
|
||||
Keywords() []string
|
||||
}
|
||||
|
||||
type Result struct {
|
||||
// DetectorType is the type of Detector.
|
||||
DetectorType detectorspb.DetectorType
|
||||
Verified bool
|
||||
// Raw contains the raw secret identifier data. Prefer IDs over secrets since it is used for deduping after hashing.
|
||||
Raw []byte
|
||||
// Redacted contains the redacted version of the raw secret identification data for display purposes.
|
||||
// A secret ID should be used if available.
|
||||
Redacted string
|
||||
ExtraData map[string]string
|
||||
StructuredData *detectorspb.StructuredData
|
||||
}
|
||||
|
||||
type ResultWithMetadata struct {
|
||||
// SourceMetadata contains source-specific contextual information
|
||||
SourceMetadata *source_metadatapb.MetaData
|
||||
// SourceID is the ID of the source that the API uses to map secrets to specific sources.
|
||||
SourceID int64
|
||||
// SourceType is the type of Source.
|
||||
SourceType sourcespb.SourceType
|
||||
// SourceName is the name of the Source.
|
||||
SourceName string
|
||||
Result
|
||||
}
|
||||
|
||||
func CopyMetadata(chunk *sources.Chunk, result Result) ResultWithMetadata {
|
||||
return ResultWithMetadata{
|
||||
SourceMetadata: chunk.SourceMetadata,
|
||||
SourceID: chunk.SourceID,
|
||||
SourceType: chunk.SourceType,
|
||||
SourceName: chunk.SourceName,
|
||||
Result: result,
|
||||
}
|
||||
}
|
||||
|
||||
// CleanResults returns all verified secrets, and if there are no verified secrets,
|
||||
// just one unverified secret if there are any.
|
||||
func CleanResults(results []Result) []Result {
|
||||
if len(results) == 0 {
|
||||
return results
|
||||
}
|
||||
|
||||
var cleaned = make([]Result, 0)
|
||||
|
||||
for _, s := range results {
|
||||
if s.Verified {
|
||||
cleaned = append(cleaned, s)
|
||||
}
|
||||
}
|
||||
|
||||
if len(cleaned) == 0 {
|
||||
return results[:1]
|
||||
}
|
||||
|
||||
return cleaned
|
||||
}
|
57
pkg/detectors/testdetector/testdetector.go
Normal file
57
pkg/detectors/testdetector/testdetector.go
Normal file
|
@ -0,0 +1,57 @@
|
|||
package testdetector
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/trufflesecurity/trufflehog/pkg/common"
|
||||
"github.com/trufflesecurity/trufflehog/pkg/detectors"
|
||||
"github.com/trufflesecurity/trufflehog/pkg/pb/detectorspb"
|
||||
)
|
||||
|
||||
type Detector struct{}
|
||||
|
||||
// Ensure the Detector satisfies the interface at compile time
|
||||
var _ detectors.Detector = (*Detector)(nil)
|
||||
|
||||
var (
|
||||
client = common.SaneHttpClient()
|
||||
|
||||
// Make sure that your group is surrounded in boundry characters such as below to reduce false positives
|
||||
keyPat = regexp.MustCompile(`\b(test)\b`)
|
||||
)
|
||||
|
||||
// Keywords are used for efficiently pre-filtering chunks.
|
||||
// Use identifiers in the secret preferably, or the provider name.
|
||||
func (d Detector) Keywords() []string {
|
||||
return []string{"test"}
|
||||
}
|
||||
|
||||
// FromData will find and optionally verify testdetector secrets in a given set of bytes.
|
||||
func (d Detector) FromData(ctx context.Context, verify bool, data []byte) (results []detectors.Result, err error) {
|
||||
dataStr := string(data)
|
||||
|
||||
matches := keyPat.FindAllStringSubmatch(dataStr, -1)
|
||||
|
||||
for _, match := range matches {
|
||||
if len(match) != 2 {
|
||||
continue
|
||||
}
|
||||
resMatch := strings.TrimSpace(match[1])
|
||||
|
||||
s1 := detectors.Result{
|
||||
DetectorType: detectorspb.DetectorType_AdafruitIO,
|
||||
Raw: []byte(resMatch),
|
||||
}
|
||||
|
||||
if verify {
|
||||
s1.Verified = true
|
||||
}
|
||||
|
||||
results = append(results, s1)
|
||||
}
|
||||
|
||||
return detectors.CleanResults(results), nil
|
||||
}
|
12
pkg/engine/defaults.go
Normal file
12
pkg/engine/defaults.go
Normal file
|
@ -0,0 +1,12 @@
|
|||
package engine
|
||||
|
||||
import (
|
||||
"github.com/trufflesecurity/trufflehog/pkg/detectors"
|
||||
"github.com/trufflesecurity/trufflehog/pkg/detectors/testdetector"
|
||||
)
|
||||
|
||||
func DefaultDetectors() []detectors.Detector {
|
||||
return []detectors.Detector{
|
||||
&testdetector.Detector{},
|
||||
}
|
||||
}
|
140
pkg/engine/engine.go
Normal file
140
pkg/engine/engine.go
Normal file
|
@ -0,0 +1,140 @@
|
|||
package engine
|
||||
|
||||
import (
|
||||
"context"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/trufflesecurity/trufflehog/pkg/decoders"
|
||||
"github.com/trufflesecurity/trufflehog/pkg/detectors"
|
||||
"github.com/trufflesecurity/trufflehog/pkg/sources"
|
||||
)
|
||||
|
||||
type Engine struct {
|
||||
concurrency int
|
||||
chunks chan *sources.Chunk
|
||||
results chan detectors.ResultWithMetadata
|
||||
decoders []decoders.Decoder
|
||||
detectors map[bool][]detectors.Detector
|
||||
}
|
||||
|
||||
type EngineOption func(*Engine)
|
||||
|
||||
func WithConcurrency(concurrency int) EngineOption {
|
||||
return func(e *Engine) {
|
||||
e.concurrency = concurrency
|
||||
}
|
||||
}
|
||||
|
||||
func WithDetectors(verify bool, d ...detectors.Detector) EngineOption {
|
||||
return func(e *Engine) {
|
||||
if e.detectors == nil {
|
||||
e.detectors = make(map[bool][]detectors.Detector)
|
||||
}
|
||||
if e.detectors[verify] == nil {
|
||||
e.detectors[verify] = []detectors.Detector{}
|
||||
}
|
||||
e.detectors[verify] = append(e.detectors[verify], d...)
|
||||
}
|
||||
}
|
||||
|
||||
func WithDecoders(decoders ...decoders.Decoder) EngineOption {
|
||||
return func(e *Engine) {
|
||||
e.decoders = decoders
|
||||
}
|
||||
}
|
||||
|
||||
func Start(ctx context.Context, options ...EngineOption) *Engine {
|
||||
e := &Engine{
|
||||
chunks: make(chan *sources.Chunk),
|
||||
results: make(chan detectors.ResultWithMetadata),
|
||||
}
|
||||
|
||||
for _, option := range options {
|
||||
option(e)
|
||||
}
|
||||
|
||||
// set defaults
|
||||
|
||||
if e.concurrency == 0 {
|
||||
numCPU := runtime.NumCPU()
|
||||
logrus.Warn("No concurrency specified, defaulting to ", numCPU)
|
||||
e.concurrency = numCPU
|
||||
}
|
||||
|
||||
var workerWg sync.WaitGroup
|
||||
|
||||
for i := 0; i < e.concurrency; i++ {
|
||||
workerWg.Add(1)
|
||||
go func() {
|
||||
e.detectorWorker(ctx)
|
||||
workerWg.Done()
|
||||
}()
|
||||
}
|
||||
|
||||
go func() {
|
||||
// close results chan when all workers are done
|
||||
workerWg.Wait()
|
||||
// not entirely sure why results don't get processed without this pause
|
||||
// since we've put all results on the channel at this point.
|
||||
time.Sleep(time.Second)
|
||||
close(e.ResultsChan())
|
||||
}()
|
||||
|
||||
if len(e.decoders) == 0 {
|
||||
e.decoders = decoders.DefaultDecoders()
|
||||
}
|
||||
|
||||
if len(e.detectors) == 0 {
|
||||
e.detectors = map[bool][]detectors.Detector{}
|
||||
e.detectors[true] = DefaultDetectors()
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
func (e *Engine) ChunksChan() chan *sources.Chunk {
|
||||
return e.chunks
|
||||
}
|
||||
|
||||
func (e *Engine) ResultsChan() chan detectors.ResultWithMetadata {
|
||||
return e.results
|
||||
}
|
||||
|
||||
func (e *Engine) detectorWorker(ctx context.Context) {
|
||||
for chunk := range e.chunks {
|
||||
for _, decoder := range e.decoders {
|
||||
decoded := decoder.FromChunk(chunk)
|
||||
if decoded == nil {
|
||||
continue
|
||||
}
|
||||
dataLower := strings.ToLower(string(decoded.Data))
|
||||
for verify, detectorsSet := range e.detectors {
|
||||
for _, detector := range detectorsSet {
|
||||
foundKeyword := false
|
||||
for _, kw := range detector.Keywords() {
|
||||
if strings.Contains(dataLower, strings.ToLower(kw)) {
|
||||
foundKeyword = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !foundKeyword {
|
||||
continue
|
||||
}
|
||||
results, err := detector.FromData(ctx, verify, decoded.Data)
|
||||
if err != nil {
|
||||
logrus.WithError(err).Error("could not scan chunk")
|
||||
continue
|
||||
}
|
||||
for _, result := range results {
|
||||
e.results <- detectors.CopyMetadata(chunk, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
111
pkg/engine/git.go
Normal file
111
pkg/engine/git.go
Normal file
|
@ -0,0 +1,111 @@
|
|||
package engine
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
gogit "github.com/go-git/go-git/v5"
|
||||
"github.com/go-git/go-git/v5/plumbing"
|
||||
"github.com/go-git/go-git/v5/plumbing/object"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/trufflesecurity/trufflehog/pkg/pb/source_metadatapb"
|
||||
"github.com/trufflesecurity/trufflehog/pkg/pb/sourcespb"
|
||||
"github.com/trufflesecurity/trufflehog/pkg/sources/git"
|
||||
)
|
||||
|
||||
func (e *Engine) ScanGit(ctx context.Context, gitScanURI, gitScanBranch, headRef string) error {
|
||||
var path string
|
||||
switch {
|
||||
case strings.HasPrefix(gitScanURI, "file://"):
|
||||
path = strings.TrimPrefix(gitScanURI, "file://")
|
||||
case strings.HasPrefix(gitScanURI, "https://"):
|
||||
// TODO: clone repo and get path
|
||||
return errors.New("TODO: clone repo and get path")
|
||||
default:
|
||||
logrus.Fatalf("Unsupported Git URI: %s", gitScanURI)
|
||||
}
|
||||
|
||||
repo, err := gogit.PlainOpenWithOptions(path, &gogit.PlainOpenOptions{DetectDotGit: true})
|
||||
if err != nil {
|
||||
return fmt.Errorf("could open repo: %s: %w", path, err)
|
||||
}
|
||||
|
||||
scanOptions := &gogit.LogOptions{
|
||||
All: true,
|
||||
Order: gogit.LogOrderCommitterTime,
|
||||
}
|
||||
|
||||
if gitScanBranch != "" {
|
||||
baseHash, err := git.TryAdditionalBaseRefs(repo, gitScanBranch)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not parse base revision: %q: %w", gitScanBranch, err)
|
||||
}
|
||||
|
||||
headHash, err := repo.ResolveRevision(plumbing.Revision(headRef))
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not parse revision: %q: %w", headRef, err)
|
||||
}
|
||||
|
||||
baseCommit, err := repo.CommitObject(*baseHash)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not find commit: %q: %w", headRef, err)
|
||||
}
|
||||
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"commit": baseCommit.Hash.String(),
|
||||
}).Debug("resolved base reference")
|
||||
|
||||
headCommit, err := repo.CommitObject(*headHash)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not find commit: %q: %w", headRef, err)
|
||||
}
|
||||
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"commit": headCommit.Hash.String(),
|
||||
}).Debug("resolved head reference")
|
||||
|
||||
mergeBase, err := baseCommit.MergeBase(headCommit)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not find common base between the given references: %q: %w", headRef, err)
|
||||
}
|
||||
|
||||
if len(mergeBase) == 0 {
|
||||
return fmt.Errorf("no common mergeable base between the given references: %q: %w", headRef, err)
|
||||
}
|
||||
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"commit": mergeBase[0].Hash.String(),
|
||||
}).Debug("resolved common merge base between references")
|
||||
|
||||
scanOptions = &gogit.LogOptions{
|
||||
From: *headHash,
|
||||
Order: gogit.LogOrderCommitterTime,
|
||||
}
|
||||
}
|
||||
|
||||
gitSource := git.NewGit(sourcespb.SourceType_SOURCE_TYPE_GIT, 0, 0, "local", true, runtime.NumCPU(),
|
||||
func(file, email, commit, repository string) *source_metadatapb.MetaData {
|
||||
return &source_metadatapb.MetaData{
|
||||
Data: &source_metadatapb.MetaData_Git{
|
||||
Git: &source_metadatapb.Git{
|
||||
Commit: commit,
|
||||
File: file,
|
||||
Email: email,
|
||||
Repository: repository,
|
||||
},
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
go func() {
|
||||
err := gitSource.ScanRepo(ctx, repo, scanOptions, &object.Commit{}, e.ChunksChan())
|
||||
if err != nil {
|
||||
logrus.WithError(err).Fatal("could not scan repo")
|
||||
}
|
||||
close(e.ChunksChan())
|
||||
}()
|
||||
return nil
|
||||
}
|
82
pkg/giturl/giturl.go
Normal file
82
pkg/giturl/giturl.go
Normal file
|
@ -0,0 +1,82 @@
|
|||
package giturl
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func NormalizeBitbucketRepo(repoURL string) (string, error) {
|
||||
if !strings.HasPrefix(repoURL, "https") {
|
||||
return "", errors.New("Bitbucket requires https repo urls: e.g. https://bitbucket.org/org/repo.git")
|
||||
}
|
||||
|
||||
return NormalizeOrgRepoURL("Gitlab", repoURL)
|
||||
}
|
||||
|
||||
func NormalizeGerritProject(project string) (string, error) {
|
||||
return "", errors.Errorf("Not yet implemented")
|
||||
}
|
||||
|
||||
func NormalizeGithubRepo(repoURL string) (string, error) {
|
||||
return NormalizeOrgRepoURL("Github", repoURL)
|
||||
}
|
||||
|
||||
func NormalizeGitlabRepo(repoURL string) (string, error) {
|
||||
if !strings.HasPrefix(repoURL, "https") {
|
||||
return "", errors.New("Gitlab requires https repo urls: e.g. https://gitlab.com/org/repo.git")
|
||||
}
|
||||
|
||||
return NormalizeOrgRepoURL("Gitlab", repoURL)
|
||||
}
|
||||
|
||||
// NormalizeOrgRepoURL attempts to normalize repos for any provider using the example.com/org/repo style.
|
||||
// e.g. %s, Gitlab and Bitbucket
|
||||
func NormalizeOrgRepoURL(provider, repoURL string) (string, error) {
|
||||
if strings.HasSuffix(repoURL, ".git") {
|
||||
return repoURL, nil
|
||||
}
|
||||
|
||||
parsed, err := url.Parse(repoURL)
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "Unable to parse %s Repo URL", provider)
|
||||
}
|
||||
|
||||
// The provider repo url should have a path length of 3
|
||||
// 0 / 1 / 2 (3 total)
|
||||
// e.g. example.com/org/repo
|
||||
// If it is 1, or the 2nd section is empty, it's likely just the org.
|
||||
// If it is 3, there's something at the end that shouldn't be there.
|
||||
// Let the user know in any case.
|
||||
switch parts := strings.Split(parsed.Path, "/"); {
|
||||
case len(parts) <= 1:
|
||||
return "", errors.Errorf("%s repo appears to be missing the path. Repo url: %q", provider, repoURL)
|
||||
|
||||
case len(parts) == 2:
|
||||
org := parts[1]
|
||||
|
||||
if len(org) == 0 {
|
||||
return "", errors.Errorf("%s repo appears to be missing the org name. Repo url: %q", provider, repoURL)
|
||||
} else {
|
||||
return "", errors.Errorf("%s repo appears to be missing the repo name. Org: %q Repo url: %q", provider, org, repoURL)
|
||||
}
|
||||
|
||||
case len(parts) == 3:
|
||||
org, repo := parts[1], parts[2]
|
||||
|
||||
if len(org) == 0 {
|
||||
return "", errors.Errorf("%s repo appears to be missing the org name. Repo url: %q", provider, repoURL)
|
||||
}
|
||||
if len(repo) == 0 {
|
||||
return "", errors.Errorf("%s repo appears to be missing the repo name. Org: %q Repo url: %q", provider, org, repoURL)
|
||||
}
|
||||
|
||||
case len(parts) > 3:
|
||||
return "", errors.Errorf("%s repo appears to be too long or contains a trailing slash. Repo url: %q", provider, repoURL)
|
||||
}
|
||||
|
||||
// If we're here it's probably a provider repo without ".git" at the end, so add it and return
|
||||
parsed.Path += ".git"
|
||||
return parsed.String(), nil
|
||||
}
|
95
pkg/giturl/giturl_test.go
Normal file
95
pkg/giturl/giturl_test.go
Normal file
|
@ -0,0 +1,95 @@
|
|||
package giturl
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func Test_NormalizeOrgRepoURL(t *testing.T) {
|
||||
tests := map[string]struct {
|
||||
Provider string
|
||||
Repo string
|
||||
Out string
|
||||
Err error
|
||||
}{
|
||||
"github is good": {Provider: "Github", Repo: "https://github.com/org/repo", Out: "https://github.com/org/repo.git", Err: nil},
|
||||
"gitlab is good": {Provider: "Gitlab", Repo: "https://gitlab.com/org/repo", Out: "https://gitlab.com/org/repo.git", Err: nil},
|
||||
"bitbucket is good": {Provider: "Bitbucket", Repo: "https://bitbucket.com/org/repo", Out: "https://bitbucket.com/org/repo.git", Err: nil},
|
||||
"example provider is good": {Provider: "example", Repo: "https://example.com/org/repo", Out: "https://example.com/org/repo.git", Err: nil},
|
||||
"example provider problem": {Provider: "example", Repo: "https://example.com/org", Out: "", Err: errors.Errorf("example repo appears to be missing the repo name. Org: %q Repo url: %q", "org", "https://example.com/org")},
|
||||
"no path": {Provider: "Github", Repo: "https://github.com", Out: "", Err: errors.Errorf("Github repo appears to be missing the path. Repo url: %q", "https://github.com")},
|
||||
"org but no repo": {Provider: "Github", Repo: "https://github.com/org", Out: "", Err: errors.Errorf("Github repo appears to be missing the repo name. Org: %q Repo url: %q", "org", "https://github.com/org")},
|
||||
"org but no repo with slash": {Provider: "Github", Repo: "https://github.com/org/", Out: "", Err: errors.Errorf("Github repo appears to be missing the repo name. Org: %q Repo url: %q", "org", "https://github.com/org/")},
|
||||
"two slashes": {Provider: "Github", Repo: "https://github.com//", Out: "", Err: errors.Errorf("Github repo appears to be missing the org name. Repo url: %q", "https://github.com//")},
|
||||
"repo with trailing slash": {Provider: "Github", Repo: "https://github.com/org/repo/", Out: "", Err: errors.Errorf("Github repo appears to be too long or contains a trailing slash. Repo url: %q", "https://github.com/org/repo/")},
|
||||
"too many url path parts": {Provider: "Github", Repo: "https://github.com/org/repo/unknown/", Out: "", Err: errors.Errorf("Github repo appears to be too long or contains a trailing slash. Repo url: %q", "https://github.com/org/repo/unknown/")},
|
||||
}
|
||||
|
||||
for name, tt := range tests {
|
||||
out, err := NormalizeOrgRepoURL(tt.Provider, tt.Repo)
|
||||
|
||||
switch {
|
||||
case err != nil && tt.Err != nil && (err.Error() != tt.Err.Error()):
|
||||
t.Errorf("Test %q, error does not match expected error, \n got: %v \nwant: %v", name, err.Error(), tt.Err.Error())
|
||||
case (err != nil && tt.Err == nil) || (err == nil && tt.Err != nil):
|
||||
t.Errorf("Test %q, error does not match expected error, \n got: %v \nwant: %v", name, err, tt.Err)
|
||||
}
|
||||
|
||||
if out != tt.Out {
|
||||
t.Errorf("Test %q, output does not match expected out, got: %q want: %q", name, out, tt.Out)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Test_NormalizeBitbucketRepo(t *testing.T) {
|
||||
tests := map[string]struct {
|
||||
Repo string
|
||||
Out string
|
||||
Err error
|
||||
}{
|
||||
"good": {Repo: "https://bitbucket.org/org/repo", Out: "https://bitbucket.org/org/repo.git", Err: nil},
|
||||
"bitbucket needs https": {Repo: "git@bitbucket.org:org/repo.git", Out: "", Err: errors.New("Bitbucket requires https repo urls: e.g. https://bitbucket.org/org/repo.git")},
|
||||
}
|
||||
|
||||
for name, tt := range tests {
|
||||
out, err := NormalizeBitbucketRepo(tt.Repo)
|
||||
|
||||
switch {
|
||||
case err != nil && tt.Err != nil && (err.Error() != tt.Err.Error()):
|
||||
t.Errorf("Test %q, error does not match expected error, \n got: %v \nwant: %v", name, err.Error(), tt.Err.Error())
|
||||
case (err != nil && tt.Err == nil) || (err == nil && tt.Err != nil):
|
||||
t.Errorf("Test %q, error does not match expected error, \n got: %v \nwant: %v", name, err, tt.Err)
|
||||
}
|
||||
|
||||
if out != tt.Out {
|
||||
t.Errorf("Test %q, output does not match expected out, got: %q want: %q", name, out, tt.Out)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Test_NormalizeGitlabRepo(t *testing.T) {
|
||||
tests := map[string]struct {
|
||||
Repo string
|
||||
Out string
|
||||
Err error
|
||||
}{
|
||||
"good": {Repo: "https://gitlab.com/org/repo", Out: "https://gitlab.com/org/repo.git", Err: nil},
|
||||
"gitlab needs https": {Repo: "git@gitlab.com:org/repo.git:", Out: "", Err: errors.New("Gitlab requires https repo urls: e.g. https://gitlab.com/org/repo.git")},
|
||||
}
|
||||
|
||||
for name, tt := range tests {
|
||||
out, err := NormalizeGitlabRepo(tt.Repo)
|
||||
|
||||
switch {
|
||||
case err != nil && tt.Err != nil && (err.Error() != tt.Err.Error()):
|
||||
t.Errorf("Test %q, error does not match expected error, \n got: %v \nwant: %v", name, err.Error(), tt.Err.Error())
|
||||
case (err != nil && tt.Err == nil) || (err == nil && tt.Err != nil):
|
||||
t.Errorf("Test %q, error does not match expected error, \n got: %v \nwant: %v", name, err, tt.Err)
|
||||
}
|
||||
|
||||
if out != tt.Out {
|
||||
t.Errorf("Test %q, output does not match expected out, got: %q want: %q", name, out, tt.Out)
|
||||
}
|
||||
}
|
||||
}
|
839
pkg/pb/credentialspb/credentials.pb.go
Normal file
839
pkg/pb/credentialspb/credentials.pb.go
Normal file
|
@ -0,0 +1,839 @@
|
|||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.27.1
|
||||
// protoc v3.19.1
|
||||
// source: credentials.proto
|
||||
|
||||
package credentialspb
|
||||
|
||||
import (
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
type Unauthenticated struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
}
|
||||
|
||||
func (x *Unauthenticated) Reset() {
|
||||
*x = Unauthenticated{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_credentials_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *Unauthenticated) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*Unauthenticated) ProtoMessage() {}
|
||||
|
||||
func (x *Unauthenticated) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_credentials_proto_msgTypes[0]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use Unauthenticated.ProtoReflect.Descriptor instead.
|
||||
func (*Unauthenticated) Descriptor() ([]byte, []int) {
|
||||
return file_credentials_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
type CloudEnvironment struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
}
|
||||
|
||||
func (x *CloudEnvironment) Reset() {
|
||||
*x = CloudEnvironment{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_credentials_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *CloudEnvironment) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*CloudEnvironment) ProtoMessage() {}
|
||||
|
||||
func (x *CloudEnvironment) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_credentials_proto_msgTypes[1]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use CloudEnvironment.ProtoReflect.Descriptor instead.
|
||||
func (*CloudEnvironment) Descriptor() ([]byte, []int) {
|
||||
return file_credentials_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
type BasicAuth struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Username string `protobuf:"bytes,1,opt,name=username,proto3" json:"username,omitempty"`
|
||||
Password string `protobuf:"bytes,2,opt,name=password,proto3" json:"password,omitempty"`
|
||||
}
|
||||
|
||||
func (x *BasicAuth) Reset() {
|
||||
*x = BasicAuth{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_credentials_proto_msgTypes[2]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *BasicAuth) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*BasicAuth) ProtoMessage() {}
|
||||
|
||||
func (x *BasicAuth) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_credentials_proto_msgTypes[2]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use BasicAuth.ProtoReflect.Descriptor instead.
|
||||
func (*BasicAuth) Descriptor() ([]byte, []int) {
|
||||
return file_credentials_proto_rawDescGZIP(), []int{2}
|
||||
}
|
||||
|
||||
func (x *BasicAuth) GetUsername() string {
|
||||
if x != nil {
|
||||
return x.Username
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *BasicAuth) GetPassword() string {
|
||||
if x != nil {
|
||||
return x.Password
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type ClientCrednetials struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
TenantId string `protobuf:"bytes,1,opt,name=tenant_id,json=tenantId,proto3" json:"tenant_id,omitempty"`
|
||||
ClientId string `protobuf:"bytes,2,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty"`
|
||||
ClientSecret string `protobuf:"bytes,3,opt,name=client_secret,json=clientSecret,proto3" json:"client_secret,omitempty"`
|
||||
}
|
||||
|
||||
func (x *ClientCrednetials) Reset() {
|
||||
*x = ClientCrednetials{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_credentials_proto_msgTypes[3]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *ClientCrednetials) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ClientCrednetials) ProtoMessage() {}
|
||||
|
||||
func (x *ClientCrednetials) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_credentials_proto_msgTypes[3]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use ClientCrednetials.ProtoReflect.Descriptor instead.
|
||||
func (*ClientCrednetials) Descriptor() ([]byte, []int) {
|
||||
return file_credentials_proto_rawDescGZIP(), []int{3}
|
||||
}
|
||||
|
||||
func (x *ClientCrednetials) GetTenantId() string {
|
||||
if x != nil {
|
||||
return x.TenantId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ClientCrednetials) GetClientId() string {
|
||||
if x != nil {
|
||||
return x.ClientId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ClientCrednetials) GetClientSecret() string {
|
||||
if x != nil {
|
||||
return x.ClientSecret
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type ClientCertificate struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
TenantId string `protobuf:"bytes,1,opt,name=tenant_id,json=tenantId,proto3" json:"tenant_id,omitempty"`
|
||||
ClientId string `protobuf:"bytes,2,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty"`
|
||||
CertificatePath string `protobuf:"bytes,3,opt,name=certificate_path,json=certificatePath,proto3" json:"certificate_path,omitempty"`
|
||||
CertificatePassword string `protobuf:"bytes,4,opt,name=certificate_password,json=certificatePassword,proto3" json:"certificate_password,omitempty"`
|
||||
}
|
||||
|
||||
func (x *ClientCertificate) Reset() {
|
||||
*x = ClientCertificate{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_credentials_proto_msgTypes[4]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *ClientCertificate) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ClientCertificate) ProtoMessage() {}
|
||||
|
||||
func (x *ClientCertificate) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_credentials_proto_msgTypes[4]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use ClientCertificate.ProtoReflect.Descriptor instead.
|
||||
func (*ClientCertificate) Descriptor() ([]byte, []int) {
|
||||
return file_credentials_proto_rawDescGZIP(), []int{4}
|
||||
}
|
||||
|
||||
func (x *ClientCertificate) GetTenantId() string {
|
||||
if x != nil {
|
||||
return x.TenantId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ClientCertificate) GetClientId() string {
|
||||
if x != nil {
|
||||
return x.ClientId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ClientCertificate) GetCertificatePath() string {
|
||||
if x != nil {
|
||||
return x.CertificatePath
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ClientCertificate) GetCertificatePassword() string {
|
||||
if x != nil {
|
||||
return x.CertificatePassword
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type Oauth2 struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
RefreshToken string `protobuf:"bytes,1,opt,name=refresh_token,json=refreshToken,proto3" json:"refresh_token,omitempty"`
|
||||
ClientId string `protobuf:"bytes,2,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty"`
|
||||
ClientSecret string `protobuf:"bytes,3,opt,name=client_secret,json=clientSecret,proto3" json:"client_secret,omitempty"`
|
||||
}
|
||||
|
||||
func (x *Oauth2) Reset() {
|
||||
*x = Oauth2{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_credentials_proto_msgTypes[5]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *Oauth2) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*Oauth2) ProtoMessage() {}
|
||||
|
||||
func (x *Oauth2) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_credentials_proto_msgTypes[5]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use Oauth2.ProtoReflect.Descriptor instead.
|
||||
func (*Oauth2) Descriptor() ([]byte, []int) {
|
||||
return file_credentials_proto_rawDescGZIP(), []int{5}
|
||||
}
|
||||
|
||||
func (x *Oauth2) GetRefreshToken() string {
|
||||
if x != nil {
|
||||
return x.RefreshToken
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Oauth2) GetClientId() string {
|
||||
if x != nil {
|
||||
return x.ClientId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Oauth2) GetClientSecret() string {
|
||||
if x != nil {
|
||||
return x.ClientSecret
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type KeySecret struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
|
||||
Secret string `protobuf:"bytes,2,opt,name=secret,proto3" json:"secret,omitempty"`
|
||||
}
|
||||
|
||||
func (x *KeySecret) Reset() {
|
||||
*x = KeySecret{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_credentials_proto_msgTypes[6]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *KeySecret) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*KeySecret) ProtoMessage() {}
|
||||
|
||||
func (x *KeySecret) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_credentials_proto_msgTypes[6]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use KeySecret.ProtoReflect.Descriptor instead.
|
||||
func (*KeySecret) Descriptor() ([]byte, []int) {
|
||||
return file_credentials_proto_rawDescGZIP(), []int{6}
|
||||
}
|
||||
|
||||
func (x *KeySecret) GetKey() string {
|
||||
if x != nil {
|
||||
return x.Key
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *KeySecret) GetSecret() string {
|
||||
if x != nil {
|
||||
return x.Secret
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type AWS struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
|
||||
Secret string `protobuf:"bytes,2,opt,name=secret,proto3" json:"secret,omitempty"`
|
||||
Region string `protobuf:"bytes,3,opt,name=region,proto3" json:"region,omitempty"`
|
||||
}
|
||||
|
||||
func (x *AWS) Reset() {
|
||||
*x = AWS{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_credentials_proto_msgTypes[7]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *AWS) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*AWS) ProtoMessage() {}
|
||||
|
||||
func (x *AWS) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_credentials_proto_msgTypes[7]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use AWS.ProtoReflect.Descriptor instead.
|
||||
func (*AWS) Descriptor() ([]byte, []int) {
|
||||
return file_credentials_proto_rawDescGZIP(), []int{7}
|
||||
}
|
||||
|
||||
func (x *AWS) GetKey() string {
|
||||
if x != nil {
|
||||
return x.Key
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *AWS) GetSecret() string {
|
||||
if x != nil {
|
||||
return x.Secret
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *AWS) GetRegion() string {
|
||||
if x != nil {
|
||||
return x.Region
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type SES struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Creds *AWS `protobuf:"bytes,1,opt,name=creds,proto3" json:"creds,omitempty"`
|
||||
Sender string `protobuf:"bytes,2,opt,name=sender,proto3" json:"sender,omitempty"`
|
||||
Recipients []string `protobuf:"bytes,3,rep,name=recipients,proto3" json:"recipients,omitempty"`
|
||||
}
|
||||
|
||||
func (x *SES) Reset() {
|
||||
*x = SES{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_credentials_proto_msgTypes[8]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *SES) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*SES) ProtoMessage() {}
|
||||
|
||||
func (x *SES) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_credentials_proto_msgTypes[8]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use SES.ProtoReflect.Descriptor instead.
|
||||
func (*SES) Descriptor() ([]byte, []int) {
|
||||
return file_credentials_proto_rawDescGZIP(), []int{8}
|
||||
}
|
||||
|
||||
func (x *SES) GetCreds() *AWS {
|
||||
if x != nil {
|
||||
return x.Creds
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *SES) GetSender() string {
|
||||
if x != nil {
|
||||
return x.Sender
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *SES) GetRecipients() []string {
|
||||
if x != nil {
|
||||
return x.Recipients
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type GitHubApp struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
PrivateKey string `protobuf:"bytes,1,opt,name=private_key,json=privateKey,proto3" json:"private_key,omitempty"`
|
||||
InstallationId string `protobuf:"bytes,2,opt,name=installation_id,json=installationId,proto3" json:"installation_id,omitempty"`
|
||||
AppId string `protobuf:"bytes,3,opt,name=app_id,json=appId,proto3" json:"app_id,omitempty"`
|
||||
}
|
||||
|
||||
func (x *GitHubApp) Reset() {
|
||||
*x = GitHubApp{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_credentials_proto_msgTypes[9]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *GitHubApp) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*GitHubApp) ProtoMessage() {}
|
||||
|
||||
func (x *GitHubApp) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_credentials_proto_msgTypes[9]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use GitHubApp.ProtoReflect.Descriptor instead.
|
||||
func (*GitHubApp) Descriptor() ([]byte, []int) {
|
||||
return file_credentials_proto_rawDescGZIP(), []int{9}
|
||||
}
|
||||
|
||||
func (x *GitHubApp) GetPrivateKey() string {
|
||||
if x != nil {
|
||||
return x.PrivateKey
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *GitHubApp) GetInstallationId() string {
|
||||
if x != nil {
|
||||
return x.InstallationId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *GitHubApp) GetAppId() string {
|
||||
if x != nil {
|
||||
return x.AppId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
var File_credentials_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_credentials_proto_rawDesc = []byte{
|
||||
0x0a, 0x11, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x2e, 0x70, 0x72,
|
||||
0x6f, 0x74, 0x6f, 0x12, 0x0b, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73,
|
||||
0x22, 0x11, 0x0a, 0x0f, 0x55, 0x6e, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61,
|
||||
0x74, 0x65, 0x64, 0x22, 0x12, 0x0a, 0x10, 0x43, 0x6c, 0x6f, 0x75, 0x64, 0x45, 0x6e, 0x76, 0x69,
|
||||
0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x22, 0x43, 0x0a, 0x09, 0x42, 0x61, 0x73, 0x69, 0x63,
|
||||
0x41, 0x75, 0x74, 0x68, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65,
|
||||
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65,
|
||||
0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x02, 0x20, 0x01,
|
||||
0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x22, 0x72, 0x0a, 0x11,
|
||||
0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x43, 0x72, 0x65, 0x64, 0x6e, 0x65, 0x74, 0x69, 0x61, 0x6c,
|
||||
0x73, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01,
|
||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x1b,
|
||||
0x0a, 0x09, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28,
|
||||
0x09, 0x52, 0x08, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x63,
|
||||
0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x03, 0x20, 0x01,
|
||||
0x28, 0x09, 0x52, 0x0c, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74,
|
||||
0x22, 0xab, 0x01, 0x0a, 0x11, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x43, 0x65, 0x72, 0x74, 0x69,
|
||||
0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74,
|
||||
0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x65, 0x6e, 0x61, 0x6e,
|
||||
0x74, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64,
|
||||
0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, 0x64,
|
||||
0x12, 0x29, 0x0a, 0x10, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x5f,
|
||||
0x70, 0x61, 0x74, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x63, 0x65, 0x72, 0x74,
|
||||
0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x50, 0x61, 0x74, 0x68, 0x12, 0x31, 0x0a, 0x14, 0x63,
|
||||
0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x5f, 0x70, 0x61, 0x73, 0x73, 0x77,
|
||||
0x6f, 0x72, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x63, 0x65, 0x72, 0x74, 0x69,
|
||||
0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x22, 0x6f,
|
||||
0x0a, 0x06, 0x4f, 0x61, 0x75, 0x74, 0x68, 0x32, 0x12, 0x23, 0x0a, 0x0d, 0x72, 0x65, 0x66, 0x72,
|
||||
0x65, 0x73, 0x68, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||
0x0c, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x1b, 0x0a,
|
||||
0x09, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
|
||||
0x52, 0x08, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6c,
|
||||
0x69, 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28,
|
||||
0x09, 0x52, 0x0c, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x22,
|
||||
0x35, 0x0a, 0x09, 0x4b, 0x65, 0x79, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x10, 0x0a, 0x03,
|
||||
0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x16,
|
||||
0x0a, 0x06, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06,
|
||||
0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x22, 0x47, 0x0a, 0x03, 0x41, 0x57, 0x53, 0x12, 0x10, 0x0a,
|
||||
0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12,
|
||||
0x16, 0x0a, 0x06, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||
0x06, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x67, 0x69, 0x6f,
|
||||
0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x22,
|
||||
0x65, 0x0a, 0x03, 0x53, 0x45, 0x53, 0x12, 0x26, 0x0a, 0x05, 0x63, 0x72, 0x65, 0x64, 0x73, 0x18,
|
||||
0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69,
|
||||
0x61, 0x6c, 0x73, 0x2e, 0x41, 0x57, 0x53, 0x52, 0x05, 0x63, 0x72, 0x65, 0x64, 0x73, 0x12, 0x16,
|
||||
0x0a, 0x06, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06,
|
||||
0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x12, 0x1e, 0x0a, 0x0a, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69,
|
||||
0x65, 0x6e, 0x74, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x65, 0x63, 0x69,
|
||||
0x70, 0x69, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x6c, 0x0a, 0x09, 0x47, 0x69, 0x74, 0x48, 0x75, 0x62,
|
||||
0x41, 0x70, 0x70, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b,
|
||||
0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74,
|
||||
0x65, 0x4b, 0x65, 0x79, 0x12, 0x27, 0x0a, 0x0f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61,
|
||||
0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x69,
|
||||
0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x15, 0x0a,
|
||||
0x06, 0x61, 0x70, 0x70, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x61,
|
||||
0x70, 0x70, 0x49, 0x64, 0x42, 0x3c, 0x5a, 0x3a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63,
|
||||
0x6f, 0x6d, 0x2f, 0x74, 0x72, 0x75, 0x66, 0x66, 0x6c, 0x65, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69,
|
||||
0x74, 0x79, 0x2f, 0x74, 0x72, 0x75, 0x66, 0x66, 0x6c, 0x65, 0x68, 0x6f, 0x67, 0x2f, 0x70, 0x6b,
|
||||
0x67, 0x2f, 0x70, 0x62, 0x2f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73,
|
||||
0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
file_credentials_proto_rawDescOnce sync.Once
|
||||
file_credentials_proto_rawDescData = file_credentials_proto_rawDesc
|
||||
)
|
||||
|
||||
func file_credentials_proto_rawDescGZIP() []byte {
|
||||
file_credentials_proto_rawDescOnce.Do(func() {
|
||||
file_credentials_proto_rawDescData = protoimpl.X.CompressGZIP(file_credentials_proto_rawDescData)
|
||||
})
|
||||
return file_credentials_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_credentials_proto_msgTypes = make([]protoimpl.MessageInfo, 10)
|
||||
var file_credentials_proto_goTypes = []interface{}{
|
||||
(*Unauthenticated)(nil), // 0: credentials.Unauthenticated
|
||||
(*CloudEnvironment)(nil), // 1: credentials.CloudEnvironment
|
||||
(*BasicAuth)(nil), // 2: credentials.BasicAuth
|
||||
(*ClientCrednetials)(nil), // 3: credentials.ClientCrednetials
|
||||
(*ClientCertificate)(nil), // 4: credentials.ClientCertificate
|
||||
(*Oauth2)(nil), // 5: credentials.Oauth2
|
||||
(*KeySecret)(nil), // 6: credentials.KeySecret
|
||||
(*AWS)(nil), // 7: credentials.AWS
|
||||
(*SES)(nil), // 8: credentials.SES
|
||||
(*GitHubApp)(nil), // 9: credentials.GitHubApp
|
||||
}
|
||||
var file_credentials_proto_depIdxs = []int32{
|
||||
7, // 0: credentials.SES.creds:type_name -> credentials.AWS
|
||||
1, // [1:1] is the sub-list for method output_type
|
||||
1, // [1:1] is the sub-list for method input_type
|
||||
1, // [1:1] is the sub-list for extension type_name
|
||||
1, // [1:1] is the sub-list for extension extendee
|
||||
0, // [0:1] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_credentials_proto_init() }
|
||||
func file_credentials_proto_init() {
|
||||
if File_credentials_proto != nil {
|
||||
return
|
||||
}
|
||||
if !protoimpl.UnsafeEnabled {
|
||||
file_credentials_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*Unauthenticated); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_credentials_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*CloudEnvironment); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_credentials_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*BasicAuth); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_credentials_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*ClientCrednetials); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_credentials_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*ClientCertificate); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_credentials_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*Oauth2); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_credentials_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*KeySecret); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_credentials_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*AWS); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_credentials_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*SES); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_credentials_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*GitHubApp); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_credentials_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 10,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
GoTypes: file_credentials_proto_goTypes,
|
||||
DependencyIndexes: file_credentials_proto_depIdxs,
|
||||
MessageInfos: file_credentials_proto_msgTypes,
|
||||
}.Build()
|
||||
File_credentials_proto = out.File
|
||||
file_credentials_proto_rawDesc = nil
|
||||
file_credentials_proto_goTypes = nil
|
||||
file_credentials_proto_depIdxs = nil
|
||||
}
|
1092
pkg/pb/credentialspb/credentials.pb.validate.go
Normal file
1092
pkg/pb/credentialspb/credentials.pb.validate.go
Normal file
File diff suppressed because it is too large
Load diff
3474
pkg/pb/detectorspb/detectors.pb.go
Normal file
3474
pkg/pb/detectorspb/detectors.pb.go
Normal file
File diff suppressed because it is too large
Load diff
546
pkg/pb/detectorspb/detectors.pb.validate.go
Normal file
546
pkg/pb/detectorspb/detectors.pb.validate.go
Normal file
|
@ -0,0 +1,546 @@
|
|||
// Code generated by protoc-gen-validate. DO NOT EDIT.
|
||||
// source: detectors.proto
|
||||
|
||||
package detectorspb
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/mail"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode/utf8"
|
||||
|
||||
"google.golang.org/protobuf/types/known/anypb"
|
||||
)
|
||||
|
||||
// ensure the imports are used
|
||||
var (
|
||||
_ = bytes.MinRead
|
||||
_ = errors.New("")
|
||||
_ = fmt.Print
|
||||
_ = utf8.UTFMax
|
||||
_ = (*regexp.Regexp)(nil)
|
||||
_ = (*strings.Reader)(nil)
|
||||
_ = net.IPv4len
|
||||
_ = time.Duration(0)
|
||||
_ = (*url.URL)(nil)
|
||||
_ = (*mail.Address)(nil)
|
||||
_ = anypb.Any{}
|
||||
_ = sort.Sort
|
||||
)
|
||||
|
||||
// Validate checks the field values on Result with the rules defined in the
|
||||
// proto definition for this message. If any rules are violated, the first
|
||||
// error encountered is returned, or nil if there are no violations.
|
||||
func (m *Result) Validate() error {
|
||||
return m.validate(false)
|
||||
}
|
||||
|
||||
// ValidateAll checks the field values on Result with the rules defined in the
|
||||
// proto definition for this message. If any rules are violated, the result is
|
||||
// a list of violation errors wrapped in ResultMultiError, or nil if none found.
|
||||
func (m *Result) ValidateAll() error {
|
||||
return m.validate(true)
|
||||
}
|
||||
|
||||
func (m *Result) validate(all bool) error {
|
||||
if m == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var errors []error
|
||||
|
||||
// no validation rules for SourceId
|
||||
|
||||
// no validation rules for Redacted
|
||||
|
||||
// no validation rules for Verified
|
||||
|
||||
// no validation rules for Hash
|
||||
|
||||
// no validation rules for ExtraData
|
||||
|
||||
if all {
|
||||
switch v := interface{}(m.GetStructuredData()).(type) {
|
||||
case interface{ ValidateAll() error }:
|
||||
if err := v.ValidateAll(); err != nil {
|
||||
errors = append(errors, ResultValidationError{
|
||||
field: "StructuredData",
|
||||
reason: "embedded message failed validation",
|
||||
cause: err,
|
||||
})
|
||||
}
|
||||
case interface{ Validate() error }:
|
||||
if err := v.Validate(); err != nil {
|
||||
errors = append(errors, ResultValidationError{
|
||||
field: "StructuredData",
|
||||
reason: "embedded message failed validation",
|
||||
cause: err,
|
||||
})
|
||||
}
|
||||
}
|
||||
} else if v, ok := interface{}(m.GetStructuredData()).(interface{ Validate() error }); ok {
|
||||
if err := v.Validate(); err != nil {
|
||||
return ResultValidationError{
|
||||
field: "StructuredData",
|
||||
reason: "embedded message failed validation",
|
||||
cause: err,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(errors) > 0 {
|
||||
return ResultMultiError(errors)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ResultMultiError is an error wrapping multiple validation errors returned by
|
||||
// Result.ValidateAll() if the designated constraints aren't met.
|
||||
type ResultMultiError []error
|
||||
|
||||
// Error returns a concatenation of all the error messages it wraps.
|
||||
func (m ResultMultiError) Error() string {
|
||||
var msgs []string
|
||||
for _, err := range m {
|
||||
msgs = append(msgs, err.Error())
|
||||
}
|
||||
return strings.Join(msgs, "; ")
|
||||
}
|
||||
|
||||
// AllErrors returns a list of validation violation errors.
|
||||
func (m ResultMultiError) AllErrors() []error { return m }
|
||||
|
||||
// ResultValidationError is the validation error returned by Result.Validate if
|
||||
// the designated constraints aren't met.
|
||||
type ResultValidationError struct {
|
||||
field string
|
||||
reason string
|
||||
cause error
|
||||
key bool
|
||||
}
|
||||
|
||||
// Field function returns field value.
|
||||
func (e ResultValidationError) Field() string { return e.field }
|
||||
|
||||
// Reason function returns reason value.
|
||||
func (e ResultValidationError) Reason() string { return e.reason }
|
||||
|
||||
// Cause function returns cause value.
|
||||
func (e ResultValidationError) Cause() error { return e.cause }
|
||||
|
||||
// Key function returns key value.
|
||||
func (e ResultValidationError) Key() bool { return e.key }
|
||||
|
||||
// ErrorName returns error name.
|
||||
func (e ResultValidationError) ErrorName() string { return "ResultValidationError" }
|
||||
|
||||
// Error satisfies the builtin error interface
|
||||
func (e ResultValidationError) Error() string {
|
||||
cause := ""
|
||||
if e.cause != nil {
|
||||
cause = fmt.Sprintf(" | caused by: %v", e.cause)
|
||||
}
|
||||
|
||||
key := ""
|
||||
if e.key {
|
||||
key = "key for "
|
||||
}
|
||||
|
||||
return fmt.Sprintf(
|
||||
"invalid %sResult.%s: %s%s",
|
||||
key,
|
||||
e.field,
|
||||
e.reason,
|
||||
cause)
|
||||
}
|
||||
|
||||
var _ error = ResultValidationError{}
|
||||
|
||||
var _ interface {
|
||||
Field() string
|
||||
Reason() string
|
||||
Key() bool
|
||||
Cause() error
|
||||
ErrorName() string
|
||||
} = ResultValidationError{}
|
||||
|
||||
// Validate checks the field values on StructuredData with the rules defined in
|
||||
// the proto definition for this message. If any rules are violated, the first
|
||||
// error encountered is returned, or nil if there are no violations.
|
||||
func (m *StructuredData) Validate() error {
|
||||
return m.validate(false)
|
||||
}
|
||||
|
||||
// ValidateAll checks the field values on StructuredData with the rules defined
|
||||
// in the proto definition for this message. If any rules are violated, the
|
||||
// result is a list of violation errors wrapped in StructuredDataMultiError,
|
||||
// or nil if none found.
|
||||
func (m *StructuredData) ValidateAll() error {
|
||||
return m.validate(true)
|
||||
}
|
||||
|
||||
func (m *StructuredData) validate(all bool) error {
|
||||
if m == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var errors []error
|
||||
|
||||
for idx, item := range m.GetTlsPrivateKey() {
|
||||
_, _ = idx, item
|
||||
|
||||
if all {
|
||||
switch v := interface{}(item).(type) {
|
||||
case interface{ ValidateAll() error }:
|
||||
if err := v.ValidateAll(); err != nil {
|
||||
errors = append(errors, StructuredDataValidationError{
|
||||
field: fmt.Sprintf("TlsPrivateKey[%v]", idx),
|
||||
reason: "embedded message failed validation",
|
||||
cause: err,
|
||||
})
|
||||
}
|
||||
case interface{ Validate() error }:
|
||||
if err := v.Validate(); err != nil {
|
||||
errors = append(errors, StructuredDataValidationError{
|
||||
field: fmt.Sprintf("TlsPrivateKey[%v]", idx),
|
||||
reason: "embedded message failed validation",
|
||||
cause: err,
|
||||
})
|
||||
}
|
||||
}
|
||||
} else if v, ok := interface{}(item).(interface{ Validate() error }); ok {
|
||||
if err := v.Validate(); err != nil {
|
||||
return StructuredDataValidationError{
|
||||
field: fmt.Sprintf("TlsPrivateKey[%v]", idx),
|
||||
reason: "embedded message failed validation",
|
||||
cause: err,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for idx, item := range m.GetGithubSshKey() {
|
||||
_, _ = idx, item
|
||||
|
||||
if all {
|
||||
switch v := interface{}(item).(type) {
|
||||
case interface{ ValidateAll() error }:
|
||||
if err := v.ValidateAll(); err != nil {
|
||||
errors = append(errors, StructuredDataValidationError{
|
||||
field: fmt.Sprintf("GithubSshKey[%v]", idx),
|
||||
reason: "embedded message failed validation",
|
||||
cause: err,
|
||||
})
|
||||
}
|
||||
case interface{ Validate() error }:
|
||||
if err := v.Validate(); err != nil {
|
||||
errors = append(errors, StructuredDataValidationError{
|
||||
field: fmt.Sprintf("GithubSshKey[%v]", idx),
|
||||
reason: "embedded message failed validation",
|
||||
cause: err,
|
||||
})
|
||||
}
|
||||
}
|
||||
} else if v, ok := interface{}(item).(interface{ Validate() error }); ok {
|
||||
if err := v.Validate(); err != nil {
|
||||
return StructuredDataValidationError{
|
||||
field: fmt.Sprintf("GithubSshKey[%v]", idx),
|
||||
reason: "embedded message failed validation",
|
||||
cause: err,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if len(errors) > 0 {
|
||||
return StructuredDataMultiError(errors)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// StructuredDataMultiError is an error wrapping multiple validation errors
|
||||
// returned by StructuredData.ValidateAll() if the designated constraints
|
||||
// aren't met.
|
||||
type StructuredDataMultiError []error
|
||||
|
||||
// Error returns a concatenation of all the error messages it wraps.
|
||||
func (m StructuredDataMultiError) Error() string {
|
||||
var msgs []string
|
||||
for _, err := range m {
|
||||
msgs = append(msgs, err.Error())
|
||||
}
|
||||
return strings.Join(msgs, "; ")
|
||||
}
|
||||
|
||||
// AllErrors returns a list of validation violation errors.
|
||||
func (m StructuredDataMultiError) AllErrors() []error { return m }
|
||||
|
||||
// StructuredDataValidationError is the validation error returned by
|
||||
// StructuredData.Validate if the designated constraints aren't met.
|
||||
type StructuredDataValidationError struct {
|
||||
field string
|
||||
reason string
|
||||
cause error
|
||||
key bool
|
||||
}
|
||||
|
||||
// Field function returns field value.
|
||||
func (e StructuredDataValidationError) Field() string { return e.field }
|
||||
|
||||
// Reason function returns reason value.
|
||||
func (e StructuredDataValidationError) Reason() string { return e.reason }
|
||||
|
||||
// Cause function returns cause value.
|
||||
func (e StructuredDataValidationError) Cause() error { return e.cause }
|
||||
|
||||
// Key function returns key value.
|
||||
func (e StructuredDataValidationError) Key() bool { return e.key }
|
||||
|
||||
// ErrorName returns error name.
|
||||
func (e StructuredDataValidationError) ErrorName() string { return "StructuredDataValidationError" }
|
||||
|
||||
// Error satisfies the builtin error interface
|
||||
func (e StructuredDataValidationError) Error() string {
|
||||
cause := ""
|
||||
if e.cause != nil {
|
||||
cause = fmt.Sprintf(" | caused by: %v", e.cause)
|
||||
}
|
||||
|
||||
key := ""
|
||||
if e.key {
|
||||
key = "key for "
|
||||
}
|
||||
|
||||
return fmt.Sprintf(
|
||||
"invalid %sStructuredData.%s: %s%s",
|
||||
key,
|
||||
e.field,
|
||||
e.reason,
|
||||
cause)
|
||||
}
|
||||
|
||||
var _ error = StructuredDataValidationError{}
|
||||
|
||||
var _ interface {
|
||||
Field() string
|
||||
Reason() string
|
||||
Key() bool
|
||||
Cause() error
|
||||
ErrorName() string
|
||||
} = StructuredDataValidationError{}
|
||||
|
||||
// Validate checks the field values on TlsPrivateKey with the rules defined in
|
||||
// the proto definition for this message. If any rules are violated, the first
|
||||
// error encountered is returned, or nil if there are no violations.
|
||||
func (m *TlsPrivateKey) Validate() error {
|
||||
return m.validate(false)
|
||||
}
|
||||
|
||||
// ValidateAll checks the field values on TlsPrivateKey with the rules defined
|
||||
// in the proto definition for this message. If any rules are violated, the
|
||||
// result is a list of violation errors wrapped in TlsPrivateKeyMultiError, or
|
||||
// nil if none found.
|
||||
func (m *TlsPrivateKey) ValidateAll() error {
|
||||
return m.validate(true)
|
||||
}
|
||||
|
||||
func (m *TlsPrivateKey) validate(all bool) error {
|
||||
if m == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var errors []error
|
||||
|
||||
// no validation rules for CertificateFingerprint
|
||||
|
||||
// no validation rules for VerificationUrl
|
||||
|
||||
// no validation rules for ExpirationTimestamp
|
||||
|
||||
if len(errors) > 0 {
|
||||
return TlsPrivateKeyMultiError(errors)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// TlsPrivateKeyMultiError is an error wrapping multiple validation errors
|
||||
// returned by TlsPrivateKey.ValidateAll() if the designated constraints
|
||||
// aren't met.
|
||||
type TlsPrivateKeyMultiError []error
|
||||
|
||||
// Error returns a concatenation of all the error messages it wraps.
|
||||
func (m TlsPrivateKeyMultiError) Error() string {
|
||||
var msgs []string
|
||||
for _, err := range m {
|
||||
msgs = append(msgs, err.Error())
|
||||
}
|
||||
return strings.Join(msgs, "; ")
|
||||
}
|
||||
|
||||
// AllErrors returns a list of validation violation errors.
|
||||
func (m TlsPrivateKeyMultiError) AllErrors() []error { return m }
|
||||
|
||||
// TlsPrivateKeyValidationError is the validation error returned by
|
||||
// TlsPrivateKey.Validate if the designated constraints aren't met.
|
||||
type TlsPrivateKeyValidationError struct {
|
||||
field string
|
||||
reason string
|
||||
cause error
|
||||
key bool
|
||||
}
|
||||
|
||||
// Field function returns field value.
|
||||
func (e TlsPrivateKeyValidationError) Field() string { return e.field }
|
||||
|
||||
// Reason function returns reason value.
|
||||
func (e TlsPrivateKeyValidationError) Reason() string { return e.reason }
|
||||
|
||||
// Cause function returns cause value.
|
||||
func (e TlsPrivateKeyValidationError) Cause() error { return e.cause }
|
||||
|
||||
// Key function returns key value.
|
||||
func (e TlsPrivateKeyValidationError) Key() bool { return e.key }
|
||||
|
||||
// ErrorName returns error name.
|
||||
func (e TlsPrivateKeyValidationError) ErrorName() string { return "TlsPrivateKeyValidationError" }
|
||||
|
||||
// Error satisfies the builtin error interface
|
||||
func (e TlsPrivateKeyValidationError) Error() string {
|
||||
cause := ""
|
||||
if e.cause != nil {
|
||||
cause = fmt.Sprintf(" | caused by: %v", e.cause)
|
||||
}
|
||||
|
||||
key := ""
|
||||
if e.key {
|
||||
key = "key for "
|
||||
}
|
||||
|
||||
return fmt.Sprintf(
|
||||
"invalid %sTlsPrivateKey.%s: %s%s",
|
||||
key,
|
||||
e.field,
|
||||
e.reason,
|
||||
cause)
|
||||
}
|
||||
|
||||
var _ error = TlsPrivateKeyValidationError{}
|
||||
|
||||
var _ interface {
|
||||
Field() string
|
||||
Reason() string
|
||||
Key() bool
|
||||
Cause() error
|
||||
ErrorName() string
|
||||
} = TlsPrivateKeyValidationError{}
|
||||
|
||||
// Validate checks the field values on GitHubSSHKey with the rules defined in
|
||||
// the proto definition for this message. If any rules are violated, the first
|
||||
// error encountered is returned, or nil if there are no violations.
|
||||
func (m *GitHubSSHKey) Validate() error {
|
||||
return m.validate(false)
|
||||
}
|
||||
|
||||
// ValidateAll checks the field values on GitHubSSHKey with the rules defined
|
||||
// in the proto definition for this message. If any rules are violated, the
|
||||
// result is a list of violation errors wrapped in GitHubSSHKeyMultiError, or
|
||||
// nil if none found.
|
||||
func (m *GitHubSSHKey) ValidateAll() error {
|
||||
return m.validate(true)
|
||||
}
|
||||
|
||||
func (m *GitHubSSHKey) validate(all bool) error {
|
||||
if m == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var errors []error
|
||||
|
||||
// no validation rules for User
|
||||
|
||||
// no validation rules for PublicKeyFingerprint
|
||||
|
||||
if len(errors) > 0 {
|
||||
return GitHubSSHKeyMultiError(errors)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GitHubSSHKeyMultiError is an error wrapping multiple validation errors
|
||||
// returned by GitHubSSHKey.ValidateAll() if the designated constraints aren't met.
|
||||
type GitHubSSHKeyMultiError []error
|
||||
|
||||
// Error returns a concatenation of all the error messages it wraps.
|
||||
func (m GitHubSSHKeyMultiError) Error() string {
|
||||
var msgs []string
|
||||
for _, err := range m {
|
||||
msgs = append(msgs, err.Error())
|
||||
}
|
||||
return strings.Join(msgs, "; ")
|
||||
}
|
||||
|
||||
// AllErrors returns a list of validation violation errors.
|
||||
func (m GitHubSSHKeyMultiError) AllErrors() []error { return m }
|
||||
|
||||
// GitHubSSHKeyValidationError is the validation error returned by
|
||||
// GitHubSSHKey.Validate if the designated constraints aren't met.
|
||||
type GitHubSSHKeyValidationError struct {
|
||||
field string
|
||||
reason string
|
||||
cause error
|
||||
key bool
|
||||
}
|
||||
|
||||
// Field function returns field value.
|
||||
func (e GitHubSSHKeyValidationError) Field() string { return e.field }
|
||||
|
||||
// Reason function returns reason value.
|
||||
func (e GitHubSSHKeyValidationError) Reason() string { return e.reason }
|
||||
|
||||
// Cause function returns cause value.
|
||||
func (e GitHubSSHKeyValidationError) Cause() error { return e.cause }
|
||||
|
||||
// Key function returns key value.
|
||||
func (e GitHubSSHKeyValidationError) Key() bool { return e.key }
|
||||
|
||||
// ErrorName returns error name.
|
||||
func (e GitHubSSHKeyValidationError) ErrorName() string { return "GitHubSSHKeyValidationError" }
|
||||
|
||||
// Error satisfies the builtin error interface
|
||||
func (e GitHubSSHKeyValidationError) Error() string {
|
||||
cause := ""
|
||||
if e.cause != nil {
|
||||
cause = fmt.Sprintf(" | caused by: %v", e.cause)
|
||||
}
|
||||
|
||||
key := ""
|
||||
if e.key {
|
||||
key = "key for "
|
||||
}
|
||||
|
||||
return fmt.Sprintf(
|
||||
"invalid %sGitHubSSHKey.%s: %s%s",
|
||||
key,
|
||||
e.field,
|
||||
e.reason,
|
||||
cause)
|
||||
}
|
||||
|
||||
var _ error = GitHubSSHKeyValidationError{}
|
||||
|
||||
var _ interface {
|
||||
Field() string
|
||||
Reason() string
|
||||
Key() bool
|
||||
Cause() error
|
||||
ErrorName() string
|
||||
} = GitHubSSHKeyValidationError{}
|
2527
pkg/pb/source_metadatapb/source_metadata.pb.go
Normal file
2527
pkg/pb/source_metadatapb/source_metadata.pb.go
Normal file
File diff suppressed because it is too large
Load diff
2902
pkg/pb/source_metadatapb/source_metadata.pb.validate.go
Normal file
2902
pkg/pb/source_metadatapb/source_metadata.pb.validate.go
Normal file
File diff suppressed because it is too large
Load diff
2510
pkg/pb/sourcespb/sources.pb.go
Normal file
2510
pkg/pb/sourcespb/sources.pb.go
Normal file
File diff suppressed because it is too large
Load diff
2978
pkg/pb/sourcespb/sources.pb.validate.go
Normal file
2978
pkg/pb/sourcespb/sources.pb.validate.go
Normal file
File diff suppressed because it is too large
Load diff
9
pkg/sanitizer/utf8.go
Normal file
9
pkg/sanitizer/utf8.go
Normal file
|
@ -0,0 +1,9 @@
|
|||
package sanitizer
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
func UTF8(in string) string {
|
||||
return strings.Replace(strings.ToValidUTF8(in, "❗"), "\x00", "", -1)
|
||||
}
|
43
pkg/sanitizer/utf8_test.go
Normal file
43
pkg/sanitizer/utf8_test.go
Normal file
|
@ -0,0 +1,43 @@
|
|||
package sanitizer
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestUTF8(t *testing.T) {
|
||||
type args struct {
|
||||
in string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "valid",
|
||||
args: args{
|
||||
in: "hello123",
|
||||
},
|
||||
want: "hello123",
|
||||
},
|
||||
{
|
||||
name: "santized",
|
||||
args: args{
|
||||
in: "Gr\351gory Smith",
|
||||
},
|
||||
want: "Gr❗gory Smith",
|
||||
},
|
||||
{
|
||||
name: "santized",
|
||||
args: args{
|
||||
in: "no \x00 nulls because postgres does not support it in text fields",
|
||||
},
|
||||
want: "no nulls because postgres does not support it in text fields",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := UTF8(tt.args.in); got != tt.want {
|
||||
t.Errorf("UTF8() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
185
pkg/sources/filesystem/filesystem.go
Normal file
185
pkg/sources/filesystem/filesystem.go
Normal file
|
@ -0,0 +1,185 @@
|
|||
package filesystem
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/go-errors/errors"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"google.golang.org/protobuf/types/known/anypb"
|
||||
|
||||
"github.com/trufflesecurity/trufflehog/pkg/common"
|
||||
"github.com/trufflesecurity/trufflehog/pkg/sanitizer"
|
||||
"github.com/trufflesecurity/trufflehog/pkg/sources"
|
||||
|
||||
"github.com/trufflesecurity/trufflehog/pkg/pb/source_metadatapb"
|
||||
"github.com/trufflesecurity/trufflehog/pkg/pb/sourcespb"
|
||||
)
|
||||
|
||||
const (
|
||||
// These buffer sizes are mainly driven by our largest credential size, which is GCP @ ~2.25KB.
|
||||
// Having a peek size larger than that ensures that we have complete credential coverage in our chunks.
|
||||
BufferSize = 10 * 1024 // 10KB
|
||||
PeekSize = 3 * 1024 // 3KB
|
||||
)
|
||||
|
||||
type Source struct {
|
||||
name string
|
||||
sourceId int64
|
||||
jobId int64
|
||||
verify bool
|
||||
paths []string
|
||||
aCtx context.Context
|
||||
log *log.Entry
|
||||
sources.Progress
|
||||
}
|
||||
|
||||
// Ensure the Source satisfies the interface at compile time
|
||||
var _ sources.Source = (*Source)(nil)
|
||||
|
||||
// Type returns the type of source.
|
||||
// It is used for matching source types in configuration and job input.
|
||||
func (s *Source) Type() sourcespb.SourceType {
|
||||
return sourcespb.SourceType_SOURCE_TYPE_FILESYSTEM
|
||||
}
|
||||
|
||||
func (s *Source) SourceID() int64 {
|
||||
return s.sourceId
|
||||
}
|
||||
|
||||
func (s *Source) JobID() int64 {
|
||||
return s.jobId
|
||||
}
|
||||
|
||||
// Init returns an initialized Filesystem source.
|
||||
func (s *Source) Init(aCtx context.Context, name string, jobId, sourceId int64, verify bool, connection *anypb.Any, concurrency int) error {
|
||||
s.log = log.WithField("source", s.Type()).WithField("name", name)
|
||||
|
||||
s.aCtx = aCtx
|
||||
s.name = name
|
||||
s.sourceId = sourceId
|
||||
s.jobId = jobId
|
||||
s.verify = verify
|
||||
|
||||
var conn sourcespb.Filesystem
|
||||
err := anypb.UnmarshalTo(connection, &conn, proto.UnmarshalOptions{})
|
||||
if err != nil {
|
||||
errors.WrapPrefix(err, "error unmarshalling connection", 0)
|
||||
}
|
||||
|
||||
s.paths = conn.Directories
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func isDirectory(path string) (bool, error) {
|
||||
fileInfo, err := os.Stat(path)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return fileInfo.IsDir(), err
|
||||
}
|
||||
|
||||
// Chunks emits chunks of bytes over a channel.
|
||||
func (s *Source) Chunks(ctx context.Context, chunksChan chan *sources.Chunk) error {
|
||||
for i, path := range s.paths {
|
||||
s.SetProgressComplete(i, len(s.paths), fmt.Sprintf("Path: %s", path))
|
||||
|
||||
cleanPath := filepath.Clean(path)
|
||||
done := false
|
||||
go func() {
|
||||
<-ctx.Done()
|
||||
done = true
|
||||
}()
|
||||
|
||||
err := fs.WalkDir(os.DirFS(cleanPath), ".", func(relativePath string, d fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
path := filepath.Join(cleanPath, relativePath)
|
||||
|
||||
if ok, _ := isDirectory(path); ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
inputFile, err := os.Open(path)
|
||||
if err != nil {
|
||||
log.Warn(err)
|
||||
return nil
|
||||
}
|
||||
defer inputFile.Close()
|
||||
|
||||
reader := bufio.NewReaderSize(bufio.NewReader(inputFile), BufferSize)
|
||||
firstChunk := true
|
||||
for {
|
||||
if done {
|
||||
return nil
|
||||
}
|
||||
|
||||
end := BufferSize
|
||||
buf := make([]byte, BufferSize)
|
||||
n, err := reader.Read(buf)
|
||||
|
||||
if n < BufferSize {
|
||||
end = n
|
||||
}
|
||||
|
||||
if end > 0 {
|
||||
data := buf[0:end]
|
||||
|
||||
if firstChunk {
|
||||
firstChunk = false
|
||||
if common.SkipFile(path, data) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// We are peeking in case a secret exists in our chunk boundaries,
|
||||
// but we never care if we've run into a peek error.
|
||||
peekData, _ := reader.Peek(PeekSize)
|
||||
chunksChan <- &sources.Chunk{
|
||||
SourceType: s.Type(),
|
||||
SourceName: s.name,
|
||||
SourceID: s.SourceID(),
|
||||
Data: append(data, peekData...),
|
||||
SourceMetadata: &source_metadatapb.MetaData{
|
||||
Data: &source_metadatapb.MetaData_Filesystem{
|
||||
Filesystem: &source_metadatapb.Filesystem{
|
||||
File: sanitizer.UTF8(path),
|
||||
},
|
||||
},
|
||||
},
|
||||
Verify: s.verify,
|
||||
}
|
||||
}
|
||||
|
||||
// io.EOF can be emmitted when 0<n<buffer size
|
||||
if err != nil {
|
||||
if errors.Is(err, io.EOF) {
|
||||
return nil
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
if err != nil && err != io.EOF {
|
||||
return errors.New(err)
|
||||
}
|
||||
|
||||
if done {
|
||||
return nil
|
||||
}
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
84
pkg/sources/filesystem/filesystem_test.go
Normal file
84
pkg/sources/filesystem/filesystem_test.go
Normal file
|
@ -0,0 +1,84 @@
|
|||
package filesystem
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/kylelemons/godebug/pretty"
|
||||
"google.golang.org/protobuf/types/known/anypb"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/trufflesecurity/trufflehog/pkg/pb/source_metadatapb"
|
||||
"github.com/trufflesecurity/trufflehog/pkg/pb/sourcespb"
|
||||
"github.com/trufflesecurity/trufflehog/pkg/sources"
|
||||
)
|
||||
|
||||
func TestSource_Scan(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*3)
|
||||
defer cancel()
|
||||
|
||||
type init struct {
|
||||
name string
|
||||
verify bool
|
||||
connection *sourcespb.Filesystem
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
init init
|
||||
wantSourceMetadata *source_metadatapb.MetaData
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "get a chunk",
|
||||
init: init{
|
||||
name: "this repo",
|
||||
connection: &sourcespb.Filesystem{
|
||||
Directories: []string{"."},
|
||||
},
|
||||
verify: true,
|
||||
},
|
||||
wantSourceMetadata: &source_metadatapb.MetaData{
|
||||
Data: &source_metadatapb.MetaData_Filesystem{
|
||||
Filesystem: &source_metadatapb.Filesystem{
|
||||
File: "filesystem.go",
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
s := Source{}
|
||||
log.SetLevel(log.DebugLevel)
|
||||
log.SetFormatter(&log.TextFormatter{ForceColors: true})
|
||||
|
||||
conn, err := anypb.New(tt.init.connection)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = s.Init(ctx, tt.init.name, 0, 0, tt.init.verify, conn, 5)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("Source.Init() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
chunksCh := make(chan *sources.Chunk, 1)
|
||||
// TODO: this is kind of bad, if it errors right away we don't see it as a test failure.
|
||||
// Debugging this usually requires setting a breakpoint on L78 and running test w/ debug.
|
||||
go func() {
|
||||
err = s.Chunks(ctx, chunksCh)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("Source.Chunks() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
}()
|
||||
gotChunk := <-chunksCh
|
||||
if diff := pretty.Compare(gotChunk.SourceMetadata, tt.wantSourceMetadata); diff != "" {
|
||||
t.Errorf("Source.Chunks() %s diff: (-got +want)\n%s", tt.name, diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
541
pkg/sources/git/git.go
Normal file
541
pkg/sources/git/git.go
Normal file
|
@ -0,0 +1,541 @@
|
|||
package git
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/go-errors/errors"
|
||||
"github.com/go-git/go-git/v5"
|
||||
"github.com/go-git/go-git/v5/plumbing"
|
||||
"github.com/go-git/go-git/v5/plumbing/format/diff"
|
||||
"github.com/go-git/go-git/v5/plumbing/object"
|
||||
"github.com/go-git/go-git/v5/plumbing/storer"
|
||||
"github.com/go-git/go-git/v5/plumbing/transport/http"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"golang.org/x/sync/semaphore"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"google.golang.org/protobuf/types/known/anypb"
|
||||
|
||||
"github.com/trufflesecurity/trufflehog/pkg/pb/source_metadatapb"
|
||||
"github.com/trufflesecurity/trufflehog/pkg/pb/sourcespb"
|
||||
|
||||
"github.com/trufflesecurity/trufflehog/pkg/sanitizer"
|
||||
"github.com/trufflesecurity/trufflehog/pkg/sources"
|
||||
)
|
||||
|
||||
type Source struct {
|
||||
name string
|
||||
sourceId int64
|
||||
jobId int64
|
||||
verify bool
|
||||
git *Git
|
||||
aCtx context.Context
|
||||
sources.Progress
|
||||
conn *sourcespb.Git
|
||||
}
|
||||
|
||||
type Git struct {
|
||||
sourceType sourcespb.SourceType
|
||||
sourceName string
|
||||
sourceID int64
|
||||
jobID int64
|
||||
sourceMetadataFunc func(file, email, commit, repository string) *source_metadatapb.MetaData
|
||||
verify bool
|
||||
// sem is used to limit concurrency
|
||||
sem *semaphore.Weighted
|
||||
}
|
||||
|
||||
func NewGit(sourceType sourcespb.SourceType, jobID, sourceID int64, sourceName string, verify bool, concurrency int,
|
||||
sourceMetadataFunc func(file, email, commit, repository string) *source_metadatapb.MetaData,
|
||||
) *Git {
|
||||
return &Git{
|
||||
sourceType: sourceType,
|
||||
sourceName: sourceName,
|
||||
sourceID: sourceID,
|
||||
jobID: jobID,
|
||||
sourceMetadataFunc: sourceMetadataFunc,
|
||||
verify: verify,
|
||||
sem: semaphore.NewWeighted(int64(concurrency)),
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure the Source satisfies the interface at compile time
|
||||
var _ sources.Source = (*Source)(nil)
|
||||
|
||||
// Type returns the type of source.
|
||||
// It is used for matching source types in configuration and job input.
|
||||
func (s *Source) Type() sourcespb.SourceType {
|
||||
return sourcespb.SourceType_SOURCE_TYPE_GIT
|
||||
}
|
||||
|
||||
func (s *Source) SourceID() int64 {
|
||||
return s.sourceId
|
||||
}
|
||||
|
||||
func (s *Source) JobID() int64 {
|
||||
return s.jobId
|
||||
}
|
||||
|
||||
// Init returns an initialized GitHub source.
|
||||
func (s *Source) Init(aCtx context.Context, name string, jobId, sourceId int64, verify bool, connection *anypb.Any, concurrency int) error {
|
||||
|
||||
s.aCtx = aCtx
|
||||
s.name = name
|
||||
s.sourceId = sourceId
|
||||
s.jobId = jobId
|
||||
s.verify = verify
|
||||
|
||||
var conn sourcespb.Git
|
||||
err := anypb.UnmarshalTo(connection, &conn, proto.UnmarshalOptions{})
|
||||
if err != nil {
|
||||
errors.WrapPrefix(err, "error unmarshalling connection", 0)
|
||||
}
|
||||
|
||||
s.conn = &conn
|
||||
|
||||
s.git = NewGit(s.Type(), s.jobId, s.sourceId, s.name, s.verify, runtime.NumCPU(),
|
||||
func(file, email, commit, repository string) *source_metadatapb.MetaData {
|
||||
return &source_metadatapb.MetaData{
|
||||
Data: &source_metadatapb.MetaData_Git{
|
||||
Git: &source_metadatapb.Git{
|
||||
Commit: sanitizer.UTF8(commit),
|
||||
File: sanitizer.UTF8(file),
|
||||
Email: sanitizer.UTF8(email),
|
||||
Repository: sanitizer.UTF8(repository),
|
||||
},
|
||||
},
|
||||
}
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
// Chunks emits chunks of bytes over a channel.
|
||||
func (s *Source) Chunks(ctx context.Context, chunksChan chan *sources.Chunk) error {
|
||||
switch cred := s.conn.GetCredential().(type) {
|
||||
case *sourcespb.Git_BasicAuth:
|
||||
user := cred.BasicAuth.Username
|
||||
token := cred.BasicAuth.Password
|
||||
|
||||
for i, repoURI := range s.conn.Repositories {
|
||||
s.SetProgressComplete(i, len(s.conn.Repositories), fmt.Sprintf("Repo: %s", repoURI))
|
||||
if len(repoURI) == 0 {
|
||||
continue
|
||||
}
|
||||
path, repo, err := CloneRepoUsingToken(token, repoURI, user)
|
||||
defer os.RemoveAll(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = s.git.ScanRepo(ctx, repo, &git.LogOptions{All: true}, nil, chunksChan)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
case *sourcespb.Git_Unauthenticated:
|
||||
for i, repoURI := range s.conn.Repositories {
|
||||
s.SetProgressComplete(i, len(s.conn.Repositories), fmt.Sprintf("Repo: %s", repoURI))
|
||||
if len(repoURI) == 0 {
|
||||
continue
|
||||
}
|
||||
path, repo, err := CloneRepoUsingUnauthenticated(repoURI)
|
||||
defer os.RemoveAll(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = s.git.ScanRepo(ctx, repo, &git.LogOptions{All: true}, nil, chunksChan)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
default:
|
||||
return errors.New("invalid connection type for git source")
|
||||
}
|
||||
|
||||
for i, u := range s.conn.Directories {
|
||||
s.SetProgressComplete(i, len(s.conn.Repositories), fmt.Sprintf("Repo: %s", u))
|
||||
|
||||
if len(u) == 0 {
|
||||
continue
|
||||
}
|
||||
if !strings.HasSuffix(u, "git") {
|
||||
//try paths instead of url
|
||||
repo, err := RepoFromPath(u)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if strings.HasPrefix(u, filepath.Join(os.TempDir(), "trufflehog")) {
|
||||
defer os.RemoveAll(u)
|
||||
}
|
||||
|
||||
err = s.git.ScanRepo(ctx, repo, &git.LogOptions{All: true}, nil, chunksChan)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func RepoFromPath(path string) (*git.Repository, error) {
|
||||
return git.PlainOpen(path)
|
||||
}
|
||||
|
||||
func CloneRepoUsingToken(token, url, user string) (clonePath string, repo *git.Repository, err error) {
|
||||
log.Debugf("Scanning Repo: %s", url)
|
||||
if user == "" {
|
||||
user = "cloner"
|
||||
}
|
||||
//some git clones require username not just token
|
||||
cloneOptions := &git.CloneOptions{
|
||||
URL: url,
|
||||
Auth: &http.BasicAuth{
|
||||
Username: user,
|
||||
Password: token,
|
||||
},
|
||||
}
|
||||
clonePath, err = ioutil.TempDir(os.TempDir(), "trufflehog")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
repo, err = git.PlainClone(clonePath, false, cloneOptions)
|
||||
safeRepo, err := stripPassword(url)
|
||||
if err != nil {
|
||||
err = errors.New(err)
|
||||
return
|
||||
}
|
||||
if _, ok := err.(*os.PathError); ok {
|
||||
log.WithField("repo", safeRepo).WithError(err).Error("error cloning repo")
|
||||
}
|
||||
if err != nil && strings.Contains(err.Error(), "cannot read hash, pkt-line too short") {
|
||||
log.WithField("repo", safeRepo).WithError(err).Error("error cloning repo")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func CloneRepoUsingUnauthenticated(url string) (clonePath string, repo *git.Repository, err error) {
|
||||
cloneOptions := &git.CloneOptions{
|
||||
URL: url,
|
||||
}
|
||||
clonePath, err = ioutil.TempDir(os.TempDir(), "trufflehog")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
repo, err = git.PlainClone(clonePath, false, cloneOptions)
|
||||
safeRepo, err := stripPassword(url)
|
||||
if err != nil {
|
||||
err = errors.New(err)
|
||||
return
|
||||
}
|
||||
if _, ok := err.(*os.PathError); ok {
|
||||
log.WithField("repo", safeRepo).WithError(err).Error("error cloning repo")
|
||||
}
|
||||
if err != nil && strings.Contains(err.Error(), "cannot read hash, pkt-line too short") {
|
||||
log.WithField("repo", safeRepo).WithError(err).Error("error cloning repo")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s *Git) ScanRepo(ctx context.Context, repo *git.Repository, opts *git.LogOptions, untilCommit *object.Commit, chunksChan chan *sources.Chunk) error {
|
||||
wg := &sync.WaitGroup{}
|
||||
|
||||
remote, err := repo.Remote("origin")
|
||||
if err != nil {
|
||||
return errors.New(err)
|
||||
}
|
||||
safeRepo, err := stripPassword(remote.Config().URLs[0])
|
||||
if err != nil {
|
||||
return errors.New(err)
|
||||
}
|
||||
|
||||
commitIter, err := repo.Log(opts)
|
||||
if err != nil {
|
||||
return errors.New(err)
|
||||
}
|
||||
|
||||
scanOneCommit := false
|
||||
if untilCommit != nil && opts.From.String() == untilCommit.Hash.String() {
|
||||
// Our head and base commits are the same, so scan the one commit
|
||||
scanOneCommit = true
|
||||
}
|
||||
breakIteration := false
|
||||
|
||||
err = commitIter.ForEach(func(commit *object.Commit) error {
|
||||
if breakIteration {
|
||||
return storer.ErrStop
|
||||
}
|
||||
if scanOneCommit {
|
||||
breakIteration = true
|
||||
}
|
||||
|
||||
if untilCommit != nil && commit.Hash == untilCommit.Hash && !scanOneCommit {
|
||||
return storer.ErrStop
|
||||
}
|
||||
|
||||
// err := s.sem.Acquire(ctx, 1)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// wg.Add(1)
|
||||
// go func(wg *sync.WaitGroup) {
|
||||
// defer wg.Done()
|
||||
// defer s.sem.Release(1)
|
||||
err = s.scanCommitPatches(ctx, repo, commit, chunksChan)
|
||||
if err != nil {
|
||||
switch e := err.Error(); {
|
||||
case strings.Contains(e, "operation canceled"):
|
||||
log.WithError(err).
|
||||
WithField("repo", safeRepo).
|
||||
WithField("commit", commit.Hash.String()).
|
||||
Warn("commit took too long to compute")
|
||||
return nil
|
||||
case strings.Contains(e, "packfile not found"):
|
||||
log.WithError(err).WithField("repo", safeRepo).Warn("invalid commit reference while scanning commit")
|
||||
return nil
|
||||
default:
|
||||
log.WithError(err).WithField("repo", safeRepo).Error("unhandled error scanning commit")
|
||||
return err
|
||||
}
|
||||
}
|
||||
// }(wg)
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
// https://github.com/src-d/go-git/issues/879
|
||||
if strings.Contains(err.Error(), "object not found") {
|
||||
log.WithError(err).Error("known issue: probably caused by a dangling reference in the repo")
|
||||
} else {
|
||||
return errors.New(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Also scan any unstaged changes in the working tree of the repo
|
||||
_, err = repo.Head()
|
||||
if err == nil || err == plumbing.ErrReferenceNotFound {
|
||||
wt, err := repo.Worktree()
|
||||
if err != nil {
|
||||
log.WithError(err).Error("error obtaining repo worktree")
|
||||
return err
|
||||
}
|
||||
|
||||
status, err := wt.Status()
|
||||
if err != nil {
|
||||
log.WithError(err).Error("error obtaining worktree status")
|
||||
return err
|
||||
}
|
||||
for fh := range status {
|
||||
metadata := s.sourceMetadataFunc(
|
||||
fh, "unstaged", "unstaged", safeRepo,
|
||||
)
|
||||
|
||||
fileBuf := bytes.NewBuffer(nil)
|
||||
fileHandle, err := wt.Filesystem.Open(fh)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
defer fileHandle.Close()
|
||||
_, err = io.Copy(fileBuf, fileHandle)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
chunksChan <- &sources.Chunk{
|
||||
SourceType: s.sourceType,
|
||||
SourceName: s.sourceName,
|
||||
SourceID: s.sourceID,
|
||||
Data: fileBuf.Bytes(),
|
||||
SourceMetadata: metadata,
|
||||
Verify: s.verify,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
return nil
|
||||
}
|
||||
|
||||
//GenerateLink crafts a link to the specific file from a commit. This works in most major git providers (Github/Gitlab)
|
||||
func GenerateLink(repo, commit, file string) string {
|
||||
//bitbucket links are commits not commit...
|
||||
if strings.Contains(repo, "bitbucket.org/") {
|
||||
return repo[:len(repo)-4] + "/commits/" + commit
|
||||
}
|
||||
link := repo[:len(repo)-4] + "/blob/" + commit + "/" + file
|
||||
|
||||
if file == "" {
|
||||
link = repo[:len(repo)-4] + "/commit/" + commit
|
||||
}
|
||||
return link
|
||||
}
|
||||
|
||||
func (s *Git) scanCommitPatches(ctx context.Context, repo *git.Repository, commit *object.Commit, chunksChan chan *sources.Chunk) error {
|
||||
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
// If there are no parents, just scan all files present in the commit
|
||||
//log.Debugf("scanning: %v : %s", repo, commit.Hash)
|
||||
if len(commit.ParentHashes) == 0 {
|
||||
err := s.scanFilesForCommit(ctx, repo, commit, chunksChan)
|
||||
if err != nil {
|
||||
return errors.New(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
parent, err := commit.Parent(0)
|
||||
if err != nil {
|
||||
return errors.New(err)
|
||||
}
|
||||
|
||||
remote, err := repo.Remote("origin")
|
||||
if err != nil {
|
||||
return errors.New(err)
|
||||
}
|
||||
|
||||
safeRepo, err := stripPassword(remote.Config().URLs[0])
|
||||
if err != nil {
|
||||
return errors.New(err)
|
||||
}
|
||||
|
||||
patchCtx, cancel := context.WithTimeout(ctx, time.Second*10)
|
||||
defer cancel()
|
||||
patch, err := parent.PatchContext(patchCtx, commit)
|
||||
if err != nil {
|
||||
if errors.Is(context.DeadlineExceeded, err) {
|
||||
return nil
|
||||
}
|
||||
return errors.New(err)
|
||||
}
|
||||
|
||||
email := commit.Author.Email
|
||||
commitHash := commit.Hash.String()
|
||||
|
||||
for _, file := range patch.FilePatches() {
|
||||
bf, f := file.Files()
|
||||
filename := f.Path()
|
||||
if filename == "" {
|
||||
filename = bf.Path()
|
||||
}
|
||||
|
||||
metadata := s.sourceMetadataFunc(
|
||||
filename, email, commitHash, safeRepo,
|
||||
)
|
||||
|
||||
chunk := bytes.NewBuffer(nil)
|
||||
// This makes a chunk for every section of the diff, so lots of little changes in a file can produce a lot of chunks.
|
||||
for _, patchChunk := range file.Chunks() {
|
||||
if patchChunk.Type() != diff.Add {
|
||||
continue
|
||||
}
|
||||
// I wonder if we can eliminate this string conversion
|
||||
chunk.Write([]byte(patchChunk.Content()))
|
||||
}
|
||||
|
||||
chunksChan <- &sources.Chunk{
|
||||
SourceType: s.sourceType,
|
||||
SourceName: s.sourceName,
|
||||
SourceID: s.sourceID,
|
||||
Data: chunk.Bytes(),
|
||||
SourceMetadata: metadata,
|
||||
Verify: s.verify,
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Git) scanFilesForCommit(ctx context.Context, repo *git.Repository, commit *object.Commit, chunksChan chan *sources.Chunk) error {
|
||||
fileIter, err := commit.Files()
|
||||
if err != nil {
|
||||
return errors.New(err)
|
||||
}
|
||||
|
||||
remote, err := repo.Remote("origin")
|
||||
if err != nil {
|
||||
return errors.New(err)
|
||||
}
|
||||
safeRepo, err := stripPassword(remote.Config().URLs[0])
|
||||
if err != nil {
|
||||
return errors.New(err)
|
||||
}
|
||||
|
||||
err = fileIter.ForEach(func(f *object.File) error {
|
||||
isBinary, err := f.IsBinary()
|
||||
if isBinary {
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
return errors.New(err)
|
||||
}
|
||||
|
||||
chunkStr, err := f.Contents()
|
||||
if err != nil {
|
||||
return errors.New(err)
|
||||
}
|
||||
|
||||
chunksChan <- &sources.Chunk{
|
||||
SourceType: s.sourceType,
|
||||
SourceName: s.sourceName,
|
||||
SourceID: s.sourceID,
|
||||
Data: []byte(chunkStr),
|
||||
SourceMetadata: s.sourceMetadataFunc(
|
||||
f.Name, commit.Author.Email, commit.Hash.String(), safeRepo,
|
||||
),
|
||||
Verify: s.verify,
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func stripPassword(u string) (string, error) {
|
||||
if strings.HasPrefix(u, "git@") {
|
||||
return u, nil
|
||||
}
|
||||
|
||||
repoURL, err := url.Parse(u)
|
||||
if err != nil {
|
||||
return "", errors.WrapPrefix(err, "repo remote cannot be sanitized as URI", 0)
|
||||
}
|
||||
|
||||
_, passSet := repoURL.User.Password()
|
||||
if passSet {
|
||||
return strings.Replace(repoURL.String(), repoURL.User.String()+"@", repoURL.User.Username()+":***@", 1), nil
|
||||
}
|
||||
return repoURL.String(), nil
|
||||
}
|
||||
|
||||
func TryAdditionalBaseRefs(repo *git.Repository, base string) (*plumbing.Hash, error) {
|
||||
revisionPrefixes := []string{
|
||||
"",
|
||||
"refs/heads/",
|
||||
"refs/remotes/origin/",
|
||||
}
|
||||
for _, prefix := range revisionPrefixes {
|
||||
outHash, err := repo.ResolveRevision(plumbing.Revision(prefix + base))
|
||||
if err == plumbing.ErrReferenceNotFound {
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return outHash, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("no base refs succeeded for base: %q", base)
|
||||
}
|
316
pkg/sources/git/git_test.go
Normal file
316
pkg/sources/git/git_test.go
Normal file
|
@ -0,0 +1,316 @@
|
|||
package git
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/kylelemons/godebug/pretty"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/trufflesecurity/trufflehog/pkg/pb/credentialspb"
|
||||
"github.com/trufflesecurity/trufflehog/pkg/pb/source_metadatapb"
|
||||
"github.com/trufflesecurity/trufflehog/pkg/pb/sourcespb"
|
||||
"github.com/trufflesecurity/trufflehog/pkg/sources"
|
||||
"google.golang.org/protobuf/types/known/anypb"
|
||||
)
|
||||
|
||||
func TestSource_Scan(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
type init struct {
|
||||
name string
|
||||
verify bool
|
||||
connection *sourcespb.Git
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
init init
|
||||
wantChunk *sources.Chunk
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "local repo",
|
||||
init: init{
|
||||
name: "this repo",
|
||||
connection: &sourcespb.Git{
|
||||
Directories: []string{"../../../../"},
|
||||
Credential: &sourcespb.Git_Unauthenticated{
|
||||
Unauthenticated: &credentialspb.Unauthenticated{},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantChunk: &sources.Chunk{
|
||||
SourceType: sourcespb.SourceType_SOURCE_TYPE_GIT,
|
||||
SourceName: "this repo",
|
||||
Verify: false,
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "remote repo, unauthenticated",
|
||||
init: init{
|
||||
name: "test source",
|
||||
connection: &sourcespb.Git{
|
||||
Repositories: []string{"https://github.com/dustin-decker/secretsandstuff.git"},
|
||||
Credential: &sourcespb.Git_Unauthenticated{
|
||||
Unauthenticated: &credentialspb.Unauthenticated{},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantChunk: &sources.Chunk{
|
||||
SourceType: sourcespb.SourceType_SOURCE_TYPE_GIT,
|
||||
SourceName: "test source",
|
||||
Verify: false,
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
s := Source{}
|
||||
log.SetLevel(log.DebugLevel)
|
||||
|
||||
conn, err := anypb.New(tt.init.connection)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = s.Init(ctx, tt.init.name, 0, 0, tt.init.verify, conn, 4)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("Source.Init() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
chunksCh := make(chan *sources.Chunk, 1)
|
||||
go func() {
|
||||
s.Chunks(ctx, chunksCh)
|
||||
}()
|
||||
gotChunk := <-chunksCh
|
||||
gotChunk.Data = nil
|
||||
// Commits don't come in a deterministic order, so remove metadata comparison
|
||||
gotChunk.SourceMetadata = nil
|
||||
if diff := pretty.Compare(gotChunk, tt.wantChunk); diff != "" {
|
||||
t.Errorf("Source.Chunks() %s diff: (-got +want)\n%s", tt.name, diff)
|
||||
t.Errorf("Data: %s", string(gotChunk.Data))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_generateLink(t *testing.T) {
|
||||
type args struct {
|
||||
repo string
|
||||
commit string
|
||||
file string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "test link gen",
|
||||
args: args{
|
||||
repo: "https://github.com/trufflesec-julian/confluence-go-api.git",
|
||||
commit: "047b4a2ba42fc5b6c0bd535c5307434a666db5ec",
|
||||
file: ".gitignore",
|
||||
},
|
||||
want: "https://github.com/trufflesec-julian/confluence-go-api/blob/047b4a2ba42fc5b6c0bd535c5307434a666db5ec/.gitignore",
|
||||
},
|
||||
{
|
||||
name: "test link gen - no file",
|
||||
args: args{
|
||||
repo: "https://github.com/trufflesec-julian/confluence-go-api.git",
|
||||
commit: "047b4a2ba42fc5b6c0bd535c5307434a666db5ec",
|
||||
},
|
||||
want: "https://github.com/trufflesec-julian/confluence-go-api/commit/047b4a2ba42fc5b6c0bd535c5307434a666db5ec",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := GenerateLink(tt.args.repo, tt.args.commit, tt.args.file); got != tt.want {
|
||||
t.Errorf("generateLink() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// We ran into an issue where upgrading a dependency caused the git patch chunking to break
|
||||
// So this test exists to make sure that when something changes, we know about it.
|
||||
func TestSource_Chunks_Integration(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
type init struct {
|
||||
name string
|
||||
verify bool
|
||||
connection *sourcespb.Git
|
||||
}
|
||||
|
||||
type byteCompare struct {
|
||||
B []byte
|
||||
Found bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
init init
|
||||
//verified
|
||||
expectedChunkData map[string]*byteCompare
|
||||
}{
|
||||
{
|
||||
name: "remote repo, unauthenticated",
|
||||
init: init{
|
||||
name: "test source",
|
||||
connection: &sourcespb.Git{
|
||||
Repositories: []string{"https://github.com/dustin-decker/secretsandstuff.git"},
|
||||
Credential: &sourcespb.Git_Unauthenticated{
|
||||
Unauthenticated: &credentialspb.Unauthenticated{},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedChunkData: map[string]*byteCompare{
|
||||
"70001020fab32b1fcf2f1f0e5c66424eae649826-aws": {B: []byte("[default]\naws_access_key_id = AKIAXYZDQCEN4B6JSJQI\naws_secret_access_key = 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\noutput = json\nregion = us-east-2\n")},
|
||||
"a6f8aa55736d4a85be31a0048a4607396898647a-bump": {B: []byte("f\n")},
|
||||
"73ab4713057944753f1bdeb80e757380e64c6b5b-bump": {B: []byte(" s \n")},
|
||||
"2f251b8c1e72135a375b659951097ec7749d4af9-bump": {B: []byte(" \n")},
|
||||
"90c75f884c65dc3638ca1610bd9844e668f213c2-aws": {B: []byte("this is the secret: [Default]\nAccess key Id: AKIAILE3JG6KMS3HZGCA\nSecret Access Key: 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\nokay thank you bye\n")},
|
||||
"e6c8bbabd8796ea3cd85bfc2e55b27e0a491747f-bump": {B: []byte("oops \n")},
|
||||
"735b52b0eb40610002bb1088e902bd61824eb305-bump": {B: []byte("oops\n")},
|
||||
"ce62d79908803153ef6e145e042d3e80488ef747-bump": {B: []byte("\n")},
|
||||
"27fbead3bf883cdb7de9d7825ed401f28f9398f1-slack": {B: []byte("yup, just did that\n\ngithub_lol: \"ffc7e0f9400fb6300167009e42d2f842cd7956e2\"\n\noh, goodness. there's another one!")},
|
||||
"8afb0ecd4998b1179e428db5ebbcdc8221214432-slack": {B: []byte("oops might drop a slack token here\n\ngithub_secret=\"369963c1434c377428ca8531fbc46c0c43d037a0\"\n\nyup, just did that")},
|
||||
"8fe6f04ef1839e3fc54b5147e3d0e0b7ab971bd5-aws": {B: []byte("blah blaj\n\nthis is the secret: AKIA2E0A8F3B244C9986\n\nokay thank you bye")},
|
||||
"84e9c75e388ae3e866e121087ea2dd45a71068f2-aws": {B: []byte("this is the secret: [Default]\nAccess key Id: AKIAILE3JG6KMS3HZGCA\nSecret Access Key: 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\nokay thank you bye\n")},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
s := Source{}
|
||||
log.SetLevel(log.DebugLevel)
|
||||
|
||||
conn, err := anypb.New(tt.init.connection)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = s.Init(ctx, tt.init.name, 0, 0, tt.init.verify, conn, 4)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
chunksCh := make(chan *sources.Chunk, 1)
|
||||
go func() {
|
||||
defer close(chunksCh)
|
||||
err := s.Chunks(ctx, chunksCh)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}()
|
||||
|
||||
for chunk := range chunksCh {
|
||||
|
||||
key := ""
|
||||
switch meta := chunk.SourceMetadata.GetData().(type) {
|
||||
case *source_metadatapb.MetaData_Git:
|
||||
key = meta.Git.Commit + "-" + meta.Git.File
|
||||
}
|
||||
|
||||
if expectedData, exists := tt.expectedChunkData[key]; !exists {
|
||||
t.Errorf("A chunk exists that was not expected with key %q", key)
|
||||
} else {
|
||||
(*tt.expectedChunkData[key]).Found = true
|
||||
if !bytes.Equal(chunk.Data, expectedData.B) {
|
||||
t.Errorf("Got %q: %q, which was not expected", key, string(chunk.Data))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for key, expected := range tt.expectedChunkData {
|
||||
if !expected.Found {
|
||||
t.Errorf("Expected data with key %q not found", key)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSource_Chunks_Edge_Cases(t *testing.T) {
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
type init struct {
|
||||
name string
|
||||
verify bool
|
||||
connection *sourcespb.Git
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
init init
|
||||
wantErr string
|
||||
}{
|
||||
{
|
||||
name: "empty repo",
|
||||
init: init{
|
||||
name: "test source",
|
||||
connection: &sourcespb.Git{
|
||||
Repositories: []string{"https://github.com/git-fixtures/empty.git"},
|
||||
Credential: &sourcespb.Git_Unauthenticated{
|
||||
Unauthenticated: &credentialspb.Unauthenticated{},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: "remote",
|
||||
},
|
||||
{
|
||||
name: "symlinks repo",
|
||||
init: init{
|
||||
name: "test source",
|
||||
connection: &sourcespb.Git{
|
||||
Repositories: []string{"https://github.com/git-fixtures/symlinks.git"},
|
||||
Credential: &sourcespb.Git_Unauthenticated{
|
||||
Unauthenticated: &credentialspb.Unauthenticated{},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "submodule repo",
|
||||
init: init{
|
||||
name: "test source",
|
||||
connection: &sourcespb.Git{
|
||||
Repositories: []string{"https://github.com/git-fixtures/submodule.git"},
|
||||
Credential: &sourcespb.Git_Unauthenticated{
|
||||
Unauthenticated: &credentialspb.Unauthenticated{},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
s := Source{}
|
||||
log.SetLevel(log.DebugLevel)
|
||||
|
||||
conn, err := anypb.New(tt.init.connection)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = s.Init(ctx, tt.init.name, 0, 0, tt.init.verify, conn, 4)
|
||||
if err != nil {
|
||||
t.Errorf("Source.Init() error = %v", err)
|
||||
return
|
||||
}
|
||||
chunksCh := make(chan *sources.Chunk, 1)
|
||||
go func() {
|
||||
for chunk := range chunksCh {
|
||||
chunk.Data = nil
|
||||
}
|
||||
|
||||
}()
|
||||
if err := s.Chunks(ctx, chunksCh); err != nil && !strings.Contains(err.Error(), tt.wantErr) {
|
||||
t.Errorf("Source.Chunks() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
}
|
513
pkg/sources/github/github.go
Normal file
513
pkg/sources/github/github.go
Normal file
|
@ -0,0 +1,513 @@
|
|||
package github
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"os"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/bradleyfalzon/ghinstallation"
|
||||
"github.com/go-errors/errors"
|
||||
gogit "github.com/go-git/go-git/v5"
|
||||
"github.com/google/go-github/v41/github"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"golang.org/x/oauth2"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"google.golang.org/protobuf/types/known/anypb"
|
||||
|
||||
"github.com/trufflesecurity/trufflehog/pkg/pb/source_metadatapb"
|
||||
"github.com/trufflesecurity/trufflehog/pkg/pb/sourcespb"
|
||||
|
||||
"github.com/trufflesecurity/trufflehog/pkg/common"
|
||||
"github.com/trufflesecurity/trufflehog/pkg/giturl"
|
||||
"github.com/trufflesecurity/trufflehog/pkg/sanitizer"
|
||||
"github.com/trufflesecurity/trufflehog/pkg/sources"
|
||||
"github.com/trufflesecurity/trufflehog/pkg/sources/git"
|
||||
)
|
||||
|
||||
type Source struct {
|
||||
name string
|
||||
sourceId int64
|
||||
jobId int64
|
||||
verify bool
|
||||
repos []string
|
||||
orgs []string
|
||||
members []string
|
||||
git *git.Git
|
||||
httpClient *http.Client
|
||||
aCtx context.Context
|
||||
sources.Progress
|
||||
log *log.Entry
|
||||
token string
|
||||
conn *sourcespb.GitHub
|
||||
}
|
||||
|
||||
// Ensure the Source satisfies the interface at compile time
|
||||
var _ sources.Source = (*Source)(nil)
|
||||
var endsWithGithub = regexp.MustCompile(`github.com/?$`)
|
||||
|
||||
// Type returns the type of source.
|
||||
// It is used for matching source types in configuration and job input.
|
||||
func (s *Source) Type() sourcespb.SourceType {
|
||||
return sourcespb.SourceType_SOURCE_TYPE_GITHUB
|
||||
}
|
||||
|
||||
func (s *Source) SourceID() int64 {
|
||||
return s.sourceId
|
||||
}
|
||||
|
||||
func (s *Source) JobID() int64 {
|
||||
return s.jobId
|
||||
}
|
||||
|
||||
func (s *Source) Token(ctx context.Context, installationClient *github.Client) (string, error) {
|
||||
switch cred := s.conn.GetCredential().(type) {
|
||||
case *sourcespb.GitHub_Unauthenticated:
|
||||
// do nothing
|
||||
case *sourcespb.GitHub_GithubApp:
|
||||
id, err := strconv.ParseInt(cred.GithubApp.InstallationId, 10, 64)
|
||||
if err != nil {
|
||||
return "", errors.New(err)
|
||||
}
|
||||
token, _, err := installationClient.Apps.CreateInstallationToken(
|
||||
ctx, id, &github.InstallationTokenOptions{})
|
||||
if err != nil {
|
||||
return "", errors.WrapPrefix(err, "unable to create installation token", 0)
|
||||
}
|
||||
return token.GetToken(), nil // TODO: multiple workers request this, track the TTL
|
||||
case *sourcespb.GitHub_Token:
|
||||
return cred.Token, nil
|
||||
}
|
||||
|
||||
return "", errors.New("unhandled credential type for token fetch")
|
||||
}
|
||||
|
||||
// Init returns an initialized GitHub source.
|
||||
func (s *Source) Init(aCtx context.Context, name string, jobId, sourceId int64, verify bool, connection *anypb.Any, concurrency int) error {
|
||||
s.log = log.WithField("source", s.Type()).WithField("name", name)
|
||||
|
||||
s.aCtx = aCtx
|
||||
s.name = name
|
||||
s.sourceId = sourceId
|
||||
s.jobId = jobId
|
||||
s.verify = verify
|
||||
|
||||
s.httpClient = common.SaneHttpClient()
|
||||
|
||||
var conn sourcespb.GitHub
|
||||
err := anypb.UnmarshalTo(connection, &conn, proto.UnmarshalOptions{})
|
||||
if err != nil {
|
||||
errors.WrapPrefix(err, "error unmarshalling connection", 0)
|
||||
}
|
||||
s.conn = &conn
|
||||
|
||||
s.git = git.NewGit(s.Type(), s.JobID(), s.SourceID(), s.name, s.verify, runtime.NumCPU(),
|
||||
func(file, email, commit, repository string) *source_metadatapb.MetaData {
|
||||
return &source_metadatapb.MetaData{
|
||||
Data: &source_metadatapb.MetaData_Github{
|
||||
Github: &source_metadatapb.Github{
|
||||
Commit: sanitizer.UTF8(commit),
|
||||
File: sanitizer.UTF8(file),
|
||||
Email: sanitizer.UTF8(email),
|
||||
Repository: sanitizer.UTF8(repository),
|
||||
Link: git.GenerateLink(repository, commit, file),
|
||||
},
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Chunks emits chunks of bytes over a channel.
|
||||
func (s *Source) Chunks(ctx context.Context, chunksChan chan *sources.Chunk) error {
|
||||
apiEndpoint := s.conn.Endpoint
|
||||
if len(s.conn.Endpoint) == 0 || endsWithGithub.MatchString(apiEndpoint) {
|
||||
apiEndpoint = "https://api.github.com"
|
||||
}
|
||||
|
||||
var installationClient *github.Client
|
||||
s.repos = s.conn.Repositories
|
||||
s.orgs = s.conn.Organizations
|
||||
|
||||
switch cred := s.conn.GetCredential().(type) {
|
||||
case *sourcespb.GitHub_Unauthenticated:
|
||||
apiClient := github.NewClient(s.httpClient)
|
||||
if len(s.orgs) > 30 {
|
||||
log.Warn("You may experience rate limiting due with the unauthenticated GitHub api. Consider using the authenticated org user scan feature instead")
|
||||
}
|
||||
for _, org := range s.orgs {
|
||||
org = strings.TrimSpace(org)
|
||||
if !strings.HasSuffix(org, ".git") {
|
||||
s.paginateRepos(ctx, apiClient, org)
|
||||
}
|
||||
}
|
||||
case *sourcespb.GitHub_Token:
|
||||
// needed for clones
|
||||
s.token = cred.Token
|
||||
|
||||
// needed to list repos
|
||||
ts := oauth2.StaticTokenSource(
|
||||
&oauth2.Token{AccessToken: cred.Token},
|
||||
)
|
||||
tc := oauth2.NewClient(context.TODO(), ts)
|
||||
|
||||
var apiClient *github.Client
|
||||
var err error
|
||||
// If we're using public github, make a regular client.
|
||||
// Otherwise make an enterprise client
|
||||
if apiEndpoint == "https://api.github.com" {
|
||||
apiClient = github.NewClient(tc)
|
||||
} else {
|
||||
apiClient, err = github.NewEnterpriseClient(apiEndpoint, apiEndpoint, tc)
|
||||
if err != nil {
|
||||
return errors.New(err)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: this should support scanning users too
|
||||
|
||||
specificScope := false
|
||||
|
||||
if len(s.repos) > 0 {
|
||||
specificScope = true
|
||||
for i, repo := range s.repos {
|
||||
if !strings.HasSuffix(repo, ".git") {
|
||||
if repo, err := giturl.NormalizeGithubRepo(repo); err != nil {
|
||||
// This wasn't formatted as expected, let the user know why that might be.
|
||||
log.WithError(err).Warnf("Repo not in expected format, attempting to paginate repos instead.")
|
||||
} else {
|
||||
s.repos[i] = repo
|
||||
}
|
||||
s.paginateRepos(ctx, apiClient, repo)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(s.orgs) > 0 {
|
||||
specificScope = true
|
||||
for _, org := range s.orgs {
|
||||
if !strings.HasSuffix(org, ".git") {
|
||||
s.paginateRepos(ctx, apiClient, org)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
user, _, err := apiClient.Users.Get(context.TODO(), "")
|
||||
if err != nil {
|
||||
return errors.New(err)
|
||||
}
|
||||
s.paginateGists(ctx, user.GetLogin(), chunksChan)
|
||||
|
||||
if !specificScope {
|
||||
s.paginateRepos(ctx, apiClient, user.GetLogin())
|
||||
// Scan for orgs is default with a token. GitHub App enumerates the repositories
|
||||
// that were assigned to it in GitHub App settings.
|
||||
s.paginateOrgs(ctx, apiClient, *user.Name)
|
||||
}
|
||||
case *sourcespb.GitHub_GithubApp:
|
||||
installationID, err := strconv.ParseInt(cred.GithubApp.InstallationId, 10, 64)
|
||||
if err != nil {
|
||||
return errors.New(err)
|
||||
}
|
||||
|
||||
appID, err := strconv.ParseInt(cred.GithubApp.AppId, 10, 64)
|
||||
if err != nil {
|
||||
return errors.New(err)
|
||||
}
|
||||
|
||||
// This client is used for most APIs
|
||||
itr, err := ghinstallation.New(
|
||||
common.SaneHttpClient().Transport,
|
||||
appID,
|
||||
installationID,
|
||||
[]byte(cred.GithubApp.PrivateKey))
|
||||
if err != nil {
|
||||
return errors.New(err)
|
||||
}
|
||||
itr.BaseURL = apiEndpoint
|
||||
apiClient, err := github.NewEnterpriseClient(apiEndpoint, apiEndpoint, &http.Client{Transport: itr})
|
||||
if err != nil {
|
||||
return errors.New(err)
|
||||
}
|
||||
|
||||
// This client is required to create installation tokens for cloning.. Otherwise the required JWT is not in the
|
||||
// request for the token :/
|
||||
appItr, err := ghinstallation.NewAppsTransport(
|
||||
common.SaneHttpClient().Transport,
|
||||
appID,
|
||||
[]byte(cred.GithubApp.PrivateKey))
|
||||
if err != nil {
|
||||
return errors.New(err)
|
||||
}
|
||||
appItr.BaseURL = apiEndpoint
|
||||
installationClient, err = github.NewEnterpriseClient(apiEndpoint, apiEndpoint, &http.Client{Transport: appItr})
|
||||
if err != nil {
|
||||
return errors.New(err)
|
||||
}
|
||||
|
||||
err = s.paginateApp(ctx, apiClient)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
//check if we need to find user repos
|
||||
if s.conn.ScanUsers {
|
||||
err := s.paginateMembers(ctx, installationClient, apiClient)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Infof("Scanning repos from %v organization members.", len(s.members))
|
||||
for _, member := range s.members {
|
||||
//all org member's gists
|
||||
s.paginateGists(ctx, member, chunksChan)
|
||||
s.paginateRepos(ctx, apiClient, member)
|
||||
}
|
||||
|
||||
}
|
||||
default:
|
||||
return errors.Errorf("Invalid configuration given for source. Name: %s, Type: %s", s.name, s.Type())
|
||||
}
|
||||
|
||||
if _, ok := os.LookupEnv("DO_NOT_RANDOMIZE"); !ok {
|
||||
//Randomize channel scan order on each scan
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
rand.Shuffle(len(s.repos), func(i, j int) { s.repos[i], s.repos[j] = s.repos[j], s.repos[i] })
|
||||
}
|
||||
|
||||
log.Infof("Found %v total repos to scan", len(s.repos))
|
||||
for i, repoURL := range s.repos {
|
||||
s.SetProgressComplete(i, len(s.repos), fmt.Sprintf("Repo: %s", repoURL))
|
||||
|
||||
if !strings.HasSuffix(repoURL, ".git") {
|
||||
continue
|
||||
}
|
||||
if strings.Contains(repoURL, "DefinitelyTyped") {
|
||||
continue
|
||||
}
|
||||
s.log.WithField("repo", repoURL).Debug("attempting to clone repo")
|
||||
var path string
|
||||
var repo *gogit.Repository
|
||||
var err error
|
||||
|
||||
switch s.conn.GetCredential().(type) {
|
||||
case *sourcespb.GitHub_Unauthenticated:
|
||||
path, repo, err = git.CloneRepoUsingUnauthenticated(repoURL)
|
||||
default:
|
||||
var token string
|
||||
token, err = s.Token(ctx, installationClient)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
path, repo, err = git.CloneRepoUsingToken(token, repoURL, "clone")
|
||||
}
|
||||
|
||||
defer os.RemoveAll(path)
|
||||
if err != nil {
|
||||
log.WithError(err).Warnf("unable to clone repo, continuing")
|
||||
continue
|
||||
}
|
||||
err = s.git.ScanRepo(ctx, repo, &gogit.LogOptions{All: true}, nil, chunksChan)
|
||||
if err != nil {
|
||||
log.WithError(err).Warnf("unable to scan repo")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// handleRateLimit returns true if a rate limit was handled
|
||||
//unauthed github has a rate limit of 60 requests per hour. This will likely only be exhausted if many users/orgs are scanned without auth
|
||||
func handleRateLimit(err error) bool {
|
||||
limit, ok := err.(*github.RateLimitError)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
log.WithField("retry-after", limit.Message).Debug("handling rate limit (5 minutes retry)")
|
||||
time.Sleep(time.Minute * 5)
|
||||
return true
|
||||
}
|
||||
|
||||
func (s *Source) paginateReposByOrg(ctx context.Context, apiClient *github.Client, org string) {
|
||||
opts := &github.RepositoryListByOrgOptions{
|
||||
ListOptions: github.ListOptions{
|
||||
PerPage: 100,
|
||||
},
|
||||
}
|
||||
for {
|
||||
someRepos, res, err := apiClient.Repositories.ListByOrg(ctx, org, opts)
|
||||
if err == nil {
|
||||
defer res.Body.Close()
|
||||
}
|
||||
if handled := handleRateLimit(err); handled {
|
||||
continue
|
||||
}
|
||||
if len(someRepos) == 0 || err != nil {
|
||||
break
|
||||
}
|
||||
for _, r := range someRepos {
|
||||
s.repos = append(s.repos, r.GetCloneURL())
|
||||
}
|
||||
if res.NextPage == 0 {
|
||||
break
|
||||
}
|
||||
opts.Page = res.NextPage
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Source) paginateRepos(ctx context.Context, apiClient *github.Client, user string) {
|
||||
opts := &github.RepositoryListOptions{
|
||||
// Visibility: "all",
|
||||
ListOptions: github.ListOptions{
|
||||
PerPage: 50,
|
||||
},
|
||||
}
|
||||
for {
|
||||
someRepos, res, err := apiClient.Repositories.List(ctx, user, opts)
|
||||
if err == nil {
|
||||
defer res.Body.Close()
|
||||
}
|
||||
if handled := handleRateLimit(err); handled {
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
for _, r := range someRepos {
|
||||
s.repos = append(s.repos, r.GetCloneURL())
|
||||
}
|
||||
if res.NextPage == 0 {
|
||||
break
|
||||
}
|
||||
opts.Page = res.NextPage
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Source) paginateGists(ctx context.Context, user string, chunksChan chan *sources.Chunk) {
|
||||
apiClient := github.NewClient(s.httpClient)
|
||||
gists, _, err := apiClient.Gists.List(ctx, user, &github.GistListOptions{})
|
||||
if err != nil {
|
||||
log.WithError(err).Warnf("Could not get gists for user %s", user)
|
||||
return
|
||||
}
|
||||
for _, gist := range gists {
|
||||
path, repo, err := git.CloneRepoUsingUnauthenticated(*gist.GitPullURL)
|
||||
defer os.RemoveAll(path)
|
||||
if err != nil {
|
||||
log.WithError(err).Warnf("Could not get gist %s from user %s", *gist.HTMLURL, user)
|
||||
continue
|
||||
}
|
||||
s.log.WithField("repo", *gist.HTMLURL).Debugf("attempting to clone gist from user %s", user)
|
||||
|
||||
scanCtx := context.Background()
|
||||
err = s.git.ScanRepo(scanCtx, repo, &gogit.LogOptions{All: true}, nil, chunksChan)
|
||||
if err != nil {
|
||||
log.WithError(err).Warnf("Could not scan after clone: %s", *gist.HTMLURL)
|
||||
continue
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (s *Source) paginateMembers(ctx context.Context, installationClient *github.Client, apiClient *github.Client) error {
|
||||
|
||||
opts := &github.ListOptions{
|
||||
PerPage: 500,
|
||||
}
|
||||
optsOrg := &github.ListMembersOptions{
|
||||
PublicOnly: false,
|
||||
ListOptions: *opts,
|
||||
}
|
||||
|
||||
installs, _, err := installationClient.Apps.ListInstallations(ctx, opts)
|
||||
if err != nil {
|
||||
log.WithError(err).Warn("Could not enumerate organizations using user")
|
||||
return err
|
||||
}
|
||||
for _, org := range installs {
|
||||
for {
|
||||
members, res, err := apiClient.Organizations.ListMembers(ctx, *org.Account.Login, optsOrg)
|
||||
if err == nil {
|
||||
defer res.Body.Close()
|
||||
}
|
||||
if handled := handleRateLimit(err); handled {
|
||||
continue
|
||||
}
|
||||
if err != nil || len(members) == 0 {
|
||||
errText := "Could not list organization members: Please install on an organization. Otherwise, this is an older version of the Github app, please delete and re-add this source!"
|
||||
log.WithError(err).Warnf(errText)
|
||||
return errors.New(errText)
|
||||
}
|
||||
for _, m := range members {
|
||||
usr := m.Login
|
||||
if usr == nil || *usr == "" {
|
||||
continue
|
||||
}
|
||||
s.members = append(s.members, *usr)
|
||||
}
|
||||
if res.NextPage == 0 {
|
||||
break
|
||||
}
|
||||
opts.Page = res.NextPage
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Source) paginateApp(ctx context.Context, apiClient *github.Client) error {
|
||||
// Authenticated enumeration of repos
|
||||
opts := &github.ListOptions{
|
||||
PerPage: 100,
|
||||
}
|
||||
for {
|
||||
someRepos, res, err := apiClient.Apps.ListRepos(ctx, opts)
|
||||
if err == nil {
|
||||
defer res.Body.Close()
|
||||
}
|
||||
if handled := handleRateLimit(err); handled {
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
return errors.WrapPrefix(err, "unable to list repositories", 0)
|
||||
}
|
||||
for _, r := range someRepos.Repositories {
|
||||
s.repos = append(s.repos, r.GetCloneURL())
|
||||
}
|
||||
if res.NextPage == 0 {
|
||||
break
|
||||
}
|
||||
opts.Page = res.NextPage
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Source) paginateOrgs(ctx context.Context, apiClient *github.Client, user string) {
|
||||
orgOpts := &github.ListOptions{}
|
||||
orgs, _, err := apiClient.Organizations.List(ctx, "", orgOpts)
|
||||
if err != nil {
|
||||
log.WithError(err).Errorf("Could not list organizations for %s", user)
|
||||
return
|
||||
}
|
||||
for _, org := range orgs {
|
||||
var name string
|
||||
if org.Name != nil {
|
||||
name = *org.Name
|
||||
} else if org.Login != nil {
|
||||
name = *org.Login
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
s.paginateReposByOrg(ctx, apiClient, name)
|
||||
}
|
||||
|
||||
}
|
406
pkg/sources/github/github_test.go
Normal file
406
pkg/sources/github/github_test.go
Normal file
|
@ -0,0 +1,406 @@
|
|||
package github
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/kylelemons/godebug/pretty"
|
||||
"github.com/mattn/go-colorable"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/trufflesecurity/trufflehog/pkg/common"
|
||||
"github.com/trufflesecurity/trufflehog/pkg/pb/credentialspb"
|
||||
"github.com/trufflesecurity/trufflehog/pkg/pb/source_metadatapb"
|
||||
"github.com/trufflesecurity/trufflehog/pkg/pb/sourcespb"
|
||||
"github.com/trufflesecurity/trufflehog/pkg/sources"
|
||||
"google.golang.org/protobuf/types/known/anypb"
|
||||
)
|
||||
|
||||
func TestSource_Scan(t *testing.T) {
|
||||
|
||||
os.Setenv("DO_NOT_RANDOMIZE", "true")
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*300)
|
||||
defer cancel()
|
||||
|
||||
secret, err := common.GetTestSecret(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(fmt.Errorf("failed to access secret: %v", err))
|
||||
}
|
||||
|
||||
// For the personal access token test
|
||||
githubToken := secret.MustGetField("GITHUB_TOKEN")
|
||||
|
||||
//For the NEW github app test (+Member enum)
|
||||
githubPrivateKeyB64New := secret.MustGetField("GITHUB_PRIVATE_KEY_NEW")
|
||||
githubPrivateKeyBytesNew, err := base64.StdEncoding.DecodeString(githubPrivateKeyB64New)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
githubPrivateKeyNew := string(githubPrivateKeyBytesNew)
|
||||
githubInstallationIDNew := secret.MustGetField("GITHUB_INSTALLATION_ID_NEW")
|
||||
githubAppIDNew := secret.MustGetField("GITHUB_APP_ID_NEW")
|
||||
|
||||
//OLD app for breaking app change tests
|
||||
githubPrivateKeyB64 := secret.MustGetField("GITHUB_PRIVATE_KEY")
|
||||
githubPrivateKeyBytes, err := base64.StdEncoding.DecodeString(githubPrivateKeyB64)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
githubPrivateKey := string(githubPrivateKeyBytes)
|
||||
githubInstallationID := secret.MustGetField("GITHUB_INSTALLATION_ID")
|
||||
githubAppID := secret.MustGetField("GITHUB_APP_ID")
|
||||
|
||||
type init struct {
|
||||
name string
|
||||
verify bool
|
||||
connection *sourcespb.GitHub
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
init init
|
||||
wantChunk *sources.Chunk
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "get an authenticated (token) chunk",
|
||||
init: init{
|
||||
name: "test source",
|
||||
connection: &sourcespb.GitHub{
|
||||
Repositories: []string{"https://github.com/dustin-decker/secretsandstuff.git"},
|
||||
Credential: &sourcespb.GitHub_Token{
|
||||
Token: githubToken,
|
||||
},
|
||||
},
|
||||
},
|
||||
wantChunk: &sources.Chunk{
|
||||
SourceType: sourcespb.SourceType_SOURCE_TYPE_GITHUB,
|
||||
SourceName: "test source",
|
||||
SourceMetadata: &source_metadatapb.MetaData{
|
||||
Data: &source_metadatapb.MetaData_Github{
|
||||
Github: &source_metadatapb.Github{
|
||||
Repository: "https://gist.github.com/be45ad1ebabe98482d9c0bb80c07c619.git",
|
||||
},
|
||||
},
|
||||
},
|
||||
Verify: false,
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
// {
|
||||
// name: "get an authenticated (token) chunk with specific 'org' enum",
|
||||
// init: init{
|
||||
// name: "test source",
|
||||
// connection: &sourcespb.GitHub{
|
||||
// Repositories: []string{"https://github.com/dustin-decker/"},
|
||||
// Credential: &sourcespb.GitHub_Token{
|
||||
// Token: githubToken,
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// wantChunk: &sources.Chunk{
|
||||
// SourceType: sourcespb.SourceType_SOURCE_TYPE_GITHUB,
|
||||
// SourceName: "test source",
|
||||
// SourceMetadata: &source_metadatapb.MetaData{
|
||||
// Data: &source_metadatapb.MetaData_Github{
|
||||
// Github: &source_metadatapb.Github{
|
||||
// Repository: "https://github.com/dustin-decker/secretsandstuff.git",
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// Verify: false,
|
||||
// },
|
||||
// wantErr: false,
|
||||
// },
|
||||
{
|
||||
name: "get an authenticated (token) chunk with enumeration",
|
||||
//Enum cannot be restricted w/ token
|
||||
init: init{
|
||||
name: "test source",
|
||||
connection: &sourcespb.GitHub{
|
||||
Credential: &sourcespb.GitHub_Token{
|
||||
Token: githubToken,
|
||||
},
|
||||
},
|
||||
},
|
||||
wantChunk: &sources.Chunk{
|
||||
SourceType: sourcespb.SourceType_SOURCE_TYPE_GITHUB,
|
||||
SourceName: "test source",
|
||||
SourceMetadata: &source_metadatapb.MetaData{
|
||||
Data: &source_metadatapb.MetaData_Github{
|
||||
Github: &source_metadatapb.Github{
|
||||
Repository: "https://gist.github.com/be45ad1ebabe98482d9c0bb80c07c619.git",
|
||||
},
|
||||
},
|
||||
},
|
||||
Verify: false,
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "get an authenticated (old app) chunk w/ enum",
|
||||
init: init{
|
||||
name: "test source",
|
||||
connection: &sourcespb.GitHub{
|
||||
ScanUsers: false,
|
||||
Credential: &sourcespb.GitHub_GithubApp{
|
||||
GithubApp: &credentialspb.GitHubApp{
|
||||
PrivateKey: githubPrivateKey,
|
||||
InstallationId: githubInstallationID,
|
||||
AppId: githubAppID,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantChunk: &sources.Chunk{
|
||||
SourceType: sourcespb.SourceType_SOURCE_TYPE_GITHUB,
|
||||
SourceName: "test source",
|
||||
SourceMetadata: &source_metadatapb.MetaData{
|
||||
Data: &source_metadatapb.MetaData_Github{
|
||||
Github: &source_metadatapb.Github{
|
||||
Repository: "https://github.com/dustin-decker/dockerfiles.git",
|
||||
},
|
||||
},
|
||||
},
|
||||
Verify: false,
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "get an unauthenticated org chunk with enumeration",
|
||||
init: init{
|
||||
name: "test source",
|
||||
connection: &sourcespb.GitHub{
|
||||
Organizations: []string{"trufflesecurity"},
|
||||
Credential: &sourcespb.GitHub_Unauthenticated{},
|
||||
},
|
||||
},
|
||||
wantChunk: &sources.Chunk{
|
||||
SourceType: sourcespb.SourceType_SOURCE_TYPE_GITHUB,
|
||||
SourceName: "test source",
|
||||
SourceMetadata: &source_metadatapb.MetaData{
|
||||
Data: &source_metadatapb.MetaData_Github{
|
||||
Github: &source_metadatapb.Github{
|
||||
Repository: "https://github.com/trufflesecurity/driftwood.git",
|
||||
},
|
||||
},
|
||||
},
|
||||
Verify: false,
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "installed app on org w/ enum",
|
||||
init: init{
|
||||
name: "test source",
|
||||
connection: &sourcespb.GitHub{
|
||||
ScanUsers: true,
|
||||
Credential: &sourcespb.GitHub_GithubApp{
|
||||
GithubApp: &credentialspb.GitHubApp{
|
||||
PrivateKey: githubPrivateKeyNew,
|
||||
InstallationId: githubInstallationIDNew,
|
||||
AppId: githubAppIDNew,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantChunk: &sources.Chunk{
|
||||
SourceType: sourcespb.SourceType_SOURCE_TYPE_GITHUB,
|
||||
SourceName: "test source",
|
||||
SourceMetadata: &source_metadatapb.MetaData{
|
||||
Data: &source_metadatapb.MetaData_Github{
|
||||
Github: &source_metadatapb.Github{
|
||||
Repository: "https://gist.github.com/be45ad1ebabe98482d9c0bb80c07c619.git",
|
||||
},
|
||||
},
|
||||
},
|
||||
Verify: false,
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
// {
|
||||
// name: "early termination of org/users",
|
||||
// init: init{
|
||||
// name: "test source",
|
||||
// connection: &sourcespb.GitHub{
|
||||
// Repositories: strings.Split(testAccts, "\n"),
|
||||
// Credential: &sourcespb.GitHub_Unauthenticated{},
|
||||
// },
|
||||
// },
|
||||
// wantChunk: &sources.Chunk{
|
||||
// SourceType: sourcespb.SourceType_SOURCE_TYPE_GITHUB,
|
||||
// SourceName: "test source",
|
||||
// SourceMetadata: &source_metadatapb.MetaData{
|
||||
// Data: &source_metadatapb.MetaData_Github{
|
||||
// Github: &source_metadatapb.Github{
|
||||
// Repository: "https://gist.github.com/be45ad1ebabe98482d9c0bb80c07c619.git",
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// Verify: false,
|
||||
// },
|
||||
// wantErr: false,
|
||||
// },
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
s := Source{}
|
||||
|
||||
log.SetLevel(log.DebugLevel)
|
||||
//uncomment for windows Testing
|
||||
log.SetFormatter(&log.TextFormatter{ForceColors: true})
|
||||
log.SetOutput(colorable.NewColorableStdout())
|
||||
|
||||
conn, err := anypb.New(tt.init.connection)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = s.Init(ctx, tt.init.name, 0, 0, tt.init.verify, conn, 4)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("Source.Init() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
chunksCh := make(chan *sources.Chunk, 5)
|
||||
go func() {
|
||||
err = s.Chunks(ctx, chunksCh)
|
||||
if (err != nil) != tt.wantErr {
|
||||
if ctx.Err() != nil {
|
||||
return
|
||||
}
|
||||
t.Errorf("Source.Chunks() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
}()
|
||||
gotChunk := <-chunksCh
|
||||
if diff := pretty.Compare(gotChunk.SourceMetadata.GetGithub().Repository, tt.wantChunk.SourceMetadata.GetGithub().Repository); diff != "" {
|
||||
t.Errorf("Source.Chunks() %s diff: (-got +want)\n%s", tt.name, diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSource_paginateGists(t *testing.T) {
|
||||
|
||||
os.Setenv("DO_NOT_RANDOMIZE", "true")
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
|
||||
defer cancel()
|
||||
|
||||
secret, err := common.GetTestSecret(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(fmt.Errorf("failed to access secret: %v", err))
|
||||
}
|
||||
//For the NEW github app test (+Member enum)
|
||||
githubPrivateKeyB64New := secret.MustGetField("GITHUB_PRIVATE_KEY_NEW")
|
||||
githubPrivateKeyBytesNew, err := base64.StdEncoding.DecodeString(githubPrivateKeyB64New)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
githubPrivateKeyNew := string(githubPrivateKeyBytesNew)
|
||||
githubInstallationIDNew := secret.MustGetField("GITHUB_INSTALLATION_ID_NEW")
|
||||
githubAppIDNew := secret.MustGetField("GITHUB_APP_ID_NEW")
|
||||
type init struct {
|
||||
name string
|
||||
verify bool
|
||||
connection *sourcespb.GitHub
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
init init
|
||||
wantChunk *sources.Chunk
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "get gist secret",
|
||||
init: init{
|
||||
name: "test source",
|
||||
connection: &sourcespb.GitHub{
|
||||
Credential: &sourcespb.GitHub_GithubApp{
|
||||
GithubApp: &credentialspb.GitHubApp{
|
||||
PrivateKey: githubPrivateKeyNew,
|
||||
InstallationId: githubInstallationIDNew,
|
||||
AppId: githubAppIDNew,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantChunk: &sources.Chunk{
|
||||
SourceType: sourcespb.SourceType_SOURCE_TYPE_GITHUB,
|
||||
SourceName: "test source",
|
||||
SourceMetadata: &source_metadatapb.MetaData{
|
||||
Data: &source_metadatapb.MetaData_Github{
|
||||
Github: &source_metadatapb.Github{
|
||||
Repository: "https://gist.github.com/be45ad1ebabe98482d9c0bb80c07c619.git",
|
||||
},
|
||||
},
|
||||
},
|
||||
Verify: false,
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
s := Source{}
|
||||
|
||||
log.SetLevel(log.DebugLevel)
|
||||
//uncomment for windows Testing
|
||||
log.SetFormatter(&log.TextFormatter{ForceColors: true})
|
||||
log.SetOutput(colorable.NewColorableStdout())
|
||||
|
||||
conn, err := anypb.New(tt.init.connection)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = s.Init(ctx, tt.init.name, 0, 0, tt.init.verify, conn, 4)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("Source.Init() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
chunksCh := make(chan *sources.Chunk, 5)
|
||||
go func() {
|
||||
s.paginateGists(ctx, "dustin-decker", chunksCh)
|
||||
}()
|
||||
gotChunk := <-chunksCh
|
||||
if diff := pretty.Compare(gotChunk.SourceMetadata.GetGithub().Repository, tt.wantChunk.SourceMetadata.GetGithub().Repository); diff != "" {
|
||||
t.Errorf("Source.Chunks() %s diff: (-got +want)\n%s", tt.name, diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// func TestSource_paginateRepos(t *testing.T) {
|
||||
// type args struct {
|
||||
// ctx context.Context
|
||||
// apiClient *github.Client
|
||||
// }
|
||||
// tests := []struct {
|
||||
// name string
|
||||
// org string
|
||||
// args args
|
||||
// }{
|
||||
// {
|
||||
// org: "fakeNetflix",
|
||||
// args: args{
|
||||
// ctx: context.Background(),
|
||||
// apiClient: github.NewClient(common.SaneHttpClient()),
|
||||
// },
|
||||
// },
|
||||
// }
|
||||
// for _, tt := range tests {
|
||||
// t.Run(tt.name, func(t *testing.T) {
|
||||
// s := &Source{httpClient: common.SaneHttpClient()}
|
||||
// s.paginateRepos(tt.args.ctx, tt.args.apiClient, tt.org)
|
||||
// if len(s.repos) < 101 {
|
||||
// t.Errorf("expected > 100 repos, got %d", len(s.repos))
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
// }
|
293
pkg/sources/gitlab/gitlab.go
Normal file
293
pkg/sources/gitlab/gitlab.go
Normal file
|
@ -0,0 +1,293 @@
|
|||
package gitlab
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
"runtime"
|
||||
|
||||
"github.com/go-errors/errors"
|
||||
gogit "github.com/go-git/go-git/v5"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/xanzy/go-gitlab"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"google.golang.org/protobuf/types/known/anypb"
|
||||
|
||||
"github.com/trufflesecurity/trufflehog/pkg/pb/source_metadatapb"
|
||||
"github.com/trufflesecurity/trufflehog/pkg/pb/sourcespb"
|
||||
|
||||
"github.com/trufflesecurity/trufflehog/pkg/giturl"
|
||||
"github.com/trufflesecurity/trufflehog/pkg/sanitizer"
|
||||
"github.com/trufflesecurity/trufflehog/pkg/sources"
|
||||
"github.com/trufflesecurity/trufflehog/pkg/sources/git"
|
||||
)
|
||||
|
||||
type Source struct {
|
||||
name string
|
||||
sourceId int64
|
||||
jobId int64
|
||||
verify bool
|
||||
authMethod string
|
||||
user string
|
||||
password string
|
||||
token string
|
||||
url string
|
||||
repos []string
|
||||
git *git.Git
|
||||
aCtx context.Context
|
||||
sources.Progress
|
||||
}
|
||||
|
||||
// Ensure the Source satisfies the interface at compile time
|
||||
var _ sources.Source = (*Source)(nil)
|
||||
|
||||
// Type returns the type of source.
|
||||
// It is used for matching source types in configuration and job input.
|
||||
func (s *Source) Type() sourcespb.SourceType {
|
||||
return sourcespb.SourceType_SOURCE_TYPE_GITLAB
|
||||
}
|
||||
|
||||
func (s *Source) SourceID() int64 {
|
||||
return s.sourceId
|
||||
}
|
||||
|
||||
func (s *Source) JobID() int64 {
|
||||
return s.jobId
|
||||
}
|
||||
|
||||
// Init returns an initialized Gitlab source.
|
||||
func (s *Source) Init(aCtx context.Context, name string, jobId, sourceId int64, verify bool, connection *anypb.Any, concurrency int) error {
|
||||
|
||||
s.aCtx = aCtx
|
||||
s.name = name
|
||||
s.sourceId = sourceId
|
||||
s.jobId = jobId
|
||||
s.verify = verify
|
||||
|
||||
var conn sourcespb.GitLab
|
||||
err := anypb.UnmarshalTo(connection, &conn, proto.UnmarshalOptions{})
|
||||
if err != nil {
|
||||
errors.WrapPrefix(err, "error unmarshalling connection", 0)
|
||||
}
|
||||
|
||||
s.repos = conn.Repositories
|
||||
s.url = conn.Endpoint
|
||||
switch cred := conn.GetCredential().(type) {
|
||||
case *sourcespb.GitLab_Token:
|
||||
s.authMethod = "TOKEN"
|
||||
s.token = cred.Token
|
||||
case *sourcespb.GitLab_Oauth:
|
||||
s.authMethod = "OAUTH"
|
||||
s.token = cred.Oauth.RefreshToken
|
||||
// TODO: is it okay if there is no client id and secret? Might be an issue when marshalling config to proto
|
||||
case *sourcespb.GitLab_BasicAuth:
|
||||
s.authMethod = "BASIC_AUTH"
|
||||
s.user = cred.BasicAuth.Username
|
||||
s.password = cred.BasicAuth.Password
|
||||
default:
|
||||
return errors.Errorf("Invalid configuration given for source. Name: %s, Type: %s", name, s.Type())
|
||||
}
|
||||
|
||||
if len(s.url) == 0 {
|
||||
//assuming not custom gitlab url
|
||||
s.url = "https://gitlab.com/"
|
||||
}
|
||||
|
||||
s.git = git.NewGit(s.Type(), s.JobID(), s.SourceID(), s.name, s.verify, runtime.NumCPU(),
|
||||
func(file, email, commit, repository string) *source_metadatapb.MetaData {
|
||||
return &source_metadatapb.MetaData{
|
||||
Data: &source_metadatapb.MetaData_Gitlab{
|
||||
Gitlab: &source_metadatapb.Gitlab{
|
||||
Commit: sanitizer.UTF8(commit),
|
||||
File: sanitizer.UTF8(file),
|
||||
Email: sanitizer.UTF8(email),
|
||||
Repository: sanitizer.UTF8(repository),
|
||||
Link: git.GenerateLink(repository, commit, file),
|
||||
},
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Source) newClient() (*gitlab.Client, error) {
|
||||
|
||||
// initialize a new api instance
|
||||
switch s.authMethod {
|
||||
case "OAUTH":
|
||||
apiClient, err := gitlab.NewOAuthClient(s.token, gitlab.WithBaseURL(s.url))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not authenticate to Gitlab instance %s via OAUTH. Error: %v", s.url, err)
|
||||
}
|
||||
return apiClient, nil
|
||||
case "BASIC_AUTH":
|
||||
apiClient, err := gitlab.NewBasicAuthClient(s.user, s.password, gitlab.WithBaseURL(s.url))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not authenticate to Gitlab instance %s via BASICAUTH. Error: %v", s.url, err)
|
||||
}
|
||||
return apiClient, nil
|
||||
|
||||
case "TOKEN":
|
||||
apiClient, err := gitlab.NewOAuthClient(s.token, gitlab.WithBaseURL(s.url))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not authenticate to Gitlab instance %s via TOKEN Auth. Error: %v", s.url, err)
|
||||
}
|
||||
return apiClient, nil
|
||||
|
||||
default:
|
||||
return nil, errors.New("Could not determine authMethod specified for GitLab")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (s *Source) getAllProjects(apiClient *gitlab.Client) ([]*gitlab.Project, error) {
|
||||
// projects without repo will get user projects, groups projects, and subgroup projects.
|
||||
user, _, err := apiClient.Users.CurrentUser()
|
||||
//TODO what happens if the user is anonymous
|
||||
if err != nil {
|
||||
return nil, errors.Errorf("unable to authenticate using: %s", s.authMethod)
|
||||
}
|
||||
//when bool pointers are req'd
|
||||
//yes := true
|
||||
no := false
|
||||
projectQuery := &gitlab.ListProjectsOptions{}
|
||||
projects, _, err := apiClient.Projects.ListUserProjects(user.ID, projectQuery)
|
||||
if err != nil {
|
||||
return nil, errors.Errorf("received error on listing projects: %s\n", err)
|
||||
}
|
||||
groups, _, err := apiClient.Groups.ListGroups(&gitlab.ListGroupsOptions{AllAvailable: &no})
|
||||
if err != nil {
|
||||
return nil, errors.Errorf("received error on listing projects: %s\n", err)
|
||||
}
|
||||
for _, group := range groups {
|
||||
grpPrjs, _, err := apiClient.Groups.ListGroupProjects(group.ID, &gitlab.ListGroupProjectsOptions{})
|
||||
if err != nil {
|
||||
return nil, errors.Errorf("received error on listing projects: %s\n", err)
|
||||
}
|
||||
projects = append(projects, grpPrjs...)
|
||||
subgroups, _, err := apiClient.Groups.ListSubgroups(group.ID, &gitlab.ListSubgroupsOptions{AllAvailable: &no})
|
||||
if err != nil {
|
||||
log.Debugf("could not retrieve subgroups from %s", group.Name)
|
||||
continue
|
||||
}
|
||||
for _, subgroup := range subgroups {
|
||||
projects = append(projects, subgroup.Projects...)
|
||||
}
|
||||
}
|
||||
return projects, nil
|
||||
}
|
||||
|
||||
func (s *Source) getRepos(apiClient *gitlab.Client) ([]*url.URL, []error) {
|
||||
//is repo defined?
|
||||
var validRepos []*url.URL
|
||||
var errs []error
|
||||
if len(s.repos) > 0 {
|
||||
for _, prj := range s.repos {
|
||||
repo, err := giturl.NormalizeGitlabRepo(prj)
|
||||
if err != nil {
|
||||
errs = append(errs, errors.WrapPrefix(err, fmt.Sprintf("unable to normalize gitlab repo url %s", prj), 0))
|
||||
}
|
||||
|
||||
// The repo normalization has already successfully parsed the URL at this point, so we can ignore the error.
|
||||
u, _ := url.ParseRequestURI(repo)
|
||||
validRepos = append(validRepos, u)
|
||||
}
|
||||
return validRepos, errs
|
||||
}
|
||||
return nil, nil
|
||||
|
||||
}
|
||||
|
||||
func (s *Source) scanRepos(ctx context.Context, chunksChan chan *sources.Chunk, repos []*url.URL) []error {
|
||||
var errors []error
|
||||
if s.authMethod == "UNAUTHENTICATED" {
|
||||
for i, u := range repos {
|
||||
s.SetProgressComplete(i, len(repos), fmt.Sprintf("Repo: %s", u))
|
||||
|
||||
if len(u.String()) == 0 {
|
||||
continue
|
||||
}
|
||||
path, repo, err := git.CloneRepoUsingUnauthenticated(u.String())
|
||||
defer os.RemoveAll(path)
|
||||
if err != nil {
|
||||
errors = append(errors, err)
|
||||
continue
|
||||
}
|
||||
err = s.git.ScanRepo(ctx, repo, &gogit.LogOptions{All: true}, nil, chunksChan)
|
||||
if err != nil {
|
||||
errors = append(errors, err)
|
||||
continue
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
for i, u := range repos {
|
||||
s.SetProgressComplete(i, len(repos), fmt.Sprintf("Repo: %s", u))
|
||||
|
||||
if len(u.String()) == 0 {
|
||||
continue
|
||||
}
|
||||
path, repo, err := git.CloneRepoUsingToken(s.token, u.String(), s.user)
|
||||
defer os.RemoveAll(path)
|
||||
if err != nil {
|
||||
errors = append(errors, err)
|
||||
continue
|
||||
}
|
||||
err = s.git.ScanRepo(ctx, repo, &gogit.LogOptions{All: true}, nil, chunksChan)
|
||||
if err != nil {
|
||||
errors = append(errors, err)
|
||||
continue
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return errors
|
||||
|
||||
}
|
||||
|
||||
// Chunks emits chunks of bytes over a channel.
|
||||
func (s *Source) Chunks(ctx context.Context, chunksChan chan *sources.Chunk) error {
|
||||
// start client
|
||||
apiClient, err := s.newClient()
|
||||
if err != nil {
|
||||
return errors.New(err)
|
||||
}
|
||||
// get repo within target
|
||||
repos, errs := s.getRepos(apiClient)
|
||||
for _, repoErr := range errs {
|
||||
log.WithError(repoErr).Warn("error getting repo")
|
||||
}
|
||||
// get all repos if not specified
|
||||
if repos == nil {
|
||||
projects, err := s.getAllProjects(apiClient)
|
||||
if err != nil {
|
||||
return errors.New(err)
|
||||
}
|
||||
// turn projects into URLs for Git cloner
|
||||
for _, prj := range projects {
|
||||
u, err := url.Parse(prj.HTTPURLToRepo)
|
||||
if err != nil {
|
||||
fmt.Printf("could not parse url given by project: %s", prj.HTTPURLToRepo)
|
||||
}
|
||||
repos = append(repos, u)
|
||||
}
|
||||
if repos == nil {
|
||||
return errors.Errorf("unable to discover any repos")
|
||||
}
|
||||
}
|
||||
errs = s.scanRepos(ctx, chunksChan, repos)
|
||||
for _, err := range errs {
|
||||
log.WithError(err).WithFields(
|
||||
log.Fields{
|
||||
"source_name": s.name,
|
||||
"source_type": s.Type(),
|
||||
"repos": repos,
|
||||
},
|
||||
).Error("error scanning repo")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
110
pkg/sources/gitlab/gitlab_test.go
Normal file
110
pkg/sources/gitlab/gitlab_test.go
Normal file
|
@ -0,0 +1,110 @@
|
|||
package gitlab
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/kylelemons/godebug/pretty"
|
||||
"google.golang.org/protobuf/types/known/anypb"
|
||||
|
||||
"github.com/trufflesecurity/trufflehog/pkg/pb/sourcespb"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/trufflesecurity/trufflehog/pkg/common"
|
||||
"github.com/trufflesecurity/trufflehog/pkg/sources"
|
||||
)
|
||||
|
||||
func TestSource_Scan(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
log.SetLevel(log.DebugLevel)
|
||||
log.SetFormatter(&log.TextFormatter{ForceColors: true})
|
||||
secret, err := common.GetTestSecret(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(fmt.Errorf("failed to access secret: %v", err))
|
||||
}
|
||||
token := secret.MustGetField("GITLAB_TOKEN")
|
||||
|
||||
type init struct {
|
||||
name string
|
||||
verify bool
|
||||
connection *sourcespb.GitLab
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
init init
|
||||
wantChunk *sources.Chunk
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "token auth, enumerate repo",
|
||||
init: init{
|
||||
name: "test source",
|
||||
connection: &sourcespb.GitLab{
|
||||
Credential: &sourcespb.GitLab_Token{
|
||||
Token: token,
|
||||
},
|
||||
},
|
||||
},
|
||||
wantChunk: &sources.Chunk{
|
||||
SourceType: sourcespb.SourceType_SOURCE_TYPE_GITLAB,
|
||||
SourceName: "test source",
|
||||
Verify: false,
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "token auth, scoped repo",
|
||||
init: init{
|
||||
name: "test source scoped",
|
||||
connection: &sourcespb.GitLab{
|
||||
Repositories: []string{"https://gitlab.com/testermctestface/testy.git"},
|
||||
Credential: &sourcespb.GitLab_Token{
|
||||
Token: token,
|
||||
},
|
||||
},
|
||||
},
|
||||
wantChunk: &sources.Chunk{
|
||||
SourceType: sourcespb.SourceType_SOURCE_TYPE_GITLAB,
|
||||
SourceName: "test source scoped",
|
||||
Verify: false,
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
s := Source{}
|
||||
log.SetLevel(log.DebugLevel)
|
||||
|
||||
conn, err := anypb.New(tt.init.connection)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = s.Init(ctx, tt.init.name, 0, 0, tt.init.verify, conn, 10)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("Source.Init() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
chunksCh := make(chan *sources.Chunk, 1)
|
||||
go func() {
|
||||
err = s.Chunks(ctx, chunksCh)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("Source.Chunks() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
}()
|
||||
gotChunk := <-chunksCh
|
||||
// Commits don't come in a deterministic order, so remove metadata comparison
|
||||
gotChunk.Data = nil
|
||||
gotChunk.SourceMetadata = nil
|
||||
if diff := pretty.Compare(gotChunk, tt.wantChunk); diff != "" {
|
||||
t.Errorf("Source.Chunks() %s diff: (-got +want)\n%s", tt.name, diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
72
pkg/sources/sources.go
Normal file
72
pkg/sources/sources.go
Normal file
|
@ -0,0 +1,72 @@
|
|||
package sources
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
|
||||
"github.com/trufflesecurity/trufflehog/pkg/pb/source_metadatapb"
|
||||
"github.com/trufflesecurity/trufflehog/pkg/pb/sourcespb"
|
||||
"google.golang.org/protobuf/types/known/anypb"
|
||||
)
|
||||
|
||||
// Chunk contains data to be decoded and scanned along with context on where it came from.
|
||||
type Chunk struct {
|
||||
// SourceName is the name of the Source that produced the chunk.
|
||||
SourceName string
|
||||
// SourceID is the ID of the source that the Chunk originated from.
|
||||
SourceID int64
|
||||
// SourceType is the type of Source that produced the chunk.
|
||||
SourceType sourcespb.SourceType
|
||||
// SourceMetadata holds the context of where the Chunk was found.
|
||||
SourceMetadata *source_metadatapb.MetaData
|
||||
|
||||
// Data is the data to decode and scan.
|
||||
Data []byte
|
||||
// Verify specifies whether any secrets in the Chunk should be verified.
|
||||
Verify bool
|
||||
}
|
||||
|
||||
// Source defines the interface required to implement a source chunker.
|
||||
type Source interface {
|
||||
// Type returns the source type, used for matching against configuration and jobs.
|
||||
Type() sourcespb.SourceType
|
||||
// SourceID returns the initialized source ID used for tracking relationships in the DB.
|
||||
SourceID() int64
|
||||
// JobID returns the initialized job ID used for tracking relationships in the DB.
|
||||
JobID() int64
|
||||
// Init initializes the source.
|
||||
Init(aCtx context.Context, name string, jobId, sourceId int64, verify bool, connection *anypb.Any, concurrency int) error
|
||||
// Chunks emits data over a channel that is decoded and scanned for secrets.
|
||||
Chunks(ctx context.Context, chunksChan chan *Chunk) error
|
||||
// Completion Percentage for Scanned Source
|
||||
GetProgress() *Progress
|
||||
}
|
||||
|
||||
// PercentComplete is used to update job completion percentages across sources
|
||||
type Progress struct {
|
||||
mut sync.Mutex
|
||||
PercentComplete int64
|
||||
Message string
|
||||
SectionsCompleted int32
|
||||
SectionsRemaining int32
|
||||
}
|
||||
|
||||
// SetProgressComplete sets job complete percentage based on passed in scope array of highest level objects. i is the current iteration in the loop of target scope, scope should be len(scoped_items)
|
||||
func (p *Progress) SetProgressComplete(i, scope int, message string) {
|
||||
p.mut.Lock()
|
||||
defer p.mut.Unlock()
|
||||
if p == nil {
|
||||
p = &Progress{}
|
||||
}
|
||||
p.Message = message
|
||||
p.SectionsCompleted = int32(i)
|
||||
p.SectionsRemaining = int32(scope)
|
||||
p.PercentComplete = int64((float64(i) / float64(scope)) * 100)
|
||||
}
|
||||
|
||||
//GetProgressComplete gets job completion percentage for metrics reporting
|
||||
func (p *Progress) GetProgress() *Progress {
|
||||
p.mut.Lock()
|
||||
defer p.mut.Unlock()
|
||||
return p
|
||||
}
|
56
proto/credentials.proto
Normal file
56
proto/credentials.proto
Normal file
|
@ -0,0 +1,56 @@
|
|||
syntax = "proto3";
|
||||
|
||||
package credentials;
|
||||
|
||||
option go_package = "github.com/trufflesecurity/trufflehog/pkg/pb/credentialspb";
|
||||
|
||||
message Unauthenticated {}
|
||||
|
||||
message CloudEnvironment {}
|
||||
|
||||
message BasicAuth {
|
||||
string username = 1;
|
||||
string password = 2;
|
||||
}
|
||||
|
||||
message ClientCrednetials {
|
||||
string tenant_id = 1;
|
||||
string client_id =2;
|
||||
string client_secret=3;
|
||||
}
|
||||
|
||||
message ClientCertificate {
|
||||
string tenant_id = 1;
|
||||
string client_id =2;
|
||||
string certificate_path =3;
|
||||
string certificate_password =4;
|
||||
}
|
||||
|
||||
message Oauth2 {
|
||||
string refresh_token = 1;
|
||||
string client_id = 2;
|
||||
string client_secret = 3;
|
||||
}
|
||||
|
||||
message KeySecret {
|
||||
string key = 1;
|
||||
string secret = 2;
|
||||
}
|
||||
|
||||
message AWS {
|
||||
string key = 1;
|
||||
string secret = 2;
|
||||
string region = 3;
|
||||
}
|
||||
|
||||
message SES {
|
||||
AWS creds = 1;
|
||||
string sender = 2;
|
||||
repeated string recipients = 3;
|
||||
}
|
||||
|
||||
message GitHubApp {
|
||||
string private_key = 1;
|
||||
string installation_id = 2;
|
||||
string app_id = 3;
|
||||
}
|
778
proto/detectors.proto
Normal file
778
proto/detectors.proto
Normal file
|
@ -0,0 +1,778 @@
|
|||
syntax = "proto3";
|
||||
|
||||
package detectors;
|
||||
|
||||
option go_package = "github.com/trufflesecurity/trufflehog/pkg/pb/detectorspb";
|
||||
|
||||
enum DetectorType {
|
||||
Alibaba = 0;
|
||||
AMQP = 1;
|
||||
AWS = 2;
|
||||
Azure = 3;
|
||||
Circle = 4;
|
||||
Coinbase = 5;
|
||||
GCP = 6;
|
||||
Generic = 7;
|
||||
Github = 8;
|
||||
Gitlab = 9;
|
||||
JDBC = 10;
|
||||
RazorPay = 11;
|
||||
SendGrid = 12;
|
||||
Slack = 13;
|
||||
Square = 14;
|
||||
PrivateKey = 15;
|
||||
Stripe = 16;
|
||||
URI = 17;
|
||||
Dropbox = 18;
|
||||
Heroku = 19;
|
||||
Mailchimp = 20;
|
||||
Okta = 21;
|
||||
OneLogin = 22;
|
||||
PivotalTracker = 23;
|
||||
SquareApp = 25;
|
||||
Twilio = 26;
|
||||
Test = 27;
|
||||
TravisCI = 29;
|
||||
SlackWebhook = 30;
|
||||
PaypalOauth = 31;
|
||||
PagerDutyApiKey = 32;
|
||||
Firebase = 33;
|
||||
Mailgun = 34;
|
||||
HubSpot = 35;
|
||||
GitHubApp = 36;
|
||||
CircleCI = 37;
|
||||
WpEngine = 38;
|
||||
DatadogToken = 39;
|
||||
FacebookOAuth = 40;
|
||||
AsanaPersonalAccessToken = 41;
|
||||
AmplitudeApiKey = 42;
|
||||
BitLyAccessToken = 43;
|
||||
CalendlyApiKey = 44;
|
||||
ZapierWebhook = 45;
|
||||
YoutubeApiKey = 46;
|
||||
SalesforceOauth2 = 47;
|
||||
TwitterApiSecret = 48;
|
||||
NpmToken = 49;
|
||||
NewRelicPersonalApiKey = 50;
|
||||
AirtableApiKey = 51;
|
||||
AkamaiToken = 52;
|
||||
AmazonMWS = 53;
|
||||
KubeConfig = 54;
|
||||
Auth0oauth = 55;
|
||||
Bitfinex = 56;
|
||||
Clarifai = 57;
|
||||
CloudflareGlobalApiKey = 58;
|
||||
CloudflareCaKey = 59;
|
||||
Confluent = 60;
|
||||
ContentfulDelivery = 61; // Didn't do
|
||||
DatabricksToken = 62;
|
||||
DigitalOceanSpaces = 63; // Didn't do
|
||||
DigitalOceanToken = 64;
|
||||
DiscordBotToken = 65;
|
||||
DiscordWebhook = 66;
|
||||
EtsyApiKey = 67;
|
||||
FastlyPersonalToken = 68;
|
||||
GoogleOauth2 = 69;
|
||||
ReCAPTCHA = 70; // Didn't do
|
||||
GoogleApiKey = 71; // Didn't do
|
||||
Hunter = 72;
|
||||
IbmCloudUserKey = 73;
|
||||
Netlify = 74;
|
||||
Vonage = 75;
|
||||
EquinixOauth = 76;
|
||||
Paystack = 77;
|
||||
PlaidToken = 78;
|
||||
PlaidKey = 79;
|
||||
Plivo = 80;
|
||||
Postmark = 81;
|
||||
PubNubPublishKey = 82;
|
||||
PubNubSubscriptionKey = 83;
|
||||
PusherChannelKey = 84;
|
||||
ScalewayKey = 85;
|
||||
SendinBlueV2 = 86;
|
||||
SentryToken = 87;
|
||||
ShodanKey = 88;
|
||||
SnykKey = 89;
|
||||
SpotifyKey = 90;
|
||||
TelegramBotToken = 91;
|
||||
TencentCloudKey = 92;
|
||||
TerraformCloudPersonalToken = 93;
|
||||
TrelloApiKey = 94;
|
||||
ZendeskApi = 95;
|
||||
MaxMindLicense = 96;
|
||||
AirtableMetadataApiKey = 97;
|
||||
AsanaOauth = 98;
|
||||
RapidApi = 99;
|
||||
CloudflareApiToken = 100;
|
||||
Webex = 101;
|
||||
FirebaseCloudMessaging = 102;
|
||||
ContentfulPersonalAccessToken = 103;
|
||||
MapBox = 104;
|
||||
MailJetBasicAuth = 105;
|
||||
MailJetSMS = 106;
|
||||
HubSpotApiKey = 107;
|
||||
HubSpotOauth = 108;
|
||||
SslMate = 109;
|
||||
Auth0ManagementApiToken = 110;
|
||||
MessageBird = 111;
|
||||
ElasticEmail = 112;
|
||||
FigmaPersonalAccessToken = 113;
|
||||
MicrosoftTeamsWebhook = 114;
|
||||
GitHubOld = 115;
|
||||
VultrApiKey = 116;
|
||||
Pepipost = 117;
|
||||
Postman = 118;
|
||||
CloudsightKey = 119;
|
||||
JiraToken = 120;
|
||||
NexmoApiKey = 121;
|
||||
SegmentApiKey = 122;
|
||||
SumoLogicKey = 123;
|
||||
PushBulletApiKey = 124;
|
||||
AirbrakeProjectKey = 125;
|
||||
AirbrakeUserKey = 126;
|
||||
PendoIntegrationKey = 127;
|
||||
SplunkOberservabilityToken = 128;
|
||||
LokaliseToken = 129;
|
||||
Calendarific = 130;
|
||||
Jumpcloud = 131;
|
||||
IpStack = 133;
|
||||
Notion = 134;
|
||||
DroneCI = 135;
|
||||
AdobeIO = 136;
|
||||
TwelveData = 137;
|
||||
D7Network = 138;
|
||||
ScrapingBee = 139;
|
||||
KeenIO = 140;
|
||||
Wakatime = 141;
|
||||
Buildkite = 142;
|
||||
Verimail = 143;
|
||||
Zerobounce = 144;
|
||||
Mailboxlayer = 145;
|
||||
Fastspring = 146;
|
||||
Paddle = 147;
|
||||
Sellfy = 148;
|
||||
FixerIO = 149;
|
||||
ButterCMS = 150;
|
||||
Taxjar = 151;
|
||||
Avalara = 152;
|
||||
Helpscout = 153;
|
||||
ElasticPath = 154;
|
||||
Zeplin = 155;
|
||||
Intercom = 156;
|
||||
Mailmodo = 157;
|
||||
CannyIo = 158;
|
||||
Pipedrive = 159;
|
||||
Vercel = 160;
|
||||
PosthogApp = 161;
|
||||
SinchMessage = 162;
|
||||
Ayrshare = 163;
|
||||
HelpCrunch = 164;
|
||||
LiveAgent = 165;
|
||||
Beamer = 166;
|
||||
WeChatAppKey = 167;
|
||||
LineMessaging = 168;
|
||||
UberServerToken = 169;
|
||||
AlgoliaAdminKey = 170;
|
||||
FullContact = 171;
|
||||
Mandrill = 172;
|
||||
Flutterwave = 173;
|
||||
MattermostPersonalToken = 174;
|
||||
Cloudant = 175;
|
||||
LineNotify = 176;
|
||||
LinearAPI = 177;
|
||||
Ubidots = 178;
|
||||
Anypoint = 179;
|
||||
Dwolla = 180;
|
||||
ArtifactoryAccessToken = 181;
|
||||
Surge = 182;
|
||||
Sparkpost = 183;
|
||||
GoCardless = 184;
|
||||
Codacy = 185;
|
||||
Kraken = 186;
|
||||
Checkout = 187;
|
||||
Kairos = 188;
|
||||
ClockworkSMS = 189;
|
||||
Atlassian = 190;
|
||||
LaunchDarkly = 191;
|
||||
Coveralls = 192;
|
||||
Linode = 193;
|
||||
WePay = 194;
|
||||
PlanetScale = 195;
|
||||
Doppler = 196;
|
||||
Agora = 197;
|
||||
Samsara = 198;
|
||||
FrameIO = 199;
|
||||
RubyGems = 200;
|
||||
OpenAI = 201;
|
||||
SurveySparrow = 202;
|
||||
Simvoly = 203;
|
||||
Survicate = 204;
|
||||
Omnisend = 205;
|
||||
Groovehq = 206;
|
||||
Newsapi = 207;
|
||||
Chatbot = 208;
|
||||
ClickSendsms = 209;
|
||||
Getgist = 210;
|
||||
CustomerIO = 211;
|
||||
ApiDeck = 212;
|
||||
Nftport = 213;
|
||||
Copper = 214;
|
||||
Close = 215;
|
||||
Myfreshworks = 216;
|
||||
Salesflare = 217;
|
||||
Webflow = 218;
|
||||
Duda = 219;
|
||||
Yext = 220;
|
||||
ContentStack = 221;
|
||||
Storyblok = 222;
|
||||
GraphCMS = 223;
|
||||
Checkmarket = 224;
|
||||
Convertkit = 225;
|
||||
CustomerGuru = 226;
|
||||
Kaleyra = 227;
|
||||
Mailerlite = 228;
|
||||
Qualaroo = 229;
|
||||
SatismeterProjectkey = 230;
|
||||
SatismeterWritekey = 231;
|
||||
Simplesat = 232;
|
||||
SurveyAnyplace = 233;
|
||||
SurveyBot = 234;
|
||||
Webengage = 235;
|
||||
ZonkaFeedback = 236;
|
||||
Delighted = 237;
|
||||
Feedier = 238;
|
||||
Abbysale = 239;
|
||||
Magnetic = 240;
|
||||
Nytimes = 241;
|
||||
Polygon = 242;
|
||||
Powrbot = 243;
|
||||
ProspectIO = 244;
|
||||
Skrappio = 245;
|
||||
Monday = 246;
|
||||
Smartsheets = 247;
|
||||
Wrike = 248;
|
||||
Float = 249;
|
||||
Imagekit = 250;
|
||||
Integromat = 251;
|
||||
Salesblink = 252;
|
||||
Bored = 253;
|
||||
Campayn = 254;
|
||||
Clinchpad = 255;
|
||||
CompanyHub = 256;
|
||||
Debounce = 257;
|
||||
Dyspatch = 258;
|
||||
Guardianapi = 259;
|
||||
Harvest = 260;
|
||||
Moosend = 261;
|
||||
OpenWeather = 262;
|
||||
Siteleaf = 263;
|
||||
Squarespace = 264;
|
||||
FlowFlu = 265;
|
||||
Nimble = 266;
|
||||
LessAnnoyingCRM = 267;
|
||||
Nethunt = 268;
|
||||
Apptivo = 269;
|
||||
CapsuleCRM = 270;
|
||||
Insightly = 271;
|
||||
Kylas = 272;
|
||||
OnepageCRM = 273;
|
||||
User = 274;
|
||||
ProspectCRM = 275;
|
||||
ReallySimpleSystems = 276;
|
||||
Airship = 277;
|
||||
Artsy = 278;
|
||||
Yandex = 279;
|
||||
Clockify = 280;
|
||||
Dnscheck = 281;
|
||||
EasyInsight = 282;
|
||||
Ethplorer = 283;
|
||||
Everhour = 284;
|
||||
Fulcrum = 285;
|
||||
GeoIpifi = 286;
|
||||
Jotform = 287;
|
||||
Refiner = 288;
|
||||
Timezoneapi = 289;
|
||||
TogglTrack = 290;
|
||||
Vpnapi = 291;
|
||||
Workstack = 292;
|
||||
Apollo = 293;
|
||||
Eversign = 294;
|
||||
Juro = 295;
|
||||
KarmaCRM = 296;
|
||||
Metrilo = 297;
|
||||
Pandadoc = 298;
|
||||
RevampCRM = 299;
|
||||
Salescookie = 300;
|
||||
Alconost = 301;
|
||||
Blogger = 302;
|
||||
Accuweather = 303;
|
||||
Opengraphr = 304;
|
||||
Rawg = 305;
|
||||
Riotgames = 306;
|
||||
RoninApp = 307;
|
||||
Stormglass = 308;
|
||||
Tomtom = 309;
|
||||
Twitch = 310;
|
||||
Documo = 311;
|
||||
Cloudways = 312;
|
||||
Veevavault = 313;
|
||||
KiteConnect = 314;
|
||||
ShopeeOpenPlatform = 315;
|
||||
TeamViewer = 316;
|
||||
Bulbul = 317;
|
||||
CentralStationCRM = 318;
|
||||
Teamgate = 319;
|
||||
Axonaut = 320;
|
||||
Tyntec = 321;
|
||||
Appcues = 322;
|
||||
Autoklose = 323;
|
||||
Cloudplan = 324;
|
||||
Dotmailer = 325;
|
||||
GetEmail = 326;
|
||||
GetEmails = 327;
|
||||
Kontent = 328;
|
||||
Leadfeeder = 329;
|
||||
Raven = 330;
|
||||
RocketReach = 331;
|
||||
Uplead = 332;
|
||||
Brandfetch = 333;
|
||||
Clearbit = 334;
|
||||
Crowdin = 335;
|
||||
Mapquest = 336;
|
||||
Noticeable = 337;
|
||||
Onbuka = 338;
|
||||
Todoist = 339;
|
||||
Storychief = 340;
|
||||
LinkedIn = 341;
|
||||
YouSign = 342;
|
||||
Docker = 343;
|
||||
Telesign = 344;
|
||||
Spoonacular = 345;
|
||||
Aerisweather = 346;
|
||||
Alphavantage = 347;
|
||||
Imgur = 348;
|
||||
Imagga = 349;
|
||||
SMSApi = 350;
|
||||
Distribusion = 351;
|
||||
Blablabus = 352;
|
||||
WordsApi = 353;
|
||||
Currencylayer = 354;
|
||||
Html2Pdf = 355;
|
||||
IPGeolocation = 356;
|
||||
Owlbot = 357;
|
||||
Cloudmersive = 358;
|
||||
Dynalist = 359;
|
||||
ExchangeRateAPI = 360;
|
||||
HolidayAPI = 361;
|
||||
Ipapi = 362;
|
||||
Marketstack = 363;
|
||||
Nutritionix = 364;
|
||||
Swell = 365;
|
||||
ClickupPersonalToken = 366;
|
||||
Nitro = 367;
|
||||
Rev = 368;
|
||||
RunRunIt = 369;
|
||||
Typeform = 370;
|
||||
Mixpanel = 371;
|
||||
Tradier = 372;
|
||||
Verifier = 373;
|
||||
Vouchery = 374;
|
||||
Alegra = 375;
|
||||
Audd = 376;
|
||||
Baremetrics = 377;
|
||||
Coinlib = 378;
|
||||
ExchangeRatesAPI = 379;
|
||||
CurrencyScoop = 380;
|
||||
FXMarket = 381;
|
||||
CurrencyCloud = 382;
|
||||
GetGeoAPI = 383;
|
||||
Abstract = 384;
|
||||
Billomat = 385;
|
||||
Dovico = 386;
|
||||
Bitbar = 387;
|
||||
Bugsnag = 388;
|
||||
AssemblyAI = 389;
|
||||
AdafruitIO = 390;
|
||||
Apify = 391;
|
||||
CoinGecko = 392;
|
||||
CryptoCompare = 393;
|
||||
Fullstory = 394;
|
||||
HelloSign = 395;
|
||||
Loyverse = 396;
|
||||
NetCore = 397;
|
||||
SauceLabs = 398;
|
||||
AlienVault = 399;
|
||||
Apiflash = 401;
|
||||
Coinlayer = 402;
|
||||
CurrentsAPI = 403;
|
||||
DataGov = 404;
|
||||
Enigma = 405;
|
||||
FinancialModelingPrep = 406;
|
||||
Geocodio = 407;
|
||||
HereAPI = 408;
|
||||
Macaddress = 409;
|
||||
OOPSpam = 410;
|
||||
ProtocolsIO = 411;
|
||||
ScraperAPI = 412;
|
||||
SecurityTrails = 413;
|
||||
TomorrowIO = 414;
|
||||
WorldCoinIndex = 415;
|
||||
FacePlusPlus = 416;
|
||||
Voicegain = 417;
|
||||
Deepgram = 418;
|
||||
VisualCrossing = 419;
|
||||
Finnhub = 420;
|
||||
Tiingo = 421;
|
||||
RingCentral = 422;
|
||||
Finage = 423;
|
||||
Edamam = 424;
|
||||
HypeAuditor = 425;
|
||||
Gengo = 426;
|
||||
Front = 427;
|
||||
Fleetbase = 428;
|
||||
Bubble = 429;
|
||||
Bannerbear = 430;
|
||||
Adzuna = 431;
|
||||
BitcoinAverage = 432;
|
||||
CommerceJS = 433;
|
||||
DetectLanguage = 434;
|
||||
FakeJSON = 435;
|
||||
Graphhopper = 436;
|
||||
Lexigram = 437;
|
||||
LinkPreview = 438;
|
||||
Numverify = 439;
|
||||
ProxyCrawl = 440;
|
||||
ZipCodeAPI = 441;
|
||||
Cometchat = 442;
|
||||
Keygen = 443;
|
||||
Mixcloud = 444;
|
||||
TatumIO = 445;
|
||||
Tmetric = 446;
|
||||
Lastfm = 447;
|
||||
Browshot = 448;
|
||||
JSONbin = 449;
|
||||
LocationIQ = 450;
|
||||
ScreenshotAPI = 451;
|
||||
WeatherStack = 452;
|
||||
Amadeus = 453;
|
||||
FourSquare = 454;
|
||||
Flickr = 455;
|
||||
ClickHelp = 456;
|
||||
Ambee = 457;
|
||||
Api2Cart = 458;
|
||||
Hypertrack = 459;
|
||||
KakaoTalk = 460;
|
||||
RiteKit = 461;
|
||||
Shutterstock = 462;
|
||||
Text2Data = 463;
|
||||
YouNeedABudget = 464;
|
||||
Cricket = 465;
|
||||
Filestack = 466;
|
||||
Gyazo = 467;
|
||||
Mavenlink = 468;
|
||||
Sheety = 469;
|
||||
Sportsmonk = 470;
|
||||
Stockdata = 471;
|
||||
Unsplash = 472;
|
||||
Allsports = 473;
|
||||
CalorieNinja = 474;
|
||||
WalkScore = 475;
|
||||
Strava = 476;
|
||||
Cicero = 477;
|
||||
IPQuality = 478;
|
||||
ParallelDots = 479;
|
||||
Roaring = 480;
|
||||
Mailsac = 481;
|
||||
Whoxy = 482;
|
||||
WorldWeather = 483;
|
||||
ApiFonica = 484;
|
||||
Aylien = 485;
|
||||
Geocode = 486;
|
||||
IconFinder = 487;
|
||||
Ipify = 488;
|
||||
LanguageLayer = 489;
|
||||
Lob = 490;
|
||||
OnWaterIO = 491;
|
||||
Pastebin = 492;
|
||||
PdfLayer = 493;
|
||||
Pixabay = 494;
|
||||
ReadMe = 495;
|
||||
VatLayer = 496;
|
||||
VirusTotal = 497;
|
||||
AirVisual = 498;
|
||||
Currencyfreaks = 499;
|
||||
Duffel = 500;
|
||||
FlatIO = 501;
|
||||
M3o = 502;
|
||||
Mesibo = 503;
|
||||
Openuv = 504;
|
||||
Snipcart = 505;
|
||||
Besttime = 506;
|
||||
Happyscribe = 507;
|
||||
Humanity = 508;
|
||||
Impala = 509;
|
||||
Loginradius = 510;
|
||||
AutoPilot = 511;
|
||||
Bitmex = 512;
|
||||
ClustDoc = 513;
|
||||
Messari = 514;
|
||||
PdfShift = 515;
|
||||
Poloniex = 516;
|
||||
RestpackHtmlToPdfAPI = 517;
|
||||
RestpackScreenshotAPI = 518;
|
||||
ShutterstockOAuth = 519;
|
||||
SkyBiometry = 520;
|
||||
AbuseIPDB = 521;
|
||||
AletheiaApi = 522;
|
||||
BlitApp = 523;
|
||||
Censys = 524;
|
||||
Cloverly = 525;
|
||||
CountryLayer = 526;
|
||||
FileIO = 527;
|
||||
FlightApi = 528;
|
||||
Geoapify = 529;
|
||||
IPinfoDB = 530;
|
||||
MediaStack = 531;
|
||||
NasdaqDataLink = 532;
|
||||
OpenCageData = 533;
|
||||
Paymongo = 534;
|
||||
PositionStack = 535;
|
||||
Rebrandly = 536;
|
||||
ScreenshotLayer = 537;
|
||||
Stytch = 538;
|
||||
Unplugg = 539;
|
||||
UPCDatabase = 540;
|
||||
UserStack = 541;
|
||||
Geocodify = 542;
|
||||
Newscatcher = 543;
|
||||
Nicereply = 544;
|
||||
Partnerstack = 545;
|
||||
Route4me = 546;
|
||||
Scrapeowl = 547;
|
||||
ScrapingDog = 548;
|
||||
Streak = 549;
|
||||
Veriphone = 550;
|
||||
Webscraping = 551;
|
||||
Zenscrape = 552;
|
||||
Zenserp = 553;
|
||||
CoinApi = 554;
|
||||
Gitter = 555;
|
||||
Host = 556;
|
||||
Iexcloud = 557;
|
||||
Restpack = 558;
|
||||
ScraperBox = 559;
|
||||
ScrapingAnt = 560;
|
||||
SerpStack = 561;
|
||||
SmartyStreets = 562;
|
||||
TicketMaster = 563;
|
||||
AviationStack = 564;
|
||||
BombBomb = 565;
|
||||
Commodities = 566;
|
||||
Dfuse = 567;
|
||||
EdenAI = 568;
|
||||
Glassnode = 569;
|
||||
Guru = 570;
|
||||
Hive = 571;
|
||||
Hiveage = 572;
|
||||
Kickbox = 573;
|
||||
Passbase = 574;
|
||||
PostageApp = 575;
|
||||
PureStake = 576;
|
||||
Qubole = 577;
|
||||
CarbonInterface = 578;
|
||||
Intrinio = 579;
|
||||
QuickMetrics = 580;
|
||||
ScrapeStack = 581;
|
||||
TechnicalAnalysisApi = 582;
|
||||
Urlscan = 583;
|
||||
BaseApiIO = 584;
|
||||
DailyCO = 585;
|
||||
TLy = 586;
|
||||
Shortcut = 587;
|
||||
Appfollow = 588;
|
||||
Thinkific = 589;
|
||||
Feedly = 590;
|
||||
Stitchdata = 591;
|
||||
Fetchrss = 592;
|
||||
Signupgenius = 593;
|
||||
Signaturit = 594;
|
||||
Optimizely = 595;
|
||||
OcrSpace = 596;
|
||||
WeatherBit = 597;
|
||||
BuddyNS = 598;
|
||||
ZipAPI = 599;
|
||||
ZipBooks = 600;
|
||||
Onedesk = 601;
|
||||
Bugherd = 602;
|
||||
Blazemeter = 603;
|
||||
Autodesk = 604;
|
||||
Tru = 605;
|
||||
UnifyID = 606;
|
||||
Trimble = 607;
|
||||
Smooch = 608;
|
||||
Semaphore = 609;
|
||||
Telnyx = 610;
|
||||
Signalwire = 611;
|
||||
Textmagic = 612;
|
||||
Serphouse = 613;
|
||||
Planyo = 614;
|
||||
Simplybook = 615;
|
||||
Vyte = 616;
|
||||
Nylas = 617;
|
||||
Squareup = 618;
|
||||
Dandelion = 619;
|
||||
DataFire = 620;
|
||||
DeepAI = 621;
|
||||
MeaningCloud = 622;
|
||||
NeutrinoApi = 623;
|
||||
Storecove = 624;
|
||||
Shipday = 625;
|
||||
Sentiment = 626;
|
||||
StreamChatMessaging = 627;
|
||||
TeamworkCRM = 628;
|
||||
TeamworkDesk = 629;
|
||||
TeamworkSpaces = 630;
|
||||
TheOddsApi = 631;
|
||||
Apacta = 632;
|
||||
GetSandbox = 633;
|
||||
Happi = 634;
|
||||
Oanda = 635;
|
||||
FastForex = 636;
|
||||
APIMatic = 637;
|
||||
VersionEye = 638;
|
||||
EagleEyeNetworks = 639;
|
||||
ThousandEyes = 640;
|
||||
SelectPDF = 641;
|
||||
Flightstats = 642;
|
||||
ChecIO = 643;
|
||||
Manifest = 644;
|
||||
ApiScience = 645;
|
||||
AppSynergy = 646;
|
||||
Caflou = 647;
|
||||
Caspio = 648;
|
||||
ChecklyHQ = 649;
|
||||
CloudElements = 650;
|
||||
DronaHQ = 651;
|
||||
Enablex = 652;
|
||||
Fmfw = 653;
|
||||
GoodDay = 654;
|
||||
Luno = 655;
|
||||
Meistertask = 656;
|
||||
Mindmeister = 657;
|
||||
PeopleDataLabs = 658;
|
||||
ScraperSite = 659;
|
||||
Scrapfly = 660;
|
||||
SimplyNoted = 661;
|
||||
TravelPayouts = 662;
|
||||
WebScraper = 663;
|
||||
Convier = 664;
|
||||
Courier = 665;
|
||||
Ditto = 666;
|
||||
Findl = 667;
|
||||
Lendflow = 668;
|
||||
Moderation = 669;
|
||||
Opendatasoft = 670;
|
||||
Podio = 671;
|
||||
Rockset = 672;
|
||||
Rownd = 673;
|
||||
Shotstack = 674;
|
||||
Swiftype = 675;
|
||||
Twitter = 676;
|
||||
Honey = 677;
|
||||
Freshdesk = 678;
|
||||
Upwave = 679;
|
||||
Fountain = 680;
|
||||
Freshbooks = 681;
|
||||
Mite = 682;
|
||||
Deputy = 683;
|
||||
Beebole = 684;
|
||||
Cashboard = 685;
|
||||
Kanban = 686;
|
||||
Worksnaps = 687;
|
||||
MyIntervals = 688;
|
||||
InvoiceOcean = 689;
|
||||
Sherpadesk = 690;
|
||||
Mrticktock = 691;
|
||||
Chatfule = 692;
|
||||
Aeroworkflow = 693;
|
||||
Emailoctopus = 694;
|
||||
Fusebill = 695;
|
||||
Geckoboard = 696;
|
||||
Gosquared = 697;
|
||||
Moonclerck = 698;
|
||||
Paymoapp = 699;
|
||||
Mixmax = 700;
|
||||
Processst = 701;
|
||||
Repairshopr = 702;
|
||||
Goshippo = 703;
|
||||
Sigopt = 704;
|
||||
Sugester = 705;
|
||||
Viewneo = 706;
|
||||
BoostNote = 707;
|
||||
CaptainData = 708;
|
||||
Checkvist = 709;
|
||||
Cliengo = 710;
|
||||
Cloze = 711;
|
||||
FormIO = 712;
|
||||
FormBucket = 713;
|
||||
GoCanvas = 714;
|
||||
MadKudu = 715;
|
||||
NozbeTeams = 716;
|
||||
Papyrs = 717;
|
||||
SuperNotesAPI = 718;
|
||||
Tallyfy = 719;
|
||||
ZenkitAPI = 720;
|
||||
CloudImage = 721;
|
||||
UploadCare = 722;
|
||||
Borgbase = 723;
|
||||
Pipedream = 724;
|
||||
Sirv = 725;
|
||||
Diffbot = 726;
|
||||
EightxEight = 727;
|
||||
Sendoso = 728;
|
||||
Printfection = 729;
|
||||
Authorize = 730;
|
||||
PandaScore = 731;
|
||||
Paymo = 732;
|
||||
AvazaPersonalAccessToken = 733;
|
||||
PlanviewLeanKit = 734;
|
||||
Livestorm = 735;
|
||||
KuCoin = 736;
|
||||
MetaAPI = 737;
|
||||
NiceHash = 738;
|
||||
CexIO = 739;
|
||||
Klipfolio = 740;
|
||||
Dynatrace = 741;
|
||||
MollieAPIKey = 742;
|
||||
MollieAccessToken = 743;
|
||||
BasisTheory = 744;
|
||||
Nordigen = 745;
|
||||
FlagsmithEnvironmentKey = 746;
|
||||
FlagsmithToken = 747;
|
||||
Mux = 748;
|
||||
}
|
||||
|
||||
message Result {
|
||||
int64 source_id = 2;
|
||||
string redacted = 3;
|
||||
bool verified = 4;
|
||||
string hash = 5;
|
||||
map<string, string> extra_data = 6;
|
||||
StructuredData structured_data = 7;
|
||||
}
|
||||
|
||||
message StructuredData {
|
||||
repeated TlsPrivateKey tls_private_key = 1;
|
||||
repeated GitHubSSHKey github_ssh_key = 2;
|
||||
}
|
||||
|
||||
message TlsPrivateKey {
|
||||
string certificate_fingerprint = 1;
|
||||
string verification_url = 2;
|
||||
int64 expiration_timestamp = 3;
|
||||
}
|
||||
|
||||
message GitHubSSHKey {
|
||||
string user = 1;
|
||||
string public_key_fingerprint = 2;
|
||||
}
|
189
proto/source_metadata.proto
Normal file
189
proto/source_metadata.proto
Normal file
|
@ -0,0 +1,189 @@
|
|||
syntax = "proto3";
|
||||
|
||||
package source_metadata;
|
||||
|
||||
option go_package = "github.com/trufflesecurity/trufflehog/pkg/pb/source_metadatapb";
|
||||
|
||||
message Azure {
|
||||
string container = 1;
|
||||
string file = 2;
|
||||
string uploaded = 3;
|
||||
string link = 4;
|
||||
string email = 5;
|
||||
}
|
||||
|
||||
message Bitbucket {
|
||||
string file = 1;
|
||||
string repository = 2;
|
||||
string workspace = 3;
|
||||
string snippet_id = 4;
|
||||
string title = 5;
|
||||
string commit = 6;
|
||||
string email = 7;
|
||||
string link = 8;
|
||||
}
|
||||
|
||||
message Buildkite {
|
||||
string org = 1;
|
||||
string pipeline = 2;
|
||||
string link = 3;
|
||||
string email = 4;
|
||||
int64 build_number = 5;
|
||||
}
|
||||
|
||||
message CircleCI {
|
||||
string vcs_type = 1;
|
||||
string username = 2;
|
||||
string repository = 3;
|
||||
int64 build_number = 4;
|
||||
string build_step = 5;
|
||||
string link = 6;
|
||||
string email = 7;
|
||||
}
|
||||
|
||||
message Confluence {
|
||||
string page = 1;
|
||||
string space = 2;
|
||||
string version = 3;
|
||||
string link = 4;
|
||||
string email = 5;
|
||||
}
|
||||
|
||||
message Dockerhub {
|
||||
string file = 1;
|
||||
string image = 2;
|
||||
string layer = 3;
|
||||
string tag = 4;
|
||||
string link = 5;
|
||||
string email = 6;
|
||||
}
|
||||
|
||||
message ECR {
|
||||
string file = 1;
|
||||
string layer = 2;
|
||||
string image = 3;
|
||||
string registry = 4;
|
||||
string region = 5;
|
||||
string link = 6;
|
||||
string email = 7;
|
||||
}
|
||||
|
||||
message Filesystem {
|
||||
string file = 1;
|
||||
string link = 2;
|
||||
string email = 3;
|
||||
}
|
||||
|
||||
message Git {
|
||||
string commit = 1;
|
||||
string file = 2;
|
||||
string email = 3;
|
||||
string repository = 4;
|
||||
}
|
||||
|
||||
message Github {
|
||||
string link = 1;
|
||||
string username = 2;
|
||||
string repository = 3;
|
||||
string commit = 4;
|
||||
string email = 5;
|
||||
string file = 6;
|
||||
}
|
||||
|
||||
message Gitlab {
|
||||
string commit = 1;
|
||||
string file = 2;
|
||||
string link = 3;
|
||||
string email = 4;
|
||||
string repository = 5;
|
||||
}
|
||||
|
||||
message GCS {
|
||||
string bucket = 1;
|
||||
string file = 2;
|
||||
string link = 3;
|
||||
string email = 4;
|
||||
}
|
||||
|
||||
message Jira {
|
||||
string issue = 1;
|
||||
string author = 2;
|
||||
string link = 3;
|
||||
string location = 4;
|
||||
string email = 5;
|
||||
}
|
||||
|
||||
message NPM {
|
||||
string file = 1;
|
||||
string package = 2;
|
||||
string release = 3;
|
||||
string link = 4;
|
||||
string email = 5;
|
||||
}
|
||||
|
||||
message PyPi {
|
||||
string file = 1;
|
||||
string package = 2;
|
||||
string release = 3;
|
||||
string link = 4;
|
||||
string email = 5;
|
||||
}
|
||||
|
||||
message S3 {
|
||||
string bucket = 1;
|
||||
string file = 2;
|
||||
string link = 3;
|
||||
string email = 5;
|
||||
}
|
||||
|
||||
message Slack {
|
||||
string channel_id = 1;
|
||||
string channel_name = 2;
|
||||
string timestamp = 3;
|
||||
string user_id = 4;
|
||||
string link = 5;
|
||||
string file = 6;
|
||||
string email = 7;
|
||||
}
|
||||
|
||||
message Gerrit {
|
||||
string commit = 1;
|
||||
string file = 2;
|
||||
string email = 3;
|
||||
string project = 4; // projects are what Gerrit calls repositories
|
||||
}
|
||||
|
||||
message Test {
|
||||
string file = 1;
|
||||
}
|
||||
|
||||
message Jenkins {
|
||||
string project_name = 1;
|
||||
int64 build_number = 2;
|
||||
string link = 3;
|
||||
}
|
||||
|
||||
message MetaData {
|
||||
oneof data {
|
||||
Azure azure = 1;
|
||||
Bitbucket bitbucket = 2;
|
||||
CircleCI circleci = 3;
|
||||
Confluence confluence = 4;
|
||||
Dockerhub dockerhub = 5;
|
||||
ECR ecr = 6;
|
||||
GCS gcs = 7;
|
||||
Github github = 8;
|
||||
Gitlab gitlab = 9;
|
||||
Jira jira = 10;
|
||||
NPM npm = 11;
|
||||
PyPi pypi = 12;
|
||||
S3 s3 = 13;
|
||||
Slack slack = 14;
|
||||
Filesystem filesystem = 15;
|
||||
Git git = 16;
|
||||
Test test = 17;
|
||||
Buildkite buildkite = 18;
|
||||
Gerrit gerrit = 19;
|
||||
Jenkins jenkins = 20;
|
||||
}
|
||||
}
|
195
proto/sources.proto
Normal file
195
proto/sources.proto
Normal file
|
@ -0,0 +1,195 @@
|
|||
syntax = "proto3";
|
||||
|
||||
package sources;
|
||||
|
||||
option go_package = "github.com/trufflesecurity/trufflehog/pkg/pb/sourcespb";
|
||||
|
||||
import "validate/validate.proto";
|
||||
import "credentials.proto";
|
||||
import "google/protobuf/duration.proto";
|
||||
|
||||
|
||||
enum SourceType {
|
||||
SOURCE_TYPE_AZURE_STORAGE = 0;
|
||||
SOURCE_TYPE_BITBUCKET = 1;
|
||||
SOURCE_TYPE_CIRCLECI = 2;
|
||||
SOURCE_TYPE_CONFLUENCE = 3;
|
||||
SOURCE_TYPE_DOCKERHUB_IMAGES = 4;
|
||||
SOURCE_TYPE_ECR = 5;
|
||||
SOURCE_TYPE_GCS = 6;
|
||||
SOURCE_TYPE_GITHUB = 7;
|
||||
SOURCE_TYPE_PUBLIC_GIT = 8;
|
||||
SOURCE_TYPE_GITLAB = 9;
|
||||
SOURCE_TYPE_JIRA = 10;
|
||||
SOURCE_TYPE_NPM_UNAUTHD_PACKAGES = 11;
|
||||
SOURCE_TYPE_PYPI_UNAUTHD_PACKAGES = 12;
|
||||
SOURCE_TYPE_S3 = 13;
|
||||
SOURCE_TYPE_SLACK = 14;
|
||||
SOURCE_TYPE_FILESYSTEM = 15;
|
||||
SOURCE_TYPE_GIT = 16;
|
||||
SOURCE_TYPE_TEST = 17;
|
||||
SOURCE_TYPE_S3_UNAUTHED = 18;
|
||||
SOURCE_TYPE_GITHUB_UNAUTHENTICATED_ORG = 19;
|
||||
SOURCE_TYPE_BUILDKITE = 20;
|
||||
SOURCE_TYPE_GERRIT = 21;
|
||||
SOURCE_TYPE_JENKINS = 22;
|
||||
}
|
||||
|
||||
message AzureStorage {
|
||||
oneof credential {
|
||||
string connection_string = 1;
|
||||
credentials.BasicAuth basic_auth = 2;
|
||||
string client_certificate = 3;
|
||||
credentials.Unauthenticated unauthenticated = 4;
|
||||
}
|
||||
repeated string storage_containers = 5;
|
||||
}
|
||||
|
||||
message Bitbucket {
|
||||
string endpoint = 1 [(validate.rules).string.uri_ref = true];
|
||||
oneof credential {
|
||||
string token = 2;
|
||||
credentials.Oauth2 oauth = 3;
|
||||
credentials.BasicAuth basic_auth = 4;
|
||||
|
||||
}
|
||||
repeated string repositories = 5;
|
||||
}
|
||||
|
||||
message CircleCI {
|
||||
string endpoint = 1 [(validate.rules).string.uri_ref = true];
|
||||
oneof credential {
|
||||
string token = 2;
|
||||
}
|
||||
}
|
||||
|
||||
message Confluence {
|
||||
string endpoint = 1 [(validate.rules).string.uri_ref = true];
|
||||
oneof credential {
|
||||
credentials.Unauthenticated unauthenticated = 2;
|
||||
credentials.BasicAuth basic_auth = 3;
|
||||
string token = 4;
|
||||
}
|
||||
}
|
||||
|
||||
message DockerHub {
|
||||
oneof credential {
|
||||
credentials.Unauthenticated unauthenticated = 1;
|
||||
}
|
||||
repeated string repositories = 2;
|
||||
}
|
||||
|
||||
message ECR {
|
||||
oneof credential {
|
||||
credentials.KeySecret access_key = 1;
|
||||
}
|
||||
repeated string registries = 2;
|
||||
}
|
||||
|
||||
message Filesystem{
|
||||
repeated string directories = 1;
|
||||
}
|
||||
|
||||
message GCS {
|
||||
oneof credential {
|
||||
string json_sa = 1;
|
||||
}
|
||||
repeated string buckets = 2;
|
||||
}
|
||||
|
||||
message Git {
|
||||
oneof credential {
|
||||
credentials.BasicAuth basic_auth = 1;
|
||||
credentials.Unauthenticated unauthenticated = 2;
|
||||
}
|
||||
repeated string directories = 3;
|
||||
repeated string repositories = 4;
|
||||
}
|
||||
|
||||
message GitLab {
|
||||
string endpoint = 1 [(validate.rules).string.uri_ref = true];
|
||||
oneof credential {
|
||||
string token = 2;
|
||||
credentials.Oauth2 oauth = 3;
|
||||
credentials.BasicAuth basic_auth = 4;
|
||||
}
|
||||
repeated string repositories = 5;
|
||||
}
|
||||
|
||||
message GitHub {
|
||||
string endpoint = 1 [(validate.rules).string.uri_ref = true];
|
||||
oneof credential {
|
||||
credentials.GitHubApp github_app = 2;
|
||||
string token = 3;
|
||||
credentials.Unauthenticated unauthenticated = 4;
|
||||
}
|
||||
repeated string repositories = 5;
|
||||
repeated string organizations = 6;
|
||||
bool scanUsers = 7;
|
||||
}
|
||||
|
||||
message JIRA {
|
||||
string endpoint = 1 [(validate.rules).string.uri_ref = true];
|
||||
oneof credential {
|
||||
credentials.BasicAuth basic_auth = 2;
|
||||
credentials.Unauthenticated unauthenticated = 3;
|
||||
credentials.Oauth2 oauth = 4;
|
||||
|
||||
}
|
||||
repeated string projects = 5;
|
||||
}
|
||||
|
||||
message NPMUnauthenticatedPackage {
|
||||
oneof credential {
|
||||
credentials.Unauthenticated unauthenticated = 1;
|
||||
}
|
||||
}
|
||||
|
||||
message PyPIUnauthenticatedPackage {
|
||||
oneof credential {
|
||||
credentials.Unauthenticated unauthenticated = 1;
|
||||
}
|
||||
}
|
||||
|
||||
message S3 {
|
||||
oneof credential {
|
||||
credentials.KeySecret access_key = 1;
|
||||
credentials.Unauthenticated unauthenticated = 2;
|
||||
credentials.CloudEnvironment cloud_environment = 4;
|
||||
}
|
||||
repeated string buckets = 3;
|
||||
}
|
||||
|
||||
message Slack {
|
||||
string endpoint = 1 [(validate.rules).string.uri_ref = true];
|
||||
oneof credential {
|
||||
string token = 2;
|
||||
}
|
||||
repeated string channels = 3;
|
||||
|
||||
|
||||
}
|
||||
|
||||
message Test{}
|
||||
|
||||
message Buildkite {
|
||||
oneof credential {
|
||||
string token = 1;
|
||||
}
|
||||
}
|
||||
|
||||
message Gerrit {
|
||||
string endpoint = 1 [(validate.rules).string.uri_ref = true];
|
||||
oneof credential {
|
||||
credentials.BasicAuth basic_auth = 2;
|
||||
credentials.Unauthenticated unauthenticated = 3;
|
||||
}
|
||||
repeated string projects = 4;
|
||||
}
|
||||
|
||||
message Jenkins {
|
||||
string endpoint = 1 [(validate.rules).string.uri_ref = true];
|
||||
oneof credential {
|
||||
credentials.BasicAuth basic_auth = 2;
|
||||
}
|
||||
}
|
32
scripts/gen_proto.sh
Executable file
32
scripts/gen_proto.sh
Executable file
|
@ -0,0 +1,32 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -eux
|
||||
|
||||
protoc -I proto/ \
|
||||
-I ${GOPATH}/src \
|
||||
-I /usr/local/include \
|
||||
-I ${GOPATH}/src/github.com/envoyproxy/protoc-gen-validate \
|
||||
--go_out=plugins=grpc:./pkg/pb/credentialspb --go_opt=paths=source_relative \
|
||||
--validate_out="lang=go,paths=source_relative:./pkg/pb/credentialspb" \
|
||||
proto/credentials.proto
|
||||
protoc -I proto/ \
|
||||
-I ${GOPATH}/src \
|
||||
-I /usr/local/include \
|
||||
-I ${GOPATH}/src/github.com/envoyproxy/protoc-gen-validate \
|
||||
--go_out=plugins=grpc:./pkg/pb/sourcespb --go_opt=paths=source_relative \
|
||||
--validate_out="lang=go,paths=source_relative:./pkg/pb/sourcespb" \
|
||||
proto/sources.proto
|
||||
protoc -I proto/ \
|
||||
-I ${GOPATH}/src \
|
||||
-I /usr/local/include \
|
||||
-I ${GOPATH}/src/github.com/envoyproxy/protoc-gen-validate \
|
||||
--go_out=plugins=grpc:./pkg/pb/detectorspb --go_opt=paths=source_relative \
|
||||
--validate_out="lang=go,paths=source_relative:./pkg/pb/detectorspb" \
|
||||
proto/detectors.proto
|
||||
protoc -I proto/ \
|
||||
-I ${GOPATH}/src \
|
||||
-I /usr/local/include \
|
||||
-I ${GOPATH}/src/github.com/envoyproxy/protoc-gen-validate \
|
||||
--go_out=plugins=grpc:./pkg/pb/source_metadatapb --go_opt=paths=source_relative \
|
||||
--validate_out="lang=go,paths=source_relative:./pkg/pb/source_metadatapb" \
|
||||
proto/source_metadata.proto
|
Loading…
Reference in a new issue