Skip to content

Commit dffde7c

Browse files
committed
feat: add policy develop eval
Signed-off-by: Sylwester Piskozub <sylwesterpiskozub@gmail.com>
1 parent df71cee commit dffde7c

7 files changed

Lines changed: 592 additions & 1 deletion

File tree

app/cli/cmd/policy.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
//
2+
// Copyright 2025 The Chainloop Authors.
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
//
8+
// http://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS,
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
16+
package cmd
17+
18+
import (
19+
"github.com/spf13/cobra"
20+
)
21+
22+
func newPolicyCmd() *cobra.Command {
23+
cmd := &cobra.Command{
24+
Use: "policy",
25+
Short: "Craft chainloop policies",
26+
}
27+
28+
cmd.AddCommand(newPolicyDevelopCmd())
29+
return cmd
30+
}

app/cli/cmd/policy_develop.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
//
2+
// Copyright 2025 The Chainloop Authors.
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
//
8+
// http://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS,
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
16+
package cmd
17+
18+
import (
19+
"github.com/spf13/cobra"
20+
)
21+
22+
func newPolicyDevelopCmd() *cobra.Command {
23+
cmd := &cobra.Command{
24+
Use: "develop",
25+
Aliases: []string{"devel"},
26+
Short: `Tools for policy development
27+
Refer to https://docs.chainloop.dev/guides/custom-policies
28+
`,
29+
}
30+
31+
cmd.AddCommand(newPolicyDevelopEvalCmd())
32+
return cmd
33+
}

app/cli/cmd/policy_develop_eval.go

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
//
2+
// Copyright 2025 The Chainloop Authors.
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
//
8+
// http://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS,
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
16+
package cmd
17+
18+
import (
19+
"fmt"
20+
"strings"
21+
22+
"github.com/chainloop-dev/chainloop/app/cli/internal/action"
23+
schemaapi "github.com/chainloop-dev/chainloop/app/controlplane/api/workflowcontract/v1"
24+
"github.com/spf13/cobra"
25+
)
26+
27+
func newPolicyDevelopEvalCmd() *cobra.Command {
28+
var (
29+
materialFile string
30+
kind string
31+
annotations []string
32+
policyPath string
33+
)
34+
35+
cmd := &cobra.Command{
36+
Use: "eval",
37+
Short: "Evaluate policy against provided material",
38+
Long: `Perform a full evaluation of the policy against the provided material type.
39+
The command checks if there is a path in the policy for the specified kind and
40+
evaluates the policy against the provided material or attestation.`,
41+
Example: `
42+
# Evaluate policy against a material file
43+
chainloop policy eval --material sbom.json --kind SBOM_CYCLONEDX_JSON --annotations key1=value1,key2=value2`,
44+
RunE: func(_ *cobra.Command, _ []string) error {
45+
opts := &action.PolicyEvalOpts{
46+
MaterialFile: materialFile,
47+
Kind: kind,
48+
Annotations: parseAnnotations(annotations),
49+
PolicyPath: policyPath,
50+
}
51+
52+
policyEval, err := action.NewPolicyEval(opts, actionOpts)
53+
if err != nil {
54+
return fmt.Errorf("failed to initialize policy evaluation: %w", err)
55+
}
56+
57+
result, err := policyEval.Run()
58+
if err != nil {
59+
return newGracefulError(err)
60+
}
61+
62+
if result.Passed {
63+
logger.Info().Msg("Policy evaluation passed")
64+
} else {
65+
for _, violation := range result.Violations {
66+
logger.Error().Msgf("- %s", violation)
67+
}
68+
logger.Error().Msg("Policy evaluation failed")
69+
}
70+
71+
return nil
72+
},
73+
}
74+
75+
cmd.Flags().StringVar(&materialFile, "material", "", "path to material or attestation file")
76+
cobra.CheckErr(cmd.MarkFlagRequired("material"))
77+
cmd.Flags().StringVar(&kind, "kind", "", fmt.Sprintf("kind of the material: %q", schemaapi.ListAvailableMaterialKind()))
78+
cobra.CheckErr(cmd.MarkFlagRequired("kind"))
79+
cmd.Flags().StringSliceVar(&annotations, "annotations", []string{}, "key-value pairs of annotations (key=value)")
80+
cmd.Flags().StringVar(&policyPath, "policy", "", "path to custom policy file (defaults to policy.yaml)")
81+
cobra.CheckErr(cmd.MarkFlagRequired("policy"))
82+
83+
return cmd
84+
}
85+
86+
func parseAnnotations(raw []string) map[string]string {
87+
annotations := make(map[string]string)
88+
for _, a := range raw {
89+
if key, val, ok := strings.Cut(a, "="); ok {
90+
annotations[key] = val
91+
}
92+
}
93+
return annotations
94+
}

