Skip to content
59 changes: 59 additions & 0 deletions internal/config/config_custom_rules_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package config

import (
"os"
"testing"
)

func TestDefaultConfig_CustomRuleFiles(t *testing.T) {
cfg := DefaultConfig()
if len(cfg.CustomRuleFiles) != 0 {
t.Errorf("CustomRuleFiles = %v, want empty slice", cfg.CustomRuleFiles)
}
}

func TestLoadConfig_CustomRuleFiles(t *testing.T) {
tmpFile, err := os.CreateTemp("", "secure-push-*.yaml")
if err != nil {
t.Fatalf("failed to create temp file: %v", err)
}
defer os.Remove(tmpFile.Name())

content := "custom_rule_files:\n - rules/custom-secrets.yaml\n - rules/custom-api-keys.yaml\n"
if _, err := tmpFile.WriteString(content); err != nil {
t.Fatalf("failed to write temp file: %v", err)
}
tmpFile.Close()

cfg, err := Load(tmpFile.Name())
if err != nil {
t.Fatalf("unexpected error: %v", err)
}

if len(cfg.CustomRuleFiles) != 2 {
t.Errorf("CustomRuleFiles = %v, want 2 files", cfg.CustomRuleFiles)
}
}

func TestLoadConfig_CustomRuleFilesDefault(t *testing.T) {
tmpFile, err := os.CreateTemp("", "secure-push-*.yaml")
if err != nil {
t.Fatalf("failed to create temp file: %v", err)
}
defer os.Remove(tmpFile.Name())

content := "severity_threshold: high\n"
if _, err := tmpFile.WriteString(content); err != nil {
t.Fatalf("failed to write temp file: %v", err)
}
tmpFile.Close()

cfg, err := Load(tmpFile.Name())
if err != nil {
t.Fatalf("unexpected error: %v", err)
}

if len(cfg.CustomRuleFiles) != 0 {
t.Errorf("CustomRuleFiles = %v, want empty slice (default)", cfg.CustomRuleFiles)
}
}
59 changes: 59 additions & 0 deletions internal/config/config_exit_code_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package config

import (
"os"
"testing"
)

func TestDefaultConfig_ExitCode(t *testing.T) {
cfg := DefaultConfig()
if cfg.ExitCode != 1 {
t.Errorf("ExitCode = %d, want 1", cfg.ExitCode)
}
}

func TestLoadConfig_ExitCode(t *testing.T) {
tmpFile, err := os.CreateTemp("", "secure-push-*.yaml")
if err != nil {
t.Fatalf("failed to create temp file: %v", err)
}
defer os.Remove(tmpFile.Name())

content := "exit_code: 2\n"
if _, err := tmpFile.WriteString(content); err != nil {
t.Fatalf("failed to write temp file: %v", err)
}
tmpFile.Close()

cfg, err := Load(tmpFile.Name())
if err != nil {
t.Fatalf("unexpected error: %v", err)
}

if cfg.ExitCode != 2 {
t.Errorf("ExitCode = %d, want 2", cfg.ExitCode)
}
}

func TestLoadConfig_ExitCodeDefault(t *testing.T) {
tmpFile, err := os.CreateTemp("", "secure-push-*.yaml")
if err != nil {
t.Fatalf("failed to create temp file: %v", err)
}
defer os.Remove(tmpFile.Name())

content := "severity_threshold: high\n"
if _, err := tmpFile.WriteString(content); err != nil {
t.Fatalf("failed to write temp file: %v", err)
}
tmpFile.Close()

cfg, err := Load(tmpFile.Name())
if err != nil {
t.Fatalf("unexpected error: %v", err)
}

if cfg.ExitCode != 1 {
t.Errorf("ExitCode = %d, want 1 (default)", cfg.ExitCode)
}
}
64 changes: 64 additions & 0 deletions internal/config/config_ignore_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package config

import (
"testing"
)

func TestShouldIgnore_Allowlist(t *testing.T) {
cfg := &Config{
IgnorePaths: []string{"*.test.go"},
Allowlist: []string{"important.test.go"},
}

if cfg.ShouldIgnore("important.test.go") {
t.Error("Expected allowlisted file to not be ignored")
}
if !cfg.ShouldIgnore("other.test.go") {
t.Error("Expected non-allowlisted file to be ignored")
}
}

func TestShouldIgnore_PathPatterns(t *testing.T) {
cfg := &Config{
IgnorePaths: []string{"vendor/**", "**/*_test.go"},
}

tests := []struct {
path string
ignore bool
}{
{"vendor/lib.go", true},
{"vendor/sub/lib.go", true},
{"main_test.go", true},
{"main.go", false},
{"internal/main.go", false},
}

for _, tt := range tests {
got := cfg.ShouldIgnore(tt.path)
if got != tt.ignore {
t.Errorf("ShouldIgnore(%q) = %v, want %v", tt.path, got, tt.ignore)
}
}
}

func TestMatchPath_GlobPatterns(t *testing.T) {
tests := []struct {
pattern string
target string
want bool
}{
{"*.go", "main.go", true},
{"*.go", "main.txt", false},
{"**/*.go", "dir/main.go", true},
{"vendor/*", "vendor/lib.go", true},
{"vendor/*", "vendor/sub/lib.go", false},
}

for _, tt := range tests {
got := matchPath(tt.pattern, tt.target)
if got != tt.want {
t.Errorf("matchPath(%q, %q) = %v, want %v", tt.pattern, tt.target, got, tt.want)
}
}
}
41 changes: 41 additions & 0 deletions internal/config/config_severity_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package config

