mirror of
https://github.com/kyverno/kyverno.git
synced 2025-03-31 03:45:17 +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.pattern
|
||||||
// - validate.anyPattern[*]
|
// - validate.anyPattern[*]
|
||||||
// variables to filter
|
// variables to filter
|
||||||
// - request.userInfo
|
// - request.userInfo*
|
||||||
filterVars := []string{"request.userInfo*"}
|
// - serviceAccountName
|
||||||
|
// - serviceAccountNamespace
|
||||||
|
filterVars := []string{"request.userInfo*", "serviceAccountName", "serviceAccountNamespace"}
|
||||||
for condIdx, condition := range rule.Conditions {
|
for condIdx, condition := range rule.Conditions {
|
||||||
if err := variables.CheckVariables(condition.Key, filterVars, "/"); err != nil {
|
if err := variables.CheckVariables(condition.Key, filterVars, "/"); err != nil {
|
||||||
return fmt.Errorf("path: spec/rules[%d]/condition[%d]/key%s", idx, condIdx, err)
|
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")
|
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 {
|
func ValidateVariables(ctx context.EvalInterface, pattern interface{}) string {
|
||||||
var pathsNotPresent []string
|
var pathsNotPresent []string
|
||||||
variableList := extractVariables(pattern)
|
variableList := extractVariables(pattern)
|
||||||
for i := 0; i < len(variableList)-1; i = i + 2 {
|
for _, variable := range variableList {
|
||||||
p := variableList[i+1]
|
if len(variable) == 2 {
|
||||||
glog.V(3).Infof("validating variables %s", p)
|
varName := variable[0]
|
||||||
val, err := ctx.Query(p)
|
varValue := variable[1]
|
||||||
// reference path is not present
|
glog.V(3).Infof("validating variable %s", varName)
|
||||||
if err == nil && val == nil {
|
val, err := ctx.Query(varValue)
|
||||||
pathsNotPresent = append(pathsNotPresent, p)
|
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 strings.Join(pathsNotPresent, ";")
|
||||||
}
|
}
|
||||||
|
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// extractVariables extracts variables in the pattern
|
// extractVariables extracts variables in the pattern
|
||||||
func extractVariables(pattern interface{}) []string {
|
func extractVariables(pattern interface{}) [][]string {
|
||||||
switch typedPattern := pattern.(type) {
|
switch typedPattern := pattern.(type) {
|
||||||
case map[string]interface{}:
|
case map[string]interface{}:
|
||||||
return extractMap(typedPattern)
|
return extractMap(typedPattern)
|
||||||
|
@ -44,8 +46,8 @@ func extractVariables(pattern interface{}) []string {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func extractMap(patternMap map[string]interface{}) []string {
|
func extractMap(patternMap map[string]interface{}) [][]string {
|
||||||
var variableList []string
|
var variableList [][]string
|
||||||
|
|
||||||
for _, patternElement := range patternMap {
|
for _, patternElement := range patternMap {
|
||||||
if vars := extractVariables(patternElement); vars != nil {
|
if vars := extractVariables(patternElement); vars != nil {
|
||||||
|
@ -55,8 +57,8 @@ func extractMap(patternMap map[string]interface{}) []string {
|
||||||
return variableList
|
return variableList
|
||||||
}
|
}
|
||||||
|
|
||||||
func extractArray(patternList []interface{}) []string {
|
func extractArray(patternList []interface{}) [][]string {
|
||||||
var variableList []string
|
var variableList [][]string
|
||||||
|
|
||||||
for _, patternElement := range patternList {
|
for _, patternElement := range patternList {
|
||||||
if vars := extractVariables(patternElement); vars != nil {
|
if vars := extractVariables(patternElement); vars != nil {
|
||||||
|
@ -66,17 +68,22 @@ func extractArray(patternList []interface{}) []string {
|
||||||
return variableList
|
return variableList
|
||||||
}
|
}
|
||||||
|
|
||||||
func extractValue(valuePattern string) []string {
|
func extractValue(valuePattern string) [][]string {
|
||||||
operatorVariable := getOperator(valuePattern)
|
operatorVariable := getOperator(valuePattern)
|
||||||
variable := valuePattern[len(operatorVariable):]
|
variable := valuePattern[len(operatorVariable):]
|
||||||
return extractValueVariable(variable)
|
return extractValueVariable(variable)
|
||||||
}
|
}
|
||||||
|
|
||||||
func extractValueVariable(valuePattern string) []string {
|
// returns multiple variable match groups
|
||||||
|
func extractValueVariable(valuePattern string) [][]string {
|
||||||
variableRegex := regexp.MustCompile(variableRegex)
|
variableRegex := regexp.MustCompile(variableRegex)
|
||||||
groups := variableRegex.FindStringSubmatch(valuePattern)
|
groups := variableRegex.FindAllStringSubmatch(valuePattern, -1)
|
||||||
if len(groups)%2 != 0 {
|
if len(groups) == 0 {
|
||||||
|
// no variables
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
// group[*] <- all the matches
|
||||||
|
// group[*][0] <- group match
|
||||||
|
// group[*][1] <- group capture value
|
||||||
return groups
|
return groups
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,7 +40,9 @@ func Test_ExtractVariables(t *testing.T) {
|
||||||
json.Unmarshal(patternRaw, &pattern)
|
json.Unmarshal(patternRaw, &pattern)
|
||||||
|
|
||||||
vars := extractVariables(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))
|
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 {
|
for _, group := range groups {
|
||||||
if len(group) == 2 {
|
if len(group) == 2 {
|
||||||
// 0th is string
|
// 0th is string
|
||||||
// 1st is the capture group
|
varName := group[0]
|
||||||
variable, err := ctx.Query(group[1])
|
varValue := group[1]
|
||||||
|
variable, err := ctx.Query(varValue)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.V(4).Infof("variable substitution failed for query %s: %v", group[0], err)
|
glog.V(4).Infof("variable substitution failed for query %s: %v", varName, err)
|
||||||
subs[group[0]] = emptyInterface
|
subs[varName] = emptyInterface
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if variable == nil {
|
if variable == nil {
|
||||||
subs[group[0]] = emptyInterface
|
subs[varName] = emptyInterface
|
||||||
} else {
|
} 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 {
|
func checkValueVariable(valuePattern string, variables []string) bool {
|
||||||
variableRegex := regexp.MustCompile(variableRegex)
|
variableRegex := regexp.MustCompile(variableRegex)
|
||||||
groups := variableRegex.FindStringSubmatch(valuePattern)
|
groups := variableRegex.FindAllStringSubmatch(valuePattern, -1)
|
||||||
if len(groups) < 2 {
|
if len(groups) == 0 {
|
||||||
|
// no variables
|
||||||
return false
|
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 {
|
func variablePatternSearch(pattern string, regexs []string) bool {
|
||||||
|
|
Loading…
Add table
Reference in a new issue