From 614599b5cdd78b95f226063c64653a6df6ad54e1 Mon Sep 17 00:00:00 2001 From: Juan Antonio Osorio Date: Wed, 4 Oct 2023 15:26:15 +0300 Subject: [PATCH 1/4] cleanup: Allow for policy files to not specify projects This makes it easier to create policies for self-registered users, since they can simply do `medic policy create -f ` and mediator will just adopt the default project for the user. This also moves the policy validations to the `validations.go` file in our API, since it makes things more predictable. Finally, this removes the context validation, as it's not super useful and we anyway check for this along the code-base. --- cmd/cli/app/policy/policy_create.go | 2 +- examples/github/policies/policy.yaml | 1 - examples/github/policies/policy_artifact.yaml | 1 - examples/github/policies/pr_vuln_check.yaml | 1 - .../rule-types/actions_check_pinned_tags.yaml | 1 - .../rule-types/allowed_selected_actions.yaml | 1 - .../github/rule-types/artifact_signature.yaml | 1 - .../github/rule-types/branch_protection.yaml | 1 - .../github/rule-types/codeql_enabled.yaml | 1 - .../default_workflow_permissions.yaml | 1 - .../rule-types/dependabot_configured.yaml | 1 - .../rule-types/dockerfile_no_latest_tag.yaml | 1 - .../rule-types/github_actions_allowed.yaml | 1 - .../rule-types/pr_vulnerability_check.yaml | 1 - .../repo_workflow_access_level.yaml | 1 - .../rule-types/secret_push_protection.yaml | 1 - .../github/rule-types/secret_scanning.yaml | 1 - .../rule-types/trivy_action_enabled.yaml | 1 - internal/controlplane/handlers_policy.go | 2 +- internal/engine/policy.go | 87 +------------------ internal/engine/policy_test.go | 78 ++++++++--------- pkg/api/protobuf/go/mediator/v1/policy.go | 15 ++++ pkg/api/protobuf/go/mediator/v1/validators.go | 57 ++++++++++++ 23 files changed, 114 insertions(+), 144 deletions(-) create mode 100644 pkg/api/protobuf/go/mediator/v1/policy.go diff --git a/cmd/cli/app/policy/policy_create.go b/cmd/cli/app/policy/policy_create.go index 5b624d850f..dd898181f3 100644 --- a/cmd/cli/app/policy/policy_create.go +++ b/cmd/cli/app/policy/policy_create.go @@ -75,7 +75,7 @@ within a mediator control plane.`, p, err := engine.ParseYAML(preader) if err != nil { - return fmt.Errorf("error reading fragment from file: %w", err) + return fmt.Errorf("error reading policy from file: %w", err) } // create a policy diff --git a/examples/github/policies/policy.yaml b/examples/github/policies/policy.yaml index 4b32e0e761..3dfea3319e 100644 --- a/examples/github/policies/policy.yaml +++ b/examples/github/policies/policy.yaml @@ -4,7 +4,6 @@ version: v1 type: policy name: acme-github-policy context: - project: Root Project provider: github repository: - type: secret_scanning diff --git a/examples/github/policies/policy_artifact.yaml b/examples/github/policies/policy_artifact.yaml index 42290bea94..0698d5fd97 100644 --- a/examples/github/policies/policy_artifact.yaml +++ b/examples/github/policies/policy_artifact.yaml @@ -4,7 +4,6 @@ version: v1 type: policy name: acme-github-policy-artifact context: - project: Root Project provider: github artifact: - type: artifact_signature diff --git a/examples/github/policies/pr_vuln_check.yaml b/examples/github/policies/pr_vuln_check.yaml index 7c99e3eb8a..45e67e20c9 100644 --- a/examples/github/policies/pr_vuln_check.yaml +++ b/examples/github/policies/pr_vuln_check.yaml @@ -3,7 +3,6 @@ version: v1 type: policy name: acme-github-policy-pr-vuln-check context: - project: Root Project provider: github pull_request: - type: pr_vulnerability_check diff --git a/examples/github/rule-types/actions_check_pinned_tags.yaml b/examples/github/rule-types/actions_check_pinned_tags.yaml index 6e41112ec1..f980a4d90f 100644 --- a/examples/github/rule-types/actions_check_pinned_tags.yaml +++ b/examples/github/rule-types/actions_check_pinned_tags.yaml @@ -4,7 +4,6 @@ type: rule-type name: actions_check_pinned_tags context: provider: github - group: Root Group description: Verifies that any actions use pinned tags guidance: | Verifies that any actions use pinned tags diff --git a/examples/github/rule-types/allowed_selected_actions.yaml b/examples/github/rule-types/allowed_selected_actions.yaml index ec80586472..b4ef86bc67 100644 --- a/examples/github/rule-types/allowed_selected_actions.yaml +++ b/examples/github/rule-types/allowed_selected_actions.yaml @@ -4,7 +4,6 @@ type: rule-type name: allowed_selected_actions context: provider: github - group: Root Group description: | Verifies the settings for selected actions and reusable workflows that are allowed in a repository. To use this rule, the repository policy for allowed_actions must diff --git a/examples/github/rule-types/artifact_signature.yaml b/examples/github/rule-types/artifact_signature.yaml index cd3be1fa74..7309ae88a6 100644 --- a/examples/github/rule-types/artifact_signature.yaml +++ b/examples/github/rule-types/artifact_signature.yaml @@ -4,7 +4,6 @@ type: rule-type name: artifact_signature context: provider: github - group: Root Group description: Verifies that a given artifact has a valid signature. guidance: | Artifact signing allows a user to add a digital fingerprint to an artifact and verify its trust later. diff --git a/examples/github/rule-types/branch_protection.yaml b/examples/github/rule-types/branch_protection.yaml index 567e335ff3..3c1940a663 100644 --- a/examples/github/rule-types/branch_protection.yaml +++ b/examples/github/rule-types/branch_protection.yaml @@ -4,7 +4,6 @@ type: rule-type name: branch_protection context: provider: github - group: Root Group description: Verifies that a branch has proper protections. guidance: | You can protect important branches by setting branch protection rules, which define whether diff --git a/examples/github/rule-types/codeql_enabled.yaml b/examples/github/rule-types/codeql_enabled.yaml index 051e2cf16f..ca79a2e496 100644 --- a/examples/github/rule-types/codeql_enabled.yaml +++ b/examples/github/rule-types/codeql_enabled.yaml @@ -4,7 +4,6 @@ type: rule-type name: codeql_enabled context: provider: github - group: Root Group description: Verifies that CodeQL is enabled for the repository guidance: | CodeQL is a tool that can be used to analyze code for security vulnerabilities. diff --git a/examples/github/rule-types/default_workflow_permissions.yaml b/examples/github/rule-types/default_workflow_permissions.yaml index 759a8fba15..9a353704c6 100644 --- a/examples/github/rule-types/default_workflow_permissions.yaml +++ b/examples/github/rule-types/default_workflow_permissions.yaml @@ -4,7 +4,6 @@ type: rule-type name: default_workflow_permissions context: provider: github - group: Root Group description: | Verifies the default workflow permissions granted to the GITHUB_TOKEN when running workflows in a repository, as well as if GitHub Actions diff --git a/examples/github/rule-types/dependabot_configured.yaml b/examples/github/rule-types/dependabot_configured.yaml index 1ce6be3e50..045afd67be 100644 --- a/examples/github/rule-types/dependabot_configured.yaml +++ b/examples/github/rule-types/dependabot_configured.yaml @@ -4,7 +4,6 @@ type: rule-type name: dependabot_configured context: provider: github - group: Root Group description: Verifies that Dependabot is configured for the repository guidance: | Dependabot enables Automated dependency updates for repositories. diff --git a/examples/github/rule-types/dockerfile_no_latest_tag.yaml b/examples/github/rule-types/dockerfile_no_latest_tag.yaml index 78b3fc1e08..85b11db01c 100644 --- a/examples/github/rule-types/dockerfile_no_latest_tag.yaml +++ b/examples/github/rule-types/dockerfile_no_latest_tag.yaml @@ -4,7 +4,6 @@ type: rule-type name: dockerfile_no_latest_tag context: provider: github - group: Root Group description: Verifies that the Dockerfile image references don't use the latest tag guidance: | Using the latest tag for Docker images is not recommended as it can lead to unexpected behavior. diff --git a/examples/github/rule-types/github_actions_allowed.yaml b/examples/github/rule-types/github_actions_allowed.yaml index 331b4ab8a5..8182a0e738 100644 --- a/examples/github/rule-types/github_actions_allowed.yaml +++ b/examples/github/rule-types/github_actions_allowed.yaml @@ -4,7 +4,6 @@ type: rule-type name: github_actions_allowed context: provider: github - group: Root Group description: | Verifies permissions for github actions for a specific repository. diff --git a/examples/github/rule-types/pr_vulnerability_check.yaml b/examples/github/rule-types/pr_vulnerability_check.yaml index 8b2f1f9057..7a9f920658 100644 --- a/examples/github/rule-types/pr_vulnerability_check.yaml +++ b/examples/github/rule-types/pr_vulnerability_check.yaml @@ -4,7 +4,6 @@ type: rule-type name: pr_vulnerability_check context: provider: github - group: Root Group description: Verifies that pull requests do not add any vulnerable dependecies guidance: | For every pull request submitted to a repository, this rule will check if the pull request diff --git a/examples/github/rule-types/repo_workflow_access_level.yaml b/examples/github/rule-types/repo_workflow_access_level.yaml index 592fb09c3b..516c97523b 100644 --- a/examples/github/rule-types/repo_workflow_access_level.yaml +++ b/examples/github/rule-types/repo_workflow_access_level.yaml @@ -4,7 +4,6 @@ type: rule-type name: repo_workflow_access_level context: provider: github - group: Root Group description: | Verifies the level of access that workflows outside of the repository have to actions and reusable workflows in the repository. This only applies to private repositories. diff --git a/examples/github/rule-types/secret_push_protection.yaml b/examples/github/rule-types/secret_push_protection.yaml index 342acf500d..04fd824bf4 100644 --- a/examples/github/rule-types/secret_push_protection.yaml +++ b/examples/github/rule-types/secret_push_protection.yaml @@ -4,7 +4,6 @@ type: rule-type name: secret_push_protection context: provider: github - group: Root Group description: Verfies that secret push protection is enabled for a given repository. guidance: | You can use secret scanning to prevent supported secrets from being pushed into your repository by enabling secret scanning push protection. diff --git a/examples/github/rule-types/secret_scanning.yaml b/examples/github/rule-types/secret_scanning.yaml index 78a0fad182..942d2efc3b 100644 --- a/examples/github/rule-types/secret_scanning.yaml +++ b/examples/github/rule-types/secret_scanning.yaml @@ -4,7 +4,6 @@ type: rule-type name: secret_scanning context: provider: github - group: Root Group description: Verifies that secret scanning is enabled for a given repository. guidance: | Secret scanning is a feature that scans repositories for secrets and alerts diff --git a/examples/github/rule-types/trivy_action_enabled.yaml b/examples/github/rule-types/trivy_action_enabled.yaml index b8a905e4ca..820eee87f7 100644 --- a/examples/github/rule-types/trivy_action_enabled.yaml +++ b/examples/github/rule-types/trivy_action_enabled.yaml @@ -4,7 +4,6 @@ type: rule-type name: trivy_action_enabled context: provider: github - group: Root Group description: Verifies that the Trivy action is enabled for the repository and scanning guidance: | ## Please set up trivy! diff --git a/internal/controlplane/handlers_policy.go b/internal/controlplane/handlers_policy.go index a4643928ab..3b61f90a5a 100644 --- a/internal/controlplane/handlers_policy.go +++ b/internal/controlplane/handlers_policy.go @@ -129,7 +129,7 @@ func (s *Server) CreatePolicy(ctx context.Context, return nil, providerError(fmt.Errorf("provider error: %w", err)) } - if err := engine.ValidatePolicy(in); err != nil { + if err := in.Validate(); err != nil { return nil, status.Errorf(codes.InvalidArgument, "invalid policy: %v", err) } diff --git a/internal/engine/policy.go b/internal/engine/policy.go index fe6e79651f..06f80ea5db 100644 --- a/internal/engine/policy.go +++ b/internal/engine/policy.go @@ -30,11 +30,6 @@ import ( pb "github.com/stacklok/mediator/pkg/api/protobuf/go/mediator/v1" ) -var ( - // ErrValidationFailed is returned when a policy fails validation - ErrValidationFailed = fmt.Errorf("validation failed") -) - // RuleValidationError is used to report errors from evaluating a rule, including // attribution of the particular error encountered. type RuleValidationError struct { @@ -71,7 +66,7 @@ func ParseJSON(r io.Reader) (*pb.Policy, error) { return nil, fmt.Errorf("error decoding json: %w", err) } - if err := ValidatePolicy(&out); err != nil { + if err := out.Validate(); err != nil { return nil, fmt.Errorf("error validating policy: %w", err) } @@ -101,86 +96,6 @@ func ReadPolicyFromFile(fpath string) (*pb.Policy, error) { return out, nil } -// ValidatePolicy validates a pipeline policy -func ValidatePolicy(p *pb.Policy) error { - if err := validateContext(p.Context); err != nil { - return err - } - - // If the policy is nil or empty, we don't need to validate it - if p.Repository != nil && len(p.Repository) > 0 { - return validateEntity(p.Repository) - } - - if p.BuildEnvironment != nil && len(p.BuildEnvironment) > 0 { - return validateEntity(p.BuildEnvironment) - } - - if p.Artifact != nil && len(p.Artifact) > 0 { - return validateEntity(p.Artifact) - } - - if p.PullRequest != nil && len(p.PullRequest) > 0 { - return validateEntity(p.PullRequest) - } - - return nil -} - -func validateContext(c *pb.Context) error { - if c == nil { - return fmt.Errorf("%w: context cannot be empty", ErrValidationFailed) - } - - if c.Provider == "" { - return fmt.Errorf("%w: context provider cannot be empty", ErrValidationFailed) - } - - if c.Organization == nil && c.Project == nil { - return fmt.Errorf("%w: context organization or group must be set", ErrValidationFailed) - } - - if c.Organization != nil && *c.Organization == "" { - return fmt.Errorf("%w: context organization cannot be empty", ErrValidationFailed) - } - - if c.Project != nil && *c.Project == "" { - return fmt.Errorf("%w: context group cannot be empty", ErrValidationFailed) - } - - return nil -} - -func validateEntity(e []*pb.Policy_Rule) error { - if len(e) == 0 { - return fmt.Errorf("%w: entity rules cannot be empty", ErrValidationFailed) - } - - for _, r := range e { - if r == nil { - return fmt.Errorf("%w: entity contextual rules cannot be nil", ErrValidationFailed) - } - - if err := validateRule(r); err != nil { - return err - } - } - - return nil -} - -func validateRule(r *pb.Policy_Rule) error { - if r.Type == "" { - return fmt.Errorf("%w: rule type cannot be empty", ErrValidationFailed) - } - - if r.Def == nil { - return fmt.Errorf("%w: rule def cannot be nil", ErrValidationFailed) - } - - return nil -} - // GetRulesForEntity returns the rules for the given entity func GetRulesForEntity(p *pb.Policy, entity pb.Entity) ([]*pb.Policy_Rule, error) { switch entity { diff --git a/internal/engine/policy_test.go b/internal/engine/policy_test.go index 42eb4ca154..1426de418d 100644 --- a/internal/engine/policy_test.go +++ b/internal/engine/policy_test.go @@ -23,14 +23,14 @@ import ( "google.golang.org/protobuf/types/known/structpb" "github.com/stacklok/mediator/internal/engine" - pb "github.com/stacklok/mediator/pkg/api/protobuf/go/mediator/v1" + mediatorv1 "github.com/stacklok/mediator/pkg/api/protobuf/go/mediator/v1" ) var ( defaultOrg = "ACME" ) -func comparePolicies(t *testing.T, a *pb.Policy, b *pb.Policy) { +func comparePolicies(t *testing.T, a *mediatorv1.Policy, b *mediatorv1.Policy) { t.Helper() require.Equal(t, a.Name, b.Name, "policy names should match") @@ -40,7 +40,7 @@ func comparePolicies(t *testing.T, a *pb.Policy, b *pb.Policy) { compareEntityRules(t, a.Artifact, b.Artifact) } -func compareEntityRules(t *testing.T, a []*pb.Policy_Rule, b []*pb.Policy_Rule) { +func compareEntityRules(t *testing.T, a []*mediatorv1.Policy_Rule, b []*mediatorv1.Policy_Rule) { t.Helper() require.Equal(t, len(a), len(b), "rule sets should have the same length") @@ -50,7 +50,7 @@ func compareEntityRules(t *testing.T, a []*pb.Policy_Rule, b []*pb.Policy_Rule) } } -func compareRule(t *testing.T, a *pb.Policy_Rule, b *pb.Policy_Rule) { +func compareRule(t *testing.T, a *mediatorv1.Policy_Rule, b *mediatorv1.Policy_Rule) { t.Helper() require.Equal(t, a.Type, b.Type, "rule types should match") @@ -110,7 +110,7 @@ func TestParseYAML(t *testing.T) { tests := []struct { name string policy string - want *pb.Policy + want *mediatorv1.Policy wantErr bool errIs error }{ @@ -141,13 +141,13 @@ artifact: def: state: exists `, - want: &pb.Policy{ + want: &mediatorv1.Policy{ Name: "acme-github-policy", - Context: &pb.Context{ + Context: &mediatorv1.Context{ Organization: &defaultOrg, Provider: "github", }, - Repository: []*pb.Policy_Rule{ + Repository: []*mediatorv1.Policy_Rule{ { Type: "secret_scanning", Def: &structpb.Struct{ @@ -161,7 +161,7 @@ artifact: }, }, }, - BuildEnvironment: []*pb.Policy_Rule{ + BuildEnvironment: []*mediatorv1.Policy_Rule{ { Type: "no_org_wide_github_action_permissions", Def: &structpb.Struct{ @@ -175,7 +175,7 @@ artifact: }, }, }, - Artifact: []*pb.Policy_Rule{ + Artifact: []*mediatorv1.Policy_Rule{ { Type: "ctlog_entry", Params: &structpb.Struct{ @@ -225,13 +225,13 @@ repository: def: enabled: true `, - want: &pb.Policy{ + want: &mediatorv1.Policy{ Name: "acme-github-policy", - Context: &pb.Context{ + Context: &mediatorv1.Context{ Organization: &defaultOrg, Provider: "github", }, - Repository: []*pb.Policy_Rule{ + Repository: []*mediatorv1.Policy_Rule{ { Type: "secret_scanning", Def: &structpb.Struct{ @@ -277,7 +277,7 @@ repository: - type: secret_scanning `, wantErr: true, - errIs: engine.ErrValidationFailed, + errIs: mediatorv1.ErrValidationFailed, }, { name: "invalid with nil rule", @@ -296,7 +296,7 @@ repository: enabled: true `, wantErr: true, - errIs: engine.ErrValidationFailed, + errIs: mediatorv1.ErrValidationFailed, }, { name: "invalid with no context", @@ -311,7 +311,7 @@ repository: enabled: true `, wantErr: true, - errIs: engine.ErrValidationFailed, + errIs: mediatorv1.ErrValidationFailed, }, { name: "invalid with no name", @@ -325,7 +325,7 @@ repository: enabled: true `, wantErr: true, - errIs: engine.ErrValidationFailed, + errIs: mediatorv1.ErrValidationFailed, }, } for _, tt := range tests { @@ -354,13 +354,13 @@ repository: func TestGetRulesForEntity(t *testing.T) { t.Parallel() - pol := &pb.Policy{ + pol := &mediatorv1.Policy{ Name: "acme-github-policy", - Context: &pb.Context{ + Context: &mediatorv1.Context{ Organization: &defaultOrg, Provider: "github", }, - Repository: []*pb.Policy_Rule{ + Repository: []*mediatorv1.Policy_Rule{ { Type: "secret_scanning", Def: &structpb.Struct{ @@ -374,7 +374,7 @@ func TestGetRulesForEntity(t *testing.T) { }, }, }, - BuildEnvironment: []*pb.Policy_Rule{ + BuildEnvironment: []*mediatorv1.Policy_Rule{ { Type: "no_org_wide_github_action_permissions", Def: &structpb.Struct{ @@ -388,7 +388,7 @@ func TestGetRulesForEntity(t *testing.T) { }, }, }, - Artifact: []*pb.Policy_Rule{ + Artifact: []*mediatorv1.Policy_Rule{ { Type: "ctlog_entry", Params: &structpb.Struct{ @@ -424,22 +424,22 @@ func TestGetRulesForEntity(t *testing.T) { } type args struct { - p *pb.Policy - entity pb.Entity + p *mediatorv1.Policy + entity mediatorv1.Entity } tests := []struct { name string args args - want []*pb.Policy_Rule + want []*mediatorv1.Policy_Rule wantErr bool }{ { name: "valid rules for repository", args: args{ p: pol, - entity: pb.Entity_ENTITY_REPOSITORIES, + entity: mediatorv1.Entity_ENTITY_REPOSITORIES, }, - want: []*pb.Policy_Rule{ + want: []*mediatorv1.Policy_Rule{ { Type: "secret_scanning", Def: &structpb.Struct{ @@ -458,9 +458,9 @@ func TestGetRulesForEntity(t *testing.T) { name: "valid rules for build environment", args: args{ p: pol, - entity: pb.Entity_ENTITY_BUILD_ENVIRONMENTS, + entity: mediatorv1.Entity_ENTITY_BUILD_ENVIRONMENTS, }, - want: []*pb.Policy_Rule{ + want: []*mediatorv1.Policy_Rule{ { Type: "no_org_wide_github_action_permissions", Def: &structpb.Struct{ @@ -479,9 +479,9 @@ func TestGetRulesForEntity(t *testing.T) { name: "valid rules for artifacts", args: args{ p: pol, - entity: pb.Entity_ENTITY_ARTIFACTS, + entity: mediatorv1.Entity_ENTITY_ARTIFACTS, }, - want: []*pb.Policy_Rule{ + want: []*mediatorv1.Policy_Rule{ { Type: "ctlog_entry", Params: &structpb.Struct{ @@ -536,7 +536,7 @@ func TestGetRulesForEntity(t *testing.T) { func TestFilterRulesForType(t *testing.T) { t.Parallel() - crs := []*pb.Policy_Rule{ + crs := []*mediatorv1.Policy_Rule{ { Type: "secret_scanning", Def: &structpb.Struct{ @@ -595,8 +595,8 @@ func TestFilterRulesForType(t *testing.T) { } type args struct { - cr []*pb.Policy_Rule - rt *pb.RuleType + cr []*mediatorv1.Policy_Rule + rt *mediatorv1.RuleType } tests := []struct { name string @@ -608,7 +608,7 @@ func TestFilterRulesForType(t *testing.T) { name: "valid filter for secret scanning", args: args{ cr: crs, - rt: &pb.RuleType{ + rt: &mediatorv1.RuleType{ Name: "secret_scanning", }, }, @@ -618,7 +618,7 @@ func TestFilterRulesForType(t *testing.T) { name: "valid filter for no_org_wide_github_action_permissions", args: args{ cr: crs, - rt: &pb.RuleType{ + rt: &mediatorv1.RuleType{ Name: "no_org_wide_github_action_permissions", }, }, @@ -628,7 +628,7 @@ func TestFilterRulesForType(t *testing.T) { name: "valid filter for ctlog_entry", args: args{ cr: crs, - rt: &pb.RuleType{ + rt: &mediatorv1.RuleType{ Name: "ctlog_entry", }, }, @@ -641,8 +641,8 @@ func TestFilterRulesForType(t *testing.T) { t.Run(tt.name, func(t *testing.T) { t.Parallel() - got := []*pb.Policy_Rule{} - err := engine.TraverseRules(tt.args.cr, func(pp *pb.Policy_Rule) error { + got := []*mediatorv1.Policy_Rule{} + err := engine.TraverseRules(tt.args.cr, func(pp *mediatorv1.Policy_Rule) error { if pp.Type == tt.args.rt.Name { got = append(got, pp) } diff --git a/pkg/api/protobuf/go/mediator/v1/policy.go b/pkg/api/protobuf/go/mediator/v1/policy.go new file mode 100644 index 0000000000..50b69c0393 --- /dev/null +++ b/pkg/api/protobuf/go/mediator/v1/policy.go @@ -0,0 +1,15 @@ +// Copyright 2023 Stacklok, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package _go diff --git a/pkg/api/protobuf/go/mediator/v1/validators.go b/pkg/api/protobuf/go/mediator/v1/validators.go index c644a9d054..76a8d75eb5 100644 --- a/pkg/api/protobuf/go/mediator/v1/validators.go +++ b/pkg/api/protobuf/go/mediator/v1/validators.go @@ -19,6 +19,11 @@ import ( "fmt" ) +var ( + // ErrValidationFailed is returned when a policy fails validation + ErrValidationFailed = fmt.Errorf("validation failed") +) + // Validator is an interface which allows for the validation of a struct. type Validator interface { Validate() error @@ -112,3 +117,55 @@ func (def *RuleType_Definition) Validate() error { return nil } + +// ValidatePolicy validates a pipeline policy +func (p *Policy) Validate() error { + // If the policy is nil or empty, we don't need to validate it + if p.Repository != nil && len(p.Repository) > 0 { + return validateEntity(p.Repository) + } + + if p.BuildEnvironment != nil && len(p.BuildEnvironment) > 0 { + return validateEntity(p.BuildEnvironment) + } + + if p.Artifact != nil && len(p.Artifact) > 0 { + return validateEntity(p.Artifact) + } + + if p.PullRequest != nil && len(p.PullRequest) > 0 { + return validateEntity(p.PullRequest) + } + + return nil +} + +func validateEntity(e []*Policy_Rule) error { + if len(e) == 0 { + return fmt.Errorf("%w: entity rules cannot be empty", ErrValidationFailed) + } + + for _, r := range e { + if r == nil { + return fmt.Errorf("%w: entity contextual rules cannot be nil", ErrValidationFailed) + } + + if err := validateRule(r); err != nil { + return err + } + } + + return nil +} + +func validateRule(r *Policy_Rule) error { + if r.Type == "" { + return fmt.Errorf("%w: rule type cannot be empty", ErrValidationFailed) + } + + if r.Def == nil { + return fmt.Errorf("%w: rule def cannot be nil", ErrValidationFailed) + } + + return nil +} From ca594b0c70cbf4875a0a12dc070e9795f6957fca Mon Sep 17 00:00:00 2001 From: Juan Antonio Osorio Date: Wed, 4 Oct 2023 15:31:00 +0300 Subject: [PATCH 2/4] cli: Add flag to policy create to overwrite the project for a policy --- cmd/cli/app/policy/policy_create.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/cmd/cli/app/policy/policy_create.go b/cmd/cli/app/policy/policy_create.go index dd898181f3..40a56f4186 100644 --- a/cmd/cli/app/policy/policy_create.go +++ b/cmd/cli/app/policy/policy_create.go @@ -42,6 +42,7 @@ within a mediator control plane.`, }, RunE: func(cmd *cobra.Command, args []string) error { f := util.GetConfigValue("file", "file", cmd, "").(string) + proj := viper.GetString("project") var err error @@ -78,6 +79,14 @@ within a mediator control plane.`, return fmt.Errorf("error reading policy from file: %w", err) } + if proj != "" { + if p.Context == nil { + p.Context = &pb.Context{} + } + + p.Context.Project = &proj + } + // create a policy resp, err := client.CreatePolicy(ctx, &pb.CreatePolicyRequest{ Policy: p, @@ -96,4 +105,5 @@ within a mediator control plane.`, func init() { PolicyCmd.AddCommand(Policy_createCmd) Policy_createCmd.Flags().StringP("file", "f", "", "Path to the YAML defining the policy (or - for stdin)") + Policy_createCmd.Flags().StringP("project", "p", "", "Project to create the policy in") } From 2d17b1866bcf4cb12ce0198d5bef9b4b0d59b016 Mon Sep 17 00:00:00 2001 From: Juan Antonio Osorio Date: Wed, 4 Oct 2023 15:31:21 +0300 Subject: [PATCH 3/4] add comment to validator function --- pkg/api/protobuf/go/mediator/v1/validators.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/api/protobuf/go/mediator/v1/validators.go b/pkg/api/protobuf/go/mediator/v1/validators.go index 76a8d75eb5..55cc57c963 100644 --- a/pkg/api/protobuf/go/mediator/v1/validators.go +++ b/pkg/api/protobuf/go/mediator/v1/validators.go @@ -118,7 +118,7 @@ func (def *RuleType_Definition) Validate() error { return nil } -// ValidatePolicy validates a pipeline policy +// Validate validates a pipeline policy func (p *Policy) Validate() error { // If the policy is nil or empty, we don't need to validate it if p.Repository != nil && len(p.Repository) > 0 { From bc4caaa68b5fa5e96b03b891c6003a100891b165 Mon Sep 17 00:00:00 2001 From: Juan Antonio Osorio Date: Wed, 4 Oct 2023 15:39:59 +0300 Subject: [PATCH 4/4] policy: Validate name explicitly This also removes the context validation test. --- internal/engine/policy_test.go | 15 --------------- pkg/api/protobuf/go/mediator/v1/validators.go | 4 ++++ 2 files changed, 4 insertions(+), 15 deletions(-) diff --git a/internal/engine/policy_test.go b/internal/engine/policy_test.go index 1426de418d..e04abf1593 100644 --- a/internal/engine/policy_test.go +++ b/internal/engine/policy_test.go @@ -294,21 +294,6 @@ repository: - type: secret_scanning def: enabled: true -`, - wantErr: true, - errIs: mediatorv1.ErrValidationFailed, - }, - { - name: "invalid with no context", - policy: ` ---- -version: v1 -type: policy -name: acme-github-policy -repository: - - type: secret_scanning - def: - enabled: true `, wantErr: true, errIs: mediatorv1.ErrValidationFailed, diff --git a/pkg/api/protobuf/go/mediator/v1/validators.go b/pkg/api/protobuf/go/mediator/v1/validators.go index 55cc57c963..ebf3037817 100644 --- a/pkg/api/protobuf/go/mediator/v1/validators.go +++ b/pkg/api/protobuf/go/mediator/v1/validators.go @@ -120,6 +120,10 @@ func (def *RuleType_Definition) Validate() error { // Validate validates a pipeline policy func (p *Policy) Validate() error { + if p.Name == "" { + return fmt.Errorf("%w: policy name cannot be empty", ErrValidationFailed) + } + // If the policy is nil or empty, we don't need to validate it if p.Repository != nil && len(p.Repository) > 0 { return validateEntity(p.Repository)