Skip to content
4 changes: 0 additions & 4 deletions cmd/cli/app/repo/repo_register.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,6 @@ var repoRegisterCmd = &cobra.Command{
return fmt.Errorf("cannot use --name and --all together")
}

if len(inputRepoList) == 0 && !registerAll {
return fmt.Errorf("must provide either --name or --all")
}

return nil
},

Expand Down
40 changes: 27 additions & 13 deletions cmd/cli/app/ruletype/ruletype_delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ func deleteCommand(ctx context.Context, cmd *cobra.Command, _ []string, conn *gr

project := viper.GetString("project")
id := viper.GetString("id")
name := viper.GetString("name")
deleteAll := viper.GetBool("all")
yesFlag := viper.GetBool("yes")

Expand All @@ -55,17 +56,31 @@ func deleteCommand(ctx context.Context, cmd *cobra.Command, _ []string, conn *gr

// List of rule types to delete
var rulesToDelete []*minderv1.RuleType

if !deleteAll {
// Fetch the rule type from the DB, so we can get its name
rtype, err := client.GetRuleTypeById(ctx, &minderv1.GetRuleTypeByIdRequest{
Context: &minderv1.Context{Project: &project},
Id: id,
})
if err != nil {
return cli.MessageAndError("Error getting rule type", err)
// Fetch the rule type from the DB by either ID or Name
if id != "" {
rtype, err := client.GetRuleTypeById(ctx, &minderv1.GetRuleTypeByIdRequest{
Context: &minderv1.Context{Project: &project},
Id: id,
})
if err != nil {
return cli.MessageAndError("Error getting rule type by id", err)
}
rulesToDelete = append(rulesToDelete, rtype.RuleType)
}
// Add the rule type for deletion
rulesToDelete = append(rulesToDelete, rtype.RuleType)

if name != "" {
rtype, err := client.GetRuleTypeByName(ctx, &minderv1.GetRuleTypeByNameRequest{
Context: &minderv1.Context{Project: &project},
Name: name,
})
if err != nil {
return cli.MessageAndError("Error getting rule type by name", err)
}
rulesToDelete = append(rulesToDelete, rtype.RuleType)
}

} else {
// List all rule types
resp, err := client.ListRuleTypes(ctx, &minderv1.ListRuleTypesRequest{
Expand Down Expand Up @@ -174,11 +189,10 @@ func init() {
ruleTypeCmd.AddCommand(deleteCmd)
// Flags
deleteCmd.Flags().StringP("id", "i", "", "ID of rule type to delete")
deleteCmd.Flags().StringP("name", "n", "", "Name of rule type to delete")
deleteCmd.Flags().BoolP("all", "a", false, "Warning: Deletes all rule types")
deleteCmd.Flags().BoolP("yes", "y", false, "Bypass yes/no prompt when deleting all rule types")
// TODO: add a flag for the rule type name
// Exclusive
deleteCmd.MarkFlagsOneRequired("id", "all")
deleteCmd.MarkFlagsMutuallyExclusive("id", "all")

deleteCmd.MarkFlagsOneRequired("id", "name", "all")
deleteCmd.MarkFlagsMutuallyExclusive("id", "name", "all")
}
9 changes: 5 additions & 4 deletions docs/docs/ref/cli/minder_ruletype_delete.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 0 additions & 4 deletions internal/controlplane/handlers_profile_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1051,10 +1051,6 @@ func TestPatchProfile(t *testing.T) {
Name: tc.baseProfile.GetProfile().GetName(),
ProjectID: dbproj.ID,
})
if err != nil {
t.Fatalf("Error getting profile: %v", err)
}

if err != nil {
t.Fatalf("Error getting profile: %v", err)
}
Expand Down
1 change: 0 additions & 1 deletion internal/controlplane/handlers_repositories_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -531,7 +531,6 @@ const (
remoteRepoId int64 = 123456
repoName2 = "another-repo"
remoteRepoId2 int64 = 234567
accessToken = "TOKEN"
)

var (
Expand Down
29 changes: 5 additions & 24 deletions internal/db/domain.go

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file looks like it might be a bad merge -- try git checkout upstream/main internal/db/domain.go

Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ package db

import (
"slices"
"strings"

"github.com/sqlc-dev/pqtype"

"github.com/mindersec/minder/internal/labels"
)

// This file contains domain-level methods for db structs
Expand Down Expand Up @@ -48,27 +49,7 @@ func (r ListProfilesByProjectIDAndLabelRow) GetSelectors() []ProfileSelector {

// LabelsFromFilter parses the filter string and populates the IncludeLabels and ExcludeLabels fields
func (lp *ListProfilesByProjectIDAndLabelParams) LabelsFromFilter(filter string) {
// If s does not contain sep and sep is not empty, Split returns a
// slice of length 1 whose only element is s. Work around that by
// returning early if filter is empty.
if filter == "" {
return
}

var starMatched bool
for _, label := range strings.Split(filter, ",") {
switch {
case label == "*":
starMatched = true
case strings.HasPrefix(label, "!"):
// if the label starts with a "!", it is a negative filter, add it to the negative list
lp.ExcludeLabels = append(lp.ExcludeLabels, label[1:])
default:
lp.IncludeLabels = append(lp.IncludeLabels, label)
}
}

if starMatched {
lp.IncludeLabels = []string{"*"}
}
inc, exc := labels.ParseLabelFilter(filter)
lp.IncludeLabels = inc
lp.ExcludeLabels = exc
}
2 changes: 1 addition & 1 deletion internal/engine/actions/actions_test.go

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this has been fixed at head; try git pull upstream main after reverting this change.

Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import (

"github.com/mindersec/minder/internal/db"
"github.com/mindersec/minder/internal/engine/actions/remediate/pull_request"
enginerr "github.com/mindersec/minder/internal/engine/errors"
engif "github.com/mindersec/minder/internal/engine/interfaces"
enginerr "github.com/mindersec/minder/pkg/engine/errors"
)

func TestShouldRemediate(t *testing.T) {
Expand Down
10 changes: 9 additions & 1 deletion internal/events/nats/natschannel.go
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,15 @@ func sendEvent(
event.SetID(msg.UUID)
event.SetType(eventType)
event.SetSource("minder") // The system which generated the event. The Minder URL would be nice here.
event.SetSubject("TODO") // This *should* represent the entity, but we don't have a standard field for it yet.
subject := ""

if val, ok := msg.Metadata["entity_id"]; ok && val != "" {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't need to check for presence if you're already checking that val is non-zero-valued.

Suggested change
if val, ok := msg.Metadata["entity_id"]; ok && val != "" {
if val := msg.Metadata["entity_id"]; val != "" {

subject = val
} else {
subject = "minder"
}

event.SetSubject(subject)
Comment on lines +209 to +217

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a stupid little Go trick, but you can use the default-zero return value from map lookup combined with cmp.Or (return first non-zero value) to write this as:

Suggested change
subject := ""
if val, ok := msg.Metadata["entity_id"]; ok && val != "" {
subject = val
} else {
subject = "minder"
}
event.SetSubject(subject)
event.SetSubject(cmp.Or(msg.Metadata["entity_id"], "minder"))


// All our current payloads are encoded JSON; we need to unmarshal
payload := map[string]any{}
Expand Down
20 changes: 10 additions & 10 deletions internal/history/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (

"github.com/mindersec/minder/internal/db"
em "github.com/mindersec/minder/internal/entities/models"
"github.com/mindersec/minder/internal/labels"
)

var (
Expand Down Expand Up @@ -375,22 +376,21 @@ func (filter *listEvaluationFilter) ExcludedProfileNames() []string {
}

func (filter *listEvaluationFilter) AddLabel(label string) error {
if label == "!*" {
return fmt.Errorf("%w: label", ErrInvalidIdentifier)
}
if label == "*" && len(filter.includedLabels) != 0 {
return fmt.Errorf("%w: label", ErrInvalidIdentifier)
inc, exc := labels.ParseLabel(label)

if inc != "" {
filter.includedLabels = append(filter.includedLabels, inc)
}
if strings.HasPrefix(label, "!") {
label = strings.Split(label, "!")[1] // guaranteed to exist
filter.excludedLabels = append(filter.excludedLabels, label)
} else {
filter.includedLabels = append(filter.includedLabels, label)
if exc != "" {
filter.excludedLabels = append(filter.excludedLabels, exc)
}

return nil
}
func (filter *listEvaluationFilter) IncludedLabels() []string {
if slices.Contains(filter.includedLabels, "*") {
return []string{"*"}
}
return filter.includedLabels
}
func (filter *listEvaluationFilter) ExcludedLabels() []string {
Expand Down
4 changes: 3 additions & 1 deletion internal/history/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -398,7 +398,9 @@ func paramsFromLabelFilter(
if len(filter.IncludedLabels()) != 0 {
params.Labels = filter.IncludedLabels()
}
// We do not exclude based on labels
if len(filter.ExcludedLabels()) != 0 {
params.Notlabels = filter.ExcludedLabels()
}
return nil
}

Expand Down
54 changes: 54 additions & 0 deletions internal/labels/labels.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// SPDX-FileCopyrightText: Copyright 2026 The Minder Authors
// SPDX-License-Identifier: Apache-2.0

// Package labels contains logic for parsing label filters.
package labels

import (
"strings"
)

// ParseLabelFilter parses a comma-separated label filter string into lists of
// labels to include and exclude. It resolves wildcards so that if any inclusion
// rule is `*`, the included labels list evaluates simply to `["*"]`.
func ParseLabelFilter(filter string) (include []string, exclude []string) {
if filter == "" {
return nil, nil
}

var starMatched bool
for _, label := range strings.Split(filter, ",") {
label = strings.TrimSpace(label)
if label == "" {
continue
}

inc, exc := ParseLabel(label)

if inc != "" {
if inc == "*" {
starMatched = true
} else {
include = append(include, inc)
}
}
if exc != "" {
exclude = append(exclude, exc)
}
}

if starMatched {
include = []string{"*"}
}

return include, exclude
}

// ParseLabel parses a single label (without commas) into an include or exclude string.
// Returns the include label (if any) and the exclude label (if any).
func ParseLabel(label string) (include string, exclude string) {
if strings.HasPrefix(label, "!") {
return "", strings.TrimPrefix(label, "!")
}
return label, ""
}
94 changes: 94 additions & 0 deletions internal/labels/labels_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// SPDX-FileCopyrightText: Copyright 2026 The Minder Authors
// SPDX-License-Identifier: Apache-2.0

package labels

import (
"testing"

"github.com/stretchr/testify/require"
)

func TestParseLabelFilter(t *testing.T) {
t.Parallel()
tests := []struct {
name string
filter string
expectedInc []string
expectedExc []string
}{
{
name: "empty",
filter: "",
},
{
name: "single include",
filter: "foo",
expectedInc: []string{"foo"},
},
{
name: "single exclude",
filter: "!foo",
expectedExc: []string{"foo"},
},
{
name: "star include",
filter: "*",
expectedInc: []string{"*"},
},
{
name: "star exclude",
filter: "!*",
expectedExc: []string{"*"},
},
{
name: "multiple includes",
filter: "foo,bar",
expectedInc: []string{"foo", "bar"},
},
{
name: "includes and excludes",
filter: "foo,!bar,baz,!qux",
expectedInc: []string{"foo", "baz"},
expectedExc: []string{"bar", "qux"},
},
{
name: "star mixed with includes",
filter: "foo,*",
expectedInc: []string{"*"},
},
{
name: "includes mixed with star",
filter: "*,foo",
expectedInc: []string{"*"},
},
{
name: "star and excludes",
filter: "*,!foo",
expectedInc: []string{"*"},
expectedExc: []string{"foo"},
},
{
name: "whitespace handling",
filter: " foo , !bar ",
expectedInc: []string{"foo"},
expectedExc: []string{"bar"},
},
{
name: "trailing commas",
filter: "foo,,!bar,",
expectedInc: []string{"foo"},
expectedExc: []string{"bar"},
},
}

for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
inc, exc := ParseLabelFilter(tt.filter)
require.Equal(t, tt.expectedInc, inc)
require.Equal(t, tt.expectedExc, exc)
})
}
}
Loading
Loading