Skip to content

Commit 0364180

Browse files
authored
feat(policy-devel): allow external policy references (#2524)
Signed-off-by: Sylwester Piskozub <sylwesterpiskozub@gmail.com>
1 parent c2c8547 commit 0364180

File tree

7 files changed

+45
-27
lines changed

7 files changed

+45
-27
lines changed

app/cli/cmd/policy_develop_eval.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ The command checks if there is a path in the policy for the specified kind and
4444
evaluates the policy against the provided material or attestation.`,
4545
Example: `
4646
# Evaluate policy against a material file
47-
chainloop policy eval --policy policy.yaml --material sbom.json --kind SBOM_CYCLONEDX_JSON --annotation key1=value1,key2=value2 --input key3=value3`,
47+
chainloop policy develop eval --policy policy.yaml --material sbom.json --kind SBOM_CYCLONEDX_JSON --annotation key1=value1,key2=value2 --input key3=value3`,
4848
RunE: func(_ *cobra.Command, _ []string) error {
4949
opts := &action.PolicyEvalOpts{
5050
MaterialPath: materialPath,
@@ -74,7 +74,7 @@ evaluates the policy against the provided material or attestation.`,
7474
cobra.CheckErr(cmd.MarkFlagRequired("material"))
7575
cmd.Flags().StringVar(&kind, "kind", "", fmt.Sprintf("Kind of the material: %q", schemaapi.ListAvailableMaterialKind()))
7676
cmd.Flags().StringSliceVar(&annotations, "annotation", []string{}, "Key-value pairs of material annotations (key=value)")
77-
cmd.Flags().StringVarP(&policyPath, "policy", "p", "policy.yaml", "Path to custom policy file")
77+
cmd.Flags().StringVarP(&policyPath, "policy", "p", "policy.yaml", "Policy reference (./my-policy.yaml, https://my-domain.com/my-policy.yaml, chainloop://my-stored-policy)")
7878
cmd.Flags().StringArrayVar(&inputs, "input", []string{}, "Key-value pairs of policy inputs (key=value)")
7979
cmd.Flags().StringSliceVar(&allowedHostnames, "allowed-hostnames", []string{}, "Additional hostnames allowed for http.send requests in policies")
8080
cmd.Flags().BoolVarP(&debug, "debug", "", false, "Include detailed evaluation inputs/outputs in JSON output and enable verbose logging")

app/cli/documentation/cli-reference.mdx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2859,7 +2859,7 @@ Examples
28592859
```
28602860
28612861
Evaluate policy against a material file
2862-
chainloop policy eval --policy policy.yaml --material sbom.json --kind SBOM_CYCLONEDX_JSON --annotation key1=value1,key2=value2 --input key3=value3
2862+
chainloop policy develop eval --policy policy.yaml --material sbom.json --kind SBOM_CYCLONEDX_JSON --annotation key1=value1,key2=value2 --input key3=value3
28632863
```
28642864

28652865
Options
@@ -2872,7 +2872,7 @@ Options
28722872
--input stringArray Key-value pairs of policy inputs (key=value)
28732873
--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"]
28742874
--material string Path to material or attestation file
2875-
-p, --policy string Path to custom policy file (default "policy.yaml")
2875+
-p, --policy string Policy reference (./my-policy.yaml, https://my-domain.com/my-policy.yaml, chainloop://my-stored-policy) (default "policy.yaml")
28762876
```
28772877

28782878
Options inherited from parent commands

app/cli/internal/policydevel/eval.go

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"fmt"
2121
"os"
2222

23+
controlplanev1 "github.com/chainloop-dev/chainloop/app/controlplane/api/controlplane/v1"
2324
v1 "github.com/chainloop-dev/chainloop/app/controlplane/api/workflowcontract/v1"
2425
"github.com/chainloop-dev/chainloop/pkg/casclient"
2526
"github.com/chainloop-dev/chainloop/pkg/policies"
@@ -34,13 +35,14 @@ const (
3435
)
3536

3637
type EvalOptions struct {
37-
PolicyPath string
38-
MaterialKind string
39-
Annotations map[string]string
40-
MaterialPath string
41-
Inputs map[string]string
42-
AllowedHostnames []string
43-
Debug bool
38+
PolicyPath string
39+
MaterialKind string
40+
Annotations map[string]string
41+
MaterialPath string
42+
Inputs map[string]string
43+
AllowedHostnames []string
44+
Debug bool
45+
AttestationClient controlplanev1.AttestationServiceClient
4446
}
4547

4648
type EvalResult struct {
@@ -74,7 +76,7 @@ func Evaluate(opts *EvalOptions, logger zerolog.Logger) (*EvalSummary, error) {
7476
material.Annotations = opts.Annotations
7577

7678
// 3. Verify material against policy
77-
summary, err := verifyMaterial(policies, material, opts.MaterialPath, opts.Debug, opts.AllowedHostnames, &logger)
79+
summary, err := verifyMaterial(policies, material, opts.MaterialPath, opts.Debug, opts.AllowedHostnames, opts.AttestationClient, &logger)
7880
if err != nil {
7981
return nil, err
8082
}
@@ -83,18 +85,26 @@ func Evaluate(opts *EvalOptions, logger zerolog.Logger) (*EvalSummary, error) {
8385
}
8486

8587
func createPolicies(policyPath string, inputs map[string]string) (*v1.Policies, error) {
88+
// Check if the policy path already has a scheme (chainloop://, http://, https://, file://)
89+
ref := policyPath
90+
scheme, _ := policies.RefParts(policyPath)
91+
if scheme == "" {
92+
// Default to file://
93+
ref = fmt.Sprintf("file://%s", policyPath)
94+
}
95+
8696
return &v1.Policies{
8797
Materials: []*v1.PolicyAttachment{
8898
{
89-
Policy: &v1.PolicyAttachment_Ref{Ref: fmt.Sprintf("file://%s", policyPath)},
99+
Policy: &v1.PolicyAttachment_Ref{Ref: ref},
90100
With: inputs,
91101
},
92102
},
93103
Attestation: nil,
94104
}, nil
95105
}
96106

97-
func verifyMaterial(pol *v1.Policies, material *v12.Attestation_Material, materialPath string, debug bool, allowedHostnames []string, logger *zerolog.Logger) (*EvalSummary, error) {
107+
func verifyMaterial(pol *v1.Policies, material *v12.Attestation_Material, materialPath string, debug bool, allowedHostnames []string, attestationClient controlplanev1.AttestationServiceClient, logger *zerolog.Logger) (*EvalSummary, error) {
98108
var opts []policies.PolicyVerifierOption
99109
if len(allowedHostnames) > 0 {
100110
opts = append(opts, policies.WithAllowedHostnames(allowedHostnames...))
@@ -103,7 +113,7 @@ func verifyMaterial(pol *v1.Policies, material *v12.Attestation_Material, materi
103113
opts = append(opts, policies.WithIncludeRawData(debug))
104114
opts = append(opts, policies.WithEnablePrint(enablePrint))
105115

106-
v := policies.NewPolicyVerifier(pol, nil, logger, opts...)
116+
v := policies.NewPolicyVerifier(pol, attestationClient, logger, opts...)
107117
policyEvs, err := v.VerifyMaterial(context.Background(), material, materialPath)
108118
if err != nil {
109119
return nil, err

app/cli/pkg/action/policy_develop_eval.go

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
package action
1717

1818
import (
19+
pb "github.com/chainloop-dev/chainloop/app/controlplane/api/controlplane/v1"
20+
1921
"github.com/chainloop-dev/chainloop/app/cli/internal/policydevel"
2022
)
2123

@@ -42,14 +44,20 @@ func NewPolicyEval(opts *PolicyEvalOpts, actionOpts *ActionsOpts) (*PolicyEval,
4244
}
4345

4446
func (action *PolicyEval) Run() (*policydevel.EvalSummary, error) {
47+
var attClient pb.AttestationServiceClient
48+
if action.CPConnection != nil {
49+
attClient = pb.NewAttestationServiceClient(action.CPConnection)
50+
}
51+
4552
evalOpts := &policydevel.EvalOptions{
46-
PolicyPath: action.opts.PolicyPath,
47-
MaterialKind: action.opts.Kind,
48-
Annotations: action.opts.Annotations,
49-
MaterialPath: action.opts.MaterialPath,
50-
Inputs: action.opts.Inputs,
51-
AllowedHostnames: action.opts.AllowedHostnames,
52-
Debug: action.opts.Debug,
53+
PolicyPath: action.opts.PolicyPath,
54+
MaterialKind: action.opts.Kind,
55+
Annotations: action.opts.Annotations,
56+
MaterialPath: action.opts.MaterialPath,
57+
Inputs: action.opts.Inputs,
58+
AllowedHostnames: action.opts.AllowedHostnames,
59+
Debug: action.opts.Debug,
60+
AttestationClient: attClient,
5361
}
5462

5563
// Evaluate policy

pkg/policies/loader.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ func unmarshallResource(raw []byte, ref string, digest string, dest proto.Messag
244244

245245
// IsProviderScheme takes a policy reference and returns whether it's referencing to an external provider or not
246246
func IsProviderScheme(ref string) bool {
247-
scheme, _ := refParts(ref)
247+
scheme, _ := RefParts(ref)
248248
return scheme == chainloopScheme || scheme == ""
249249
}
250250

@@ -306,7 +306,7 @@ func ProviderParts(reference string) *ProviderRef {
306306
}
307307

308308
func ensureScheme(ref string, expected ...string) (string, error) {
309-
scheme, id := refParts(ref)
309+
scheme, id := RefParts(ref)
310310
for _, ex := range expected {
311311
if scheme == ex {
312312
return id, nil
@@ -316,7 +316,7 @@ func ensureScheme(ref string, expected ...string) (string, error) {
316316
return "", fmt.Errorf("unexpected policy reference scheme: %q", scheme)
317317
}
318318

319-
func refParts(ref string) (string, string) {
319+
func RefParts(ref string) (string, string) {
320320
parts := strings.SplitN(ref, "://", 2)
321321
if len(parts) == 2 {
322322
return parts[0], parts[1]

pkg/policies/policies.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -406,7 +406,7 @@ func (pv *PolicyVerifier) getLoader(attachment *v1.PolicyAttachment) (Loader, er
406406
}
407407

408408
var loader Loader
409-
scheme, _ := refParts(ref)
409+
scheme, _ := RefParts(ref)
410410
switch scheme {
411411
// No scheme means chainloop loader
412412
case chainloopScheme, "":

pkg/policies/policy_groups.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ func getGroupLoader(attachment *v1.PolicyGroupAttachment, opts *LoadPolicyGroupO
192192
}
193193

194194
var loader GroupLoader
195-
scheme, _ := refParts(ref)
195+
scheme, _ := RefParts(ref)
196196
switch scheme {
197197
// No scheme means chainloop loader
198198
case chainloopScheme, "":

0 commit comments

Comments
 (0)