import (
"testing"

"secure-push/internal/detectors"
)

func TestIsSeverityEnabled_AllThresholds(t *testing.T) {
tests := []struct {
threshold string
severity detectors.Severity
want bool
}{
{"low", detectors.Low, true},
{"low", detectors.Medium, true},
{"low", detectors.High, true},
{"low", detectors.Critical, true},
{"medium", detectors.Low, false},
{"medium", detectors.Medium, true},
{"medium", detectors.High, true},
{"medium", detectors.Critical, true},
{"high", detectors.Low, false},
{"high", detectors.Medium, false},
{"high", detectors.High, true},
{"high", detectors.Critical, true},
{"critical", detectors.Low, false},
{"critical", detectors.Medium, false},
{"critical", detectors.High, false},
{"critical", detectors.Critical, true},
{"unknown", detectors.Critical, true},
}

for _, tt := range tests {
cfg := &Config{SeverityThreshold: tt.threshold}
got := cfg.IsSeverityEnabled(tt.severity)
if got != tt.want {
t.Errorf("IsSeverityEnabled(%q, %v) = %v, want %v", tt.threshold, tt.severity, got, tt.want)
}
}
}
6 changes: 5 additions & 1 deletion internal/detectors/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,11 @@ func (d *ASTDetector) Detect(content string, filename string) ([]Finding, error)
// Check for potentially dangerous function calls
if sel, ok := x.Fun.(*ast.SelectorExpr); ok {
funcName := strings.ToLower(sel.Sel.Name)
if isDangerousFunction(funcName) {
packageName := ""
if ident, ok := sel.X.(*ast.Ident); ok {
packageName = strings.ToLower(ident.Name)
}
if isDangerousFunction(packageName+"."+funcName) || isDangerousFunction(funcName) {
findings = append(findings, Finding{
Rule: d.Name(),
Severity: Medium,
Expand Down
93 changes: 93 additions & 0 deletions internal/detectors/auth_additional_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package detectors

import (
"testing"
)

func TestAuthDetector_DetectSlackToken(t *testing.T) {
d := &AuthDetector{}
got, err := d.Detect("slack_token = 'xoxb-test0000000-test00000000000-TESTTESTTEST'", "config.go")
if err != nil {
t.Fatalf("Detect() error = %v", err)
}
if len(got) != 1 {
t.Errorf("Detect() returned %d findings, want 1", len(got))
}
}

func TestAuthDetector_DetectDiscordWebhook(t *testing.T) {
d := &AuthDetector{}
got, err := d.Detect("discord_webhook = 'https://discord.com/api/webhooks/123456789012345678/abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'", "config.go")
if err != nil {
t.Fatalf("Detect() error = %v", err)
}
if len(got) != 1 {
t.Errorf("Detect() returned %d findings, want 1", len(got))
}
}

func TestAuthDetector_DetectTelegramBotToken(t *testing.T) {
d := &AuthDetector{}
got, err := d.Detect("telegram_token = '1234567890:ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghiJKLMNO'", "config.go")
if err != nil {
t.Fatalf("Detect() error = %v", err)
}
if len(got) != 1 {
t.Errorf("Detect() returned %d findings, want 1", len(got))
}
}

func TestAuthDetector_DetectAzureKeyVault(t *testing.T) {
d := &AuthDetector{}
got, err := d.Detect("-----BEGIN AZURE KEY VAULT-----\nkey-data\n-----END AZURE KEY VAULT-----", "azure.key")
if err != nil {
t.Fatalf("Detect() error = %v", err)
}
if len(got) != 1 {
t.Errorf("Detect() returned %d findings, want 1", len(got))
}
}

func TestAuthDetector_DetectPersonalAccessToken(t *testing.T) {
d := &AuthDetector{}
got, err := d.Detect("pat = 'abc123def456ghij789klmn.ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJ'", "config.go")
if err != nil {
t.Fatalf("Detect() error = %v", err)
}
if len(got) != 1 {
t.Errorf("Detect() returned %d findings, want 1", len(got))
}
}

func TestAuthDetector_DetectFigmaToken(t *testing.T) {
d := &AuthDetector{}
got, err := d.Detect("figma_token = 'figd_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789ABCDEFGH'", "config.go")
if err != nil {
t.Fatalf("Detect() error = %v", err)
}
if len(got) != 1 {
t.Errorf("Detect() returned %d findings, want 1", len(got))
}
}

func TestAuthDetector_DetectNotionToken(t *testing.T) {
d := &AuthDetector{}
got, err := d.Detect("notion_token = 'secret_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrs'", "config.go")
if err != nil {
t.Fatalf("Detect() error = %v", err)
}
if len(got) != 1 {
t.Errorf("Detect() returned %d findings, want 1", len(got))
}
}

func TestAuthDetector_DetectLinearToken(t *testing.T) {
d := &AuthDetector{}
got, err := d.Detect("linear_api_token = 'lin_api_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789ABCDEFGH'", "config.go")
if err != nil {
t.Fatalf("Detect() error = %v", err)
}
if len(got) != 1 {
t.Errorf("Detect() returned %d findings, want 1", len(got))
}
}
Loading
Loading