Initial CLI w/ partially implemented Git source and demo detector (#1)
This commit is contained in:
@@ -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,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
|
||||||
|
)
|
||||||
@@ -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=
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "github.com/tailscale/depaware/depaware"
|
||||||
|
)
|
||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -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{},
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -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: "[email protected]: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: "[email protected]: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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -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{}
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,9 @@
|
|||||||
|
package sanitizer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func UTF8(in string) string {
|
||||||
|
return strings.Replace(strings.ToValidUTF8(in, "❗"), "\x00", "", -1)
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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))
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
// }
|
||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
Executable
+32
@@ -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
|
||||||
Reference in New Issue
Block a user