mirror of
https://github.com/BishopFox/cloudfox.git
synced 2026-11-08 08:03:50 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
599ddc247d | ||
|
|
9bedfcb4de | ||
|
|
afbcab6652 | ||
|
|
3ec90f51d2 | ||
|
|
1343060c4b | ||
|
|
bbc26e0a23 | ||
|
|
8518b7678d | ||
|
|
07cd08ccc2 | ||
|
|
4ffc02c285 | ||
|
|
f61dd2bfb1 | ||
|
|
5365206a49 | ||
|
|
fc390b763e | ||
|
|
a2140f1ef1 | ||
|
|
48f92dd4f8 | ||
|
|
139e4d391f | ||
|
|
6251b76a12 | ||
|
|
48c4928767 | ||
|
|
7578d4299d | ||
|
|
b08ca87e6b | ||
|
|
5f029319a6 | ||
|
|
42a32bb8e3 | ||
|
|
c397f858db | ||
|
|
980e28bc9d | ||
|
|
9b752ce4e6 | ||
|
|
c102ea8d17 | ||
|
|
ba04dade7a | ||
|
|
61cb727a64 | ||
|
|
25fb66d6a1 | ||
|
|
82adab9863 | ||
|
|
02026c7a78 |
No files matched your search
@@ -1,20 +1,20 @@
|
||||
run:
|
||||
go run main.go
|
||||
/home/marauder/Downloads/go/bin/go run main.go
|
||||
|
||||
windows:
|
||||
GOOS=windows GOARCH=386 go build
|
||||
GOOS=windows GOARCH=386 /home/marauder/Downloads/go/bin/go build
|
||||
sha1sum cloudfox.exe > sha1sum-win.txt
|
||||
zip cloudfox-win.zip cloudfox.exe sha1sum-win.txt
|
||||
rm cloudfox.exe sha1sum-win.txt
|
||||
|
||||
linux:
|
||||
GOOS=linux GOARCH=386 go build
|
||||
GOOS=linux GOARCH=386 /home/marauder/Downloads/go/bin/go build
|
||||
sha1sum cloudfox > sha1sum-linux.txt
|
||||
zip cloudfox-linux.zip cloudfox sha1sum-linux.txt
|
||||
rm cloudfox sha1sum-linux.txt
|
||||
|
||||
macos:
|
||||
GOOS=darwin GOARCH=amd64 go build
|
||||
GOOS=darwin GOARCH=amd64 /home/marauder/Downloads/go/bin/go build
|
||||
sha1sum cloudfox > sha1sum-mac.txt
|
||||
zip cloudfox-mac.zip cloudfox sha1sum-mac.txt
|
||||
rm cloudfox sha1sum-mac.txt
|
||||
@@ -25,21 +25,21 @@ all: windows linux macos
|
||||
release: clean
|
||||
mkdir -p ./cloudfox
|
||||
mkdir -p ./cloudfox
|
||||
GOOS=windows GOARCH=amd64 go build -o ./cloudfox/cloudfox.exe .
|
||||
GOOS=windows GOARCH=amd64 /home/marauder/Downloads/go/bin/go build -o ./cloudfox/cloudfox.exe .
|
||||
zip ./cloudfox/cloudfox-windows-amd64.zip ./cloudfox/cloudfox.exe
|
||||
rm -rf ./cloudfox/cloudfox.exe
|
||||
|
||||
GOOS=linux GOARCH=amd64 go build -o ./cloudfox/cloudfox .
|
||||
GOOS=linux GOARCH=amd64 /home/marauder/Downloads/go/bin/go build -o ./cloudfox/cloudfox .
|
||||
zip ./cloudfox/cloudfox-linux-amd64.zip ./cloudfox/cloudfox .
|
||||
rm -rf ./cloudfox/cloudfox
|
||||
|
||||
GOOS=darwin GOARCH=amd64 go build -o ./cloudfox/cloudfox .
|
||||
GOOS=darwin GOARCH=amd64 /home/marauder/Downloads/go/bin/go build -o ./cloudfox/cloudfox .
|
||||
zip ./cloudfox/cloudfox-macos-amd64.zip ./cloudfox/cloudfox
|
||||
rm -rf ./cloudfox/cloudfox
|
||||
|
||||
GOOS=darwin GOARCH=arm64 go build -o ./cloudfox/cloudfox .
|
||||
GOOS=darwin GOARCH=arm64 /home/marauder/Downloads/go/bin/go build -o ./cloudfox/cloudfox .
|
||||
zip ./cloudfox/cloudfox-macos-arm64.zip ./cloudfox/cloudfox
|
||||
rm -rf ./cloudfox/cloudfox
|
||||
|
||||
clean:
|
||||
rm -rf ./cloudfox
|
||||
rm -rf ./cloudfox
|
||||
@@ -41,7 +41,7 @@ For the full documentation please refer to our [wiki](https://github.com/BishopF
|
||||
| - | - |
|
||||
| AWS | 30 |
|
||||
| Azure | 4 |
|
||||
| GCP | Support Planned |
|
||||
| GCP | 4 |
|
||||
| Kubernetes | Support Planned |
|
||||
|
||||
|
||||
@@ -79,7 +79,12 @@ Additional policy notes (as of 09/2022):
|
||||
| `arn:aws:iam::aws:policy/AdministratorAccess` | This will work just fine with CloudFox, but if you were handed this level of access as a penetration tester, that should probably be a finding in itself :) |
|
||||
|
||||
### Azure
|
||||
* Viewer or similar permissions applied.
|
||||
* Viewer or similar permissions applied.
|
||||
|
||||
### GCP
|
||||
* `gcloud` CLI installed
|
||||
* Proper SDK credentials available (`gcloud auth application-default login`)
|
||||
* Viewer or similar permissions applied.
|
||||
|
||||
# AWS Commands
|
||||
| Provider | Command Name | Description
|
||||
@@ -122,6 +127,14 @@ Additional policy notes (as of 09/2022):
|
||||
| Azure | [rbac](https://github.com/BishopFox/cloudfox/wiki/Azure-Commands#rbac) | Lists Azure RBAC role assignments at subscription or tenant level |
|
||||
| Azure | [storage](https://github.com/BishopFox/cloudfox/wiki/Azure-Commands#storage) | The storage command is still under development. Currently it only displays limited data about the storage accounts |
|
||||
|
||||
# GCP Commands
|
||||
| Provider | Command Name | Description
|
||||
| - | - | - |
|
||||
| GCP | [access-tokens](https://github.com/BishopFox/cloudfox/wiki/GCP-Commands#access-tokens) | Displays handy information about existing GCP credentials on your local machine |
|
||||
| GCP | [hierarchy](https://github.com/BishopFox/cloudfox/wiki/GCP-Commands#hierarchy) Displays information on the projects available to your current Gcloud SDK credentials. This is useful to provide situation awareness on what organizations, folders and projects to use with the other sub commands.|
|
||||
| GCP | [inventory](https://github.com/BishopFox/cloudfox/wiki/GCP-Commands#inventory) Gain a rough understanding of size of the available organizations |
|
||||
| GCP | [buckets](https://github.com/BishopFox/cloudfox/wiki/GCP-Commands#buckets) Lists the buckets in the available organizations and gives you handy commands for inspecting them further. |
|
||||
|
||||
# Authors
|
||||
* [Carlos Vendramini](https://github.com/carlosvendramini-bf)
|
||||
* [Seth Art (@sethsec](https://twitter.com/sethsec))
|
||||
|
||||
+232
@@ -0,0 +1,232 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"log"
|
||||
"fmt"
|
||||
"os"
|
||||
"github.com/BishopFox/cloudfox/gcp"
|
||||
internal_gcp "github.com/BishopFox/cloudfox/internal/gcp"
|
||||
"github.com/BishopFox/cloudfox/internal"
|
||||
"github.com/BishopFox/cloudfox/globals"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var (
|
||||
// GCP resources filtering options
|
||||
GCPOrganizations []string
|
||||
GCPProjectIDs []string
|
||||
GCPFolderIDs []string
|
||||
|
||||
// Output formatting options
|
||||
GCPOutputFormat string
|
||||
GCPOutputDirectory string
|
||||
GCPVerbosity int
|
||||
GCPWrapTable bool
|
||||
|
||||
GCPProfilesList string
|
||||
GCPAllProfiles bool
|
||||
GCPProfiles []string
|
||||
|
||||
|
||||
// misc options
|
||||
GCPConfirm bool
|
||||
GCPSkipAdminCheck bool
|
||||
GCPIgnoreCache bool
|
||||
|
||||
// command specific options
|
||||
GCPTreeFormat string
|
||||
|
||||
// logger
|
||||
GCPLogger = internal.NewLogger()
|
||||
|
||||
GCPCommands = &cobra.Command{
|
||||
Use: "gcp",
|
||||
Aliases: []string{"gcloud"},
|
||||
Long: `See "Available Commands" for GCP Modules below`,
|
||||
Short: "See \"Available Commands\" for GCP Modules below",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
cmd.Help()
|
||||
},
|
||||
}
|
||||
|
||||
GCPInventoryCommand = &cobra.Command{
|
||||
Use: "inventory",
|
||||
Aliases: []string{},
|
||||
Short: "Display all available GCP resources",
|
||||
Long: `
|
||||
Display available GCP resources:
|
||||
./cloudfox gcp inventory`,
|
||||
Run: runGCPInventoryCommand,
|
||||
}
|
||||
|
||||
GCPAccessTokensCommand = &cobra.Command{
|
||||
Use: "access-tokens",
|
||||
Aliases: []string{},
|
||||
Short: "Display all available local gcloud access tokens",
|
||||
Long: `
|
||||
Display available gcloud access tokens:
|
||||
./cloudfox gcp access-tokens`,
|
||||
Run: runGCPAccessTokensCommand,
|
||||
}
|
||||
|
||||
GCPBucketsCommand = &cobra.Command{
|
||||
Use: "buckets",
|
||||
Aliases: []string{},
|
||||
Short: "Display GCP bucket information",
|
||||
Args: cobra.MinimumNArgs(0),
|
||||
Long: `
|
||||
Display available bucket information:
|
||||
./cloudfox gcp buckets [project [project...]]`,
|
||||
Run: runGCPBucketsCommand,
|
||||
}
|
||||
|
||||
HierarchyIncludeIDs bool
|
||||
GCPHierarchyCommand = &cobra.Command{
|
||||
Use: "hierarchy",
|
||||
Aliases: []string{},
|
||||
Short: "Display GCP resources hierarchy",
|
||||
Long: `
|
||||
Display available resources' hierarchy:
|
||||
./cloudfox gcp hierarchy`,
|
||||
Run: runGCPHierarchyCommand,
|
||||
}
|
||||
)
|
||||
|
||||
func initGCPProfiles() {
|
||||
// bypass profile selection logic for commands that don't require authentication
|
||||
if GCPAccessTokensCommand.CalledAs() != "" {
|
||||
return
|
||||
} else if GCPAccessTokensCommand.CalledAs() != "" {
|
||||
return
|
||||
}
|
||||
// Ensure that profile selection is consistent
|
||||
if (len(GCPProfiles) != 0 || GCPProfilesList != "") && GCPAllProfiles {
|
||||
GCPLogger.Fatal("Error specifying GCP profiles. Choose only one of -p/--profile, -a/--all-profiles, -l/--profiles-list")
|
||||
|
||||
}
|
||||
// at this point, GCPProfiles only contains the profiles submitted over the CLI
|
||||
|
||||
// if the --all-profiles option is submitted, replacing the profiles with all existing ones
|
||||
if GCPAllProfiles {
|
||||
GCPProfiles = internal_gcp.GetAllGCPProfiles()
|
||||
}
|
||||
|
||||
// if --profiles-list option is submitted, adding the profiles from the list to the existing ones
|
||||
if GCPProfilesList != "" {
|
||||
GCPProfiles = append(GCPProfiles, internal_gcp.GetSelectedGCPProfiles(GCPProfilesList)...)
|
||||
}
|
||||
|
||||
// remove duplicate
|
||||
GCPProfiles = internal.RemoveDuplicateStr(GCPProfiles)
|
||||
|
||||
if (len(GCPProfiles) == 0) {
|
||||
GCPLogger.Info("No GCP profile selection submitted, trying to find the default profile...")
|
||||
var allProfiles = internal_gcp.GetAllGCPProfiles()
|
||||
if len(allProfiles) > 0 {
|
||||
GCPProfiles = append(GCPProfiles, allProfiles[len(allProfiles) - 1])
|
||||
} else {
|
||||
GCPLogger.Fatal("Could not find any usable GCP profile")
|
||||
}
|
||||
}
|
||||
|
||||
if !GCPConfirm {
|
||||
result := internal_gcp.ConfirmSelectedProfiles(GCPProfiles)
|
||||
if !result {
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func runGCPHierarchyCommand(cmd *cobra.Command, args []string) {
|
||||
for _, profile := range GCPProfiles {
|
||||
var client = internal_gcp.NewGCPClient(profile)
|
||||
m := gcp.HierarchyModule{
|
||||
Organizations: GCPOrganizations,
|
||||
Projects: GCPProjectIDs,
|
||||
Folders: GCPFolderIDs,
|
||||
Client: *client,
|
||||
}
|
||||
err := m.DisplayHierarchy(GCPTreeFormat)
|
||||
if err != nil {
|
||||
GCPLogger.ErrorM(fmt.Sprintf("Error running %s module on profile %s", globals.GCP_HIERARCHY_MODULE_NAME, profile), globals.GCP_HIERARCHY_MODULE_NAME)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func runGCPBucketsCommand(cmd *cobra.Command, args []string) {
|
||||
for _, profile := range GCPProfiles {
|
||||
var client = internal_gcp.NewGCPClient(profile)
|
||||
m := gcp.BucketsModule{
|
||||
Organizations: GCPOrganizations,
|
||||
Projects: GCPProjectIDs,
|
||||
Folders: GCPFolderIDs,
|
||||
Client: *client,
|
||||
}
|
||||
err := m.ListBuckets(args)
|
||||
if err != nil {
|
||||
GCPLogger.ErrorM(fmt.Sprintf("Error running %s module on profile %s", globals.GCP_BUCKETS_MODULE_NAME, profile), globals.GCP_BUCKETS_MODULE_NAME)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func runGCPAccessTokensCommand(cmd *cobra.Command, args []string) {
|
||||
m := gcp.AccessTokensModule{
|
||||
Organizations: GCPOrganizations,
|
||||
Projects: GCPProjectIDs,
|
||||
Folders: GCPFolderIDs,
|
||||
}
|
||||
err := m.PrintAccessTokens(GCPOutputFormat, GCPOutputDirectory, Verbosity)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func runGCPInventoryCommand(cmd *cobra.Command, args []string) {
|
||||
for _, profile := range GCPProfiles {
|
||||
var client = internal_gcp.NewGCPClient(profile)
|
||||
m := gcp.InventoryModule{
|
||||
Organizations: GCPOrganizations,
|
||||
Projects: GCPProjectIDs,
|
||||
Folders: GCPFolderIDs,
|
||||
Client: *client,
|
||||
}
|
||||
err := m.PrintInventory(GCPOutputFormat, GCPOutputDirectory, Verbosity)
|
||||
if err != nil {
|
||||
GCPLogger.ErrorM(fmt.Sprintf("Error running %s module on profile %s: %v", globals.GCP_INVENTORY_MODULE_NAME, profile, err), globals.GCP_INVENTORY_MODULE_NAME)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
cobra.OnInitialize(initGCPProfiles)
|
||||
// Globals flags for the GCP modules
|
||||
|
||||
GCPCommands.PersistentFlags().StringArrayVar(&GCPProfiles, "profile", []string{}, "GCloud CLI Profile Name")
|
||||
GCPCommands.PersistentFlags().StringVarP(&GCPProfilesList, "profiles-list", "l", "", "File containing a list of GCP CLI profile names separated by newlines")
|
||||
GCPCommands.PersistentFlags().BoolVarP(&GCPAllProfiles, "all-profiles", "a", false, "Use all available and valid GCP CLI profiles")
|
||||
|
||||
// Resource filtering options
|
||||
GCPCommands.PersistentFlags().StringArrayVarP(&GCPOrganizations, "organization", "o", []string{}, "Organization name or number, repetable")
|
||||
GCPCommands.PersistentFlags().StringArrayVarP(&GCPProjectIDs, "projectid", "p", []string{}, "Project slug or number, repeatable")
|
||||
GCPCommands.PersistentFlags().StringArrayVarP(&GCPFolderIDs, "folderid", "f", []string{}, "Folder name or number, repeatable")
|
||||
|
||||
// Global flags for the GCP modules
|
||||
GCPCommands.PersistentFlags().BoolVarP(&GCPConfirm, "yes", "y", false, "Non-interactive mode (like apt/yum)")
|
||||
GCPCommands.PersistentFlags().StringVarP(&GCPOutputFormat, "output", "", "brief", "[\"brief\" | \"wide\" ]")
|
||||
GCPCommands.PersistentFlags().IntVarP(&Verbosity, "verbosity", "v", 1, "1 = Print control messages only\n2 = Print control messages, module output\n3 = Print control messages, module output, and loot file output\n")
|
||||
GCPCommands.PersistentFlags().StringVar(&GCPOutputDirectory, "outdir", defaultOutputDir, "Output Directory ")
|
||||
GCPCommands.PersistentFlags().IntVarP(&Goroutines, "max-goroutines", "g", 30, "Maximum number of concurrent goroutines")
|
||||
GCPCommands.PersistentFlags().BoolVar(&GCPSkipAdminCheck, "skip-admin-check", false, "Skip check to determine if role is an Admin")
|
||||
GCPCommands.PersistentFlags().BoolVarP(&GCPWrapTable, "wrap", "w", false, "Wrap table to fit in terminal (complicates grepping)")
|
||||
GCPCommands.PersistentFlags().BoolVar(&GCPIgnoreCache, "ignore-cache", false, "Disable loading of cached data. Slower, but important if changes have been recently made")
|
||||
|
||||
GCPHierarchyCommand.Flags().BoolVarP(&HierarchyIncludeIDs, "ids", "i", false, "Use this flag to display resources IDs in the hierarchy tree")
|
||||
GCPHierarchyCommand.Flags().StringVar(&GCPTreeFormat, "tree", "horizontal", "[\"horizontal\" | \"vertical\" ]")
|
||||
|
||||
GCPCommands.AddCommand(
|
||||
GCPAccessTokensCommand,
|
||||
GCPHierarchyCommand,
|
||||
GCPInventoryCommand,
|
||||
GCPBucketsCommand,
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package gcp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
"github.com/fatih/color"
|
||||
"github.com/BishopFox/cloudfox/internal/gcp"
|
||||
"github.com/BishopFox/cloudfox/internal"
|
||||
"github.com/BishopFox/cloudfox/globals"
|
||||
)
|
||||
|
||||
type AccessTokensModule struct {
|
||||
// Filtering data
|
||||
Organizations []string
|
||||
Folders []string
|
||||
Projects []string
|
||||
}
|
||||
|
||||
func (m *AccessTokensModule) PrintAccessTokens(outputFormat string, outputDirectory string, verbosity int) error {
|
||||
GCPLogger.InfoM(fmt.Sprintf("Enumerating GCP local access tokens (%s, %s)...", color.CyanString("default user token"), color.RedString("application-default token")), globals.GCP_ACCESSTOKENS_MODULE_NAME)
|
||||
tokens := gcp.ReadRefreshTokens()
|
||||
accessTokens := gcp.ReadAccessTokens()
|
||||
applicationdefaulthash := gcp.GetDefaultApplicationHash()
|
||||
activeAccount := gcp.GetActiveAccount()
|
||||
|
||||
var tableHead = []string{"Account", "Credential Type", "Validity", "Refresh Token", "Refresh Token needs password"}
|
||||
var tableBody [][]string
|
||||
for _, accessToken := range accessTokens {
|
||||
// prepare token's associated ID (email or hash)
|
||||
var emailinfo string
|
||||
if (accessToken.AccountID == activeAccount) {
|
||||
emailinfo = color.BlueString(accessToken.AccountID)
|
||||
} else {
|
||||
emailinfo = accessToken.AccountID
|
||||
}
|
||||
|
||||
// format expiration time
|
||||
Exp, _ := time.Parse(time.RFC3339, accessToken.TokenExpiry)
|
||||
var timeinfo string
|
||||
if Exp.After(time.Now()) {
|
||||
timeinfo = Exp.Format(time.RFC1123)
|
||||
} else {
|
||||
timeinfo = "EXPIRED"
|
||||
}
|
||||
|
||||
// display token type, user or application credential
|
||||
var tokentype string
|
||||
if (strings.Contains(accessToken.AccountID, "@")) {
|
||||
tokentype = "User"
|
||||
} else {
|
||||
tokentype = "Application"
|
||||
}
|
||||
// is there an associated refres htoken ?
|
||||
var refreshtokeninfo string
|
||||
refreshtokeninfo = "No"
|
||||
for _, refreshtoken := range tokens {
|
||||
if (accessToken.AccountID == refreshtoken.Email) {
|
||||
refreshtokeninfo = "Yes"
|
||||
} else if (accessToken.AccountID == applicationdefaulthash) {
|
||||
refreshtokeninfo = "Yes"
|
||||
emailinfo = color.RedString(emailinfo)
|
||||
}
|
||||
}
|
||||
|
||||
// is there a proof of reauthentication to use the refresh
|
||||
// token ? If not, password is needed
|
||||
var raptinfo string
|
||||
if refreshtokeninfo == "No" {
|
||||
raptinfo = "-"
|
||||
} else if accessToken.RaptToken.Valid {
|
||||
raptinfo = "No"
|
||||
} else {
|
||||
if (tokentype == "Application") {
|
||||
raptinfo = "TBD"
|
||||
} else {
|
||||
raptinfo = "Yes"
|
||||
}
|
||||
}
|
||||
tableBody = append(
|
||||
tableBody,
|
||||
[]string{
|
||||
emailinfo,
|
||||
tokentype,
|
||||
timeinfo,
|
||||
refreshtokeninfo,
|
||||
raptinfo,
|
||||
})
|
||||
}
|
||||
internal.PrintTableToScreen(tableHead, tableBody, false)
|
||||
return nil
|
||||
}
|
||||
+134
@@ -0,0 +1,134 @@
|
||||
package gcp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"fmt"
|
||||
"sort"
|
||||
"github.com/BishopFox/cloudfox/globals"
|
||||
"github.com/BishopFox/cloudfox/internal/gcp"
|
||||
"github.com/BishopFox/cloudfox/internal"
|
||||
"google.golang.org/api/option"
|
||||
|
||||
"cloud.google.com/go/storage"
|
||||
"google.golang.org/api/iterator"
|
||||
)
|
||||
|
||||
var (
|
||||
GCPLogger = internal.NewLogger()
|
||||
)
|
||||
|
||||
type BucketsModule struct {
|
||||
Client gcp.GCPClient
|
||||
StorageClient *storage.Client
|
||||
|
||||
// Filtering data
|
||||
Organizations []string
|
||||
Folders []string
|
||||
Projects []string
|
||||
}
|
||||
|
||||
type ObjectInfo struct {
|
||||
ObjectName string `json:"objecttName"`
|
||||
ObjectSizeBytes float64 `json:"objectSizeBytes"`
|
||||
IsPublic bool `json:"isPublic"`
|
||||
}
|
||||
|
||||
type BucketInfo struct {
|
||||
BucketName string `json:"bucketName"`
|
||||
BucketSizeMB float64 `json:"bucketSizeMB"`
|
||||
Objects []ObjectInfo `json:"objectInfo"`
|
||||
}
|
||||
|
||||
func (m *BucketsModule) ListBuckets(projects []string) error {
|
||||
// retrieve all resource and do filtering
|
||||
m.Client.GetResourcesRoots(m.Organizations, m.Folders, m.Projects)
|
||||
GCPLogger.InfoM(fmt.Sprintf("Enumerating GCP buckets with account %s...", m.Client.Name), globals.GCP_BUCKETS_MODULE_NAME)
|
||||
|
||||
ctx := context.Background()
|
||||
var err error
|
||||
m.StorageClient, err = storage.NewClient(ctx, option.WithHTTPClient(m.Client.HTTPClient))
|
||||
|
||||
if err != nil {
|
||||
GCPLogger.ErrorM("Could not create GCP storage client...", globals.GCP_BUCKETS_MODULE_NAME)
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
if len(projects) > 0 {
|
||||
for _, project := range projects {
|
||||
m.ListBucketsInProject(project)
|
||||
}
|
||||
} else {
|
||||
for _, project := range m.Client.Projects {
|
||||
m.ListBucketsInProject(project)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
func (m *BucketsModule) ListBucketsInProject(project string) error {
|
||||
bucketInfos := make(map[string][]BucketInfo)
|
||||
ctx := context.Background()
|
||||
GCPLogger.InfoM(fmt.Sprintf("Iterating through project %s...", project), globals.GCP_BUCKETS_MODULE_NAME)
|
||||
buckets := m.StorageClient.Buckets(ctx, project)
|
||||
for {
|
||||
bucketAttrs, err := buckets.Next()
|
||||
if err == iterator.Done {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
GCPLogger.ErrorM(fmt.Sprintf("Failed to list buckets : %v", err), globals.GCP_BUCKETS_MODULE_NAME)
|
||||
return err
|
||||
}
|
||||
|
||||
bucketName := bucketAttrs.Name
|
||||
GCPLogger.InfoM(fmt.Sprintf("Enumerating bucket %s...", bucketName), globals.GCP_BUCKETS_MODULE_NAME)
|
||||
|
||||
// List all objects in the bucket and calculate total size
|
||||
totalSize := int64(0)
|
||||
var objects []ObjectInfo
|
||||
it := m.StorageClient.Bucket(bucketName).Objects(ctx, nil)
|
||||
for {
|
||||
objectAttrs, err := it.Next()
|
||||
if err == iterator.Done {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to list objects: %v", err)
|
||||
GCPLogger.ErrorM(fmt.Sprintf("Failed to list objects : %v", err), globals.GCP_BUCKETS_MODULE_NAME)
|
||||
break
|
||||
}
|
||||
|
||||
// Get size
|
||||
objectSize := objectAttrs.Size
|
||||
totalSize += objectSize
|
||||
|
||||
// Check if public
|
||||
isPublic := false
|
||||
for _, rule := range objectAttrs.ACL {
|
||||
if rule.Entity == storage.AllUsers {
|
||||
isPublic = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
objects = append(objects, ObjectInfo{ObjectName: objectAttrs.Name, ObjectSizeBytes: float64(objectSize), IsPublic: isPublic})
|
||||
|
||||
if totalSize > 3221225472 { // 3 GiB in bytes
|
||||
GCPLogger.InfoM(fmt.Sprintf("%s bucket is over 3 GiB. Skipping remaining objects in this bucket...", bucketName), globals.GCP_BUCKETS_MODULE_NAME)
|
||||
break
|
||||
}
|
||||
}
|
||||
bucketSizeMB := float64(totalSize) / 1024 / 1024
|
||||
bucketInfos[project] = append(bucketInfos[project], BucketInfo{BucketName: bucketName, BucketSizeMB: bucketSizeMB, Objects: objects})
|
||||
}
|
||||
log.Printf("Sorting resulting list of buckets in descending order %s", project)
|
||||
sort.Slice(bucketInfos[project], func(i, j int) bool {
|
||||
return bucketInfos[project][i].BucketSizeMB > bucketInfos[project][j].BucketSizeMB
|
||||
})
|
||||
|
||||
log.Printf("Done with project %s", project)
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package gcp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/BishopFox/cloudfox/internal/gcp"
|
||||
"github.com/BishopFox/cloudfox/globals"
|
||||
|
||||
// tree stuff
|
||||
"github.com/shivamMg/ppds/tree"
|
||||
)
|
||||
|
||||
type HierarchyModule struct {
|
||||
Client gcp.GCPClient
|
||||
|
||||
// Filtering data
|
||||
Organizations []string
|
||||
Folders []string
|
||||
Projects []string
|
||||
}
|
||||
|
||||
|
||||
func (m *HierarchyModule) DisplayHierarchy(format string) error {
|
||||
GCPLogger.InfoM(fmt.Sprintf("Fetching GCP resources and hierarchy with account %s...", m.Client.Name), globals.GCP_HIERARCHY_MODULE_NAME)
|
||||
var roots = m.Client.GetResourcesRoots(m.Organizations, m.Folders, m.Projects)
|
||||
|
||||
for _, root := range roots {
|
||||
if (format == "horizontal") {
|
||||
tree.PrintHr(root)
|
||||
} else if (format == "vertical") {
|
||||
tree.Print(root)
|
||||
} else {
|
||||
GCPLogger.ErrorM(fmt.Sprintf("Unknown tree format '%s'", format), globals.GCP_HIERARCHY_MODULE_NAME)
|
||||
}
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package gcp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/BishopFox/cloudfox/internal/gcp"
|
||||
"github.com/BishopFox/cloudfox/globals"
|
||||
)
|
||||
|
||||
type InventoryModule struct {
|
||||
Client gcp.GCPClient
|
||||
// Filtering data
|
||||
Organizations []string
|
||||
Folders []string
|
||||
Projects []string
|
||||
}
|
||||
|
||||
func (m *InventoryModule) PrintInventory(outputFormat string, outputDirectory string, verbosity int) error {
|
||||
GCPLogger.InfoM(fmt.Sprintf("Enumerating GCP resources with account %s...", m.Client.Name), globals.GCP_INVENTORY_MODULE_NAME)
|
||||
for _, project := range m.Projects {
|
||||
searchAllResponse, err := m.Client.ResourcesService.SearchAll(fmt.Sprintf("projects/%s", project)).Do()
|
||||
if (err != nil) {
|
||||
GCPLogger.ErrorM(fmt.Sprintf("Could not list resource within scope: %v", err), globals.GCP_INVENTORY_MODULE_NAME)
|
||||
continue
|
||||
}
|
||||
for _, resource := range searchAllResponse.Results {
|
||||
fmt.Printf("%s - %s\n", resource.DisplayName, resource.AssetType)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package globals
|
||||
|
||||
// Module names
|
||||
const GCP_WHOAMI_MODULE_NAME = "whoami"
|
||||
const GCP_HIERARCHY_MODULE_NAME = "hierarchy"
|
||||
const GCP_INVENTORY_MODULE_NAME = "inventory"
|
||||
const GCP_ACCESSTOKENS_MODULE_NAME = "access-tokens"
|
||||
const GCP_BUCKETS_MODULE_NAME = "buckets"
|
||||
const GCP_GCLOUD_REFRESH_TOKENS_DB_PATH = ".config/gcloud/credentials.db"
|
||||
const GCP_GCLOUD_ACCESS_TOKENS_DB_PATH = ".config/gcloud/access_tokens.db"
|
||||
const GCP_GCLOUD_DEFAULT_CONFIG_PATH = ".config/gcloud/configurations/config_default"
|
||||
const GCP_GCLOUD_APPLICATION_DEFAULT_PATH = ".config/gcloud/application_default_credentials.json"
|
||||
@@ -4,3 +4,4 @@ const CLOUDFOX_USER_AGENT = "cloudfox"
|
||||
const CLOUDFOX_LOG_FILE_DIR_NAME = ".cloudfox"
|
||||
const CLOUDFOX_BASE_DIRECTORY = ".cloudfox/cloudfox-output"
|
||||
const LOOT_DIRECTORY_NAME = "loot"
|
||||
const CLOUDFOX_VERSION = "1.11.2"
|
||||
@@ -3,6 +3,8 @@ module github.com/BishopFox/cloudfox
|
||||
go 1.20
|
||||
|
||||
require (
|
||||
cloud.google.com/go/resourcemanager v1.7.0
|
||||
cloud.google.com/go/storage v1.28.1
|
||||
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.1.1
|
||||
@@ -56,10 +58,52 @@ require (
|
||||
github.com/jedib0t/go-pretty v4.3.0+incompatible
|
||||
github.com/kyokomi/emoji v2.2.4+incompatible
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible
|
||||
github.com/shivamMg/ppds v0.0.1
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
github.com/spf13/afero v1.9.5
|
||||
github.com/spf13/cobra v1.7.0
|
||||
golang.org/x/crypto v0.9.0
|
||||
golang.org/x/oauth2 v0.8.0
|
||||
google.golang.org/api v0.127.0
|
||||
gopkg.in/ini.v1 v1.67.0
|
||||
modernc.org/sqlite v1.23.1
|
||||
)
|
||||
|
||||
require (
|
||||
cloud.google.com/go v0.110.0 // indirect
|
||||
cloud.google.com/go/compute v1.19.3 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.2.3 // indirect
|
||||
cloud.google.com/go/iam v0.13.0 // indirect
|
||||
cloud.google.com/go/longrunning v0.4.1 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/google/go-cmp v0.5.9 // indirect
|
||||
github.com/google/s2a-go v0.1.4 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.2.4 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.10.0 // indirect
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.17 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
golang.org/x/mod v0.10.0 // indirect
|
||||
golang.org/x/tools v0.9.1 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc // indirect
|
||||
google.golang.org/grpc v1.55.0 // indirect
|
||||
google.golang.org/protobuf v1.30.0 // indirect
|
||||
lukechampine.com/uint128 v1.2.0 // indirect
|
||||
modernc.org/cc/v3 v3.40.0 // indirect
|
||||
modernc.org/ccgo/v3 v3.16.13 // indirect
|
||||
modernc.org/libc v1.22.5 // indirect
|
||||
modernc.org/mathutil v1.5.0 // indirect
|
||||
modernc.org/memory v1.5.0 // indirect
|
||||
modernc.org/opt v0.1.3 // indirect
|
||||
modernc.org/strutil v1.1.3 // indirect
|
||||
modernc.org/token v1.0.1 // indirect
|
||||
)
|
||||
|
||||
require (
|
||||
|
||||
@@ -17,24 +17,38 @@ cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHOb
|
||||
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.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY=
|
||||
cloud.google.com/go v0.110.0 h1:Zc8gqp3+a9/Eyph2KDmcGaPtbKRIoqq4YTlL4NMD0Ys=
|
||||
cloud.google.com/go v0.110.0/go.mod h1:SJnCLqQ0FCFGSZMUNUf84MV3Aia54kn7pi8st7tMzaY=
|
||||
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/compute v1.19.3 h1:DcTwsFgGev/wV5+q8o2fzgcHOaac+DKGC91ZlvpsQds=
|
||||
cloud.google.com/go/compute v1.19.3/go.mod h1:qxvISKp/gYnXkSAD1ppcSOveRAmzxicEv/JlizULFrI=
|
||||
cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY=
|
||||
cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
|
||||
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/iam v0.13.0 h1:+CmB+K0J/33d0zSQ9SlFWUeCCEn5XJA0ZMZ3pHE9u8k=
|
||||
cloud.google.com/go/iam v0.13.0/go.mod h1:ljOg+rcNfzZ5d6f1nAUJ8ZIxOaZUVoS14bKCtaLZ/D0=
|
||||
cloud.google.com/go/longrunning v0.4.1 h1:v+yFJOfKC3yZdY6ZUI933pIYdhyhV8S3NpWrXWmg7jM=
|
||||
cloud.google.com/go/longrunning v0.4.1/go.mod h1:4iWDqhBZ70CvZ6BfETbvam3T8FMvLK+eFj0E6AaRQTo=
|
||||
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/resourcemanager v1.7.0 h1:NRM0p+RJkaQF9Ee9JMnUV9BQ2QBIOq/v8M+Pbv/wmCs=
|
||||
cloud.google.com/go/resourcemanager v1.7.0/go.mod h1:HlD3m6+bwhzj9XCouqmeiGuni95NTrExfhoSrkC/3EI=
|
||||
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=
|
||||
cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
|
||||
cloud.google.com/go/storage v1.28.1 h1:F5QDG5ChchaAVQhINh24U99OWHURqrW8OmQcGKXcbgI=
|
||||
cloud.google.com/go/storage v1.28.1/go.mod h1:Qnisd4CqDdo6BGs2AD5LLnEsmSQ80wQ5ogcBBKhU86Y=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU=
|
||||
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
|
||||
@@ -81,6 +95,7 @@ github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0 h1:OBhqkivkhkM
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0/go.mod h1:kgDmCTgBzIEPFElEF+FK0SdjAor06dRq2Go927dnQ6o=
|
||||
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/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
||||
github.com/aquasecurity/table v1.8.0 h1:9ntpSwrUfjrM6/YviArlx/ZBGd6ix8W+MtojQcM7tv0=
|
||||
github.com/aquasecurity/table v1.8.0/go.mod h1:eqOmvjjB7AhXFgFqpJUEE/ietg7RrMSJZXyTN8E/wZw=
|
||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so=
|
||||
@@ -200,6 +215,7 @@ github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J
|
||||
github.com/bishopfox/awsservicemap v1.0.2 h1:+NnuInVcQkja6y6h6eGseneU3m5Nzp9O++gR1s+FiyM=
|
||||
github.com/bishopfox/awsservicemap v1.0.2/go.mod h1:oy9Fyqh6AozQjShSx+zRNouTlp7k3z3YEMoFkN8rquc=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
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=
|
||||
@@ -207,6 +223,10 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk
|
||||
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/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
|
||||
github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
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=
|
||||
@@ -217,14 +237,18 @@ github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/
|
||||
github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI=
|
||||
github.com/dominikbraun/graph v0.22.2 h1:rFtrufgqXJy7daEMTxHIQWQc12Y1XHelXi2Heo1Bfho=
|
||||
github.com/dominikbraun/graph v0.22.2/go.mod h1:yOjYyogZLY1LSG9E33JWZJiq5k83Qy2C6POAuiViluc=
|
||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
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.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
|
||||
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
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=
|
||||
@@ -240,6 +264,8 @@ github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfU
|
||||
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/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/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=
|
||||
@@ -261,6 +287,10 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq
|
||||
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.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/snappy v0.0.1/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=
|
||||
@@ -272,12 +302,17 @@ github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
||||
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.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no=
|
||||
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.3.2 h1:IqNFLAmvJOgVlpdEBiQbDc2EwKW77amAycfTuWKdfvw=
|
||||
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=
|
||||
@@ -288,13 +323,21 @@ github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hf
|
||||
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-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/s2a-go v0.1.4 h1:1kZ/sQM3srePvKs3tXAvQzo66XfcReoqFpIpIccE7Oc=
|
||||
github.com/google/s2a-go v0.1.4/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.2.4 h1:uGy6JWR/uMIILU8wbf+OkstIrNiMjGpEIyhx8f6W7s4=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.2.4/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k=
|
||||
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.10.0 h1:ebSgKfMxynOdxw8QQuFOKMgomqeLGPqNLQox2bo42zg=
|
||||
github.com/googleapis/gax-go/v2 v2.10.0/go.mod h1:4UOEnMCrxsSqQ940WnTiD6qJ63le2ev3xfyagutxiPw=
|
||||
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
||||
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/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
@@ -309,6 +352,8 @@ github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGw
|
||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
|
||||
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/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
||||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||
@@ -327,6 +372,8 @@ github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APP
|
||||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
|
||||
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mattn/go-sqlite3 v1.14.17 h1:mCRHCLDUBXgpKAqIKsaAaAsrAlbkeomtRFKXh2L6YIM=
|
||||
github.com/mattn/go-sqlite3 v1.14.17/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||
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/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
@@ -344,11 +391,17 @@ github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qR
|
||||
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/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
|
||||
github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
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/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/shivamMg/ppds v0.0.1 h1:idK2dpaen652zOO+OmcwmyoPNncBNqfHjF/14eS5JIk=
|
||||
github.com/shivamMg/ppds v0.0.1/go.mod h1:hb39VqUO6qfkb9zBBQPTIV1vWBtI7yQsG0wr3pN78fM=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM=
|
||||
@@ -367,6 +420,7 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
|
||||
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4=
|
||||
@@ -388,6 +442,9 @@ 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.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
|
||||
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
||||
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
||||
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=
|
||||
@@ -396,6 +453,7 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
|
||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
|
||||
@@ -435,6 +493,8 @@ 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.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk=
|
||||
golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
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=
|
||||
@@ -463,6 +523,7 @@ golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81R
|
||||
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-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
@@ -480,6 +541,8 @@ golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ
|
||||
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.8.0 h1:6dkIjl3j3LtZ/O3sTgZTMsLKSftL/B8Zgq4huOIIUu8=
|
||||
golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE=
|
||||
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=
|
||||
@@ -492,6 +555,7 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/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=
|
||||
@@ -549,6 +613,7 @@ 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.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
@@ -603,10 +668,14 @@ golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4f
|
||||
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.9.1 h1:8WMNJAz3zrtPmnYC7ISf5dEn3MT0gY7jBJfw27yrrLo=
|
||||
golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc=
|
||||
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/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk=
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
|
||||
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=
|
||||
@@ -626,12 +695,15 @@ google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz513
|
||||
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.127.0 h1:v7rj0vA0imM3Ou81k1eyFxQNScLzn71EyGnJDr+V/XI=
|
||||
google.golang.org/api v0.127.0/go.mod h1:Y611qgqaE92On/7g65MQgxYul3c0rEB894kniWLY750=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
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=
|
||||
@@ -656,6 +728,7 @@ google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfG
|
||||
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=
|
||||
@@ -669,6 +742,12 @@ google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6D
|
||||
google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc h1:8DyZCyvI8mE1IdLy/60bS+52xfymkE72wv1asokgtao=
|
||||
google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:xZnkP7mREFX5MORlOPEzLMr+90PPZQ2QWzrVTWfAq64=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc h1:kVKPf/IiYSBWEWtkIn6wZXwWGCnLKcC8oWfZvXjsGnM=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc h1:XSJ8Vk1SWuNr8S18z1NZSziL0CPIXLCCMDOEFtHBOFc=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA=
|
||||
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=
|
||||
@@ -682,9 +761,14 @@ google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3Iji
|
||||
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.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ=
|
||||
google.golang.org/grpc v1.55.0 h1:3Oj82/tFSCeUrRTg/5E/7d/W5A1tj6Ky1ABAuZuv5ag=
|
||||
google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8=
|
||||
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=
|
||||
@@ -695,11 +779,18 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2
|
||||
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.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
|
||||
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
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-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
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.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
@@ -712,6 +803,30 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
|
||||
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=
|
||||
lukechampine.com/uint128 v1.2.0 h1:mBi/5l91vocEN8otkC5bDLhi2KdCticRiwbdB0O+rjI=
|
||||
lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
|
||||
modernc.org/cc/v3 v3.40.0 h1:P3g79IUS/93SYhtoeaHW+kRCIrYaxJ27MFPv+7kaTOw=
|
||||
modernc.org/cc/v3 v3.40.0/go.mod h1:/bTg4dnWkSXowUO6ssQKnOV0yMVxDYNIsIrzqTFDGH0=
|
||||
modernc.org/ccgo/v3 v3.16.13 h1:Mkgdzl46i5F/CNR/Kj80Ri59hC8TKAhZrYSaqvkwzUw=
|
||||
modernc.org/ccgo/v3 v3.16.13/go.mod h1:2Quk+5YgpImhPjv2Qsob1DnZ/4som1lJTodubIcoUkY=
|
||||
modernc.org/ccorpus v1.11.6 h1:J16RXiiqiCgua6+ZvQot4yUuUy8zxgqbqEEUuGPlISk=
|
||||
modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM=
|
||||
modernc.org/libc v1.22.5 h1:91BNch/e5B0uPbJFgqbxXuOnxBQjlS//icfQEGmvyjE=
|
||||
modernc.org/libc v1.22.5/go.mod h1:jj+Z7dTNX8fBScMVNRAYZ/jF91K8fdT2hYMThc3YjBY=
|
||||
modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ=
|
||||
modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
|
||||
modernc.org/memory v1.5.0 h1:N+/8c5rE6EqugZwHii4IFsaJ7MUhoWX07J5tC/iI5Ds=
|
||||
modernc.org/memory v1.5.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
|
||||
modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4=
|
||||
modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
|
||||
modernc.org/sqlite v1.23.1 h1:nrSBg4aRQQwq59JpvGEQ15tNxoO5pX/kUjcRNwSAGQM=
|
||||
modernc.org/sqlite v1.23.1/go.mod h1:OrDj17Mggn6MhE+iPbBNf7RGKODDE9NFT0f3EwDzJqk=
|
||||
modernc.org/strutil v1.1.3 h1:fNMm+oJklMGYfU9Ylcywl0CO5O6nTfaowNsh2wpPjzY=
|
||||
modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw=
|
||||
modernc.org/tcl v1.15.2 h1:C4ybAYCGJw968e+Me18oW55kD/FexcHbqH2xak1ROSY=
|
||||
modernc.org/token v1.0.1 h1:A3qvTqOwexpfZZeyI0FeGPDlSWX5pjZu9hF4lU+EKWg=
|
||||
modernc.org/token v1.0.1/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
||||
modernc.org/z v1.7.3 h1:zDJf6iHjrnB+WRD88stbXokugjyc0/pB91ri1gO6LZY=
|
||||
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=
|
||||
@@ -31,3 +31,15 @@ func Contains(element string, array []string) bool {
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func RemoveDuplicateStr(strSlice []string) []string {
|
||||
allKeys := make(map[string]bool)
|
||||
list := []string{}
|
||||
for _, item := range strSlice {
|
||||
if _, value := allKeys[item]; !value {
|
||||
allKeys[item] = true
|
||||
list = append(list, item)
|
||||
}
|
||||
}
|
||||
return list
|
||||
}
|
||||
@@ -0,0 +1,238 @@
|
||||
package gcp
|
||||
|
||||
import (
|
||||
"log"
|
||||
"database/sql"
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
"encoding/json"
|
||||
_ "modernc.org/sqlite"
|
||||
|
||||
"golang.org/x/oauth2"
|
||||
"golang.org/x/oauth2/google"
|
||||
//"google.golang.org/api/option"
|
||||
//goauth2 "google.golang.org/api/oauth2/v2"
|
||||
//"google.golang.org/api/cloudresourcemanager/v1"
|
||||
//"google.golang.org/api/cloudasset/v1p1beta1"
|
||||
"os"
|
||||
"io/ioutil"
|
||||
"os/user"
|
||||
"gopkg.in/ini.v1"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/BishopFox/cloudfox/globals"
|
||||
//"github.com/aws/smithy-go/ptr"
|
||||
)
|
||||
|
||||
type ApplicationCredential struct {
|
||||
ClientID string `json:"client_id"`
|
||||
ClientSecret string `json:"client_secret"`
|
||||
RefreshToken string `json:"refresh_token"`
|
||||
Type string `json:"authorized_user"`
|
||||
Scopes []string `json:"scopes"`
|
||||
}
|
||||
|
||||
type RefreshToken struct {
|
||||
Email string
|
||||
JSON string
|
||||
}
|
||||
// CREATE TABLE IF NOT EXISTS "access_tokens" (account_id TEXT PRIMARY KEY, access_token TEXT, token_expiry TIMESTAMP, rapt_token TEXT, id_token TEXT);
|
||||
|
||||
type Token struct {
|
||||
AccountID string
|
||||
AccessToken string
|
||||
TokenExpiry string
|
||||
RaptToken sql.NullString
|
||||
IdentityToken string
|
||||
}
|
||||
|
||||
type GCloudProfile struct {
|
||||
Name string
|
||||
oauth_conf oauth2.Config
|
||||
initial_token oauth2.Token
|
||||
}
|
||||
func listAllProfiles() []GCloudProfile {
|
||||
var (
|
||||
profiles []GCloudProfile
|
||||
access_tokens []Token
|
||||
refresh_tokens []RefreshToken
|
||||
exp time.Time
|
||||
data ApplicationCredential
|
||||
)
|
||||
access_tokens = ReadAccessTokens()
|
||||
refresh_tokens = ReadRefreshTokens()
|
||||
for _, access_token := range access_tokens {
|
||||
if (strings.Contains(access_token.AccountID, "@")) {
|
||||
exp, _ = time.Parse(time.RFC3339, access_token.TokenExpiry)
|
||||
for _, refresh_token := range refresh_tokens {
|
||||
if (refresh_token.Email == access_token.AccountID) {
|
||||
json.Unmarshal([]byte(refresh_token.JSON), &data)
|
||||
profiles = append(
|
||||
profiles,
|
||||
GCloudProfile{
|
||||
Name: access_token.AccountID,
|
||||
oauth_conf: oauth2.Config{
|
||||
ClientID: data.ClientID,
|
||||
ClientSecret: data.ClientSecret,
|
||||
Scopes: data.Scopes,
|
||||
Endpoint: google.Endpoint,
|
||||
},
|
||||
initial_token: oauth2.Token{
|
||||
AccessToken: access_token.AccessToken,
|
||||
Expiry: exp,
|
||||
},
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return profiles
|
||||
|
||||
}
|
||||
/* The active gcloud CLI profile is stored in INI configuration file */
|
||||
func GetActiveAccount() string {
|
||||
configPath := GetDefaultConfigPath()
|
||||
cfg, err := ini.Load(configPath)
|
||||
if err != nil {
|
||||
fmt.Printf("Fail to read %s: %v", configPath, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
account := cfg.Section("core").Key("account").String()
|
||||
return account
|
||||
}
|
||||
|
||||
/* Access tokens are stored in the access_tokens.db database, along with the user ID
|
||||
*/
|
||||
func ReadAccessTokens() []Token{
|
||||
db, err := sql.Open("sqlite", GetAccessTokensDBPath())
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer db.Close()
|
||||
rows, err := db.Query("SELECT * from access_tokens;")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
defer rows.Close()
|
||||
var tokens []Token
|
||||
for rows.Next() {
|
||||
var (
|
||||
AccountID string
|
||||
AccessToken string
|
||||
TokenExpiry string
|
||||
RaptToken sql.NullString
|
||||
IdentityToken string
|
||||
)
|
||||
|
||||
err = rows.Scan(&AccountID, &AccessToken, &TokenExpiry, &RaptToken, &IdentityToken)
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
tokens = append(
|
||||
tokens,
|
||||
Token{
|
||||
AccountID: AccountID,
|
||||
AccessToken: AccessToken,
|
||||
TokenExpiry: TokenExpiry,
|
||||
RaptToken: RaptToken,
|
||||
IdentityToken: IdentityToken,
|
||||
})
|
||||
}
|
||||
return tokens
|
||||
}
|
||||
/* The application default access tokens are not stored the same way as the user ones
|
||||
The access_tokens.db does not contain the associated user id, but the sha256 digest of the refresh token
|
||||
stored in the application default json file
|
||||
*/
|
||||
func GetDefaultApplicationHash() string {
|
||||
var (
|
||||
err error
|
||||
jsonFile string
|
||||
)
|
||||
|
||||
jsonFile, err = GetApplicationDefaultPath()
|
||||
if (err != nil) {
|
||||
return "invalid"
|
||||
}
|
||||
|
||||
var applicationcred ApplicationCredential
|
||||
byteValue, _ := ioutil.ReadFile(jsonFile)
|
||||
json.Unmarshal(byteValue, &applicationcred)
|
||||
sum := sha256.Sum256([]byte(applicationcred.RefreshToken))
|
||||
return fmt.Sprintf("%x", sum)
|
||||
}
|
||||
|
||||
/* The refresh tokens are stored in a separate database called credentials.db
|
||||
*/
|
||||
func ReadRefreshTokens() []RefreshToken{
|
||||
db, err := sql.Open("sqlite", GetRefreshTokensDBPath())
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer db.Close()
|
||||
rows, err := db.Query("SELECT * from credentials;")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
defer rows.Close()
|
||||
var tokens []RefreshToken
|
||||
for rows.Next() {
|
||||
var accountName string
|
||||
var JSON string
|
||||
|
||||
err = rows.Scan(&accountName, &JSON)
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
tokens = append(tokens, RefreshToken{Email: accountName, JSON: JSON})
|
||||
}
|
||||
return tokens
|
||||
}
|
||||
|
||||
|
||||
// This function returns the path to gcloud credentials
|
||||
func GetRefreshTokensDBPath() string {
|
||||
user, _ := user.Current()
|
||||
credspath := filepath.Join(user.HomeDir, globals.GCP_GCLOUD_REFRESH_TOKENS_DB_PATH)
|
||||
if _, err := os.Stat(credspath); os.IsNotExist(err) {
|
||||
log.Fatalf("[-] Failed to read gcloud refresh tokens database")
|
||||
}
|
||||
return credspath
|
||||
}
|
||||
func GetAccessTokensDBPath() string {
|
||||
user, _ := user.Current()
|
||||
credspath := filepath.Join(user.HomeDir, globals.GCP_GCLOUD_ACCESS_TOKENS_DB_PATH)
|
||||
if _, err := os.Stat(credspath); os.IsNotExist(err) {
|
||||
log.Fatalf("[-] Failed to read gcloud access tokens database")
|
||||
}
|
||||
return credspath
|
||||
}
|
||||
|
||||
func GetDefaultConfigPath() string {
|
||||
user, _ := user.Current()
|
||||
credspath := filepath.Join(user.HomeDir, globals.GCP_GCLOUD_DEFAULT_CONFIG_PATH)
|
||||
if _, err := os.Stat(credspath); os.IsNotExist(err) {
|
||||
log.Fatalf("[-] Failed to read gcloud default config")
|
||||
}
|
||||
return credspath
|
||||
}
|
||||
|
||||
func GetApplicationDefaultPath() (string, error) {
|
||||
user, _ := user.Current()
|
||||
credspath := filepath.Join(user.HomeDir, globals.GCP_GCLOUD_APPLICATION_DEFAULT_PATH)
|
||||
var err error
|
||||
if _, err := os.Stat(credspath); os.IsNotExist(err) {
|
||||
log.Printf("[-] Default application credentials not set")
|
||||
}
|
||||
return credspath, err
|
||||
}
|
||||
@@ -0,0 +1,321 @@
|
||||
package gcp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"fmt"
|
||||
"bufio"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"golang.org/x/oauth2"
|
||||
"golang.org/x/oauth2/google"
|
||||
"google.golang.org/api/option"
|
||||
goauth2 "google.golang.org/api/oauth2/v2"
|
||||
"google.golang.org/api/cloudresourcemanager/v1"
|
||||
"google.golang.org/api/storage/v1"
|
||||
"google.golang.org/api/cloudasset/v1p1beta1"
|
||||
"github.com/BishopFox/cloudfox/internal"
|
||||
"github.com/BishopFox/cloudfox/globals"
|
||||
resourcemanager "cloud.google.com/go/resourcemanager/apiv3"
|
||||
resourcemanagerpb "cloud.google.com/go/resourcemanager/apiv3/resourcemanagerpb"
|
||||
"google.golang.org/api/iterator"
|
||||
)
|
||||
|
||||
var (
|
||||
logger = internal.NewLogger()
|
||||
)
|
||||
|
||||
type GCPClient struct {
|
||||
Name string
|
||||
HTTPClient *http.Client
|
||||
Logger internal.Logger
|
||||
TokenSource *oauth2.TokenSource
|
||||
TokenInfo *goauth2.Tokeninfo
|
||||
CloudresourcemanagerService *cloudresourcemanager.Service
|
||||
OrganizationsService *cloudresourcemanager.OrganizationsService
|
||||
OrganizationsClient *resourcemanager.OrganizationsClient
|
||||
FoldersClient *resourcemanager.FoldersClient
|
||||
ProjectsClient *resourcemanager.ProjectsClient
|
||||
StorageService *storage.Service
|
||||
FoldersService *cloudresourcemanager.FoldersService
|
||||
ProjectsService *cloudresourcemanager.ProjectsService
|
||||
CloudAssetService *cloudasset.Service
|
||||
ResourcesService *cloudasset.ResourcesService
|
||||
IamPoliciesService *cloudasset.IamPoliciesService
|
||||
|
||||
// user resource selectors
|
||||
selectedOrganizations []string
|
||||
selectedFolders []string
|
||||
selectedProjects []string
|
||||
|
||||
// final resources for module usage
|
||||
ResourceRoots []*internal.Node
|
||||
Organizations []string
|
||||
Folders []string
|
||||
Projects []string
|
||||
}
|
||||
|
||||
func (g *GCPClient) init(profile string) {
|
||||
g.Logger = internal.NewLogger()
|
||||
ctx := context.Background()
|
||||
var (
|
||||
profiles []GCloudProfile
|
||||
client_profile *GCloudProfile
|
||||
err error
|
||||
)
|
||||
profiles = listAllProfiles()
|
||||
for _, p := range profiles {
|
||||
if (p.Name == profile) {
|
||||
client_profile = &p
|
||||
g.Name = profile
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Initiate an http.Client. The following GET request will be
|
||||
// authorized and authenticated on the behalf of the SDK user.
|
||||
g.HTTPClient = client_profile.oauth_conf.Client(ctx, &(client_profile.initial_token))
|
||||
|
||||
// Create all the clients
|
||||
g.OrganizationsClient, err = resourcemanager.NewOrganizationsRESTClient(ctx, option.WithHTTPClient(g.HTTPClient))
|
||||
if (err != nil){
|
||||
g.Logger.Fatal(fmt.Sprintf("Could not initiate GCP Organization Client: %v", err))
|
||||
}
|
||||
g.FoldersClient, err = resourcemanager.NewFoldersRESTClient(ctx, option.WithHTTPClient(g.HTTPClient))
|
||||
if (err != nil){
|
||||
g.Logger.Fatal(fmt.Sprintf("Could not initiate GCP Folders Client: %v", err))
|
||||
}
|
||||
g.ProjectsClient, err = resourcemanager.NewProjectsRESTClient(ctx, option.WithHTTPClient(g.HTTPClient))
|
||||
if (err != nil){
|
||||
g.Logger.Fatal(fmt.Sprintf("Could not initiate GCP Projects Client: %v", err))
|
||||
}
|
||||
|
||||
ts, err := google.DefaultTokenSource(ctx)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
g.TokenSource = &ts
|
||||
oauth2Service, err := goauth2.NewService(ctx, option.WithHTTPClient(g.HTTPClient))
|
||||
tokenInfo, err := oauth2Service.Tokeninfo().Do()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
g.TokenInfo = tokenInfo
|
||||
cloudresourcemanagerService, err := cloudresourcemanager.NewService(ctx, option.WithHTTPClient(g.HTTPClient))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
g.CloudresourcemanagerService = cloudresourcemanagerService
|
||||
|
||||
cloudassetService, err := cloudasset.NewService(ctx, option.WithHTTPClient(g.HTTPClient))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
g.CloudAssetService = cloudassetService
|
||||
|
||||
storageService, err := storage.NewService(ctx, option.WithHTTPClient(g.HTTPClient))
|
||||
g.StorageService = storageService
|
||||
|
||||
g.ResourcesService = cloudasset.NewResourcesService(cloudassetService)
|
||||
g.IamPoliciesService = cloudasset.NewIamPoliciesService(cloudassetService)
|
||||
g.OrganizationsService = cloudresourcemanager.NewOrganizationsService(cloudresourcemanagerService)
|
||||
g.FoldersService = cloudresourcemanager.NewFoldersService(cloudresourcemanagerService)
|
||||
g.ProjectsService = cloudresourcemanager.NewProjectsService(cloudresourcemanagerService)
|
||||
|
||||
}
|
||||
|
||||
func (g *GCPClient) GetResourcesRoots(organizations []string, folders []string, projects []string) []*internal.Node {
|
||||
var (
|
||||
root internal.Node
|
||||
current *internal.Node
|
||||
selected bool
|
||||
)
|
||||
// make user selectors available to whole client
|
||||
g.selectedOrganizations = organizations
|
||||
g.selectedFolders = folders
|
||||
g.selectedProjects = projects
|
||||
ctx := context.Background()
|
||||
|
||||
// define root node, labelled with current profile name
|
||||
root = internal.Node{ID: g.Name}
|
||||
|
||||
// by default, don't select organization
|
||||
selected = false
|
||||
// if a no selector is submitted, all the resources will have to be included
|
||||
if len(organizations) == 0 && len(folders) == 0 && len(projects) == 0 {
|
||||
selected = true
|
||||
}
|
||||
|
||||
// now look for resources (projects) that do not have a parent and that live in the user' account directly
|
||||
// might need to investigate later but it looks like we can't have folders under a user account
|
||||
projectsListResponse, _ := g.CloudresourcemanagerService.Projects.List().Do()
|
||||
for _, project := range projectsListResponse.Projects {
|
||||
if project.Parent == nil {
|
||||
if selected {
|
||||
g.Projects = append(g.Projects, project.ProjectId)
|
||||
}
|
||||
// if project is selected, add a new root node
|
||||
for _, filterProject := range g.selectedProjects {
|
||||
if (project.ProjectId == filterProject || fmt.Sprint(project.ProjectNumber) == filterProject) {
|
||||
g.ResourceRoots = append(g.ResourceRoots, current)
|
||||
break
|
||||
}
|
||||
}
|
||||
current = &internal.Node{ID: fmt.Sprintf("p:%s (%s - %d)", project.Name, project.ProjectId, project.ProjectNumber)}
|
||||
root.Add(*current)
|
||||
}
|
||||
}
|
||||
|
||||
// iterate over available organizations
|
||||
organizationIterator := g.OrganizationsClient.SearchOrganizations(ctx, &resourcemanagerpb.SearchOrganizationsRequest{})
|
||||
for {
|
||||
organization, err := organizationIterator.Next()
|
||||
if err == iterator.Done {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
g.Logger.FatalM(fmt.Sprintf("An error occurred when listing organizations: %v", err), globals.GCP_HIERARCHY_MODULE_NAME)
|
||||
}
|
||||
current = &internal.Node{ID: fmt.Sprintf("%s (%s)", organization.DisplayName, organization.Name[14:])}
|
||||
// if organization is selected, add a new root node
|
||||
for _, filterOrg := range organizations {
|
||||
if (organization.DisplayName == filterOrg || organization.Name[14:] == filterOrg) {
|
||||
g.Organizations = append(g.Organizations, organization.DisplayName)
|
||||
g.ResourceRoots = append(g.ResourceRoots, current)
|
||||
selected = true
|
||||
break
|
||||
}
|
||||
}
|
||||
// get childs (projects & folders) within current orgnization
|
||||
g.getChilds(current, organization.Name, selected)
|
||||
// add the folder as a child to the root node
|
||||
root.Add(*current)
|
||||
}
|
||||
|
||||
// if no resource root has been found through filtering, add the default root, which is the current user account
|
||||
if len(g.ResourceRoots) == 0 {
|
||||
g.Logger.InfoM("Could not find resources with resources selectors, default root resource", globals.GCP_HIERARCHY_MODULE_NAME)
|
||||
g.ResourceRoots = append(g.ResourceRoots, &root)
|
||||
}
|
||||
return g.ResourceRoots
|
||||
}
|
||||
|
||||
func (g *GCPClient) getChilds(parent *internal.Node, parentName string, selected bool){
|
||||
//GCPLogger.Success(fmt.Sprintf("Listing stuff in parent %s", parentName))
|
||||
var (
|
||||
current *internal.Node
|
||||
)
|
||||
ctx := context.Background()
|
||||
folderIterator := g.FoldersClient.SearchFolders(ctx, &resourcemanagerpb.SearchFoldersRequest{Query: fmt.Sprintf("parent=%s", parentName)})
|
||||
for {
|
||||
folder, err := folderIterator.Next()
|
||||
if err == iterator.Done {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
g.Logger.FatalM(fmt.Sprintf("An error occurred when listing folders in parent %s: %v", parentName, err), globals.GCP_HIERARCHY_MODULE_NAME)
|
||||
}
|
||||
current = &internal.Node{ID: fmt.Sprintf("f:%s (%s)", folder.DisplayName, folder.Name[8:])}
|
||||
if !selected {
|
||||
for _, filterFolder := range g.selectedFolders {
|
||||
if (folder.DisplayName == filterFolder || folder.Name[8:] == filterFolder) {
|
||||
g.Folders = append(g.Folders, folder.DisplayName)
|
||||
g.ResourceRoots = append(g.ResourceRoots, current)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
g.getChilds(current, folder.Name, selected)
|
||||
(*parent).Add(*current)
|
||||
}
|
||||
projectIterator := g.ProjectsClient.SearchProjects(ctx, &resourcemanagerpb.SearchProjectsRequest{Query: fmt.Sprintf("parent=%s", parentName)})
|
||||
for {
|
||||
project, err := projectIterator.Next()
|
||||
if err == iterator.Done {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
g.Logger.FatalM(fmt.Sprintf("An error occurred when listing projects in parent %s: %v", parentName, err), globals.GCP_HIERARCHY_MODULE_NAME)
|
||||
}
|
||||
current = &internal.Node{ID: fmt.Sprintf("p:%s (%s - %s)", project.DisplayName, project.ProjectId, project.Name[9:])}
|
||||
if !selected {
|
||||
for _, filterProject := range g.selectedProjects {
|
||||
if (project.ProjectId == filterProject || project.Name[9:] == filterProject) {
|
||||
g.Projects = append(g.Projects, project.ProjectId)
|
||||
g.ResourceRoots = append(g.ResourceRoots, current)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
(*parent).Add(*current)
|
||||
}
|
||||
}
|
||||
|
||||
func NewGCPClient(profileName string) *GCPClient {
|
||||
client := new(GCPClient)
|
||||
client.init(profileName)
|
||||
return client
|
||||
}
|
||||
|
||||
/*
|
||||
Get all usable GCP Profiles
|
||||
We are using only non expired user-tokens
|
||||
*/
|
||||
func GetAllGCPProfiles() []string {
|
||||
var (
|
||||
GCPProfiles []string
|
||||
accessTokens []Token
|
||||
)
|
||||
accessTokens = ReadAccessTokens()
|
||||
for _, accessToken := range accessTokens {
|
||||
if (!strings.Contains(accessToken.AccountID, "@")) {
|
||||
continue
|
||||
}
|
||||
|
||||
exp, _ := time.Parse(time.RFC3339, accessToken.TokenExpiry)
|
||||
if exp.After(time.Now()) {
|
||||
GCPProfiles = append(GCPProfiles, accessToken.AccountID)
|
||||
}
|
||||
}
|
||||
return GCPProfiles
|
||||
}
|
||||
|
||||
func ConfirmSelectedProfiles(GCPProfiles []string) bool {
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
logger.Info("Identified profiles:\n")
|
||||
for _, profile := range GCPProfiles {
|
||||
fmt.Printf("\t* %s\n", profile)
|
||||
}
|
||||
fmt.Printf("\n")
|
||||
logger.Info(fmt.Sprintf("Are you sure you'd like to run this command against the [%d] listed profile(s)? (Y\\n): ", len(GCPProfiles)))
|
||||
text, _ := reader.ReadString('\n')
|
||||
switch text {
|
||||
case "\n", "Y\n", "y\n":
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
||||
}
|
||||
|
||||
func GetSelectedGCPProfiles(GCPProfilesListPath string) []string {
|
||||
GCPProfilesListFile, err := internal.UtilsFs.Open(GCPProfilesListPath)
|
||||
internal.CheckErr(err, fmt.Sprintf("could not open given file %s", GCPProfilesListPath))
|
||||
if err != nil {
|
||||
fmt.Printf("\nError loading profiles. Could not open file at location[%s]\n", GCPProfilesListPath)
|
||||
os.Exit(1)
|
||||
}
|
||||
defer GCPProfilesListFile.Close()
|
||||
var GCPProfiles []string
|
||||
scanner := bufio.NewScanner(GCPProfilesListFile)
|
||||
scanner.Split(bufio.ScanLines)
|
||||
for scanner.Scan() {
|
||||
profile := strings.TrimSpace(scanner.Text())
|
||||
if len(profile) != 0 {
|
||||
GCPProfiles = append(GCPProfiles, profile)
|
||||
}
|
||||
}
|
||||
return GCPProfiles
|
||||
}
|
||||
@@ -3,12 +3,16 @@ package internal
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"fmt"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/BishopFox/cloudfox/globals"
|
||||
"github.com/aws/smithy-go/ptr"
|
||||
"github.com/jedib0t/go-pretty/text"
|
||||
"github.com/kyokomi/emoji"
|
||||
"github.com/fatih/color"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -28,3 +32,54 @@ func GetLogDirPath() *string {
|
||||
}
|
||||
return ptr.String(dir)
|
||||
}
|
||||
|
||||
type Logger struct {
|
||||
version string
|
||||
txtLog *logrus.Logger
|
||||
}
|
||||
|
||||
func NewLogger() Logger {
|
||||
var logger = Logger{
|
||||
version: globals.CLOUDFOX_VERSION,
|
||||
txtLog: TxtLog,
|
||||
}
|
||||
return logger
|
||||
}
|
||||
|
||||
func (l *Logger) Info(text string){
|
||||
l.InfoM(text, "config")
|
||||
}
|
||||
|
||||
func (l *Logger) InfoM(text string, module string) {
|
||||
var cyan = color.New(color.FgCyan).SprintFunc()
|
||||
fmt.Printf("[%s][%s] %s\n", cyan(emoji.Sprintf(":fox:cloudfox %s :fox:", l.version)), cyan(module), text)
|
||||
}
|
||||
|
||||
func (l *Logger) Success(text string){
|
||||
l.SuccessM(text, "config")
|
||||
}
|
||||
func (l *Logger) SuccessM(text string, module string) {
|
||||
var green = color.New(color.FgGreen).SprintFunc()
|
||||
fmt.Printf("[%s][%s] %s\n", green(emoji.Sprintf(":fox:cloudfox %s :fox:", l.version)), green(module), text)
|
||||
}
|
||||
|
||||
func (l *Logger) Error(text string){
|
||||
l.ErrorM(text, "config")
|
||||
}
|
||||
|
||||
func (l *Logger) ErrorM(text string, module string) {
|
||||
var red = color.New(color.FgRed).SprintFunc()
|
||||
fmt.Printf("[%s][%s] %s\n", red(emoji.Sprintf(":fox:cloudfox %s :fox:", l.version)), red(module), text)
|
||||
l.txtLog.Printf("[%s] %s", module, text)
|
||||
}
|
||||
|
||||
func (l *Logger) Fatal(text string){
|
||||
l.FatalM(text, "config")
|
||||
}
|
||||
|
||||
func (l *Logger) FatalM(text string, module string) {
|
||||
var red = color.New(color.FgRed).SprintFunc()
|
||||
l.txtLog.Printf("[%s] %s", module, text)
|
||||
fmt.Printf("[%s][%s] %s\n", red(emoji.Sprintf(":fox:cloudfox %s :fox:", l.version)), red(module), text)
|
||||
os.Exit(1)
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"github.com/shivamMg/ppds/tree"
|
||||
)
|
||||
|
||||
type Node struct {
|
||||
ID string
|
||||
c []*Node
|
||||
}
|
||||
|
||||
func (n *Node) Data() interface{} {
|
||||
return n.ID
|
||||
}
|
||||
|
||||
func (n *Node) Children() (children []tree.Node) {
|
||||
for _, c := range n.c {
|
||||
children = append(children, tree.Node(c))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (n *Node) Add(child Node) {
|
||||
n.c = append(n.c, &child)
|
||||
return
|
||||
}
|
||||
@@ -4,17 +4,18 @@ import (
|
||||
"os"
|
||||
|
||||
"github.com/BishopFox/cloudfox/cli"
|
||||
"github.com/BishopFox/cloudfox/globals"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var (
|
||||
rootCmd = &cobra.Command{
|
||||
Use: os.Args[0],
|
||||
Version: "1.11.2",
|
||||
Version: globals.CLOUDFOX_VERSION,
|
||||
}
|
||||
)
|
||||
|
||||
func main() {
|
||||
rootCmd.AddCommand(cli.AWSCommands, cli.AzCommands)
|
||||
rootCmd.AddCommand(cli.AWSCommands, cli.AzCommands, cli.GCPCommands)
|
||||
rootCmd.Execute()
|
||||
}
|
||||
Reference in new issue
Block a user