Skip to content

Commit

Permalink
Merge pull request #1442 from zregvart/issue/1362
Browse files Browse the repository at this point in the history
Move Rego builtins to internal/rego sub-packages
  • Loading branch information
lcarva authored Mar 14, 2024
2 parents 5e16346 + 66465cc commit 274643f
Show file tree
Hide file tree
Showing 8 changed files with 303 additions and 203 deletions.
23 changes: 23 additions & 0 deletions internal/rego/builtins.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Copyright The Enterprise Contract Contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0

package rego

import (
_ "github.com/enterprise-contract/ec-cli/internal/rego/oci"
_ "github.com/enterprise-contract/ec-cli/internal/rego/purl"
_ "github.com/enterprise-contract/ec-cli/internal/rego/sigstore"
)
123 changes: 1 addition & 122 deletions internal/evaluator/rego.go → internal/rego/oci/oci.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
// when an error is encountered. If they did return an error, opa would exit abruptly and it would
// not produce a report of which policy rules succeeded/failed.

package evaluator
package oci

import (
"bytes"
Expand All @@ -34,7 +34,6 @@ import (
"github.com/open-policy-agent/opa/ast"
"github.com/open-policy-agent/opa/rego"
"github.com/open-policy-agent/opa/types"
"github.com/package-url/packageurl-go"
log "github.com/sirupsen/logrus"

"github.com/enterprise-contract/ec-cli/internal/fetchers/oci"
Expand All @@ -43,8 +42,6 @@ import (
const (
ociBlobName = "ec.oci.blob"
ociImageManifestName = "ec.oci.image_manifest"
purlIsValidName = "ec.purl.is_valid"
purlParseName = "ec.purl.parse"
)

func registerOCIBlob() {
Expand Down Expand Up @@ -150,80 +147,6 @@ func registerOCIImageManifest() {
})
}

func registerPURLIsValid() {
decl := rego.Function{
Name: purlIsValidName,
Decl: types.NewFunction(
types.Args(
types.Named("purl", types.S).Description("the PURL"),
),
types.Named("result", types.S).Description("PURL validity"),
),
// As per the documentation, enable memoization to ensure function evaluation is
// deterministic.
Memoize: true,
Nondeterministic: false,
}

rego.RegisterBuiltin1(&decl, purlIsValid)
// Due to https://github.com/open-policy-agent/opa/issues/6449, we cannot set a description for
// the custom function through the call above. As a workaround we re-register the function with
// a declaration that does include the description.
ast.RegisterBuiltin(&ast.Builtin{
Name: decl.Name,
Description: "Determine whether or not a given PURL is valid.",
Decl: decl.Decl,
Nondeterministic: decl.Nondeterministic,
})
}

func registerPURLParse() {
decl := rego.Function{
Name: purlParseName,
Decl: types.NewFunction(
types.Args(
types.Named("purl", types.S).Description("the PURL"),
),
types.Named("object", types.NewObject(
[]*types.StaticProperty{
// Specifying the properties like this ensure the compiler catches typos when
// evaluating rego functions.
{Key: "type", Value: types.S},
{Key: "namespace", Value: types.S},
{Key: "name", Value: types.S},
{Key: "version", Value: types.S},
{Key: "qualifiers", Value: types.NewArray(
nil, types.NewObject(
[]*types.StaticProperty{
{Key: "key", Value: types.S},
{Key: "value", Value: types.S},
},
nil,
),
)},
{Key: "subpath", Value: types.S},
},
nil,
)).Description("the parsed PURL object"),
),
// As per the documentation, enable memoization to ensure function evaluation is
// deterministic.
Memoize: true,
Nondeterministic: false,
}

rego.RegisterBuiltin1(&decl, purlParse)
// Due to https://github.com/open-policy-agent/opa/issues/6449, we cannot set a description for
// the custom function through the call above. As a workaround we re-register the function with
// a declaration that does include the description.
ast.RegisterBuiltin(&ast.Builtin{
Name: decl.Name,
Description: "Parse a valid PURL into an object.",
Decl: decl.Decl,
Nondeterministic: decl.Nondeterministic,
})
}

const maxBytes = 10 * 1024 * 1024 // 10 MB

func ociBlob(bctx rego.BuiltinContext, a *ast.Term) (*ast.Term, error) {
Expand Down Expand Up @@ -392,51 +315,7 @@ func newAnnotationsTerm(annotations map[string]string) *ast.Term {
return ast.ObjectTerm(annotationTerms...)
}

func purlIsValid(bctx rego.BuiltinContext, a *ast.Term) (*ast.Term, error) {
uri, ok := a.Value.(ast.String)
if !ok {
return ast.BooleanTerm(false), nil
}
_, err := packageurl.FromString(string(uri))
if err != nil {
log.Errorf("Parsing PURL %s failed: %s", uri, err)
return ast.BooleanTerm(false), nil
}
return ast.BooleanTerm(true), nil
}

func purlParse(bctx rego.BuiltinContext, a *ast.Term) (*ast.Term, error) {
uri, ok := a.Value.(ast.String)
if !ok {
return nil, nil
}
instance, err := packageurl.FromString(string(uri))
if err != nil {
log.Errorf("Parsing PURL %s failed: %s", uri, err)
return nil, nil
}

qualifiers := ast.NewArray()
for _, q := range instance.Qualifiers {
o := ast.NewObject(
ast.Item(ast.StringTerm("key"), ast.StringTerm(q.Key)),
ast.Item(ast.StringTerm("value"), ast.StringTerm(q.Value)),
)
qualifiers = qualifiers.Append(ast.NewTerm(o))
}
return ast.ObjectTerm(
ast.Item(ast.StringTerm("type"), ast.StringTerm(instance.Type)),
ast.Item(ast.StringTerm("namespace"), ast.StringTerm(instance.Namespace)),
ast.Item(ast.StringTerm("name"), ast.StringTerm(instance.Name)),
ast.Item(ast.StringTerm("version"), ast.StringTerm(instance.Version)),
ast.Item(ast.StringTerm("qualifiers"), ast.NewTerm(qualifiers)),
ast.Item(ast.StringTerm("subpath"), ast.StringTerm(instance.Subpath)),
), nil
}

func init() {
registerOCIBlob()
registerOCIImageManifest()
registerPURLIsValid()
registerPURLParse()
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

//go:build unit

package evaluator
package oci

import (
"context"
Expand Down Expand Up @@ -281,88 +281,10 @@ func TestOCIImageManifest(t *testing.T) {
}
}

func TestPURLIsValid(t *testing.T) {
cases := []struct {
name string
uri *ast.Term
expected bool
}{
{
name: "success",
uri: ast.StringTerm("pkg:rpm/fedora/[email protected]?arch=i386&distro=fedora-25"),
expected: true,
},
{
name: "unexpected uri type",
uri: ast.IntNumberTerm(42),
expected: false,
},
{
name: "malformed PURL string",
uri: ast.StringTerm("pkg::rpm//fedora/curl7.50.3-1.fc25?arch=i386&distro=fedora-"),
expected: false,
},
}

for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
ctx := context.Background()
bctx := rego.BuiltinContext{Context: ctx}

isValid, err := purlIsValid(bctx, c.uri)
require.NoError(t, err)
require.NotNil(t, isValid)
require.Equal(t, isValid, ast.BooleanTerm(c.expected))
})
}
}

func TestPURLParse(t *testing.T) {
cases := []struct {
name string
uri *ast.Term
err bool
}{
{
name: "success",
uri: ast.StringTerm("pkg:rpm/fedora/[email protected]?arch=i386&distro=fedora-25"),
},
{
name: "unexpected uri type",
uri: ast.IntNumberTerm(42),
err: true,
},
{
name: "malformed PURL string",
uri: ast.StringTerm("pkg::rpm//fedora/curl7.50.3-1.fc25?arch=i386&distro=fedora-"),
err: true,
},
}

for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
ctx := context.Background()
bctx := rego.BuiltinContext{Context: ctx}

instance, err := purlParse(bctx, c.uri)
require.NoError(t, err)
if c.err {
require.Nil(t, instance)
} else {
require.NotNil(t, instance)
data := instance.Get(ast.StringTerm("type")).Value
require.Equal(t, ast.String("rpm"), data)
}
})
}
}

func TestFunctionsRegistered(t *testing.T) {
names := []string{
ociBlobName,
ociImageManifestName,
purlIsValidName,
purlParseName,
}
for _, name := range names {
t.Run(name, func(t *testing.T) {
Expand Down
Loading

0 comments on commit 274643f

Please sign in to comment.