app/cli/cmd/root.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,7 @@ func NewRootCmd(l zerolog.Logger) *cobra.Command {
242242
rootCmd.AddCommand(newWorkflowCmd(), newAuthCmd(), NewVersionCmd(),
243243
newAttestationCmd(), newArtifactCmd(), newConfigCmd(),
244244
newIntegrationCmd(), newOrganizationCmd(), newCASBackendCmd(),
245-
newReferrerDiscoverCmd(),
245+
newReferrerDiscoverCmd(), newPolicyCmd(),
246246
)
247247

248248
// Load plugins if we are not running a subcommand

app/cli/documentation/cli-reference.mdx

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2703,6 +2703,177 @@ Options inherited from parent commands
27032703
-y, --yes Skip confirmation
27042704
```
27052705

2706+
## chainloop policy
2707+
2708+
Craft chainloop policies
2709+
2710+
Options
2711+
2712+
```
2713+
-h, --help help for policy
2714+
```
2715+
2716+
Options inherited from parent commands
2717+
2718+
```
2719+
--artifact-cas string URL for the Artifacts Content Addressable Storage API ($CHAINLOOP_ARTIFACT_CAS_API) (default "api.cas.chainloop.dev:443")
2720+
--artifact-cas-ca string CUSTOM CA file for the Artifacts CAS API (optional) ($CHAINLOOP_ARTIFACT_CAS_API_CA)
2721+
-c, --config string Path to an existing config file (default is $HOME/.config/chainloop/config.toml)
2722+
--control-plane string URL for the Control Plane API ($CHAINLOOP_CONTROL_PLANE_API) (default "api.cp.chainloop.dev:443")
2723+
--control-plane-ca string CUSTOM CA file for the Control Plane API (optional) ($CHAINLOOP_CONTROL_PLANE_API_CA)
2724+
--debug Enable debug/verbose logging mode
2725+
-i, --insecure Skip TLS transport during connection to the control plane ($CHAINLOOP_API_INSECURE)
2726+
-n, --org string organization name
2727+
-o, --output string Output format, valid options are json and table (default "table")
2728+
-t, --token string API token. NOTE: Alternatively use the env variable CHAINLOOP_TOKEN
2729+
-y, --yes Skip confirmation
2730+
```
2731+
2732+
### chainloop policy develop
2733+
2734+
Tools for policy development
2735+
Refer to https://docs.chainloop.dev/guides/custom-policies
2736+
2737+
Options
2738+
2739+
```
2740+
-h, --help help for develop
2741+
```
2742+
2743+
Options inherited from parent commands
2744+
2745+
```
2746+
--artifact-cas string URL for the Artifacts Content Addressable Storage API ($CHAINLOOP_ARTIFACT_CAS_API) (default "api.cas.chainloop.dev:443")
2747+
--artifact-cas-ca string CUSTOM CA file for the Artifacts CAS API (optional) ($CHAINLOOP_ARTIFACT_CAS_API_CA)
2748+
-c, --config string Path to an existing config file (default is $HOME/.config/chainloop/config.toml)
2749+
--control-plane string URL for the Control Plane API ($CHAINLOOP_CONTROL_PLANE_API) (default "api.cp.chainloop.dev:443")
2750+
--control-plane-ca string CUSTOM CA file for the Control Plane API (optional) ($CHAINLOOP_CONTROL_PLANE_API_CA)
2751+
--debug Enable debug/verbose logging mode
2752+
-i, --insecure Skip TLS transport during connection to the control plane ($CHAINLOOP_API_INSECURE)
2753+
-n, --org string organization name
2754+
-o, --output string Output format, valid options are json and table (default "table")
2755+
-t, --token string API token. NOTE: Alternatively use the env variable CHAINLOOP_TOKEN
2756+
-y, --yes Skip confirmation
2757+
```
2758+
2759+
#### chainloop policy develop eval
2760+
2761+
Evaluate policy against provided material
2762+
2763+
Synopsis
2764+
2765+
Perform a full evaluation of the policy against the provided material type.
2766+
The command checks if there is a path in the policy for the specified kind and
2767+
evaluates the policy against the provided material or attestation.
2768+
2769+
```
2770+
chainloop policy develop eval [flags]
2771+
```
2772+
2773+
Examples
2774+
2775+
```
2776+
2777+
Evaluate policy against a material file
2778+
chainloop policy eval --material sbom.json --kind SBOM_CYCLONEDX_JSON --annotations key1=value1,key2=value2
2779+
```
2780+
2781+
Options
2782+
2783+
```
2784+
--annotations strings key-value pairs of annotations (key=value)
2785+
-h, --help help for eval
2786+
--kind string kind of the material: ["ARTIFACT" "ATTESTATION" "BLACKDUCK_SCA_JSON" "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"]
2787+
--material string path to material or attestation file
2788+
--policy string path to custom policy file (defaults to policy.yaml)
2789+
```
2790+
2791+
Options inherited from parent commands
2792+
2793+
```
2794+
--artifact-cas string URL for the Artifacts Content Addressable Storage API ($CHAINLOOP_ARTIFACT_CAS_API) (default "api.cas.chainloop.dev:443")
2795+
--artifact-cas-ca string CUSTOM CA file for the Artifacts CAS API (optional) ($CHAINLOOP_ARTIFACT_CAS_API_CA)
2796+
-c, --config string Path to an existing config file (default is $HOME/.config/chainloop/config.toml)
2797+
--control-plane string URL for the Control Plane API ($CHAINLOOP_CONTROL_PLANE_API) (default "api.cp.chainloop.dev:443")
2798+
--control-plane-ca string CUSTOM CA file for the Control Plane API (optional) ($CHAINLOOP_CONTROL_PLANE_API_CA)
2799+
--debug Enable debug/verbose logging mode
2800+
-i, --insecure Skip TLS transport during connection to the control plane ($CHAINLOOP_API_INSECURE)
2801+
-n, --org string organization name
2802+
-o, --output string Output format, valid options are json and table (default "table")
2803+
-t, --token string API token. NOTE: Alternatively use the env variable CHAINLOOP_TOKEN
2804+
-y, --yes Skip confirmation
2805+
```
2806+
2807+
#### chainloop policy develop help
2808+
2809+
Help about any command
2810+
2811+
Synopsis
2812+
2813+
Help provides help for any command in the application.
2814+
Simply type develop help [path to command] for full details.
2815+
2816+
```
2817+
chainloop policy develop help [command] [flags]
2818+
```
2819+
2820+
Options
2821+
2822+
```
2823+
-h, --help help for help
2824+
```
2825+
2826+
Options inherited from parent commands
2827+
2828+
```
2829+
--artifact-cas string URL for the Artifacts Content Addressable Storage API ($CHAINLOOP_ARTIFACT_CAS_API) (default "api.cas.chainloop.dev:443")
2830+
--artifact-cas-ca string CUSTOM CA file for the Artifacts CAS API (optional) ($CHAINLOOP_ARTIFACT_CAS_API_CA)
2831+
-c, --config string Path to an existing config file (default is $HOME/.config/chainloop/config.toml)
2832+
--control-plane string URL for the Control Plane API ($CHAINLOOP_CONTROL_PLANE_API) (default "api.cp.chainloop.dev:443")
2833+
--control-plane-ca string CUSTOM CA file for the Control Plane API (optional) ($CHAINLOOP_CONTROL_PLANE_API_CA)
2834+
--debug Enable debug/verbose logging mode
2835+
-i, --insecure Skip TLS transport during connection to the control plane ($CHAINLOOP_API_INSECURE)
2836+
-n, --org string organization name
2837+
-o, --output string Output format, valid options are json and table (default "table")
2838+
-t, --token string API token. NOTE: Alternatively use the env variable CHAINLOOP_TOKEN
2839+
-y, --yes Skip confirmation
2840+
```
2841+
2842+
### chainloop policy help
2843+
2844+
Help about any command
2845+
2846+
Synopsis
2847+
2848+
Help provides help for any command in the application.
2849+
Simply type policy help [path to command] for full details.
2850+
2851+
```
2852+
chainloop policy help [command] [flags]
2853+
```
2854+
2855+
Options
2856+
2857+
```
2858+
-h, --help help for help
2859+
```
2860+
2861+
Options inherited from parent commands
2862+
2863+
```
2864+
--artifact-cas string URL for the Artifacts Content Addressable Storage API ($CHAINLOOP_ARTIFACT_CAS_API) (default "api.cas.chainloop.dev:443")
2865+
--artifact-cas-ca string CUSTOM CA file for the Artifacts CAS API (optional) ($CHAINLOOP_ARTIFACT_CAS_API_CA)
2866+
-c, --config string Path to an existing config file (default is $HOME/.config/chainloop/config.toml)
2867+
--control-plane string URL for the Control Plane API ($CHAINLOOP_CONTROL_PLANE_API) (default "api.cp.chainloop.dev:443")
2868+
--control-plane-ca string CUSTOM CA file for the Control Plane API (optional) ($CHAINLOOP_CONTROL_PLANE_API_CA)
2869+
--debug Enable debug/verbose logging mode
2870+
-i, --insecure Skip TLS transport during connection to the control plane ($CHAINLOOP_API_INSECURE)
2871+
-n, --org string organization name
2872+
-o, --output string Output format, valid options are json and table (default "table")
2873+
-t, --token string API token. NOTE: Alternatively use the env variable CHAINLOOP_TOKEN
2874+
-y, --yes Skip confirmation
2875+
```
2876+
27062877
## chainloop version
27072878

27082879
Command line version

0 commit comments

Comments
 (0)