mirror of
https://github.com/kyverno/kyverno.git
synced 2025-03-29 10:55:05 +00:00
check for multiple variables in a expression & serviceAccount variables (#610)
* check for multiple variables in a expression & serviceAccount variables * update the regex matcher
This commit is contained in:
parent
af824f28b0
commit
cadd8f6b1b
6 changed files with 131 additions and 30 deletions
|
@ -26,8 +26,10 @@ func ContainsUserInfo(policy kyverno.ClusterPolicy) error {
|
|||
// - validate.pattern
|
||||
// - validate.anyPattern[*]
|
||||
// variables to filter
|
||||
// - request.userInfo
|
||||
filterVars := []string{"request.userInfo*"}
|
||||
// - request.userInfo*
|
||||
// - serviceAccountName
|
||||
// - serviceAccountNamespace
|
||||
filterVars := []string{"request.userInfo*", "serviceAccountName", "serviceAccountNamespace"}
|
||||
for condIdx, condition := range rule.Conditions {
|
||||
if err := variables.CheckVariables(condition.Key, filterVars, "/"); err != nil {
|
||||
return fmt.Errorf("path: spec/rules[%d]/condition[%d]/key%s", idx, condIdx, err)
|
||||
|
|
|
@ -1434,3 +1434,81 @@ func Test_BackGroundUserInfo_validate_anyPattern(t *testing.T) {
|
|||
t.Error("Incorrect Path")
|
||||
}
|
||||
}
|
||||
|
||||
func Test_BackGroundUserInfo_validate_anyPattern_multiple_var(t *testing.T) {
|
||||
var err error
|
||||
rawPolicy := []byte(`
|
||||
{
|
||||
"apiVersion": "kyverno.io/v1",
|
||||
"kind": "ClusterPolicy",
|
||||
"metadata": {
|
||||
"name": "disallow-root-user"
|
||||
},
|
||||
"spec": {
|
||||
"rules": [
|
||||
{
|
||||
"name": "validate.anyPattern",
|
||||
"validate": {
|
||||
"anyPattern": [
|
||||
{
|
||||
"var1": "temp"
|
||||
},
|
||||
{
|
||||
"var1": "{{request.userInfo}}-{{temp}}"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
} `)
|
||||
var policy *kyverno.ClusterPolicy
|
||||
err = json.Unmarshal(rawPolicy, &policy)
|
||||
assert.NilError(t, err)
|
||||
|
||||
err = ContainsUserInfo(*policy)
|
||||
|
||||
if err.Error() != "path: spec/rules[0]/validate/anyPattern[1]/var1/{{request.userInfo}}-{{temp}}" {
|
||||
t.Log(err)
|
||||
t.Error("Incorrect Path")
|
||||
}
|
||||
}
|
||||
|
||||
func Test_BackGroundUserInfo_validate_anyPattern_serviceAccount(t *testing.T) {
|
||||
var err error
|
||||
rawPolicy := []byte(`
|
||||
{
|
||||
"apiVersion": "kyverno.io/v1",
|
||||
"kind": "ClusterPolicy",
|
||||
"metadata": {
|
||||
"name": "disallow-root-user"
|
||||
},
|
||||
"spec": {
|
||||
"rules": [
|
||||
{
|
||||
"name": "validate.anyPattern",
|
||||
"validate": {
|
||||
"anyPattern": [
|
||||
{
|
||||
"var1": "temp"
|
||||
},
|
||||
{
|
||||
"var1": "{{serviceAccountName}}"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
} `)
|
||||
var policy *kyverno.ClusterPolicy
|
||||
err = json.Unmarshal(rawPolicy, &policy)
|
||||
assert.NilError(t, err)
|
||||
|
||||
err = ContainsUserInfo(*policy)
|
||||
|
||||
if err.Error() != "path: spec/rules[0]/validate/anyPattern[1]/var1/{{serviceAccountName}}" {
|
||||
t.Log(err)
|
||||
t.Error("Incorrect Path")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,25 +13,27 @@ import (
|
|||
func ValidateVariables(ctx context.EvalInterface, pattern interface{}) string {
|
||||
var pathsNotPresent []string
|
||||
variableList := extractVariables(pattern)
|
||||
for i := 0; i < len(variableList)-1; i = i + 2 {
|
||||
p := variableList[i+1]
|
||||
glog.V(3).Infof("validating variables %s", p)
|
||||
val, err := ctx.Query(p)
|
||||
// reference path is not present
|
||||
if err == nil && val == nil {
|
||||
pathsNotPresent = append(pathsNotPresent, p)
|
||||
for _, variable := range variableList {
|
||||
if len(variable) == 2 {
|
||||
varName := variable[0]
|
||||
varValue := variable[1]
|
||||
glog.V(3).Infof("validating variable %s", varName)
|
||||
val, err := ctx.Query(varValue)
|
||||
if err == nil && val == nil {
|
||||
// path is not present, returns nil interface
|
||||
pathsNotPresent = append(pathsNotPresent, varValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(variableList) != 0 && len(pathsNotPresent) != 0 {
|
||||
if len(pathsNotPresent) != 0 {
|
||||
return strings.Join(pathsNotPresent, ";")
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
// extractVariables extracts variables in the pattern
|
||||
func extractVariables(pattern interface{}) []string {
|
||||
func extractVariables(pattern interface{}) [][]string {
|
||||
switch typedPattern := pattern.(type) {
|
||||
case map[string]interface{}:
|
||||
return extractMap(typedPattern)
|
||||
|
@ -44,8 +46,8 @@ func extractVariables(pattern interface{}) []string {
|
|||
}
|
||||
}
|
||||
|
||||
func extractMap(patternMap map[string]interface{}) []string {
|
||||
var variableList []string
|
||||
func extractMap(patternMap map[string]interface{}) [][]string {
|
||||
var variableList [][]string
|
||||
|
||||
for _, patternElement := range patternMap {
|
||||
if vars := extractVariables(patternElement); vars != nil {
|
||||
|
@ -55,8 +57,8 @@ func extractMap(patternMap map[string]interface{}) []string {
|
|||
return variableList
|
||||
}
|
||||
|
||||
func extractArray(patternList []interface{}) []string {
|
||||
var variableList []string
|
||||
func extractArray(patternList []interface{}) [][]string {
|
||||
var variableList [][]string
|
||||
|
||||
for _, patternElement := range patternList {
|
||||
if vars := extractVariables(patternElement); vars != nil {
|
||||
|
@ -66,17 +68,22 @@ func extractArray(patternList []interface{}) []string {
|
|||
return variableList
|
||||
}
|
||||
|
||||
func extractValue(valuePattern string) []string {
|
||||
func extractValue(valuePattern string) [][]string {
|
||||
operatorVariable := getOperator(valuePattern)
|
||||
variable := valuePattern[len(operatorVariable):]
|
||||
return extractValueVariable(variable)
|
||||
}
|
||||
|
||||
func extractValueVariable(valuePattern string) []string {
|
||||
// returns multiple variable match groups
|
||||
func extractValueVariable(valuePattern string) [][]string {
|
||||
variableRegex := regexp.MustCompile(variableRegex)
|
||||
groups := variableRegex.FindStringSubmatch(valuePattern)
|
||||
if len(groups)%2 != 0 {
|
||||
groups := variableRegex.FindAllStringSubmatch(valuePattern, -1)
|
||||
if len(groups) == 0 {
|
||||
// no variables
|
||||
return nil
|
||||
}
|
||||
// group[*] <- all the matches
|
||||
// group[*][0] <- group match
|
||||
// group[*][1] <- group capture value
|
||||
return groups
|
||||
}
|
||||
|
|
|
@ -40,7 +40,9 @@ func Test_ExtractVariables(t *testing.T) {
|
|||
json.Unmarshal(patternRaw, &pattern)
|
||||
|
||||
vars := extractVariables(pattern)
|
||||
result := []string{"{{request.userInfo.username}}", "request.userInfo.username", "{{request.object.metadata.name}}", "request.object.metadata.name"}
|
||||
|
||||
result := [][]string{[]string{"{{request.userInfo.username}}", "request.userInfo.username"},
|
||||
[]string{"{{request.object.metadata.name}}", "request.object.metadata.name"}}
|
||||
|
||||
assert.Assert(t, len(vars) == len(result), fmt.Sprintf("result does not match, var: %s", vars))
|
||||
}
|
||||
|
|
|
@ -110,17 +110,18 @@ func getValues(ctx context.EvalInterface, groups [][]string) map[string]interfac
|
|||
for _, group := range groups {
|
||||
if len(group) == 2 {
|
||||
// 0th is string
|
||||
// 1st is the capture group
|
||||
variable, err := ctx.Query(group[1])
|
||||
varName := group[0]
|
||||
varValue := group[1]
|
||||
variable, err := ctx.Query(varValue)
|
||||
if err != nil {
|
||||
glog.V(4).Infof("variable substitution failed for query %s: %v", group[0], err)
|
||||
subs[group[0]] = emptyInterface
|
||||
glog.V(4).Infof("variable substitution failed for query %s: %v", varName, err)
|
||||
subs[varName] = emptyInterface
|
||||
continue
|
||||
}
|
||||
if variable == nil {
|
||||
subs[group[0]] = emptyInterface
|
||||
subs[varName] = emptyInterface
|
||||
} else {
|
||||
subs[group[0]] = variable
|
||||
subs[varName] = variable
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,11 +50,22 @@ func checkValue(valuePattern string, variables []string, path string) error {
|
|||
|
||||
func checkValueVariable(valuePattern string, variables []string) bool {
|
||||
variableRegex := regexp.MustCompile(variableRegex)
|
||||
groups := variableRegex.FindStringSubmatch(valuePattern)
|
||||
if len(groups) < 2 {
|
||||
groups := variableRegex.FindAllStringSubmatch(valuePattern, -1)
|
||||
if len(groups) == 0 {
|
||||
// no variables
|
||||
return false
|
||||
}
|
||||
return variablePatternSearch(groups[1], variables)
|
||||
// if variables are defined, check against the list of variables to be filtered
|
||||
for _, group := range groups {
|
||||
if len(group) == 2 {
|
||||
// group[0] -> {{variable}}
|
||||
// group[1] -> variable
|
||||
if variablePatternSearch(group[1], variables) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func variablePatternSearch(pattern string, regexs []string) bool {
|
||||
|
|
Loading…
Add table
Reference in a new issue