Skip to content

Commit

Permalink
Add alert info to RuleEvaluationStatuses (#2675)
Browse files Browse the repository at this point in the history
* Add alert info to RuleEvaluationStatuses

Signed-off-by: Radoslav Dimitrov <[email protected]>

* Always return guidance info, fix linting and logging

Signed-off-by: Radoslav Dimitrov <[email protected]>

* Use a separate function for getting the GHSA ID from the alert metadata

Signed-off-by: Radoslav Dimitrov <[email protected]>

---------

Signed-off-by: Radoslav Dimitrov <[email protected]>
  • Loading branch information
rdimitrov authored Mar 15, 2024
1 parent bc09ce7 commit fafc696
Showing 1 changed file with 104 additions and 39 deletions.
143 changes: 104 additions & 39 deletions internal/controlplane/handlers_profile.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,14 @@ package controlplane
import (
"context"
"database/sql"
"encoding/json"
"errors"
"fmt"
"sort"
"strings"

"github.com/google/uuid"
"github.com/rs/zerolog/log"
"github.com/rs/zerolog"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/types/known/timestamppb"
Expand Down Expand Up @@ -227,6 +228,7 @@ func getRuleEvalEntityInfo(
rs db.ListRuleEvaluationsByProfileIdRow,
providerName string,
) map[string]string {
l := zerolog.Ctx(ctx)
entityInfo := map[string]string{
"provider": providerName,
}
Expand All @@ -245,7 +247,7 @@ func getRuleEvalEntityInfo(
if entityType.Entities == db.EntitiesArtifact {
artifact, err := store.GetArtifactByID(ctx, selector.UUID)
if err != nil {
log.Printf("error getting artifact: %v", err)
l.Err(err).Msg("error getting artifact by ID")
return entityInfo
}
entityInfo["artifact_id"] = artifact.ID.String()
Expand Down Expand Up @@ -367,52 +369,115 @@ func (s *Server) getRuleEvaluationStatuses(
ruleEvaluationStatuses := make(
[]*minderv1.RuleEvaluationStatus, 0, len(dbRuleEvaluationStatuses),
)
l := zerolog.Ctx(ctx)
// Loop through the rule evaluation statuses and convert them to protobuf
for _, dbRuleEvalStat := range dbRuleEvaluationStatuses {
var guidance string

// make sure all fields are valid
if !dbRuleEvalStat.EvalStatus.Valid ||
!dbRuleEvalStat.EvalDetails.Valid ||
!dbRuleEvalStat.RemStatus.Valid ||
!dbRuleEvalStat.RemDetails.Valid ||
!dbRuleEvalStat.EvalLastUpdated.Valid {
log.Print("error rule evaluation value not valid")
// Get the rule evaluation status
st, err := getRuleEvalStatus(ctx, s.store, profileId, dbEntity, selector, providerName, dbRuleEvalStat)
if err != nil {
l.Err(err).Msg("error getting rule evaluation status")
continue
}
// Append the rule evaluation status to the list
ruleEvaluationStatuses = append(ruleEvaluationStatuses, st)
}
return ruleEvaluationStatuses
}

if dbRuleEvalStat.EvalStatus.EvalStatusTypes == db.EvalStatusTypesFailure ||
dbRuleEvalStat.EvalStatus.EvalStatusTypes == db.EvalStatusTypesError {
ruleTypeInfo, err := s.store.GetRuleTypeByID(ctx, dbRuleEvalStat.RuleTypeID)
if err != nil {
log.Printf("error getting rule type info: %v", err)
} else {
guidance = ruleTypeInfo.Guidance
}
// getRuleEvalStatus is a helper function to get rule evaluation status from a db row
func getRuleEvalStatus(
ctx context.Context,
store db.Store,
profileID string,
dbEntity *db.NullEntities,
selector *uuid.NullUUID,
providerName string,
dbRuleEvalStat db.ListRuleEvaluationsByProfileIdRow,
) (*minderv1.RuleEvaluationStatus, error) {
l := zerolog.Ctx(ctx)
var guidance string

// make sure all fields are valid
if !dbRuleEvalStat.EvalStatus.Valid ||
!dbRuleEvalStat.EvalDetails.Valid ||
!dbRuleEvalStat.EvalLastUpdated.Valid ||
!dbRuleEvalStat.RemStatus.Valid ||
!dbRuleEvalStat.RemDetails.Valid ||
!dbRuleEvalStat.AlertStatus.Valid ||
!dbRuleEvalStat.AlertDetails.Valid {
return nil, fmt.Errorf("rule evaluation status not valid")
}

if dbRuleEvalStat.EvalStatus.EvalStatusTypes == db.EvalStatusTypesFailure ||
dbRuleEvalStat.EvalStatus.EvalStatusTypes == db.EvalStatusTypesError {
ruleTypeInfo, err := store.GetRuleTypeByID(ctx, dbRuleEvalStat.RuleTypeID)
if err != nil {
l.Err(err).Msg("error getting rule type info from db")
} else {
guidance = ruleTypeInfo.Guidance
}
}

st := &minderv1.RuleEvaluationStatus{
ProfileId: profileId,
RuleId: dbRuleEvalStat.RuleTypeID.String(),
RuleName: dbRuleEvalStat.RuleTypeName,
RuleTypeName: dbRuleEvalStat.RuleTypeName,
RuleDescriptionName: dbRuleEvalStat.RuleName,
Entity: string(dbRuleEvalStat.Entity),
Status: string(dbRuleEvalStat.EvalStatus.EvalStatusTypes),
Details: dbRuleEvalStat.EvalDetails.String,
EntityInfo: getRuleEvalEntityInfo(ctx, s.store, dbEntity, selector, dbRuleEvalStat, providerName),
Guidance: guidance,
LastUpdated: timestamppb.New(dbRuleEvalStat.EvalLastUpdated.Time),
RemediationStatus: string(dbRuleEvalStat.RemStatus.RemediationStatusTypes),
RemediationDetails: dbRuleEvalStat.RemDetails.String,
}
st := &minderv1.RuleEvaluationStatus{
ProfileId: profileID,
RuleId: dbRuleEvalStat.RuleTypeID.String(),
RuleName: dbRuleEvalStat.RuleTypeName,
RuleTypeName: dbRuleEvalStat.RuleTypeName,
RuleDescriptionName: dbRuleEvalStat.RuleName,
Entity: string(dbRuleEvalStat.Entity),
Status: string(dbRuleEvalStat.EvalStatus.EvalStatusTypes),
Details: dbRuleEvalStat.EvalDetails.String,
EntityInfo: getRuleEvalEntityInfo(ctx, store, dbEntity, selector, dbRuleEvalStat, providerName),
Guidance: guidance,
LastUpdated: timestamppb.New(dbRuleEvalStat.EvalLastUpdated.Time),
RemediationStatus: string(dbRuleEvalStat.RemStatus.RemediationStatusTypes),
RemediationDetails: dbRuleEvalStat.RemDetails.String,
Alert: &minderv1.EvalResultAlert{
Status: string(dbRuleEvalStat.AlertStatus.AlertStatusTypes),
Details: dbRuleEvalStat.AlertDetails.String,
},
}

if dbRuleEvalStat.RemLastUpdated.Valid {
st.RemediationLastUpdated = timestamppb.New(dbRuleEvalStat.RemLastUpdated.Time)
}
if dbRuleEvalStat.RemLastUpdated.Valid {
st.RemediationLastUpdated = timestamppb.New(dbRuleEvalStat.RemLastUpdated.Time)
}

ruleEvaluationStatuses = append(ruleEvaluationStatuses, st)
if dbRuleEvalStat.AlertLastUpdated.Valid {
st.Alert.LastUpdated = timestamppb.New(dbRuleEvalStat.AlertLastUpdated.Time)
}
return ruleEvaluationStatuses

// If the alert metadata is valid, parse it and set the URL
if dbRuleEvalStat.AlertMetadata.Valid {
alertURL, err := getAlertURLFromMetadata(
dbRuleEvalStat.AlertMetadata.RawMessage,
fmt.Sprintf("%s/%s", st.EntityInfo["repo_owner"], st.EntityInfo["repo_name"]),
)
if err != nil {
l.Err(err).Msg("error getting alert URL from metadata")
} else {
st.Alert.Url = alertURL
}
}
return st, nil
}

// getAlertURLFromMetadata is a helper function to get the alert URL from the alert metadata
func getAlertURLFromMetadata(data []byte, repo string) (string, error) {
// Define a struct to match the JSON structure
jsonMeta := struct {
GhsaId string `json:"ghsa_id"`
}{}
err := json.Unmarshal(data, &jsonMeta)
if err != nil {
return "", err
} else if jsonMeta.GhsaId != "" {
return fmt.Sprintf(
"https://github.com/%s/security/advisories/%s",
repo,
jsonMeta.GhsaId,
), nil
}
return "", fmt.Errorf("no alert ID found in alert metadata")
}

// GetProfileStatusByProject is a method to get profile status for a project
Expand Down

0 comments on commit fafc696

Please sign in to comment.