Skip to content

Commit

Permalink
fix: list exception group
Browse files Browse the repository at this point in the history
Signed-off-by: Matthias Theuermann <[email protected]>
  • Loading branch information
mati007thm committed Dec 5, 2024
1 parent e91f28e commit 8379f89
Show file tree
Hide file tree
Showing 3 changed files with 152 additions and 101 deletions.
4 changes: 2 additions & 2 deletions examples/resources/mondoo_exception/resource.tf
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
variable "spaceId" {
type = string
type = string
}

provider "mondoo" {
Expand All @@ -19,7 +19,7 @@ locals {

resource "mondoo_exception" "exception" {
scope_mrn = "//assets.api.mondoo.app/spaces/${var.spaceId}/assets/${local.assetId}"
valid_until = "2024-12-12T09:33:46.206Z"
valid_until = "2024-12-11" # will be formatted to 2024-12-12T09:33:46.206Z
justification = "testing"
action ="SNOOZE"
// check_mrns = ["//policy.api.mondoo.app/queries/mondoo-tls-security-no-weak-block-cipher-modes"]
Expand Down
139 changes: 88 additions & 51 deletions internal/provider/exception_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,11 @@ func (r *exceptionResource) Create(ctx context.Context, req resource.CreateReque
return
}

scopeMrn := data.ScopeMrn.ValueString()
if scopeMrn == "" {
scopeMrn = r.client.space.MRN()
}

checks := []string{}
data.CheckMrns.ElementsAs(ctx, &checks, false)

Expand All @@ -141,22 +146,65 @@ func (r *exceptionResource) Create(ctx context.Context, req resource.CreateReque
resp.Diagnostics.AddError("Invalid Configuration", err.Error())
return
}
validUntilStr = time.Date(year, month, day, time.Now().Hour(), time.Now().Minute(), time.Now().Second(), time.Now().Nanosecond(), time.Now().Location()).Format(time.RFC3339)
now := time.Now().UTC() // Use UTC directly
validUntilStr = time.Date(
year,
time.Month(month),

Check failure on line 152 in internal/provider/exception_resource.go

View workflow job for this annotation

GitHub Actions / Build

unnecessary conversion (unconvert)
day,
now.Hour(),
now.Minute(),
now.Second(),
now.Nanosecond(),
time.UTC,
).Format(time.RFC3339Nano) // Use RFC3339Nano to include nanoseconds
}

// Disable existing exceptions
checksToDisable := []string{}
for _, mrn := range checks {
exceptionGroups, err := r.client.ListExceptionGroups(ctx, scopeMrn, mrn, []string{}, []mondoov1.ExceptionMutationAction{})
if err != nil {
resp.Diagnostics.AddError("Failed to list existing exceptions", err.Error())
return
}
if len(exceptionGroups) > 0 {
for _, group := range exceptionGroups {
checksToDisable = append(checksToDisable, group.Exceptions[0].AssetCheckException.Mrn)
}
}
}
vulnerabilitiesToDisable := []string{}
for _, mrn := range vulnerabilities {
exceptionGroups, err := r.client.ListExceptionGroups(ctx, scopeMrn, mrn, []string{}, []mondoov1.ExceptionMutationAction{})
if err != nil {
resp.Diagnostics.AddError("Failed to list existing exceptions", err.Error())
return
}
if len(exceptionGroups) > 0 {
for _, group := range exceptionGroups {
vulnerabilitiesToDisable = append(vulnerabilitiesToDisable, group.Exceptions[0].AssetAdvisoryException.Mrn)
}
}
}
if len(checksToDisable) > 0 || len(vulnerabilitiesToDisable) > 0 {

tflog.Debug((ctx), fmt.Sprintf("Disabling existing exceptions for scope %s", data.ScopeMrn.ValueString()))
err := r.client.ApplyException(ctx, scopeMrn, mondoov1.ExceptionMutationAction("ENABLE"), checksToDisable, []string{}, []string{}, vulnerabilitiesToDisable, (*string)(mondoov1.NewStringPtr("")), (*string)(mondoov1.NewStringPtr("")), (*bool)(mondoov1.NewBooleanPtr(false)))
if err != nil {
resp.Diagnostics.AddError("Failed to disable existing exceptions", err.Error())
return
}
}

// Create API call logic
// mondoov1.ExceptionMutationAction(data.Action.ValueString())
tflog.Debug(ctx, fmt.Sprintf("Creating exception for scope %s", data.ScopeMrn.ValueString()))
err := r.client.ApplyException(ctx, data.ScopeMrn.ValueString(), mondoov1.ExceptionMutationAction(data.Action.ValueString()), checks, []string{}, []string{}, vulnerabilities, data.Justification.ValueStringPointer(), &validUntilStr, (*bool)(mondoov1.NewBooleanPtr(false)))
fmt.Println("====================================")
fmt.Println("Error:", err)
fmt.Println("====================================")
err := r.client.ApplyException(ctx, scopeMrn, mondoov1.ExceptionMutationAction(data.Action.ValueString()), checks, []string{}, []string{}, vulnerabilities, data.Justification.ValueStringPointer(), &validUntilStr, (*bool)(mondoov1.NewBooleanPtr(false)))
if err != nil {
resp.Diagnostics.AddError("Failed to create exception", err.Error())
return
}

data.ScopeMrn = types.StringValue(data.ScopeMrn.ValueString())
data.ScopeMrn = types.StringValue(scopeMrn)
data.ValidUntil = types.StringValue(validUntilStr)

// Save data into Terraform state
Expand Down Expand Up @@ -189,25 +237,11 @@ func (r *exceptionResource) Update(ctx context.Context, req resource.UpdateReque
return
}

// Compute and validate the space
scope, err := r.client.ComputeSpace(data.ScopeMrn)
if err != nil {
resp.Diagnostics.AddError("Invalid Configuration", err.Error())
return
}
ctx = tflog.SetField(ctx, "scope_mrn", scope.MRN())

checks := make([]string, 0)
checkMrns := data.CheckMrns.Elements()
for _, check := range checkMrns {
checks = append(checks, check.(types.String).ValueString())
}
checks := []string{}
data.CheckMrns.ElementsAs(ctx, &checks, false)

vulnerabilities := make([]string, 0)
vulnerabilityMrns := data.VulnerabilityMrns.Elements()
for _, vulnerability := range vulnerabilityMrns {
vulnerabilities = append(vulnerabilities, vulnerability.(types.String).ValueString())
}
vulnerabilities := []string{}
data.VulnerabilityMrns.ElementsAs(ctx, &vulnerabilities, false)

// Format ValidUntil to RFC3339 if provided
var validUntilStr string
Expand All @@ -218,18 +252,35 @@ func (r *exceptionResource) Update(ctx context.Context, req resource.UpdateReque
resp.Diagnostics.AddError("Invalid Configuration", err.Error())
return
}
validUntilStr = time.Date(year, month, day, time.Now().Hour(), time.Now().Minute(), time.Now().Second(), time.Now().Nanosecond(), time.Now().Location()).Format(time.RFC3339)
now := time.Now().UTC() // Use UTC directly
validUntilStr = time.Date(
year,
time.Month(month),

Check failure on line 258 in internal/provider/exception_resource.go

View workflow job for this annotation

GitHub Actions / Build

unnecessary conversion (unconvert)
day,
now.Hour(),
now.Minute(),
now.Second(),
now.Nanosecond(),
time.UTC,
).Format(time.RFC3339Nano) // Use RFC3339Nano to include nanoseconds
}

tflog.Debug((ctx), fmt.Sprintf("Disabling existing exceptions for scope %s", data.ScopeMrn.ValueString()))
err := r.client.ApplyException(ctx, data.ScopeMrn.ValueString(), mondoov1.ExceptionMutationAction("ENABLE"), checks, []string{}, []string{}, vulnerabilities, (*string)(mondoov1.NewStringPtr("")), (*string)(mondoov1.NewStringPtr("")), (*bool)(mondoov1.NewBooleanPtr(false)))
if err != nil {
resp.Diagnostics.AddError("Failed to disable existing exceptions", err.Error())
return
}

// Update API call logic
tflog.Debug(ctx, fmt.Sprintf("Updating exception for scope %s", scope.MRN()))
err = r.client.ApplyException(ctx, data.ScopeMrn.ValueString(), mondoov1.ExceptionMutationAction(data.Action.ValueString()), checks, []string{}, []string{}, vulnerabilities, data.Justification.ValueStringPointer(), &validUntilStr, (*bool)(mondoov1.NewBooleanPtr(false)))
// Create API call logic
tflog.Debug(ctx, fmt.Sprintf("Creating exception for scope %s", data.ScopeMrn.ValueString()))
err = r.client.ApplyException(ctx, data.ScopeMrn.String(), mondoov1.ExceptionMutationAction(data.Action.ValueString()), checks, []string{}, []string{}, vulnerabilities, data.Justification.ValueStringPointer(), &validUntilStr, (*bool)(mondoov1.NewBooleanPtr(false)))
if err != nil {
resp.Diagnostics.AddError("Failed to update exception", err.Error())
resp.Diagnostics.AddError("Failed to create exception", err.Error())
return
}

data.ScopeMrn = types.StringValue(scope.MRN())
data.ScopeMrn = types.StringValue(data.ScopeMrn.ValueString())
data.ValidUntil = types.StringValue(validUntilStr)

// Save updated data into Terraform state
Expand All @@ -246,29 +297,15 @@ func (r *exceptionResource) Delete(ctx context.Context, req resource.DeleteReque
return
}

// Compute and validate the space
scope, err := r.client.ComputeSpace(data.ScopeMrn)
if err != nil {
resp.Diagnostics.AddError("Invalid Configuration", err.Error())
return
}
ctx = tflog.SetField(ctx, "scope_mrn", scope.MRN())

checks := make([]string, 0)
checkMrns := data.CheckMrns.Elements()
for _, check := range checkMrns {
checks = append(checks, check.(types.String).ValueString())
}
checks := []string{}
data.CheckMrns.ElementsAs(ctx, &checks, false)

vulnerabilities := make([]string, 0)
vulnerabilityMrns := data.VulnerabilityMrns.Elements()
for _, vulnerability := range vulnerabilityMrns {
vulnerabilities = append(vulnerabilities, vulnerability.(types.String).ValueString())
}
vulnerabilities := []string{}
data.VulnerabilityMrns.ElementsAs(ctx, &vulnerabilities, false)

// Delete API call logic
tflog.Debug(ctx, fmt.Sprintf("Deleting exception for scope %s", scope.MRN()))
err = r.client.ApplyException(ctx, data.ScopeMrn.ValueString(), mondoov1.ExceptionMutationAction("ENABLE"), checks, []string{}, []string{}, vulnerabilities, data.Justification.ValueStringPointer(), (*string)(mondoov1.NewStringPtr("")), (*bool)(mondoov1.NewBooleanPtr(false)))
tflog.Debug(ctx, fmt.Sprintf("Deleting exception for scope %s", data.ScopeMrn.ValueString()))
err := r.client.ApplyException(ctx, data.ScopeMrn.ValueString(), mondoov1.ExceptionMutationAction("ENABLE"), checks, []string{}, []string{}, vulnerabilities, (*string)(mondoov1.NewStringPtr("")), (*string)(mondoov1.NewStringPtr("")), (*bool)(mondoov1.NewBooleanPtr(false)))
if err != nil {
resp.Diagnostics.AddError("Failed to delete exception", err.Error())
return
Expand Down
110 changes: 62 additions & 48 deletions internal/provider/gql.go
Original file line number Diff line number Diff line change
Expand Up @@ -962,74 +962,95 @@ func (c *ExtendedGqlClient) ApplyException(

// Prepare input fields
input := mondoov1.ExceptionMutationInput{
ScopeMrn: mondoov1.String(scopeMrn),
Action: action,
QueryMrns: convertToGraphQLList(checkMrns),
// ControlMrns: convertToGraphQLList(controlMrns),
// CveMrns: convertToGraphQLList(cveMrns),
ScopeMrn: mondoov1.String(scopeMrn),
Action: action,
QueryMrns: convertToGraphQLList(checkMrns),
ControlMrns: convertToGraphQLList(controlMrns),
CveMrns: convertToGraphQLList(cveMrns),
AdvisoryMrns: convertToGraphQLList(vulnerabilityMrns),
Justification: (*mondoov1.String)(justification),
// ValidUntil: (*mondoov1.String)(validUntil),
ApplyToCves: mondoov1.NewBooleanPtr(mondoov1.Boolean(*applyToCves)),
ValidUntil: (*mondoov1.String)(validUntil),
ApplyToCves: mondoov1.NewBooleanPtr(mondoov1.Boolean(*applyToCves)),
}

fmt.Println("====================================")
fmt.Println("Scope:", input.ScopeMrn)
fmt.Println("Action:", input.Action)
fmt.Println("CheckMrns:", *input.QueryMrns)
// fmt.Println("ControlMrns:", *input.ControlMrns)
// fmt.Println("CveMrns:", *input.CveMrns)
// fmt.Println("VulnerabilityMrns:", *input.AdvisoryMrns)
// fmt.Println("Justification:", *input.Justification)
// fmt.Println("ValidUntil:", *input.ValidUntil)
fmt.Println("Justification:", *input.Justification)
fmt.Println("ValidUntil:", *input.ValidUntil)
fmt.Println("ApplyToCves:", *input.ApplyToCves)
fmt.Println("====================================")

return c.Mutate(ctx, &applyException, input, nil)
}

// ListExceptionGroupsInput defines the input for the ListExceptionGroups query
type ListExceptionGroupsInput struct {
ScopeMrn string `json:"scopeMrn"`
Types []string `json:"types"`
}

// Reviewer represents the reviewer of the exception group

Check failure on line 991 in internal/provider/gql.go

View workflow job for this annotation

GitHub Actions / Build

Comment should end in a period (godot)
type Reviewer struct {
Email string `json:"email"`
Name string `json:"name"`
}

type ExceptionGroup struct {
Action string `json:"action"`
CreatedAt string `json:"createdAt"`
ID string `json:"id"`
Justification string `json:"justification"`
ModifiedAt string `json:"modifiedAt"`
ReviewStatus string `json:"reviewStatus"`
ScopeMrn string `json:"scopeMrn"`
Title string `json:"title"`
Author Author `json:"author"`
Reviewer Reviewer `json:"reviewer"`
// AssetAdvisoryException represents exceptions related to asset advisories

Check failure on line 997 in internal/provider/gql.go

View workflow job for this annotation

GitHub Actions / Build

Comment should end in a period (godot)
type AssetAdvisoryException struct {
Mrn string `graphql:"mrn"`
}

// ListExceptionGroupsResponse represents the GraphQL response structure
type ListExceptionGroupsResponse struct {
ExceptionGroups []ExceptionGroup `json:"exceptionGroups"`
// AssetCheckException represents exceptions related to asset checks

Check failure on line 1002 in internal/provider/gql.go

View workflow job for this annotation

GitHub Actions / Build

Comment should end in a period (godot)
type AssetCheckException struct {
Mrn string `graphql:"mrn"`
}

// ExtendedGqlClient represents a client to communicate with the GraphQL API
// AssetCveException represents exceptions related to CVEs

Check failure on line 1007 in internal/provider/gql.go

View workflow job for this annotation

GitHub Actions / Build

Comment should end in a period (godot)
type AssetCveException struct {
Mrn string `graphql:"mrn"`
}

// SpaceCheckException represents exceptions related to space checks

Check failure on line 1012 in internal/provider/gql.go

View workflow job for this annotation

GitHub Actions / Build

Comment should end in a period (godot)
type SpaceCheckException struct {
Mrn string `graphql:"mrn"`
}

// Exception is a union of all exception types

Check failure on line 1017 in internal/provider/gql.go

View workflow job for this annotation

GitHub Actions / Build

Comment should end in a period (godot)
type Exception struct {
AssetAdvisoryException *AssetAdvisoryException `graphql:"... on AssetAdvisoryException"`
AssetCheckException *AssetCheckException `graphql:"... on AssetCheckException"`
AssetCveException *AssetCveException `graphql:"... on AssetCveException"`
SpaceCheckException *SpaceCheckException `graphql:"... on SpaceCheckException"`
}

// ExceptionGroup represents a group of exceptions

Check failure on line 1025 in internal/provider/gql.go

View workflow job for this annotation

GitHub Actions / Build

Comment should end in a period (godot)
type ExceptionGroup struct {
ID string `graphql:"id"`
ScopeMrn string `graphql:"scopeMrn"`
Title string `graphql:"title"`
Justification string `graphql:"justification"`
ReviewStatus string `graphql:"reviewStatus"`
Action string `graphql:"action"`
CreatedAt string `graphql:"createdAt"`
ModifiedAt string `graphql:"modifiedAt"`
Author Author `graphql:"author"`
Reviewer Reviewer `graphql:"reviewer"`
Exceptions []Exception `graphql:"exceptions"`
}

// ListExceptionGroups retrieves a list of exception groups

Check failure on line 1040 in internal/provider/gql.go

View workflow job for this annotation

GitHub Actions / Build

Comment should end in a period (godot)
func (c *ExtendedGqlClient) ListExceptionGroups(
ctx context.Context,
scopeMrn string,
mrn string,
types []string,
actions []mondoov1.ExceptionMutationAction,
) ([]ExceptionGroup, error) {
// Struct to hold the query response
var listExceptionGroups struct {
ExceptionGroups []ExceptionGroup `graphql:"exceptionGroups(input: $input)"`
}

// Helper function to convert string slices to *[]mondoov1.String
// Helper function to convert string slices to *[]mondoov1.ExceptionType
convertToGraphQLList := func(values []string) *[]mondoov1.ExceptionType {
if len(values) == 0 {
return nil
Expand All @@ -1041,10 +1062,20 @@ func (c *ExtendedGqlClient) ListExceptionGroups(
return &entries
}

// Safely construct input
var mrnValue *mondoov1.String
if mrn != "" {
mrnValue = mondoov1.NewStringPtr(mondoov1.String(mrn))
}

fmt.Println("mrnValue", mrn)

// Prepare input for the query
input := mondoov1.ExceptionGroupsInput{
ScopeMrn: mondoov1.String(scopeMrn),
Mrn: mrnValue,
Types: convertToGraphQLList(types),
Actions: &actions,
}
variables := map[string]interface{}{
"input": input,
Expand All @@ -1057,20 +1088,3 @@ func (c *ExtendedGqlClient) ListExceptionGroups(

return listExceptionGroups.ExceptionGroups, nil
}

// ListExceptionGroup
// func (c *ExtendedGqlClient) ListExceptionGroup(ctx context.Context, scopeMrn string) ([]mondoov1.ExceptionGroupsInput, error) {
// var q struct {
// ExceptionGroups []mondoov1.ExceptionGroupsInput `graphql:"exceptionGroups(scopeMrn: $scopeMrn)"`
// }
// variables := map[string]interface{}{
// "scopeMrn": mondoov1.String(scopeMrn),
// }

// err := c.Query(ctx, &q, variables)
// if err != nil {
// return nil, err
// }

// return q.ExceptionGroups, nil
// }

0 comments on commit 8379f89

Please sign in to comment.