chainloop icon indicating copy to clipboard operation
chainloop copied to clipboard

CLI command to evaluate policies

Open jiparis opened this issue 1 year ago • 2 comments

it would be useful to have something like chainloop policy eval that receives a material and a policy (file, URL) and returns a report with potential violations. I would be used in manual control gates

jiparis avatar Jul 18 '24 13:07 jiparis

I'd love if this command has a 'development mode` (flag) that allows us to see the input, data,linting errors, and evaluation output, similar to what the rego playground does.

migmartri avatar Aug 16 '24 16:08 migmartri

I'd love if this command has a 'development mode` (flag) that allows us to see the input, data,linting errors, and evaluation output, similar to what the rego playground does.

I think opa eval might be of help for this:

> cat input.json
{"components": [{"name": "thename", "bom-ref": "theref", "licenses": []}]}

> opa eval -d cyclonedx-licenses.rego -i input.json 'data.cyclonedx_licenses.violations'
{
  "result": [
    {
      "expressions": [
        {
          "value": [
            "Missing licenses for thename (theref)"
          ],
          "text": "data.cyclonedx_licenses.violations",
          "location": {
            "row": 1,
            "col": 1
          }
        }
      ]
    }
  ]
}

jiparis avatar Aug 22 '24 21:08 jiparis

@jiparis I just wrote a quick spec, what do you think?

Overview

Today, to write a custom policy, you need to

  • Copy our rego template
  • Create a policy.yaml file
  • Use rego-playground to develop the policy code by providing some test data
  • use rego linter
  • run it during an attestation process to see if it works

This approach has some issues:

  • Our policies are more than just rego scripts, their root is a policy.yaml file that's in charge of defining arguments, encode different material type paths, etc. This is not easily tested using the process above.
  • It's not trivial to know in advance if a writen rego script meets our content standards (result struct, skip_reason, etc) defined in our template.
  • A test run is an expensive process. It requires attaching the policy to a contract, running an attestation, or manually crafting some test data in rego-playground.

Goals

To have a development environment to write, lint, and test custom chainloop policies.

chainloop policy develop [subcommand]

init [directory]

Bootstraps a chainloop policy and example script based on our template. This template should be workable out of the box

lint

By default it load a policy.yaml file in the current directory and perform

  • rego script(s)

    • linting (maybe regal)
    • opa fmt check
    • chainloop result structure check
  • chainloop.yaml

    • marshall content / proto validate

eval --material [material-file] --kind [...] --annotations ...

  • Make sure there is a path in the policy.yaml for that kind
  • Perform a full evaluation of the policy against the provided material type, provide any annotations
  • Should work for the provided materials or attestations

migmartri avatar Jul 01 '25 09:07 migmartri

For policy evaluations, we have something like this internally that can be used as inspiration. It essentially takes a policy file (the yaml spec), a material (the raw content, like an SBOM, report, or even an ATTESTATION), and the policy arguments:

func runTest(policyFile, materialFile, kind string, inputs map[string]string) error {
	schema := &v1.CraftingSchema{
		Policies: &v1.Policies{
			Materials: []*v1.PolicyAttachment{
				{
					Policy: &v1.PolicyAttachment_Ref{Ref: fmt.Sprintf("file://%s", policyFile)},
					With:   inputs,
				},
			},
			Attestation: nil,
		},
		PolicyGroups: nil,
	}

	material := &v2.Attestation_Material{
		M:            &v2.Attestation_Material_Artifact_{Artifact: &v2.Attestation_Material_Artifact{Id: "test-material"}},
		MaterialType: v1.CraftingSchema_Material_MaterialType(v1.CraftingSchema_Material_MaterialType_value[kind]),
	}

	logger := zerolog.New(os.Stderr).Level(zerolog.WarnLevel)
	v := policies.NewPolicyVerifier(schema, nil, &logger)
	evs, err := v.VerifyMaterial(context.Background(), material, materialFile)
	if err != nil {
		return err
	}
	return nil
}

jiparis avatar Jul 01 '25 12:07 jiparis

btw, what I mentioned today about making sure we are running the policies with the right capabilities, I think this is done automatically already if you eval the policies with our engine https://github.com/chainloop-dev/chainloop/blob/17dbde7fbf97298c025e935691dd2de7c23c167c/pkg/policies/engine/rego/rego.go#L233

migmartri avatar Jul 17 '25 12:07 migmartri