chainloop-dev / chainloop

Chainloop is an Open Source evidence store for your Software Supply Chain attestations, SBOMs, VEX, SARIF, CSAF files, QA reports, and more.
https://docs.chainloop.dev
Apache License 2.0
365 stars 27 forks source link

make contract server-side validations as good as the client side ones in the CLI #995

Closed migmartri closed 1 month ago

migmartri commented 3 months ago

When creating a contract through the API, if it contains a typo on the actual fields of the json, it throws an error from the buf validation, we should return a better error as in the CLI:

$ chainloop wf contract create --name "testing" -f 'testing/scratch_8.yml'
ERR proto: (line 1:472): unknown field "type:"

Interesting enough, using grpcurl produces the same error:

$ grpcurl -plaintext -d "$(cat 'testing/scratch_8.yml')" -H "Authorization: Bearer $TOKEN" localhost:9000 controlplane.v1.WorkflowContractService/Create
Error invoking method "controlplane.v1.WorkflowContractService/Create": error getting request data: message type workflowcontract.v1.CraftingSchema.Material has no known field named type:
migmartri commented 3 months ago
{
  "schemaVersion": "v1",
  "materials": [
    {
      "type": "CONTAINER_IMAGE",
      "name": "skynet-control-plane",
      "output": true,
      "annotations": [
        {
          "name": "component",
          "value": "control-plane"
        },
        {
          "name": "asset"
        }
      ]
    },
    { "type:": "ARTIFACT", "name": "rootfs" },
  ]
}
javirln commented 3 months ago

Doing a research on the topic I've found the following:


What this means is that, when reaching the server, the validations are run thanks to protovalidate and raise `validation error: - v1.materials[4].type: value must not be in list [0] [enum.not_in]` and this is because the incoming proto although well formed is not valid upon our constraints.

We cannot perform the same operation as the CLI does under the hood since what is received on the server is correct although not valid so going from `proto => json => proto` won't work because the type would still be `type: MATERIAL_TYPE_UNSPECIFIED`, if we later run the validation, then we will fail.

The solution then, would be on the approach of improving the error message. Unfortunately the buf validation library although wide, does not provide much more configuration except the `cel` option:

Something like this will perform in the same way as the enum validation we have in place. From here:
```proto
    MaterialType type = 1 [
      (buf.validate.field).enum = {
        not_in: [0]
      },
      (buf.validate.field).enum.defined_only = true
    ];

To here:

    MaterialType type = 1 [(buf.validate.field).cel = {
      id: "type"
      message: "Type must be one of the allowed values"
      expression: "this != 0 && this in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]"
    }];

As you can see one of the drawback is, that we need to keep track of the list of values.

To sum up, we need to find a way of improving the general return of errors in the API level.

danlishka commented 3 months ago

what are the next steps on this then?

migmartri commented 3 months ago

I'd like to double check the possibility of improving validation error messages server side. It's very discouraging that at first this is not trivial. So next step is to look into how hard is to have custom error messages applied to the proto validations.