kubernetes escaping_test 源码

  • 2022-09-18
  • 浏览 (213)

kubernetes escaping_test 代码

文件路径:/staging/src/k8s.io/apiextensions-apiserver/third_party/forked/celopenapi/model/escaping_test.go

/*
Copyright 2021 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package model

import (
	"fmt"
	"regexp"
	"testing"

	fuzz "github.com/google/gofuzz"
)

// TestEscaping tests that property names are escaped as expected.
func TestEscaping(t *testing.T) {
	cases := []struct {
		unescaped   string
		escaped     string
		unescapable bool
	}{
		// '.', '-', '/' and '__' are escaped since
		// CEL only allows identifiers of the form: [a-zA-Z_][a-zA-Z0-9_]*
		{unescaped: "a.a", escaped: "a__dot__a"},
		{unescaped: "a-a", escaped: "a__dash__a"},
		{unescaped: "a__a", escaped: "a__underscores__a"},
		{unescaped: "a.-/__a", escaped: "a__dot____dash____slash____underscores__a"},
		{unescaped: "a._a", escaped: "a__dot___a"},
		{unescaped: "a__.__a", escaped: "a__underscores____dot____underscores__a"},
		{unescaped: "a___a", escaped: "a__underscores___a"},
		{unescaped: "a____a", escaped: "a__underscores____underscores__a"},
		{unescaped: "a__dot__a", escaped: "a__underscores__dot__underscores__a"},
		{unescaped: "a__underscores__a", escaped: "a__underscores__underscores__underscores__a"},
		// CEL lexer RESERVED keywords must be escaped
		{unescaped: "true", escaped: "__true__"},
		{unescaped: "false", escaped: "__false__"},
		{unescaped: "null", escaped: "__null__"},
		{unescaped: "in", escaped: "__in__"},
		{unescaped: "as", escaped: "__as__"},
		{unescaped: "break", escaped: "__break__"},
		{unescaped: "const", escaped: "__const__"},
		{unescaped: "continue", escaped: "__continue__"},
		{unescaped: "else", escaped: "__else__"},
		{unescaped: "for", escaped: "__for__"},
		{unescaped: "function", escaped: "__function__"},
		{unescaped: "if", escaped: "__if__"},
		{unescaped: "import", escaped: "__import__"},
		{unescaped: "let", escaped: "__let__"},
		{unescaped: "loop", escaped: "__loop__"},
		{unescaped: "package", escaped: "__package__"},
		{unescaped: "namespace", escaped: "__namespace__"},
		{unescaped: "return", escaped: "__return__"},
		{unescaped: "var", escaped: "__var__"},
		{unescaped: "void", escaped: "__void__"},
		{unescaped: "while", escaped: "__while__"},
		// Not all property names are escapable
		{unescaped: "@", unescapable: true},
		{unescaped: "1up", unescapable: true},
		{unescaped: "?", unescapable: true},
		// CEL macro and function names do not need to be escaped because the parser keeps identifiers in a
		// different namespace than function and  macro names.
		{unescaped: "has", escaped: "has"},
		{unescaped: "all", escaped: "all"},
		{unescaped: "exists", escaped: "exists"},
		{unescaped: "exists_one", escaped: "exists_one"},
		{unescaped: "filter", escaped: "filter"},
		{unescaped: "size", escaped: "size"},
		{unescaped: "contains", escaped: "contains"},
		{unescaped: "startsWith", escaped: "startsWith"},
		{unescaped: "endsWith", escaped: "endsWith"},
		{unescaped: "matches", escaped: "matches"},
		{unescaped: "duration", escaped: "duration"},
		{unescaped: "timestamp", escaped: "timestamp"},
		{unescaped: "getDate", escaped: "getDate"},
		{unescaped: "getDayOfMonth", escaped: "getDayOfMonth"},
		{unescaped: "getDayOfWeek", escaped: "getDayOfWeek"},
		{unescaped: "getFullYear", escaped: "getFullYear"},
		{unescaped: "getHours", escaped: "getHours"},
		{unescaped: "getMilliseconds", escaped: "getMilliseconds"},
		{unescaped: "getMinutes", escaped: "getMinutes"},
		{unescaped: "getMonth", escaped: "getMonth"},
		{unescaped: "getSeconds", escaped: "getSeconds"},
		// we don't escape a single _
		{unescaped: "_if", escaped: "_if"},
		{unescaped: "_has", escaped: "_has"},
		{unescaped: "_int", escaped: "_int"},
		{unescaped: "_anything", escaped: "_anything"},
	}

	for _, tc := range cases {
		t.Run(tc.unescaped, func(t *testing.T) {
			e, escapable := Escape(tc.unescaped)
			if tc.unescapable {
				if escapable {
					t.Errorf("Expected escapable=false, but got %t", escapable)
				}
				return
			}
			if !escapable {
				t.Fatalf("Expected escapable=true, but got %t", escapable)
			}
			if tc.escaped != e {
				t.Errorf("Expected %s to escape to %s, but got %s", tc.unescaped, tc.escaped, e)
			}

			if !validCelIdent.MatchString(e) {
				t.Errorf("Expected %s to escape to a valid CEL identifier, but got %s", tc.unescaped, e)
			}

			u, ok := Unescape(tc.escaped)
			if !ok {
				t.Fatalf("Expected %s to be escapable, but it was not", tc.escaped)
			}
			if tc.unescaped != u {
				t.Errorf("Expected %s to unescape to %s, but got %s", tc.escaped, tc.unescaped, u)
			}
		})
	}
}

func TestUnescapeMalformed(t *testing.T) {
	for _, s := range []string{"__int__extra", "__illegal__"} {
		t.Run(s, func(t *testing.T) {
			e, ok := Unescape(s)
			if ok {
				t.Fatalf("Expected %s to be unescapable, but it escaped to: %s", s, e)
			}
		})
	}
}

func TestEscapingFuzz(t *testing.T) {
	fuzzer := fuzz.New()
	for i := 0; i < 1000; i++ {
		var unescaped string
		fuzzer.Fuzz(&unescaped)
		t.Run(fmt.Sprintf("%d - '%s'", i, unescaped), func(t *testing.T) {
			if len(unescaped) == 0 {
				return
			}
			escaped, ok := Escape(unescaped)
			if !ok {
				return
			}

			if !validCelIdent.MatchString(escaped) {
				t.Errorf("Expected %s to escape to a valid CEL identifier, but got %s", unescaped, escaped)
			}
			u, ok := Unescape(escaped)
			if !ok {
				t.Fatalf("Expected %s to be unescapable, but it was not", escaped)
			}
			if unescaped != u {
				t.Errorf("Expected %s to unescape to %s, but got %s", escaped, unescaped, u)
			}
		})
	}
}

var validCelIdent = regexp.MustCompile(`^[a-zA-Z_][a-zA-Z0-9_]*$`)

func TestCanSkipRegex(t *testing.T) {
	cases := []struct {
		unescaped        string
		canSkip          bool
		invalidCharFound bool
	}{
		{unescaped: "a.a", canSkip: false},
		{unescaped: "a-a", canSkip: false},
		{unescaped: "a__a", canSkip: false},
		{unescaped: "a.-/__a", canSkip: false},
		{unescaped: "a_a", canSkip: true},
		{unescaped: "a_a_a", canSkip: true},
		{unescaped: "@", invalidCharFound: true},
		{unescaped: "?", invalidCharFound: true},
		// if escape keyword is detected before invalid character, invalidCharFound
		{unescaped: "/?", canSkip: false},
	}

	for _, tc := range cases {
		t.Run(tc.unescaped, func(t *testing.T) {
			escapeCheck := skipRegexCheck(tc.unescaped)
			if escapeCheck.invalidCharFound {
				if escapeCheck.invalidCharFound != tc.invalidCharFound {
					t.Errorf("Expected input validation to be %v, but got %t", tc.invalidCharFound, escapeCheck.invalidCharFound)
				}
			} else {
				if escapeCheck.canSkipRegex != tc.canSkip {
					t.Errorf("Expected %v, but got %t", tc.canSkip, escapeCheck.canSkipRegex)
				}
			}
		})
	}
}

相关信息

kubernetes 源码目录

相关文章

kubernetes escaping 源码

kubernetes registry 源码

kubernetes schemas 源码

kubernetes schemas_test 源码

kubernetes types 源码

kubernetes types_test 源码

kubernetes url 源码

kubernetes value 源码

kubernetes value_test 源码

0  赞