Repository: sa7mon/S3Scanner
Pull request #341 (2024-09-14 02:38:02)

feat: add linter config
Add config for golangci-lint and run linter across project.

github-actions[bot]: ![Code Coverage](https://img.shields.io/badge/Code%20Coverage-58%25-yellow?style=flat)

Package | Line Rate | Complexity | Health
-------- | --------- | ---------- | ------
github.com/sa7mon/s3scanner | 0% | 0 | ❌
github.com/sa7mon/s3scanner/bucket | 91% | 0 | ✔
github.com/sa7mon/s3scanner/cmd/mqingest | 0% | 0 | ❌
github.com/sa7mon/s3scanner/cmd/regioncheck | 55% | 0 | ➖
github.com/sa7mon/s3scanner/cmd/s3scanner | 19% | 0 | ❌
github.com/sa7mon/s3scanner/collection | 0% | 0 | ❌
github.com/sa7mon/s3scanner/db | 73% | 0 | ➖
github.com/sa7mon/s3scanner/log | 0% | 0 | ❌
github.com/sa7mon/s3scanner/mq | 71% | 0 | ➖
github.com/sa7mon/s3scanner/permission | 28% | 0 | ❌
github.com/sa7mon/s3scanner/provider | 79% | 0 | ✔
github.com/sa7mon/s3scanner/provider/clientmap | 100% | 0 | ✔
github.com/sa7mon/s3scanner/worker | 49% | 0 | ❌
**Summary** | **58%** (622 / 1076) | **0** | ➖

<!-- Sticky Pull Request Comment -->

Patch:
diff --git a/.golangci.yml b/.golangci.yml
new file mode 100644
index 0000000..e357ffc
--- /dev/null
+++ b/.golangci.yml
@@ -0,0 +1,25 @@
+linters-settings:
+  misspell:
+    locale: US
+  gosec:
+    excludes:
+      - G115 # Flags for potentially-unsafe casting of ints
+
+issues:
+  exclude-rules:
+    - path: provider/providers.go # TLS verification is skipped when provider.Insecure() == true
+      text: "InsecureSkipVerify"
+
+linters:
+  enable:
+    - gosec
+    - govet
+    - loggercheck
+    - misspell
+    - revive
+    - sloglint
+    - tparallel
+    - unconvert
+    - unparam
+    - unused
+    - whitespace
\ No newline at end of file
diff --git a/bucket/bucket.go b/bucket/bucket.go
index 26e07a0..2947ca8 100644
--- a/bucket/bucket.go
+++ b/bucket/bucket.go
@@ -28,19 +28,19 @@ var bucketRe = regexp.MustCompile(`[^.\-a-z0-9]`)
 
 type Bucket struct {
 	//gorm.Model
-	ID                uint           `gorm:"primarykey" json:",omitempty"`
-	Name              string         `json:"name" gorm:"name;size:64;index"`
-	Region            string         `json:"region" gorm:"size:20"`
-	Exists            uint8          `json:"exists"`
-	DateScanned       time.Time      `json:"date_scanned"`
-	Objects           []BucketObject `json:"objects"`
-	ObjectsEnumerated bool           `json:"objects_enumerated"`
-	Provider          string         `json:"provider"`
-	NumObjects        int32          `json:"num_objects"`
+	ID                uint      `gorm:"primarykey" json:",omitempty"`
+	Name              string    `json:"name" gorm:"name;size:64;index"`
+	Region            string    `json:"region" gorm:"size:20"`
+	Exists            uint8     `json:"exists"`
+	DateScanned       time.Time `json:"date_scanned"`
+	Objects           []Object  `json:"objects"`
+	ObjectsEnumerated bool      `json:"objects_enumerated"`
+	Provider          string    `json:"provider"`
+	NumObjects        int32     `json:"num_objects"`
 
 	// Total size of all bucket objects in bytes
 	BucketSize       uint64 `json:"bucket_size"`
-	OwnerId          string `json:"owner_id"`
+	OwnerID          string `json:"owner_id"`
 	OwnerDisplayName string `json:"owner_display_name"`
 
 	PermAuthUsersRead        uint8 `json:"perm_auth_users_read"`
@@ -56,7 +56,7 @@ type Bucket struct {
 	PermAllUsersFullControl uint8 `json:"perm_all_users_full_control"`
 }
 
-type BucketObject struct {
+type Object struct {
 	//gorm.Model
 	ID       uint   `gorm:"primarykey" json:",omitempty"`
 	Key      string `json:"key" gorm:"type:string;size:1024"` // Keys can be up to 1,024 bytes long, UTF-8 encoded plus an additional byte just in case. https://docs.aws.amazon.com/AmazonS3/latest/userguide/object-keys.html
@@ -82,63 +82,63 @@ func NewBucket(name string) Bucket {
 	}
 }
 
-func (bucket *Bucket) String() string {
-	if bucket.Exists == BucketNotExist {
-		return fmt.Sprintf("%v | bucket_not_exist", bucket.Name)
+func (b *Bucket) String() string {
+	if b.Exists == BucketNotExist {
+		return fmt.Sprintf("%v | bucket_not_exist", b.Name)
 	}
 
 	var authUserPerms []string
-	if bucket.PermAuthUsersRead == PermissionAllowed {
+	if b.PermAuthUsersRead == PermissionAllowed {
 		authUserPerms = append(authUserPerms, "READ")
 	}
-	if bucket.PermAuthUsersWrite == PermissionAllowed {
+	if b.PermAuthUsersWrite == PermissionAllowed {
 		authUserPerms = append(authUserPerms, "WRITE")
 	}
-	if bucket.PermAuthUsersReadACL == PermissionAllowed {
+	if b.PermAuthUsersReadACL == PermissionAllowed {
 		authUserPerms = append(authUserPerms, "READ_ACP")
 	}
-	if bucket.PermAuthUsersWriteACL == PermissionAllowed {
+	if b.PermAuthUsersWriteACL == PermissionAllowed {
 		authUserPerms = append(authUserPerms, "WRITE_ACP")
 	}
-	if bucket.PermAuthUsersFullControl == PermissionAllowed {
+	if b.PermAuthUsersFullControl == PermissionAllowed {
 		authUserPerms = append(authUserPerms, "FULL_CONTROL")
 	}
 
 	var allUsersPerms []string
-	if bucket.PermAllUsersRead == PermissionAllowed {
+	if b.PermAllUsersRead == PermissionAllowed {
 		allUsersPerms = append(allUsersPerms, "READ")
 	}
-	if bucket.PermAllUsersWrite == PermissionAllowed {
+	if b.PermAllUsersWrite == PermissionAllowed {
 		allUsersPerms = append(allUsersPerms, "WRITE")
 	}
-	if bucket.PermAllUsersReadACL == PermissionAllowed {
+	if b.PermAllUsersReadACL == PermissionAllowed {
 		allUsersPerms = append(allUsersPerms, "READ_ACP")
 	}
-	if bucket.PermAllUsersWriteACL == PermissionAllowed {
+	if b.PermAllUsersWriteACL == PermissionAllowed {
 		allUsersPerms = append(allUsersPerms, "WRITE_ACP")
 	}
-	if bucket.PermAllUsersFullControl == PermissionAllowed {
+	if b.PermAllUsersFullControl == PermissionAllowed {
 		allUsersPerms = append(allUsersPerms, "FULL_CONTROL")
 	}
 
 	return fmt.Sprintf("AuthUsers: [%v] | AllUsers: [%v]", strings.Join(authUserPerms, ", "), strings.Join(allUsersPerms, ", "))
 }
 
-func (bucket *Bucket) Permissions() map[*types.Grantee]map[string]uint8 {
+func (b *Bucket) Permissions() map[*types.Grantee]map[string]uint8 {
 	return map[*types.Grantee]map[string]uint8{
 		groups.AllUsersv2: {
-			"READ":         bucket.PermAllUsersRead,
-			"WRITE":        bucket.PermAllUsersWrite,
-			"READ_ACP":     bucket.PermAllUsersReadACL,
-			"WRITE_ACP":    bucket.PermAllUsersWriteACL,
-			"FULL_CONTROL": bucket.PermAllUsersFullControl,
+			"READ":         b.PermAllUsersRead,
+			"WRITE":        b.PermAllUsersWrite,
+			"READ_ACP":     b.PermAllUsersReadACL,
+			"WRITE_ACP":    b.PermAllUsersWriteACL,
+			"FULL_CONTROL": b.PermAllUsersFullControl,
 		},
 		groups.AuthenticatedUsersv2: {
-			"READ":         bucket.PermAuthUsersRead,
-			"WRITE":        bucket.PermAuthUsersWrite,
-			"READ_ACP":     bucket.PermAuthUsersReadACL,
-			"WRITE_ACP":    bucket.PermAuthUsersWriteACL,
-			"FULL_CONTROL": bucket.PermAuthUsersFullControl,
+			"READ":         b.PermAuthUsersRead,
+			"WRITE":        b.PermAuthUsersWrite,
+			"READ_ACP":     b.PermAuthUsersReadACL,
+			"WRITE_ACP":    b.PermAuthUsersWriteACL,
+			"FULL_CONTROL": b.PermAuthUsersFullControl,
 		},
 	}
 }
@@ -179,45 +179,45 @@ func ReadFromFile(bucketFile string, bucketChan chan Bucket) error {
 	return nil
 }
 
-// ParseAclOutputv2 TODO: probably move this to providers.go
-func (bucket *Bucket) ParseAclOutputv2(aclOutput *s3.GetBucketAclOutput) error {
-	bucket.OwnerId = *aclOutput.Owner.ID
+// ParseACLOutputV2 TODO: probably move this to providers.go
+func (b *Bucket) ParseACLOutputV2(aclOutput *s3.GetBucketAclOutput) error {
+	b.OwnerID = *aclOutput.Owner.ID
 	if aclOutput.Owner.DisplayName != nil {
-		bucket.OwnerDisplayName = *aclOutput.Owner.DisplayName
+		b.OwnerDisplayName = *aclOutput.Owner.DisplayName
 	}
 	// Since we can read the permissions, there should be no unknowns. Set all to denied, then read each grant and
 	// set the corresponding permission to allowed.
-	bucket.DenyAll()
+	b.DenyAll()
 
-	for _, b := range aclOutput.Grants {
-		if b.Grantee != nil && b.Grantee.Type == "Group" && *b.Grantee.URI == groups.AllUsersGroup {
-			switch b.Permission {
+	for _, g := range aclOutput.Grants {
+		if g.Grantee != nil && g.Grantee.Type == "Group" && *g.Grantee.URI == groups.AllUsersGroup {
+			switch g.Permission {
 			case types.PermissionRead:
-				bucket.PermAllUsersRead = PermissionAllowed
+				b.PermAllUsersRead = PermissionAllowed
 			case types.PermissionWrite:
-				bucket.PermAllUsersWrite = PermissionAllowed
+				b.PermAllUsersWrite = PermissionAllowed
 			case types.PermissionReadAcp:
-				bucket.PermAllUsersReadACL = PermissionAllowed
+				b.PermAllUsersReadACL = PermissionAllowed
 			case types.PermissionWriteAcp:
-				bucket.PermAllUsersWriteACL = PermissionAllowed
+				b.PermAllUsersWriteACL = PermissionAllowed
 			case types.PermissionFullControl:
-				bucket.PermAllUsersFullControl = PermissionAllowed
+				b.PermAllUsersFullControl = PermissionAllowed
 			default:
 				break
 			}
 		}
-		if b.Grantee != nil && b.Grantee.Type == "Group" && *b.Grantee.URI == groups.AuthUsersGroup {
-			switch b.Permission {
+		if g.Grantee != nil && g.Grantee.Type == "Group" && *g.Grantee.URI == groups.AuthUsersGroup {
+			switch g.Permission {
 			case types.PermissionRead:
-				bucket.PermAuthUsersRead = PermissionAllowed
+				b.PermAuthUsersRead = PermissionAllowed
 			case types.PermissionWrite:
-				bucket.PermAuthUsersWrite = PermissionAllowed
+				b.PermAuthUsersWrite = PermissionAllowed
 			case types.PermissionReadAcp:
-				bucket.PermAuthUsersReadACL = PermissionAllowed
+				b.PermAuthUsersReadACL = PermissionAllowed
 			case types.PermissionWriteAcp:
-				bucket.PermAuthUsersWriteACL = PermissionAllowed
+				b.PermAuthUsersWriteACL = PermissionAllowed
 			case types.PermissionFullControl:
-				bucket.PermAuthUsersFullControl = PermissionAllowed
+				b.PermAuthUsersFullControl = PermissionAllowed
 			default:
 				break
 			}
@@ -230,9 +230,8 @@ func (bucket *Bucket) ParseAclOutputv2(aclOutput *s3.GetBucketAclOutput) error {
 func Permission(canDo bool) uint8 {
 	if canDo {
 		return PermissionAllowed
-	} else {
-		return PermissionDenied
 	}
+	return PermissionDenied
 }
 
 func IsValidS3BucketName(bucketName string) bool {
diff --git a/cmd/mqingest/mqingest.go b/cmd/mqingest/mqingest.go
index 706b1ae..5837b78 100644
--- a/cmd/mqingest/mqingest.go
+++ b/cmd/mqingest/mqingest.go
@@ -33,15 +33,15 @@ type BucketMessage struct {
 func main() {
 	var filename string
 	var url string
-	var queue_name string
+	var queueName string
 
 	flag.StringVar(&filename, "file", "", "File name of buckets to send to MQ")
 	flag.StringVar(&url, "url", "amqp://guest:guest@localhost:5672/", "AMQP URI of RabbitMQ server")
-	flag.StringVar(&queue_name, "queue", "", "Name of message queue to publish buckets to")
+	flag.StringVar(&queueName, "queue", "", "Name of message queue to publish buckets to")
 
 	flag.Parse()
 
-	if filename == "" || queue_name == "" {
+	if filename == "" || queueName == "" {
 		fmt.Println("Flags 'file' and 'queue' are required")
 		printUsage()
 		os.Exit(1)
@@ -56,16 +56,16 @@ func main() {
 	defer ch.Close()
 
 	// Declare dead letter queue
-	dlq, dlErr := ch.QueueDeclare(queue_name+"_dead", true, false, false,
+	dlq, dlErr := ch.QueueDeclare(queueName+"_dead", true, false, false,
 		false, nil)
 	failOnError(dlErr, "Failed to declare dead letter queue")
 
 	q, err := ch.QueueDeclare(
-		queue_name, // name
-		true,       // durable
-		false,      // delete when unused
-		false,      // exclusive
-		false,      // no-wait
+		queueName, // name
+		true,      // durable
+		false,     // delete when unused
+		false,     // exclusive
+		false,     // no-wait
 		amqp.Table{
 			"x-dead-letter-exchange":    "",
 			"x-dead-letter-routing-key": dlq.Name,
@@ -112,12 +112,11 @@ func main() {
 		if err != nil {
 			failOnError(err, "Failed to publish to channel")
 		}
-		msgsPublished += 1
+		msgsPublished++
 	}
 	if err := fileScanner.Err(); err != nil {
 		failOnError(err, "fileScanner failed")
 	}
 
-	log.Printf("%v bucket names published to queue %v\n", msgsPublished, queue_name)
-
+	log.Printf("%v bucket names published to queue %v\n", msgsPublished, queueName)
 }
diff --git a/cmd/regioncheck/regioncheck.go b/cmd/regioncheck/regioncheck.go
index 3d3037d..86b4de8 100644
--- a/cmd/regioncheck/regioncheck.go
+++ b/cmd/regioncheck/regioncheck.go
@@ -45,35 +45,35 @@ func GetRegionsDO() ([]string, error) {
 	}
 
 	regions := []string{}
-	doc.Find("h3#other-products + table thead tr th").Each(func(i int, t *goquery.Selection) {
+	doc.Find("h3#other-products + table thead tr th").Each(func(_ int, t *goquery.Selection) {
 		regions = append(regions, t.Text())
 	})
 
-	spaces_supported := []bool{}
-	doc.Find("h3#other-products + table tbody tr").Each(func(i int, t *goquery.Selection) {
+	spacesSupported := []bool{}
+	doc.Find("h3#other-products + table tbody tr").Each(func(_ int, t *goquery.Selection) {
 		// For each row, check the first cell for a value of "Spaces"
 		rowHeader := t.Find("td").First().Text()
 		if rowHeader == "Spaces" {
 			// For each cell in the "Spaces" row, check if the contents are not empty - meaning Spaces is supported
-			t.Find("td").Each(func(j int, v *goquery.Selection) {
+			t.Find("td").Each(func(_ int, v *goquery.Selection) {
 				supported := v.Text() != ""
-				spaces_supported = append(spaces_supported, supported)
+				spacesSupported = append(spacesSupported, supported)
 			})
 		}
 	})
 
-	supported_regions := []string{}
+	supportedRegions := []string{}
 	for i := 0; i < len(regions); i++ {
 		if regions[i] == "Product" {
 			continue
 		}
-		if spaces_supported[i] {
-			supported_regions = append(supported_regions, strings.ToLower(regions[i]))
+		if spacesSupported[i] {
+			supportedRegions = append(supportedRegions, strings.ToLower(regions[i]))
 		}
 	}
 
 	// Return slice of region names
-	return supported_regions, nil
+	return supportedRegions, nil
 }
 
 // GetRegionsLinode fetches region names from Linode docs HTML page. Linode also provides this info via
@@ -105,7 +105,7 @@ func GetRegionsLinode() ([]string, error) {
 	}
 
 	regions := []string{}
-	doc.Find(".rdmd-table:nth-of-type(1) tbody tr td:nth-of-type(2)").Each(func(i int, t *goquery.Selection) {
+	doc.Find(".rdmd-table:nth-of-type(1) tbody tr td:nth-of-type(2)").Each(func(_ int, t *goquery.Selection) {
 		regions = append(regions, t.Text())
 	})
 
@@ -152,7 +152,7 @@ func main() {
 	for name, get := range p {
 		name := name
 		get := get
-		go func(w *sync.WaitGroup) {
+		go func(_ *sync.WaitGroup) {
 			results[name], errors[name] = get()
 			wg.Done()
 		}(&wg)
diff --git a/cmd/s3scanner/args.go b/cmd/s3scanner/args.go
index 9f2d75d..45cda92 100644
--- a/cmd/s3scanner/args.go
+++ b/cmd/s3scanner/args.go
@@ -10,7 +10,7 @@ type ArgCollection struct {
 	BucketFile   string
 	BucketName   string
 	DoEnumerate  bool
-	Json         bool
+	JSON         bool
 	ProviderFlag string
 	Threads      int
 	UseMq        bool
@@ -23,13 +23,13 @@ func (args ArgCollection) Validate() error {
 	// Validate: only 1 input flag is provided
 	numInputFlags := 0
 	if args.UseMq {
-		numInputFlags += 1
+		numInputFlags++
 	}
 	if args.BucketName != "" {
-		numInputFlags += 1
+		numInputFlags++
 	}
 	if args.BucketFile != "" {
-		numInputFlags += 1
+		numInputFlags++
 	}
 	if numInputFlags != 1 {
 		return errors.New("exactly one of: -bucket, -bucket-file, -mq required")
@@ -63,11 +63,11 @@ func validateConfig(args ArgCollection) error {
 
 	// Try to find and read config file
 	if err := viper.ReadInConfig(); err != nil {
-		if _, ok := err.(viper.ConfigFileNotFoundError); ok {
+		_, ok := err.(viper.ConfigFileNotFoundError)
+		if ok {
 			return errors.New("config file not found")
-		} else {
-			return err
 		}
+		return err
 	}
 
 	// Verify all expected keys are in the config file
diff --git a/cmd/s3scanner/s3scanner.go b/cmd/s3scanner/s3scanner.go
index d1e5c9f..ebae5cc 100644
--- a/cmd/s3scanner/s3scanner.go
+++ b/cmd/s3scanner/s3scanner.go
@@ -118,7 +118,7 @@ func Run(version string) {
 	flag.BoolVar(&args.UseMq, "mq", false, "Connect to RabbitMQ to get buckets. Requires config file key \"mq\".")
 
 	flag.BoolVar(&args.WriteToDB, "db", false, "Save results to a Postgres database. Requires config file key \"db.uri\".")
-	flag.BoolVar(&args.Json, "json", false, "Print logs to stdout in JSON format instead of human-readable.")
+	flag.BoolVar(&args.JSON, "json", false, "Print logs to stdout in JSON format instead of human-readable.")
 
 	flag.BoolVar(&args.DoEnumerate, "enumerate", false, "Enumerate bucket objects (can be time-consuming).")
 	flag.IntVar(&args.Threads, "threads", 4, "Number of threads to scan with.")
@@ -145,7 +145,7 @@ func Run(version string) {
 		log.SetLevel(log.DebugLevel)
 	}
 	log.SetOutput(os.Stdout)
-	if args.Json {
+	if args.JSON {
 		log.SetFormatter(&log2.NestedJSONFormatter{})
 	} else {
 		log.SetFormatter(&log.TextFormatter{DisableTimestamp: true})
@@ -197,7 +197,7 @@ func Run(version string) {
 
 		for i := 0; i < args.Threads; i++ {
 			wg.Add(1)
-			go worker.Work(&wg, buckets, p, args.DoEnumerate, args.WriteToDB, args.Json)
+			go worker.Work(&wg, buckets, p, args.DoEnumerate, args.WriteToDB, args.JSON)
 		}
 
 		if args.BucketFile != "" {
@@ -222,11 +222,11 @@ func Run(version string) {
 	}
 
 	// Setup mq connection and spin off consumers
-	mqUri := viper.GetString("mq.uri")
+	mqURI := viper.GetString("mq.uri")
 	mqName := viper.GetString("mq.queue_name")
-	conn, err := amqp.Dial(mqUri)
+	conn, err := amqp.Dial(mqURI)
 	if err != nil {
-		log.Fatalf("%s: %s", fmt.Sprintf("failed to connect to AMQP URI '%s'", mqUri), err)
+		log.Fatalf("%s: %s", fmt.Sprintf("failed to connect to AMQP URI '%s'", mqURI), err)
 	}
 	defer conn.Close()
 
diff --git a/db/database.go b/db/database.go
index b94e310..ae630bb 100644
--- a/db/database.go
+++ b/db/database.go
@@ -44,7 +44,7 @@ func Connect(dbConn string, migrate bool) error {
 	}
 
 	if migrate {
-		err = database.AutoMigrate(&bucket.Bucket{}, &bucket.BucketObject{})
+		err = database.AutoMigrate(&bucket.Bucket{}, &bucket.Object{})
 		if err != nil {
 			return err
 		}
diff --git a/groups/groups.go b/groups/groups.go
index 1606431..ccdf2e0 100644
--- a/groups/groups.go
+++ b/groups/groups.go
@@ -16,5 +16,5 @@ var AuthenticatedUsersv2 = &types.Grantee{
 	Type: types.TypeGroup,
 	URI:  aws.String(AuthUsersGroup)}
 
-const AllUsersUri = "uri=" + AllUsersGroup
-const AuthUsersUri = "uri=" + AuthUsersGroup
+const AllUsersURI = "uri=" + AllUsersGroup
+const AuthUsersURI = "uri=" + AuthUsersGroup
diff --git a/log/formatter_hook.go b/log/formatter_hook.go
index 3aa1778..e0ec0d6 100644
--- a/log/formatter_hook.go
+++ b/log/formatter_hook.go
@@ -18,7 +18,6 @@ type FormatterHook struct {
 // Fire will be called when some logging function is called with current hook
 // It will format log entry and write it to appropriate writer
 func (hook *FormatterHook) Fire(entry *log.Entry) error {
-
 	// Add default fields to any set by the user (entry.Data)
 	newFieldsMap := make(log.Fields, len(hook.DefaultFields)+len(entry.Data))
 	for k, v := range entry.Data {
diff --git a/mq/mq.go b/mq/mq.go
index 62eac74..53ae2d1 100644
--- a/mq/mq.go
+++ b/mq/mq.go
@@ -6,8 +6,8 @@ import (
 	"github.com/streadway/amqp"
 )
 
-func Connect(conn *amqp.Connection, queue string, threads int, threadId int) (*amqp.Channel, error) {
-	log.Debugf("{thread%v} Opening channel...", threadId)
+func Connect(conn *amqp.Connection, queue string, threads int, threadID int) (*amqp.Channel, error) {
+	log.Debugf("{thread%v} Opening channel...", threadID)
 	ch, channelErr := conn.Channel()
 	if channelErr != nil {
 		return nil, fmt.Errorf("[Connect()] failed to open a channel: %w", channelErr)
diff --git a/permission/permission.go b/permission/permission.go
index a210ca1..f78e29b 100644
--- a/permission/permission.go
+++ b/permission/permission.go
@@ -7,14 +7,14 @@ import (
 	"github.com/aws/aws-sdk-go-v2/aws"
 	awshttp "github.com/aws/aws-sdk-go-v2/aws/transport/http"
 	"github.com/aws/aws-sdk-go-v2/service/s3"
-	. "github.com/sa7mon/s3scanner/bucket"
-	. "github.com/sa7mon/s3scanner/groups"
+	"github.com/sa7mon/s3scanner/bucket"
+	"github.com/sa7mon/s3scanner/groups"
 	log "github.com/sirupsen/logrus"
 	"strings"
 	"time"
 )
 
-func CheckPermReadACL(s3Client *s3.Client, bucket *Bucket) (bool, error) {
+func CheckPermReadACL(s3Client *s3.Client, bucket *bucket.Bucket) (bool, error) {
 	aclOutput, err := s3Client.GetBucketAcl(context.TODO(),
 		&s3.GetBucketAclInput{Bucket: &bucket.Name})
 	if err != nil {
@@ -27,54 +27,53 @@ func CheckPermReadACL(s3Client *s3.Client, bucket *Bucket) (bool, error) {
 			if re.HTTPStatusCode() == 403 {
 				//fmt.Println("Access Denied!")
 				return false, nil
-			} else {
-				return false, err
 			}
+			return false, err
 		}
 		return false, err
 	}
-	err = bucket.ParseAclOutputv2(aclOutput)
+	err = bucket.ParseACLOutputV2(aclOutput)
 	if err != nil {
 		return false, err
 	}
 	return true, nil
 }
 
-func CheckPermWriteAcl(svc *s3.Client, b *Bucket) (bool, error) {
+func CheckPermWriteACL(svc *s3.Client, b *bucket.Bucket) (bool, error) {
 	// TODO: Ensure bucket exists
 	// TODO: Make sure this works with a bucket that allows PutACL. 400's returned always right now, is that because no creds?
 
 	grants := map[string][]string{}
-	if b.PermAuthUsersFullControl == PermissionAllowed {
-		grants["FULL_CONTROL"] = append(grants["FULL_CONTROL"], AuthUsersUri)
+	if b.PermAuthUsersFullControl == bucket.PermissionAllowed {
+		grants["FULL_CONTROL"] = append(grants["FULL_CONTROL"], groups.AuthUsersURI)
 	}
-	if b.PermAuthUsersWriteACL == PermissionAllowed {
-		grants["WRITE_ACP"] = append(grants["WRITE_ACP"], AuthUsersUri)
+	if b.PermAuthUsersWriteACL == bucket.PermissionAllowed {
+		grants["WRITE_ACP"] = append(grants["WRITE_ACP"], groups.AuthUsersURI)
 	}
-	if b.PermAuthUsersWrite == PermissionAllowed {
-		grants["WRITE"] = append(grants["WRITE"], AuthUsersUri)
+	if b.PermAuthUsersWrite == bucket.PermissionAllowed {
+		grants["WRITE"] = append(grants["WRITE"], groups.AuthUsersURI)
 	}
-	if b.PermAuthUsersReadACL == PermissionAllowed {
-		grants["READ_ACP"] = append(grants["READ_ACP"], AuthUsersUri)
+	if b.PermAuthUsersReadACL == bucket.PermissionAllowed {
+		grants["READ_ACP"] = append(grants["READ_ACP"], groups.AuthUsersURI)
 	}
-	if b.PermAuthUsersRead == PermissionAllowed {
-		grants["READ"] = append(grants["READ"], AuthUsersUri)
+	if b.PermAuthUsersRead == bucket.PermissionAllowed {
+		grants["READ"] = append(grants["READ"], groups.AuthUsersURI)
 	}
 
-	if b.PermAllUsersFullControl == PermissionAllowed {
-		grants["FULL_CONTROL"] = append(grants["FULL_CONTROL"], AllUsersUri)
+	if b.PermAllUsersFullControl == bucket.PermissionAllowed {
+		grants["FULL_CONTROL"] = append(grants["FULL_CONTROL"], groups.AllUsersURI)
 	}
-	if b.PermAllUsersWriteACL == PermissionAllowed {
-		grants["WRITE_ACP"] = append(grants["WRITE_ACP"], AllUsersUri)
+	if b.PermAllUsersWriteACL == bucket.PermissionAllowed {
+		grants["WRITE_ACP"] = append(grants["WRITE_ACP"], groups.AllUsersURI)
 	}
-	if b.PermAllUsersWrite == PermissionAllowed {
-		grants["WRITE"] = append(grants["WRITE"], AllUsersUri)
+	if b.PermAllUsersWrite == bucket.PermissionAllowed {
+		grants["WRITE"] = append(grants["WRITE"], groups.AllUsersURI)
 	}
-	if b.PermAllUsersReadACL == PermissionAllowed {
-		grants["READ_ACP"] = append(grants["READ_ACP"], AllUsersUri)
+	if b.PermAllUsersReadACL == bucket.PermissionAllowed {
+		grants["READ_ACP"] = append(grants["READ_ACP"], groups.AllUsersURI)
 	}
-	if b.PermAllUsersRead == PermissionAllowed {
-		grants["READ"] = append(grants["READ"], AllUsersUri)
+	if b.PermAllUsersRead == bucket.PermissionAllowed {
+		grants["READ"] = append(grants["READ"], groups.AllUsersURI)
 	}
 
 	_, err := svc.PutBucketAcl(context.TODO(), &s3.PutBucketAclInput{
@@ -91,9 +90,8 @@ func CheckPermWriteAcl(svc *s3.Client, b *Bucket) (bool, error) {
 			if re.HTTPStatusCode() == 400 || re.HTTPStatusCode() == 403 {
 				//fmt.Println("Access Denied!")
 				return false, nil
-			} else {
-				return false, err
 			}
+			return false, err
 		}
 		return false, err
 	}
@@ -101,7 +99,7 @@ func CheckPermWriteAcl(svc *s3.Client, b *Bucket) (bool, error) {
 	return true, nil
 }
 
-func CheckPermWrite(svc *s3.Client, bucket *Bucket) (bool, error) {
+func CheckPermWrite(svc *s3.Client, bucket *bucket.Bucket) (bool, error) {
 	// TODO: Ensure bucket exists
 	// TODO: What happens if we fail to clean up temp file
 
@@ -117,9 +115,8 @@ func CheckPermWrite(svc *s3.Client, bucket *Bucket) (bool, error) {
 		if errors.As(err, &re) {
 			if re.HTTPStatusCode() == 403 { // No permission
 				return false, nil
-			} else {
-				return false, err
 			}
+			return false, err
 		}
 	}
 
@@ -135,7 +132,7 @@ func CheckPermWrite(svc *s3.Client, bucket *Bucket) (bool, error) {
 	return true, nil
 }
 
-func CheckPermRead(svc *s3.Client, bucket *Bucket) (bool, error) {
+func CheckPermRead(svc *s3.Client, bucket *bucket.Bucket) (bool, error) {
 	_, err := svc.HeadBucket(context.TODO(), &s3.HeadBucketInput{Bucket: &bucket.Name})
 	if err != nil {
 		log.Debugf("[%v][CheckPermRead] err: %v", bucket.Name, err)
@@ -143,9 +140,8 @@ func CheckPermRead(svc *s3.Client, bucket *Bucket) (bool, error) {
 		if errors.As(err, &re) {
 			if re.HTTPStatusCode() == 403 { // No permission
 				return false, nil
-			} else {
-				return false, fmt.Errorf("[CheckPermRead] %s : %s : %w", bucket.Name, bucket.Region, err)
 			}
+			return false, fmt.Errorf("[CheckPermRead] %s : %s : %w", bucket.Name, bucket.Region, err)
 		}
 		return false, fmt.Errorf("[CheckPermRead] %s : %s : %w", bucket.Name, bucket.Region, err)
 	}
diff --git a/provider/aws.go b/provider/aws.go
index 2cd9877..158655d 100644
--- a/provider/aws.go
+++ b/provider/aws.go
@@ -19,13 +19,13 @@ import (
 	log "github.com/sirupsen/logrus"
 )
 
-type providerAWS struct {
+type AWS struct {
 	existsClient   *s3.Client
 	clients        *clientmap.ClientMap
 	hasCredentials bool
 }
 
-func (a *providerAWS) BucketExists(b *bucket.Bucket) (*bucket.Bucket, error) {
+func (a *AWS) BucketExists(b *bucket.Bucket) (*bucket.Bucket, error) {
 	b.Provider = a.Name()
 	if !bucket.IsValidS3BucketName(b.Name) {
 		return nil, errors.New("invalid bucket name")
@@ -51,13 +51,12 @@ func (a *providerAWS) BucketExists(b *bucket.Bucket) (*bucket.Bucket, error) {
 		b.Exists = bucket.BucketExists
 		b.Region = region
 		return b, nil
-	} else {
-		// Error wasn't BucketNotFound or 403
-		return b, err
 	}
+	// Error wasn't BucketNotFound or 403
+	return b, err
 }
 
-func (a *providerAWS) Scan(b *bucket.Bucket, doDestructiveChecks bool) error {
+func (a *AWS) Scan(b *bucket.Bucket, doDestructiveChecks bool) error {
 	anonClient, anonErr := a.getRegionClient(b.Region, false)
 	if anonErr != nil {
 		return anonErr
@@ -73,7 +72,7 @@ func (a *providerAWS) Scan(b *bucket.Bucket, doDestructiveChecks bool) error {
 	return checkPermissionsWithAuth(anonClient, nil, b, doDestructiveChecks)
 }
 
-func (a *providerAWS) Enumerate(b *bucket.Bucket) error {
+func (a *AWS) Enumerate(b *bucket.Bucket) error {
 	useCreds := false
 	if b.PermAuthUsersRead == bucket.PermissionAllowed {
 		useCreds = true
@@ -90,8 +89,8 @@ func (a *providerAWS) Enumerate(b *bucket.Bucket) error {
 	return nil
 }
 
-func NewProviderAWS() (*providerAWS, error) {
-	pa := new(providerAWS)
+func NewProviderAWS() (*AWS, error) {
+	pa := new(AWS)
 	client, err := pa.newAnonClient("us-east-1")
 	if err != nil {
 		return nil, err
@@ -99,7 +98,7 @@ func NewProviderAWS() (*providerAWS, error) {
 	pa.existsClient = client
 
 	// Seed the clients map with a common region
-	usEastClient, usErr := pa.newClient("us-east-1", nil)
+	usEastClient, usErr := pa.newClient("us-east-1")
 	if usErr != nil {
 		return nil, usErr
 	}
@@ -112,20 +111,20 @@ func NewProviderAWS() (*providerAWS, error) {
 	return pa, nil
 }
 
-func (a *providerAWS) AddressStyle() int {
+func (a *AWS) AddressStyle() int {
 	// AWS supports both styles
 	return VirtualHostStyle
 }
 
-func (*providerAWS) Insecure() bool {
+func (*AWS) Insecure() bool {
 	return false
 }
 
-func (*providerAWS) Name() string {
+func (*AWS) Name() string {
 	return "aws"
 }
 
-func (*providerAWS) newAnonClient(region string) (*s3.Client, error) {
+func (*AWS) newAnonClient(region string) (*s3.Client, error) {
 	cfg, err := config.LoadDefaultConfig(
 		context.TODO(),
 		config.WithHTTPClient(&http.Client{Transport: &http.Transport{
@@ -141,8 +140,8 @@ func (*providerAWS) newAnonClient(region string) (*s3.Client, error) {
 	return s3.NewFromConfig(cfg), nil
 }
 
-func (a *providerAWS) newClient(region string, profile *string) (*s3.Client, error) {
-	logFields := log.Fields{"profile": profile, "method": "aws.newClient", "region": region}
+func (a *AWS) newClient(region string) (*s3.Client, error) {
+	logFields := log.Fields{"method": "aws.newClient", "region": region}
 
 	configOpts := []func(*config.LoadOptions) error{
 		config.WithHTTPClient(&http.Client{Transport: &http.Transport{
@@ -151,9 +150,6 @@ func (a *providerAWS) newClient(region string, profile *string) (*s3.Client, err
 		config.WithRegion(region),
 		config.WithEC2IMDSClientEnableState(imds.ClientDisabled), // Otherwise we wait 4 seconds to IMDSv2 to timeout
 	}
-	if profile != nil {
-		configOpts = append(configOpts, config.WithSharedConfigProfile(*profile))
-	}
 
 	cfg, err := config.LoadDefaultConfig(
 		context.TODO(),
@@ -173,8 +169,8 @@ func (a *providerAWS) newClient(region string, profile *string) (*s3.Client, err
 	return s3.NewFromConfig(cfg), nil
 }
 
-// TODO: This method is copied from providerLinode
-func (a *providerAWS) getRegionClient(region string, useCreds bool) (*s3.Client, error) {
+// TODO: This method is copied from Linode
+func (a *AWS) getRegionClient(region string, useCreds bool) (*s3.Client, error) {
 	c := a.clients.Get(region, useCreds)
 	if c != nil {
 		return c, nil
@@ -184,7 +180,7 @@ func (a *providerAWS) getRegionClient(region string, useCreds bool) (*s3.Client,
 	var newClient *s3.Client
 	var newClientErr error
 	if useCreds {
-		newClient, newClientErr = a.newClient(region, nil)
+		newClient, newClientErr = a.newClient(region)
 	} else {
 		newClient, newClientErr = a.newAnonClient(region)
 	}
@@ -220,9 +216,9 @@ func checkPermissionsWithAuth(anonClient *s3.Client, authClient *s3.Client, b *b
 
 	// Check for auth READ_ACP permission. If allowed, exit
 	if authClient != nil {
-		authReadACL, authAclErr := permission.CheckPermReadACL(authClient, b)
-		if authAclErr != nil {
-			return fmt.Errorf("error occured while checking for auth ReadACL: %v", authAclErr.Error())
+		authReadACL, authACLErr := permission.CheckPermReadACL(authClient, b)
+		if authACLErr != nil {
+			return fmt.Errorf("error occurred while checking for auth ReadACL: %v", authACLErr.Error())
 		}
 		b.PermAuthUsersReadACL = bucket.Permission(authReadACL)
 		if b.PermAuthUsersReadACL == bucket.PermissionAllowed {
@@ -233,7 +229,7 @@ func checkPermissionsWithAuth(anonClient *s3.Client, authClient *s3.Client, b *b
 	// Check for anon READ
 	canRead, err := permission.CheckPermRead(anonClient, b)
 	if err != nil {
-		return fmt.Errorf("error occured while checking for anon READ: %v", err.Error())
+		return fmt.Errorf("error occurred while checking for anon READ: %v", err.Error())
 	}
 	b.PermAllUsersRead = bucket.Permission(canRead)
 
@@ -241,7 +237,7 @@ func checkPermissionsWithAuth(anonClient *s3.Client, authClient *s3.Client, b *b
 	if authClient != nil {
 		authCanRead, authReadErr := permission.CheckPermRead(authClient, b)
 		if authReadErr != nil {
-			return fmt.Errorf("error occured while checking for auth READ: %v", authReadErr.Error())
+			return fmt.Errorf("error occurred while checking for auth READ: %v", authReadErr.Error())
 		}
 		b.PermAuthUsersRead = bucket.Permission(authCanRead)
 	}
@@ -250,16 +246,16 @@ func checkPermissionsWithAuth(anonClient *s3.Client, authClient *s3.Client, b *b
 		// Check for WRITE permission
 		permWrite, writeErr := permission.CheckPermWrite(anonClient, b)
 		if writeErr != nil {
-			return fmt.Errorf("%v | error occured while checking for WRITE: %v", b.Name, writeErr.Error())
+			return fmt.Errorf("%v | error occurred while checking for WRITE: %v", b.Name, writeErr.Error())
 		}
 		b.PermAllUsersWrite = bucket.Permission(permWrite)
 
 		// Check for WRITE_ACP permission
-		permWriteAcl, writeAclErr := permission.CheckPermWriteAcl(anonClient, b)
-		if writeAclErr != nil {
-			return fmt.Errorf("error occured while checking for WriteACL: %v", writeAclErr.Error())
+		permWriteACL, writeACLErr := permission.CheckPermWriteACL(anonClient, b)
+		if writeACLErr != nil {
+			return fmt.Errorf("error occurred while checking for WriteACL: %v", writeACLErr.Error())
 		}
-		b.PermAllUsersWriteACL = bucket.Permission(permWriteAcl)
+		b.PermAllUsersWriteACL = bucket.Permission(permWriteACL)
 	}
 	return nil
 }
diff --git a/provider/custom.go b/provider/custom.go
index 0f7b5fe..8879771 100644
--- a/provider/custom.go
+++ b/provider/custom.go
@@ -99,8 +99,8 @@ func NewCustomProvider(addressStyle string, insecure bool, regions []string, end
 func (cp *CustomProvider) newClients() (*clientmap.ClientMap, error) {
 	clients := clientmap.WithCapacity(len(cp.regions))
 	for _, r := range cp.regions {
-		regionUrl := strings.Replace(cp.endpointFormat, "$REGION", r, -1)
-		client, err := newNonAWSClient(cp, regionUrl)
+		regionURL := strings.Replace(cp.endpointFormat, "$REGION", r, -1)
+		client, err := newNonAWSClient(cp, regionURL)
 		if err != nil {
 			return nil, err
 		}
diff --git a/provider/digitalocean.go b/provider/digitalocean.go
index 15d6e4f..3ba1200 100644
--- a/provider/digitalocean.go
+++ b/provider/digitalocean.go
@@ -9,23 +9,23 @@ import (
 	"github.com/sa7mon/s3scanner/provider/clientmap"
 )
 
-type providerDO struct {
+type DigitalOcean struct {
 	clients *clientmap.ClientMap
 }
 
-func (pdo providerDO) Insecure() bool {
+func (pdo DigitalOcean) Insecure() bool {
 	return false
 }
 
-func (pdo providerDO) Name() string {
+func (pdo DigitalOcean) Name() string {
 	return "digitalocean"
 }
 
-func (pdo providerDO) AddressStyle() int {
+func (pdo DigitalOcean) AddressStyle() int {
 	return PathStyle
 }
 
-func (pdo providerDO) BucketExists(b *bucket.Bucket) (*bucket.Bucket, error) {
+func (pdo DigitalOcean) BucketExists(b *bucket.Bucket) (*bucket.Bucket, error) {
 	b.Provider = pdo.Name()
 	exists, region, err := bucketExists(pdo.clients, b)
 	if err != nil {
@@ -41,12 +41,12 @@ func (pdo providerDO) BucketExists(b *bucket.Bucket) (*bucket.Bucket, error) {
 	return b, nil
 }
 
-func (pdo providerDO) Scan(bucket *bucket.Bucket, doDestructiveChecks bool) error {
+func (pdo DigitalOcean) Scan(bucket *bucket.Bucket, doDestructiveChecks bool) error {
 	client := pdo.getRegionClient(bucket.Region)
 	return checkPermissions(client, bucket, doDestructiveChecks)
 }
 
-func (pdo providerDO) Enumerate(b *bucket.Bucket) error {
+func (pdo DigitalOcean) Enumerate(b *bucket.Bucket) error {
 	if b.Exists != bucket.BucketExists {
 		return errors.New("bucket might not exist")
 	}
@@ -59,7 +59,7 @@ func (pdo providerDO) Enumerate(b *bucket.Bucket) error {
 	return nil
 }
 
-func (pdo *providerDO) newClients() (*clientmap.ClientMap, error) {
+func (pdo *DigitalOcean) newClients() (*clientmap.ClientMap, error) {
 	clients := clientmap.WithCapacity(len(ProviderRegions[pdo.Name()]))
 	for _, r := range ProviderRegions[pdo.Name()] {
 		client, err := newNonAWSClient(pdo, fmt.Sprintf("https://%s.digitaloceanspaces.com", r))
@@ -72,12 +72,12 @@ func (pdo *providerDO) newClients() (*clientmap.ClientMap, error) {
 	return clients, nil
 }
 
-func (pdo *providerDO) getRegionClient(region string) *s3.Client {
+func (pdo *DigitalOcean) getRegionClient(region string) *s3.Client {
 	return pdo.clients.Get(region, false)
 }
 
-func NewProviderDO() (*providerDO, error) {
-	pdo := new(providerDO)
+func NewDigitalOcean() (*DigitalOcean, error) {
+	pdo := new(DigitalOcean)
 
 	clients, err := pdo.newClients()
 	if err != nil {
diff --git a/provider/dreamhost.go b/provider/dreamhost.go
index d5d71a4..2976cd0 100644
--- a/provider/dreamhost.go
+++ b/provider/dreamhost.go
@@ -12,23 +12,23 @@ import (
 // Dreamhost responds strangely if you attempt to access a bucket named 'auth'
 var forbiddenBuckets = []string{"auth"}
 
-type ProviderDreamhost struct {
+type Dreamhost struct {
 	clients *clientmap.ClientMap
 }
 
-func (p ProviderDreamhost) Insecure() bool {
+func (p Dreamhost) Insecure() bool {
 	return false
 }
 
-func (ProviderDreamhost) Name() string {
+func (Dreamhost) Name() string {
 	return "dreamhost"
 }
 
-func (p ProviderDreamhost) AddressStyle() int {
+func (p Dreamhost) AddressStyle() int {
 	return PathStyle
 }
 
-func (p ProviderDreamhost) BucketExists(b *bucket.Bucket) (*bucket.Bucket, error) {
+func (p Dreamhost) BucketExists(b *bucket.Bucket) (*bucket.Bucket, error) {
 	// Check for forbidden name
 	for _, fb := range forbiddenBuckets {
 		if strings.ToLower(b.Name) == fb {
@@ -52,16 +52,16 @@ func (p ProviderDreamhost) BucketExists(b *bucket.Bucket) (*bucket.Bucket, error
 	return b, nil
 }
 
-func (p ProviderDreamhost) Scan(bucket *bucket.Bucket, doDestructiveChecks bool) error {
+func (p Dreamhost) Scan(bucket *bucket.Bucket, doDestructiveChecks bool) error {
 	client := p.getRegionClient(bucket.Region)
 	return checkPermissions(client, bucket, doDestructiveChecks)
 }
 
-func (p ProviderDreamhost) getRegionClient(region string) *s3.Client {
+func (p Dreamhost) getRegionClient(region string) *s3.Client {
 	return p.clients.Get(region, false)
 }
 
-func (p ProviderDreamhost) Enumerate(b *bucket.Bucket) error {
+func (p Dreamhost) Enumerate(b *bucket.Bucket) error {
 	if b.Exists != bucket.BucketExists {
 		return errors.New("bucket might not exist")
 	}
@@ -74,7 +74,7 @@ func (p ProviderDreamhost) Enumerate(b *bucket.Bucket) error {
 	return nil
 }
 
-func (p *ProviderDreamhost) newClients() (*clientmap.ClientMap, error) {
+func (p *Dreamhost) newClients() (*clientmap.ClientMap, error) {
 	clients := clientmap.WithCapacity(len(ProviderRegions[p.Name()]))
 	for _, r := range ProviderRegions[p.Name()] {
 		client, err := newNonAWSClient(p, fmt.Sprintf("https://objects-%s.dream.io", r))
@@ -87,8 +87,8 @@ func (p *ProviderDreamhost) newClients() (*clientmap.ClientMap, error) {
 	return clients, nil
 }
 
-func NewProviderDreamhost() (*ProviderDreamhost, error) {
-	pd := new(ProviderDreamhost)
+func NewProviderDreamhost() (*Dreamhost, error) {
+	pd := new(Dreamhost)
 
 	clients, err := pd.newClients()
 	if err != nil {
diff --git a/provider/linode.go b/provider/linode.go
index 0232427..f52e300 100644
--- a/provider/linode.go
+++ b/provider/linode.go
@@ -9,12 +9,12 @@ import (
 	"github.com/sa7mon/s3scanner/provider/clientmap"
 )
 
-type providerLinode struct {
+type Linode struct {
 	clients *clientmap.ClientMap
 }
 
-func NewProviderLinode() (*providerLinode, error) {
-	pl := new(providerLinode)
+func NewProviderLinode() (*Linode, error) {
+	pl := new(Linode)
 
 	clients, err := pl.newClients()
 	if err != nil {
@@ -24,11 +24,11 @@ func NewProviderLinode() (*providerLinode, error) {
 	return pl, nil
 }
 
-func (pl *providerLinode) getRegionClient(region string) *s3.Client {
+func (pl *Linode) getRegionClient(region string) *s3.Client {
 	return pl.clients.Get(region, false)
 }
 
-func (pl *providerLinode) BucketExists(b *bucket.Bucket) (*bucket.Bucket, error) {
+func (pl *Linode) BucketExists(b *bucket.Bucket) (*bucket.Bucket, error) {
 	b.Provider = pl.Name()
 	exists, region, err := bucketExists(pl.clients, b)
 	if err != nil {
@@ -44,7 +44,7 @@ func (pl *providerLinode) BucketExists(b *bucket.Bucket) (*bucket.Bucket, error)
 	return b, nil
 }
 
-func (pl *providerLinode) Enumerate(b *bucket.Bucket) error {
+func (pl *Linode) Enumerate(b *bucket.Bucket) error {
 	if b.Exists != bucket.BucketExists {
 		return errors.New("bucket might not exist")
 	}
@@ -57,7 +57,7 @@ func (pl *providerLinode) Enumerate(b *bucket.Bucket) error {
 	return nil
 }
 
-func (pl *providerLinode) newClients() (*clientmap.ClientMap, error) {
+func (pl *Linode) newClients() (*clientmap.ClientMap, error) {
 	clients := clientmap.WithCapacity(len(ProviderRegions[pl.Name()]))
 	for _, r := range ProviderRegions[pl.Name()] {
 		client, err := newNonAWSClient(pl, fmt.Sprintf("https://%s.linodeobjects.com", r))
@@ -70,19 +70,19 @@ func (pl *providerLinode) newClients() (*clientmap.ClientMap, error) {
 	return clients, nil
 }
 
-func (pl *providerLinode) Scan(b *bucket.Bucket, doDestructiveChecks bool) error {
+func (pl *Linode) Scan(b *bucket.Bucket, doDestructiveChecks bool) error {
 	client := pl.getRegionClient(b.Region)
 	return checkPermissions(client, b, doDestructiveChecks)
 }
 
-func (*providerLinode) Insecure() bool {
+func (*Linode) Insecure() bool {
 	return false
 }
 
-func (*providerLinode) Name() string {
+func (*Linode) Name() string {
 	return "linode"
 }
 
-func (*providerLinode) AddressStyle() int {
+func (*Linode) AddressStyle() int {
 	return VirtualHostStyle
 }
diff --git a/provider/providers.go b/provider/providers.go
index a2d4cb5..97507c8 100644
--- a/provider/providers.go
+++ b/provider/providers.go
@@ -59,7 +59,7 @@ func NewProvider(name string) (StorageProvider, error) {
 	case "aws":
 		provider, err = NewProviderAWS()
 	case "digitalocean":
-		provider, err = NewProviderDO()
+		provider, err = NewDigitalOcean()
 	case "dreamhost":
 		provider, err = NewProviderDreamhost()
 	case "gcp":
@@ -90,12 +90,6 @@ func newNonAWSClient(sp StorageProvider, regionURL string) (*s3.Client, error) {
 
 	cfg, err := config.LoadDefaultConfig(
 		context.TODO(),
-		config.WithEndpointResolverWithOptions(
-			aws.EndpointResolverWithOptionsFunc(func(service, region string, options ...interface{}) (aws.Endpoint, error) {
-				return aws.Endpoint{
-					URL: regionURL,
-				}, nil
-			})),
 		config.WithCredentialsProvider(aws.AnonymousCredentials{}),
 		config.WithHTTPClient(httpClient),
 		config.WithRegion("auto"),
@@ -109,6 +103,7 @@ func newNonAWSClient(sp StorageProvider, regionURL string) (*s3.Client, error) {
 		addrStyleOption = func(o *s3.Options) { o.UsePathStyle = true }
 	}
 
+	cfg.BaseEndpoint = aws.String(regionURL)
 	cfg.Credentials = nil // TODO: Remove and test
 	return s3.NewFromConfig(cfg, addrStyleOption), nil
 }
@@ -138,7 +133,7 @@ func enumerateListObjectsV2(client *s3.Client, b *bucket.Bucket) error {
 		}
 
 		for _, obj := range output.Contents {
-			b.Objects = append(b.Objects, bucket.BucketObject{Key: *obj.Key, Size: uint64(*obj.Size)})
+			b.Objects = append(b.Objects, bucket.Object{Key: *obj.Key, Size: uint64(*obj.Size)})
 			b.BucketSize += uint64(*obj.Size)
 		}
 
@@ -147,7 +142,7 @@ func enumerateListObjectsV2(client *s3.Client, b *bucket.Bucket) error {
 			break
 		}
 		continuationToken = output.NextContinuationToken
-		page += 1
+		page++
 		if page >= 5000 { // TODO: Should this limit be lowered?
 			return errors.New("more than 5000 pages of objects found. Skipping for now")
 		}
@@ -185,7 +180,7 @@ func checkPermissions(client *s3.Client, b *bucket.Bucket, doDestructiveChecks b
 	// Check for READ permission
 	canRead, err := permission.CheckPermRead(client, b)
 	if err != nil {
-		return fmt.Errorf("%v | error occured while checking for READ: %v", b.Name, err.Error())
+		return fmt.Errorf("%v | error occurred while checking for READ: %v", b.Name, err.Error())
 	}
 	b.PermAllUsersRead = bucket.Permission(canRead)
 
@@ -193,16 +188,16 @@ func checkPermissions(client *s3.Client, b *bucket.Bucket, doDestructiveChecks b
 		// Check for WRITE permission
 		permWrite, writeErr := permission.CheckPermWrite(client, b)
 		if writeErr != nil {
-			return fmt.Errorf("%v | error occured while checking for WRITE: %v", b.Name, writeErr.Error())
+			return fmt.Errorf("%v | error occurred while checking for WRITE: %v", b.Name, writeErr.Error())
 		}
 		b.PermAllUsersWrite = bucket.Permission(permWrite)
 
 		// Check for WRITE_ACP permission
-		permWriteAcl, writeAclErr := permission.CheckPermWriteAcl(client, b)
-		if writeAclErr != nil {
-			return fmt.Errorf("error occured while checking for WriteACL: %v", writeAclErr.Error())
+		permWriteACL, writeACLErr := permission.CheckPermWriteACL(client, b)
+		if writeACLErr != nil {
+			return fmt.Errorf("error occurred while checking for WriteACL: %v", writeACLErr.Error())
 		}
-		b.PermAllUsersWriteACL = bucket.Permission(permWriteAcl)
+		b.PermAllUsersWriteACL = bucket.Permission(permWriteACL)
 	}
 	return nil
 }
@@ -212,7 +207,7 @@ func bucketExists(clients *clientmap.ClientMap, b *bucket.Bucket) (bool, string,
 	results := make(chan bucketCheckResult, clients.Len())
 	e := make(chan error, 1)
 
-	clients.Each(func(region string, credentials bool, client *s3.Client) {
+	clients.Each(func(region string, _ bool, client *s3.Client) {
 		go func(bucketName string, client *s3.Client, region string) {
 			logFields := log.Fields{
 				"bucket_name": b.Name,
diff --git a/provider/scaleway.go b/provider/scaleway.go
index 4c3c944..7fe5547 100644
--- a/provider/scaleway.go
+++ b/provider/scaleway.go
@@ -7,12 +7,12 @@ import (
 	"github.com/sa7mon/s3scanner/provider/clientmap"
 )
 
-type providerScaleway struct {
+type Scaleway struct {
 	clients *clientmap.ClientMap
 }
 
-func NewProviderScaleway() (*providerScaleway, error) {
-	sc := new(providerScaleway)
+func NewProviderScaleway() (*Scaleway, error) {
+	sc := new(Scaleway)
 
 	clients, err := sc.newClients()
 	if err != nil {
@@ -22,7 +22,7 @@ func NewProviderScaleway() (*providerScaleway, error) {
 	return sc, nil
 }
 
-func (sc *providerScaleway) newClients() (*clientmap.ClientMap, error) {
+func (sc *Scaleway) newClients() (*clientmap.ClientMap, error) {
 	clients := clientmap.WithCapacity(len(ProviderRegions[sc.Name()]))
 	for _, r := range ProviderRegions[sc.Name()] {
 		client, err := newNonAWSClient(sc, fmt.Sprintf("https://s3.%s.scw.cloud", r))
@@ -35,24 +35,24 @@ func (sc *providerScaleway) newClients() (*clientmap.ClientMap, error) {
 	return clients, nil
 }
 
-func (sc *providerScaleway) Scan(b *bucket.Bucket, doDestructiveChecks bool) error {
+func (sc *Scaleway) Scan(b *bucket.Bucket, doDestructiveChecks bool) error {
 	client := sc.clients.Get(b.Region, false)
 	return checkPermissions(client, b, doDestructiveChecks)
 }
 
-func (*providerScaleway) Insecure() bool {
+func (*Scaleway) Insecure() bool {
 	return false
 }
 
-func (*providerScaleway) Name() string {
+func (*Scaleway) Name() string {
 	return "scaleway"
 }
 
-func (*providerScaleway) AddressStyle() int {
+func (*Scaleway) AddressStyle() int {
 	return PathStyle
 }
 
-func (sc *providerScaleway) BucketExists(b *bucket.Bucket) (*bucket.Bucket, error) {
+func (sc *Scaleway) BucketExists(b *bucket.Bucket) (*bucket.Bucket, error) {
 	b.Provider = sc.Name()
 	exists, region, err := bucketExists(sc.clients, b)
 	if err != nil {
@@ -68,7 +68,7 @@ func (sc *providerScaleway) BucketExists(b *bucket.Bucket) (*bucket.Bucket, erro
 	return b, nil
 }
 
-func (sc *providerScaleway) Enumerate(b *bucket.Bucket) error {
+func (sc *Scaleway) Enumerate(b *bucket.Bucket) error {
 	if b.Exists != bucket.BucketExists {
 		return errors.New("bucket might not exist")
 	}
diff --git a/worker/mq_worker.go b/worker/mq_worker.go
index 2e097df..13039b5 100644
--- a/worker/mq_worker.go
+++ b/worker/mq_worker.go
@@ -19,19 +19,19 @@ func FailOnError(err error, msg string) {
 	}
 }
 
-func WorkMQ(threadId int, wg *sync.WaitGroup, conn *amqp.Connection, provider provider.StorageProvider, queue string,
+func WorkMQ(threadID int, wg *sync.WaitGroup, conn *amqp.Connection, provider provider.StorageProvider, queue string,
 	threads int, doEnumerate bool, writeToDB bool) {
 	_, once := os.LookupEnv("TEST_MQ") // If we're being tested, exit after one bucket is scanned
 	defer wg.Done()
 
 	// Wrap the whole thing in a for (while) loop so if the mq server kills the channel, we start it up again
 	for {
-		ch, chErr := mq.Connect(conn, queue, threads, threadId)
+		ch, chErr := mq.Connect(conn, queue, threads, threadID)
 		if chErr != nil {
 			FailOnError(chErr, "couldn't connect to message queue")
 		}
 
-		msgs, consumeErr := ch.Consume(queue, fmt.Sprintf("%s_%v", queue, threadId), false, false, false, false, nil)
+		msgs, consumeErr := ch.Consume(queue, fmt.Sprintf("%s_%v", queue, threadID), false, false, false, false, nil)
 		if consumeErr != nil {
 			log.Error(fmt.Errorf("failed to register a consumer: %w", consumeErr))
 			return


Test patch:
diff --git a/bucket/bucket_test.go b/bucket/bucket_test.go
index 6d8c3c8..23b66dd 100644
--- a/bucket/bucket_test.go
+++ b/bucket/bucket_test.go
@@ -246,8 +246,9 @@ func TestBucket_ParseAclOutputv2(t *testing.T) {
 
 	for _, tt := range tests {
 		t.Run(tt.name, func(t2 *testing.T) {
+			t2.Parallel()
 			b := NewBucket("mytestbucket")
-			err := b.ParseAclOutputv2(&tt.acl)
+			err := b.ParseACLOutputV2(&tt.acl)
 			assert.Nil(t2, err)
 
 			for grantee, perms := range tt.expectedAllowed {
@@ -382,8 +383,8 @@ func TestBucket_String(t *testing.T) {
 
 	for _, tt := range tests {
 		t.Run(tt.name, func(t2 *testing.T) {
+			t2.Parallel()
 			assert.Equal(t2, tt.string, tt.bucket.String())
 		})
 	}
-
 }
diff --git a/cmd/s3scanner/args_test.go b/cmd/s3scanner/args_test.go
index 102f9af..36072da 100644
--- a/cmd/s3scanner/args_test.go
+++ b/cmd/s3scanner/args_test.go
@@ -60,7 +60,7 @@ func TestArgCollection_Validate(t *testing.T) {
 func TestValidateConfig(t *testing.T) {
 	a := ArgCollection{
 		DoEnumerate:  false,
-		Json:         false,
+		JSON:         false,
 		ProviderFlag: "custom",
 		UseMq:        true,
 		WriteToDB:    true,
@@ -75,7 +75,7 @@ func TestValidateConfig(t *testing.T) {
 func TestValidateConfig_NotFound(t *testing.T) {
 	a := ArgCollection{
 		DoEnumerate:  false,
-		Json:         false,
+		JSON:         false,
 		ProviderFlag: "custom",
 		UseMq:        true,
 		WriteToDB:    true,
diff --git a/db/database_test.go b/db/database_test.go
index f115b2e..730c040 100644
--- a/db/database_test.go
+++ b/db/database_test.go
@@ -1,10 +1,11 @@
 package db
 
 import (
+	"crypto/rand"
 	"fmt"
 	"github.com/sa7mon/s3scanner/bucket"
 	"github.com/stretchr/testify/assert"
-	"math/rand"
+	"math/big"
 	"os"
 	"testing"
 	"time"
@@ -12,10 +13,16 @@ import (
 
 var letterRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_/0123456789.")
 
+func randPermission() uint8 {
+	randInt, _ := rand.Int(rand.Reader, big.NewInt(2))
+	return uint8(randInt.Int64())
+}
+
 func RandStringRunes(n int) string {
 	b := make([]rune, n)
 	for i := range b {
-		b[i] = letterRunes[rand.Intn(len(letterRunes))]
+		randInt, _ := rand.Int(rand.Reader, big.NewInt(int64(len(letterRunes))))
+		b[i] = letterRunes[randInt.Int64()]
 	}
 	return string(b)
 }
@@ -29,22 +36,24 @@ func makeRandomBucket(numObjects int) bucket.Bucket {
 		ObjectsEnumerated:        true,
 		Provider:                 "aws",
 		BucketSize:               0,
-		PermAuthUsersRead:        uint8(rand.Intn(2)),
-		PermAuthUsersWrite:       uint8(rand.Intn(2)),
-		PermAuthUsersReadACL:     uint8(rand.Intn(2)),
-		PermAuthUsersWriteACL:    uint8(rand.Intn(2)),
-		PermAuthUsersFullControl: uint8(rand.Intn(2)),
-		PermAllUsersRead:         uint8(rand.Intn(2)),
-		PermAllUsersWrite:        uint8(rand.Intn(2)),
-		PermAllUsersReadACL:      uint8(rand.Intn(2)),
-		PermAllUsersWriteACL:     uint8(rand.Intn(2)),
-		PermAllUsersFullControl:  uint8(rand.Intn(2)),
+		PermAuthUsersRead:        randPermission(),
+		PermAuthUsersWrite:       randPermission(),
+		PermAuthUsersReadACL:     randPermission(),
+		PermAuthUsersWriteACL:    randPermission(),
+		PermAuthUsersFullControl: randPermission(),
+		PermAllUsersRead:         randPermission(),
+		PermAllUsersWrite:        randPermission(),
+		PermAllUsersReadACL:      randPermission(),
+		PermAllUsersWriteACL:     randPermission(),
+		PermAllUsersFullControl:  randPermission(),
 	}
-	bucketObjects := make([]bucket.BucketObject, numObjects)
+	bucketObjects := make([]bucket.Object, numObjects)
 	for j := 0; j < numObjects; j++ {
-		obj := bucket.BucketObject{
+		randSize, _ := rand.Int(rand.Reader, big.NewInt(250000000000))
+
+		obj := bucket.Object{
 			Key:  RandStringRunes(50),
-			Size: uint64(rand.Intn(250000000000)), // 25GB max
+			Size: randSize.Uint64(), // 25GB max
 		}
 		c.BucketSize += obj.Size
 		bucketObjects[j] = obj
@@ -54,8 +63,6 @@ func makeRandomBucket(numObjects int) bucket.Bucket {
 }
 
 func BenchmarkStoreBucket(b *testing.B) {
-	rand.Seed(time.Now().UnixNano())
-
 	// Connect to database
 	err := Connect("host=localhost user=postgres password=example dbname=postgres port=5432 sslmode=disable TimeZone=America/Chicago", false)
 	if err != nil {
diff --git a/permission/permission_test.go b/permission/permission_test.go
index 24ca65e..2cbad2a 100644
--- a/permission/permission_test.go
+++ b/permission/permission_test.go
@@ -171,7 +171,7 @@ func TestCheckPermWriteACL(t *testing.T) {
 	}
 
 	// Assert we can read the bucket without creds
-	permWrite, err := CheckPermWriteAcl(east1AnonClient, &readAllowedBucket)
+	permWrite, err := CheckPermWriteACL(east1AnonClient, &readAllowedBucket)
 	assert.Nil(t, err)
 	assert.False(t, permWrite)
 }
diff --git a/provider/aws_test.go b/provider/aws_test.go
index 61b0896..9d18aea 100644
--- a/provider/aws_test.go
+++ b/provider/aws_test.go
@@ -119,6 +119,7 @@ func TestProviderAWS_Scan(t *testing.T) {
 
 	for _, testCase := range testCases {
 		t.Run(testCase.b.Name, func(t2 *testing.T) {
+			t2.Parallel()
 			b, err := p.BucketExists(&testCase.b)
 			if err != nil {
 				t2.Error(err)
diff --git a/provider/custom_test.go b/provider/custom_test.go
index 5cb21d6..3c62d24 100644
--- a/provider/custom_test.go
+++ b/provider/custom_test.go
@@ -23,6 +23,7 @@ func TestCustomProvider_BucketExists(t *testing.T) {
 
 	for _, tt := range tests {
 		t.Run(tt.name, func(t2 *testing.T) {
+			t2.Parallel()
 			gb, err := p.BucketExists(&tt.b)
 			assert.Nil(t2, err)
 			assert.Equal(t2, tt.exists, gb.Exists)
diff --git a/provider/digitalocean_test.go b/provider/digitalocean_test.go
index 668068b..6c0ee76 100644
--- a/provider/digitalocean_test.go
+++ b/provider/digitalocean_test.go
@@ -9,7 +9,7 @@ import (
 func TestScanBucketPermissions_DO(t *testing.T) {
 	t.Parallel()
 
-	do, doErr := NewProviderDO()
+	do, doErr := NewDigitalOcean()
 	assert.Nil(t, doErr)
 
 	// Bucket exists but isn't open
@@ -56,5 +56,4 @@ func TestScanBucketPermissions_DO(t *testing.T) {
 	//}
 	//assert.Equal(t, bucket.BucketExists, dotBucket.Exists)
 	//assert.Equal(t, bucket.PermissionAllowed, dotBucket.PermAllUsersRead)
-
 }
diff --git a/provider/providers_test.go b/provider/providers_test.go
index 5ae49fd..5e05243 100644
--- a/provider/providers_test.go
+++ b/provider/providers_test.go
@@ -28,7 +28,7 @@ func TestMain(m *testing.M) {
 	}
 	providers["custom"] = provider
 
-	provider, err = NewProviderDO()
+	provider, err = NewDigitalOcean()
 	if err != nil {
 		panic(err)
 	}
@@ -71,7 +71,7 @@ func TestProvider_EnumerateListObjectsV2_short(t *testing.T) {
 	t.Parallel()
 	p, pErr := NewProviderAWS()
 	failIfError(t, pErr)
-	c, cErr := p.newClient("us-east-1", nil)
+	c, cErr := p.newClient("us-east-1")
 	failIfError(t, cErr)
 
 	// Bucket with "page" of objects (<1k keys)
@@ -91,7 +91,7 @@ func Test_EnumerateListObjectsV2_long(t *testing.T) {
 	t.Parallel()
 	p, pErr := NewProviderAWS()
 	failIfError(t, pErr)
-	c, cErr := p.newClient("us-east-1", nil)
+	c, cErr := p.newClient("us-east-1")
 	failIfError(t, cErr)
 
 	// Bucket with more than 1k objects
@@ -108,7 +108,6 @@ func Test_EnumerateListObjectsV2_long(t *testing.T) {
 
 func Test_StorageProvider_Statics(t *testing.T) {
 	t.Parallel()
-
 	var tests = []struct {
 		name         string
 		provider     StorageProvider
@@ -125,6 +124,7 @@ func Test_StorageProvider_Statics(t *testing.T) {
 
 	for _, tt := range tests {
 		t.Run(tt.name, func(t2 *testing.T) {
+			t2.Parallel()
 			assert.Equal(t2, tt.insecure, tt.provider.Insecure())
 			assert.Equal(t2, tt.addressStyle, tt.provider.AddressStyle())
 		})
@@ -133,7 +133,6 @@ func Test_StorageProvider_Statics(t *testing.T) {
 
 func Test_StorageProvider_BucketExists(t *testing.T) {
 	t.Parallel()
-
 	var tests = []struct {
 		name       string
 		provider   StorageProvider
@@ -150,6 +149,7 @@ func Test_StorageProvider_BucketExists(t *testing.T) {
 
 	for _, tt := range tests {
 		t.Run(tt.name, func(t2 *testing.T) {
+			t2.Parallel()
 			gb, err := tt.provider.BucketExists(&tt.goodBucket)
 			assert.Nil(t2, err)
 			assert.Equal(t2, bucket.BucketExists, gb.Exists)
@@ -157,14 +157,12 @@ func Test_StorageProvider_BucketExists(t *testing.T) {
 			bb, err := tt.provider.BucketExists(&tt.badBucket)
 			assert.Nil(t2, err)
 			assert.Equal(t2, bucket.BucketNotExist, bb.Exists)
-
 		})
 	}
 }
 
 func Test_StorageProvider_Enum(t *testing.T) {
 	t.Parallel()
-
 	var tests = []struct {
 		name       string
 		provider   StorageProvider
@@ -183,6 +181,7 @@ func Test_StorageProvider_Enum(t *testing.T) {
 
 	for _, tt := range tests {
 		t.Run(tt.name, func(t2 *testing.T) {
+			t2.Parallel()
 			gb, err := tt.provider.BucketExists(&tt.goodBucket)
 			assert.Nil(t2, err)
 			err = tt.provider.Scan(&tt.goodBucket, false)
@@ -197,7 +196,6 @@ func Test_StorageProvider_Enum(t *testing.T) {
 
 func Test_StorageProvider_Scan(t *testing.T) {
 	t.Parallel()
-
 	var tests = []struct {
 		name        string
 		provider    StorageProvider
@@ -216,6 +214,7 @@ func Test_StorageProvider_Scan(t *testing.T) {
 
 	for _, tt := range tests {
 		t.Run(tt.name, func(t2 *testing.T) {
+			t2.Parallel()
 			gb, err := tt.provider.BucketExists(&tt.bucket)
 			scanErr := tt.provider.Scan(gb, false)
 			assert.Nil(t2, err)
diff --git a/worker/worker_test.go b/worker/worker_test.go
index 0698aa2..db50707 100644
--- a/worker/worker_test.go
+++ b/worker/worker_test.go
@@ -79,5 +79,4 @@ func TestLogs(t *testing.T) {
 			assert.Contains(t2, buf.String(), tt.expected)
 		})
 	}
-
 }