diff --git a/go.mod b/go.mod index 6ce77d0fa7..16f60faebd 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/owncloud/ocis/v2 go 1.20 require ( - github.com/CiscoM31/godata v1.0.7 + github.com/CiscoM31/godata v1.0.8 github.com/KimMachineGun/automemlimit v0.2.4 github.com/Masterminds/semver v1.5.0 github.com/MicahParks/keyfunc v1.5.1 diff --git a/go.sum b/go.sum index 12343ffaad..0924676295 100644 --- a/go.sum +++ b/go.sum @@ -639,8 +639,8 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/CiscoM31/godata v1.0.7 h1:y3FHdICAU9j+IkK6E66ezCghaQSamFbYoj/YEHig0kY= -github.com/CiscoM31/godata v1.0.7/go.mod h1:ZMiT6JuD3Rm83HEtiTx4JEChsd25YCrxchKGag/sdTc= +github.com/CiscoM31/godata v1.0.8 h1:ZhPjm1dSwZWMUvb33P4bcVm048iiQ1wbncoCc9bLChQ= +github.com/CiscoM31/godata v1.0.8/go.mod h1:ZMiT6JuD3Rm83HEtiTx4JEChsd25YCrxchKGag/sdTc= github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c/go.mod h1:X0CRv0ky0k6m906ixxpzmDRLvX58TFUKS2eePweuyxk= github.com/KimMachineGun/automemlimit v0.2.4 h1:GBty8TK8k0aJer1Pq5/3Vdt2ef+YpLhcqNo+PSD5CoI= diff --git a/vendor/github.com/CiscoM31/godata/compute_parser.go b/vendor/github.com/CiscoM31/godata/compute_parser.go index a127027a30..7e7b972ee8 100644 --- a/vendor/github.com/CiscoM31/godata/compute_parser.go +++ b/vendor/github.com/CiscoM31/godata/compute_parser.go @@ -2,6 +2,8 @@ package godata import ( "context" + "errors" + "fmt" "regexp" "strings" ) @@ -10,18 +12,33 @@ import ( // See https://docs.oasis-open.org/odata/odata/v4.01/os/part2-url-conventions/odata-v4.01-os-part2-url-conventions.html#sec_SystemQueryOptioncompute const computeAsSeparator = " as " -// Dynamic property names are restricted to case-insensitive a-z -var computeFieldRegex = regexp.MustCompile("^[a-zA-Z]+$") +// Dynamic property names are restricted to case-insensitive a-z and the path separator /. +var computeFieldRegex = regexp.MustCompile("^[a-zA-Z/]+$") type ComputeItem struct { Tree *ParseNode // The compute expression parsed as a tree. Field string // The name of the computed dynamic property. } +// GlobalAllTokenParser is a Tokenizer which matches all tokens and ignores none. It differs from the +// GlobalExpressionTokenizer which ignores whitespace tokens. +var GlobalAllTokenParser *Tokenizer + +func init() { + t := NewExpressionParser().tokenizer + t.TokenMatchers = append(t.IgnoreMatchers, t.TokenMatchers...) + t.IgnoreMatchers = nil + GlobalAllTokenParser = t +} + func ParseComputeString(ctx context.Context, compute string) (*GoDataComputeQuery, error) { - items := strings.Split(compute, ",") + items, err := SplitComputeItems(compute) + if err != nil { + return nil, err + } result := make([]*ComputeItem, 0) + fields := map[string]struct{}{} for _, v := range items { v = strings.TrimSpace(v) @@ -45,7 +62,7 @@ func ParseComputeString(ctx context.Context, compute string) (*GoDataComputeQuer case *GoDataError: return nil, &GoDataError{ ResponseCode: e.ResponseCode, - Message: "Invalid $compute query option", + Message: fmt.Sprintf("Invalid $compute query option, %s", e.Message), Cause: e, } default: @@ -62,6 +79,16 @@ func ParseComputeString(ctx context.Context, compute string) (*GoDataComputeQuer Message: "Invalid $compute query option", } } + + if _, ok := fields[field]; ok { + return nil, &GoDataError{ + ResponseCode: 400, + Message: "Invalid $compute query option", + } + } + + fields[field] = struct{}{} + result = append(result, &ComputeItem{ Tree: tree.Tree, Field: field, @@ -71,3 +98,52 @@ func ParseComputeString(ctx context.Context, compute string) (*GoDataComputeQuer return &GoDataComputeQuery{result, compute}, nil } + +// SplitComputeItems splits the input string based on the comma delimiter. It does so with awareness as to +// which commas delimit $compute items and which ones are an inline part of the item, such as a separator +// for function arguments. +// +// For example the input "someFunc(one,two) as three, 1 add 2 as four" results in the +// output ["someFunc(one,two) as three", "1 add 2 as four"] +func SplitComputeItems(in string) ([]string, error) { + + var ret []string + + tokens, err := GlobalAllTokenParser.Tokenize(context.Background(), in) + if err != nil { + return nil, err + } + + item := strings.Builder{} + parenGauge := 0 + + for _, v := range tokens { + switch v.Type { + case ExpressionTokenOpenParen: + parenGauge++ + case ExpressionTokenCloseParen: + if parenGauge == 0 { + return nil, errors.New("unmatched parentheses") + } + parenGauge-- + case ExpressionTokenComma: + if parenGauge == 0 { + ret = append(ret, item.String()) + item.Reset() + continue + } + } + + item.WriteString(v.Value) + } + + if parenGauge != 0 { + return nil, errors.New("unmatched parentheses") + } + + if item.Len() > 0 { + ret = append(ret, item.String()) + } + + return ret, nil +} diff --git a/vendor/modules.txt b/vendor/modules.txt index be59fad1a8..5c6e03531e 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -8,7 +8,7 @@ github.com/Azure/go-ntlmssp ## explicit; go 1.16 github.com/BurntSushi/toml github.com/BurntSushi/toml/internal -# github.com/CiscoM31/godata v1.0.7 +# github.com/CiscoM31/godata v1.0.8 ## explicit; go 1.19 github.com/CiscoM31/godata # github.com/KimMachineGun/automemlimit v0.2.4