Skip to content

Commit

Permalink
chore: add rego function to consume modules
Browse files Browse the repository at this point in the history
Signed-off-by: hectorj2f <[email protected]>
  • Loading branch information
hectorj2f committed Apr 22, 2022
1 parent 3c2084c commit 8a2f717
Show file tree
Hide file tree
Showing 3 changed files with 176 additions and 0 deletions.
4 changes: 4 additions & 0 deletions pkg/client/clientset/versioned/clientset.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

57 changes: 57 additions & 0 deletions pkg/cosign/rego/rego.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,20 @@ import (
"fmt"

"github.com/open-policy-agent/opa/rego"
"knative.dev/pkg/logging"
)

// The query below should meet the following requirements:
// * Provides no Bindings. Do not use a query that sets a variable, e.g. x := data.signature.allow
// * Queries for a single value.
const QUERY = "data.signature.allow"

// CosignRegoPackageName defines the expected package name of a provided rego module
const CosignRegoPackageName = "sigstore"

// CosignEvaluationRule defines the expected evaluation role of a provided rego module
const CosignEvaluationRule = "isCompliant"

func ValidateJSON(jsonBody []byte, entrypoints []string) []error {
ctx := context.Background()

Expand Down Expand Up @@ -73,3 +80,53 @@ func ValidateJSON(jsonBody []byte, entrypoints []string) []error {
}
return errs
}

// ValidateJSONWithModuleInput takes the body of the results to evaluate and the defined module
// in a policy to validate against the input data
func ValidateJSONWithModuleInput(jsonBody []byte, moduleInput string) error {
ctx := context.Background()
query := fmt.Sprintf("%s = data.%s.%s", CosignEvaluationRule, CosignRegoPackageName, CosignEvaluationRule)
module := fmt.Sprintf("%s.rego", CosignRegoPackageName)

r := rego.New(
rego.Query(query),
rego.Module(module, moduleInput))

evalQuery, err := r.PrepareForEval(ctx)
if err != nil {
return err
}

var input interface{}
dec := json.NewDecoder(bytes.NewBuffer(jsonBody))
dec.UseNumber()
if err := dec.Decode(&input); err != nil {
return err
}

rs, err := evalQuery.Eval(ctx, rego.EvalInput(input))
if err != nil {
return err
}

var errMsg string
for _, result := range rs {
if isCompliant, ok := result.Bindings[CosignEvaluationRule].(bool); !ok {
for _, expression := range result.Expressions {
errMsg += fmt.Sprintf("expression value, %v, is not true\n", expression)
}
} else if isCompliant {
logging.FromContext(ctx).Info("Validated policy is compliant")
return nil
} else if ok && !isCompliant {
return fmt.Errorf("result is not compliant for query '%s'", query)
}
}

// When len(rs) is 0, the result is undefined. This is a policy
// check failure.
if len(errMsg) == 0 {
return fmt.Errorf("result is undefined for query '%s'", query)
}
return nil
}
115 changes: 115 additions & 0 deletions pkg/cosign/rego/rego_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,3 +98,118 @@ func TestValidationJSON(t *testing.T) {
})
}
}

const attestationsJSONBody = `{
"authorityMatches": {
"keyatt": {
"signatures": null,
"attestations": {
"vuln-key": [
{
"subject": "PLACEHOLDER",
"issuer": "PLACEHOLDER"
}
]
}
},
"keysignature": {
"signatures": [
{
"subject": "PLACEHOLDER",
"issuer": "PLACEHOLDER"
}
],
"attestations": null
},
"keylessatt": {
"signatures": null,
"attestations": {
"custom-keyless": [
{
"subject": "PLACEHOLDER",
"issuer": "PLACEHOLDER"
}
]
}
},
"keylesssignature": {
"signatures": [
{
"subject": "PLACEHOLDER",
"issuer": "PLACEHOLDER"
}
],
"attestations": null
}
}
}`

func TestValidateJSONWithModuleInput(t *testing.T) {
cases := []struct {
name string
jsonBody string
policy string
pass bool
errorMsg string
}{
{
name: "passing policy attestations",
jsonBody: attestationsJSONBody,
policy: `
package sigstore
default isCompliant = false
isCompliant {
attestationsKeylessATT := input.authorityMatches.keylessatt.attestations
count(attestationsKeylessATT) == 1
attestationsKeyATT := input.authorityMatches.keyatt.attestations
count(attestationsKeyATT) == 1
keylessSignature := input.authorityMatches.keylesssignature.signatures
count(keylessSignature) == 1
keySignature := input.authorityMatches.keysignature.signatures
count(keySignature) == 1
}
`,
pass: true,
},
{
name: "not passing policy attestations",
jsonBody: attestationsJSONBody,
policy: `
package sigstore
default isCompliant = false
isCompliant {
attestationsKeylessATT := input.authorityMatches.keylessatt.attestations
count(attestationsKeylessATT) == 0
attestationsKeyATT := input.authorityMatches.keyatt.attestations
count(attestationsKeyATT) == 1
keylessSignature := input.authorityMatches.keylesssignature.signatures
count(keylessSignature) == 1
keySignature := input.authorityMatches.keysignature.signatures
count(keySignature) == 1
}
`,
pass: false,
errorMsg: "result is not compliant for query 'isCompliant = data.sigstore.isCompliant'",
},
}

for _, tt := range cases {
t.Run(tt.name, func(t *testing.T) {
if err := ValidateJSONWithModuleInput([]byte(tt.jsonBody), tt.policy); (err == nil) != tt.pass {
t.Fatalf("Unexpected result: %v", err)
} else if err != nil {
if fmt.Sprintf("%s", err) != tt.errorMsg {
t.Errorf("Expected error %q, got %q", tt.errorMsg, err)
}
}
})
}
}

0 comments on commit 8a2f717

Please sign in to comment.