diff --git a/docs/docs/ref/proto.mdx b/docs/docs/ref/proto.mdx index 2fd42324b6..5e843da9eb 100644 --- a/docs/docs/ref/proto.mdx +++ b/docs/docs/ref/proto.mdx @@ -2766,6 +2766,7 @@ Definition defines the rule type. It encompases the schema and the data evaluati | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | review_message | string | | | +| action | string | | | diff --git a/internal/engine/actions/alert/pull_request_comment/pull_request_comment.go b/internal/engine/actions/alert/pull_request_comment/pull_request_comment.go index c5748dd95b..04c3c710e5 100644 --- a/internal/engine/actions/alert/pull_request_comment/pull_request_comment.go +++ b/internal/engine/actions/alert/pull_request_comment/pull_request_comment.go @@ -141,9 +141,15 @@ func (alert *Alert) run(ctx context.Context, params *paramsPR, cmd interfaces.Ac switch cmd { // Create a review case interfaces.ActionCmdOn: + + event := "COMMENT" + if alert.reviewCfg.GetAction() == "request_changes" { + event = "REQUEST_CHANGES" + } + review := &github.PullRequestReviewRequest{ CommitID: github.String(params.CommitSha), - Event: github.String("COMMENT"), + Event: github.String(event), Body: github.String(params.Comment), } diff --git a/internal/engine/actions/alert/pull_request_comment/pull_request_comment_test.go b/internal/engine/actions/alert/pull_request_comment/pull_request_comment_test.go index 7499d93ad7..8c5d8e0a2e 100644 --- a/internal/engine/actions/alert/pull_request_comment/pull_request_comment_test.go +++ b/internal/engine/actions/alert/pull_request_comment/pull_request_comment_test.go @@ -43,6 +43,7 @@ func TestPullRequestCommentAlert(t *testing.T) { actionType engif.ActionType cmd engif.ActionCmd reviewMsg string + cfgAction string inputMetadata *json.RawMessage mockSetup func(*mockghclient.MockGitHub) expectedErr error @@ -52,6 +53,7 @@ func TestPullRequestCommentAlert(t *testing.T) { name: "create a PR comment", actionType: TestActionTypeValid, reviewMsg: "This is a constant review message", + cfgAction: "comment", cmd: engif.ActionCmdOn, mockSetup: func(mockGitHub *mockghclient.MockGitHub) { mockGitHub.EXPECT(). @@ -64,6 +66,7 @@ func TestPullRequestCommentAlert(t *testing.T) { name: "create a PR comment with eval error details template", actionType: TestActionTypeValid, reviewMsg: "{{ .EvalErrorDetails }}", + cfgAction: "comment", cmd: engif.ActionCmdOn, mockSetup: func(mockGitHub *mockghclient.MockGitHub) { mockGitHub.EXPECT(). @@ -76,6 +79,7 @@ func TestPullRequestCommentAlert(t *testing.T) { name: "create a PR comment with eval result output template", actionType: TestActionTypeValid, reviewMsg: "{{ .EvalResultOutput.ViolationMsg }}", + cfgAction: "comment", cmd: engif.ActionCmdOn, mockSetup: func(mockGitHub *mockghclient.MockGitHub) { mockGitHub.EXPECT(). @@ -88,6 +92,7 @@ func TestPullRequestCommentAlert(t *testing.T) { name: "error from provider creating PR comment", actionType: TestActionTypeValid, reviewMsg: "This is a constant review message", + cfgAction: "comment", cmd: engif.ActionCmdOn, mockSetup: func(mockGitHub *mockghclient.MockGitHub) { mockGitHub.EXPECT(). @@ -100,6 +105,7 @@ func TestPullRequestCommentAlert(t *testing.T) { name: "dismiss PR comment", actionType: TestActionTypeValid, reviewMsg: "This is a constant review message", + cfgAction: "comment", cmd: engif.ActionCmdOff, inputMetadata: &successfulRunMetadata, mockSetup: func(mockGitHub *mockghclient.MockGitHub) { @@ -109,6 +115,24 @@ func TestPullRequestCommentAlert(t *testing.T) { }, expectedErr: enginerr.ErrActionTurnedOff, }, + { + name: "create a PR review with request_changes action", + actionType: TestActionTypeValid, + reviewMsg: "Security block: please remove binaries", + cfgAction: "request_changes", + cmd: engif.ActionCmdOn, + mockSetup: func(mockGitHub *mockghclient.MockGitHub) { + mockGitHub.EXPECT(). + CreateReview(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). + DoAndReturn(func(_ context.Context, _, _ string, _ int, review *github.PullRequestReviewRequest) (*github.PullRequestReview, error) { + if review.GetEvent() != "REQUEST_CHANGES" { + return nil, fmt.Errorf("expected event REQUEST_CHANGES, got %s", review.GetEvent()) + } + return &github.PullRequestReview{ID: &reviewID}, nil + }) + }, + expectedMetadata: json.RawMessage(fmt.Sprintf(`{"review_id":"%s"}`, reviewIDStr)), + }, } for _, tt := range tests { @@ -123,6 +147,7 @@ func TestPullRequestCommentAlert(t *testing.T) { prCommentCfg := pb.RuleType_Definition_Alert_AlertTypePRComment{ ReviewMessage: tt.reviewMsg, + Action: tt.cfgAction, } mockClient := mockghclient.NewMockGitHub(ctrl) diff --git a/pkg/api/openapi/minder/v1/minder.swagger.json b/pkg/api/openapi/minder/v1/minder.swagger.json index c3fbcc8f72..25fbdaaff6 100644 --- a/pkg/api/openapi/minder/v1/minder.swagger.json +++ b/pkg/api/openapi/minder/v1/minder.swagger.json @@ -3448,6 +3448,9 @@ "properties": { "reviewMessage": { "type": "string" + }, + "action": { + "type": "string" } }, "required": [ diff --git a/pkg/api/protobuf/go/minder/v1/minder.pb.go b/pkg/api/protobuf/go/minder/v1/minder.pb.go index 1a21604a64..e6a4a7c2c8 100644 --- a/pkg/api/protobuf/go/minder/v1/minder.pb.go +++ b/pkg/api/protobuf/go/minder/v1/minder.pb.go @@ -14253,6 +14253,7 @@ func (x *RuleType_Definition_Alert_AlertTypeSA) GetSeverity() string { type RuleType_Definition_Alert_AlertTypePRComment struct { state protoimpl.MessageState `protogen:"open.v1"` ReviewMessage string `protobuf:"bytes,1,opt,name=review_message,json=reviewMessage,proto3" json:"review_message,omitempty"` + Action string `protobuf:"bytes,2,opt,name=action,proto3" json:"action,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -14294,6 +14295,13 @@ func (x *RuleType_Definition_Alert_AlertTypePRComment) GetReviewMessage() string return "" } +func (x *RuleType_Definition_Alert_AlertTypePRComment) GetAction() string { + if x != nil { + return x.Action + } + return "" +} + // Rule defines the individual call of a certain rule type. type Profile_Rule struct { state protoimpl.MessageState `protogen:"open.v1"` @@ -15333,7 +15341,7 @@ const file_minder_v1_minder_proto_rawDesc = "" + "\xea\xdc\x14\x06medium\x12\x18\n" + "\n" + "VALUE_HIGH\x10\x05\x1a\b\xea\xdc\x14\x04high\x12 \n" + - "\x0eVALUE_CRITICAL\x10\x06\x1a\f\xea\xdc\x14\bcritical\"\x81$\n" + + "\x0eVALUE_CRITICAL\x10\x06\x1a\f\xea\xdc\x14\bcritical\"\xbe$\n" + "\bRuleType\x12&\n" + "\aversion\x18\v \x01(\tB\f\xbaH\tr\a2\x05^v\\d$R\aversion\x12$\n" + "\x04type\x18\f \x01(\tB\x10\xbaH\rr\v2\trule-typeR\x04type\x12\x1d\n" + @@ -15347,7 +15355,7 @@ const file_minder_v1_minder_proto_rawDesc = "" + "\vdescription\x18\x05 \x01(\tB\r\xe0A\x02\xbaH\ar\x05\x10\x01\x18\xdc\vR\vdescription\x12)\n" + "\bguidance\x18\x06 \x01(\tB\r\xe0A\x02\xbaH\ar\x05\x10\x01\x18\xe8\aR\bguidance\x12/\n" + "\bseverity\x18\a \x01(\v2\x13.minder.v1.SeverityR\bseverity\x12D\n" + - "\rrelease_phase\x18\t \x01(\x0e2\x1f.minder.v1.RuleTypeReleasePhaseR\freleasePhase\x1a\xff\x1e\n" + + "\rrelease_phase\x18\t \x01(\x0e2\x1f.minder.v1.RuleTypeReleasePhaseR\freleasePhase\x1a\xbc\x1f\n" + "\n" + "Definition\x12;\n" + "\tin_entity\x18\x01 \x01(\tB\x1e\xbaH\x1br\x19\x10\x01\x18\xc8\x012\x12^[a-z]+(_[a-z]+)*$R\binEntity\x128\n" + @@ -15433,15 +15441,16 @@ const file_minder_v1_minder_proto_rawDesc = "" + "\x1e_actions_replace_tags_with_shaB\a\n" + "\x05_restB\x17\n" + "\x15_gh_branch_protectionB\x0f\n" + - "\r_pull_request\x1a\xfd\x03\n" + + "\r_pull_request\x1a\xba\x04\n" + "\x05Alert\x12E\n" + "\x04type\x18\x01 \x01(\tB1\xbaH.\xd8\x01\x01r)R\x11security_advisoryR\x14pull_request_commentR\x04type\x12b\n" + "\x11security_advisory\x18\x02 \x01(\v20.minder.v1.RuleType.Definition.Alert.AlertTypeSAH\x00R\x10securityAdvisory\x88\x01\x01\x12n\n" + "\x14pull_request_comment\x18\x03 \x01(\v27.minder.v1.RuleType.Definition.Alert.AlertTypePRCommentH\x01R\x12pullRequestComment\x88\x01\x01\x1a_\n" + "\vAlertTypeSA\x12P\n" + - "\bseverity\x18\x01 \x01(\tB4\xbaH1\xd8\x01\x01r,R\aunknownR\x04infoR\x03lowR\x06mediumR\x04highR\bcriticalR\bseverity\x1aI\n" + + "\bseverity\x18\x01 \x01(\tB4\xbaH1\xd8\x01\x01r,R\aunknownR\x04infoR\x03lowR\x06mediumR\x04highR\bcriticalR\bseverity\x1a\x85\x01\n" + "\x12AlertTypePRComment\x123\n" + - "\x0ereview_message\x18\x01 \x01(\tB\f\xe0A\x02\xbaH\x06r\x04\x18\x80\x80\x04R\rreviewMessageB\x14\n" + + "\x0ereview_message\x18\x01 \x01(\tB\f\xe0A\x02\xbaH\x06r\x04\x18\x80\x80\x04R\rreviewMessage\x12:\n" + + "\x06action\x18\x02 \x01(\tB\"\xbaH\x1f\xd8\x01\x01r\x1aR\acommentR\x0frequest_changesR\x06actionB\x14\n" + "\x12_security_advisoryB\x17\n" + "\x15_pull_request_commentB\x0f\n" + "\r_param_schemaB\x05\n" + diff --git a/proto/minder/v1/minder.proto b/proto/minder/v1/minder.proto index 8760ccd017..75c5d840e7 100644 --- a/proto/minder/v1/minder.proto +++ b/proto/minder/v1/minder.proto @@ -2933,6 +2933,12 @@ message RuleType { }, (google.api.field_behavior) = REQUIRED ]; + string action = 2 [ + (buf.validate.field).string = { + in: ["comment", "request_changes"], + }, + (buf.validate.field).ignore = IGNORE_IF_ZERO_VALUE + ]; } optional AlertTypePRComment pull_request_comment = 3; }