Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions ee/tables/secretscan/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# This config extends the default gitleaks config with some additional settings
# to avoid false positives.
title = "Kolide secretscan config"

[extend]
useDefault = true

# Ignore K8s Sealed Secrets
# https://github.com/gitleaks/gitleaks/issues/1728
[[rules]]
id = "generic-api-key"
[[rules.allowlists]]
condition = "AND"
regexes = ['''Ag[a-zA-Z0-9+/]{500,}={0,2}''']
paths = ['''(?i).*\.ya?ml$''']
53 changes: 30 additions & 23 deletions ee/tables/secretscan/table.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package secretscan

import (
"context"
_ "embed"
"encoding/base64"
"encoding/json"
"fmt"
Expand Down Expand Up @@ -31,30 +32,43 @@ const (
directoryScanConcurrency = 4
)

func newDefaultConfig() (config.Config, error) {
var (
//go:embed config.toml
rawKolideConfig string
kolideConfig *config.Config
configErr error
)

// newConfigOnce sets up our config, which pulls in the default gitleaks config as our base.
// When gitleaks pulls in the default config, it updates multiple global vars (`viper.SetConfigType("toml")`,
// and a private variable extendDepth). We use a OnceFunc here to avoid both the data race
// and undesirable behavior resulting from extendDepth being modified multiple times.
var newConfigOnce = sync.OnceFunc(func() {
v := viper.New() // init viper here so we don't update a global var
v.SetConfigType("toml")
err := v.ReadConfig(strings.NewReader(config.DefaultConfig))
err := v.ReadConfig(strings.NewReader(rawKolideConfig))
if err != nil {
return config.Config{}, err
configErr = fmt.Errorf("reading config: %w", err)
return
}
var vc config.ViperConfig
err = v.Unmarshal(&vc)
if err != nil {
return config.Config{}, err
configErr = fmt.Errorf("unmarshalling config: %w", err)
return
}

cfg, err := vc.Translate()
if err != nil {
return config.Config{}, err
configErr = fmt.Errorf("translating: %w", err)
return
}
return cfg, nil
}

kolideConfig = &cfg
})

type Table struct {
slogger *slog.Logger
defaultConfig *config.Config
configOnce sync.Once
configErr error
slogger *slog.Logger
}

func TablePlugin(flags types.Flags, slogger *slog.Logger) *table.Plugin {
Expand Down Expand Up @@ -83,16 +97,9 @@ func TablePlugin(flags types.Flags, slogger *slog.Logger) *table.Plugin {
}

func (t *Table) generate(ctx context.Context, queryContext table.QueryContext) ([]map[string]string, error) {
t.configOnce.Do(func() {
cfg, err := newDefaultConfig()
if err != nil {
t.configErr = fmt.Errorf("creating default config: %w", err)
return
}
t.defaultConfig = &cfg
})
if t.configErr != nil {
return nil, t.configErr
newConfigOnce()
if configErr != nil {
return nil, configErr
}

var results []map[string]string
Expand Down Expand Up @@ -161,7 +168,7 @@ func (t *Table) scanPath(ctx context.Context, argon2idSalts []string, targetPath
}

// Fresh detector per scan - gitleaks accumulates findings internally
detector := detect.NewDetector(*t.defaultConfig)
detector := detect.NewDetector(*kolideConfig)

var source sources.Source
var file *os.File
Expand Down Expand Up @@ -199,7 +206,7 @@ func (t *Table) scanPath(ctx context.Context, argon2idSalts []string, targetPath

func (t *Table) scanContent(ctx context.Context, argon2idSalts []string, content []byte) ([]map[string]string, error) {
// Fresh detector per scan - gitleaks accumulates findings internally
detector := detect.NewDetector(*t.defaultConfig)
detector := detect.NewDetector(*kolideConfig)

fileSource := &sources.File{
Content: strings.NewReader(string(content)),
Expand Down
7 changes: 3 additions & 4 deletions ee/tables/secretscan/table_posix_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,13 @@ func TestScanPathFIFO(t *testing.T) {
}

// Initialize config for direct scanPath calls
cfg, err := newDefaultConfig()
require.NoError(t, err)
tbl.defaultConfig = &cfg
newConfigOnce()
require.NoError(t, configErr)

tempDir := t.TempDir()
fifoPath := filepath.Join(tempDir, "test_fifo")

err = unix.Mkfifo(fifoPath, 0644)
err := unix.Mkfifo(fifoPath, 0644)
require.NoError(t, err, "creating FIFO")

// Scanning a FIFO should return an error
Expand Down
64 changes: 56 additions & 8 deletions ee/tables/secretscan/table_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -417,13 +417,9 @@ func Test_isEncryptedJWTFamilyValue(t *testing.T) {
func Test_isEmptyVariable(t *testing.T) {
t.Parallel()

// Set up one table for use for all test cases
tbl := &Table{
slogger: multislogger.NewNopLogger(),
}
cfg, err := newDefaultConfig()
require.NoError(t, err)
tbl.defaultConfig = &cfg
// Make sure config exists
newConfigOnce()
require.NoError(t, configErr)

for _, tt := range []struct {
testCaseName string
Expand Down Expand Up @@ -482,7 +478,7 @@ func Test_isEmptyVariable(t *testing.T) {
t.Run(tt.testCaseName, func(t *testing.T) {
t.Parallel()

detector := detect.NewDetector(*tbl.defaultConfig)
detector := detect.NewDetector(*kolideConfig)
fileSource := &sources.File{
Content: strings.NewReader(tt.rawData),
Config: &detector.Config,
Expand All @@ -502,6 +498,58 @@ func Test_isEmptyVariable(t *testing.T) {
}
}

// Test_kolideConfig confirms that our overrides in config.toml work as expected
func Test_kolideConfig(t *testing.T) {
t.Parallel()

// Make sure config exists
newConfigOnce()
require.NoError(t, configErr)

for _, tt := range []struct {
testCaseName string
pathName string
rawData string
}{
{
testCaseName: "K8s sealed secrets",
pathName: "k8s.yaml",
rawData: `
apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
creationTimestamp: null
name: basic-auth
namespace: default
spec:
encryptedData:
password: AgAYsM4W6e5MMp90oqj7sIolG+//xxJTwRh8ke50CUFiZ8M/r5BoV/vHh3zKnlskt/s4jK058M1i4Y3ETgfsrbiYD/3ADLKRHjr2TO5O6xUrWtfFh6aDR8fOSbVpm1qCSmxFGMaHq5QLJ3Ab0FQTKF/eFehogkzXb8opY/PlI9r+9DcUJ1epAjbNjzvHDDSEZxwi/i5kCNfxqnQNZHQ4uIgEysOk/kjBEtfISxyeD2PGYiyMYk9zMtEUk9LoUTR3/EXiFcJc4iw83DUVICbaCdMPh2ZRsv3A1CWx608rtIyF+qqKnmfX5me7njnYi0vGVDY97T4cV5rKrdZTTOZVggur2l+sPG+BJSYmEVqz3cZ81mVOr4znwU6w3f2e5HxD7ivdJJEz70xgPFX8pFruQulAOohd8qXakqdtA+ew+tr0h3M+cWvOu6VNXQlbijRgC4R89CclHW4/GX3j0OulJUotrV29rTpmVZ7MTGrnZkwJbNUYAe1GEFo7LNws+GJTcNM9R6QA/AYvJ2eE3SjE2G7VaIUh2RvSe2O094Ln7yIJctzEK8afiCiIQQnIgy/M+YheoC+TzogvLZGuNsvrt/oiiilUNa6WODTr1DmJGIyM4cg0pZuVKJ8dx+zf4l7+efkBOa/2mzU9DakvoRQK8/ClR6tuOAVXHZksPehcI3eTX/ZI0MtM2CLJouoi86hPbgwEorBt+nLClkw=
user: AgAMrlzJFHS+mqUhv2ZpG57VNoTSRrewuu6FZVPZcV35dCmdZwesz3MSrmweNHXAlJbVMSMRlINIEBQZKgjw0szEh2ZKkXjGv1926p34GJJSB5/rqYBIDUFkIRY3aJsijMN6etjLRQi68sbYIQAZQM4pGdN23++CfNmXoQgDm9ItspcSYAcOeKP8tZ799pQTdM1pMMur5EyrYWxckORCz+OT1+buCL9+5DJkjU7JuQCk5QkwXRE1sm76FvmmP+a3FbNCIgqsBpD42AqqD4/ex0PogKr7gDkG27MWZNIvCWDd2iF4x1cwJgOtNZJEzQ7tDr+Mf4438w03sJQPMtCEUuuzX1I7SPuT6D9eRSV00GA/IS0/fmNbPf2pPHFxj8t1RMsGI2ZLdZBOXapn3P0SLYZ2Xh6QIqdxzb3VR37WS/Ir4c8v86ZzDTbnqVdT/rwb7U4Iy2k3nDj+/ghxD+7HQbmBx4zzFVYe70Sb1QIWthzZHtuvoX7+FeSa6iU6ipUj4g0U9r53vD+AYt7ntJtCI3EdX+Fh9yJe4AAL4ToHjnt3s2EG4K5i0/21KwAy9WX2rkwBb8GD3POT3zZq2b4uB5gUYyF467kw0J7MfEsPJjAfhd72+IMM9BZDU4tlrFBJPRubVsmRJKelM/o1YTkbl+eFNyWBE1t5IQ9DFjHpcppgUm1CUiDdI0RbIc2goHfFWNePxZZQBg==
template:
metadata:
creationTimestamp: null
name: basic-auth
namespace: default
`,
},
} {
t.Run(tt.testCaseName, func(t *testing.T) {
t.Parallel()

detector := detect.NewDetector(*kolideConfig)
fileSource := &sources.File{
Content: strings.NewReader(tt.rawData),
Path: tt.pathName,
Config: &detector.Config,
}

findings, err := detector.DetectSource(t.Context(), fileSource)
require.NoError(t, err)
require.Equal(t, 0, len(findings))
})
}
}

// Benchmarks

func BenchmarkSecretScanDirectory(b *testing.B) {
Expand Down
Loading