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:
@@ -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
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user