policy: match parent unknown keys in collectUnknowns

Allow child refs from partial evaluation to match allowed parent keys on
path boundaries and return canonical unknowns for metadata resolution.

Add tests for parent-child matching and boundary safety cases.

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
This commit is contained in:
Tonis Tiigi
2026-02-26 17:12:18 -08:00
parent 73ea669465
commit 1c4a436a0d
2 changed files with 73 additions and 2 deletions
+44
View File
@@ -31,6 +31,9 @@ func TestTrimKey(t *testing.T) {
{"input.git.tag[0]", "git.tag"},
{"input.image.provenance.materials[0].image.hasProvenance", "image.provenance.materials[0].image.hasProvenance"},
{"image.provenance.materials[0].image.labels", "image.provenance.materials[0].image.labels"},
{"input.image.provenance.materials[0].image.provenance.predicateType", "image.provenance.materials[0].image.provenance.predicateType"},
{"input.image.provenance.materials[0].image.signatures[0].signer.certificateIssuer", "image.provenance.materials[0].image.signatures[0].signer.certificateIssuer"},
{"input.image.provenance.materials[10].image.hasProvenance", "image.provenance.materials[10].image.hasProvenance"},
{"a.b.c", "a.b"},
}
@@ -61,6 +64,47 @@ func TestCollectUnknowns(t *testing.T) {
require.ElementsMatch(t, []string{"image.signatures", "image.provenance.materials[0].image.hasProvenance"}, filtered)
}
func TestCollectUnknownsParentAllowedMatchesChildRef(t *testing.T) {
mod, err := ast.ParseModule("x.rego", `
package x
p if {
input.image.provenance.materials[0].image.provenance.predicateType != ""
input.image.provenance.materials[0].image.signatures[0].signer.certificateIssuer != ""
input.image.provenance.materials[0].git.tag.name != ""
input.foo.bar != ""
input.image.provenance.materials[10].image.hasProvenance
}
`)
require.NoError(t, err)
filtered := collectUnknowns([]*ast.Module{mod}, []string{
"input.image.provenance.materials[0].image.provenance",
"input.image.provenance.materials[0].image.signatures",
"input.image.provenance.materials[0].git.tag",
"input.foo.b",
"input.image.provenance.materials[1].image",
})
require.ElementsMatch(t, []string{
"image.provenance.materials[0].image.provenance",
"image.provenance.materials[0].image.signatures",
"image.provenance.materials[0].git.tag",
}, filtered)
}
func TestMatchAllowedOrParentBoundary(t *testing.T) {
allowed := map[string]struct{}{
"foo.b": {},
"image.provenance.materials[1].image": {},
}
_, ok := matchAllowedOrParent("foo.bar", allowed)
require.False(t, ok)
_, ok = matchAllowedOrParent("image.provenance.materials[10].image.hasProvenance", allowed)
require.False(t, ok)
}
func TestRuntimeUnknownInputRefs(t *testing.T) {
require.Nil(t, runtimeUnknownInputRefs(nil))
require.Nil(t, runtimeUnknownInputRefs(&state{}))
+29 -2
View File
@@ -808,15 +808,42 @@ func collectUnknowns(mods []*ast.Module, allowed []string) []string {
}
filtered := make([]string, 0, len(out))
filteredSeen := map[string]struct{}{}
for _, k := range out {
if _, ok := valid[k]; ok {
filtered = append(filtered, k)
matched, ok := matchAllowedOrParent(k, valid)
if !ok {
continue
}
if _, exists := filteredSeen[matched]; exists {
continue
}
filteredSeen[matched] = struct{}{}
filtered = append(filtered, matched)
}
return filtered
}
func matchAllowedOrParent(key string, allowed map[string]struct{}) (string, bool) {
if _, ok := allowed[key]; ok {
return key, true
}
// Find the nearest parent on a component boundary.
for i := len(key) - 1; i >= 0; i-- {
switch key[i] {
case '.', '[':
if i == 0 {
continue
}
candidate := key[:i]
if _, ok := allowed[candidate]; ok {
return candidate, true
}
}
}
return "", false
}
func runtimeUnknownInputRefs(st *state) []string {
if st == nil || len(st.Unknowns) == 0 {
return nil