2021-10-05 22:42:42 -07:00
package engine
import (
2022-12-09 14:45:11 +01:00
"context"
2021-10-05 22:42:42 -07:00
"encoding/json"
2022-04-27 08:09:52 -07:00
"fmt"
2022-04-19 08:35:12 -07:00
"strings"
2021-10-29 11:24:26 +01:00
"testing"
2021-10-29 18:13:20 +02:00
kyverno "github.com/kyverno/kyverno/api/kyverno/v1"
2023-01-03 13:02:15 +01:00
"github.com/kyverno/kyverno/pkg/config"
2021-10-05 22:42:42 -07:00
"github.com/kyverno/kyverno/pkg/cosign"
2023-01-30 12:41:09 +01:00
engineapi "github.com/kyverno/kyverno/pkg/engine/api"
2022-12-09 14:45:11 +01:00
enginecontext "github.com/kyverno/kyverno/pkg/engine/context"
2022-12-02 19:29:51 +05:30
"github.com/kyverno/kyverno/pkg/engine/context/resolvers"
2023-02-07 17:51:25 +01:00
"github.com/kyverno/kyverno/pkg/engine/internal"
2021-10-05 22:42:42 -07:00
"github.com/kyverno/kyverno/pkg/engine/utils"
2023-01-03 13:02:15 +01:00
"github.com/kyverno/kyverno/pkg/logging"
"github.com/kyverno/kyverno/pkg/registryclient"
kubeutils "github.com/kyverno/kyverno/pkg/utils/kube"
2021-10-05 22:42:42 -07:00
"gotest.tools/assert"
2023-01-03 13:02:15 +01:00
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
kubefake "k8s.io/client-go/kubernetes/fake"
2021-10-05 22:42:42 -07:00
)
2022-04-19 08:35:12 -07:00
var testPolicyGood = ` {
2021-10-05 22:42:42 -07:00
"apiVersion" : "kyverno.io/v1" ,
"kind" : "ClusterPolicy" ,
"metadata" : {
"name" : "attest"
} ,
"spec" : {
"rules" : [
{
"name" : "attest" ,
"match" : {
"resources" : {
"kinds" : [
"Pod"
]
}
} ,
"verifyImages" : [
{
"image" : "*" ,
"key" : "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEHMmDjK65krAyDaGaeyWNzgvIu155JI50B2vezCw8+3CVeE0lJTL5dbL3OP98Za0oAEBJcOxky8Riy/XcmfKZbw==\n-----END PUBLIC KEY-----" ,
"attestations" : [
{
"predicateType" : "https://example.com/CodeReview/v1" ,
2022-12-16 00:44:49 -08:00
"attestors" : [
{
"entries" : [
{
"keys" : {
"publicKeys" : "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEHMmDjK65krAyDaGaeyWNzgvIu155JI50B2vezCw8+3CVeE0lJTL5dbL3OP98Za0oAEBJcOxky8Riy/XcmfKZbw==\n-----END PUBLIC KEY-----"
}
}
]
}
] ,
2021-10-05 22:42:42 -07:00
"conditions" : [
{
"all" : [
{
"key" : "{{ repo.uri }}" ,
"operator" : "Equals" ,
"value" : "https://github.com/example/my-project"
2023-01-02 18:14:40 +01:00
} ,
2021-10-05 22:42:42 -07:00
{
"key" : "{{ repo.branch }}" ,
"operator" : "Equals" ,
"value" : "main"
}
]
}
]
}
]
}
]
}
]
}
} `
2022-04-19 08:35:12 -07:00
var testPolicyBad = ` {
2021-10-05 22:42:42 -07:00
"apiVersion" : "kyverno.io/v1" ,
"kind" : "ClusterPolicy" ,
"metadata" : {
"name" : "attest"
} ,
"spec" : {
"rules" : [
{
"name" : "attest" ,
"match" : {
"resources" : {
"kinds" : [
"Pod"
]
}
} ,
"verifyImages" : [
{
"image" : "*" ,
"key" : "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEHMmDjK65krAyDaGaeyWNzgvIu155JI50B2vezCw8+3CVeE0lJTL5dbL3OP98Za0oAEBJcOxky8Riy/XcmfKZbw==\n-----END PUBLIC KEY-----" ,
"attestations" : [
{
"predicateType" : "https://example.com/CodeReview/v1" ,
"conditions" : [
{
"all" : [
{
"key" : "{{ repo.uri }}" ,
"operator" : "Equals" ,
"value" : "https://github.com/example/my-project"
2023-01-02 18:14:40 +01:00
} ,
2021-10-05 22:42:42 -07:00
{
"key" : "{{ repo.branch }}" ,
"operator" : "Equals" ,
"value" : "prod"
}
]
}
]
}
]
}
]
}
]
}
} `
2022-04-19 08:35:12 -07:00
var testResource = ` {
2021-10-05 22:42:42 -07:00
"apiVersion" : "v1" ,
"kind" : "Pod" ,
2022-04-27 08:09:52 -07:00
"metadata" : {
"name" : "test" ,
"annotations" : { }
} ,
2021-10-05 22:42:42 -07:00
"spec" : {
"containers" : [
{
"name" : "pause2" ,
"image" : "ghcr.io/jimbugwadia/pause2"
}
]
}
} `
2022-08-12 03:06:14 -07:00
var attestationPayloads = [ ] [ ] byte {
2021-10-05 22:42:42 -07:00
[ ] byte ( ` { "payloadType":"https://example.com/CodeReview/v1","payload":"eyJfdHlwZSI6Imh0dHBzOi8vaW4tdG90by5pby9TdGF0ZW1lbnQvdjAuMSIsInByZWRpY2F0ZVR5cGUiOiJodHRwczovL2V4YW1wbGUuY29tL0NvZGVSZXZpZXcvdjEiLCJzdWJqZWN0IjpbeyJuYW1lIjoiZ2hjci5pby9qaW1idWd3YWRpYS9wYXVzZTIiLCJkaWdlc3QiOnsic2hhMjU2IjoiYjMxYmZiNGQwMjEzZjI1NGQzNjFlMDA3OWRlYWFlYmVmYTRmODJiYTdhYTc2ZWY4MmU5MGI0OTM1YWQ1YjEwNSJ9fV0sInByZWRpY2F0ZSI6eyJhdXRob3IiOiJtYWlsdG86YWxpY2VAZXhhbXBsZS5jb20iLCJyZXBvIjp7ImJyYW5jaCI6Im1haW4iLCJ0eXBlIjoiZ2l0IiwidXJpIjoiaHR0cHM6Ly9naXRodWIuY29tL2V4YW1wbGUvbXktcHJvamVjdCJ9LCJyZXZpZXdlcnMiOlsibWFpbHRvOmJvYkBleGFtcGxlLmNvbSJdfX0=","signatures":[ { "keyid":"","sig":"MEYCIQCrEr+vgPDmNCrqGDE/4z9iMLmCXMXcDlGKtSoiuMTSFgIhAN2riBaGk4accWzVl7ypi1XTRxyrPYHst8DesugPXgOf"}]} ` ) ,
[ ] byte ( ` { "payloadType":"cosign.sigstore.dev/attestation/v1","payload":"eyJfdHlwZSI6Imh0dHBzOi8vaW4tdG90by5pby9TdGF0ZW1lbnQvdjAuMSIsInByZWRpY2F0ZVR5cGUiOiJjb3NpZ24uc2lnc3RvcmUuZGV2L2F0dGVzdGF0aW9uL3YxIiwic3ViamVjdCI6W3sibmFtZSI6ImdoY3IuaW8vamltYnVnd2FkaWEvcGF1c2UyIiwiZGlnZXN0Ijp7InNoYTI1NiI6ImIzMWJmYjRkMDIxM2YyNTRkMzYxZTAwNzlkZWFhZWJlZmE0ZjgyYmE3YWE3NmVmODJlOTBiNDkzNWFkNWIxMDUifX1dLCJwcmVkaWNhdGUiOnsiRGF0YSI6ImhlbGxvIVxuIiwiVGltZXN0YW1wIjoiMjAyMS0xMC0wNVQwNToxODoxMVoifX0=","signatures":[ { "keyid":"","sig":"MEQCIF5r9lf55rnYNPByZ9v6bortww694UEPvmyBIelIDYbIAiBNTGX4V64Oj6jZVRpkJQRxdzKUPYqC5GZTb4oS6eQ6aQ=="}]} ` ) ,
[ ] byte ( ` { "payloadType":"https://example.com/CodeReview/v1","payload":"eyJfdHlwZSI6Imh0dHBzOi8vaW4tdG90by5pby9TdGF0ZW1lbnQvdjAuMSIsInByZWRpY2F0ZVR5cGUiOiJodHRwczovL2V4YW1wbGUuY29tL0NvZGVSZXZpZXcvdjEiLCJzdWJqZWN0IjpbeyJuYW1lIjoiZ2hjci5pby9qaW1idWd3YWRpYS9wYXVzZTIiLCJkaWdlc3QiOnsic2hhMjU2IjoiYjMxYmZiNGQwMjEzZjI1NGQzNjFlMDA3OWRlYWFlYmVmYTRmODJiYTdhYTc2ZWY4MmU5MGI0OTM1YWQ1YjEwNSJ9fV0sInByZWRpY2F0ZSI6eyJhdXRob3IiOiJtYWlsdG86YWxpY2VAZXhhbXBsZS5jb20iLCJyZXBvIjp7ImJyYW5jaCI6Im1haW4iLCJ0eXBlIjoiZ2l0IiwidXJpIjoiaHR0cHM6Ly9naXRodWIuY29tL2V4YW1wbGUvbXktcHJvamVjdCJ9LCJyZXZpZXdlcnMiOlsibWFpbHRvOmJvYkBleGFtcGxlLmNvbSJdfX0=","signatures":[ { "keyid":"","sig":"MEUCIEeZbdBEFQzWqiMhB+SJgM6yFppUuQSKrpOIX1mxLDmRAiEA8pXqFq0GVc9LKhPzrnJRZhSruDNiKbiLHG5x7ETFyY8="}]} ` ) ,
}
2022-08-12 03:06:14 -07:00
var signaturePayloads = [ ] [ ] byte {
[ ] byte ( ` { "critical": { "identity": { "docker-reference":"ghcr.io/kyverno/test-verify-image"},"image": { "docker-manifest-digest":"sha256:b31bfb4d0213f254d361e0079deaaebefa4f82ba7aa76ef82e90b4935ad5b105"},"type":"cosign container image signature"},"optional":null} ` ) ,
}
2023-01-02 18:14:40 +01:00
var cfg = config . NewDefaultConfiguration ( )
2023-02-02 11:58:34 +01:00
func testVerifyAndPatchImages (
2023-01-31 15:30:40 +01:00
ctx context . Context ,
rclient registryclient . Client ,
2023-02-03 06:01:11 +01:00
cmResolver engineapi . ConfigmapResolver ,
2023-01-31 16:28:48 +01:00
pContext engineapi . PolicyContext ,
2023-01-31 15:30:40 +01:00
cfg config . Configuration ,
2023-02-02 11:58:34 +01:00
) ( * engineapi . EngineResponse , * engineapi . ImageVerificationMetadata ) {
2023-02-06 13:49:04 +01:00
e := NewEngine (
cfg ,
2023-02-07 16:09:15 +01:00
nil ,
2023-02-08 06:55:03 +01:00
rclient ,
LegacyContextLoaderFactory ( cmResolver ) ,
2023-02-06 06:49:47 +01:00
nil ,
2023-02-06 13:49:04 +01:00
)
return e . VerifyAndPatchImages (
ctx ,
2023-01-31 15:30:40 +01:00
pContext ,
)
}
2022-04-19 08:35:12 -07:00
func Test_CosignMockAttest ( t * testing . T ) {
2022-04-27 08:09:52 -07:00
policyContext := buildContext ( t , testPolicyGood , testResource , "" )
2022-08-12 03:06:14 -07:00
err := cosign . SetMock ( "ghcr.io/jimbugwadia/pause2:latest" , attestationPayloads )
2021-10-05 22:42:42 -07:00
assert . NilError ( t , err )
2023-02-03 06:01:11 +01:00
er , ivm := testVerifyAndPatchImages ( context . TODO ( ) , registryclient . NewOrDie ( ) , nil , policyContext , cfg )
2021-10-05 22:42:42 -07:00
assert . Equal ( t , len ( er . PolicyResponse . Rules ) , 1 )
2023-01-30 12:41:09 +01:00
assert . Equal ( t , er . PolicyResponse . Rules [ 0 ] . Status , engineapi . RuleStatusPass ,
2022-12-02 06:09:44 +08:00
fmt . Sprintf ( "expected: %v, got: %v, failure: %v" ,
2023-01-30 12:41:09 +01:00
engineapi . RuleStatusPass , er . PolicyResponse . Rules [ 0 ] . Status , er . PolicyResponse . Rules [ 0 ] . Message ) )
2022-05-05 14:06:18 -07:00
assert . Equal ( t , ivm . IsEmpty ( ) , false )
2023-02-02 11:58:34 +01:00
assert . Equal ( t , ivm . IsVerified ( "ghcr.io/jimbugwadia/pause2:latest" ) , true )
2021-10-05 22:42:42 -07:00
}
2022-04-19 08:35:12 -07:00
func Test_CosignMockAttest_fail ( t * testing . T ) {
2022-04-27 08:09:52 -07:00
policyContext := buildContext ( t , testPolicyBad , testResource , "" )
2022-08-12 03:06:14 -07:00
err := cosign . SetMock ( "ghcr.io/jimbugwadia/pause2:latest" , attestationPayloads )
2021-10-05 22:42:42 -07:00
assert . NilError ( t , err )
2023-02-03 06:01:11 +01:00
er , _ := testVerifyAndPatchImages ( context . TODO ( ) , registryclient . NewOrDie ( ) , nil , policyContext , cfg )
2021-10-05 22:42:42 -07:00
assert . Equal ( t , len ( er . PolicyResponse . Rules ) , 1 )
2023-01-30 12:41:09 +01:00
assert . Equal ( t , er . PolicyResponse . Rules [ 0 ] . Status , engineapi . RuleStatusFail )
2021-10-05 22:42:42 -07:00
}
2022-04-27 08:09:52 -07:00
func buildContext ( t * testing . T , policy , resource string , oldResource string ) * PolicyContext {
2021-10-05 22:42:42 -07:00
var cpol kyverno . ClusterPolicy
2022-04-27 08:09:52 -07:00
err := json . Unmarshal ( [ ] byte ( policy ) , & cpol )
assert . NilError ( t , err )
2021-10-05 22:42:42 -07:00
2023-01-03 13:02:15 +01:00
resourceUnstructured , err := kubeutils . BytesToUnstructured ( [ ] byte ( resource ) )
2021-10-05 22:42:42 -07:00
assert . NilError ( t , err )
2022-04-27 08:09:52 -07:00
2022-12-09 14:45:11 +01:00
ctx := enginecontext . NewContext ( )
err = enginecontext . AddResource ( ctx , [ ] byte ( resource ) )
2022-04-27 08:09:52 -07:00
assert . NilError ( t , err )
2021-10-05 22:42:42 -07:00
policyContext := & PolicyContext {
2022-12-02 09:14:23 +01:00
policy : & cpol ,
jsonContext : ctx ,
newResource : * resourceUnstructured ,
2022-04-27 08:09:52 -07:00
}
if oldResource != "" {
2023-01-03 13:02:15 +01:00
oldResourceUnstructured , err := kubeutils . BytesToUnstructured ( [ ] byte ( oldResource ) )
2022-04-27 08:09:52 -07:00
assert . NilError ( t , err )
2022-12-09 14:45:11 +01:00
err = enginecontext . AddOldResource ( ctx , [ ] byte ( oldResource ) )
2022-04-27 08:09:52 -07:00
assert . NilError ( t , err )
2022-12-02 09:14:23 +01:00
policyContext . oldResource = * oldResourceUnstructured
2022-04-27 08:09:52 -07:00
}
2021-10-05 22:42:42 -07:00
2023-01-02 18:14:40 +01:00
if err := ctx . AddImageInfos ( resourceUnstructured , cfg ) ; err != nil {
2021-10-05 22:42:42 -07:00
t . Errorf ( "unable to add image info to variables context: %v" , err )
t . Fail ( )
}
2022-04-27 08:09:52 -07:00
2021-10-05 22:42:42 -07:00
return policyContext
}
2022-04-19 08:35:12 -07:00
var testSampleSingleKeyPolicy = `
{
"apiVersion" : "kyverno.io/v1" ,
"kind" : "ClusterPolicy" ,
"metadata" : {
"name" : "check-image" ,
"annotations" : {
"pod-policies.kyverno.io/autogen-controllers" : "none"
}
} ,
"spec" : {
"validationFailureAction" : "enforce" ,
"background" : false ,
"webhookTimeoutSeconds" : 30 ,
"failurePolicy" : "Fail" ,
"rules" : [
{
"name" : "check-signature" ,
"match" : {
"resources" : {
"kinds" : [
"Pod"
]
}
} ,
"verifyImages" : [
{
"imageReferences" : [
"ghcr.io/kyverno/test-verify-image:*"
] ,
"attestors" : [
{
"entries" : [
{
2022-05-05 21:57:20 -07:00
"keys" : {
"publicKeys" : "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE8nXRh950IZbRj8Ra/N9sbqOPZrfM\n5/KAQN0/KjHcorm/J5yctVd7iEcnessRQjU917hmKO6JWVGHpDguIyakZA==\n-----END PUBLIC KEY-----"
2022-04-19 08:35:12 -07:00
}
}
]
}
]
}
]
}
]
}
}
`
var testSampleMultipleKeyPolicy = `
{
"apiVersion" : "kyverno.io/v1" ,
"kind" : "ClusterPolicy" ,
"metadata" : {
"name" : "check-image" ,
"annotations" : {
"pod-policies.kyverno.io/autogen-controllers" : "none"
}
} ,
"spec" : {
"validationFailureAction" : "enforce" ,
"background" : false ,
"webhookTimeoutSeconds" : 30 ,
"failurePolicy" : "Fail" ,
"rules" : [
{
"name" : "check-signature" ,
"match" : {
"resources" : {
"kinds" : [
"Pod"
]
}
} ,
"verifyImages" : [
{
"imageReferences" : [
"ghcr.io/kyverno/test-verify-image:*"
] ,
"attestors" : [
{
"count" : COUNT ,
"entries" : [
{
2022-05-05 21:57:20 -07:00
"keys" : {
"publicKeys" : "KEY1"
2022-04-19 08:35:12 -07:00
}
} ,
{
2022-05-05 21:57:20 -07:00
"keys" : {
"publicKeys" : "KEY2"
2022-04-19 08:35:12 -07:00
}
}
]
}
]
}
]
}
]
}
}
`
2022-11-18 13:57:34 +05:30
var testConfigMapMissing = ` {
"apiVersion" : "kyverno.io/v1" ,
"kind" : "ClusterPolicy" ,
"metadata" : {
"annotations" : {
"pod-policies.kyverno.io/autogen-controllers" : "none"
} ,
"name" : "image-verify-polset"
} ,
"spec" : {
"background" : false ,
"failurePolicy" : "Fail" ,
"rules" : [
{
"context" : [
{
"configMap" : {
"name" : "myconfigmap" ,
"namespace" : "mynamespace"
} ,
"name" : "myconfigmap"
}
] ,
"match" : {
"any" : [
{
"resources" : {
"kinds" : [
"Pod"
]
}
}
]
} ,
"name" : "image-verify-pol1" ,
"verifyImages" : [
{
"imageReferences" : [
"ghcr.io/*"
] ,
"mutateDigest" : false ,
"verifyDigest" : false ,
"attestors" : [
{
"entries" : [
{
"keys" : {
"publicKeys" : "{{myconfigmap.data.configmapkey}}"
}
}
]
}
]
}
]
}
] ,
"validationFailureAction" : "Audit" ,
"webhookTimeoutSeconds" : 30
}
} `
2022-04-19 08:35:12 -07:00
var testSampleResource = ` {
"apiVersion" : "v1" ,
"kind" : "Pod" ,
"metadata" : { "name" : "test" } ,
"spec" : {
"containers" : [
{
"name" : "pause2" ,
"image" : "ghcr.io/kyverno/test-verify-image:signed"
}
]
}
} `
2022-11-18 13:57:34 +05:30
var testConfigMapMissingResource = ` {
"apiVersion" : "v1" ,
"kind" : "Pod" ,
"metadata" : {
"labels" : {
"run" : "test"
} ,
"name" : "test"
} ,
"spec" : {
"containers" : [
{
"image" : "nginx:latest" ,
"name" : "test" ,
"resources" : { }
}
]
}
} `
2022-12-12 07:20:20 -08:00
var (
testVerifyImageKey = ` -----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE8nXRh950IZbRj8Ra/N9sbqOPZrfM5/KAQN0/KjHcorm/J5yctVd7iEcnessRQjU917hmKO6JWVGHpDguIyakZA==\n-----END PUBLIC KEY-----\n `
testOtherKey = ` -----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEpNlOGZ323zMlhs4bcKSpAKQvbcWi5ZLRmijm6SqXDy0Fp0z0Eal+BekFnLzs8rUXUaXlhZ3hNudlgFJH+nFNMw==\n-----END PUBLIC KEY-----\n `
)
2022-04-19 08:35:12 -07:00
2022-11-18 13:57:34 +05:30
func Test_ConfigMapMissingSuccess ( t * testing . T ) {
policyContext := buildContext ( t , testConfigMapMissing , testConfigMapMissingResource , "" )
cosign . ClearMock ( )
2023-02-03 06:01:11 +01:00
err , _ := testVerifyAndPatchImages ( context . TODO ( ) , registryclient . NewOrDie ( ) , nil , policyContext , cfg )
2022-11-18 13:57:34 +05:30
assert . Equal ( t , len ( err . PolicyResponse . Rules ) , 1 )
2023-01-30 12:41:09 +01:00
assert . Equal ( t , err . PolicyResponse . Rules [ 0 ] . Status , engineapi . RuleStatusSkip , err . PolicyResponse . Rules [ 0 ] . Message )
2022-11-18 13:57:34 +05:30
}
func Test_ConfigMapMissingFailure ( t * testing . T ) {
ghcrImage := strings . Replace ( testConfigMapMissingResource , "nginx:latest" , "ghcr.io/kyverno/test-verify-image:signed" , - 1 )
policyContext := buildContext ( t , testConfigMapMissing , ghcrImage , "" )
2022-12-02 19:29:51 +05:30
resolver , err := resolvers . NewClientBasedResolver ( kubefake . NewSimpleClientset ( ) )
assert . NilError ( t , err )
2022-11-18 13:57:34 +05:30
cosign . ClearMock ( )
2023-02-03 06:01:11 +01:00
resp , _ := testVerifyAndPatchImages ( context . TODO ( ) , registryclient . NewOrDie ( ) , resolver , policyContext , cfg )
2022-12-02 19:29:51 +05:30
assert . Equal ( t , len ( resp . PolicyResponse . Rules ) , 1 )
2023-01-30 12:41:09 +01:00
assert . Equal ( t , resp . PolicyResponse . Rules [ 0 ] . Status , engineapi . RuleStatusError , resp . PolicyResponse . Rules [ 0 ] . Message )
2022-11-18 13:57:34 +05:30
}
2022-04-19 08:35:12 -07:00
func Test_SignatureGoodSigned ( t * testing . T ) {
2022-04-27 08:09:52 -07:00
policyContext := buildContext ( t , testSampleSingleKeyPolicy , testSampleResource , "" )
2022-12-16 00:44:49 -08:00
policyContext . policy . GetSpec ( ) . Rules [ 0 ] . VerifyImages [ 0 ] . MutateDigest = true
2022-04-19 08:35:12 -07:00
cosign . ClearMock ( )
2023-02-03 06:01:11 +01:00
engineResp , _ := testVerifyAndPatchImages ( context . TODO ( ) , registryclient . NewOrDie ( ) , nil , policyContext , cfg )
2022-12-16 00:44:49 -08:00
assert . Equal ( t , len ( engineResp . PolicyResponse . Rules ) , 1 )
2023-01-30 12:41:09 +01:00
assert . Equal ( t , engineResp . PolicyResponse . Rules [ 0 ] . Status , engineapi . RuleStatusPass , engineResp . PolicyResponse . Rules [ 0 ] . Message )
2022-12-16 00:44:49 -08:00
assert . Equal ( t , len ( engineResp . PolicyResponse . Rules [ 0 ] . Patches ) , 1 )
patch := engineResp . PolicyResponse . Rules [ 0 ] . Patches [ 0 ]
assert . Equal ( t , string ( patch ) , "{\"op\":\"replace\",\"path\":\"/spec/containers/0/image\",\"value\":\"ghcr.io/kyverno/test-verify-image:signed@sha256:b31bfb4d0213f254d361e0079deaaebefa4f82ba7aa76ef82e90b4935ad5b105\"}" )
2022-04-19 08:35:12 -07:00
}
func Test_SignatureUnsigned ( t * testing . T ) {
cosign . ClearMock ( )
unsigned := strings . Replace ( testSampleResource , ":signed" , ":unsigned" , - 1 )
2022-04-27 08:09:52 -07:00
policyContext := buildContext ( t , testSampleSingleKeyPolicy , unsigned , "" )
2023-02-03 06:01:11 +01:00
engineResp , _ := testVerifyAndPatchImages ( context . TODO ( ) , registryclient . NewOrDie ( ) , nil , policyContext , cfg )
2022-12-16 00:44:49 -08:00
assert . Equal ( t , len ( engineResp . PolicyResponse . Rules ) , 1 )
2023-01-30 12:41:09 +01:00
assert . Equal ( t , engineResp . PolicyResponse . Rules [ 0 ] . Status , engineapi . RuleStatusFail , engineResp . PolicyResponse . Rules [ 0 ] . Message )
2022-04-19 08:35:12 -07:00
}
func Test_SignatureWrongKey ( t * testing . T ) {
cosign . ClearMock ( )
otherKey := strings . Replace ( testSampleResource , ":signed" , ":signed-by-someone-else" , - 1 )
2022-04-27 08:09:52 -07:00
policyContext := buildContext ( t , testSampleSingleKeyPolicy , otherKey , "" )
2023-02-03 06:01:11 +01:00
engineResp , _ := testVerifyAndPatchImages ( context . TODO ( ) , registryclient . NewOrDie ( ) , nil , policyContext , cfg )
2022-12-16 00:44:49 -08:00
assert . Equal ( t , len ( engineResp . PolicyResponse . Rules ) , 1 )
2023-01-30 12:41:09 +01:00
assert . Equal ( t , engineResp . PolicyResponse . Rules [ 0 ] . Status , engineapi . RuleStatusFail , engineResp . PolicyResponse . Rules [ 0 ] . Message )
2022-04-19 08:35:12 -07:00
}
func Test_SignaturesMultiKey ( t * testing . T ) {
cosign . ClearMock ( )
policy := strings . Replace ( testSampleMultipleKeyPolicy , "KEY1" , testVerifyImageKey , - 1 )
policy = strings . Replace ( policy , "KEY2" , testVerifyImageKey , - 1 )
policy = strings . Replace ( policy , "COUNT" , "0" , - 1 )
2022-04-27 08:09:52 -07:00
policyContext := buildContext ( t , policy , testSampleResource , "" )
2023-02-03 06:01:11 +01:00
engineResp , _ := testVerifyAndPatchImages ( context . TODO ( ) , registryclient . NewOrDie ( ) , nil , policyContext , cfg )
2022-12-16 00:44:49 -08:00
assert . Equal ( t , len ( engineResp . PolicyResponse . Rules ) , 1 )
2023-01-30 12:41:09 +01:00
assert . Equal ( t , engineResp . PolicyResponse . Rules [ 0 ] . Status , engineapi . RuleStatusPass , engineResp . PolicyResponse . Rules [ 0 ] . Message )
2022-04-19 08:35:12 -07:00
}
func Test_SignaturesMultiKeyFail ( t * testing . T ) {
cosign . ClearMock ( )
policy := strings . Replace ( testSampleMultipleKeyPolicy , "KEY1" , testVerifyImageKey , - 1 )
policy = strings . Replace ( policy , "COUNT" , "0" , - 1 )
2022-04-27 08:09:52 -07:00
policyContext := buildContext ( t , policy , testSampleResource , "" )
2023-02-03 06:01:11 +01:00
engineResp , _ := testVerifyAndPatchImages ( context . TODO ( ) , registryclient . NewOrDie ( ) , nil , policyContext , cfg )
2022-12-16 00:44:49 -08:00
assert . Equal ( t , len ( engineResp . PolicyResponse . Rules ) , 1 )
2023-01-30 12:41:09 +01:00
assert . Equal ( t , engineResp . PolicyResponse . Rules [ 0 ] . Status , engineapi . RuleStatusFail , engineResp . PolicyResponse . Rules [ 0 ] . Message )
2022-04-19 08:35:12 -07:00
}
func Test_SignaturesMultiKeyOneGoodKey ( t * testing . T ) {
cosign . ClearMock ( )
policy := strings . Replace ( testSampleMultipleKeyPolicy , "KEY1" , testVerifyImageKey , - 1 )
policy = strings . Replace ( policy , "KEY2" , testOtherKey , - 1 )
policy = strings . Replace ( policy , "COUNT" , "1" , - 1 )
2022-04-27 08:09:52 -07:00
policyContext := buildContext ( t , policy , testSampleResource , "" )
2023-02-03 06:01:11 +01:00
engineResp , _ := testVerifyAndPatchImages ( context . TODO ( ) , registryclient . NewOrDie ( ) , nil , policyContext , cfg )
2022-12-16 00:44:49 -08:00
assert . Equal ( t , len ( engineResp . PolicyResponse . Rules ) , 1 )
2023-01-30 12:41:09 +01:00
assert . Equal ( t , engineResp . PolicyResponse . Rules [ 0 ] . Status , engineapi . RuleStatusPass , engineResp . PolicyResponse . Rules [ 0 ] . Message )
2022-04-19 08:35:12 -07:00
}
func Test_SignaturesMultiKeyZeroGoodKey ( t * testing . T ) {
cosign . ClearMock ( )
policy := strings . Replace ( testSampleMultipleKeyPolicy , "KEY1" , testOtherKey , - 1 )
policy = strings . Replace ( policy , "KEY2" , testOtherKey , - 1 )
policy = strings . Replace ( policy , "COUNT" , "1" , - 1 )
2022-04-27 08:09:52 -07:00
policyContext := buildContext ( t , policy , testSampleResource , "" )
2023-02-03 06:01:11 +01:00
resp , _ := testVerifyAndPatchImages ( context . TODO ( ) , registryclient . NewOrDie ( ) , nil , policyContext , cfg )
2022-07-29 00:02:26 -07:00
assert . Equal ( t , len ( resp . PolicyResponse . Rules ) , 1 )
2023-01-30 12:41:09 +01:00
assert . Equal ( t , resp . PolicyResponse . Rules [ 0 ] . Status , engineapi . RuleStatusFail , resp . PolicyResponse . Rules [ 0 ] . Message )
2022-07-29 00:02:26 -07:00
}
func Test_RuleSelectorImageVerify ( t * testing . T ) {
cosign . ClearMock ( )
policyContext := buildContext ( t , testSampleSingleKeyPolicy , testSampleResource , "" )
rule := newStaticKeyRule ( "match-all" , "*" , testOtherKey )
2022-12-02 09:14:23 +01:00
spec := policyContext . policy . GetSpec ( )
2022-07-29 00:02:26 -07:00
spec . Rules = append ( spec . Rules , * rule )
applyAll := kyverno . ApplyAll
spec . ApplyRules = & applyAll
2023-02-03 06:01:11 +01:00
resp , _ := testVerifyAndPatchImages ( context . TODO ( ) , registryclient . NewOrDie ( ) , nil , policyContext , cfg )
2022-07-29 00:02:26 -07:00
assert . Equal ( t , len ( resp . PolicyResponse . Rules ) , 2 )
2023-01-30 12:41:09 +01:00
assert . Equal ( t , resp . PolicyResponse . Rules [ 0 ] . Status , engineapi . RuleStatusPass , resp . PolicyResponse . Rules [ 0 ] . Message )
assert . Equal ( t , resp . PolicyResponse . Rules [ 1 ] . Status , engineapi . RuleStatusFail , resp . PolicyResponse . Rules [ 1 ] . Message )
2022-07-29 00:02:26 -07:00
applyOne := kyverno . ApplyOne
spec . ApplyRules = & applyOne
2023-02-03 06:01:11 +01:00
resp , _ = testVerifyAndPatchImages ( context . TODO ( ) , registryclient . NewOrDie ( ) , nil , policyContext , cfg )
2022-07-29 00:02:26 -07:00
assert . Equal ( t , len ( resp . PolicyResponse . Rules ) , 1 )
2023-01-30 12:41:09 +01:00
assert . Equal ( t , resp . PolicyResponse . Rules [ 0 ] . Status , engineapi . RuleStatusPass , resp . PolicyResponse . Rules [ 0 ] . Message )
2022-07-29 00:02:26 -07:00
}
func newStaticKeyRule ( name , imageReference , key string ) * kyverno . Rule {
return & kyverno . Rule {
Name : name ,
MatchResources : kyverno . MatchResources {
All : kyverno . ResourceFilters {
{
ResourceDescription : kyverno . ResourceDescription {
Kinds : [ ] string { "Pod" } ,
} ,
} ,
} ,
} ,
VerifyImages : [ ] kyverno . ImageVerification {
{
ImageReferences : [ ] string { "*" } ,
Attestors : [ ] kyverno . AttestorSet {
{
Entries : [ ] kyverno . Attestor {
{
Keys : & kyverno . StaticKeyAttestor {
PublicKeys : key ,
} ,
} ,
} ,
} ,
} ,
} ,
} ,
}
2022-04-19 08:35:12 -07:00
}
var testNestedAttestorPolicy = `
{
"apiVersion" : "kyverno.io/v1" ,
"kind" : "ClusterPolicy" ,
"metadata" : {
"name" : "check-image-keyless" ,
"annotations" : {
"pod-policies.kyverno.io/autogen-controllers" : "none"
}
} ,
"spec" : {
"validationFailureAction" : "enforce" ,
"background" : false ,
"webhookTimeoutSeconds" : 30 ,
"failurePolicy" : "Fail" ,
"rules" : [
{
"name" : "check-image-keyless" ,
"match" : {
"resources" : {
"kinds" : [
"Pod"
]
}
} ,
"verifyImages" : [
{
"imageReferences" : [
"ghcr.io/kyverno/test-verify-image:*"
] ,
"attestors" : [
{
"count" : COUNT ,
"entries" : [
{
2022-05-05 21:57:20 -07:00
"keys" : {
"publicKeys" : "KEY1"
2022-04-19 08:35:12 -07:00
}
} ,
{
"attestor" : {
"entries" : [
{
2022-05-05 21:57:20 -07:00
"keys" : {
"publicKeys" : "KEY2"
2022-04-19 08:35:12 -07:00
}
}
]
}
}
]
}
]
}
]
}
]
}
}
`
func Test_NestedAttestors ( t * testing . T ) {
cosign . ClearMock ( )
policy := strings . Replace ( testNestedAttestorPolicy , "KEY1" , testVerifyImageKey , - 1 )
policy = strings . Replace ( policy , "KEY2" , testVerifyImageKey , - 1 )
policy = strings . Replace ( policy , "COUNT" , "0" , - 1 )
2022-04-27 08:09:52 -07:00
policyContext := buildContext ( t , policy , testSampleResource , "" )
2023-02-03 06:01:11 +01:00
err , _ := testVerifyAndPatchImages ( context . TODO ( ) , registryclient . NewOrDie ( ) , nil , policyContext , cfg )
2022-04-19 08:35:12 -07:00
assert . Equal ( t , len ( err . PolicyResponse . Rules ) , 1 )
2023-01-30 12:41:09 +01:00
assert . Equal ( t , err . PolicyResponse . Rules [ 0 ] . Status , engineapi . RuleStatusPass )
2022-04-19 08:35:12 -07:00
policy = strings . Replace ( testNestedAttestorPolicy , "KEY1" , testVerifyImageKey , - 1 )
policy = strings . Replace ( policy , "KEY2" , testOtherKey , - 1 )
policy = strings . Replace ( policy , "COUNT" , "0" , - 1 )
2022-04-27 08:09:52 -07:00
policyContext = buildContext ( t , policy , testSampleResource , "" )
2023-02-03 06:01:11 +01:00
err , _ = testVerifyAndPatchImages ( context . TODO ( ) , registryclient . NewOrDie ( ) , nil , policyContext , cfg )
2022-04-19 08:35:12 -07:00
assert . Equal ( t , len ( err . PolicyResponse . Rules ) , 1 )
2023-01-30 12:41:09 +01:00
assert . Equal ( t , err . PolicyResponse . Rules [ 0 ] . Status , engineapi . RuleStatusFail )
2022-04-19 08:35:12 -07:00
policy = strings . Replace ( testNestedAttestorPolicy , "KEY1" , testVerifyImageKey , - 1 )
policy = strings . Replace ( policy , "KEY2" , testOtherKey , - 1 )
policy = strings . Replace ( policy , "COUNT" , "1" , - 1 )
2022-04-27 08:09:52 -07:00
policyContext = buildContext ( t , policy , testSampleResource , "" )
2023-02-03 06:01:11 +01:00
err , _ = testVerifyAndPatchImages ( context . TODO ( ) , registryclient . NewOrDie ( ) , nil , policyContext , cfg )
2022-04-19 08:35:12 -07:00
assert . Equal ( t , len ( err . PolicyResponse . Rules ) , 1 )
2023-01-30 12:41:09 +01:00
assert . Equal ( t , err . PolicyResponse . Rules [ 0 ] . Status , engineapi . RuleStatusPass )
2022-04-19 08:35:12 -07:00
}
2022-04-22 00:10:02 -07:00
func Test_ExpandKeys ( t * testing . T ) {
2023-02-07 17:51:25 +01:00
as := internal . ExpandStaticKeys ( createStaticKeyAttestorSet ( "" , true , false , false ) )
2022-04-22 00:10:02 -07:00
assert . Equal ( t , 1 , len ( as . Entries ) )
2023-02-07 17:51:25 +01:00
as = internal . ExpandStaticKeys ( createStaticKeyAttestorSet ( testOtherKey , true , false , false ) )
2022-04-22 00:10:02 -07:00
assert . Equal ( t , 1 , len ( as . Entries ) )
2023-02-07 17:51:25 +01:00
as = internal . ExpandStaticKeys ( createStaticKeyAttestorSet ( testOtherKey + testOtherKey + testOtherKey , true , false , false ) )
2022-04-22 00:10:02 -07:00
assert . Equal ( t , 3 , len ( as . Entries ) )
2022-10-14 15:10:46 +05:30
2023-02-07 17:51:25 +01:00
as = internal . ExpandStaticKeys ( createStaticKeyAttestorSet ( "" , false , true , false ) )
2022-10-14 15:10:46 +05:30
assert . Equal ( t , 1 , len ( as . Entries ) )
assert . DeepEqual ( t , & kyverno . SecretReference { Name : "testsecret" , Namespace : "default" } ,
as . Entries [ 0 ] . Keys . Secret )
2023-02-07 17:51:25 +01:00
as = internal . ExpandStaticKeys ( createStaticKeyAttestorSet ( "" , false , false , true ) )
2022-10-14 15:10:46 +05:30
assert . Equal ( t , 1 , len ( as . Entries ) )
assert . DeepEqual ( t , "gcpkms://projects/test_project_id/locations/asia-south1/keyRings/test_key_ring_name/cryptoKeys/test_key_name/versions/1" , as . Entries [ 0 ] . Keys . KMS )
2023-02-07 17:51:25 +01:00
as = internal . ExpandStaticKeys ( ( createStaticKeyAttestorSet ( testOtherKey , true , true , false ) ) )
2022-10-14 15:10:46 +05:30
assert . Equal ( t , 2 , len ( as . Entries ) )
assert . DeepEqual ( t , testOtherKey , as . Entries [ 0 ] . Keys . PublicKeys )
2023-02-07 17:51:25 +01:00
assert . DeepEqual ( t , & kyverno . SecretReference { Name : "testsecret" , Namespace : "default" } , as . Entries [ 1 ] . Keys . Secret )
2022-04-22 00:10:02 -07:00
}
2022-10-14 15:10:46 +05:30
func createStaticKeyAttestorSet ( s string , withPublicKey , withSecret , withKMS bool ) kyverno . AttestorSet {
var entries [ ] kyverno . Attestor
if withPublicKey {
attestor := kyverno . Attestor {
Keys : & kyverno . StaticKeyAttestor {
PublicKeys : s ,
} ,
}
entries = append ( entries , attestor )
}
if withSecret {
attestor := kyverno . Attestor {
Keys : & kyverno . StaticKeyAttestor {
Secret : & kyverno . SecretReference {
Name : "testsecret" ,
Namespace : "default" ,
2022-04-22 00:10:02 -07:00
} ,
} ,
2022-10-14 15:10:46 +05:30
}
entries = append ( entries , attestor )
}
if withKMS {
kmsKey := "gcpkms://projects/test_project_id/locations/asia-south1/keyRings/test_key_ring_name/cryptoKeys/test_key_name/versions/1"
attestor := kyverno . Attestor {
Keys : & kyverno . StaticKeyAttestor {
KMS : kmsKey ,
} ,
}
entries = append ( entries , attestor )
2022-04-22 00:10:02 -07:00
}
2022-10-14 15:10:46 +05:30
return kyverno . AttestorSet { Entries : entries }
2022-04-22 00:10:02 -07:00
}
2022-04-27 08:09:52 -07:00
func Test_ChangedAnnotation ( t * testing . T ) {
2023-02-02 11:58:34 +01:00
annotationKey := engineapi . ImageVerifyAnnotationKey
2022-04-27 08:09:52 -07:00
annotationNew := fmt . Sprintf ( "\"annotations\": {\"%s\": \"%s\"}" , annotationKey , "true" )
newResource := strings . ReplaceAll ( testResource , "\"annotations\": {}" , annotationNew )
policyContext := buildContext ( t , testPolicyGood , testResource , testResource )
2022-05-05 14:06:18 -07:00
2023-02-07 17:51:25 +01:00
hasChanged := internal . HasImageVerifiedAnnotationChanged ( policyContext , logging . GlobalLogger ( ) )
2022-04-27 08:09:52 -07:00
assert . Equal ( t , hasChanged , false )
policyContext = buildContext ( t , testPolicyGood , newResource , testResource )
2023-02-07 17:51:25 +01:00
hasChanged = internal . HasImageVerifiedAnnotationChanged ( policyContext , logging . GlobalLogger ( ) )
2022-04-27 08:09:52 -07:00
assert . Equal ( t , hasChanged , true )
annotationOld := fmt . Sprintf ( "\"annotations\": {\"%s\": \"%s\"}" , annotationKey , "false" )
oldResource := strings . ReplaceAll ( testResource , "\"annotations\": {}" , annotationOld )
policyContext = buildContext ( t , testPolicyGood , newResource , oldResource )
2023-02-07 17:51:25 +01:00
hasChanged = internal . HasImageVerifiedAnnotationChanged ( policyContext , logging . GlobalLogger ( ) )
2022-04-27 08:09:52 -07:00
assert . Equal ( t , hasChanged , true )
}
func Test_MarkImageVerified ( t * testing . T ) {
2022-05-05 14:06:18 -07:00
image := "ghcr.io/jimbugwadia/pause2:latest"
cosign . ClearMock ( )
policyContext := buildContext ( t , testPolicyGood , testResource , "" )
2022-08-12 03:06:14 -07:00
err := cosign . SetMock ( image , attestationPayloads )
2022-05-05 14:06:18 -07:00
assert . NilError ( t , err )
2022-04-27 08:09:52 -07:00
2023-02-03 06:01:11 +01:00
engineResponse , verifiedImages := testVerifyAndPatchImages ( context . TODO ( ) , registryclient . NewOrDie ( ) , nil , policyContext , cfg )
2022-05-05 14:06:18 -07:00
assert . Assert ( t , engineResponse != nil )
assert . Equal ( t , len ( engineResponse . PolicyResponse . Rules ) , 1 )
2023-01-30 12:41:09 +01:00
assert . Equal ( t , engineResponse . PolicyResponse . Rules [ 0 ] . Status , engineapi . RuleStatusPass )
2022-04-27 08:09:52 -07:00
2022-05-05 14:06:18 -07:00
assert . Assert ( t , verifiedImages != nil )
assert . Assert ( t , verifiedImages . Data != nil )
assert . Equal ( t , len ( verifiedImages . Data ) , 1 )
2023-02-02 11:58:34 +01:00
assert . Equal ( t , verifiedImages . IsVerified ( image ) , true )
2022-05-01 16:02:49 -07:00
2022-10-02 20:45:03 +01:00
patches , err := verifiedImages . Patches ( false , logging . GlobalLogger ( ) )
2022-05-01 16:02:49 -07:00
assert . NilError ( t , err )
2022-05-05 14:06:18 -07:00
assert . Equal ( t , len ( patches ) , 2 )
2022-05-01 16:02:49 -07:00
2022-12-12 07:20:20 -08:00
resource := testApplyPatches ( t , patches )
2022-05-05 14:06:18 -07:00
patchedAnnotations := resource . GetAnnotations ( )
assert . Equal ( t , len ( patchedAnnotations ) , 1 )
2022-04-27 08:09:52 -07:00
2023-02-02 11:58:34 +01:00
json := patchedAnnotations [ engineapi . ImageVerifyAnnotationKey ]
2022-05-05 14:06:18 -07:00
assert . Assert ( t , json != "" )
2022-10-02 20:45:03 +01:00
verified , err := isImageVerified ( resource , image , logging . GlobalLogger ( ) )
2022-05-05 14:06:18 -07:00
assert . NilError ( t , err )
assert . Equal ( t , verified , true )
2022-04-27 08:09:52 -07:00
}
2022-12-12 07:20:20 -08:00
func testApplyPatches ( t * testing . T , patches [ ] [ ] byte ) unstructured . Unstructured {
2022-05-05 14:06:18 -07:00
patchedResource , err := utils . ApplyPatches ( [ ] byte ( testResource ) , patches )
2022-04-27 08:09:52 -07:00
assert . NilError ( t , err )
assert . Assert ( t , patchedResource != nil )
u := unstructured . Unstructured { }
err = u . UnmarshalJSON ( patchedResource )
assert . NilError ( t , err )
return u
}
2022-08-12 03:06:14 -07:00
func Test_ParsePEMDelimited ( t * testing . T ) {
testPEMPolicy := ` {
"apiVersion" : "kyverno.io/v1" ,
"kind" : "Policy" ,
"metadata" : {
"name" : "check-image"
} ,
"spec" : {
"validationFailureAction" : "enforce" ,
"background" : false ,
"webhookTimeoutSeconds" : 30 ,
"failurePolicy" : "Fail" ,
"rules" : [
{
"name" : "check-image" ,
"match" : {
"any" : [
{
"resources" : {
"kinds" : [
"Pod"
]
}
}
]
} ,
"verifyImages" : [
{
"imageReferences" : [
"*"
] ,
"attestors" : [
{
"count" : 1 ,
"entries" : [
{
"keys" : {
"publicKeys" : "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEfVMHGmFK4OgVqhy36KZ7a3r4R4/o\nCwaCVvXZV4ZULFbkFZ0IodGqKqcVmgycnoj7d8TpKpAUVNF8kKh90ewH3A==\n-----END PUBLIC KEY-----\n-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE0f1W0XigyPFbX8Xq3QmkbL9gDFTf\nRfc8jF7UadBcwKxiyvPSOKZn+igQfXzpNjrwPSZ58JGvF4Fs8BB3fSRP2g==\n-----END PUBLIC KEY-----"
}
}
]
}
]
}
]
}
]
}
} `
image := "ghcr.io/jimbugwadia/pause2:latest"
cosign . ClearMock ( )
policyContext := buildContext ( t , testPEMPolicy , testResource , "" )
err := cosign . SetMock ( image , signaturePayloads )
assert . NilError ( t , err )
2023-02-03 06:01:11 +01:00
engineResponse , verifiedImages := testVerifyAndPatchImages ( context . TODO ( ) , registryclient . NewOrDie ( ) , nil , policyContext , cfg )
2022-08-12 03:06:14 -07:00
assert . Assert ( t , engineResponse != nil )
assert . Equal ( t , len ( engineResponse . PolicyResponse . Rules ) , 1 )
2023-01-30 12:41:09 +01:00
assert . Equal ( t , engineResponse . PolicyResponse . Rules [ 0 ] . Status , engineapi . RuleStatusPass )
2022-08-12 03:06:14 -07:00
assert . Assert ( t , verifiedImages != nil )
assert . Assert ( t , verifiedImages . Data != nil )
assert . Equal ( t , len ( verifiedImages . Data ) , 1 )
2023-02-02 11:58:34 +01:00
assert . Equal ( t , verifiedImages . IsVerified ( image ) , true )
2022-08-12 03:06:14 -07:00
}