Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 26 additions & 2 deletions app/cli/cmd/attestation_push.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,20 @@ func newAttestationPushCmd() *cobra.Command {
return fmt.Errorf("failed to render output: %w", err)
}

// We do a final check to see if the attestation has policy violations
// and fail the command if needed
// Block if any of the policies has been configured as a gate
for _, evaluations := range res.Status.PolicyEvaluations {
for _, eval := range evaluations {
if len(eval.Violations) > 0 && eval.Gate {
if bypassPolicyCheck {
logger.Warn().Msg(exceptionBypassPolicyCheck)
continue
}
return NewGateError(eval.Name)
}
}
}

// Do a final check in case the operator has configured the attestation to be blocked on any policy violation
if res.Status.MustBlockOnPolicyViolations {
if bypassPolicyCheck {
logger.Warn().Msg(exceptionBypassPolicyCheck)
Expand Down Expand Up @@ -158,3 +170,15 @@ var (
ErrBlockedByPolicyViolation = fmt.Errorf("the operator requires all policies to pass before continuing, please fix them and try again or temporarily bypass the policy check using --%s", exceptionFlagName)
exceptionBypassPolicyCheck = fmt.Sprintf("Attention: You have opted to bypass the policy enforcement check and an operator has been notified of this exception.\nPlease make sure you are back on track with the policy evaluations and remove the --%s as soon as possible.", exceptionFlagName)
)

type GateError struct {
PolicyName string
}

func NewGateError(policyName string) error {
return &GateError{PolicyName: policyName}
}

func (e *GateError) Error() string {
return fmt.Sprintf("the policy %q is configured as a gate and has violations", e.PolicyName)
}
6 changes: 5 additions & 1 deletion app/cli/cmd/workflow_workflow_run_describe.go
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,11 @@ func policiesTable(evs []*action.PolicyEvaluation, mt table.Writer) {
msg = strings.Join(violations, prefix)
}

mt.AppendRow(table.Row{"", fmt.Sprintf("%s: %s", ev.Name, msg)})
name := ev.Name
if ev.Gate {
name = fmt.Sprintf("%s (gate)", ev.Name)
}
mt.AppendRow(table.Row{"", fmt.Sprintf("%s: %s", name, msg)})
}
}

Expand Down
5 changes: 5 additions & 0 deletions app/cli/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ func errorInfo(err error, logger zerolog.Logger) (string, int) {
}
}

var gateErr *cmd.GateError

// Make overrides
switch {
case v1.IsCasBackendErrorReasonRequired(err):
Expand All @@ -101,6 +103,9 @@ func errorInfo(err error, logger zerolog.Logger) (string, int) {
case errors.Is(err, cmd.ErrBlockedByPolicyViolation):
// default exit code for policy violations
exitCode = 3
case errors.As(err, &gateErr):
// exit code for gate errors
exitCode = 4
}

return msg, exitCode
Expand Down
6 changes: 1 addition & 5 deletions app/cli/pkg/action/attestation_add.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,11 +173,7 @@ func (action *AttestationAdd) GetPolicyEvaluations(ctx context.Context, attestat
return nil, err
}

policyEvaluations, _, err := getPolicyEvaluations(crafter)

if err != nil {
return nil, fmt.Errorf("getting policy evaluations: %w", err)
}
policyEvaluations, _ := getPolicyEvaluations(crafter)

return policyEvaluations, nil
}
10 changes: 4 additions & 6 deletions app/cli/pkg/action/attestation_status.go
Original file line number Diff line number Diff line change
Expand Up @@ -158,10 +158,7 @@ func (action *AttestationStatus) Run(ctx context.Context, attestationID string,
return nil, fmt.Errorf("evaluating attestation policies: %w", err)
}

res.PolicyEvaluations, res.HasPolicyViolations, err = getPolicyEvaluations(c)
if err != nil {
return nil, fmt.Errorf("getting policy evaluations: %w", err)
}
res.PolicyEvaluations, res.HasPolicyViolations = getPolicyEvaluations(c)
}

if v := workflowMeta.GetVersion(); v != nil {
Expand Down Expand Up @@ -211,7 +208,7 @@ func (action *AttestationStatus) Run(ctx context.Context, attestationID string,
}

// getPolicyEvaluations retrieves both material-level and attestation-level policy evaluations and returns if it has violations
func getPolicyEvaluations(c *crafter.Crafter) (map[string][]*PolicyEvaluation, bool, error) {
func getPolicyEvaluations(c *crafter.Crafter) (map[string][]*PolicyEvaluation, bool) {
// grouped by material name
evaluations := make(map[string][]*PolicyEvaluation)
var hasViolations bool
Expand All @@ -234,7 +231,7 @@ func getPolicyEvaluations(c *crafter.Crafter) (map[string][]*PolicyEvaluation, b
}
}

return evaluations, hasViolations, nil
return evaluations, hasViolations
}

// populateMaterials populates the materials in the attestation result regardless of where they are defined
Expand Down Expand Up @@ -415,5 +412,6 @@ func policyEvaluationStateToActionForStatus(in *v1.PolicyEvaluation) *PolicyEval
Violations: violations,
Skipped: in.Skipped,
SkipReasons: in.SkipReasons,
Gate: in.Gate,
}
}
1 change: 1 addition & 0 deletions app/cli/pkg/action/workflow_run_describe.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ type PolicyEvaluation struct {
Type string `json:"type"`
Skipped bool `json:"skipped"`
SkipReasons []string `json:"skip_reasons,omitempty"`
Gate bool `json:"gate,omitempty"`
}

type PolicyViolation struct {
Expand Down
16 changes: 16 additions & 0 deletions app/controlplane/api/gen/frontend/attestation/v1/crafting_state.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading