Skip to content

Commit

Permalink
address review comments
Browse files Browse the repository at this point in the history
Signed-off-by: Ignasi Barrera <nacx@apache.org>
  • Loading branch information
nacx committed Dec 11, 2024
1 parent eb122c3 commit 51c1ce1
Show file tree
Hide file tree
Showing 7 changed files with 41 additions and 19 deletions.
8 changes: 4 additions & 4 deletions docs/reference/domains/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

Domains, generically, are the areas or categories from which data is collected to be evaluated by a `Lula Validation`. Currently supported Domains are:

* Kubernetes
* API
* File
* [Kubernetes](kubernetes-domain.md)
* [API](api-domain.md)
* [File](file-domain.md)

The domain block of a `Lula Validation` is given as follows, where the sample is indicating a Kubernetes domain is in use:
```yaml
Expand All @@ -16,4 +16,4 @@ domain:
# ... Rest of Lula Validation
```

Each domain has a particular specification, given by the respective `<domain>-spec` field of the `domain` property of the `Lula Validation`. The sub-pages describe each of these specifications in greater detail.
Each domain has a particular specification, given by the respective `<domain>-spec` field of the `domain` property of the `Lula Validation`. The sub-pages describe each of these specifications in greater detail.
6 changes: 3 additions & 3 deletions docs/reference/providers/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

Providers are the policy engines which evaluate the input data from the specified domain. Currently supported Providers are:

* OPA (Open Policy Agent)
* Kyverno
* [OPA (Open Policy Agent)](opa-provider.md)
* [Kyverno](kyverno-provider.md)

The provider block of a `Lula Validation` is given as follows, where the sample is indicating the OPA provider is in use:
```yaml
Expand All @@ -15,4 +15,4 @@ provider:
# ... Rest of Lula Validation
```

Each domain specification retreives a specific dataset, and each will return that data to the selected `Provider` in a domain-specific format. However, this data will always take the form of a JSON object when input to a `Provider`. For that reason, it is important that `Domain` and `Provider`specifications are not built wholly independently in a given Validation.
Each domain specification retreives a specific dataset, and each will return that data to the selected `Provider` in a domain-specific format. However, this data will always take the form of a JSON object when input to a `Provider`. For that reason, it is important that `Domain` and `Provider`specifications are not built wholly independently in a given Validation.
2 changes: 2 additions & 0 deletions docs/reference/providers/opa-provider.md
Original file line number Diff line number Diff line change
Expand Up @@ -156,3 +156,5 @@ provider:
}
}
```
> [!Note]
> The `validate.rego` module name is reserved for the main rego policy and cannot be used as a custom module name.
2 changes: 1 addition & 1 deletion src/pkg/common/schemas/validation.json
Original file line number Diff line number Diff line change
Expand Up @@ -524,7 +524,7 @@
"pattern": ".*\\S\\s\\n.*"
},
"modules": {
"type": "object",
"type": "object"
},
"output": {
"type": "object",
Expand Down
13 changes: 9 additions & 4 deletions src/pkg/providers/opa/opa.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package opa

import (
"context"
"errors"
"fmt"
"reflect"

Expand All @@ -12,26 +13,30 @@ import (
)

var (
ErrCompileRego = fmt.Errorf("failed to compile rego policy")
ErrEvaluateRego = fmt.Errorf("failed to evaluate rego policy")
ErrCompileRego = errors.New("failed to compile rego policy")
ErrEvaluateRego = errors.New("failed to evaluate rego policy")
)

// mainPolicyModuleName is the name of the OPA module containing the main policy from the spec.rego field.
const mainPolicyModuleName = "validate.rego"

// GetValidatedAssets performs the validation of the dataset against the given rego policy
func GetValidatedAssets(ctx context.Context, regoPolicy string, regoModules map[string]string, dataset map[string]interface{}, output *OpaOutput) (types.Result, error) {
var matchResult types.Result

if len(dataset) == 0 {
return matchResult, fmt.Errorf("opa validation not performed - no resources to validate")
return matchResult, errors.New("opa validation not performed - no resources to validate")
}

if output == nil {
output = &OpaOutput{}
}

modules := map[string]string{"validate.rego": regoPolicy}
modules := make(map[string]string, len(regoModules)+1)
for k, v := range regoModules {
modules[k] = v
}
modules[mainPolicyModuleName] = regoPolicy

compiler, err := ast.CompileModules(modules)
if err != nil {
Expand Down
8 changes: 8 additions & 0 deletions src/pkg/providers/opa/opa_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,14 @@ func TestOpaModules(t *testing.T) {
},
wantErr: opa.ErrDownloadModule,
},
{
name: "reserved module validation",
spec: &opa.OpaSpec{
Rego: "package validate\n\nimport data.lula.labels as lula_labels\n\nvalidate { lula_labels.has_lula_label(input.pod) }",
Modules: map[string]string{"lula.labels": "testdata/lula.rego", "validate.rego": "not-used"},
},
wantErr: opa.ErrReservedModuleName,
},
{
name: "module validation",
spec: &opa.OpaSpec{
Expand Down
21 changes: 14 additions & 7 deletions src/pkg/providers/opa/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package opa

import (
"context"
"errors"
"fmt"
"os"
"path/filepath"
Expand All @@ -12,12 +13,13 @@ import (
)

var (
ErrNilSpec = fmt.Errorf("spec is nil")
ErrEmptyRego = fmt.Errorf("rego policy cannot be empty")
ErrInvalidValidationPath = fmt.Errorf("validation field must be a json path")
ErrInvalidObservationPath = fmt.Errorf("observation field must be a json path")
ErrDownloadModule = fmt.Errorf("error downloading module")
ErrReadModule = fmt.Errorf("error reading module")
ErrNilSpec = errors.New("spec is nil")
ErrEmptyRego = errors.New("rego policy cannot be empty")
ErrInvalidValidationPath = errors.New("validation field must be a json path")
ErrInvalidObservationPath = errors.New("observation field must be a json path")
ErrDownloadModule = errors.New("error downloading module")
ErrReadModule = errors.New("error reading module")
ErrReservedModuleName = errors.New("module name is reserved and cannot be used in custom modules")
)

type OpaProvider struct {
Expand Down Expand Up @@ -62,6 +64,10 @@ func loadModules(ctx context.Context, modulePaths map[string]string) (map[string
return nil, nil
}

if _, ok := modulePaths[mainPolicyModuleName]; ok {
return nil, fmt.Errorf("%w: %s", ErrReservedModuleName, mainPolicyModuleName)
}

workDir, ok := ctx.Value(types.LulaValidationWorkDir).(string)
if !ok { // if unset, assume lula is already working in the same directory the inputFile is in
workDir = "."
Expand Down Expand Up @@ -107,7 +113,8 @@ type OpaSpec struct {
// Required: Rego is the OPA policy
Rego string `json:"rego" yaml:"rego"`
// Optional: Modules is a map of additional OPA modules to include. The key is the name of the
// module and the value is the file with the contents of the module.
// module and the value is the file with the contents of the module. The `validate.rego` module
// name is reserved and cannot be used in custom modules.
Modules map[string]string `json:"modules,omitempty" yaml:"modules,omitempty"`
// Optional: Output is the output of the OPA policy
Output *OpaOutput `json:"output,omitempty" yaml:"output,omitempty"`
Expand Down

0 comments on commit 51c1ce1

Please sign in to comment.