Skip to content

Commit

Permalink
fix: provide full path at compile time (#520)
Browse files Browse the repository at this point in the history
* fix: provide full path at compile time

Signed-off-by: Charles-Edouard Brétéché <[email protected]>

* codegen

Signed-off-by: Charles-Edouard Brétéché <[email protected]>

---------

Signed-off-by: Charles-Edouard Brétéché <[email protected]>
  • Loading branch information
eddycharly authored Sep 26, 2024
1 parent 27c5040 commit 58174c5
Show file tree
Hide file tree
Showing 10 changed files with 39 additions and 34 deletions.
37 changes: 21 additions & 16 deletions pkg/json-engine/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,29 +9,27 @@ import (
"github.com/kyverno/kyverno-json/pkg/core/compilers"
"github.com/kyverno/kyverno-json/pkg/engine"
"github.com/kyverno/kyverno-json/pkg/engine/builder"
"k8s.io/apimachinery/pkg/util/validation/field"
)

func New() engine.Engine[Request, Response] {
type ruleRequest struct {
policy v1alpha1.ValidatingPolicy
rule v1alpha1.ValidatingRule
resource any
bindings binding.Bindings
compilers compilers.Compilers
path *field.Path
policy v1alpha1.ValidatingPolicy
rule v1alpha1.ValidatingRule
resource any
bindings binding.Bindings
}
type policyRequest struct {
policy v1alpha1.ValidatingPolicy
resource any
bindings binding.Bindings
}
compilers := compilers.DefaultCompilers.WithDefaultCompiler(compilers.CompilerJP)
ruleCompiler := compiler{}
ruleEngine := builder.
Function(func(ctx context.Context, r ruleRequest) *RuleResponse {
compilers := compilers
if r.policy.Spec.Compiler != nil {
compilers = compilers.WithDefaultCompiler(string(*r.policy.Spec.Compiler))
}
compiled, err := ruleCompiler.compileRule(nil, compilers, r.rule)
ruleCompiler := compiler{}
compiled, err := ruleCompiler.compileRule(r.path, r.compilers, r.rule)
if err != nil {
return &RuleResponse{
Rule: r.rule,
Expand All @@ -47,13 +45,20 @@ func New() engine.Engine[Request, Response] {
response := PolicyResponse{
Policy: r.policy,
}
path := field.NewPath("spec", "rules")
compilers := compilers.DefaultCompilers.WithDefaultCompiler(compilers.CompilerJP)
if r.policy.Spec.Compiler != nil {
compilers = compilers.WithDefaultCompiler(string(*r.policy.Spec.Compiler))
}
bindings := r.bindings.Register("$policy", binding.NewBinding(r.policy))
for _, rule := range r.policy.Spec.Rules {
for i, rule := range r.policy.Spec.Rules {
if ruleResponse := ruleEngine.Run(ctx, ruleRequest{
policy: r.policy,
rule: rule,
resource: r.resource,
bindings: bindings.Register("$rule", binding.NewBinding(rule)),
compilers: compilers,
path: path.Index(i),
policy: r.policy,
rule: rule,
resource: r.resource,
bindings: bindings.Register("$rule", binding.NewBinding(rule)),
}); ruleResponse != nil {
response.Rules = append(response.Rules, *ruleResponse)
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/server/ui/dist/assets/data.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
{
"category": "Dockerfile",
"name": "check-dockerfile",
"policy": "apiVersion: json.kyverno.io/v1alpha1\nkind: ValidatingPolicy\nmetadata:\n name: check-dockerfile\nspec:\n rules:\n - name: deny-external-calls\n assert:\n all:\n - message: HTTP calls are not allowed\n check:\n ~.(Stages[].Commands[].Args[].Value):\n (contains(@, 'https://') || contains(@, 'http://')): false\n - message: HTTP calls are not allowed\n check:\n ~.(Stages[].Commands[].CmdLine[]):\n (contains(@, 'https://') || contains(@, 'http://')): false\n - message: curl is not allowed\n check:\n ~.(Stages[].Commands[].CmdLine[]):\n (contains(@, 'curl')): false\n - message: wget is not allowed\n check:\n ~.(Stages[].Commands[].CmdLine[]):\n (contains(@, 'wget')): false",
"policy": "apiVersion: json.kyverno.io/v1alpha1\nkind: ValidatingPolicy\nmetadata:\n name: check-dockerfile\nspec:\n rules:\n - name: deny-external-calls\n assert:\n all:\n - message: HTTP calls are not allowed\n check:\n ~.(Stages[].Commands[].Args[].Value):\n (contains(@, 'https://') || contains(@, 'http://')): false\n - message: HTTP calls are not allowed\n check:\n ~.(Stages[].Commands[].CmdLine[]):\n (contains(@, 'https://') || contains(@, 'http://')): false\n - message: curl is not allowed\n check:\n ~.(Stages[].Commands[].CmdLine[]):\n (contains(@, 'curl')): false\n - message: wget is not allowed\n check:\n ~.(Stages[].Commands[].CmdLine[]):\n (contains(@, 'wget')): false\n",
"payload": "MetaArgs:\n- DefaultValue: '\"linux/amd64\"'\n Key: BUILD_PLATFORM\n ProvidedValue: null\n Value: '\"linux/amd64\"'\n- DefaultValue: '\"golang:1.20.6-alpine3.18\"'\n Key: BUILDER_IMAGE\n ProvidedValue: null\n Value: '\"golang:1.20.6-alpine3.18\"'\nStages:\n- As: builder\n BaseName: '\"golang:1.20.6-alpine3.18\"'\n Commands:\n - Name: WORKDIR\n Path: /\n - Chmod: \"\"\n Chown: \"\"\n DestPath: ./\n From: \"\"\n Link: false\n Name: COPY\n SourceContents: null\n SourcePaths:\n - .\n - Args:\n - Comment: \"\"\n Key: SIGNER_BINARY_LINK\n Value: '\"https://d2hvyiie56hcat.cloudfront.net/linux/amd64/plugin/latest/notation-aws-signer-plugin.zip\"'\n Name: ARG\n - Args:\n - Comment: \"\"\n Key: SIGNER_BINARY_FILE\n Value: '\"notation-aws-signer-plugin.zip\"'\n Name: ARG\n - CmdLine:\n - wget -O ${SIGNER_BINARY_FILE} ${SIGNER_BINARY_LINK}\n Files: null\n FlagsUsed: []\n Name: RUN\n PrependShell: true\n - CmdLine:\n - apk update \u0026\u0026 apk add unzip \u0026\u0026 unzip -o ${SIGNER_BINARY_FILE}\n Files: null\n FlagsUsed: []\n Name: RUN\n PrependShell: true\n - CmdLine:\n - GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -ldflags=\"-w -s\" -o kyverno-notation-aws\n .\n Files: null\n FlagsUsed: []\n Name: RUN\n PrependShell: true\n Comment: \"\"\n From:\n Image: '\"golang:1.20.6-alpine3.18\"'\n Location:\n - End:\n Character: 0\n Line: 4\n Start:\n Character: 0\n Line: 4\n Name: builder\n Platform: $BUILD_PLATFORM\n SourceCode: FROM --platform=$BUILD_PLATFORM $BUILDER_IMAGE as builder\n- BaseName: gcr.io/distroless/static:nonroot\n Commands:\n - Name: WORKDIR\n Path: /\n - Env:\n - Key: PLUGINS_DIR\n Value: /plugins\n Name: ENV\n - Chmod: \"\"\n Chown: \"\"\n DestPath: plugins/com.amazonaws.signer.notation.plugin/notation-com.amazonaws.signer.notation.plugin\n From: builder\n Link: false\n Name: COPY\n SourceContents: null\n SourcePaths:\n - notation-com.amazonaws.signer.notation.plugin\n - Chmod: \"\"\n Chown: \"\"\n DestPath: kyverno-notation-aws\n From: builder\n Link: false\n Name: COPY\n SourceContents: null\n SourcePaths:\n - kyverno-notation-aws\n - CmdLine:\n - /kyverno-notation-aws\n Files: null\n Name: ENTRYPOINT\n PrependShell: false\n Comment: \"\"\n From:\n Image: gcr.io/distroless/static:nonroot\n Location:\n - End:\n Character: 0\n Line: 20\n Start:\n Character: 0\n Line: 20\n Name: \"\"\n Platform: \"\"\n SourceCode: FROM gcr.io/distroless/static:nonroot\n"
},
{
Expand Down
4 changes: 2 additions & 2 deletions test/commands/scan/dockerfile/out.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Pre processing ...
Running ( evaluating 1 resource against 1 policy ) ...
- check-dockerfile / deny-external-calls / FAILED
-> HTTP calls are not allowed
-> assert.all[0].check.~.(Stages[].Commands[].Args[].Value)[0].(contains(@, 'https://') || contains(@, 'http://')): Invalid value: true: Expected value: false
-> spec.rules[0].assert.all[0].check.~.(Stages[].Commands[].Args[].Value)[0].(contains(@, 'https://') || contains(@, 'http://')): Invalid value: true: Expected value: false
-> wget is not allowed
-> assert.all[3].check.~.(Stages[].Commands[].CmdLine[])[0].(contains(@, 'wget')): Invalid value: true: Expected value: false
-> spec.rules[0].assert.all[3].check.~.(Stages[].Commands[].CmdLine[])[0].(contains(@, 'wget')): Invalid value: true: Expected value: false
Done
2 changes: 1 addition & 1 deletion test/commands/scan/dockerfile/policy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,4 @@ spec:
- message: wget is not allowed
check:
~.(Stages[].Commands[].CmdLine[]):
(contains(@, 'wget')): false
(contains(@, 'wget')): false
2 changes: 1 addition & 1 deletion test/commands/scan/payload-yaml/out.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ Pre processing ...
Running ( evaluating 1 resource against 1 policy ) ...
- required-s3-tags / require-team-tag / aws_s3_bucket.example FAILED
-> Bucket `example` (aws_s3_bucket.example) does not have the required tags {"Team":"Kyverno"}
-> assert.all[0].check.values.tags: Invalid value: map[string]interface {}{"Environment":"Dev", "Name":"My bucket"}: Expected value: map[string]interface {}{"Team":"Kyverno"}
-> spec.rules[0].assert.all[0].check.values.tags: Invalid value: map[string]interface {}{"Environment":"Dev", "Name":"My bucket"}: Expected value: map[string]interface {}{"Team":"Kyverno"}
Done
18 changes: 9 additions & 9 deletions test/commands/scan/pod-no-latest/out.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ Loading payload ...
Pre processing ...
Running ( evaluating 1 resource against 1 policy ) ...
- test / pod-no-latest / webserver FAILED
-> assert.all[0].check.spec.~foo.containers->foos[0].(at($foos, $foo).image)->foo.(ends_with($foo, $tag)): Invalid value: true: Expected value: false
-> assert.all[0].check.spec.~foo.containers->foos[1].(at($foos, $foo).image)->foo.(ends_with($foo, $tag)): Invalid value: true: Expected value: false
-> assert.all[0].check.spec.~foo.containers->foos[2].(at($foos, $foo).image)->foo.(ends_with($foo, $tag)): Invalid value: true: Expected value: false
-> assert.all[1].check.spec.~.containers->foo[0].image.(ends_with(@, ':latest')): Invalid value: true: Expected value: false
-> assert.all[1].check.spec.~.containers->foo[1].image.(ends_with(@, ':latest')): Invalid value: true: Expected value: false
-> assert.all[1].check.spec.~.containers->foo[2].image.(ends_with(@, ':latest')): Invalid value: true: Expected value: false
-> assert.all[2].check.~index.(spec.containers[*].image)->images[0].(ends_with(@, ':latest')): Invalid value: true: Expected value: false
-> assert.all[2].check.~index.(spec.containers[*].image)->images[1].(ends_with(@, ':latest')): Invalid value: true: Expected value: false
-> assert.all[2].check.~index.(spec.containers[*].image)->images[2].(ends_with(@, ':latest')): Invalid value: true: Expected value: false
-> spec.rules[0].assert.all[0].check.spec.~foo.containers->foos[0].(at($foos, $foo).image)->foo.(ends_with($foo, $tag)): Invalid value: true: Expected value: false
-> spec.rules[0].assert.all[0].check.spec.~foo.containers->foos[1].(at($foos, $foo).image)->foo.(ends_with($foo, $tag)): Invalid value: true: Expected value: false
-> spec.rules[0].assert.all[0].check.spec.~foo.containers->foos[2].(at($foos, $foo).image)->foo.(ends_with($foo, $tag)): Invalid value: true: Expected value: false
-> spec.rules[0].assert.all[1].check.spec.~.containers->foo[0].image.(ends_with(@, ':latest')): Invalid value: true: Expected value: false
-> spec.rules[0].assert.all[1].check.spec.~.containers->foo[1].image.(ends_with(@, ':latest')): Invalid value: true: Expected value: false
-> spec.rules[0].assert.all[1].check.spec.~.containers->foo[2].image.(ends_with(@, ':latest')): Invalid value: true: Expected value: false
-> spec.rules[0].assert.all[2].check.~index.(spec.containers[*].image)->images[0].(ends_with(@, ':latest')): Invalid value: true: Expected value: false
-> spec.rules[0].assert.all[2].check.~index.(spec.containers[*].image)->images[1].(ends_with(@, ':latest')): Invalid value: true: Expected value: false
-> spec.rules[0].assert.all[2].check.~index.(spec.containers[*].image)->images[2].(ends_with(@, ':latest')): Invalid value: true: Expected value: false
Done
2 changes: 1 addition & 1 deletion test/commands/scan/tf-plan/out.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ Pre processing ...
Running ( evaluating 1 resource against 1 policy ) ...
- required-s3-tags / require-team-tag / aws_s3_bucket.example FAILED
-> Bucket `example` (aws_s3_bucket.example) does not have the required tags {"Team":"Kyverno"}
-> assert.all[0].check.values.tags: Invalid value: map[string]interface {}{"Environment":"Dev", "Name":"My bucket"}: Expected value: map[string]interface {}{"Team":"Kyverno"}
-> spec.rules[0].assert.all[0].check.values.tags: Invalid value: map[string]interface {}{"Environment":"Dev", "Name":"My bucket"}: Expected value: map[string]interface {}{"Team":"Kyverno"}
Done
2 changes: 1 addition & 1 deletion test/commands/scan/tf-s3/out.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ Loading payload ...
Pre processing ...
Running ( evaluating 1 resource against 1 policy ) ...
- s3 / check-tags / FAILED
-> assert.all[0].check.planned_values.root_module.~.resources[0].values.(keys(tags_all)).(contains(@, 'Team')): Invalid value: false: Expected value: true
-> spec.rules[0].assert.all[0].check.planned_values.root_module.~.resources[0].values.(keys(tags_all)).(contains(@, 'Team')): Invalid value: false: Expected value: true
Done
2 changes: 1 addition & 1 deletion test/commands/scan/wildcard/out.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ Loading payload ...
Pre processing ...
Running ( evaluating 1 resource against 1 policy ) ...
- required-s3-tags / require-team-tag / bucket1 FAILED
-> assert.all[0].check.tags.(wildcard('?*', Team)): Invalid value: true: Expected value: false
-> spec.rules[0].assert.all[0].check.tags.(wildcard('?*', Team)): Invalid value: true: Expected value: false
Done
2 changes: 1 addition & 1 deletion website/playground/assets/data.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
{
"category": "Dockerfile",
"name": "check-dockerfile",
"policy": "apiVersion: json.kyverno.io/v1alpha1\nkind: ValidatingPolicy\nmetadata:\n name: check-dockerfile\nspec:\n rules:\n - name: deny-external-calls\n assert:\n all:\n - message: HTTP calls are not allowed\n check:\n ~.(Stages[].Commands[].Args[].Value):\n (contains(@, 'https://') || contains(@, 'http://')): false\n - message: HTTP calls are not allowed\n check:\n ~.(Stages[].Commands[].CmdLine[]):\n (contains(@, 'https://') || contains(@, 'http://')): false\n - message: curl is not allowed\n check:\n ~.(Stages[].Commands[].CmdLine[]):\n (contains(@, 'curl')): false\n - message: wget is not allowed\n check:\n ~.(Stages[].Commands[].CmdLine[]):\n (contains(@, 'wget')): false",
"policy": "apiVersion: json.kyverno.io/v1alpha1\nkind: ValidatingPolicy\nmetadata:\n name: check-dockerfile\nspec:\n rules:\n - name: deny-external-calls\n assert:\n all:\n - message: HTTP calls are not allowed\n check:\n ~.(Stages[].Commands[].Args[].Value):\n (contains(@, 'https://') || contains(@, 'http://')): false\n - message: HTTP calls are not allowed\n check:\n ~.(Stages[].Commands[].CmdLine[]):\n (contains(@, 'https://') || contains(@, 'http://')): false\n - message: curl is not allowed\n check:\n ~.(Stages[].Commands[].CmdLine[]):\n (contains(@, 'curl')): false\n - message: wget is not allowed\n check:\n ~.(Stages[].Commands[].CmdLine[]):\n (contains(@, 'wget')): false\n",
"payload": "MetaArgs:\n- DefaultValue: '\"linux/amd64\"'\n Key: BUILD_PLATFORM\n ProvidedValue: null\n Value: '\"linux/amd64\"'\n- DefaultValue: '\"golang:1.20.6-alpine3.18\"'\n Key: BUILDER_IMAGE\n ProvidedValue: null\n Value: '\"golang:1.20.6-alpine3.18\"'\nStages:\n- As: builder\n BaseName: '\"golang:1.20.6-alpine3.18\"'\n Commands:\n - Name: WORKDIR\n Path: /\n - Chmod: \"\"\n Chown: \"\"\n DestPath: ./\n From: \"\"\n Link: false\n Name: COPY\n SourceContents: null\n SourcePaths:\n - .\n - Args:\n - Comment: \"\"\n Key: SIGNER_BINARY_LINK\n Value: '\"https://d2hvyiie56hcat.cloudfront.net/linux/amd64/plugin/latest/notation-aws-signer-plugin.zip\"'\n Name: ARG\n - Args:\n - Comment: \"\"\n Key: SIGNER_BINARY_FILE\n Value: '\"notation-aws-signer-plugin.zip\"'\n Name: ARG\n - CmdLine:\n - wget -O ${SIGNER_BINARY_FILE} ${SIGNER_BINARY_LINK}\n Files: null\n FlagsUsed: []\n Name: RUN\n PrependShell: true\n - CmdLine:\n - apk update \u0026\u0026 apk add unzip \u0026\u0026 unzip -o ${SIGNER_BINARY_FILE}\n Files: null\n FlagsUsed: []\n Name: RUN\n PrependShell: true\n - CmdLine:\n - GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -ldflags=\"-w -s\" -o kyverno-notation-aws\n .\n Files: null\n FlagsUsed: []\n Name: RUN\n PrependShell: true\n Comment: \"\"\n From:\n Image: '\"golang:1.20.6-alpine3.18\"'\n Location:\n - End:\n Character: 0\n Line: 4\n Start:\n Character: 0\n Line: 4\n Name: builder\n Platform: $BUILD_PLATFORM\n SourceCode: FROM --platform=$BUILD_PLATFORM $BUILDER_IMAGE as builder\n- BaseName: gcr.io/distroless/static:nonroot\n Commands:\n - Name: WORKDIR\n Path: /\n - Env:\n - Key: PLUGINS_DIR\n Value: /plugins\n Name: ENV\n - Chmod: \"\"\n Chown: \"\"\n DestPath: plugins/com.amazonaws.signer.notation.plugin/notation-com.amazonaws.signer.notation.plugin\n From: builder\n Link: false\n Name: COPY\n SourceContents: null\n SourcePaths:\n - notation-com.amazonaws.signer.notation.plugin\n - Chmod: \"\"\n Chown: \"\"\n DestPath: kyverno-notation-aws\n From: builder\n Link: false\n Name: COPY\n SourceContents: null\n SourcePaths:\n - kyverno-notation-aws\n - CmdLine:\n - /kyverno-notation-aws\n Files: null\n Name: ENTRYPOINT\n PrependShell: false\n Comment: \"\"\n From:\n Image: gcr.io/distroless/static:nonroot\n Location:\n - End:\n Character: 0\n Line: 20\n Start:\n Character: 0\n Line: 20\n Name: \"\"\n Platform: \"\"\n SourceCode: FROM gcr.io/distroless/static:nonroot\n"
},
{
Expand Down

0 comments on commit 58174c5

Please sign in to comment.