/* ©AngelaMos | 2026 registry_test.go Tests for rules/registry.go Tests: Register and Get round-trip Get on missing ID returns false Duplicate registration panics All() returns rules sorted alphabetically by ID Disable removes rule from Get and All, and reduces Len MatchKeywords returns only enabled rules with matching keywords, case-insensitively MatchKeywords skips disabled rules even when the keyword matches Replace overwrites an existing rule by ID GlobalPathAllowlist allowlists go.sum, node_modules, vendor, .git, dist/*.min, binaries GlobalValueAllowlist allowlists placeholder patterns and blocks real secret values */ package rules import ( "regexp" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/CarterPerez-dev/portia/pkg/types" ) func testRule(id string, keywords []string) *types.Rule { return &types.Rule{ ID: id, Keywords: keywords, Pattern: regexp.MustCompile(`test`), Severity: types.SeverityHigh, } } func TestRegistryRegisterAndGet(t *testing.T) { t.Parallel() reg := NewRegistry() rule := testRule("test-rule", []string{"test"}) reg.Register(rule) got, ok := reg.Get("test-rule") require.True(t, ok) assert.Equal(t, "test-rule", got.ID) } func TestRegistryGetMissing(t *testing.T) { t.Parallel() reg := NewRegistry() _, ok := reg.Get("nonexistent") assert.False(t, ok) } func TestRegistryDuplicatePanics(t *testing.T) { t.Parallel() reg := NewRegistry() rule := testRule("dup", []string{"test"}) reg.Register(rule) assert.Panics(t, func() { reg.Register(testRule("dup", []string{"test"})) }) } func TestRegistryAll(t *testing.T) { t.Parallel() reg := NewRegistry() reg.Register(testRule("b-rule", []string{"b"})) reg.Register(testRule("a-rule", []string{"a"})) reg.Register(testRule("c-rule", []string{"c"})) all := reg.All() require.Len(t, all, 3) assert.Equal(t, "a-rule", all[0].ID) assert.Equal(t, "b-rule", all[1].ID) assert.Equal(t, "c-rule", all[2].ID) } func TestRegistryDisable(t *testing.T) { t.Parallel() reg := NewRegistry() reg.Register(testRule("keep", []string{"a"})) reg.Register(testRule("drop", []string{"b"})) reg.Disable("drop") _, ok := reg.Get("drop") assert.False(t, ok) all := reg.All() require.Len(t, all, 1) assert.Equal(t, "keep", all[0].ID) } func TestRegistryMatchKeywords(t *testing.T) { t.Parallel() reg := NewRegistry() reg.Register( testRule("aws", []string{"AKIA", "aws"}), ) reg.Register( testRule("github", []string{"ghp_"}), ) reg.Register( testRule("stripe", []string{"sk_live_"}), ) tests := map[string]struct { content string wantIDs []string wantNone bool }{ "matches aws by AKIA prefix": { //nolint:gosec content: "found AKIAIOSFODNN7EXAMPLE in config", wantIDs: []string{"aws"}, }, "matches github token": { content: "token = ghp_abc123def456", wantIDs: []string{"github"}, }, "matches multiple rules": { content: "aws key AKIA and ghp_ token here", wantIDs: []string{"aws", "github"}, }, "case insensitive matching": { content: "AWS_ACCESS_KEY with akia prefix", wantIDs: []string{"aws"}, }, "no matches on unrelated content": { content: "just some normal code here", wantNone: true, }, } for name, tc := range tests { t.Run(name, func(t *testing.T) { t.Parallel() matched := reg.MatchKeywords(tc.content) if tc.wantNone { assert.Empty(t, matched) return } ids := make([]string, len(matched)) for i, r := range matched { ids[i] = r.ID } for _, wantID := range tc.wantIDs { assert.Contains(t, ids, wantID) } }) } } func TestRegistryMatchKeywordsSkipsDisabled(t *testing.T) { t.Parallel() reg := NewRegistry() reg.Register( testRule("enabled", []string{"secret"}), ) reg.Register( testRule("disabled", []string{"secret"}), ) reg.Disable("disabled") matched := reg.MatchKeywords("my secret value") require.Len(t, matched, 1) assert.Equal(t, "enabled", matched[0].ID) } func TestRegistryLen(t *testing.T) { t.Parallel() reg := NewRegistry() reg.Register(testRule("a", []string{"a"})) reg.Register(testRule("b", []string{"b"})) assert.Equal(t, 2, reg.Len()) reg.Disable("a") assert.Equal(t, 1, reg.Len()) } func TestRegistryReplace(t *testing.T) { t.Parallel() reg := NewRegistry() reg.Register(testRule("r", []string{"old"})) newRule := testRule("r", []string{"new"}) newRule.Description = "replaced" reg.Replace(newRule) got, ok := reg.Get("r") require.True(t, ok) assert.Equal(t, "replaced", got.Description) } func TestGlobalPathAllowlist(t *testing.T) { t.Parallel() allowed := []string{ "go.sum", "go.mod", "package-lock.json", "pnpm-lock.yaml", "node_modules/pkg/index.js", "vendor/lib/code.go", ".git/objects/pack/abc", "dist/bundle.min.js", "assets/logo.png", "data/file.pdf", "Cargo.lock", "composer.lock", "Gemfile.lock", "poetry.lock", "Pipfile.lock", "bun.lockb", ".svn/entries", ".hg/store/data", "__pycache__/module.cpython-313.pyc", ".mypy_cache/3.13/module.meta.json", ".next/server/pages/index.html", ".terraform/providers/registry.terraform.io", "target/debug/build/crate/output", "dist/bundle.min.css", "dist/bundle.min.css.map", "assets/font.otf", } blocked := []string{ "src/main.go", "config.yaml", ".env", "internal/auth/handler.go", } for _, path := range allowed { matched := false for _, re := range GlobalPathAllowlist { if re.MatchString(path) { matched = true break } } assert.True(t, matched, "expected %s to be allowlisted", path) } for _, path := range blocked { matched := false for _, re := range GlobalPathAllowlist { if re.MatchString(path) { matched = true break } } assert.False(t, matched, "expected %s to NOT be allowlisted", path) } } func TestGlobalValueAllowlist(t *testing.T) { t.Parallel() allowed := []string{ "EXAMPLE_KEY", "test_token", "your-api-key", "placeholder", "xxxxxxxxxxxx", "****", "${SECRET_KEY}", "{{api_token}}", "CHANGEME", "none", "undefined", "", "", "", "00000000000000000000", "1111111111111111", "REDACTED", "N/A", "TBD", "REMOVED", "MASKED", "changeit", "PUT_YOUR_KEY_HERE", "ENTER_YOUR_TOKEN_HERE", "dGVzdA==", "cGFzc3dvcmQ=", } blocked := []string{ "AKIAIOSFODNN7EXAMPLE", "sk_live_4eC39HqLyjWDarjtT1", "ghp_ABCDEFGHIJKLMNOPQRSTUVWXYZab", "xK9mP2vL5nQ8", } for _, val := range allowed { matched := false for _, re := range GlobalValueAllowlist { if re.MatchString(val) { matched = true break } } assert.True(t, matched, "expected %q to be allowlisted", val) } for _, val := range blocked { matched := false for _, re := range GlobalValueAllowlist { if re.MatchString(val) { matched = true break } } assert.False(t, matched, "expected %q to NOT be allowlisted", val) } }