policy: update unknown keys normalization

Make the code more unified between validation and
test command. Normalize to key without the input prefix.

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
This commit is contained in:
Tonis Tiigi
2026-02-09 18:27:35 -08:00
parent df542b3f80
commit 4e16277937
4 changed files with 104 additions and 29 deletions
+14 -15
View File
@@ -311,7 +311,7 @@ func runPolicyTest(ctx context.Context, policyModules map[string]*ast.Module, te
result.Allow = allow
result.DenyMessages = deny
missing := missingInputRefs(policyPackageModules, effectiveInput)
missing := missingInputRefs(policyPackageModules, effectiveInput, runtimeUnknownInputRefs(testState), runtimeUnknownInputRefs(decisionState))
result.MissingInput = uniqueSortedStrings(missing)
result.MetadataNeeded = summarizeMetadataRequests(result.MissingInput)
@@ -534,7 +534,7 @@ func hasEnv(env Env) bool {
func filterResolvableMissing(missing []string) []string {
out := make([]string, 0, len(missing))
for _, m := range missing {
if strings.HasPrefix(m, "input.image.") || strings.HasPrefix(m, "input.git.") {
if strings.HasPrefix(m, "image.") || strings.HasPrefix(m, "git.") {
out = append(out, m)
}
}
@@ -595,24 +595,27 @@ func modulesForPackage(modules map[string]*ast.Module, pkgPath string) []*ast.Mo
return out
}
func missingInputRefs(mods []*ast.Module, input *Input) []string {
func missingInputRefs(mods []*ast.Module, input *Input, extraRefs ...[]string) []string {
if len(mods) == 0 {
return nil
}
inputMap := normalizeInput(input)
refs := collectUnknowns(mods, nil)
for _, er := range extraRefs {
refs = append(refs, er...)
}
seen := map[string]struct{}{}
missing := make([]string, 0, len(refs))
for _, ref := range refs {
key := strings.TrimPrefix(ref, "input.")
if key == ref {
continue
}
key = trimKey(key)
for _, key := range refs {
if key == "" {
continue
}
if _, ok := seen[key]; ok {
continue
}
seen[key] = struct{}{}
if !inputHasPath(inputMap, strings.Split(key, ".")) {
missing = append(missing, "input."+key)
missing = append(missing, key)
}
}
return missing
@@ -703,11 +706,7 @@ func summarizeMetadataRequests(missing []string) []string {
return nil
}
req := &gwpb.ResolveSourceMetaRequest{}
trimmed := make([]string, 0, len(missing))
for _, m := range missing {
trimmed = append(trimmed, strings.TrimPrefix(m, "input."))
}
if err := AddUnknowns(req, trimmed); err != nil {
if err := AddUnknowns(req, missing); err != nil {
return nil
}
var out []string
+53
View File
@@ -3,6 +3,7 @@ package policy
import (
"testing"
"github.com/open-policy-agent/opa/v1/ast"
"github.com/stretchr/testify/require"
)
@@ -18,12 +19,16 @@ func TestTrimKey(t *testing.T) {
// one separator → stays as-is
{"git.tag", "git.tag"},
{"git[tag", "git[tag"},
{"input.git.tag", "git.tag"},
{"input.git[tag", "git[tag"},
// multiple separators → cut before second one
{"git.tag.author", "git.tag"},
{"git.tag.author.email", "git.tag"},
{"git.tag[0][1]", "git.tag"},
{"git.tag[0]", "git.tag"},
{"input.git.tag.author", "git.tag"},
{"input.git.tag[0]", "git.tag"},
{"a.b.c", "a.b"},
}
@@ -34,3 +39,51 @@ func TestTrimKey(t *testing.T) {
})
}
}
func TestCollectUnknowns(t *testing.T) {
mod, err := ast.ParseModule("x.rego", `
package x
p if {
input.git.tag[0].author == "a"
input.image.signatures[_].signer.certificateIssuer != ""
data.foo.bar == 1
}
`)
require.NoError(t, err)
all := collectUnknowns([]*ast.Module{mod}, nil)
require.ElementsMatch(t, []string{"git.tag", "image.signatures"}, all)
filtered := collectUnknowns([]*ast.Module{mod}, []string{"input.image.signatures"})
require.Equal(t, []string{"image.signatures"}, filtered)
}
func TestRuntimeUnknownInputRefs(t *testing.T) {
require.Nil(t, runtimeUnknownInputRefs(nil))
require.Nil(t, runtimeUnknownInputRefs(&state{}))
st := &state{
Unknowns: map[string]struct{}{
funcVerifyGitSignature: {},
},
}
require.Equal(t, []string{"git.commit"}, runtimeUnknownInputRefs(st))
}
func TestMissingInputRefsWithRuntimeUnknowns(t *testing.T) {
mod, err := ast.ParseModule("x.rego", `
package x
p if {
input.git.ref != ""
}
`)
require.NoError(t, err)
in := &Input{
Git: &Git{
Ref: "refs/heads/main",
},
}
missing := missingInputRefs([]*ast.Module{mod}, in, []string{"git.commit"})
require.Equal(t, []string{"git.commit"}, missing)
}
+28 -13
View File
@@ -224,9 +224,7 @@ func (p *Policy) CheckPolicy(ctx context.Context, req *policysession.CheckPolicy
return nil, nil, err
}
unk := collectUnknowns(pq.Support, unknowns)
if _, ok := st.Unknowns[funcVerifyGitSignature]; ok {
unk = append(unk, "input.git.commit")
}
unk = append(unk, runtimeUnknownInputRefs(st)...)
if len(unk) > 0 {
next := &gwpb.ResolveSourceMetaRequest{
@@ -621,14 +619,12 @@ func AddUnknowns(req *gwpb.ResolveSourceMetaRequest, unk []string) error {
func AddUnknownsWithLogger(logf func(logrus.Level, string), req *gwpb.ResolveSourceMetaRequest, unk []string) error {
unk2 := make([]string, 0, len(unk))
for _, u := range unk {
k := strings.TrimPrefix(u, "input.")
k = trimKey(k)
switch k {
switch u {
case "image", "git", "http", "local":
// parents are returned as unknowns for some reason, ignore
continue
default:
unk2 = append(unk2, k)
unk2 = append(unk2, u)
}
}
if len(unk2) == 0 {
@@ -680,8 +676,10 @@ func collectUnknowns(mods []*ast.Module, allowed []string) []string {
for _, mod := range mods {
ast.WalkRefs(mod, func(ref ast.Ref) bool {
if ref.HasPrefix(ast.InputRootRef) {
s := ref.String() // e.g. "input.request.path"
s = "input." + trimKey(strings.TrimPrefix(s, "input."))
s := trimKey(ref.String())
if s == "" {
return true
}
if _, ok := seen[s]; !ok {
seen[s] = struct{}{}
out = append(out, s)
@@ -696,6 +694,10 @@ func collectUnknowns(mods []*ast.Module, allowed []string) []string {
valid := map[string]struct{}{}
for _, k := range allowed {
k = trimKey(k)
if k == "" {
continue
}
valid[k] = struct{}{}
}
@@ -709,14 +711,25 @@ func collectUnknowns(mods []*ast.Module, allowed []string) []string {
return filtered
}
func runtimeUnknownInputRefs(st *state) []string {
if st == nil || len(st.Unknowns) == 0 {
return nil
}
var out []string
if _, ok := st.Unknowns[funcVerifyGitSignature]; ok {
out = append(out, "git.commit")
}
return out
}
func summarizeUnknownsForLog(unk []string) []string {
out := make([]string, 0, len(unk))
seen := map[string]struct{}{}
for _, u := range unk {
if strings.HasPrefix(u, "input.image.signatures") {
u = "input.image.signatures"
if strings.HasPrefix(u, "image.signatures") {
u = "image.signatures"
}
if u == "input.image" {
if u == "image" {
continue
}
if _, ok := seen[u]; ok {
@@ -730,7 +743,7 @@ func summarizeUnknownsForLog(unk []string) []string {
func hasHTTPUnknowns(unk []string) bool {
for _, u := range unk {
if strings.HasPrefix(u, "input.http.") {
if strings.HasPrefix(u, "http.") {
return true
}
}
@@ -738,6 +751,8 @@ func hasHTTPUnknowns(unk []string) bool {
}
func trimKey(s string) string {
s = strings.TrimPrefix(s, "input.")
const (
dot = '.'
sb = '['