diff --git a/pkg/policy/policy.go b/pkg/policy/policy.go index f9cd1c13d7fd26..98085712e4683b 100644 --- a/pkg/policy/policy.go +++ b/pkg/policy/policy.go @@ -1,6 +1,9 @@ package policy -import "fmt" +import ( + "fmt" + "strings" +) type Policies []*Policy @@ -63,6 +66,40 @@ func (p Policy) TotalQueries() int { return count + len(p.Checks) } +func (p Policy) Filter(path string) Policy { + if path == "" { + return p + } + selectorPath := strings.SplitN(path, "/", 3) + if len(selectorPath) == 0 { + return p + } + var emptyPolicy Policy + if selectorPath[0] != p.Name { + return emptyPolicy + } + + if strings.Count(path, "/") > 0 { + for _, policy := range p.Policies { + if policy.Name == selectorPath[1] { + return policy.Filter(strings.SplitN(path, "/", 2)[1]) + } + } + } + if selectorPath[0] == p.Name && strings.Count(path, "/") == 0 { + return p + } + for _, check := range p.Checks { + if check.Name == selectorPath[1] { + p.Checks = make([]*Check, 0) + p.Checks = append(p.Checks, check) + return p + } + } + + return emptyPolicy +} + type Meta struct { Type string Version string diff --git a/pkg/policy/policy_test.go b/pkg/policy/policy_test.go new file mode 100644 index 00000000000000..a1ef3e21c22268 --- /dev/null +++ b/pkg/policy/policy_test.go @@ -0,0 +1,296 @@ +package policy + +import ( + "fmt" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" +) + +func TestFilterPolicies(t *testing.T) { + filterTests := []struct { + p Policy + path string + expectError bool + expectedPolicy Policy + }{ + { + expectError: false, + p: Policy{ + Name: "aws", + Policies: Policies{ + &Policy{ + Name: "test2", + Policies: Policies{ + &Policy{ + Name: "test", + }, + }, + }, + }, + }, + path: "", + expectedPolicy: Policy{ + Name: "aws", + Policies: Policies{ + &Policy{ + Name: "test2", + Policies: Policies{ + &Policy{ + Name: "test", + }, + }, + }, + }, + }, + }, + { + expectError: false, + p: Policy{ + Name: "aws", + Policies: Policies{ + &Policy{ + Name: "test2", + Policies: Policies{ + &Policy{ + Name: "test", + }, + }, + }, + }, + }, + path: "aws/test1", + expectedPolicy: Policy{}, + }, { + expectError: true, + p: Policy{ + Name: "aws", + Policies: Policies{ + &Policy{ + Name: "test", + }, + }, + }, + path: "aws/test1", + expectedPolicy: Policy{ + Name: "test", + }, + }, + { + p: Policy{ + Name: "aws", + Policies: Policies{ + &Policy{ + Name: "level-1", + Policies: Policies{ + &Policy{ + Name: "level-2", + Policies: Policies{ + &Policy{ + Name: "level-3", + }, + }, + }, + }, + }, + }, + }, + path: "aws/level-1/level-2/level-3", + expectedPolicy: Policy{ + Name: "level-3", + }, + }, { + p: Policy{ + Name: "aws", + Policies: Policies{ + &Policy{ + Name: "level-1", + Policies: Policies{ + &Policy{ + Name: "level-2", + Policies: Policies{ + &Policy{ + Name: "level-3", + }, + }, + }, + }, + }, + }, + }, + path: "aws/level-1/level-2", + expectedPolicy: Policy{ + Name: "level-2", + Policies: Policies{ + &Policy{ + Name: "level-3", + }, + }, + }, + }, + { + p: Policy{ + Name: "aws", + Policies: Policies{ + &Policy{ + Name: "level-1", + Policies: Policies{ + &Policy{ + Name: "level-2", + Policies: Policies{ + &Policy{ + Name: "level-3", + Checks: []*Check{ + { + Name: "Control-1", + }, + }, + }, + }, + }, + }, + }, + }, + }, + path: "aws/level-1/level-2/level-3/Control-1", + expectedPolicy: Policy{ + Name: "level-3", + Checks: []*Check{ + { + Name: "Control-1", + }, + }, + }, + }, + { + p: Policy{ + Name: "aws", + Policies: Policies{ + &Policy{ + Name: "level-1", + Policies: Policies{ + &Policy{ + Name: "level-2", + Policies: Policies{ + &Policy{ + Name: "level-3", + Checks: []*Check{ + { + Name: "Control-1", + }, + { + Name: "Control-2", + }, + { + Name: "Control-3", + }, + { + Name: "Control-4", + }, + { + Name: "Control-5", + }, + }, + }, + }, + }, + }, + }, + }, + }, + path: "aws/level-1/level-2/level-3", + expectedPolicy: Policy{ + Name: "level-3", + Checks: []*Check{ + { + Name: "Control-1", + }, + { + Name: "Control-2", + }, + { + Name: "Control-3", + }, + { + Name: "Control-4", + }, + { + Name: "Control-5", + }, + }, + }, + }, + { + p: Policy{ + Name: "aws", + Policies: Policies{ + &Policy{ + Name: "level-1", + Policies: Policies{ + &Policy{ + Name: "level-2", + Policies: Policies{ + &Policy{ + Name: "level-3", + Checks: []*Check{ + { + Name: "Control-1", + }, + { + Name: "Control-2", + }, + { + Name: "Control-3", + }, + { + Name: "Control-4", + }, + { + Name: "Control-5", + }, + }, + }, + }, + }, + }, + }, + }, + }, + path: "aws/level-1/level-2", + expectedPolicy: Policy{ + Name: "level-2", + Policies: Policies{ + &Policy{ + Name: "level-3", + Checks: []*Check{ + { + Name: "Control-1", + }, + { + Name: "Control-2", + }, + { + Name: "Control-3", + }, + { + Name: "Control-4", + }, + { + Name: "Control-5", + }, + }, + }, + }, + }, + }, + } + for i, tt := range filterTests { + t.Run(fmt.Sprintf("case-%d", i), func(t *testing.T) { + diff := cmp.Diff(tt.expectedPolicy, tt.p.Filter(tt.path), cmpopts.IgnoreUnexported(Policy{})) + if diff != "" && !tt.expectError { + t.Fatalf("values are not the same %s", diff) + } + }) + } +} diff --git a/pkg/ui/console/client.go b/pkg/ui/console/client.go index 3325a3eabb8cb5..bf22f4ef49af6c 100644 --- a/pkg/ui/console/client.go +++ b/pkg/ui/console/client.go @@ -8,6 +8,7 @@ import ( "fmt" "os" "strconv" + "strings" "time" "github.com/cloudquery/cloudquery/internal/telemetry" @@ -236,7 +237,7 @@ func (c Client) DescribePolicies(ctx context.Context, policySource string) error } c.c.Logger.Debug("policies to describe", "policies", policiesToDescribe.All()) for _, p := range policiesToDescribe { - if err := c.describePolicy(ctx, p); err != nil { + if err := c.describePolicy(ctx, p, policySource); err != nil { return err } } @@ -527,7 +528,7 @@ func (c Client) setTelemetryAttributes(span trace.Span) { }) } -func (c Client) describePolicy(ctx context.Context, p *policy.Policy) error { +func (c Client) describePolicy(ctx context.Context, p *policy.Policy, selector string) error { p, err := c.c.LoadPolicy(ctx, p.Name, p.Source) if err != nil { ui.ColorizedOutput(ui.ColorError, err.Error()) @@ -536,9 +537,10 @@ func (c Client) describePolicy(ctx context.Context, p *policy.Policy) error { ui.ColorizedOutput(ui.ColorHeader, "Describe Policy %s output:\n\n", p.String()) t := &Table{writer: tablewriter.NewWriter(os.Stdout)} t.SetHeaders("Path", "Description") - buildDescribePolicyTable(t, policy.Policies{p}, "") + pol := p.Filter(strings.ReplaceAll(selector, "//", "/")) + buildDescribePolicyTable(t, policy.Policies{&pol}, selector[:strings.LastIndexAny(selector, "/")]) t.Render() - ui.ColorizedOutput(ui.ColorInfo, "To execute any policy use the path defined in the table above.\nFor example `cloudquery policy run %s`", buildPolicyPath(p.Name, getNestedPolicyExample(p.Policies[0], ""))) + ui.ColorizedOutput(ui.ColorInfo, "To execute any policy use the path defined in the table above.\nFor example `cloudquery policy run %s`\n", buildPolicyPath(p.Name, getNestedPolicyExample(p.Policies[0], ""))) return nil }