mirror of
https://github.com/ceph/ceph-csi.git
synced 2025-03-09 17:09:29 +00:00
Several packages are only used while running the e2e suite. These packages are less important to update, as the they can not influence the final executable that is part of the Ceph-CSI container-image. By moving these dependencies out of the main Ceph-CSI go.mod, it is easier to identify if a reported CVE affects Ceph-CSI, or only the testing (like most of the Kubernetes CVEs). Signed-off-by: Niels de Vos <ndevos@ibm.com>
327 lines
8.3 KiB
Go
327 lines
8.3 KiB
Go
//
|
|
// Copyright (c) 2011-2019 Canonical Ltd
|
|
//
|
|
// 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 yaml
|
|
|
|
import (
|
|
"encoding/base64"
|
|
"math"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
type resolveMapItem struct {
|
|
value interface{}
|
|
tag string
|
|
}
|
|
|
|
var resolveTable = make([]byte, 256)
|
|
var resolveMap = make(map[string]resolveMapItem)
|
|
|
|
func init() {
|
|
t := resolveTable
|
|
t[int('+')] = 'S' // Sign
|
|
t[int('-')] = 'S'
|
|
for _, c := range "0123456789" {
|
|
t[int(c)] = 'D' // Digit
|
|
}
|
|
for _, c := range "yYnNtTfFoO~" {
|
|
t[int(c)] = 'M' // In map
|
|
}
|
|
t[int('.')] = '.' // Float (potentially in map)
|
|
|
|
var resolveMapList = []struct {
|
|
v interface{}
|
|
tag string
|
|
l []string
|
|
}{
|
|
{true, boolTag, []string{"true", "True", "TRUE"}},
|
|
{false, boolTag, []string{"false", "False", "FALSE"}},
|
|
{nil, nullTag, []string{"", "~", "null", "Null", "NULL"}},
|
|
{math.NaN(), floatTag, []string{".nan", ".NaN", ".NAN"}},
|
|
{math.Inf(+1), floatTag, []string{".inf", ".Inf", ".INF"}},
|
|
{math.Inf(+1), floatTag, []string{"+.inf", "+.Inf", "+.INF"}},
|
|
{math.Inf(-1), floatTag, []string{"-.inf", "-.Inf", "-.INF"}},
|
|
{"<<", mergeTag, []string{"<<"}},
|
|
}
|
|
|
|
m := resolveMap
|
|
for _, item := range resolveMapList {
|
|
for _, s := range item.l {
|
|
m[s] = resolveMapItem{item.v, item.tag}
|
|
}
|
|
}
|
|
}
|
|
|
|
const (
|
|
nullTag = "!!null"
|
|
boolTag = "!!bool"
|
|
strTag = "!!str"
|
|
intTag = "!!int"
|
|
floatTag = "!!float"
|
|
timestampTag = "!!timestamp"
|
|
seqTag = "!!seq"
|
|
mapTag = "!!map"
|
|
binaryTag = "!!binary"
|
|
mergeTag = "!!merge"
|
|
)
|
|
|
|
var longTags = make(map[string]string)
|
|
var shortTags = make(map[string]string)
|
|
|
|
func init() {
|
|
for _, stag := range []string{nullTag, boolTag, strTag, intTag, floatTag, timestampTag, seqTag, mapTag, binaryTag, mergeTag} {
|
|
ltag := longTag(stag)
|
|
longTags[stag] = ltag
|
|
shortTags[ltag] = stag
|
|
}
|
|
}
|
|
|
|
const longTagPrefix = "tag:yaml.org,2002:"
|
|
|
|
func shortTag(tag string) string {
|
|
if strings.HasPrefix(tag, longTagPrefix) {
|
|
if stag, ok := shortTags[tag]; ok {
|
|
return stag
|
|
}
|
|
return "!!" + tag[len(longTagPrefix):]
|
|
}
|
|
return tag
|
|
}
|
|
|
|
func longTag(tag string) string {
|
|
if strings.HasPrefix(tag, "!!") {
|
|
if ltag, ok := longTags[tag]; ok {
|
|
return ltag
|
|
}
|
|
return longTagPrefix + tag[2:]
|
|
}
|
|
return tag
|
|
}
|
|
|
|
func resolvableTag(tag string) bool {
|
|
switch tag {
|
|
case "", strTag, boolTag, intTag, floatTag, nullTag, timestampTag:
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
var yamlStyleFloat = regexp.MustCompile(`^[-+]?(\.[0-9]+|[0-9]+(\.[0-9]*)?)([eE][-+]?[0-9]+)?$`)
|
|
|
|
func resolve(tag string, in string) (rtag string, out interface{}) {
|
|
tag = shortTag(tag)
|
|
if !resolvableTag(tag) {
|
|
return tag, in
|
|
}
|
|
|
|
defer func() {
|
|
switch tag {
|
|
case "", rtag, strTag, binaryTag:
|
|
return
|
|
case floatTag:
|
|
if rtag == intTag {
|
|
switch v := out.(type) {
|
|
case int64:
|
|
rtag = floatTag
|
|
out = float64(v)
|
|
return
|
|
case int:
|
|
rtag = floatTag
|
|
out = float64(v)
|
|
return
|
|
}
|
|
}
|
|
}
|
|
failf("cannot decode %s `%s` as a %s", shortTag(rtag), in, shortTag(tag))
|
|
}()
|
|
|
|
// Any data is accepted as a !!str or !!binary.
|
|
// Otherwise, the prefix is enough of a hint about what it might be.
|
|
hint := byte('N')
|
|
if in != "" {
|
|
hint = resolveTable[in[0]]
|
|
}
|
|
if hint != 0 && tag != strTag && tag != binaryTag {
|
|
// Handle things we can lookup in a map.
|
|
if item, ok := resolveMap[in]; ok {
|
|
return item.tag, item.value
|
|
}
|
|
|
|
// Base 60 floats are a bad idea, were dropped in YAML 1.2, and
|
|
// are purposefully unsupported here. They're still quoted on
|
|
// the way out for compatibility with other parser, though.
|
|
|
|
switch hint {
|
|
case 'M':
|
|
// We've already checked the map above.
|
|
|
|
case '.':
|
|
// Not in the map, so maybe a normal float.
|
|
floatv, err := strconv.ParseFloat(in, 64)
|
|
if err == nil {
|
|
return floatTag, floatv
|
|
}
|
|
|
|
case 'D', 'S':
|
|
// Int, float, or timestamp.
|
|
// Only try values as a timestamp if the value is unquoted or there's an explicit
|
|
// !!timestamp tag.
|
|
if tag == "" || tag == timestampTag {
|
|
t, ok := parseTimestamp(in)
|
|
if ok {
|
|
return timestampTag, t
|
|
}
|
|
}
|
|
|
|
plain := strings.Replace(in, "_", "", -1)
|
|
intv, err := strconv.ParseInt(plain, 0, 64)
|
|
if err == nil {
|
|
if intv == int64(int(intv)) {
|
|
return intTag, int(intv)
|
|
} else {
|
|
return intTag, intv
|
|
}
|
|
}
|
|
uintv, err := strconv.ParseUint(plain, 0, 64)
|
|
if err == nil {
|
|
return intTag, uintv
|
|
}
|
|
if yamlStyleFloat.MatchString(plain) {
|
|
floatv, err := strconv.ParseFloat(plain, 64)
|
|
if err == nil {
|
|
return floatTag, floatv
|
|
}
|
|
}
|
|
if strings.HasPrefix(plain, "0b") {
|
|
intv, err := strconv.ParseInt(plain[2:], 2, 64)
|
|
if err == nil {
|
|
if intv == int64(int(intv)) {
|
|
return intTag, int(intv)
|
|
} else {
|
|
return intTag, intv
|
|
}
|
|
}
|
|
uintv, err := strconv.ParseUint(plain[2:], 2, 64)
|
|
if err == nil {
|
|
return intTag, uintv
|
|
}
|
|
} else if strings.HasPrefix(plain, "-0b") {
|
|
intv, err := strconv.ParseInt("-"+plain[3:], 2, 64)
|
|
if err == nil {
|
|
if true || intv == int64(int(intv)) {
|
|
return intTag, int(intv)
|
|
} else {
|
|
return intTag, intv
|
|
}
|
|
}
|
|
}
|
|
// Octals as introduced in version 1.2 of the spec.
|
|
// Octals from the 1.1 spec, spelled as 0777, are still
|
|
// decoded by default in v3 as well for compatibility.
|
|
// May be dropped in v4 depending on how usage evolves.
|
|
if strings.HasPrefix(plain, "0o") {
|
|
intv, err := strconv.ParseInt(plain[2:], 8, 64)
|
|
if err == nil {
|
|
if intv == int64(int(intv)) {
|
|
return intTag, int(intv)
|
|
} else {
|
|
return intTag, intv
|
|
}
|
|
}
|
|
uintv, err := strconv.ParseUint(plain[2:], 8, 64)
|
|
if err == nil {
|
|
return intTag, uintv
|
|
}
|
|
} else if strings.HasPrefix(plain, "-0o") {
|
|
intv, err := strconv.ParseInt("-"+plain[3:], 8, 64)
|
|
if err == nil {
|
|
if true || intv == int64(int(intv)) {
|
|
return intTag, int(intv)
|
|
} else {
|
|
return intTag, intv
|
|
}
|
|
}
|
|
}
|
|
default:
|
|
panic("internal error: missing handler for resolver table: " + string(rune(hint)) + " (with " + in + ")")
|
|
}
|
|
}
|
|
return strTag, in
|
|
}
|
|
|
|
// encodeBase64 encodes s as base64 that is broken up into multiple lines
|
|
// as appropriate for the resulting length.
|
|
func encodeBase64(s string) string {
|
|
const lineLen = 70
|
|
encLen := base64.StdEncoding.EncodedLen(len(s))
|
|
lines := encLen/lineLen + 1
|
|
buf := make([]byte, encLen*2+lines)
|
|
in := buf[0:encLen]
|
|
out := buf[encLen:]
|
|
base64.StdEncoding.Encode(in, []byte(s))
|
|
k := 0
|
|
for i := 0; i < len(in); i += lineLen {
|
|
j := i + lineLen
|
|
if j > len(in) {
|
|
j = len(in)
|
|
}
|
|
k += copy(out[k:], in[i:j])
|
|
if lines > 1 {
|
|
out[k] = '\n'
|
|
k++
|
|
}
|
|
}
|
|
return string(out[:k])
|
|
}
|
|
|
|
// This is a subset of the formats allowed by the regular expression
|
|
// defined at http://yaml.org/type/timestamp.html.
|
|
var allowedTimestampFormats = []string{
|
|
"2006-1-2T15:4:5.999999999Z07:00", // RCF3339Nano with short date fields.
|
|
"2006-1-2t15:4:5.999999999Z07:00", // RFC3339Nano with short date fields and lower-case "t".
|
|
"2006-1-2 15:4:5.999999999", // space separated with no time zone
|
|
"2006-1-2", // date only
|
|
// Notable exception: time.Parse cannot handle: "2001-12-14 21:59:43.10 -5"
|
|
// from the set of examples.
|
|
}
|
|
|
|
// parseTimestamp parses s as a timestamp string and
|
|
// returns the timestamp and reports whether it succeeded.
|
|
// Timestamp formats are defined at http://yaml.org/type/timestamp.html
|
|
func parseTimestamp(s string) (time.Time, bool) {
|
|
// TODO write code to check all the formats supported by
|
|
// http://yaml.org/type/timestamp.html instead of using time.Parse.
|
|
|
|
// Quick check: all date formats start with YYYY-.
|
|
i := 0
|
|
for ; i < len(s); i++ {
|
|
if c := s[i]; c < '0' || c > '9' {
|
|
break
|
|
}
|
|
}
|
|
if i != 4 || i == len(s) || s[i] != '-' {
|
|
return time.Time{}, false
|
|
}
|
|
for _, format := range allowedTimestampFormats {
|
|
if t, err := time.Parse(format, s); err == nil {
|
|
return t, true
|
|
}
|
|
}
|
|
return time.Time{}, false
|
|
}
|