Cybersecurity-Projects/PROJECTS/intermediate/secrets-scanner/internal/config/config.go

203 lines
3.6 KiB
Go

// ©AngelaMos | 2026
// config.go
package config
import (
"fmt"
"os"
"path/filepath"
"github.com/pelletier/go-toml/v2"
)
const DefaultConfigFile = ".portia.toml"
type Config struct {
Rules RulesConfig `toml:"rules"`
Scan ScanConfig `toml:"scan"`
Output OutputConfig `toml:"output"`
HIBP HIBPConfig `toml:"hibp"`
Allowlist AllowlistConfig `toml:"allowlist"`
}
type RulesConfig struct {
Disable []string `toml:"disable"`
Enable []string `toml:"enable"`
}
type ScanConfig struct {
MaxFileSize int64 `toml:"max_file_size"`
Excludes []string `toml:"excludes"`
Depth int `toml:"depth"`
Since string `toml:"since"`
}
type OutputConfig struct {
Format string `toml:"format"`
Verbose bool `toml:"verbose"`
NoColor bool `toml:"no_color"`
}
type HIBPConfig struct {
Enabled bool `toml:"enabled"`
}
type AllowlistConfig struct {
Paths []string `toml:"paths"`
Values []string `toml:"values"`
Stopwords []string `toml:"stopwords"`
}
func Load(path string) (*Config, error) {
cfg := &Config{
Scan: ScanConfig{
MaxFileSize: 1 << 20,
},
Output: OutputConfig{
Format: "terminal",
},
}
if path == "" {
path = findConfig()
}
if path == "" {
if pyCfg, ok := loadFromPyproject(); ok {
return pyCfg, nil
}
return cfg, nil
}
data, err := os.ReadFile(path) //nolint:gosec
if err != nil {
if os.IsNotExist(err) {
return cfg, nil
}
return nil, err
}
if err := toml.Unmarshal(data, cfg); err != nil {
return nil, err
}
return cfg, nil
}
type pyprojectWrapper struct {
Tool struct {
Portia Config `toml:"portia"`
} `toml:"tool"`
}
func loadFromPyproject() (*Config, bool) {
data, err := os.ReadFile("pyproject.toml")
if err != nil {
return nil, false
}
var wrapper pyprojectWrapper
if err := toml.Unmarshal(data, &wrapper); err != nil {
return nil, false
}
cfg := wrapper.Tool.Portia
if cfg.Output.Format == "" && !cfg.Output.Verbose &&
!cfg.Output.NoColor && !cfg.HIBP.Enabled &&
len(cfg.Rules.Disable) == 0 && len(cfg.Scan.Excludes) == 0 {
return nil, false
}
if cfg.Scan.MaxFileSize == 0 {
cfg.Scan.MaxFileSize = 1 << 20
}
if cfg.Output.Format == "" {
cfg.Output.Format = "terminal"
}
return &cfg, true
}
func findConfig() string {
candidates := []string{
DefaultConfigFile,
filepath.Join(".portia", "config.toml"),
}
for _, c := range candidates {
if _, err := os.Stat(c); err == nil {
return c
}
}
home, err := os.UserHomeDir()
if err == nil {
global := filepath.Join(home, ".config", "portia", "config.toml")
if _, err := os.Stat(global); err == nil {
return global
}
}
return ""
}
func PyprojectTemplate(name string) string {
return fmt.Sprintf(`[project]
name = "%s"
version = "0.1.0"
description = ""
requires-python = ">=3.13"
dependencies = []
[tool.portia]
# [tool.portia.rules]
# disable = ["generic-password"]
# [tool.portia.scan]
# max_file_size = 1048576
# excludes = ["*.test.js", "fixtures/"]
# [tool.portia.output]
# format = "terminal"
# verbose = false
# no_color = false
# [tool.portia.hibp]
# enabled = false
# [tool.portia.allowlist]
# paths = ["test/fixtures/"]
# values = ["my_test_value"]
# stopwords = ["mycompany"]
`, name)
}
func DefaultTemplate() string {
return `# ©AngelaMos | 2026
# .portia.toml
[rules]
# disable = ["generic-password"]
[scan]
max_file_size = 1048576
# excludes = ["*.test.js", "fixtures/"]
# depth = 0
# since = ""
[output]
format = "terminal"
verbose = false
no_color = false
[hibp]
enabled = false
[allowlist]
# paths = ["test/fixtures/"]
# values = ["my_test_value"]
# stopwords = ["mycompany"]
`
}