Skip to content
Closed
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
12 changes: 11 additions & 1 deletion app/cli/cmd/attestation_add.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ func newAttestationAddCmd() *cobra.Command {
var name, value, kind string
var artifactCASConn *grpc.ClientConn
var annotationsFlag []string
var bypassPolicyCheck bool

// OCI registry credentials can be passed as flags or environment variables
var registryServer, registryUsername, registryPassword string
Expand Down Expand Up @@ -129,7 +130,15 @@ func newAttestationAddCmd() *cobra.Command {
for _, evaluations := range policies {
for _, eval := range evaluations {
if len(eval.Violations) > 0 && eval.Gate {
return NewGateError(eval.Name)
if !bypassPolicyCheck {
// Auto-push incomplete attestation
logger.Info().Msgf("gated policy %q failed during material add, pushing attestation", eval.Name)

if err := a.PushIncompleteAttestation(cmd.Context(), attestationID); err != nil {
logger.Warn().Msgf("failed to push attestation: %v", err)
}
return NewGateError(eval.Name)
}
}
}
}
Expand All @@ -155,6 +164,7 @@ func newAttestationAddCmd() *cobra.Command {
cmd.Flags().StringSliceVar(&annotationsFlag, "annotation", nil, "additional annotation in the format of key=value")
flagAttestationID(cmd)
cmd.Flags().StringVar(&kind, "kind", "", fmt.Sprintf("kind of the material to be recorded: %q", schemaapi.ListAvailableMaterialKind()))
cmd.Flags().BoolVar(&bypassPolicyCheck, exceptionFlagName, false, "do not push attestation when a gated policy fails")

// Optional OCI registry credentials
cmd.Flags().StringVar(&registryServer, "registry-server", "", fmt.Sprintf("OCI repository server, ($%s)", registryServerEnvVarName))
Expand Down
19 changes: 10 additions & 9 deletions app/cli/documentation/cli-reference.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -197,15 +197,16 @@ chainloop attestation add --value https://example.com/sbom.json
Options

```
--annotation strings additional annotation in the format of key=value
--attestation-id string Unique identifier of the in-progress attestation
-h, --help help for add
--kind string kind of the material to be recorded: ["ARTIFACT" "ATTESTATION" "BLACKDUCK_SCA_JSON" "CHAINLOOP_PR_INFO" "CHAINLOOP_RUNNER_CONTEXT" "CONTAINER_IMAGE" "CSAF_INFORMATIONAL_ADVISORY" "CSAF_SECURITY_ADVISORY" "CSAF_SECURITY_INCIDENT_RESPONSE" "CSAF_VEX" "EVIDENCE" "GHAS_CODE_SCAN" "GHAS_DEPENDENCY_SCAN" "GHAS_SECRET_SCAN" "GITLAB_SECURITY_REPORT" "HELM_CHART" "JACOCO_XML" "JUNIT_XML" "OPENVEX" "SARIF" "SBOM_CYCLONEDX_JSON" "SBOM_SPDX_JSON" "SLSA_PROVENANCE" "STRING" "TWISTCLI_SCAN_JSON" "ZAP_DAST_ZIP"]
--name string name of the material as shown in the contract
--registry-password string registry password, ($CHAINLOOP_REGISTRY_PASSWORD)
--registry-server string OCI repository server, ($CHAINLOOP_REGISTRY_SERVER)
--registry-username string registry username, ($CHAINLOOP_REGISTRY_USERNAME)
--value string value to be recorded
--annotation strings additional annotation in the format of key=value
--attestation-id string Unique identifier of the in-progress attestation
--exception-bypass-policy-check do not push attestation when a gated policy fails
-h, --help help for add
--kind string kind of the material to be recorded: ["ARTIFACT" "ATTESTATION" "BLACKDUCK_SCA_JSON" "CHAINLOOP_PR_INFO" "CHAINLOOP_RUNNER_CONTEXT" "CONTAINER_IMAGE" "CSAF_INFORMATIONAL_ADVISORY" "CSAF_SECURITY_ADVISORY" "CSAF_SECURITY_INCIDENT_RESPONSE" "CSAF_VEX" "EVIDENCE" "GHAS_CODE_SCAN" "GHAS_DEPENDENCY_SCAN" "GHAS_SECRET_SCAN" "GITLAB_SECURITY_REPORT" "HELM_CHART" "JACOCO_XML" "JUNIT_XML" "OPENVEX" "SARIF" "SBOM_CYCLONEDX_JSON" "SBOM_SPDX_JSON" "SLSA_PROVENANCE" "STRING" "TWISTCLI_SCAN_JSON" "ZAP_DAST_ZIP"]
--name string name of the material as shown in the contract
--registry-password string registry password, ($CHAINLOOP_REGISTRY_PASSWORD)
--registry-server string OCI repository server, ($CHAINLOOP_REGISTRY_SERVER)
--registry-username string registry username, ($CHAINLOOP_REGISTRY_USERNAME)
--value string value to be recorded
```

Options inherited from parent commands
Expand Down
34 changes: 34 additions & 0 deletions app/cli/pkg/action/attestation_add.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,3 +177,37 @@ func (action *AttestationAdd) GetPolicyEvaluations(ctx context.Context, attestat

return policyEvaluations, nil
}

// PushIncompleteAttestation pushes an attestation to the control plane
// Note: All required materials must be present for the push to succeed and only keyless signing is supported

func (action *AttestationAdd) PushIncompleteAttestation(ctx context.Context, attestationID string) error {
// Check if keyless signing is available
crafter, err := newCrafter(&newCrafterStateOpts{enableRemoteState: (attestationID != ""), localStatePath: action.localStatePath}, action.CPConnection, action.opts...)
if err != nil {
return fmt.Errorf("loading crafter: %w", err)
}

if err := crafter.LoadCraftingState(ctx, attestationID); err != nil {
return fmt.Errorf("loading existing attestation: %w", err)
}

if crafter.CraftingState.GetAttestation().GetSigningOptions().GetSigningCa() == "" {
return fmt.Errorf("keyless signing not configured")
}

pushAction, err := NewAttestationPush(&AttestationPushOpts{
ActionsOpts: action.ActionsOpts,
LocalStatePath: action.localStatePath,
})
if err != nil {
return fmt.Errorf("creating push action: %w", err)
}

_, err = pushAction.Run(ctx, attestationID, nil, false)
if err != nil {
return fmt.Errorf("pushing attestation: %w", err)
}

return nil
}
Loading