)
```go
-func (h Hunter) Hunt() error
+func (h *Hunter) Hunt() (scan.Report, error)
```
-Hunt walks over the filesystem at the configured path\, looking for sensitive information
+Hunt walks over the filesystem at the configured path\, looking for sensitive information\.
-Example
-
-
-This method also accepts custom output formats using go template/html\. So if you don't like yaml or json\, you can format to your heart's content\.
+### func \(\*Hunter\) [Report]()
```go
-{
- fs := afero.NewMemMapFs()
- f, err := fs.Create("example.yaml")
- if err != nil {
- panic(err)
- }
- defer f.Close()
-
- _, err = f.Write([]byte(`https://github.com/brittonhayes/pillager`))
- if err != nil {
- panic(err)
- }
-
- config := NewConfig(fs, "./", true, rules.Load(""), CustomFormat, DefaultTemplate, 5)
- h := NewHunter(config)
- _ = h.Hunt()
-}
+func (h *Hunter) Report(w io.Writer, results scan.Report) error
```
-
-
-
-Example
-
-
-This is an example of how to run a scan on a single file to look for email addresses\. We're using an in\-memory file system for simplicity\, but this supports using an actual file system as well\.
-
-```go
-{
- fs := afero.NewMemMapFs()
- f, err := fs.Create("example.toml")
- if err != nil {
- panic(err)
- }
- defer f.Close()
-
- _, err = f.Write([]byte(`example@email.com`))
- if err != nil {
- panic(err)
- }
-
- config := NewConfig(fs, "./", true, rules.Load(""), StringToFormat("yaml"), DefaultTemplate, 5)
- h := NewHunter(config)
- _ = h.Hunt()
-}
-```
-
-
-
-
-Example
-
-
-This method accepts json output format as well
-
-```go
-{
- fs := afero.NewMemMapFs()
- f, err := fs.Create("fake.json")
- if err != nil {
- panic(err)
- }
- defer f.Close()
- _, err = f.Write([]byte(`git@github.com:brittonhayes/pillager.git`))
- if err != nil {
- panic(err)
- }
-
- config := NewConfig(fs, ".", true, rules.Load(""), JSONFormat, DefaultTemplate, 5)
- h := NewHunter(config)
- _ = h.Hunt()
-}
-```
-
-
-
-
-Example
-
-
-Hunter will also look personally identifiable info in TOML
-
-```go
-{
- fs := afero.NewMemMapFs()
- f, err := fs.Create("fake.toml")
- if err != nil {
- panic(err)
- }
- defer f.Close()
- _, err = f.Write([]byte(`fakeperson@example.com`))
- if err != nil {
- panic(err)
- }
-
- config := NewConfig(fs, ".", true, rules.Load(""), JSONFormat, DefaultTemplate, 5)
-
- h := NewHunter(config)
- _ = h.Hunt()
-}
-```
-
-
-
-
-## type [Hunting]()
-
-Hunting is the primary API interface for the hunter package
-
-```go
-type Hunting interface {
- Hunt() error
-}
-```
+Report prints out the Findings in the preferred output format\.
diff --git a/pkg/hunter/config.go b/pkg/hunter/config.go
deleted file mode 100644
index 0284d5f..0000000
--- a/pkg/hunter/config.go
+++ /dev/null
@@ -1,68 +0,0 @@
-package hunter
-
-import (
- "fmt"
-
- "github.com/brittonhayes/pillager/internal/validate"
- "github.com/brittonhayes/pillager/pkg/rules"
- "github.com/spf13/afero"
- gitleaks "github.com/zricethezav/gitleaks/v7/config"
-)
-
-var _ Configer = &Config{}
-
-// Config takes all of the configurable
-// parameters for a Hunter
-type Config struct {
- System afero.Fs
- BasePath string
- Verbose bool
- Workers int
- Gitleaks gitleaks.Config
- Format Format
- Template string
-}
-
-type Configer interface {
- Default() *Config
- Validate() (err error)
-}
-
-// NewConfig generates a new config for the Hunter
-func NewConfig(fs afero.Fs, path string, verbose bool, gitleaks gitleaks.Config, format Format, template string, workers int) *Config {
- p := validate.New().Path(fs, path)
- return &Config{
- System: fs,
- BasePath: p,
- Verbose: verbose,
- Gitleaks: gitleaks,
- Format: format,
- Template: template,
- Workers: workers,
- }
-}
-
-// Default loads the default configuration
-// for the Hunter
-func (c *Config) Default() *Config {
- fs := afero.NewOsFs()
- v := validate.New()
- return &Config{
- System: fs,
- BasePath: v.Path(fs, "."),
- Verbose: false,
- Gitleaks: rules.Load(""),
- Format: JSONFormat,
- }
-}
-
-func (c *Config) Validate() (err error) {
- if c.System == nil {
- err = fmt.Errorf("missing filesystem in Hunter Config")
- }
-
- if c.Gitleaks.Rules == nil {
- err = fmt.Errorf("no gitleaks config provided")
- }
- return
-}
diff --git a/pkg/hunter/format.go b/pkg/hunter/format.go
deleted file mode 100644
index a0e839d..0000000
--- a/pkg/hunter/format.go
+++ /dev/null
@@ -1,40 +0,0 @@
-package hunter
-
-import "strings"
-
-const (
- JSONFormat Format = iota + 1
- YAMLFormat
- TableFormat
- HTMLFormat
- HTMLTableFormat
- MarkdownFormat
- CustomFormat
-)
-
-type Format int
-
-func (f Format) String() string {
- return [...]string{"json", "yaml", "table", "html", "html-table", "markdown", "custom"}[f]
-}
-
-// StringToFormat takes in a string representation of the preferred
-// output format and returns to enum equivalent
-func StringToFormat(s string) Format {
- switch strings.ToLower(s) {
- case "yaml":
- return YAMLFormat
- case "table":
- return TableFormat
- case "html":
- return HTMLFormat
- case "html-table":
- return HTMLTableFormat
- case "markdown":
- return MarkdownFormat
- case "custom":
- return CustomFormat
- default:
- return JSONFormat
- }
-}
diff --git a/pkg/hunter/hound.go b/pkg/hunter/hound.go
deleted file mode 100644
index 04ad31f..0000000
--- a/pkg/hunter/hound.go
+++ /dev/null
@@ -1,73 +0,0 @@
-package hunter
-
-import (
- "encoding/json"
- "fmt"
- "log"
- "os"
-
- "github.com/brittonhayes/pillager/templates"
- "github.com/ghodss/yaml"
- "github.com/zricethezav/gitleaks/v7/scan"
-)
-
-var _ Hounder = &Hound{}
-
-// The Hounder interface defines the available methods
-// for instances of the Hound type
-type Hounder interface {
- Howl(findings scan.Report)
-}
-
-// A Hound performs the file inspection and returns the results
-type Hound struct {
- Config *Config
- Findings scan.Report `json:"findings"`
-}
-
-// NewHound creates an instance of the Hound type
-func NewHound(c *Config) *Hound {
- if c == nil {
- var config Config
- return &Hound{config.Default(), scan.Report{}}
- }
- if c.System == nil {
- log.Fatal("Missing filesystem in Hunter Config")
- }
-
- return &Hound{c, scan.Report{}}
-}
-
-// Howl prints out the Findings from the Hound in the preferred output format
-func (h *Hound) Howl(findings scan.Report) {
- if h.Config.Template != "" {
- h.Config.Format = CustomFormat
- }
- switch h.Config.Format {
- case JSONFormat:
- b, err := json.Marshal(&findings.Leaks)
- if err != nil {
- log.Fatal("Failed to unmarshal findings")
- }
- fmt.Println(string(b))
- case YAMLFormat:
- b, err := yaml.Marshal(&findings.Leaks)
- if err != nil {
- fmt.Printf("err: %v\n", err)
- return
- }
- fmt.Println(string(b))
- case HTMLFormat:
- RenderTemplate(os.Stdout, templates.HTML, findings)
- case HTMLTableFormat:
- RenderTemplate(os.Stdout, templates.HTMLTable, findings)
- case MarkdownFormat:
- RenderTemplate(os.Stdout, templates.Markdown, findings)
- case TableFormat:
- RenderTemplate(os.Stdout, templates.Table, findings)
- case CustomFormat:
- RenderTemplate(os.Stdout, h.Config.Template, findings)
- default:
- RenderTemplate(os.Stdout, templates.Simple, findings)
- }
-}
diff --git a/pkg/hunter/hound_test.go b/pkg/hunter/hound_test.go
deleted file mode 100644
index 725f619..0000000
--- a/pkg/hunter/hound_test.go
+++ /dev/null
@@ -1,26 +0,0 @@
-package hunter
-
-import (
- "github.com/brittonhayes/pillager/pkg/rules"
- "github.com/spf13/afero"
- "github.com/zricethezav/gitleaks/v7/scan"
-)
-
-// Here is an example of utilizing the Howl function
-// on a slice of findings. The Howl method is the final
-// method in the hunting process. It takes whatever
-// has been found and outputs it for the user.
-func ExampleHound_Howl_json() {
- h := NewHound(&Config{
- System: afero.NewMemMapFs(),
- Gitleaks: rules.Load(""),
- Format: JSONFormat,
- })
- findings := scan.Report{
- Leaks: []scan.Leak{
- {Line: "person@email.com", LineNumber: 16, Offender: "person@email.com", Rule: "Email Addresses"},
- },
- }
-
- h.Howl(findings)
-}
diff --git a/pkg/hunter/hunter.go b/pkg/hunter/hunter.go
index 4e8673a..6d3606e 100644
--- a/pkg/hunter/hunter.go
+++ b/pkg/hunter/hunter.go
@@ -1,62 +1,82 @@
package hunter
import (
+ "encoding/json"
"fmt"
- "os"
+ "io"
+ "github.com/brittonhayes/pillager"
+ "github.com/brittonhayes/pillager/pkg/format"
+ "github.com/brittonhayes/pillager/templates"
+ "github.com/rs/zerolog/log"
"github.com/zricethezav/gitleaks/v7/config"
"github.com/zricethezav/gitleaks/v7/options"
"github.com/zricethezav/gitleaks/v7/scan"
+ "gopkg.in/yaml.v2"
)
-// Hunter holds the required fields to implement
-// the Hunting interface and utilize the hunter package
+// Hunter is the secret scanner.
type Hunter struct {
- Config *Config
- Hound *Hound
+ *pillager.Config
}
-var _ Hunting = Hunter{}
-
-// Hunting is the primary API interface for the hunter package
-type Hunting interface {
- Hunt() error
+// New creates an instance of the Hunter.
+func New(opts ...pillager.ConfigOption) (*Hunter, error) {
+ return &Hunter{
+ Config: pillager.NewConfig(opts...),
+ }, nil
}
-// NewHunter creates an instance of the Hunter type
-func NewHunter(c *Config) *Hunter {
- if c == nil {
- var conf Config
- return &Hunter{conf.Default(), NewHound(conf.Default())}
- }
+// Hunt walks over the filesystem at the configured path, looking for sensitive information.
+func (h *Hunter) Hunt() (scan.Report, error) {
+ opt := options.Options{Path: h.ScanPath, Verbose: h.Verbose, Threads: h.Workers}
+ conf := config.Config{Allowlist: h.Gitleaks.Allowlist, Rules: h.Gitleaks.Rules}
+
+ scanner := scan.NewNoGitScanner(opt, conf)
+ log.Debug().Str("style", h.Style.String()).Bool("verbose", h.Verbose).Msg("scanner created")
- err := c.Validate()
+ report, err := scanner.Scan()
if err != nil {
- fmt.Println(err.Error())
- os.Exit(1)
+ return scan.Report{}, err
}
- return &Hunter{c, NewHound(c)}
+ return report, nil
}
-// Hunt walks over the filesystem at the configured path, looking for sensitive information
-func (h Hunter) Hunt() error {
- h.Hound = NewHound(h.Config)
- if _, err := os.Stat(h.Config.BasePath); os.IsNotExist(err) {
- return fmt.Errorf("config file does not exist")
- }
+// Report prints out the Findings in the preferred output format.
+func (h *Hunter) Report(w io.Writer, results scan.Report) error {
+ switch h.Style {
+ case format.StyleJSON:
+ encoder := json.NewEncoder(w)
+ err := encoder.Encode(&results.Leaks)
+ if err != nil {
+ return err
+ }
- opt := options.Options{Path: h.Config.BasePath, Verbose: h.Config.Verbose, Threads: h.Config.Workers}
- conf := config.Config{Allowlist: h.Config.Gitleaks.Allowlist, Rules: h.Config.Gitleaks.Rules}
+ case format.StyleYAML:
+ b, err := yaml.Marshal(&results.Leaks)
+ if err != nil {
+ return err
+ }
+ fmt.Fprintf(w, "%s\n", string(b))
- scanner := scan.NewNoGitScanner(opt, conf)
- report, err := scanner.Scan()
- if err != nil {
- return err
- }
+ case format.StyleHTML:
+ return format.RenderTemplate(w, templates.HTML, results)
+
+ case format.StyleHTMLTable:
+ return format.RenderTemplate(w, templates.HTMLTable, results)
+
+ case format.StyleMarkdown:
+ return format.RenderTemplate(w, templates.Markdown, results)
+
+ case format.StyleTable:
+ return format.RenderTemplate(w, templates.Table, results)
+
+ case format.StyleCustom:
+ return format.RenderTemplate(w, h.Template, results)
- if !opt.Verbose {
- h.Hound.Howl(report)
+ default:
+ return format.RenderTemplate(w, templates.Simple, results)
}
return nil
diff --git a/pkg/hunter/hunter_test.go b/pkg/hunter/hunter_test.go
deleted file mode 100644
index a70b865..0000000
--- a/pkg/hunter/hunter_test.go
+++ /dev/null
@@ -1,86 +0,0 @@
-package hunter
-
-import (
- "github.com/brittonhayes/pillager/pkg/rules"
- "github.com/spf13/afero"
-)
-
-// This is an example of how to run a scan on a single file to look for
-// email addresses. We're using an in-memory file system for simplicity,
-// but this supports using an actual file system as well.
-func ExampleHunter_Hunt_email() {
- fs := afero.NewMemMapFs()
- f, err := fs.Create("example.toml")
- if err != nil {
- panic(err)
- }
- defer f.Close()
-
- _, err = f.Write([]byte(`example@email.com`))
- if err != nil {
- panic(err)
- }
-
- config := NewConfig(fs, "./", true, rules.Load(""), StringToFormat("yaml"), DefaultTemplate, 5)
- h := NewHunter(config)
- _ = h.Hunt()
-}
-
-// This method also accepts custom output formats using
-// go template/html. So if you don't like yaml or json,
-// you can format to your heart's content.
-func ExampleHunter_Hunt_custom_output() {
- fs := afero.NewMemMapFs()
- f, err := fs.Create("example.yaml")
- if err != nil {
- panic(err)
- }
- defer f.Close()
-
- _, err = f.Write([]byte(`https://github.com/brittonhayes/pillager`))
- if err != nil {
- panic(err)
- }
-
- config := NewConfig(fs, "./", true, rules.Load(""), CustomFormat, DefaultTemplate, 5)
- h := NewHunter(config)
- _ = h.Hunt()
-}
-
-// This method accepts json output format
-// as well
-func ExampleHunter_Hunt_json() {
- fs := afero.NewMemMapFs()
- f, err := fs.Create("fake.json")
- if err != nil {
- panic(err)
- }
- defer f.Close()
- _, err = f.Write([]byte(`git@github.com:brittonhayes/pillager.git`))
- if err != nil {
- panic(err)
- }
-
- config := NewConfig(fs, ".", true, rules.Load(""), JSONFormat, DefaultTemplate, 5)
- h := NewHunter(config)
- _ = h.Hunt()
-}
-
-// Hunter will also look personally identifiable info in TOML
-func ExampleHunter_Hunt_toml() {
- fs := afero.NewMemMapFs()
- f, err := fs.Create("fake.toml")
- if err != nil {
- panic(err)
- }
- defer f.Close()
- _, err = f.Write([]byte(`fakeperson@example.com`))
- if err != nil {
- panic(err)
- }
-
- config := NewConfig(fs, ".", true, rules.Load(""), JSONFormat, DefaultTemplate, 5)
-
- h := NewHunter(config)
- _ = h.Hunt()
-}
diff --git a/pkg/rules/README.md b/pkg/rules/README.md
index c025c2e..e3239dc 100755
--- a/pkg/rules/README.md
+++ b/pkg/rules/README.md
@@ -3,56 +3,86 @@
# rules
```go
-import "github.com/brittonhayes/pillager/rules"
+import "github.com/brittonhayes/pillager/pkg/rules"
```
## Index
- [Constants](<#constants>)
-- [func Load(filepath string) gitleaks.Config](<#func-load>)
+- [Variables](<#variables>)
+- [type Loader](<#type-loader>)
+ - [func NewLoader(opts ...LoaderOption) *Loader](<#func-newloader>)
+ - [func (l *Loader) Load() gitleaks.Config](<#func-loader-load>)
+ - [func (l *Loader) WithStrict() LoaderOption](<#func-loader-withstrict>)
+- [type LoaderOption](<#type-loaderoption>)
+ - [func FromFile(file string) LoaderOption](<#func-fromfile>)
## Constants
-DefaultConfig is the default ruleset for pillager's hunting parameters\. This can be overridden by providing a rules\.toml file as an argument\.
-
-```go
-const DefaultConfig = `
-title = "pillager config"
-[[rules]]
- description = "AWS Access Key"
- regex = '''(A3T[A-Z0-9]|AKIA|AGPA|AIDA|AROA|AIPA|ANPA|ANVA|ASIA)[A-Z0-9]{16}'''
- tags = ["key", "AWS"]
-[[rules]]
- description = "AWS Secret Key"
- regex = '''(?i)aws(.{0,20})?(?-i)['\"][0-9a-zA-Z\/+]{40}['\"]'''
- tags = ["key", "AWS"]
-[[rules]]
- description = "Github"
- regex = '''(?i)github(.{0,20})?(?-i)[0-9a-zA-Z]{35,40}'''
- tags = ["key", "Github"]
-[[rules]]
- description = "Slack"
- regex = '''xox[baprs]-([0-9a-zA-Z]{10,48})?'''
- tags = ["key", "Slack"]
-[[rules]]
- description = "Asymmetric Private Key"
- regex = '''-----BEGIN ((EC|PGP|DSA|RSA|OPENSSH) )?PRIVATE KEY( BLOCK)?-----'''
- tags = ["key", "AsymmetricPrivateKey"]
-[[rules]]
- description = "Slack Webhook"
- regex = '''https://hooks.slack.com/services/T[a-zA-Z0-9_]{8}/B[a-zA-Z0-9_]{8}/[a-zA-Z0-9_]{24}'''
- tags = ["key", "slack"]
-`
-```
-
-## func [Load]()
-
-```go
-func Load(filepath string) gitleaks.Config
-```
-
-Load loads the config file into an array of gitleaks rules
+```go
+const (
+ ErrReadConfig = "Failed to read config"
+)
+```
+
+## Variables
+
+```go
+var (
+ //go:embed rules_simple.toml
+ RulesDefault string
+
+ //go:embed rules_strict.toml
+ RulesStrict string
+)
+```
+
+## type [Loader]()
+
+```go
+type Loader struct {
+ // contains filtered or unexported fields
+}
+```
+
+### func [NewLoader]()
+
+```go
+func NewLoader(opts ...LoaderOption) *Loader
+```
+
+NewLoader creates a configuration loader\.
+
+### func \(\*Loader\) [Load]()
+
+```go
+func (l *Loader) Load() gitleaks.Config
+```
+
+Load parses the gitleaks configuration\.
+
+### func \(\*Loader\) [WithStrict]()
+
+```go
+func (l *Loader) WithStrict() LoaderOption
+```
+
+WithStrict enables more strict pillager scanning\.
+
+## type [LoaderOption]()
+
+```go
+type LoaderOption func(*Loader)
+```
+
+### func [FromFile]()
+
+```go
+func FromFile(file string) LoaderOption
+```
+
+FromFile decodes the configuration from a local file\.
diff --git a/pkg/rules/rules.go b/pkg/rules/rules.go
index 1ead21f..f676c6c 100644
--- a/pkg/rules/rules.go
+++ b/pkg/rules/rules.go
@@ -1,32 +1,74 @@
package rules
import (
- "log"
+ _ "embed"
"github.com/BurntSushi/toml"
+ "github.com/rs/zerolog/log"
gitleaks "github.com/zricethezav/gitleaks/v7/config"
)
-// Load loads the config file into an array of gitleaks rules
-func Load(filepath string) gitleaks.Config {
- var (
- config gitleaks.TomlLoader
- err error
- )
-
- if filepath != "" {
- _, err = toml.DecodeFile(filepath, &config)
- } else {
- _, err = toml.Decode(DefaultConfig, &config)
- }
+const (
+ ErrReadConfig = "Failed to read config"
+)
+
+var (
+ //go:embed rules_simple.toml
+ RulesDefault string
+
+ //go:embed rules_strict.toml
+ RulesStrict string
+)
+
+type Loader struct {
+ loader gitleaks.TomlLoader
+}
+
+type LoaderOption func(*Loader)
+
+// NewLoader creates a configuration
+// loader.
+func NewLoader(opts ...LoaderOption) *Loader {
+ var loader Loader
+ _, err := toml.Decode(RulesDefault, &loader.loader)
if err != nil {
- log.Fatal("Failed to read in config ", err.Error())
+ log.Fatal().Err(err).Msg(ErrReadConfig)
}
- c, err := config.Parse()
+ for _, opt := range opts {
+ opt(&loader)
+ }
+
+ return &loader
+}
+
+// WithStrict enables more strict pillager scanning.
+func (l *Loader) WithStrict() LoaderOption {
+ return func(l *Loader) {
+ _, err := toml.Decode(RulesStrict, &l.loader)
+ if err != nil {
+ log.Fatal().Err(err).Msg(ErrReadConfig)
+ }
+ }
+}
+
+// Load parses the gitleaks configuration.
+func (l *Loader) Load() gitleaks.Config {
+ config, err := l.loader.Parse()
if err != nil {
- log.Fatal("Failed to parse in toml config")
+ log.Fatal().Err(err).Msg(ErrReadConfig)
}
- return c
+ return config
+}
+
+// FromFile decodes the configuration
+// from a local file.
+func FromFile(file string) LoaderOption {
+ return func(l *Loader) {
+ _, err := toml.DecodeFile(file, &l.loader)
+ if err != nil {
+ log.Fatal().Err(err).Msg(ErrReadConfig)
+ }
+ }
}
diff --git a/pkg/rules/default.go b/pkg/rules/rules_simple.toml
similarity index 74%
rename from pkg/rules/default.go
rename to pkg/rules/rules_simple.toml
index e57d833..f0fc62d 100644
--- a/pkg/rules/default.go
+++ b/pkg/rules/rules_simple.toml
@@ -1,8 +1,3 @@
-package rules
-
-// DefaultConfig is the default ruleset for pillager's hunting parameters.
-// This can be overridden by providing a rules.toml file as an argument.
-const DefaultConfig = `
title = "pillager config"
[[rules]]
description = "AWS Access Key"
@@ -31,7 +26,4 @@ title = "pillager config"
[allowlist]
description = "Allowlisted files"
- files = ['''^\.?gitleaks.toml$''',
- '''(.*?)(png|jpg|gif|doc|docx|pdf|bin|xls|pyc|zip)$''',
- '''(go.mod|go.sum)$''']
-`
+ files = ['''^\.?gitleaks.toml$''', '''(.*?)(png|jpg|gif|doc|docx|pdf|bin|xls|pyc|zip)$''', '''(go.mod|go.sum)$''']
diff --git a/pkg/rules/rules_strict.toml b/pkg/rules/rules_strict.toml
new file mode 100644
index 0000000..32d4a0c
--- /dev/null
+++ b/pkg/rules/rules_strict.toml
@@ -0,0 +1,226 @@
+title = "Global gitleaks config"
+
+[allowlist]
+ description = "Allowlisted files"
+ files = ['''^\.?gitleaks.toml$''', # Ignoring this file
+ '''(.*?)(jpg|gif|doc|pdf|bin)$''', # Ignoring common binaries
+ '''^(.*?)_test\.go$''', # Ignoring Go test files
+ '''^(.*?)\.(spec|test)\.(j|t)s$''', # Ignoring JavaScript and TypeScript test files
+ '''(go.mod|go.sum)$''', # Ignoring Go manifests
+ '''vendor\.json''',
+ '''Gopkg\.(lock|toml)''',
+ '''package-lock\.json''', # Ignoring Node/JS manifests
+ '''package\.json''',
+ '''composer\.json''',
+ '''composer\.lock''', #Ignoring PHP manifests
+ '''yarn\.lock''']
+ paths = ["node_modules", # Ignoring Node dependencies
+ "vendor", # Ignoring Go dependencies
+ "test", # Ignoring test directories
+ "tests"]
+ regexes = ['''test'''] # Ignoring lines with test
+
+
+[[rules]]
+ description = "AWS Secret Key"
+ regex = '''(?i)aws(.{0,20})?(?-i)[0-9a-zA-Z\/+]{40}'''
+ tags = ["key", "AWS"]
+
+[[rules]]
+ description = "AWS MWS key"
+ regex = '''amzn\.mws\.[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}'''
+ tags = ["key", "AWS", "MWS"]
+
+[[rules]]
+ description = "Facebook Secret Key"
+ regex = '''(?i)(facebook|fb)(.{0,20})?(?-i)[0-9a-f]{32}'''
+ tags = ["key", "Facebook"]
+
+[[rules]]
+ description = "Facebook Client ID"
+ regex = '''(?i)(facebook|fb)(.{0,20})?[0-9]{13,17}'''
+ tags = ["key", "Facebook"]
+
+[[rules]]
+ description = "Twitter Secret Key"
+ regex = '''(?i)twitter(.{0,20})?[0-9a-z]{35,44}'''
+ tags = ["key", "Twitter"]
+
+[[rules]]
+ description = "Twitter Client ID"
+ regex = '''(?i)twitter(.{0,20})?[0-9a-z]{18,25}'''
+ tags = ["client", "Twitter"]
+
+[[rules]]
+ description = "Github"
+ regex = '''(?i)github(.{0,20})?(?-i)[0-9a-zA-Z]{35,40}'''
+ tags = ["key", "Github"]
+
+[[rules]]
+ description = "LinkedIn Client ID"
+ regex = '''(?i)linkedin(.{0,20})?(?-i)[0-9a-z]{12}'''
+ tags = ["client", "LinkedIn"]
+
+[[rules]]
+ description = "LinkedIn Secret Key"
+ regex = '''(?i)linkedin(.{0,20})?[0-9a-z]{16}'''
+ tags = ["secret", "LinkedIn"]
+
+[[rules]]
+ description = "Slack"
+ regex = '''xox[baprs]-([0-9a-zA-Z]{10,48})?'''
+ tags = ["key", "Slack"]
+
+[[rules]]
+ description = "Asymmetric Private Key"
+ regex = '''-----BEGIN ((EC|PGP|DSA|RSA|OPENSSH) )?PRIVATE KEY( BLOCK)?-----'''
+ tags = ["key", "AsymmetricPrivateKey"]
+
+[[rules]]
+ description = "Google API key"
+ regex = '''AIza[0-9A-Za-z\\-_]{35}'''
+ tags = ["key", "Google"]
+
+[[rules]]
+ description = "Google (GCP) Service Account"
+ regex = '''"type": "service_account"'''
+ tags = ["key", "Google"]
+
+[[rules]]
+ description = "Heroku API key"
+ regex = '''(?i)heroku(.{0,20})?[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}'''
+ tags = ["key", "Heroku"]
+
+[[rules]]
+ description = "MailChimp API key"
+ regex = '''(?i)(mailchimp|mc)(.{0,20})?[0-9a-f]{32}-us[0-9]{1,2}'''
+ tags = ["key", "Mailchimp"]
+
+[[rules]]
+ description = "Mailgun API key"
+ regex = '''((?i)(mailgun|mg)(.{0,20})?)?key-[0-9a-z]{32}'''
+ tags = ["key", "Mailgun"]
+
+[[rules]]
+ description = "PayPal Braintree access token"
+ regex = '''access_token\$production\$[0-9a-z]{16}\$[0-9a-f]{32}'''
+ tags = ["key", "Paypal"]
+
+[[rules]]
+ description = "Picatic API key"
+ regex = '''sk_live_[0-9a-z]{32}'''
+ tags = ["key", "Picatic"]
+
+[[rules]]
+ description = "SendGrid API Key"
+ regex = '''SG\.[\w_]{16,32}\.[\w_]{16,64}'''
+ tags = ["key", "SendGrid"]
+
+[[rules]]
+ description = "Slack Webhook"
+ regex = '''https://hooks.slack.com/services/T[a-zA-Z0-9_]{8}/B[a-zA-Z0-9_]{8}/[a-zA-Z0-9_]{24}'''
+ tags = ["key", "slack"]
+
+[[rules]]
+ description = "Stripe API key"
+ regex = '''(?i)stripe(.{0,20})?[sr]k_live_[0-9a-zA-Z]{24}'''
+ tags = ["key", "Stripe"]
+
+[[rules]]
+ description = "Square access token"
+ regex = '''sq0atp-[0-9A-Za-z\-_]{22}'''
+ tags = ["key", "square"]
+
+[[rules]]
+ description = "Square OAuth secret"
+ regex = '''sq0csp-[0-9A-Za-z\\-_]{43}'''
+ tags = ["key", "square"]
+
+[[rules]]
+ description = "Twilio API key"
+ regex = '''(?i)twilio(.{0,20})?SK[0-9a-f]{32}'''
+ tags = ["key", "twilio"]
+
+# The following rules check for credentials assigned to variables that its value has an entropy of more than 3 bits.
+# To achieve this there's a regexp for each language. The regexp checks for a variable with a suspicious name followed
+# by a value assignation (for example, := in Go, = in JS, etc.). Then, looks for a group of non-space characters enclosed
+# between quotes. If that group has an entropy higher than 3 bits the rule will trigger.
+
+[[rules]]
+ description = "Hardcoded credentials in Go files"
+ file = '''^(.*?)\.go$'''
+ regex = '''(?i)(?:secret|key|signature|password|pwd|pass|token)(?:\w|\s*?)(?:=|:=)(?:\s*?)[\"'`](.{4,120}?)[\"'`]'''
+ tags = ["credentials", "hardcoded", "go"]
+ [[rules.Entropies]]
+ Min = "3"
+ Max = "7"
+ Group = "1"
+
+[[rules]]
+ description = "Hardcoded credentials in JavaScript or TypeScript files"
+ file = '''^(.*?)\.(?:j|t)s$'''
+ regex = '''(?i)(?:secret|key|signature|password|pwd|pass|token)(?:\w|\s*?)(?:=){1}(?:\s{0,10})[\"'`](.*?)[\"'`]'''
+ tags = ["credentials", "hardcoded", "js"]
+ [[rules.Entropies]]
+ Min = "3"
+ Max = "7"
+ Group = "1"
+
+[[rules]]
+ description = "Hardcoded credentials in PHP files"
+ file = '''^(.*?)\.php$'''
+ regex = '''(?i)(?:secret|key|signature|password|pwd|pass|token)(?:.{0,20})(?:=){1}(?:.{0,10})[\"'`](.{4,120})[\"'`]'''
+ tags = ["credentials", "hardcoded", "php"]
+ [[rules.Entropies]]
+ Min = "3"
+ Max = "7"
+ Group = "1"
+
+[[rules]]
+ description = "Hardcoded credentials in YAML files as quoted strings"
+ file = '''^(.*?)\.y(a|)ml$'''
+ regex = '''(?i)(?:secret|key|signature|password|pwd|pass|token)(?:.{0,20})(?::){1}(?:\s{0,10})(?:[\"'](.{4,120})[\"'])'''
+ tags = ["credentials", "hardcoded", "yaml"]
+ [[rules.Entropies]]
+ Min = "3"
+ Max = "7"
+ Group = "1"
+ [rules.allowlist]
+ description = "Skip YAML Serverless variables, grabbed and concated values, encrypted secrets, and values with jinja2 placeholders"
+ regexes = ['''\${(?:.)+}''', '''(?i)\(\((?:\s)*?(?:grab|concat)(?:.)*?(?:\s)*?\)\)''', '''(?i)!!enveloped:(?:\S)+''', '''(?:.)*?{{(?:.)*?}}''']
+
+[[rules]]
+ description = "Hardcoded credentials in YAML files as unquoted strings"
+ file = '''^(.*?)\.y(a|)ml$'''
+ regex = '''(?i)(?:secret|key|signature|password|pwd|pass|token)(?:.{0,20})(?::){1}(?:\s{0,10})(\S{4,120})'''
+ tags = ["credentials", "hardcoded", "yaml"]
+ [[rules.Entropies]]
+ Min = "3.5" # A higher entropy is required for this type of match, as unquoted can trigger many false positives
+ Max = "7"
+ Group = "1"
+ [rules.allowlist]
+ description = "Skip YAML Serverless variables, grabbed and concated values, encrypted secrets, and values with jinja2 placeholders"
+ regexes = ['''\${(?:.)+}''', '''(?i)\(\((?:\s)*?(?:grab|concat)(?:.)*?(?:\s)*?\)\)''', '''(?i)!!enveloped:(?:\S)+''', '''(?:.)*?{{(?:.)*?}}''']
+
+[[rules]]
+ description = "Hardcoded credentials in YAML files as multiline strings"
+ file = '''^(.*?)\.y(a|)ml$'''
+ regex = '''(?i)(?:secret|key|signature|password|pwd|pass|token)(?:.{0,20})(?::){1}(?:\s{0,10})(?:\|(?:-|))\n(?:\s{0,10})(\S{4,120})'''
+ tags = ["credentials", "hardcoded", "yaml"]
+ [[rules.Entropies]]
+ Min = "4"
+ Max = "7"
+ Group = "1"
+
+[[rules]]
+ description = "Hardcoded credentials in HCL files (*.tf)"
+ file = '''^(.*?)\.tf$'''
+ regex = '''(?i)(?:secret|key|signature|password|pwd|pass|token)(?:.{0,20})(?:=){1}(?:\s)*?"(.{4,120})"'''
+ tags = ["credentials", "hardcoded", "hcl"]
+ [[rules.Entropies]]
+ Min = "3"
+ Max = "7"
+ Group = "1"
+ [rules.allowlist]
+ description = "Skip variable substitution"
+ regexes = ['''\${(?:.)*?}''']
diff --git a/waypoint.hcl b/waypoint.hcl
deleted file mode 100644
index fb78d68..0000000
--- a/waypoint.hcl
+++ /dev/null
@@ -1,46 +0,0 @@
-project = "pillager"
-
-app "cli" {
-
- labels = {
- "maintainer" = "Britton Hayes"
- "github" = "https://github.com/brittonhayes/pillager"
- }
-
- build {
- use "docker" {}
-
- registry {
- use "docker" {
- image = "bjhayes/pillager"
- tag = "latest"
- }
- }
- hook {
- when = "before"
- command = [
- "golangci-lint",
- "run",
- "./..."]
- on_failure = "fail"
- }
-
- hook {
- when = "before"
- command = [
- "go",
- "test",
- "-v",
- "./..."]
- on_failure = "fail"
- }
- }
-
- deploy {
- use "docker" {}
- }
-
- url {
- auto_hostname = false
- }
-}