1
0
Fork 0
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:
Shivkumar Dudhani 2020-01-13 18:56:11 -08:00 committed by GitHub
parent af824f28b0
commit cadd8f6b1b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 131 additions and 30 deletions

View file

@ -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)

View file

@ -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")
}
}

View file

@ -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
}

View file

@ -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))
}

View file

@ -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
}
}
}

View file

@ -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 {