+Decode/encode XML to/from map[string]interface{} (or JSON) values, and extract/modify values from maps by key or key-path, including wildcards.
+
+mxj supplants the legacy x2j and j2x packages. If you want the old syntax, use mxj/x2j and mxj/j2x packages.
+
+
Installation
+Using go.mod:
+
+go get github.com/clbanning/mxj/v2@v2.3.2
+
+
+
+import "github.com/clbanning/mxj/v2"
+
+
+... or just vendor the package.
+
+
Related Packages
+
+https://github.com/clbanning/checkxml provides functions for validating XML data.
+
+
Refactor Encoder - 2020.05.01
+Issue #70 highlighted that encoding large maps does not scale well, since the original logic used string appends operations. Using bytes.Buffer results in linear scaling for very large XML docs. (Metrics based on MacBook Pro i7 w/ 16 GB.)
+
+ Nodes m.XML() time
+ 54809 12.53708ms
+ 109780 32.403183ms
+ 164678 59.826412ms
+ 482598 109.358007ms
+
+
Refactor Decoder - 2015.11.15
+For over a year I've wanted to refactor the XML-to-map[string]interface{} decoder to make it more performant. I recently took the time to do that, since we were using github.com/clbanning/mxj in a production system that could be deployed on a Raspberry Pi. Now the decoder is comparable to the stdlib JSON-to-map[string]interface{} decoder in terms of its additional processing overhead relative to decoding to a structure value. As shown by:
+
+ BenchmarkNewMapXml-4 100000 18043 ns/op
+ BenchmarkNewStructXml-4 100000 14892 ns/op
+ BenchmarkNewMapJson-4 300000 4633 ns/op
+ BenchmarkNewStructJson-4 300000 3427 ns/op
+ BenchmarkNewMapXmlBooks-4 20000 82850 ns/op
+ BenchmarkNewStructXmlBooks-4 20000 67822 ns/op
+ BenchmarkNewMapJsonBooks-4 100000 17222 ns/op
+ BenchmarkNewStructJsonBooks-4 100000 15309 ns/op
+
+
Notices
+
+ 2021.02.02: v2.5 - add XmlCheckIsValid toggle to force checking that the encoded XML is valid
+ 2020.12.14: v2.4 - add XMLEscapeCharsDecoder to preserve XML escaped characters in Map values
+ 2020.10.28: v2.3 - add TrimWhiteSpace option
+ 2020.05.01: v2.2 - optimize map to XML encoding for large XML docs.
+ 2019.07.04: v2.0 - remove unnecessary methods - mv.XmlWriterRaw, mv.XmlIndentWriterRaw - for Map and MapSeq.
+ 2019.07.04: Add MapSeq type and move associated functions and methods from Map to MapSeq.
+ 2019.01.21: DecodeSimpleValuesAsMap - decode to map[:map["#text":]] rather than map[:]
+ 2018.04.18: mv.Xml/mv.XmlIndent encodes non-map[string]interface{} map values - map[string]string, map[int]uint, etc.
+ 2018.03.29: mv.Gob/NewMapGob support gob encoding/decoding of Maps.
+ 2018.03.26: Added mxj/x2j-wrapper sub-package for migrating from legacy x2j package.
+ 2017.02.22: LeafNode paths can use ".N" syntax rather than "[N]" for list member indexing.
+ 2017.02.10: SetFieldSeparator changes field separator for args in UpdateValuesForPath, ValuesFor... methods.
+ 2017.02.06: Support XMPP stream processing - HandleXMPPStreamTag().
+ 2016.11.07: Preserve name space prefix syntax in XmlSeq parser - NewMapXmlSeq(), etc.
+ 2016.06.25: Support overriding default XML attribute prefix, "-", in Map keys - SetAttrPrefix().
+ 2016.05.26: Support customization of xml.Decoder by exposing CustomDecoder variable.
+ 2016.03.19: Escape invalid chars when encoding XML attribute and element values - XMLEscapeChars().
+ 2016.03.02: By default decoding XML with float64 and bool value casting will not cast "NaN", "Inf", and "-Inf".
+ To cast them to float64, first set flag with CastNanInf(true).
+ 2016.02.22: New mv.Root(), mv.Elements(), mv.Attributes methods let you examine XML document structure.
+ 2016.02.16: Add CoerceKeysToLower() option to handle tags with mixed capitalization.
+ 2016.02.12: Seek for first xml.StartElement token; only return error if io.EOF is reached first (handles BOM).
+ 2015.12.02: XML decoding/encoding that preserves original structure of document. See NewMapXmlSeq()
+ and mv.XmlSeq() / mv.XmlSeqIndent().
+ 2015-05-20: New: mv.StringIndentNoTypeInfo().
+ Also, alphabetically sort map[string]interface{} values by key to prettify output for mv.Xml(),
+ mv.XmlIndent(), mv.StringIndent(), mv.StringIndentNoTypeInfo().
+ 2014-11-09: IncludeTagSeqNum() adds "_seq" key with XML doc positional information.
+ (NOTE: PreserveXmlList() is similar and will be here soon.)
+ 2014-09-18: inspired by NYTimes fork, added PrependAttrWithHyphen() to allow stripping hyphen from attribute tag.
+ 2014-08-02: AnyXml() and AnyXmlIndent() will try to marshal arbitrary values to XML.
+ 2014-04-28: ValuesForPath() and NewMap() now accept path with indexed array references.
+
+
Basic Unmarshal XML to map[string]interface{}
+
type Map map[string]interface{}
+
+Create a `Map` value, 'mv', from any `map[string]interface{}` value, 'v':
+
mv := Map(v)
+
+Unmarshal / marshal XML as a `Map` value, 'mv':
+
+
+Unmarshal XML from an `io.Reader` as a `Map` value, 'mv':
+
mv, err := NewMapXmlReader(xmlReader) // repeated calls, as with an os.File Reader, will process stream
+mv, raw, err := NewMapXmlReaderRaw(xmlReader) // 'raw' is the raw XML that was decoded
+
+Marshal `Map` value, 'mv', to an XML Writer (`io.Writer`):
+
err := mv.XmlWriter(xmlWriter)
+raw, err := mv.XmlWriterRaw(xmlWriter) // 'raw' is the raw XML that was written on xmlWriter
+
+Converting XML to JSON: see Examples for `NewMapXml` and `HandleXmlReader`.
+
+There are comparable functions and methods for JSON processing.
+
+Arbitrary structure values can be decoded to / encoded from `Map` values:
+
+To work with XML tag values, JSON or Map key values or structure field values, decode the XML, JSON
+or structure to a `Map` value, 'mv', or cast a `map[string]interface{}` value to a `Map` value, 'mv', then:
+
+
+A new `Map` with whatever keys are desired can be created from the current `Map` and then encoded in XML
+or JSON. (Note: keys can use dot-notation.)
+
newMap, err := mv.NewMap("oldKey_1:newKey_1", "oldKey_2:newKey_2", ..., "oldKey_N:newKey_N")
+newMap, err := mv.NewMap("oldKey1", "oldKey3", "oldKey5") // a subset of 'mv'; see "examples/partial.go"
+newXml, err := newMap.Xml() // for example
+newJson, err := newMap.Json() // ditto
+
+
Usage
+
+The package is fairly well [self-documented with examples](http://godoc.org/github.com/clbanning/mxj).
+
+Also, the subdirectory "examples" contains a wide range of examples, several taken from golang-nuts discussions.
+
+
XML parsing conventions
+
+Using NewMapXml()
+
+ - Attributes are parsed to `map[string]interface{}` values by prefixing a hyphen, `-`,
+ to the attribute label. (Unless overridden by `PrependAttrWithHyphen(false)` or
+ `SetAttrPrefix()`.)
+ - If the element is a simple element and has attributes, the element value
+ is given the key `#text` for its `map[string]interface{}` representation. (See
+ the 'atomFeedString.xml' test data, below.)
+ - XML comments, directives, and process instructions are ignored.
+ - If CoerceKeysToLower() has been called, then the resultant keys will be lower case.
+
+Using NewMapXmlSeq()
+
+ - Attributes are parsed to `map["#attr"]map[]map[string]interface{}`values
+ where the `` value has "#text" and "#seq" keys - the "#text" key holds the
+ value for ``.
+ - All elements, except for the root, have a "#seq" key.
+ - Comments, directives, and process instructions are unmarshalled into the Map using the
+ keys "#comment", "#directive", and "#procinst", respectively. (See documentation for more
+ specifics.)
+ - Name space syntax is preserved:
+ - `something` parses to `map["ns:key"]interface{}{"something"}`
+ - `xmlns:ns="http://myns.com/ns"` parses to `map["xmlns:ns"]interface{}{"http://myns.com/ns"}`
+
+Both
+
+ - By default, "Nan", "Inf", and "-Inf" values are not cast to float64. If you want them
+ to be cast, set a flag to cast them using CastNanInf(true).
+
+
XML encoding conventions
+
+ - 'nil' `Map` values, which may represent 'null' JSON values, are encoded as ``.
+ NOTE: the operation is not symmetric as `` elements are decoded as `tag:""` `Map` values,
+ which, then, encode in JSON as `"tag":""` values.
+ - ALSO: there is no guarantee that the encoded XML doc will be the same as the decoded one. (Go
+ randomizes the walk through map[string]interface{} values.) If you plan to re-encode the
+ Map value to XML and want the same sequencing of elements look at NewMapXmlSeq() and
+ mv.XmlSeq() - these try to preserve the element sequencing but with added complexity when
+ working with the Map representation.
+
+
Running "go test"
+
+Because there are no guarantees on the sequence map elements are retrieved, the tests have been
+written for visual verification in most cases. One advantage is that you can easily use the
+output from running "go test" as examples of calling the various functions and methods.
+
+
Motivation
+
+I make extensive use of JSON for messaging and typically unmarshal the messages into
+`map[string]interface{}` values. This is easily done using `json.Unmarshal` from the
+standard Go libraries. Unfortunately, many legacy solutions use structured
+XML messages; in those environments the applications would have to be refactored to
+interoperate with my components.
+
+The better solution is to just provide an alternative HTTP handler that receives
+XML messages and parses it into a `map[string]interface{}` value and then reuse
+all the JSON-based code. The Go `xml.Unmarshal()` function does not provide the same
+option of unmarshaling XML messages into `map[string]interface{}` values. So I wrote
+a couple of small functions to fill this gap and released them as the x2j package.
+
+Over the next year and a half additional features were added, and the companion j2x
+package was released to address XML encoding of arbitrary JSON and `map[string]interface{}`
+values. As part of a refactoring of our production system and looking at how we had been
+using the x2j and j2x packages we found that we rarely performed direct XML-to-JSON or
+JSON-to_XML conversion and that working with the XML or JSON as `map[string]interface{}`
+values was the primary value. Thus, everything was refactored into the mxj package.
+
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/clbanning/mxj/v2/remove.go b/go/ql/test/experimental/CWE-321/vendor/github.com/clbanning/mxj/v2/remove.go
new file mode 100644
index 000000000000..8362ab17fa4f
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/clbanning/mxj/v2/remove.go
@@ -0,0 +1,37 @@
+package mxj
+
+import "strings"
+
+// Removes the path.
+func (mv Map) Remove(path string) error {
+ m := map[string]interface{}(mv)
+ return remove(m, path)
+}
+
+func remove(m interface{}, path string) error {
+ val, err := prevValueByPath(m, path)
+ if err != nil {
+ return err
+ }
+
+ lastKey := lastKey(path)
+ delete(val, lastKey)
+
+ return nil
+}
+
+// returns the last key of the path.
+// lastKey("a.b.c") would had returned "c"
+func lastKey(path string) string {
+ keys := strings.Split(path, ".")
+ key := keys[len(keys)-1]
+ return key
+}
+
+// returns the path without the last key
+// parentPath("a.b.c") whould had returned "a.b"
+func parentPath(path string) string {
+ keys := strings.Split(path, ".")
+ parentPath := strings.Join(keys[0:len(keys)-1], ".")
+ return parentPath
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/clbanning/mxj/v2/rename.go b/go/ql/test/experimental/CWE-321/vendor/github.com/clbanning/mxj/v2/rename.go
new file mode 100644
index 000000000000..4c655ed5d0ad
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/clbanning/mxj/v2/rename.go
@@ -0,0 +1,61 @@
+package mxj
+
+import (
+ "errors"
+ "strings"
+)
+
+// RenameKey renames a key in a Map.
+// It works only for nested maps.
+// It doesn't work for cases when the key is in a list.
+func (mv Map) RenameKey(path string, newName string) error {
+ var v bool
+ var err error
+ if v, err = mv.Exists(path); err == nil && !v {
+ return errors.New("RenameKey: path not found: " + path)
+ } else if err != nil {
+ return err
+ }
+ if v, err = mv.Exists(parentPath(path) + "." + newName); err == nil && v {
+ return errors.New("RenameKey: key already exists: " + newName)
+ } else if err != nil {
+ return err
+ }
+
+ m := map[string]interface{}(mv)
+ return renameKey(m, path, newName)
+}
+
+func renameKey(m interface{}, path string, newName string) error {
+ val, err := prevValueByPath(m, path)
+ if err != nil {
+ return err
+ }
+
+ oldName := lastKey(path)
+ val[newName] = val[oldName]
+ delete(val, oldName)
+
+ return nil
+}
+
+// returns a value which contains a last key in the path
+// For example: prevValueByPath("a.b.c", {a{b{c: 3}}}) returns {c: 3}
+func prevValueByPath(m interface{}, path string) (map[string]interface{}, error) {
+ keys := strings.Split(path, ".")
+
+ switch mValue := m.(type) {
+ case map[string]interface{}:
+ for key, value := range mValue {
+ if key == keys[0] {
+ if len(keys) == 1 {
+ return mValue, nil
+ } else {
+ // keep looking for the full path to the key
+ return prevValueByPath(value, strings.Join(keys[1:], "."))
+ }
+ }
+ }
+ }
+ return nil, errors.New("prevValueByPath: didn't find path – " + path)
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/clbanning/mxj/v2/set.go b/go/ql/test/experimental/CWE-321/vendor/github.com/clbanning/mxj/v2/set.go
new file mode 100644
index 000000000000..a297fc38887a
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/clbanning/mxj/v2/set.go
@@ -0,0 +1,26 @@
+package mxj
+
+import (
+ "strings"
+)
+
+// Sets the value for the path
+func (mv Map) SetValueForPath(value interface{}, path string) error {
+ pathAry := strings.Split(path, ".")
+ parentPathAry := pathAry[0 : len(pathAry)-1]
+ parentPath := strings.Join(parentPathAry, ".")
+
+ val, err := mv.ValueForPath(parentPath)
+ if err != nil {
+ return err
+ }
+ if val == nil {
+ return nil // we just ignore the request if there's no val
+ }
+
+ key := pathAry[len(pathAry)-1]
+ cVal := val.(map[string]interface{})
+ cVal[key] = value
+
+ return nil
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/clbanning/mxj/v2/setfieldsep.go b/go/ql/test/experimental/CWE-321/vendor/github.com/clbanning/mxj/v2/setfieldsep.go
new file mode 100644
index 000000000000..b70715ebc65a
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/clbanning/mxj/v2/setfieldsep.go
@@ -0,0 +1,20 @@
+package mxj
+
+// Per: https://github.com/clbanning/mxj/issues/37#issuecomment-278651862
+var fieldSep string = ":"
+
+// SetFieldSeparator changes the default field separator, ":", for the
+// newVal argument in mv.UpdateValuesForPath and the optional 'subkey' arguments
+// in mv.ValuesForKey and mv.ValuesForPath.
+//
+// E.g., if the newVal value is "http://blah/blah", setting the field separator
+// to "|" will allow the newVal specification, "|http://blah/blah" to parse
+// properly. If called with no argument or an empty string value, the field
+// separator is set to the default, ":".
+func SetFieldSeparator(s ...string) {
+ if len(s) == 0 || s[0] == "" {
+ fieldSep = ":" // the default
+ return
+ }
+ fieldSep = s[0]
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/clbanning/mxj/v2/songtext.xml b/go/ql/test/experimental/CWE-321/vendor/github.com/clbanning/mxj/v2/songtext.xml
new file mode 100644
index 000000000000..8c0f2becb128
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/clbanning/mxj/v2/songtext.xml
@@ -0,0 +1,29 @@
+
+ help me!
+
+
+
+ Henry was a renegade
+ Didn't like to play it safe
+ One component at a time
+ There's got to be a better way
+ Oh, people came from miles around
+ Searching for a steady job
+ Welcome to the Motor Town
+ Booming like an atom bomb
+
+
+ Oh, Henry was the end of the story
+ Then everything went wrong
+ And we'll return it to its former glory
+ But it just takes so long
+
+
+
+ It's going to take a long time
+ It's going to take it, but we'll make it one day
+ It's going to take a long time
+ It's going to take it, but we'll make it one day
+
+
+
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/clbanning/mxj/v2/strict.go b/go/ql/test/experimental/CWE-321/vendor/github.com/clbanning/mxj/v2/strict.go
new file mode 100644
index 000000000000..1e769560ba0b
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/clbanning/mxj/v2/strict.go
@@ -0,0 +1,30 @@
+// Copyright 2016 Charles Banning. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file
+
+// strict.go actually addresses setting xml.Decoder attribute
+// values. This'll let you parse non-standard XML.
+
+package mxj
+
+import (
+ "encoding/xml"
+)
+
+// CustomDecoder can be used to specify xml.Decoder attribute
+// values, e.g., Strict:false, to be used. By default CustomDecoder
+// is nil. If CustomeDecoder != nil, then mxj.XmlCharsetReader variable is
+// ignored and must be set as part of the CustomDecoder value, if needed.
+// Usage:
+// mxj.CustomDecoder = &xml.Decoder{Strict:false}
+var CustomDecoder *xml.Decoder
+
+// useCustomDecoder copy over public attributes from customDecoder
+func useCustomDecoder(d *xml.Decoder) {
+ d.Strict = CustomDecoder.Strict
+ d.AutoClose = CustomDecoder.AutoClose
+ d.Entity = CustomDecoder.Entity
+ d.CharsetReader = CustomDecoder.CharsetReader
+ d.DefaultSpace = CustomDecoder.DefaultSpace
+}
+
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/clbanning/mxj/v2/struct.go b/go/ql/test/experimental/CWE-321/vendor/github.com/clbanning/mxj/v2/struct.go
new file mode 100644
index 000000000000..9be636cdcab7
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/clbanning/mxj/v2/struct.go
@@ -0,0 +1,54 @@
+// Copyright 2012-2017 Charles Banning. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file
+
+package mxj
+
+import (
+ "encoding/json"
+ "errors"
+ "reflect"
+
+ // "github.com/fatih/structs"
+)
+
+// Create a new Map value from a structure. Error returned if argument is not a structure.
+// Only public structure fields are decoded in the Map value. See github.com/fatih/structs#Map
+// for handling of "structs" tags.
+
+// DEPRECATED - import github.com/fatih/structs and cast result of structs.Map to mxj.Map.
+// import "github.com/fatih/structs"
+// ...
+// sm, err := structs.Map()
+// if err != nil {
+// // handle error
+// }
+// m := mxj.Map(sm)
+// Alernatively uncomment the old source and import in struct.go.
+func NewMapStruct(structVal interface{}) (Map, error) {
+ return nil, errors.New("deprecated - see package documentation")
+ /*
+ if !structs.IsStruct(structVal) {
+ return nil, errors.New("NewMapStruct() error: argument is not type Struct")
+ }
+ return structs.Map(structVal), nil
+ */
+}
+
+// Marshal a map[string]interface{} into a structure referenced by 'structPtr'. Error returned
+// if argument is not a pointer or if json.Unmarshal returns an error.
+// json.Unmarshal structure encoding rules are followed to encode public structure fields.
+func (mv Map) Struct(structPtr interface{}) error {
+ // should check that we're getting a pointer.
+ if reflect.ValueOf(structPtr).Kind() != reflect.Ptr {
+ return errors.New("mv.Struct() error: argument is not type Ptr")
+ }
+
+ m := map[string]interface{}(mv)
+ j, err := json.Marshal(m)
+ if err != nil {
+ return err
+ }
+
+ return json.Unmarshal(j, structPtr)
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/clbanning/mxj/v2/updatevalues.go b/go/ql/test/experimental/CWE-321/vendor/github.com/clbanning/mxj/v2/updatevalues.go
new file mode 100644
index 000000000000..9e10d84e8d57
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/clbanning/mxj/v2/updatevalues.go
@@ -0,0 +1,258 @@
+// Copyright 2012-2014, 2017 Charles Banning. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file
+
+// updatevalues.go - modify a value based on path and possibly sub-keys
+// TODO(clb): handle simple elements with attributes and NewMapXmlSeq Map values.
+
+package mxj
+
+import (
+ "fmt"
+ "strconv"
+ "strings"
+)
+
+// Update value based on path and possible sub-key values.
+// A count of the number of values changed and any error are returned.
+// If the count == 0, then no path (and subkeys) matched.
+// 'newVal' can be a Map or map[string]interface{} value with a single 'key' that is the key to be modified
+// or a string value "key:value[:type]" where type is "bool" or "num" to cast the value.
+// 'path' is dot-notation list of keys to traverse; last key in path can be newVal key
+// NOTE: 'path' spec does not currently support indexed array references.
+// 'subkeys' are "key:value[:type]" entries that must match for path node
+// - For attributes prefix the label with the attribute prefix character, by default a
+// hyphen, '-', e.g., "-seq:3". (See SetAttrPrefix function.)
+// - The subkey can be wildcarded - "key:*" - to require that it's there with some value.
+// - If a subkey is preceeded with the '!' character, the key:value[:type] entry is treated as an
+// exclusion critera - e.g., "!author:William T. Gaddis".
+//
+// NOTES:
+// 1. Simple elements with attributes need a path terminated as ".#text" to modify the actual value.
+// 2. Values in Maps created using NewMapXmlSeq are map[string]interface{} values with a "#text" key.
+// 3. If values in 'newVal' or 'subkeys' args contain ":", use SetFieldSeparator to an unused symbol,
+// perhaps "|".
+func (mv Map) UpdateValuesForPath(newVal interface{}, path string, subkeys ...string) (int, error) {
+ m := map[string]interface{}(mv)
+
+ // extract the subkeys
+ var subKeyMap map[string]interface{}
+ if len(subkeys) > 0 {
+ var err error
+ subKeyMap, err = getSubKeyMap(subkeys...)
+ if err != nil {
+ return 0, err
+ }
+ }
+
+ // extract key and value from newVal
+ var key string
+ var val interface{}
+ switch newVal.(type) {
+ case map[string]interface{}, Map:
+ switch newVal.(type) { // "fallthrough is not permitted in type switch" (Spec)
+ case Map:
+ newVal = newVal.(Map).Old()
+ }
+ if len(newVal.(map[string]interface{})) != 1 {
+ return 0, fmt.Errorf("newVal map can only have len == 1 - %+v", newVal)
+ }
+ for key, val = range newVal.(map[string]interface{}) {
+ }
+ case string: // split it as a key:value pair
+ ss := strings.Split(newVal.(string), fieldSep)
+ n := len(ss)
+ if n < 2 || n > 3 {
+ return 0, fmt.Errorf("unknown newVal spec - %+v", newVal)
+ }
+ key = ss[0]
+ if n == 2 {
+ val = interface{}(ss[1])
+ } else if n == 3 {
+ switch ss[2] {
+ case "bool", "boolean":
+ nv, err := strconv.ParseBool(ss[1])
+ if err != nil {
+ return 0, fmt.Errorf("can't convert newVal to bool - %+v", newVal)
+ }
+ val = interface{}(nv)
+ case "num", "numeric", "float", "int":
+ nv, err := strconv.ParseFloat(ss[1], 64)
+ if err != nil {
+ return 0, fmt.Errorf("can't convert newVal to float64 - %+v", newVal)
+ }
+ val = interface{}(nv)
+ default:
+ return 0, fmt.Errorf("unknown type for newVal value - %+v", newVal)
+ }
+ }
+ default:
+ return 0, fmt.Errorf("invalid newVal type - %+v", newVal)
+ }
+
+ // parse path
+ keys := strings.Split(path, ".")
+
+ var count int
+ updateValuesForKeyPath(key, val, m, keys, subKeyMap, &count)
+
+ return count, nil
+}
+
+// navigate the path
+func updateValuesForKeyPath(key string, value interface{}, m interface{}, keys []string, subkeys map[string]interface{}, cnt *int) {
+ // ----- at end node: looking at possible node to get 'key' ----
+ if len(keys) == 1 {
+ updateValue(key, value, m, keys[0], subkeys, cnt)
+ return
+ }
+
+ // ----- here we are navigating the path thru the penultimate node --------
+ // key of interest is keys[0] - the next in the path
+ switch keys[0] {
+ case "*": // wildcard - scan all values
+ switch m.(type) {
+ case map[string]interface{}:
+ for _, v := range m.(map[string]interface{}) {
+ updateValuesForKeyPath(key, value, v, keys[1:], subkeys, cnt)
+ }
+ case []interface{}:
+ for _, v := range m.([]interface{}) {
+ switch v.(type) {
+ // flatten out a list of maps - keys are processed
+ case map[string]interface{}:
+ for _, vv := range v.(map[string]interface{}) {
+ updateValuesForKeyPath(key, value, vv, keys[1:], subkeys, cnt)
+ }
+ default:
+ updateValuesForKeyPath(key, value, v, keys[1:], subkeys, cnt)
+ }
+ }
+ }
+ default: // key - must be map[string]interface{}
+ switch m.(type) {
+ case map[string]interface{}:
+ if v, ok := m.(map[string]interface{})[keys[0]]; ok {
+ updateValuesForKeyPath(key, value, v, keys[1:], subkeys, cnt)
+ }
+ case []interface{}: // may be buried in list
+ for _, v := range m.([]interface{}) {
+ switch v.(type) {
+ case map[string]interface{}:
+ if vv, ok := v.(map[string]interface{})[keys[0]]; ok {
+ updateValuesForKeyPath(key, value, vv, keys[1:], subkeys, cnt)
+ }
+ }
+ }
+ }
+ }
+}
+
+// change value if key and subkeys are present
+func updateValue(key string, value interface{}, m interface{}, keys0 string, subkeys map[string]interface{}, cnt *int) {
+ // there are two possible options for the value of 'keys0': map[string]interface, []interface{}
+ // and 'key' is a key in the map or is a key in a map in a list.
+ switch m.(type) {
+ case map[string]interface{}: // gotta have the last key
+ if keys0 == "*" {
+ for k := range m.(map[string]interface{}) {
+ updateValue(key, value, m, k, subkeys, cnt)
+ }
+ return
+ }
+ endVal, _ := m.(map[string]interface{})[keys0]
+
+ // if newV key is the end of path, replace the value for path-end
+ // may be []interface{} - means replace just an entry w/ subkeys
+ // otherwise replace the keys0 value if subkeys are there
+ // NOTE: this will replace the subkeys, also
+ if key == keys0 {
+ switch endVal.(type) {
+ case map[string]interface{}:
+ if hasSubKeys(m, subkeys) {
+ (m.(map[string]interface{}))[keys0] = value
+ (*cnt)++
+ }
+ case []interface{}:
+ // without subkeys can't select list member to modify
+ // so key:value spec is it ...
+ if hasSubKeys(m, subkeys) {
+ (m.(map[string]interface{}))[keys0] = value
+ (*cnt)++
+ break
+ }
+ nv := make([]interface{}, 0)
+ var valmodified bool
+ for _, v := range endVal.([]interface{}) {
+ // check entry subkeys
+ if hasSubKeys(v, subkeys) {
+ // replace v with value
+ nv = append(nv, value)
+ valmodified = true
+ (*cnt)++
+ continue
+ }
+ nv = append(nv, v)
+ }
+ if valmodified {
+ (m.(map[string]interface{}))[keys0] = interface{}(nv)
+ }
+ default: // anything else is a strict replacement
+ if hasSubKeys(m, subkeys) {
+ (m.(map[string]interface{}))[keys0] = value
+ (*cnt)++
+ }
+ }
+ return
+ }
+
+ // so value is for an element of endVal
+ // if endVal is a map then 'key' must be there w/ subkeys
+ // if endVal is a list then 'key' must be in a list member w/ subkeys
+ switch endVal.(type) {
+ case map[string]interface{}:
+ if !hasSubKeys(endVal, subkeys) {
+ return
+ }
+ if _, ok := (endVal.(map[string]interface{}))[key]; ok {
+ (endVal.(map[string]interface{}))[key] = value
+ (*cnt)++
+ }
+ case []interface{}: // keys0 points to a list, check subkeys
+ for _, v := range endVal.([]interface{}) {
+ // got to be a map so we can replace value for 'key'
+ vv, vok := v.(map[string]interface{})
+ if !vok {
+ continue
+ }
+ if _, ok := vv[key]; !ok {
+ continue
+ }
+ if !hasSubKeys(vv, subkeys) {
+ continue
+ }
+ vv[key] = value
+ (*cnt)++
+ }
+ }
+ case []interface{}: // key may be in a list member
+ // don't need to handle keys0 == "*"; we're looking at everything, anyway.
+ for _, v := range m.([]interface{}) {
+ // only map values - we're looking for 'key'
+ mm, ok := v.(map[string]interface{})
+ if !ok {
+ continue
+ }
+ if _, ok := mm[key]; !ok {
+ continue
+ }
+ if !hasSubKeys(mm, subkeys) {
+ continue
+ }
+ mm[key] = value
+ (*cnt)++
+ }
+ }
+
+ // return
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/clbanning/mxj/v2/xml.go b/go/ql/test/experimental/CWE-321/vendor/github.com/clbanning/mxj/v2/xml.go
new file mode 100644
index 000000000000..9aa04233924e
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/clbanning/mxj/v2/xml.go
@@ -0,0 +1,1410 @@
+// Copyright 2012-2016, 2018-2019 Charles Banning. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file
+
+// xml.go - basically the core of X2j for map[string]interface{} values.
+// NewMapXml, NewMapXmlReader, mv.Xml, mv.XmlWriter
+// see x2j and j2x for wrappers to provide end-to-end transformation of XML and JSON messages.
+
+package mxj
+
+import (
+ "bytes"
+ "encoding/json"
+ "encoding/xml"
+ "errors"
+ "fmt"
+ "io"
+ "reflect"
+ "sort"
+ "strconv"
+ "strings"
+ "time"
+)
+
+// ------------------- NewMapXml & NewMapXmlReader ... -------------------------
+
+// If XmlCharsetReader != nil, it will be used to decode the XML, if required.
+// Note: if CustomDecoder != nil, then XmlCharsetReader is ignored;
+// set the CustomDecoder attribute instead.
+// import (
+// charset "code.google.com/p/go-charset/charset"
+// github.com/clbanning/mxj
+// )
+// ...
+// mxj.XmlCharsetReader = charset.NewReader
+// m, merr := mxj.NewMapXml(xmlValue)
+var XmlCharsetReader func(charset string, input io.Reader) (io.Reader, error)
+
+// NewMapXml - convert a XML doc into a Map
+// (This is analogous to unmarshalling a JSON string to map[string]interface{} using json.Unmarshal().)
+// If the optional argument 'cast' is 'true', then values will be converted to boolean or float64 if possible.
+//
+// Converting XML to JSON is a simple as:
+// ...
+// mapVal, merr := mxj.NewMapXml(xmlVal)
+// if merr != nil {
+// // handle error
+// }
+// jsonVal, jerr := mapVal.Json()
+// if jerr != nil {
+// // handle error
+// }
+//
+// NOTES:
+// 1. Declarations, directives, process instructions and comments are NOT parsed.
+// 2. The 'xmlVal' will be parsed looking for an xml.StartElement, so BOM and other
+// extraneous xml.CharData will be ignored unless io.EOF is reached first.
+// 3. If CoerceKeysToLower() has been called, then all key values will be lower case.
+// 4. If CoerceKeysToSnakeCase() has been called, then all key values will be converted to snake case.
+// 5. If DisableTrimWhiteSpace(b bool) has been called, then all values will be trimmed or not. 'true' by default.
+func NewMapXml(xmlVal []byte, cast ...bool) (Map, error) {
+ var r bool
+ if len(cast) == 1 {
+ r = cast[0]
+ }
+ return xmlToMap(xmlVal, r)
+}
+
+// Get next XML doc from an io.Reader as a Map value. Returns Map value.
+// NOTES:
+// 1. Declarations, directives, process instructions and comments are NOT parsed.
+// 2. The 'xmlReader' will be parsed looking for an xml.StartElement, so BOM and other
+// extraneous xml.CharData will be ignored unless io.EOF is reached first.
+// 3. If CoerceKeysToLower() has been called, then all key values will be lower case.
+// 4. If CoerceKeysToSnakeCase() has been called, then all key values will be converted to snake case.
+func NewMapXmlReader(xmlReader io.Reader, cast ...bool) (Map, error) {
+ var r bool
+ if len(cast) == 1 {
+ r = cast[0]
+ }
+
+ // We need to put an *os.File reader in a ByteReader or the xml.NewDecoder
+ // will wrap it in a bufio.Reader and seek on the file beyond where the
+ // xml.Decoder parses!
+ if _, ok := xmlReader.(io.ByteReader); !ok {
+ xmlReader = myByteReader(xmlReader) // see code at EOF
+ }
+
+ // build the map
+ return xmlReaderToMap(xmlReader, r)
+}
+
+// Get next XML doc from an io.Reader as a Map value. Returns Map value and slice with the raw XML.
+// NOTES:
+// 1. Declarations, directives, process instructions and comments are NOT parsed.
+// 2. Due to the implementation of xml.Decoder, the raw XML off the reader is buffered to []byte
+// using a ByteReader. If the io.Reader is an os.File, there may be significant performance impact.
+// See the examples - getmetrics1.go through getmetrics4.go - for comparative use cases on a large
+// data set. If the io.Reader is wrapping a []byte value in-memory, however, such as http.Request.Body
+// you CAN use it to efficiently unmarshal a XML doc and retrieve the raw XML in a single call.
+// 3. The 'raw' return value may be larger than the XML text value.
+// 4. The 'xmlReader' will be parsed looking for an xml.StartElement, so BOM and other
+// extraneous xml.CharData will be ignored unless io.EOF is reached first.
+// 5. If CoerceKeysToLower() has been called, then all key values will be lower case.
+// 6. If CoerceKeysToSnakeCase() has been called, then all key values will be converted to snake case.
+func NewMapXmlReaderRaw(xmlReader io.Reader, cast ...bool) (Map, []byte, error) {
+ var r bool
+ if len(cast) == 1 {
+ r = cast[0]
+ }
+ // create TeeReader so we can retrieve raw XML
+ buf := make([]byte, 0)
+ wb := bytes.NewBuffer(buf)
+ trdr := myTeeReader(xmlReader, wb) // see code at EOF
+
+ m, err := xmlReaderToMap(trdr, r)
+
+ // retrieve the raw XML that was decoded
+ b := wb.Bytes()
+
+ if err != nil {
+ return nil, b, err
+ }
+
+ return m, b, nil
+}
+
+// xmlReaderToMap() - parse a XML io.Reader to a map[string]interface{} value
+func xmlReaderToMap(rdr io.Reader, r bool) (map[string]interface{}, error) {
+ // parse the Reader
+ p := xml.NewDecoder(rdr)
+ if CustomDecoder != nil {
+ useCustomDecoder(p)
+ } else {
+ p.CharsetReader = XmlCharsetReader
+ }
+ return xmlToMapParser("", nil, p, r)
+}
+
+// xmlToMap - convert a XML doc into map[string]interface{} value
+func xmlToMap(doc []byte, r bool) (map[string]interface{}, error) {
+ b := bytes.NewReader(doc)
+ p := xml.NewDecoder(b)
+ if CustomDecoder != nil {
+ useCustomDecoder(p)
+ } else {
+ p.CharsetReader = XmlCharsetReader
+ }
+ return xmlToMapParser("", nil, p, r)
+}
+
+// ===================================== where the work happens =============================
+
+// PrependAttrWithHyphen. Prepend attribute tags with a hyphen.
+// Default is 'true'. (Not applicable to NewMapXmlSeq(), mv.XmlSeq(), etc.)
+// Note:
+// If 'false', unmarshaling and marshaling is not symmetric. Attributes will be
+// marshal'd as attr and may be part of a list.
+func PrependAttrWithHyphen(v bool) {
+ if v {
+ attrPrefix = "-"
+ lenAttrPrefix = len(attrPrefix)
+ return
+ }
+ attrPrefix = ""
+ lenAttrPrefix = len(attrPrefix)
+}
+
+// Include sequence id with inner tags. - per Sean Murphy, murphysean84@gmail.com.
+var includeTagSeqNum bool
+
+// IncludeTagSeqNum - include a "_seq":N key:value pair with each inner tag, denoting
+// its position when parsed. This is of limited usefulness, since list values cannot
+// be tagged with "_seq" without changing their depth in the Map.
+// So THIS SHOULD BE USED WITH CAUTION - see the test cases. Here's a sample of what
+// you get.
+/*
+
+
+
+
+ hello
+
+
+ parses as:
+
+ {
+ Obj:{
+ "-c":"la",
+ "-h":"da",
+ "-x":"dee",
+ "intObj":[
+ {
+ "-id"="3",
+ "_seq":"0" // if mxj.Cast is passed, then: "_seq":0
+ },
+ {
+ "-id"="2",
+ "_seq":"2"
+ }],
+ "intObj1":{
+ "-id":"1",
+ "_seq":"1"
+ },
+ "StrObj":{
+ "#text":"hello", // simple element value gets "#text" tag
+ "_seq":"3"
+ }
+ }
+ }
+*/
+func IncludeTagSeqNum(b ...bool) {
+ if len(b) == 0 {
+ includeTagSeqNum = !includeTagSeqNum
+ } else if len(b) == 1 {
+ includeTagSeqNum = b[0]
+ }
+}
+
+// all keys will be "lower case"
+var lowerCase bool
+
+// Coerce all tag values to keys in lower case. This is useful if you've got sources with variable
+// tag capitalization, and you want to use m.ValuesForKeys(), etc., with the key or path spec
+// in lower case.
+// CoerceKeysToLower() will toggle the coercion flag true|false - on|off
+// CoerceKeysToLower(true|false) will set the coercion flag on|off
+//
+// NOTE: only recognized by NewMapXml, NewMapXmlReader, and NewMapXmlReaderRaw functions as well as
+// the associated HandleXmlReader and HandleXmlReaderRaw.
+func CoerceKeysToLower(b ...bool) {
+ if len(b) == 0 {
+ lowerCase = !lowerCase
+ } else if len(b) == 1 {
+ lowerCase = b[0]
+ }
+}
+
+// disableTrimWhiteSpace sets if the white space should be removed or not
+var disableTrimWhiteSpace bool
+var trimRunes = "\t\r\b\n "
+
+// DisableTrimWhiteSpace set if the white space should be trimmed or not. By default white space is always trimmed. If
+// no argument is provided, trim white space will be disabled.
+func DisableTrimWhiteSpace(b ...bool) {
+ if len(b) == 0 {
+ disableTrimWhiteSpace = true
+ } else {
+ disableTrimWhiteSpace = b[0]
+ }
+
+ if disableTrimWhiteSpace {
+ trimRunes = "\t\r\b\n"
+ } else {
+ trimRunes = "\t\r\b\n "
+ }
+}
+
+// 25jun16: Allow user to specify the "prefix" character for XML attribute key labels.
+// We do this by replacing '`' constant with attrPrefix var, replacing useHyphen with attrPrefix = "",
+// and adding a SetAttrPrefix(s string) function.
+
+var attrPrefix string = `-` // the default
+var lenAttrPrefix int = 1 // the default
+
+// SetAttrPrefix changes the default, "-", to the specified value, s.
+// SetAttrPrefix("") is the same as PrependAttrWithHyphen(false).
+// (Not applicable for NewMapXmlSeq(), mv.XmlSeq(), etc.)
+func SetAttrPrefix(s string) {
+ attrPrefix = s
+ lenAttrPrefix = len(attrPrefix)
+}
+
+// 18jan17: Allows user to specify if the map keys should be in snake case instead
+// of the default hyphenated notation.
+var snakeCaseKeys bool
+
+// CoerceKeysToSnakeCase changes the default, false, to the specified value, b.
+// Note: the attribute prefix will be a hyphen, '-', or what ever string value has
+// been specified using SetAttrPrefix.
+func CoerceKeysToSnakeCase(b ...bool) {
+ if len(b) == 0 {
+ snakeCaseKeys = !snakeCaseKeys
+ } else if len(b) == 1 {
+ snakeCaseKeys = b[0]
+ }
+}
+
+// 10jan19: use of pull request #57 should be conditional - legacy code assumes
+// numeric values are float64.
+var castToInt bool
+
+// CastValuesToInt tries to coerce numeric valus to int64 or uint64 instead of the
+// default float64. Repeated calls with no argument will toggle this on/off, or this
+// handling will be set with the value of 'b'.
+func CastValuesToInt(b ...bool) {
+ if len(b) == 0 {
+ castToInt = !castToInt
+ } else if len(b) == 1 {
+ castToInt = b[0]
+ }
+}
+
+// 05feb17: support processing XMPP streams (issue #36)
+var handleXMPPStreamTag bool
+
+// HandleXMPPStreamTag causes decoder to parse XMPP elements.
+// If called with no argument, XMPP stream element handling is toggled on/off.
+// (See xmppStream_test.go for example.)
+// If called with NewMapXml, NewMapXmlReader, New MapXmlReaderRaw the "stream"
+// element will be returned as:
+// map["stream"]interface{}{map[-]interface{}}.
+// If called with NewMapSeq, NewMapSeqReader, NewMapSeqReaderRaw the "stream"
+// element will be returned as:
+// map["stream:stream"]interface{}{map["#attr"]interface{}{map[string]interface{}}}
+// where the "#attr" values have "#text" and "#seq" keys. (See NewMapXmlSeq.)
+func HandleXMPPStreamTag(b ...bool) {
+ if len(b) == 0 {
+ handleXMPPStreamTag = !handleXMPPStreamTag
+ } else if len(b) == 1 {
+ handleXMPPStreamTag = b[0]
+ }
+}
+
+// 21jan18 - decode all values as map["#text":value] (issue #56)
+var decodeSimpleValuesAsMap bool
+
+// DecodeSimpleValuesAsMap forces all values to be decoded as map["#text":].
+// If called with no argument, the decoding is toggled on/off.
+//
+// By default the NewMapXml functions decode simple values without attributes as
+// map[:]. This function causes simple values without attributes to be
+// decoded the same as simple values with attributes - map[:map["#text":]].
+func DecodeSimpleValuesAsMap(b ...bool) {
+ if len(b) == 0 {
+ decodeSimpleValuesAsMap = !decodeSimpleValuesAsMap
+ } else if len(b) == 1 {
+ decodeSimpleValuesAsMap = b[0]
+ }
+}
+
+// xmlToMapParser (2015.11.12) - load a 'clean' XML doc into a map[string]interface{} directly.
+// A refactoring of xmlToTreeParser(), markDuplicate() and treeToMap() - here, all-in-one.
+// We've removed the intermediate *node tree with the allocation and subsequent rescanning.
+func xmlToMapParser(skey string, a []xml.Attr, p *xml.Decoder, r bool) (map[string]interface{}, error) {
+ if lowerCase {
+ skey = strings.ToLower(skey)
+ }
+ if snakeCaseKeys {
+ skey = strings.Replace(skey, "-", "_", -1)
+ }
+
+ // NOTE: all attributes and sub-elements parsed into 'na', 'na' is returned as value for 'skey' in 'n'.
+ // Unless 'skey' is a simple element w/o attributes, in which case the xml.CharData value is the value.
+ var n, na map[string]interface{}
+ var seq int // for includeTagSeqNum
+
+ // Allocate maps and load attributes, if any.
+ // NOTE: on entry from NewMapXml(), etc., skey=="", and we fall through
+ // to get StartElement then recurse with skey==xml.StartElement.Name.Local
+ // where we begin allocating map[string]interface{} values 'n' and 'na'.
+ if skey != "" {
+ n = make(map[string]interface{}) // old n
+ na = make(map[string]interface{}) // old n.nodes
+ if len(a) > 0 {
+ for _, v := range a {
+ if snakeCaseKeys {
+ v.Name.Local = strings.Replace(v.Name.Local, "-", "_", -1)
+ }
+ var key string
+ key = attrPrefix + v.Name.Local
+ if lowerCase {
+ key = strings.ToLower(key)
+ }
+ if xmlEscapeCharsDecoder { // per issue#84
+ v.Value = escapeChars(v.Value)
+ }
+ na[key] = cast(v.Value, r, key)
+ }
+ }
+ }
+ // Return XMPP message.
+ if handleXMPPStreamTag && skey == "stream" {
+ n[skey] = na
+ return n, nil
+ }
+
+ for {
+ t, err := p.Token()
+ if err != nil {
+ if err != io.EOF {
+ return nil, errors.New("xml.Decoder.Token() - " + err.Error())
+ }
+ return nil, err
+ }
+ switch t.(type) {
+ case xml.StartElement:
+ tt := t.(xml.StartElement)
+
+ // First call to xmlToMapParser() doesn't pass xml.StartElement - the map key.
+ // So when the loop is first entered, the first token is the root tag along
+ // with any attributes, which we process here.
+ //
+ // Subsequent calls to xmlToMapParser() will pass in tag+attributes for
+ // processing before getting the next token which is the element value,
+ // which is done above.
+ if skey == "" {
+ return xmlToMapParser(tt.Name.Local, tt.Attr, p, r)
+ }
+
+ // If not initializing the map, parse the element.
+ // len(nn) == 1, necessarily - it is just an 'n'.
+ nn, err := xmlToMapParser(tt.Name.Local, tt.Attr, p, r)
+ if err != nil {
+ return nil, err
+ }
+
+ // The nn map[string]interface{} value is a na[nn_key] value.
+ // We need to see if nn_key already exists - means we're parsing a list.
+ // This may require converting na[nn_key] value into []interface{} type.
+ // First, extract the key:val for the map - it's a singleton.
+ // Note:
+ // * if CoerceKeysToLower() called, then key will be lower case.
+ // * if CoerceKeysToSnakeCase() called, then key will be converted to snake case.
+ var key string
+ var val interface{}
+ for key, val = range nn {
+ break
+ }
+
+ // IncludeTagSeqNum requests that the element be augmented with a "_seq" sub-element.
+ // In theory, we don't need this if len(na) == 1. But, we don't know what might
+ // come next - we're only parsing forward. So if you ask for 'includeTagSeqNum' you
+ // get it on every element. (Personally, I never liked this, but I added it on request
+ // and did get a $50 Amazon gift card in return - now we support it for backwards compatibility!)
+ if includeTagSeqNum {
+ switch val.(type) {
+ case []interface{}:
+ // noop - There's no clean way to handle this w/o changing message structure.
+ case map[string]interface{}:
+ val.(map[string]interface{})["_seq"] = seq // will overwrite an "_seq" XML tag
+ seq++
+ case interface{}: // a non-nil simple element: string, float64, bool
+ v := map[string]interface{}{"#text": val}
+ v["_seq"] = seq
+ seq++
+ val = v
+ }
+ }
+
+ // 'na' holding sub-elements of n.
+ // See if 'key' already exists.
+ // If 'key' exists, then this is a list, if not just add key:val to na.
+ if v, ok := na[key]; ok {
+ var a []interface{}
+ switch v.(type) {
+ case []interface{}:
+ a = v.([]interface{})
+ default: // anything else - note: v.(type) != nil
+ a = []interface{}{v}
+ }
+ a = append(a, val)
+ na[key] = a
+ } else {
+ na[key] = val // save it as a singleton
+ }
+ case xml.EndElement:
+ // len(n) > 0 if this is a simple element w/o xml.Attrs - see xml.CharData case.
+ if len(n) == 0 {
+ // If len(na)==0 we have an empty element == "";
+ // it has no xml.Attr nor xml.CharData.
+ // Note: in original node-tree parser, val defaulted to "";
+ // so we always had the default if len(node.nodes) == 0.
+ if len(na) > 0 {
+ n[skey] = na
+ } else {
+ n[skey] = "" // empty element
+ }
+ } else if len(n) == 1 && len(na) > 0 {
+ // it's a simple element w/ no attributes w/ subelements
+ for _, v := range n {
+ na["#text"] = v
+ }
+ n[skey] = na
+ }
+ return n, nil
+ case xml.CharData:
+ // clean up possible noise
+ tt := strings.Trim(string(t.(xml.CharData)), trimRunes)
+ if xmlEscapeCharsDecoder { // issue#84
+ tt = escapeChars(tt)
+ }
+ if len(tt) > 0 {
+ if len(na) > 0 || decodeSimpleValuesAsMap {
+ na["#text"] = cast(tt, r, "#text")
+ } else if skey != "" {
+ n[skey] = cast(tt, r, skey)
+ } else {
+ // per Adrian (http://www.adrianlungu.com/) catch stray text
+ // in decoder stream -
+ // https://github.com/clbanning/mxj/pull/14#issuecomment-182816374
+ // NOTE: CharSetReader must be set to non-UTF-8 CharSet or you'll get
+ // a p.Token() decoding error when the BOM is UTF-16 or UTF-32.
+ continue
+ }
+ }
+ default:
+ // noop
+ }
+ }
+}
+
+var castNanInf bool
+
+// Cast "Nan", "Inf", "-Inf" XML values to 'float64'.
+// By default, these values will be decoded as 'string'.
+func CastNanInf(b ...bool) {
+ if len(b) == 0 {
+ castNanInf = !castNanInf
+ } else if len(b) == 1 {
+ castNanInf = b[0]
+ }
+}
+
+// cast - try to cast string values to bool or float64
+// 't' is the tag key that can be checked for 'not-casting'
+func cast(s string, r bool, t string) interface{} {
+ if checkTagToSkip != nil && t != "" && checkTagToSkip(t) {
+ // call the check-function here with 't[0]'
+ // if 'true' return s
+ return s
+ }
+
+ if r {
+ // handle nan and inf
+ if !castNanInf {
+ switch strings.ToLower(s) {
+ case "nan", "inf", "-inf":
+ return s
+ }
+ }
+
+ // handle numeric strings ahead of boolean
+ if castToInt {
+ if f, err := strconv.ParseInt(s, 10, 64); err == nil {
+ return f
+ }
+ if f, err := strconv.ParseUint(s, 10, 64); err == nil {
+ return f
+ }
+ }
+
+ if castToFloat {
+ if f, err := strconv.ParseFloat(s, 64); err == nil {
+ return f
+ }
+ }
+
+ // ParseBool treats "1"==true & "0"==false, we've already scanned those
+ // values as float64. See if value has 't' or 'f' as initial screen to
+ // minimize calls to ParseBool; also, see if len(s) < 6.
+ if castToBool {
+ if len(s) > 0 && len(s) < 6 {
+ switch s[:1] {
+ case "t", "T", "f", "F":
+ if b, err := strconv.ParseBool(s); err == nil {
+ return b
+ }
+ }
+ }
+ }
+ }
+ return s
+}
+
+// pull request, #59
+var castToFloat = true
+
+// CastValuesToFloat can be used to skip casting to float64 when
+// "cast" argument is 'true' in NewMapXml, etc.
+// Default is true.
+func CastValuesToFloat(b ...bool) {
+ if len(b) == 0 {
+ castToFloat = !castToFloat
+ } else if len(b) == 1 {
+ castToFloat = b[0]
+ }
+}
+
+var castToBool = true
+
+// CastValuesToBool can be used to skip casting to bool when
+// "cast" argument is 'true' in NewMapXml, etc.
+// Default is true.
+func CastValuesToBool(b ...bool) {
+ if len(b) == 0 {
+ castToBool = !castToBool
+ } else if len(b) == 1 {
+ castToBool = b[0]
+ }
+}
+
+// checkTagToSkip - switch to address Issue #58
+
+var checkTagToSkip func(string) bool
+
+// SetCheckTagToSkipFunc registers function to test whether the value
+// for a tag should be cast to bool or float64 when "cast" argument is 'true'.
+// (Dot tag path notation is not supported.)
+// NOTE: key may be "#text" if it's a simple element with attributes
+// or "decodeSimpleValuesAsMap == true".
+// NOTE: does not apply to NewMapXmlSeq... functions.
+func SetCheckTagToSkipFunc(fn func(string) bool) {
+ checkTagToSkip = fn
+}
+
+// ------------------ END: NewMapXml & NewMapXmlReader -------------------------
+
+// ------------------ mv.Xml & mv.XmlWriter - from j2x ------------------------
+
+const (
+ DefaultRootTag = "doc"
+)
+
+var useGoXmlEmptyElemSyntax bool
+
+// XmlGoEmptyElemSyntax() - rather than .
+// Go's encoding/xml package marshals empty XML elements as . By default this package
+// encodes empty elements as . If you're marshaling Map values that include structures
+// (which are passed to xml.Marshal for encoding), this will let you conform to the standard package.
+func XmlGoEmptyElemSyntax() {
+ useGoXmlEmptyElemSyntax = true
+}
+
+// XmlDefaultEmptyElemSyntax() - rather than .
+// Return XML encoding for empty elements to the default package setting.
+// Reverses effect of XmlGoEmptyElemSyntax().
+func XmlDefaultEmptyElemSyntax() {
+ useGoXmlEmptyElemSyntax = false
+}
+
+// ------- issue #88 ----------
+// xmlCheckIsValid set switch to force decoding the encoded XML to
+// see if it is valid XML.
+var xmlCheckIsValid bool
+
+// XmlCheckIsValid forces the encoded XML to be checked for validity.
+func XmlCheckIsValid(b ...bool) {
+ if len(b) == 1 {
+ xmlCheckIsValid = b[0]
+ return
+ }
+ xmlCheckIsValid = !xmlCheckIsValid
+}
+
+// Encode a Map as XML. The companion of NewMapXml().
+// The following rules apply.
+// - The key label "#text" is treated as the value for a simple element with attributes.
+// - Map keys that begin with a hyphen, '-', are interpreted as attributes.
+// It is an error if the attribute doesn't have a []byte, string, number, or boolean value.
+// - Map value type encoding:
+// > string, bool, float64, int, int32, int64, float32: per "%v" formating
+// > []bool, []uint8: by casting to string
+// > structures, etc.: handed to xml.Marshal() - if there is an error, the element
+// value is "UNKNOWN"
+// - Elements with only attribute values or are null are terminated using "/>".
+// - If len(mv) == 1 and no rootTag is provided, then the map key is used as the root tag, possible.
+// Thus, `{ "key":"value" }` encodes as "value".
+// - To encode empty elements in a syntax consistent with encoding/xml call UseGoXmlEmptyElementSyntax().
+// The attributes tag=value pairs are alphabetized by "tag". Also, when encoding map[string]interface{} values -
+// complex elements, etc. - the key:value pairs are alphabetized by key so the resulting tags will appear sorted.
+func (mv Map) Xml(rootTag ...string) ([]byte, error) {
+ m := map[string]interface{}(mv)
+ var err error
+ b := new(bytes.Buffer)
+ p := new(pretty) // just a stub
+
+ if len(m) == 1 && len(rootTag) == 0 {
+ for key, value := range m {
+ // if it an array, see if all values are map[string]interface{}
+ // we force a new root tag if we'll end up with no key:value in the list
+ // so: key:[string_val, bool:true] --> string_valtrue
+ switch value.(type) {
+ case []interface{}:
+ for _, v := range value.([]interface{}) {
+ switch v.(type) {
+ case map[string]interface{}: // noop
+ default: // anything else
+ err = marshalMapToXmlIndent(false, b, DefaultRootTag, m, p)
+ goto done
+ }
+ }
+ }
+ err = marshalMapToXmlIndent(false, b, key, value, p)
+ }
+ } else if len(rootTag) == 1 {
+ err = marshalMapToXmlIndent(false, b, rootTag[0], m, p)
+ } else {
+ err = marshalMapToXmlIndent(false, b, DefaultRootTag, m, p)
+ }
+done:
+ if xmlCheckIsValid {
+ d := xml.NewDecoder(bytes.NewReader(b.Bytes()))
+ for {
+ _, err = d.Token()
+ if err == io.EOF {
+ err = nil
+ break
+ } else if err != nil {
+ return nil, err
+ }
+ }
+ }
+ return b.Bytes(), err
+}
+
+// The following implementation is provided only for symmetry with NewMapXmlReader[Raw]
+// The names will also provide a key for the number of return arguments.
+
+// Writes the Map as XML on the Writer.
+// See Xml() for encoding rules.
+func (mv Map) XmlWriter(xmlWriter io.Writer, rootTag ...string) error {
+ x, err := mv.Xml(rootTag...)
+ if err != nil {
+ return err
+ }
+
+ _, err = xmlWriter.Write(x)
+ return err
+}
+
+// Writes the Map as XML on the Writer. []byte is the raw XML that was written.
+// See Xml() for encoding rules.
+/*
+func (mv Map) XmlWriterRaw(xmlWriter io.Writer, rootTag ...string) ([]byte, error) {
+ x, err := mv.Xml(rootTag...)
+ if err != nil {
+ return x, err
+ }
+
+ _, err = xmlWriter.Write(x)
+ return x, err
+}
+*/
+
+// Writes the Map as pretty XML on the Writer.
+// See Xml() for encoding rules.
+func (mv Map) XmlIndentWriter(xmlWriter io.Writer, prefix, indent string, rootTag ...string) error {
+ x, err := mv.XmlIndent(prefix, indent, rootTag...)
+ if err != nil {
+ return err
+ }
+
+ _, err = xmlWriter.Write(x)
+ return err
+}
+
+// Writes the Map as pretty XML on the Writer. []byte is the raw XML that was written.
+// See Xml() for encoding rules.
+/*
+func (mv Map) XmlIndentWriterRaw(xmlWriter io.Writer, prefix, indent string, rootTag ...string) ([]byte, error) {
+ x, err := mv.XmlIndent(prefix, indent, rootTag...)
+ if err != nil {
+ return x, err
+ }
+
+ _, err = xmlWriter.Write(x)
+ return x, err
+}
+*/
+
+// -------------------- END: mv.Xml & mv.XmlWriter -------------------------------
+
+// -------------- Handle XML stream by processing Map value --------------------
+
+// Default poll delay to keep Handler from spinning on an open stream
+// like sitting on os.Stdin waiting for imput.
+var xhandlerPollInterval = time.Millisecond
+
+// Bulk process XML using handlers that process a Map value.
+// 'rdr' is an io.Reader for XML (stream)
+// 'mapHandler' is the Map processor. Return of 'false' stops io.Reader processing.
+// 'errHandler' is the error processor. Return of 'false' stops io.Reader processing and returns the error.
+// Note: mapHandler() and errHandler() calls are blocking, so reading and processing of messages is serialized.
+// This means that you can stop reading the file on error or after processing a particular message.
+// To have reading and handling run concurrently, pass argument to a go routine in handler and return 'true'.
+func HandleXmlReader(xmlReader io.Reader, mapHandler func(Map) bool, errHandler func(error) bool) error {
+ var n int
+ for {
+ m, merr := NewMapXmlReader(xmlReader)
+ n++
+
+ // handle error condition with errhandler
+ if merr != nil && merr != io.EOF {
+ merr = fmt.Errorf("[xmlReader: %d] %s", n, merr.Error())
+ if ok := errHandler(merr); !ok {
+ // caused reader termination
+ return merr
+ }
+ continue
+ }
+
+ // pass to maphandler
+ if len(m) != 0 {
+ if ok := mapHandler(m); !ok {
+ break
+ }
+ } else if merr != io.EOF {
+ time.Sleep(xhandlerPollInterval)
+ }
+
+ if merr == io.EOF {
+ break
+ }
+ }
+ return nil
+}
+
+// Bulk process XML using handlers that process a Map value and the raw XML.
+// 'rdr' is an io.Reader for XML (stream)
+// 'mapHandler' is the Map and raw XML - []byte - processor. Return of 'false' stops io.Reader processing.
+// 'errHandler' is the error and raw XML processor. Return of 'false' stops io.Reader processing and returns the error.
+// Note: mapHandler() and errHandler() calls are blocking, so reading and processing of messages is serialized.
+// This means that you can stop reading the file on error or after processing a particular message.
+// To have reading and handling run concurrently, pass argument(s) to a go routine in handler and return 'true'.
+// See NewMapXmlReaderRaw for comment on performance associated with retrieving raw XML from a Reader.
+func HandleXmlReaderRaw(xmlReader io.Reader, mapHandler func(Map, []byte) bool, errHandler func(error, []byte) bool) error {
+ var n int
+ for {
+ m, raw, merr := NewMapXmlReaderRaw(xmlReader)
+ n++
+
+ // handle error condition with errhandler
+ if merr != nil && merr != io.EOF {
+ merr = fmt.Errorf("[xmlReader: %d] %s", n, merr.Error())
+ if ok := errHandler(merr, raw); !ok {
+ // caused reader termination
+ return merr
+ }
+ continue
+ }
+
+ // pass to maphandler
+ if len(m) != 0 {
+ if ok := mapHandler(m, raw); !ok {
+ break
+ }
+ } else if merr != io.EOF {
+ time.Sleep(xhandlerPollInterval)
+ }
+
+ if merr == io.EOF {
+ break
+ }
+ }
+ return nil
+}
+
+// ----------------- END: Handle XML stream by processing Map value --------------
+
+// -------- a hack of io.TeeReader ... need one that's an io.ByteReader for xml.NewDecoder() ----------
+
+// This is a clone of io.TeeReader with the additional method t.ReadByte().
+// Thus, this TeeReader is also an io.ByteReader.
+// This is necessary because xml.NewDecoder uses a ByteReader not a Reader. It appears to have been written
+// with bufio.Reader or bytes.Reader in mind ... not a generic io.Reader, which doesn't have to have ReadByte()..
+// If NewDecoder is passed a Reader that does not satisfy ByteReader() it wraps the Reader with
+// bufio.NewReader and uses ReadByte rather than Read that runs the TeeReader pipe logic.
+
+type teeReader struct {
+ r io.Reader
+ w io.Writer
+ b []byte
+}
+
+func myTeeReader(r io.Reader, w io.Writer) io.Reader {
+ b := make([]byte, 1)
+ return &teeReader{r, w, b}
+}
+
+// need for io.Reader - but we don't use it ...
+func (t *teeReader) Read(p []byte) (int, error) {
+ return 0, nil
+}
+
+func (t *teeReader) ReadByte() (byte, error) {
+ n, err := t.r.Read(t.b)
+ if n > 0 {
+ if _, err := t.w.Write(t.b[:1]); err != nil {
+ return t.b[0], err
+ }
+ }
+ return t.b[0], err
+}
+
+// For use with NewMapXmlReader & NewMapXmlSeqReader.
+type byteReader struct {
+ r io.Reader
+ b []byte
+}
+
+func myByteReader(r io.Reader) io.Reader {
+ b := make([]byte, 1)
+ return &byteReader{r, b}
+}
+
+// Need for io.Reader interface ...
+// Needed if reading a malformed http.Request.Body - issue #38.
+func (b *byteReader) Read(p []byte) (int, error) {
+ return b.r.Read(p)
+}
+
+func (b *byteReader) ReadByte() (byte, error) {
+ _, err := b.r.Read(b.b)
+ if len(b.b) > 0 {
+ return b.b[0], nil
+ }
+ var c byte
+ return c, err
+}
+
+// ----------------------- END: io.TeeReader hack -----------------------------------
+
+// ---------------------- XmlIndent - from j2x package ----------------------------
+
+// Encode a map[string]interface{} as a pretty XML string.
+// See Xml for encoding rules.
+func (mv Map) XmlIndent(prefix, indent string, rootTag ...string) ([]byte, error) {
+ m := map[string]interface{}(mv)
+
+ var err error
+ b := new(bytes.Buffer)
+ p := new(pretty)
+ p.indent = indent
+ p.padding = prefix
+
+ if len(m) == 1 && len(rootTag) == 0 {
+ // this can extract the key for the single map element
+ // use it if it isn't a key for a list
+ for key, value := range m {
+ if _, ok := value.([]interface{}); ok {
+ err = marshalMapToXmlIndent(true, b, DefaultRootTag, m, p)
+ } else {
+ err = marshalMapToXmlIndent(true, b, key, value, p)
+ }
+ }
+ } else if len(rootTag) == 1 {
+ err = marshalMapToXmlIndent(true, b, rootTag[0], m, p)
+ } else {
+ err = marshalMapToXmlIndent(true, b, DefaultRootTag, m, p)
+ }
+ if xmlCheckIsValid {
+ d := xml.NewDecoder(bytes.NewReader(b.Bytes()))
+ for {
+ _, err = d.Token()
+ if err == io.EOF {
+ err = nil
+ break
+ } else if err != nil {
+ return nil, err
+ }
+ }
+ }
+ return b.Bytes(), err
+}
+
+type pretty struct {
+ indent string
+ cnt int
+ padding string
+ mapDepth int
+ start int
+}
+
+func (p *pretty) Indent() {
+ p.padding += p.indent
+ p.cnt++
+}
+
+func (p *pretty) Outdent() {
+ if p.cnt > 0 {
+ p.padding = p.padding[:len(p.padding)-len(p.indent)]
+ p.cnt--
+ }
+}
+
+// where the work actually happens
+// returns an error if an attribute is not atomic
+// NOTE: 01may20 - replaces mapToXmlIndent(); uses bytes.Buffer instead for string appends.
+func marshalMapToXmlIndent(doIndent bool, b *bytes.Buffer, key string, value interface{}, pp *pretty) error {
+ var err error
+ var endTag bool
+ var isSimple bool
+ var elen int
+ p := &pretty{pp.indent, pp.cnt, pp.padding, pp.mapDepth, pp.start}
+
+ // per issue #48, 18apr18 - try and coerce maps to map[string]interface{}
+ // Don't need for mapToXmlSeqIndent, since maps there are decoded by NewMapXmlSeq().
+ if reflect.ValueOf(value).Kind() == reflect.Map {
+ switch value.(type) {
+ case map[string]interface{}:
+ default:
+ val := make(map[string]interface{})
+ vv := reflect.ValueOf(value)
+ keys := vv.MapKeys()
+ for _, k := range keys {
+ val[fmt.Sprint(k)] = vv.MapIndex(k).Interface()
+ }
+ value = val
+ }
+ }
+
+ // 14jul20. The following block of code has become something of a catch all for odd stuff
+ // that might be passed in as a result of casting an arbitrary map[] to an mxj.Map
+ // value and then call m.Xml or m.XmlIndent. See issue #71 (and #73) for such edge cases.
+ switch value.(type) {
+ // these types are handled during encoding
+ case map[string]interface{}, []byte, string, float64, bool, int, int32, int64, float32, json.Number:
+ case []map[string]interface{}, []string, []float64, []bool, []int, []int32, []int64, []float32, []json.Number:
+ case []interface{}:
+ case nil:
+ value = ""
+ default:
+ // see if value is a struct, if so marshal using encoding/xml package
+ if reflect.ValueOf(value).Kind() == reflect.Struct {
+ if v, err := xml.Marshal(value); err != nil {
+ return err
+ } else {
+ value = string(v)
+ }
+ } else {
+ // coerce eveything else into a string value
+ value = fmt.Sprint(value)
+ }
+ }
+
+ // start the XML tag with required indentaton and padding
+ if doIndent {
+ if _, err = b.WriteString(p.padding); err != nil {
+ return err
+ }
+ }
+ switch value.(type) {
+ case []interface{}:
+ default:
+ if _, err = b.WriteString(`<` + key); err != nil {
+ return err
+ }
+ }
+
+ switch value.(type) {
+ case map[string]interface{}:
+ vv := value.(map[string]interface{})
+ lenvv := len(vv)
+ // scan out attributes - attribute keys have prepended attrPrefix
+ attrlist := make([][2]string, len(vv))
+ var n int
+ var ss string
+ for k, v := range vv {
+ if lenAttrPrefix > 0 && lenAttrPrefix < len(k) && k[:lenAttrPrefix] == attrPrefix {
+ switch v.(type) {
+ case string:
+ if xmlEscapeChars {
+ ss = escapeChars(v.(string))
+ } else {
+ ss = v.(string)
+ }
+ attrlist[n][0] = k[lenAttrPrefix:]
+ attrlist[n][1] = ss
+ case float64, bool, int, int32, int64, float32, json.Number:
+ attrlist[n][0] = k[lenAttrPrefix:]
+ attrlist[n][1] = fmt.Sprintf("%v", v)
+ case []byte:
+ if xmlEscapeChars {
+ ss = escapeChars(string(v.([]byte)))
+ } else {
+ ss = string(v.([]byte))
+ }
+ attrlist[n][0] = k[lenAttrPrefix:]
+ attrlist[n][1] = ss
+ default:
+ return fmt.Errorf("invalid attribute value for: %s:<%T>", k, v)
+ }
+ n++
+ }
+ }
+ if n > 0 {
+ attrlist = attrlist[:n]
+ sort.Sort(attrList(attrlist))
+ for _, v := range attrlist {
+ if _, err = b.WriteString(` ` + v[0] + `="` + v[1] + `"`); err != nil {
+ return err
+ }
+ }
+ }
+ // only attributes?
+ if n == lenvv {
+ if useGoXmlEmptyElemSyntax {
+ if _, err = b.WriteString(`` + key + ">"); err != nil {
+ return err
+ }
+ } else {
+ if _, err = b.WriteString(`/>`); err != nil {
+ return err
+ }
+ }
+ break
+ }
+
+ // simple element? Note: '#text" is an invalid XML tag.
+ isComplex := false
+ if v, ok := vv["#text"]; ok && n+1 == lenvv {
+ // just the value and attributes
+ switch v.(type) {
+ case string:
+ if xmlEscapeChars {
+ v = escapeChars(v.(string))
+ } else {
+ v = v.(string)
+ }
+ case []byte:
+ if xmlEscapeChars {
+ v = escapeChars(string(v.([]byte)))
+ } else {
+ v = string(v.([]byte))
+ }
+ }
+ if _, err = b.WriteString(">" + fmt.Sprintf("%v", v)); err != nil {
+ return err
+ }
+ endTag = true
+ elen = 1
+ isSimple = true
+ break
+ } else if ok {
+ // need to handle when there are subelements in addition to the simple element value
+ // issue #90
+ switch v.(type) {
+ case string:
+ if xmlEscapeChars {
+ v = escapeChars(v.(string))
+ } else {
+ v = v.(string)
+ }
+ case []byte:
+ if xmlEscapeChars {
+ v = escapeChars(string(v.([]byte)))
+ } else {
+ v = string(v.([]byte))
+ }
+ }
+ if _, err = b.WriteString(">" + fmt.Sprintf("%v", v)); err != nil {
+ return err
+ }
+ isComplex = true
+ }
+
+ // close tag with possible attributes
+ if !isComplex {
+ if _, err = b.WriteString(">"); err != nil {
+ return err
+ }
+ }
+ if doIndent {
+ // *s += "\n"
+ if _, err = b.WriteString("\n"); err != nil {
+ return err
+ }
+ }
+ // something more complex
+ p.mapDepth++
+ // extract the map k:v pairs and sort on key
+ elemlist := make([][2]interface{}, len(vv))
+ n = 0
+ for k, v := range vv {
+ if k == "#text" {
+ // simple element handled above
+ continue
+ }
+ if lenAttrPrefix > 0 && lenAttrPrefix < len(k) && k[:lenAttrPrefix] == attrPrefix {
+ continue
+ }
+ elemlist[n][0] = k
+ elemlist[n][1] = v
+ n++
+ }
+ elemlist = elemlist[:n]
+ sort.Sort(elemList(elemlist))
+ var i int
+ for _, v := range elemlist {
+ switch v[1].(type) {
+ case []interface{}:
+ default:
+ if i == 0 && doIndent {
+ p.Indent()
+ }
+ }
+ i++
+ if err := marshalMapToXmlIndent(doIndent, b, v[0].(string), v[1], p); err != nil {
+ return err
+ }
+ switch v[1].(type) {
+ case []interface{}: // handled in []interface{} case
+ default:
+ if doIndent {
+ p.Outdent()
+ }
+ }
+ i--
+ }
+ p.mapDepth--
+ endTag = true
+ elen = 1 // we do have some content ...
+ case []interface{}:
+ // special case - found during implementing Issue #23
+ if len(value.([]interface{})) == 0 {
+ if doIndent {
+ if _, err = b.WriteString(p.padding + p.indent); err != nil {
+ return err
+ }
+ }
+ if _, err = b.WriteString("<" + key); err != nil {
+ return err
+ }
+ elen = 0
+ endTag = true
+ break
+ }
+ for _, v := range value.([]interface{}) {
+ if doIndent {
+ p.Indent()
+ }
+ if err := marshalMapToXmlIndent(doIndent, b, key, v, p); err != nil {
+ return err
+ }
+ if doIndent {
+ p.Outdent()
+ }
+ }
+ return nil
+ case []string:
+ // This was added by https://github.com/slotix ... not a type that
+ // would be encountered if mv generated from NewMapXml, NewMapJson.
+ // Could be encountered in AnyXml(), so we'll let it stay, though
+ // it should be merged with case []interface{}, above.
+ //quick fix for []string type
+ //[]string should be treated exaclty as []interface{}
+ if len(value.([]string)) == 0 {
+ if doIndent {
+ if _, err = b.WriteString(p.padding + p.indent); err != nil {
+ return err
+ }
+ }
+ if _, err = b.WriteString("<" + key); err != nil {
+ return err
+ }
+ elen = 0
+ endTag = true
+ break
+ }
+ for _, v := range value.([]string) {
+ if doIndent {
+ p.Indent()
+ }
+ if err := marshalMapToXmlIndent(doIndent, b, key, v, p); err != nil {
+ return err
+ }
+ if doIndent {
+ p.Outdent()
+ }
+ }
+ return nil
+ case nil:
+ // terminate the tag
+ if doIndent {
+ // *s += p.padding
+ if _, err = b.WriteString(p.padding); err != nil {
+ return err
+ }
+ }
+ if _, err = b.WriteString("<" + key); err != nil {
+ return err
+ }
+ endTag, isSimple = true, true
+ break
+ default: // handle anything - even goofy stuff
+ elen = 0
+ switch value.(type) {
+ case string:
+ v := value.(string)
+ if xmlEscapeChars {
+ v = escapeChars(v)
+ }
+ elen = len(v)
+ if elen > 0 {
+ // *s += ">" + v
+ if _, err = b.WriteString(">" + v); err != nil {
+ return err
+ }
+ }
+ case float64, bool, int, int32, int64, float32, json.Number:
+ v := fmt.Sprintf("%v", value)
+ elen = len(v) // always > 0
+ if _, err = b.WriteString(">" + v); err != nil {
+ return err
+ }
+ case []byte: // NOTE: byte is just an alias for uint8
+ // similar to how xml.Marshal handles []byte structure members
+ v := string(value.([]byte))
+ if xmlEscapeChars {
+ v = escapeChars(v)
+ }
+ elen = len(v)
+ if elen > 0 {
+ // *s += ">" + v
+ if _, err = b.WriteString(">" + v); err != nil {
+ return err
+ }
+ }
+ default:
+ if _, err = b.WriteString(">"); err != nil {
+ return err
+ }
+ var v []byte
+ var err error
+ if doIndent {
+ v, err = xml.MarshalIndent(value, p.padding, p.indent)
+ } else {
+ v, err = xml.Marshal(value)
+ }
+ if err != nil {
+ if _, err = b.WriteString(">UNKNOWN"); err != nil {
+ return err
+ }
+ } else {
+ elen = len(v)
+ if elen > 0 {
+ if _, err = b.Write(v); err != nil {
+ return err
+ }
+ }
+ }
+ }
+ isSimple = true
+ endTag = true
+ }
+ if endTag {
+ if doIndent {
+ if !isSimple {
+ if _, err = b.WriteString(p.padding); err != nil {
+ return err
+ }
+ }
+ }
+ if elen > 0 || useGoXmlEmptyElemSyntax {
+ if elen == 0 {
+ if _, err = b.WriteString(">"); err != nil {
+ return err
+ }
+ }
+ if _, err = b.WriteString(`` + key + ">"); err != nil {
+ return err
+ }
+ } else {
+ if _, err = b.WriteString(`/>`); err != nil {
+ return err
+ }
+ }
+ }
+ if doIndent {
+ if p.cnt > p.start {
+ if _, err = b.WriteString("\n"); err != nil {
+ return err
+ }
+ }
+ p.Outdent()
+ }
+
+ return nil
+}
+
+// ============================ sort interface implementation =================
+
+type attrList [][2]string
+
+func (a attrList) Len() int {
+ return len(a)
+}
+
+func (a attrList) Swap(i, j int) {
+ a[i], a[j] = a[j], a[i]
+}
+
+func (a attrList) Less(i, j int) bool {
+ return a[i][0] <= a[j][0]
+}
+
+type elemList [][2]interface{}
+
+func (e elemList) Len() int {
+ return len(e)
+}
+
+func (e elemList) Swap(i, j int) {
+ e[i], e[j] = e[j], e[i]
+}
+
+func (e elemList) Less(i, j int) bool {
+ return e[i][0].(string) <= e[j][0].(string)
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/clbanning/mxj/v2/xmlseq.go b/go/ql/test/experimental/CWE-321/vendor/github.com/clbanning/mxj/v2/xmlseq.go
new file mode 100644
index 000000000000..80632bd3c3bc
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/clbanning/mxj/v2/xmlseq.go
@@ -0,0 +1,877 @@
+// Copyright 2012-2016, 2019 Charles Banning. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file
+
+// xmlseq.go - version of xml.go with sequence # injection on Decoding and sorting on Encoding.
+// Also, handles comments, directives and process instructions.
+
+package mxj
+
+import (
+ "bytes"
+ "encoding/xml"
+ "errors"
+ "fmt"
+ "io"
+ "sort"
+ "strings"
+)
+
+// MapSeq is like Map but contains seqencing indices to allow recovering the original order of
+// the XML elements when the map[string]interface{} is marshaled. Element attributes are
+// stored as a map["#attr"]map[]map[string]interface{}{"#text":"", "#seq":}
+// value instead of denoting the keys with a prefix character. Also, comments, directives and
+// process instructions are preserved.
+type MapSeq map[string]interface{}
+
+// NoRoot is returned by NewXmlSeq, etc., when a comment, directive or procinstr element is parsed
+// in the XML data stream and the element is not contained in an XML object with a root element.
+var NoRoot = errors.New("no root key")
+var NO_ROOT = NoRoot // maintain backwards compatibility
+
+// ------------------- NewMapXmlSeq & NewMapXmlSeqReader ... -------------------------
+
+// NewMapXmlSeq converts a XML doc into a MapSeq value with elements id'd with decoding sequence key represented
+// as map["#seq"].
+// If the optional argument 'cast' is 'true', then values will be converted to boolean or float64 if possible.
+// NOTE: "#seq" key/value pairs are removed on encoding with msv.Xml() / msv.XmlIndent().
+// • attributes are a map - map["#attr"]map["attr_key"]map[string]interface{}{"#text":, "#seq":}
+// • all simple elements are decoded as map["#text"]interface{} with a "#seq" k:v pair, as well.
+// • lists always decode as map["list_tag"][]map[string]interface{} where the array elements are maps that
+// include a "#seq" k:v pair based on sequence they are decoded. Thus, XML like:
+//
+// value 1
+// value 2
+// value 3
+//
+// is decoded as:
+// doc :
+// ltag :[[]interface{}]
+// [item: 0]
+// #seq :[int] 0
+// #text :[string] value 1
+// [item: 1]
+// #seq :[int] 2
+// #text :[string] value 3
+// newtag :
+// #seq :[int] 1
+// #text :[string] value 2
+// It will encode in proper sequence even though the MapSeq representation merges all "ltag" elements in an array.
+// • comments - "" - are decoded as map["#comment"]map["#text"]"cmnt_text" with a "#seq" k:v pair.
+// • directives - "" - are decoded as map["#directive"]map[#text"]"directive_text" with a "#seq" k:v pair.
+// • process instructions - "" - are decoded as map["#procinst"]interface{} where the #procinst value
+// is of map[string]interface{} type with the following keys: #target, #inst, and #seq.
+// • comments, directives, and procinsts that are NOT part of a document with a root key will be returned as
+// map[string]interface{} and the error value 'NoRoot'.
+// • note: ": tag preserve the
+// ":" notation rather than stripping it as with NewMapXml().
+// 2. Attribute keys for name space prefix declarations preserve "xmlns:" notation.
+//
+// ERRORS:
+// 1. If a NoRoot error, "no root key," is returned, check the initial map key for a "#comment",
+// "#directive" or #procinst" key.
+func NewMapXmlSeq(xmlVal []byte, cast ...bool) (MapSeq, error) {
+ var r bool
+ if len(cast) == 1 {
+ r = cast[0]
+ }
+ return xmlSeqToMap(xmlVal, r)
+}
+
+// NewMpaXmlSeqReader returns next XML doc from an io.Reader as a MapSeq value.
+// NOTES:
+// 1. The 'xmlReader' will be parsed looking for an xml.StartElement, xml.Comment, etc., so BOM and other
+// extraneous xml.CharData will be ignored unless io.EOF is reached first.
+// 2. CoerceKeysToLower() is NOT recognized, since the intent here is to eventually call m.XmlSeq() to
+// re-encode the message in its original structure.
+// 3. If CoerceKeysToSnakeCase() has been called, then all key values will be converted to snake case.
+//
+// ERRORS:
+// 1. If a NoRoot error, "no root key," is returned, check the initial map key for a "#comment",
+// "#directive" or #procinst" key.
+func NewMapXmlSeqReader(xmlReader io.Reader, cast ...bool) (MapSeq, error) {
+ var r bool
+ if len(cast) == 1 {
+ r = cast[0]
+ }
+
+ // We need to put an *os.File reader in a ByteReader or the xml.NewDecoder
+ // will wrap it in a bufio.Reader and seek on the file beyond where the
+ // xml.Decoder parses!
+ if _, ok := xmlReader.(io.ByteReader); !ok {
+ xmlReader = myByteReader(xmlReader) // see code at EOF
+ }
+
+ // build the map
+ return xmlSeqReaderToMap(xmlReader, r)
+}
+
+// NewMapXmlSeqReaderRaw returns the next XML doc from an io.Reader as a MapSeq value.
+// Returns MapSeq value, slice with the raw XML, and any error.
+// NOTES:
+// 1. Due to the implementation of xml.Decoder, the raw XML off the reader is buffered to []byte
+// using a ByteReader. If the io.Reader is an os.File, there may be significant performance impact.
+// See the examples - getmetrics1.go through getmetrics4.go - for comparative use cases on a large
+// data set. If the io.Reader is wrapping a []byte value in-memory, however, such as http.Request.Body
+// you CAN use it to efficiently unmarshal a XML doc and retrieve the raw XML in a single call.
+// 2. The 'raw' return value may be larger than the XML text value.
+// 3. The 'xmlReader' will be parsed looking for an xml.StartElement, xml.Comment, etc., so BOM and other
+// extraneous xml.CharData will be ignored unless io.EOF is reached first.
+// 4. CoerceKeysToLower() is NOT recognized, since the intent here is to eventually call m.XmlSeq() to
+// re-encode the message in its original structure.
+// 5. If CoerceKeysToSnakeCase() has been called, then all key values will be converted to snake case.
+//
+// ERRORS:
+// 1. If a NoRoot error, "no root key," is returned, check if the initial map key is "#comment",
+// "#directive" or #procinst" key.
+func NewMapXmlSeqReaderRaw(xmlReader io.Reader, cast ...bool) (MapSeq, []byte, error) {
+ var r bool
+ if len(cast) == 1 {
+ r = cast[0]
+ }
+ // create TeeReader so we can retrieve raw XML
+ buf := make([]byte, 0)
+ wb := bytes.NewBuffer(buf)
+ trdr := myTeeReader(xmlReader, wb)
+
+ m, err := xmlSeqReaderToMap(trdr, r)
+
+ // retrieve the raw XML that was decoded
+ b := wb.Bytes()
+
+ // err may be NoRoot
+ return m, b, err
+}
+
+// xmlSeqReaderToMap() - parse a XML io.Reader to a map[string]interface{} value
+func xmlSeqReaderToMap(rdr io.Reader, r bool) (map[string]interface{}, error) {
+ // parse the Reader
+ p := xml.NewDecoder(rdr)
+ if CustomDecoder != nil {
+ useCustomDecoder(p)
+ } else {
+ p.CharsetReader = XmlCharsetReader
+ }
+ return xmlSeqToMapParser("", nil, p, r)
+}
+
+// xmlSeqToMap - convert a XML doc into map[string]interface{} value
+func xmlSeqToMap(doc []byte, r bool) (map[string]interface{}, error) {
+ b := bytes.NewReader(doc)
+ p := xml.NewDecoder(b)
+ if CustomDecoder != nil {
+ useCustomDecoder(p)
+ } else {
+ p.CharsetReader = XmlCharsetReader
+ }
+ return xmlSeqToMapParser("", nil, p, r)
+}
+
+// ===================================== where the work happens =============================
+
+// xmlSeqToMapParser - load a 'clean' XML doc into a map[string]interface{} directly.
+// Add #seq tag value for each element decoded - to be used for Encoding later.
+func xmlSeqToMapParser(skey string, a []xml.Attr, p *xml.Decoder, r bool) (map[string]interface{}, error) {
+ if snakeCaseKeys {
+ skey = strings.Replace(skey, "-", "_", -1)
+ }
+
+ // NOTE: all attributes and sub-elements parsed into 'na', 'na' is returned as value for 'skey' in 'n'.
+ var n, na map[string]interface{}
+ var seq int // for including seq num when decoding
+
+ // Allocate maps and load attributes, if any.
+ // NOTE: on entry from NewMapXml(), etc., skey=="", and we fall through
+ // to get StartElement then recurse with skey==xml.StartElement.Name.Local
+ // where we begin allocating map[string]interface{} values 'n' and 'na'.
+ if skey != "" {
+ // 'n' only needs one slot - save call to runtime•hashGrow()
+ // 'na' we don't know
+ n = make(map[string]interface{}, 1)
+ na = make(map[string]interface{})
+ if len(a) > 0 {
+ // xml.Attr is decoded into: map["#attr"]map[]interface{}
+ // where interface{} is map[string]interface{}{"#text":, "#seq":}
+ aa := make(map[string]interface{}, len(a))
+ for i, v := range a {
+ if snakeCaseKeys {
+ v.Name.Local = strings.Replace(v.Name.Local, "-", "_", -1)
+ }
+ if xmlEscapeCharsDecoder { // per issue#84
+ v.Value = escapeChars(v.Value)
+ }
+ if len(v.Name.Space) > 0 {
+ aa[v.Name.Space+`:`+v.Name.Local] = map[string]interface{}{"#text": cast(v.Value, r, ""), "#seq": i}
+ } else {
+ aa[v.Name.Local] = map[string]interface{}{"#text": cast(v.Value, r, ""), "#seq": i}
+ }
+ }
+ na["#attr"] = aa
+ }
+ }
+
+ // Return XMPP message.
+ if handleXMPPStreamTag && skey == "stream:stream" {
+ n[skey] = na
+ return n, nil
+ }
+
+ for {
+ t, err := p.RawToken()
+ if err != nil {
+ if err != io.EOF {
+ return nil, errors.New("xml.Decoder.Token() - " + err.Error())
+ }
+ return nil, err
+ }
+ switch t.(type) {
+ case xml.StartElement:
+ tt := t.(xml.StartElement)
+
+ // First call to xmlSeqToMapParser() doesn't pass xml.StartElement - the map key.
+ // So when the loop is first entered, the first token is the root tag along
+ // with any attributes, which we process here.
+ //
+ // Subsequent calls to xmlSeqToMapParser() will pass in tag+attributes for
+ // processing before getting the next token which is the element value,
+ // which is done above.
+ if skey == "" {
+ if len(tt.Name.Space) > 0 {
+ return xmlSeqToMapParser(tt.Name.Space+`:`+tt.Name.Local, tt.Attr, p, r)
+ } else {
+ return xmlSeqToMapParser(tt.Name.Local, tt.Attr, p, r)
+ }
+ }
+
+ // If not initializing the map, parse the element.
+ // len(nn) == 1, necessarily - it is just an 'n'.
+ var nn map[string]interface{}
+ if len(tt.Name.Space) > 0 {
+ nn, err = xmlSeqToMapParser(tt.Name.Space+`:`+tt.Name.Local, tt.Attr, p, r)
+ } else {
+ nn, err = xmlSeqToMapParser(tt.Name.Local, tt.Attr, p, r)
+ }
+ if err != nil {
+ return nil, err
+ }
+
+ // The nn map[string]interface{} value is a na[nn_key] value.
+ // We need to see if nn_key already exists - means we're parsing a list.
+ // This may require converting na[nn_key] value into []interface{} type.
+ // First, extract the key:val for the map - it's a singleton.
+ var key string
+ var val interface{}
+ for key, val = range nn {
+ break
+ }
+
+ // add "#seq" k:v pair -
+ // Sequence number included even in list elements - this should allow us
+ // to properly resequence even something goofy like:
+ // item 1
+ // item 2
+ // item 3
+ // where all the "list" subelements are decoded into an array.
+ switch val.(type) {
+ case map[string]interface{}:
+ val.(map[string]interface{})["#seq"] = seq
+ seq++
+ case interface{}: // a non-nil simple element: string, float64, bool
+ v := map[string]interface{}{"#text": val, "#seq": seq}
+ seq++
+ val = v
+ }
+
+ // 'na' holding sub-elements of n.
+ // See if 'key' already exists.
+ // If 'key' exists, then this is a list, if not just add key:val to na.
+ if v, ok := na[key]; ok {
+ var a []interface{}
+ switch v.(type) {
+ case []interface{}:
+ a = v.([]interface{})
+ default: // anything else - note: v.(type) != nil
+ a = []interface{}{v}
+ }
+ a = append(a, val)
+ na[key] = a
+ } else {
+ na[key] = val // save it as a singleton
+ }
+ case xml.EndElement:
+ if skey != "" {
+ tt := t.(xml.EndElement)
+ if snakeCaseKeys {
+ tt.Name.Local = strings.Replace(tt.Name.Local, "-", "_", -1)
+ }
+ var name string
+ if len(tt.Name.Space) > 0 {
+ name = tt.Name.Space + `:` + tt.Name.Local
+ } else {
+ name = tt.Name.Local
+ }
+ if skey != name {
+ return nil, fmt.Errorf("element %s not properly terminated, got %s at #%d",
+ skey, name, p.InputOffset())
+ }
+ }
+ // len(n) > 0 if this is a simple element w/o xml.Attrs - see xml.CharData case.
+ if len(n) == 0 {
+ // If len(na)==0 we have an empty element == "";
+ // it has no xml.Attr nor xml.CharData.
+ // Empty element content will be map["etag"]map["#text"]""
+ // after #seq injection - map["etag"]map["#seq"]seq - after return.
+ if len(na) > 0 {
+ n[skey] = na
+ } else {
+ n[skey] = "" // empty element
+ }
+ }
+ return n, nil
+ case xml.CharData:
+ // clean up possible noise
+ tt := strings.Trim(string(t.(xml.CharData)), trimRunes)
+ if xmlEscapeCharsDecoder { // issue#84
+ tt = escapeChars(tt)
+ }
+ if skey == "" {
+ // per Adrian (http://www.adrianlungu.com/) catch stray text
+ // in decoder stream -
+ // https://github.com/clbanning/mxj/pull/14#issuecomment-182816374
+ // NOTE: CharSetReader must be set to non-UTF-8 CharSet or you'll get
+ // a p.Token() decoding error when the BOM is UTF-16 or UTF-32.
+ continue
+ }
+ if len(tt) > 0 {
+ // every simple element is a #text and has #seq associated with it
+ na["#text"] = cast(tt, r, "")
+ na["#seq"] = seq
+ seq++
+ }
+ case xml.Comment:
+ if n == nil { // no root 'key'
+ n = map[string]interface{}{"#comment": string(t.(xml.Comment))}
+ return n, NoRoot
+ }
+ cm := make(map[string]interface{}, 2)
+ cm["#text"] = string(t.(xml.Comment))
+ cm["#seq"] = seq
+ seq++
+ na["#comment"] = cm
+ case xml.Directive:
+ if n == nil { // no root 'key'
+ n = map[string]interface{}{"#directive": string(t.(xml.Directive))}
+ return n, NoRoot
+ }
+ dm := make(map[string]interface{}, 2)
+ dm["#text"] = string(t.(xml.Directive))
+ dm["#seq"] = seq
+ seq++
+ na["#directive"] = dm
+ case xml.ProcInst:
+ if n == nil {
+ na = map[string]interface{}{"#target": t.(xml.ProcInst).Target, "#inst": string(t.(xml.ProcInst).Inst)}
+ n = map[string]interface{}{"#procinst": na}
+ return n, NoRoot
+ }
+ pm := make(map[string]interface{}, 3)
+ pm["#target"] = t.(xml.ProcInst).Target
+ pm["#inst"] = string(t.(xml.ProcInst).Inst)
+ pm["#seq"] = seq
+ seq++
+ na["#procinst"] = pm
+ default:
+ // noop - shouldn't ever get here, now, since we handle all token types
+ }
+ }
+}
+
+// ------------------ END: NewMapXml & NewMapXmlReader -------------------------
+
+// --------------------- mv.XmlSeq & mv.XmlSeqWriter -------------------------
+
+// Xml encodes a MapSeq as XML with elements sorted on #seq. The companion of NewMapXmlSeq().
+// The following rules apply.
+// - The "#seq" key value is used to seqence the subelements or attributes only.
+// - The "#attr" map key identifies the map of attribute map[string]interface{} values with "#text" key.
+// - The "#comment" map key identifies a comment in the value "#text" map entry - .
+// - The "#directive" map key identifies a directive in the value "#text" map entry - .
+// - The "#procinst" map key identifies a process instruction in the value "#target" and "#inst"
+// map entries - .
+// - Value type encoding:
+// > string, bool, float64, int, int32, int64, float32: per "%v" formating
+// > []bool, []uint8: by casting to string
+// > structures, etc.: handed to xml.Marshal() - if there is an error, the element
+// value is "UNKNOWN"
+// - Elements with only attribute values or are null are terminated using "/>" unless XmlGoEmptyElemSystax() called.
+// - If len(mv) == 1 and no rootTag is provided, then the map key is used as the root tag, possible.
+// Thus, `{ "key":"value" }` encodes as "value".
+func (mv MapSeq) Xml(rootTag ...string) ([]byte, error) {
+ m := map[string]interface{}(mv)
+ var err error
+ s := new(string)
+ p := new(pretty) // just a stub
+
+ if len(m) == 1 && len(rootTag) == 0 {
+ for key, value := range m {
+ // if it's an array, see if all values are map[string]interface{}
+ // we force a new root tag if we'll end up with no key:value in the list
+ // so: key:[string_val, bool:true] --> string_valtrue
+ switch value.(type) {
+ case []interface{}:
+ for _, v := range value.([]interface{}) {
+ switch v.(type) {
+ case map[string]interface{}: // noop
+ default: // anything else
+ err = mapToXmlSeqIndent(false, s, DefaultRootTag, m, p)
+ goto done
+ }
+ }
+ }
+ err = mapToXmlSeqIndent(false, s, key, value, p)
+ }
+ } else if len(rootTag) == 1 {
+ err = mapToXmlSeqIndent(false, s, rootTag[0], m, p)
+ } else {
+ err = mapToXmlSeqIndent(false, s, DefaultRootTag, m, p)
+ }
+done:
+ if xmlCheckIsValid {
+ d := xml.NewDecoder(bytes.NewReader([]byte(*s)))
+ for {
+ _, err = d.Token()
+ if err == io.EOF {
+ err = nil
+ break
+ } else if err != nil {
+ return nil, err
+ }
+ }
+ }
+ return []byte(*s), err
+}
+
+// The following implementation is provided only for symmetry with NewMapXmlReader[Raw]
+// The names will also provide a key for the number of return arguments.
+
+// XmlWriter Writes the MapSeq value as XML on the Writer.
+// See MapSeq.Xml() for encoding rules.
+func (mv MapSeq) XmlWriter(xmlWriter io.Writer, rootTag ...string) error {
+ x, err := mv.Xml(rootTag...)
+ if err != nil {
+ return err
+ }
+
+ _, err = xmlWriter.Write(x)
+ return err
+}
+
+// XmlWriteRaw writes the MapSeq value as XML on the Writer. []byte is the raw XML that was written.
+// See Map.XmlSeq() for encoding rules.
+/*
+func (mv MapSeq) XmlWriterRaw(xmlWriter io.Writer, rootTag ...string) ([]byte, error) {
+ x, err := mv.Xml(rootTag...)
+ if err != nil {
+ return x, err
+ }
+
+ _, err = xmlWriter.Write(x)
+ return x, err
+}
+*/
+
+// XmlIndentWriter writes the MapSeq value as pretty XML on the Writer.
+// See MapSeq.Xml() for encoding rules.
+func (mv MapSeq) XmlIndentWriter(xmlWriter io.Writer, prefix, indent string, rootTag ...string) error {
+ x, err := mv.XmlIndent(prefix, indent, rootTag...)
+ if err != nil {
+ return err
+ }
+
+ _, err = xmlWriter.Write(x)
+ return err
+}
+
+// XmlIndentWriterRaw writes the Map as pretty XML on the Writer. []byte is the raw XML that was written.
+// See Map.XmlSeq() for encoding rules.
+/*
+func (mv MapSeq) XmlIndentWriterRaw(xmlWriter io.Writer, prefix, indent string, rootTag ...string) ([]byte, error) {
+ x, err := mv.XmlSeqIndent(prefix, indent, rootTag...)
+ if err != nil {
+ return x, err
+ }
+
+ _, err = xmlWriter.Write(x)
+ return x, err
+}
+*/
+
+// -------------------- END: mv.Xml & mv.XmlWriter -------------------------------
+
+// ---------------------- XmlSeqIndent ----------------------------
+
+// XmlIndent encodes a map[string]interface{} as a pretty XML string.
+// See MapSeq.XmlSeq() for encoding rules.
+func (mv MapSeq) XmlIndent(prefix, indent string, rootTag ...string) ([]byte, error) {
+ m := map[string]interface{}(mv)
+
+ var err error
+ s := new(string)
+ p := new(pretty)
+ p.indent = indent
+ p.padding = prefix
+
+ if len(m) == 1 && len(rootTag) == 0 {
+ // this can extract the key for the single map element
+ // use it if it isn't a key for a list
+ for key, value := range m {
+ if _, ok := value.([]interface{}); ok {
+ err = mapToXmlSeqIndent(true, s, DefaultRootTag, m, p)
+ } else {
+ err = mapToXmlSeqIndent(true, s, key, value, p)
+ }
+ }
+ } else if len(rootTag) == 1 {
+ err = mapToXmlSeqIndent(true, s, rootTag[0], m, p)
+ } else {
+ err = mapToXmlSeqIndent(true, s, DefaultRootTag, m, p)
+ }
+ if xmlCheckIsValid {
+ if _, err = NewMapXml([]byte(*s)); err != nil {
+ return nil, err
+ }
+ d := xml.NewDecoder(bytes.NewReader([]byte(*s)))
+ for {
+ _, err = d.Token()
+ if err == io.EOF {
+ err = nil
+ break
+ } else if err != nil {
+ return nil, err
+ }
+ }
+ }
+ return []byte(*s), err
+}
+
+// where the work actually happens
+// returns an error if an attribute is not atomic
+func mapToXmlSeqIndent(doIndent bool, s *string, key string, value interface{}, pp *pretty) error {
+ var endTag bool
+ var isSimple bool
+ var noEndTag bool
+ var elen int
+ var ss string
+ p := &pretty{pp.indent, pp.cnt, pp.padding, pp.mapDepth, pp.start}
+
+ switch value.(type) {
+ case map[string]interface{}, []byte, string, float64, bool, int, int32, int64, float32:
+ if doIndent {
+ *s += p.padding
+ }
+ if key != "#comment" && key != "#directive" && key != "#procinst" {
+ *s += `<` + key
+ }
+ }
+ switch value.(type) {
+ case map[string]interface{}:
+ val := value.(map[string]interface{})
+
+ if key == "#comment" {
+ *s += ``
+ noEndTag = true
+ break
+ }
+
+ if key == "#directive" {
+ *s += ``
+ noEndTag = true
+ break
+ }
+
+ if key == "#procinst" {
+ *s += `` + val["#target"].(string) + ` ` + val["#inst"].(string) + `?>`
+ noEndTag = true
+ break
+ }
+
+ haveAttrs := false
+ // process attributes first
+ if v, ok := val["#attr"].(map[string]interface{}); ok {
+ // First, unroll the map[string]interface{} into a []keyval array.
+ // Then sequence it.
+ kv := make([]keyval, len(v))
+ n := 0
+ for ak, av := range v {
+ kv[n] = keyval{ak, av}
+ n++
+ }
+ sort.Sort(elemListSeq(kv))
+ // Now encode the attributes in original decoding sequence, using keyval array.
+ for _, a := range kv {
+ vv := a.v.(map[string]interface{})
+ switch vv["#text"].(type) {
+ case string:
+ if xmlEscapeChars {
+ ss = escapeChars(vv["#text"].(string))
+ } else {
+ ss = vv["#text"].(string)
+ }
+ *s += ` ` + a.k + `="` + ss + `"`
+ case float64, bool, int, int32, int64, float32:
+ *s += ` ` + a.k + `="` + fmt.Sprintf("%v", vv["#text"]) + `"`
+ case []byte:
+ if xmlEscapeChars {
+ ss = escapeChars(string(vv["#text"].([]byte)))
+ } else {
+ ss = string(vv["#text"].([]byte))
+ }
+ *s += ` ` + a.k + `="` + ss + `"`
+ default:
+ return fmt.Errorf("invalid attribute value for: %s", a.k)
+ }
+ }
+ haveAttrs = true
+ }
+
+ // simple element?
+ // every map value has, at least, "#seq" and, perhaps, "#text" and/or "#attr"
+ _, seqOK := val["#seq"] // have key
+ if v, ok := val["#text"]; ok && ((len(val) == 3 && haveAttrs) || (len(val) == 2 && !haveAttrs)) && seqOK {
+ if stmp, ok := v.(string); ok && stmp != "" {
+ if xmlEscapeChars {
+ stmp = escapeChars(stmp)
+ }
+ *s += ">" + stmp
+ endTag = true
+ elen = 1
+ }
+ isSimple = true
+ break
+ } else if !ok && ((len(val) == 2 && haveAttrs) || (len(val) == 1 && !haveAttrs)) && seqOK {
+ // here no #text but have #seq or #seq+#attr
+ endTag = false
+ break
+ }
+
+ // we now need to sequence everything except attributes
+ // 'kv' will hold everything that needs to be written
+ kv := make([]keyval, 0)
+ for k, v := range val {
+ if k == "#attr" { // already processed
+ continue
+ }
+ if k == "#seq" { // ignore - just for sorting
+ continue
+ }
+ switch v.(type) {
+ case []interface{}:
+ // unwind the array as separate entries
+ for _, vv := range v.([]interface{}) {
+ kv = append(kv, keyval{k, vv})
+ }
+ default:
+ kv = append(kv, keyval{k, v})
+ }
+ }
+
+ // close tag with possible attributes
+ *s += ">"
+ if doIndent {
+ *s += "\n"
+ }
+ // something more complex
+ p.mapDepth++
+ sort.Sort(elemListSeq(kv))
+ i := 0
+ for _, v := range kv {
+ switch v.v.(type) {
+ case []interface{}:
+ default:
+ if i == 0 && doIndent {
+ p.Indent()
+ }
+ }
+ i++
+ if err := mapToXmlSeqIndent(doIndent, s, v.k, v.v, p); err != nil {
+ return err
+ }
+ switch v.v.(type) {
+ case []interface{}: // handled in []interface{} case
+ default:
+ if doIndent {
+ p.Outdent()
+ }
+ }
+ i--
+ }
+ p.mapDepth--
+ endTag = true
+ elen = 1 // we do have some content other than attrs
+ case []interface{}:
+ for _, v := range value.([]interface{}) {
+ if doIndent {
+ p.Indent()
+ }
+ if err := mapToXmlSeqIndent(doIndent, s, key, v, p); err != nil {
+ return err
+ }
+ if doIndent {
+ p.Outdent()
+ }
+ }
+ return nil
+ case nil:
+ // terminate the tag
+ if doIndent {
+ *s += p.padding
+ }
+ *s += "<" + key
+ endTag, isSimple = true, true
+ break
+ default: // handle anything - even goofy stuff
+ elen = 0
+ switch value.(type) {
+ case string:
+ if xmlEscapeChars {
+ ss = escapeChars(value.(string))
+ } else {
+ ss = value.(string)
+ }
+ elen = len(ss)
+ if elen > 0 {
+ *s += ">" + ss
+ }
+ case float64, bool, int, int32, int64, float32:
+ v := fmt.Sprintf("%v", value)
+ elen = len(v)
+ if elen > 0 {
+ *s += ">" + v
+ }
+ case []byte: // NOTE: byte is just an alias for uint8
+ // similar to how xml.Marshal handles []byte structure members
+ if xmlEscapeChars {
+ ss = escapeChars(string(value.([]byte)))
+ } else {
+ ss = string(value.([]byte))
+ }
+ elen = len(ss)
+ if elen > 0 {
+ *s += ">" + ss
+ }
+ default:
+ var v []byte
+ var err error
+ if doIndent {
+ v, err = xml.MarshalIndent(value, p.padding, p.indent)
+ } else {
+ v, err = xml.Marshal(value)
+ }
+ if err != nil {
+ *s += ">UNKNOWN"
+ } else {
+ elen = len(v)
+ if elen > 0 {
+ *s += string(v)
+ }
+ }
+ }
+ isSimple = true
+ endTag = true
+ }
+ if endTag && !noEndTag {
+ if doIndent {
+ if !isSimple {
+ *s += p.padding
+ }
+ }
+ switch value.(type) {
+ case map[string]interface{}, []byte, string, float64, bool, int, int32, int64, float32:
+ if elen > 0 || useGoXmlEmptyElemSyntax {
+ if elen == 0 {
+ *s += ">"
+ }
+ *s += `` + key + ">"
+ } else {
+ *s += `/>`
+ }
+ }
+ } else if !noEndTag {
+ if useGoXmlEmptyElemSyntax {
+ *s += `` + key + ">"
+ // *s += ">" + key + ">"
+ } else {
+ *s += "/>"
+ }
+ }
+ if doIndent {
+ if p.cnt > p.start {
+ *s += "\n"
+ }
+ p.Outdent()
+ }
+
+ return nil
+}
+
+// the element sort implementation
+
+type keyval struct {
+ k string
+ v interface{}
+}
+type elemListSeq []keyval
+
+func (e elemListSeq) Len() int {
+ return len(e)
+}
+
+func (e elemListSeq) Swap(i, j int) {
+ e[i], e[j] = e[j], e[i]
+}
+
+func (e elemListSeq) Less(i, j int) bool {
+ var iseq, jseq int
+ var fiseq, fjseq float64
+ var ok bool
+ if iseq, ok = e[i].v.(map[string]interface{})["#seq"].(int); !ok {
+ if fiseq, ok = e[i].v.(map[string]interface{})["#seq"].(float64); ok {
+ iseq = int(fiseq)
+ } else {
+ iseq = 9999999
+ }
+ }
+
+ if jseq, ok = e[j].v.(map[string]interface{})["#seq"].(int); !ok {
+ if fjseq, ok = e[j].v.(map[string]interface{})["#seq"].(float64); ok {
+ jseq = int(fjseq)
+ } else {
+ jseq = 9999999
+ }
+ }
+
+ return iseq <= jseq
+}
+
+// =============== https://groups.google.com/forum/#!topic/golang-nuts/lHPOHD-8qio
+
+// BeautifyXml (re)formats an XML doc similar to Map.XmlIndent().
+// It preserves comments, directives and process instructions,
+func BeautifyXml(b []byte, prefix, indent string) ([]byte, error) {
+ x, err := NewMapXmlSeq(b)
+ if err != nil {
+ return nil, err
+ }
+ return x.XmlIndent(prefix, indent)
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/clbanning/mxj/v2/xmlseq2.go b/go/ql/test/experimental/CWE-321/vendor/github.com/clbanning/mxj/v2/xmlseq2.go
new file mode 100644
index 000000000000..467fd07697d3
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/clbanning/mxj/v2/xmlseq2.go
@@ -0,0 +1,18 @@
+// Copyright 2012-2016, 2019 Charles Banning. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file
+
+package mxj
+
+// ---------------- expose Map methods to MapSeq type ---------------------------
+
+// Pretty print a Map.
+func (msv MapSeq) StringIndent(offset ...int) string {
+ return writeMap(map[string]interface{}(msv), true, true, offset...)
+}
+
+// Pretty print a Map without the value type information - just key:value entries.
+func (msv MapSeq) StringIndentNoTypeInfo(offset ...int) string {
+ return writeMap(map[string]interface{}(msv), false, true, offset...)
+}
+
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/cristalhq/jwt/v3/LICENSE b/go/ql/test/experimental/CWE-321/vendor/github.com/cristalhq/jwt/v3/LICENSE
new file mode 100644
index 000000000000..ce4fabaf8c21
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/cristalhq/jwt/v3/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2019 Oleg Kovalov
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/cristalhq/jwt/v3/README.md b/go/ql/test/experimental/CWE-321/vendor/github.com/cristalhq/jwt/v3/README.md
new file mode 100644
index 000000000000..f00036667eee
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/cristalhq/jwt/v3/README.md
@@ -0,0 +1,113 @@
+# jwt
+
+[![build-img]][build-url]
+[![pkg-img]][pkg-url]
+[![reportcard-img]][reportcard-url]
+[![coverage-img]][coverage-url]
+
+JSON Web Token for Go [RFC 7519](https://tools.ietf.org/html/rfc7519), also see [jwt.io](https://jwt.io) for more.
+
+The latest version is `v3`.
+
+## Rationale
+
+There are many JWT libraries, but many of them are hard to use (unclear or fixed API), not optimal (unneeded allocations + strange API). This library addresses all these issues. It's simple to read, to use, memory and CPU conservative.
+
+## Features
+
+* Simple API.
+* Clean and tested code.
+* Optimized for speed.
+* Concurrent-safe.
+* Dependency-free.
+* All well-known algorithms are supported
+ * HMAC (HS)
+ * RSA (RS)
+ * RSA-PSS (PS)
+ * ECDSA (ES)
+ * EdDSA (EdDSA)
+ * or your own!
+
+## Install
+
+Go version 1.13+
+
+```
+GO111MODULE=on go get github.com/cristalhq/jwt/v3
+```
+
+## Example
+
+Build new token:
+
+```go
+// create a Signer (HMAC in this example)
+key := []byte(`secret`)
+signer, err := jwt.NewSignerHS(jwt.HS256, key)
+checkErr(err)
+
+// create claims (you can create your own, see: Example_BuildUserClaims)
+claims := &jwt.RegisteredClaims{
+ Audience: []string{"admin"},
+ ID: "random-unique-string",
+}
+
+// create a Builder
+builder := jwt.NewBuilder(signer)
+
+// and build a Token
+token, err := builder.Build(claims)
+checkErr(err)
+
+// here is token as byte slice
+var _ []byte = token.Bytes() // or just token.String() for string
+```
+
+Parse and verify token:
+```go
+// create a Verifier (HMAC in this example)
+key := []byte(`secret`)
+verifier, err := jwt.NewVerifierHS(jwt.HS256, key)
+checkErr(err)
+
+// parse a Token (by example received from a request)
+tokenStr := ``
+token, err := jwt.ParseString(tokenStr)
+checkErr(err)
+
+// and verify it's signature
+err = verifier.Verify(token.Payload(), token.Signature())
+checkErr(err)
+
+// also you can parse and verify together
+newToken, err := jwt.ParseAndVerifyString(tokenStr, verifier)
+checkErr(err)
+
+// get standard claims
+var newClaims jwt.StandardClaims
+errClaims := json.Unmarshal(newToken.RawClaims(), &newClaims)
+checkErr(errClaims)
+
+// verify claims as you
+var _ bool = newClaims.IsForAudience("admin")
+var _ bool = newClaims.IsValidAt(time.Now())
+```
+
+Also see examples: [example_test.go](https://github.com/cristalhq/jwt/blob/master/example_test.go).
+
+## Documentation
+
+See [these docs][pkg-url].
+
+## License
+
+[MIT License](LICENSE).
+
+[build-img]: https://github.com/cristalhq/jwt/workflows/build/badge.svg
+[build-url]: https://github.com/cristalhq/jwt/actions
+[pkg-img]: https://pkg.go.dev/badge/cristalhq/jwt/v3
+[pkg-url]: https://pkg.go.dev/github.com/cristalhq/jwt/v3
+[reportcard-img]: https://goreportcard.com/badge/cristalhq/jwt
+[reportcard-url]: https://goreportcard.com/report/cristalhq/jwt
+[coverage-img]: https://codecov.io/gh/cristalhq/jwt/branch/master/graph/badge.svg
+[coverage-url]: https://codecov.io/gh/cristalhq/jwt
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/cristalhq/jwt/v3/algo.go b/go/ql/test/experimental/CWE-321/vendor/github.com/cristalhq/jwt/v3/algo.go
new file mode 100644
index 000000000000..c812a3c2278d
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/cristalhq/jwt/v3/algo.go
@@ -0,0 +1,81 @@
+package jwt
+
+import (
+ "crypto"
+ _ "crypto/sha256" // to register a hash
+ _ "crypto/sha512" // to register a hash
+)
+
+// Signer is used to sign tokens.
+type Signer interface {
+ Algorithm() Algorithm
+ SignSize() int
+ Sign(payload []byte) ([]byte, error)
+}
+
+// Verifier is used to verify tokens.
+type Verifier interface {
+ Algorithm() Algorithm
+ Verify(payload, signature []byte) error
+}
+
+// Algorithm for signing and verifying.
+type Algorithm string
+
+func (a Algorithm) String() string { return string(a) }
+
+// keySize of the algorithm's key (if exist). Is similar to Signer.SignSize.
+func (a Algorithm) keySize() int { return algsKeySize[a] }
+
+var algsKeySize = map[Algorithm]int{
+ // for EdDSA private and public key have different sizes, so 0
+ // for HS there is no limits for key size, so 0
+
+ RS256: 256,
+ RS384: 384,
+ RS512: 512,
+
+ ES256: 64,
+ ES384: 96,
+ ES512: 132,
+
+ PS256: 256,
+ PS384: 384,
+ PS512: 512,
+}
+
+// Algorithm names for signing and verifying.
+const (
+ EdDSA Algorithm = "EdDSA"
+
+ HS256 Algorithm = "HS256"
+ HS384 Algorithm = "HS384"
+ HS512 Algorithm = "HS512"
+
+ RS256 Algorithm = "RS256"
+ RS384 Algorithm = "RS384"
+ RS512 Algorithm = "RS512"
+
+ ES256 Algorithm = "ES256"
+ ES384 Algorithm = "ES384"
+ ES512 Algorithm = "ES512"
+
+ PS256 Algorithm = "PS256"
+ PS384 Algorithm = "PS384"
+ PS512 Algorithm = "PS512"
+)
+
+func hashPayload(hash crypto.Hash, payload []byte) ([]byte, error) {
+ hasher := hash.New()
+
+ _, err := hasher.Write(payload)
+ if err != nil {
+ return nil, err
+ }
+ signed := hasher.Sum(nil)
+ return signed, nil
+}
+
+func constTimeAlgEqual(a, b Algorithm) bool {
+ return constTimeEqual(a.String(), b.String())
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/cristalhq/jwt/v3/algo_eddsa.go b/go/ql/test/experimental/CWE-321/vendor/github.com/cristalhq/jwt/v3/algo_eddsa.go
new file mode 100644
index 000000000000..1af3d3ffabaf
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/cristalhq/jwt/v3/algo_eddsa.go
@@ -0,0 +1,65 @@
+package jwt
+
+import (
+ "crypto/ed25519"
+)
+
+// NewSignerEdDSA returns a new ed25519-based signer.
+func NewSignerEdDSA(key ed25519.PrivateKey) (Signer, error) {
+ if len(key) == 0 {
+ return nil, ErrNilKey
+ }
+ if len(key) != ed25519.PrivateKeySize {
+ return nil, ErrInvalidKey
+ }
+ return &edDSAAlg{
+ alg: EdDSA,
+ privateKey: key,
+ }, nil
+}
+
+// NewVerifierEdDSA returns a new ed25519-based verifier.
+func NewVerifierEdDSA(key ed25519.PublicKey) (Verifier, error) {
+ if len(key) == 0 {
+ return nil, ErrNilKey
+ }
+ if len(key) != ed25519.PublicKeySize {
+ return nil, ErrInvalidKey
+ }
+ return &edDSAAlg{
+ alg: EdDSA,
+ publicKey: key,
+ }, nil
+}
+
+type edDSAAlg struct {
+ alg Algorithm
+ publicKey ed25519.PublicKey
+ privateKey ed25519.PrivateKey
+}
+
+func (ed *edDSAAlg) Algorithm() Algorithm {
+ return ed.alg
+}
+
+func (ed *edDSAAlg) SignSize() int {
+ return ed25519.SignatureSize
+}
+
+func (ed *edDSAAlg) Sign(payload []byte) ([]byte, error) {
+ return ed25519.Sign(ed.privateKey, payload), nil
+}
+
+func (ed *edDSAAlg) VerifyToken(token *Token) error {
+ if constTimeAlgEqual(token.Header().Algorithm, ed.alg) {
+ return ed.Verify(token.Payload(), token.Signature())
+ }
+ return ErrAlgorithmMismatch
+}
+
+func (ed *edDSAAlg) Verify(payload, signature []byte) error {
+ if !ed25519.Verify(ed.publicKey, payload, signature) {
+ return ErrInvalidSignature
+ }
+ return nil
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/cristalhq/jwt/v3/algo_es.go b/go/ql/test/experimental/CWE-321/vendor/github.com/cristalhq/jwt/v3/algo_es.go
new file mode 100644
index 000000000000..2b3126c8a7f8
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/cristalhq/jwt/v3/algo_es.go
@@ -0,0 +1,132 @@
+package jwt
+
+import (
+ "crypto"
+ "crypto/ecdsa"
+ "crypto/rand"
+ "math/big"
+)
+
+// NewSignerES returns a new ECDSA-based signer.
+func NewSignerES(alg Algorithm, key *ecdsa.PrivateKey) (Signer, error) {
+ if key == nil {
+ return nil, ErrNilKey
+ }
+ hash, err := getParamsES(alg, roundBytes(key.PublicKey.Params().BitSize)*2)
+ if err != nil {
+ return nil, err
+ }
+ return &esAlg{
+ alg: alg,
+ hash: hash,
+ privateKey: key,
+ signSize: roundBytes(key.PublicKey.Params().BitSize) * 2,
+ }, nil
+}
+
+// NewVerifierES returns a new ECDSA-based verifier.
+func NewVerifierES(alg Algorithm, key *ecdsa.PublicKey) (Verifier, error) {
+ if key == nil {
+ return nil, ErrNilKey
+ }
+ hash, err := getParamsES(alg, roundBytes(key.Params().BitSize)*2)
+ if err != nil {
+ return nil, err
+ }
+ return &esAlg{
+ alg: alg,
+ hash: hash,
+ publicKey: key,
+ signSize: roundBytes(key.Params().BitSize) * 2,
+ }, nil
+}
+
+func getParamsES(alg Algorithm, size int) (crypto.Hash, error) {
+ var hash crypto.Hash
+ switch alg {
+ case ES256:
+ hash = crypto.SHA256
+ case ES384:
+ hash = crypto.SHA384
+ case ES512:
+ hash = crypto.SHA512
+ default:
+ return 0, ErrUnsupportedAlg
+ }
+
+ if alg.keySize() != size {
+ return 0, ErrInvalidKey
+ }
+ return hash, nil
+}
+
+type esAlg struct {
+ alg Algorithm
+ hash crypto.Hash
+ publicKey *ecdsa.PublicKey
+ privateKey *ecdsa.PrivateKey
+ signSize int
+}
+
+func (es *esAlg) Algorithm() Algorithm {
+ return es.alg
+}
+
+func (es *esAlg) SignSize() int {
+ return es.signSize
+}
+
+func (es *esAlg) Sign(payload []byte) ([]byte, error) {
+ digest, err := hashPayload(es.hash, payload)
+ if err != nil {
+ return nil, err
+ }
+
+ r, s, errSign := ecdsa.Sign(rand.Reader, es.privateKey, digest)
+ if err != nil {
+ return nil, errSign
+ }
+
+ pivot := es.SignSize() / 2
+
+ rBytes, sBytes := r.Bytes(), s.Bytes()
+ signature := make([]byte, es.SignSize())
+ copy(signature[pivot-len(rBytes):], rBytes)
+ copy(signature[pivot*2-len(sBytes):], sBytes)
+ return signature, nil
+}
+
+func (es *esAlg) VerifyToken(token *Token) error {
+ if constTimeAlgEqual(token.Header().Algorithm, es.alg) {
+ return es.Verify(token.Payload(), token.Signature())
+ }
+ return ErrAlgorithmMismatch
+}
+
+func (es *esAlg) Verify(payload, signature []byte) error {
+ if len(signature) != es.SignSize() {
+ return ErrInvalidSignature
+ }
+
+ digest, err := hashPayload(es.hash, payload)
+ if err != nil {
+ return err
+ }
+
+ pivot := es.SignSize() / 2
+ r := big.NewInt(0).SetBytes(signature[:pivot])
+ s := big.NewInt(0).SetBytes(signature[pivot:])
+
+ if !ecdsa.Verify(es.publicKey, digest, r, s) {
+ return ErrInvalidSignature
+ }
+ return nil
+}
+
+func roundBytes(n int) int {
+ res := n / 8
+ if n%8 > 0 {
+ return res + 1
+ }
+ return res
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/cristalhq/jwt/v3/algo_hs.go b/go/ql/test/experimental/CWE-321/vendor/github.com/cristalhq/jwt/v3/algo_hs.go
new file mode 100644
index 000000000000..9438fbff7f99
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/cristalhq/jwt/v3/algo_hs.go
@@ -0,0 +1,111 @@
+package jwt
+
+import (
+ "crypto"
+ "crypto/hmac"
+ "hash"
+ "sync"
+)
+
+// NewSignerHS returns a new HMAC-based signer.
+func NewSignerHS(alg Algorithm, key []byte) (Signer, error) {
+ return newHS(alg, key)
+}
+
+// NewVerifierHS returns a new HMAC-based verifier.
+func NewVerifierHS(alg Algorithm, key []byte) (Verifier, error) {
+ return newHS(alg, key)
+}
+
+type hmacAlgo interface {
+ // copy-pasted Signer & Verifier due to older Go versions
+ Algorithm() Algorithm
+ SignSize() int
+ Sign(payload []byte) ([]byte, error)
+ Verify(payload, signature []byte) error
+ VerifyToken(token *Token) error
+}
+
+func newHS(alg Algorithm, key []byte) (hmacAlgo, error) {
+ if len(key) == 0 {
+ return nil, ErrNilKey
+ }
+ hash, ok := getHashHMAC(alg)
+ if !ok {
+ return nil, ErrUnsupportedAlg
+ }
+ return &hsAlg{
+ alg: alg,
+ hash: hash,
+ key: key,
+ hashPool: &sync.Pool{
+ New: func() interface{} {
+ return hmac.New(hash.New, key)
+ },
+ },
+ }, nil
+}
+
+func getHashHMAC(alg Algorithm) (crypto.Hash, bool) {
+ switch alg {
+ case HS256:
+ return crypto.SHA256, true
+ case HS384:
+ return crypto.SHA384, true
+ case HS512:
+ return crypto.SHA512, true
+ default:
+ return 0, false
+ }
+}
+
+type hsAlg struct {
+ alg Algorithm
+ hash crypto.Hash
+ key []byte
+ hashPool *sync.Pool
+}
+
+func (hs *hsAlg) Algorithm() Algorithm {
+ return hs.alg
+}
+
+func (hs *hsAlg) SignSize() int {
+ return hs.hash.Size()
+}
+
+func (hs *hsAlg) Sign(payload []byte) ([]byte, error) {
+ return hs.sign(payload)
+}
+
+func (hs *hsAlg) VerifyToken(token *Token) error {
+ if constTimeAlgEqual(token.Header().Algorithm, hs.alg) {
+ return hs.Verify(token.Payload(), token.Signature())
+ }
+ return ErrAlgorithmMismatch
+}
+
+func (hs *hsAlg) Verify(payload, signature []byte) error {
+ digest, err := hs.sign(payload)
+ if err != nil {
+ return err
+ }
+ if !hmac.Equal(signature, digest) {
+ return ErrInvalidSignature
+ }
+ return nil
+}
+
+func (hs *hsAlg) sign(payload []byte) ([]byte, error) {
+ hasher := hs.hashPool.Get().(hash.Hash)
+ defer func() {
+ hasher.Reset()
+ hs.hashPool.Put(hasher)
+ }()
+
+ _, err := hasher.Write(payload)
+ if err != nil {
+ return nil, err
+ }
+ return hasher.Sum(nil), nil
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/cristalhq/jwt/v3/algo_ps.go b/go/ql/test/experimental/CWE-321/vendor/github.com/cristalhq/jwt/v3/algo_ps.go
new file mode 100644
index 000000000000..21d552d37976
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/cristalhq/jwt/v3/algo_ps.go
@@ -0,0 +1,127 @@
+package jwt
+
+import (
+ "crypto"
+ "crypto/rand"
+ "crypto/rsa"
+)
+
+// NewSignerPS returns a new RSA-PSS-based signer.
+func NewSignerPS(alg Algorithm, key *rsa.PrivateKey) (Signer, error) {
+ if key == nil {
+ return nil, ErrNilKey
+ }
+ hash, opts, err := getParamsPS(alg, key.Size())
+ if err != nil {
+ return nil, err
+ }
+ return &psAlg{
+ alg: alg,
+ hash: hash,
+ privateKey: key,
+ opts: opts,
+ }, nil
+}
+
+// NewVerifierPS returns a new RSA-PSS-based signer.
+func NewVerifierPS(alg Algorithm, key *rsa.PublicKey) (Verifier, error) {
+ if key == nil {
+ return nil, ErrNilKey
+ }
+ hash, opts, err := getParamsPS(alg, key.Size())
+ if err != nil {
+ return nil, err
+ }
+ return &psAlg{
+ alg: alg,
+ hash: hash,
+ publicKey: key,
+ opts: opts,
+ }, nil
+}
+
+func getParamsPS(alg Algorithm, size int) (crypto.Hash, *rsa.PSSOptions, error) {
+ var hash crypto.Hash
+ var opts *rsa.PSSOptions
+ switch alg {
+ case PS256:
+ hash, opts = crypto.SHA256, optsPS256
+ case PS384:
+ hash, opts = crypto.SHA384, optsPS384
+ case PS512:
+ hash, opts = crypto.SHA512, optsPS512
+ default:
+ return 0, nil, ErrUnsupportedAlg
+ }
+
+ if alg.keySize() != size {
+ return 0, nil, ErrInvalidKey
+ }
+ return hash, opts, nil
+}
+
+var (
+ optsPS256 = &rsa.PSSOptions{
+ SaltLength: rsa.PSSSaltLengthAuto,
+ Hash: crypto.SHA256,
+ }
+
+ optsPS384 = &rsa.PSSOptions{
+ SaltLength: rsa.PSSSaltLengthAuto,
+ Hash: crypto.SHA384,
+ }
+
+ optsPS512 = &rsa.PSSOptions{
+ SaltLength: rsa.PSSSaltLengthAuto,
+ Hash: crypto.SHA512,
+ }
+)
+
+type psAlg struct {
+ alg Algorithm
+ hash crypto.Hash
+ publicKey *rsa.PublicKey
+ privateKey *rsa.PrivateKey
+ opts *rsa.PSSOptions
+}
+
+func (ps *psAlg) SignSize() int {
+ return ps.privateKey.Size()
+}
+
+func (ps *psAlg) Algorithm() Algorithm {
+ return ps.alg
+}
+
+func (ps *psAlg) Sign(payload []byte) ([]byte, error) {
+ digest, err := hashPayload(ps.hash, payload)
+ if err != nil {
+ return nil, err
+ }
+
+ signature, errSign := rsa.SignPSS(rand.Reader, ps.privateKey, ps.hash, digest, ps.opts)
+ if errSign != nil {
+ return nil, errSign
+ }
+ return signature, nil
+}
+
+func (ps *psAlg) VerifyToken(token *Token) error {
+ if constTimeAlgEqual(token.Header().Algorithm, ps.alg) {
+ return ps.Verify(token.Payload(), token.Signature())
+ }
+ return ErrAlgorithmMismatch
+}
+
+func (ps *psAlg) Verify(payload, signature []byte) error {
+ digest, err := hashPayload(ps.hash, payload)
+ if err != nil {
+ return err
+ }
+
+ errVerify := rsa.VerifyPSS(ps.publicKey, ps.hash, digest, signature, ps.opts)
+ if errVerify != nil {
+ return ErrInvalidSignature
+ }
+ return nil
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/cristalhq/jwt/v3/algo_rs.go b/go/ql/test/experimental/CWE-321/vendor/github.com/cristalhq/jwt/v3/algo_rs.go
new file mode 100644
index 000000000000..393e80bea93a
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/cristalhq/jwt/v3/algo_rs.go
@@ -0,0 +1,102 @@
+package jwt
+
+import (
+ "crypto"
+ "crypto/rand"
+ "crypto/rsa"
+)
+
+// NewSignerRS returns a new RSA-based signer.
+func NewSignerRS(alg Algorithm, key *rsa.PrivateKey) (Signer, error) {
+ if key == nil {
+ return nil, ErrNilKey
+ }
+ hash, err := getHashRS(alg, key.Size())
+ if err != nil {
+ return nil, err
+ }
+ return &rsAlg{
+ alg: alg,
+ hash: hash,
+ privateKey: key,
+ }, nil
+}
+
+// NewVerifierRS returns a new RSA-based verifier.
+func NewVerifierRS(alg Algorithm, key *rsa.PublicKey) (Verifier, error) {
+ if key == nil {
+ return nil, ErrNilKey
+ }
+ hash, err := getHashRS(alg, key.Size())
+ if err != nil {
+ return nil, err
+ }
+ return &rsAlg{
+ alg: alg,
+ hash: hash,
+ publicKey: key,
+ }, nil
+}
+
+func getHashRS(alg Algorithm, size int) (crypto.Hash, error) {
+ var hash crypto.Hash
+ switch alg {
+ case RS256:
+ hash = crypto.SHA256
+ case RS384:
+ hash = crypto.SHA384
+ case RS512:
+ hash = crypto.SHA512
+ default:
+ return 0, ErrUnsupportedAlg
+ }
+ return hash, nil
+}
+
+type rsAlg struct {
+ alg Algorithm
+ hash crypto.Hash
+ publicKey *rsa.PublicKey
+ privateKey *rsa.PrivateKey
+}
+
+func (rs *rsAlg) Algorithm() Algorithm {
+ return rs.alg
+}
+
+func (rs *rsAlg) SignSize() int {
+ return rs.privateKey.Size()
+}
+
+func (rs *rsAlg) Sign(payload []byte) ([]byte, error) {
+ digest, err := hashPayload(rs.hash, payload)
+ if err != nil {
+ return nil, err
+ }
+
+ signature, errSign := rsa.SignPKCS1v15(rand.Reader, rs.privateKey, rs.hash, digest)
+ if errSign != nil {
+ return nil, errSign
+ }
+ return signature, nil
+}
+
+func (rs *rsAlg) VerifyToken(token *Token) error {
+ if constTimeAlgEqual(token.Header().Algorithm, rs.alg) {
+ return rs.Verify(token.Payload(), token.Signature())
+ }
+ return ErrAlgorithmMismatch
+}
+
+func (rs *rsAlg) Verify(payload, signature []byte) error {
+ digest, err := hashPayload(rs.hash, payload)
+ if err != nil {
+ return err
+ }
+
+ errVerify := rsa.VerifyPKCS1v15(rs.publicKey, rs.hash, digest, signature)
+ if errVerify != nil {
+ return ErrInvalidSignature
+ }
+ return nil
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/cristalhq/jwt/v3/audience.go b/go/ql/test/experimental/CWE-321/vendor/github.com/cristalhq/jwt/v3/audience.go
new file mode 100644
index 000000000000..6b311a033a65
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/cristalhq/jwt/v3/audience.go
@@ -0,0 +1,46 @@
+package jwt
+
+import "encoding/json"
+
+// Audience is a special claim that be a single string or an array of strings
+// see RFC 7519.
+type Audience []string
+
+// MarshalJSON implements a marshaling function for "aud" claim.
+func (a Audience) MarshalJSON() ([]byte, error) {
+ switch len(a) {
+ case 0:
+ return []byte(`""`), nil
+ case 1:
+ return json.Marshal(a[0])
+ default:
+ return json.Marshal([]string(a))
+ }
+}
+
+// UnmarshalJSON implements json.Unmarshaler interface.
+func (a *Audience) UnmarshalJSON(b []byte) error {
+ var v interface{}
+ if err := json.Unmarshal(b, &v); err != nil {
+ return ErrAudienceInvalidFormat
+ }
+
+ switch v := v.(type) {
+ case string:
+ *a = Audience{v}
+ return nil
+ case []interface{}:
+ aud := make(Audience, len(v))
+ for i := range v {
+ v, ok := v[i].(string)
+ if !ok {
+ return ErrAudienceInvalidFormat
+ }
+ aud[i] = v
+ }
+ *a = aud
+ return nil
+ default:
+ return ErrAudienceInvalidFormat
+ }
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/cristalhq/jwt/v3/build.go b/go/ql/test/experimental/CWE-321/vendor/github.com/cristalhq/jwt/v3/build.go
new file mode 100644
index 000000000000..82e292a07bab
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/cristalhq/jwt/v3/build.go
@@ -0,0 +1,178 @@
+package jwt
+
+import (
+ "encoding/base64"
+ "encoding/json"
+)
+
+// BuilderOption is used to modify builder properties.
+type BuilderOption func(*Builder)
+
+// WithKeyID sets `kid` header for token.
+func WithKeyID(kid string) BuilderOption {
+ return func(b *Builder) { b.header.KeyID = kid }
+}
+
+// WithContentType sets `cty` header for token.
+func WithContentType(cty string) BuilderOption {
+ return func(b *Builder) { b.header.ContentType = cty }
+}
+
+// Builder is used to create a new token.
+type Builder struct {
+ signer Signer
+ header Header
+ headerRaw []byte
+}
+
+// BuildBytes is used to create and encode JWT with a provided claims.
+func BuildBytes(signer Signer, claims interface{}) ([]byte, error) {
+ return NewBuilder(signer).BuildBytes(claims)
+}
+
+// Build is used to create and encode JWT with a provided claims.
+func Build(signer Signer, claims interface{}) (*Token, error) {
+ return NewBuilder(signer).Build(claims)
+}
+
+// NewBuilder returns new instance of Builder.
+func NewBuilder(signer Signer, opts ...BuilderOption) *Builder {
+ b := &Builder{
+ signer: signer,
+ header: Header{
+ Algorithm: signer.Algorithm(),
+ Type: "JWT",
+ },
+ }
+
+ for _, opt := range opts {
+ opt(b)
+ }
+
+ b.headerRaw = encodeHeader(b.header)
+ return b
+}
+
+// BuildBytes used to create and encode JWT with a provided claims.
+func (b *Builder) BuildBytes(claims interface{}) ([]byte, error) {
+ token, err := b.Build(claims)
+ if err != nil {
+ return nil, err
+ }
+ return token.Raw(), nil
+}
+
+// Build used to create and encode JWT with a provided claims.
+// If claims param is of type []byte or string then it's treated as a marshaled JSON.
+// In other words you can pass already marshaled claims.
+//
+func (b *Builder) Build(claims interface{}) (*Token, error) {
+ rawClaims, errClaims := encodeClaims(claims)
+ if errClaims != nil {
+ return nil, errClaims
+ }
+
+ lenH := len(b.headerRaw)
+ lenC := b64EncodedLen(len(rawClaims))
+ lenS := b64EncodedLen(b.signer.SignSize())
+
+ token := make([]byte, lenH+1+lenC+1+lenS)
+ idx := 0
+ idx = copy(token[idx:], b.headerRaw)
+
+ // add '.' and append encoded claims
+ token[idx] = '.'
+ idx++
+ b64Encode(token[idx:], rawClaims)
+ idx += lenC
+
+ // calculate signature of already written 'header.claims'
+ rawSignature, errSign := b.signer.Sign(token[:idx])
+ if errSign != nil {
+ return nil, errSign
+ }
+
+ // add '.' and append encoded signature
+ token[idx] = '.'
+ idx++
+ b64Encode(token[idx:], rawSignature)
+
+ t := &Token{
+ raw: token,
+ dot1: lenH,
+ dot2: lenH + 1 + lenC,
+ header: b.header,
+ claims: rawClaims,
+ signature: rawSignature,
+ }
+ return t, nil
+}
+
+func encodeClaims(claims interface{}) ([]byte, error) {
+ switch claims := claims.(type) {
+ case []byte:
+ return claims, nil
+ case string:
+ return []byte(claims), nil
+ default:
+ return json.Marshal(claims)
+ }
+}
+
+func encodeHeader(header Header) []byte {
+ if header.Type == "JWT" && header.ContentType == "" && header.KeyID == "" {
+ if h := getPredefinedHeader(header); h != "" {
+ return []byte(h)
+ }
+ // another algorithm? encode below
+ }
+ // returned err is always nil, see *Header.MarshalJSON
+ buf, _ := json.Marshal(header)
+
+ encoded := make([]byte, b64EncodedLen(len(buf)))
+ b64Encode(encoded, buf)
+ return encoded
+}
+
+func getPredefinedHeader(header Header) string {
+ switch header.Algorithm {
+ case EdDSA:
+ return "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9"
+
+ case HS256:
+ return "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9"
+ case HS384:
+ return "eyJhbGciOiJIUzM4NCIsInR5cCI6IkpXVCJ9"
+ case HS512:
+ return "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9"
+
+ case RS256:
+ return "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9"
+ case RS384:
+ return "eyJhbGciOiJSUzM4NCIsInR5cCI6IkpXVCJ9"
+ case RS512:
+ return "eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9"
+
+ case ES256:
+ return "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9"
+ case ES384:
+ return "eyJhbGciOiJFUzM4NCIsInR5cCI6IkpXVCJ9"
+ case ES512:
+ return "eyJhbGciOiJFUzUxMiIsInR5cCI6IkpXVCJ9"
+
+ case PS256:
+ return "eyJhbGciOiJQUzI1NiIsInR5cCI6IkpXVCJ9"
+ case PS384:
+ return "eyJhbGciOiJQUzM4NCIsInR5cCI6IkpXVCJ9"
+ case PS512:
+ return "eyJhbGciOiJQUzUxMiIsInR5cCI6IkpXVCJ9"
+
+ default:
+ return ""
+ }
+}
+
+var (
+ b64Encode = base64.RawURLEncoding.Encode
+ b64EncodedLen = base64.RawURLEncoding.EncodedLen
+)
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/cristalhq/jwt/v3/claims.go b/go/ql/test/experimental/CWE-321/vendor/github.com/cristalhq/jwt/v3/claims.go
new file mode 100644
index 000000000000..f0d4a07f27da
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/cristalhq/jwt/v3/claims.go
@@ -0,0 +1,90 @@
+package jwt
+
+import (
+ "crypto/subtle"
+ "time"
+)
+
+// RegisteredClaims will replace StandardClaims in v4.
+type RegisteredClaims = StandardClaims
+
+// StandardClaims represents claims for JWT.
+// See: https://tools.ietf.org/html/rfc7519#section-4.1
+//
+type StandardClaims struct {
+ // ID claim provides a unique identifier for the JWT.
+ ID string `json:"jti,omitempty"`
+
+ // Audience claim identifies the recipients that the JWT is intended for.
+ Audience Audience `json:"aud,omitempty"`
+
+ // Issuer claim identifies the principal that issued the JWT.
+ // Use of this claim is OPTIONAL.
+ Issuer string `json:"iss,omitempty"`
+
+ // Subject claim identifies the principal that is the subject of the JWT.
+ // Use of this claim is OPTIONAL.
+ Subject string `json:"sub,omitempty"`
+
+ // ExpiresAt claim identifies the expiration time on or after which the JWT MUST NOT be accepted for processing.
+ // Use of this claim is OPTIONAL.
+ ExpiresAt *NumericDate `json:"exp,omitempty"`
+
+ // IssuedAt claim identifies the time at which the JWT was issued.
+ // This claim can be used to determine the age of the JWT.
+ // Use of this claim is OPTIONAL.
+ IssuedAt *NumericDate `json:"iat,omitempty"`
+
+ // NotBefore claim identifies the time before which the JWT MUST NOT be accepted for processing.
+ // Use of this claim is OPTIONAL.
+ NotBefore *NumericDate `json:"nbf,omitempty"`
+}
+
+// IsForAudience reports whether token has a given audience.
+func (sc *StandardClaims) IsForAudience(audience string) bool {
+ for _, aud := range sc.Audience {
+ if constTimeEqual(aud, audience) {
+ return true
+ }
+ }
+ return false
+}
+
+// IsIssuer reports whether token has a given issuer.
+func (sc *StandardClaims) IsIssuer(issuer string) bool {
+ return constTimeEqual(sc.Issuer, issuer)
+}
+
+// IsSubject reports whether token has a given subject.
+func (sc *StandardClaims) IsSubject(subject string) bool {
+ return constTimeEqual(sc.Subject, subject)
+}
+
+// IsID reports whether token has a given id.
+func (sc *StandardClaims) IsID(id string) bool {
+ return constTimeEqual(sc.ID, id)
+}
+
+// IsValidExpiresAt reports whether a token isn't expired at a given time.
+func (sc *StandardClaims) IsValidExpiresAt(now time.Time) bool {
+ return sc.ExpiresAt == nil || sc.ExpiresAt.After(now)
+}
+
+// IsValidNotBefore reports whether a token isn't used before a given time.
+func (sc *StandardClaims) IsValidNotBefore(now time.Time) bool {
+ return sc.NotBefore == nil || sc.NotBefore.Before(now)
+}
+
+// IsValidIssuedAt reports whether a token was created before a given time.
+func (sc *StandardClaims) IsValidIssuedAt(now time.Time) bool {
+ return sc.IssuedAt == nil || sc.IssuedAt.Before(now)
+}
+
+// IsValidAt reports whether a token is valid at a given time.
+func (sc *StandardClaims) IsValidAt(now time.Time) bool {
+ return sc.IsValidExpiresAt(now) && sc.IsValidNotBefore(now) && sc.IsValidIssuedAt(now)
+}
+
+func constTimeEqual(a, b string) bool {
+ return subtle.ConstantTimeCompare([]byte(a), []byte(b)) == 1
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/cristalhq/jwt/v3/doc.go b/go/ql/test/experimental/CWE-321/vendor/github.com/cristalhq/jwt/v3/doc.go
new file mode 100644
index 000000000000..fdfa3651a476
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/cristalhq/jwt/v3/doc.go
@@ -0,0 +1,7 @@
+// Package jwt represents JSON Web Token for Go.
+//
+// Builder, all the Signers and Verifiers are safe for use by multiple goroutines simultaneously.
+//
+// See [RFC 7519](https://tools.ietf.org/html/rfc7519) and see [jwt.io](https://jwt.io) for more.
+//
+package jwt
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/cristalhq/jwt/v3/errors.go b/go/ql/test/experimental/CWE-321/vendor/github.com/cristalhq/jwt/v3/errors.go
new file mode 100644
index 000000000000..83a26c1c5fc1
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/cristalhq/jwt/v3/errors.go
@@ -0,0 +1,37 @@
+package jwt
+
+// Error represents a JWT error.
+type Error string
+
+func (e Error) Error() string {
+ return string(e)
+}
+
+var _ error = (Error)("")
+
+// Build and parse errors.
+const (
+ // ErrNilKey indicates that key is nil.
+ ErrNilKey = Error("jwt: key is nil")
+
+ // ErrInvalidKey indicates that key is not valid.
+ ErrInvalidKey = Error("jwt: key is not valid")
+
+ // ErrUnsupportedAlg indicates that given algorithm is not supported.
+ ErrUnsupportedAlg = Error("jwt: algorithm is not supported")
+
+ // ErrInvalidFormat indicates that token format is not valid.
+ ErrInvalidFormat = Error("jwt: token format is not valid")
+
+ // ErrAudienceInvalidFormat indicates that audience format is not valid.
+ ErrAudienceInvalidFormat = Error("jwt: audience format is not valid")
+
+ // ErrDateInvalidFormat indicates that date format is not valid.
+ ErrDateInvalidFormat = Error("jwt: date is not valid")
+
+ // ErrAlgorithmMismatch indicates that token is signed by another algorithm.
+ ErrAlgorithmMismatch = Error("jwt: token is signed by another algorithm")
+
+ // ErrInvalidSignature indicates that signature is not valid.
+ ErrInvalidSignature = Error("jwt: signature is not valid")
+)
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/cristalhq/jwt/v3/fuzz.go b/go/ql/test/experimental/CWE-321/vendor/github.com/cristalhq/jwt/v3/fuzz.go
new file mode 100644
index 000000000000..5fd1e8c83c79
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/cristalhq/jwt/v3/fuzz.go
@@ -0,0 +1,17 @@
+// +build gofuzz
+// To run the fuzzer, run the following commands:
+// $ GO111MODULE=off go get -u github.com/dvyukov/go-fuzz/go-fuzz github.com/dvyukov/go-fuzz/go-fuzz-build
+// $ cd $GOPATH/src/github.com/cristalhq/jwt/
+// $ go-fuzz-build
+// $ go-fuzz
+// Note: go-fuzz doesn't support go modules, so you must have your local
+// installation of jwt under $GOPATH.
+
+package jwt
+
+func Fuzz(data []byte) int {
+ if _, err := Parse(data); err != nil {
+ return 0
+ }
+ return 1
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/cristalhq/jwt/v3/jwt.go b/go/ql/test/experimental/CWE-321/vendor/github.com/cristalhq/jwt/v3/jwt.go
new file mode 100644
index 000000000000..9bd46927b81e
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/cristalhq/jwt/v3/jwt.go
@@ -0,0 +1,91 @@
+package jwt
+
+import (
+ "bytes"
+ "encoding/json"
+)
+
+// Token represents a JWT token.
+// See: https://tools.ietf.org/html/rfc7519
+//
+type Token struct {
+ raw []byte
+ dot1 int
+ dot2 int
+ signature []byte
+ header Header
+ claims json.RawMessage
+}
+
+func (t *Token) String() string {
+ return string(t.raw)
+}
+
+// SecureString returns token without a signature (replaced with `.`).
+func (t *Token) SecureString() string {
+ dot := bytes.LastIndexByte(t.raw, '.')
+ return string(t.raw[:dot]) + `.`
+}
+
+// Raw returns token's raw bytes.
+func (t *Token) Raw() []byte {
+ return t.raw
+}
+
+// Header returns token's header.
+func (t *Token) Header() Header {
+ return t.header
+}
+
+// RawHeader returns token's header raw bytes.
+func (t *Token) RawHeader() []byte {
+ return t.raw[:t.dot1]
+}
+
+// RawClaims returns token's claims as a raw bytes.
+func (t *Token) RawClaims() []byte {
+ return t.claims
+}
+
+// Payload returns token's payload.
+func (t *Token) Payload() []byte {
+ return t.raw[:t.dot2]
+}
+
+// Signature returns token's signature.
+func (t *Token) Signature() []byte {
+ return t.signature
+}
+
+// Header representa JWT header data.
+// See: https://tools.ietf.org/html/rfc7519#section-5, https://tools.ietf.org/html/rfc7517
+//
+type Header struct {
+ Algorithm Algorithm `json:"alg"`
+ Type string `json:"typ,omitempty"` // only "JWT" can be here
+ ContentType string `json:"cty,omitempty"`
+ KeyID string `json:"kid,omitempty"`
+}
+
+// MarshalJSON implements the json.Marshaler interface.
+func (h *Header) MarshalJSON() ([]byte, error) {
+ buf := bytes.Buffer{}
+ buf.WriteString(`{"alg":"`)
+ buf.WriteString(string(h.Algorithm))
+
+ if h.Type != "" {
+ buf.WriteString(`","typ":"`)
+ buf.WriteString(h.Type)
+ }
+ if h.ContentType != "" {
+ buf.WriteString(`","cty":"`)
+ buf.WriteString(h.ContentType)
+ }
+ if h.KeyID != "" {
+ buf.WriteString(`","kid":"`)
+ buf.WriteString(h.KeyID)
+ }
+ buf.WriteString(`"}`)
+
+ return buf.Bytes(), nil
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/cristalhq/jwt/v3/numeric_date.go b/go/ql/test/experimental/CWE-321/vendor/github.com/cristalhq/jwt/v3/numeric_date.go
new file mode 100644
index 000000000000..6bfb0046f4a3
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/cristalhq/jwt/v3/numeric_date.go
@@ -0,0 +1,44 @@
+package jwt
+
+import (
+ "encoding/json"
+ "math"
+ "strconv"
+ "time"
+)
+
+// NumericDate represents date for StandardClaims
+// See: https://tools.ietf.org/html/rfc7519#section-2
+//
+type NumericDate struct {
+ time.Time
+}
+
+// NewNumericDate creates a new NumericDate value from time.Time.
+func NewNumericDate(t time.Time) *NumericDate {
+ if t.IsZero() {
+ return nil
+ }
+ return &NumericDate{t}
+}
+
+// MarshalJSON implements the json.Marshaler interface.
+func (t *NumericDate) MarshalJSON() ([]byte, error) {
+ return []byte(strconv.FormatInt(t.Unix(), 10)), nil
+}
+
+// UnmarshalJSON implements the json.Unmarshaler interface.
+func (t *NumericDate) UnmarshalJSON(data []byte) error {
+ var value json.Number
+ if err := json.Unmarshal(data, &value); err != nil {
+ return ErrDateInvalidFormat
+ }
+ f, err := value.Float64()
+ if err != nil {
+ return ErrDateInvalidFormat
+ }
+ sec, dec := math.Modf(f)
+ ts := time.Unix(int64(sec), int64(dec*1e9))
+ *t = NumericDate{ts}
+ return nil
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/cristalhq/jwt/v3/parse.go b/go/ql/test/experimental/CWE-321/vendor/github.com/cristalhq/jwt/v3/parse.go
new file mode 100644
index 000000000000..b339de17419a
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/cristalhq/jwt/v3/parse.go
@@ -0,0 +1,86 @@
+package jwt
+
+import (
+ "bytes"
+ "encoding/base64"
+ "encoding/json"
+)
+
+// ParseString decodes a token.
+func ParseString(raw string) (*Token, error) {
+ return Parse([]byte(raw))
+}
+
+// Parse decodes a token from a raw bytes.
+func Parse(raw []byte) (*Token, error) {
+ return parse(raw)
+}
+
+// ParseAndVerifyString decodes a token and verifies it's signature.
+func ParseAndVerifyString(raw string, verifier Verifier) (*Token, error) {
+ return ParseAndVerify([]byte(raw), verifier)
+}
+
+// ParseAndVerify decodes a token and verifies it's signature.
+func ParseAndVerify(raw []byte, verifier Verifier) (*Token, error) {
+ token, err := parse(raw)
+ if err != nil {
+ return nil, err
+ }
+ if !constTimeAlgEqual(token.Header().Algorithm, verifier.Algorithm()) {
+ return nil, ErrAlgorithmMismatch
+ }
+ if err := verifier.Verify(token.Payload(), token.Signature()); err != nil {
+ return nil, err
+ }
+ return token, nil
+}
+
+func parse(token []byte) (*Token, error) {
+ // "eyJ" is `{"` which is begin of every JWT token.
+ // Quick check for the invalid input.
+ if !bytes.HasPrefix(token, []byte("eyJ")) {
+ return nil, ErrInvalidFormat
+ }
+
+ dot1 := bytes.IndexByte(token, '.')
+ dot2 := bytes.LastIndexByte(token, '.')
+ if dot2 <= dot1 {
+ return nil, ErrInvalidFormat
+ }
+
+ buf := make([]byte, len(token))
+
+ headerN, err := b64Decode(buf, token[:dot1])
+ if err != nil {
+ return nil, ErrInvalidFormat
+ }
+ var header Header
+ if err := json.Unmarshal(buf[:headerN], &header); err != nil {
+ return nil, ErrInvalidFormat
+ }
+
+ claimsN, err := b64Decode(buf[headerN:], token[dot1+1:dot2])
+ if err != nil {
+ return nil, ErrInvalidFormat
+ }
+ claims := buf[headerN : headerN+claimsN]
+
+ signN, err := b64Decode(buf[headerN+claimsN:], token[dot2+1:])
+ if err != nil {
+ return nil, ErrInvalidFormat
+ }
+ signature := buf[headerN+claimsN : headerN+claimsN+signN]
+
+ tk := &Token{
+ raw: token,
+ dot1: dot1,
+ dot2: dot2,
+ signature: signature,
+ header: header,
+ claims: claims,
+ }
+ return tk, nil
+}
+
+var b64Decode = base64.RawURLEncoding.Decode
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/cristalhq/jwt/v3/stub.go b/go/ql/test/experimental/CWE-321/vendor/github.com/cristalhq/jwt/v3/stub.go
deleted file mode 100644
index c4f718e4f1a8..000000000000
--- a/go/ql/test/experimental/CWE-321/vendor/github.com/cristalhq/jwt/v3/stub.go
+++ /dev/null
@@ -1,26 +0,0 @@
-// Code generated by depstubber. DO NOT EDIT.
-// This is a simple stub for github.com/cristalhq/jwt/v3, strictly for use in testing.
-
-// See the LICENSE file for information about the licensing of the original library.
-// Source: github.com/cristalhq/jwt/v3 (exports: Signer; functions: NewSignerHS,HS256)
-
-// Package jwt is a stub of github.com/cristalhq/jwt/v3, generated by depstubber.
-package jwt
-
-type Algorithm string
-
-func (_ Algorithm) String() string {
- return ""
-}
-
-var HS256 Algorithm = ""
-
-func NewSignerHS(_ Algorithm, _ []byte) (Signer, error) {
- return nil, nil
-}
-
-type Signer interface {
- Algorithm() Algorithm
- Sign(_ []byte) ([]byte, error)
- SignSize() int
-}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/davecgh/go-spew/LICENSE b/go/ql/test/experimental/CWE-321/vendor/github.com/davecgh/go-spew/LICENSE
new file mode 100644
index 000000000000..bc52e96f2b0e
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/davecgh/go-spew/LICENSE
@@ -0,0 +1,15 @@
+ISC License
+
+Copyright (c) 2012-2016 Dave Collins
+
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/davecgh/go-spew/spew/bypass.go b/go/ql/test/experimental/CWE-321/vendor/github.com/davecgh/go-spew/spew/bypass.go
new file mode 100644
index 000000000000..792994785e36
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/davecgh/go-spew/spew/bypass.go
@@ -0,0 +1,145 @@
+// Copyright (c) 2015-2016 Dave Collins
+//
+// Permission to use, copy, modify, and distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+// NOTE: Due to the following build constraints, this file will only be compiled
+// when the code is not running on Google App Engine, compiled by GopherJS, and
+// "-tags safe" is not added to the go build command line. The "disableunsafe"
+// tag is deprecated and thus should not be used.
+// Go versions prior to 1.4 are disabled because they use a different layout
+// for interfaces which make the implementation of unsafeReflectValue more complex.
+// +build !js,!appengine,!safe,!disableunsafe,go1.4
+
+package spew
+
+import (
+ "reflect"
+ "unsafe"
+)
+
+const (
+ // UnsafeDisabled is a build-time constant which specifies whether or
+ // not access to the unsafe package is available.
+ UnsafeDisabled = false
+
+ // ptrSize is the size of a pointer on the current arch.
+ ptrSize = unsafe.Sizeof((*byte)(nil))
+)
+
+type flag uintptr
+
+var (
+ // flagRO indicates whether the value field of a reflect.Value
+ // is read-only.
+ flagRO flag
+
+ // flagAddr indicates whether the address of the reflect.Value's
+ // value may be taken.
+ flagAddr flag
+)
+
+// flagKindMask holds the bits that make up the kind
+// part of the flags field. In all the supported versions,
+// it is in the lower 5 bits.
+const flagKindMask = flag(0x1f)
+
+// Different versions of Go have used different
+// bit layouts for the flags type. This table
+// records the known combinations.
+var okFlags = []struct {
+ ro, addr flag
+}{{
+ // From Go 1.4 to 1.5
+ ro: 1 << 5,
+ addr: 1 << 7,
+}, {
+ // Up to Go tip.
+ ro: 1<<5 | 1<<6,
+ addr: 1 << 8,
+}}
+
+var flagValOffset = func() uintptr {
+ field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag")
+ if !ok {
+ panic("reflect.Value has no flag field")
+ }
+ return field.Offset
+}()
+
+// flagField returns a pointer to the flag field of a reflect.Value.
+func flagField(v *reflect.Value) *flag {
+ return (*flag)(unsafe.Pointer(uintptr(unsafe.Pointer(v)) + flagValOffset))
+}
+
+// unsafeReflectValue converts the passed reflect.Value into a one that bypasses
+// the typical safety restrictions preventing access to unaddressable and
+// unexported data. It works by digging the raw pointer to the underlying
+// value out of the protected value and generating a new unprotected (unsafe)
+// reflect.Value to it.
+//
+// This allows us to check for implementations of the Stringer and error
+// interfaces to be used for pretty printing ordinarily unaddressable and
+// inaccessible values such as unexported struct fields.
+func unsafeReflectValue(v reflect.Value) reflect.Value {
+ if !v.IsValid() || (v.CanInterface() && v.CanAddr()) {
+ return v
+ }
+ flagFieldPtr := flagField(&v)
+ *flagFieldPtr &^= flagRO
+ *flagFieldPtr |= flagAddr
+ return v
+}
+
+// Sanity checks against future reflect package changes
+// to the type or semantics of the Value.flag field.
+func init() {
+ field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag")
+ if !ok {
+ panic("reflect.Value has no flag field")
+ }
+ if field.Type.Kind() != reflect.TypeOf(flag(0)).Kind() {
+ panic("reflect.Value flag field has changed kind")
+ }
+ type t0 int
+ var t struct {
+ A t0
+ // t0 will have flagEmbedRO set.
+ t0
+ // a will have flagStickyRO set
+ a t0
+ }
+ vA := reflect.ValueOf(t).FieldByName("A")
+ va := reflect.ValueOf(t).FieldByName("a")
+ vt0 := reflect.ValueOf(t).FieldByName("t0")
+
+ // Infer flagRO from the difference between the flags
+ // for the (otherwise identical) fields in t.
+ flagPublic := *flagField(&vA)
+ flagWithRO := *flagField(&va) | *flagField(&vt0)
+ flagRO = flagPublic ^ flagWithRO
+
+ // Infer flagAddr from the difference between a value
+ // taken from a pointer and not.
+ vPtrA := reflect.ValueOf(&t).Elem().FieldByName("A")
+ flagNoPtr := *flagField(&vA)
+ flagPtr := *flagField(&vPtrA)
+ flagAddr = flagNoPtr ^ flagPtr
+
+ // Check that the inferred flags tally with one of the known versions.
+ for _, f := range okFlags {
+ if flagRO == f.ro && flagAddr == f.addr {
+ return
+ }
+ }
+ panic("reflect.Value read-only flag has changed semantics")
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/davecgh/go-spew/spew/bypasssafe.go b/go/ql/test/experimental/CWE-321/vendor/github.com/davecgh/go-spew/spew/bypasssafe.go
new file mode 100644
index 000000000000..205c28d68c47
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/davecgh/go-spew/spew/bypasssafe.go
@@ -0,0 +1,38 @@
+// Copyright (c) 2015-2016 Dave Collins
+//
+// Permission to use, copy, modify, and distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+// NOTE: Due to the following build constraints, this file will only be compiled
+// when the code is running on Google App Engine, compiled by GopherJS, or
+// "-tags safe" is added to the go build command line. The "disableunsafe"
+// tag is deprecated and thus should not be used.
+// +build js appengine safe disableunsafe !go1.4
+
+package spew
+
+import "reflect"
+
+const (
+ // UnsafeDisabled is a build-time constant which specifies whether or
+ // not access to the unsafe package is available.
+ UnsafeDisabled = true
+)
+
+// unsafeReflectValue typically converts the passed reflect.Value into a one
+// that bypasses the typical safety restrictions preventing access to
+// unaddressable and unexported data. However, doing this relies on access to
+// the unsafe package. This is a stub version which simply returns the passed
+// reflect.Value when the unsafe package is not available.
+func unsafeReflectValue(v reflect.Value) reflect.Value {
+ return v
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/davecgh/go-spew/spew/common.go b/go/ql/test/experimental/CWE-321/vendor/github.com/davecgh/go-spew/spew/common.go
new file mode 100644
index 000000000000..1be8ce945761
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/davecgh/go-spew/spew/common.go
@@ -0,0 +1,341 @@
+/*
+ * Copyright (c) 2013-2016 Dave Collins
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+package spew
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "reflect"
+ "sort"
+ "strconv"
+)
+
+// Some constants in the form of bytes to avoid string overhead. This mirrors
+// the technique used in the fmt package.
+var (
+ panicBytes = []byte("(PANIC=")
+ plusBytes = []byte("+")
+ iBytes = []byte("i")
+ trueBytes = []byte("true")
+ falseBytes = []byte("false")
+ interfaceBytes = []byte("(interface {})")
+ commaNewlineBytes = []byte(",\n")
+ newlineBytes = []byte("\n")
+ openBraceBytes = []byte("{")
+ openBraceNewlineBytes = []byte("{\n")
+ closeBraceBytes = []byte("}")
+ asteriskBytes = []byte("*")
+ colonBytes = []byte(":")
+ colonSpaceBytes = []byte(": ")
+ openParenBytes = []byte("(")
+ closeParenBytes = []byte(")")
+ spaceBytes = []byte(" ")
+ pointerChainBytes = []byte("->")
+ nilAngleBytes = []byte("")
+ maxNewlineBytes = []byte("\n")
+ maxShortBytes = []byte("")
+ circularBytes = []byte("")
+ circularShortBytes = []byte("")
+ invalidAngleBytes = []byte("")
+ openBracketBytes = []byte("[")
+ closeBracketBytes = []byte("]")
+ percentBytes = []byte("%")
+ precisionBytes = []byte(".")
+ openAngleBytes = []byte("<")
+ closeAngleBytes = []byte(">")
+ openMapBytes = []byte("map[")
+ closeMapBytes = []byte("]")
+ lenEqualsBytes = []byte("len=")
+ capEqualsBytes = []byte("cap=")
+)
+
+// hexDigits is used to map a decimal value to a hex digit.
+var hexDigits = "0123456789abcdef"
+
+// catchPanic handles any panics that might occur during the handleMethods
+// calls.
+func catchPanic(w io.Writer, v reflect.Value) {
+ if err := recover(); err != nil {
+ w.Write(panicBytes)
+ fmt.Fprintf(w, "%v", err)
+ w.Write(closeParenBytes)
+ }
+}
+
+// handleMethods attempts to call the Error and String methods on the underlying
+// type the passed reflect.Value represents and outputes the result to Writer w.
+//
+// It handles panics in any called methods by catching and displaying the error
+// as the formatted value.
+func handleMethods(cs *ConfigState, w io.Writer, v reflect.Value) (handled bool) {
+ // We need an interface to check if the type implements the error or
+ // Stringer interface. However, the reflect package won't give us an
+ // interface on certain things like unexported struct fields in order
+ // to enforce visibility rules. We use unsafe, when it's available,
+ // to bypass these restrictions since this package does not mutate the
+ // values.
+ if !v.CanInterface() {
+ if UnsafeDisabled {
+ return false
+ }
+
+ v = unsafeReflectValue(v)
+ }
+
+ // Choose whether or not to do error and Stringer interface lookups against
+ // the base type or a pointer to the base type depending on settings.
+ // Technically calling one of these methods with a pointer receiver can
+ // mutate the value, however, types which choose to satisify an error or
+ // Stringer interface with a pointer receiver should not be mutating their
+ // state inside these interface methods.
+ if !cs.DisablePointerMethods && !UnsafeDisabled && !v.CanAddr() {
+ v = unsafeReflectValue(v)
+ }
+ if v.CanAddr() {
+ v = v.Addr()
+ }
+
+ // Is it an error or Stringer?
+ switch iface := v.Interface().(type) {
+ case error:
+ defer catchPanic(w, v)
+ if cs.ContinueOnMethod {
+ w.Write(openParenBytes)
+ w.Write([]byte(iface.Error()))
+ w.Write(closeParenBytes)
+ w.Write(spaceBytes)
+ return false
+ }
+
+ w.Write([]byte(iface.Error()))
+ return true
+
+ case fmt.Stringer:
+ defer catchPanic(w, v)
+ if cs.ContinueOnMethod {
+ w.Write(openParenBytes)
+ w.Write([]byte(iface.String()))
+ w.Write(closeParenBytes)
+ w.Write(spaceBytes)
+ return false
+ }
+ w.Write([]byte(iface.String()))
+ return true
+ }
+ return false
+}
+
+// printBool outputs a boolean value as true or false to Writer w.
+func printBool(w io.Writer, val bool) {
+ if val {
+ w.Write(trueBytes)
+ } else {
+ w.Write(falseBytes)
+ }
+}
+
+// printInt outputs a signed integer value to Writer w.
+func printInt(w io.Writer, val int64, base int) {
+ w.Write([]byte(strconv.FormatInt(val, base)))
+}
+
+// printUint outputs an unsigned integer value to Writer w.
+func printUint(w io.Writer, val uint64, base int) {
+ w.Write([]byte(strconv.FormatUint(val, base)))
+}
+
+// printFloat outputs a floating point value using the specified precision,
+// which is expected to be 32 or 64bit, to Writer w.
+func printFloat(w io.Writer, val float64, precision int) {
+ w.Write([]byte(strconv.FormatFloat(val, 'g', -1, precision)))
+}
+
+// printComplex outputs a complex value using the specified float precision
+// for the real and imaginary parts to Writer w.
+func printComplex(w io.Writer, c complex128, floatPrecision int) {
+ r := real(c)
+ w.Write(openParenBytes)
+ w.Write([]byte(strconv.FormatFloat(r, 'g', -1, floatPrecision)))
+ i := imag(c)
+ if i >= 0 {
+ w.Write(plusBytes)
+ }
+ w.Write([]byte(strconv.FormatFloat(i, 'g', -1, floatPrecision)))
+ w.Write(iBytes)
+ w.Write(closeParenBytes)
+}
+
+// printHexPtr outputs a uintptr formatted as hexadecimal with a leading '0x'
+// prefix to Writer w.
+func printHexPtr(w io.Writer, p uintptr) {
+ // Null pointer.
+ num := uint64(p)
+ if num == 0 {
+ w.Write(nilAngleBytes)
+ return
+ }
+
+ // Max uint64 is 16 bytes in hex + 2 bytes for '0x' prefix
+ buf := make([]byte, 18)
+
+ // It's simpler to construct the hex string right to left.
+ base := uint64(16)
+ i := len(buf) - 1
+ for num >= base {
+ buf[i] = hexDigits[num%base]
+ num /= base
+ i--
+ }
+ buf[i] = hexDigits[num]
+
+ // Add '0x' prefix.
+ i--
+ buf[i] = 'x'
+ i--
+ buf[i] = '0'
+
+ // Strip unused leading bytes.
+ buf = buf[i:]
+ w.Write(buf)
+}
+
+// valuesSorter implements sort.Interface to allow a slice of reflect.Value
+// elements to be sorted.
+type valuesSorter struct {
+ values []reflect.Value
+ strings []string // either nil or same len and values
+ cs *ConfigState
+}
+
+// newValuesSorter initializes a valuesSorter instance, which holds a set of
+// surrogate keys on which the data should be sorted. It uses flags in
+// ConfigState to decide if and how to populate those surrogate keys.
+func newValuesSorter(values []reflect.Value, cs *ConfigState) sort.Interface {
+ vs := &valuesSorter{values: values, cs: cs}
+ if canSortSimply(vs.values[0].Kind()) {
+ return vs
+ }
+ if !cs.DisableMethods {
+ vs.strings = make([]string, len(values))
+ for i := range vs.values {
+ b := bytes.Buffer{}
+ if !handleMethods(cs, &b, vs.values[i]) {
+ vs.strings = nil
+ break
+ }
+ vs.strings[i] = b.String()
+ }
+ }
+ if vs.strings == nil && cs.SpewKeys {
+ vs.strings = make([]string, len(values))
+ for i := range vs.values {
+ vs.strings[i] = Sprintf("%#v", vs.values[i].Interface())
+ }
+ }
+ return vs
+}
+
+// canSortSimply tests whether a reflect.Kind is a primitive that can be sorted
+// directly, or whether it should be considered for sorting by surrogate keys
+// (if the ConfigState allows it).
+func canSortSimply(kind reflect.Kind) bool {
+ // This switch parallels valueSortLess, except for the default case.
+ switch kind {
+ case reflect.Bool:
+ return true
+ case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
+ return true
+ case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
+ return true
+ case reflect.Float32, reflect.Float64:
+ return true
+ case reflect.String:
+ return true
+ case reflect.Uintptr:
+ return true
+ case reflect.Array:
+ return true
+ }
+ return false
+}
+
+// Len returns the number of values in the slice. It is part of the
+// sort.Interface implementation.
+func (s *valuesSorter) Len() int {
+ return len(s.values)
+}
+
+// Swap swaps the values at the passed indices. It is part of the
+// sort.Interface implementation.
+func (s *valuesSorter) Swap(i, j int) {
+ s.values[i], s.values[j] = s.values[j], s.values[i]
+ if s.strings != nil {
+ s.strings[i], s.strings[j] = s.strings[j], s.strings[i]
+ }
+}
+
+// valueSortLess returns whether the first value should sort before the second
+// value. It is used by valueSorter.Less as part of the sort.Interface
+// implementation.
+func valueSortLess(a, b reflect.Value) bool {
+ switch a.Kind() {
+ case reflect.Bool:
+ return !a.Bool() && b.Bool()
+ case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
+ return a.Int() < b.Int()
+ case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
+ return a.Uint() < b.Uint()
+ case reflect.Float32, reflect.Float64:
+ return a.Float() < b.Float()
+ case reflect.String:
+ return a.String() < b.String()
+ case reflect.Uintptr:
+ return a.Uint() < b.Uint()
+ case reflect.Array:
+ // Compare the contents of both arrays.
+ l := a.Len()
+ for i := 0; i < l; i++ {
+ av := a.Index(i)
+ bv := b.Index(i)
+ if av.Interface() == bv.Interface() {
+ continue
+ }
+ return valueSortLess(av, bv)
+ }
+ }
+ return a.String() < b.String()
+}
+
+// Less returns whether the value at index i should sort before the
+// value at index j. It is part of the sort.Interface implementation.
+func (s *valuesSorter) Less(i, j int) bool {
+ if s.strings == nil {
+ return valueSortLess(s.values[i], s.values[j])
+ }
+ return s.strings[i] < s.strings[j]
+}
+
+// sortValues is a sort function that handles both native types and any type that
+// can be converted to error or Stringer. Other inputs are sorted according to
+// their Value.String() value to ensure display stability.
+func sortValues(values []reflect.Value, cs *ConfigState) {
+ if len(values) == 0 {
+ return
+ }
+ sort.Sort(newValuesSorter(values, cs))
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/davecgh/go-spew/spew/config.go b/go/ql/test/experimental/CWE-321/vendor/github.com/davecgh/go-spew/spew/config.go
new file mode 100644
index 000000000000..2e3d22f31202
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/davecgh/go-spew/spew/config.go
@@ -0,0 +1,306 @@
+/*
+ * Copyright (c) 2013-2016 Dave Collins
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+package spew
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "os"
+)
+
+// ConfigState houses the configuration options used by spew to format and
+// display values. There is a global instance, Config, that is used to control
+// all top-level Formatter and Dump functionality. Each ConfigState instance
+// provides methods equivalent to the top-level functions.
+//
+// The zero value for ConfigState provides no indentation. You would typically
+// want to set it to a space or a tab.
+//
+// Alternatively, you can use NewDefaultConfig to get a ConfigState instance
+// with default settings. See the documentation of NewDefaultConfig for default
+// values.
+type ConfigState struct {
+ // Indent specifies the string to use for each indentation level. The
+ // global config instance that all top-level functions use set this to a
+ // single space by default. If you would like more indentation, you might
+ // set this to a tab with "\t" or perhaps two spaces with " ".
+ Indent string
+
+ // MaxDepth controls the maximum number of levels to descend into nested
+ // data structures. The default, 0, means there is no limit.
+ //
+ // NOTE: Circular data structures are properly detected, so it is not
+ // necessary to set this value unless you specifically want to limit deeply
+ // nested data structures.
+ MaxDepth int
+
+ // DisableMethods specifies whether or not error and Stringer interfaces are
+ // invoked for types that implement them.
+ DisableMethods bool
+
+ // DisablePointerMethods specifies whether or not to check for and invoke
+ // error and Stringer interfaces on types which only accept a pointer
+ // receiver when the current type is not a pointer.
+ //
+ // NOTE: This might be an unsafe action since calling one of these methods
+ // with a pointer receiver could technically mutate the value, however,
+ // in practice, types which choose to satisify an error or Stringer
+ // interface with a pointer receiver should not be mutating their state
+ // inside these interface methods. As a result, this option relies on
+ // access to the unsafe package, so it will not have any effect when
+ // running in environments without access to the unsafe package such as
+ // Google App Engine or with the "safe" build tag specified.
+ DisablePointerMethods bool
+
+ // DisablePointerAddresses specifies whether to disable the printing of
+ // pointer addresses. This is useful when diffing data structures in tests.
+ DisablePointerAddresses bool
+
+ // DisableCapacities specifies whether to disable the printing of capacities
+ // for arrays, slices, maps and channels. This is useful when diffing
+ // data structures in tests.
+ DisableCapacities bool
+
+ // ContinueOnMethod specifies whether or not recursion should continue once
+ // a custom error or Stringer interface is invoked. The default, false,
+ // means it will print the results of invoking the custom error or Stringer
+ // interface and return immediately instead of continuing to recurse into
+ // the internals of the data type.
+ //
+ // NOTE: This flag does not have any effect if method invocation is disabled
+ // via the DisableMethods or DisablePointerMethods options.
+ ContinueOnMethod bool
+
+ // SortKeys specifies map keys should be sorted before being printed. Use
+ // this to have a more deterministic, diffable output. Note that only
+ // native types (bool, int, uint, floats, uintptr and string) and types
+ // that support the error or Stringer interfaces (if methods are
+ // enabled) are supported, with other types sorted according to the
+ // reflect.Value.String() output which guarantees display stability.
+ SortKeys bool
+
+ // SpewKeys specifies that, as a last resort attempt, map keys should
+ // be spewed to strings and sorted by those strings. This is only
+ // considered if SortKeys is true.
+ SpewKeys bool
+}
+
+// Config is the active configuration of the top-level functions.
+// The configuration can be changed by modifying the contents of spew.Config.
+var Config = ConfigState{Indent: " "}
+
+// Errorf is a wrapper for fmt.Errorf that treats each argument as if it were
+// passed with a Formatter interface returned by c.NewFormatter. It returns
+// the formatted string as a value that satisfies error. See NewFormatter
+// for formatting details.
+//
+// This function is shorthand for the following syntax:
+//
+// fmt.Errorf(format, c.NewFormatter(a), c.NewFormatter(b))
+func (c *ConfigState) Errorf(format string, a ...interface{}) (err error) {
+ return fmt.Errorf(format, c.convertArgs(a)...)
+}
+
+// Fprint is a wrapper for fmt.Fprint that treats each argument as if it were
+// passed with a Formatter interface returned by c.NewFormatter. It returns
+// the number of bytes written and any write error encountered. See
+// NewFormatter for formatting details.
+//
+// This function is shorthand for the following syntax:
+//
+// fmt.Fprint(w, c.NewFormatter(a), c.NewFormatter(b))
+func (c *ConfigState) Fprint(w io.Writer, a ...interface{}) (n int, err error) {
+ return fmt.Fprint(w, c.convertArgs(a)...)
+}
+
+// Fprintf is a wrapper for fmt.Fprintf that treats each argument as if it were
+// passed with a Formatter interface returned by c.NewFormatter. It returns
+// the number of bytes written and any write error encountered. See
+// NewFormatter for formatting details.
+//
+// This function is shorthand for the following syntax:
+//
+// fmt.Fprintf(w, format, c.NewFormatter(a), c.NewFormatter(b))
+func (c *ConfigState) Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) {
+ return fmt.Fprintf(w, format, c.convertArgs(a)...)
+}
+
+// Fprintln is a wrapper for fmt.Fprintln that treats each argument as if it
+// passed with a Formatter interface returned by c.NewFormatter. See
+// NewFormatter for formatting details.
+//
+// This function is shorthand for the following syntax:
+//
+// fmt.Fprintln(w, c.NewFormatter(a), c.NewFormatter(b))
+func (c *ConfigState) Fprintln(w io.Writer, a ...interface{}) (n int, err error) {
+ return fmt.Fprintln(w, c.convertArgs(a)...)
+}
+
+// Print is a wrapper for fmt.Print that treats each argument as if it were
+// passed with a Formatter interface returned by c.NewFormatter. It returns
+// the number of bytes written and any write error encountered. See
+// NewFormatter for formatting details.
+//
+// This function is shorthand for the following syntax:
+//
+// fmt.Print(c.NewFormatter(a), c.NewFormatter(b))
+func (c *ConfigState) Print(a ...interface{}) (n int, err error) {
+ return fmt.Print(c.convertArgs(a)...)
+}
+
+// Printf is a wrapper for fmt.Printf that treats each argument as if it were
+// passed with a Formatter interface returned by c.NewFormatter. It returns
+// the number of bytes written and any write error encountered. See
+// NewFormatter for formatting details.
+//
+// This function is shorthand for the following syntax:
+//
+// fmt.Printf(format, c.NewFormatter(a), c.NewFormatter(b))
+func (c *ConfigState) Printf(format string, a ...interface{}) (n int, err error) {
+ return fmt.Printf(format, c.convertArgs(a)...)
+}
+
+// Println is a wrapper for fmt.Println that treats each argument as if it were
+// passed with a Formatter interface returned by c.NewFormatter. It returns
+// the number of bytes written and any write error encountered. See
+// NewFormatter for formatting details.
+//
+// This function is shorthand for the following syntax:
+//
+// fmt.Println(c.NewFormatter(a), c.NewFormatter(b))
+func (c *ConfigState) Println(a ...interface{}) (n int, err error) {
+ return fmt.Println(c.convertArgs(a)...)
+}
+
+// Sprint is a wrapper for fmt.Sprint that treats each argument as if it were
+// passed with a Formatter interface returned by c.NewFormatter. It returns
+// the resulting string. See NewFormatter for formatting details.
+//
+// This function is shorthand for the following syntax:
+//
+// fmt.Sprint(c.NewFormatter(a), c.NewFormatter(b))
+func (c *ConfigState) Sprint(a ...interface{}) string {
+ return fmt.Sprint(c.convertArgs(a)...)
+}
+
+// Sprintf is a wrapper for fmt.Sprintf that treats each argument as if it were
+// passed with a Formatter interface returned by c.NewFormatter. It returns
+// the resulting string. See NewFormatter for formatting details.
+//
+// This function is shorthand for the following syntax:
+//
+// fmt.Sprintf(format, c.NewFormatter(a), c.NewFormatter(b))
+func (c *ConfigState) Sprintf(format string, a ...interface{}) string {
+ return fmt.Sprintf(format, c.convertArgs(a)...)
+}
+
+// Sprintln is a wrapper for fmt.Sprintln that treats each argument as if it
+// were passed with a Formatter interface returned by c.NewFormatter. It
+// returns the resulting string. See NewFormatter for formatting details.
+//
+// This function is shorthand for the following syntax:
+//
+// fmt.Sprintln(c.NewFormatter(a), c.NewFormatter(b))
+func (c *ConfigState) Sprintln(a ...interface{}) string {
+ return fmt.Sprintln(c.convertArgs(a)...)
+}
+
+/*
+NewFormatter returns a custom formatter that satisfies the fmt.Formatter
+interface. As a result, it integrates cleanly with standard fmt package
+printing functions. The formatter is useful for inline printing of smaller data
+types similar to the standard %v format specifier.
+
+The custom formatter only responds to the %v (most compact), %+v (adds pointer
+addresses), %#v (adds types), and %#+v (adds types and pointer addresses) verb
+combinations. Any other verbs such as %x and %q will be sent to the the
+standard fmt package for formatting. In addition, the custom formatter ignores
+the width and precision arguments (however they will still work on the format
+specifiers not handled by the custom formatter).
+
+Typically this function shouldn't be called directly. It is much easier to make
+use of the custom formatter by calling one of the convenience functions such as
+c.Printf, c.Println, or c.Printf.
+*/
+func (c *ConfigState) NewFormatter(v interface{}) fmt.Formatter {
+ return newFormatter(c, v)
+}
+
+// Fdump formats and displays the passed arguments to io.Writer w. It formats
+// exactly the same as Dump.
+func (c *ConfigState) Fdump(w io.Writer, a ...interface{}) {
+ fdump(c, w, a...)
+}
+
+/*
+Dump displays the passed parameters to standard out with newlines, customizable
+indentation, and additional debug information such as complete types and all
+pointer addresses used to indirect to the final value. It provides the
+following features over the built-in printing facilities provided by the fmt
+package:
+
+ * Pointers are dereferenced and followed
+ * Circular data structures are detected and handled properly
+ * Custom Stringer/error interfaces are optionally invoked, including
+ on unexported types
+ * Custom types which only implement the Stringer/error interfaces via
+ a pointer receiver are optionally invoked when passing non-pointer
+ variables
+ * Byte arrays and slices are dumped like the hexdump -C command which
+ includes offsets, byte values in hex, and ASCII output
+
+The configuration options are controlled by modifying the public members
+of c. See ConfigState for options documentation.
+
+See Fdump if you would prefer dumping to an arbitrary io.Writer or Sdump to
+get the formatted result as a string.
+*/
+func (c *ConfigState) Dump(a ...interface{}) {
+ fdump(c, os.Stdout, a...)
+}
+
+// Sdump returns a string with the passed arguments formatted exactly the same
+// as Dump.
+func (c *ConfigState) Sdump(a ...interface{}) string {
+ var buf bytes.Buffer
+ fdump(c, &buf, a...)
+ return buf.String()
+}
+
+// convertArgs accepts a slice of arguments and returns a slice of the same
+// length with each argument converted to a spew Formatter interface using
+// the ConfigState associated with s.
+func (c *ConfigState) convertArgs(args []interface{}) (formatters []interface{}) {
+ formatters = make([]interface{}, len(args))
+ for index, arg := range args {
+ formatters[index] = newFormatter(c, arg)
+ }
+ return formatters
+}
+
+// NewDefaultConfig returns a ConfigState with the following default settings.
+//
+// Indent: " "
+// MaxDepth: 0
+// DisableMethods: false
+// DisablePointerMethods: false
+// ContinueOnMethod: false
+// SortKeys: false
+func NewDefaultConfig() *ConfigState {
+ return &ConfigState{Indent: " "}
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/davecgh/go-spew/spew/doc.go b/go/ql/test/experimental/CWE-321/vendor/github.com/davecgh/go-spew/spew/doc.go
new file mode 100644
index 000000000000..aacaac6f1e1e
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/davecgh/go-spew/spew/doc.go
@@ -0,0 +1,211 @@
+/*
+ * Copyright (c) 2013-2016 Dave Collins
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+Package spew implements a deep pretty printer for Go data structures to aid in
+debugging.
+
+A quick overview of the additional features spew provides over the built-in
+printing facilities for Go data types are as follows:
+
+ * Pointers are dereferenced and followed
+ * Circular data structures are detected and handled properly
+ * Custom Stringer/error interfaces are optionally invoked, including
+ on unexported types
+ * Custom types which only implement the Stringer/error interfaces via
+ a pointer receiver are optionally invoked when passing non-pointer
+ variables
+ * Byte arrays and slices are dumped like the hexdump -C command which
+ includes offsets, byte values in hex, and ASCII output (only when using
+ Dump style)
+
+There are two different approaches spew allows for dumping Go data structures:
+
+ * Dump style which prints with newlines, customizable indentation,
+ and additional debug information such as types and all pointer addresses
+ used to indirect to the final value
+ * A custom Formatter interface that integrates cleanly with the standard fmt
+ package and replaces %v, %+v, %#v, and %#+v to provide inline printing
+ similar to the default %v while providing the additional functionality
+ outlined above and passing unsupported format verbs such as %x and %q
+ along to fmt
+
+Quick Start
+
+This section demonstrates how to quickly get started with spew. See the
+sections below for further details on formatting and configuration options.
+
+To dump a variable with full newlines, indentation, type, and pointer
+information use Dump, Fdump, or Sdump:
+ spew.Dump(myVar1, myVar2, ...)
+ spew.Fdump(someWriter, myVar1, myVar2, ...)
+ str := spew.Sdump(myVar1, myVar2, ...)
+
+Alternatively, if you would prefer to use format strings with a compacted inline
+printing style, use the convenience wrappers Printf, Fprintf, etc with
+%v (most compact), %+v (adds pointer addresses), %#v (adds types), or
+%#+v (adds types and pointer addresses):
+ spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2)
+ spew.Printf("myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
+ spew.Fprintf(someWriter, "myVar1: %v -- myVar2: %+v", myVar1, myVar2)
+ spew.Fprintf(someWriter, "myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
+
+Configuration Options
+
+Configuration of spew is handled by fields in the ConfigState type. For
+convenience, all of the top-level functions use a global state available
+via the spew.Config global.
+
+It is also possible to create a ConfigState instance that provides methods
+equivalent to the top-level functions. This allows concurrent configuration
+options. See the ConfigState documentation for more details.
+
+The following configuration options are available:
+ * Indent
+ String to use for each indentation level for Dump functions.
+ It is a single space by default. A popular alternative is "\t".
+
+ * MaxDepth
+ Maximum number of levels to descend into nested data structures.
+ There is no limit by default.
+
+ * DisableMethods
+ Disables invocation of error and Stringer interface methods.
+ Method invocation is enabled by default.
+
+ * DisablePointerMethods
+ Disables invocation of error and Stringer interface methods on types
+ which only accept pointer receivers from non-pointer variables.
+ Pointer method invocation is enabled by default.
+
+ * DisablePointerAddresses
+ DisablePointerAddresses specifies whether to disable the printing of
+ pointer addresses. This is useful when diffing data structures in tests.
+
+ * DisableCapacities
+ DisableCapacities specifies whether to disable the printing of
+ capacities for arrays, slices, maps and channels. This is useful when
+ diffing data structures in tests.
+
+ * ContinueOnMethod
+ Enables recursion into types after invoking error and Stringer interface
+ methods. Recursion after method invocation is disabled by default.
+
+ * SortKeys
+ Specifies map keys should be sorted before being printed. Use
+ this to have a more deterministic, diffable output. Note that
+ only native types (bool, int, uint, floats, uintptr and string)
+ and types which implement error or Stringer interfaces are
+ supported with other types sorted according to the
+ reflect.Value.String() output which guarantees display
+ stability. Natural map order is used by default.
+
+ * SpewKeys
+ Specifies that, as a last resort attempt, map keys should be
+ spewed to strings and sorted by those strings. This is only
+ considered if SortKeys is true.
+
+Dump Usage
+
+Simply call spew.Dump with a list of variables you want to dump:
+
+ spew.Dump(myVar1, myVar2, ...)
+
+You may also call spew.Fdump if you would prefer to output to an arbitrary
+io.Writer. For example, to dump to standard error:
+
+ spew.Fdump(os.Stderr, myVar1, myVar2, ...)
+
+A third option is to call spew.Sdump to get the formatted output as a string:
+
+ str := spew.Sdump(myVar1, myVar2, ...)
+
+Sample Dump Output
+
+See the Dump example for details on the setup of the types and variables being
+shown here.
+
+ (main.Foo) {
+ unexportedField: (*main.Bar)(0xf84002e210)({
+ flag: (main.Flag) flagTwo,
+ data: (uintptr)
+ }),
+ ExportedField: (map[interface {}]interface {}) (len=1) {
+ (string) (len=3) "one": (bool) true
+ }
+ }
+
+Byte (and uint8) arrays and slices are displayed uniquely like the hexdump -C
+command as shown.
+ ([]uint8) (len=32 cap=32) {
+ 00000000 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 |............... |
+ 00000010 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 |!"#$%&'()*+,-./0|
+ 00000020 31 32 |12|
+ }
+
+Custom Formatter
+
+Spew provides a custom formatter that implements the fmt.Formatter interface
+so that it integrates cleanly with standard fmt package printing functions. The
+formatter is useful for inline printing of smaller data types similar to the
+standard %v format specifier.
+
+The custom formatter only responds to the %v (most compact), %+v (adds pointer
+addresses), %#v (adds types), or %#+v (adds types and pointer addresses) verb
+combinations. Any other verbs such as %x and %q will be sent to the the
+standard fmt package for formatting. In addition, the custom formatter ignores
+the width and precision arguments (however they will still work on the format
+specifiers not handled by the custom formatter).
+
+Custom Formatter Usage
+
+The simplest way to make use of the spew custom formatter is to call one of the
+convenience functions such as spew.Printf, spew.Println, or spew.Printf. The
+functions have syntax you are most likely already familiar with:
+
+ spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2)
+ spew.Printf("myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
+ spew.Println(myVar, myVar2)
+ spew.Fprintf(os.Stderr, "myVar1: %v -- myVar2: %+v", myVar1, myVar2)
+ spew.Fprintf(os.Stderr, "myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
+
+See the Index for the full list convenience functions.
+
+Sample Formatter Output
+
+Double pointer to a uint8:
+ %v: <**>5
+ %+v: <**>(0xf8400420d0->0xf8400420c8)5
+ %#v: (**uint8)5
+ %#+v: (**uint8)(0xf8400420d0->0xf8400420c8)5
+
+Pointer to circular struct with a uint8 field and a pointer to itself:
+ %v: <*>{1 <*>}
+ %+v: <*>(0xf84003e260){ui8:1 c:<*>(0xf84003e260)}
+ %#v: (*main.circular){ui8:(uint8)1 c:(*main.circular)}
+ %#+v: (*main.circular)(0xf84003e260){ui8:(uint8)1 c:(*main.circular)(0xf84003e260)}
+
+See the Printf example for details on the setup of variables being shown
+here.
+
+Errors
+
+Since it is possible for custom Stringer/error interfaces to panic, spew
+detects them and handles them internally by printing the panic information
+inline with the output. Since spew is intended to provide deep pretty printing
+capabilities on structures, it intentionally does not return any errors.
+*/
+package spew
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/davecgh/go-spew/spew/dump.go b/go/ql/test/experimental/CWE-321/vendor/github.com/davecgh/go-spew/spew/dump.go
new file mode 100644
index 000000000000..f78d89fc1f6c
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/davecgh/go-spew/spew/dump.go
@@ -0,0 +1,509 @@
+/*
+ * Copyright (c) 2013-2016 Dave Collins
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+package spew
+
+import (
+ "bytes"
+ "encoding/hex"
+ "fmt"
+ "io"
+ "os"
+ "reflect"
+ "regexp"
+ "strconv"
+ "strings"
+)
+
+var (
+ // uint8Type is a reflect.Type representing a uint8. It is used to
+ // convert cgo types to uint8 slices for hexdumping.
+ uint8Type = reflect.TypeOf(uint8(0))
+
+ // cCharRE is a regular expression that matches a cgo char.
+ // It is used to detect character arrays to hexdump them.
+ cCharRE = regexp.MustCompile(`^.*\._Ctype_char$`)
+
+ // cUnsignedCharRE is a regular expression that matches a cgo unsigned
+ // char. It is used to detect unsigned character arrays to hexdump
+ // them.
+ cUnsignedCharRE = regexp.MustCompile(`^.*\._Ctype_unsignedchar$`)
+
+ // cUint8tCharRE is a regular expression that matches a cgo uint8_t.
+ // It is used to detect uint8_t arrays to hexdump them.
+ cUint8tCharRE = regexp.MustCompile(`^.*\._Ctype_uint8_t$`)
+)
+
+// dumpState contains information about the state of a dump operation.
+type dumpState struct {
+ w io.Writer
+ depth int
+ pointers map[uintptr]int
+ ignoreNextType bool
+ ignoreNextIndent bool
+ cs *ConfigState
+}
+
+// indent performs indentation according to the depth level and cs.Indent
+// option.
+func (d *dumpState) indent() {
+ if d.ignoreNextIndent {
+ d.ignoreNextIndent = false
+ return
+ }
+ d.w.Write(bytes.Repeat([]byte(d.cs.Indent), d.depth))
+}
+
+// unpackValue returns values inside of non-nil interfaces when possible.
+// This is useful for data types like structs, arrays, slices, and maps which
+// can contain varying types packed inside an interface.
+func (d *dumpState) unpackValue(v reflect.Value) reflect.Value {
+ if v.Kind() == reflect.Interface && !v.IsNil() {
+ v = v.Elem()
+ }
+ return v
+}
+
+// dumpPtr handles formatting of pointers by indirecting them as necessary.
+func (d *dumpState) dumpPtr(v reflect.Value) {
+ // Remove pointers at or below the current depth from map used to detect
+ // circular refs.
+ for k, depth := range d.pointers {
+ if depth >= d.depth {
+ delete(d.pointers, k)
+ }
+ }
+
+ // Keep list of all dereferenced pointers to show later.
+ pointerChain := make([]uintptr, 0)
+
+ // Figure out how many levels of indirection there are by dereferencing
+ // pointers and unpacking interfaces down the chain while detecting circular
+ // references.
+ nilFound := false
+ cycleFound := false
+ indirects := 0
+ ve := v
+ for ve.Kind() == reflect.Ptr {
+ if ve.IsNil() {
+ nilFound = true
+ break
+ }
+ indirects++
+ addr := ve.Pointer()
+ pointerChain = append(pointerChain, addr)
+ if pd, ok := d.pointers[addr]; ok && pd < d.depth {
+ cycleFound = true
+ indirects--
+ break
+ }
+ d.pointers[addr] = d.depth
+
+ ve = ve.Elem()
+ if ve.Kind() == reflect.Interface {
+ if ve.IsNil() {
+ nilFound = true
+ break
+ }
+ ve = ve.Elem()
+ }
+ }
+
+ // Display type information.
+ d.w.Write(openParenBytes)
+ d.w.Write(bytes.Repeat(asteriskBytes, indirects))
+ d.w.Write([]byte(ve.Type().String()))
+ d.w.Write(closeParenBytes)
+
+ // Display pointer information.
+ if !d.cs.DisablePointerAddresses && len(pointerChain) > 0 {
+ d.w.Write(openParenBytes)
+ for i, addr := range pointerChain {
+ if i > 0 {
+ d.w.Write(pointerChainBytes)
+ }
+ printHexPtr(d.w, addr)
+ }
+ d.w.Write(closeParenBytes)
+ }
+
+ // Display dereferenced value.
+ d.w.Write(openParenBytes)
+ switch {
+ case nilFound:
+ d.w.Write(nilAngleBytes)
+
+ case cycleFound:
+ d.w.Write(circularBytes)
+
+ default:
+ d.ignoreNextType = true
+ d.dump(ve)
+ }
+ d.w.Write(closeParenBytes)
+}
+
+// dumpSlice handles formatting of arrays and slices. Byte (uint8 under
+// reflection) arrays and slices are dumped in hexdump -C fashion.
+func (d *dumpState) dumpSlice(v reflect.Value) {
+ // Determine whether this type should be hex dumped or not. Also,
+ // for types which should be hexdumped, try to use the underlying data
+ // first, then fall back to trying to convert them to a uint8 slice.
+ var buf []uint8
+ doConvert := false
+ doHexDump := false
+ numEntries := v.Len()
+ if numEntries > 0 {
+ vt := v.Index(0).Type()
+ vts := vt.String()
+ switch {
+ // C types that need to be converted.
+ case cCharRE.MatchString(vts):
+ fallthrough
+ case cUnsignedCharRE.MatchString(vts):
+ fallthrough
+ case cUint8tCharRE.MatchString(vts):
+ doConvert = true
+
+ // Try to use existing uint8 slices and fall back to converting
+ // and copying if that fails.
+ case vt.Kind() == reflect.Uint8:
+ // We need an addressable interface to convert the type
+ // to a byte slice. However, the reflect package won't
+ // give us an interface on certain things like
+ // unexported struct fields in order to enforce
+ // visibility rules. We use unsafe, when available, to
+ // bypass these restrictions since this package does not
+ // mutate the values.
+ vs := v
+ if !vs.CanInterface() || !vs.CanAddr() {
+ vs = unsafeReflectValue(vs)
+ }
+ if !UnsafeDisabled {
+ vs = vs.Slice(0, numEntries)
+
+ // Use the existing uint8 slice if it can be
+ // type asserted.
+ iface := vs.Interface()
+ if slice, ok := iface.([]uint8); ok {
+ buf = slice
+ doHexDump = true
+ break
+ }
+ }
+
+ // The underlying data needs to be converted if it can't
+ // be type asserted to a uint8 slice.
+ doConvert = true
+ }
+
+ // Copy and convert the underlying type if needed.
+ if doConvert && vt.ConvertibleTo(uint8Type) {
+ // Convert and copy each element into a uint8 byte
+ // slice.
+ buf = make([]uint8, numEntries)
+ for i := 0; i < numEntries; i++ {
+ vv := v.Index(i)
+ buf[i] = uint8(vv.Convert(uint8Type).Uint())
+ }
+ doHexDump = true
+ }
+ }
+
+ // Hexdump the entire slice as needed.
+ if doHexDump {
+ indent := strings.Repeat(d.cs.Indent, d.depth)
+ str := indent + hex.Dump(buf)
+ str = strings.Replace(str, "\n", "\n"+indent, -1)
+ str = strings.TrimRight(str, d.cs.Indent)
+ d.w.Write([]byte(str))
+ return
+ }
+
+ // Recursively call dump for each item.
+ for i := 0; i < numEntries; i++ {
+ d.dump(d.unpackValue(v.Index(i)))
+ if i < (numEntries - 1) {
+ d.w.Write(commaNewlineBytes)
+ } else {
+ d.w.Write(newlineBytes)
+ }
+ }
+}
+
+// dump is the main workhorse for dumping a value. It uses the passed reflect
+// value to figure out what kind of object we are dealing with and formats it
+// appropriately. It is a recursive function, however circular data structures
+// are detected and handled properly.
+func (d *dumpState) dump(v reflect.Value) {
+ // Handle invalid reflect values immediately.
+ kind := v.Kind()
+ if kind == reflect.Invalid {
+ d.w.Write(invalidAngleBytes)
+ return
+ }
+
+ // Handle pointers specially.
+ if kind == reflect.Ptr {
+ d.indent()
+ d.dumpPtr(v)
+ return
+ }
+
+ // Print type information unless already handled elsewhere.
+ if !d.ignoreNextType {
+ d.indent()
+ d.w.Write(openParenBytes)
+ d.w.Write([]byte(v.Type().String()))
+ d.w.Write(closeParenBytes)
+ d.w.Write(spaceBytes)
+ }
+ d.ignoreNextType = false
+
+ // Display length and capacity if the built-in len and cap functions
+ // work with the value's kind and the len/cap itself is non-zero.
+ valueLen, valueCap := 0, 0
+ switch v.Kind() {
+ case reflect.Array, reflect.Slice, reflect.Chan:
+ valueLen, valueCap = v.Len(), v.Cap()
+ case reflect.Map, reflect.String:
+ valueLen = v.Len()
+ }
+ if valueLen != 0 || !d.cs.DisableCapacities && valueCap != 0 {
+ d.w.Write(openParenBytes)
+ if valueLen != 0 {
+ d.w.Write(lenEqualsBytes)
+ printInt(d.w, int64(valueLen), 10)
+ }
+ if !d.cs.DisableCapacities && valueCap != 0 {
+ if valueLen != 0 {
+ d.w.Write(spaceBytes)
+ }
+ d.w.Write(capEqualsBytes)
+ printInt(d.w, int64(valueCap), 10)
+ }
+ d.w.Write(closeParenBytes)
+ d.w.Write(spaceBytes)
+ }
+
+ // Call Stringer/error interfaces if they exist and the handle methods flag
+ // is enabled
+ if !d.cs.DisableMethods {
+ if (kind != reflect.Invalid) && (kind != reflect.Interface) {
+ if handled := handleMethods(d.cs, d.w, v); handled {
+ return
+ }
+ }
+ }
+
+ switch kind {
+ case reflect.Invalid:
+ // Do nothing. We should never get here since invalid has already
+ // been handled above.
+
+ case reflect.Bool:
+ printBool(d.w, v.Bool())
+
+ case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
+ printInt(d.w, v.Int(), 10)
+
+ case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
+ printUint(d.w, v.Uint(), 10)
+
+ case reflect.Float32:
+ printFloat(d.w, v.Float(), 32)
+
+ case reflect.Float64:
+ printFloat(d.w, v.Float(), 64)
+
+ case reflect.Complex64:
+ printComplex(d.w, v.Complex(), 32)
+
+ case reflect.Complex128:
+ printComplex(d.w, v.Complex(), 64)
+
+ case reflect.Slice:
+ if v.IsNil() {
+ d.w.Write(nilAngleBytes)
+ break
+ }
+ fallthrough
+
+ case reflect.Array:
+ d.w.Write(openBraceNewlineBytes)
+ d.depth++
+ if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) {
+ d.indent()
+ d.w.Write(maxNewlineBytes)
+ } else {
+ d.dumpSlice(v)
+ }
+ d.depth--
+ d.indent()
+ d.w.Write(closeBraceBytes)
+
+ case reflect.String:
+ d.w.Write([]byte(strconv.Quote(v.String())))
+
+ case reflect.Interface:
+ // The only time we should get here is for nil interfaces due to
+ // unpackValue calls.
+ if v.IsNil() {
+ d.w.Write(nilAngleBytes)
+ }
+
+ case reflect.Ptr:
+ // Do nothing. We should never get here since pointers have already
+ // been handled above.
+
+ case reflect.Map:
+ // nil maps should be indicated as different than empty maps
+ if v.IsNil() {
+ d.w.Write(nilAngleBytes)
+ break
+ }
+
+ d.w.Write(openBraceNewlineBytes)
+ d.depth++
+ if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) {
+ d.indent()
+ d.w.Write(maxNewlineBytes)
+ } else {
+ numEntries := v.Len()
+ keys := v.MapKeys()
+ if d.cs.SortKeys {
+ sortValues(keys, d.cs)
+ }
+ for i, key := range keys {
+ d.dump(d.unpackValue(key))
+ d.w.Write(colonSpaceBytes)
+ d.ignoreNextIndent = true
+ d.dump(d.unpackValue(v.MapIndex(key)))
+ if i < (numEntries - 1) {
+ d.w.Write(commaNewlineBytes)
+ } else {
+ d.w.Write(newlineBytes)
+ }
+ }
+ }
+ d.depth--
+ d.indent()
+ d.w.Write(closeBraceBytes)
+
+ case reflect.Struct:
+ d.w.Write(openBraceNewlineBytes)
+ d.depth++
+ if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) {
+ d.indent()
+ d.w.Write(maxNewlineBytes)
+ } else {
+ vt := v.Type()
+ numFields := v.NumField()
+ for i := 0; i < numFields; i++ {
+ d.indent()
+ vtf := vt.Field(i)
+ d.w.Write([]byte(vtf.Name))
+ d.w.Write(colonSpaceBytes)
+ d.ignoreNextIndent = true
+ d.dump(d.unpackValue(v.Field(i)))
+ if i < (numFields - 1) {
+ d.w.Write(commaNewlineBytes)
+ } else {
+ d.w.Write(newlineBytes)
+ }
+ }
+ }
+ d.depth--
+ d.indent()
+ d.w.Write(closeBraceBytes)
+
+ case reflect.Uintptr:
+ printHexPtr(d.w, uintptr(v.Uint()))
+
+ case reflect.UnsafePointer, reflect.Chan, reflect.Func:
+ printHexPtr(d.w, v.Pointer())
+
+ // There were not any other types at the time this code was written, but
+ // fall back to letting the default fmt package handle it in case any new
+ // types are added.
+ default:
+ if v.CanInterface() {
+ fmt.Fprintf(d.w, "%v", v.Interface())
+ } else {
+ fmt.Fprintf(d.w, "%v", v.String())
+ }
+ }
+}
+
+// fdump is a helper function to consolidate the logic from the various public
+// methods which take varying writers and config states.
+func fdump(cs *ConfigState, w io.Writer, a ...interface{}) {
+ for _, arg := range a {
+ if arg == nil {
+ w.Write(interfaceBytes)
+ w.Write(spaceBytes)
+ w.Write(nilAngleBytes)
+ w.Write(newlineBytes)
+ continue
+ }
+
+ d := dumpState{w: w, cs: cs}
+ d.pointers = make(map[uintptr]int)
+ d.dump(reflect.ValueOf(arg))
+ d.w.Write(newlineBytes)
+ }
+}
+
+// Fdump formats and displays the passed arguments to io.Writer w. It formats
+// exactly the same as Dump.
+func Fdump(w io.Writer, a ...interface{}) {
+ fdump(&Config, w, a...)
+}
+
+// Sdump returns a string with the passed arguments formatted exactly the same
+// as Dump.
+func Sdump(a ...interface{}) string {
+ var buf bytes.Buffer
+ fdump(&Config, &buf, a...)
+ return buf.String()
+}
+
+/*
+Dump displays the passed parameters to standard out with newlines, customizable
+indentation, and additional debug information such as complete types and all
+pointer addresses used to indirect to the final value. It provides the
+following features over the built-in printing facilities provided by the fmt
+package:
+
+ * Pointers are dereferenced and followed
+ * Circular data structures are detected and handled properly
+ * Custom Stringer/error interfaces are optionally invoked, including
+ on unexported types
+ * Custom types which only implement the Stringer/error interfaces via
+ a pointer receiver are optionally invoked when passing non-pointer
+ variables
+ * Byte arrays and slices are dumped like the hexdump -C command which
+ includes offsets, byte values in hex, and ASCII output
+
+The configuration options are controlled by an exported package global,
+spew.Config. See ConfigState for options documentation.
+
+See Fdump if you would prefer dumping to an arbitrary io.Writer or Sdump to
+get the formatted result as a string.
+*/
+func Dump(a ...interface{}) {
+ fdump(&Config, os.Stdout, a...)
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/davecgh/go-spew/spew/format.go b/go/ql/test/experimental/CWE-321/vendor/github.com/davecgh/go-spew/spew/format.go
new file mode 100644
index 000000000000..b04edb7d7ac2
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/davecgh/go-spew/spew/format.go
@@ -0,0 +1,419 @@
+/*
+ * Copyright (c) 2013-2016 Dave Collins
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+package spew
+
+import (
+ "bytes"
+ "fmt"
+ "reflect"
+ "strconv"
+ "strings"
+)
+
+// supportedFlags is a list of all the character flags supported by fmt package.
+const supportedFlags = "0-+# "
+
+// formatState implements the fmt.Formatter interface and contains information
+// about the state of a formatting operation. The NewFormatter function can
+// be used to get a new Formatter which can be used directly as arguments
+// in standard fmt package printing calls.
+type formatState struct {
+ value interface{}
+ fs fmt.State
+ depth int
+ pointers map[uintptr]int
+ ignoreNextType bool
+ cs *ConfigState
+}
+
+// buildDefaultFormat recreates the original format string without precision
+// and width information to pass in to fmt.Sprintf in the case of an
+// unrecognized type. Unless new types are added to the language, this
+// function won't ever be called.
+func (f *formatState) buildDefaultFormat() (format string) {
+ buf := bytes.NewBuffer(percentBytes)
+
+ for _, flag := range supportedFlags {
+ if f.fs.Flag(int(flag)) {
+ buf.WriteRune(flag)
+ }
+ }
+
+ buf.WriteRune('v')
+
+ format = buf.String()
+ return format
+}
+
+// constructOrigFormat recreates the original format string including precision
+// and width information to pass along to the standard fmt package. This allows
+// automatic deferral of all format strings this package doesn't support.
+func (f *formatState) constructOrigFormat(verb rune) (format string) {
+ buf := bytes.NewBuffer(percentBytes)
+
+ for _, flag := range supportedFlags {
+ if f.fs.Flag(int(flag)) {
+ buf.WriteRune(flag)
+ }
+ }
+
+ if width, ok := f.fs.Width(); ok {
+ buf.WriteString(strconv.Itoa(width))
+ }
+
+ if precision, ok := f.fs.Precision(); ok {
+ buf.Write(precisionBytes)
+ buf.WriteString(strconv.Itoa(precision))
+ }
+
+ buf.WriteRune(verb)
+
+ format = buf.String()
+ return format
+}
+
+// unpackValue returns values inside of non-nil interfaces when possible and
+// ensures that types for values which have been unpacked from an interface
+// are displayed when the show types flag is also set.
+// This is useful for data types like structs, arrays, slices, and maps which
+// can contain varying types packed inside an interface.
+func (f *formatState) unpackValue(v reflect.Value) reflect.Value {
+ if v.Kind() == reflect.Interface {
+ f.ignoreNextType = false
+ if !v.IsNil() {
+ v = v.Elem()
+ }
+ }
+ return v
+}
+
+// formatPtr handles formatting of pointers by indirecting them as necessary.
+func (f *formatState) formatPtr(v reflect.Value) {
+ // Display nil if top level pointer is nil.
+ showTypes := f.fs.Flag('#')
+ if v.IsNil() && (!showTypes || f.ignoreNextType) {
+ f.fs.Write(nilAngleBytes)
+ return
+ }
+
+ // Remove pointers at or below the current depth from map used to detect
+ // circular refs.
+ for k, depth := range f.pointers {
+ if depth >= f.depth {
+ delete(f.pointers, k)
+ }
+ }
+
+ // Keep list of all dereferenced pointers to possibly show later.
+ pointerChain := make([]uintptr, 0)
+
+ // Figure out how many levels of indirection there are by derferencing
+ // pointers and unpacking interfaces down the chain while detecting circular
+ // references.
+ nilFound := false
+ cycleFound := false
+ indirects := 0
+ ve := v
+ for ve.Kind() == reflect.Ptr {
+ if ve.IsNil() {
+ nilFound = true
+ break
+ }
+ indirects++
+ addr := ve.Pointer()
+ pointerChain = append(pointerChain, addr)
+ if pd, ok := f.pointers[addr]; ok && pd < f.depth {
+ cycleFound = true
+ indirects--
+ break
+ }
+ f.pointers[addr] = f.depth
+
+ ve = ve.Elem()
+ if ve.Kind() == reflect.Interface {
+ if ve.IsNil() {
+ nilFound = true
+ break
+ }
+ ve = ve.Elem()
+ }
+ }
+
+ // Display type or indirection level depending on flags.
+ if showTypes && !f.ignoreNextType {
+ f.fs.Write(openParenBytes)
+ f.fs.Write(bytes.Repeat(asteriskBytes, indirects))
+ f.fs.Write([]byte(ve.Type().String()))
+ f.fs.Write(closeParenBytes)
+ } else {
+ if nilFound || cycleFound {
+ indirects += strings.Count(ve.Type().String(), "*")
+ }
+ f.fs.Write(openAngleBytes)
+ f.fs.Write([]byte(strings.Repeat("*", indirects)))
+ f.fs.Write(closeAngleBytes)
+ }
+
+ // Display pointer information depending on flags.
+ if f.fs.Flag('+') && (len(pointerChain) > 0) {
+ f.fs.Write(openParenBytes)
+ for i, addr := range pointerChain {
+ if i > 0 {
+ f.fs.Write(pointerChainBytes)
+ }
+ printHexPtr(f.fs, addr)
+ }
+ f.fs.Write(closeParenBytes)
+ }
+
+ // Display dereferenced value.
+ switch {
+ case nilFound:
+ f.fs.Write(nilAngleBytes)
+
+ case cycleFound:
+ f.fs.Write(circularShortBytes)
+
+ default:
+ f.ignoreNextType = true
+ f.format(ve)
+ }
+}
+
+// format is the main workhorse for providing the Formatter interface. It
+// uses the passed reflect value to figure out what kind of object we are
+// dealing with and formats it appropriately. It is a recursive function,
+// however circular data structures are detected and handled properly.
+func (f *formatState) format(v reflect.Value) {
+ // Handle invalid reflect values immediately.
+ kind := v.Kind()
+ if kind == reflect.Invalid {
+ f.fs.Write(invalidAngleBytes)
+ return
+ }
+
+ // Handle pointers specially.
+ if kind == reflect.Ptr {
+ f.formatPtr(v)
+ return
+ }
+
+ // Print type information unless already handled elsewhere.
+ if !f.ignoreNextType && f.fs.Flag('#') {
+ f.fs.Write(openParenBytes)
+ f.fs.Write([]byte(v.Type().String()))
+ f.fs.Write(closeParenBytes)
+ }
+ f.ignoreNextType = false
+
+ // Call Stringer/error interfaces if they exist and the handle methods
+ // flag is enabled.
+ if !f.cs.DisableMethods {
+ if (kind != reflect.Invalid) && (kind != reflect.Interface) {
+ if handled := handleMethods(f.cs, f.fs, v); handled {
+ return
+ }
+ }
+ }
+
+ switch kind {
+ case reflect.Invalid:
+ // Do nothing. We should never get here since invalid has already
+ // been handled above.
+
+ case reflect.Bool:
+ printBool(f.fs, v.Bool())
+
+ case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
+ printInt(f.fs, v.Int(), 10)
+
+ case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
+ printUint(f.fs, v.Uint(), 10)
+
+ case reflect.Float32:
+ printFloat(f.fs, v.Float(), 32)
+
+ case reflect.Float64:
+ printFloat(f.fs, v.Float(), 64)
+
+ case reflect.Complex64:
+ printComplex(f.fs, v.Complex(), 32)
+
+ case reflect.Complex128:
+ printComplex(f.fs, v.Complex(), 64)
+
+ case reflect.Slice:
+ if v.IsNil() {
+ f.fs.Write(nilAngleBytes)
+ break
+ }
+ fallthrough
+
+ case reflect.Array:
+ f.fs.Write(openBracketBytes)
+ f.depth++
+ if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) {
+ f.fs.Write(maxShortBytes)
+ } else {
+ numEntries := v.Len()
+ for i := 0; i < numEntries; i++ {
+ if i > 0 {
+ f.fs.Write(spaceBytes)
+ }
+ f.ignoreNextType = true
+ f.format(f.unpackValue(v.Index(i)))
+ }
+ }
+ f.depth--
+ f.fs.Write(closeBracketBytes)
+
+ case reflect.String:
+ f.fs.Write([]byte(v.String()))
+
+ case reflect.Interface:
+ // The only time we should get here is for nil interfaces due to
+ // unpackValue calls.
+ if v.IsNil() {
+ f.fs.Write(nilAngleBytes)
+ }
+
+ case reflect.Ptr:
+ // Do nothing. We should never get here since pointers have already
+ // been handled above.
+
+ case reflect.Map:
+ // nil maps should be indicated as different than empty maps
+ if v.IsNil() {
+ f.fs.Write(nilAngleBytes)
+ break
+ }
+
+ f.fs.Write(openMapBytes)
+ f.depth++
+ if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) {
+ f.fs.Write(maxShortBytes)
+ } else {
+ keys := v.MapKeys()
+ if f.cs.SortKeys {
+ sortValues(keys, f.cs)
+ }
+ for i, key := range keys {
+ if i > 0 {
+ f.fs.Write(spaceBytes)
+ }
+ f.ignoreNextType = true
+ f.format(f.unpackValue(key))
+ f.fs.Write(colonBytes)
+ f.ignoreNextType = true
+ f.format(f.unpackValue(v.MapIndex(key)))
+ }
+ }
+ f.depth--
+ f.fs.Write(closeMapBytes)
+
+ case reflect.Struct:
+ numFields := v.NumField()
+ f.fs.Write(openBraceBytes)
+ f.depth++
+ if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) {
+ f.fs.Write(maxShortBytes)
+ } else {
+ vt := v.Type()
+ for i := 0; i < numFields; i++ {
+ if i > 0 {
+ f.fs.Write(spaceBytes)
+ }
+ vtf := vt.Field(i)
+ if f.fs.Flag('+') || f.fs.Flag('#') {
+ f.fs.Write([]byte(vtf.Name))
+ f.fs.Write(colonBytes)
+ }
+ f.format(f.unpackValue(v.Field(i)))
+ }
+ }
+ f.depth--
+ f.fs.Write(closeBraceBytes)
+
+ case reflect.Uintptr:
+ printHexPtr(f.fs, uintptr(v.Uint()))
+
+ case reflect.UnsafePointer, reflect.Chan, reflect.Func:
+ printHexPtr(f.fs, v.Pointer())
+
+ // There were not any other types at the time this code was written, but
+ // fall back to letting the default fmt package handle it if any get added.
+ default:
+ format := f.buildDefaultFormat()
+ if v.CanInterface() {
+ fmt.Fprintf(f.fs, format, v.Interface())
+ } else {
+ fmt.Fprintf(f.fs, format, v.String())
+ }
+ }
+}
+
+// Format satisfies the fmt.Formatter interface. See NewFormatter for usage
+// details.
+func (f *formatState) Format(fs fmt.State, verb rune) {
+ f.fs = fs
+
+ // Use standard formatting for verbs that are not v.
+ if verb != 'v' {
+ format := f.constructOrigFormat(verb)
+ fmt.Fprintf(fs, format, f.value)
+ return
+ }
+
+ if f.value == nil {
+ if fs.Flag('#') {
+ fs.Write(interfaceBytes)
+ }
+ fs.Write(nilAngleBytes)
+ return
+ }
+
+ f.format(reflect.ValueOf(f.value))
+}
+
+// newFormatter is a helper function to consolidate the logic from the various
+// public methods which take varying config states.
+func newFormatter(cs *ConfigState, v interface{}) fmt.Formatter {
+ fs := &formatState{value: v, cs: cs}
+ fs.pointers = make(map[uintptr]int)
+ return fs
+}
+
+/*
+NewFormatter returns a custom formatter that satisfies the fmt.Formatter
+interface. As a result, it integrates cleanly with standard fmt package
+printing functions. The formatter is useful for inline printing of smaller data
+types similar to the standard %v format specifier.
+
+The custom formatter only responds to the %v (most compact), %+v (adds pointer
+addresses), %#v (adds types), or %#+v (adds types and pointer addresses) verb
+combinations. Any other verbs such as %x and %q will be sent to the the
+standard fmt package for formatting. In addition, the custom formatter ignores
+the width and precision arguments (however they will still work on the format
+specifiers not handled by the custom formatter).
+
+Typically this function shouldn't be called directly. It is much easier to make
+use of the custom formatter by calling one of the convenience functions such as
+Printf, Println, or Fprintf.
+*/
+func NewFormatter(v interface{}) fmt.Formatter {
+ return newFormatter(&Config, v)
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/davecgh/go-spew/spew/spew.go b/go/ql/test/experimental/CWE-321/vendor/github.com/davecgh/go-spew/spew/spew.go
new file mode 100644
index 000000000000..32c0e3388253
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/davecgh/go-spew/spew/spew.go
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 2013-2016 Dave Collins
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+package spew
+
+import (
+ "fmt"
+ "io"
+)
+
+// Errorf is a wrapper for fmt.Errorf that treats each argument as if it were
+// passed with a default Formatter interface returned by NewFormatter. It
+// returns the formatted string as a value that satisfies error. See
+// NewFormatter for formatting details.
+//
+// This function is shorthand for the following syntax:
+//
+// fmt.Errorf(format, spew.NewFormatter(a), spew.NewFormatter(b))
+func Errorf(format string, a ...interface{}) (err error) {
+ return fmt.Errorf(format, convertArgs(a)...)
+}
+
+// Fprint is a wrapper for fmt.Fprint that treats each argument as if it were
+// passed with a default Formatter interface returned by NewFormatter. It
+// returns the number of bytes written and any write error encountered. See
+// NewFormatter for formatting details.
+//
+// This function is shorthand for the following syntax:
+//
+// fmt.Fprint(w, spew.NewFormatter(a), spew.NewFormatter(b))
+func Fprint(w io.Writer, a ...interface{}) (n int, err error) {
+ return fmt.Fprint(w, convertArgs(a)...)
+}
+
+// Fprintf is a wrapper for fmt.Fprintf that treats each argument as if it were
+// passed with a default Formatter interface returned by NewFormatter. It
+// returns the number of bytes written and any write error encountered. See
+// NewFormatter for formatting details.
+//
+// This function is shorthand for the following syntax:
+//
+// fmt.Fprintf(w, format, spew.NewFormatter(a), spew.NewFormatter(b))
+func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) {
+ return fmt.Fprintf(w, format, convertArgs(a)...)
+}
+
+// Fprintln is a wrapper for fmt.Fprintln that treats each argument as if it
+// passed with a default Formatter interface returned by NewFormatter. See
+// NewFormatter for formatting details.
+//
+// This function is shorthand for the following syntax:
+//
+// fmt.Fprintln(w, spew.NewFormatter(a), spew.NewFormatter(b))
+func Fprintln(w io.Writer, a ...interface{}) (n int, err error) {
+ return fmt.Fprintln(w, convertArgs(a)...)
+}
+
+// Print is a wrapper for fmt.Print that treats each argument as if it were
+// passed with a default Formatter interface returned by NewFormatter. It
+// returns the number of bytes written and any write error encountered. See
+// NewFormatter for formatting details.
+//
+// This function is shorthand for the following syntax:
+//
+// fmt.Print(spew.NewFormatter(a), spew.NewFormatter(b))
+func Print(a ...interface{}) (n int, err error) {
+ return fmt.Print(convertArgs(a)...)
+}
+
+// Printf is a wrapper for fmt.Printf that treats each argument as if it were
+// passed with a default Formatter interface returned by NewFormatter. It
+// returns the number of bytes written and any write error encountered. See
+// NewFormatter for formatting details.
+//
+// This function is shorthand for the following syntax:
+//
+// fmt.Printf(format, spew.NewFormatter(a), spew.NewFormatter(b))
+func Printf(format string, a ...interface{}) (n int, err error) {
+ return fmt.Printf(format, convertArgs(a)...)
+}
+
+// Println is a wrapper for fmt.Println that treats each argument as if it were
+// passed with a default Formatter interface returned by NewFormatter. It
+// returns the number of bytes written and any write error encountered. See
+// NewFormatter for formatting details.
+//
+// This function is shorthand for the following syntax:
+//
+// fmt.Println(spew.NewFormatter(a), spew.NewFormatter(b))
+func Println(a ...interface{}) (n int, err error) {
+ return fmt.Println(convertArgs(a)...)
+}
+
+// Sprint is a wrapper for fmt.Sprint that treats each argument as if it were
+// passed with a default Formatter interface returned by NewFormatter. It
+// returns the resulting string. See NewFormatter for formatting details.
+//
+// This function is shorthand for the following syntax:
+//
+// fmt.Sprint(spew.NewFormatter(a), spew.NewFormatter(b))
+func Sprint(a ...interface{}) string {
+ return fmt.Sprint(convertArgs(a)...)
+}
+
+// Sprintf is a wrapper for fmt.Sprintf that treats each argument as if it were
+// passed with a default Formatter interface returned by NewFormatter. It
+// returns the resulting string. See NewFormatter for formatting details.
+//
+// This function is shorthand for the following syntax:
+//
+// fmt.Sprintf(format, spew.NewFormatter(a), spew.NewFormatter(b))
+func Sprintf(format string, a ...interface{}) string {
+ return fmt.Sprintf(format, convertArgs(a)...)
+}
+
+// Sprintln is a wrapper for fmt.Sprintln that treats each argument as if it
+// were passed with a default Formatter interface returned by NewFormatter. It
+// returns the resulting string. See NewFormatter for formatting details.
+//
+// This function is shorthand for the following syntax:
+//
+// fmt.Sprintln(spew.NewFormatter(a), spew.NewFormatter(b))
+func Sprintln(a ...interface{}) string {
+ return fmt.Sprintln(convertArgs(a)...)
+}
+
+// convertArgs accepts a slice of arguments and returns a slice of the same
+// length with each argument converted to a default spew Formatter interface.
+func convertArgs(args []interface{}) (formatters []interface{}) {
+ formatters = make([]interface{}, len(args))
+ for index, arg := range args {
+ formatters[index] = NewFormatter(arg)
+ }
+ return formatters
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/dgryski/go-rendezvous/LICENSE b/go/ql/test/experimental/CWE-321/vendor/github.com/dgryski/go-rendezvous/LICENSE
new file mode 100644
index 000000000000..22080f736a41
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/dgryski/go-rendezvous/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2017-2020 Damian Gryski
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/dgryski/go-rendezvous/rdv.go b/go/ql/test/experimental/CWE-321/vendor/github.com/dgryski/go-rendezvous/rdv.go
new file mode 100644
index 000000000000..7a6f8203c678
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/dgryski/go-rendezvous/rdv.go
@@ -0,0 +1,79 @@
+package rendezvous
+
+type Rendezvous struct {
+ nodes map[string]int
+ nstr []string
+ nhash []uint64
+ hash Hasher
+}
+
+type Hasher func(s string) uint64
+
+func New(nodes []string, hash Hasher) *Rendezvous {
+ r := &Rendezvous{
+ nodes: make(map[string]int, len(nodes)),
+ nstr: make([]string, len(nodes)),
+ nhash: make([]uint64, len(nodes)),
+ hash: hash,
+ }
+
+ for i, n := range nodes {
+ r.nodes[n] = i
+ r.nstr[i] = n
+ r.nhash[i] = hash(n)
+ }
+
+ return r
+}
+
+func (r *Rendezvous) Lookup(k string) string {
+ // short-circuit if we're empty
+ if len(r.nodes) == 0 {
+ return ""
+ }
+
+ khash := r.hash(k)
+
+ var midx int
+ var mhash = xorshiftMult64(khash ^ r.nhash[0])
+
+ for i, nhash := range r.nhash[1:] {
+ if h := xorshiftMult64(khash ^ nhash); h > mhash {
+ midx = i + 1
+ mhash = h
+ }
+ }
+
+ return r.nstr[midx]
+}
+
+func (r *Rendezvous) Add(node string) {
+ r.nodes[node] = len(r.nstr)
+ r.nstr = append(r.nstr, node)
+ r.nhash = append(r.nhash, r.hash(node))
+}
+
+func (r *Rendezvous) Remove(node string) {
+ // find index of node to remove
+ nidx := r.nodes[node]
+
+ // remove from the slices
+ l := len(r.nstr)
+ r.nstr[nidx] = r.nstr[l]
+ r.nstr = r.nstr[:l]
+
+ r.nhash[nidx] = r.nhash[l]
+ r.nhash = r.nhash[:l]
+
+ // update the map
+ delete(r.nodes, node)
+ moved := r.nstr[nidx]
+ r.nodes[moved] = nidx
+}
+
+func xorshiftMult64(x uint64) uint64 {
+ x ^= x >> 12 // a
+ x ^= x << 25 // b
+ x ^= x >> 27 // c
+ return x * 2685821657736338717
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/eknkc/amber/.travis.yml b/go/ql/test/experimental/CWE-321/vendor/github.com/eknkc/amber/.travis.yml
new file mode 100644
index 000000000000..fa3d8d31c20b
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/eknkc/amber/.travis.yml
@@ -0,0 +1,7 @@
+language: go
+
+go:
+ - tip
+
+script:
+ - go test -v ./...
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/eknkc/amber/LICENSE b/go/ql/test/experimental/CWE-321/vendor/github.com/eknkc/amber/LICENSE
new file mode 100644
index 000000000000..77dfb91cecc1
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/eknkc/amber/LICENSE
@@ -0,0 +1,9 @@
+(The MIT License)
+
+Copyright (c) 2012 Ekin Koc ekin@eknkc.com
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/eknkc/amber/README.md b/go/ql/test/experimental/CWE-321/vendor/github.com/eknkc/amber/README.md
new file mode 100644
index 000000000000..ec0e52fc4ca6
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/eknkc/amber/README.md
@@ -0,0 +1,442 @@
+# amber [](http://godoc.org/github.com/eknkc/amber) [](https://travis-ci.org/eknkc/amber)
+
+## Notice
+> While Amber is perfectly fine and stable to use, I've been working on a direct Pug.js port for Go. It is somewhat hacky at the moment but take a look at [Pug.go](https://github.com/eknkc/pug) if you are looking for a [Pug.js](https://github.com/pugjs/pug) compatible Go template engine.
+
+### Usage
+```go
+import "github.com/eknkc/amber"
+```
+
+Amber is an elegant templating engine for Go Programming Language
+It is inspired from HAML and Jade
+
+### Tags
+
+A tag is simply a word:
+
+ html
+
+is converted to
+
+```html
+
+```
+
+It is possible to add ID and CLASS attributes to tags:
+
+ div#main
+ span.time
+
+are converted to
+
+```html
+
+
+```
+
+Any arbitrary attribute name / value pair can be added this way:
+
+ a[href="http://www.google.com"]
+
+You can mix multiple attributes together
+
+ a#someid[href="/"][title="Main Page"].main.link Click Link
+
+gets converted to
+
+```html
+Click Link
+```
+
+It is also possible to define these attributes within the block of a tag
+
+ a
+ #someid
+ [href="/"]
+ [title="Main Page"]
+ .main
+ .link
+ | Click Link
+
+### Doctypes
+
+To add a doctype, use `!!!` or `doctype` keywords:
+
+ !!! transitional
+ //
+
+or use `doctype`
+
+ doctype 5
+ //
+
+Available options: `5`, `default`, `xml`, `transitional`, `strict`, `frameset`, `1.1`, `basic`, `mobile`
+
+### Tag Content
+
+For single line tag text, you can just append the text after tag name:
+
+ p Testing!
+
+would yield
+
+
Testing!
+
+For multi line tag text, or nested tags, use indentation:
+
+ html
+ head
+ title Page Title
+ body
+ div#content
+ p
+ | This is a long page content
+ | These lines are all part of the parent p
+
+ a[href="/"] Go To Main Page
+
+### Data
+
+Input template data can be reached by key names directly. For example, assuming the template has been
+executed with following JSON data:
+
+```json
+{
+ "Name": "Ekin",
+ "LastName": "Koc",
+ "Repositories": [
+ "amber",
+ "dateformat"
+ ],
+ "Avatar": "/images/ekin.jpg",
+ "Friends": 17
+}
+```
+
+It is possible to interpolate fields using `#{}`
+
+ p Welcome #{Name}!
+
+would print
+
+```html
+
Welcome Ekin!
+```
+
+Attributes can have field names as well
+
+ a[title=Name][href="/ekin.koc"]
+
+would print
+
+```html
+
+```
+
+### Expressions
+
+Amber can expand basic expressions. For example, it is possible to concatenate strings with + operator:
+
+ p Welcome #{Name + " " + LastName}
+
+Arithmetic expressions are also supported:
+
+ p You need #{50 - Friends} more friends to reach 50!
+
+Expressions can be used within attributes
+
+ img[alt=Name + " " + LastName][src=Avatar]
+
+### Variables
+
+It is possible to define dynamic variables within templates,
+all variables must start with a $ character and can be assigned as in the following example:
+
+ div
+ $fullname = Name + " " + LastName
+ p Welcome #{$fullname}
+
+If you need to access the supplied data itself (i.e. the object containing Name, LastName etc fields.) you can use `$` variable
+
+ p $.Name
+
+### Conditions
+
+For conditional blocks, it is possible to use `if `
+
+ div
+ if Friends > 10
+ p You have more than 10 friends
+ else if Friends > 5
+ p You have more than 5 friends
+ else
+ p You need more friends
+
+Again, it is possible to use arithmetic and boolean operators
+
+ div
+ if Name == "Ekin" && LastName == "Koc"
+ p Hey! I know you..
+
+There is a special syntax for conditional attributes. Only block attributes can have conditions;
+
+ div
+ .hasfriends ? Friends > 0
+
+This would yield a div with `hasfriends` class only if the `Friends > 0` condition holds. It is
+perfectly fine to use the same method for other types of attributes:
+
+ div
+ #foo ? Name == "Ekin"
+ [bar=baz] ? len(Repositories) > 0
+
+### Iterations
+
+It is possible to iterate over arrays and maps using `each`:
+
+ each $repo in Repositories
+ p #{$repo}
+
+would print
+
+ p amber
+ p dateformat
+
+It is also possible to iterate over values and indexes at the same time
+
+ each $i, $repo in Repositories
+ p
+ .even ? $i % 2 == 0
+ .odd ? $i % 2 == 1
+
+### Mixins
+
+Mixins (reusable template blocks that accept arguments) can be defined:
+
+ mixin surprise
+ span Surprise!
+ mixin link($href, $title, $text)
+ a[href=$href][title=$title] #{$text}
+
+and then called multiple times within a template (or even within another mixin definition):
+
+ div
+ +surprise
+ +surprise
+ +link("http://google.com", "Google", "Check out Google")
+
+Template data, variables, expressions, etc., can all be passed as arguments:
+
+ +link(GoogleUrl, $googleTitle, "Check out " + $googleTitle)
+
+### Imports
+
+A template can import other templates using `import`:
+
+ a.amber
+ p this is template a
+
+ b.amber
+ p this is template b
+
+ c.amber
+ div
+ import a
+ import b
+
+gets compiled to
+
+ div
+ p this is template a
+ p this is template b
+
+### Inheritance
+
+A template can inherit other templates. In order to inherit another template, an `extends` keyword should be used.
+Parent template can define several named blocks and child template can modify the blocks.
+
+ master.amber
+ !!! 5
+ html
+ head
+ block meta
+ meta[name="description"][content="This is a great website"]
+
+ title
+ block title
+ | Default title
+ body
+ block content
+
+ subpage.amber
+ extends master
+
+ block title
+ | Some sub page!
+
+ block append meta
+ // This will be added after the description meta tag. It is also possible
+ // to prepend someting to an existing block
+ meta[name="keywords"][content="foo bar"]
+
+ block content
+ div#main
+ p Some content here
+
+### License
+(The MIT License)
+
+Copyright (c) 2012 Ekin Koc
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+## Usage
+
+```go
+var DefaultOptions = Options{true, false}
+var DefaultDirOptions = DirOptions{".amber", true}
+```
+
+#### func Compile
+
+```go
+func Compile(input string, options Options) (*template.Template, error)
+```
+Parses and compiles the supplied amber template string. Returns corresponding Go
+Template (html/templates) instance. Necessary runtime functions will be injected
+and the template will be ready to be executed.
+
+#### func CompileFile
+
+```go
+func CompileFile(filename string, options Options) (*template.Template, error)
+```
+Parses and compiles the contents of supplied filename. Returns corresponding Go
+Template (html/templates) instance. Necessary runtime functions will be injected
+and the template will be ready to be executed.
+
+#### func CompileDir
+```go
+func CompileDir(dirname string, dopt DirOptions, opt Options) (map[string]*template.Template, error)
+```
+Parses and compiles the contents of a supplied directory name. Returns a mapping of template name (extension stripped) to corresponding Go Template (html/template) instance. Necessary runtime functions will be injected and the template will be ready to be executed.
+
+If there are templates in subdirectories, its key in the map will be it's path relative to `dirname`. For example:
+```
+templates/
+ |-- index.amber
+ |-- layouts/
+ |-- base.amber
+```
+```go
+templates, err := amber.CompileDir("templates/", amber.DefaultDirOptions, amber.DefaultOptions)
+templates["index"] // index.amber Go Template
+templates["layouts/base"] // base.amber Go Template
+```
+By default, the search will be recursive and will match only files ending in ".amber". If recursive is turned off, it will only search the top level of the directory. Specified extension must start with a period.
+
+#### type Compiler
+
+```go
+type Compiler struct {
+ // Compiler options
+ Options
+}
+```
+
+Compiler is the main interface of Amber Template Engine. In order to use an
+Amber template, it is required to create a Compiler and compile an Amber source
+to native Go template.
+
+ compiler := amber.New()
+ // Parse the input file
+ err := compiler.ParseFile("./input.amber")
+ if err == nil {
+ // Compile input file to Go template
+ tpl, err := compiler.Compile()
+ if err == nil {
+ // Check built in html/template documentation for further details
+ tpl.Execute(os.Stdout, somedata)
+ }
+ }
+
+#### func New
+
+```go
+func New() *Compiler
+```
+Create and initialize a new Compiler
+
+#### func (*Compiler) Compile
+
+```go
+func (c *Compiler) Compile() (*template.Template, error)
+```
+Compile amber and create a Go Template (html/templates) instance. Necessary
+runtime functions will be injected and the template will be ready to be
+executed.
+
+#### func (*Compiler) CompileString
+
+```go
+func (c *Compiler) CompileString() (string, error)
+```
+Compile template and return the Go Template source You would not be using this
+unless debugging / checking the output. Please use Compile method to obtain a
+template instance directly.
+
+#### func (*Compiler) CompileWriter
+
+```go
+func (c *Compiler) CompileWriter(out io.Writer) (err error)
+```
+Compile amber and write the Go Template source into given io.Writer instance You
+would not be using this unless debugging / checking the output. Please use
+Compile method to obtain a template instance directly.
+
+#### func (*Compiler) Parse
+
+```go
+func (c *Compiler) Parse(input string) (err error)
+```
+Parse given raw amber template string.
+
+#### func (*Compiler) ParseFile
+
+```go
+func (c *Compiler) ParseFile(filename string) (err error)
+```
+Parse the amber template file in given path
+
+#### type Options
+
+```go
+type Options struct {
+ // Setting if pretty printing is enabled.
+ // Pretty printing ensures that the output html is properly indented and in human readable form.
+ // If disabled, produced HTML is compact. This might be more suitable in production environments.
+ // Defaukt: true
+ PrettyPrint bool
+ // Setting if line number emitting is enabled
+ // In this form, Amber emits line number comments in the output template. It is usable in debugging environments.
+ // Default: false
+ LineNumbers bool
+}
+```
+
+#### type DirOptions
+
+```go
+// Used to provide options to directory compilation
+type DirOptions struct {
+ // File extension to match for compilation
+ Ext string
+ // Whether or not to walk subdirectories
+ Recursive bool
+}
+```
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/eknkc/amber/compiler.go b/go/ql/test/experimental/CWE-321/vendor/github.com/eknkc/amber/compiler.go
new file mode 100644
index 000000000000..bec57b72f267
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/eknkc/amber/compiler.go
@@ -0,0 +1,844 @@
+package amber
+
+import (
+ "bytes"
+ "container/list"
+ "errors"
+ "fmt"
+ "go/ast"
+ gp "go/parser"
+ gt "go/token"
+ "html/template"
+ "io"
+ "net/http"
+ "os"
+ "path/filepath"
+ "reflect"
+ "regexp"
+ "sort"
+ "strconv"
+ "strings"
+
+ "github.com/eknkc/amber/parser"
+)
+
+var builtinFunctions = [...]string{
+ "len",
+ "print",
+ "printf",
+ "println",
+ "urlquery",
+ "js",
+ "json",
+ "index",
+ "html",
+ "unescaped",
+}
+
+const (
+ dollar = "__DOLLAR__"
+)
+
+// Compiler is the main interface of Amber Template Engine.
+// In order to use an Amber template, it is required to create a Compiler and
+// compile an Amber source to native Go template.
+// compiler := amber.New()
+// // Parse the input file
+// err := compiler.ParseFile("./input.amber")
+// if err == nil {
+// // Compile input file to Go template
+// tpl, err := compiler.Compile()
+// if err == nil {
+// // Check built in html/template documentation for further details
+// tpl.Execute(os.Stdout, somedata)
+// }
+// }
+type Compiler struct {
+ // Compiler options
+ Options
+ filename string
+ node parser.Node
+ indentLevel int
+ newline bool
+ buffer *bytes.Buffer
+ tempvarIndex int
+ mixins map[string]*parser.Mixin
+}
+
+// New creates and initialize a new Compiler.
+func New() *Compiler {
+ compiler := new(Compiler)
+ compiler.filename = ""
+ compiler.tempvarIndex = 0
+ compiler.PrettyPrint = true
+ compiler.Options = DefaultOptions
+ compiler.mixins = make(map[string]*parser.Mixin)
+
+ return compiler
+}
+
+// Options defines template output behavior.
+type Options struct {
+ // Setting if pretty printing is enabled.
+ // Pretty printing ensures that the output html is properly indented and in human readable form.
+ // If disabled, produced HTML is compact. This might be more suitable in production environments.
+ // Default: true
+ PrettyPrint bool
+ // Setting if line number emitting is enabled
+ // In this form, Amber emits line number comments in the output template. It is usable in debugging environments.
+ // Default: false
+ LineNumbers bool
+ // Setting the virtual filesystem to use
+ // If set, will attempt to use a virtual filesystem provided instead of os.
+ // Default: nil
+ VirtualFilesystem http.FileSystem
+}
+
+// DirOptions is used to provide options to directory compilation.
+type DirOptions struct {
+ // File extension to match for compilation
+ Ext string
+ // Whether or not to walk subdirectories
+ Recursive bool
+}
+
+// DefaultOptions sets pretty-printing to true and line numbering to false.
+var DefaultOptions = Options{true, false, nil}
+
+// DefaultDirOptions sets expected file extension to ".amber" and recursive search for templates within a directory to true.
+var DefaultDirOptions = DirOptions{".amber", true}
+
+// Compile parses and compiles the supplied amber template string. Returns corresponding Go Template (html/templates) instance.
+// Necessary runtime functions will be injected and the template will be ready to be executed.
+func Compile(input string, options Options) (*template.Template, error) {
+ comp := New()
+ comp.Options = options
+
+ err := comp.Parse(input)
+ if err != nil {
+ return nil, err
+ }
+
+ return comp.Compile()
+}
+
+// Compile parses and compiles the supplied amber template []byte.
+// Returns corresponding Go Template (html/templates) instance.
+// Necessary runtime functions will be injected and the template will be ready to be executed.
+func CompileData(input []byte, filename string, options Options) (*template.Template, error) {
+ comp := New()
+ comp.Options = options
+
+ err := comp.ParseData(input, filename)
+ if err != nil {
+ return nil, err
+ }
+
+ return comp.Compile()
+}
+
+// MustCompile is the same as Compile, except the input is assumed error free. If else, panic.
+func MustCompile(input string, options Options) *template.Template {
+ t, err := Compile(input, options)
+ if err != nil {
+ panic(err)
+ }
+ return t
+}
+
+// CompileFile parses and compiles the contents of supplied filename. Returns corresponding Go Template (html/templates) instance.
+// Necessary runtime functions will be injected and the template will be ready to be executed.
+func CompileFile(filename string, options Options) (*template.Template, error) {
+ comp := New()
+ comp.Options = options
+
+ err := comp.ParseFile(filename)
+ if err != nil {
+ return nil, err
+ }
+
+ return comp.Compile()
+}
+
+// MustCompileFile is the same as CompileFile, except the input is assumed error free. If else, panic.
+func MustCompileFile(filename string, options Options) *template.Template {
+ t, err := CompileFile(filename, options)
+ if err != nil {
+ panic(err)
+ }
+ return t
+}
+
+// CompileDir parses and compiles the contents of a supplied directory path, with options.
+// Returns a map of a template identifier (key) to a Go Template instance.
+// Ex: if the dirname="templates/" had a file "index.amber" the key would be "index"
+// If option for recursive is True, this parses every file of relevant extension
+// in all subdirectories. The key then is the path e.g: "layouts/layout"
+func CompileDir(dirname string, dopt DirOptions, opt Options) (map[string]*template.Template, error) {
+ dir, err := os.Open(dirname)
+ if err != nil && opt.VirtualFilesystem != nil {
+ vdir, err := opt.VirtualFilesystem.Open(dirname)
+ if err != nil {
+ return nil, err
+ }
+ dir = vdir.(*os.File)
+ } else if err != nil {
+ return nil, err
+ }
+ defer dir.Close()
+
+ files, err := dir.Readdir(0)
+ if err != nil {
+ return nil, err
+ }
+
+ compiled := make(map[string]*template.Template)
+ for _, file := range files {
+ // filename is for example "index.amber"
+ filename := file.Name()
+ fileext := filepath.Ext(filename)
+
+ // If recursive is true and there's a subdirectory, recurse
+ if dopt.Recursive && file.IsDir() {
+ dirpath := filepath.Join(dirname, filename)
+ subcompiled, err := CompileDir(dirpath, dopt, opt)
+ if err != nil {
+ return nil, err
+ }
+ // Copy templates from subdirectory into parent template mapping
+ for k, v := range subcompiled {
+ // Concat with parent directory name for unique paths
+ key := filepath.Join(filename, k)
+ compiled[key] = v
+ }
+ } else if fileext == dopt.Ext {
+ // Otherwise compile the file and add to mapping
+ fullpath := filepath.Join(dirname, filename)
+ tmpl, err := CompileFile(fullpath, opt)
+ if err != nil {
+ return nil, err
+ }
+ // Strip extension
+ key := filename[0 : len(filename)-len(fileext)]
+ compiled[key] = tmpl
+ }
+ }
+
+ return compiled, nil
+}
+
+// MustCompileDir is the same as CompileDir, except input is assumed error free. If else, panic.
+func MustCompileDir(dirname string, dopt DirOptions, opt Options) map[string]*template.Template {
+ m, err := CompileDir(dirname, dopt, opt)
+ if err != nil {
+ panic(err)
+ }
+ return m
+}
+
+// Parse given raw amber template string.
+func (c *Compiler) Parse(input string) (err error) {
+ defer func() {
+ if r := recover(); r != nil {
+ err = errors.New(r.(string))
+ }
+ }()
+
+ parser, err := parser.StringParser(input)
+
+ if err != nil {
+ return
+ }
+
+ c.node = parser.Parse()
+ return
+}
+
+// Parse given raw amber template bytes, and the filename that belongs with it
+func (c *Compiler) ParseData(input []byte, filename string) (err error) {
+ defer func() {
+ if r := recover(); r != nil {
+ err = errors.New(r.(string))
+ }
+ }()
+
+ parser, err := parser.ByteParser(input)
+ parser.SetFilename(filename)
+ if c.VirtualFilesystem != nil {
+ parser.SetVirtualFilesystem(c.VirtualFilesystem)
+ }
+
+ if err != nil {
+ return
+ }
+
+ c.node = parser.Parse()
+ return
+}
+
+// ParseFile parses the amber template file in given path.
+func (c *Compiler) ParseFile(filename string) (err error) {
+ defer func() {
+ if r := recover(); r != nil {
+ err = errors.New(r.(string))
+ }
+ }()
+
+ p, err := parser.FileParser(filename)
+ if err != nil && c.VirtualFilesystem != nil {
+ p, err = parser.VirtualFileParser(filename, c.VirtualFilesystem)
+ }
+ if err != nil {
+ return
+ }
+
+ c.node = p.Parse()
+ c.filename = filename
+ return
+}
+
+// Compile amber and create a Go Template (html/templates) instance.
+// Necessary runtime functions will be injected and the template will be ready to be executed.
+func (c *Compiler) Compile() (*template.Template, error) {
+ return c.CompileWithName(filepath.Base(c.filename))
+}
+
+// CompileWithName is the same as Compile, but allows to specify a name for the template.
+func (c *Compiler) CompileWithName(name string) (*template.Template, error) {
+ return c.CompileWithTemplate(template.New(name))
+}
+
+// CompileWithTemplate is the same as Compile but allows to specify a template.
+func (c *Compiler) CompileWithTemplate(t *template.Template) (*template.Template, error) {
+ data, err := c.CompileString()
+
+ if err != nil {
+ return nil, err
+ }
+
+ tpl, err := t.Funcs(FuncMap).Parse(data)
+
+ if err != nil {
+ return nil, err
+ }
+
+ return tpl, nil
+}
+
+// CompileWriter compiles amber and writes the Go Template source into given io.Writer instance.
+// You would not be using this unless debugging / checking the output. Please use Compile
+// method to obtain a template instance directly.
+func (c *Compiler) CompileWriter(out io.Writer) (err error) {
+ defer func() {
+ if r := recover(); r != nil {
+ err = errors.New(r.(string))
+ }
+ }()
+
+ c.buffer = new(bytes.Buffer)
+ c.visit(c.node)
+
+ if c.buffer.Len() > 0 {
+ c.write("\n")
+ }
+
+ _, err = c.buffer.WriteTo(out)
+ return
+}
+
+// CompileString compiles the template and returns the Go Template source.
+// You would not be using this unless debugging / checking the output. Please use Compile
+// method to obtain a template instance directly.
+func (c *Compiler) CompileString() (string, error) {
+ var buf bytes.Buffer
+
+ if err := c.CompileWriter(&buf); err != nil {
+ return "", err
+ }
+
+ result := buf.String()
+
+ return result, nil
+}
+
+func (c *Compiler) visit(node parser.Node) {
+ defer func() {
+ if r := recover(); r != nil {
+ if rs, ok := r.(string); ok && rs[:len("Amber Error")] == "Amber Error" {
+ panic(r)
+ }
+
+ pos := node.Pos()
+
+ if len(pos.Filename) > 0 {
+ panic(fmt.Sprintf("Amber Error in <%s>: %v - Line: %d, Column: %d, Length: %d", pos.Filename, r, pos.LineNum, pos.ColNum, pos.TokenLength))
+ } else {
+ panic(fmt.Sprintf("Amber Error: %v - Line: %d, Column: %d, Length: %d", r, pos.LineNum, pos.ColNum, pos.TokenLength))
+ }
+ }
+ }()
+
+ switch node.(type) {
+ case *parser.Block:
+ c.visitBlock(node.(*parser.Block))
+ case *parser.Doctype:
+ c.visitDoctype(node.(*parser.Doctype))
+ case *parser.Comment:
+ c.visitComment(node.(*parser.Comment))
+ case *parser.Tag:
+ c.visitTag(node.(*parser.Tag))
+ case *parser.Text:
+ c.visitText(node.(*parser.Text))
+ case *parser.Condition:
+ c.visitCondition(node.(*parser.Condition))
+ case *parser.Each:
+ c.visitEach(node.(*parser.Each))
+ case *parser.Assignment:
+ c.visitAssignment(node.(*parser.Assignment))
+ case *parser.Mixin:
+ c.visitMixin(node.(*parser.Mixin))
+ case *parser.MixinCall:
+ c.visitMixinCall(node.(*parser.MixinCall))
+ }
+}
+
+func (c *Compiler) write(value string) {
+ c.buffer.WriteString(value)
+}
+
+func (c *Compiler) indent(offset int, newline bool) {
+ if !c.PrettyPrint {
+ return
+ }
+
+ if newline && c.buffer.Len() > 0 {
+ c.write("\n")
+ }
+
+ for i := 0; i < c.indentLevel+offset; i++ {
+ c.write("\t")
+ }
+}
+
+func (c *Compiler) tempvar() string {
+ c.tempvarIndex++
+ return "$__amber_" + strconv.Itoa(c.tempvarIndex)
+}
+
+func (c *Compiler) escape(input string) string {
+ return strings.Replace(strings.Replace(input, `\`, `\\`, -1), `"`, `\"`, -1)
+}
+
+func (c *Compiler) visitBlock(block *parser.Block) {
+ for _, node := range block.Children {
+ if _, ok := node.(*parser.Text); !block.CanInline() && ok {
+ c.indent(0, true)
+ }
+
+ c.visit(node)
+ }
+}
+
+func (c *Compiler) visitDoctype(doctype *parser.Doctype) {
+ c.write(doctype.String())
+}
+
+func (c *Compiler) visitComment(comment *parser.Comment) {
+ if comment.Silent {
+ return
+ }
+
+ c.indent(0, false)
+
+ if comment.Block == nil {
+ c.write(`{{unescaped ""}}`)
+ } else {
+ c.write(``)
+ }
+}
+
+func (c *Compiler) visitCondition(condition *parser.Condition) {
+ c.write(`{{if ` + c.visitRawInterpolation(condition.Expression) + `}}`)
+ c.visitBlock(condition.Positive)
+ if condition.Negative != nil {
+ c.write(`{{else}}`)
+ c.visitBlock(condition.Negative)
+ }
+ c.write(`{{end}}`)
+}
+
+func (c *Compiler) visitEach(each *parser.Each) {
+ if each.Block == nil {
+ return
+ }
+
+ if len(each.Y) == 0 {
+ c.write(`{{range ` + each.X + ` := ` + c.visitRawInterpolation(each.Expression) + `}}`)
+ } else {
+ c.write(`{{range ` + each.X + `, ` + each.Y + ` := ` + c.visitRawInterpolation(each.Expression) + `}}`)
+ }
+ c.visitBlock(each.Block)
+ c.write(`{{end}}`)
+}
+
+func (c *Compiler) visitAssignment(assgn *parser.Assignment) {
+ c.write(`{{` + assgn.X + ` := ` + c.visitRawInterpolation(assgn.Expression) + `}}`)
+}
+
+func (c *Compiler) visitTag(tag *parser.Tag) {
+ type attrib struct {
+ name string
+ value func() string
+ condition string
+ }
+
+ attribs := make(map[string]*attrib)
+
+ for _, item := range tag.Attributes {
+ attritem := item
+ attr := new(attrib)
+ attr.name = item.Name
+
+ attr.value = func() string {
+ if !attritem.IsRaw {
+ return c.visitInterpolation(attritem.Value)
+ } else if attritem.Value == "" {
+ return ""
+ } else {
+ return attritem.Value
+ }
+ }
+
+ if len(attritem.Condition) != 0 {
+ attr.condition = c.visitRawInterpolation(attritem.Condition)
+ }
+
+ if attr.name == "class" && attribs["class"] != nil {
+ prevclass := attribs["class"]
+ prevvalue := prevclass.value
+
+ prevclass.value = func() string {
+ aval := attr.value()
+
+ if len(attr.condition) > 0 {
+ aval = `{{if ` + attr.condition + `}}` + aval + `{{end}}`
+ }
+
+ if len(prevclass.condition) > 0 {
+ return `{{if ` + prevclass.condition + `}}` + prevvalue() + `{{end}} ` + aval
+ }
+
+ return prevvalue() + " " + aval
+ }
+ } else {
+ attribs[attritem.Name] = attr
+ }
+ }
+
+ keys := make([]string, 0, len(attribs))
+ for key := range attribs {
+ keys = append(keys, key)
+ }
+ sort.Strings(keys)
+
+ c.indent(0, true)
+ c.write("<" + tag.Name)
+
+ for _, name := range keys {
+ value := attribs[name]
+
+ if len(value.condition) > 0 {
+ c.write(`{{if ` + value.condition + `}}`)
+ }
+
+ val := value.value()
+
+ if val == "" {
+ c.write(` ` + name)
+ } else {
+ c.write(` ` + name + `="` + val + `"`)
+ }
+
+ if len(value.condition) > 0 {
+ c.write(`{{end}}`)
+ }
+ }
+
+ if tag.IsSelfClosing() {
+ c.write(` />`)
+ } else {
+ c.write(`>`)
+
+ if tag.Block != nil {
+ if !tag.Block.CanInline() {
+ c.indentLevel++
+ }
+
+ c.visitBlock(tag.Block)
+
+ if !tag.Block.CanInline() {
+ c.indentLevel--
+ c.indent(0, true)
+ }
+ }
+
+ c.write(`` + tag.Name + `>`)
+ }
+}
+
+var textInterpolateRegexp = regexp.MustCompile(`#\{(.*?)\}`)
+var textEscapeRegexp = regexp.MustCompile(`\{\{(.*?)\}\}`)
+
+func (c *Compiler) visitText(txt *parser.Text) {
+ value := textEscapeRegexp.ReplaceAllStringFunc(txt.Value, func(value string) string {
+ return `{{"{{"}}` + value[2:len(value)-2] + `{{"}}"}}`
+ })
+
+ value = textInterpolateRegexp.ReplaceAllStringFunc(value, func(value string) string {
+ return c.visitInterpolation(value[2 : len(value)-1])
+ })
+
+ lines := strings.Split(value, "\n")
+ for i := 0; i < len(lines); i++ {
+ c.write(lines[i])
+
+ if i < len(lines)-1 {
+ c.write("\n")
+ c.indent(0, false)
+ }
+ }
+}
+
+func (c *Compiler) visitInterpolation(value string) string {
+ return `{{` + c.visitRawInterpolation(value) + `}}`
+}
+
+func (c *Compiler) visitRawInterpolation(value string) string {
+ if value == "" {
+ value = "\"\""
+ }
+
+ value = strings.Replace(value, "$", dollar, -1)
+ expr, err := gp.ParseExpr(value)
+ if err != nil {
+ panic("Unable to parse expression.")
+ }
+ value = strings.Replace(c.visitExpression(expr), dollar, "$", -1)
+ return value
+}
+
+func (c *Compiler) visitExpression(outerexpr ast.Expr) string {
+ stack := list.New()
+
+ pop := func() string {
+ if stack.Front() == nil {
+ return ""
+ }
+
+ val := stack.Front().Value.(string)
+ stack.Remove(stack.Front())
+ return val
+ }
+
+ var exec func(ast.Expr)
+
+ exec = func(expr ast.Expr) {
+ switch expr := expr.(type) {
+ case *ast.BinaryExpr:
+ {
+ be := expr
+
+ exec(be.Y)
+ exec(be.X)
+
+ negate := false
+ name := c.tempvar()
+ c.write(`{{` + name + ` := `)
+
+ switch be.Op {
+ case gt.ADD:
+ c.write("__amber_add ")
+ case gt.SUB:
+ c.write("__amber_sub ")
+ case gt.MUL:
+ c.write("__amber_mul ")
+ case gt.QUO:
+ c.write("__amber_quo ")
+ case gt.REM:
+ c.write("__amber_rem ")
+ case gt.LAND:
+ c.write("and ")
+ case gt.LOR:
+ c.write("or ")
+ case gt.EQL:
+ c.write("__amber_eql ")
+ case gt.NEQ:
+ c.write("__amber_eql ")
+ negate = true
+ case gt.LSS:
+ c.write("__amber_lss ")
+ case gt.GTR:
+ c.write("__amber_gtr ")
+ case gt.LEQ:
+ c.write("__amber_gtr ")
+ negate = true
+ case gt.GEQ:
+ c.write("__amber_lss ")
+ negate = true
+ default:
+ panic("Unexpected operator!")
+ }
+
+ c.write(pop() + ` ` + pop() + `}}`)
+
+ if !negate {
+ stack.PushFront(name)
+ } else {
+ negname := c.tempvar()
+ c.write(`{{` + negname + ` := not ` + name + `}}`)
+ stack.PushFront(negname)
+ }
+ }
+ case *ast.UnaryExpr:
+ {
+ ue := expr
+
+ exec(ue.X)
+
+ name := c.tempvar()
+ c.write(`{{` + name + ` := `)
+
+ switch ue.Op {
+ case gt.SUB:
+ c.write("__amber_minus ")
+ case gt.ADD:
+ c.write("__amber_plus ")
+ case gt.NOT:
+ c.write("not ")
+ default:
+ panic("Unexpected operator!")
+ }
+
+ c.write(pop() + `}}`)
+ stack.PushFront(name)
+ }
+ case *ast.ParenExpr:
+ exec(expr.X)
+ case *ast.BasicLit:
+ stack.PushFront(strings.Replace(expr.Value, dollar, "$", -1))
+ case *ast.Ident:
+ name := expr.Name
+ if len(name) >= len(dollar) && name[:len(dollar)] == dollar {
+ if name == dollar {
+ stack.PushFront(`.`)
+ } else {
+ stack.PushFront(`$` + expr.Name[len(dollar):])
+ }
+ } else {
+ stack.PushFront(`.` + expr.Name)
+ }
+ case *ast.SelectorExpr:
+ se := expr
+ exec(se.X)
+ x := pop()
+
+ if x == "." {
+ x = ""
+ }
+
+ name := c.tempvar()
+ c.write(`{{` + name + ` := ` + x + `.` + se.Sel.Name + `}}`)
+ stack.PushFront(name)
+ case *ast.CallExpr:
+ ce := expr
+
+ for i := len(ce.Args) - 1; i >= 0; i-- {
+ exec(ce.Args[i])
+ }
+
+ name := c.tempvar()
+ builtin := false
+
+ if ident, ok := ce.Fun.(*ast.Ident); ok {
+ for _, fname := range builtinFunctions {
+ if fname == ident.Name {
+ builtin = true
+ break
+ }
+ }
+ for fname, _ := range FuncMap {
+ if fname == ident.Name {
+ builtin = true
+ break
+ }
+ }
+ }
+
+ if builtin {
+ stack.PushFront(ce.Fun.(*ast.Ident).Name)
+ c.write(`{{` + name + ` := ` + pop())
+ } else if se, ok := ce.Fun.(*ast.SelectorExpr); ok {
+ exec(se.X)
+ x := pop()
+
+ if x == "." {
+ x = ""
+ }
+ stack.PushFront(se.Sel.Name)
+ c.write(`{{` + name + ` := ` + x + `.` + pop())
+ } else {
+ exec(ce.Fun)
+ c.write(`{{` + name + ` := call ` + pop())
+ }
+
+ for i := 0; i < len(ce.Args); i++ {
+ c.write(` `)
+ c.write(pop())
+ }
+
+ c.write(`}}`)
+
+ stack.PushFront(name)
+ default:
+ panic("Unable to parse expression. Unsupported: " + reflect.TypeOf(expr).String())
+ }
+ }
+
+ exec(outerexpr)
+ return pop()
+}
+
+func (c *Compiler) visitMixin(mixin *parser.Mixin) {
+ c.mixins[mixin.Name] = mixin
+}
+
+func (c *Compiler) visitMixinCall(mixinCall *parser.MixinCall) {
+ mixin := c.mixins[mixinCall.Name]
+
+ switch {
+ case mixin == nil:
+ panic(fmt.Sprintf("unknown mixin %q", mixinCall.Name))
+
+ case len(mixinCall.Args) < len(mixin.Args):
+ panic(fmt.Sprintf(
+ "not enough arguments in call to mixin %q (have: %d, want: %d)",
+ mixinCall.Name,
+ len(mixinCall.Args),
+ len(mixin.Args),
+ ))
+ case len(mixinCall.Args) > len(mixin.Args):
+ panic(fmt.Sprintf(
+ "too many arguments in call to mixin %q (have: %d, want: %d)",
+ mixinCall.Name,
+ len(mixinCall.Args),
+ len(mixin.Args),
+ ))
+ }
+
+ for i, arg := range mixin.Args {
+ c.write(fmt.Sprintf(`{{%s := %s}}`, arg, c.visitRawInterpolation(mixinCall.Args[i])))
+ }
+ c.visitBlock(mixin.Block)
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/eknkc/amber/doc.go b/go/ql/test/experimental/CWE-321/vendor/github.com/eknkc/amber/doc.go
new file mode 100644
index 000000000000..76ee96a82027
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/eknkc/amber/doc.go
@@ -0,0 +1,257 @@
+/*
+Package amber is an elegant templating engine for Go Programming Language.
+It is inspired from HAML and Jade.
+
+Tags
+
+A tag is simply a word:
+
+ html
+
+is converted to
+
+
+
+It is possible to add ID and CLASS attributes to tags:
+
+ div#main
+ span.time
+
+are converted to
+
+
+
+
+Any arbitrary attribute name / value pair can be added this way:
+
+ a[href="http://www.google.com"]
+
+You can mix multiple attributes together
+
+ a#someid[href="/"][title="Main Page"].main.link Click Link
+
+gets converted to
+
+ Click Link
+
+It is also possible to define these attributes within the block of a tag
+
+ a
+ #someid
+ [href="/"]
+ [title="Main Page"]
+ .main
+ .link
+ | Click Link
+
+Doctypes
+
+To add a doctype, use `!!!` or `doctype` keywords:
+
+ !!! transitional
+ //
+
+or use `doctype`
+
+ doctype 5
+ //
+
+Available options: `5`, `default`, `xml`, `transitional`, `strict`, `frameset`, `1.1`, `basic`, `mobile`
+
+Tag Content
+
+For single line tag text, you can just append the text after tag name:
+
+ p Testing!
+
+would yield
+
+
Testing!
+
+For multi line tag text, or nested tags, use indentation:
+
+ html
+ head
+ title Page Title
+ body
+ div#content
+ p
+ | This is a long page content
+ | These lines are all part of the parent p
+
+ a[href="/"] Go To Main Page
+
+Data
+
+Input template data can be reached by key names directly. For example, assuming the template has been
+executed with following JSON data:
+
+ {
+ "Name": "Ekin",
+ "LastName": "Koc",
+ "Repositories": [
+ "amber",
+ "dateformat"
+ ],
+ "Avatar": "/images/ekin.jpg",
+ "Friends": 17
+ }
+
+It is possible to interpolate fields using `#{}`
+
+ p Welcome #{Name}!
+
+would print
+
+
Welcome Ekin!
+
+Attributes can have field names as well
+
+ a[title=Name][href="/ekin.koc"]
+
+would print
+
+
+
+Expressions
+
+Amber can expand basic expressions. For example, it is possible to concatenate strings with + operator:
+
+ p Welcome #{Name + " " + LastName}
+
+Arithmetic expressions are also supported:
+
+ p You need #{50 - Friends} more friends to reach 50!
+
+Expressions can be used within attributes
+
+ img[alt=Name + " " + LastName][src=Avatar]
+
+Variables
+
+It is possible to define dynamic variables within templates,
+all variables must start with a $ character and can be assigned as in the following example:
+
+ div
+ $fullname = Name + " " + LastName
+ p Welcome #{$fullname}
+
+If you need to access the supplied data itself (i.e. the object containing Name, LastName etc fields.) you can use `$` variable
+
+ p $.Name
+
+Conditions
+
+For conditional blocks, it is possible to use `if `
+
+ div
+ if Friends > 10
+ p You have more than 10 friends
+ else if Friends > 5
+ p You have more than 5 friends
+ else
+ p You need more friends
+
+Again, it is possible to use arithmetic and boolean operators
+
+ div
+ if Name == "Ekin" && LastName == "Koc"
+ p Hey! I know you..
+
+There is a special syntax for conditional attributes. Only block attributes can have conditions;
+
+ div
+ .hasfriends ? Friends > 0
+
+This would yield a div with `hasfriends` class only if the `Friends > 0` condition holds. It is
+perfectly fine to use the same method for other types of attributes:
+
+ div
+ #foo ? Name == "Ekin"
+ [bar=baz] ? len(Repositories) > 0
+
+Iterations
+
+It is possible to iterate over arrays and maps using `each`:
+
+ each $repo in Repositories
+ p #{$repo}
+
+would print
+
+ p amber
+ p dateformat
+
+It is also possible to iterate over values and indexes at the same time
+
+ each $i, $repo in Repositories
+ p
+ .even ? $i % 2 == 0
+ .odd ? $i % 2 == 1
+
+Includes
+
+A template can include other templates using `include`:
+
+ a.amber
+ p this is template a
+
+ b.amber
+ p this is template b
+
+ c.amber
+ div
+ include a
+ include b
+
+gets compiled to
+
+ div
+ p this is template a
+ p this is template b
+
+Inheritance
+
+A template can inherit other templates. In order to inherit another template, an `extends` keyword should be used.
+Parent template can define several named blocks and child template can modify the blocks.
+
+ master.amber
+ !!! 5
+ html
+ head
+ block meta
+ meta[name="description"][content="This is a great website"]
+
+ title
+ block title
+ | Default title
+ body
+ block content
+
+ subpage.amber
+ extends master
+
+ block title
+ | Some sub page!
+
+ block append meta
+ // This will be added after the description meta tag. It is also possible
+ // to prepend something to an existing block
+ meta[name="keywords"][content="foo bar"]
+
+ block content
+ div#main
+ p Some content here
+
+License
+(The MIT License)
+
+Copyright (c) 2012 Ekin Koc
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+package amber
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/eknkc/amber/parser/nodes.go b/go/ql/test/experimental/CWE-321/vendor/github.com/eknkc/amber/parser/nodes.go
new file mode 100644
index 000000000000..2f75ddddbc24
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/eknkc/amber/parser/nodes.go
@@ -0,0 +1,285 @@
+package parser
+
+import (
+ "regexp"
+ "strings"
+)
+
+var selfClosingTags = [...]string{
+ "meta",
+ "img",
+ "link",
+ "input",
+ "source",
+ "area",
+ "base",
+ "col",
+ "br",
+ "hr",
+}
+
+var doctypes = map[string]string{
+ "5": ``,
+ "default": ``,
+ "xml": ``,
+ "transitional": ``,
+ "strict": ``,
+ "frameset": ``,
+ "1.1": ``,
+ "basic": ``,
+ "mobile": ``,
+}
+
+type Node interface {
+ Pos() SourcePosition
+}
+
+type SourcePosition struct {
+ LineNum int
+ ColNum int
+ TokenLength int
+ Filename string
+}
+
+func (s *SourcePosition) Pos() SourcePosition {
+ return *s
+}
+
+type Doctype struct {
+ SourcePosition
+ Value string
+}
+
+func newDoctype(value string) *Doctype {
+ dt := new(Doctype)
+ dt.Value = value
+ return dt
+}
+
+func (d *Doctype) String() string {
+ if defined := doctypes[d.Value]; len(defined) != 0 {
+ return defined
+ }
+
+ return ``
+}
+
+type Comment struct {
+ SourcePosition
+ Value string
+ Block *Block
+ Silent bool
+}
+
+func newComment(value string) *Comment {
+ dt := new(Comment)
+ dt.Value = value
+ dt.Block = nil
+ dt.Silent = false
+ return dt
+}
+
+type Text struct {
+ SourcePosition
+ Value string
+ Raw bool
+}
+
+func newText(value string, raw bool) *Text {
+ dt := new(Text)
+ dt.Value = value
+ dt.Raw = raw
+ return dt
+}
+
+type Block struct {
+ SourcePosition
+ Children []Node
+}
+
+func newBlock() *Block {
+ block := new(Block)
+ block.Children = make([]Node, 0)
+ return block
+}
+
+func (b *Block) push(node Node) {
+ b.Children = append(b.Children, node)
+}
+
+func (b *Block) pushFront(node Node) {
+ b.Children = append([]Node{node}, b.Children...)
+}
+
+func (b *Block) CanInline() bool {
+ if len(b.Children) == 0 {
+ return true
+ }
+
+ allText := true
+
+ for _, child := range b.Children {
+ if txt, ok := child.(*Text); !ok || txt.Raw {
+ allText = false
+ break
+ }
+ }
+
+ return allText
+}
+
+const (
+ NamedBlockDefault = iota
+ NamedBlockAppend
+ NamedBlockPrepend
+)
+
+type NamedBlock struct {
+ Block
+ Name string
+ Modifier int
+}
+
+func newNamedBlock(name string) *NamedBlock {
+ bb := new(NamedBlock)
+ bb.Name = name
+ bb.Block.Children = make([]Node, 0)
+ bb.Modifier = NamedBlockDefault
+ return bb
+}
+
+type Attribute struct {
+ SourcePosition
+ Name string
+ Value string
+ IsRaw bool
+ Condition string
+}
+
+type Tag struct {
+ SourcePosition
+ Block *Block
+ Name string
+ IsInterpolated bool
+ Attributes []Attribute
+}
+
+func newTag(name string) *Tag {
+ tag := new(Tag)
+ tag.Block = nil
+ tag.Name = name
+ tag.Attributes = make([]Attribute, 0)
+ tag.IsInterpolated = false
+ return tag
+
+}
+
+func (t *Tag) IsSelfClosing() bool {
+ for _, tag := range selfClosingTags {
+ if tag == t.Name {
+ return true
+ }
+ }
+
+ return false
+}
+
+func (t *Tag) IsRawText() bool {
+ return t.Name == "style" || t.Name == "script"
+}
+
+type Condition struct {
+ SourcePosition
+ Positive *Block
+ Negative *Block
+ Expression string
+}
+
+func newCondition(exp string) *Condition {
+ cond := new(Condition)
+ cond.Expression = exp
+ return cond
+}
+
+type Each struct {
+ SourcePosition
+ X string
+ Y string
+ Expression string
+ Block *Block
+}
+
+func newEach(exp string) *Each {
+ each := new(Each)
+ each.Expression = exp
+ return each
+}
+
+type Assignment struct {
+ SourcePosition
+ X string
+ Expression string
+}
+
+func newAssignment(x, expression string) *Assignment {
+ assgn := new(Assignment)
+ assgn.X = x
+ assgn.Expression = expression
+ return assgn
+}
+
+type Mixin struct {
+ SourcePosition
+ Block *Block
+ Name string
+ Args []string
+}
+
+func newMixin(name, args string) *Mixin {
+ mixin := new(Mixin)
+ mixin.Name = name
+
+ delExp := regexp.MustCompile(`,\s`)
+ mixin.Args = delExp.Split(args, -1)
+
+ for i := 0; i < len(mixin.Args); i++ {
+ mixin.Args[i] = strings.TrimSpace(mixin.Args[i])
+ if mixin.Args[i] == "" {
+ mixin.Args = append(mixin.Args[:i], mixin.Args[i+1:]...)
+ i--
+ }
+ }
+
+ return mixin
+}
+
+type MixinCall struct {
+ SourcePosition
+ Name string
+ Args []string
+}
+
+func newMixinCall(name, args string) *MixinCall {
+ mixinCall := new(MixinCall)
+ mixinCall.Name = name
+
+ if args != "" {
+ const t = "%s"
+ quoteExp := regexp.MustCompile(`"(.*?)"`)
+ delExp := regexp.MustCompile(`,\s`)
+
+ quotes := quoteExp.FindAllString(args, -1)
+ replaced := quoteExp.ReplaceAllString(args, t)
+ mixinCall.Args = delExp.Split(replaced, -1)
+
+ qi := 0
+ for i, arg := range mixinCall.Args {
+ if arg == t {
+ mixinCall.Args[i] = quotes[qi]
+ qi++
+ }
+ }
+ }
+
+ return mixinCall
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/eknkc/amber/parser/parser.go b/go/ql/test/experimental/CWE-321/vendor/github.com/eknkc/amber/parser/parser.go
new file mode 100644
index 000000000000..9769f0b288a8
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/eknkc/amber/parser/parser.go
@@ -0,0 +1,482 @@
+package parser
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "net/http"
+ "path/filepath"
+ "strings"
+)
+
+type Parser struct {
+ scanner *scanner
+ filename string
+ fs http.FileSystem
+ currenttoken *token
+ namedBlocks map[string]*NamedBlock
+ parent *Parser
+ result *Block
+}
+
+func newParser(rdr io.Reader) *Parser {
+ p := new(Parser)
+ p.scanner = newScanner(rdr)
+ p.namedBlocks = make(map[string]*NamedBlock)
+ return p
+}
+
+func StringParser(input string) (*Parser, error) {
+ return newParser(bytes.NewReader([]byte(input))), nil
+}
+
+func ByteParser(input []byte) (*Parser, error) {
+ return newParser(bytes.NewReader(input)), nil
+}
+
+func (p *Parser) SetFilename(filename string) {
+ p.filename = filename
+}
+
+func (p *Parser) SetVirtualFilesystem(fs http.FileSystem) {
+ p.fs = fs
+}
+
+func FileParser(filename string) (*Parser, error) {
+ data, err := ioutil.ReadFile(filename)
+
+ if err != nil {
+ return nil, err
+ }
+
+ parser := newParser(bytes.NewReader(data))
+ parser.filename = filename
+ return parser, nil
+}
+
+func VirtualFileParser(filename string, fs http.FileSystem) (*Parser, error) {
+ file, err := fs.Open(filename)
+ if err != nil {
+ return nil, err
+ }
+
+ data, err := ioutil.ReadAll(file)
+ if err != nil {
+ return nil, err
+ }
+
+ parser := newParser(bytes.NewReader(data))
+ parser.filename = filename
+ parser.fs = fs
+ return parser, nil
+}
+
+func (p *Parser) Parse() *Block {
+ if p.result != nil {
+ return p.result
+ }
+
+ defer func() {
+ if r := recover(); r != nil {
+ if rs, ok := r.(string); ok && rs[:len("Amber Error")] == "Amber Error" {
+ panic(r)
+ }
+
+ pos := p.pos()
+
+ if len(pos.Filename) > 0 {
+ panic(fmt.Sprintf("Amber Error in <%s>: %v - Line: %d, Column: %d, Length: %d", pos.Filename, r, pos.LineNum, pos.ColNum, pos.TokenLength))
+ } else {
+ panic(fmt.Sprintf("Amber Error: %v - Line: %d, Column: %d, Length: %d", r, pos.LineNum, pos.ColNum, pos.TokenLength))
+ }
+ }
+ }()
+
+ block := newBlock()
+ p.advance()
+
+ for {
+ if p.currenttoken == nil || p.currenttoken.Kind == tokEOF {
+ break
+ }
+
+ if p.currenttoken.Kind == tokBlank {
+ p.advance()
+ continue
+ }
+
+ block.push(p.parse())
+ }
+
+ if p.parent != nil {
+ p.parent.Parse()
+
+ for _, prev := range p.parent.namedBlocks {
+ ours := p.namedBlocks[prev.Name]
+
+ if ours == nil {
+ // Put a copy of the named block into current context, so that sub-templates can use the block
+ p.namedBlocks[prev.Name] = prev
+ continue
+ }
+
+ top := findTopmostParentWithNamedBlock(p, prev.Name)
+ nb := top.namedBlocks[prev.Name]
+ switch ours.Modifier {
+ case NamedBlockAppend:
+ for i := 0; i < len(ours.Children); i++ {
+ nb.push(ours.Children[i])
+ }
+ case NamedBlockPrepend:
+ for i := len(ours.Children) - 1; i >= 0; i-- {
+ nb.pushFront(ours.Children[i])
+ }
+ default:
+ nb.Children = ours.Children
+ }
+ }
+
+ block = p.parent.result
+ }
+
+ p.result = block
+ return block
+}
+
+func (p *Parser) pos() SourcePosition {
+ pos := p.scanner.Pos()
+ pos.Filename = p.filename
+ return pos
+}
+
+func (p *Parser) parseRelativeFile(filename string) *Parser {
+ if len(p.filename) == 0 {
+ panic("Unable to import or extend " + filename + " in a non filesystem based parser.")
+ }
+
+ filename = filepath.Join(filepath.Dir(p.filename), filename)
+
+ if strings.IndexRune(filepath.Base(filename), '.') < 0 {
+ filename = filename + ".amber"
+ }
+
+ parser, err := FileParser(filename)
+ if err != nil && p.fs != nil {
+ parser, err = VirtualFileParser(filename, p.fs)
+ }
+ if err != nil {
+ panic("Unable to read " + filename + ", Error: " + string(err.Error()))
+ }
+
+ return parser
+}
+
+func (p *Parser) parse() Node {
+ switch p.currenttoken.Kind {
+ case tokDoctype:
+ return p.parseDoctype()
+ case tokComment:
+ return p.parseComment()
+ case tokText:
+ return p.parseText()
+ case tokIf:
+ return p.parseIf()
+ case tokEach:
+ return p.parseEach()
+ case tokImport:
+ return p.parseImport()
+ case tokTag:
+ return p.parseTag()
+ case tokAssignment:
+ return p.parseAssignment()
+ case tokNamedBlock:
+ return p.parseNamedBlock()
+ case tokExtends:
+ return p.parseExtends()
+ case tokIndent:
+ return p.parseBlock(nil)
+ case tokMixin:
+ return p.parseMixin()
+ case tokMixinCall:
+ return p.parseMixinCall()
+ }
+
+ panic(fmt.Sprintf("Unexpected token: %d", p.currenttoken.Kind))
+}
+
+func (p *Parser) expect(typ rune) *token {
+ if p.currenttoken.Kind != typ {
+ panic("Unexpected token!")
+ }
+ curtok := p.currenttoken
+ p.advance()
+ return curtok
+}
+
+func (p *Parser) advance() {
+ p.currenttoken = p.scanner.Next()
+}
+
+func (p *Parser) parseExtends() *Block {
+ if p.parent != nil {
+ panic("Unable to extend multiple parent templates.")
+ }
+
+ tok := p.expect(tokExtends)
+ parser := p.parseRelativeFile(tok.Value)
+ parser.Parse()
+ p.parent = parser
+ return newBlock()
+}
+
+func (p *Parser) parseBlock(parent Node) *Block {
+ p.expect(tokIndent)
+ block := newBlock()
+ block.SourcePosition = p.pos()
+
+ for {
+ if p.currenttoken == nil || p.currenttoken.Kind == tokEOF || p.currenttoken.Kind == tokOutdent {
+ break
+ }
+
+ if p.currenttoken.Kind == tokBlank {
+ p.advance()
+ continue
+ }
+
+ if p.currenttoken.Kind == tokId ||
+ p.currenttoken.Kind == tokClassName ||
+ p.currenttoken.Kind == tokAttribute {
+
+ if tag, ok := parent.(*Tag); ok {
+ attr := p.expect(p.currenttoken.Kind)
+ cond := attr.Data["Condition"]
+
+ switch attr.Kind {
+ case tokId:
+ tag.Attributes = append(tag.Attributes, Attribute{p.pos(), "id", attr.Value, true, cond})
+ case tokClassName:
+ tag.Attributes = append(tag.Attributes, Attribute{p.pos(), "class", attr.Value, true, cond})
+ case tokAttribute:
+ tag.Attributes = append(tag.Attributes, Attribute{p.pos(), attr.Value, attr.Data["Content"], attr.Data["Mode"] == "raw", cond})
+ }
+
+ continue
+ } else {
+ panic("Conditional attributes must be placed immediately within a parent tag.")
+ }
+ }
+
+ block.push(p.parse())
+ }
+
+ p.expect(tokOutdent)
+
+ return block
+}
+
+func (p *Parser) parseIf() *Condition {
+ tok := p.expect(tokIf)
+ cnd := newCondition(tok.Value)
+ cnd.SourcePosition = p.pos()
+
+readmore:
+ switch p.currenttoken.Kind {
+ case tokIndent:
+ cnd.Positive = p.parseBlock(cnd)
+ goto readmore
+ case tokElse:
+ p.expect(tokElse)
+ if p.currenttoken.Kind == tokIf {
+ cnd.Negative = newBlock()
+ cnd.Negative.push(p.parseIf())
+ } else if p.currenttoken.Kind == tokIndent {
+ cnd.Negative = p.parseBlock(cnd)
+ } else {
+ panic("Unexpected token!")
+ }
+ goto readmore
+ }
+
+ return cnd
+}
+
+func (p *Parser) parseEach() *Each {
+ tok := p.expect(tokEach)
+ ech := newEach(tok.Value)
+ ech.SourcePosition = p.pos()
+ ech.X = tok.Data["X"]
+ ech.Y = tok.Data["Y"]
+
+ if p.currenttoken.Kind == tokIndent {
+ ech.Block = p.parseBlock(ech)
+ }
+
+ return ech
+}
+
+func (p *Parser) parseImport() *Block {
+ tok := p.expect(tokImport)
+ node := p.parseRelativeFile(tok.Value).Parse()
+ node.SourcePosition = p.pos()
+ return node
+}
+
+func (p *Parser) parseNamedBlock() *Block {
+ tok := p.expect(tokNamedBlock)
+
+ if p.namedBlocks[tok.Value] != nil {
+ panic("Multiple definitions of named blocks are not permitted. Block " + tok.Value + " has been re defined.")
+ }
+
+ block := newNamedBlock(tok.Value)
+ block.SourcePosition = p.pos()
+
+ if tok.Data["Modifier"] == "append" {
+ block.Modifier = NamedBlockAppend
+ } else if tok.Data["Modifier"] == "prepend" {
+ block.Modifier = NamedBlockPrepend
+ }
+
+ if p.currenttoken.Kind == tokIndent {
+ block.Block = *(p.parseBlock(nil))
+ }
+
+ p.namedBlocks[block.Name] = block
+
+ if block.Modifier == NamedBlockDefault {
+ return &block.Block
+ }
+
+ return newBlock()
+}
+
+func (p *Parser) parseDoctype() *Doctype {
+ tok := p.expect(tokDoctype)
+ node := newDoctype(tok.Value)
+ node.SourcePosition = p.pos()
+ return node
+}
+
+func (p *Parser) parseComment() *Comment {
+ tok := p.expect(tokComment)
+ cmnt := newComment(tok.Value)
+ cmnt.SourcePosition = p.pos()
+ cmnt.Silent = tok.Data["Mode"] == "silent"
+
+ if p.currenttoken.Kind == tokIndent {
+ cmnt.Block = p.parseBlock(cmnt)
+ }
+
+ return cmnt
+}
+
+func (p *Parser) parseText() *Text {
+ tok := p.expect(tokText)
+ node := newText(tok.Value, tok.Data["Mode"] == "raw")
+ node.SourcePosition = p.pos()
+ return node
+}
+
+func (p *Parser) parseAssignment() *Assignment {
+ tok := p.expect(tokAssignment)
+ node := newAssignment(tok.Data["X"], tok.Value)
+ node.SourcePosition = p.pos()
+ return node
+}
+
+func (p *Parser) parseTag() *Tag {
+ tok := p.expect(tokTag)
+ tag := newTag(tok.Value)
+ tag.SourcePosition = p.pos()
+
+ ensureBlock := func() {
+ if tag.Block == nil {
+ tag.Block = newBlock()
+ }
+ }
+
+readmore:
+ switch p.currenttoken.Kind {
+ case tokIndent:
+ if tag.IsRawText() {
+ p.scanner.readRaw = true
+ }
+
+ block := p.parseBlock(tag)
+ if tag.Block == nil {
+ tag.Block = block
+ } else {
+ for _, c := range block.Children {
+ tag.Block.push(c)
+ }
+ }
+ case tokId:
+ id := p.expect(tokId)
+ if len(id.Data["Condition"]) > 0 {
+ panic("Conditional attributes must be placed in a block within a tag.")
+ }
+ tag.Attributes = append(tag.Attributes, Attribute{p.pos(), "id", id.Value, true, ""})
+ goto readmore
+ case tokClassName:
+ cls := p.expect(tokClassName)
+ if len(cls.Data["Condition"]) > 0 {
+ panic("Conditional attributes must be placed in a block within a tag.")
+ }
+ tag.Attributes = append(tag.Attributes, Attribute{p.pos(), "class", cls.Value, true, ""})
+ goto readmore
+ case tokAttribute:
+ attr := p.expect(tokAttribute)
+ if len(attr.Data["Condition"]) > 0 {
+ panic("Conditional attributes must be placed in a block within a tag.")
+ }
+ tag.Attributes = append(tag.Attributes, Attribute{p.pos(), attr.Value, attr.Data["Content"], attr.Data["Mode"] == "raw", ""})
+ goto readmore
+ case tokText:
+ if p.currenttoken.Data["Mode"] != "piped" {
+ ensureBlock()
+ tag.Block.pushFront(p.parseText())
+ goto readmore
+ }
+ }
+
+ return tag
+}
+
+func (p *Parser) parseMixin() *Mixin {
+ tok := p.expect(tokMixin)
+ mixin := newMixin(tok.Value, tok.Data["Args"])
+ mixin.SourcePosition = p.pos()
+
+ if p.currenttoken.Kind == tokIndent {
+ mixin.Block = p.parseBlock(mixin)
+ }
+
+ return mixin
+}
+
+func (p *Parser) parseMixinCall() *MixinCall {
+ tok := p.expect(tokMixinCall)
+ mixinCall := newMixinCall(tok.Value, tok.Data["Args"])
+ mixinCall.SourcePosition = p.pos()
+ return mixinCall
+}
+
+func findTopmostParentWithNamedBlock(p *Parser, name string) *Parser {
+ top := p
+
+ for {
+ if top.namedBlocks[name] == nil {
+ return nil
+ }
+ if top.parent == nil {
+ return top
+ }
+ if top.parent.namedBlocks[name] != nil {
+ top = top.parent
+ } else {
+ return top
+ }
+ }
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/eknkc/amber/parser/scanner.go b/go/ql/test/experimental/CWE-321/vendor/github.com/eknkc/amber/parser/scanner.go
new file mode 100644
index 000000000000..70e0203512cd
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/eknkc/amber/parser/scanner.go
@@ -0,0 +1,501 @@
+package parser
+
+import (
+ "bufio"
+ "container/list"
+ "fmt"
+ "io"
+ "regexp"
+)
+
+const (
+ tokEOF = -(iota + 1)
+ tokDoctype
+ tokComment
+ tokIndent
+ tokOutdent
+ tokBlank
+ tokId
+ tokClassName
+ tokTag
+ tokText
+ tokAttribute
+ tokIf
+ tokElse
+ tokEach
+ tokAssignment
+ tokImport
+ tokNamedBlock
+ tokExtends
+ tokMixin
+ tokMixinCall
+)
+
+const (
+ scnNewLine = iota
+ scnLine
+ scnEOF
+)
+
+type scanner struct {
+ reader *bufio.Reader
+ indentStack *list.List
+ stash *list.List
+
+ state int32
+ buffer string
+
+ line int
+ col int
+ lastTokenLine int
+ lastTokenCol int
+ lastTokenSize int
+
+ readRaw bool
+}
+
+type token struct {
+ Kind rune
+ Value string
+ Data map[string]string
+}
+
+func newScanner(r io.Reader) *scanner {
+ s := new(scanner)
+ s.reader = bufio.NewReader(r)
+ s.indentStack = list.New()
+ s.stash = list.New()
+ s.state = scnNewLine
+ s.line = -1
+ s.col = 0
+
+ return s
+}
+
+func (s *scanner) Pos() SourcePosition {
+ return SourcePosition{s.lastTokenLine + 1, s.lastTokenCol + 1, s.lastTokenSize, ""}
+}
+
+// Returns next token found in buffer
+func (s *scanner) Next() *token {
+ if s.readRaw {
+ s.readRaw = false
+ return s.NextRaw()
+ }
+
+ s.ensureBuffer()
+
+ if stashed := s.stash.Front(); stashed != nil {
+ tok := stashed.Value.(*token)
+ s.stash.Remove(stashed)
+ return tok
+ }
+
+ switch s.state {
+ case scnEOF:
+ if outdent := s.indentStack.Back(); outdent != nil {
+ s.indentStack.Remove(outdent)
+ return &token{tokOutdent, "", nil}
+ }
+
+ return &token{tokEOF, "", nil}
+ case scnNewLine:
+ s.state = scnLine
+
+ if tok := s.scanIndent(); tok != nil {
+ return tok
+ }
+
+ return s.Next()
+ case scnLine:
+ if tok := s.scanMixin(); tok != nil {
+ return tok
+ }
+
+ if tok := s.scanMixinCall(); tok != nil {
+ return tok
+ }
+
+ if tok := s.scanDoctype(); tok != nil {
+ return tok
+ }
+
+ if tok := s.scanCondition(); tok != nil {
+ return tok
+ }
+
+ if tok := s.scanEach(); tok != nil {
+ return tok
+ }
+
+ if tok := s.scanImport(); tok != nil {
+ return tok
+ }
+
+ if tok := s.scanExtends(); tok != nil {
+ return tok
+ }
+
+ if tok := s.scanBlock(); tok != nil {
+ return tok
+ }
+
+ if tok := s.scanAssignment(); tok != nil {
+ return tok
+ }
+
+ if tok := s.scanTag(); tok != nil {
+ return tok
+ }
+
+ if tok := s.scanId(); tok != nil {
+ return tok
+ }
+
+ if tok := s.scanClassName(); tok != nil {
+ return tok
+ }
+
+ if tok := s.scanAttribute(); tok != nil {
+ return tok
+ }
+
+ if tok := s.scanComment(); tok != nil {
+ return tok
+ }
+
+ if tok := s.scanText(); tok != nil {
+ return tok
+ }
+ }
+
+ return nil
+}
+
+func (s *scanner) NextRaw() *token {
+ result := ""
+ level := 0
+
+ for {
+ s.ensureBuffer()
+
+ switch s.state {
+ case scnEOF:
+ return &token{tokText, result, map[string]string{"Mode": "raw"}}
+ case scnNewLine:
+ s.state = scnLine
+
+ if tok := s.scanIndent(); tok != nil {
+ if tok.Kind == tokIndent {
+ level++
+ } else if tok.Kind == tokOutdent {
+ level--
+ } else {
+ result = result + "\n"
+ continue
+ }
+
+ if level < 0 {
+ s.stash.PushBack(&token{tokOutdent, "", nil})
+
+ if len(result) > 0 && result[len(result)-1] == '\n' {
+ result = result[:len(result)-1]
+ }
+
+ return &token{tokText, result, map[string]string{"Mode": "raw"}}
+ }
+ }
+ case scnLine:
+ if len(result) > 0 {
+ result = result + "\n"
+ }
+ for i := 0; i < level; i++ {
+ result += "\t"
+ }
+ result = result + s.buffer
+ s.consume(len(s.buffer))
+ }
+ }
+
+ return nil
+}
+
+var rgxIndent = regexp.MustCompile(`^(\s+)`)
+
+func (s *scanner) scanIndent() *token {
+ if len(s.buffer) == 0 {
+ return &token{tokBlank, "", nil}
+ }
+
+ var head *list.Element
+ for head = s.indentStack.Front(); head != nil; head = head.Next() {
+ value := head.Value.(*regexp.Regexp)
+
+ if match := value.FindString(s.buffer); len(match) != 0 {
+ s.consume(len(match))
+ } else {
+ break
+ }
+ }
+
+ newIndent := rgxIndent.FindString(s.buffer)
+
+ if len(newIndent) != 0 && head == nil {
+ s.indentStack.PushBack(regexp.MustCompile(regexp.QuoteMeta(newIndent)))
+ s.consume(len(newIndent))
+ return &token{tokIndent, newIndent, nil}
+ }
+
+ if len(newIndent) == 0 && head != nil {
+ for head != nil {
+ next := head.Next()
+ s.indentStack.Remove(head)
+ if next == nil {
+ return &token{tokOutdent, "", nil}
+ } else {
+ s.stash.PushBack(&token{tokOutdent, "", nil})
+ }
+ head = next
+ }
+ }
+
+ if len(newIndent) != 0 && head != nil {
+ panic("Mismatching indentation. Please use a coherent indent schema.")
+ }
+
+ return nil
+}
+
+var rgxDoctype = regexp.MustCompile(`^(!!!|doctype)\s*(.*)`)
+
+func (s *scanner) scanDoctype() *token {
+ if sm := rgxDoctype.FindStringSubmatch(s.buffer); len(sm) != 0 {
+ if len(sm[2]) == 0 {
+ sm[2] = "html"
+ }
+
+ s.consume(len(sm[0]))
+ return &token{tokDoctype, sm[2], nil}
+ }
+
+ return nil
+}
+
+var rgxIf = regexp.MustCompile(`^if\s+(.+)$`)
+var rgxElse = regexp.MustCompile(`^else\s*`)
+
+func (s *scanner) scanCondition() *token {
+ if sm := rgxIf.FindStringSubmatch(s.buffer); len(sm) != 0 {
+ s.consume(len(sm[0]))
+ return &token{tokIf, sm[1], nil}
+ }
+
+ if sm := rgxElse.FindStringSubmatch(s.buffer); len(sm) != 0 {
+ s.consume(len(sm[0]))
+ return &token{tokElse, "", nil}
+ }
+
+ return nil
+}
+
+var rgxEach = regexp.MustCompile(`^each\s+(\$[\w0-9\-_]*)(?:\s*,\s*(\$[\w0-9\-_]*))?\s+in\s+(.+)$`)
+
+func (s *scanner) scanEach() *token {
+ if sm := rgxEach.FindStringSubmatch(s.buffer); len(sm) != 0 {
+ s.consume(len(sm[0]))
+ return &token{tokEach, sm[3], map[string]string{"X": sm[1], "Y": sm[2]}}
+ }
+
+ return nil
+}
+
+var rgxAssignment = regexp.MustCompile(`^(\$[\w0-9\-_]*)?\s*=\s*(.+)$`)
+
+func (s *scanner) scanAssignment() *token {
+ if sm := rgxAssignment.FindStringSubmatch(s.buffer); len(sm) != 0 {
+ s.consume(len(sm[0]))
+ return &token{tokAssignment, sm[2], map[string]string{"X": sm[1]}}
+ }
+
+ return nil
+}
+
+var rgxComment = regexp.MustCompile(`^\/\/(-)?\s*(.*)$`)
+
+func (s *scanner) scanComment() *token {
+ if sm := rgxComment.FindStringSubmatch(s.buffer); len(sm) != 0 {
+ mode := "embed"
+ if len(sm[1]) != 0 {
+ mode = "silent"
+ }
+
+ s.consume(len(sm[0]))
+ return &token{tokComment, sm[2], map[string]string{"Mode": mode}}
+ }
+
+ return nil
+}
+
+var rgxId = regexp.MustCompile(`^#([\w-]+)(?:\s*\?\s*(.*)$)?`)
+
+func (s *scanner) scanId() *token {
+ if sm := rgxId.FindStringSubmatch(s.buffer); len(sm) != 0 {
+ s.consume(len(sm[0]))
+ return &token{tokId, sm[1], map[string]string{"Condition": sm[2]}}
+ }
+
+ return nil
+}
+
+var rgxClassName = regexp.MustCompile(`^\.([\w-]+)(?:\s*\?\s*(.*)$)?`)
+
+func (s *scanner) scanClassName() *token {
+ if sm := rgxClassName.FindStringSubmatch(s.buffer); len(sm) != 0 {
+ s.consume(len(sm[0]))
+ return &token{tokClassName, sm[1], map[string]string{"Condition": sm[2]}}
+ }
+
+ return nil
+}
+
+var rgxAttribute = regexp.MustCompile(`^\[([\w\-:@\.]+)\s*(?:=\s*(\"([^\"\\]*)\"|([^\]]+)))?\](?:\s*\?\s*(.*)$)?`)
+
+func (s *scanner) scanAttribute() *token {
+ if sm := rgxAttribute.FindStringSubmatch(s.buffer); len(sm) != 0 {
+ s.consume(len(sm[0]))
+
+ if len(sm[3]) != 0 || sm[2] == "" {
+ return &token{tokAttribute, sm[1], map[string]string{"Content": sm[3], "Mode": "raw", "Condition": sm[5]}}
+ }
+
+ return &token{tokAttribute, sm[1], map[string]string{"Content": sm[4], "Mode": "expression", "Condition": sm[5]}}
+ }
+
+ return nil
+}
+
+var rgxImport = regexp.MustCompile(`^import\s+([0-9a-zA-Z_\-\. \/]*)$`)
+
+func (s *scanner) scanImport() *token {
+ if sm := rgxImport.FindStringSubmatch(s.buffer); len(sm) != 0 {
+ s.consume(len(sm[0]))
+ return &token{tokImport, sm[1], nil}
+ }
+
+ return nil
+}
+
+var rgxExtends = regexp.MustCompile(`^extends\s+([0-9a-zA-Z_\-\. \/]*)$`)
+
+func (s *scanner) scanExtends() *token {
+ if sm := rgxExtends.FindStringSubmatch(s.buffer); len(sm) != 0 {
+ s.consume(len(sm[0]))
+ return &token{tokExtends, sm[1], nil}
+ }
+
+ return nil
+}
+
+var rgxBlock = regexp.MustCompile(`^block\s+(?:(append|prepend)\s+)?([0-9a-zA-Z_\-\. \/]*)$`)
+
+func (s *scanner) scanBlock() *token {
+ if sm := rgxBlock.FindStringSubmatch(s.buffer); len(sm) != 0 {
+ s.consume(len(sm[0]))
+ return &token{tokNamedBlock, sm[2], map[string]string{"Modifier": sm[1]}}
+ }
+
+ return nil
+}
+
+var rgxTag = regexp.MustCompile(`^(\w[-:\w]*)`)
+
+func (s *scanner) scanTag() *token {
+ if sm := rgxTag.FindStringSubmatch(s.buffer); len(sm) != 0 {
+ s.consume(len(sm[0]))
+ return &token{tokTag, sm[1], nil}
+ }
+
+ return nil
+}
+
+var rgxMixin = regexp.MustCompile(`^mixin ([a-zA-Z_-]+\w*)(\(((\$\w*(,\s)?)*)\))?$`)
+
+func (s *scanner) scanMixin() *token {
+ if sm := rgxMixin.FindStringSubmatch(s.buffer); len(sm) != 0 {
+ s.consume(len(sm[0]))
+ return &token{tokMixin, sm[1], map[string]string{"Args": sm[3]}}
+ }
+
+ return nil
+}
+
+var rgxMixinCall = regexp.MustCompile(`^\+([A-Za-z_-]+\w*)(\((.+(,\s)?)*\))?$`)
+
+func (s *scanner) scanMixinCall() *token {
+ if sm := rgxMixinCall.FindStringSubmatch(s.buffer); len(sm) != 0 {
+ s.consume(len(sm[0]))
+ return &token{tokMixinCall, sm[1], map[string]string{"Args": sm[3]}}
+ }
+
+ return nil
+}
+
+var rgxText = regexp.MustCompile(`^(\|)? ?(.*)$`)
+
+func (s *scanner) scanText() *token {
+ if sm := rgxText.FindStringSubmatch(s.buffer); len(sm) != 0 {
+ s.consume(len(sm[0]))
+
+ mode := "inline"
+ if sm[1] == "|" {
+ mode = "piped"
+ }
+
+ return &token{tokText, sm[2], map[string]string{"Mode": mode}}
+ }
+
+ return nil
+}
+
+// Moves position forward, and removes beginning of s.buffer (len bytes)
+func (s *scanner) consume(runes int) {
+ if len(s.buffer) < runes {
+ panic(fmt.Sprintf("Unable to consume %d runes from buffer.", runes))
+ }
+
+ s.lastTokenLine = s.line
+ s.lastTokenCol = s.col
+ s.lastTokenSize = runes
+
+ s.buffer = s.buffer[runes:]
+ s.col += runes
+}
+
+// Reads string into s.buffer
+func (s *scanner) ensureBuffer() {
+ if len(s.buffer) > 0 {
+ return
+ }
+
+ buf, err := s.reader.ReadString('\n')
+
+ if err != nil && err != io.EOF {
+ panic(err)
+ } else if err != nil && len(buf) == 0 {
+ s.state = scnEOF
+ } else {
+ // endline "LF only" or "\n" use Unix, Linux, modern MacOS X, FreeBSD, BeOS, RISC OS
+ if buf[len(buf)-1] == '\n' {
+ buf = buf[:len(buf)-1]
+ }
+ // endline "CR+LF" or "\r\n" use internet protocols, DEC RT-11, Windows, CP/M, MS-DOS, OS/2, Symbian OS
+ if len(buf) > 0 && buf[len(buf)-1] == '\r' {
+ buf = buf[:len(buf)-1]
+ }
+
+ s.state = scnNewLine
+ s.buffer = buf
+ s.line += 1
+ s.col = 0
+ }
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/eknkc/amber/runtime.go b/go/ql/test/experimental/CWE-321/vendor/github.com/eknkc/amber/runtime.go
new file mode 100644
index 000000000000..6438fd1fc29b
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/eknkc/amber/runtime.go
@@ -0,0 +1,287 @@
+package amber
+
+import (
+ "encoding/json"
+ "fmt"
+ "html/template"
+ "reflect"
+)
+
+var FuncMap = template.FuncMap{
+ "__amber_add": runtime_add,
+ "__amber_sub": runtime_sub,
+ "__amber_mul": runtime_mul,
+ "__amber_quo": runtime_quo,
+ "__amber_rem": runtime_rem,
+ "__amber_minus": runtime_minus,
+ "__amber_plus": runtime_plus,
+ "__amber_eql": runtime_eql,
+ "__amber_gtr": runtime_gtr,
+ "__amber_lss": runtime_lss,
+
+ "json": runtime_json,
+ "unescaped": runtime_unescaped,
+}
+
+func runtime_add(x, y interface{}) interface{} {
+ vx, vy := reflect.ValueOf(x), reflect.ValueOf(y)
+ switch vx.Kind() {
+ case reflect.Int, reflect.Int32, reflect.Int64, reflect.Int16, reflect.Int8:
+ {
+ switch vy.Kind() {
+ case reflect.Int, reflect.Int32, reflect.Int64, reflect.Int16, reflect.Int8:
+ return vx.Int() + vy.Int()
+ case reflect.Float32, reflect.Float64:
+ return float64(vx.Int()) + vy.Float()
+ case reflect.String:
+ return fmt.Sprintf("%d%s", vx.Int(), vy.String())
+ }
+ }
+ case reflect.Float32, reflect.Float64:
+ {
+ switch vy.Kind() {
+ case reflect.Int, reflect.Int32, reflect.Int64, reflect.Int16, reflect.Int8:
+ return vx.Float() + float64(vy.Int())
+ case reflect.Float32, reflect.Float64:
+ return vx.Float() + vy.Float()
+ case reflect.String:
+ return fmt.Sprintf("%f%s", vx.Float(), vy.String())
+ }
+ }
+ case reflect.String:
+ {
+ switch vy.Kind() {
+ case reflect.Int, reflect.Int32, reflect.Int64, reflect.Int16, reflect.Int8:
+ return fmt.Sprintf("%s%d", vx.String(), vy.Int())
+ case reflect.Float32, reflect.Float64:
+ return fmt.Sprintf("%s%f", vx.String(), vy.Float())
+ case reflect.String:
+ return fmt.Sprintf("%s%s", vx.String(), vy.String())
+ }
+ }
+ }
+
+ return ""
+}
+
+func runtime_sub(x, y interface{}) interface{} {
+ vx, vy := reflect.ValueOf(x), reflect.ValueOf(y)
+ switch vx.Kind() {
+ case reflect.Int, reflect.Int32, reflect.Int64, reflect.Int16, reflect.Int8:
+ {
+ switch vy.Kind() {
+ case reflect.Int, reflect.Int32, reflect.Int64, reflect.Int16, reflect.Int8:
+ return vx.Int() - vy.Int()
+ case reflect.Float32, reflect.Float64:
+ return float64(vx.Int()) - vy.Float()
+ }
+ }
+ case reflect.Float32, reflect.Float64:
+ {
+ switch vy.Kind() {
+ case reflect.Int, reflect.Int32, reflect.Int64, reflect.Int16, reflect.Int8:
+ return vx.Float() - float64(vy.Int())
+ case reflect.Float32, reflect.Float64:
+ return vx.Float() - vy.Float()
+ }
+ }
+ }
+
+ return ""
+}
+
+func runtime_mul(x, y interface{}) interface{} {
+ vx, vy := reflect.ValueOf(x), reflect.ValueOf(y)
+ switch vx.Kind() {
+ case reflect.Int, reflect.Int32, reflect.Int64, reflect.Int16, reflect.Int8:
+ {
+ switch vy.Kind() {
+ case reflect.Int, reflect.Int32, reflect.Int64, reflect.Int16, reflect.Int8:
+ return vx.Int() * vy.Int()
+ case reflect.Float32, reflect.Float64:
+ return float64(vx.Int()) * vy.Float()
+ }
+ }
+ case reflect.Float32, reflect.Float64:
+ {
+ switch vy.Kind() {
+ case reflect.Int, reflect.Int32, reflect.Int64, reflect.Int16, reflect.Int8:
+ return vx.Float() * float64(vy.Int())
+ case reflect.Float32, reflect.Float64:
+ return vx.Float() * vy.Float()
+ }
+ }
+ }
+
+ return ""
+}
+
+func runtime_quo(x, y interface{}) interface{} {
+ vx, vy := reflect.ValueOf(x), reflect.ValueOf(y)
+ switch vx.Kind() {
+ case reflect.Int, reflect.Int32, reflect.Int64, reflect.Int16, reflect.Int8:
+ {
+ switch vy.Kind() {
+ case reflect.Int, reflect.Int32, reflect.Int64, reflect.Int16, reflect.Int8:
+ return vx.Int() / vy.Int()
+ case reflect.Float32, reflect.Float64:
+ return float64(vx.Int()) / vy.Float()
+ }
+ }
+ case reflect.Float32, reflect.Float64:
+ {
+ switch vy.Kind() {
+ case reflect.Int, reflect.Int32, reflect.Int64, reflect.Int16, reflect.Int8:
+ return vx.Float() / float64(vy.Int())
+ case reflect.Float32, reflect.Float64:
+ return vx.Float() / vy.Float()
+ }
+ }
+ }
+
+ return ""
+}
+
+func runtime_rem(x, y interface{}) interface{} {
+ vx, vy := reflect.ValueOf(x), reflect.ValueOf(y)
+ switch vx.Kind() {
+ case reflect.Int, reflect.Int32, reflect.Int64, reflect.Int16, reflect.Int8:
+ {
+ switch vy.Kind() {
+ case reflect.Int, reflect.Int32, reflect.Int64, reflect.Int16, reflect.Int8:
+ return vx.Int() % vy.Int()
+ }
+ }
+ }
+
+ return ""
+}
+
+func runtime_minus(x interface{}) interface{} {
+ vx := reflect.ValueOf(x)
+ switch vx.Kind() {
+ case reflect.Int, reflect.Int32, reflect.Int64, reflect.Int16, reflect.Int8:
+ return -vx.Int()
+ case reflect.Float32, reflect.Float64:
+ return -vx.Float()
+ }
+
+ return ""
+}
+
+func runtime_plus(x interface{}) interface{} {
+ vx := reflect.ValueOf(x)
+ switch vx.Kind() {
+ case reflect.Int, reflect.Int32, reflect.Int64, reflect.Int16, reflect.Int8:
+ return +vx.Int()
+ case reflect.Float32, reflect.Float64:
+ return +vx.Float()
+ }
+
+ return ""
+}
+
+func runtime_eql(x, y interface{}) bool {
+ vx, vy := reflect.ValueOf(x), reflect.ValueOf(y)
+ switch vx.Kind() {
+ case reflect.Int, reflect.Int32, reflect.Int64, reflect.Int16, reflect.Int8:
+ {
+ switch vy.Kind() {
+ case reflect.Int, reflect.Int32, reflect.Int64, reflect.Int16, reflect.Int8:
+ return vx.Int() == vy.Int()
+ case reflect.Float32, reflect.Float64:
+ return float64(vx.Int()) == vy.Float()
+ case reflect.String:
+ return fmt.Sprintf("%d", vx.Int()) == vy.String()
+ }
+ }
+ case reflect.Float32, reflect.Float64:
+ {
+ switch vy.Kind() {
+ case reflect.Int, reflect.Int32, reflect.Int64, reflect.Int16, reflect.Int8:
+ return vx.Float() == float64(vy.Int())
+ case reflect.Float32, reflect.Float64:
+ return vx.Float() == vy.Float()
+ case reflect.String:
+ return fmt.Sprintf("%f", vx.Float()) == vy.String()
+ }
+ }
+ case reflect.String:
+ {
+ switch vy.Kind() {
+ case reflect.Int, reflect.Int32, reflect.Int64, reflect.Int16, reflect.Int8:
+ return vx.String() == fmt.Sprintf("%d", vy.Int())
+ case reflect.Float32, reflect.Float64:
+ return vx.String() == fmt.Sprintf("%f", vy.Float())
+ case reflect.String:
+ return vx.String() == fmt.Sprintf("%s", vy.String())
+ }
+ }
+ case reflect.Bool:
+ {
+ switch vy.Kind() {
+ case reflect.Int, reflect.Int32, reflect.Int64, reflect.Int16, reflect.Int8:
+ return vx.Bool() && vy.Int() != 0
+ case reflect.Bool:
+ return vx.Bool() == vy.Bool()
+ }
+ }
+ }
+
+ return false
+}
+
+func runtime_lss(x, y interface{}) bool {
+ vx, vy := reflect.ValueOf(x), reflect.ValueOf(y)
+ switch vx.Kind() {
+ case reflect.Int, reflect.Int32, reflect.Int64, reflect.Int16, reflect.Int8:
+ {
+ switch vy.Kind() {
+ case reflect.Int, reflect.Int32, reflect.Int64, reflect.Int16, reflect.Int8:
+ return vx.Int() < vy.Int()
+ case reflect.Float32, reflect.Float64:
+ return float64(vx.Int()) < vy.Float()
+ case reflect.String:
+ return fmt.Sprintf("%d", vx.Int()) < vy.String()
+ }
+ }
+ case reflect.Float32, reflect.Float64:
+ {
+ switch vy.Kind() {
+ case reflect.Int, reflect.Int32, reflect.Int64, reflect.Int16, reflect.Int8:
+ return vx.Float() < float64(vy.Int())
+ case reflect.Float32, reflect.Float64:
+ return vx.Float() < vy.Float()
+ case reflect.String:
+ return fmt.Sprintf("%f", vx.Float()) < vy.String()
+ }
+ }
+ case reflect.String:
+ {
+ switch vy.Kind() {
+ case reflect.Int, reflect.Int32, reflect.Int64, reflect.Int16, reflect.Int8:
+ return vx.String() < fmt.Sprintf("%d", vy.Int())
+ case reflect.Float32, reflect.Float64:
+ return vx.String() < fmt.Sprintf("%f", vy.Float())
+ case reflect.String:
+ return vx.String() < vy.String()
+ }
+ }
+ }
+
+ return false
+}
+
+func runtime_gtr(x, y interface{}) bool {
+ return !runtime_lss(x, y) && !runtime_eql(x, y)
+}
+
+func runtime_json(x interface{}) (res string, err error) {
+ bres, err := json.Marshal(x)
+ res = string(bres)
+ return
+}
+
+func runtime_unescaped(x string) interface{} {
+ return template.HTML(x)
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/fatih/color/LICENSE.md b/go/ql/test/experimental/CWE-321/vendor/github.com/fatih/color/LICENSE.md
new file mode 100644
index 000000000000..25fdaf639dfc
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/fatih/color/LICENSE.md
@@ -0,0 +1,20 @@
+The MIT License (MIT)
+
+Copyright (c) 2013 Fatih Arslan
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/fatih/color/README.md b/go/ql/test/experimental/CWE-321/vendor/github.com/fatih/color/README.md
new file mode 100644
index 000000000000..5152bf59bf8e
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/fatih/color/README.md
@@ -0,0 +1,178 @@
+# color [](https://github.com/fatih/color/actions) [](https://pkg.go.dev/github.com/fatih/color)
+
+Color lets you use colorized outputs in terms of [ANSI Escape
+Codes](http://en.wikipedia.org/wiki/ANSI_escape_code#Colors) in Go (Golang). It
+has support for Windows too! The API can be used in several ways, pick one that
+suits you.
+
+
+
+
+## Install
+
+```bash
+go get github.com/fatih/color
+```
+
+## Examples
+
+### Standard colors
+
+```go
+// Print with default helper functions
+color.Cyan("Prints text in cyan.")
+
+// A newline will be appended automatically
+color.Blue("Prints %s in blue.", "text")
+
+// These are using the default foreground colors
+color.Red("We have red")
+color.Magenta("And many others ..")
+
+```
+
+### Mix and reuse colors
+
+```go
+// Create a new color object
+c := color.New(color.FgCyan).Add(color.Underline)
+c.Println("Prints cyan text with an underline.")
+
+// Or just add them to New()
+d := color.New(color.FgCyan, color.Bold)
+d.Printf("This prints bold cyan %s\n", "too!.")
+
+// Mix up foreground and background colors, create new mixes!
+red := color.New(color.FgRed)
+
+boldRed := red.Add(color.Bold)
+boldRed.Println("This will print text in bold red.")
+
+whiteBackground := red.Add(color.BgWhite)
+whiteBackground.Println("Red text with white background.")
+```
+
+### Use your own output (io.Writer)
+
+```go
+// Use your own io.Writer output
+color.New(color.FgBlue).Fprintln(myWriter, "blue color!")
+
+blue := color.New(color.FgBlue)
+blue.Fprint(writer, "This will print text in blue.")
+```
+
+### Custom print functions (PrintFunc)
+
+```go
+// Create a custom print function for convenience
+red := color.New(color.FgRed).PrintfFunc()
+red("Warning")
+red("Error: %s", err)
+
+// Mix up multiple attributes
+notice := color.New(color.Bold, color.FgGreen).PrintlnFunc()
+notice("Don't forget this...")
+```
+
+### Custom fprint functions (FprintFunc)
+
+```go
+blue := color.New(color.FgBlue).FprintfFunc()
+blue(myWriter, "important notice: %s", stars)
+
+// Mix up with multiple attributes
+success := color.New(color.Bold, color.FgGreen).FprintlnFunc()
+success(myWriter, "Don't forget this...")
+```
+
+### Insert into noncolor strings (SprintFunc)
+
+```go
+// Create SprintXxx functions to mix strings with other non-colorized strings:
+yellow := color.New(color.FgYellow).SprintFunc()
+red := color.New(color.FgRed).SprintFunc()
+fmt.Printf("This is a %s and this is %s.\n", yellow("warning"), red("error"))
+
+info := color.New(color.FgWhite, color.BgGreen).SprintFunc()
+fmt.Printf("This %s rocks!\n", info("package"))
+
+// Use helper functions
+fmt.Println("This", color.RedString("warning"), "should be not neglected.")
+fmt.Printf("%v %v\n", color.GreenString("Info:"), "an important message.")
+
+// Windows supported too! Just don't forget to change the output to color.Output
+fmt.Fprintf(color.Output, "Windows support: %s", color.GreenString("PASS"))
+```
+
+### Plug into existing code
+
+```go
+// Use handy standard colors
+color.Set(color.FgYellow)
+
+fmt.Println("Existing text will now be in yellow")
+fmt.Printf("This one %s\n", "too")
+
+color.Unset() // Don't forget to unset
+
+// You can mix up parameters
+color.Set(color.FgMagenta, color.Bold)
+defer color.Unset() // Use it in your function
+
+fmt.Println("All text will now be bold magenta.")
+```
+
+### Disable/Enable color
+
+There might be a case where you want to explicitly disable/enable color output. the
+`go-isatty` package will automatically disable color output for non-tty output streams
+(for example if the output were piped directly to `less`).
+
+The `color` package also disables color output if the [`NO_COLOR`](https://no-color.org) environment
+variable is set (regardless of its value).
+
+`Color` has support to disable/enable colors programatically both globally and
+for single color definitions. For example suppose you have a CLI app and a
+`--no-color` bool flag. You can easily disable the color output with:
+
+```go
+var flagNoColor = flag.Bool("no-color", false, "Disable color output")
+
+if *flagNoColor {
+ color.NoColor = true // disables colorized output
+}
+```
+
+It also has support for single color definitions (local). You can
+disable/enable color output on the fly:
+
+```go
+c := color.New(color.FgCyan)
+c.Println("Prints cyan text")
+
+c.DisableColor()
+c.Println("This is printed without any color")
+
+c.EnableColor()
+c.Println("This prints again cyan...")
+```
+
+## GitHub Actions
+
+To output color in GitHub Actions (or other CI systems that support ANSI colors), make sure to set `color.NoColor = false` so that it bypasses the check for non-tty output streams.
+
+## Todo
+
+* Save/Return previous values
+* Evaluate fmt.Formatter interface
+
+
+## Credits
+
+ * [Fatih Arslan](https://github.com/fatih)
+ * Windows support via @mattn: [colorable](https://github.com/mattn/go-colorable)
+
+## License
+
+The MIT License (MIT) - see [`LICENSE.md`](https://github.com/fatih/color/blob/master/LICENSE.md) for more details
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/fatih/color/color.go b/go/ql/test/experimental/CWE-321/vendor/github.com/fatih/color/color.go
new file mode 100644
index 000000000000..98a60f3c88d6
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/fatih/color/color.go
@@ -0,0 +1,618 @@
+package color
+
+import (
+ "fmt"
+ "io"
+ "os"
+ "strconv"
+ "strings"
+ "sync"
+
+ "github.com/mattn/go-colorable"
+ "github.com/mattn/go-isatty"
+)
+
+var (
+ // NoColor defines if the output is colorized or not. It's dynamically set to
+ // false or true based on the stdout's file descriptor referring to a terminal
+ // or not. It's also set to true if the NO_COLOR environment variable is
+ // set (regardless of its value). This is a global option and affects all
+ // colors. For more control over each color block use the methods
+ // DisableColor() individually.
+ NoColor = noColorExists() || os.Getenv("TERM") == "dumb" ||
+ (!isatty.IsTerminal(os.Stdout.Fd()) && !isatty.IsCygwinTerminal(os.Stdout.Fd()))
+
+ // Output defines the standard output of the print functions. By default
+ // os.Stdout is used.
+ Output = colorable.NewColorableStdout()
+
+ // Error defines a color supporting writer for os.Stderr.
+ Error = colorable.NewColorableStderr()
+
+ // colorsCache is used to reduce the count of created Color objects and
+ // allows to reuse already created objects with required Attribute.
+ colorsCache = make(map[Attribute]*Color)
+ colorsCacheMu sync.Mutex // protects colorsCache
+)
+
+// noColorExists returns true if the environment variable NO_COLOR exists.
+func noColorExists() bool {
+ _, exists := os.LookupEnv("NO_COLOR")
+ return exists
+}
+
+// Color defines a custom color object which is defined by SGR parameters.
+type Color struct {
+ params []Attribute
+ noColor *bool
+}
+
+// Attribute defines a single SGR Code
+type Attribute int
+
+const escape = "\x1b"
+
+// Base attributes
+const (
+ Reset Attribute = iota
+ Bold
+ Faint
+ Italic
+ Underline
+ BlinkSlow
+ BlinkRapid
+ ReverseVideo
+ Concealed
+ CrossedOut
+)
+
+// Foreground text colors
+const (
+ FgBlack Attribute = iota + 30
+ FgRed
+ FgGreen
+ FgYellow
+ FgBlue
+ FgMagenta
+ FgCyan
+ FgWhite
+)
+
+// Foreground Hi-Intensity text colors
+const (
+ FgHiBlack Attribute = iota + 90
+ FgHiRed
+ FgHiGreen
+ FgHiYellow
+ FgHiBlue
+ FgHiMagenta
+ FgHiCyan
+ FgHiWhite
+)
+
+// Background text colors
+const (
+ BgBlack Attribute = iota + 40
+ BgRed
+ BgGreen
+ BgYellow
+ BgBlue
+ BgMagenta
+ BgCyan
+ BgWhite
+)
+
+// Background Hi-Intensity text colors
+const (
+ BgHiBlack Attribute = iota + 100
+ BgHiRed
+ BgHiGreen
+ BgHiYellow
+ BgHiBlue
+ BgHiMagenta
+ BgHiCyan
+ BgHiWhite
+)
+
+// New returns a newly created color object.
+func New(value ...Attribute) *Color {
+ c := &Color{
+ params: make([]Attribute, 0),
+ }
+
+ if noColorExists() {
+ c.noColor = boolPtr(true)
+ }
+
+ c.Add(value...)
+ return c
+}
+
+// Set sets the given parameters immediately. It will change the color of
+// output with the given SGR parameters until color.Unset() is called.
+func Set(p ...Attribute) *Color {
+ c := New(p...)
+ c.Set()
+ return c
+}
+
+// Unset resets all escape attributes and clears the output. Usually should
+// be called after Set().
+func Unset() {
+ if NoColor {
+ return
+ }
+
+ fmt.Fprintf(Output, "%s[%dm", escape, Reset)
+}
+
+// Set sets the SGR sequence.
+func (c *Color) Set() *Color {
+ if c.isNoColorSet() {
+ return c
+ }
+
+ fmt.Fprintf(Output, c.format())
+ return c
+}
+
+func (c *Color) unset() {
+ if c.isNoColorSet() {
+ return
+ }
+
+ Unset()
+}
+
+func (c *Color) setWriter(w io.Writer) *Color {
+ if c.isNoColorSet() {
+ return c
+ }
+
+ fmt.Fprintf(w, c.format())
+ return c
+}
+
+func (c *Color) unsetWriter(w io.Writer) {
+ if c.isNoColorSet() {
+ return
+ }
+
+ if NoColor {
+ return
+ }
+
+ fmt.Fprintf(w, "%s[%dm", escape, Reset)
+}
+
+// Add is used to chain SGR parameters. Use as many as parameters to combine
+// and create custom color objects. Example: Add(color.FgRed, color.Underline).
+func (c *Color) Add(value ...Attribute) *Color {
+ c.params = append(c.params, value...)
+ return c
+}
+
+func (c *Color) prepend(value Attribute) {
+ c.params = append(c.params, 0)
+ copy(c.params[1:], c.params[0:])
+ c.params[0] = value
+}
+
+// Fprint formats using the default formats for its operands and writes to w.
+// Spaces are added between operands when neither is a string.
+// It returns the number of bytes written and any write error encountered.
+// On Windows, users should wrap w with colorable.NewColorable() if w is of
+// type *os.File.
+func (c *Color) Fprint(w io.Writer, a ...interface{}) (n int, err error) {
+ c.setWriter(w)
+ defer c.unsetWriter(w)
+
+ return fmt.Fprint(w, a...)
+}
+
+// Print formats using the default formats for its operands and writes to
+// standard output. Spaces are added between operands when neither is a
+// string. It returns the number of bytes written and any write error
+// encountered. This is the standard fmt.Print() method wrapped with the given
+// color.
+func (c *Color) Print(a ...interface{}) (n int, err error) {
+ c.Set()
+ defer c.unset()
+
+ return fmt.Fprint(Output, a...)
+}
+
+// Fprintf formats according to a format specifier and writes to w.
+// It returns the number of bytes written and any write error encountered.
+// On Windows, users should wrap w with colorable.NewColorable() if w is of
+// type *os.File.
+func (c *Color) Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) {
+ c.setWriter(w)
+ defer c.unsetWriter(w)
+
+ return fmt.Fprintf(w, format, a...)
+}
+
+// Printf formats according to a format specifier and writes to standard output.
+// It returns the number of bytes written and any write error encountered.
+// This is the standard fmt.Printf() method wrapped with the given color.
+func (c *Color) Printf(format string, a ...interface{}) (n int, err error) {
+ c.Set()
+ defer c.unset()
+
+ return fmt.Fprintf(Output, format, a...)
+}
+
+// Fprintln formats using the default formats for its operands and writes to w.
+// Spaces are always added between operands and a newline is appended.
+// On Windows, users should wrap w with colorable.NewColorable() if w is of
+// type *os.File.
+func (c *Color) Fprintln(w io.Writer, a ...interface{}) (n int, err error) {
+ c.setWriter(w)
+ defer c.unsetWriter(w)
+
+ return fmt.Fprintln(w, a...)
+}
+
+// Println formats using the default formats for its operands and writes to
+// standard output. Spaces are always added between operands and a newline is
+// appended. It returns the number of bytes written and any write error
+// encountered. This is the standard fmt.Print() method wrapped with the given
+// color.
+func (c *Color) Println(a ...interface{}) (n int, err error) {
+ c.Set()
+ defer c.unset()
+
+ return fmt.Fprintln(Output, a...)
+}
+
+// Sprint is just like Print, but returns a string instead of printing it.
+func (c *Color) Sprint(a ...interface{}) string {
+ return c.wrap(fmt.Sprint(a...))
+}
+
+// Sprintln is just like Println, but returns a string instead of printing it.
+func (c *Color) Sprintln(a ...interface{}) string {
+ return c.wrap(fmt.Sprintln(a...))
+}
+
+// Sprintf is just like Printf, but returns a string instead of printing it.
+func (c *Color) Sprintf(format string, a ...interface{}) string {
+ return c.wrap(fmt.Sprintf(format, a...))
+}
+
+// FprintFunc returns a new function that prints the passed arguments as
+// colorized with color.Fprint().
+func (c *Color) FprintFunc() func(w io.Writer, a ...interface{}) {
+ return func(w io.Writer, a ...interface{}) {
+ c.Fprint(w, a...)
+ }
+}
+
+// PrintFunc returns a new function that prints the passed arguments as
+// colorized with color.Print().
+func (c *Color) PrintFunc() func(a ...interface{}) {
+ return func(a ...interface{}) {
+ c.Print(a...)
+ }
+}
+
+// FprintfFunc returns a new function that prints the passed arguments as
+// colorized with color.Fprintf().
+func (c *Color) FprintfFunc() func(w io.Writer, format string, a ...interface{}) {
+ return func(w io.Writer, format string, a ...interface{}) {
+ c.Fprintf(w, format, a...)
+ }
+}
+
+// PrintfFunc returns a new function that prints the passed arguments as
+// colorized with color.Printf().
+func (c *Color) PrintfFunc() func(format string, a ...interface{}) {
+ return func(format string, a ...interface{}) {
+ c.Printf(format, a...)
+ }
+}
+
+// FprintlnFunc returns a new function that prints the passed arguments as
+// colorized with color.Fprintln().
+func (c *Color) FprintlnFunc() func(w io.Writer, a ...interface{}) {
+ return func(w io.Writer, a ...interface{}) {
+ c.Fprintln(w, a...)
+ }
+}
+
+// PrintlnFunc returns a new function that prints the passed arguments as
+// colorized with color.Println().
+func (c *Color) PrintlnFunc() func(a ...interface{}) {
+ return func(a ...interface{}) {
+ c.Println(a...)
+ }
+}
+
+// SprintFunc returns a new function that returns colorized strings for the
+// given arguments with fmt.Sprint(). Useful to put into or mix into other
+// string. Windows users should use this in conjunction with color.Output, example:
+//
+// put := New(FgYellow).SprintFunc()
+// fmt.Fprintf(color.Output, "This is a %s", put("warning"))
+func (c *Color) SprintFunc() func(a ...interface{}) string {
+ return func(a ...interface{}) string {
+ return c.wrap(fmt.Sprint(a...))
+ }
+}
+
+// SprintfFunc returns a new function that returns colorized strings for the
+// given arguments with fmt.Sprintf(). Useful to put into or mix into other
+// string. Windows users should use this in conjunction with color.Output.
+func (c *Color) SprintfFunc() func(format string, a ...interface{}) string {
+ return func(format string, a ...interface{}) string {
+ return c.wrap(fmt.Sprintf(format, a...))
+ }
+}
+
+// SprintlnFunc returns a new function that returns colorized strings for the
+// given arguments with fmt.Sprintln(). Useful to put into or mix into other
+// string. Windows users should use this in conjunction with color.Output.
+func (c *Color) SprintlnFunc() func(a ...interface{}) string {
+ return func(a ...interface{}) string {
+ return c.wrap(fmt.Sprintln(a...))
+ }
+}
+
+// sequence returns a formatted SGR sequence to be plugged into a "\x1b[...m"
+// an example output might be: "1;36" -> bold cyan
+func (c *Color) sequence() string {
+ format := make([]string, len(c.params))
+ for i, v := range c.params {
+ format[i] = strconv.Itoa(int(v))
+ }
+
+ return strings.Join(format, ";")
+}
+
+// wrap wraps the s string with the colors attributes. The string is ready to
+// be printed.
+func (c *Color) wrap(s string) string {
+ if c.isNoColorSet() {
+ return s
+ }
+
+ return c.format() + s + c.unformat()
+}
+
+func (c *Color) format() string {
+ return fmt.Sprintf("%s[%sm", escape, c.sequence())
+}
+
+func (c *Color) unformat() string {
+ return fmt.Sprintf("%s[%dm", escape, Reset)
+}
+
+// DisableColor disables the color output. Useful to not change any existing
+// code and still being able to output. Can be used for flags like
+// "--no-color". To enable back use EnableColor() method.
+func (c *Color) DisableColor() {
+ c.noColor = boolPtr(true)
+}
+
+// EnableColor enables the color output. Use it in conjunction with
+// DisableColor(). Otherwise this method has no side effects.
+func (c *Color) EnableColor() {
+ c.noColor = boolPtr(false)
+}
+
+func (c *Color) isNoColorSet() bool {
+ // check first if we have user set action
+ if c.noColor != nil {
+ return *c.noColor
+ }
+
+ // if not return the global option, which is disabled by default
+ return NoColor
+}
+
+// Equals returns a boolean value indicating whether two colors are equal.
+func (c *Color) Equals(c2 *Color) bool {
+ if len(c.params) != len(c2.params) {
+ return false
+ }
+
+ for _, attr := range c.params {
+ if !c2.attrExists(attr) {
+ return false
+ }
+ }
+
+ return true
+}
+
+func (c *Color) attrExists(a Attribute) bool {
+ for _, attr := range c.params {
+ if attr == a {
+ return true
+ }
+ }
+
+ return false
+}
+
+func boolPtr(v bool) *bool {
+ return &v
+}
+
+func getCachedColor(p Attribute) *Color {
+ colorsCacheMu.Lock()
+ defer colorsCacheMu.Unlock()
+
+ c, ok := colorsCache[p]
+ if !ok {
+ c = New(p)
+ colorsCache[p] = c
+ }
+
+ return c
+}
+
+func colorPrint(format string, p Attribute, a ...interface{}) {
+ c := getCachedColor(p)
+
+ if !strings.HasSuffix(format, "\n") {
+ format += "\n"
+ }
+
+ if len(a) == 0 {
+ c.Print(format)
+ } else {
+ c.Printf(format, a...)
+ }
+}
+
+func colorString(format string, p Attribute, a ...interface{}) string {
+ c := getCachedColor(p)
+
+ if len(a) == 0 {
+ return c.SprintFunc()(format)
+ }
+
+ return c.SprintfFunc()(format, a...)
+}
+
+// Black is a convenient helper function to print with black foreground. A
+// newline is appended to format by default.
+func Black(format string, a ...interface{}) { colorPrint(format, FgBlack, a...) }
+
+// Red is a convenient helper function to print with red foreground. A
+// newline is appended to format by default.
+func Red(format string, a ...interface{}) { colorPrint(format, FgRed, a...) }
+
+// Green is a convenient helper function to print with green foreground. A
+// newline is appended to format by default.
+func Green(format string, a ...interface{}) { colorPrint(format, FgGreen, a...) }
+
+// Yellow is a convenient helper function to print with yellow foreground.
+// A newline is appended to format by default.
+func Yellow(format string, a ...interface{}) { colorPrint(format, FgYellow, a...) }
+
+// Blue is a convenient helper function to print with blue foreground. A
+// newline is appended to format by default.
+func Blue(format string, a ...interface{}) { colorPrint(format, FgBlue, a...) }
+
+// Magenta is a convenient helper function to print with magenta foreground.
+// A newline is appended to format by default.
+func Magenta(format string, a ...interface{}) { colorPrint(format, FgMagenta, a...) }
+
+// Cyan is a convenient helper function to print with cyan foreground. A
+// newline is appended to format by default.
+func Cyan(format string, a ...interface{}) { colorPrint(format, FgCyan, a...) }
+
+// White is a convenient helper function to print with white foreground. A
+// newline is appended to format by default.
+func White(format string, a ...interface{}) { colorPrint(format, FgWhite, a...) }
+
+// BlackString is a convenient helper function to return a string with black
+// foreground.
+func BlackString(format string, a ...interface{}) string { return colorString(format, FgBlack, a...) }
+
+// RedString is a convenient helper function to return a string with red
+// foreground.
+func RedString(format string, a ...interface{}) string { return colorString(format, FgRed, a...) }
+
+// GreenString is a convenient helper function to return a string with green
+// foreground.
+func GreenString(format string, a ...interface{}) string { return colorString(format, FgGreen, a...) }
+
+// YellowString is a convenient helper function to return a string with yellow
+// foreground.
+func YellowString(format string, a ...interface{}) string { return colorString(format, FgYellow, a...) }
+
+// BlueString is a convenient helper function to return a string with blue
+// foreground.
+func BlueString(format string, a ...interface{}) string { return colorString(format, FgBlue, a...) }
+
+// MagentaString is a convenient helper function to return a string with magenta
+// foreground.
+func MagentaString(format string, a ...interface{}) string {
+ return colorString(format, FgMagenta, a...)
+}
+
+// CyanString is a convenient helper function to return a string with cyan
+// foreground.
+func CyanString(format string, a ...interface{}) string { return colorString(format, FgCyan, a...) }
+
+// WhiteString is a convenient helper function to return a string with white
+// foreground.
+func WhiteString(format string, a ...interface{}) string { return colorString(format, FgWhite, a...) }
+
+// HiBlack is a convenient helper function to print with hi-intensity black foreground. A
+// newline is appended to format by default.
+func HiBlack(format string, a ...interface{}) { colorPrint(format, FgHiBlack, a...) }
+
+// HiRed is a convenient helper function to print with hi-intensity red foreground. A
+// newline is appended to format by default.
+func HiRed(format string, a ...interface{}) { colorPrint(format, FgHiRed, a...) }
+
+// HiGreen is a convenient helper function to print with hi-intensity green foreground. A
+// newline is appended to format by default.
+func HiGreen(format string, a ...interface{}) { colorPrint(format, FgHiGreen, a...) }
+
+// HiYellow is a convenient helper function to print with hi-intensity yellow foreground.
+// A newline is appended to format by default.
+func HiYellow(format string, a ...interface{}) { colorPrint(format, FgHiYellow, a...) }
+
+// HiBlue is a convenient helper function to print with hi-intensity blue foreground. A
+// newline is appended to format by default.
+func HiBlue(format string, a ...interface{}) { colorPrint(format, FgHiBlue, a...) }
+
+// HiMagenta is a convenient helper function to print with hi-intensity magenta foreground.
+// A newline is appended to format by default.
+func HiMagenta(format string, a ...interface{}) { colorPrint(format, FgHiMagenta, a...) }
+
+// HiCyan is a convenient helper function to print with hi-intensity cyan foreground. A
+// newline is appended to format by default.
+func HiCyan(format string, a ...interface{}) { colorPrint(format, FgHiCyan, a...) }
+
+// HiWhite is a convenient helper function to print with hi-intensity white foreground. A
+// newline is appended to format by default.
+func HiWhite(format string, a ...interface{}) { colorPrint(format, FgHiWhite, a...) }
+
+// HiBlackString is a convenient helper function to return a string with hi-intensity black
+// foreground.
+func HiBlackString(format string, a ...interface{}) string {
+ return colorString(format, FgHiBlack, a...)
+}
+
+// HiRedString is a convenient helper function to return a string with hi-intensity red
+// foreground.
+func HiRedString(format string, a ...interface{}) string { return colorString(format, FgHiRed, a...) }
+
+// HiGreenString is a convenient helper function to return a string with hi-intensity green
+// foreground.
+func HiGreenString(format string, a ...interface{}) string {
+ return colorString(format, FgHiGreen, a...)
+}
+
+// HiYellowString is a convenient helper function to return a string with hi-intensity yellow
+// foreground.
+func HiYellowString(format string, a ...interface{}) string {
+ return colorString(format, FgHiYellow, a...)
+}
+
+// HiBlueString is a convenient helper function to return a string with hi-intensity blue
+// foreground.
+func HiBlueString(format string, a ...interface{}) string { return colorString(format, FgHiBlue, a...) }
+
+// HiMagentaString is a convenient helper function to return a string with hi-intensity magenta
+// foreground.
+func HiMagentaString(format string, a ...interface{}) string {
+ return colorString(format, FgHiMagenta, a...)
+}
+
+// HiCyanString is a convenient helper function to return a string with hi-intensity cyan
+// foreground.
+func HiCyanString(format string, a ...interface{}) string { return colorString(format, FgHiCyan, a...) }
+
+// HiWhiteString is a convenient helper function to return a string with hi-intensity white
+// foreground.
+func HiWhiteString(format string, a ...interface{}) string {
+ return colorString(format, FgHiWhite, a...)
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/fatih/color/doc.go b/go/ql/test/experimental/CWE-321/vendor/github.com/fatih/color/doc.go
new file mode 100644
index 000000000000..04541de786f3
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/fatih/color/doc.go
@@ -0,0 +1,135 @@
+/*
+Package color is an ANSI color package to output colorized or SGR defined
+output to the standard output. The API can be used in several way, pick one
+that suits you.
+
+Use simple and default helper functions with predefined foreground colors:
+
+ color.Cyan("Prints text in cyan.")
+
+ // a newline will be appended automatically
+ color.Blue("Prints %s in blue.", "text")
+
+ // More default foreground colors..
+ color.Red("We have red")
+ color.Yellow("Yellow color too!")
+ color.Magenta("And many others ..")
+
+ // Hi-intensity colors
+ color.HiGreen("Bright green color.")
+ color.HiBlack("Bright black means gray..")
+ color.HiWhite("Shiny white color!")
+
+However there are times where custom color mixes are required. Below are some
+examples to create custom color objects and use the print functions of each
+separate color object.
+
+ // Create a new color object
+ c := color.New(color.FgCyan).Add(color.Underline)
+ c.Println("Prints cyan text with an underline.")
+
+ // Or just add them to New()
+ d := color.New(color.FgCyan, color.Bold)
+ d.Printf("This prints bold cyan %s\n", "too!.")
+
+
+ // Mix up foreground and background colors, create new mixes!
+ red := color.New(color.FgRed)
+
+ boldRed := red.Add(color.Bold)
+ boldRed.Println("This will print text in bold red.")
+
+ whiteBackground := red.Add(color.BgWhite)
+ whiteBackground.Println("Red text with White background.")
+
+ // Use your own io.Writer output
+ color.New(color.FgBlue).Fprintln(myWriter, "blue color!")
+
+ blue := color.New(color.FgBlue)
+ blue.Fprint(myWriter, "This will print text in blue.")
+
+You can create PrintXxx functions to simplify even more:
+
+ // Create a custom print function for convenient
+ red := color.New(color.FgRed).PrintfFunc()
+ red("warning")
+ red("error: %s", err)
+
+ // Mix up multiple attributes
+ notice := color.New(color.Bold, color.FgGreen).PrintlnFunc()
+ notice("don't forget this...")
+
+You can also FprintXxx functions to pass your own io.Writer:
+
+ blue := color.New(FgBlue).FprintfFunc()
+ blue(myWriter, "important notice: %s", stars)
+
+ // Mix up with multiple attributes
+ success := color.New(color.Bold, color.FgGreen).FprintlnFunc()
+ success(myWriter, don't forget this...")
+
+
+Or create SprintXxx functions to mix strings with other non-colorized strings:
+
+ yellow := New(FgYellow).SprintFunc()
+ red := New(FgRed).SprintFunc()
+
+ fmt.Printf("this is a %s and this is %s.\n", yellow("warning"), red("error"))
+
+ info := New(FgWhite, BgGreen).SprintFunc()
+ fmt.Printf("this %s rocks!\n", info("package"))
+
+Windows support is enabled by default. All Print functions work as intended.
+However only for color.SprintXXX functions, user should use fmt.FprintXXX and
+set the output to color.Output:
+
+ fmt.Fprintf(color.Output, "Windows support: %s", color.GreenString("PASS"))
+
+ info := New(FgWhite, BgGreen).SprintFunc()
+ fmt.Fprintf(color.Output, "this %s rocks!\n", info("package"))
+
+Using with existing code is possible. Just use the Set() method to set the
+standard output to the given parameters. That way a rewrite of an existing
+code is not required.
+
+ // Use handy standard colors.
+ color.Set(color.FgYellow)
+
+ fmt.Println("Existing text will be now in Yellow")
+ fmt.Printf("This one %s\n", "too")
+
+ color.Unset() // don't forget to unset
+
+ // You can mix up parameters
+ color.Set(color.FgMagenta, color.Bold)
+ defer color.Unset() // use it in your function
+
+ fmt.Println("All text will be now bold magenta.")
+
+There might be a case where you want to disable color output (for example to
+pipe the standard output of your app to somewhere else). `Color` has support to
+disable colors both globally and for single color definition. For example
+suppose you have a CLI app and a `--no-color` bool flag. You can easily disable
+the color output with:
+
+ var flagNoColor = flag.Bool("no-color", false, "Disable color output")
+
+ if *flagNoColor {
+ color.NoColor = true // disables colorized output
+ }
+
+You can also disable the color by setting the NO_COLOR environment variable to any value.
+
+It also has support for single color definitions (local). You can
+disable/enable color output on the fly:
+
+ c := color.New(color.FgCyan)
+ c.Println("Prints cyan text")
+
+ c.DisableColor()
+ c.Println("This is printed without any color")
+
+ c.EnableColor()
+ c.Println("This prints again cyan...")
+*/
+package color
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/fatih/structs/.gitignore b/go/ql/test/experimental/CWE-321/vendor/github.com/fatih/structs/.gitignore
new file mode 100644
index 000000000000..836562412fe8
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/fatih/structs/.gitignore
@@ -0,0 +1,23 @@
+# Compiled Object files, Static and Dynamic libs (Shared Objects)
+*.o
+*.a
+*.so
+
+# Folders
+_obj
+_test
+
+# Architecture specific extensions/prefixes
+*.[568vq]
+[568vq].out
+
+*.cgo1.go
+*.cgo2.c
+_cgo_defun.c
+_cgo_gotypes.go
+_cgo_export.*
+
+_testmain.go
+
+*.exe
+*.test
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/fatih/structs/.travis.yml b/go/ql/test/experimental/CWE-321/vendor/github.com/fatih/structs/.travis.yml
new file mode 100644
index 000000000000..a08df798127a
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/fatih/structs/.travis.yml
@@ -0,0 +1,13 @@
+language: go
+go:
+ - 1.7.x
+ - 1.8.x
+ - 1.9.x
+ - tip
+sudo: false
+before_install:
+- go get github.com/axw/gocov/gocov
+- go get github.com/mattn/goveralls
+- if ! go get github.com/golang/tools/cmd/cover; then go get golang.org/x/tools/cmd/cover; fi
+script:
+- $HOME/gopath/bin/goveralls -service=travis-ci
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/fatih/structs/LICENSE b/go/ql/test/experimental/CWE-321/vendor/github.com/fatih/structs/LICENSE
new file mode 100644
index 000000000000..34504e4b3efb
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/fatih/structs/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2014 Fatih Arslan
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
\ No newline at end of file
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/fatih/structs/README.md b/go/ql/test/experimental/CWE-321/vendor/github.com/fatih/structs/README.md
new file mode 100644
index 000000000000..a75eabf37bbc
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/fatih/structs/README.md
@@ -0,0 +1,163 @@
+# Structs [](http://godoc.org/github.com/fatih/structs) [](https://travis-ci.org/fatih/structs) [](https://coveralls.io/r/fatih/structs)
+
+Structs contains various utilities to work with Go (Golang) structs. It was
+initially used by me to convert a struct into a `map[string]interface{}`. With
+time I've added other utilities for structs. It's basically a high level
+package based on primitives from the reflect package. Feel free to add new
+functions or improve the existing code.
+
+## Install
+
+```bash
+go get github.com/fatih/structs
+```
+
+## Usage and Examples
+
+Just like the standard lib `strings`, `bytes` and co packages, `structs` has
+many global functions to manipulate or organize your struct data. Lets define
+and declare a struct:
+
+```go
+type Server struct {
+ Name string `json:"name,omitempty"`
+ ID int
+ Enabled bool
+ users []string // not exported
+ http.Server // embedded
+}
+
+server := &Server{
+ Name: "gopher",
+ ID: 123456,
+ Enabled: true,
+}
+```
+
+```go
+// Convert a struct to a map[string]interface{}
+// => {"Name":"gopher", "ID":123456, "Enabled":true}
+m := structs.Map(server)
+
+// Convert the values of a struct to a []interface{}
+// => ["gopher", 123456, true]
+v := structs.Values(server)
+
+// Convert the names of a struct to a []string
+// (see "Names methods" for more info about fields)
+n := structs.Names(server)
+
+// Convert the values of a struct to a []*Field
+// (see "Field methods" for more info about fields)
+f := structs.Fields(server)
+
+// Return the struct name => "Server"
+n := structs.Name(server)
+
+// Check if any field of a struct is initialized or not.
+h := structs.HasZero(server)
+
+// Check if all fields of a struct is initialized or not.
+z := structs.IsZero(server)
+
+// Check if server is a struct or a pointer to struct
+i := structs.IsStruct(server)
+```
+
+### Struct methods
+
+The structs functions can be also used as independent methods by creating a new
+`*structs.Struct`. This is handy if you want to have more control over the
+structs (such as retrieving a single Field).
+
+```go
+// Create a new struct type:
+s := structs.New(server)
+
+m := s.Map() // Get a map[string]interface{}
+v := s.Values() // Get a []interface{}
+f := s.Fields() // Get a []*Field
+n := s.Names() // Get a []string
+f := s.Field(name) // Get a *Field based on the given field name
+f, ok := s.FieldOk(name) // Get a *Field based on the given field name
+n := s.Name() // Get the struct name
+h := s.HasZero() // Check if any field is uninitialized
+z := s.IsZero() // Check if all fields are uninitialized
+```
+
+### Field methods
+
+We can easily examine a single Field for more detail. Below you can see how we
+get and interact with various field methods:
+
+
+```go
+s := structs.New(server)
+
+// Get the Field struct for the "Name" field
+name := s.Field("Name")
+
+// Get the underlying value, value => "gopher"
+value := name.Value().(string)
+
+// Set the field's value
+name.Set("another gopher")
+
+// Get the field's kind, kind => "string"
+name.Kind()
+
+// Check if the field is exported or not
+if name.IsExported() {
+ fmt.Println("Name field is exported")
+}
+
+// Check if the value is a zero value, such as "" for string, 0 for int
+if !name.IsZero() {
+ fmt.Println("Name is initialized")
+}
+
+// Check if the field is an anonymous (embedded) field
+if !name.IsEmbedded() {
+ fmt.Println("Name is not an embedded field")
+}
+
+// Get the Field's tag value for tag name "json", tag value => "name,omitempty"
+tagValue := name.Tag("json")
+```
+
+Nested structs are supported too:
+
+```go
+addrField := s.Field("Server").Field("Addr")
+
+// Get the value for addr
+a := addrField.Value().(string)
+
+// Or get all fields
+httpServer := s.Field("Server").Fields()
+```
+
+We can also get a slice of Fields from the Struct type to iterate over all
+fields. This is handy if you wish to examine all fields:
+
+```go
+s := structs.New(server)
+
+for _, f := range s.Fields() {
+ fmt.Printf("field name: %+v\n", f.Name())
+
+ if f.IsExported() {
+ fmt.Printf("value : %+v\n", f.Value())
+ fmt.Printf("is zero : %+v\n", f.IsZero())
+ }
+}
+```
+
+## Credits
+
+ * [Fatih Arslan](https://github.com/fatih)
+ * [Cihangir Savas](https://github.com/cihangir)
+
+## License
+
+The MIT License (MIT) - see LICENSE.md for more details
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/fatih/structs/field.go b/go/ql/test/experimental/CWE-321/vendor/github.com/fatih/structs/field.go
new file mode 100644
index 000000000000..e69783230b4e
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/fatih/structs/field.go
@@ -0,0 +1,141 @@
+package structs
+
+import (
+ "errors"
+ "fmt"
+ "reflect"
+)
+
+var (
+ errNotExported = errors.New("field is not exported")
+ errNotSettable = errors.New("field is not settable")
+)
+
+// Field represents a single struct field that encapsulates high level
+// functions around the field.
+type Field struct {
+ value reflect.Value
+ field reflect.StructField
+ defaultTag string
+}
+
+// Tag returns the value associated with key in the tag string. If there is no
+// such key in the tag, Tag returns the empty string.
+func (f *Field) Tag(key string) string {
+ return f.field.Tag.Get(key)
+}
+
+// Value returns the underlying value of the field. It panics if the field
+// is not exported.
+func (f *Field) Value() interface{} {
+ return f.value.Interface()
+}
+
+// IsEmbedded returns true if the given field is an anonymous field (embedded)
+func (f *Field) IsEmbedded() bool {
+ return f.field.Anonymous
+}
+
+// IsExported returns true if the given field is exported.
+func (f *Field) IsExported() bool {
+ return f.field.PkgPath == ""
+}
+
+// IsZero returns true if the given field is not initialized (has a zero value).
+// It panics if the field is not exported.
+func (f *Field) IsZero() bool {
+ zero := reflect.Zero(f.value.Type()).Interface()
+ current := f.Value()
+
+ return reflect.DeepEqual(current, zero)
+}
+
+// Name returns the name of the given field
+func (f *Field) Name() string {
+ return f.field.Name
+}
+
+// Kind returns the fields kind, such as "string", "map", "bool", etc ..
+func (f *Field) Kind() reflect.Kind {
+ return f.value.Kind()
+}
+
+// Set sets the field to given value v. It returns an error if the field is not
+// settable (not addressable or not exported) or if the given value's type
+// doesn't match the fields type.
+func (f *Field) Set(val interface{}) error {
+ // we can't set unexported fields, so be sure this field is exported
+ if !f.IsExported() {
+ return errNotExported
+ }
+
+ // do we get here? not sure...
+ if !f.value.CanSet() {
+ return errNotSettable
+ }
+
+ given := reflect.ValueOf(val)
+
+ if f.value.Kind() != given.Kind() {
+ return fmt.Errorf("wrong kind. got: %s want: %s", given.Kind(), f.value.Kind())
+ }
+
+ f.value.Set(given)
+ return nil
+}
+
+// Zero sets the field to its zero value. It returns an error if the field is not
+// settable (not addressable or not exported).
+func (f *Field) Zero() error {
+ zero := reflect.Zero(f.value.Type()).Interface()
+ return f.Set(zero)
+}
+
+// Fields returns a slice of Fields. This is particular handy to get the fields
+// of a nested struct . A struct tag with the content of "-" ignores the
+// checking of that particular field. Example:
+//
+// // Field is ignored by this package.
+// Field *http.Request `structs:"-"`
+//
+// It panics if field is not exported or if field's kind is not struct
+func (f *Field) Fields() []*Field {
+ return getFields(f.value, f.defaultTag)
+}
+
+// Field returns the field from a nested struct. It panics if the nested struct
+// is not exported or if the field was not found.
+func (f *Field) Field(name string) *Field {
+ field, ok := f.FieldOk(name)
+ if !ok {
+ panic("field not found")
+ }
+
+ return field
+}
+
+// FieldOk returns the field from a nested struct. The boolean returns whether
+// the field was found (true) or not (false).
+func (f *Field) FieldOk(name string) (*Field, bool) {
+ value := &f.value
+ // value must be settable so we need to make sure it holds the address of the
+ // variable and not a copy, so we can pass the pointer to strctVal instead of a
+ // copy (which is not assigned to any variable, hence not settable).
+ // see "https://blog.golang.org/laws-of-reflection#TOC_8."
+ if f.value.Kind() != reflect.Ptr {
+ a := f.value.Addr()
+ value = &a
+ }
+ v := strctVal(value.Interface())
+ t := v.Type()
+
+ field, ok := t.FieldByName(name)
+ if !ok {
+ return nil, false
+ }
+
+ return &Field{
+ field: field,
+ value: v.FieldByName(name),
+ }, true
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/fatih/structs/structs.go b/go/ql/test/experimental/CWE-321/vendor/github.com/fatih/structs/structs.go
new file mode 100644
index 000000000000..3a87706525f7
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/fatih/structs/structs.go
@@ -0,0 +1,584 @@
+// Package structs contains various utilities functions to work with structs.
+package structs
+
+import (
+ "fmt"
+
+ "reflect"
+)
+
+var (
+ // DefaultTagName is the default tag name for struct fields which provides
+ // a more granular to tweak certain structs. Lookup the necessary functions
+ // for more info.
+ DefaultTagName = "structs" // struct's field default tag name
+)
+
+// Struct encapsulates a struct type to provide several high level functions
+// around the struct.
+type Struct struct {
+ raw interface{}
+ value reflect.Value
+ TagName string
+}
+
+// New returns a new *Struct with the struct s. It panics if the s's kind is
+// not struct.
+func New(s interface{}) *Struct {
+ return &Struct{
+ raw: s,
+ value: strctVal(s),
+ TagName: DefaultTagName,
+ }
+}
+
+// Map converts the given struct to a map[string]interface{}, where the keys
+// of the map are the field names and the values of the map the associated
+// values of the fields. The default key string is the struct field name but
+// can be changed in the struct field's tag value. The "structs" key in the
+// struct's field tag value is the key name. Example:
+//
+// // Field appears in map as key "myName".
+// Name string `structs:"myName"`
+//
+// A tag value with the content of "-" ignores that particular field. Example:
+//
+// // Field is ignored by this package.
+// Field bool `structs:"-"`
+//
+// A tag value with the content of "string" uses the stringer to get the value. Example:
+//
+// // The value will be output of Animal's String() func.
+// // Map will panic if Animal does not implement String().
+// Field *Animal `structs:"field,string"`
+//
+// A tag value with the option of "flatten" used in a struct field is to flatten its fields
+// in the output map. Example:
+//
+// // The FieldStruct's fields will be flattened into the output map.
+// FieldStruct time.Time `structs:",flatten"`
+//
+// A tag value with the option of "omitnested" stops iterating further if the type
+// is a struct. Example:
+//
+// // Field is not processed further by this package.
+// Field time.Time `structs:"myName,omitnested"`
+// Field *http.Request `structs:",omitnested"`
+//
+// A tag value with the option of "omitempty" ignores that particular field if
+// the field value is empty. Example:
+//
+// // Field appears in map as key "myName", but the field is
+// // skipped if empty.
+// Field string `structs:"myName,omitempty"`
+//
+// // Field appears in map as key "Field" (the default), but
+// // the field is skipped if empty.
+// Field string `structs:",omitempty"`
+//
+// Note that only exported fields of a struct can be accessed, non exported
+// fields will be neglected.
+func (s *Struct) Map() map[string]interface{} {
+ out := make(map[string]interface{})
+ s.FillMap(out)
+ return out
+}
+
+// FillMap is the same as Map. Instead of returning the output, it fills the
+// given map.
+func (s *Struct) FillMap(out map[string]interface{}) {
+ if out == nil {
+ return
+ }
+
+ fields := s.structFields()
+
+ for _, field := range fields {
+ name := field.Name
+ val := s.value.FieldByName(name)
+ isSubStruct := false
+ var finalVal interface{}
+
+ tagName, tagOpts := parseTag(field.Tag.Get(s.TagName))
+ if tagName != "" {
+ name = tagName
+ }
+
+ // if the value is a zero value and the field is marked as omitempty do
+ // not include
+ if tagOpts.Has("omitempty") {
+ zero := reflect.Zero(val.Type()).Interface()
+ current := val.Interface()
+
+ if reflect.DeepEqual(current, zero) {
+ continue
+ }
+ }
+
+ if !tagOpts.Has("omitnested") {
+ finalVal = s.nested(val)
+
+ v := reflect.ValueOf(val.Interface())
+ if v.Kind() == reflect.Ptr {
+ v = v.Elem()
+ }
+
+ switch v.Kind() {
+ case reflect.Map, reflect.Struct:
+ isSubStruct = true
+ }
+ } else {
+ finalVal = val.Interface()
+ }
+
+ if tagOpts.Has("string") {
+ s, ok := val.Interface().(fmt.Stringer)
+ if ok {
+ out[name] = s.String()
+ }
+ continue
+ }
+
+ if isSubStruct && (tagOpts.Has("flatten")) {
+ for k := range finalVal.(map[string]interface{}) {
+ out[k] = finalVal.(map[string]interface{})[k]
+ }
+ } else {
+ out[name] = finalVal
+ }
+ }
+}
+
+// Values converts the given s struct's field values to a []interface{}. A
+// struct tag with the content of "-" ignores the that particular field.
+// Example:
+//
+// // Field is ignored by this package.
+// Field int `structs:"-"`
+//
+// A value with the option of "omitnested" stops iterating further if the type
+// is a struct. Example:
+//
+// // Fields is not processed further by this package.
+// Field time.Time `structs:",omitnested"`
+// Field *http.Request `structs:",omitnested"`
+//
+// A tag value with the option of "omitempty" ignores that particular field and
+// is not added to the values if the field value is empty. Example:
+//
+// // Field is skipped if empty
+// Field string `structs:",omitempty"`
+//
+// Note that only exported fields of a struct can be accessed, non exported
+// fields will be neglected.
+func (s *Struct) Values() []interface{} {
+ fields := s.structFields()
+
+ var t []interface{}
+
+ for _, field := range fields {
+ val := s.value.FieldByName(field.Name)
+
+ _, tagOpts := parseTag(field.Tag.Get(s.TagName))
+
+ // if the value is a zero value and the field is marked as omitempty do
+ // not include
+ if tagOpts.Has("omitempty") {
+ zero := reflect.Zero(val.Type()).Interface()
+ current := val.Interface()
+
+ if reflect.DeepEqual(current, zero) {
+ continue
+ }
+ }
+
+ if tagOpts.Has("string") {
+ s, ok := val.Interface().(fmt.Stringer)
+ if ok {
+ t = append(t, s.String())
+ }
+ continue
+ }
+
+ if IsStruct(val.Interface()) && !tagOpts.Has("omitnested") {
+ // look out for embedded structs, and convert them to a
+ // []interface{} to be added to the final values slice
+ t = append(t, Values(val.Interface())...)
+ } else {
+ t = append(t, val.Interface())
+ }
+ }
+
+ return t
+}
+
+// Fields returns a slice of Fields. A struct tag with the content of "-"
+// ignores the checking of that particular field. Example:
+//
+// // Field is ignored by this package.
+// Field bool `structs:"-"`
+//
+// It panics if s's kind is not struct.
+func (s *Struct) Fields() []*Field {
+ return getFields(s.value, s.TagName)
+}
+
+// Names returns a slice of field names. A struct tag with the content of "-"
+// ignores the checking of that particular field. Example:
+//
+// // Field is ignored by this package.
+// Field bool `structs:"-"`
+//
+// It panics if s's kind is not struct.
+func (s *Struct) Names() []string {
+ fields := getFields(s.value, s.TagName)
+
+ names := make([]string, len(fields))
+
+ for i, field := range fields {
+ names[i] = field.Name()
+ }
+
+ return names
+}
+
+func getFields(v reflect.Value, tagName string) []*Field {
+ if v.Kind() == reflect.Ptr {
+ v = v.Elem()
+ }
+
+ t := v.Type()
+
+ var fields []*Field
+
+ for i := 0; i < t.NumField(); i++ {
+ field := t.Field(i)
+
+ if tag := field.Tag.Get(tagName); tag == "-" {
+ continue
+ }
+
+ f := &Field{
+ field: field,
+ value: v.FieldByName(field.Name),
+ }
+
+ fields = append(fields, f)
+
+ }
+
+ return fields
+}
+
+// Field returns a new Field struct that provides several high level functions
+// around a single struct field entity. It panics if the field is not found.
+func (s *Struct) Field(name string) *Field {
+ f, ok := s.FieldOk(name)
+ if !ok {
+ panic("field not found")
+ }
+
+ return f
+}
+
+// FieldOk returns a new Field struct that provides several high level functions
+// around a single struct field entity. The boolean returns true if the field
+// was found.
+func (s *Struct) FieldOk(name string) (*Field, bool) {
+ t := s.value.Type()
+
+ field, ok := t.FieldByName(name)
+ if !ok {
+ return nil, false
+ }
+
+ return &Field{
+ field: field,
+ value: s.value.FieldByName(name),
+ defaultTag: s.TagName,
+ }, true
+}
+
+// IsZero returns true if all fields in a struct is a zero value (not
+// initialized) A struct tag with the content of "-" ignores the checking of
+// that particular field. Example:
+//
+// // Field is ignored by this package.
+// Field bool `structs:"-"`
+//
+// A value with the option of "omitnested" stops iterating further if the type
+// is a struct. Example:
+//
+// // Field is not processed further by this package.
+// Field time.Time `structs:"myName,omitnested"`
+// Field *http.Request `structs:",omitnested"`
+//
+// Note that only exported fields of a struct can be accessed, non exported
+// fields will be neglected. It panics if s's kind is not struct.
+func (s *Struct) IsZero() bool {
+ fields := s.structFields()
+
+ for _, field := range fields {
+ val := s.value.FieldByName(field.Name)
+
+ _, tagOpts := parseTag(field.Tag.Get(s.TagName))
+
+ if IsStruct(val.Interface()) && !tagOpts.Has("omitnested") {
+ ok := IsZero(val.Interface())
+ if !ok {
+ return false
+ }
+
+ continue
+ }
+
+ // zero value of the given field, such as "" for string, 0 for int
+ zero := reflect.Zero(val.Type()).Interface()
+
+ // current value of the given field
+ current := val.Interface()
+
+ if !reflect.DeepEqual(current, zero) {
+ return false
+ }
+ }
+
+ return true
+}
+
+// HasZero returns true if a field in a struct is not initialized (zero value).
+// A struct tag with the content of "-" ignores the checking of that particular
+// field. Example:
+//
+// // Field is ignored by this package.
+// Field bool `structs:"-"`
+//
+// A value with the option of "omitnested" stops iterating further if the type
+// is a struct. Example:
+//
+// // Field is not processed further by this package.
+// Field time.Time `structs:"myName,omitnested"`
+// Field *http.Request `structs:",omitnested"`
+//
+// Note that only exported fields of a struct can be accessed, non exported
+// fields will be neglected. It panics if s's kind is not struct.
+func (s *Struct) HasZero() bool {
+ fields := s.structFields()
+
+ for _, field := range fields {
+ val := s.value.FieldByName(field.Name)
+
+ _, tagOpts := parseTag(field.Tag.Get(s.TagName))
+
+ if IsStruct(val.Interface()) && !tagOpts.Has("omitnested") {
+ ok := HasZero(val.Interface())
+ if ok {
+ return true
+ }
+
+ continue
+ }
+
+ // zero value of the given field, such as "" for string, 0 for int
+ zero := reflect.Zero(val.Type()).Interface()
+
+ // current value of the given field
+ current := val.Interface()
+
+ if reflect.DeepEqual(current, zero) {
+ return true
+ }
+ }
+
+ return false
+}
+
+// Name returns the structs's type name within its package. For more info refer
+// to Name() function.
+func (s *Struct) Name() string {
+ return s.value.Type().Name()
+}
+
+// structFields returns the exported struct fields for a given s struct. This
+// is a convenient helper method to avoid duplicate code in some of the
+// functions.
+func (s *Struct) structFields() []reflect.StructField {
+ t := s.value.Type()
+
+ var f []reflect.StructField
+
+ for i := 0; i < t.NumField(); i++ {
+ field := t.Field(i)
+ // we can't access the value of unexported fields
+ if field.PkgPath != "" {
+ continue
+ }
+
+ // don't check if it's omitted
+ if tag := field.Tag.Get(s.TagName); tag == "-" {
+ continue
+ }
+
+ f = append(f, field)
+ }
+
+ return f
+}
+
+func strctVal(s interface{}) reflect.Value {
+ v := reflect.ValueOf(s)
+
+ // if pointer get the underlying element≤
+ for v.Kind() == reflect.Ptr {
+ v = v.Elem()
+ }
+
+ if v.Kind() != reflect.Struct {
+ panic("not struct")
+ }
+
+ return v
+}
+
+// Map converts the given struct to a map[string]interface{}. For more info
+// refer to Struct types Map() method. It panics if s's kind is not struct.
+func Map(s interface{}) map[string]interface{} {
+ return New(s).Map()
+}
+
+// FillMap is the same as Map. Instead of returning the output, it fills the
+// given map.
+func FillMap(s interface{}, out map[string]interface{}) {
+ New(s).FillMap(out)
+}
+
+// Values converts the given struct to a []interface{}. For more info refer to
+// Struct types Values() method. It panics if s's kind is not struct.
+func Values(s interface{}) []interface{} {
+ return New(s).Values()
+}
+
+// Fields returns a slice of *Field. For more info refer to Struct types
+// Fields() method. It panics if s's kind is not struct.
+func Fields(s interface{}) []*Field {
+ return New(s).Fields()
+}
+
+// Names returns a slice of field names. For more info refer to Struct types
+// Names() method. It panics if s's kind is not struct.
+func Names(s interface{}) []string {
+ return New(s).Names()
+}
+
+// IsZero returns true if all fields is equal to a zero value. For more info
+// refer to Struct types IsZero() method. It panics if s's kind is not struct.
+func IsZero(s interface{}) bool {
+ return New(s).IsZero()
+}
+
+// HasZero returns true if any field is equal to a zero value. For more info
+// refer to Struct types HasZero() method. It panics if s's kind is not struct.
+func HasZero(s interface{}) bool {
+ return New(s).HasZero()
+}
+
+// IsStruct returns true if the given variable is a struct or a pointer to
+// struct.
+func IsStruct(s interface{}) bool {
+ v := reflect.ValueOf(s)
+ if v.Kind() == reflect.Ptr {
+ v = v.Elem()
+ }
+
+ // uninitialized zero value of a struct
+ if v.Kind() == reflect.Invalid {
+ return false
+ }
+
+ return v.Kind() == reflect.Struct
+}
+
+// Name returns the structs's type name within its package. It returns an
+// empty string for unnamed types. It panics if s's kind is not struct.
+func Name(s interface{}) string {
+ return New(s).Name()
+}
+
+// nested retrieves recursively all types for the given value and returns the
+// nested value.
+func (s *Struct) nested(val reflect.Value) interface{} {
+ var finalVal interface{}
+
+ v := reflect.ValueOf(val.Interface())
+ if v.Kind() == reflect.Ptr {
+ v = v.Elem()
+ }
+
+ switch v.Kind() {
+ case reflect.Struct:
+ n := New(val.Interface())
+ n.TagName = s.TagName
+ m := n.Map()
+
+ // do not add the converted value if there are no exported fields, ie:
+ // time.Time
+ if len(m) == 0 {
+ finalVal = val.Interface()
+ } else {
+ finalVal = m
+ }
+ case reflect.Map:
+ // get the element type of the map
+ mapElem := val.Type()
+ switch val.Type().Kind() {
+ case reflect.Ptr, reflect.Array, reflect.Map,
+ reflect.Slice, reflect.Chan:
+ mapElem = val.Type().Elem()
+ if mapElem.Kind() == reflect.Ptr {
+ mapElem = mapElem.Elem()
+ }
+ }
+
+ // only iterate over struct types, ie: map[string]StructType,
+ // map[string][]StructType,
+ if mapElem.Kind() == reflect.Struct ||
+ (mapElem.Kind() == reflect.Slice &&
+ mapElem.Elem().Kind() == reflect.Struct) {
+ m := make(map[string]interface{}, val.Len())
+ for _, k := range val.MapKeys() {
+ m[k.String()] = s.nested(val.MapIndex(k))
+ }
+ finalVal = m
+ break
+ }
+
+ // TODO(arslan): should this be optional?
+ finalVal = val.Interface()
+ case reflect.Slice, reflect.Array:
+ if val.Type().Kind() == reflect.Interface {
+ finalVal = val.Interface()
+ break
+ }
+
+ // TODO(arslan): should this be optional?
+ // do not iterate of non struct types, just pass the value. Ie: []int,
+ // []string, co... We only iterate further if it's a struct.
+ // i.e []foo or []*foo
+ if val.Type().Elem().Kind() != reflect.Struct &&
+ !(val.Type().Elem().Kind() == reflect.Ptr &&
+ val.Type().Elem().Elem().Kind() == reflect.Struct) {
+ finalVal = val.Interface()
+ break
+ }
+
+ slices := make([]interface{}, val.Len())
+ for x := 0; x < val.Len(); x++ {
+ slices[x] = s.nested(val.Index(x))
+ }
+ finalVal = slices
+ default:
+ finalVal = val.Interface()
+ }
+
+ return finalVal
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/fatih/structs/tags.go b/go/ql/test/experimental/CWE-321/vendor/github.com/fatih/structs/tags.go
new file mode 100644
index 000000000000..136a31eba9a9
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/fatih/structs/tags.go
@@ -0,0 +1,32 @@
+package structs
+
+import "strings"
+
+// tagOptions contains a slice of tag options
+type tagOptions []string
+
+// Has returns true if the given option is available in tagOptions
+func (t tagOptions) Has(opt string) bool {
+ for _, tagOpt := range t {
+ if tagOpt == opt {
+ return true
+ }
+ }
+
+ return false
+}
+
+// parseTag splits a struct field's tag into its name and a list of options
+// which comes after a name. A tag is in the form of: "name,option1,option2".
+// The name can be neglectected.
+func parseTag(tag string) (string, tagOptions) {
+ // tag is one of followings:
+ // ""
+ // "name"
+ // "name,opt"
+ // "name,opt,opt2"
+ // ",opt"
+
+ res := strings.Split(tag, ",")
+ return res[0], res[1:]
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/.gitattributes b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/.gitattributes
new file mode 100644
index 000000000000..fcadb2cf9791
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/.gitattributes
@@ -0,0 +1 @@
+* text eol=lf
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/.gitignore b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/.gitignore
new file mode 100644
index 000000000000..1346be5512f8
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/.gitignore
@@ -0,0 +1,41 @@
+# Compiled Object files, Static and Dynamic libs (Shared Objects)
+*.o
+*.a
+*.so
+
+# Folders
+_obj
+_test
+.idea
+.vscode
+
+# Architecture specific extensions/prefixes
+*.[568vq]
+[568vq].out
+
+*.cgo1.go
+*.cgo2.c
+_cgo_defun.c
+_cgo_gotypes.go
+_cgo_export.*
+
+_testmain.go
+
+*.exe
+
+.project
+EBNF.txt
+test1.tpl
+pongo2_internal_test.go
+tpl-error.out
+/count.out
+/cover.out
+*.swp
+*.iml
+/cpu.out
+/mem.out
+/pongo2.test
+*.error
+/profile
+/coverage.out
+/pongo2_internal_test.ignore
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/.travis.yml b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/.travis.yml
new file mode 100644
index 000000000000..28eeff66c067
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/.travis.yml
@@ -0,0 +1,11 @@
+language: go
+arch:
+ - AMD64
+ - ppc64le
+os:
+ - linux
+ - osx
+go:
+ - 1.12
+script:
+ - go test -v
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/AUTHORS b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/AUTHORS
new file mode 100644
index 000000000000..601697cfa91a
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/AUTHORS
@@ -0,0 +1,11 @@
+Main author and maintainer of pongo2:
+
+* Florian Schlachter
+
+Contributors (in no specific order):
+
+* @romanoaugusto88
+* @vitalbh
+* @blaubaer
+
+Feel free to add yourself to the list or to modify your entry if you did a contribution.
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/LICENSE b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/LICENSE
new file mode 100644
index 000000000000..e876f869054c
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/LICENSE
@@ -0,0 +1,20 @@
+The MIT License (MIT)
+
+Copyright (c) 2013-2014 Florian Schlachter
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/README.md b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/README.md
new file mode 100644
index 000000000000..d684034b85b7
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/README.md
@@ -0,0 +1,170 @@
+# [pongo](https://en.wikipedia.org/wiki/Pongo_%28genus%29)2
+
+[](https://pkg.go.dev/github.com/flosch/pongo2)
+[](https://travis-ci.org/flosch/pongo2)
+
+pongo2 is a Django-syntax like templating-language ([official website](https://www.schlachter.tech/solutions/pongo2-template-engine/)).
+
+Install/update using `go get` (no dependencies required by pongo2):
+
+```sh
+go get -u github.com/flosch/pongo2/v4
+```
+
+Please use the [issue tracker](https://github.com/flosch/pongo2/issues) if you're encountering any problems with pongo2 or if you need help with implementing tags or filters ([create a ticket!](https://github.com/flosch/pongo2/issues/new)).
+
+## First impression of a template
+
+```django
+
+
+ Our admins and users
+
+ {# This is a short example to give you a quick overview of pongo2's syntax. #}
+ {% macro user_details(user, is_admin=false) %}
+
This user registered {{ user.register_date|naturaltime }}.
+
+
+
The user's biography:
+
+ {{ user.biography|markdown|truncatewords_html:15 }}
+ read more
+
+
+ {% if is_admin %}
+
This user is an admin!
+ {% endif %}
+
+ {% endmacro %}
+
+
+
+
+
Our admins
+ {% for admin in adminlist %} {{ user_details(admin, true) }} {% endfor %}
+
+
Our members
+ {% for user in userlist %} {{ user_details(user) }} {% endfor %}
+
+
+```
+
+## Features
+
+- Syntax- and feature-set-compatible with [Django 1.7](https://django.readthedocs.io/en/1.7.x/topics/templates.html)
+- [Advanced C-like expressions](https://github.com/flosch/pongo2/blob/master/template_tests/expressions.tpl).
+- [Complex function calls within expressions](https://github.com/flosch/pongo2/blob/master/template_tests/function_calls_wrapper.tpl).
+- [Easy API to create new filters and tags](http://godoc.org/github.com/flosch/pongo2#RegisterFilter) ([including parsing arguments](http://godoc.org/github.com/flosch/pongo2#Parser))
+- Additional features:
+ - Macros including importing macros from other files (see [template_tests/macro.tpl](https://github.com/flosch/pongo2/blob/master/template_tests/macro.tpl))
+ - [Template sandboxing](https://godoc.org/github.com/flosch/pongo2#TemplateSet) ([directory patterns](http://golang.org/pkg/path/filepath/#Match), banned tags/filters)
+
+## Caveats
+
+### Filters
+
+- **date** / **time**: The `date` and `time` filter are taking the Golang specific time- and date-format (not Django's one) currently. [Take a look on the format here](http://golang.org/pkg/time/#Time.Format).
+- **stringformat**: `stringformat` does **not** take Python's string format syntax as a parameter, instead it takes Go's. Essentially `{{ 3.14|stringformat:"pi is %.2f" }}` is `fmt.Sprintf("pi is %.2f", 3.14)`.
+- **escape** / **force_escape**: Unlike Django's behaviour, the `escape`-filter is applied immediately. Therefore there is no need for a `force_escape`-filter yet.
+
+### Tags
+
+- **for**: All the `forloop` fields (like `forloop.counter`) are written with a capital letter at the beginning. For example, the `counter` can be accessed by `forloop.Counter` and the parentloop by `forloop.Parentloop`.
+- **now**: takes Go's time format (see **date** and **time**-filter).
+
+### Misc
+
+- **not in-operator**: You can check whether a map/struct/string contains a key/field/substring by using the in-operator (or the negation of it):
+ `{% if key in map %}Key is in map{% else %}Key not in map{% endif %}` or `{% if !(key in map) %}Key is NOT in map{% else %}Key is in map{% endif %}`.
+
+## Add-ons, libraries and helpers
+
+### Official
+
+- [pongo2-addons](https://github.com/flosch/pongo2-addons) - Official additional filters/tags for pongo2 (for example a **markdown**-filter). They are in their own repository because they're relying on 3rd-party-libraries.
+
+### 3rd-party
+
+- [beego-pongo2](https://github.com/oal/beego-pongo2) - A tiny little helper for using Pongo2 with [Beego](https://github.com/astaxie/beego).
+- [beego-pongo2.v2](https://github.com/ipfans/beego-pongo2.v2) - Same as `beego-pongo2`, but for pongo2 v2.
+- [macaron-pongo2](https://github.com/macaron-contrib/pongo2) - pongo2 support for [Macaron](https://github.com/Unknwon/macaron), a modular web framework.
+- [ginpongo2](https://github.com/ngerakines/ginpongo2) - middleware for [gin](github.com/gin-gonic/gin) to use pongo2 templates
+- [Build'n support for Iris' template engine](https://github.com/kataras/iris)
+- [pongo2gin](https://gitlab.com/go-box/pongo2gin) - alternative renderer for [gin](github.com/gin-gonic/gin) to use pongo2 templates
+- [pongo2-trans](https://github.com/digitalcrab/pongo2trans) - `trans`-tag implementation for internationalization
+- [tpongo2](https://github.com/tango-contrib/tpongo2) - pongo2 support for [Tango](https://github.com/lunny/tango), a micro-kernel & pluggable web framework.
+- [p2cli](https://github.com/wrouesnel/p2cli) - command line templating utility based on pongo2
+- [Pongo2echo](https://github.com/stnc/pongo2echo) - pongo2 echo framework stability renderer [stnc]
+- [Pongo2gin](https://github.com/stnc/pongo2gin) - pongo2 gin minimal framework stability renderer [stnc]
+
+
+Please add your project to this list and send me a pull request when you've developed something nice for pongo2.
+
+## Who's using pongo2
+
+[I'm compiling a list of pongo2 users](https://github.com/flosch/pongo2/issues/241). Add your project or company!
+
+## API-usage examples
+
+Please see the documentation for a full list of provided API methods.
+
+### A tiny example (template string)
+
+```go
+// Compile the template first (i. e. creating the AST)
+tpl, err := pongo2.FromString("Hello {{ name|capfirst }}!")
+if err != nil {
+ panic(err)
+}
+// Now you can render the template with the given
+// pongo2.Context how often you want to.
+out, err := tpl.Execute(pongo2.Context{"name": "florian"})
+if err != nil {
+ panic(err)
+}
+fmt.Println(out) // Output: Hello Florian!
+```
+
+## Example server-usage (template file)
+
+```go
+package main
+
+import (
+ "github.com/flosch/pongo2/v4"
+ "net/http"
+)
+
+// Pre-compiling the templates at application startup using the
+// little Must()-helper function (Must() will panic if FromFile()
+// or FromString() will return with an error - that's it).
+// It's faster to pre-compile it anywhere at startup and only
+// execute the template later.
+var tplExample = pongo2.Must(pongo2.FromFile("example.html"))
+
+func examplePage(w http.ResponseWriter, r *http.Request) {
+ // Execute the template per HTTP request
+ err := tplExample.ExecuteWriter(pongo2.Context{"query": r.FormValue("query")}, w)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ }
+}
+
+func main() {
+ http.HandleFunc("/", examplePage)
+ http.ListenAndServe(":8080", nil)
+}
+```
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/context.go b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/context.go
new file mode 100644
index 000000000000..dbc5e3e375ae
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/context.go
@@ -0,0 +1,137 @@
+package pongo2
+
+import (
+ "fmt"
+ "regexp"
+
+ "errors"
+)
+
+var reIdentifiers = regexp.MustCompile("^[a-zA-Z0-9_]+$")
+
+var autoescape = true
+
+func SetAutoescape(newValue bool) {
+ autoescape = newValue
+}
+
+// A Context type provides constants, variables, instances or functions to a template.
+//
+// pongo2 automatically provides meta-information or functions through the "pongo2"-key.
+// Currently, context["pongo2"] contains the following keys:
+// 1. version: returns the version string
+//
+// Template examples for accessing items from your context:
+// {{ myconstant }}
+// {{ myfunc("test", 42) }}
+// {{ user.name }}
+// {{ pongo2.version }}
+type Context map[string]interface{}
+
+func (c Context) checkForValidIdentifiers() *Error {
+ for k, v := range c {
+ if !reIdentifiers.MatchString(k) {
+ return &Error{
+ Sender: "checkForValidIdentifiers",
+ OrigError: fmt.Errorf("context-key '%s' (value: '%+v') is not a valid identifier", k, v),
+ }
+ }
+ }
+ return nil
+}
+
+// Update updates this context with the key/value-pairs from another context.
+func (c Context) Update(other Context) Context {
+ for k, v := range other {
+ c[k] = v
+ }
+ return c
+}
+
+// ExecutionContext contains all data important for the current rendering state.
+//
+// If you're writing a custom tag, your tag's Execute()-function will
+// have access to the ExecutionContext. This struct stores anything
+// about the current rendering process's Context including
+// the Context provided by the user (field Public).
+// You can safely use the Private context to provide data to the user's
+// template (like a 'forloop'-information). The Shared-context is used
+// to share data between tags. All ExecutionContexts share this context.
+//
+// Please be careful when accessing the Public data.
+// PLEASE DO NOT MODIFY THE PUBLIC CONTEXT (read-only).
+//
+// To create your own execution context within tags, use the
+// NewChildExecutionContext(parent) function.
+type ExecutionContext struct {
+ template *Template
+
+ Autoescape bool
+ Public Context
+ Private Context
+ Shared Context
+}
+
+var pongo2MetaContext = Context{
+ "version": Version,
+}
+
+func newExecutionContext(tpl *Template, ctx Context) *ExecutionContext {
+ privateCtx := make(Context)
+
+ // Make the pongo2-related funcs/vars available to the context
+ privateCtx["pongo2"] = pongo2MetaContext
+
+ return &ExecutionContext{
+ template: tpl,
+
+ Public: ctx,
+ Private: privateCtx,
+ Autoescape: autoescape,
+ }
+}
+
+func NewChildExecutionContext(parent *ExecutionContext) *ExecutionContext {
+ newctx := &ExecutionContext{
+ template: parent.template,
+
+ Public: parent.Public,
+ Private: make(Context),
+ Autoescape: parent.Autoescape,
+ }
+ newctx.Shared = parent.Shared
+
+ // Copy all existing private items
+ newctx.Private.Update(parent.Private)
+
+ return newctx
+}
+
+func (ctx *ExecutionContext) Error(msg string, token *Token) *Error {
+ return ctx.OrigError(errors.New(msg), token)
+}
+
+func (ctx *ExecutionContext) OrigError(err error, token *Token) *Error {
+ filename := ctx.template.name
+ var line, col int
+ if token != nil {
+ // No tokens available
+ // TODO: Add location (from where?)
+ filename = token.Filename
+ line = token.Line
+ col = token.Col
+ }
+ return &Error{
+ Template: ctx.template,
+ Filename: filename,
+ Line: line,
+ Column: col,
+ Token: token,
+ Sender: "execution",
+ OrigError: err,
+ }
+}
+
+func (ctx *ExecutionContext) Logf(format string, args ...interface{}) {
+ ctx.template.set.logf(format, args...)
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/doc.go b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/doc.go
new file mode 100644
index 000000000000..0a161deec123
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/doc.go
@@ -0,0 +1,31 @@
+// Package pongo2 is a Django-syntax like template-engine
+//
+// Blog posts about pongo2 (including introduction and migration):
+// https://www.florian-schlachter.de/?tag=pongo2
+//
+// Complete documentation on the template language:
+// https://docs.djangoproject.com/en/dev/topics/templates/
+//
+// Try out pongo2 live in the pongo2 playground:
+// https://www.florian-schlachter.de/pongo2/
+//
+// Make sure to read README.md in the repository as well.
+//
+// A tiny example with template strings:
+//
+// (Snippet on playground: https://www.florian-schlachter.de/pongo2/?id=1206546277)
+//
+// // Compile the template first (i. e. creating the AST)
+// tpl, err := pongo2.FromString("Hello {{ name|capfirst }}!")
+// if err != nil {
+// panic(err)
+// }
+// // Now you can render the template with the given
+// // pongo2.Context how often you want to.
+// out, err := tpl.Execute(pongo2.Context{"name": "fred"})
+// if err != nil {
+// panic(err)
+// }
+// fmt.Println(out) // Output: Hello Fred!
+//
+package pongo2
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/error.go b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/error.go
new file mode 100644
index 000000000000..8aec8c10034d
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/error.go
@@ -0,0 +1,91 @@
+package pongo2
+
+import (
+ "bufio"
+ "fmt"
+ "os"
+)
+
+// The Error type is being used to address an error during lexing, parsing or
+// execution. If you want to return an error object (for example in your own
+// tag or filter) fill this object with as much information as you have.
+// Make sure "Sender" is always given (if you're returning an error within
+// a filter, make Sender equals 'filter:yourfilter'; same goes for tags: 'tag:mytag').
+// It's okay if you only fill in ErrorMsg if you don't have any other details at hand.
+type Error struct {
+ Template *Template
+ Filename string
+ Line int
+ Column int
+ Token *Token
+ Sender string
+ OrigError error
+}
+
+func (e *Error) updateFromTokenIfNeeded(template *Template, t *Token) *Error {
+ if e.Template == nil {
+ e.Template = template
+ }
+
+ if e.Token == nil {
+ e.Token = t
+ if e.Line <= 0 {
+ e.Line = t.Line
+ e.Column = t.Col
+ }
+ }
+
+ return e
+}
+
+// Returns a nice formatted error string.
+func (e *Error) Error() string {
+ s := "[Error"
+ if e.Sender != "" {
+ s += " (where: " + e.Sender + ")"
+ }
+ if e.Filename != "" {
+ s += " in " + e.Filename
+ }
+ if e.Line > 0 {
+ s += fmt.Sprintf(" | Line %d Col %d", e.Line, e.Column)
+ if e.Token != nil {
+ s += fmt.Sprintf(" near '%s'", e.Token.Val)
+ }
+ }
+ s += "] "
+ s += e.OrigError.Error()
+ return s
+}
+
+// RawLine returns the affected line from the original template, if available.
+func (e *Error) RawLine() (line string, available bool, outErr error) {
+ if e.Line <= 0 || e.Filename == "" {
+ return "", false, nil
+ }
+
+ filename := e.Filename
+ if e.Template != nil {
+ filename = e.Template.set.resolveFilename(e.Template, e.Filename)
+ }
+ file, err := os.Open(filename)
+ if err != nil {
+ return "", false, err
+ }
+ defer func() {
+ err := file.Close()
+ if err != nil && outErr == nil {
+ outErr = err
+ }
+ }()
+
+ scanner := bufio.NewScanner(file)
+ l := 0
+ for scanner.Scan() {
+ l++
+ if l == e.Line {
+ return scanner.Text(), true, nil
+ }
+ }
+ return "", false, nil
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/filters.go b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/filters.go
new file mode 100644
index 000000000000..8d4c89e22f76
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/filters.go
@@ -0,0 +1,141 @@
+package pongo2
+
+import (
+ "fmt"
+)
+
+// FilterFunction is the type filter functions must fulfil
+type FilterFunction func(in *Value, param *Value) (out *Value, err *Error)
+
+var filters map[string]FilterFunction
+
+func init() {
+ filters = make(map[string]FilterFunction)
+}
+
+// FilterExists returns true if the given filter is already registered
+func FilterExists(name string) bool {
+ _, existing := filters[name]
+ return existing
+}
+
+// RegisterFilter registers a new filter. If there's already a filter with the same
+// name, RegisterFilter will panic. You usually want to call this
+// function in the filter's init() function:
+// http://golang.org/doc/effective_go.html#init
+//
+// See http://www.florian-schlachter.de/post/pongo2/ for more about
+// writing filters and tags.
+func RegisterFilter(name string, fn FilterFunction) error {
+ if FilterExists(name) {
+ return fmt.Errorf("filter with name '%s' is already registered", name)
+ }
+ filters[name] = fn
+ return nil
+}
+
+// ReplaceFilter replaces an already registered filter with a new implementation. Use this
+// function with caution since it allows you to change existing filter behaviour.
+func ReplaceFilter(name string, fn FilterFunction) error {
+ if !FilterExists(name) {
+ return fmt.Errorf("filter with name '%s' does not exist (therefore cannot be overridden)", name)
+ }
+ filters[name] = fn
+ return nil
+}
+
+// MustApplyFilter behaves like ApplyFilter, but panics on an error.
+func MustApplyFilter(name string, value *Value, param *Value) *Value {
+ val, err := ApplyFilter(name, value, param)
+ if err != nil {
+ panic(err)
+ }
+ return val
+}
+
+// ApplyFilter applies a filter to a given value using the given parameters.
+// Returns a *pongo2.Value or an error.
+func ApplyFilter(name string, value *Value, param *Value) (*Value, *Error) {
+ fn, existing := filters[name]
+ if !existing {
+ return nil, &Error{
+ Sender: "applyfilter",
+ OrigError: fmt.Errorf("Filter with name '%s' not found.", name),
+ }
+ }
+
+ // Make sure param is a *Value
+ if param == nil {
+ param = AsValue(nil)
+ }
+
+ return fn(value, param)
+}
+
+type filterCall struct {
+ token *Token
+
+ name string
+ parameter IEvaluator
+
+ filterFunc FilterFunction
+}
+
+func (fc *filterCall) Execute(v *Value, ctx *ExecutionContext) (*Value, *Error) {
+ var param *Value
+ var err *Error
+
+ if fc.parameter != nil {
+ param, err = fc.parameter.Evaluate(ctx)
+ if err != nil {
+ return nil, err
+ }
+ } else {
+ param = AsValue(nil)
+ }
+
+ filteredValue, err := fc.filterFunc(v, param)
+ if err != nil {
+ return nil, err.updateFromTokenIfNeeded(ctx.template, fc.token)
+ }
+ return filteredValue, nil
+}
+
+// Filter = IDENT | IDENT ":" FilterArg | IDENT "|" Filter
+func (p *Parser) parseFilter() (*filterCall, *Error) {
+ identToken := p.MatchType(TokenIdentifier)
+
+ // Check filter ident
+ if identToken == nil {
+ return nil, p.Error("Filter name must be an identifier.", nil)
+ }
+
+ filter := &filterCall{
+ token: identToken,
+ name: identToken.Val,
+ }
+
+ // Get the appropriate filter function and bind it
+ filterFn, exists := filters[identToken.Val]
+ if !exists {
+ return nil, p.Error(fmt.Sprintf("Filter '%s' does not exist.", identToken.Val), identToken)
+ }
+
+ filter.filterFunc = filterFn
+
+ // Check for filter-argument (2 tokens needed: ':' ARG)
+ if p.Match(TokenSymbol, ":") != nil {
+ if p.Peek(TokenSymbol, "}}") != nil {
+ return nil, p.Error("Filter parameter required after ':'.", nil)
+ }
+
+ // Get filter argument expression
+ v, err := p.parseVariableOrLiteral()
+ if err != nil {
+ return nil, err
+ }
+ filter.parameter = v
+ }
+
+ return filter, nil
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/filters_builtin.go b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/filters_builtin.go
new file mode 100644
index 000000000000..c0ec6161401d
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/filters_builtin.go
@@ -0,0 +1,927 @@
+package pongo2
+
+/* Filters that are provided through github.com/flosch/pongo2-addons:
+ ------------------------------------------------------------------
+
+ filesizeformat
+ slugify
+ timesince
+ timeuntil
+
+ Filters that won't be added:
+ ----------------------------
+
+ get_static_prefix (reason: web-framework specific)
+ pprint (reason: python-specific)
+ static (reason: web-framework specific)
+
+ Reconsideration (not implemented yet):
+ --------------------------------------
+
+ force_escape (reason: not yet needed since this is the behaviour of pongo2's escape filter)
+ safeseq (reason: same reason as `force_escape`)
+ unordered_list (python-specific; not sure whether needed or not)
+ dictsort (python-specific; maybe one could add a filter to sort a list of structs by a specific field name)
+ dictsortreversed (see dictsort)
+*/
+
+import (
+ "bytes"
+ "fmt"
+ "math/rand"
+ "net/url"
+ "regexp"
+ "strconv"
+ "strings"
+ "time"
+ "unicode/utf8"
+
+ "errors"
+)
+
+func init() {
+ rand.Seed(time.Now().Unix())
+
+ RegisterFilter("escape", filterEscape)
+ RegisterFilter("safe", filterSafe)
+ RegisterFilter("escapejs", filterEscapejs)
+
+ RegisterFilter("add", filterAdd)
+ RegisterFilter("addslashes", filterAddslashes)
+ RegisterFilter("capfirst", filterCapfirst)
+ RegisterFilter("center", filterCenter)
+ RegisterFilter("cut", filterCut)
+ RegisterFilter("date", filterDate)
+ RegisterFilter("default", filterDefault)
+ RegisterFilter("default_if_none", filterDefaultIfNone)
+ RegisterFilter("divisibleby", filterDivisibleby)
+ RegisterFilter("first", filterFirst)
+ RegisterFilter("floatformat", filterFloatformat)
+ RegisterFilter("get_digit", filterGetdigit)
+ RegisterFilter("iriencode", filterIriencode)
+ RegisterFilter("join", filterJoin)
+ RegisterFilter("last", filterLast)
+ RegisterFilter("length", filterLength)
+ RegisterFilter("length_is", filterLengthis)
+ RegisterFilter("linebreaks", filterLinebreaks)
+ RegisterFilter("linebreaksbr", filterLinebreaksbr)
+ RegisterFilter("linenumbers", filterLinenumbers)
+ RegisterFilter("ljust", filterLjust)
+ RegisterFilter("lower", filterLower)
+ RegisterFilter("make_list", filterMakelist)
+ RegisterFilter("phone2numeric", filterPhone2numeric)
+ RegisterFilter("pluralize", filterPluralize)
+ RegisterFilter("random", filterRandom)
+ RegisterFilter("removetags", filterRemovetags)
+ RegisterFilter("rjust", filterRjust)
+ RegisterFilter("slice", filterSlice)
+ RegisterFilter("split", filterSplit)
+ RegisterFilter("stringformat", filterStringformat)
+ RegisterFilter("striptags", filterStriptags)
+ RegisterFilter("time", filterDate) // time uses filterDate (same golang-format)
+ RegisterFilter("title", filterTitle)
+ RegisterFilter("truncatechars", filterTruncatechars)
+ RegisterFilter("truncatechars_html", filterTruncatecharsHTML)
+ RegisterFilter("truncatewords", filterTruncatewords)
+ RegisterFilter("truncatewords_html", filterTruncatewordsHTML)
+ RegisterFilter("upper", filterUpper)
+ RegisterFilter("urlencode", filterUrlencode)
+ RegisterFilter("urlize", filterUrlize)
+ RegisterFilter("urlizetrunc", filterUrlizetrunc)
+ RegisterFilter("wordcount", filterWordcount)
+ RegisterFilter("wordwrap", filterWordwrap)
+ RegisterFilter("yesno", filterYesno)
+
+ RegisterFilter("float", filterFloat) // pongo-specific
+ RegisterFilter("integer", filterInteger) // pongo-specific
+}
+
+func filterTruncatecharsHelper(s string, newLen int) string {
+ runes := []rune(s)
+ if newLen < len(runes) {
+ if newLen >= 3 {
+ return fmt.Sprintf("%s...", string(runes[:newLen-3]))
+ }
+ // Not enough space for the ellipsis
+ return string(runes[:newLen])
+ }
+ return string(runes)
+}
+
+func filterTruncateHTMLHelper(value string, newOutput *bytes.Buffer, cond func() bool, fn func(c rune, s int, idx int) int, finalize func()) {
+ vLen := len(value)
+ var tagStack []string
+ idx := 0
+
+ for idx < vLen && !cond() {
+ c, s := utf8.DecodeRuneInString(value[idx:])
+ if c == utf8.RuneError {
+ idx += s
+ continue
+ }
+
+ if c == '<' {
+ newOutput.WriteRune(c)
+ idx += s // consume "<"
+
+ if idx+1 < vLen {
+ if value[idx] == '/' {
+ // Close tag
+
+ newOutput.WriteString("/")
+
+ tag := ""
+ idx++ // consume "/"
+
+ for idx < vLen {
+ c2, size2 := utf8.DecodeRuneInString(value[idx:])
+ if c2 == utf8.RuneError {
+ idx += size2
+ continue
+ }
+
+ // End of tag found
+ if c2 == '>' {
+ idx++ // consume ">"
+ break
+ }
+ tag += string(c2)
+ idx += size2
+ }
+
+ if len(tagStack) > 0 {
+ // Ideally, the close tag is TOP of tag stack
+ // In malformed HTML, it must not be, so iterate through the stack and remove the tag
+ for i := len(tagStack) - 1; i >= 0; i-- {
+ if tagStack[i] == tag {
+ // Found the tag
+ tagStack[i] = tagStack[len(tagStack)-1]
+ tagStack = tagStack[:len(tagStack)-1]
+ break
+ }
+ }
+ }
+
+ newOutput.WriteString(tag)
+ newOutput.WriteString(">")
+ } else {
+ // Open tag
+
+ tag := ""
+
+ params := false
+ for idx < vLen {
+ c2, size2 := utf8.DecodeRuneInString(value[idx:])
+ if c2 == utf8.RuneError {
+ idx += size2
+ continue
+ }
+
+ newOutput.WriteRune(c2)
+
+ // End of tag found
+ if c2 == '>' {
+ idx++ // consume ">"
+ break
+ }
+
+ if !params {
+ if c2 == ' ' {
+ params = true
+ } else {
+ tag += string(c2)
+ }
+ }
+
+ idx += size2
+ }
+
+ // Add tag to stack
+ tagStack = append(tagStack, tag)
+ }
+ }
+ } else {
+ idx = fn(c, s, idx)
+ }
+ }
+
+ finalize()
+
+ for i := len(tagStack) - 1; i >= 0; i-- {
+ tag := tagStack[i]
+ // Close everything from the regular tag stack
+ newOutput.WriteString(fmt.Sprintf("%s>", tag))
+ }
+}
+
+func filterTruncatechars(in *Value, param *Value) (*Value, *Error) {
+ s := in.String()
+ newLen := param.Integer()
+ return AsValue(filterTruncatecharsHelper(s, newLen)), nil
+}
+
+func filterTruncatecharsHTML(in *Value, param *Value) (*Value, *Error) {
+ value := in.String()
+ newLen := max(param.Integer()-3, 0)
+
+ newOutput := bytes.NewBuffer(nil)
+
+ textcounter := 0
+
+ filterTruncateHTMLHelper(value, newOutput, func() bool {
+ return textcounter >= newLen
+ }, func(c rune, s int, idx int) int {
+ textcounter++
+ newOutput.WriteRune(c)
+
+ return idx + s
+ }, func() {
+ if textcounter >= newLen && textcounter < len(value) {
+ newOutput.WriteString("...")
+ }
+ })
+
+ return AsSafeValue(newOutput.String()), nil
+}
+
+func filterTruncatewords(in *Value, param *Value) (*Value, *Error) {
+ words := strings.Fields(in.String())
+ n := param.Integer()
+ if n <= 0 {
+ return AsValue(""), nil
+ }
+ nlen := min(len(words), n)
+ out := make([]string, 0, nlen)
+ for i := 0; i < nlen; i++ {
+ out = append(out, words[i])
+ }
+
+ if n < len(words) {
+ out = append(out, "...")
+ }
+
+ return AsValue(strings.Join(out, " ")), nil
+}
+
+func filterTruncatewordsHTML(in *Value, param *Value) (*Value, *Error) {
+ value := in.String()
+ newLen := max(param.Integer(), 0)
+
+ newOutput := bytes.NewBuffer(nil)
+
+ wordcounter := 0
+
+ filterTruncateHTMLHelper(value, newOutput, func() bool {
+ return wordcounter >= newLen
+ }, func(_ rune, _ int, idx int) int {
+ // Get next word
+ wordFound := false
+
+ for idx < len(value) {
+ c2, size2 := utf8.DecodeRuneInString(value[idx:])
+ if c2 == utf8.RuneError {
+ idx += size2
+ continue
+ }
+
+ if c2 == '<' {
+ // HTML tag start, don't consume it
+ return idx
+ }
+
+ newOutput.WriteRune(c2)
+ idx += size2
+
+ if c2 == ' ' || c2 == '.' || c2 == ',' || c2 == ';' {
+ // Word ends here, stop capturing it now
+ break
+ } else {
+ wordFound = true
+ }
+ }
+
+ if wordFound {
+ wordcounter++
+ }
+
+ return idx
+ }, func() {
+ if wordcounter >= newLen {
+ newOutput.WriteString("...")
+ }
+ })
+
+ return AsSafeValue(newOutput.String()), nil
+}
+
+func filterEscape(in *Value, param *Value) (*Value, *Error) {
+ output := strings.Replace(in.String(), "&", "&", -1)
+ output = strings.Replace(output, ">", ">", -1)
+ output = strings.Replace(output, "<", "<", -1)
+ output = strings.Replace(output, "\"", """, -1)
+ output = strings.Replace(output, "'", "'", -1)
+ return AsValue(output), nil
+}
+
+func filterSafe(in *Value, param *Value) (*Value, *Error) {
+ return in, nil // nothing to do here, just to keep track of the safe application
+}
+
+func filterEscapejs(in *Value, param *Value) (*Value, *Error) {
+ sin := in.String()
+
+ var b bytes.Buffer
+
+ idx := 0
+ for idx < len(sin) {
+ c, size := utf8.DecodeRuneInString(sin[idx:])
+ if c == utf8.RuneError {
+ idx += size
+ continue
+ }
+
+ if c == '\\' {
+ // Escape seq?
+ if idx+1 < len(sin) {
+ switch sin[idx+1] {
+ case 'r':
+ b.WriteString(fmt.Sprintf(`\u%04X`, '\r'))
+ idx += 2
+ continue
+ case 'n':
+ b.WriteString(fmt.Sprintf(`\u%04X`, '\n'))
+ idx += 2
+ continue
+ /*case '\'':
+ b.WriteString(fmt.Sprintf(`\u%04X`, '\''))
+ idx += 2
+ continue
+ case '"':
+ b.WriteString(fmt.Sprintf(`\u%04X`, '"'))
+ idx += 2
+ continue*/
+ }
+ }
+ }
+
+ if (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == ' ' || c == '/' {
+ b.WriteRune(c)
+ } else {
+ b.WriteString(fmt.Sprintf(`\u%04X`, c))
+ }
+
+ idx += size
+ }
+
+ return AsValue(b.String()), nil
+}
+
+func filterAdd(in *Value, param *Value) (*Value, *Error) {
+ if in.IsNumber() && param.IsNumber() {
+ if in.IsFloat() || param.IsFloat() {
+ return AsValue(in.Float() + param.Float()), nil
+ }
+ return AsValue(in.Integer() + param.Integer()), nil
+ }
+ // If in/param is not a number, we're relying on the
+ // Value's String() conversion and just add them both together
+ return AsValue(in.String() + param.String()), nil
+}
+
+func filterAddslashes(in *Value, param *Value) (*Value, *Error) {
+ output := strings.Replace(in.String(), "\\", "\\\\", -1)
+ output = strings.Replace(output, "\"", "\\\"", -1)
+ output = strings.Replace(output, "'", "\\'", -1)
+ return AsValue(output), nil
+}
+
+func filterCut(in *Value, param *Value) (*Value, *Error) {
+ return AsValue(strings.Replace(in.String(), param.String(), "", -1)), nil
+}
+
+func filterLength(in *Value, param *Value) (*Value, *Error) {
+ return AsValue(in.Len()), nil
+}
+
+func filterLengthis(in *Value, param *Value) (*Value, *Error) {
+ return AsValue(in.Len() == param.Integer()), nil
+}
+
+func filterDefault(in *Value, param *Value) (*Value, *Error) {
+ if !in.IsTrue() {
+ return param, nil
+ }
+ return in, nil
+}
+
+func filterDefaultIfNone(in *Value, param *Value) (*Value, *Error) {
+ if in.IsNil() {
+ return param, nil
+ }
+ return in, nil
+}
+
+func filterDivisibleby(in *Value, param *Value) (*Value, *Error) {
+ if param.Integer() == 0 {
+ return AsValue(false), nil
+ }
+ return AsValue(in.Integer()%param.Integer() == 0), nil
+}
+
+func filterFirst(in *Value, param *Value) (*Value, *Error) {
+ if in.CanSlice() && in.Len() > 0 {
+ return in.Index(0), nil
+ }
+ return AsValue(""), nil
+}
+
+func filterFloatformat(in *Value, param *Value) (*Value, *Error) {
+ val := in.Float()
+
+ decimals := -1
+ if !param.IsNil() {
+ // Any argument provided?
+ decimals = param.Integer()
+ }
+
+ // if the argument is not a number (e. g. empty), the default
+ // behaviour is trim the result
+ trim := !param.IsNumber()
+
+ if decimals <= 0 {
+ // argument is negative or zero, so we
+ // want the output being trimmed
+ decimals = -decimals
+ trim = true
+ }
+
+ if trim {
+ // Remove zeroes
+ if float64(int(val)) == val {
+ return AsValue(in.Integer()), nil
+ }
+ }
+
+ return AsValue(strconv.FormatFloat(val, 'f', decimals, 64)), nil
+}
+
+func filterGetdigit(in *Value, param *Value) (*Value, *Error) {
+ i := param.Integer()
+ l := len(in.String()) // do NOT use in.Len() here!
+ if i <= 0 || i > l {
+ return in, nil
+ }
+ return AsValue(in.String()[l-i] - 48), nil
+}
+
+const filterIRIChars = "/#%[]=:;$&()+,!?*@'~"
+
+func filterIriencode(in *Value, param *Value) (*Value, *Error) {
+ var b bytes.Buffer
+
+ sin := in.String()
+ for _, r := range sin {
+ if strings.IndexRune(filterIRIChars, r) >= 0 {
+ b.WriteRune(r)
+ } else {
+ b.WriteString(url.QueryEscape(string(r)))
+ }
+ }
+
+ return AsValue(b.String()), nil
+}
+
+func filterJoin(in *Value, param *Value) (*Value, *Error) {
+ if !in.CanSlice() {
+ return in, nil
+ }
+ sep := param.String()
+ sl := make([]string, 0, in.Len())
+ for i := 0; i < in.Len(); i++ {
+ sl = append(sl, in.Index(i).String())
+ }
+ return AsValue(strings.Join(sl, sep)), nil
+}
+
+func filterLast(in *Value, param *Value) (*Value, *Error) {
+ if in.CanSlice() && in.Len() > 0 {
+ return in.Index(in.Len() - 1), nil
+ }
+ return AsValue(""), nil
+}
+
+func filterUpper(in *Value, param *Value) (*Value, *Error) {
+ return AsValue(strings.ToUpper(in.String())), nil
+}
+
+func filterLower(in *Value, param *Value) (*Value, *Error) {
+ return AsValue(strings.ToLower(in.String())), nil
+}
+
+func filterMakelist(in *Value, param *Value) (*Value, *Error) {
+ s := in.String()
+ result := make([]string, 0, len(s))
+ for _, c := range s {
+ result = append(result, string(c))
+ }
+ return AsValue(result), nil
+}
+
+func filterCapfirst(in *Value, param *Value) (*Value, *Error) {
+ if in.Len() <= 0 {
+ return AsValue(""), nil
+ }
+ t := in.String()
+ r, size := utf8.DecodeRuneInString(t)
+ return AsValue(strings.ToUpper(string(r)) + t[size:]), nil
+}
+
+func filterCenter(in *Value, param *Value) (*Value, *Error) {
+ width := param.Integer()
+ slen := in.Len()
+ if width <= slen {
+ return in, nil
+ }
+
+ spaces := width - slen
+ left := spaces/2 + spaces%2
+ right := spaces / 2
+
+ return AsValue(fmt.Sprintf("%s%s%s", strings.Repeat(" ", left),
+ in.String(), strings.Repeat(" ", right))), nil
+}
+
+func filterDate(in *Value, param *Value) (*Value, *Error) {
+ t, isTime := in.Interface().(time.Time)
+ if !isTime {
+ return nil, &Error{
+ Sender: "filter:date",
+ OrigError: errors.New("filter input argument must be of type 'time.Time'"),
+ }
+ }
+ return AsValue(t.Format(param.String())), nil
+}
+
+func filterFloat(in *Value, param *Value) (*Value, *Error) {
+ return AsValue(in.Float()), nil
+}
+
+func filterInteger(in *Value, param *Value) (*Value, *Error) {
+ return AsValue(in.Integer()), nil
+}
+
+func filterLinebreaks(in *Value, param *Value) (*Value, *Error) {
+ if in.Len() == 0 {
+ return in, nil
+ }
+
+ var b bytes.Buffer
+
+ // Newline =
+ // Double newline =
...
+ lines := strings.Split(in.String(), "\n")
+ lenlines := len(lines)
+
+ opened := false
+
+ for idx, line := range lines {
+
+ if !opened {
+ b.WriteString("
")
+ opened = true
+ }
+
+ b.WriteString(line)
+
+ if idx < lenlines-1 && strings.TrimSpace(lines[idx]) != "" {
+ // We've not reached the end
+ if strings.TrimSpace(lines[idx+1]) == "" {
+ // Next line is empty
+ if opened {
+ b.WriteString("
")
+ opened = false
+ }
+ } else {
+ b.WriteString(" ")
+ }
+ }
+ }
+
+ if opened {
+ b.WriteString("
")
+ }
+
+ return AsValue(b.String()), nil
+}
+
+func filterSplit(in *Value, param *Value) (*Value, *Error) {
+ chunks := strings.Split(in.String(), param.String())
+
+ return AsValue(chunks), nil
+}
+
+func filterLinebreaksbr(in *Value, param *Value) (*Value, *Error) {
+ return AsValue(strings.Replace(in.String(), "\n", " ", -1)), nil
+}
+
+func filterLinenumbers(in *Value, param *Value) (*Value, *Error) {
+ lines := strings.Split(in.String(), "\n")
+ output := make([]string, 0, len(lines))
+ for idx, line := range lines {
+ output = append(output, fmt.Sprintf("%d. %s", idx+1, line))
+ }
+ return AsValue(strings.Join(output, "\n")), nil
+}
+
+func filterLjust(in *Value, param *Value) (*Value, *Error) {
+ times := param.Integer() - in.Len()
+ if times < 0 {
+ times = 0
+ }
+ return AsValue(fmt.Sprintf("%s%s", in.String(), strings.Repeat(" ", times))), nil
+}
+
+func filterUrlencode(in *Value, param *Value) (*Value, *Error) {
+ return AsValue(url.QueryEscape(in.String())), nil
+}
+
+// TODO: This regexp could do some work
+var filterUrlizeURLRegexp = regexp.MustCompile(`((((http|https)://)|www\.|((^|[ ])[0-9A-Za-z_\-]+(\.com|\.net|\.org|\.info|\.biz|\.de))))(?U:.*)([ ]+|$)`)
+var filterUrlizeEmailRegexp = regexp.MustCompile(`(\w+@\w+\.\w{2,4})`)
+
+func filterUrlizeHelper(input string, autoescape bool, trunc int) (string, error) {
+ var soutErr error
+ sout := filterUrlizeURLRegexp.ReplaceAllStringFunc(input, func(raw_url string) string {
+ var prefix string
+ var suffix string
+ if strings.HasPrefix(raw_url, " ") {
+ prefix = " "
+ }
+ if strings.HasSuffix(raw_url, " ") {
+ suffix = " "
+ }
+
+ raw_url = strings.TrimSpace(raw_url)
+
+ t, err := ApplyFilter("iriencode", AsValue(raw_url), nil)
+ if err != nil {
+ soutErr = err
+ return ""
+ }
+ url := t.String()
+
+ if !strings.HasPrefix(url, "http") {
+ url = fmt.Sprintf("http://%s", url)
+ }
+
+ title := raw_url
+
+ if trunc > 3 && len(title) > trunc {
+ title = fmt.Sprintf("%s...", title[:trunc-3])
+ }
+
+ if autoescape {
+ t, err := ApplyFilter("escape", AsValue(title), nil)
+ if err != nil {
+ soutErr = err
+ return ""
+ }
+ title = t.String()
+ }
+
+ return fmt.Sprintf(`%s%s%s`, prefix, url, title, suffix)
+ })
+ if soutErr != nil {
+ return "", soutErr
+ }
+
+ sout = filterUrlizeEmailRegexp.ReplaceAllStringFunc(sout, func(mail string) string {
+ title := mail
+
+ if trunc > 3 && len(title) > trunc {
+ title = fmt.Sprintf("%s...", title[:trunc-3])
+ }
+
+ return fmt.Sprintf(`%s`, mail, title)
+ })
+
+ return sout, nil
+}
+
+func filterUrlize(in *Value, param *Value) (*Value, *Error) {
+ autoescape := true
+ if param.IsBool() {
+ autoescape = param.Bool()
+ }
+
+ s, err := filterUrlizeHelper(in.String(), autoescape, -1)
+ if err != nil {
+
+ }
+
+ return AsValue(s), nil
+}
+
+func filterUrlizetrunc(in *Value, param *Value) (*Value, *Error) {
+ s, err := filterUrlizeHelper(in.String(), true, param.Integer())
+ if err != nil {
+ return nil, &Error{
+ Sender: "filter:urlizetrunc",
+ OrigError: errors.New("you cannot pass more than 2 arguments to filter 'pluralize'"),
+ }
+ }
+ return AsValue(s), nil
+}
+
+func filterStringformat(in *Value, param *Value) (*Value, *Error) {
+ return AsValue(fmt.Sprintf(param.String(), in.Interface())), nil
+}
+
+var reStriptags = regexp.MustCompile("<[^>]*?>")
+
+func filterStriptags(in *Value, param *Value) (*Value, *Error) {
+ s := in.String()
+
+ // Strip all tags
+ s = reStriptags.ReplaceAllString(s, "")
+
+ return AsValue(strings.TrimSpace(s)), nil
+}
+
+// https://en.wikipedia.org/wiki/Phoneword
+var filterPhone2numericMap = map[string]string{
+ "a": "2", "b": "2", "c": "2", "d": "3", "e": "3", "f": "3", "g": "4", "h": "4", "i": "4", "j": "5", "k": "5",
+ "l": "5", "m": "6", "n": "6", "o": "6", "p": "7", "q": "7", "r": "7", "s": "7", "t": "8", "u": "8", "v": "8",
+ "w": "9", "x": "9", "y": "9", "z": "9",
+}
+
+func filterPhone2numeric(in *Value, param *Value) (*Value, *Error) {
+ sin := in.String()
+ for k, v := range filterPhone2numericMap {
+ sin = strings.Replace(sin, k, v, -1)
+ sin = strings.Replace(sin, strings.ToUpper(k), v, -1)
+ }
+ return AsValue(sin), nil
+}
+
+func filterPluralize(in *Value, param *Value) (*Value, *Error) {
+ if in.IsNumber() {
+ // Works only on numbers
+ if param.Len() > 0 {
+ endings := strings.Split(param.String(), ",")
+ if len(endings) > 2 {
+ return nil, &Error{
+ Sender: "filter:pluralize",
+ OrigError: errors.New("you cannot pass more than 2 arguments to filter 'pluralize'"),
+ }
+ }
+ if len(endings) == 1 {
+ // 1 argument
+ if in.Integer() != 1 {
+ return AsValue(endings[0]), nil
+ }
+ } else {
+ if in.Integer() != 1 {
+ // 2 arguments
+ return AsValue(endings[1]), nil
+ }
+ return AsValue(endings[0]), nil
+ }
+ } else {
+ if in.Integer() != 1 {
+ // return default 's'
+ return AsValue("s"), nil
+ }
+ }
+
+ return AsValue(""), nil
+ }
+ return nil, &Error{
+ Sender: "filter:pluralize",
+ OrigError: errors.New("filter 'pluralize' does only work on numbers"),
+ }
+}
+
+func filterRandom(in *Value, param *Value) (*Value, *Error) {
+ if !in.CanSlice() || in.Len() <= 0 {
+ return in, nil
+ }
+ i := rand.Intn(in.Len())
+ return in.Index(i), nil
+}
+
+func filterRemovetags(in *Value, param *Value) (*Value, *Error) {
+ s := in.String()
+ tags := strings.Split(param.String(), ",")
+
+ // Strip only specific tags
+ for _, tag := range tags {
+ re := regexp.MustCompile(fmt.Sprintf("?%s/?>", tag))
+ s = re.ReplaceAllString(s, "")
+ }
+
+ return AsValue(strings.TrimSpace(s)), nil
+}
+
+func filterRjust(in *Value, param *Value) (*Value, *Error) {
+ return AsValue(fmt.Sprintf(fmt.Sprintf("%%%ds", param.Integer()), in.String())), nil
+}
+
+func filterSlice(in *Value, param *Value) (*Value, *Error) {
+ comp := strings.Split(param.String(), ":")
+ if len(comp) != 2 {
+ return nil, &Error{
+ Sender: "filter:slice",
+ OrigError: errors.New("Slice string must have the format 'from:to' [from/to can be omitted, but the ':' is required]"),
+ }
+ }
+
+ if !in.CanSlice() {
+ return in, nil
+ }
+
+ from := AsValue(comp[0]).Integer()
+ to := in.Len()
+
+ if from > to {
+ from = to
+ }
+
+ vto := AsValue(comp[1]).Integer()
+ if vto >= from && vto <= in.Len() {
+ to = vto
+ }
+
+ return in.Slice(from, to), nil
+}
+
+func filterTitle(in *Value, param *Value) (*Value, *Error) {
+ if !in.IsString() {
+ return AsValue(""), nil
+ }
+ return AsValue(strings.Title(strings.ToLower(in.String()))), nil
+}
+
+func filterWordcount(in *Value, param *Value) (*Value, *Error) {
+ return AsValue(len(strings.Fields(in.String()))), nil
+}
+
+func filterWordwrap(in *Value, param *Value) (*Value, *Error) {
+ words := strings.Fields(in.String())
+ wordsLen := len(words)
+ wrapAt := param.Integer()
+ if wrapAt <= 0 {
+ return in, nil
+ }
+
+ linecount := wordsLen/wrapAt + wordsLen%wrapAt
+ lines := make([]string, 0, linecount)
+ for i := 0; i < linecount; i++ {
+ lines = append(lines, strings.Join(words[wrapAt*i:min(wrapAt*(i+1), wordsLen)], " "))
+ }
+ return AsValue(strings.Join(lines, "\n")), nil
+}
+
+func filterYesno(in *Value, param *Value) (*Value, *Error) {
+ choices := map[int]string{
+ 0: "yes",
+ 1: "no",
+ 2: "maybe",
+ }
+ paramString := param.String()
+ customChoices := strings.Split(paramString, ",")
+ if len(paramString) > 0 {
+ if len(customChoices) > 3 {
+ return nil, &Error{
+ Sender: "filter:yesno",
+ OrigError: fmt.Errorf("You cannot pass more than 3 options to the 'yesno'-filter (got: '%s').", paramString),
+ }
+ }
+ if len(customChoices) < 2 {
+ return nil, &Error{
+ Sender: "filter:yesno",
+ OrigError: fmt.Errorf("You must pass either no or at least 2 arguments to the 'yesno'-filter (got: '%s').", paramString),
+ }
+ }
+
+ // Map to the options now
+ choices[0] = customChoices[0]
+ choices[1] = customChoices[1]
+ if len(customChoices) == 3 {
+ choices[2] = customChoices[2]
+ }
+ }
+
+ // maybe
+ if in.IsNil() {
+ return AsValue(choices[2]), nil
+ }
+
+ // yes
+ if in.IsTrue() {
+ return AsValue(choices[0]), nil
+ }
+
+ // no
+ return AsValue(choices[1]), nil
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/helpers.go b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/helpers.go
new file mode 100644
index 000000000000..880dbc044435
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/helpers.go
@@ -0,0 +1,15 @@
+package pongo2
+
+func max(a, b int) int {
+ if a > b {
+ return a
+ }
+ return b
+}
+
+func min(a, b int) int {
+ if a < b {
+ return a
+ }
+ return b
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/lexer.go b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/lexer.go
new file mode 100644
index 000000000000..f1897984a98b
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/lexer.go
@@ -0,0 +1,432 @@
+package pongo2
+
+import (
+ "fmt"
+ "strings"
+ "unicode/utf8"
+
+ "errors"
+)
+
+const (
+ TokenError = iota
+ EOF
+
+ TokenHTML
+
+ TokenKeyword
+ TokenIdentifier
+ TokenString
+ TokenNumber
+ TokenSymbol
+)
+
+var (
+ tokenSpaceChars = " \n\r\t"
+ tokenIdentifierChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_"
+ tokenIdentifierCharsWithDigits = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789"
+ tokenDigits = "0123456789"
+
+ // Available symbols in pongo2 (within filters/tag)
+ TokenSymbols = []string{
+ // 3-Char symbols
+ "{{-", "-}}", "{%-", "-%}",
+
+ // 2-Char symbols
+ "==", ">=", "<=", "&&", "||", "{{", "}}", "{%", "%}", "!=", "<>",
+
+ // 1-Char symbol
+ "(", ")", "+", "-", "*", "<", ">", "/", "^", ",", ".", "!", "|", ":", "=", "%",
+ }
+
+ // Available keywords in pongo2
+ TokenKeywords = []string{"in", "and", "or", "not", "true", "false", "as", "export"}
+)
+
+type TokenType int
+type Token struct {
+ Filename string
+ Typ TokenType
+ Val string
+ Line int
+ Col int
+ TrimWhitespaces bool
+}
+
+type lexerStateFn func() lexerStateFn
+type lexer struct {
+ name string
+ input string
+ start int // start pos of the item
+ pos int // current pos
+ width int // width of last rune
+ tokens []*Token
+ errored bool
+ startline int
+ startcol int
+ line int
+ col int
+
+ inVerbatim bool
+ verbatimName string
+}
+
+func (t *Token) String() string {
+ val := t.Val
+ if len(val) > 1000 {
+ val = fmt.Sprintf("%s...%s", val[:10], val[len(val)-5:])
+ }
+
+ typ := ""
+ switch t.Typ {
+ case TokenHTML:
+ typ = "HTML"
+ case TokenError:
+ typ = "Error"
+ case TokenIdentifier:
+ typ = "Identifier"
+ case TokenKeyword:
+ typ = "Keyword"
+ case TokenNumber:
+ typ = "Number"
+ case TokenString:
+ typ = "String"
+ case TokenSymbol:
+ typ = "Symbol"
+ default:
+ typ = "Unknown"
+ }
+
+ return fmt.Sprintf("",
+ typ, t.Typ, val, t.Line, t.Col, t.TrimWhitespaces)
+}
+
+func lex(name string, input string) ([]*Token, *Error) {
+ l := &lexer{
+ name: name,
+ input: input,
+ tokens: make([]*Token, 0, 100),
+ line: 1,
+ col: 1,
+ startline: 1,
+ startcol: 1,
+ }
+ l.run()
+ if l.errored {
+ errtoken := l.tokens[len(l.tokens)-1]
+ return nil, &Error{
+ Filename: name,
+ Line: errtoken.Line,
+ Column: errtoken.Col,
+ Sender: "lexer",
+ OrigError: errors.New(errtoken.Val),
+ }
+ }
+ return l.tokens, nil
+}
+
+func (l *lexer) value() string {
+ return l.input[l.start:l.pos]
+}
+
+func (l *lexer) length() int {
+ return l.pos - l.start
+}
+
+func (l *lexer) emit(t TokenType) {
+ tok := &Token{
+ Filename: l.name,
+ Typ: t,
+ Val: l.value(),
+ Line: l.startline,
+ Col: l.startcol,
+ }
+
+ if t == TokenString {
+ // Escape sequence \" in strings
+ tok.Val = strings.Replace(tok.Val, `\"`, `"`, -1)
+ tok.Val = strings.Replace(tok.Val, `\\`, `\`, -1)
+ }
+
+ if t == TokenSymbol && len(tok.Val) == 3 && (strings.HasSuffix(tok.Val, "-") || strings.HasPrefix(tok.Val, "-")) {
+ tok.TrimWhitespaces = true
+ tok.Val = strings.Replace(tok.Val, "-", "", -1)
+ }
+
+ l.tokens = append(l.tokens, tok)
+ l.start = l.pos
+ l.startline = l.line
+ l.startcol = l.col
+}
+
+func (l *lexer) next() rune {
+ if l.pos >= len(l.input) {
+ l.width = 0
+ return EOF
+ }
+ r, w := utf8.DecodeRuneInString(l.input[l.pos:])
+ l.width = w
+ l.pos += l.width
+ l.col += l.width
+ return r
+}
+
+func (l *lexer) backup() {
+ l.pos -= l.width
+ l.col -= l.width
+}
+
+func (l *lexer) peek() rune {
+ r := l.next()
+ l.backup()
+ return r
+}
+
+func (l *lexer) ignore() {
+ l.start = l.pos
+ l.startline = l.line
+ l.startcol = l.col
+}
+
+func (l *lexer) accept(what string) bool {
+ if strings.IndexRune(what, l.next()) >= 0 {
+ return true
+ }
+ l.backup()
+ return false
+}
+
+func (l *lexer) acceptRun(what string) {
+ for strings.IndexRune(what, l.next()) >= 0 {
+ }
+ l.backup()
+}
+
+func (l *lexer) errorf(format string, args ...interface{}) lexerStateFn {
+ t := &Token{
+ Filename: l.name,
+ Typ: TokenError,
+ Val: fmt.Sprintf(format, args...),
+ Line: l.startline,
+ Col: l.startcol,
+ }
+ l.tokens = append(l.tokens, t)
+ l.errored = true
+ l.startline = l.line
+ l.startcol = l.col
+ return nil
+}
+
+func (l *lexer) eof() bool {
+ return l.start >= len(l.input)-1
+}
+
+func (l *lexer) run() {
+ for {
+ // TODO: Support verbatim tag names
+ // https://docs.djangoproject.com/en/dev/ref/templates/builtins/#verbatim
+ if l.inVerbatim {
+ name := l.verbatimName
+ if name != "" {
+ name += " "
+ }
+ if strings.HasPrefix(l.input[l.pos:], fmt.Sprintf("{%% endverbatim %s%%}", name)) { // end verbatim
+ if l.pos > l.start {
+ l.emit(TokenHTML)
+ }
+ w := len("{% endverbatim %}")
+ l.pos += w
+ l.col += w
+ l.ignore()
+ l.inVerbatim = false
+ }
+ } else if strings.HasPrefix(l.input[l.pos:], "{% verbatim %}") { // tag
+ if l.pos > l.start {
+ l.emit(TokenHTML)
+ }
+ l.inVerbatim = true
+ w := len("{% verbatim %}")
+ l.pos += w
+ l.col += w
+ l.ignore()
+ }
+
+ if !l.inVerbatim {
+ // Ignore single-line comments {# ... #}
+ if strings.HasPrefix(l.input[l.pos:], "{#") {
+ if l.pos > l.start {
+ l.emit(TokenHTML)
+ }
+
+ l.pos += 2 // pass '{#'
+ l.col += 2
+
+ for {
+ switch l.peek() {
+ case EOF:
+ l.errorf("Single-line comment not closed.")
+ return
+ case '\n':
+ l.errorf("Newline not permitted in a single-line comment.")
+ return
+ }
+
+ if strings.HasPrefix(l.input[l.pos:], "#}") {
+ l.pos += 2 // pass '#}'
+ l.col += 2
+ break
+ }
+
+ l.next()
+ }
+ l.ignore() // ignore whole comment
+
+ // Comment skipped
+ continue // next token
+ }
+
+ if strings.HasPrefix(l.input[l.pos:], "{{") || // variable
+ strings.HasPrefix(l.input[l.pos:], "{%") { // tag
+ if l.pos > l.start {
+ l.emit(TokenHTML)
+ }
+ l.tokenize()
+ if l.errored {
+ return
+ }
+ continue
+ }
+ }
+
+ switch l.peek() {
+ case '\n':
+ l.line++
+ l.col = 0
+ }
+ if l.next() == EOF {
+ break
+ }
+ }
+
+ if l.pos > l.start {
+ l.emit(TokenHTML)
+ }
+
+ if l.inVerbatim {
+ l.errorf("verbatim-tag not closed, got EOF.")
+ }
+}
+
+func (l *lexer) tokenize() {
+ for state := l.stateCode; state != nil; {
+ state = state()
+ }
+}
+
+func (l *lexer) stateCode() lexerStateFn {
+outer_loop:
+ for {
+ switch {
+ case l.accept(tokenSpaceChars):
+ if l.value() == "\n" {
+ return l.errorf("Newline not allowed within tag/variable.")
+ }
+ l.ignore()
+ continue
+ case l.accept(tokenIdentifierChars):
+ return l.stateIdentifier
+ case l.accept(tokenDigits):
+ return l.stateNumber
+ case l.accept(`"'`):
+ return l.stateString
+ }
+
+ // Check for symbol
+ for _, sym := range TokenSymbols {
+ if strings.HasPrefix(l.input[l.start:], sym) {
+ l.pos += len(sym)
+ l.col += l.length()
+ l.emit(TokenSymbol)
+
+ if sym == "%}" || sym == "-%}" || sym == "}}" || sym == "-}}" {
+ // Tag/variable end, return after emit
+ return nil
+ }
+
+ continue outer_loop
+ }
+ }
+
+ break
+ }
+
+ // Normal shut down
+ return nil
+}
+
+func (l *lexer) stateIdentifier() lexerStateFn {
+ l.acceptRun(tokenIdentifierChars)
+ l.acceptRun(tokenIdentifierCharsWithDigits)
+ for _, kw := range TokenKeywords {
+ if kw == l.value() {
+ l.emit(TokenKeyword)
+ return l.stateCode
+ }
+ }
+ l.emit(TokenIdentifier)
+ return l.stateCode
+}
+
+func (l *lexer) stateNumber() lexerStateFn {
+ l.acceptRun(tokenDigits)
+ if l.accept(tokenIdentifierCharsWithDigits) {
+ // This seems to be an identifier starting with a number.
+ // See https://github.com/flosch/pongo2/issues/151
+ return l.stateIdentifier()
+ }
+ /*
+ Maybe context-sensitive number lexing?
+ * comments.0.Text // first comment
+ * usercomments.1.0 // second user, first comment
+ * if (score >= 8.5) // 8.5 as a number
+
+ if l.peek() == '.' {
+ l.accept(".")
+ if !l.accept(tokenDigits) {
+ return l.errorf("Malformed number.")
+ }
+ l.acceptRun(tokenDigits)
+ }
+ */
+ l.emit(TokenNumber)
+ return l.stateCode
+}
+
+func (l *lexer) stateString() lexerStateFn {
+ quotationMark := l.value()
+ l.ignore()
+ l.startcol-- // we're starting the position at the first "
+ for !l.accept(quotationMark) {
+ switch l.next() {
+ case '\\':
+ // escape sequence
+ switch l.peek() {
+ case '"', '\\':
+ l.next()
+ default:
+ return l.errorf("Unknown escape sequence: \\%c", l.peek())
+ }
+ case EOF:
+ return l.errorf("Unexpected EOF, string not closed.")
+ case '\n':
+ return l.errorf("Newline in string is not allowed.")
+ }
+ }
+ l.backup()
+ l.emit(TokenString)
+
+ l.next()
+ l.ignore()
+
+ return l.stateCode
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/nodes.go b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/nodes.go
new file mode 100644
index 000000000000..5b039cdf40ca
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/nodes.go
@@ -0,0 +1,16 @@
+package pongo2
+
+// The root document
+type nodeDocument struct {
+ Nodes []INode
+}
+
+func (doc *nodeDocument) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error {
+ for _, n := range doc.Nodes {
+ err := n.Execute(ctx, writer)
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/nodes_html.go b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/nodes_html.go
new file mode 100644
index 000000000000..b980a3a5cf97
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/nodes_html.go
@@ -0,0 +1,23 @@
+package pongo2
+
+import (
+ "strings"
+)
+
+type nodeHTML struct {
+ token *Token
+ trimLeft bool
+ trimRight bool
+}
+
+func (n *nodeHTML) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error {
+ res := n.token.Val
+ if n.trimLeft {
+ res = strings.TrimLeft(res, tokenSpaceChars)
+ }
+ if n.trimRight {
+ res = strings.TrimRight(res, tokenSpaceChars)
+ }
+ writer.WriteString(res)
+ return nil
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/nodes_wrapper.go b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/nodes_wrapper.go
new file mode 100644
index 000000000000..d1bcb8d851ff
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/nodes_wrapper.go
@@ -0,0 +1,16 @@
+package pongo2
+
+type NodeWrapper struct {
+ Endtag string
+ nodes []INode
+}
+
+func (wrapper *NodeWrapper) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error {
+ for _, n := range wrapper.nodes {
+ err := n.Execute(ctx, writer)
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/options.go b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/options.go
new file mode 100644
index 000000000000..9c39e467ef66
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/options.go
@@ -0,0 +1,26 @@
+package pongo2
+
+// Options allow you to change the behavior of template-engine.
+// You can change the options before calling the Execute method.
+type Options struct {
+ // If this is set to true the first newline after a block is removed (block, not variable tag!). Defaults to false.
+ TrimBlocks bool
+
+ // If this is set to true leading spaces and tabs are stripped from the start of a line to a block. Defaults to false
+ LStripBlocks bool
+}
+
+func newOptions() *Options {
+ return &Options{
+ TrimBlocks: false,
+ LStripBlocks: false,
+ }
+}
+
+// Update updates this options from another options.
+func (opt *Options) Update(other *Options) *Options {
+ opt.TrimBlocks = other.TrimBlocks
+ opt.LStripBlocks = other.LStripBlocks
+
+ return opt
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/parser.go b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/parser.go
new file mode 100644
index 000000000000..19553f1716d3
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/parser.go
@@ -0,0 +1,309 @@
+package pongo2
+
+import (
+ "fmt"
+ "strings"
+
+ "errors"
+)
+
+type INode interface {
+ Execute(*ExecutionContext, TemplateWriter) *Error
+}
+
+type IEvaluator interface {
+ INode
+ GetPositionToken() *Token
+ Evaluate(*ExecutionContext) (*Value, *Error)
+ FilterApplied(name string) bool
+}
+
+// The parser provides you a comprehensive and easy tool to
+// work with the template document and arguments provided by
+// the user for your custom tag.
+//
+// The parser works on a token list which will be provided by pongo2.
+// A token is a unit you can work with. Tokens are either of type identifier,
+// string, number, keyword, HTML or symbol.
+//
+// (See Token's documentation for more about tokens)
+type Parser struct {
+ name string
+ idx int
+ tokens []*Token
+ lastToken *Token
+
+ // if the parser parses a template document, here will be
+ // a reference to it (needed to access the template through Tags)
+ template *Template
+}
+
+// Creates a new parser to parse tokens.
+// Used inside pongo2 to parse documents and to provide an easy-to-use
+// parser for tag authors
+func newParser(name string, tokens []*Token, template *Template) *Parser {
+ p := &Parser{
+ name: name,
+ tokens: tokens,
+ template: template,
+ }
+ if len(tokens) > 0 {
+ p.lastToken = tokens[len(tokens)-1]
+ }
+ return p
+}
+
+// Consume one token. It will be gone forever.
+func (p *Parser) Consume() {
+ p.ConsumeN(1)
+}
+
+// Consume N tokens. They will be gone forever.
+func (p *Parser) ConsumeN(count int) {
+ p.idx += count
+}
+
+// Returns the current token.
+func (p *Parser) Current() *Token {
+ return p.Get(p.idx)
+}
+
+// Returns the CURRENT token if the given type matches.
+// Consumes this token on success.
+func (p *Parser) MatchType(typ TokenType) *Token {
+ if t := p.PeekType(typ); t != nil {
+ p.Consume()
+ return t
+ }
+ return nil
+}
+
+// Returns the CURRENT token if the given type AND value matches.
+// Consumes this token on success.
+func (p *Parser) Match(typ TokenType, val string) *Token {
+ if t := p.Peek(typ, val); t != nil {
+ p.Consume()
+ return t
+ }
+ return nil
+}
+
+// Returns the CURRENT token if the given type AND *one* of
+// the given values matches.
+// Consumes this token on success.
+func (p *Parser) MatchOne(typ TokenType, vals ...string) *Token {
+ for _, val := range vals {
+ if t := p.Peek(typ, val); t != nil {
+ p.Consume()
+ return t
+ }
+ }
+ return nil
+}
+
+// Returns the CURRENT token if the given type matches.
+// It DOES NOT consume the token.
+func (p *Parser) PeekType(typ TokenType) *Token {
+ return p.PeekTypeN(0, typ)
+}
+
+// Returns the CURRENT token if the given type AND value matches.
+// It DOES NOT consume the token.
+func (p *Parser) Peek(typ TokenType, val string) *Token {
+ return p.PeekN(0, typ, val)
+}
+
+// Returns the CURRENT token if the given type AND *one* of
+// the given values matches.
+// It DOES NOT consume the token.
+func (p *Parser) PeekOne(typ TokenType, vals ...string) *Token {
+ for _, v := range vals {
+ t := p.PeekN(0, typ, v)
+ if t != nil {
+ return t
+ }
+ }
+ return nil
+}
+
+// Returns the tokens[current position + shift] token if the
+// given type AND value matches for that token.
+// DOES NOT consume the token.
+func (p *Parser) PeekN(shift int, typ TokenType, val string) *Token {
+ t := p.Get(p.idx + shift)
+ if t != nil {
+ if t.Typ == typ && t.Val == val {
+ return t
+ }
+ }
+ return nil
+}
+
+// Returns the tokens[current position + shift] token if the given type matches.
+// DOES NOT consume the token for that token.
+func (p *Parser) PeekTypeN(shift int, typ TokenType) *Token {
+ t := p.Get(p.idx + shift)
+ if t != nil {
+ if t.Typ == typ {
+ return t
+ }
+ }
+ return nil
+}
+
+// Returns the UNCONSUMED token count.
+func (p *Parser) Remaining() int {
+ return len(p.tokens) - p.idx
+}
+
+// Returns the total token count.
+func (p *Parser) Count() int {
+ return len(p.tokens)
+}
+
+// Returns tokens[i] or NIL (if i >= len(tokens))
+func (p *Parser) Get(i int) *Token {
+ if i < len(p.tokens) && i >= 0 {
+ return p.tokens[i]
+ }
+ return nil
+}
+
+// Returns tokens[current-position + shift] or NIL
+// (if (current-position + i) >= len(tokens))
+func (p *Parser) GetR(shift int) *Token {
+ i := p.idx + shift
+ return p.Get(i)
+}
+
+// Error produces a nice error message and returns an error-object.
+// The 'token'-argument is optional. If provided, it will take
+// the token's position information. If not provided, it will
+// automatically use the CURRENT token's position information.
+func (p *Parser) Error(msg string, token *Token) *Error {
+ if token == nil {
+ // Set current token
+ token = p.Current()
+ if token == nil {
+ // Set to last token
+ if len(p.tokens) > 0 {
+ token = p.tokens[len(p.tokens)-1]
+ }
+ }
+ }
+ var line, col int
+ if token != nil {
+ line = token.Line
+ col = token.Col
+ }
+ return &Error{
+ Template: p.template,
+ Filename: p.name,
+ Sender: "parser",
+ Line: line,
+ Column: col,
+ Token: token,
+ OrigError: errors.New(msg),
+ }
+}
+
+// Wraps all nodes between starting tag and "{% endtag %}" and provides
+// one simple interface to execute the wrapped nodes.
+// It returns a parser to process provided arguments to the tag.
+func (p *Parser) WrapUntilTag(names ...string) (*NodeWrapper, *Parser, *Error) {
+ wrapper := &NodeWrapper{}
+
+ var tagArgs []*Token
+
+ for p.Remaining() > 0 {
+ // New tag, check whether we have to stop wrapping here
+ if p.Peek(TokenSymbol, "{%") != nil {
+ tagIdent := p.PeekTypeN(1, TokenIdentifier)
+
+ if tagIdent != nil {
+ // We've found a (!) end-tag
+
+ found := false
+ for _, n := range names {
+ if tagIdent.Val == n {
+ found = true
+ break
+ }
+ }
+
+ // We only process the tag if we've found an end tag
+ if found {
+ // Okay, endtag found.
+ p.ConsumeN(2) // '{%' tagname
+
+ for {
+ if p.Match(TokenSymbol, "%}") != nil {
+ // Okay, end the wrapping here
+ wrapper.Endtag = tagIdent.Val
+ return wrapper, newParser(p.template.name, tagArgs, p.template), nil
+ }
+ t := p.Current()
+ p.Consume()
+ if t == nil {
+ return nil, nil, p.Error("Unexpected EOF.", p.lastToken)
+ }
+ tagArgs = append(tagArgs, t)
+ }
+ }
+ }
+
+ }
+
+ // Otherwise process next element to be wrapped
+ node, err := p.parseDocElement()
+ if err != nil {
+ return nil, nil, err
+ }
+ wrapper.nodes = append(wrapper.nodes, node)
+ }
+
+ return nil, nil, p.Error(fmt.Sprintf("Unexpected EOF, expected tag %s.", strings.Join(names, " or ")),
+ p.lastToken)
+}
+
+// Skips all nodes between starting tag and "{% endtag %}"
+func (p *Parser) SkipUntilTag(names ...string) *Error {
+ for p.Remaining() > 0 {
+ // New tag, check whether we have to stop wrapping here
+ if p.Peek(TokenSymbol, "{%") != nil {
+ tagIdent := p.PeekTypeN(1, TokenIdentifier)
+
+ if tagIdent != nil {
+ // We've found a (!) end-tag
+
+ found := false
+ for _, n := range names {
+ if tagIdent.Val == n {
+ found = true
+ break
+ }
+ }
+
+ // We only process the tag if we've found an end tag
+ if found {
+ // Okay, endtag found.
+ p.ConsumeN(2) // '{%' tagname
+
+ for {
+ if p.Match(TokenSymbol, "%}") != nil {
+ // Done skipping, exit.
+ return nil
+ }
+ }
+ }
+ }
+ }
+ t := p.Current()
+ p.Consume()
+ if t == nil {
+ return p.Error("Unexpected EOF.", p.lastToken)
+ }
+ }
+
+ return p.Error(fmt.Sprintf("Unexpected EOF, expected tag %s.", strings.Join(names, " or ")), p.lastToken)
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/parser_document.go b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/parser_document.go
new file mode 100644
index 000000000000..e3ac2c8e9d89
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/parser_document.go
@@ -0,0 +1,59 @@
+package pongo2
+
+// Doc = { ( Filter | Tag | HTML ) }
+func (p *Parser) parseDocElement() (INode, *Error) {
+ t := p.Current()
+
+ switch t.Typ {
+ case TokenHTML:
+ n := &nodeHTML{token: t}
+ left := p.PeekTypeN(-1, TokenSymbol)
+ right := p.PeekTypeN(1, TokenSymbol)
+ n.trimLeft = left != nil && left.TrimWhitespaces
+ n.trimRight = right != nil && right.TrimWhitespaces
+ p.Consume() // consume HTML element
+ return n, nil
+ case TokenSymbol:
+ switch t.Val {
+ case "{{":
+ // parse variable
+ variable, err := p.parseVariableElement()
+ if err != nil {
+ return nil, err
+ }
+ return variable, nil
+ case "{%":
+ // parse tag
+ tag, err := p.parseTagElement()
+ if err != nil {
+ return nil, err
+ }
+ return tag, nil
+ }
+ }
+ return nil, p.Error("Unexpected token (only HTML/tags/filters in templates allowed)", t)
+}
+
+func (tpl *Template) parse() *Error {
+ tpl.parser = newParser(tpl.name, tpl.tokens, tpl)
+ doc, err := tpl.parser.parseDocument()
+ if err != nil {
+ return err
+ }
+ tpl.root = doc
+ return nil
+}
+
+func (p *Parser) parseDocument() (*nodeDocument, *Error) {
+ doc := &nodeDocument{}
+
+ for p.Remaining() > 0 {
+ node, err := p.parseDocElement()
+ if err != nil {
+ return nil, err
+ }
+ doc.Nodes = append(doc.Nodes, node)
+ }
+
+ return doc, nil
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/parser_expression.go b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/parser_expression.go
new file mode 100644
index 000000000000..215b0afb10a2
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/parser_expression.go
@@ -0,0 +1,517 @@
+package pongo2
+
+import (
+ "fmt"
+ "math"
+)
+
+type Expression struct {
+ // TODO: Add location token?
+ expr1 IEvaluator
+ expr2 IEvaluator
+ opToken *Token
+}
+
+type relationalExpression struct {
+ // TODO: Add location token?
+ expr1 IEvaluator
+ expr2 IEvaluator
+ opToken *Token
+}
+
+type simpleExpression struct {
+ negate bool
+ negativeSign bool
+ term1 IEvaluator
+ term2 IEvaluator
+ opToken *Token
+}
+
+type term struct {
+ // TODO: Add location token?
+ factor1 IEvaluator
+ factor2 IEvaluator
+ opToken *Token
+}
+
+type power struct {
+ // TODO: Add location token?
+ power1 IEvaluator
+ power2 IEvaluator
+}
+
+func (expr *Expression) FilterApplied(name string) bool {
+ return expr.expr1.FilterApplied(name) && (expr.expr2 == nil ||
+ (expr.expr2 != nil && expr.expr2.FilterApplied(name)))
+}
+
+func (expr *relationalExpression) FilterApplied(name string) bool {
+ return expr.expr1.FilterApplied(name) && (expr.expr2 == nil ||
+ (expr.expr2 != nil && expr.expr2.FilterApplied(name)))
+}
+
+func (expr *simpleExpression) FilterApplied(name string) bool {
+ return expr.term1.FilterApplied(name) && (expr.term2 == nil ||
+ (expr.term2 != nil && expr.term2.FilterApplied(name)))
+}
+
+func (expr *term) FilterApplied(name string) bool {
+ return expr.factor1.FilterApplied(name) && (expr.factor2 == nil ||
+ (expr.factor2 != nil && expr.factor2.FilterApplied(name)))
+}
+
+func (expr *power) FilterApplied(name string) bool {
+ return expr.power1.FilterApplied(name) && (expr.power2 == nil ||
+ (expr.power2 != nil && expr.power2.FilterApplied(name)))
+}
+
+func (expr *Expression) GetPositionToken() *Token {
+ return expr.expr1.GetPositionToken()
+}
+
+func (expr *relationalExpression) GetPositionToken() *Token {
+ return expr.expr1.GetPositionToken()
+}
+
+func (expr *simpleExpression) GetPositionToken() *Token {
+ return expr.term1.GetPositionToken()
+}
+
+func (expr *term) GetPositionToken() *Token {
+ return expr.factor1.GetPositionToken()
+}
+
+func (expr *power) GetPositionToken() *Token {
+ return expr.power1.GetPositionToken()
+}
+
+func (expr *Expression) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error {
+ value, err := expr.Evaluate(ctx)
+ if err != nil {
+ return err
+ }
+ writer.WriteString(value.String())
+ return nil
+}
+
+func (expr *relationalExpression) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error {
+ value, err := expr.Evaluate(ctx)
+ if err != nil {
+ return err
+ }
+ writer.WriteString(value.String())
+ return nil
+}
+
+func (expr *simpleExpression) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error {
+ value, err := expr.Evaluate(ctx)
+ if err != nil {
+ return err
+ }
+ writer.WriteString(value.String())
+ return nil
+}
+
+func (expr *term) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error {
+ value, err := expr.Evaluate(ctx)
+ if err != nil {
+ return err
+ }
+ writer.WriteString(value.String())
+ return nil
+}
+
+func (expr *power) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error {
+ value, err := expr.Evaluate(ctx)
+ if err != nil {
+ return err
+ }
+ writer.WriteString(value.String())
+ return nil
+}
+
+func (expr *Expression) Evaluate(ctx *ExecutionContext) (*Value, *Error) {
+ v1, err := expr.expr1.Evaluate(ctx)
+ if err != nil {
+ return nil, err
+ }
+ if expr.expr2 != nil {
+ switch expr.opToken.Val {
+ case "and", "&&":
+ if !v1.IsTrue() {
+ return AsValue(false), nil
+ } else {
+ v2, err := expr.expr2.Evaluate(ctx)
+ if err != nil {
+ return nil, err
+ }
+ return AsValue(v2.IsTrue()), nil
+ }
+ case "or", "||":
+ if v1.IsTrue() {
+ return AsValue(true), nil
+ } else {
+ v2, err := expr.expr2.Evaluate(ctx)
+ if err != nil {
+ return nil, err
+ }
+ return AsValue(v2.IsTrue()), nil
+ }
+ default:
+ return nil, ctx.Error(fmt.Sprintf("unimplemented: %s", expr.opToken.Val), expr.opToken)
+ }
+ } else {
+ return v1, nil
+ }
+}
+
+func (expr *relationalExpression) Evaluate(ctx *ExecutionContext) (*Value, *Error) {
+ v1, err := expr.expr1.Evaluate(ctx)
+ if err != nil {
+ return nil, err
+ }
+ if expr.expr2 != nil {
+ v2, err := expr.expr2.Evaluate(ctx)
+ if err != nil {
+ return nil, err
+ }
+ switch expr.opToken.Val {
+ case "<=":
+ if v1.IsFloat() || v2.IsFloat() {
+ return AsValue(v1.Float() <= v2.Float()), nil
+ }
+ if v1.IsTime() && v2.IsTime() {
+ tm1, tm2 := v1.Time(), v2.Time()
+ return AsValue(tm1.Before(tm2) || tm1.Equal(tm2)), nil
+ }
+ return AsValue(v1.Integer() <= v2.Integer()), nil
+ case ">=":
+ if v1.IsFloat() || v2.IsFloat() {
+ return AsValue(v1.Float() >= v2.Float()), nil
+ }
+ if v1.IsTime() && v2.IsTime() {
+ tm1, tm2 := v1.Time(), v2.Time()
+ return AsValue(tm1.After(tm2) || tm1.Equal(tm2)), nil
+ }
+ return AsValue(v1.Integer() >= v2.Integer()), nil
+ case "==":
+ return AsValue(v1.EqualValueTo(v2)), nil
+ case ">":
+ if v1.IsFloat() || v2.IsFloat() {
+ return AsValue(v1.Float() > v2.Float()), nil
+ }
+ if v1.IsTime() && v2.IsTime() {
+ return AsValue(v1.Time().After(v2.Time())), nil
+ }
+ return AsValue(v1.Integer() > v2.Integer()), nil
+ case "<":
+ if v1.IsFloat() || v2.IsFloat() {
+ return AsValue(v1.Float() < v2.Float()), nil
+ }
+ if v1.IsTime() && v2.IsTime() {
+ return AsValue(v1.Time().Before(v2.Time())), nil
+ }
+ return AsValue(v1.Integer() < v2.Integer()), nil
+ case "!=", "<>":
+ return AsValue(!v1.EqualValueTo(v2)), nil
+ case "in":
+ return AsValue(v2.Contains(v1)), nil
+ default:
+ return nil, ctx.Error(fmt.Sprintf("unimplemented: %s", expr.opToken.Val), expr.opToken)
+ }
+ } else {
+ return v1, nil
+ }
+}
+
+func (expr *simpleExpression) Evaluate(ctx *ExecutionContext) (*Value, *Error) {
+ t1, err := expr.term1.Evaluate(ctx)
+ if err != nil {
+ return nil, err
+ }
+ result := t1
+
+ if expr.negate {
+ result = result.Negate()
+ }
+
+ if expr.negativeSign {
+ if result.IsNumber() {
+ switch {
+ case result.IsFloat():
+ result = AsValue(-1 * result.Float())
+ case result.IsInteger():
+ result = AsValue(-1 * result.Integer())
+ default:
+ return nil, ctx.Error("Operation between a number and a non-(float/integer) is not possible", nil)
+ }
+ } else {
+ return nil, ctx.Error("Negative sign on a non-number expression", expr.GetPositionToken())
+ }
+ }
+
+ if expr.term2 != nil {
+ t2, err := expr.term2.Evaluate(ctx)
+ if err != nil {
+ return nil, err
+ }
+ switch expr.opToken.Val {
+ case "+":
+ if result.IsFloat() || t2.IsFloat() {
+ // Result will be a float
+ return AsValue(result.Float() + t2.Float()), nil
+ }
+ // Result will be an integer
+ return AsValue(result.Integer() + t2.Integer()), nil
+ case "-":
+ if result.IsFloat() || t2.IsFloat() {
+ // Result will be a float
+ return AsValue(result.Float() - t2.Float()), nil
+ }
+ // Result will be an integer
+ return AsValue(result.Integer() - t2.Integer()), nil
+ default:
+ return nil, ctx.Error("Unimplemented", expr.GetPositionToken())
+ }
+ }
+
+ return result, nil
+}
+
+func (expr *term) Evaluate(ctx *ExecutionContext) (*Value, *Error) {
+ f1, err := expr.factor1.Evaluate(ctx)
+ if err != nil {
+ return nil, err
+ }
+ if expr.factor2 != nil {
+ f2, err := expr.factor2.Evaluate(ctx)
+ if err != nil {
+ return nil, err
+ }
+ switch expr.opToken.Val {
+ case "*":
+ if f1.IsFloat() || f2.IsFloat() {
+ // Result will be float
+ return AsValue(f1.Float() * f2.Float()), nil
+ }
+ // Result will be int
+ return AsValue(f1.Integer() * f2.Integer()), nil
+ case "/":
+ if f1.IsFloat() || f2.IsFloat() {
+ // Result will be float
+ return AsValue(f1.Float() / f2.Float()), nil
+ }
+ // Result will be int
+ return AsValue(f1.Integer() / f2.Integer()), nil
+ case "%":
+ // Result will be int
+ return AsValue(f1.Integer() % f2.Integer()), nil
+ default:
+ return nil, ctx.Error("unimplemented", expr.opToken)
+ }
+ } else {
+ return f1, nil
+ }
+}
+
+func (expr *power) Evaluate(ctx *ExecutionContext) (*Value, *Error) {
+ p1, err := expr.power1.Evaluate(ctx)
+ if err != nil {
+ return nil, err
+ }
+ if expr.power2 != nil {
+ p2, err := expr.power2.Evaluate(ctx)
+ if err != nil {
+ return nil, err
+ }
+ return AsValue(math.Pow(p1.Float(), p2.Float())), nil
+ }
+ return p1, nil
+}
+
+func (p *Parser) parseFactor() (IEvaluator, *Error) {
+ if p.Match(TokenSymbol, "(") != nil {
+ expr, err := p.ParseExpression()
+ if err != nil {
+ return nil, err
+ }
+ if p.Match(TokenSymbol, ")") == nil {
+ return nil, p.Error("Closing bracket expected after expression", nil)
+ }
+ return expr, nil
+ }
+
+ return p.parseVariableOrLiteralWithFilter()
+}
+
+func (p *Parser) parsePower() (IEvaluator, *Error) {
+ pw := new(power)
+
+ power1, err := p.parseFactor()
+ if err != nil {
+ return nil, err
+ }
+ pw.power1 = power1
+
+ if p.Match(TokenSymbol, "^") != nil {
+ power2, err := p.parsePower()
+ if err != nil {
+ return nil, err
+ }
+ pw.power2 = power2
+ }
+
+ if pw.power2 == nil {
+ // Shortcut for faster evaluation
+ return pw.power1, nil
+ }
+
+ return pw, nil
+}
+
+func (p *Parser) parseTerm() (IEvaluator, *Error) {
+ returnTerm := new(term)
+
+ factor1, err := p.parsePower()
+ if err != nil {
+ return nil, err
+ }
+ returnTerm.factor1 = factor1
+
+ for p.PeekOne(TokenSymbol, "*", "/", "%") != nil {
+ if returnTerm.opToken != nil {
+ // Create new sub-term
+ returnTerm = &term{
+ factor1: returnTerm,
+ }
+ }
+
+ op := p.Current()
+ p.Consume()
+
+ factor2, err := p.parsePower()
+ if err != nil {
+ return nil, err
+ }
+
+ returnTerm.opToken = op
+ returnTerm.factor2 = factor2
+ }
+
+ if returnTerm.opToken == nil {
+ // Shortcut for faster evaluation
+ return returnTerm.factor1, nil
+ }
+
+ return returnTerm, nil
+}
+
+func (p *Parser) parseSimpleExpression() (IEvaluator, *Error) {
+ expr := new(simpleExpression)
+
+ if sign := p.MatchOne(TokenSymbol, "+", "-"); sign != nil {
+ if sign.Val == "-" {
+ expr.negativeSign = true
+ }
+ }
+
+ if p.Match(TokenSymbol, "!") != nil || p.Match(TokenKeyword, "not") != nil {
+ expr.negate = true
+ }
+
+ term1, err := p.parseTerm()
+ if err != nil {
+ return nil, err
+ }
+ expr.term1 = term1
+
+ for p.PeekOne(TokenSymbol, "+", "-") != nil {
+ if expr.opToken != nil {
+ // New sub expr
+ expr = &simpleExpression{
+ term1: expr,
+ }
+ }
+
+ op := p.Current()
+ p.Consume()
+
+ term2, err := p.parseTerm()
+ if err != nil {
+ return nil, err
+ }
+
+ expr.term2 = term2
+ expr.opToken = op
+ }
+
+ if expr.negate == false && expr.negativeSign == false && expr.term2 == nil {
+ // Shortcut for faster evaluation
+ return expr.term1, nil
+ }
+
+ return expr, nil
+}
+
+func (p *Parser) parseRelationalExpression() (IEvaluator, *Error) {
+ expr1, err := p.parseSimpleExpression()
+ if err != nil {
+ return nil, err
+ }
+
+ expr := &relationalExpression{
+ expr1: expr1,
+ }
+
+ if t := p.MatchOne(TokenSymbol, "==", "<=", ">=", "!=", "<>", ">", "<"); t != nil {
+ expr2, err := p.parseRelationalExpression()
+ if err != nil {
+ return nil, err
+ }
+ expr.opToken = t
+ expr.expr2 = expr2
+ } else if t := p.MatchOne(TokenKeyword, "in"); t != nil {
+ expr2, err := p.parseSimpleExpression()
+ if err != nil {
+ return nil, err
+ }
+ expr.opToken = t
+ expr.expr2 = expr2
+ }
+
+ if expr.expr2 == nil {
+ // Shortcut for faster evaluation
+ return expr.expr1, nil
+ }
+
+ return expr, nil
+}
+
+func (p *Parser) ParseExpression() (IEvaluator, *Error) {
+ rexpr1, err := p.parseRelationalExpression()
+ if err != nil {
+ return nil, err
+ }
+
+ exp := &Expression{
+ expr1: rexpr1,
+ }
+
+ if p.PeekOne(TokenSymbol, "&&", "||") != nil || p.PeekOne(TokenKeyword, "and", "or") != nil {
+ op := p.Current()
+ p.Consume()
+ expr2, err := p.ParseExpression()
+ if err != nil {
+ return nil, err
+ }
+ exp.expr2 = expr2
+ exp.opToken = op
+ }
+
+ if exp.expr2 == nil {
+ // Shortcut for faster evaluation
+ return exp.expr1, nil
+ }
+
+ return exp, nil
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/pongo2.go b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/pongo2.go
new file mode 100644
index 000000000000..8e222139aebd
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/pongo2.go
@@ -0,0 +1,14 @@
+package pongo2
+
+// Version string
+const Version = "4.0.2"
+
+// Must panics, if a Template couldn't successfully parsed. This is how you
+// would use it:
+// var baseTemplate = pongo2.Must(pongo2.FromFile("templates/base.html"))
+func Must(tpl *Template, err error) *Template {
+ if err != nil {
+ panic(err)
+ }
+ return tpl
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/tags.go b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/tags.go
new file mode 100644
index 000000000000..710ee2527ba6
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/tags.go
@@ -0,0 +1,133 @@
+package pongo2
+
+/* Incomplete:
+ -----------
+
+ verbatim (only the "name" argument is missing for verbatim)
+
+ Reconsideration:
+ ----------------
+
+ debug (reason: not sure what to output yet)
+ regroup / Grouping on other properties (reason: maybe too python-specific; not sure how useful this would be in Go)
+
+ Following built-in tags wont be added:
+ --------------------------------------
+
+ csrf_token (reason: web-framework specific)
+ load (reason: python-specific)
+ url (reason: web-framework specific)
+*/
+
+import (
+ "fmt"
+)
+
+type INodeTag interface {
+ INode
+}
+
+// This is the function signature of the tag's parser you will have
+// to implement in order to create a new tag.
+//
+// 'doc' is providing access to the whole document while 'arguments'
+// is providing access to the user's arguments to the tag:
+//
+// {% your_tag_name some "arguments" 123 %}
+//
+// start_token will be the *Token with the tag's name in it (here: your_tag_name).
+//
+// Please see the Parser documentation on how to use the parser.
+// See RegisterTag()'s documentation for more information about
+// writing a tag as well.
+type TagParser func(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Error)
+
+type tag struct {
+ name string
+ parser TagParser
+}
+
+var tags map[string]*tag
+
+func init() {
+ tags = make(map[string]*tag)
+}
+
+// Registers a new tag. You usually want to call this
+// function in the tag's init() function:
+// http://golang.org/doc/effective_go.html#init
+//
+// See http://www.florian-schlachter.de/post/pongo2/ for more about
+// writing filters and tags.
+func RegisterTag(name string, parserFn TagParser) error {
+ _, existing := tags[name]
+ if existing {
+ return fmt.Errorf("tag with name '%s' is already registered", name)
+ }
+ tags[name] = &tag{
+ name: name,
+ parser: parserFn,
+ }
+ return nil
+}
+
+// Replaces an already registered tag with a new implementation. Use this
+// function with caution since it allows you to change existing tag behaviour.
+func ReplaceTag(name string, parserFn TagParser) error {
+ _, existing := tags[name]
+ if !existing {
+ return fmt.Errorf("tag with name '%s' does not exist (therefore cannot be overridden)", name)
+ }
+ tags[name] = &tag{
+ name: name,
+ parser: parserFn,
+ }
+ return nil
+}
+
+// Tag = "{%" IDENT ARGS "%}"
+func (p *Parser) parseTagElement() (INodeTag, *Error) {
+ p.Consume() // consume "{%"
+ tokenName := p.MatchType(TokenIdentifier)
+
+ // Check for identifier
+ if tokenName == nil {
+ return nil, p.Error("Tag name must be an identifier.", nil)
+ }
+
+ // Check for the existing tag
+ tag, exists := tags[tokenName.Val]
+ if !exists {
+ // Does not exists
+ return nil, p.Error(fmt.Sprintf("Tag '%s' not found (or beginning tag not provided)", tokenName.Val), tokenName)
+ }
+
+ // Check sandbox tag restriction
+ if _, isBanned := p.template.set.bannedTags[tokenName.Val]; isBanned {
+ return nil, p.Error(fmt.Sprintf("Usage of tag '%s' is not allowed (sandbox restriction active).", tokenName.Val), tokenName)
+ }
+
+ var argsToken []*Token
+ for p.Peek(TokenSymbol, "%}") == nil && p.Remaining() > 0 {
+ // Add token to args
+ argsToken = append(argsToken, p.Current())
+ p.Consume() // next token
+ }
+
+ // EOF?
+ if p.Remaining() == 0 {
+ return nil, p.Error("Unexpectedly reached EOF, no tag end found.", p.lastToken)
+ }
+
+ p.Match(TokenSymbol, "%}")
+
+ argParser := newParser(p.name, argsToken, p.template)
+ if len(argsToken) == 0 {
+ // This is done to have nice EOF error messages
+ argParser.lastToken = tokenName
+ }
+
+ p.template.level++
+ defer func() { p.template.level-- }()
+ return tag.parser(p, tokenName, argParser)
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/tags_autoescape.go b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/tags_autoescape.go
new file mode 100644
index 000000000000..590a1db3506e
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/tags_autoescape.go
@@ -0,0 +1,52 @@
+package pongo2
+
+type tagAutoescapeNode struct {
+ wrapper *NodeWrapper
+ autoescape bool
+}
+
+func (node *tagAutoescapeNode) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error {
+ old := ctx.Autoescape
+ ctx.Autoescape = node.autoescape
+
+ err := node.wrapper.Execute(ctx, writer)
+ if err != nil {
+ return err
+ }
+
+ ctx.Autoescape = old
+
+ return nil
+}
+
+func tagAutoescapeParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Error) {
+ autoescapeNode := &tagAutoescapeNode{}
+
+ wrapper, _, err := doc.WrapUntilTag("endautoescape")
+ if err != nil {
+ return nil, err
+ }
+ autoescapeNode.wrapper = wrapper
+
+ modeToken := arguments.MatchType(TokenIdentifier)
+ if modeToken == nil {
+ return nil, arguments.Error("A mode is required for autoescape-tag.", nil)
+ }
+ if modeToken.Val == "on" {
+ autoescapeNode.autoescape = true
+ } else if modeToken.Val == "off" {
+ autoescapeNode.autoescape = false
+ } else {
+ return nil, arguments.Error("Only 'on' or 'off' is valid as an autoescape-mode.", nil)
+ }
+
+ if arguments.Remaining() > 0 {
+ return nil, arguments.Error("Malformed autoescape-tag arguments.", nil)
+ }
+
+ return autoescapeNode, nil
+}
+
+func init() {
+ RegisterTag("autoescape", tagAutoescapeParser)
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/tags_block.go b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/tags_block.go
new file mode 100644
index 000000000000..86145f329aef
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/tags_block.go
@@ -0,0 +1,129 @@
+package pongo2
+
+import (
+ "bytes"
+ "fmt"
+)
+
+type tagBlockNode struct {
+ name string
+}
+
+func (node *tagBlockNode) getBlockWrappers(tpl *Template) []*NodeWrapper {
+ nodeWrappers := make([]*NodeWrapper, 0)
+ var t *NodeWrapper
+
+ for tpl != nil {
+ t = tpl.blocks[node.name]
+ if t != nil {
+ nodeWrappers = append(nodeWrappers, t)
+ }
+ tpl = tpl.child
+ }
+
+ return nodeWrappers
+}
+
+func (node *tagBlockNode) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error {
+ tpl := ctx.template
+ if tpl == nil {
+ panic("internal error: tpl == nil")
+ }
+
+ // Determine the block to execute
+ blockWrappers := node.getBlockWrappers(tpl)
+ lenBlockWrappers := len(blockWrappers)
+
+ if lenBlockWrappers == 0 {
+ return ctx.Error("internal error: len(block_wrappers) == 0 in tagBlockNode.Execute()", nil)
+ }
+
+ blockWrapper := blockWrappers[lenBlockWrappers-1]
+ ctx.Private["block"] = tagBlockInformation{
+ ctx: ctx,
+ wrappers: blockWrappers[0 : lenBlockWrappers-1],
+ }
+ err := blockWrapper.Execute(ctx, writer)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+type tagBlockInformation struct {
+ ctx *ExecutionContext
+ wrappers []*NodeWrapper
+}
+
+func (t tagBlockInformation) Super() string {
+ lenWrappers := len(t.wrappers)
+
+ if lenWrappers == 0 {
+ return ""
+ }
+
+ superCtx := NewChildExecutionContext(t.ctx)
+ superCtx.Private["block"] = tagBlockInformation{
+ ctx: t.ctx,
+ wrappers: t.wrappers[0 : lenWrappers-1],
+ }
+
+ blockWrapper := t.wrappers[lenWrappers-1]
+ buf := bytes.NewBufferString("")
+ err := blockWrapper.Execute(superCtx, &templateWriter{buf})
+ if err != nil {
+ return ""
+ }
+ return buf.String()
+}
+
+func tagBlockParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Error) {
+ if arguments.Count() == 0 {
+ return nil, arguments.Error("Tag 'block' requires an identifier.", nil)
+ }
+
+ nameToken := arguments.MatchType(TokenIdentifier)
+ if nameToken == nil {
+ return nil, arguments.Error("First argument for tag 'block' must be an identifier.", nil)
+ }
+
+ if arguments.Remaining() != 0 {
+ return nil, arguments.Error("Tag 'block' takes exactly 1 argument (an identifier).", nil)
+ }
+
+ wrapper, endtagargs, err := doc.WrapUntilTag("endblock")
+ if err != nil {
+ return nil, err
+ }
+ if endtagargs.Remaining() > 0 {
+ endtagnameToken := endtagargs.MatchType(TokenIdentifier)
+ if endtagnameToken != nil {
+ if endtagnameToken.Val != nameToken.Val {
+ return nil, endtagargs.Error(fmt.Sprintf("Name for 'endblock' must equal to 'block'-tag's name ('%s' != '%s').",
+ nameToken.Val, endtagnameToken.Val), nil)
+ }
+ }
+
+ if endtagnameToken == nil || endtagargs.Remaining() > 0 {
+ return nil, endtagargs.Error("Either no or only one argument (identifier) allowed for 'endblock'.", nil)
+ }
+ }
+
+ tpl := doc.template
+ if tpl == nil {
+ panic("internal error: tpl == nil")
+ }
+ _, hasBlock := tpl.blocks[nameToken.Val]
+ if !hasBlock {
+ tpl.blocks[nameToken.Val] = wrapper
+ } else {
+ return nil, arguments.Error(fmt.Sprintf("Block named '%s' already defined", nameToken.Val), nil)
+ }
+
+ return &tagBlockNode{name: nameToken.Val}, nil
+}
+
+func init() {
+ RegisterTag("block", tagBlockParser)
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/tags_comment.go b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/tags_comment.go
new file mode 100644
index 000000000000..56a02ed99db4
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/tags_comment.go
@@ -0,0 +1,27 @@
+package pongo2
+
+type tagCommentNode struct{}
+
+func (node *tagCommentNode) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error {
+ return nil
+}
+
+func tagCommentParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Error) {
+ commentNode := &tagCommentNode{}
+
+ // TODO: Process the endtag's arguments (see django 'comment'-tag documentation)
+ err := doc.SkipUntilTag("endcomment")
+ if err != nil {
+ return nil, err
+ }
+
+ if arguments.Count() != 0 {
+ return nil, arguments.Error("Tag 'comment' does not take any argument.", nil)
+ }
+
+ return commentNode, nil
+}
+
+func init() {
+ RegisterTag("comment", tagCommentParser)
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/tags_cycle.go b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/tags_cycle.go
new file mode 100644
index 000000000000..ffbd254eea8b
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/tags_cycle.go
@@ -0,0 +1,106 @@
+package pongo2
+
+type tagCycleValue struct {
+ node *tagCycleNode
+ value *Value
+}
+
+type tagCycleNode struct {
+ position *Token
+ args []IEvaluator
+ idx int
+ asName string
+ silent bool
+}
+
+func (cv *tagCycleValue) String() string {
+ return cv.value.String()
+}
+
+func (node *tagCycleNode) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error {
+ item := node.args[node.idx%len(node.args)]
+ node.idx++
+
+ val, err := item.Evaluate(ctx)
+ if err != nil {
+ return err
+ }
+
+ if t, ok := val.Interface().(*tagCycleValue); ok {
+ // {% cycle "test1" "test2"
+ // {% cycle cycleitem %}
+
+ // Update the cycle value with next value
+ item := t.node.args[t.node.idx%len(t.node.args)]
+ t.node.idx++
+
+ val, err := item.Evaluate(ctx)
+ if err != nil {
+ return err
+ }
+
+ t.value = val
+
+ if !t.node.silent {
+ writer.WriteString(val.String())
+ }
+ } else {
+ // Regular call
+
+ cycleValue := &tagCycleValue{
+ node: node,
+ value: val,
+ }
+
+ if node.asName != "" {
+ ctx.Private[node.asName] = cycleValue
+ }
+ if !node.silent {
+ writer.WriteString(val.String())
+ }
+ }
+
+ return nil
+}
+
+// HINT: We're not supporting the old comma-separated list of expressions argument-style
+func tagCycleParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Error) {
+ cycleNode := &tagCycleNode{
+ position: start,
+ }
+
+ for arguments.Remaining() > 0 {
+ node, err := arguments.ParseExpression()
+ if err != nil {
+ return nil, err
+ }
+ cycleNode.args = append(cycleNode.args, node)
+
+ if arguments.MatchOne(TokenKeyword, "as") != nil {
+ // as
+
+ nameToken := arguments.MatchType(TokenIdentifier)
+ if nameToken == nil {
+ return nil, arguments.Error("Name (identifier) expected after 'as'.", nil)
+ }
+ cycleNode.asName = nameToken.Val
+
+ if arguments.MatchOne(TokenIdentifier, "silent") != nil {
+ cycleNode.silent = true
+ }
+
+ // Now we're finished
+ break
+ }
+ }
+
+ if arguments.Remaining() > 0 {
+ return nil, arguments.Error("Malformed cycle-tag.", nil)
+ }
+
+ return cycleNode, nil
+}
+
+func init() {
+ RegisterTag("cycle", tagCycleParser)
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/tags_extends.go b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/tags_extends.go
new file mode 100644
index 000000000000..5771020a0661
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/tags_extends.go
@@ -0,0 +1,52 @@
+package pongo2
+
+type tagExtendsNode struct {
+ filename string
+}
+
+func (node *tagExtendsNode) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error {
+ return nil
+}
+
+func tagExtendsParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Error) {
+ extendsNode := &tagExtendsNode{}
+
+ if doc.template.level > 1 {
+ return nil, arguments.Error("The 'extends' tag can only defined on root level.", start)
+ }
+
+ if doc.template.parent != nil {
+ // Already one parent
+ return nil, arguments.Error("This template has already one parent.", start)
+ }
+
+ if filenameToken := arguments.MatchType(TokenString); filenameToken != nil {
+ // prepared, static template
+
+ // Get parent's filename
+ parentFilename := doc.template.set.resolveFilename(doc.template, filenameToken.Val)
+
+ // Parse the parent
+ parentTemplate, err := doc.template.set.FromFile(parentFilename)
+ if err != nil {
+ return nil, err.(*Error)
+ }
+
+ // Keep track of things
+ parentTemplate.child = doc.template
+ doc.template.parent = parentTemplate
+ extendsNode.filename = parentFilename
+ } else {
+ return nil, arguments.Error("Tag 'extends' requires a template filename as string.", nil)
+ }
+
+ if arguments.Remaining() > 0 {
+ return nil, arguments.Error("Tag 'extends' does only take 1 argument.", nil)
+ }
+
+ return extendsNode, nil
+}
+
+func init() {
+ RegisterTag("extends", tagExtendsParser)
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/tags_filter.go b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/tags_filter.go
new file mode 100644
index 000000000000..b38fd929821d
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/tags_filter.go
@@ -0,0 +1,95 @@
+package pongo2
+
+import (
+ "bytes"
+)
+
+type nodeFilterCall struct {
+ name string
+ paramExpr IEvaluator
+}
+
+type tagFilterNode struct {
+ position *Token
+ bodyWrapper *NodeWrapper
+ filterChain []*nodeFilterCall
+}
+
+func (node *tagFilterNode) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error {
+ temp := bytes.NewBuffer(make([]byte, 0, 1024)) // 1 KiB size
+
+ err := node.bodyWrapper.Execute(ctx, temp)
+ if err != nil {
+ return err
+ }
+
+ value := AsValue(temp.String())
+
+ for _, call := range node.filterChain {
+ var param *Value
+ if call.paramExpr != nil {
+ param, err = call.paramExpr.Evaluate(ctx)
+ if err != nil {
+ return err
+ }
+ } else {
+ param = AsValue(nil)
+ }
+ value, err = ApplyFilter(call.name, value, param)
+ if err != nil {
+ return ctx.Error(err.Error(), node.position)
+ }
+ }
+
+ writer.WriteString(value.String())
+
+ return nil
+}
+
+func tagFilterParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Error) {
+ filterNode := &tagFilterNode{
+ position: start,
+ }
+
+ wrapper, _, err := doc.WrapUntilTag("endfilter")
+ if err != nil {
+ return nil, err
+ }
+ filterNode.bodyWrapper = wrapper
+
+ for arguments.Remaining() > 0 {
+ filterCall := &nodeFilterCall{}
+
+ nameToken := arguments.MatchType(TokenIdentifier)
+ if nameToken == nil {
+ return nil, arguments.Error("Expected a filter name (identifier).", nil)
+ }
+ filterCall.name = nameToken.Val
+
+ if arguments.MatchOne(TokenSymbol, ":") != nil {
+ // Filter parameter
+ // NOTICE: we can't use ParseExpression() here, because it would parse the next filter "|..." as well in the argument list
+ expr, err := arguments.parseVariableOrLiteral()
+ if err != nil {
+ return nil, err
+ }
+ filterCall.paramExpr = expr
+ }
+
+ filterNode.filterChain = append(filterNode.filterChain, filterCall)
+
+ if arguments.MatchOne(TokenSymbol, "|") == nil {
+ break
+ }
+ }
+
+ if arguments.Remaining() > 0 {
+ return nil, arguments.Error("Malformed filter-tag arguments.", nil)
+ }
+
+ return filterNode, nil
+}
+
+func init() {
+ RegisterTag("filter", tagFilterParser)
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/tags_firstof.go b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/tags_firstof.go
new file mode 100644
index 000000000000..5b2888e2be5d
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/tags_firstof.go
@@ -0,0 +1,49 @@
+package pongo2
+
+type tagFirstofNode struct {
+ position *Token
+ args []IEvaluator
+}
+
+func (node *tagFirstofNode) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error {
+ for _, arg := range node.args {
+ val, err := arg.Evaluate(ctx)
+ if err != nil {
+ return err
+ }
+
+ if val.IsTrue() {
+ if ctx.Autoescape && !arg.FilterApplied("safe") {
+ val, err = ApplyFilter("escape", val, nil)
+ if err != nil {
+ return err
+ }
+ }
+
+ writer.WriteString(val.String())
+ return nil
+ }
+ }
+
+ return nil
+}
+
+func tagFirstofParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Error) {
+ firstofNode := &tagFirstofNode{
+ position: start,
+ }
+
+ for arguments.Remaining() > 0 {
+ node, err := arguments.ParseExpression()
+ if err != nil {
+ return nil, err
+ }
+ firstofNode.args = append(firstofNode.args, node)
+ }
+
+ return firstofNode, nil
+}
+
+func init() {
+ RegisterTag("firstof", tagFirstofParser)
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/tags_for.go b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/tags_for.go
new file mode 100644
index 000000000000..5b0b5554c83f
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/tags_for.go
@@ -0,0 +1,159 @@
+package pongo2
+
+type tagForNode struct {
+ key string
+ value string // only for maps: for key, value in map
+ objectEvaluator IEvaluator
+ reversed bool
+ sorted bool
+
+ bodyWrapper *NodeWrapper
+ emptyWrapper *NodeWrapper
+}
+
+type tagForLoopInformation struct {
+ Counter int
+ Counter0 int
+ Revcounter int
+ Revcounter0 int
+ First bool
+ Last bool
+ Parentloop *tagForLoopInformation
+}
+
+func (node *tagForNode) Execute(ctx *ExecutionContext, writer TemplateWriter) (forError *Error) {
+ // Backup forloop (as parentloop in public context), key-name and value-name
+ forCtx := NewChildExecutionContext(ctx)
+ parentloop := forCtx.Private["forloop"]
+
+ // Create loop struct
+ loopInfo := &tagForLoopInformation{
+ First: true,
+ }
+
+ // Is it a loop in a loop?
+ if parentloop != nil {
+ loopInfo.Parentloop = parentloop.(*tagForLoopInformation)
+ }
+
+ // Register loopInfo in public context
+ forCtx.Private["forloop"] = loopInfo
+
+ obj, err := node.objectEvaluator.Evaluate(forCtx)
+ if err != nil {
+ return err
+ }
+
+ obj.IterateOrder(func(idx, count int, key, value *Value) bool {
+ // There's something to iterate over (correct type and at least 1 item)
+
+ // Update loop infos and public context
+ forCtx.Private[node.key] = key
+ if value != nil {
+ forCtx.Private[node.value] = value
+ }
+ loopInfo.Counter = idx + 1
+ loopInfo.Counter0 = idx
+ if idx == 1 {
+ loopInfo.First = false
+ }
+ if idx+1 == count {
+ loopInfo.Last = true
+ }
+ loopInfo.Revcounter = count - idx // TODO: Not sure about this, have to look it up
+ loopInfo.Revcounter0 = count - (idx + 1) // TODO: Not sure about this, have to look it up
+
+ // Render elements with updated context
+ err := node.bodyWrapper.Execute(forCtx, writer)
+ if err != nil {
+ forError = err
+ return false
+ }
+ return true
+ }, func() {
+ // Nothing to iterate over (maybe wrong type or no items)
+ if node.emptyWrapper != nil {
+ err := node.emptyWrapper.Execute(forCtx, writer)
+ if err != nil {
+ forError = err
+ }
+ }
+ }, node.reversed, node.sorted)
+
+ return forError
+}
+
+func tagForParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Error) {
+ forNode := &tagForNode{}
+
+ // Arguments parsing
+ var valueToken *Token
+ keyToken := arguments.MatchType(TokenIdentifier)
+ if keyToken == nil {
+ return nil, arguments.Error("Expected an key identifier as first argument for 'for'-tag", nil)
+ }
+
+ if arguments.Match(TokenSymbol, ",") != nil {
+ // Value name is provided
+ valueToken = arguments.MatchType(TokenIdentifier)
+ if valueToken == nil {
+ return nil, arguments.Error("Value name must be an identifier.", nil)
+ }
+ }
+
+ if arguments.Match(TokenKeyword, "in") == nil {
+ return nil, arguments.Error("Expected keyword 'in'.", nil)
+ }
+
+ objectEvaluator, err := arguments.ParseExpression()
+ if err != nil {
+ return nil, err
+ }
+ forNode.objectEvaluator = objectEvaluator
+ forNode.key = keyToken.Val
+ if valueToken != nil {
+ forNode.value = valueToken.Val
+ }
+
+ if arguments.MatchOne(TokenIdentifier, "reversed") != nil {
+ forNode.reversed = true
+ }
+
+ if arguments.MatchOne(TokenIdentifier, "sorted") != nil {
+ forNode.sorted = true
+ }
+
+ if arguments.Remaining() > 0 {
+ return nil, arguments.Error("Malformed for-loop arguments.", nil)
+ }
+
+ // Body wrapping
+ wrapper, endargs, err := doc.WrapUntilTag("empty", "endfor")
+ if err != nil {
+ return nil, err
+ }
+ forNode.bodyWrapper = wrapper
+
+ if endargs.Count() > 0 {
+ return nil, endargs.Error("Arguments not allowed here.", nil)
+ }
+
+ if wrapper.Endtag == "empty" {
+ // if there's an else in the if-statement, we need the else-Block as well
+ wrapper, endargs, err = doc.WrapUntilTag("endfor")
+ if err != nil {
+ return nil, err
+ }
+ forNode.emptyWrapper = wrapper
+
+ if endargs.Count() > 0 {
+ return nil, endargs.Error("Arguments not allowed here.", nil)
+ }
+ }
+
+ return forNode, nil
+}
+
+func init() {
+ RegisterTag("for", tagForParser)
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/tags_if.go b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/tags_if.go
new file mode 100644
index 000000000000..3eeaf3b49983
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/tags_if.go
@@ -0,0 +1,76 @@
+package pongo2
+
+type tagIfNode struct {
+ conditions []IEvaluator
+ wrappers []*NodeWrapper
+}
+
+func (node *tagIfNode) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error {
+ for i, condition := range node.conditions {
+ result, err := condition.Evaluate(ctx)
+ if err != nil {
+ return err
+ }
+
+ if result.IsTrue() {
+ return node.wrappers[i].Execute(ctx, writer)
+ }
+ // Last condition?
+ if len(node.conditions) == i+1 && len(node.wrappers) > i+1 {
+ return node.wrappers[i+1].Execute(ctx, writer)
+ }
+ }
+ return nil
+}
+
+func tagIfParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Error) {
+ ifNode := &tagIfNode{}
+
+ // Parse first and main IF condition
+ condition, err := arguments.ParseExpression()
+ if err != nil {
+ return nil, err
+ }
+ ifNode.conditions = append(ifNode.conditions, condition)
+
+ if arguments.Remaining() > 0 {
+ return nil, arguments.Error("If-condition is malformed.", nil)
+ }
+
+ // Check the rest
+ for {
+ wrapper, tagArgs, err := doc.WrapUntilTag("elif", "else", "endif")
+ if err != nil {
+ return nil, err
+ }
+ ifNode.wrappers = append(ifNode.wrappers, wrapper)
+
+ if wrapper.Endtag == "elif" {
+ // elif can take a condition
+ condition, err = tagArgs.ParseExpression()
+ if err != nil {
+ return nil, err
+ }
+ ifNode.conditions = append(ifNode.conditions, condition)
+
+ if tagArgs.Remaining() > 0 {
+ return nil, tagArgs.Error("Elif-condition is malformed.", nil)
+ }
+ } else {
+ if tagArgs.Count() > 0 {
+ // else/endif can't take any conditions
+ return nil, tagArgs.Error("Arguments not allowed here.", nil)
+ }
+ }
+
+ if wrapper.Endtag == "endif" {
+ break
+ }
+ }
+
+ return ifNode, nil
+}
+
+func init() {
+ RegisterTag("if", tagIfParser)
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/tags_ifchanged.go b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/tags_ifchanged.go
new file mode 100644
index 000000000000..45296a0a3459
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/tags_ifchanged.go
@@ -0,0 +1,116 @@
+package pongo2
+
+import (
+ "bytes"
+)
+
+type tagIfchangedNode struct {
+ watchedExpr []IEvaluator
+ lastValues []*Value
+ lastContent []byte
+ thenWrapper *NodeWrapper
+ elseWrapper *NodeWrapper
+}
+
+func (node *tagIfchangedNode) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error {
+ if len(node.watchedExpr) == 0 {
+ // Check against own rendered body
+
+ buf := bytes.NewBuffer(make([]byte, 0, 1024)) // 1 KiB
+ err := node.thenWrapper.Execute(ctx, buf)
+ if err != nil {
+ return err
+ }
+
+ bufBytes := buf.Bytes()
+ if !bytes.Equal(node.lastContent, bufBytes) {
+ // Rendered content changed, output it
+ writer.Write(bufBytes)
+ node.lastContent = bufBytes
+ }
+ } else {
+ nowValues := make([]*Value, 0, len(node.watchedExpr))
+ for _, expr := range node.watchedExpr {
+ val, err := expr.Evaluate(ctx)
+ if err != nil {
+ return err
+ }
+ nowValues = append(nowValues, val)
+ }
+
+ // Compare old to new values now
+ changed := len(node.lastValues) == 0
+
+ for idx, oldVal := range node.lastValues {
+ if !oldVal.EqualValueTo(nowValues[idx]) {
+ changed = true
+ break // we can stop here because ONE value changed
+ }
+ }
+
+ node.lastValues = nowValues
+
+ if changed {
+ // Render thenWrapper
+ err := node.thenWrapper.Execute(ctx, writer)
+ if err != nil {
+ return err
+ }
+ } else {
+ // Render elseWrapper
+ err := node.elseWrapper.Execute(ctx, writer)
+ if err != nil {
+ return err
+ }
+ }
+ }
+
+ return nil
+}
+
+func tagIfchangedParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Error) {
+ ifchangedNode := &tagIfchangedNode{}
+
+ for arguments.Remaining() > 0 {
+ // Parse condition
+ expr, err := arguments.ParseExpression()
+ if err != nil {
+ return nil, err
+ }
+ ifchangedNode.watchedExpr = append(ifchangedNode.watchedExpr, expr)
+ }
+
+ if arguments.Remaining() > 0 {
+ return nil, arguments.Error("Ifchanged-arguments are malformed.", nil)
+ }
+
+ // Wrap then/else-blocks
+ wrapper, endargs, err := doc.WrapUntilTag("else", "endifchanged")
+ if err != nil {
+ return nil, err
+ }
+ ifchangedNode.thenWrapper = wrapper
+
+ if endargs.Count() > 0 {
+ return nil, endargs.Error("Arguments not allowed here.", nil)
+ }
+
+ if wrapper.Endtag == "else" {
+ // if there's an else in the if-statement, we need the else-Block as well
+ wrapper, endargs, err = doc.WrapUntilTag("endifchanged")
+ if err != nil {
+ return nil, err
+ }
+ ifchangedNode.elseWrapper = wrapper
+
+ if endargs.Count() > 0 {
+ return nil, endargs.Error("Arguments not allowed here.", nil)
+ }
+ }
+
+ return ifchangedNode, nil
+}
+
+func init() {
+ RegisterTag("ifchanged", tagIfchangedParser)
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/tags_ifequal.go b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/tags_ifequal.go
new file mode 100644
index 000000000000..103f1c7ba6ea
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/tags_ifequal.go
@@ -0,0 +1,78 @@
+package pongo2
+
+type tagIfEqualNode struct {
+ var1, var2 IEvaluator
+ thenWrapper *NodeWrapper
+ elseWrapper *NodeWrapper
+}
+
+func (node *tagIfEqualNode) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error {
+ r1, err := node.var1.Evaluate(ctx)
+ if err != nil {
+ return err
+ }
+ r2, err := node.var2.Evaluate(ctx)
+ if err != nil {
+ return err
+ }
+
+ result := r1.EqualValueTo(r2)
+
+ if result {
+ return node.thenWrapper.Execute(ctx, writer)
+ }
+ if node.elseWrapper != nil {
+ return node.elseWrapper.Execute(ctx, writer)
+ }
+ return nil
+}
+
+func tagIfEqualParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Error) {
+ ifequalNode := &tagIfEqualNode{}
+
+ // Parse two expressions
+ var1, err := arguments.ParseExpression()
+ if err != nil {
+ return nil, err
+ }
+ var2, err := arguments.ParseExpression()
+ if err != nil {
+ return nil, err
+ }
+ ifequalNode.var1 = var1
+ ifequalNode.var2 = var2
+
+ if arguments.Remaining() > 0 {
+ return nil, arguments.Error("ifequal only takes 2 arguments.", nil)
+ }
+
+ // Wrap then/else-blocks
+ wrapper, endargs, err := doc.WrapUntilTag("else", "endifequal")
+ if err != nil {
+ return nil, err
+ }
+ ifequalNode.thenWrapper = wrapper
+
+ if endargs.Count() > 0 {
+ return nil, endargs.Error("Arguments not allowed here.", nil)
+ }
+
+ if wrapper.Endtag == "else" {
+ // if there's an else in the if-statement, we need the else-Block as well
+ wrapper, endargs, err = doc.WrapUntilTag("endifequal")
+ if err != nil {
+ return nil, err
+ }
+ ifequalNode.elseWrapper = wrapper
+
+ if endargs.Count() > 0 {
+ return nil, endargs.Error("Arguments not allowed here.", nil)
+ }
+ }
+
+ return ifequalNode, nil
+}
+
+func init() {
+ RegisterTag("ifequal", tagIfEqualParser)
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/tags_ifnotequal.go b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/tags_ifnotequal.go
new file mode 100644
index 000000000000..0d287d349d00
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/tags_ifnotequal.go
@@ -0,0 +1,78 @@
+package pongo2
+
+type tagIfNotEqualNode struct {
+ var1, var2 IEvaluator
+ thenWrapper *NodeWrapper
+ elseWrapper *NodeWrapper
+}
+
+func (node *tagIfNotEqualNode) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error {
+ r1, err := node.var1.Evaluate(ctx)
+ if err != nil {
+ return err
+ }
+ r2, err := node.var2.Evaluate(ctx)
+ if err != nil {
+ return err
+ }
+
+ result := !r1.EqualValueTo(r2)
+
+ if result {
+ return node.thenWrapper.Execute(ctx, writer)
+ }
+ if node.elseWrapper != nil {
+ return node.elseWrapper.Execute(ctx, writer)
+ }
+ return nil
+}
+
+func tagIfNotEqualParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Error) {
+ ifnotequalNode := &tagIfNotEqualNode{}
+
+ // Parse two expressions
+ var1, err := arguments.ParseExpression()
+ if err != nil {
+ return nil, err
+ }
+ var2, err := arguments.ParseExpression()
+ if err != nil {
+ return nil, err
+ }
+ ifnotequalNode.var1 = var1
+ ifnotequalNode.var2 = var2
+
+ if arguments.Remaining() > 0 {
+ return nil, arguments.Error("ifequal only takes 2 arguments.", nil)
+ }
+
+ // Wrap then/else-blocks
+ wrapper, endargs, err := doc.WrapUntilTag("else", "endifnotequal")
+ if err != nil {
+ return nil, err
+ }
+ ifnotequalNode.thenWrapper = wrapper
+
+ if endargs.Count() > 0 {
+ return nil, endargs.Error("Arguments not allowed here.", nil)
+ }
+
+ if wrapper.Endtag == "else" {
+ // if there's an else in the if-statement, we need the else-Block as well
+ wrapper, endargs, err = doc.WrapUntilTag("endifnotequal")
+ if err != nil {
+ return nil, err
+ }
+ ifnotequalNode.elseWrapper = wrapper
+
+ if endargs.Count() > 0 {
+ return nil, endargs.Error("Arguments not allowed here.", nil)
+ }
+ }
+
+ return ifnotequalNode, nil
+}
+
+func init() {
+ RegisterTag("ifnotequal", tagIfNotEqualParser)
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/tags_import.go b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/tags_import.go
new file mode 100644
index 000000000000..7e0d6a01a51f
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/tags_import.go
@@ -0,0 +1,84 @@
+package pongo2
+
+import (
+ "fmt"
+)
+
+type tagImportNode struct {
+ position *Token
+ filename string
+ macros map[string]*tagMacroNode // alias/name -> macro instance
+}
+
+func (node *tagImportNode) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error {
+ for name, macro := range node.macros {
+ func(name string, macro *tagMacroNode) {
+ ctx.Private[name] = func(args ...*Value) *Value {
+ return macro.call(ctx, args...)
+ }
+ }(name, macro)
+ }
+ return nil
+}
+
+func tagImportParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Error) {
+ importNode := &tagImportNode{
+ position: start,
+ macros: make(map[string]*tagMacroNode),
+ }
+
+ filenameToken := arguments.MatchType(TokenString)
+ if filenameToken == nil {
+ return nil, arguments.Error("Import-tag needs a filename as string.", nil)
+ }
+
+ importNode.filename = doc.template.set.resolveFilename(doc.template, filenameToken.Val)
+
+ if arguments.Remaining() == 0 {
+ return nil, arguments.Error("You must at least specify one macro to import.", nil)
+ }
+
+ // Compile the given template
+ tpl, err := doc.template.set.FromFile(importNode.filename)
+ if err != nil {
+ return nil, err.(*Error).updateFromTokenIfNeeded(doc.template, start)
+ }
+
+ for arguments.Remaining() > 0 {
+ macroNameToken := arguments.MatchType(TokenIdentifier)
+ if macroNameToken == nil {
+ return nil, arguments.Error("Expected macro name (identifier).", nil)
+ }
+
+ asName := macroNameToken.Val
+ if arguments.Match(TokenKeyword, "as") != nil {
+ aliasToken := arguments.MatchType(TokenIdentifier)
+ if aliasToken == nil {
+ return nil, arguments.Error("Expected macro alias name (identifier).", nil)
+ }
+ asName = aliasToken.Val
+ }
+
+ macroInstance, has := tpl.exportedMacros[macroNameToken.Val]
+ if !has {
+ return nil, arguments.Error(fmt.Sprintf("Macro '%s' not found (or not exported) in '%s'.", macroNameToken.Val,
+ importNode.filename), macroNameToken)
+ }
+
+ importNode.macros[asName] = macroInstance
+
+ if arguments.Remaining() == 0 {
+ break
+ }
+
+ if arguments.Match(TokenSymbol, ",") == nil {
+ return nil, arguments.Error("Expected ','.", nil)
+ }
+ }
+
+ return importNode, nil
+}
+
+func init() {
+ RegisterTag("import", tagImportParser)
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/tags_include.go b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/tags_include.go
new file mode 100644
index 000000000000..6d619fdabebc
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/tags_include.go
@@ -0,0 +1,146 @@
+package pongo2
+
+type tagIncludeNode struct {
+ tpl *Template
+ filenameEvaluator IEvaluator
+ lazy bool
+ only bool
+ filename string
+ withPairs map[string]IEvaluator
+ ifExists bool
+}
+
+func (node *tagIncludeNode) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error {
+ // Building the context for the template
+ includeCtx := make(Context)
+
+ // Fill the context with all data from the parent
+ if !node.only {
+ includeCtx.Update(ctx.Public)
+ includeCtx.Update(ctx.Private)
+ }
+
+ // Put all custom with-pairs into the context
+ for key, value := range node.withPairs {
+ val, err := value.Evaluate(ctx)
+ if err != nil {
+ return err
+ }
+ includeCtx[key] = val
+ }
+
+ // Execute the template
+ if node.lazy {
+ // Evaluate the filename
+ filename, err := node.filenameEvaluator.Evaluate(ctx)
+ if err != nil {
+ return err
+ }
+
+ if filename.String() == "" {
+ return ctx.Error("Filename for 'include'-tag evaluated to an empty string.", nil)
+ }
+
+ // Get include-filename
+ includedFilename := ctx.template.set.resolveFilename(ctx.template, filename.String())
+
+ includedTpl, err2 := ctx.template.set.FromFile(includedFilename)
+ if err2 != nil {
+ // if this is ReadFile error, and "if_exists" flag is enabled
+ if node.ifExists && err2.(*Error).Sender == "fromfile" {
+ return nil
+ }
+ return err2.(*Error)
+ }
+ err2 = includedTpl.ExecuteWriter(includeCtx, writer)
+ if err2 != nil {
+ return err2.(*Error)
+ }
+ return nil
+ }
+ // Template is already parsed with static filename
+ err := node.tpl.ExecuteWriter(includeCtx, writer)
+ if err != nil {
+ return err.(*Error)
+ }
+ return nil
+}
+
+type tagIncludeEmptyNode struct{}
+
+func (node *tagIncludeEmptyNode) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error {
+ return nil
+}
+
+func tagIncludeParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Error) {
+ includeNode := &tagIncludeNode{
+ withPairs: make(map[string]IEvaluator),
+ }
+
+ if filenameToken := arguments.MatchType(TokenString); filenameToken != nil {
+ // prepared, static template
+
+ // "if_exists" flag
+ ifExists := arguments.Match(TokenIdentifier, "if_exists") != nil
+
+ // Get include-filename
+ includedFilename := doc.template.set.resolveFilename(doc.template, filenameToken.Val)
+
+ // Parse the parent
+ includeNode.filename = includedFilename
+ includedTpl, err := doc.template.set.FromFile(includedFilename)
+ if err != nil {
+ // if this is ReadFile error, and "if_exists" token presents we should create and empty node
+ if err.(*Error).Sender == "fromfile" && ifExists {
+ return &tagIncludeEmptyNode{}, nil
+ }
+ return nil, err.(*Error).updateFromTokenIfNeeded(doc.template, filenameToken)
+ }
+ includeNode.tpl = includedTpl
+ } else {
+ // No String, then the user wants to use lazy-evaluation (slower, but possible)
+ filenameEvaluator, err := arguments.ParseExpression()
+ if err != nil {
+ return nil, err.updateFromTokenIfNeeded(doc.template, filenameToken)
+ }
+ includeNode.filenameEvaluator = filenameEvaluator
+ includeNode.lazy = true
+ includeNode.ifExists = arguments.Match(TokenIdentifier, "if_exists") != nil // "if_exists" flag
+ }
+
+ // After having parsed the filename we're gonna parse the with+only options
+ if arguments.Match(TokenIdentifier, "with") != nil {
+ for arguments.Remaining() > 0 {
+ // We have at least one key=expr pair (because of starting "with")
+ keyToken := arguments.MatchType(TokenIdentifier)
+ if keyToken == nil {
+ return nil, arguments.Error("Expected an identifier", nil)
+ }
+ if arguments.Match(TokenSymbol, "=") == nil {
+ return nil, arguments.Error("Expected '='.", nil)
+ }
+ valueExpr, err := arguments.ParseExpression()
+ if err != nil {
+ return nil, err.updateFromTokenIfNeeded(doc.template, keyToken)
+ }
+
+ includeNode.withPairs[keyToken.Val] = valueExpr
+
+ // Only?
+ if arguments.Match(TokenIdentifier, "only") != nil {
+ includeNode.only = true
+ break // stop parsing arguments because it's the last option
+ }
+ }
+ }
+
+ if arguments.Remaining() > 0 {
+ return nil, arguments.Error("Malformed 'include'-tag arguments.", nil)
+ }
+
+ return includeNode, nil
+}
+
+func init() {
+ RegisterTag("include", tagIncludeParser)
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/tags_lorem.go b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/tags_lorem.go
new file mode 100644
index 000000000000..7794f6c126ff
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/tags_lorem.go
@@ -0,0 +1,132 @@
+package pongo2
+
+import (
+ "fmt"
+ "math/rand"
+ "strings"
+ "time"
+)
+
+var (
+ tagLoremParagraphs = strings.Split(tagLoremText, "\n")
+ tagLoremWords = strings.Fields(tagLoremText)
+)
+
+type tagLoremNode struct {
+ position *Token
+ count int // number of paragraphs
+ method string // w = words, p = HTML paragraphs, b = plain-text (default is b)
+ random bool // does not use the default paragraph "Lorem ipsum dolor sit amet, ..."
+}
+
+func (node *tagLoremNode) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error {
+ switch node.method {
+ case "b":
+ if node.random {
+ for i := 0; i < node.count; i++ {
+ if i > 0 {
+ writer.WriteString("\n")
+ }
+ par := tagLoremParagraphs[rand.Intn(len(tagLoremParagraphs))]
+ writer.WriteString(par)
+ }
+ } else {
+ for i := 0; i < node.count; i++ {
+ if i > 0 {
+ writer.WriteString("\n")
+ }
+ par := tagLoremParagraphs[i%len(tagLoremParagraphs)]
+ writer.WriteString(par)
+ }
+ }
+ case "w":
+ if node.random {
+ for i := 0; i < node.count; i++ {
+ if i > 0 {
+ writer.WriteString(" ")
+ }
+ word := tagLoremWords[rand.Intn(len(tagLoremWords))]
+ writer.WriteString(word)
+ }
+ } else {
+ for i := 0; i < node.count; i++ {
+ if i > 0 {
+ writer.WriteString(" ")
+ }
+ word := tagLoremWords[i%len(tagLoremWords)]
+ writer.WriteString(word)
+ }
+ }
+ case "p":
+ if node.random {
+ for i := 0; i < node.count; i++ {
+ if i > 0 {
+ writer.WriteString("\n")
+ }
+ writer.WriteString("
")
+ par := tagLoremParagraphs[rand.Intn(len(tagLoremParagraphs))]
+ writer.WriteString(par)
+ writer.WriteString("
")
+ }
+ } else {
+ for i := 0; i < node.count; i++ {
+ if i > 0 {
+ writer.WriteString("\n")
+ }
+ writer.WriteString("
")
+ par := tagLoremParagraphs[i%len(tagLoremParagraphs)]
+ writer.WriteString(par)
+ writer.WriteString("
")
+
+ }
+ }
+ default:
+ return ctx.OrigError(fmt.Errorf("unsupported method: %s", node.method), nil)
+ }
+
+ return nil
+}
+
+func tagLoremParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Error) {
+ loremNode := &tagLoremNode{
+ position: start,
+ count: 1,
+ method: "b",
+ }
+
+ if countToken := arguments.MatchType(TokenNumber); countToken != nil {
+ loremNode.count = AsValue(countToken.Val).Integer()
+ }
+
+ if methodToken := arguments.MatchType(TokenIdentifier); methodToken != nil {
+ if methodToken.Val != "w" && methodToken.Val != "p" && methodToken.Val != "b" {
+ return nil, arguments.Error("lorem-method must be either 'w', 'p' or 'b'.", nil)
+ }
+
+ loremNode.method = methodToken.Val
+ }
+
+ if arguments.MatchOne(TokenIdentifier, "random") != nil {
+ loremNode.random = true
+ }
+
+ if arguments.Remaining() > 0 {
+ return nil, arguments.Error("Malformed lorem-tag arguments.", nil)
+ }
+
+ return loremNode, nil
+}
+
+func init() {
+ rand.Seed(time.Now().Unix())
+
+ RegisterTag("lorem", tagLoremParser)
+}
+
+const tagLoremText = `Lorem ipsum dolor sit amet, consectetur adipisici elit, sed eiusmod tempor incidunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquid ex ea commodi consequat. Quis aute iure reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint obcaecat cupiditat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
+Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat.
+Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi.
+Nam liber tempor cum soluta nobis eleifend option congue nihil imperdiet doming id quod mazim placerat facer possim assum. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat.
+Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis.
+At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, At accusam aliquyam diam diam dolore dolores duo eirmod eos erat, et nonumy sed tempor et et invidunt justo labore Stet clita ea et gubergren, kasd magna no rebum. sanctus sea sed takimata ut vero voluptua. est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat.
+Consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.`
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/tags_macro.go b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/tags_macro.go
new file mode 100644
index 000000000000..dd3e0bf48a35
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/tags_macro.go
@@ -0,0 +1,149 @@
+package pongo2
+
+import (
+ "bytes"
+ "fmt"
+)
+
+type tagMacroNode struct {
+ position *Token
+ name string
+ argsOrder []string
+ args map[string]IEvaluator
+ exported bool
+
+ wrapper *NodeWrapper
+}
+
+func (node *tagMacroNode) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error {
+ ctx.Private[node.name] = func(args ...*Value) *Value {
+ return node.call(ctx, args...)
+ }
+
+ return nil
+}
+
+func (node *tagMacroNode) call(ctx *ExecutionContext, args ...*Value) *Value {
+ argsCtx := make(Context)
+
+ for k, v := range node.args {
+ if v == nil {
+ // User did not provided a default value
+ argsCtx[k] = nil
+ } else {
+ // Evaluate the default value
+ valueExpr, err := v.Evaluate(ctx)
+ if err != nil {
+ ctx.Logf(err.Error())
+ return AsSafeValue(err.Error())
+ }
+
+ argsCtx[k] = valueExpr
+ }
+ }
+
+ if len(args) > len(node.argsOrder) {
+ // Too many arguments, we're ignoring them and just logging into debug mode.
+ err := ctx.Error(fmt.Sprintf("Macro '%s' called with too many arguments (%d instead of %d).",
+ node.name, len(args), len(node.argsOrder)), nil).updateFromTokenIfNeeded(ctx.template, node.position)
+
+ ctx.Logf(err.Error()) // TODO: This is a workaround, because the error is not returned yet to the Execution()-methods
+ return AsSafeValue(err.Error())
+ }
+
+ // Make a context for the macro execution
+ macroCtx := NewChildExecutionContext(ctx)
+
+ // Register all arguments in the private context
+ macroCtx.Private.Update(argsCtx)
+
+ for idx, argValue := range args {
+ macroCtx.Private[node.argsOrder[idx]] = argValue.Interface()
+ }
+
+ var b bytes.Buffer
+ err := node.wrapper.Execute(macroCtx, &b)
+ if err != nil {
+ return AsSafeValue(err.updateFromTokenIfNeeded(ctx.template, node.position).Error())
+ }
+
+ return AsSafeValue(b.String())
+}
+
+func tagMacroParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Error) {
+ macroNode := &tagMacroNode{
+ position: start,
+ args: make(map[string]IEvaluator),
+ }
+
+ nameToken := arguments.MatchType(TokenIdentifier)
+ if nameToken == nil {
+ return nil, arguments.Error("Macro-tag needs at least an identifier as name.", nil)
+ }
+ macroNode.name = nameToken.Val
+
+ if arguments.MatchOne(TokenSymbol, "(") == nil {
+ return nil, arguments.Error("Expected '('.", nil)
+ }
+
+ for arguments.Match(TokenSymbol, ")") == nil {
+ argNameToken := arguments.MatchType(TokenIdentifier)
+ if argNameToken == nil {
+ return nil, arguments.Error("Expected argument name as identifier.", nil)
+ }
+ macroNode.argsOrder = append(macroNode.argsOrder, argNameToken.Val)
+
+ if arguments.Match(TokenSymbol, "=") != nil {
+ // Default expression follows
+ argDefaultExpr, err := arguments.ParseExpression()
+ if err != nil {
+ return nil, err
+ }
+ macroNode.args[argNameToken.Val] = argDefaultExpr
+ } else {
+ // No default expression
+ macroNode.args[argNameToken.Val] = nil
+ }
+
+ if arguments.Match(TokenSymbol, ")") != nil {
+ break
+ }
+ if arguments.Match(TokenSymbol, ",") == nil {
+ return nil, arguments.Error("Expected ',' or ')'.", nil)
+ }
+ }
+
+ if arguments.Match(TokenKeyword, "export") != nil {
+ macroNode.exported = true
+ }
+
+ if arguments.Remaining() > 0 {
+ return nil, arguments.Error("Malformed macro-tag.", nil)
+ }
+
+ // Body wrapping
+ wrapper, endargs, err := doc.WrapUntilTag("endmacro")
+ if err != nil {
+ return nil, err
+ }
+ macroNode.wrapper = wrapper
+
+ if endargs.Count() > 0 {
+ return nil, endargs.Error("Arguments not allowed here.", nil)
+ }
+
+ if macroNode.exported {
+ // Now register the macro if it wants to be exported
+ _, has := doc.template.exportedMacros[macroNode.name]
+ if has {
+ return nil, doc.Error(fmt.Sprintf("another macro with name '%s' already exported", macroNode.name), start)
+ }
+ doc.template.exportedMacros[macroNode.name] = macroNode
+ }
+
+ return macroNode, nil
+}
+
+func init() {
+ RegisterTag("macro", tagMacroParser)
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/tags_now.go b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/tags_now.go
new file mode 100644
index 000000000000..d9fa4a371134
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/tags_now.go
@@ -0,0 +1,50 @@
+package pongo2
+
+import (
+ "time"
+)
+
+type tagNowNode struct {
+ position *Token
+ format string
+ fake bool
+}
+
+func (node *tagNowNode) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error {
+ var t time.Time
+ if node.fake {
+ t = time.Date(2014, time.February, 05, 18, 31, 45, 00, time.UTC)
+ } else {
+ t = time.Now()
+ }
+
+ writer.WriteString(t.Format(node.format))
+
+ return nil
+}
+
+func tagNowParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Error) {
+ nowNode := &tagNowNode{
+ position: start,
+ }
+
+ formatToken := arguments.MatchType(TokenString)
+ if formatToken == nil {
+ return nil, arguments.Error("Expected a format string.", nil)
+ }
+ nowNode.format = formatToken.Val
+
+ if arguments.MatchOne(TokenIdentifier, "fake") != nil {
+ nowNode.fake = true
+ }
+
+ if arguments.Remaining() > 0 {
+ return nil, arguments.Error("Malformed now-tag arguments.", nil)
+ }
+
+ return nowNode, nil
+}
+
+func init() {
+ RegisterTag("now", tagNowParser)
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/tags_set.go b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/tags_set.go
new file mode 100644
index 000000000000..be121c12ac4f
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/tags_set.go
@@ -0,0 +1,50 @@
+package pongo2
+
+type tagSetNode struct {
+ name string
+ expression IEvaluator
+}
+
+func (node *tagSetNode) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error {
+ // Evaluate expression
+ value, err := node.expression.Evaluate(ctx)
+ if err != nil {
+ return err
+ }
+
+ ctx.Private[node.name] = value
+ return nil
+}
+
+func tagSetParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Error) {
+ node := &tagSetNode{}
+
+ // Parse variable name
+ typeToken := arguments.MatchType(TokenIdentifier)
+ if typeToken == nil {
+ return nil, arguments.Error("Expected an identifier.", nil)
+ }
+ node.name = typeToken.Val
+
+ if arguments.Match(TokenSymbol, "=") == nil {
+ return nil, arguments.Error("Expected '='.", nil)
+ }
+
+ // Variable expression
+ keyExpression, err := arguments.ParseExpression()
+ if err != nil {
+ return nil, err
+ }
+ node.expression = keyExpression
+
+ // Remaining arguments
+ if arguments.Remaining() > 0 {
+ return nil, arguments.Error("Malformed 'set'-tag arguments.", nil)
+ }
+
+ return node, nil
+}
+
+func init() {
+ RegisterTag("set", tagSetParser)
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/tags_spaceless.go b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/tags_spaceless.go
new file mode 100644
index 000000000000..4fa851ba45ba
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/tags_spaceless.go
@@ -0,0 +1,54 @@
+package pongo2
+
+import (
+ "bytes"
+ "regexp"
+)
+
+type tagSpacelessNode struct {
+ wrapper *NodeWrapper
+}
+
+var tagSpacelessRegexp = regexp.MustCompile(`(?U:(<.*>))([\t\n\v\f\r ]+)(?U:(<.*>))`)
+
+func (node *tagSpacelessNode) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error {
+ b := bytes.NewBuffer(make([]byte, 0, 1024)) // 1 KiB
+
+ err := node.wrapper.Execute(ctx, b)
+ if err != nil {
+ return err
+ }
+
+ s := b.String()
+ // Repeat this recursively
+ changed := true
+ for changed {
+ s2 := tagSpacelessRegexp.ReplaceAllString(s, "$1$3")
+ changed = s != s2
+ s = s2
+ }
+
+ writer.WriteString(s)
+
+ return nil
+}
+
+func tagSpacelessParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Error) {
+ spacelessNode := &tagSpacelessNode{}
+
+ wrapper, _, err := doc.WrapUntilTag("endspaceless")
+ if err != nil {
+ return nil, err
+ }
+ spacelessNode.wrapper = wrapper
+
+ if arguments.Remaining() > 0 {
+ return nil, arguments.Error("Malformed spaceless-tag arguments.", nil)
+ }
+
+ return spacelessNode, nil
+}
+
+func init() {
+ RegisterTag("spaceless", tagSpacelessParser)
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/tags_ssi.go b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/tags_ssi.go
new file mode 100644
index 000000000000..c33858d5f1bd
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/tags_ssi.go
@@ -0,0 +1,68 @@
+package pongo2
+
+import (
+ "io/ioutil"
+)
+
+type tagSSINode struct {
+ filename string
+ content string
+ template *Template
+}
+
+func (node *tagSSINode) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error {
+ if node.template != nil {
+ // Execute the template within the current context
+ includeCtx := make(Context)
+ includeCtx.Update(ctx.Public)
+ includeCtx.Update(ctx.Private)
+
+ err := node.template.execute(includeCtx, writer)
+ if err != nil {
+ return err.(*Error)
+ }
+ } else {
+ // Just print out the content
+ writer.WriteString(node.content)
+ }
+ return nil
+}
+
+func tagSSIParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Error) {
+ SSINode := &tagSSINode{}
+
+ if fileToken := arguments.MatchType(TokenString); fileToken != nil {
+ SSINode.filename = fileToken.Val
+
+ if arguments.Match(TokenIdentifier, "parsed") != nil {
+ // parsed
+ temporaryTpl, err := doc.template.set.FromFile(doc.template.set.resolveFilename(doc.template, fileToken.Val))
+ if err != nil {
+ return nil, err.(*Error).updateFromTokenIfNeeded(doc.template, fileToken)
+ }
+ SSINode.template = temporaryTpl
+ } else {
+ // plaintext
+ buf, err := ioutil.ReadFile(doc.template.set.resolveFilename(doc.template, fileToken.Val))
+ if err != nil {
+ return nil, (&Error{
+ Sender: "tag:ssi",
+ OrigError: err,
+ }).updateFromTokenIfNeeded(doc.template, fileToken)
+ }
+ SSINode.content = string(buf)
+ }
+ } else {
+ return nil, arguments.Error("First argument must be a string.", nil)
+ }
+
+ if arguments.Remaining() > 0 {
+ return nil, arguments.Error("Malformed SSI-tag argument.", nil)
+ }
+
+ return SSINode, nil
+}
+
+func init() {
+ RegisterTag("ssi", tagSSIParser)
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/tags_templatetag.go b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/tags_templatetag.go
new file mode 100644
index 000000000000..164b4dc3d07e
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/tags_templatetag.go
@@ -0,0 +1,45 @@
+package pongo2
+
+type tagTemplateTagNode struct {
+ content string
+}
+
+var templateTagMapping = map[string]string{
+ "openblock": "{%",
+ "closeblock": "%}",
+ "openvariable": "{{",
+ "closevariable": "}}",
+ "openbrace": "{",
+ "closebrace": "}",
+ "opencomment": "{#",
+ "closecomment": "#}",
+}
+
+func (node *tagTemplateTagNode) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error {
+ writer.WriteString(node.content)
+ return nil
+}
+
+func tagTemplateTagParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Error) {
+ ttNode := &tagTemplateTagNode{}
+
+ if argToken := arguments.MatchType(TokenIdentifier); argToken != nil {
+ output, found := templateTagMapping[argToken.Val]
+ if !found {
+ return nil, arguments.Error("Argument not found", argToken)
+ }
+ ttNode.content = output
+ } else {
+ return nil, arguments.Error("Identifier expected.", nil)
+ }
+
+ if arguments.Remaining() > 0 {
+ return nil, arguments.Error("Malformed templatetag-tag argument.", nil)
+ }
+
+ return ttNode, nil
+}
+
+func init() {
+ RegisterTag("templatetag", tagTemplateTagParser)
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/tags_widthratio.go b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/tags_widthratio.go
new file mode 100644
index 000000000000..70c9c3e8af2e
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/tags_widthratio.go
@@ -0,0 +1,83 @@
+package pongo2
+
+import (
+ "fmt"
+ "math"
+)
+
+type tagWidthratioNode struct {
+ position *Token
+ current, max IEvaluator
+ width IEvaluator
+ ctxName string
+}
+
+func (node *tagWidthratioNode) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error {
+ current, err := node.current.Evaluate(ctx)
+ if err != nil {
+ return err
+ }
+
+ max, err := node.max.Evaluate(ctx)
+ if err != nil {
+ return err
+ }
+
+ width, err := node.width.Evaluate(ctx)
+ if err != nil {
+ return err
+ }
+
+ value := int(math.Ceil(current.Float()/max.Float()*width.Float() + 0.5))
+
+ if node.ctxName == "" {
+ writer.WriteString(fmt.Sprintf("%d", value))
+ } else {
+ ctx.Private[node.ctxName] = value
+ }
+
+ return nil
+}
+
+func tagWidthratioParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Error) {
+ widthratioNode := &tagWidthratioNode{
+ position: start,
+ }
+
+ current, err := arguments.ParseExpression()
+ if err != nil {
+ return nil, err
+ }
+ widthratioNode.current = current
+
+ max, err := arguments.ParseExpression()
+ if err != nil {
+ return nil, err
+ }
+ widthratioNode.max = max
+
+ width, err := arguments.ParseExpression()
+ if err != nil {
+ return nil, err
+ }
+ widthratioNode.width = width
+
+ if arguments.MatchOne(TokenKeyword, "as") != nil {
+ // Name follows
+ nameToken := arguments.MatchType(TokenIdentifier)
+ if nameToken == nil {
+ return nil, arguments.Error("Expected name (identifier).", nil)
+ }
+ widthratioNode.ctxName = nameToken.Val
+ }
+
+ if arguments.Remaining() > 0 {
+ return nil, arguments.Error("Malformed widthratio-tag arguments.", nil)
+ }
+
+ return widthratioNode, nil
+}
+
+func init() {
+ RegisterTag("widthratio", tagWidthratioParser)
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/tags_with.go b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/tags_with.go
new file mode 100644
index 000000000000..32b3c1c42812
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/tags_with.go
@@ -0,0 +1,88 @@
+package pongo2
+
+type tagWithNode struct {
+ withPairs map[string]IEvaluator
+ wrapper *NodeWrapper
+}
+
+func (node *tagWithNode) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error {
+ //new context for block
+ withctx := NewChildExecutionContext(ctx)
+
+ // Put all custom with-pairs into the context
+ for key, value := range node.withPairs {
+ val, err := value.Evaluate(ctx)
+ if err != nil {
+ return err
+ }
+ withctx.Private[key] = val
+ }
+
+ return node.wrapper.Execute(withctx, writer)
+}
+
+func tagWithParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Error) {
+ withNode := &tagWithNode{
+ withPairs: make(map[string]IEvaluator),
+ }
+
+ if arguments.Count() == 0 {
+ return nil, arguments.Error("Tag 'with' requires at least one argument.", nil)
+ }
+
+ wrapper, endargs, err := doc.WrapUntilTag("endwith")
+ if err != nil {
+ return nil, err
+ }
+ withNode.wrapper = wrapper
+
+ if endargs.Count() > 0 {
+ return nil, endargs.Error("Arguments not allowed here.", nil)
+ }
+
+ // Scan through all arguments to see which style the user uses (old or new style).
+ // If we find any "as" keyword we will enforce old style; otherwise we will use new style.
+ oldStyle := false // by default we're using the new_style
+ for i := 0; i < arguments.Count(); i++ {
+ if arguments.PeekN(i, TokenKeyword, "as") != nil {
+ oldStyle = true
+ break
+ }
+ }
+
+ for arguments.Remaining() > 0 {
+ if oldStyle {
+ valueExpr, err := arguments.ParseExpression()
+ if err != nil {
+ return nil, err
+ }
+ if arguments.Match(TokenKeyword, "as") == nil {
+ return nil, arguments.Error("Expected 'as' keyword.", nil)
+ }
+ keyToken := arguments.MatchType(TokenIdentifier)
+ if keyToken == nil {
+ return nil, arguments.Error("Expected an identifier", nil)
+ }
+ withNode.withPairs[keyToken.Val] = valueExpr
+ } else {
+ keyToken := arguments.MatchType(TokenIdentifier)
+ if keyToken == nil {
+ return nil, arguments.Error("Expected an identifier", nil)
+ }
+ if arguments.Match(TokenSymbol, "=") == nil {
+ return nil, arguments.Error("Expected '='.", nil)
+ }
+ valueExpr, err := arguments.ParseExpression()
+ if err != nil {
+ return nil, err
+ }
+ withNode.withPairs[keyToken.Val] = valueExpr
+ }
+ }
+
+ return withNode, nil
+}
+
+func init() {
+ RegisterTag("with", tagWithParser)
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/template.go b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/template.go
new file mode 100644
index 000000000000..47666c940c91
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/template.go
@@ -0,0 +1,276 @@
+package pongo2
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "strings"
+)
+
+type TemplateWriter interface {
+ io.Writer
+ WriteString(string) (int, error)
+}
+
+type templateWriter struct {
+ w io.Writer
+}
+
+func (tw *templateWriter) WriteString(s string) (int, error) {
+ return tw.w.Write([]byte(s))
+}
+
+func (tw *templateWriter) Write(b []byte) (int, error) {
+ return tw.w.Write(b)
+}
+
+type Template struct {
+ set *TemplateSet
+
+ // Input
+ isTplString bool
+ name string
+ tpl string
+ size int
+
+ // Calculation
+ tokens []*Token
+ parser *Parser
+
+ // first come, first serve (it's important to not override existing entries in here)
+ level int
+ parent *Template
+ child *Template
+ blocks map[string]*NodeWrapper
+ exportedMacros map[string]*tagMacroNode
+
+ // Output
+ root *nodeDocument
+
+ // Options allow you to change the behavior of template-engine.
+ // You can change the options before calling the Execute method.
+ Options *Options
+}
+
+func newTemplateString(set *TemplateSet, tpl []byte) (*Template, error) {
+ return newTemplate(set, "", true, tpl)
+}
+
+func newTemplate(set *TemplateSet, name string, isTplString bool, tpl []byte) (*Template, error) {
+ strTpl := string(tpl)
+
+ // Create the template
+ t := &Template{
+ set: set,
+ isTplString: isTplString,
+ name: name,
+ tpl: strTpl,
+ size: len(strTpl),
+ blocks: make(map[string]*NodeWrapper),
+ exportedMacros: make(map[string]*tagMacroNode),
+ Options: newOptions(),
+ }
+ // Copy all settings from another Options.
+ t.Options.Update(set.Options)
+
+ // Tokenize it
+ tokens, err := lex(name, strTpl)
+ if err != nil {
+ return nil, err
+ }
+ t.tokens = tokens
+
+ // For debugging purposes, show all tokens:
+ /*for i, t := range tokens {
+ fmt.Printf("%3d. %s\n", i, t)
+ }*/
+
+ // Parse it
+ err = t.parse()
+ if err != nil {
+ return nil, err
+ }
+
+ return t, nil
+}
+
+func (tpl *Template) newContextForExecution(context Context) (*Template, *ExecutionContext, error) {
+ if tpl.Options.TrimBlocks || tpl.Options.LStripBlocks {
+ // Issue #94 https://github.com/flosch/pongo2/issues/94
+ // If an application configures pongo2 template to trim_blocks,
+ // the first newline after a template tag is removed automatically (like in PHP).
+ prev := &Token{
+ Typ: TokenHTML,
+ Val: "\n",
+ }
+
+ for _, t := range tpl.tokens {
+ if tpl.Options.LStripBlocks {
+ if prev.Typ == TokenHTML && t.Typ != TokenHTML && t.Val == "{%" {
+ prev.Val = strings.TrimRight(prev.Val, "\t ")
+ }
+ }
+
+ if tpl.Options.TrimBlocks {
+ if prev.Typ != TokenHTML && t.Typ == TokenHTML && prev.Val == "%}" {
+ if len(t.Val) > 0 && t.Val[0] == '\n' {
+ t.Val = t.Val[1:len(t.Val)]
+ }
+ }
+ }
+
+ prev = t
+ }
+ }
+
+ // Determine the parent to be executed (for template inheritance)
+ parent := tpl
+ for parent.parent != nil {
+ parent = parent.parent
+ }
+
+ // Create context if none is given
+ newContext := make(Context)
+ newContext.Update(tpl.set.Globals)
+
+ if context != nil {
+ newContext.Update(context)
+
+ if len(newContext) > 0 {
+ // Check for context name syntax
+ err := newContext.checkForValidIdentifiers()
+ if err != nil {
+ return parent, nil, err
+ }
+
+ // Check for clashes with macro names
+ for k := range newContext {
+ _, has := tpl.exportedMacros[k]
+ if has {
+ return parent, nil, &Error{
+ Filename: tpl.name,
+ Sender: "execution",
+ OrigError: fmt.Errorf("context key name '%s' clashes with macro '%s'", k, k),
+ }
+ }
+ }
+ }
+ }
+
+ // Create operational context
+ ctx := newExecutionContext(parent, newContext)
+
+ return parent, ctx, nil
+}
+
+func (tpl *Template) execute(context Context, writer TemplateWriter) error {
+ parent, ctx, err := tpl.newContextForExecution(context)
+ if err != nil {
+ return err
+ }
+
+ // Run the selected document
+ if err := parent.root.Execute(ctx, writer); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (tpl *Template) newTemplateWriterAndExecute(context Context, writer io.Writer) error {
+ return tpl.execute(context, &templateWriter{w: writer})
+}
+
+func (tpl *Template) newBufferAndExecute(context Context) (*bytes.Buffer, error) {
+ // Create output buffer
+ // We assume that the rendered template will be 30% larger
+ buffer := bytes.NewBuffer(make([]byte, 0, int(float64(tpl.size)*1.3)))
+ if err := tpl.execute(context, buffer); err != nil {
+ return nil, err
+ }
+ return buffer, nil
+}
+
+// Executes the template with the given context and writes to writer (io.Writer)
+// on success. Context can be nil. Nothing is written on error; instead the error
+// is being returned.
+func (tpl *Template) ExecuteWriter(context Context, writer io.Writer) error {
+ buf, err := tpl.newBufferAndExecute(context)
+ if err != nil {
+ return err
+ }
+ _, err = buf.WriteTo(writer)
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+// Same as ExecuteWriter. The only difference between both functions is that
+// this function might already have written parts of the generated template in the
+// case of an execution error because there's no intermediate buffer involved for
+// performance reasons. This is handy if you need high performance template
+// generation or if you want to manage your own pool of buffers.
+func (tpl *Template) ExecuteWriterUnbuffered(context Context, writer io.Writer) error {
+ return tpl.newTemplateWriterAndExecute(context, writer)
+}
+
+// Executes the template and returns the rendered template as a []byte
+func (tpl *Template) ExecuteBytes(context Context) ([]byte, error) {
+ // Execute template
+ buffer, err := tpl.newBufferAndExecute(context)
+ if err != nil {
+ return nil, err
+ }
+ return buffer.Bytes(), nil
+}
+
+// Executes the template and returns the rendered template as a string
+func (tpl *Template) Execute(context Context) (string, error) {
+ // Execute template
+ buffer, err := tpl.newBufferAndExecute(context)
+ if err != nil {
+ return "", err
+ }
+
+ return buffer.String(), nil
+
+}
+
+func (tpl *Template) ExecuteBlocks(context Context, blocks []string) (map[string]string, error) {
+ var parents []*Template
+ result := make(map[string]string)
+
+ parent := tpl
+ for parent != nil {
+ parents = append(parents, parent)
+ parent = parent.parent
+ }
+
+ for _, t := range parents {
+ buffer := bytes.NewBuffer(make([]byte, 0, int(float64(t.size)*1.3)))
+ _, ctx, err := t.newContextForExecution(context)
+ if err != nil {
+ return nil, err
+ }
+ for _, blockName := range blocks {
+ if _, ok := result[blockName]; ok {
+ continue
+ }
+ if blockWrapper, ok := t.blocks[blockName]; ok {
+ bErr := blockWrapper.Execute(ctx, buffer)
+ if bErr != nil {
+ return nil, bErr
+ }
+ result[blockName] = buffer.String()
+ buffer.Reset()
+ }
+ }
+ // We have found all blocks
+ if len(blocks) == len(result) {
+ break
+ }
+ }
+
+ return result, nil
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/template_loader.go b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/template_loader.go
new file mode 100644
index 000000000000..303d55e685eb
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/template_loader.go
@@ -0,0 +1,213 @@
+package pongo2
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "log"
+ "net/http"
+ "os"
+ "path/filepath"
+)
+
+// LocalFilesystemLoader represents a local filesystem loader with basic
+// BaseDirectory capabilities. The access to the local filesystem is unrestricted.
+type LocalFilesystemLoader struct {
+ baseDir string
+}
+
+// MustNewLocalFileSystemLoader creates a new LocalFilesystemLoader instance
+// and panics if there's any error during instantiation. The parameters
+// are the same like NewLocalFileSystemLoader.
+func MustNewLocalFileSystemLoader(baseDir string) *LocalFilesystemLoader {
+ fs, err := NewLocalFileSystemLoader(baseDir)
+ if err != nil {
+ log.Panic(err)
+ }
+ return fs
+}
+
+// NewLocalFileSystemLoader creates a new LocalFilesystemLoader and allows
+// templatesto be loaded from disk (unrestricted). If any base directory
+// is given (or being set using SetBaseDir), this base directory is being used
+// for path calculation in template inclusions/imports. Otherwise the path
+// is calculated based relatively to the including template's path.
+func NewLocalFileSystemLoader(baseDir string) (*LocalFilesystemLoader, error) {
+ fs := &LocalFilesystemLoader{}
+ if baseDir != "" {
+ if err := fs.SetBaseDir(baseDir); err != nil {
+ return nil, err
+ }
+ }
+ return fs, nil
+}
+
+// SetBaseDir sets the template's base directory. This directory will
+// be used for any relative path in filters, tags and From*-functions to determine
+// your template. See the comment for NewLocalFileSystemLoader as well.
+func (fs *LocalFilesystemLoader) SetBaseDir(path string) error {
+ // Make the path absolute
+ if !filepath.IsAbs(path) {
+ abs, err := filepath.Abs(path)
+ if err != nil {
+ return err
+ }
+ path = abs
+ }
+
+ // Check for existence
+ fi, err := os.Stat(path)
+ if err != nil {
+ return err
+ }
+ if !fi.IsDir() {
+ return fmt.Errorf("The given path '%s' is not a directory.", path)
+ }
+
+ fs.baseDir = path
+ return nil
+}
+
+// Get reads the path's content from your local filesystem.
+func (fs *LocalFilesystemLoader) Get(path string) (io.Reader, error) {
+ buf, err := ioutil.ReadFile(path)
+ if err != nil {
+ return nil, err
+ }
+ return bytes.NewReader(buf), nil
+}
+
+// Abs resolves a filename relative to the base directory. Absolute paths are allowed.
+// When there's no base dir set, the absolute path to the filename
+// will be calculated based on either the provided base directory (which
+// might be a path of a template which includes another template) or
+// the current working directory.
+func (fs *LocalFilesystemLoader) Abs(base, name string) string {
+ if filepath.IsAbs(name) {
+ return name
+ }
+
+ // Our own base dir has always priority; if there's none
+ // we use the path provided in base.
+ var err error
+ if fs.baseDir == "" {
+ if base == "" {
+ base, err = os.Getwd()
+ if err != nil {
+ panic(err)
+ }
+ return filepath.Join(base, name)
+ }
+
+ return filepath.Join(filepath.Dir(base), name)
+ }
+
+ return filepath.Join(fs.baseDir, name)
+}
+
+// SandboxedFilesystemLoader is still WIP.
+type SandboxedFilesystemLoader struct {
+ *LocalFilesystemLoader
+}
+
+// NewSandboxedFilesystemLoader creates a new sandboxed local file system instance.
+func NewSandboxedFilesystemLoader(baseDir string) (*SandboxedFilesystemLoader, error) {
+ fs, err := NewLocalFileSystemLoader(baseDir)
+ if err != nil {
+ return nil, err
+ }
+ return &SandboxedFilesystemLoader{
+ LocalFilesystemLoader: fs,
+ }, nil
+}
+
+// Move sandbox to a virtual fs
+
+/*
+if len(set.SandboxDirectories) > 0 {
+ defer func() {
+ // Remove any ".." or other crap
+ resolvedPath = filepath.Clean(resolvedPath)
+
+ // Make the path absolute
+ absPath, err := filepath.Abs(resolvedPath)
+ if err != nil {
+ panic(err)
+ }
+ resolvedPath = absPath
+
+ // Check against the sandbox directories (once one pattern matches, we're done and can allow it)
+ for _, pattern := range set.SandboxDirectories {
+ matched, err := filepath.Match(pattern, resolvedPath)
+ if err != nil {
+ panic("Wrong sandbox directory match pattern (see http://golang.org/pkg/path/filepath/#Match).")
+ }
+ if matched {
+ // OK!
+ return
+ }
+ }
+
+ // No pattern matched, we have to log+deny the request
+ set.logf("Access attempt outside of the sandbox directories (blocked): '%s'", resolvedPath)
+ resolvedPath = ""
+ }()
+}
+*/
+
+// HttpFilesystemLoader supports loading templates
+// from an http.FileSystem - useful for using one of several
+// file-to-code generators that packs static files into
+// a go binary (ex: https://github.com/jteeuwen/go-bindata)
+type HttpFilesystemLoader struct {
+ fs http.FileSystem
+ baseDir string
+}
+
+// MustNewHttpFileSystemLoader creates a new HttpFilesystemLoader instance
+// and panics if there's any error during instantiation. The parameters
+// are the same like NewHttpFilesystemLoader.
+func MustNewHttpFileSystemLoader(httpfs http.FileSystem, baseDir string) *HttpFilesystemLoader {
+ fs, err := NewHttpFileSystemLoader(httpfs, baseDir)
+ if err != nil {
+ log.Panic(err)
+ }
+ return fs
+}
+
+// NewHttpFileSystemLoader creates a new HttpFileSystemLoader and allows
+// templates to be loaded from the virtual filesystem. The path
+// is calculated based relatively from the root of the http.Filesystem.
+func NewHttpFileSystemLoader(httpfs http.FileSystem, baseDir string) (*HttpFilesystemLoader, error) {
+ hfs := &HttpFilesystemLoader{
+ fs: httpfs,
+ baseDir: baseDir,
+ }
+ if httpfs == nil {
+ err := errors.New("httpfs cannot be nil")
+ return nil, err
+ }
+ return hfs, nil
+}
+
+// Abs in this instance simply returns the filename, since
+// there's no potential for an unexpanded path in an http.FileSystem
+func (h *HttpFilesystemLoader) Abs(base, name string) string {
+ return name
+}
+
+// Get returns an io.Reader where the template's content can be read from.
+func (h *HttpFilesystemLoader) Get(path string) (io.Reader, error) {
+ fullPath := path
+ if h.baseDir != "" {
+ fullPath = fmt.Sprintf(
+ "%s/%s",
+ h.baseDir,
+ fullPath,
+ )
+ }
+
+ return h.fs.Open(fullPath)
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/template_sets.go b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/template_sets.go
new file mode 100644
index 000000000000..4b1e43da1930
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/template_sets.go
@@ -0,0 +1,305 @@
+package pongo2
+
+import (
+ "fmt"
+ "io"
+ "io/ioutil"
+ "log"
+ "os"
+ "sync"
+
+ "errors"
+)
+
+// TemplateLoader allows to implement a virtual file system.
+type TemplateLoader interface {
+ // Abs calculates the path to a given template. Whenever a path must be resolved
+ // due to an import from another template, the base equals the parent template's path.
+ Abs(base, name string) string
+
+ // Get returns an io.Reader where the template's content can be read from.
+ Get(path string) (io.Reader, error)
+}
+
+// TemplateSet allows you to create your own group of templates with their own
+// global context (which is shared among all members of the set) and their own
+// configuration.
+// It's useful for a separation of different kind of templates
+// (e. g. web templates vs. mail templates).
+type TemplateSet struct {
+ name string
+ loaders []TemplateLoader
+
+ // Globals will be provided to all templates created within this template set
+ Globals Context
+
+ // If debug is true (default false), ExecutionContext.Logf() will work and output
+ // to STDOUT. Furthermore, FromCache() won't cache the templates.
+ // Make sure to synchronize the access to it in case you're changing this
+ // variable during program execution (and template compilation/execution).
+ Debug bool
+
+ // Options allow you to change the behavior of template-engine.
+ // You can change the options before calling the Execute method.
+ Options *Options
+
+ // Sandbox features
+ // - Disallow access to specific tags and/or filters (using BanTag() and BanFilter())
+ //
+ // For efficiency reasons you can ban tags/filters only *before* you have
+ // added your first template to the set (restrictions are statically checked).
+ // After you added one, it's not possible anymore (for your personal security).
+ firstTemplateCreated bool
+ bannedTags map[string]bool
+ bannedFilters map[string]bool
+
+ // Template cache (for FromCache())
+ templateCache map[string]*Template
+ templateCacheMutex sync.Mutex
+}
+
+// NewSet can be used to create sets with different kind of templates
+// (e. g. web from mail templates), with different globals or
+// other configurations.
+func NewSet(name string, loaders ...TemplateLoader) *TemplateSet {
+ if len(loaders) == 0 {
+ panic(fmt.Errorf("at least one template loader must be specified"))
+ }
+
+ return &TemplateSet{
+ name: name,
+ loaders: loaders,
+ Globals: make(Context),
+ bannedTags: make(map[string]bool),
+ bannedFilters: make(map[string]bool),
+ templateCache: make(map[string]*Template),
+ Options: newOptions(),
+ }
+}
+
+func (set *TemplateSet) AddLoader(loaders ...TemplateLoader) {
+ set.loaders = append(set.loaders, loaders...)
+}
+
+func (set *TemplateSet) resolveFilename(tpl *Template, path string) string {
+ return set.resolveFilenameForLoader(set.loaders[0], tpl, path)
+}
+
+func (set *TemplateSet) resolveFilenameForLoader(loader TemplateLoader, tpl *Template, path string) string {
+ name := ""
+ if tpl != nil && tpl.isTplString {
+ return path
+ }
+ if tpl != nil {
+ name = tpl.name
+ }
+
+ return loader.Abs(name, path)
+}
+
+// BanTag bans a specific tag for this template set. See more in the documentation for TemplateSet.
+func (set *TemplateSet) BanTag(name string) error {
+ _, has := tags[name]
+ if !has {
+ return fmt.Errorf("tag '%s' not found", name)
+ }
+ if set.firstTemplateCreated {
+ return errors.New("you cannot ban any tags after you've added your first template to your template set")
+ }
+ _, has = set.bannedTags[name]
+ if has {
+ return fmt.Errorf("tag '%s' is already banned", name)
+ }
+ set.bannedTags[name] = true
+
+ return nil
+}
+
+// BanFilter bans a specific filter for this template set. See more in the documentation for TemplateSet.
+func (set *TemplateSet) BanFilter(name string) error {
+ _, has := filters[name]
+ if !has {
+ return fmt.Errorf("filter '%s' not found", name)
+ }
+ if set.firstTemplateCreated {
+ return errors.New("you cannot ban any filters after you've added your first template to your template set")
+ }
+ _, has = set.bannedFilters[name]
+ if has {
+ return fmt.Errorf("filter '%s' is already banned", name)
+ }
+ set.bannedFilters[name] = true
+
+ return nil
+}
+
+func (set *TemplateSet) resolveTemplate(tpl *Template, path string) (name string, loader TemplateLoader, fd io.Reader, err error) {
+ // iterate over loaders until we appear to have a valid template
+ for _, loader = range set.loaders {
+ name = set.resolveFilenameForLoader(loader, tpl, path)
+ fd, err = loader.Get(name)
+ if err == nil {
+ return
+ }
+ }
+
+ return path, nil, nil, fmt.Errorf("unable to resolve template")
+}
+
+// CleanCache cleans the template cache. If filenames is not empty,
+// it will remove the template caches of those filenames.
+// Or it will empty the whole template cache. It is thread-safe.
+func (set *TemplateSet) CleanCache(filenames ...string) {
+ set.templateCacheMutex.Lock()
+ defer set.templateCacheMutex.Unlock()
+
+ if len(filenames) == 0 {
+ set.templateCache = make(map[string]*Template, len(set.templateCache))
+ }
+
+ for _, filename := range filenames {
+ delete(set.templateCache, set.resolveFilename(nil, filename))
+ }
+}
+
+// FromCache is a convenient method to cache templates. It is thread-safe
+// and will only compile the template associated with a filename once.
+// If TemplateSet.Debug is true (for example during development phase),
+// FromCache() will not cache the template and instead recompile it on any
+// call (to make changes to a template live instantaneously).
+func (set *TemplateSet) FromCache(filename string) (*Template, error) {
+ if set.Debug {
+ // Recompile on any request
+ return set.FromFile(filename)
+ }
+ // Cache the template
+ cleanedFilename := set.resolveFilename(nil, filename)
+
+ set.templateCacheMutex.Lock()
+ defer set.templateCacheMutex.Unlock()
+
+ tpl, has := set.templateCache[cleanedFilename]
+
+ // Cache miss
+ if !has {
+ tpl, err := set.FromFile(cleanedFilename)
+ if err != nil {
+ return nil, err
+ }
+ set.templateCache[cleanedFilename] = tpl
+ return tpl, nil
+ }
+
+ // Cache hit
+ return tpl, nil
+}
+
+// FromString loads a template from string and returns a Template instance.
+func (set *TemplateSet) FromString(tpl string) (*Template, error) {
+ set.firstTemplateCreated = true
+
+ return newTemplateString(set, []byte(tpl))
+}
+
+// FromBytes loads a template from bytes and returns a Template instance.
+func (set *TemplateSet) FromBytes(tpl []byte) (*Template, error) {
+ set.firstTemplateCreated = true
+
+ return newTemplateString(set, tpl)
+}
+
+// FromFile loads a template from a filename and returns a Template instance.
+func (set *TemplateSet) FromFile(filename string) (*Template, error) {
+ set.firstTemplateCreated = true
+
+ _, _, fd, err := set.resolveTemplate(nil, filename)
+ if err != nil {
+ return nil, &Error{
+ Filename: filename,
+ Sender: "fromfile",
+ OrigError: err,
+ }
+ }
+ buf, err := ioutil.ReadAll(fd)
+ if err != nil {
+ return nil, &Error{
+ Filename: filename,
+ Sender: "fromfile",
+ OrigError: err,
+ }
+ }
+
+ return newTemplate(set, filename, false, buf)
+}
+
+// RenderTemplateString is a shortcut and renders a template string directly.
+func (set *TemplateSet) RenderTemplateString(s string, ctx Context) (string, error) {
+ set.firstTemplateCreated = true
+
+ tpl := Must(set.FromString(s))
+ result, err := tpl.Execute(ctx)
+ if err != nil {
+ return "", err
+ }
+ return result, nil
+}
+
+// RenderTemplateBytes is a shortcut and renders template bytes directly.
+func (set *TemplateSet) RenderTemplateBytes(b []byte, ctx Context) (string, error) {
+ set.firstTemplateCreated = true
+
+ tpl := Must(set.FromBytes(b))
+ result, err := tpl.Execute(ctx)
+ if err != nil {
+ return "", err
+ }
+ return result, nil
+}
+
+// RenderTemplateFile is a shortcut and renders a template file directly.
+func (set *TemplateSet) RenderTemplateFile(fn string, ctx Context) (string, error) {
+ set.firstTemplateCreated = true
+
+ tpl := Must(set.FromFile(fn))
+ result, err := tpl.Execute(ctx)
+ if err != nil {
+ return "", err
+ }
+ return result, nil
+}
+
+func (set *TemplateSet) logf(format string, args ...interface{}) {
+ if set.Debug {
+ logger.Printf(fmt.Sprintf("[template set: %s] %s", set.name, format), args...)
+ }
+}
+
+// Logging function (internally used)
+func logf(format string, items ...interface{}) {
+ if debug {
+ logger.Printf(format, items...)
+ }
+}
+
+var (
+ debug bool // internal debugging
+ logger = log.New(os.Stdout, "[pongo2] ", log.LstdFlags|log.Lshortfile)
+
+ // DefaultLoader allows the default un-sandboxed access to the local file
+ // system and is being used by the DefaultSet.
+ DefaultLoader = MustNewLocalFileSystemLoader("")
+
+ // DefaultSet is a set created for you for convinience reasons.
+ DefaultSet = NewSet("default", DefaultLoader)
+
+ // Methods on the default set
+ FromString = DefaultSet.FromString
+ FromBytes = DefaultSet.FromBytes
+ FromFile = DefaultSet.FromFile
+ FromCache = DefaultSet.FromCache
+ RenderTemplateString = DefaultSet.RenderTemplateString
+ RenderTemplateFile = DefaultSet.RenderTemplateFile
+
+ // Globals for the default set
+ Globals = DefaultSet.Globals
+)
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/value.go b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/value.go
new file mode 100644
index 000000000000..d51d81450436
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/value.go
@@ -0,0 +1,540 @@
+package pongo2
+
+import (
+ "fmt"
+ "reflect"
+ "sort"
+ "strconv"
+ "strings"
+ "time"
+)
+
+type Value struct {
+ val reflect.Value
+ safe bool // used to indicate whether a Value needs explicit escaping in the template
+}
+
+// AsValue converts any given value to a pongo2.Value
+// Usually being used within own functions passed to a template
+// through a Context or within filter functions.
+//
+// Example:
+// AsValue("my string")
+func AsValue(i interface{}) *Value {
+ return &Value{
+ val: reflect.ValueOf(i),
+ }
+}
+
+// AsSafeValue works like AsValue, but does not apply the 'escape' filter.
+func AsSafeValue(i interface{}) *Value {
+ return &Value{
+ val: reflect.ValueOf(i),
+ safe: true,
+ }
+}
+
+func (v *Value) getResolvedValue() reflect.Value {
+ if v.val.IsValid() && v.val.Kind() == reflect.Ptr {
+ return v.val.Elem()
+ }
+ return v.val
+}
+
+// IsString checks whether the underlying value is a string
+func (v *Value) IsString() bool {
+ return v.getResolvedValue().Kind() == reflect.String
+}
+
+// IsBool checks whether the underlying value is a bool
+func (v *Value) IsBool() bool {
+ return v.getResolvedValue().Kind() == reflect.Bool
+}
+
+// IsFloat checks whether the underlying value is a float
+func (v *Value) IsFloat() bool {
+ return v.getResolvedValue().Kind() == reflect.Float32 ||
+ v.getResolvedValue().Kind() == reflect.Float64
+}
+
+// IsInteger checks whether the underlying value is an integer
+func (v *Value) IsInteger() bool {
+ return v.getResolvedValue().Kind() == reflect.Int ||
+ v.getResolvedValue().Kind() == reflect.Int8 ||
+ v.getResolvedValue().Kind() == reflect.Int16 ||
+ v.getResolvedValue().Kind() == reflect.Int32 ||
+ v.getResolvedValue().Kind() == reflect.Int64 ||
+ v.getResolvedValue().Kind() == reflect.Uint ||
+ v.getResolvedValue().Kind() == reflect.Uint8 ||
+ v.getResolvedValue().Kind() == reflect.Uint16 ||
+ v.getResolvedValue().Kind() == reflect.Uint32 ||
+ v.getResolvedValue().Kind() == reflect.Uint64
+}
+
+// IsNumber checks whether the underlying value is either an integer
+// or a float.
+func (v *Value) IsNumber() bool {
+ return v.IsInteger() || v.IsFloat()
+}
+
+// IsTime checks whether the underlying value is a time.Time.
+func (v *Value) IsTime() bool {
+ _, ok := v.Interface().(time.Time)
+ return ok
+}
+
+// IsNil checks whether the underlying value is NIL
+func (v *Value) IsNil() bool {
+ //fmt.Printf("%+v\n", v.getResolvedValue().Type().String())
+ return !v.getResolvedValue().IsValid()
+}
+
+// String returns a string for the underlying value. If this value is not
+// of type string, pongo2 tries to convert it. Currently the following
+// types for underlying values are supported:
+//
+// 1. string
+// 2. int/uint (any size)
+// 3. float (any precision)
+// 4. bool
+// 5. time.Time
+// 6. String() will be called on the underlying value if provided
+//
+// NIL values will lead to an empty string. Unsupported types are leading
+// to their respective type name.
+func (v *Value) String() string {
+ if v.IsNil() {
+ return ""
+ }
+
+ switch v.getResolvedValue().Kind() {
+ case reflect.String:
+ return v.getResolvedValue().String()
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ return strconv.FormatInt(v.getResolvedValue().Int(), 10)
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ return strconv.FormatUint(v.getResolvedValue().Uint(), 10)
+ case reflect.Float32, reflect.Float64:
+ return fmt.Sprintf("%f", v.getResolvedValue().Float())
+ case reflect.Bool:
+ if v.Bool() {
+ return "True"
+ }
+ return "False"
+ case reflect.Struct:
+ if t, ok := v.Interface().(fmt.Stringer); ok {
+ return t.String()
+ }
+ }
+
+ logf("Value.String() not implemented for type: %s\n", v.getResolvedValue().Kind().String())
+ return v.getResolvedValue().String()
+}
+
+// Integer returns the underlying value as an integer (converts the underlying
+// value, if necessary). If it's not possible to convert the underlying value,
+// it will return 0.
+func (v *Value) Integer() int {
+ switch v.getResolvedValue().Kind() {
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ return int(v.getResolvedValue().Int())
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ return int(v.getResolvedValue().Uint())
+ case reflect.Float32, reflect.Float64:
+ return int(v.getResolvedValue().Float())
+ case reflect.String:
+ // Try to convert from string to int (base 10)
+ f, err := strconv.ParseFloat(v.getResolvedValue().String(), 64)
+ if err != nil {
+ return 0
+ }
+ return int(f)
+ default:
+ logf("Value.Integer() not available for type: %s\n", v.getResolvedValue().Kind().String())
+ return 0
+ }
+}
+
+// Float returns the underlying value as a float (converts the underlying
+// value, if necessary). If it's not possible to convert the underlying value,
+// it will return 0.0.
+func (v *Value) Float() float64 {
+ switch v.getResolvedValue().Kind() {
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ return float64(v.getResolvedValue().Int())
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ return float64(v.getResolvedValue().Uint())
+ case reflect.Float32, reflect.Float64:
+ return v.getResolvedValue().Float()
+ case reflect.String:
+ // Try to convert from string to float64 (base 10)
+ f, err := strconv.ParseFloat(v.getResolvedValue().String(), 64)
+ if err != nil {
+ return 0.0
+ }
+ return f
+ default:
+ logf("Value.Float() not available for type: %s\n", v.getResolvedValue().Kind().String())
+ return 0.0
+ }
+}
+
+// Bool returns the underlying value as bool. If the value is not bool, false
+// will always be returned. If you're looking for true/false-evaluation of the
+// underlying value, have a look on the IsTrue()-function.
+func (v *Value) Bool() bool {
+ switch v.getResolvedValue().Kind() {
+ case reflect.Bool:
+ return v.getResolvedValue().Bool()
+ default:
+ logf("Value.Bool() not available for type: %s\n", v.getResolvedValue().Kind().String())
+ return false
+ }
+}
+
+// Time returns the underlying value as time.Time.
+// If the underlying value is not a time.Time, it returns the zero value of time.Time.
+func (v *Value) Time() time.Time {
+ tm, ok := v.Interface().(time.Time)
+ if ok {
+ return tm
+ }
+ return time.Time{}
+}
+
+// IsTrue tries to evaluate the underlying value the Pythonic-way:
+//
+// Returns TRUE in one the following cases:
+//
+// * int != 0
+// * uint != 0
+// * float != 0.0
+// * len(array/chan/map/slice/string) > 0
+// * bool == true
+// * underlying value is a struct
+//
+// Otherwise returns always FALSE.
+func (v *Value) IsTrue() bool {
+ switch v.getResolvedValue().Kind() {
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ return v.getResolvedValue().Int() != 0
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ return v.getResolvedValue().Uint() != 0
+ case reflect.Float32, reflect.Float64:
+ return v.getResolvedValue().Float() != 0
+ case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice, reflect.String:
+ return v.getResolvedValue().Len() > 0
+ case reflect.Bool:
+ return v.getResolvedValue().Bool()
+ case reflect.Struct:
+ return true // struct instance is always true
+ default:
+ logf("Value.IsTrue() not available for type: %s\n", v.getResolvedValue().Kind().String())
+ return false
+ }
+}
+
+// Negate tries to negate the underlying value. It's mainly used for
+// the NOT-operator and in conjunction with a call to
+// return_value.IsTrue() afterwards.
+//
+// Example:
+// AsValue(1).Negate().IsTrue() == false
+func (v *Value) Negate() *Value {
+ switch v.getResolvedValue().Kind() {
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
+ reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ if v.Integer() != 0 {
+ return AsValue(0)
+ }
+ return AsValue(1)
+ case reflect.Float32, reflect.Float64:
+ if v.Float() != 0.0 {
+ return AsValue(float64(0.0))
+ }
+ return AsValue(float64(1.1))
+ case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice, reflect.String:
+ return AsValue(v.getResolvedValue().Len() == 0)
+ case reflect.Bool:
+ return AsValue(!v.getResolvedValue().Bool())
+ case reflect.Struct:
+ return AsValue(false)
+ default:
+ logf("Value.IsTrue() not available for type: %s\n", v.getResolvedValue().Kind().String())
+ return AsValue(true)
+ }
+}
+
+// Len returns the length for an array, chan, map, slice or string.
+// Otherwise it will return 0.
+func (v *Value) Len() int {
+ switch v.getResolvedValue().Kind() {
+ case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice:
+ return v.getResolvedValue().Len()
+ case reflect.String:
+ runes := []rune(v.getResolvedValue().String())
+ return len(runes)
+ default:
+ logf("Value.Len() not available for type: %s\n", v.getResolvedValue().Kind().String())
+ return 0
+ }
+}
+
+// Slice slices an array, slice or string. Otherwise it will
+// return an empty []int.
+func (v *Value) Slice(i, j int) *Value {
+ switch v.getResolvedValue().Kind() {
+ case reflect.Array, reflect.Slice:
+ return AsValue(v.getResolvedValue().Slice(i, j).Interface())
+ case reflect.String:
+ runes := []rune(v.getResolvedValue().String())
+ return AsValue(string(runes[i:j]))
+ default:
+ logf("Value.Slice() not available for type: %s\n", v.getResolvedValue().Kind().String())
+ return AsValue([]int{})
+ }
+}
+
+// Index gets the i-th item of an array, slice or string. Otherwise
+// it will return NIL.
+func (v *Value) Index(i int) *Value {
+ switch v.getResolvedValue().Kind() {
+ case reflect.Array, reflect.Slice:
+ if i >= v.Len() {
+ return AsValue(nil)
+ }
+ return AsValue(v.getResolvedValue().Index(i).Interface())
+ case reflect.String:
+ //return AsValue(v.getResolvedValue().Slice(i, i+1).Interface())
+ s := v.getResolvedValue().String()
+ runes := []rune(s)
+ if i < len(runes) {
+ return AsValue(string(runes[i]))
+ }
+ return AsValue("")
+ default:
+ logf("Value.Slice() not available for type: %s\n", v.getResolvedValue().Kind().String())
+ return AsValue([]int{})
+ }
+}
+
+// Contains checks whether the underlying value (which must be of type struct, map,
+// string, array or slice) contains of another Value (e. g. used to check
+// whether a struct contains of a specific field or a map contains a specific key).
+//
+// Example:
+// AsValue("Hello, World!").Contains(AsValue("World")) == true
+func (v *Value) Contains(other *Value) bool {
+ switch v.getResolvedValue().Kind() {
+ case reflect.Struct:
+ fieldValue := v.getResolvedValue().FieldByName(other.String())
+ return fieldValue.IsValid()
+ case reflect.Map:
+ var mapValue reflect.Value
+ switch other.Interface().(type) {
+ case int:
+ mapValue = v.getResolvedValue().MapIndex(other.getResolvedValue())
+ case string:
+ mapValue = v.getResolvedValue().MapIndex(other.getResolvedValue())
+ default:
+ logf("Value.Contains() does not support lookup type '%s'\n", other.getResolvedValue().Kind().String())
+ return false
+ }
+
+ return mapValue.IsValid()
+ case reflect.String:
+ return strings.Contains(v.getResolvedValue().String(), other.String())
+
+ case reflect.Slice, reflect.Array:
+ for i := 0; i < v.getResolvedValue().Len(); i++ {
+ item := v.getResolvedValue().Index(i)
+ if other.EqualValueTo(AsValue(item.Interface())) {
+ return true
+ }
+ }
+ return false
+
+ default:
+ logf("Value.Contains() not available for type: %s\n", v.getResolvedValue().Kind().String())
+ return false
+ }
+}
+
+// CanSlice checks whether the underlying value is of type array, slice or string.
+// You normally would use CanSlice() before using the Slice() operation.
+func (v *Value) CanSlice() bool {
+ switch v.getResolvedValue().Kind() {
+ case reflect.Array, reflect.Slice, reflect.String:
+ return true
+ }
+ return false
+}
+
+// Iterate iterates over a map, array, slice or a string. It calls the
+// function's first argument for every value with the following arguments:
+//
+// idx current 0-index
+// count total amount of items
+// key *Value for the key or item
+// value *Value (only for maps, the respective value for a specific key)
+//
+// If the underlying value has no items or is not one of the types above,
+// the empty function (function's second argument) will be called.
+func (v *Value) Iterate(fn func(idx, count int, key, value *Value) bool, empty func()) {
+ v.IterateOrder(fn, empty, false, false)
+}
+
+// IterateOrder behaves like Value.Iterate, but can iterate through an array/slice/string in reverse. Does
+// not affect the iteration through a map because maps don't have any particular order.
+// However, you can force an order using the `sorted` keyword (and even use `reversed sorted`).
+func (v *Value) IterateOrder(fn func(idx, count int, key, value *Value) bool, empty func(), reverse bool, sorted bool) {
+ switch v.getResolvedValue().Kind() {
+ case reflect.Map:
+ keys := sortedKeys(v.getResolvedValue().MapKeys())
+ if sorted {
+ if reverse {
+ sort.Sort(sort.Reverse(keys))
+ } else {
+ sort.Sort(keys)
+ }
+ }
+ keyLen := len(keys)
+ for idx, key := range keys {
+ value := v.getResolvedValue().MapIndex(key)
+ if !fn(idx, keyLen, &Value{val: key}, &Value{val: value}) {
+ return
+ }
+ }
+ if keyLen == 0 {
+ empty()
+ }
+ return // done
+ case reflect.Array, reflect.Slice:
+ var items valuesList
+
+ itemCount := v.getResolvedValue().Len()
+ for i := 0; i < itemCount; i++ {
+ items = append(items, &Value{val: v.getResolvedValue().Index(i)})
+ }
+
+ if sorted {
+ if reverse {
+ sort.Sort(sort.Reverse(items))
+ } else {
+ sort.Sort(items)
+ }
+ } else {
+ if reverse {
+ for i := 0; i < itemCount/2; i++ {
+ items[i], items[itemCount-1-i] = items[itemCount-1-i], items[i]
+ }
+ }
+ }
+
+ if len(items) > 0 {
+ for idx, item := range items {
+ if !fn(idx, itemCount, item, nil) {
+ return
+ }
+ }
+ } else {
+ empty()
+ }
+ return // done
+ case reflect.String:
+ if sorted {
+ // TODO(flosch): Handle sorted
+ panic("TODO: handle sort for type string")
+ }
+
+ // TODO(flosch): Not utf8-compatible (utf8-decoding necessary)
+ charCount := v.getResolvedValue().Len()
+ if charCount > 0 {
+ if reverse {
+ for i := charCount - 1; i >= 0; i-- {
+ if !fn(i, charCount, &Value{val: v.getResolvedValue().Slice(i, i+1)}, nil) {
+ return
+ }
+ }
+ } else {
+ for i := 0; i < charCount; i++ {
+ if !fn(i, charCount, &Value{val: v.getResolvedValue().Slice(i, i+1)}, nil) {
+ return
+ }
+ }
+ }
+ } else {
+ empty()
+ }
+ return // done
+ default:
+ logf("Value.Iterate() not available for type: %s\n", v.getResolvedValue().Kind().String())
+ }
+ empty()
+}
+
+// Interface gives you access to the underlying value.
+func (v *Value) Interface() interface{} {
+ if v.val.IsValid() {
+ return v.val.Interface()
+ }
+ return nil
+}
+
+// EqualValueTo checks whether two values are containing the same value or object.
+func (v *Value) EqualValueTo(other *Value) bool {
+ // comparison of uint with int fails using .Interface()-comparison (see issue #64)
+ if v.IsInteger() && other.IsInteger() {
+ return v.Integer() == other.Integer()
+ }
+ if v.IsTime() && other.IsTime() {
+ return v.Time().Equal(other.Time())
+ }
+ return v.Interface() == other.Interface()
+}
+
+type sortedKeys []reflect.Value
+
+func (sk sortedKeys) Len() int {
+ return len(sk)
+}
+
+func (sk sortedKeys) Less(i, j int) bool {
+ vi := &Value{val: sk[i]}
+ vj := &Value{val: sk[j]}
+ switch {
+ case vi.IsInteger() && vj.IsInteger():
+ return vi.Integer() < vj.Integer()
+ case vi.IsFloat() && vj.IsFloat():
+ return vi.Float() < vj.Float()
+ default:
+ return vi.String() < vj.String()
+ }
+}
+
+func (sk sortedKeys) Swap(i, j int) {
+ sk[i], sk[j] = sk[j], sk[i]
+}
+
+type valuesList []*Value
+
+func (vl valuesList) Len() int {
+ return len(vl)
+}
+
+func (vl valuesList) Less(i, j int) bool {
+ vi := vl[i]
+ vj := vl[j]
+ switch {
+ case vi.IsInteger() && vj.IsInteger():
+ return vi.Integer() < vj.Integer()
+ case vi.IsFloat() && vj.IsFloat():
+ return vi.Float() < vj.Float()
+ default:
+ return vi.String() < vj.String()
+ }
+}
+
+func (vl valuesList) Swap(i, j int) {
+ vl[i], vl[j] = vl[j], vl[i]
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/variable.go b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/variable.go
new file mode 100644
index 000000000000..25e2af40b3fb
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/flosch/pongo2/v4/variable.go
@@ -0,0 +1,693 @@
+package pongo2
+
+import (
+ "fmt"
+ "reflect"
+ "strconv"
+ "strings"
+)
+
+const (
+ varTypeInt = iota
+ varTypeIdent
+)
+
+var (
+ typeOfValuePtr = reflect.TypeOf(new(Value))
+ typeOfExecCtxPtr = reflect.TypeOf(new(ExecutionContext))
+)
+
+type variablePart struct {
+ typ int
+ s string
+ i int
+
+ isFunctionCall bool
+ callingArgs []functionCallArgument // needed for a function call, represents all argument nodes (INode supports nested function calls)
+}
+
+type functionCallArgument interface {
+ Evaluate(*ExecutionContext) (*Value, *Error)
+}
+
+// TODO: Add location tokens
+type stringResolver struct {
+ locationToken *Token
+ val string
+}
+
+type intResolver struct {
+ locationToken *Token
+ val int
+}
+
+type floatResolver struct {
+ locationToken *Token
+ val float64
+}
+
+type boolResolver struct {
+ locationToken *Token
+ val bool
+}
+
+type variableResolver struct {
+ locationToken *Token
+
+ parts []*variablePart
+}
+
+type nodeFilteredVariable struct {
+ locationToken *Token
+
+ resolver IEvaluator
+ filterChain []*filterCall
+}
+
+type nodeVariable struct {
+ locationToken *Token
+ expr IEvaluator
+}
+
+type executionCtxEval struct{}
+
+func (v *nodeFilteredVariable) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error {
+ value, err := v.Evaluate(ctx)
+ if err != nil {
+ return err
+ }
+ writer.WriteString(value.String())
+ return nil
+}
+
+func (vr *variableResolver) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error {
+ value, err := vr.Evaluate(ctx)
+ if err != nil {
+ return err
+ }
+ writer.WriteString(value.String())
+ return nil
+}
+
+func (s *stringResolver) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error {
+ value, err := s.Evaluate(ctx)
+ if err != nil {
+ return err
+ }
+ writer.WriteString(value.String())
+ return nil
+}
+
+func (i *intResolver) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error {
+ value, err := i.Evaluate(ctx)
+ if err != nil {
+ return err
+ }
+ writer.WriteString(value.String())
+ return nil
+}
+
+func (f *floatResolver) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error {
+ value, err := f.Evaluate(ctx)
+ if err != nil {
+ return err
+ }
+ writer.WriteString(value.String())
+ return nil
+}
+
+func (b *boolResolver) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error {
+ value, err := b.Evaluate(ctx)
+ if err != nil {
+ return err
+ }
+ writer.WriteString(value.String())
+ return nil
+}
+
+func (v *nodeFilteredVariable) GetPositionToken() *Token {
+ return v.locationToken
+}
+
+func (vr *variableResolver) GetPositionToken() *Token {
+ return vr.locationToken
+}
+
+func (s *stringResolver) GetPositionToken() *Token {
+ return s.locationToken
+}
+
+func (i *intResolver) GetPositionToken() *Token {
+ return i.locationToken
+}
+
+func (f *floatResolver) GetPositionToken() *Token {
+ return f.locationToken
+}
+
+func (b *boolResolver) GetPositionToken() *Token {
+ return b.locationToken
+}
+
+func (s *stringResolver) Evaluate(ctx *ExecutionContext) (*Value, *Error) {
+ return AsValue(s.val), nil
+}
+
+func (i *intResolver) Evaluate(ctx *ExecutionContext) (*Value, *Error) {
+ return AsValue(i.val), nil
+}
+
+func (f *floatResolver) Evaluate(ctx *ExecutionContext) (*Value, *Error) {
+ return AsValue(f.val), nil
+}
+
+func (b *boolResolver) Evaluate(ctx *ExecutionContext) (*Value, *Error) {
+ return AsValue(b.val), nil
+}
+
+func (s *stringResolver) FilterApplied(name string) bool {
+ return false
+}
+
+func (i *intResolver) FilterApplied(name string) bool {
+ return false
+}
+
+func (f *floatResolver) FilterApplied(name string) bool {
+ return false
+}
+
+func (b *boolResolver) FilterApplied(name string) bool {
+ return false
+}
+
+func (nv *nodeVariable) FilterApplied(name string) bool {
+ return nv.expr.FilterApplied(name)
+}
+
+func (nv *nodeVariable) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error {
+ value, err := nv.expr.Evaluate(ctx)
+ if err != nil {
+ return err
+ }
+
+ if !nv.expr.FilterApplied("safe") && !value.safe && value.IsString() && ctx.Autoescape {
+ // apply escape filter
+ value, err = filters["escape"](value, nil)
+ if err != nil {
+ return err
+ }
+ }
+
+ writer.WriteString(value.String())
+ return nil
+}
+
+func (executionCtxEval) Evaluate(ctx *ExecutionContext) (*Value, *Error) {
+ return AsValue(ctx), nil
+}
+
+func (vr *variableResolver) FilterApplied(name string) bool {
+ return false
+}
+
+func (vr *variableResolver) String() string {
+ parts := make([]string, 0, len(vr.parts))
+ for _, p := range vr.parts {
+ switch p.typ {
+ case varTypeInt:
+ parts = append(parts, strconv.Itoa(p.i))
+ case varTypeIdent:
+ parts = append(parts, p.s)
+ default:
+ panic("unimplemented")
+ }
+ }
+ return strings.Join(parts, ".")
+}
+
+func (vr *variableResolver) resolve(ctx *ExecutionContext) (*Value, error) {
+ var current reflect.Value
+ var isSafe bool
+
+ for idx, part := range vr.parts {
+ if idx == 0 {
+ // We're looking up the first part of the variable.
+ // First we're having a look in our private
+ // context (e. g. information provided by tags, like the forloop)
+ val, inPrivate := ctx.Private[vr.parts[0].s]
+ if !inPrivate {
+ // Nothing found? Then have a final lookup in the public context
+ val = ctx.Public[vr.parts[0].s]
+ }
+ current = reflect.ValueOf(val) // Get the initial value
+ } else {
+ // Next parts, resolve it from current
+
+ // Before resolving the pointer, let's see if we have a method to call
+ // Problem with resolving the pointer is we're changing the receiver
+ isFunc := false
+ if part.typ == varTypeIdent {
+ funcValue := current.MethodByName(part.s)
+ if funcValue.IsValid() {
+ current = funcValue
+ isFunc = true
+ }
+ }
+
+ if !isFunc {
+ // If current a pointer, resolve it
+ if current.Kind() == reflect.Ptr {
+ current = current.Elem()
+ if !current.IsValid() {
+ // Value is not valid (anymore)
+ return AsValue(nil), nil
+ }
+ }
+
+ // Look up which part must be called now
+ switch part.typ {
+ case varTypeInt:
+ // Calling an index is only possible for:
+ // * slices/arrays/strings
+ switch current.Kind() {
+ case reflect.String, reflect.Array, reflect.Slice:
+ if part.i >= 0 && current.Len() > part.i {
+ current = current.Index(part.i)
+ } else {
+ // In Django, exceeding the length of a list is just empty.
+ return AsValue(nil), nil
+ }
+ default:
+ return nil, fmt.Errorf("Can't access an index on type %s (variable %s)",
+ current.Kind().String(), vr.String())
+ }
+ case varTypeIdent:
+ // debugging:
+ // fmt.Printf("now = %s (kind: %s)\n", part.s, current.Kind().String())
+
+ // Calling a field or key
+ switch current.Kind() {
+ case reflect.Struct:
+ current = current.FieldByName(part.s)
+ case reflect.Map:
+ current = current.MapIndex(reflect.ValueOf(part.s))
+ default:
+ return nil, fmt.Errorf("Can't access a field by name on type %s (variable %s)",
+ current.Kind().String(), vr.String())
+ }
+ default:
+ panic("unimplemented")
+ }
+ }
+ }
+
+ if !current.IsValid() {
+ // Value is not valid (anymore)
+ return AsValue(nil), nil
+ }
+
+ // If current is a reflect.ValueOf(pongo2.Value), then unpack it
+ // Happens in function calls (as a return value) or by injecting
+ // into the execution context (e.g. in a for-loop)
+ if current.Type() == typeOfValuePtr {
+ tmpValue := current.Interface().(*Value)
+ current = tmpValue.val
+ isSafe = tmpValue.safe
+ }
+
+ // Check whether this is an interface and resolve it where required
+ if current.Kind() == reflect.Interface {
+ current = reflect.ValueOf(current.Interface())
+ }
+
+ // Check if the part is a function call
+ if part.isFunctionCall || current.Kind() == reflect.Func {
+ // Check for callable
+ if current.Kind() != reflect.Func {
+ return nil, fmt.Errorf("'%s' is not a function (it is %s)", vr.String(), current.Kind().String())
+ }
+
+ // Check for correct function syntax and types
+ // func(*Value, ...) *Value
+ t := current.Type()
+ currArgs := part.callingArgs
+
+ // If an implicit ExecCtx is needed
+ if t.NumIn() > 0 && t.In(0) == typeOfExecCtxPtr {
+ currArgs = append([]functionCallArgument{executionCtxEval{}}, currArgs...)
+ }
+
+ // Input arguments
+ if len(currArgs) != t.NumIn() && !(len(currArgs) >= t.NumIn()-1 && t.IsVariadic()) {
+ return nil,
+ fmt.Errorf("Function input argument count (%d) of '%s' must be equal to the calling argument count (%d).",
+ t.NumIn(), vr.String(), len(currArgs))
+ }
+
+ // Output arguments
+ if t.NumOut() != 1 && t.NumOut() != 2 {
+ return nil, fmt.Errorf("'%s' must have exactly 1 or 2 output arguments, the second argument must be of type error", vr.String())
+ }
+
+ // Evaluate all parameters
+ var parameters []reflect.Value
+
+ numArgs := t.NumIn()
+ isVariadic := t.IsVariadic()
+ var fnArg reflect.Type
+
+ for idx, arg := range currArgs {
+ pv, err := arg.Evaluate(ctx)
+ if err != nil {
+ return nil, err
+ }
+
+ if isVariadic {
+ if idx >= t.NumIn()-1 {
+ fnArg = t.In(numArgs - 1).Elem()
+ } else {
+ fnArg = t.In(idx)
+ }
+ } else {
+ fnArg = t.In(idx)
+ }
+
+ if fnArg != typeOfValuePtr {
+ // Function's argument is not a *pongo2.Value, then we have to check whether input argument is of the same type as the function's argument
+ if !isVariadic {
+ if fnArg != reflect.TypeOf(pv.Interface()) && fnArg.Kind() != reflect.Interface {
+ return nil, fmt.Errorf("Function input argument %d of '%s' must be of type %s or *pongo2.Value (not %T).",
+ idx, vr.String(), fnArg.String(), pv.Interface())
+ }
+ // Function's argument has another type, using the interface-value
+ parameters = append(parameters, reflect.ValueOf(pv.Interface()))
+ } else {
+ if fnArg != reflect.TypeOf(pv.Interface()) && fnArg.Kind() != reflect.Interface {
+ return nil, fmt.Errorf("Function variadic input argument of '%s' must be of type %s or *pongo2.Value (not %T).",
+ vr.String(), fnArg.String(), pv.Interface())
+ }
+ // Function's argument has another type, using the interface-value
+ parameters = append(parameters, reflect.ValueOf(pv.Interface()))
+ }
+ } else {
+ // Function's argument is a *pongo2.Value
+ parameters = append(parameters, reflect.ValueOf(pv))
+ }
+ }
+
+ // Check if any of the values are invalid
+ for _, p := range parameters {
+ if p.Kind() == reflect.Invalid {
+ return nil, fmt.Errorf("Calling a function using an invalid parameter")
+ }
+ }
+
+ // Call it and get first return parameter back
+ values := current.Call(parameters)
+ rv := values[0]
+ if t.NumOut() == 2 {
+ e := values[1].Interface()
+ if e != nil {
+ err, ok := e.(error)
+ if !ok {
+ return nil, fmt.Errorf("The second return value is not an error")
+ }
+ if err != nil {
+ return nil, err
+ }
+ }
+ }
+
+ if rv.Type() != typeOfValuePtr {
+ current = reflect.ValueOf(rv.Interface())
+ } else {
+ // Return the function call value
+ current = rv.Interface().(*Value).val
+ isSafe = rv.Interface().(*Value).safe
+ }
+ }
+
+ if !current.IsValid() {
+ // Value is not valid (e. g. NIL value)
+ return AsValue(nil), nil
+ }
+ }
+
+ return &Value{val: current, safe: isSafe}, nil
+}
+
+func (vr *variableResolver) Evaluate(ctx *ExecutionContext) (*Value, *Error) {
+ value, err := vr.resolve(ctx)
+ if err != nil {
+ return AsValue(nil), ctx.Error(err.Error(), vr.locationToken)
+ }
+ return value, nil
+}
+
+func (v *nodeFilteredVariable) FilterApplied(name string) bool {
+ for _, filter := range v.filterChain {
+ if filter.name == name {
+ return true
+ }
+ }
+ return false
+}
+
+func (v *nodeFilteredVariable) Evaluate(ctx *ExecutionContext) (*Value, *Error) {
+ value, err := v.resolver.Evaluate(ctx)
+ if err != nil {
+ return nil, err
+ }
+
+ for _, filter := range v.filterChain {
+ value, err = filter.Execute(value, ctx)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ return value, nil
+}
+
+// IDENT | IDENT.(IDENT|NUMBER)...
+func (p *Parser) parseVariableOrLiteral() (IEvaluator, *Error) {
+ t := p.Current()
+
+ if t == nil {
+ return nil, p.Error("Unexpected EOF, expected a number, string, keyword or identifier.", p.lastToken)
+ }
+
+ // Is first part a number or a string, there's nothing to resolve (because there's only to return the value then)
+ switch t.Typ {
+ case TokenNumber:
+ p.Consume()
+
+ // One exception to the rule that we don't have float64 literals is at the beginning
+ // of an expression (or a variable name). Since we know we started with an integer
+ // which can't obviously be a variable name, we can check whether the first number
+ // is followed by dot (and then a number again). If so we're converting it to a float64.
+
+ if p.Match(TokenSymbol, ".") != nil {
+ // float64
+ t2 := p.MatchType(TokenNumber)
+ if t2 == nil {
+ return nil, p.Error("Expected a number after the '.'.", nil)
+ }
+ f, err := strconv.ParseFloat(fmt.Sprintf("%s.%s", t.Val, t2.Val), 64)
+ if err != nil {
+ return nil, p.Error(err.Error(), t)
+ }
+ fr := &floatResolver{
+ locationToken: t,
+ val: f,
+ }
+ return fr, nil
+ }
+ i, err := strconv.Atoi(t.Val)
+ if err != nil {
+ return nil, p.Error(err.Error(), t)
+ }
+ nr := &intResolver{
+ locationToken: t,
+ val: i,
+ }
+ return nr, nil
+
+ case TokenString:
+ p.Consume()
+ sr := &stringResolver{
+ locationToken: t,
+ val: t.Val,
+ }
+ return sr, nil
+ case TokenKeyword:
+ p.Consume()
+ switch t.Val {
+ case "true":
+ br := &boolResolver{
+ locationToken: t,
+ val: true,
+ }
+ return br, nil
+ case "false":
+ br := &boolResolver{
+ locationToken: t,
+ val: false,
+ }
+ return br, nil
+ default:
+ return nil, p.Error("This keyword is not allowed here.", nil)
+ }
+ }
+
+ resolver := &variableResolver{
+ locationToken: t,
+ }
+
+ // First part of a variable MUST be an identifier
+ if t.Typ != TokenIdentifier {
+ return nil, p.Error("Expected either a number, string, keyword or identifier.", t)
+ }
+
+ resolver.parts = append(resolver.parts, &variablePart{
+ typ: varTypeIdent,
+ s: t.Val,
+ })
+
+ p.Consume() // we consumed the first identifier of the variable name
+
+variableLoop:
+ for p.Remaining() > 0 {
+ t = p.Current()
+
+ if p.Match(TokenSymbol, ".") != nil {
+ // Next variable part (can be either NUMBER or IDENT)
+ t2 := p.Current()
+ if t2 != nil {
+ switch t2.Typ {
+ case TokenIdentifier:
+ resolver.parts = append(resolver.parts, &variablePart{
+ typ: varTypeIdent,
+ s: t2.Val,
+ })
+ p.Consume() // consume: IDENT
+ continue variableLoop
+ case TokenNumber:
+ i, err := strconv.Atoi(t2.Val)
+ if err != nil {
+ return nil, p.Error(err.Error(), t2)
+ }
+ resolver.parts = append(resolver.parts, &variablePart{
+ typ: varTypeInt,
+ i: i,
+ })
+ p.Consume() // consume: NUMBER
+ continue variableLoop
+ default:
+ return nil, p.Error("This token is not allowed within a variable name.", t2)
+ }
+ } else {
+ // EOF
+ return nil, p.Error("Unexpected EOF, expected either IDENTIFIER or NUMBER after DOT.",
+ p.lastToken)
+ }
+ } else if p.Match(TokenSymbol, "(") != nil {
+ // Function call
+ // FunctionName '(' Comma-separated list of expressions ')'
+ part := resolver.parts[len(resolver.parts)-1]
+ part.isFunctionCall = true
+ argumentLoop:
+ for {
+ if p.Remaining() == 0 {
+ return nil, p.Error("Unexpected EOF, expected function call argument list.", p.lastToken)
+ }
+
+ if p.Peek(TokenSymbol, ")") == nil {
+ // No closing bracket, so we're parsing an expression
+ exprArg, err := p.ParseExpression()
+ if err != nil {
+ return nil, err
+ }
+ part.callingArgs = append(part.callingArgs, exprArg)
+
+ if p.Match(TokenSymbol, ")") != nil {
+ // If there's a closing bracket after an expression, we will stop parsing the arguments
+ break argumentLoop
+ } else {
+ // If there's NO closing bracket, there MUST be an comma
+ if p.Match(TokenSymbol, ",") == nil {
+ return nil, p.Error("Missing comma or closing bracket after argument.", nil)
+ }
+ }
+ } else {
+ // We got a closing bracket, so stop parsing arguments
+ p.Consume()
+ break argumentLoop
+ }
+
+ }
+ // We're done parsing the function call, next variable part
+ continue variableLoop
+ }
+
+ // No dot or function call? Then we're done with the variable parsing
+ break
+ }
+
+ return resolver, nil
+}
+
+func (p *Parser) parseVariableOrLiteralWithFilter() (*nodeFilteredVariable, *Error) {
+ v := &nodeFilteredVariable{
+ locationToken: p.Current(),
+ }
+
+ // Parse the variable name
+ resolver, err := p.parseVariableOrLiteral()
+ if err != nil {
+ return nil, err
+ }
+ v.resolver = resolver
+
+ // Parse all the filters
+filterLoop:
+ for p.Match(TokenSymbol, "|") != nil {
+ // Parse one single filter
+ filter, err := p.parseFilter()
+ if err != nil {
+ return nil, err
+ }
+
+ // Check sandbox filter restriction
+ if _, isBanned := p.template.set.bannedFilters[filter.name]; isBanned {
+ return nil, p.Error(fmt.Sprintf("Usage of filter '%s' is not allowed (sandbox restriction active).", filter.name), nil)
+ }
+
+ v.filterChain = append(v.filterChain, filter)
+
+ continue filterLoop
+ }
+
+ return v, nil
+}
+
+func (p *Parser) parseVariableElement() (INode, *Error) {
+ node := &nodeVariable{
+ locationToken: p.Current(),
+ }
+
+ p.Consume() // consume '{{'
+
+ expr, err := p.ParseExpression()
+ if err != nil {
+ return nil, err
+ }
+ node.expr = expr
+
+ if p.Match(TokenSymbol, "}}") == nil {
+ return nil, p.Error("'}}' expected", nil)
+ }
+
+ return node, nil
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/fsnotify/fsnotify/.editorconfig b/go/ql/test/experimental/CWE-321/vendor/github.com/fsnotify/fsnotify/.editorconfig
new file mode 100644
index 000000000000..fad895851e56
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/fsnotify/fsnotify/.editorconfig
@@ -0,0 +1,12 @@
+root = true
+
+[*.go]
+indent_style = tab
+indent_size = 4
+insert_final_newline = true
+
+[*.{yml,yaml}]
+indent_style = space
+indent_size = 2
+insert_final_newline = true
+trim_trailing_whitespace = true
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/fsnotify/fsnotify/.gitattributes b/go/ql/test/experimental/CWE-321/vendor/github.com/fsnotify/fsnotify/.gitattributes
new file mode 100644
index 000000000000..32f1001be0a5
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/fsnotify/fsnotify/.gitattributes
@@ -0,0 +1 @@
+go.sum linguist-generated
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/fsnotify/fsnotify/.gitignore b/go/ql/test/experimental/CWE-321/vendor/github.com/fsnotify/fsnotify/.gitignore
new file mode 100644
index 000000000000..4cd0cbaf432c
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/fsnotify/fsnotify/.gitignore
@@ -0,0 +1,6 @@
+# Setup a Global .gitignore for OS and editor generated files:
+# https://help.github.com/articles/ignoring-files
+# git config --global core.excludesfile ~/.gitignore_global
+
+.vagrant
+*.sublime-project
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/fsnotify/fsnotify/.mailmap b/go/ql/test/experimental/CWE-321/vendor/github.com/fsnotify/fsnotify/.mailmap
new file mode 100644
index 000000000000..a04f2907fed3
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/fsnotify/fsnotify/.mailmap
@@ -0,0 +1,2 @@
+Chris Howey
+Nathan Youngman <4566+nathany@users.noreply.github.com>
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/fsnotify/fsnotify/AUTHORS b/go/ql/test/experimental/CWE-321/vendor/github.com/fsnotify/fsnotify/AUTHORS
new file mode 100644
index 000000000000..6cbabe5ef50b
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/fsnotify/fsnotify/AUTHORS
@@ -0,0 +1,62 @@
+# Names should be added to this file as
+# Name or Organization
+# The email address is not required for organizations.
+
+# You can update this list using the following command:
+#
+# $ (head -n10 AUTHORS && git shortlog -se | sed -E 's/^\s+[0-9]+\t//') | tee AUTHORS
+
+# Please keep the list sorted.
+
+Aaron L
+Adrien Bustany
+Alexey Kazakov
+Amit Krishnan
+Anmol Sethi
+Bjørn Erik Pedersen
+Brian Goff
+Bruno Bigras
+Caleb Spare
+Case Nelson
+Chris Howey
+Christoffer Buchholz
+Daniel Wagner-Hall
+Dave Cheney
+Eric Lin
+Evan Phoenix
+Francisco Souza
+Gautam Dey
+Hari haran
+Ichinose Shogo
+Johannes Ebke
+John C Barstow
+Kelvin Fo
+Ken-ichirou MATSUZAWA
+Matt Layher
+Matthias Stone
+Nathan Youngman
+Nickolai Zeldovich
+Oliver Bristow
+Patrick
+Paul Hammond
+Pawel Knap
+Pieter Droogendijk
+Pratik Shinde
+Pursuit92
+Riku Voipio
+Rob Figueiredo
+Rodrigo Chiossi
+Slawek Ligus
+Soge Zhang
+Tiffany Jernigan
+Tilak Sharma
+Tobias Klauser
+Tom Payne
+Travis Cline
+Tudor Golubenco
+Vahe Khachikyan
+Yukang
+bronze1man
+debrando
+henrikedwards
+é“å“¥
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/fsnotify/fsnotify/CHANGELOG.md b/go/ql/test/experimental/CWE-321/vendor/github.com/fsnotify/fsnotify/CHANGELOG.md
new file mode 100644
index 000000000000..cc01c08f56d5
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/fsnotify/fsnotify/CHANGELOG.md
@@ -0,0 +1,357 @@
+# Changelog
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
+and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+## [Unreleased]
+
+## [1.5.4] - 2022-04-25
+
+* Windows: add missing defer to `Watcher.WatchList` [#447](https://github.com/fsnotify/fsnotify/pull/447)
+* go.mod: use latest x/sys [#444](https://github.com/fsnotify/fsnotify/pull/444)
+* Fix compilation for OpenBSD [#443](https://github.com/fsnotify/fsnotify/pull/443)
+
+## [1.5.3] - 2022-04-22
+
+* This version is retracted. An incorrect branch is published accidentally [#445](https://github.com/fsnotify/fsnotify/issues/445)
+
+## [1.5.2] - 2022-04-21
+
+* Add a feature to return the directories and files that are being monitored [#374](https://github.com/fsnotify/fsnotify/pull/374)
+* Fix potential crash on windows if `raw.FileNameLength` exceeds `syscall.MAX_PATH` [#361](https://github.com/fsnotify/fsnotify/pull/361)
+* Allow build on unsupported GOOS [#424](https://github.com/fsnotify/fsnotify/pull/424)
+* Don't set `poller.fd` twice in `newFdPoller` [#406](https://github.com/fsnotify/fsnotify/pull/406)
+* fix go vet warnings: call to `(*T).Fatalf` from a non-test goroutine [#416](https://github.com/fsnotify/fsnotify/pull/416)
+
+## [1.5.1] - 2021-08-24
+
+* Revert Add AddRaw to not follow symlinks [#394](https://github.com/fsnotify/fsnotify/pull/394)
+
+## [1.5.0] - 2021-08-20
+
+* Go: Increase minimum required version to Go 1.12 [#381](https://github.com/fsnotify/fsnotify/pull/381)
+* Feature: Add AddRaw method which does not follow symlinks when adding a watch [#289](https://github.com/fsnotify/fsnotify/pull/298)
+* Windows: Follow symlinks by default like on all other systems [#289](https://github.com/fsnotify/fsnotify/pull/289)
+* CI: Use GitHub Actions for CI and cover go 1.12-1.17
+ [#378](https://github.com/fsnotify/fsnotify/pull/378)
+ [#381](https://github.com/fsnotify/fsnotify/pull/381)
+ [#385](https://github.com/fsnotify/fsnotify/pull/385)
+* Go 1.14+: Fix unsafe pointer conversion [#325](https://github.com/fsnotify/fsnotify/pull/325)
+
+## [1.4.7] - 2018-01-09
+
+* BSD/macOS: Fix possible deadlock on closing the watcher on kqueue (thanks @nhooyr and @glycerine)
+* Tests: Fix missing verb on format string (thanks @rchiossi)
+* Linux: Fix deadlock in Remove (thanks @aarondl)
+* Linux: Watch.Add improvements (avoid race, fix consistency, reduce garbage) (thanks @twpayne)
+* Docs: Moved FAQ into the README (thanks @vahe)
+* Linux: Properly handle inotify's IN_Q_OVERFLOW event (thanks @zeldovich)
+* Docs: replace references to OS X with macOS
+
+## [1.4.2] - 2016-10-10
+
+* Linux: use InotifyInit1 with IN_CLOEXEC to stop leaking a file descriptor to a child process when using fork/exec [#178](https://github.com/fsnotify/fsnotify/pull/178) (thanks @pattyshack)
+
+## [1.4.1] - 2016-10-04
+
+* Fix flaky inotify stress test on Linux [#177](https://github.com/fsnotify/fsnotify/pull/177) (thanks @pattyshack)
+
+## [1.4.0] - 2016-10-01
+
+* add a String() method to Event.Op [#165](https://github.com/fsnotify/fsnotify/pull/165) (thanks @oozie)
+
+## [1.3.1] - 2016-06-28
+
+* Windows: fix for double backslash when watching the root of a drive [#151](https://github.com/fsnotify/fsnotify/issues/151) (thanks @brunoqc)
+
+## [1.3.0] - 2016-04-19
+
+* Support linux/arm64 by [patching](https://go-review.googlesource.com/#/c/21971/) x/sys/unix and switching to to it from syscall (thanks @suihkulokki) [#135](https://github.com/fsnotify/fsnotify/pull/135)
+
+## [1.2.10] - 2016-03-02
+
+* Fix golint errors in windows.go [#121](https://github.com/fsnotify/fsnotify/pull/121) (thanks @tiffanyfj)
+
+## [1.2.9] - 2016-01-13
+
+kqueue: Fix logic for CREATE after REMOVE [#111](https://github.com/fsnotify/fsnotify/pull/111) (thanks @bep)
+
+## [1.2.8] - 2015-12-17
+
+* kqueue: fix race condition in Close [#105](https://github.com/fsnotify/fsnotify/pull/105) (thanks @djui for reporting the issue and @ppknap for writing a failing test)
+* inotify: fix race in test
+* enable race detection for continuous integration (Linux, Mac, Windows)
+
+## [1.2.5] - 2015-10-17
+
+* inotify: use epoll_create1 for arm64 support (requires Linux 2.6.27 or later) [#100](https://github.com/fsnotify/fsnotify/pull/100) (thanks @suihkulokki)
+* inotify: fix path leaks [#73](https://github.com/fsnotify/fsnotify/pull/73) (thanks @chamaken)
+* kqueue: watch for rename events on subdirectories [#83](https://github.com/fsnotify/fsnotify/pull/83) (thanks @guotie)
+* kqueue: avoid infinite loops from symlinks cycles [#101](https://github.com/fsnotify/fsnotify/pull/101) (thanks @illicitonion)
+
+## [1.2.1] - 2015-10-14
+
+* kqueue: don't watch named pipes [#98](https://github.com/fsnotify/fsnotify/pull/98) (thanks @evanphx)
+
+## [1.2.0] - 2015-02-08
+
+* inotify: use epoll to wake up readEvents [#66](https://github.com/fsnotify/fsnotify/pull/66) (thanks @PieterD)
+* inotify: closing watcher should now always shut down goroutine [#63](https://github.com/fsnotify/fsnotify/pull/63) (thanks @PieterD)
+* kqueue: close kqueue after removing watches, fixes [#59](https://github.com/fsnotify/fsnotify/issues/59)
+
+## [1.1.1] - 2015-02-05
+
+* inotify: Retry read on EINTR [#61](https://github.com/fsnotify/fsnotify/issues/61) (thanks @PieterD)
+
+## [1.1.0] - 2014-12-12
+
+* kqueue: rework internals [#43](https://github.com/fsnotify/fsnotify/pull/43)
+ * add low-level functions
+ * only need to store flags on directories
+ * less mutexes [#13](https://github.com/fsnotify/fsnotify/issues/13)
+ * done can be an unbuffered channel
+ * remove calls to os.NewSyscallError
+* More efficient string concatenation for Event.String() [#52](https://github.com/fsnotify/fsnotify/pull/52) (thanks @mdlayher)
+* kqueue: fix regression in rework causing subdirectories to be watched [#48](https://github.com/fsnotify/fsnotify/issues/48)
+* kqueue: cleanup internal watch before sending remove event [#51](https://github.com/fsnotify/fsnotify/issues/51)
+
+## [1.0.4] - 2014-09-07
+
+* kqueue: add dragonfly to the build tags.
+* Rename source code files, rearrange code so exported APIs are at the top.
+* Add done channel to example code. [#37](https://github.com/fsnotify/fsnotify/pull/37) (thanks @chenyukang)
+
+## [1.0.3] - 2014-08-19
+
+* [Fix] Windows MOVED_TO now translates to Create like on BSD and Linux. [#36](https://github.com/fsnotify/fsnotify/issues/36)
+
+## [1.0.2] - 2014-08-17
+
+* [Fix] Missing create events on macOS. [#14](https://github.com/fsnotify/fsnotify/issues/14) (thanks @zhsso)
+* [Fix] Make ./path and path equivalent. (thanks @zhsso)
+
+## [1.0.0] - 2014-08-15
+
+* [API] Remove AddWatch on Windows, use Add.
+* Improve documentation for exported identifiers. [#30](https://github.com/fsnotify/fsnotify/issues/30)
+* Minor updates based on feedback from golint.
+
+## dev / 2014-07-09
+
+* Moved to [github.com/fsnotify/fsnotify](https://github.com/fsnotify/fsnotify).
+* Use os.NewSyscallError instead of returning errno (thanks @hariharan-uno)
+
+## dev / 2014-07-04
+
+* kqueue: fix incorrect mutex used in Close()
+* Update example to demonstrate usage of Op.
+
+## dev / 2014-06-28
+
+* [API] Don't set the Write Op for attribute notifications [#4](https://github.com/fsnotify/fsnotify/issues/4)
+* Fix for String() method on Event (thanks Alex Brainman)
+* Don't build on Plan 9 or Solaris (thanks @4ad)
+
+## dev / 2014-06-21
+
+* Events channel of type Event rather than *Event.
+* [internal] use syscall constants directly for inotify and kqueue.
+* [internal] kqueue: rename events to kevents and fileEvent to event.
+
+## dev / 2014-06-19
+
+* Go 1.3+ required on Windows (uses syscall.ERROR_MORE_DATA internally).
+* [internal] remove cookie from Event struct (unused).
+* [internal] Event struct has the same definition across every OS.
+* [internal] remove internal watch and removeWatch methods.
+
+## dev / 2014-06-12
+
+* [API] Renamed Watch() to Add() and RemoveWatch() to Remove().
+* [API] Pluralized channel names: Events and Errors.
+* [API] Renamed FileEvent struct to Event.
+* [API] Op constants replace methods like IsCreate().
+
+## dev / 2014-06-12
+
+* Fix data race on kevent buffer (thanks @tilaks) [#98](https://github.com/howeyc/fsnotify/pull/98)
+
+## dev / 2014-05-23
+
+* [API] Remove current implementation of WatchFlags.
+ * current implementation doesn't take advantage of OS for efficiency
+ * provides little benefit over filtering events as they are received, but has extra bookkeeping and mutexes
+ * no tests for the current implementation
+ * not fully implemented on Windows [#93](https://github.com/howeyc/fsnotify/issues/93#issuecomment-39285195)
+
+## [0.9.3] - 2014-12-31
+
+* kqueue: cleanup internal watch before sending remove event [#51](https://github.com/fsnotify/fsnotify/issues/51)
+
+## [0.9.2] - 2014-08-17
+
+* [Backport] Fix missing create events on macOS. [#14](https://github.com/fsnotify/fsnotify/issues/14) (thanks @zhsso)
+
+## [0.9.1] - 2014-06-12
+
+* Fix data race on kevent buffer (thanks @tilaks) [#98](https://github.com/howeyc/fsnotify/pull/98)
+
+## [0.9.0] - 2014-01-17
+
+* IsAttrib() for events that only concern a file's metadata [#79][] (thanks @abustany)
+* [Fix] kqueue: fix deadlock [#77][] (thanks @cespare)
+* [NOTICE] Development has moved to `code.google.com/p/go.exp/fsnotify` in preparation for inclusion in the Go standard library.
+
+## [0.8.12] - 2013-11-13
+
+* [API] Remove FD_SET and friends from Linux adapter
+
+## [0.8.11] - 2013-11-02
+
+* [Doc] Add Changelog [#72][] (thanks @nathany)
+* [Doc] Spotlight and double modify events on macOS [#62][] (reported by @paulhammond)
+
+## [0.8.10] - 2013-10-19
+
+* [Fix] kqueue: remove file watches when parent directory is removed [#71][] (reported by @mdwhatcott)
+* [Fix] kqueue: race between Close and readEvents [#70][] (reported by @bernerdschaefer)
+* [Doc] specify OS-specific limits in README (thanks @debrando)
+
+## [0.8.9] - 2013-09-08
+
+* [Doc] Contributing (thanks @nathany)
+* [Doc] update package path in example code [#63][] (thanks @paulhammond)
+* [Doc] GoCI badge in README (Linux only) [#60][]
+* [Doc] Cross-platform testing with Vagrant [#59][] (thanks @nathany)
+
+## [0.8.8] - 2013-06-17
+
+* [Fix] Windows: handle `ERROR_MORE_DATA` on Windows [#49][] (thanks @jbowtie)
+
+## [0.8.7] - 2013-06-03
+
+* [API] Make syscall flags internal
+* [Fix] inotify: ignore event changes
+* [Fix] race in symlink test [#45][] (reported by @srid)
+* [Fix] tests on Windows
+* lower case error messages
+
+## [0.8.6] - 2013-05-23
+
+* kqueue: Use EVT_ONLY flag on Darwin
+* [Doc] Update README with full example
+
+## [0.8.5] - 2013-05-09
+
+* [Fix] inotify: allow monitoring of "broken" symlinks (thanks @tsg)
+
+## [0.8.4] - 2013-04-07
+
+* [Fix] kqueue: watch all file events [#40][] (thanks @ChrisBuchholz)
+
+## [0.8.3] - 2013-03-13
+
+* [Fix] inoitfy/kqueue memory leak [#36][] (reported by @nbkolchin)
+* [Fix] kqueue: use fsnFlags for watching a directory [#33][] (reported by @nbkolchin)
+
+## [0.8.2] - 2013-02-07
+
+* [Doc] add Authors
+* [Fix] fix data races for map access [#29][] (thanks @fsouza)
+
+## [0.8.1] - 2013-01-09
+
+* [Fix] Windows path separators
+* [Doc] BSD License
+
+## [0.8.0] - 2012-11-09
+
+* kqueue: directory watching improvements (thanks @vmirage)
+* inotify: add `IN_MOVED_TO` [#25][] (requested by @cpisto)
+* [Fix] kqueue: deleting watched directory [#24][] (reported by @jakerr)
+
+## [0.7.4] - 2012-10-09
+
+* [Fix] inotify: fixes from https://codereview.appspot.com/5418045/ (ugorji)
+* [Fix] kqueue: preserve watch flags when watching for delete [#21][] (reported by @robfig)
+* [Fix] kqueue: watch the directory even if it isn't a new watch (thanks @robfig)
+* [Fix] kqueue: modify after recreation of file
+
+## [0.7.3] - 2012-09-27
+
+* [Fix] kqueue: watch with an existing folder inside the watched folder (thanks @vmirage)
+* [Fix] kqueue: no longer get duplicate CREATE events
+
+## [0.7.2] - 2012-09-01
+
+* kqueue: events for created directories
+
+## [0.7.1] - 2012-07-14
+
+* [Fix] for renaming files
+
+## [0.7.0] - 2012-07-02
+
+* [Feature] FSNotify flags
+* [Fix] inotify: Added file name back to event path
+
+## [0.6.0] - 2012-06-06
+
+* kqueue: watch files after directory created (thanks @tmc)
+
+## [0.5.1] - 2012-05-22
+
+* [Fix] inotify: remove all watches before Close()
+
+## [0.5.0] - 2012-05-03
+
+* [API] kqueue: return errors during watch instead of sending over channel
+* kqueue: match symlink behavior on Linux
+* inotify: add `DELETE_SELF` (requested by @taralx)
+* [Fix] kqueue: handle EINTR (reported by @robfig)
+* [Doc] Godoc example [#1][] (thanks @davecheney)
+
+## [0.4.0] - 2012-03-30
+
+* Go 1 released: build with go tool
+* [Feature] Windows support using winfsnotify
+* Windows does not have attribute change notifications
+* Roll attribute notifications into IsModify
+
+## [0.3.0] - 2012-02-19
+
+* kqueue: add files when watch directory
+
+## [0.2.0] - 2011-12-30
+
+* update to latest Go weekly code
+
+## [0.1.0] - 2011-10-19
+
+* kqueue: add watch on file creation to match inotify
+* kqueue: create file event
+* inotify: ignore `IN_IGNORED` events
+* event String()
+* linux: common FileEvent functions
+* initial commit
+
+[#79]: https://github.com/howeyc/fsnotify/pull/79
+[#77]: https://github.com/howeyc/fsnotify/pull/77
+[#72]: https://github.com/howeyc/fsnotify/issues/72
+[#71]: https://github.com/howeyc/fsnotify/issues/71
+[#70]: https://github.com/howeyc/fsnotify/issues/70
+[#63]: https://github.com/howeyc/fsnotify/issues/63
+[#62]: https://github.com/howeyc/fsnotify/issues/62
+[#60]: https://github.com/howeyc/fsnotify/issues/60
+[#59]: https://github.com/howeyc/fsnotify/issues/59
+[#49]: https://github.com/howeyc/fsnotify/issues/49
+[#45]: https://github.com/howeyc/fsnotify/issues/45
+[#40]: https://github.com/howeyc/fsnotify/issues/40
+[#36]: https://github.com/howeyc/fsnotify/issues/36
+[#33]: https://github.com/howeyc/fsnotify/issues/33
+[#29]: https://github.com/howeyc/fsnotify/issues/29
+[#25]: https://github.com/howeyc/fsnotify/issues/25
+[#24]: https://github.com/howeyc/fsnotify/issues/24
+[#21]: https://github.com/howeyc/fsnotify/issues/21
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/fsnotify/fsnotify/CONTRIBUTING.md b/go/ql/test/experimental/CWE-321/vendor/github.com/fsnotify/fsnotify/CONTRIBUTING.md
new file mode 100644
index 000000000000..8a642563d718
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/fsnotify/fsnotify/CONTRIBUTING.md
@@ -0,0 +1,60 @@
+# Contributing
+
+## Issues
+
+* Request features and report bugs using the [GitHub Issue Tracker](https://github.com/fsnotify/fsnotify/issues).
+* Please indicate the platform you are using fsnotify on.
+* A code example to reproduce the problem is appreciated.
+
+## Pull Requests
+
+### Contributor License Agreement
+
+fsnotify is derived from code in the [golang.org/x/exp](https://godoc.org/golang.org/x/exp) package and it may be included [in the standard library](https://github.com/fsnotify/fsnotify/issues/1) in the future. Therefore fsnotify carries the same [LICENSE](https://github.com/fsnotify/fsnotify/blob/master/LICENSE) as Go. Contributors retain their copyright, so you need to fill out a short form before we can accept your contribution: [Google Individual Contributor License Agreement](https://developers.google.com/open-source/cla/individual).
+
+Please indicate that you have signed the CLA in your pull request.
+
+### How fsnotify is Developed
+
+* Development is done on feature branches.
+* Tests are run on BSD, Linux, macOS and Windows.
+* Pull requests are reviewed and [applied to master][am] using [hub][].
+ * Maintainers may modify or squash commits rather than asking contributors to.
+* To issue a new release, the maintainers will:
+ * Update the CHANGELOG
+ * Tag a version, which will become available through gopkg.in.
+
+### How to Fork
+
+For smooth sailing, always use the original import path. Installing with `go get` makes this easy.
+
+1. Install from GitHub (`go get -u github.com/fsnotify/fsnotify`)
+2. Create your feature branch (`git checkout -b my-new-feature`)
+3. Ensure everything works and the tests pass (see below)
+4. Commit your changes (`git commit -am 'Add some feature'`)
+
+Contribute upstream:
+
+1. Fork fsnotify on GitHub
+2. Add your remote (`git remote add fork git@github.com:mycompany/repo.git`)
+3. Push to the branch (`git push fork my-new-feature`)
+4. Create a new Pull Request on GitHub
+
+This workflow is [thoroughly explained by Katrina Owen](https://splice.com/blog/contributing-open-source-git-repositories-go/).
+
+### Testing
+
+fsnotify uses build tags to compile different code on Linux, BSD, macOS, and Windows.
+
+Before doing a pull request, please do your best to test your changes on multiple platforms, and list which platforms you were able/unable to test on.
+
+### Maintainers
+
+Help maintaining fsnotify is welcome. To be a maintainer:
+
+* Submit a pull request and sign the CLA as above.
+* You must be able to run the test suite on Mac, Windows, Linux and BSD.
+
+All code changes should be internal pull requests.
+
+Releases are tagged using [Semantic Versioning](http://semver.org/).
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/fsnotify/fsnotify/LICENSE b/go/ql/test/experimental/CWE-321/vendor/github.com/fsnotify/fsnotify/LICENSE
new file mode 100644
index 000000000000..e180c8fb0599
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/fsnotify/fsnotify/LICENSE
@@ -0,0 +1,28 @@
+Copyright (c) 2012 The Go Authors. All rights reserved.
+Copyright (c) 2012-2019 fsnotify Authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/fsnotify/fsnotify/README.md b/go/ql/test/experimental/CWE-321/vendor/github.com/fsnotify/fsnotify/README.md
new file mode 100644
index 000000000000..0731c5ef8adc
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/fsnotify/fsnotify/README.md
@@ -0,0 +1,120 @@
+# File system notifications for Go
+
+[](https://pkg.go.dev/github.com/fsnotify/fsnotify) [](https://goreportcard.com/report/github.com/fsnotify/fsnotify) [](https://github.com/fsnotify/fsnotify/issues/413)
+
+fsnotify utilizes [`golang.org/x/sys`](https://pkg.go.dev/golang.org/x/sys) rather than [`syscall`](https://pkg.go.dev/syscall) from the standard library.
+
+Cross platform: Windows, Linux, BSD and macOS.
+
+| Adapter | OS | Status |
+| --------------------- | -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
+| inotify | Linux 2.6.27 or later, Android\* | Supported |
+| kqueue | BSD, macOS, iOS\* | Supported |
+| ReadDirectoryChangesW | Windows | Supported |
+| FSEvents | macOS | [Planned](https://github.com/fsnotify/fsnotify/issues/11) |
+| FEN | Solaris 11 | [In Progress](https://github.com/fsnotify/fsnotify/pull/371) |
+| fanotify | Linux 2.6.37+ | [Maybe](https://github.com/fsnotify/fsnotify/issues/114) |
+| USN Journals | Windows | [Maybe](https://github.com/fsnotify/fsnotify/issues/53) |
+| Polling | *All* | [Maybe](https://github.com/fsnotify/fsnotify/issues/9) |
+
+\* Android and iOS are untested.
+
+Please see [the documentation](https://pkg.go.dev/github.com/fsnotify/fsnotify) and consult the [FAQ](#faq) for usage information.
+
+## API stability
+
+fsnotify is a fork of [howeyc/fsnotify](https://github.com/howeyc/fsnotify) with a new API as of v1.0. The API is based on [this design document](http://goo.gl/MrYxyA).
+
+All [releases](https://github.com/fsnotify/fsnotify/releases) are tagged based on [Semantic Versioning](http://semver.org/).
+
+## Usage
+
+```go
+package main
+
+import (
+ "log"
+
+ "github.com/fsnotify/fsnotify"
+)
+
+func main() {
+ watcher, err := fsnotify.NewWatcher()
+ if err != nil {
+ log.Fatal(err)
+ }
+ defer watcher.Close()
+
+ done := make(chan bool)
+ go func() {
+ for {
+ select {
+ case event, ok := <-watcher.Events:
+ if !ok {
+ return
+ }
+ log.Println("event:", event)
+ if event.Op&fsnotify.Write == fsnotify.Write {
+ log.Println("modified file:", event.Name)
+ }
+ case err, ok := <-watcher.Errors:
+ if !ok {
+ return
+ }
+ log.Println("error:", err)
+ }
+ }
+ }()
+
+ err = watcher.Add("/tmp/foo")
+ if err != nil {
+ log.Fatal(err)
+ }
+ <-done
+}
+```
+
+## Contributing
+
+Please refer to [CONTRIBUTING][] before opening an issue or pull request.
+
+## FAQ
+
+**When a file is moved to another directory is it still being watched?**
+
+No (it shouldn't be, unless you are watching where it was moved to).
+
+**When I watch a directory, are all subdirectories watched as well?**
+
+No, you must add watches for any directory you want to watch (a recursive watcher is on the roadmap [#18][]).
+
+**Do I have to watch the Error and Event channels in a separate goroutine?**
+
+As of now, yes. Looking into making this single-thread friendly (see [howeyc #7][#7])
+
+**Why am I receiving multiple events for the same file on OS X?**
+
+Spotlight indexing on OS X can result in multiple events (see [howeyc #62][#62]). A temporary workaround is to add your folder(s) to the *Spotlight Privacy settings* until we have a native FSEvents implementation (see [#11][]).
+
+**How many files can be watched at once?**
+
+There are OS-specific limits as to how many watches can be created:
+* Linux: /proc/sys/fs/inotify/max_user_watches contains the limit, reaching this limit results in a "no space left on device" error.
+* BSD / OSX: sysctl variables "kern.maxfiles" and "kern.maxfilesperproc", reaching these limits results in a "too many open files" error.
+
+**Why don't notifications work with NFS filesystems or filesystem in userspace (FUSE)?**
+
+fsnotify requires support from underlying OS to work. The current NFS protocol does not provide network level support for file notifications.
+
+[#62]: https://github.com/howeyc/fsnotify/issues/62
+[#18]: https://github.com/fsnotify/fsnotify/issues/18
+[#11]: https://github.com/fsnotify/fsnotify/issues/11
+[#7]: https://github.com/howeyc/fsnotify/issues/7
+
+[contributing]: https://github.com/fsnotify/fsnotify/blob/master/CONTRIBUTING.md
+
+## Related Projects
+
+* [notify](https://github.com/rjeczalik/notify)
+* [fsevents](https://github.com/fsnotify/fsevents)
+
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/fsnotify/fsnotify/fen.go b/go/ql/test/experimental/CWE-321/vendor/github.com/fsnotify/fsnotify/fen.go
new file mode 100644
index 000000000000..b3ac3d8f55fa
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/fsnotify/fsnotify/fen.go
@@ -0,0 +1,38 @@
+// Copyright 2010 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build solaris
+// +build solaris
+
+package fsnotify
+
+import (
+ "errors"
+)
+
+// Watcher watches a set of files, delivering events to a channel.
+type Watcher struct {
+ Events chan Event
+ Errors chan error
+}
+
+// NewWatcher establishes a new watcher with the underlying OS and begins waiting for events.
+func NewWatcher() (*Watcher, error) {
+ return nil, errors.New("FEN based watcher not yet supported for fsnotify\n")
+}
+
+// Close removes all watches and closes the events channel.
+func (w *Watcher) Close() error {
+ return nil
+}
+
+// Add starts watching the named file or directory (non-recursively).
+func (w *Watcher) Add(name string) error {
+ return nil
+}
+
+// Remove stops watching the the named file or directory (non-recursively).
+func (w *Watcher) Remove(name string) error {
+ return nil
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/fsnotify/fsnotify/fsnotify.go b/go/ql/test/experimental/CWE-321/vendor/github.com/fsnotify/fsnotify/fsnotify.go
new file mode 100644
index 000000000000..0f4ee52e8aa2
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/fsnotify/fsnotify/fsnotify.go
@@ -0,0 +1,69 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build !plan9
+// +build !plan9
+
+// Package fsnotify provides a platform-independent interface for file system notifications.
+package fsnotify
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+)
+
+// Event represents a single file system notification.
+type Event struct {
+ Name string // Relative path to the file or directory.
+ Op Op // File operation that triggered the event.
+}
+
+// Op describes a set of file operations.
+type Op uint32
+
+// These are the generalized file operations that can trigger a notification.
+const (
+ Create Op = 1 << iota
+ Write
+ Remove
+ Rename
+ Chmod
+)
+
+func (op Op) String() string {
+ // Use a buffer for efficient string concatenation
+ var buffer bytes.Buffer
+
+ if op&Create == Create {
+ buffer.WriteString("|CREATE")
+ }
+ if op&Remove == Remove {
+ buffer.WriteString("|REMOVE")
+ }
+ if op&Write == Write {
+ buffer.WriteString("|WRITE")
+ }
+ if op&Rename == Rename {
+ buffer.WriteString("|RENAME")
+ }
+ if op&Chmod == Chmod {
+ buffer.WriteString("|CHMOD")
+ }
+ if buffer.Len() == 0 {
+ return ""
+ }
+ return buffer.String()[1:] // Strip leading pipe
+}
+
+// String returns a string representation of the event in the form
+// "file: REMOVE|WRITE|..."
+func (e Event) String() string {
+ return fmt.Sprintf("%q: %s", e.Name, e.Op.String())
+}
+
+// Common errors that can be reported by a watcher
+var (
+ ErrEventOverflow = errors.New("fsnotify queue overflow")
+)
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/fsnotify/fsnotify/fsnotify_unsupported.go b/go/ql/test/experimental/CWE-321/vendor/github.com/fsnotify/fsnotify/fsnotify_unsupported.go
new file mode 100644
index 000000000000..59688559836f
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/fsnotify/fsnotify/fsnotify_unsupported.go
@@ -0,0 +1,36 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build !darwin && !dragonfly && !freebsd && !openbsd && !linux && !netbsd && !solaris && !windows
+// +build !darwin,!dragonfly,!freebsd,!openbsd,!linux,!netbsd,!solaris,!windows
+
+package fsnotify
+
+import (
+ "fmt"
+ "runtime"
+)
+
+// Watcher watches a set of files, delivering events to a channel.
+type Watcher struct{}
+
+// NewWatcher establishes a new watcher with the underlying OS and begins waiting for events.
+func NewWatcher() (*Watcher, error) {
+ return nil, fmt.Errorf("fsnotify not supported on %s", runtime.GOOS)
+}
+
+// Close removes all watches and closes the events channel.
+func (w *Watcher) Close() error {
+ return nil
+}
+
+// Add starts watching the named file or directory (non-recursively).
+func (w *Watcher) Add(name string) error {
+ return nil
+}
+
+// Remove stops watching the the named file or directory (non-recursively).
+func (w *Watcher) Remove(name string) error {
+ return nil
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/fsnotify/fsnotify/inotify.go b/go/ql/test/experimental/CWE-321/vendor/github.com/fsnotify/fsnotify/inotify.go
new file mode 100644
index 000000000000..a6d0e0ec8c10
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/fsnotify/fsnotify/inotify.go
@@ -0,0 +1,351 @@
+// Copyright 2010 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build linux
+// +build linux
+
+package fsnotify
+
+import (
+ "errors"
+ "fmt"
+ "io"
+ "os"
+ "path/filepath"
+ "strings"
+ "sync"
+ "unsafe"
+
+ "golang.org/x/sys/unix"
+)
+
+// Watcher watches a set of files, delivering events to a channel.
+type Watcher struct {
+ Events chan Event
+ Errors chan error
+ mu sync.Mutex // Map access
+ fd int
+ poller *fdPoller
+ watches map[string]*watch // Map of inotify watches (key: path)
+ paths map[int]string // Map of watched paths (key: watch descriptor)
+ done chan struct{} // Channel for sending a "quit message" to the reader goroutine
+ doneResp chan struct{} // Channel to respond to Close
+}
+
+// NewWatcher establishes a new watcher with the underlying OS and begins waiting for events.
+func NewWatcher() (*Watcher, error) {
+ // Create inotify fd
+ fd, errno := unix.InotifyInit1(unix.IN_CLOEXEC)
+ if fd == -1 {
+ return nil, errno
+ }
+ // Create epoll
+ poller, err := newFdPoller(fd)
+ if err != nil {
+ unix.Close(fd)
+ return nil, err
+ }
+ w := &Watcher{
+ fd: fd,
+ poller: poller,
+ watches: make(map[string]*watch),
+ paths: make(map[int]string),
+ Events: make(chan Event),
+ Errors: make(chan error),
+ done: make(chan struct{}),
+ doneResp: make(chan struct{}),
+ }
+
+ go w.readEvents()
+ return w, nil
+}
+
+func (w *Watcher) isClosed() bool {
+ select {
+ case <-w.done:
+ return true
+ default:
+ return false
+ }
+}
+
+// Close removes all watches and closes the events channel.
+func (w *Watcher) Close() error {
+ if w.isClosed() {
+ return nil
+ }
+
+ // Send 'close' signal to goroutine, and set the Watcher to closed.
+ close(w.done)
+
+ // Wake up goroutine
+ w.poller.wake()
+
+ // Wait for goroutine to close
+ <-w.doneResp
+
+ return nil
+}
+
+// Add starts watching the named file or directory (non-recursively).
+func (w *Watcher) Add(name string) error {
+ name = filepath.Clean(name)
+ if w.isClosed() {
+ return errors.New("inotify instance already closed")
+ }
+
+ const agnosticEvents = unix.IN_MOVED_TO | unix.IN_MOVED_FROM |
+ unix.IN_CREATE | unix.IN_ATTRIB | unix.IN_MODIFY |
+ unix.IN_MOVE_SELF | unix.IN_DELETE | unix.IN_DELETE_SELF
+
+ var flags uint32 = agnosticEvents
+
+ w.mu.Lock()
+ defer w.mu.Unlock()
+ watchEntry := w.watches[name]
+ if watchEntry != nil {
+ flags |= watchEntry.flags | unix.IN_MASK_ADD
+ }
+ wd, errno := unix.InotifyAddWatch(w.fd, name, flags)
+ if wd == -1 {
+ return errno
+ }
+
+ if watchEntry == nil {
+ w.watches[name] = &watch{wd: uint32(wd), flags: flags}
+ w.paths[wd] = name
+ } else {
+ watchEntry.wd = uint32(wd)
+ watchEntry.flags = flags
+ }
+
+ return nil
+}
+
+// Remove stops watching the named file or directory (non-recursively).
+func (w *Watcher) Remove(name string) error {
+ name = filepath.Clean(name)
+
+ // Fetch the watch.
+ w.mu.Lock()
+ defer w.mu.Unlock()
+ watch, ok := w.watches[name]
+
+ // Remove it from inotify.
+ if !ok {
+ return fmt.Errorf("can't remove non-existent inotify watch for: %s", name)
+ }
+
+ // We successfully removed the watch if InotifyRmWatch doesn't return an
+ // error, we need to clean up our internal state to ensure it matches
+ // inotify's kernel state.
+ delete(w.paths, int(watch.wd))
+ delete(w.watches, name)
+
+ // inotify_rm_watch will return EINVAL if the file has been deleted;
+ // the inotify will already have been removed.
+ // watches and pathes are deleted in ignoreLinux() implicitly and asynchronously
+ // by calling inotify_rm_watch() below. e.g. readEvents() goroutine receives IN_IGNORE
+ // so that EINVAL means that the wd is being rm_watch()ed or its file removed
+ // by another thread and we have not received IN_IGNORE event.
+ success, errno := unix.InotifyRmWatch(w.fd, watch.wd)
+ if success == -1 {
+ // TODO: Perhaps it's not helpful to return an error here in every case.
+ // the only two possible errors are:
+ // EBADF, which happens when w.fd is not a valid file descriptor of any kind.
+ // EINVAL, which is when fd is not an inotify descriptor or wd is not a valid watch descriptor.
+ // Watch descriptors are invalidated when they are removed explicitly or implicitly;
+ // explicitly by inotify_rm_watch, implicitly when the file they are watching is deleted.
+ return errno
+ }
+
+ return nil
+}
+
+// WatchList returns the directories and files that are being monitered.
+func (w *Watcher) WatchList() []string {
+ w.mu.Lock()
+ defer w.mu.Unlock()
+
+ entries := make([]string, 0, len(w.watches))
+ for pathname := range w.watches {
+ entries = append(entries, pathname)
+ }
+
+ return entries
+}
+
+type watch struct {
+ wd uint32 // Watch descriptor (as returned by the inotify_add_watch() syscall)
+ flags uint32 // inotify flags of this watch (see inotify(7) for the list of valid flags)
+}
+
+// readEvents reads from the inotify file descriptor, converts the
+// received events into Event objects and sends them via the Events channel
+func (w *Watcher) readEvents() {
+ var (
+ buf [unix.SizeofInotifyEvent * 4096]byte // Buffer for a maximum of 4096 raw events
+ n int // Number of bytes read with read()
+ errno error // Syscall errno
+ ok bool // For poller.wait
+ )
+
+ defer close(w.doneResp)
+ defer close(w.Errors)
+ defer close(w.Events)
+ defer unix.Close(w.fd)
+ defer w.poller.close()
+
+ for {
+ // See if we have been closed.
+ if w.isClosed() {
+ return
+ }
+
+ ok, errno = w.poller.wait()
+ if errno != nil {
+ select {
+ case w.Errors <- errno:
+ case <-w.done:
+ return
+ }
+ continue
+ }
+
+ if !ok {
+ continue
+ }
+
+ n, errno = unix.Read(w.fd, buf[:])
+ // If a signal interrupted execution, see if we've been asked to close, and try again.
+ // http://man7.org/linux/man-pages/man7/signal.7.html :
+ // "Before Linux 3.8, reads from an inotify(7) file descriptor were not restartable"
+ if errno == unix.EINTR {
+ continue
+ }
+
+ // unix.Read might have been woken up by Close. If so, we're done.
+ if w.isClosed() {
+ return
+ }
+
+ if n < unix.SizeofInotifyEvent {
+ var err error
+ if n == 0 {
+ // If EOF is received. This should really never happen.
+ err = io.EOF
+ } else if n < 0 {
+ // If an error occurred while reading.
+ err = errno
+ } else {
+ // Read was too short.
+ err = errors.New("notify: short read in readEvents()")
+ }
+ select {
+ case w.Errors <- err:
+ case <-w.done:
+ return
+ }
+ continue
+ }
+
+ var offset uint32
+ // We don't know how many events we just read into the buffer
+ // While the offset points to at least one whole event...
+ for offset <= uint32(n-unix.SizeofInotifyEvent) {
+ // Point "raw" to the event in the buffer
+ raw := (*unix.InotifyEvent)(unsafe.Pointer(&buf[offset]))
+
+ mask := uint32(raw.Mask)
+ nameLen := uint32(raw.Len)
+
+ if mask&unix.IN_Q_OVERFLOW != 0 {
+ select {
+ case w.Errors <- ErrEventOverflow:
+ case <-w.done:
+ return
+ }
+ }
+
+ // If the event happened to the watched directory or the watched file, the kernel
+ // doesn't append the filename to the event, but we would like to always fill the
+ // the "Name" field with a valid filename. We retrieve the path of the watch from
+ // the "paths" map.
+ w.mu.Lock()
+ name, ok := w.paths[int(raw.Wd)]
+ // IN_DELETE_SELF occurs when the file/directory being watched is removed.
+ // This is a sign to clean up the maps, otherwise we are no longer in sync
+ // with the inotify kernel state which has already deleted the watch
+ // automatically.
+ if ok && mask&unix.IN_DELETE_SELF == unix.IN_DELETE_SELF {
+ delete(w.paths, int(raw.Wd))
+ delete(w.watches, name)
+ }
+ w.mu.Unlock()
+
+ if nameLen > 0 {
+ // Point "bytes" at the first byte of the filename
+ bytes := (*[unix.PathMax]byte)(unsafe.Pointer(&buf[offset+unix.SizeofInotifyEvent]))[:nameLen:nameLen]
+ // The filename is padded with NULL bytes. TrimRight() gets rid of those.
+ name += "/" + strings.TrimRight(string(bytes[0:nameLen]), "\000")
+ }
+
+ event := newEvent(name, mask)
+
+ // Send the events that are not ignored on the events channel
+ if !event.ignoreLinux(mask) {
+ select {
+ case w.Events <- event:
+ case <-w.done:
+ return
+ }
+ }
+
+ // Move to the next event in the buffer
+ offset += unix.SizeofInotifyEvent + nameLen
+ }
+ }
+}
+
+// Certain types of events can be "ignored" and not sent over the Events
+// channel. Such as events marked ignore by the kernel, or MODIFY events
+// against files that do not exist.
+func (e *Event) ignoreLinux(mask uint32) bool {
+ // Ignore anything the inotify API says to ignore
+ if mask&unix.IN_IGNORED == unix.IN_IGNORED {
+ return true
+ }
+
+ // If the event is not a DELETE or RENAME, the file must exist.
+ // Otherwise the event is ignored.
+ // *Note*: this was put in place because it was seen that a MODIFY
+ // event was sent after the DELETE. This ignores that MODIFY and
+ // assumes a DELETE will come or has come if the file doesn't exist.
+ if !(e.Op&Remove == Remove || e.Op&Rename == Rename) {
+ _, statErr := os.Lstat(e.Name)
+ return os.IsNotExist(statErr)
+ }
+ return false
+}
+
+// newEvent returns an platform-independent Event based on an inotify mask.
+func newEvent(name string, mask uint32) Event {
+ e := Event{Name: name}
+ if mask&unix.IN_CREATE == unix.IN_CREATE || mask&unix.IN_MOVED_TO == unix.IN_MOVED_TO {
+ e.Op |= Create
+ }
+ if mask&unix.IN_DELETE_SELF == unix.IN_DELETE_SELF || mask&unix.IN_DELETE == unix.IN_DELETE {
+ e.Op |= Remove
+ }
+ if mask&unix.IN_MODIFY == unix.IN_MODIFY {
+ e.Op |= Write
+ }
+ if mask&unix.IN_MOVE_SELF == unix.IN_MOVE_SELF || mask&unix.IN_MOVED_FROM == unix.IN_MOVED_FROM {
+ e.Op |= Rename
+ }
+ if mask&unix.IN_ATTRIB == unix.IN_ATTRIB {
+ e.Op |= Chmod
+ }
+ return e
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/fsnotify/fsnotify/inotify_poller.go b/go/ql/test/experimental/CWE-321/vendor/github.com/fsnotify/fsnotify/inotify_poller.go
new file mode 100644
index 000000000000..b572a37c3f1a
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/fsnotify/fsnotify/inotify_poller.go
@@ -0,0 +1,187 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build linux
+// +build linux
+
+package fsnotify
+
+import (
+ "errors"
+
+ "golang.org/x/sys/unix"
+)
+
+type fdPoller struct {
+ fd int // File descriptor (as returned by the inotify_init() syscall)
+ epfd int // Epoll file descriptor
+ pipe [2]int // Pipe for waking up
+}
+
+func emptyPoller(fd int) *fdPoller {
+ poller := new(fdPoller)
+ poller.fd = fd
+ poller.epfd = -1
+ poller.pipe[0] = -1
+ poller.pipe[1] = -1
+ return poller
+}
+
+// Create a new inotify poller.
+// This creates an inotify handler, and an epoll handler.
+func newFdPoller(fd int) (*fdPoller, error) {
+ var errno error
+ poller := emptyPoller(fd)
+ defer func() {
+ if errno != nil {
+ poller.close()
+ }
+ }()
+
+ // Create epoll fd
+ poller.epfd, errno = unix.EpollCreate1(unix.EPOLL_CLOEXEC)
+ if poller.epfd == -1 {
+ return nil, errno
+ }
+ // Create pipe; pipe[0] is the read end, pipe[1] the write end.
+ errno = unix.Pipe2(poller.pipe[:], unix.O_NONBLOCK|unix.O_CLOEXEC)
+ if errno != nil {
+ return nil, errno
+ }
+
+ // Register inotify fd with epoll
+ event := unix.EpollEvent{
+ Fd: int32(poller.fd),
+ Events: unix.EPOLLIN,
+ }
+ errno = unix.EpollCtl(poller.epfd, unix.EPOLL_CTL_ADD, poller.fd, &event)
+ if errno != nil {
+ return nil, errno
+ }
+
+ // Register pipe fd with epoll
+ event = unix.EpollEvent{
+ Fd: int32(poller.pipe[0]),
+ Events: unix.EPOLLIN,
+ }
+ errno = unix.EpollCtl(poller.epfd, unix.EPOLL_CTL_ADD, poller.pipe[0], &event)
+ if errno != nil {
+ return nil, errno
+ }
+
+ return poller, nil
+}
+
+// Wait using epoll.
+// Returns true if something is ready to be read,
+// false if there is not.
+func (poller *fdPoller) wait() (bool, error) {
+ // 3 possible events per fd, and 2 fds, makes a maximum of 6 events.
+ // I don't know whether epoll_wait returns the number of events returned,
+ // or the total number of events ready.
+ // I decided to catch both by making the buffer one larger than the maximum.
+ events := make([]unix.EpollEvent, 7)
+ for {
+ n, errno := unix.EpollWait(poller.epfd, events, -1)
+ if n == -1 {
+ if errno == unix.EINTR {
+ continue
+ }
+ return false, errno
+ }
+ if n == 0 {
+ // If there are no events, try again.
+ continue
+ }
+ if n > 6 {
+ // This should never happen. More events were returned than should be possible.
+ return false, errors.New("epoll_wait returned more events than I know what to do with")
+ }
+ ready := events[:n]
+ epollhup := false
+ epollerr := false
+ epollin := false
+ for _, event := range ready {
+ if event.Fd == int32(poller.fd) {
+ if event.Events&unix.EPOLLHUP != 0 {
+ // This should not happen, but if it does, treat it as a wakeup.
+ epollhup = true
+ }
+ if event.Events&unix.EPOLLERR != 0 {
+ // If an error is waiting on the file descriptor, we should pretend
+ // something is ready to read, and let unix.Read pick up the error.
+ epollerr = true
+ }
+ if event.Events&unix.EPOLLIN != 0 {
+ // There is data to read.
+ epollin = true
+ }
+ }
+ if event.Fd == int32(poller.pipe[0]) {
+ if event.Events&unix.EPOLLHUP != 0 {
+ // Write pipe descriptor was closed, by us. This means we're closing down the
+ // watcher, and we should wake up.
+ }
+ if event.Events&unix.EPOLLERR != 0 {
+ // If an error is waiting on the pipe file descriptor.
+ // This is an absolute mystery, and should never ever happen.
+ return false, errors.New("Error on the pipe descriptor.")
+ }
+ if event.Events&unix.EPOLLIN != 0 {
+ // This is a regular wakeup, so we have to clear the buffer.
+ err := poller.clearWake()
+ if err != nil {
+ return false, err
+ }
+ }
+ }
+ }
+
+ if epollhup || epollerr || epollin {
+ return true, nil
+ }
+ return false, nil
+ }
+}
+
+// Close the write end of the poller.
+func (poller *fdPoller) wake() error {
+ buf := make([]byte, 1)
+ n, errno := unix.Write(poller.pipe[1], buf)
+ if n == -1 {
+ if errno == unix.EAGAIN {
+ // Buffer is full, poller will wake.
+ return nil
+ }
+ return errno
+ }
+ return nil
+}
+
+func (poller *fdPoller) clearWake() error {
+ // You have to be woken up a LOT in order to get to 100!
+ buf := make([]byte, 100)
+ n, errno := unix.Read(poller.pipe[0], buf)
+ if n == -1 {
+ if errno == unix.EAGAIN {
+ // Buffer is empty, someone else cleared our wake.
+ return nil
+ }
+ return errno
+ }
+ return nil
+}
+
+// Close all poller file descriptors, but not the one passed to it.
+func (poller *fdPoller) close() {
+ if poller.pipe[1] != -1 {
+ unix.Close(poller.pipe[1])
+ }
+ if poller.pipe[0] != -1 {
+ unix.Close(poller.pipe[0])
+ }
+ if poller.epfd != -1 {
+ unix.Close(poller.epfd)
+ }
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/fsnotify/fsnotify/kqueue.go b/go/ql/test/experimental/CWE-321/vendor/github.com/fsnotify/fsnotify/kqueue.go
new file mode 100644
index 000000000000..6fb8d8532e7f
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/fsnotify/fsnotify/kqueue.go
@@ -0,0 +1,535 @@
+// Copyright 2010 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build freebsd || openbsd || netbsd || dragonfly || darwin
+// +build freebsd openbsd netbsd dragonfly darwin
+
+package fsnotify
+
+import (
+ "errors"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "sync"
+ "time"
+
+ "golang.org/x/sys/unix"
+)
+
+// Watcher watches a set of files, delivering events to a channel.
+type Watcher struct {
+ Events chan Event
+ Errors chan error
+ done chan struct{} // Channel for sending a "quit message" to the reader goroutine
+
+ kq int // File descriptor (as returned by the kqueue() syscall).
+
+ mu sync.Mutex // Protects access to watcher data
+ watches map[string]int // Map of watched file descriptors (key: path).
+ externalWatches map[string]bool // Map of watches added by user of the library.
+ dirFlags map[string]uint32 // Map of watched directories to fflags used in kqueue.
+ paths map[int]pathInfo // Map file descriptors to path names for processing kqueue events.
+ fileExists map[string]bool // Keep track of if we know this file exists (to stop duplicate create events).
+ isClosed bool // Set to true when Close() is first called
+}
+
+type pathInfo struct {
+ name string
+ isDir bool
+}
+
+// NewWatcher establishes a new watcher with the underlying OS and begins waiting for events.
+func NewWatcher() (*Watcher, error) {
+ kq, err := kqueue()
+ if err != nil {
+ return nil, err
+ }
+
+ w := &Watcher{
+ kq: kq,
+ watches: make(map[string]int),
+ dirFlags: make(map[string]uint32),
+ paths: make(map[int]pathInfo),
+ fileExists: make(map[string]bool),
+ externalWatches: make(map[string]bool),
+ Events: make(chan Event),
+ Errors: make(chan error),
+ done: make(chan struct{}),
+ }
+
+ go w.readEvents()
+ return w, nil
+}
+
+// Close removes all watches and closes the events channel.
+func (w *Watcher) Close() error {
+ w.mu.Lock()
+ if w.isClosed {
+ w.mu.Unlock()
+ return nil
+ }
+ w.isClosed = true
+
+ // copy paths to remove while locked
+ var pathsToRemove = make([]string, 0, len(w.watches))
+ for name := range w.watches {
+ pathsToRemove = append(pathsToRemove, name)
+ }
+ w.mu.Unlock()
+ // unlock before calling Remove, which also locks
+
+ for _, name := range pathsToRemove {
+ w.Remove(name)
+ }
+
+ // send a "quit" message to the reader goroutine
+ close(w.done)
+
+ return nil
+}
+
+// Add starts watching the named file or directory (non-recursively).
+func (w *Watcher) Add(name string) error {
+ w.mu.Lock()
+ w.externalWatches[name] = true
+ w.mu.Unlock()
+ _, err := w.addWatch(name, noteAllEvents)
+ return err
+}
+
+// Remove stops watching the the named file or directory (non-recursively).
+func (w *Watcher) Remove(name string) error {
+ name = filepath.Clean(name)
+ w.mu.Lock()
+ watchfd, ok := w.watches[name]
+ w.mu.Unlock()
+ if !ok {
+ return fmt.Errorf("can't remove non-existent kevent watch for: %s", name)
+ }
+
+ const registerRemove = unix.EV_DELETE
+ if err := register(w.kq, []int{watchfd}, registerRemove, 0); err != nil {
+ return err
+ }
+
+ unix.Close(watchfd)
+
+ w.mu.Lock()
+ isDir := w.paths[watchfd].isDir
+ delete(w.watches, name)
+ delete(w.paths, watchfd)
+ delete(w.dirFlags, name)
+ w.mu.Unlock()
+
+ // Find all watched paths that are in this directory that are not external.
+ if isDir {
+ var pathsToRemove []string
+ w.mu.Lock()
+ for _, path := range w.paths {
+ wdir, _ := filepath.Split(path.name)
+ if filepath.Clean(wdir) == name {
+ if !w.externalWatches[path.name] {
+ pathsToRemove = append(pathsToRemove, path.name)
+ }
+ }
+ }
+ w.mu.Unlock()
+ for _, name := range pathsToRemove {
+ // Since these are internal, not much sense in propagating error
+ // to the user, as that will just confuse them with an error about
+ // a path they did not explicitly watch themselves.
+ w.Remove(name)
+ }
+ }
+
+ return nil
+}
+
+// WatchList returns the directories and files that are being monitered.
+func (w *Watcher) WatchList() []string {
+ w.mu.Lock()
+ defer w.mu.Unlock()
+
+ entries := make([]string, 0, len(w.watches))
+ for pathname := range w.watches {
+ entries = append(entries, pathname)
+ }
+
+ return entries
+}
+
+// Watch all events (except NOTE_EXTEND, NOTE_LINK, NOTE_REVOKE)
+const noteAllEvents = unix.NOTE_DELETE | unix.NOTE_WRITE | unix.NOTE_ATTRIB | unix.NOTE_RENAME
+
+// keventWaitTime to block on each read from kevent
+var keventWaitTime = durationToTimespec(100 * time.Millisecond)
+
+// addWatch adds name to the watched file set.
+// The flags are interpreted as described in kevent(2).
+// Returns the real path to the file which was added, if any, which may be different from the one passed in the case of symlinks.
+func (w *Watcher) addWatch(name string, flags uint32) (string, error) {
+ var isDir bool
+ // Make ./name and name equivalent
+ name = filepath.Clean(name)
+
+ w.mu.Lock()
+ if w.isClosed {
+ w.mu.Unlock()
+ return "", errors.New("kevent instance already closed")
+ }
+ watchfd, alreadyWatching := w.watches[name]
+ // We already have a watch, but we can still override flags.
+ if alreadyWatching {
+ isDir = w.paths[watchfd].isDir
+ }
+ w.mu.Unlock()
+
+ if !alreadyWatching {
+ fi, err := os.Lstat(name)
+ if err != nil {
+ return "", err
+ }
+
+ // Don't watch sockets.
+ if fi.Mode()&os.ModeSocket == os.ModeSocket {
+ return "", nil
+ }
+
+ // Don't watch named pipes.
+ if fi.Mode()&os.ModeNamedPipe == os.ModeNamedPipe {
+ return "", nil
+ }
+
+ // Follow Symlinks
+ // Unfortunately, Linux can add bogus symlinks to watch list without
+ // issue, and Windows can't do symlinks period (AFAIK). To maintain
+ // consistency, we will act like everything is fine. There will simply
+ // be no file events for broken symlinks.
+ // Hence the returns of nil on errors.
+ if fi.Mode()&os.ModeSymlink == os.ModeSymlink {
+ name, err = filepath.EvalSymlinks(name)
+ if err != nil {
+ return "", nil
+ }
+
+ w.mu.Lock()
+ _, alreadyWatching = w.watches[name]
+ w.mu.Unlock()
+
+ if alreadyWatching {
+ return name, nil
+ }
+
+ fi, err = os.Lstat(name)
+ if err != nil {
+ return "", nil
+ }
+ }
+
+ watchfd, err = unix.Open(name, openMode, 0700)
+ if watchfd == -1 {
+ return "", err
+ }
+
+ isDir = fi.IsDir()
+ }
+
+ const registerAdd = unix.EV_ADD | unix.EV_CLEAR | unix.EV_ENABLE
+ if err := register(w.kq, []int{watchfd}, registerAdd, flags); err != nil {
+ unix.Close(watchfd)
+ return "", err
+ }
+
+ if !alreadyWatching {
+ w.mu.Lock()
+ w.watches[name] = watchfd
+ w.paths[watchfd] = pathInfo{name: name, isDir: isDir}
+ w.mu.Unlock()
+ }
+
+ if isDir {
+ // Watch the directory if it has not been watched before,
+ // or if it was watched before, but perhaps only a NOTE_DELETE (watchDirectoryFiles)
+ w.mu.Lock()
+
+ watchDir := (flags&unix.NOTE_WRITE) == unix.NOTE_WRITE &&
+ (!alreadyWatching || (w.dirFlags[name]&unix.NOTE_WRITE) != unix.NOTE_WRITE)
+ // Store flags so this watch can be updated later
+ w.dirFlags[name] = flags
+ w.mu.Unlock()
+
+ if watchDir {
+ if err := w.watchDirectoryFiles(name); err != nil {
+ return "", err
+ }
+ }
+ }
+ return name, nil
+}
+
+// readEvents reads from kqueue and converts the received kevents into
+// Event values that it sends down the Events channel.
+func (w *Watcher) readEvents() {
+ eventBuffer := make([]unix.Kevent_t, 10)
+
+loop:
+ for {
+ // See if there is a message on the "done" channel
+ select {
+ case <-w.done:
+ break loop
+ default:
+ }
+
+ // Get new events
+ kevents, err := read(w.kq, eventBuffer, &keventWaitTime)
+ // EINTR is okay, the syscall was interrupted before timeout expired.
+ if err != nil && err != unix.EINTR {
+ select {
+ case w.Errors <- err:
+ case <-w.done:
+ break loop
+ }
+ continue
+ }
+
+ // Flush the events we received to the Events channel
+ for len(kevents) > 0 {
+ kevent := &kevents[0]
+ watchfd := int(kevent.Ident)
+ mask := uint32(kevent.Fflags)
+ w.mu.Lock()
+ path := w.paths[watchfd]
+ w.mu.Unlock()
+ event := newEvent(path.name, mask)
+
+ if path.isDir && !(event.Op&Remove == Remove) {
+ // Double check to make sure the directory exists. This can happen when
+ // we do a rm -fr on a recursively watched folders and we receive a
+ // modification event first but the folder has been deleted and later
+ // receive the delete event
+ if _, err := os.Lstat(event.Name); os.IsNotExist(err) {
+ // mark is as delete event
+ event.Op |= Remove
+ }
+ }
+
+ if event.Op&Rename == Rename || event.Op&Remove == Remove {
+ w.Remove(event.Name)
+ w.mu.Lock()
+ delete(w.fileExists, event.Name)
+ w.mu.Unlock()
+ }
+
+ if path.isDir && event.Op&Write == Write && !(event.Op&Remove == Remove) {
+ w.sendDirectoryChangeEvents(event.Name)
+ } else {
+ // Send the event on the Events channel.
+ select {
+ case w.Events <- event:
+ case <-w.done:
+ break loop
+ }
+ }
+
+ if event.Op&Remove == Remove {
+ // Look for a file that may have overwritten this.
+ // For example, mv f1 f2 will delete f2, then create f2.
+ if path.isDir {
+ fileDir := filepath.Clean(event.Name)
+ w.mu.Lock()
+ _, found := w.watches[fileDir]
+ w.mu.Unlock()
+ if found {
+ // make sure the directory exists before we watch for changes. When we
+ // do a recursive watch and perform rm -fr, the parent directory might
+ // have gone missing, ignore the missing directory and let the
+ // upcoming delete event remove the watch from the parent directory.
+ if _, err := os.Lstat(fileDir); err == nil {
+ w.sendDirectoryChangeEvents(fileDir)
+ }
+ }
+ } else {
+ filePath := filepath.Clean(event.Name)
+ if fileInfo, err := os.Lstat(filePath); err == nil {
+ w.sendFileCreatedEventIfNew(filePath, fileInfo)
+ }
+ }
+ }
+
+ // Move to next event
+ kevents = kevents[1:]
+ }
+ }
+
+ // cleanup
+ err := unix.Close(w.kq)
+ if err != nil {
+ // only way the previous loop breaks is if w.done was closed so we need to async send to w.Errors.
+ select {
+ case w.Errors <- err:
+ default:
+ }
+ }
+ close(w.Events)
+ close(w.Errors)
+}
+
+// newEvent returns an platform-independent Event based on kqueue Fflags.
+func newEvent(name string, mask uint32) Event {
+ e := Event{Name: name}
+ if mask&unix.NOTE_DELETE == unix.NOTE_DELETE {
+ e.Op |= Remove
+ }
+ if mask&unix.NOTE_WRITE == unix.NOTE_WRITE {
+ e.Op |= Write
+ }
+ if mask&unix.NOTE_RENAME == unix.NOTE_RENAME {
+ e.Op |= Rename
+ }
+ if mask&unix.NOTE_ATTRIB == unix.NOTE_ATTRIB {
+ e.Op |= Chmod
+ }
+ return e
+}
+
+func newCreateEvent(name string) Event {
+ return Event{Name: name, Op: Create}
+}
+
+// watchDirectoryFiles to mimic inotify when adding a watch on a directory
+func (w *Watcher) watchDirectoryFiles(dirPath string) error {
+ // Get all files
+ files, err := ioutil.ReadDir(dirPath)
+ if err != nil {
+ return err
+ }
+
+ for _, fileInfo := range files {
+ filePath := filepath.Join(dirPath, fileInfo.Name())
+ filePath, err = w.internalWatch(filePath, fileInfo)
+ if err != nil {
+ return err
+ }
+
+ w.mu.Lock()
+ w.fileExists[filePath] = true
+ w.mu.Unlock()
+ }
+
+ return nil
+}
+
+// sendDirectoryEvents searches the directory for newly created files
+// and sends them over the event channel. This functionality is to have
+// the BSD version of fsnotify match Linux inotify which provides a
+// create event for files created in a watched directory.
+func (w *Watcher) sendDirectoryChangeEvents(dirPath string) {
+ // Get all files
+ files, err := ioutil.ReadDir(dirPath)
+ if err != nil {
+ select {
+ case w.Errors <- err:
+ case <-w.done:
+ return
+ }
+ }
+
+ // Search for new files
+ for _, fileInfo := range files {
+ filePath := filepath.Join(dirPath, fileInfo.Name())
+ err := w.sendFileCreatedEventIfNew(filePath, fileInfo)
+
+ if err != nil {
+ return
+ }
+ }
+}
+
+// sendFileCreatedEvent sends a create event if the file isn't already being tracked.
+func (w *Watcher) sendFileCreatedEventIfNew(filePath string, fileInfo os.FileInfo) (err error) {
+ w.mu.Lock()
+ _, doesExist := w.fileExists[filePath]
+ w.mu.Unlock()
+ if !doesExist {
+ // Send create event
+ select {
+ case w.Events <- newCreateEvent(filePath):
+ case <-w.done:
+ return
+ }
+ }
+
+ // like watchDirectoryFiles (but without doing another ReadDir)
+ filePath, err = w.internalWatch(filePath, fileInfo)
+ if err != nil {
+ return err
+ }
+
+ w.mu.Lock()
+ w.fileExists[filePath] = true
+ w.mu.Unlock()
+
+ return nil
+}
+
+func (w *Watcher) internalWatch(name string, fileInfo os.FileInfo) (string, error) {
+ if fileInfo.IsDir() {
+ // mimic Linux providing delete events for subdirectories
+ // but preserve the flags used if currently watching subdirectory
+ w.mu.Lock()
+ flags := w.dirFlags[name]
+ w.mu.Unlock()
+
+ flags |= unix.NOTE_DELETE | unix.NOTE_RENAME
+ return w.addWatch(name, flags)
+ }
+
+ // watch file to mimic Linux inotify
+ return w.addWatch(name, noteAllEvents)
+}
+
+// kqueue creates a new kernel event queue and returns a descriptor.
+func kqueue() (kq int, err error) {
+ kq, err = unix.Kqueue()
+ if kq == -1 {
+ return kq, err
+ }
+ return kq, nil
+}
+
+// register events with the queue
+func register(kq int, fds []int, flags int, fflags uint32) error {
+ changes := make([]unix.Kevent_t, len(fds))
+
+ for i, fd := range fds {
+ // SetKevent converts int to the platform-specific types:
+ unix.SetKevent(&changes[i], fd, unix.EVFILT_VNODE, flags)
+ changes[i].Fflags = fflags
+ }
+
+ // register the events
+ success, err := unix.Kevent(kq, changes, nil, nil)
+ if success == -1 {
+ return err
+ }
+ return nil
+}
+
+// read retrieves pending events, or waits until an event occurs.
+// A timeout of nil blocks indefinitely, while 0 polls the queue.
+func read(kq int, events []unix.Kevent_t, timeout *unix.Timespec) ([]unix.Kevent_t, error) {
+ n, err := unix.Kevent(kq, nil, events, timeout)
+ if err != nil {
+ return nil, err
+ }
+ return events[0:n], nil
+}
+
+// durationToTimespec prepares a timeout value
+func durationToTimespec(d time.Duration) unix.Timespec {
+ return unix.NsecToTimespec(d.Nanoseconds())
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/fsnotify/fsnotify/open_mode_bsd.go b/go/ql/test/experimental/CWE-321/vendor/github.com/fsnotify/fsnotify/open_mode_bsd.go
new file mode 100644
index 000000000000..36cc3845b6e7
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/fsnotify/fsnotify/open_mode_bsd.go
@@ -0,0 +1,12 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build freebsd || openbsd || netbsd || dragonfly
+// +build freebsd openbsd netbsd dragonfly
+
+package fsnotify
+
+import "golang.org/x/sys/unix"
+
+const openMode = unix.O_NONBLOCK | unix.O_RDONLY | unix.O_CLOEXEC
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/fsnotify/fsnotify/open_mode_darwin.go b/go/ql/test/experimental/CWE-321/vendor/github.com/fsnotify/fsnotify/open_mode_darwin.go
new file mode 100644
index 000000000000..98cd8476ffb8
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/fsnotify/fsnotify/open_mode_darwin.go
@@ -0,0 +1,13 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build darwin
+// +build darwin
+
+package fsnotify
+
+import "golang.org/x/sys/unix"
+
+// note: this constant is not defined on BSD
+const openMode = unix.O_EVTONLY | unix.O_CLOEXEC
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/fsnotify/fsnotify/windows.go b/go/ql/test/experimental/CWE-321/vendor/github.com/fsnotify/fsnotify/windows.go
new file mode 100644
index 000000000000..02ce7deb0bbf
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/fsnotify/fsnotify/windows.go
@@ -0,0 +1,586 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build windows
+// +build windows
+
+package fsnotify
+
+import (
+ "errors"
+ "fmt"
+ "os"
+ "path/filepath"
+ "reflect"
+ "runtime"
+ "sync"
+ "syscall"
+ "unsafe"
+)
+
+// Watcher watches a set of files, delivering events to a channel.
+type Watcher struct {
+ Events chan Event
+ Errors chan error
+ isClosed bool // Set to true when Close() is first called
+ mu sync.Mutex // Map access
+ port syscall.Handle // Handle to completion port
+ watches watchMap // Map of watches (key: i-number)
+ input chan *input // Inputs to the reader are sent on this channel
+ quit chan chan<- error
+}
+
+// NewWatcher establishes a new watcher with the underlying OS and begins waiting for events.
+func NewWatcher() (*Watcher, error) {
+ port, e := syscall.CreateIoCompletionPort(syscall.InvalidHandle, 0, 0, 0)
+ if e != nil {
+ return nil, os.NewSyscallError("CreateIoCompletionPort", e)
+ }
+ w := &Watcher{
+ port: port,
+ watches: make(watchMap),
+ input: make(chan *input, 1),
+ Events: make(chan Event, 50),
+ Errors: make(chan error),
+ quit: make(chan chan<- error, 1),
+ }
+ go w.readEvents()
+ return w, nil
+}
+
+// Close removes all watches and closes the events channel.
+func (w *Watcher) Close() error {
+ if w.isClosed {
+ return nil
+ }
+ w.isClosed = true
+
+ // Send "quit" message to the reader goroutine
+ ch := make(chan error)
+ w.quit <- ch
+ if err := w.wakeupReader(); err != nil {
+ return err
+ }
+ return <-ch
+}
+
+// Add starts watching the named file or directory (non-recursively).
+func (w *Watcher) Add(name string) error {
+ if w.isClosed {
+ return errors.New("watcher already closed")
+ }
+ in := &input{
+ op: opAddWatch,
+ path: filepath.Clean(name),
+ flags: sysFSALLEVENTS,
+ reply: make(chan error),
+ }
+ w.input <- in
+ if err := w.wakeupReader(); err != nil {
+ return err
+ }
+ return <-in.reply
+}
+
+// Remove stops watching the the named file or directory (non-recursively).
+func (w *Watcher) Remove(name string) error {
+ in := &input{
+ op: opRemoveWatch,
+ path: filepath.Clean(name),
+ reply: make(chan error),
+ }
+ w.input <- in
+ if err := w.wakeupReader(); err != nil {
+ return err
+ }
+ return <-in.reply
+}
+
+// WatchList returns the directories and files that are being monitered.
+func (w *Watcher) WatchList() []string {
+ w.mu.Lock()
+ defer w.mu.Unlock()
+
+ entries := make([]string, 0, len(w.watches))
+ for _, entry := range w.watches {
+ for _, watchEntry := range entry {
+ entries = append(entries, watchEntry.path)
+ }
+ }
+
+ return entries
+}
+
+const (
+ // Options for AddWatch
+ sysFSONESHOT = 0x80000000
+ sysFSONLYDIR = 0x1000000
+
+ // Events
+ sysFSACCESS = 0x1
+ sysFSALLEVENTS = 0xfff
+ sysFSATTRIB = 0x4
+ sysFSCLOSE = 0x18
+ sysFSCREATE = 0x100
+ sysFSDELETE = 0x200
+ sysFSDELETESELF = 0x400
+ sysFSMODIFY = 0x2
+ sysFSMOVE = 0xc0
+ sysFSMOVEDFROM = 0x40
+ sysFSMOVEDTO = 0x80
+ sysFSMOVESELF = 0x800
+
+ // Special events
+ sysFSIGNORED = 0x8000
+ sysFSQOVERFLOW = 0x4000
+)
+
+func newEvent(name string, mask uint32) Event {
+ e := Event{Name: name}
+ if mask&sysFSCREATE == sysFSCREATE || mask&sysFSMOVEDTO == sysFSMOVEDTO {
+ e.Op |= Create
+ }
+ if mask&sysFSDELETE == sysFSDELETE || mask&sysFSDELETESELF == sysFSDELETESELF {
+ e.Op |= Remove
+ }
+ if mask&sysFSMODIFY == sysFSMODIFY {
+ e.Op |= Write
+ }
+ if mask&sysFSMOVE == sysFSMOVE || mask&sysFSMOVESELF == sysFSMOVESELF || mask&sysFSMOVEDFROM == sysFSMOVEDFROM {
+ e.Op |= Rename
+ }
+ if mask&sysFSATTRIB == sysFSATTRIB {
+ e.Op |= Chmod
+ }
+ return e
+}
+
+const (
+ opAddWatch = iota
+ opRemoveWatch
+)
+
+const (
+ provisional uint64 = 1 << (32 + iota)
+)
+
+type input struct {
+ op int
+ path string
+ flags uint32
+ reply chan error
+}
+
+type inode struct {
+ handle syscall.Handle
+ volume uint32
+ index uint64
+}
+
+type watch struct {
+ ov syscall.Overlapped
+ ino *inode // i-number
+ path string // Directory path
+ mask uint64 // Directory itself is being watched with these notify flags
+ names map[string]uint64 // Map of names being watched and their notify flags
+ rename string // Remembers the old name while renaming a file
+ buf [4096]byte
+}
+
+type indexMap map[uint64]*watch
+type watchMap map[uint32]indexMap
+
+func (w *Watcher) wakeupReader() error {
+ e := syscall.PostQueuedCompletionStatus(w.port, 0, 0, nil)
+ if e != nil {
+ return os.NewSyscallError("PostQueuedCompletionStatus", e)
+ }
+ return nil
+}
+
+func getDir(pathname string) (dir string, err error) {
+ attr, e := syscall.GetFileAttributes(syscall.StringToUTF16Ptr(pathname))
+ if e != nil {
+ return "", os.NewSyscallError("GetFileAttributes", e)
+ }
+ if attr&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 {
+ dir = pathname
+ } else {
+ dir, _ = filepath.Split(pathname)
+ dir = filepath.Clean(dir)
+ }
+ return
+}
+
+func getIno(path string) (ino *inode, err error) {
+ h, e := syscall.CreateFile(syscall.StringToUTF16Ptr(path),
+ syscall.FILE_LIST_DIRECTORY,
+ syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE,
+ nil, syscall.OPEN_EXISTING,
+ syscall.FILE_FLAG_BACKUP_SEMANTICS|syscall.FILE_FLAG_OVERLAPPED, 0)
+ if e != nil {
+ return nil, os.NewSyscallError("CreateFile", e)
+ }
+ var fi syscall.ByHandleFileInformation
+ if e = syscall.GetFileInformationByHandle(h, &fi); e != nil {
+ syscall.CloseHandle(h)
+ return nil, os.NewSyscallError("GetFileInformationByHandle", e)
+ }
+ ino = &inode{
+ handle: h,
+ volume: fi.VolumeSerialNumber,
+ index: uint64(fi.FileIndexHigh)<<32 | uint64(fi.FileIndexLow),
+ }
+ return ino, nil
+}
+
+// Must run within the I/O thread.
+func (m watchMap) get(ino *inode) *watch {
+ if i := m[ino.volume]; i != nil {
+ return i[ino.index]
+ }
+ return nil
+}
+
+// Must run within the I/O thread.
+func (m watchMap) set(ino *inode, watch *watch) {
+ i := m[ino.volume]
+ if i == nil {
+ i = make(indexMap)
+ m[ino.volume] = i
+ }
+ i[ino.index] = watch
+}
+
+// Must run within the I/O thread.
+func (w *Watcher) addWatch(pathname string, flags uint64) error {
+ dir, err := getDir(pathname)
+ if err != nil {
+ return err
+ }
+ if flags&sysFSONLYDIR != 0 && pathname != dir {
+ return nil
+ }
+ ino, err := getIno(dir)
+ if err != nil {
+ return err
+ }
+ w.mu.Lock()
+ watchEntry := w.watches.get(ino)
+ w.mu.Unlock()
+ if watchEntry == nil {
+ if _, e := syscall.CreateIoCompletionPort(ino.handle, w.port, 0, 0); e != nil {
+ syscall.CloseHandle(ino.handle)
+ return os.NewSyscallError("CreateIoCompletionPort", e)
+ }
+ watchEntry = &watch{
+ ino: ino,
+ path: dir,
+ names: make(map[string]uint64),
+ }
+ w.mu.Lock()
+ w.watches.set(ino, watchEntry)
+ w.mu.Unlock()
+ flags |= provisional
+ } else {
+ syscall.CloseHandle(ino.handle)
+ }
+ if pathname == dir {
+ watchEntry.mask |= flags
+ } else {
+ watchEntry.names[filepath.Base(pathname)] |= flags
+ }
+ if err = w.startRead(watchEntry); err != nil {
+ return err
+ }
+ if pathname == dir {
+ watchEntry.mask &= ^provisional
+ } else {
+ watchEntry.names[filepath.Base(pathname)] &= ^provisional
+ }
+ return nil
+}
+
+// Must run within the I/O thread.
+func (w *Watcher) remWatch(pathname string) error {
+ dir, err := getDir(pathname)
+ if err != nil {
+ return err
+ }
+ ino, err := getIno(dir)
+ if err != nil {
+ return err
+ }
+ w.mu.Lock()
+ watch := w.watches.get(ino)
+ w.mu.Unlock()
+ if watch == nil {
+ return fmt.Errorf("can't remove non-existent watch for: %s", pathname)
+ }
+ if pathname == dir {
+ w.sendEvent(watch.path, watch.mask&sysFSIGNORED)
+ watch.mask = 0
+ } else {
+ name := filepath.Base(pathname)
+ w.sendEvent(filepath.Join(watch.path, name), watch.names[name]&sysFSIGNORED)
+ delete(watch.names, name)
+ }
+ return w.startRead(watch)
+}
+
+// Must run within the I/O thread.
+func (w *Watcher) deleteWatch(watch *watch) {
+ for name, mask := range watch.names {
+ if mask&provisional == 0 {
+ w.sendEvent(filepath.Join(watch.path, name), mask&sysFSIGNORED)
+ }
+ delete(watch.names, name)
+ }
+ if watch.mask != 0 {
+ if watch.mask&provisional == 0 {
+ w.sendEvent(watch.path, watch.mask&sysFSIGNORED)
+ }
+ watch.mask = 0
+ }
+}
+
+// Must run within the I/O thread.
+func (w *Watcher) startRead(watch *watch) error {
+ if e := syscall.CancelIo(watch.ino.handle); e != nil {
+ w.Errors <- os.NewSyscallError("CancelIo", e)
+ w.deleteWatch(watch)
+ }
+ mask := toWindowsFlags(watch.mask)
+ for _, m := range watch.names {
+ mask |= toWindowsFlags(m)
+ }
+ if mask == 0 {
+ if e := syscall.CloseHandle(watch.ino.handle); e != nil {
+ w.Errors <- os.NewSyscallError("CloseHandle", e)
+ }
+ w.mu.Lock()
+ delete(w.watches[watch.ino.volume], watch.ino.index)
+ w.mu.Unlock()
+ return nil
+ }
+ e := syscall.ReadDirectoryChanges(watch.ino.handle, &watch.buf[0],
+ uint32(unsafe.Sizeof(watch.buf)), false, mask, nil, &watch.ov, 0)
+ if e != nil {
+ err := os.NewSyscallError("ReadDirectoryChanges", e)
+ if e == syscall.ERROR_ACCESS_DENIED && watch.mask&provisional == 0 {
+ // Watched directory was probably removed
+ if w.sendEvent(watch.path, watch.mask&sysFSDELETESELF) {
+ if watch.mask&sysFSONESHOT != 0 {
+ watch.mask = 0
+ }
+ }
+ err = nil
+ }
+ w.deleteWatch(watch)
+ w.startRead(watch)
+ return err
+ }
+ return nil
+}
+
+// readEvents reads from the I/O completion port, converts the
+// received events into Event objects and sends them via the Events channel.
+// Entry point to the I/O thread.
+func (w *Watcher) readEvents() {
+ var (
+ n, key uint32
+ ov *syscall.Overlapped
+ )
+ runtime.LockOSThread()
+
+ for {
+ e := syscall.GetQueuedCompletionStatus(w.port, &n, &key, &ov, syscall.INFINITE)
+ watch := (*watch)(unsafe.Pointer(ov))
+
+ if watch == nil {
+ select {
+ case ch := <-w.quit:
+ w.mu.Lock()
+ var indexes []indexMap
+ for _, index := range w.watches {
+ indexes = append(indexes, index)
+ }
+ w.mu.Unlock()
+ for _, index := range indexes {
+ for _, watch := range index {
+ w.deleteWatch(watch)
+ w.startRead(watch)
+ }
+ }
+ var err error
+ if e := syscall.CloseHandle(w.port); e != nil {
+ err = os.NewSyscallError("CloseHandle", e)
+ }
+ close(w.Events)
+ close(w.Errors)
+ ch <- err
+ return
+ case in := <-w.input:
+ switch in.op {
+ case opAddWatch:
+ in.reply <- w.addWatch(in.path, uint64(in.flags))
+ case opRemoveWatch:
+ in.reply <- w.remWatch(in.path)
+ }
+ default:
+ }
+ continue
+ }
+
+ switch e {
+ case syscall.ERROR_MORE_DATA:
+ if watch == nil {
+ w.Errors <- errors.New("ERROR_MORE_DATA has unexpectedly null lpOverlapped buffer")
+ } else {
+ // The i/o succeeded but the buffer is full.
+ // In theory we should be building up a full packet.
+ // In practice we can get away with just carrying on.
+ n = uint32(unsafe.Sizeof(watch.buf))
+ }
+ case syscall.ERROR_ACCESS_DENIED:
+ // Watched directory was probably removed
+ w.sendEvent(watch.path, watch.mask&sysFSDELETESELF)
+ w.deleteWatch(watch)
+ w.startRead(watch)
+ continue
+ case syscall.ERROR_OPERATION_ABORTED:
+ // CancelIo was called on this handle
+ continue
+ default:
+ w.Errors <- os.NewSyscallError("GetQueuedCompletionPort", e)
+ continue
+ case nil:
+ }
+
+ var offset uint32
+ for {
+ if n == 0 {
+ w.Events <- newEvent("", sysFSQOVERFLOW)
+ w.Errors <- errors.New("short read in readEvents()")
+ break
+ }
+
+ // Point "raw" to the event in the buffer
+ raw := (*syscall.FileNotifyInformation)(unsafe.Pointer(&watch.buf[offset]))
+ // TODO: Consider using unsafe.Slice that is available from go1.17
+ // https://stackoverflow.com/questions/51187973/how-to-create-an-array-or-a-slice-from-an-array-unsafe-pointer-in-golang
+ // instead of using a fixed syscall.MAX_PATH buf, we create a buf that is the size of the path name
+ size := int(raw.FileNameLength / 2)
+ var buf []uint16
+ sh := (*reflect.SliceHeader)(unsafe.Pointer(&buf))
+ sh.Data = uintptr(unsafe.Pointer(&raw.FileName))
+ sh.Len = size
+ sh.Cap = size
+ name := syscall.UTF16ToString(buf)
+ fullname := filepath.Join(watch.path, name)
+
+ var mask uint64
+ switch raw.Action {
+ case syscall.FILE_ACTION_REMOVED:
+ mask = sysFSDELETESELF
+ case syscall.FILE_ACTION_MODIFIED:
+ mask = sysFSMODIFY
+ case syscall.FILE_ACTION_RENAMED_OLD_NAME:
+ watch.rename = name
+ case syscall.FILE_ACTION_RENAMED_NEW_NAME:
+ if watch.names[watch.rename] != 0 {
+ watch.names[name] |= watch.names[watch.rename]
+ delete(watch.names, watch.rename)
+ mask = sysFSMOVESELF
+ }
+ }
+
+ sendNameEvent := func() {
+ if w.sendEvent(fullname, watch.names[name]&mask) {
+ if watch.names[name]&sysFSONESHOT != 0 {
+ delete(watch.names, name)
+ }
+ }
+ }
+ if raw.Action != syscall.FILE_ACTION_RENAMED_NEW_NAME {
+ sendNameEvent()
+ }
+ if raw.Action == syscall.FILE_ACTION_REMOVED {
+ w.sendEvent(fullname, watch.names[name]&sysFSIGNORED)
+ delete(watch.names, name)
+ }
+ if w.sendEvent(fullname, watch.mask&toFSnotifyFlags(raw.Action)) {
+ if watch.mask&sysFSONESHOT != 0 {
+ watch.mask = 0
+ }
+ }
+ if raw.Action == syscall.FILE_ACTION_RENAMED_NEW_NAME {
+ fullname = filepath.Join(watch.path, watch.rename)
+ sendNameEvent()
+ }
+
+ // Move to the next event in the buffer
+ if raw.NextEntryOffset == 0 {
+ break
+ }
+ offset += raw.NextEntryOffset
+
+ // Error!
+ if offset >= n {
+ w.Errors <- errors.New("Windows system assumed buffer larger than it is, events have likely been missed.")
+ break
+ }
+ }
+
+ if err := w.startRead(watch); err != nil {
+ w.Errors <- err
+ }
+ }
+}
+
+func (w *Watcher) sendEvent(name string, mask uint64) bool {
+ if mask == 0 {
+ return false
+ }
+ event := newEvent(name, uint32(mask))
+ select {
+ case ch := <-w.quit:
+ w.quit <- ch
+ case w.Events <- event:
+ }
+ return true
+}
+
+func toWindowsFlags(mask uint64) uint32 {
+ var m uint32
+ if mask&sysFSACCESS != 0 {
+ m |= syscall.FILE_NOTIFY_CHANGE_LAST_ACCESS
+ }
+ if mask&sysFSMODIFY != 0 {
+ m |= syscall.FILE_NOTIFY_CHANGE_LAST_WRITE
+ }
+ if mask&sysFSATTRIB != 0 {
+ m |= syscall.FILE_NOTIFY_CHANGE_ATTRIBUTES
+ }
+ if mask&(sysFSMOVE|sysFSCREATE|sysFSDELETE) != 0 {
+ m |= syscall.FILE_NOTIFY_CHANGE_FILE_NAME | syscall.FILE_NOTIFY_CHANGE_DIR_NAME
+ }
+ return m
+}
+
+func toFSnotifyFlags(action uint32) uint64 {
+ switch action {
+ case syscall.FILE_ACTION_ADDED:
+ return sysFSCREATE
+ case syscall.FILE_ACTION_REMOVED:
+ return sysFSDELETE
+ case syscall.FILE_ACTION_MODIFIED:
+ return sysFSMODIFY
+ case syscall.FILE_ACTION_RENAMED_OLD_NAME:
+ return sysFSMOVEDFROM
+ case syscall.FILE_ACTION_RENAMED_NEW_NAME:
+ return sysFSMOVEDTO
+ }
+ return 0
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/gin-contrib/sse/.travis.yml b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-contrib/sse/.travis.yml
new file mode 100644
index 000000000000..d0e8fcf99437
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-contrib/sse/.travis.yml
@@ -0,0 +1,26 @@
+language: go
+sudo: false
+go:
+ - 1.8.x
+ - 1.9.x
+ - 1.10.x
+ - 1.11.x
+ - 1.12.x
+ - master
+
+git:
+ depth: 10
+
+matrix:
+ fast_finish: true
+ include:
+ - go: 1.11.x
+ env: GO111MODULE=on
+ - go: 1.12.x
+ env: GO111MODULE=on
+
+script:
+ - go test -v -covermode=count -coverprofile=coverage.out
+
+after_success:
+ - bash <(curl -s https://codecov.io/bash)
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/gin-contrib/sse/LICENSE b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-contrib/sse/LICENSE
new file mode 100644
index 000000000000..1ff7f3706055
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-contrib/sse/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2014 Manuel MartÃnez-Almeida
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/gin-contrib/sse/README.md b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-contrib/sse/README.md
new file mode 100644
index 000000000000..c9c49cf94959
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-contrib/sse/README.md
@@ -0,0 +1,58 @@
+# Server-Sent Events
+
+[](https://godoc.org/github.com/gin-contrib/sse)
+[](https://travis-ci.org/gin-contrib/sse)
+[](https://codecov.io/gh/gin-contrib/sse)
+[](https://goreportcard.com/report/github.com/gin-contrib/sse)
+
+Server-sent events (SSE) is a technology where a browser receives automatic updates from a server via HTTP connection. The Server-Sent Events EventSource API is [standardized as part of HTML5[1] by the W3C](http://www.w3.org/TR/2009/WD-eventsource-20091029/).
+
+- [Read this great SSE introduction by the HTML5Rocks guys](http://www.html5rocks.com/en/tutorials/eventsource/basics/)
+- [Browser support](http://caniuse.com/#feat=eventsource)
+
+## Sample code
+
+```go
+import "github.com/gin-contrib/sse"
+
+func httpHandler(w http.ResponseWriter, req *http.Request) {
+ // data can be a primitive like a string, an integer or a float
+ sse.Encode(w, sse.Event{
+ Event: "message",
+ Data: "some data\nmore data",
+ })
+
+ // also a complex type, like a map, a struct or a slice
+ sse.Encode(w, sse.Event{
+ Id: "124",
+ Event: "message",
+ Data: map[string]interface{}{
+ "user": "manu",
+ "date": time.Now().Unix(),
+ "content": "hi!",
+ },
+ })
+}
+```
+```
+event: message
+data: some data\\nmore data
+
+id: 124
+event: message
+data: {"content":"hi!","date":1431540810,"user":"manu"}
+
+```
+
+## Content-Type
+
+```go
+fmt.Println(sse.ContentType)
+```
+```
+text/event-stream
+```
+
+## Decoding support
+
+There is a client-side implementation of SSE coming soon.
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/gin-contrib/sse/sse-decoder.go b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-contrib/sse/sse-decoder.go
new file mode 100644
index 000000000000..fd49b9c37a45
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-contrib/sse/sse-decoder.go
@@ -0,0 +1,116 @@
+// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
+// Use of this source code is governed by a MIT style
+// license that can be found in the LICENSE file.
+
+package sse
+
+import (
+ "bytes"
+ "io"
+ "io/ioutil"
+)
+
+type decoder struct {
+ events []Event
+}
+
+func Decode(r io.Reader) ([]Event, error) {
+ var dec decoder
+ return dec.decode(r)
+}
+
+func (d *decoder) dispatchEvent(event Event, data string) {
+ dataLength := len(data)
+ if dataLength > 0 {
+ //If the data buffer's last character is a U+000A LINE FEED (LF) character, then remove the last character from the data buffer.
+ data = data[:dataLength-1]
+ dataLength--
+ }
+ if dataLength == 0 && event.Event == "" {
+ return
+ }
+ if event.Event == "" {
+ event.Event = "message"
+ }
+ event.Data = data
+ d.events = append(d.events, event)
+}
+
+func (d *decoder) decode(r io.Reader) ([]Event, error) {
+ buf, err := ioutil.ReadAll(r)
+ if err != nil {
+ return nil, err
+ }
+
+ var currentEvent Event
+ var dataBuffer *bytes.Buffer = new(bytes.Buffer)
+ // TODO (and unit tests)
+ // Lines must be separated by either a U+000D CARRIAGE RETURN U+000A LINE FEED (CRLF) character pair,
+ // a single U+000A LINE FEED (LF) character,
+ // or a single U+000D CARRIAGE RETURN (CR) character.
+ lines := bytes.Split(buf, []byte{'\n'})
+ for _, line := range lines {
+ if len(line) == 0 {
+ // If the line is empty (a blank line). Dispatch the event.
+ d.dispatchEvent(currentEvent, dataBuffer.String())
+
+ // reset current event and data buffer
+ currentEvent = Event{}
+ dataBuffer.Reset()
+ continue
+ }
+ if line[0] == byte(':') {
+ // If the line starts with a U+003A COLON character (:), ignore the line.
+ continue
+ }
+
+ var field, value []byte
+ colonIndex := bytes.IndexRune(line, ':')
+ if colonIndex != -1 {
+ // If the line contains a U+003A COLON character character (:)
+ // Collect the characters on the line before the first U+003A COLON character (:),
+ // and let field be that string.
+ field = line[:colonIndex]
+ // Collect the characters on the line after the first U+003A COLON character (:),
+ // and let value be that string.
+ value = line[colonIndex+1:]
+ // If value starts with a single U+0020 SPACE character, remove it from value.
+ if len(value) > 0 && value[0] == ' ' {
+ value = value[1:]
+ }
+ } else {
+ // Otherwise, the string is not empty but does not contain a U+003A COLON character character (:)
+ // Use the whole line as the field name, and the empty string as the field value.
+ field = line
+ value = []byte{}
+ }
+ // The steps to process the field given a field name and a field value depend on the field name,
+ // as given in the following list. Field names must be compared literally,
+ // with no case folding performed.
+ switch string(field) {
+ case "event":
+ // Set the event name buffer to field value.
+ currentEvent.Event = string(value)
+ case "id":
+ // Set the event stream's last event ID to the field value.
+ currentEvent.Id = string(value)
+ case "retry":
+ // If the field value consists of only characters in the range U+0030 DIGIT ZERO (0) to U+0039 DIGIT NINE (9),
+ // then interpret the field value as an integer in base ten, and set the event stream's reconnection time to that integer.
+ // Otherwise, ignore the field.
+ currentEvent.Id = string(value)
+ case "data":
+ // Append the field value to the data buffer,
+ dataBuffer.Write(value)
+ // then append a single U+000A LINE FEED (LF) character to the data buffer.
+ dataBuffer.WriteString("\n")
+ default:
+ //Otherwise. The field is ignored.
+ continue
+ }
+ }
+ // Once the end of the file is reached, the user agent must dispatch the event one final time.
+ d.dispatchEvent(currentEvent, dataBuffer.String())
+
+ return d.events, nil
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/gin-contrib/sse/sse-encoder.go b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-contrib/sse/sse-encoder.go
new file mode 100644
index 000000000000..f9c8087504d4
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-contrib/sse/sse-encoder.go
@@ -0,0 +1,110 @@
+// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
+// Use of this source code is governed by a MIT style
+// license that can be found in the LICENSE file.
+
+package sse
+
+import (
+ "encoding/json"
+ "fmt"
+ "io"
+ "net/http"
+ "reflect"
+ "strconv"
+ "strings"
+)
+
+// Server-Sent Events
+// W3C Working Draft 29 October 2009
+// http://www.w3.org/TR/2009/WD-eventsource-20091029/
+
+const ContentType = "text/event-stream"
+
+var contentType = []string{ContentType}
+var noCache = []string{"no-cache"}
+
+var fieldReplacer = strings.NewReplacer(
+ "\n", "\\n",
+ "\r", "\\r")
+
+var dataReplacer = strings.NewReplacer(
+ "\n", "\ndata:",
+ "\r", "\\r")
+
+type Event struct {
+ Event string
+ Id string
+ Retry uint
+ Data interface{}
+}
+
+func Encode(writer io.Writer, event Event) error {
+ w := checkWriter(writer)
+ writeId(w, event.Id)
+ writeEvent(w, event.Event)
+ writeRetry(w, event.Retry)
+ return writeData(w, event.Data)
+}
+
+func writeId(w stringWriter, id string) {
+ if len(id) > 0 {
+ w.WriteString("id:")
+ fieldReplacer.WriteString(w, id)
+ w.WriteString("\n")
+ }
+}
+
+func writeEvent(w stringWriter, event string) {
+ if len(event) > 0 {
+ w.WriteString("event:")
+ fieldReplacer.WriteString(w, event)
+ w.WriteString("\n")
+ }
+}
+
+func writeRetry(w stringWriter, retry uint) {
+ if retry > 0 {
+ w.WriteString("retry:")
+ w.WriteString(strconv.FormatUint(uint64(retry), 10))
+ w.WriteString("\n")
+ }
+}
+
+func writeData(w stringWriter, data interface{}) error {
+ w.WriteString("data:")
+ switch kindOfData(data) {
+ case reflect.Struct, reflect.Slice, reflect.Map:
+ err := json.NewEncoder(w).Encode(data)
+ if err != nil {
+ return err
+ }
+ w.WriteString("\n")
+ default:
+ dataReplacer.WriteString(w, fmt.Sprint(data))
+ w.WriteString("\n\n")
+ }
+ return nil
+}
+
+func (r Event) Render(w http.ResponseWriter) error {
+ r.WriteContentType(w)
+ return Encode(w, r)
+}
+
+func (r Event) WriteContentType(w http.ResponseWriter) {
+ header := w.Header()
+ header["Content-Type"] = contentType
+
+ if _, exist := header["Cache-Control"]; !exist {
+ header["Cache-Control"] = noCache
+ }
+}
+
+func kindOfData(data interface{}) reflect.Kind {
+ value := reflect.ValueOf(data)
+ valueType := value.Kind()
+ if valueType == reflect.Ptr {
+ valueType = value.Elem().Kind()
+ }
+ return valueType
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/gin-contrib/sse/writer.go b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-contrib/sse/writer.go
new file mode 100644
index 000000000000..6f9806c55a3e
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-contrib/sse/writer.go
@@ -0,0 +1,24 @@
+package sse
+
+import "io"
+
+type stringWriter interface {
+ io.Writer
+ WriteString(string) (int, error)
+}
+
+type stringWrapper struct {
+ io.Writer
+}
+
+func (w stringWrapper) WriteString(str string) (int, error) {
+ return w.Writer.Write([]byte(str))
+}
+
+func checkWriter(writer io.Writer) stringWriter {
+ if w, ok := writer.(stringWriter); ok {
+ return w
+ } else {
+ return stringWrapper{writer}
+ }
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/.gitignore b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/.gitignore
new file mode 100644
index 000000000000..bdd50c95cf9d
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/.gitignore
@@ -0,0 +1,7 @@
+vendor/*
+!vendor/vendor.json
+coverage.out
+count.out
+test
+profile.out
+tmp.out
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/.travis.yml b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/.travis.yml
new file mode 100644
index 000000000000..bcc21414d1b8
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/.travis.yml
@@ -0,0 +1,48 @@
+language: go
+
+matrix:
+ fast_finish: true
+ include:
+ - go: 1.13.x
+ - go: 1.13.x
+ env:
+ - TESTTAGS=nomsgpack
+ - go: 1.14.x
+ - go: 1.14.x
+ env:
+ - TESTTAGS=nomsgpack
+ - go: 1.15.x
+ - go: 1.15.x
+ env:
+ - TESTTAGS=nomsgpack
+ - go: master
+
+git:
+ depth: 10
+
+before_install:
+ - if [[ "${GO111MODULE}" = "on" ]]; then mkdir "${HOME}/go"; export GOPATH="${HOME}/go"; fi
+
+install:
+ - if [[ "${GO111MODULE}" = "on" ]]; then go mod download; fi
+ - if [[ "${GO111MODULE}" = "on" ]]; then export PATH="${GOPATH}/bin:${GOROOT}/bin:${PATH}"; fi
+ - if [[ "${GO111MODULE}" = "on" ]]; then make tools; fi
+
+go_import_path: github.com/gin-gonic/gin
+
+script:
+ - make vet
+ - make fmt-check
+ - make misspell-check
+ - make test
+
+after_success:
+ - bash <(curl -s https://codecov.io/bash)
+
+notifications:
+ webhooks:
+ urls:
+ - https://webhooks.gitter.im/e/7f95bf605c4d356372f4
+ on_success: change # options: [always|never|change] default: always
+ on_failure: always # options: [always|never|change] default: always
+ on_start: false # default: false
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/AUTHORS.md b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/AUTHORS.md
new file mode 100644
index 000000000000..c634e6be05ae
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/AUTHORS.md
@@ -0,0 +1,233 @@
+List of all the awesome people working to make Gin the best Web Framework in Go.
+
+## gin 1.x series authors
+
+**Gin Core Team:** Bo-Yi Wu (@appleboy), 田欧 (@thinkerou), Javier Provecho (@javierprovecho)
+
+## gin 0.x series authors
+
+**Maintainers:** Manu Martinez-Almeida (@manucorporat), Javier Provecho (@javierprovecho)
+
+People and companies, who have contributed, in alphabetical order.
+
+**@858806258 (æ°å“¥)**
+- Fix typo in example
+
+
+**@achedeuzot (Klemen Sever)**
+- Fix newline debug printing
+
+
+**@adammck (Adam Mckaig)**
+- Add MIT license
+
+
+**@AlexanderChen1989 (Alexander)**
+- Typos in README
+
+
+**@alexanderdidenko (Aleksandr Didenko)**
+- Add support multipart/form-data
+
+
+**@alexandernyquist (Alexander Nyquist)**
+- Using template.Must to fix multiple return issue
+- ★ Added support for OPTIONS verb
+- ★ Setting response headers before calling WriteHeader
+- Improved documentation for model binding
+- ★ Added Content.Redirect()
+- ★ Added tons of Unit tests
+
+
+**@austinheap (Austin Heap)**
+- Added travis CI integration
+
+
+**@andredublin (Andre Dublin)**
+- Fix typo in comment
+
+
+**@bredov (Ludwig Valda Vasquez)**
+- Fix html templating in debug mode
+
+
+**@bluele (Jun Kimura)**
+- Fixes code examples in README
+
+
+**@chad-russell**
+- ★ Support for serializing gin.H into XML
+
+
+**@dickeyxxx (Jeff Dickey)**
+- Typos in README
+- Add example about serving static files
+
+
+**@donileo (Adonis)**
+- Add NoMethod handler
+
+
+**@dutchcoders (DutchCoders)**
+- ★ Fix security bug that allows client to spoof ip
+- Fix typo. r.HTMLTemplates -> SetHTMLTemplate
+
+
+**@el3ctro- (Joshua Loper)**
+- Fix typo in example
+
+
+**@ethankan (Ethan Kan)**
+- Unsigned integers in binding
+
+
+**(Evgeny Persienko)**
+- Validate sub structures
+
+
+**@frankbille (Frank Bille)**
+- Add support for HTTP Realm Auth
+
+
+**@fmd (Fareed Dudhia)**
+- Fix typo. SetHTTPTemplate -> SetHTMLTemplate
+
+
+**@ironiridis (Christopher Harrington)**
+- Remove old reference
+
+
+**@jammie-stackhouse (Jamie Stackhouse)**
+- Add more shortcuts for router methods
+
+
+**@jasonrhansen**
+- Fix spelling and grammar errors in documentation
+
+
+**@JasonSoft (Jason Lee)**
+- Fix typo in comment
+
+
+**@joiggama (Ignacio Galindo)**
+- Add utf-8 charset header on renders
+
+
+**@julienschmidt (Julien Schmidt)**
+- gofmt the code examples
+
+
+**@kelcecil (Kel Cecil)**
+- Fix readme typo
+
+
+**@kyledinh (Kyle Dinh)**
+- Adds RunTLS()
+
+
+**@LinusU (Linus Unnebäck)**
+- Small fixes in README
+
+
+**@loongmxbt (Saint Asky)**
+- Fix typo in example
+
+
+**@lucas-clemente (Lucas Clemente)**
+- ★ work around path.Join removing trailing slashes from routes
+
+
+**@mattn (Yasuhiro Matsumoto)**
+- Improve color logger
+
+
+**@mdigger (Dmitry Sedykh)**
+- Fixes Form binding when content-type is x-www-form-urlencoded
+- No repeat call c.Writer.Status() in gin.Logger
+- Fixes Content-Type for json render
+
+
+**@mirzac (Mirza Ceric)**
+- Fix debug printing
+
+
+**@mopemope (Yutaka Matsubara)**
+- ★ Adds Godep support (Dependencies Manager)
+- Fix variadic parameter in the flexible render API
+- Fix Corrupted plain render
+- Add Pluggable View Renderer Example
+
+
+**@msemenistyi (Mykyta Semenistyi)**
+- update Readme.md. Add code to String method
+
+
+**@msoedov (Sasha Myasoedov)**
+- ★ Adds tons of unit tests.
+
+
+**@ngerakines (Nick Gerakines)**
+- ★ Improves API, c.GET() doesn't panic
+- Adds MustGet() method
+
+
+**@r8k (Rajiv Kilaparti)**
+- Fix Port usage in README.
+
+
+**@rayrod2030 (Ray Rodriguez)**
+- Fix typo in example
+
+
+**@rns**
+- Fix typo in example
+
+
+**@RobAWilkinson (Robert Wilkinson)**
+- Add example of forms and params
+
+
+**@rogierlommers (Rogier Lommers)**
+- Add updated static serve example
+
+**@rw-access (Ross Wolf)**
+- Added support to mix exact and param routes
+
+**@se77en (Damon Zhao)**
+- Improve color logging
+
+
+**@silasb (Silas Baronda)**
+- Fixing quotes in README
+
+
+**@SkuliOskarsson (Skuli Oskarsson)**
+- Fixes some texts in README II
+
+
+**@slimmy (Jimmy Pettersson)**
+- Added messages for required bindings
+
+
+**@smira (Andrey Smirnov)**
+- Add support for ignored/unexported fields in binding
+
+
+**@superalsrk (SRK.Lyu)**
+- Update httprouter godeps
+
+
+**@tebeka (Miki Tebeka)**
+- Use net/http constants instead of numeric values
+
+
+**@techjanitor**
+- Update context.go reserved IPs
+
+
+**@yosssi (Keiji Yoshida)**
+- Fix link in README
+
+
+**@yuyabee**
+- Fixed README
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/BENCHMARKS.md b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/BENCHMARKS.md
new file mode 100644
index 000000000000..c11ee99ae7f4
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/BENCHMARKS.md
@@ -0,0 +1,666 @@
+
+# Benchmark System
+
+**VM HOST:** Travis
+**Machine:** Ubuntu 16.04.6 LTS x64
+**Date:** May 04th, 2020
+**Version:** Gin v1.6.3
+**Go Version:** 1.14.2 linux/amd64
+**Source:** [Go HTTP Router Benchmark](https://github.com/gin-gonic/go-http-routing-benchmark)
+**Result:** [See the gist](https://gist.github.com/appleboy/b5f2ecfaf50824ae9c64dcfb9165ae5e) or [Travis result](https://travis-ci.org/github/gin-gonic/go-http-routing-benchmark/jobs/682947061)
+
+## Static Routes: 157
+
+```sh
+Gin: 34936 Bytes
+
+HttpServeMux: 14512 Bytes
+Ace: 30680 Bytes
+Aero: 34536 Bytes
+Bear: 30456 Bytes
+Beego: 98456 Bytes
+Bone: 40224 Bytes
+Chi: 83608 Bytes
+Denco: 10216 Bytes
+Echo: 80328 Bytes
+GocraftWeb: 55288 Bytes
+Goji: 29744 Bytes
+Gojiv2: 105840 Bytes
+GoJsonRest: 137496 Bytes
+GoRestful: 816936 Bytes
+GorillaMux: 585632 Bytes
+GowwwRouter: 24968 Bytes
+HttpRouter: 21712 Bytes
+HttpTreeMux: 73448 Bytes
+Kocha: 115472 Bytes
+LARS: 30640 Bytes
+Macaron: 38592 Bytes
+Martini: 310864 Bytes
+Pat: 19696 Bytes
+Possum: 89920 Bytes
+R2router: 23712 Bytes
+Rivet: 24608 Bytes
+Tango: 28264 Bytes
+TigerTonic: 78768 Bytes
+Traffic: 538976 Bytes
+Vulcan: 369960 Bytes
+```
+
+## GithubAPI Routes: 203
+
+```sh
+Gin: 58512 Bytes
+
+Ace: 48688 Bytes
+Aero: 318568 Bytes
+Bear: 84248 Bytes
+Beego: 150936 Bytes
+Bone: 100976 Bytes
+Chi: 95112 Bytes
+Denco: 36736 Bytes
+Echo: 100296 Bytes
+GocraftWeb: 95432 Bytes
+Goji: 49680 Bytes
+Gojiv2: 104704 Bytes
+GoJsonRest: 141976 Bytes
+GoRestful: 1241656 Bytes
+GorillaMux: 1322784 Bytes
+GowwwRouter: 80008 Bytes
+HttpRouter: 37144 Bytes
+HttpTreeMux: 78800 Bytes
+Kocha: 785120 Bytes
+LARS: 48600 Bytes
+Macaron: 92784 Bytes
+Martini: 485264 Bytes
+Pat: 21200 Bytes
+Possum: 85312 Bytes
+R2router: 47104 Bytes
+Rivet: 42840 Bytes
+Tango: 54840 Bytes
+TigerTonic: 95264 Bytes
+Traffic: 921744 Bytes
+Vulcan: 425992 Bytes
+```
+
+## GPlusAPI Routes: 13
+
+```sh
+Gin: 4384 Bytes
+
+Ace: 3712 Bytes
+Aero: 26056 Bytes
+Bear: 7112 Bytes
+Beego: 10272 Bytes
+Bone: 6688 Bytes
+Chi: 8024 Bytes
+Denco: 3264 Bytes
+Echo: 9688 Bytes
+GocraftWeb: 7496 Bytes
+Goji: 3152 Bytes
+Gojiv2: 7376 Bytes
+GoJsonRest: 11400 Bytes
+GoRestful: 74328 Bytes
+GorillaMux: 66208 Bytes
+GowwwRouter: 5744 Bytes
+HttpRouter: 2808 Bytes
+HttpTreeMux: 7440 Bytes
+Kocha: 128880 Bytes
+LARS: 3656 Bytes
+Macaron: 8656 Bytes
+Martini: 23920 Bytes
+Pat: 1856 Bytes
+Possum: 7248 Bytes
+R2router: 3928 Bytes
+Rivet: 3064 Bytes
+Tango: 5168 Bytes
+TigerTonic: 9408 Bytes
+Traffic: 46400 Bytes
+Vulcan: 25544 Bytes
+```
+
+## ParseAPI Routes: 26
+
+```sh
+Gin: 7776 Bytes
+
+Ace: 6704 Bytes
+Aero: 28488 Bytes
+Bear: 12320 Bytes
+Beego: 19280 Bytes
+Bone: 11440 Bytes
+Chi: 9744 Bytes
+Denco: 4192 Bytes
+Echo: 11664 Bytes
+GocraftWeb: 12800 Bytes
+Goji: 5680 Bytes
+Gojiv2: 14464 Bytes
+GoJsonRest: 14072 Bytes
+GoRestful: 116264 Bytes
+GorillaMux: 105880 Bytes
+GowwwRouter: 9344 Bytes
+HttpRouter: 5072 Bytes
+HttpTreeMux: 7848 Bytes
+Kocha: 181712 Bytes
+LARS: 6632 Bytes
+Macaron: 13648 Bytes
+Martini: 45888 Bytes
+Pat: 2560 Bytes
+Possum: 9200 Bytes
+R2router: 7056 Bytes
+Rivet: 5680 Bytes
+Tango: 8920 Bytes
+TigerTonic: 9840 Bytes
+Traffic: 79096 Bytes
+Vulcan: 44504 Bytes
+```
+
+## Static Routes
+
+```sh
+BenchmarkGin_StaticAll 62169 19319 ns/op 0 B/op 0 allocs/op
+
+BenchmarkAce_StaticAll 65428 18313 ns/op 0 B/op 0 allocs/op
+BenchmarkAero_StaticAll 121132 9632 ns/op 0 B/op 0 allocs/op
+BenchmarkHttpServeMux_StaticAll 52626 22758 ns/op 0 B/op 0 allocs/op
+BenchmarkBeego_StaticAll 9962 179058 ns/op 55264 B/op 471 allocs/op
+BenchmarkBear_StaticAll 14894 80966 ns/op 20272 B/op 469 allocs/op
+BenchmarkBone_StaticAll 18718 64065 ns/op 0 B/op 0 allocs/op
+BenchmarkChi_StaticAll 10000 149827 ns/op 67824 B/op 471 allocs/op
+BenchmarkDenco_StaticAll 211393 5680 ns/op 0 B/op 0 allocs/op
+BenchmarkEcho_StaticAll 49341 24343 ns/op 0 B/op 0 allocs/op
+BenchmarkGocraftWeb_StaticAll 10000 126209 ns/op 46312 B/op 785 allocs/op
+BenchmarkGoji_StaticAll 27956 43174 ns/op 0 B/op 0 allocs/op
+BenchmarkGojiv2_StaticAll 3430 370718 ns/op 205984 B/op 1570 allocs/op
+BenchmarkGoJsonRest_StaticAll 9134 188888 ns/op 51653 B/op 1727 allocs/op
+BenchmarkGoRestful_StaticAll 706 1703330 ns/op 613280 B/op 2053 allocs/op
+BenchmarkGorillaMux_StaticAll 1268 924083 ns/op 153233 B/op 1413 allocs/op
+BenchmarkGowwwRouter_StaticAll 63374 18935 ns/op 0 B/op 0 allocs/op
+BenchmarkHttpRouter_StaticAll 109938 10902 ns/op 0 B/op 0 allocs/op
+BenchmarkHttpTreeMux_StaticAll 109166 10861 ns/op 0 B/op 0 allocs/op
+BenchmarkKocha_StaticAll 92258 12992 ns/op 0 B/op 0 allocs/op
+BenchmarkLARS_StaticAll 65200 18387 ns/op 0 B/op 0 allocs/op
+BenchmarkMacaron_StaticAll 5671 291501 ns/op 115553 B/op 1256 allocs/op
+BenchmarkMartini_StaticAll 807 1460498 ns/op 125444 B/op 1717 allocs/op
+BenchmarkPat_StaticAll 513 2342396 ns/op 602832 B/op 12559 allocs/op
+BenchmarkPossum_StaticAll 10000 128270 ns/op 65312 B/op 471 allocs/op
+BenchmarkR2router_StaticAll 16726 71760 ns/op 22608 B/op 628 allocs/op
+BenchmarkRivet_StaticAll 41722 28723 ns/op 0 B/op 0 allocs/op
+BenchmarkTango_StaticAll 7606 205082 ns/op 39209 B/op 1256 allocs/op
+BenchmarkTigerTonic_StaticAll 26247 45806 ns/op 7376 B/op 157 allocs/op
+BenchmarkTraffic_StaticAll 550 2284518 ns/op 754864 B/op 14601 allocs/op
+BenchmarkVulcan_StaticAll 10000 131343 ns/op 15386 B/op 471 allocs/op
+```
+
+## Micro Benchmarks
+
+```sh
+BenchmarkGin_Param 18785022 63.9 ns/op 0 B/op 0 allocs/op
+
+BenchmarkAce_Param 14689765 81.5 ns/op 0 B/op 0 allocs/op
+BenchmarkAero_Param 23094770 51.2 ns/op 0 B/op 0 allocs/op
+BenchmarkBear_Param 1417045 845 ns/op 456 B/op 5 allocs/op
+BenchmarkBeego_Param 1000000 1080 ns/op 352 B/op 3 allocs/op
+BenchmarkBone_Param 1000000 1463 ns/op 816 B/op 6 allocs/op
+BenchmarkChi_Param 1378756 885 ns/op 432 B/op 3 allocs/op
+BenchmarkDenco_Param 8557899 143 ns/op 32 B/op 1 allocs/op
+BenchmarkEcho_Param 16433347 75.5 ns/op 0 B/op 0 allocs/op
+BenchmarkGocraftWeb_Param 1000000 1218 ns/op 648 B/op 8 allocs/op
+BenchmarkGoji_Param 1921248 617 ns/op 336 B/op 2 allocs/op
+BenchmarkGojiv2_Param 561848 2156 ns/op 1328 B/op 11 allocs/op
+BenchmarkGoJsonRest_Param 1000000 1358 ns/op 649 B/op 13 allocs/op
+BenchmarkGoRestful_Param 224857 5307 ns/op 4192 B/op 14 allocs/op
+BenchmarkGorillaMux_Param 498313 2459 ns/op 1280 B/op 10 allocs/op
+BenchmarkGowwwRouter_Param 1864354 654 ns/op 432 B/op 3 allocs/op
+BenchmarkHttpRouter_Param 26269074 47.7 ns/op 0 B/op 0 allocs/op
+BenchmarkHttpTreeMux_Param 2109829 557 ns/op 352 B/op 3 allocs/op
+BenchmarkKocha_Param 5050216 243 ns/op 56 B/op 3 allocs/op
+BenchmarkLARS_Param 19811712 59.9 ns/op 0 B/op 0 allocs/op
+BenchmarkMacaron_Param 662746 2329 ns/op 1072 B/op 10 allocs/op
+BenchmarkMartini_Param 279902 4260 ns/op 1072 B/op 10 allocs/op
+BenchmarkPat_Param 1000000 1382 ns/op 536 B/op 11 allocs/op
+BenchmarkPossum_Param 1000000 1014 ns/op 496 B/op 5 allocs/op
+BenchmarkR2router_Param 1712559 707 ns/op 432 B/op 5 allocs/op
+BenchmarkRivet_Param 6648086 182 ns/op 48 B/op 1 allocs/op
+BenchmarkTango_Param 1221504 994 ns/op 248 B/op 8 allocs/op
+BenchmarkTigerTonic_Param 891661 2261 ns/op 776 B/op 16 allocs/op
+BenchmarkTraffic_Param 350059 3598 ns/op 1856 B/op 21 allocs/op
+BenchmarkVulcan_Param 2517823 472 ns/op 98 B/op 3 allocs/op
+BenchmarkAce_Param5 9214365 130 ns/op 0 B/op 0 allocs/op
+BenchmarkAero_Param5 15369013 77.9 ns/op 0 B/op 0 allocs/op
+BenchmarkBear_Param5 1000000 1113 ns/op 501 B/op 5 allocs/op
+BenchmarkBeego_Param5 1000000 1269 ns/op 352 B/op 3 allocs/op
+BenchmarkBone_Param5 986820 1873 ns/op 864 B/op 6 allocs/op
+BenchmarkChi_Param5 1000000 1156 ns/op 432 B/op 3 allocs/op
+BenchmarkDenco_Param5 3036331 400 ns/op 160 B/op 1 allocs/op
+BenchmarkEcho_Param5 6447133 186 ns/op 0 B/op 0 allocs/op
+BenchmarkGin_Param5 10786068 110 ns/op 0 B/op 0 allocs/op
+BenchmarkGocraftWeb_Param5 844820 1944 ns/op 920 B/op 11 allocs/op
+BenchmarkGoji_Param5 1474965 827 ns/op 336 B/op 2 allocs/op
+BenchmarkGojiv2_Param5 442820 2516 ns/op 1392 B/op 11 allocs/op
+BenchmarkGoJsonRest_Param5 507555 2711 ns/op 1097 B/op 16 allocs/op
+BenchmarkGoRestful_Param5 216481 6093 ns/op 4288 B/op 14 allocs/op
+BenchmarkGorillaMux_Param5 314402 3628 ns/op 1344 B/op 10 allocs/op
+BenchmarkGowwwRouter_Param5 1624660 733 ns/op 432 B/op 3 allocs/op
+BenchmarkHttpRouter_Param5 13167324 92.0 ns/op 0 B/op 0 allocs/op
+BenchmarkHttpTreeMux_Param5 1000000 1295 ns/op 576 B/op 6 allocs/op
+BenchmarkKocha_Param5 1000000 1138 ns/op 440 B/op 10 allocs/op
+BenchmarkLARS_Param5 11580613 105 ns/op 0 B/op 0 allocs/op
+BenchmarkMacaron_Param5 473596 2755 ns/op 1072 B/op 10 allocs/op
+BenchmarkMartini_Param5 230756 5111 ns/op 1232 B/op 11 allocs/op
+BenchmarkPat_Param5 469190 3370 ns/op 888 B/op 29 allocs/op
+BenchmarkPossum_Param5 1000000 1002 ns/op 496 B/op 5 allocs/op
+BenchmarkR2router_Param5 1422129 844 ns/op 432 B/op 5 allocs/op
+BenchmarkRivet_Param5 2263789 539 ns/op 240 B/op 1 allocs/op
+BenchmarkTango_Param5 1000000 1256 ns/op 360 B/op 8 allocs/op
+BenchmarkTigerTonic_Param5 175500 7492 ns/op 2279 B/op 39 allocs/op
+BenchmarkTraffic_Param5 233631 5816 ns/op 2208 B/op 27 allocs/op
+BenchmarkVulcan_Param5 1923416 629 ns/op 98 B/op 3 allocs/op
+BenchmarkAce_Param20 4321266 281 ns/op 0 B/op 0 allocs/op
+BenchmarkAero_Param20 31501641 35.2 ns/op 0 B/op 0 allocs/op
+BenchmarkBear_Param20 335204 3489 ns/op 1665 B/op 5 allocs/op
+BenchmarkBeego_Param20 503674 2860 ns/op 352 B/op 3 allocs/op
+BenchmarkBone_Param20 298922 4741 ns/op 2031 B/op 6 allocs/op
+BenchmarkChi_Param20 878181 1957 ns/op 432 B/op 3 allocs/op
+BenchmarkDenco_Param20 1000000 1360 ns/op 640 B/op 1 allocs/op
+BenchmarkEcho_Param20 2104946 580 ns/op 0 B/op 0 allocs/op
+BenchmarkGin_Param20 4167204 290 ns/op 0 B/op 0 allocs/op
+BenchmarkGocraftWeb_Param20 173064 7514 ns/op 3796 B/op 15 allocs/op
+BenchmarkGoji_Param20 458778 2651 ns/op 1247 B/op 2 allocs/op
+BenchmarkGojiv2_Param20 364862 3178 ns/op 1632 B/op 11 allocs/op
+BenchmarkGoJsonRest_Param20 125514 9760 ns/op 4485 B/op 20 allocs/op
+BenchmarkGoRestful_Param20 101217 11964 ns/op 6715 B/op 18 allocs/op
+BenchmarkGorillaMux_Param20 147654 8132 ns/op 3452 B/op 12 allocs/op
+BenchmarkGowwwRouter_Param20 1000000 1225 ns/op 432 B/op 3 allocs/op
+BenchmarkHttpRouter_Param20 4920895 247 ns/op 0 B/op 0 allocs/op
+BenchmarkHttpTreeMux_Param20 173202 6605 ns/op 3196 B/op 10 allocs/op
+BenchmarkKocha_Param20 345988 3620 ns/op 1808 B/op 27 allocs/op
+BenchmarkLARS_Param20 4592326 262 ns/op 0 B/op 0 allocs/op
+BenchmarkMacaron_Param20 166492 7286 ns/op 2924 B/op 12 allocs/op
+BenchmarkMartini_Param20 122162 10653 ns/op 3595 B/op 13 allocs/op
+BenchmarkPat_Param20 78630 15239 ns/op 4424 B/op 93 allocs/op
+BenchmarkPossum_Param20 1000000 1008 ns/op 496 B/op 5 allocs/op
+BenchmarkR2router_Param20 294981 4587 ns/op 2284 B/op 7 allocs/op
+BenchmarkRivet_Param20 691798 2090 ns/op 1024 B/op 1 allocs/op
+BenchmarkTango_Param20 842440 2505 ns/op 856 B/op 8 allocs/op
+BenchmarkTigerTonic_Param20 38614 31509 ns/op 9870 B/op 119 allocs/op
+BenchmarkTraffic_Param20 57633 21107 ns/op 7853 B/op 47 allocs/op
+BenchmarkVulcan_Param20 1000000 1178 ns/op 98 B/op 3 allocs/op
+BenchmarkAce_ParamWrite 7330743 180 ns/op 8 B/op 1 allocs/op
+BenchmarkAero_ParamWrite 13833598 86.7 ns/op 0 B/op 0 allocs/op
+BenchmarkBear_ParamWrite 1363321 867 ns/op 456 B/op 5 allocs/op
+BenchmarkBeego_ParamWrite 1000000 1104 ns/op 360 B/op 4 allocs/op
+BenchmarkBone_ParamWrite 1000000 1475 ns/op 816 B/op 6 allocs/op
+BenchmarkChi_ParamWrite 1320590 892 ns/op 432 B/op 3 allocs/op
+BenchmarkDenco_ParamWrite 7093605 172 ns/op 32 B/op 1 allocs/op
+BenchmarkEcho_ParamWrite 8434424 161 ns/op 8 B/op 1 allocs/op
+BenchmarkGin_ParamWrite 10377034 118 ns/op 0 B/op 0 allocs/op
+BenchmarkGocraftWeb_ParamWrite 1000000 1266 ns/op 656 B/op 9 allocs/op
+BenchmarkGoji_ParamWrite 1874168 654 ns/op 336 B/op 2 allocs/op
+BenchmarkGojiv2_ParamWrite 459032 2352 ns/op 1360 B/op 13 allocs/op
+BenchmarkGoJsonRest_ParamWrite 499434 2145 ns/op 1128 B/op 18 allocs/op
+BenchmarkGoRestful_ParamWrite 241087 5470 ns/op 4200 B/op 15 allocs/op
+BenchmarkGorillaMux_ParamWrite 425686 2522 ns/op 1280 B/op 10 allocs/op
+BenchmarkGowwwRouter_ParamWrite 922172 1778 ns/op 976 B/op 8 allocs/op
+BenchmarkHttpRouter_ParamWrite 15392049 77.7 ns/op 0 B/op 0 allocs/op
+BenchmarkHttpTreeMux_ParamWrite 1973385 597 ns/op 352 B/op 3 allocs/op
+BenchmarkKocha_ParamWrite 4262500 281 ns/op 56 B/op 3 allocs/op
+BenchmarkLARS_ParamWrite 10764410 113 ns/op 0 B/op 0 allocs/op
+BenchmarkMacaron_ParamWrite 486769 2726 ns/op 1176 B/op 14 allocs/op
+BenchmarkMartini_ParamWrite 264804 4842 ns/op 1176 B/op 14 allocs/op
+BenchmarkPat_ParamWrite 735116 2047 ns/op 960 B/op 15 allocs/op
+BenchmarkPossum_ParamWrite 1000000 1004 ns/op 496 B/op 5 allocs/op
+BenchmarkR2router_ParamWrite 1592136 768 ns/op 432 B/op 5 allocs/op
+BenchmarkRivet_ParamWrite 3582051 339 ns/op 112 B/op 2 allocs/op
+BenchmarkTango_ParamWrite 2237337 534 ns/op 136 B/op 4 allocs/op
+BenchmarkTigerTonic_ParamWrite 439608 3136 ns/op 1216 B/op 21 allocs/op
+BenchmarkTraffic_ParamWrite 306979 4328 ns/op 2280 B/op 25 allocs/op
+BenchmarkVulcan_ParamWrite 2529973 472 ns/op 98 B/op 3 allocs/op
+```
+
+## GitHub
+
+```sh
+BenchmarkGin_GithubStatic 15629472 76.7 ns/op 0 B/op 0 allocs/op
+
+BenchmarkAce_GithubStatic 15542612 75.9 ns/op 0 B/op 0 allocs/op
+BenchmarkAero_GithubStatic 24777151 48.5 ns/op 0 B/op 0 allocs/op
+BenchmarkBear_GithubStatic 2788894 435 ns/op 120 B/op 3 allocs/op
+BenchmarkBeego_GithubStatic 1000000 1064 ns/op 352 B/op 3 allocs/op
+BenchmarkBone_GithubStatic 93507 12838 ns/op 2880 B/op 60 allocs/op
+BenchmarkChi_GithubStatic 1387743 860 ns/op 432 B/op 3 allocs/op
+BenchmarkDenco_GithubStatic 39384996 30.4 ns/op 0 B/op 0 allocs/op
+BenchmarkEcho_GithubStatic 12076382 99.1 ns/op 0 B/op 0 allocs/op
+BenchmarkGocraftWeb_GithubStatic 1596495 756 ns/op 296 B/op 5 allocs/op
+BenchmarkGoji_GithubStatic 6364876 189 ns/op 0 B/op 0 allocs/op
+BenchmarkGojiv2_GithubStatic 550202 2098 ns/op 1312 B/op 10 allocs/op
+BenchmarkGoRestful_GithubStatic 102183 12552 ns/op 4256 B/op 13 allocs/op
+BenchmarkGoJsonRest_GithubStatic 1000000 1029 ns/op 329 B/op 11 allocs/op
+BenchmarkGorillaMux_GithubStatic 255552 5190 ns/op 976 B/op 9 allocs/op
+BenchmarkGowwwRouter_GithubStatic 15531916 77.1 ns/op 0 B/op 0 allocs/op
+BenchmarkHttpRouter_GithubStatic 27920724 43.1 ns/op 0 B/op 0 allocs/op
+BenchmarkHttpTreeMux_GithubStatic 21448953 55.8 ns/op 0 B/op 0 allocs/op
+BenchmarkKocha_GithubStatic 21405310 56.0 ns/op 0 B/op 0 allocs/op
+BenchmarkLARS_GithubStatic 13625156 89.0 ns/op 0 B/op 0 allocs/op
+BenchmarkMacaron_GithubStatic 1000000 1747 ns/op 736 B/op 8 allocs/op
+BenchmarkMartini_GithubStatic 187186 7326 ns/op 768 B/op 9 allocs/op
+BenchmarkPat_GithubStatic 109143 11563 ns/op 3648 B/op 76 allocs/op
+BenchmarkPossum_GithubStatic 1575898 770 ns/op 416 B/op 3 allocs/op
+BenchmarkR2router_GithubStatic 3046231 404 ns/op 144 B/op 4 allocs/op
+BenchmarkRivet_GithubStatic 11484826 105 ns/op 0 B/op 0 allocs/op
+BenchmarkTango_GithubStatic 1000000 1153 ns/op 248 B/op 8 allocs/op
+BenchmarkTigerTonic_GithubStatic 4929780 249 ns/op 48 B/op 1 allocs/op
+BenchmarkTraffic_GithubStatic 106351 11819 ns/op 4664 B/op 90 allocs/op
+BenchmarkVulcan_GithubStatic 1613271 722 ns/op 98 B/op 3 allocs/op
+BenchmarkAce_GithubParam 8386032 143 ns/op 0 B/op 0 allocs/op
+BenchmarkAero_GithubParam 11816200 102 ns/op 0 B/op 0 allocs/op
+BenchmarkBear_GithubParam 1000000 1012 ns/op 496 B/op 5 allocs/op
+BenchmarkBeego_GithubParam 1000000 1157 ns/op 352 B/op 3 allocs/op
+BenchmarkBone_GithubParam 184653 6912 ns/op 1888 B/op 19 allocs/op
+BenchmarkChi_GithubParam 1000000 1102 ns/op 432 B/op 3 allocs/op
+BenchmarkDenco_GithubParam 3484798 352 ns/op 128 B/op 1 allocs/op
+BenchmarkEcho_GithubParam 6337380 189 ns/op 0 B/op 0 allocs/op
+BenchmarkGin_GithubParam 9132032 131 ns/op 0 B/op 0 allocs/op
+BenchmarkGocraftWeb_GithubParam 1000000 1446 ns/op 712 B/op 9 allocs/op
+BenchmarkGoji_GithubParam 1248640 977 ns/op 336 B/op 2 allocs/op
+BenchmarkGojiv2_GithubParam 383233 2784 ns/op 1408 B/op 13 allocs/op
+BenchmarkGoJsonRest_GithubParam 1000000 1991 ns/op 713 B/op 14 allocs/op
+BenchmarkGoRestful_GithubParam 76414 16015 ns/op 4352 B/op 16 allocs/op
+BenchmarkGorillaMux_GithubParam 150026 7663 ns/op 1296 B/op 10 allocs/op
+BenchmarkGowwwRouter_GithubParam 1592044 751 ns/op 432 B/op 3 allocs/op
+BenchmarkHttpRouter_GithubParam 10420628 115 ns/op 0 B/op 0 allocs/op
+BenchmarkHttpTreeMux_GithubParam 1403755 835 ns/op 384 B/op 4 allocs/op
+BenchmarkKocha_GithubParam 2286170 533 ns/op 128 B/op 5 allocs/op
+BenchmarkLARS_GithubParam 9540374 129 ns/op 0 B/op 0 allocs/op
+BenchmarkMacaron_GithubParam 533154 2742 ns/op 1072 B/op 10 allocs/op
+BenchmarkMartini_GithubParam 119397 9638 ns/op 1152 B/op 11 allocs/op
+BenchmarkPat_GithubParam 150675 8858 ns/op 2408 B/op 48 allocs/op
+BenchmarkPossum_GithubParam 1000000 1001 ns/op 496 B/op 5 allocs/op
+BenchmarkR2router_GithubParam 1602886 761 ns/op 432 B/op 5 allocs/op
+BenchmarkRivet_GithubParam 2986579 409 ns/op 96 B/op 1 allocs/op
+BenchmarkTango_GithubParam 1000000 1356 ns/op 344 B/op 8 allocs/op
+BenchmarkTigerTonic_GithubParam 388899 3429 ns/op 1176 B/op 22 allocs/op
+BenchmarkTraffic_GithubParam 123160 9734 ns/op 2816 B/op 40 allocs/op
+BenchmarkVulcan_GithubParam 1000000 1138 ns/op 98 B/op 3 allocs/op
+BenchmarkAce_GithubAll 40543 29670 ns/op 0 B/op 0 allocs/op
+BenchmarkAero_GithubAll 57632 20648 ns/op 0 B/op 0 allocs/op
+BenchmarkBear_GithubAll 9234 216179 ns/op 86448 B/op 943 allocs/op
+BenchmarkBeego_GithubAll 7407 243496 ns/op 71456 B/op 609 allocs/op
+BenchmarkBone_GithubAll 420 2922835 ns/op 720160 B/op 8620 allocs/op
+BenchmarkChi_GithubAll 7620 238331 ns/op 87696 B/op 609 allocs/op
+BenchmarkDenco_GithubAll 18355 64494 ns/op 20224 B/op 167 allocs/op
+BenchmarkEcho_GithubAll 31251 38479 ns/op 0 B/op 0 allocs/op
+BenchmarkGin_GithubAll 43550 27364 ns/op 0 B/op 0 allocs/op
+BenchmarkGocraftWeb_GithubAll 4117 300062 ns/op 131656 B/op 1686 allocs/op
+BenchmarkGoji_GithubAll 3274 416158 ns/op 56112 B/op 334 allocs/op
+BenchmarkGojiv2_GithubAll 1402 870518 ns/op 352720 B/op 4321 allocs/op
+BenchmarkGoJsonRest_GithubAll 2976 401507 ns/op 134371 B/op 2737 allocs/op
+BenchmarkGoRestful_GithubAll 410 2913158 ns/op 910144 B/op 2938 allocs/op
+BenchmarkGorillaMux_GithubAll 346 3384987 ns/op 251650 B/op 1994 allocs/op
+BenchmarkGowwwRouter_GithubAll 10000 143025 ns/op 72144 B/op 501 allocs/op
+BenchmarkHttpRouter_GithubAll 55938 21360 ns/op 0 B/op 0 allocs/op
+BenchmarkHttpTreeMux_GithubAll 10000 153944 ns/op 65856 B/op 671 allocs/op
+BenchmarkKocha_GithubAll 10000 106315 ns/op 23304 B/op 843 allocs/op
+BenchmarkLARS_GithubAll 47779 25084 ns/op 0 B/op 0 allocs/op
+BenchmarkMacaron_GithubAll 3266 371907 ns/op 149409 B/op 1624 allocs/op
+BenchmarkMartini_GithubAll 331 3444706 ns/op 226551 B/op 2325 allocs/op
+BenchmarkPat_GithubAll 273 4381818 ns/op 1483152 B/op 26963 allocs/op
+BenchmarkPossum_GithubAll 10000 164367 ns/op 84448 B/op 609 allocs/op
+BenchmarkR2router_GithubAll 10000 160220 ns/op 77328 B/op 979 allocs/op
+BenchmarkRivet_GithubAll 14625 82453 ns/op 16272 B/op 167 allocs/op
+BenchmarkTango_GithubAll 6255 279611 ns/op 63826 B/op 1618 allocs/op
+BenchmarkTigerTonic_GithubAll 2008 687874 ns/op 193856 B/op 4474 allocs/op
+BenchmarkTraffic_GithubAll 355 3478508 ns/op 820744 B/op 14114 allocs/op
+BenchmarkVulcan_GithubAll 6885 193333 ns/op 19894 B/op 609 allocs/op
+```
+
+## Google+
+
+```sh
+BenchmarkGin_GPlusStatic 19247326 62.2 ns/op 0 B/op 0 allocs/op
+
+BenchmarkAce_GPlusStatic 20235060 59.2 ns/op 0 B/op 0 allocs/op
+BenchmarkAero_GPlusStatic 31978935 37.6 ns/op 0 B/op 0 allocs/op
+BenchmarkBear_GPlusStatic 3516523 341 ns/op 104 B/op 3 allocs/op
+BenchmarkBeego_GPlusStatic 1212036 991 ns/op 352 B/op 3 allocs/op
+BenchmarkBone_GPlusStatic 6736242 183 ns/op 32 B/op 1 allocs/op
+BenchmarkChi_GPlusStatic 1490640 814 ns/op 432 B/op 3 allocs/op
+BenchmarkDenco_GPlusStatic 55006856 21.8 ns/op 0 B/op 0 allocs/op
+BenchmarkEcho_GPlusStatic 17688258 67.9 ns/op 0 B/op 0 allocs/op
+BenchmarkGocraftWeb_GPlusStatic 1829181 666 ns/op 280 B/op 5 allocs/op
+BenchmarkGoji_GPlusStatic 9147451 130 ns/op 0 B/op 0 allocs/op
+BenchmarkGojiv2_GPlusStatic 594015 2063 ns/op 1312 B/op 10 allocs/op
+BenchmarkGoJsonRest_GPlusStatic 1264906 950 ns/op 329 B/op 11 allocs/op
+BenchmarkGoRestful_GPlusStatic 231558 5341 ns/op 3872 B/op 13 allocs/op
+BenchmarkGorillaMux_GPlusStatic 908418 1809 ns/op 976 B/op 9 allocs/op
+BenchmarkGowwwRouter_GPlusStatic 40684604 29.5 ns/op 0 B/op 0 allocs/op
+BenchmarkHttpRouter_GPlusStatic 46742804 25.7 ns/op 0 B/op 0 allocs/op
+BenchmarkHttpTreeMux_GPlusStatic 32567161 36.9 ns/op 0 B/op 0 allocs/op
+BenchmarkKocha_GPlusStatic 33800060 35.3 ns/op 0 B/op 0 allocs/op
+BenchmarkLARS_GPlusStatic 20431858 60.0 ns/op 0 B/op 0 allocs/op
+BenchmarkMacaron_GPlusStatic 1000000 1745 ns/op 736 B/op 8 allocs/op
+BenchmarkMartini_GPlusStatic 442248 3619 ns/op 768 B/op 9 allocs/op
+BenchmarkPat_GPlusStatic 4328004 292 ns/op 96 B/op 2 allocs/op
+BenchmarkPossum_GPlusStatic 1570753 763 ns/op 416 B/op 3 allocs/op
+BenchmarkR2router_GPlusStatic 3339474 355 ns/op 144 B/op 4 allocs/op
+BenchmarkRivet_GPlusStatic 18570961 64.7 ns/op 0 B/op 0 allocs/op
+BenchmarkTango_GPlusStatic 1388702 860 ns/op 200 B/op 8 allocs/op
+BenchmarkTigerTonic_GPlusStatic 7803543 159 ns/op 32 B/op 1 allocs/op
+BenchmarkTraffic_GPlusStatic 878605 2171 ns/op 1112 B/op 16 allocs/op
+BenchmarkVulcan_GPlusStatic 2742446 437 ns/op 98 B/op 3 allocs/op
+BenchmarkAce_GPlusParam 11626975 105 ns/op 0 B/op 0 allocs/op
+BenchmarkAero_GPlusParam 16914322 71.6 ns/op 0 B/op 0 allocs/op
+BenchmarkBear_GPlusParam 1405173 832 ns/op 480 B/op 5 allocs/op
+BenchmarkBeego_GPlusParam 1000000 1075 ns/op 352 B/op 3 allocs/op
+BenchmarkBone_GPlusParam 1000000 1557 ns/op 816 B/op 6 allocs/op
+BenchmarkChi_GPlusParam 1347926 894 ns/op 432 B/op 3 allocs/op
+BenchmarkDenco_GPlusParam 5513000 212 ns/op 64 B/op 1 allocs/op
+BenchmarkEcho_GPlusParam 11884383 101 ns/op 0 B/op 0 allocs/op
+BenchmarkGin_GPlusParam 12898952 93.1 ns/op 0 B/op 0 allocs/op
+BenchmarkGocraftWeb_GPlusParam 1000000 1194 ns/op 648 B/op 8 allocs/op
+BenchmarkGoji_GPlusParam 1857229 645 ns/op 336 B/op 2 allocs/op
+BenchmarkGojiv2_GPlusParam 520939 2322 ns/op 1328 B/op 11 allocs/op
+BenchmarkGoJsonRest_GPlusParam 1000000 1536 ns/op 649 B/op 13 allocs/op
+BenchmarkGoRestful_GPlusParam 205449 5800 ns/op 4192 B/op 14 allocs/op
+BenchmarkGorillaMux_GPlusParam 395310 3188 ns/op 1280 B/op 10 allocs/op
+BenchmarkGowwwRouter_GPlusParam 1851798 667 ns/op 432 B/op 3 allocs/op
+BenchmarkHttpRouter_GPlusParam 18420789 65.2 ns/op 0 B/op 0 allocs/op
+BenchmarkHttpTreeMux_GPlusParam 1878463 629 ns/op 352 B/op 3 allocs/op
+BenchmarkKocha_GPlusParam 4495610 273 ns/op 56 B/op 3 allocs/op
+BenchmarkLARS_GPlusParam 14615976 83.2 ns/op 0 B/op 0 allocs/op
+BenchmarkMacaron_GPlusParam 584145 2549 ns/op 1072 B/op 10 allocs/op
+BenchmarkMartini_GPlusParam 250501 4583 ns/op 1072 B/op 10 allocs/op
+BenchmarkPat_GPlusParam 1000000 1645 ns/op 576 B/op 11 allocs/op
+BenchmarkPossum_GPlusParam 1000000 1008 ns/op 496 B/op 5 allocs/op
+BenchmarkR2router_GPlusParam 1708191 688 ns/op 432 B/op 5 allocs/op
+BenchmarkRivet_GPlusParam 5795014 211 ns/op 48 B/op 1 allocs/op
+BenchmarkTango_GPlusParam 1000000 1091 ns/op 264 B/op 8 allocs/op
+BenchmarkTigerTonic_GPlusParam 760221 2489 ns/op 856 B/op 16 allocs/op
+BenchmarkTraffic_GPlusParam 309774 4039 ns/op 1872 B/op 21 allocs/op
+BenchmarkVulcan_GPlusParam 1935730 623 ns/op 98 B/op 3 allocs/op
+BenchmarkAce_GPlus2Params 9158314 134 ns/op 0 B/op 0 allocs/op
+BenchmarkAero_GPlus2Params 11300517 107 ns/op 0 B/op 0 allocs/op
+BenchmarkBear_GPlus2Params 1239238 961 ns/op 496 B/op 5 allocs/op
+BenchmarkBeego_GPlus2Params 1000000 1202 ns/op 352 B/op 3 allocs/op
+BenchmarkBone_GPlus2Params 335576 3725 ns/op 1168 B/op 10 allocs/op
+BenchmarkChi_GPlus2Params 1000000 1014 ns/op 432 B/op 3 allocs/op
+BenchmarkDenco_GPlus2Params 4394598 280 ns/op 64 B/op 1 allocs/op
+BenchmarkEcho_GPlus2Params 7851861 154 ns/op 0 B/op 0 allocs/op
+BenchmarkGin_GPlus2Params 9958588 120 ns/op 0 B/op 0 allocs/op
+BenchmarkGocraftWeb_GPlus2Params 1000000 1433 ns/op 712 B/op 9 allocs/op
+BenchmarkGoji_GPlus2Params 1325134 909 ns/op 336 B/op 2 allocs/op
+BenchmarkGojiv2_GPlus2Params 405955 2870 ns/op 1408 B/op 14 allocs/op
+BenchmarkGoJsonRest_GPlus2Params 977038 1987 ns/op 713 B/op 14 allocs/op
+BenchmarkGoRestful_GPlus2Params 205018 6142 ns/op 4384 B/op 16 allocs/op
+BenchmarkGorillaMux_GPlus2Params 205641 6015 ns/op 1296 B/op 10 allocs/op
+BenchmarkGowwwRouter_GPlus2Params 1748542 684 ns/op 432 B/op 3 allocs/op
+BenchmarkHttpRouter_GPlus2Params 14047102 87.7 ns/op 0 B/op 0 allocs/op
+BenchmarkHttpTreeMux_GPlus2Params 1418673 828 ns/op 384 B/op 4 allocs/op
+BenchmarkKocha_GPlus2Params 2334562 520 ns/op 128 B/op 5 allocs/op
+BenchmarkLARS_GPlus2Params 11954094 101 ns/op 0 B/op 0 allocs/op
+BenchmarkMacaron_GPlus2Params 491552 2890 ns/op 1072 B/op 10 allocs/op
+BenchmarkMartini_GPlus2Params 120532 9545 ns/op 1200 B/op 13 allocs/op
+BenchmarkPat_GPlus2Params 194739 6766 ns/op 2168 B/op 33 allocs/op
+BenchmarkPossum_GPlus2Params 1201224 1009 ns/op 496 B/op 5 allocs/op
+BenchmarkR2router_GPlus2Params 1575535 756 ns/op 432 B/op 5 allocs/op
+BenchmarkRivet_GPlus2Params 3698930 325 ns/op 96 B/op 1 allocs/op
+BenchmarkTango_GPlus2Params 1000000 1212 ns/op 344 B/op 8 allocs/op
+BenchmarkTigerTonic_GPlus2Params 349350 3660 ns/op 1200 B/op 22 allocs/op
+BenchmarkTraffic_GPlus2Params 169714 7862 ns/op 2248 B/op 28 allocs/op
+BenchmarkVulcan_GPlus2Params 1222288 974 ns/op 98 B/op 3 allocs/op
+BenchmarkAce_GPlusAll 845606 1398 ns/op 0 B/op 0 allocs/op
+BenchmarkAero_GPlusAll 1000000 1009 ns/op 0 B/op 0 allocs/op
+BenchmarkBear_GPlusAll 103830 11386 ns/op 5488 B/op 61 allocs/op
+BenchmarkBeego_GPlusAll 82653 14784 ns/op 4576 B/op 39 allocs/op
+BenchmarkBone_GPlusAll 36601 33123 ns/op 11744 B/op 109 allocs/op
+BenchmarkChi_GPlusAll 95264 12831 ns/op 5616 B/op 39 allocs/op
+BenchmarkDenco_GPlusAll 567681 2950 ns/op 672 B/op 11 allocs/op
+BenchmarkEcho_GPlusAll 720366 1665 ns/op 0 B/op 0 allocs/op
+BenchmarkGin_GPlusAll 1000000 1185 ns/op 0 B/op 0 allocs/op
+BenchmarkGocraftWeb_GPlusAll 71575 16365 ns/op 8040 B/op 103 allocs/op
+BenchmarkGoji_GPlusAll 136352 9191 ns/op 3696 B/op 22 allocs/op
+BenchmarkGojiv2_GPlusAll 38006 31802 ns/op 17616 B/op 154 allocs/op
+BenchmarkGoJsonRest_GPlusAll 57238 21561 ns/op 8117 B/op 170 allocs/op
+BenchmarkGoRestful_GPlusAll 15147 79276 ns/op 55520 B/op 192 allocs/op
+BenchmarkGorillaMux_GPlusAll 24446 48410 ns/op 16112 B/op 128 allocs/op
+BenchmarkGowwwRouter_GPlusAll 150112 7770 ns/op 4752 B/op 33 allocs/op
+BenchmarkHttpRouter_GPlusAll 1367820 878 ns/op 0 B/op 0 allocs/op
+BenchmarkHttpTreeMux_GPlusAll 166628 8004 ns/op 4032 B/op 38 allocs/op
+BenchmarkKocha_GPlusAll 265694 4570 ns/op 976 B/op 43 allocs/op
+BenchmarkLARS_GPlusAll 1000000 1068 ns/op 0 B/op 0 allocs/op
+BenchmarkMacaron_GPlusAll 54564 23305 ns/op 9568 B/op 104 allocs/op
+BenchmarkMartini_GPlusAll 16274 73845 ns/op 14016 B/op 145 allocs/op
+BenchmarkPat_GPlusAll 27181 44478 ns/op 15264 B/op 271 allocs/op
+BenchmarkPossum_GPlusAll 122587 10277 ns/op 5408 B/op 39 allocs/op
+BenchmarkR2router_GPlusAll 130137 9297 ns/op 5040 B/op 63 allocs/op
+BenchmarkRivet_GPlusAll 532438 3323 ns/op 768 B/op 11 allocs/op
+BenchmarkTango_GPlusAll 86054 14531 ns/op 3656 B/op 104 allocs/op
+BenchmarkTigerTonic_GPlusAll 33936 35356 ns/op 11600 B/op 242 allocs/op
+BenchmarkTraffic_GPlusAll 17833 68181 ns/op 26248 B/op 341 allocs/op
+BenchmarkVulcan_GPlusAll 120109 9861 ns/op 1274 B/op 39 allocs/op
+```
+
+## Parse.com
+
+```sh
+BenchmarkGin_ParseStatic 18877833 63.5 ns/op 0 B/op 0 allocs/op
+
+BenchmarkAce_ParseStatic 19663731 60.8 ns/op 0 B/op 0 allocs/op
+BenchmarkAero_ParseStatic 28967341 41.5 ns/op 0 B/op 0 allocs/op
+BenchmarkBear_ParseStatic 3006984 402 ns/op 120 B/op 3 allocs/op
+BenchmarkBeego_ParseStatic 1000000 1031 ns/op 352 B/op 3 allocs/op
+BenchmarkBone_ParseStatic 1782482 675 ns/op 144 B/op 3 allocs/op
+BenchmarkChi_ParseStatic 1453261 819 ns/op 432 B/op 3 allocs/op
+BenchmarkDenco_ParseStatic 45023595 26.5 ns/op 0 B/op 0 allocs/op
+BenchmarkEcho_ParseStatic 17330470 69.3 ns/op 0 B/op 0 allocs/op
+BenchmarkGocraftWeb_ParseStatic 1644006 731 ns/op 296 B/op 5 allocs/op
+BenchmarkGoji_ParseStatic 7026930 170 ns/op 0 B/op 0 allocs/op
+BenchmarkGojiv2_ParseStatic 517618 2037 ns/op 1312 B/op 10 allocs/op
+BenchmarkGoJsonRest_ParseStatic 1227080 975 ns/op 329 B/op 11 allocs/op
+BenchmarkGoRestful_ParseStatic 192458 6659 ns/op 4256 B/op 13 allocs/op
+BenchmarkGorillaMux_ParseStatic 744062 2109 ns/op 976 B/op 9 allocs/op
+BenchmarkGowwwRouter_ParseStatic 37781062 31.8 ns/op 0 B/op 0 allocs/op
+BenchmarkHttpRouter_ParseStatic 45311223 26.5 ns/op 0 B/op 0 allocs/op
+BenchmarkHttpTreeMux_ParseStatic 21383475 56.1 ns/op 0 B/op 0 allocs/op
+BenchmarkKocha_ParseStatic 29953290 40.1 ns/op 0 B/op 0 allocs/op
+BenchmarkLARS_ParseStatic 20036196 62.7 ns/op 0 B/op 0 allocs/op
+BenchmarkMacaron_ParseStatic 1000000 1740 ns/op 736 B/op 8 allocs/op
+BenchmarkMartini_ParseStatic 404156 3801 ns/op 768 B/op 9 allocs/op
+BenchmarkPat_ParseStatic 1547180 772 ns/op 240 B/op 5 allocs/op
+BenchmarkPossum_ParseStatic 1608991 757 ns/op 416 B/op 3 allocs/op
+BenchmarkR2router_ParseStatic 3177936 385 ns/op 144 B/op 4 allocs/op
+BenchmarkRivet_ParseStatic 17783205 67.4 ns/op 0 B/op 0 allocs/op
+BenchmarkTango_ParseStatic 1210777 990 ns/op 248 B/op 8 allocs/op
+BenchmarkTigerTonic_ParseStatic 5316440 231 ns/op 48 B/op 1 allocs/op
+BenchmarkTraffic_ParseStatic 496050 2539 ns/op 1256 B/op 19 allocs/op
+BenchmarkVulcan_ParseStatic 2462798 488 ns/op 98 B/op 3 allocs/op
+BenchmarkAce_ParseParam 13393669 89.6 ns/op 0 B/op 0 allocs/op
+BenchmarkAero_ParseParam 19836619 60.4 ns/op 0 B/op 0 allocs/op
+BenchmarkBear_ParseParam 1405954 864 ns/op 467 B/op 5 allocs/op
+BenchmarkBeego_ParseParam 1000000 1065 ns/op 352 B/op 3 allocs/op
+BenchmarkBone_ParseParam 1000000 1698 ns/op 896 B/op 7 allocs/op
+BenchmarkChi_ParseParam 1356037 873 ns/op 432 B/op 3 allocs/op
+BenchmarkDenco_ParseParam 6241392 204 ns/op 64 B/op 1 allocs/op
+BenchmarkEcho_ParseParam 14088100 85.1 ns/op 0 B/op 0 allocs/op
+BenchmarkGin_ParseParam 17426064 68.9 ns/op 0 B/op 0 allocs/op
+BenchmarkGocraftWeb_ParseParam 1000000 1254 ns/op 664 B/op 8 allocs/op
+BenchmarkGoji_ParseParam 1682574 713 ns/op 336 B/op 2 allocs/op
+BenchmarkGojiv2_ParseParam 502224 2333 ns/op 1360 B/op 12 allocs/op
+BenchmarkGoJsonRest_ParseParam 1000000 1401 ns/op 649 B/op 13 allocs/op
+BenchmarkGoRestful_ParseParam 182623 7097 ns/op 4576 B/op 14 allocs/op
+BenchmarkGorillaMux_ParseParam 482332 2477 ns/op 1280 B/op 10 allocs/op
+BenchmarkGowwwRouter_ParseParam 1834873 657 ns/op 432 B/op 3 allocs/op
+BenchmarkHttpRouter_ParseParam 23593393 51.0 ns/op 0 B/op 0 allocs/op
+BenchmarkHttpTreeMux_ParseParam 2100160 574 ns/op 352 B/op 3 allocs/op
+BenchmarkKocha_ParseParam 4837220 252 ns/op 56 B/op 3 allocs/op
+BenchmarkLARS_ParseParam 18411192 66.2 ns/op 0 B/op 0 allocs/op
+BenchmarkMacaron_ParseParam 571870 2398 ns/op 1072 B/op 10 allocs/op
+BenchmarkMartini_ParseParam 286262 4268 ns/op 1072 B/op 10 allocs/op
+BenchmarkPat_ParseParam 692906 2157 ns/op 992 B/op 15 allocs/op
+BenchmarkPossum_ParseParam 1000000 1011 ns/op 496 B/op 5 allocs/op
+BenchmarkR2router_ParseParam 1722735 697 ns/op 432 B/op 5 allocs/op
+BenchmarkRivet_ParseParam 6058054 203 ns/op 48 B/op 1 allocs/op
+BenchmarkTango_ParseParam 1000000 1061 ns/op 280 B/op 8 allocs/op
+BenchmarkTigerTonic_ParseParam 890275 2277 ns/op 784 B/op 15 allocs/op
+BenchmarkTraffic_ParseParam 351322 3543 ns/op 1896 B/op 21 allocs/op
+BenchmarkVulcan_ParseParam 2076544 572 ns/op 98 B/op 3 allocs/op
+BenchmarkAce_Parse2Params 11718074 101 ns/op 0 B/op 0 allocs/op
+BenchmarkAero_Parse2Params 16264988 73.4 ns/op 0 B/op 0 allocs/op
+BenchmarkBear_Parse2Params 1238322 973 ns/op 496 B/op 5 allocs/op
+BenchmarkBeego_Parse2Params 1000000 1120 ns/op 352 B/op 3 allocs/op
+BenchmarkBone_Parse2Params 1000000 1632 ns/op 848 B/op 6 allocs/op
+BenchmarkChi_Parse2Params 1239477 955 ns/op 432 B/op 3 allocs/op
+BenchmarkDenco_Parse2Params 4944133 245 ns/op 64 B/op 1 allocs/op
+BenchmarkEcho_Parse2Params 10518286 114 ns/op 0 B/op 0 allocs/op
+BenchmarkGin_Parse2Params 14505195 82.7 ns/op 0 B/op 0 allocs/op
+BenchmarkGocraftWeb_Parse2Params 1000000 1437 ns/op 712 B/op 9 allocs/op
+BenchmarkGoji_Parse2Params 1689883 707 ns/op 336 B/op 2 allocs/op
+BenchmarkGojiv2_Parse2Params 502334 2308 ns/op 1344 B/op 11 allocs/op
+BenchmarkGoJsonRest_Parse2Params 1000000 1771 ns/op 713 B/op 14 allocs/op
+BenchmarkGoRestful_Parse2Params 159092 7583 ns/op 4928 B/op 14 allocs/op
+BenchmarkGorillaMux_Parse2Params 417548 2980 ns/op 1296 B/op 10 allocs/op
+BenchmarkGowwwRouter_Parse2Params 1751737 686 ns/op 432 B/op 3 allocs/op
+BenchmarkHttpRouter_Parse2Params 18089204 66.3 ns/op 0 B/op 0 allocs/op
+BenchmarkHttpTreeMux_Parse2Params 1556986 777 ns/op 384 B/op 4 allocs/op
+BenchmarkKocha_Parse2Params 2493082 485 ns/op 128 B/op 5 allocs/op
+BenchmarkLARS_Parse2Params 15350108 78.5 ns/op 0 B/op 0 allocs/op
+BenchmarkMacaron_Parse2Params 530974 2605 ns/op 1072 B/op 10 allocs/op
+BenchmarkMartini_Parse2Params 247069 4673 ns/op 1152 B/op 11 allocs/op
+BenchmarkPat_Parse2Params 816295 2126 ns/op 752 B/op 16 allocs/op
+BenchmarkPossum_Parse2Params 1000000 1002 ns/op 496 B/op 5 allocs/op
+BenchmarkR2router_Parse2Params 1569771 733 ns/op 432 B/op 5 allocs/op
+BenchmarkRivet_Parse2Params 4080546 295 ns/op 96 B/op 1 allocs/op
+BenchmarkTango_Parse2Params 1000000 1121 ns/op 312 B/op 8 allocs/op
+BenchmarkTigerTonic_Parse2Params 399556 3470 ns/op 1168 B/op 22 allocs/op
+BenchmarkTraffic_Parse2Params 314194 4159 ns/op 1944 B/op 22 allocs/op
+BenchmarkVulcan_Parse2Params 1827559 664 ns/op 98 B/op 3 allocs/op
+BenchmarkAce_ParseAll 478395 2503 ns/op 0 B/op 0 allocs/op
+BenchmarkAero_ParseAll 715392 1658 ns/op 0 B/op 0 allocs/op
+BenchmarkBear_ParseAll 59191 20124 ns/op 8928 B/op 110 allocs/op
+BenchmarkBeego_ParseAll 45507 27266 ns/op 9152 B/op 78 allocs/op
+BenchmarkBone_ParseAll 29328 41459 ns/op 16208 B/op 147 allocs/op
+BenchmarkChi_ParseAll 48531 25053 ns/op 11232 B/op 78 allocs/op
+BenchmarkDenco_ParseAll 325532 4284 ns/op 928 B/op 16 allocs/op
+BenchmarkEcho_ParseAll 433771 2759 ns/op 0 B/op 0 allocs/op
+BenchmarkGin_ParseAll 576316 2082 ns/op 0 B/op 0 allocs/op
+BenchmarkGocraftWeb_ParseAll 41500 29692 ns/op 13728 B/op 181 allocs/op
+BenchmarkGoji_ParseAll 80833 15563 ns/op 5376 B/op 32 allocs/op
+BenchmarkGojiv2_ParseAll 19836 60335 ns/op 34448 B/op 277 allocs/op
+BenchmarkGoJsonRest_ParseAll 32210 38027 ns/op 13866 B/op 321 allocs/op
+BenchmarkGoRestful_ParseAll 6644 190842 ns/op 117600 B/op 354 allocs/op
+BenchmarkGorillaMux_ParseAll 12634 95894 ns/op 30288 B/op 250 allocs/op
+BenchmarkGowwwRouter_ParseAll 98152 12159 ns/op 6912 B/op 48 allocs/op
+BenchmarkHttpRouter_ParseAll 933208 1273 ns/op 0 B/op 0 allocs/op
+BenchmarkHttpTreeMux_ParseAll 107191 11554 ns/op 5728 B/op 51 allocs/op
+BenchmarkKocha_ParseAll 184862 6225 ns/op 1112 B/op 54 allocs/op
+BenchmarkLARS_ParseAll 644546 1858 ns/op 0 B/op 0 allocs/op
+BenchmarkMacaron_ParseAll 26145 46484 ns/op 19136 B/op 208 allocs/op
+BenchmarkMartini_ParseAll 10000 121838 ns/op 25072 B/op 253 allocs/op
+BenchmarkPat_ParseAll 25417 47196 ns/op 15216 B/op 308 allocs/op
+BenchmarkPossum_ParseAll 58550 20735 ns/op 10816 B/op 78 allocs/op
+BenchmarkR2router_ParseAll 72732 16584 ns/op 8352 B/op 120 allocs/op
+BenchmarkRivet_ParseAll 281365 4968 ns/op 912 B/op 16 allocs/op
+BenchmarkTango_ParseAll 42831 28668 ns/op 7168 B/op 208 allocs/op
+BenchmarkTigerTonic_ParseAll 23774 49972 ns/op 16048 B/op 332 allocs/op
+BenchmarkTraffic_ParseAll 10000 104679 ns/op 45520 B/op 605 allocs/op
+BenchmarkVulcan_ParseAll 64810 18108 ns/op 2548 B/op 78 allocs/op
+```
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/CHANGELOG.md b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/CHANGELOG.md
new file mode 100644
index 000000000000..4c806a5a7de9
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/CHANGELOG.md
@@ -0,0 +1,454 @@
+# Gin ChangeLog
+
+## Gin v1.7.7
+
+### BUGFIXES
+
+* Fixed X-Forwarded-For unsafe handling of CVE-2020-28483 [#2844](https://github.com/gin-gonic/gin/pull/2844), closed issue [#2862](https://github.com/gin-gonic/gin/issues/2862).
+* Tree: updated the code logic for `latestNode` [#2897](https://github.com/gin-gonic/gin/pull/2897), closed issue [#2894](https://github.com/gin-gonic/gin/issues/2894) [#2878](https://github.com/gin-gonic/gin/issues/2878).
+* Tree: fixed the misplacement of adding slashes [#2847](https://github.com/gin-gonic/gin/pull/2847), closed issue [#2843](https://github.com/gin-gonic/gin/issues/2843).
+* Tree: fixed tsr with mixed static and wildcard paths [#2924](https://github.com/gin-gonic/gin/pull/2924), closed issue [#2918](https://github.com/gin-gonic/gin/issues/2918).
+
+### ENHANCEMENTS
+
+* TrustedProxies: make it backward-compatible [#2887](https://github.com/gin-gonic/gin/pull/2887), closed issue [#2819](https://github.com/gin-gonic/gin/issues/2819).
+* TrustedPlatform: provide custom options for another CDN services [#2906](https://github.com/gin-gonic/gin/pull/2906).
+
+### DOCS
+
+* NoMethod: added usage annotation ([#2832](https://github.com/gin-gonic/gin/pull/2832#issuecomment-929954463)).
+
+## Gin v1.7.6
+
+### BUGFIXES
+
+* bump new release to fix v1.7.5 release error by using v1.7.4 codes.
+
+## Gin v1.7.4
+
+### BUGFIXES
+
+* bump new release to fix checksum mismatch
+
+## Gin v1.7.3
+
+### BUGFIXES
+
+* fix level 1 router match [#2767](https://github.com/gin-gonic/gin/issues/2767), [#2796](https://github.com/gin-gonic/gin/issues/2796)
+
+## Gin v1.7.2
+
+### BUGFIXES
+
+* Fix conflict between param and exact path [#2706](https://github.com/gin-gonic/gin/issues/2706). Close issue [#2682](https://github.com/gin-gonic/gin/issues/2682) [#2696](https://github.com/gin-gonic/gin/issues/2696).
+
+## Gin v1.7.1
+
+### BUGFIXES
+
+* fix: data race with trustedCIDRs from [#2674](https://github.com/gin-gonic/gin/issues/2674)([#2675](https://github.com/gin-gonic/gin/pull/2675))
+
+## Gin v1.7.0
+
+### BUGFIXES
+
+* fix compile error from [#2572](https://github.com/gin-gonic/gin/pull/2572) ([#2600](https://github.com/gin-gonic/gin/pull/2600))
+* fix: print headers without Authorization header on broken pipe ([#2528](https://github.com/gin-gonic/gin/pull/2528))
+* fix(tree): reassign fullpath when register new node ([#2366](https://github.com/gin-gonic/gin/pull/2366))
+
+### ENHANCEMENTS
+
+* Support params and exact routes without creating conflicts ([#2663](https://github.com/gin-gonic/gin/pull/2663))
+* chore: improve render string performance ([#2365](https://github.com/gin-gonic/gin/pull/2365))
+* Sync route tree to httprouter latest code ([#2368](https://github.com/gin-gonic/gin/pull/2368))
+* chore: rename getQueryCache/getFormCache to initQueryCache/initFormCa ([#2375](https://github.com/gin-gonic/gin/pull/2375))
+* chore(performance): improve countParams ([#2378](https://github.com/gin-gonic/gin/pull/2378))
+* Remove some functions that have the same effect as the bytes package ([#2387](https://github.com/gin-gonic/gin/pull/2387))
+* update:SetMode function ([#2321](https://github.com/gin-gonic/gin/pull/2321))
+* remove a unused type SecureJSONPrefix ([#2391](https://github.com/gin-gonic/gin/pull/2391))
+* Add a redirect sample for POST method ([#2389](https://github.com/gin-gonic/gin/pull/2389))
+* Add CustomRecovery builtin middleware ([#2322](https://github.com/gin-gonic/gin/pull/2322))
+* binding: avoid 2038 problem on 32-bit architectures ([#2450](https://github.com/gin-gonic/gin/pull/2450))
+* Prevent panic in Context.GetQuery() when there is no Request ([#2412](https://github.com/gin-gonic/gin/pull/2412))
+* Add GetUint and GetUint64 method on gin.context ([#2487](https://github.com/gin-gonic/gin/pull/2487))
+* update content-disposition header to MIME-style ([#2512](https://github.com/gin-gonic/gin/pull/2512))
+* reduce allocs and improve the render `WriteString` ([#2508](https://github.com/gin-gonic/gin/pull/2508))
+* implement ".Unwrap() error" on Error type ([#2525](https://github.com/gin-gonic/gin/pull/2525)) ([#2526](https://github.com/gin-gonic/gin/pull/2526))
+* Allow bind with a map[string]string ([#2484](https://github.com/gin-gonic/gin/pull/2484))
+* chore: update tree ([#2371](https://github.com/gin-gonic/gin/pull/2371))
+* Support binding for slice/array obj [Rewrite] ([#2302](https://github.com/gin-gonic/gin/pull/2302))
+* basic auth: fix timing oracle ([#2609](https://github.com/gin-gonic/gin/pull/2609))
+* Add mixed param and non-param paths (port of httprouter[#329](https://github.com/gin-gonic/gin/pull/329)) ([#2663](https://github.com/gin-gonic/gin/pull/2663))
+* feat(engine): add trustedproxies and remoteIP ([#2632](https://github.com/gin-gonic/gin/pull/2632))
+
+## Gin v1.6.3
+
+### ENHANCEMENTS
+
+ * Improve performance: Change `*sync.RWMutex` to `sync.RWMutex` in context. [#2351](https://github.com/gin-gonic/gin/pull/2351)
+
+## Gin v1.6.2
+
+### BUGFIXES
+ * fix missing initial sync.RWMutex [#2305](https://github.com/gin-gonic/gin/pull/2305)
+### ENHANCEMENTS
+ * Add set samesite in cookie. [#2306](https://github.com/gin-gonic/gin/pull/2306)
+
+## Gin v1.6.1
+
+### BUGFIXES
+ * Revert "fix accept incoming network connections" [#2294](https://github.com/gin-gonic/gin/pull/2294)
+
+## Gin v1.6.0
+
+### BREAKING
+ * chore(performance): Improve performance for adding RemoveExtraSlash flag [#2159](https://github.com/gin-gonic/gin/pull/2159)
+ * drop support govendor [#2148](https://github.com/gin-gonic/gin/pull/2148)
+ * Added support for SameSite cookie flag [#1615](https://github.com/gin-gonic/gin/pull/1615)
+### FEATURES
+ * add yaml negotiation [#2220](https://github.com/gin-gonic/gin/pull/2220)
+ * FileFromFS [#2112](https://github.com/gin-gonic/gin/pull/2112)
+### BUGFIXES
+ * Unix Socket Handling [#2280](https://github.com/gin-gonic/gin/pull/2280)
+ * Use json marshall in context json to fix breaking new line issue. Fixes #2209 [#2228](https://github.com/gin-gonic/gin/pull/2228)
+ * fix accept incoming network connections [#2216](https://github.com/gin-gonic/gin/pull/2216)
+ * Fixed a bug in the calculation of the maximum number of parameters [#2166](https://github.com/gin-gonic/gin/pull/2166)
+ * [FIX] allow empty headers on DataFromReader [#2121](https://github.com/gin-gonic/gin/pull/2121)
+ * Add mutex for protect Context.Keys map [#1391](https://github.com/gin-gonic/gin/pull/1391)
+### ENHANCEMENTS
+ * Add mitigation for log injection [#2277](https://github.com/gin-gonic/gin/pull/2277)
+ * tree: range over nodes values [#2229](https://github.com/gin-gonic/gin/pull/2229)
+ * tree: remove duplicate assignment [#2222](https://github.com/gin-gonic/gin/pull/2222)
+ * chore: upgrade go-isatty and json-iterator/go [#2215](https://github.com/gin-gonic/gin/pull/2215)
+ * path: sync code with httprouter [#2212](https://github.com/gin-gonic/gin/pull/2212)
+ * Use zero-copy approach to convert types between string and byte slice [#2206](https://github.com/gin-gonic/gin/pull/2206)
+ * Reuse bytes when cleaning the URL paths [#2179](https://github.com/gin-gonic/gin/pull/2179)
+ * tree: remove one else statement [#2177](https://github.com/gin-gonic/gin/pull/2177)
+ * tree: sync httprouter update (#2173) (#2172) [#2171](https://github.com/gin-gonic/gin/pull/2171)
+ * tree: sync part httprouter codes and reduce if/else [#2163](https://github.com/gin-gonic/gin/pull/2163)
+ * use http method constant [#2155](https://github.com/gin-gonic/gin/pull/2155)
+ * upgrade go-validator to v10 [#2149](https://github.com/gin-gonic/gin/pull/2149)
+ * Refactor redirect request in gin.go [#1970](https://github.com/gin-gonic/gin/pull/1970)
+ * Add build tag nomsgpack [#1852](https://github.com/gin-gonic/gin/pull/1852)
+### DOCS
+ * docs(path): improve comments [#2223](https://github.com/gin-gonic/gin/pull/2223)
+ * Renew README to fit the modification of SetCookie method [#2217](https://github.com/gin-gonic/gin/pull/2217)
+ * Fix spelling [#2202](https://github.com/gin-gonic/gin/pull/2202)
+ * Remove broken link from README. [#2198](https://github.com/gin-gonic/gin/pull/2198)
+ * Update docs on Context.Done(), Context.Deadline() and Context.Err() [#2196](https://github.com/gin-gonic/gin/pull/2196)
+ * Update validator to v10 [#2190](https://github.com/gin-gonic/gin/pull/2190)
+ * upgrade go-validator to v10 for README [#2189](https://github.com/gin-gonic/gin/pull/2189)
+ * Update to currently output [#2188](https://github.com/gin-gonic/gin/pull/2188)
+ * Fix "Custom Validators" example [#2186](https://github.com/gin-gonic/gin/pull/2186)
+ * Add project to README [#2165](https://github.com/gin-gonic/gin/pull/2165)
+ * docs(benchmarks): for gin v1.5 [#2153](https://github.com/gin-gonic/gin/pull/2153)
+ * Changed wording for clarity in README.md [#2122](https://github.com/gin-gonic/gin/pull/2122)
+### MISC
+ * ci support go1.14 [#2262](https://github.com/gin-gonic/gin/pull/2262)
+ * chore: upgrade depend version [#2231](https://github.com/gin-gonic/gin/pull/2231)
+ * Drop support go1.10 [#2147](https://github.com/gin-gonic/gin/pull/2147)
+ * fix comment in `mode.go` [#2129](https://github.com/gin-gonic/gin/pull/2129)
+
+## Gin v1.5.0
+
+- [FIX] Use DefaultWriter and DefaultErrorWriter for debug messages [#1891](https://github.com/gin-gonic/gin/pull/1891)
+- [NEW] Now you can parse the inline lowercase start structure [#1893](https://github.com/gin-gonic/gin/pull/1893)
+- [FIX] Some code improvements [#1909](https://github.com/gin-gonic/gin/pull/1909)
+- [FIX] Use encode replace json marshal increase json encoder speed [#1546](https://github.com/gin-gonic/gin/pull/1546)
+- [NEW] Hold matched route full path in the Context [#1826](https://github.com/gin-gonic/gin/pull/1826)
+- [FIX] Fix context.Params race condition on Copy() [#1841](https://github.com/gin-gonic/gin/pull/1841)
+- [NEW] Add context param query cache [#1450](https://github.com/gin-gonic/gin/pull/1450)
+- [FIX] Improve GetQueryMap performance [#1918](https://github.com/gin-gonic/gin/pull/1918)
+- [FIX] Improve get post data [#1920](https://github.com/gin-gonic/gin/pull/1920)
+- [FIX] Use context instead of x/net/context [#1922](https://github.com/gin-gonic/gin/pull/1922)
+- [FIX] Attempt to fix PostForm cache bug [#1931](https://github.com/gin-gonic/gin/pull/1931)
+- [NEW] Add support of multipart multi files [#1949](https://github.com/gin-gonic/gin/pull/1949)
+- [NEW] Support bind http header param [#1957](https://github.com/gin-gonic/gin/pull/1957)
+- [FIX] Drop support for go1.8 and go1.9 [#1933](https://github.com/gin-gonic/gin/pull/1933)
+- [FIX] Bugfix for the FullPath feature [#1919](https://github.com/gin-gonic/gin/pull/1919)
+- [FIX] Gin1.5 bytes.Buffer to strings.Builder [#1939](https://github.com/gin-gonic/gin/pull/1939)
+- [FIX] Upgrade github.com/ugorji/go/codec [#1969](https://github.com/gin-gonic/gin/pull/1969)
+- [NEW] Support bind unix time [#1980](https://github.com/gin-gonic/gin/pull/1980)
+- [FIX] Simplify code [#2004](https://github.com/gin-gonic/gin/pull/2004)
+- [NEW] Support negative Content-Length in DataFromReader [#1981](https://github.com/gin-gonic/gin/pull/1981)
+- [FIX] Identify terminal on a RISC-V architecture for auto-colored logs [#2019](https://github.com/gin-gonic/gin/pull/2019)
+- [BREAKING] `Context.JSONP()` now expects a semicolon (`;`) at the end [#2007](https://github.com/gin-gonic/gin/pull/2007)
+- [BREAKING] Upgrade default `binding.Validator` to v9 (see [its changelog](https://github.com/go-playground/validator/releases/tag/v9.0.0)) [#1015](https://github.com/gin-gonic/gin/pull/1015)
+- [NEW] Add `DisallowUnknownFields()` in `Context.BindJSON()` [#2028](https://github.com/gin-gonic/gin/pull/2028)
+- [NEW] Use specific `net.Listener` with `Engine.RunListener()` [#2023](https://github.com/gin-gonic/gin/pull/2023)
+- [FIX] Fix some typo [#2079](https://github.com/gin-gonic/gin/pull/2079) [#2080](https://github.com/gin-gonic/gin/pull/2080)
+- [FIX] Relocate binding body tests [#2086](https://github.com/gin-gonic/gin/pull/2086)
+- [FIX] Use Writer in Context.Status [#1606](https://github.com/gin-gonic/gin/pull/1606)
+- [FIX] `Engine.RunUnix()` now returns the error if it can't change the file mode [#2093](https://github.com/gin-gonic/gin/pull/2093)
+- [FIX] `RouterGroup.StaticFS()` leaked files. Now it closes them. [#2118](https://github.com/gin-gonic/gin/pull/2118)
+- [FIX] `Context.Request.FormFile` leaked file. Now it closes it. [#2114](https://github.com/gin-gonic/gin/pull/2114)
+- [FIX] Ignore walking on `form:"-"` mapping [#1943](https://github.com/gin-gonic/gin/pull/1943)
+
+### Gin v1.4.0
+
+- [NEW] Support for [Go Modules](https://github.com/golang/go/wiki/Modules) [#1569](https://github.com/gin-gonic/gin/pull/1569)
+- [NEW] Refactor of form mapping multipart request [#1829](https://github.com/gin-gonic/gin/pull/1829)
+- [FIX] Truncate Latency precision in long running request [#1830](https://github.com/gin-gonic/gin/pull/1830)
+- [FIX] IsTerm flag should not be affected by DisableConsoleColor method. [#1802](https://github.com/gin-gonic/gin/pull/1802)
+- [NEW] Supporting file binding [#1264](https://github.com/gin-gonic/gin/pull/1264)
+- [NEW] Add support for mapping arrays [#1797](https://github.com/gin-gonic/gin/pull/1797)
+- [FIX] Readme updates [#1793](https://github.com/gin-gonic/gin/pull/1793) [#1788](https://github.com/gin-gonic/gin/pull/1788) [1789](https://github.com/gin-gonic/gin/pull/1789)
+- [FIX] StaticFS: Fixed Logging two log lines on 404. [#1805](https://github.com/gin-gonic/gin/pull/1805), [#1804](https://github.com/gin-gonic/gin/pull/1804)
+- [NEW] Make context.Keys available as LogFormatterParams [#1779](https://github.com/gin-gonic/gin/pull/1779)
+- [NEW] Use internal/json for Marshal/Unmarshal [#1791](https://github.com/gin-gonic/gin/pull/1791)
+- [NEW] Support mapping time.Duration [#1794](https://github.com/gin-gonic/gin/pull/1794)
+- [NEW] Refactor form mappings [#1749](https://github.com/gin-gonic/gin/pull/1749)
+- [NEW] Added flag to context.Stream indicates if client disconnected in middle of stream [#1252](https://github.com/gin-gonic/gin/pull/1252)
+- [FIX] Moved [examples](https://github.com/gin-gonic/examples) to stand alone Repo [#1775](https://github.com/gin-gonic/gin/pull/1775)
+- [NEW] Extend context.File to allow for the content-disposition attachments via a new method context.Attachment [#1260](https://github.com/gin-gonic/gin/pull/1260)
+- [FIX] Support HTTP content negotiation wildcards [#1112](https://github.com/gin-gonic/gin/pull/1112)
+- [NEW] Add prefix from X-Forwarded-Prefix in redirectTrailingSlash [#1238](https://github.com/gin-gonic/gin/pull/1238)
+- [FIX] context.Copy() race condition [#1020](https://github.com/gin-gonic/gin/pull/1020)
+- [NEW] Add context.HandlerNames() [#1729](https://github.com/gin-gonic/gin/pull/1729)
+- [FIX] Change color methods to public in the defaultLogger. [#1771](https://github.com/gin-gonic/gin/pull/1771)
+- [FIX] Update writeHeaders method to use http.Header.Set [#1722](https://github.com/gin-gonic/gin/pull/1722)
+- [NEW] Add response size to LogFormatterParams [#1752](https://github.com/gin-gonic/gin/pull/1752)
+- [NEW] Allow ignoring field on form mapping [#1733](https://github.com/gin-gonic/gin/pull/1733)
+- [NEW] Add a function to force color in console output. [#1724](https://github.com/gin-gonic/gin/pull/1724)
+- [FIX] Context.Next() - recheck len of handlers on every iteration. [#1745](https://github.com/gin-gonic/gin/pull/1745)
+- [FIX] Fix all errcheck warnings [#1739](https://github.com/gin-gonic/gin/pull/1739) [#1653](https://github.com/gin-gonic/gin/pull/1653)
+- [NEW] context: inherits context cancellation and deadline from http.Request context for Go>=1.7 [#1690](https://github.com/gin-gonic/gin/pull/1690)
+- [NEW] Binding for URL Params [#1694](https://github.com/gin-gonic/gin/pull/1694)
+- [NEW] Add LoggerWithFormatter method [#1677](https://github.com/gin-gonic/gin/pull/1677)
+- [FIX] CI testing updates [#1671](https://github.com/gin-gonic/gin/pull/1671) [#1670](https://github.com/gin-gonic/gin/pull/1670) [#1682](https://github.com/gin-gonic/gin/pull/1682) [#1669](https://github.com/gin-gonic/gin/pull/1669)
+- [FIX] StaticFS(): Send 404 when path does not exist [#1663](https://github.com/gin-gonic/gin/pull/1663)
+- [FIX] Handle nil body for JSON binding [#1638](https://github.com/gin-gonic/gin/pull/1638)
+- [FIX] Support bind uri param [#1612](https://github.com/gin-gonic/gin/pull/1612)
+- [FIX] recovery: fix issue with syscall import on google app engine [#1640](https://github.com/gin-gonic/gin/pull/1640)
+- [FIX] Make sure the debug log contains line breaks [#1650](https://github.com/gin-gonic/gin/pull/1650)
+- [FIX] Panic stack trace being printed during recovery of broken pipe [#1089](https://github.com/gin-gonic/gin/pull/1089) [#1259](https://github.com/gin-gonic/gin/pull/1259)
+- [NEW] RunFd method to run http.Server through a file descriptor [#1609](https://github.com/gin-gonic/gin/pull/1609)
+- [NEW] Yaml binding support [#1618](https://github.com/gin-gonic/gin/pull/1618)
+- [FIX] Pass MaxMultipartMemory when FormFile is called [#1600](https://github.com/gin-gonic/gin/pull/1600)
+- [FIX] LoadHTML* tests [#1559](https://github.com/gin-gonic/gin/pull/1559)
+- [FIX] Removed use of sync.pool from HandleContext [#1565](https://github.com/gin-gonic/gin/pull/1565)
+- [FIX] Format output log to os.Stderr [#1571](https://github.com/gin-gonic/gin/pull/1571)
+- [FIX] Make logger use a yellow background and a darkgray text for legibility [#1570](https://github.com/gin-gonic/gin/pull/1570)
+- [FIX] Remove sensitive request information from panic log. [#1370](https://github.com/gin-gonic/gin/pull/1370)
+- [FIX] log.Println() does not print timestamp [#829](https://github.com/gin-gonic/gin/pull/829) [#1560](https://github.com/gin-gonic/gin/pull/1560)
+- [NEW] Add PureJSON renderer [#694](https://github.com/gin-gonic/gin/pull/694)
+- [FIX] Add missing copyright and update if/else [#1497](https://github.com/gin-gonic/gin/pull/1497)
+- [FIX] Update msgpack usage [#1498](https://github.com/gin-gonic/gin/pull/1498)
+- [FIX] Use protobuf on render [#1496](https://github.com/gin-gonic/gin/pull/1496)
+- [FIX] Add support for Protobuf format response [#1479](https://github.com/gin-gonic/gin/pull/1479)
+- [NEW] Set default time format in form binding [#1487](https://github.com/gin-gonic/gin/pull/1487)
+- [FIX] Add BindXML and ShouldBindXML [#1485](https://github.com/gin-gonic/gin/pull/1485)
+- [NEW] Upgrade dependency libraries [#1491](https://github.com/gin-gonic/gin/pull/1491)
+
+
+## Gin v1.3.0
+
+- [NEW] Add [`func (*Context) QueryMap`](https://godoc.org/github.com/gin-gonic/gin#Context.QueryMap), [`func (*Context) GetQueryMap`](https://godoc.org/github.com/gin-gonic/gin#Context.GetQueryMap), [`func (*Context) PostFormMap`](https://godoc.org/github.com/gin-gonic/gin#Context.PostFormMap) and [`func (*Context) GetPostFormMap`](https://godoc.org/github.com/gin-gonic/gin#Context.GetPostFormMap) to support `type map[string]string` as query string or form parameters, see [#1383](https://github.com/gin-gonic/gin/pull/1383)
+- [NEW] Add [`func (*Context) AsciiJSON`](https://godoc.org/github.com/gin-gonic/gin#Context.AsciiJSON), see [#1358](https://github.com/gin-gonic/gin/pull/1358)
+- [NEW] Add `Pusher()` in [`type ResponseWriter`](https://godoc.org/github.com/gin-gonic/gin#ResponseWriter) for supporting http2 push, see [#1273](https://github.com/gin-gonic/gin/pull/1273)
+- [NEW] Add [`func (*Context) DataFromReader`](https://godoc.org/github.com/gin-gonic/gin#Context.DataFromReader) for serving dynamic data, see [#1304](https://github.com/gin-gonic/gin/pull/1304)
+- [NEW] Add [`func (*Context) ShouldBindBodyWith`](https://godoc.org/github.com/gin-gonic/gin#Context.ShouldBindBodyWith) allowing to call binding multiple times, see [#1341](https://github.com/gin-gonic/gin/pull/1341)
+- [NEW] Support pointers in form binding, see [#1336](https://github.com/gin-gonic/gin/pull/1336)
+- [NEW] Add [`func (*Context) JSONP`](https://godoc.org/github.com/gin-gonic/gin#Context.JSONP), see [#1333](https://github.com/gin-gonic/gin/pull/1333)
+- [NEW] Support default value in form binding, see [#1138](https://github.com/gin-gonic/gin/pull/1138)
+- [NEW] Expose validator engine in [`type StructValidator`](https://godoc.org/github.com/gin-gonic/gin/binding#StructValidator), see [#1277](https://github.com/gin-gonic/gin/pull/1277)
+- [NEW] Add [`func (*Context) ShouldBind`](https://godoc.org/github.com/gin-gonic/gin#Context.ShouldBind), [`func (*Context) ShouldBindQuery`](https://godoc.org/github.com/gin-gonic/gin#Context.ShouldBindQuery) and [`func (*Context) ShouldBindJSON`](https://godoc.org/github.com/gin-gonic/gin#Context.ShouldBindJSON), see [#1047](https://github.com/gin-gonic/gin/pull/1047)
+- [NEW] Add support for `time.Time` location in form binding, see [#1117](https://github.com/gin-gonic/gin/pull/1117)
+- [NEW] Add [`func (*Context) BindQuery`](https://godoc.org/github.com/gin-gonic/gin#Context.BindQuery), see [#1029](https://github.com/gin-gonic/gin/pull/1029)
+- [NEW] Make [jsonite](https://github.com/json-iterator/go) optional with build tags, see [#1026](https://github.com/gin-gonic/gin/pull/1026)
+- [NEW] Show query string in logger, see [#999](https://github.com/gin-gonic/gin/pull/999)
+- [NEW] Add [`func (*Context) SecureJSON`](https://godoc.org/github.com/gin-gonic/gin#Context.SecureJSON), see [#987](https://github.com/gin-gonic/gin/pull/987) and [#993](https://github.com/gin-gonic/gin/pull/993)
+- [DEPRECATE] `func (*Context) GetCookie` for [`func (*Context) Cookie`](https://godoc.org/github.com/gin-gonic/gin#Context.Cookie)
+- [FIX] Don't display color tags if [`func DisableConsoleColor`](https://godoc.org/github.com/gin-gonic/gin#DisableConsoleColor) called, see [#1072](https://github.com/gin-gonic/gin/pull/1072)
+- [FIX] Gin Mode `""` when calling [`func Mode`](https://godoc.org/github.com/gin-gonic/gin#Mode) now returns `const DebugMode`, see [#1250](https://github.com/gin-gonic/gin/pull/1250)
+- [FIX] `Flush()` now doesn't overwrite `responseWriter` status code, see [#1460](https://github.com/gin-gonic/gin/pull/1460)
+
+## Gin 1.2.0
+
+- [NEW] Switch from godeps to govendor
+- [NEW] Add support for Let's Encrypt via gin-gonic/autotls
+- [NEW] Improve README examples and add extra at examples folder
+- [NEW] Improved support with App Engine
+- [NEW] Add custom template delimiters, see #860
+- [NEW] Add Template Func Maps, see #962
+- [NEW] Add \*context.Handler(), see #928
+- [NEW] Add \*context.GetRawData()
+- [NEW] Add \*context.GetHeader() (request)
+- [NEW] Add \*context.AbortWithStatusJSON() (JSON content type)
+- [NEW] Add \*context.Keys type cast helpers
+- [NEW] Add \*context.ShouldBindWith()
+- [NEW] Add \*context.MustBindWith()
+- [NEW] Add \*engine.SetFuncMap()
+- [DEPRECATE] On next release: \*context.BindWith(), see #855
+- [FIX] Refactor render
+- [FIX] Reworked tests
+- [FIX] logger now supports cygwin
+- [FIX] Use X-Forwarded-For before X-Real-Ip
+- [FIX] time.Time binding (#904)
+
+## Gin 1.1.4
+
+- [NEW] Support google appengine for IsTerminal func
+
+## Gin 1.1.3
+
+- [FIX] Reverted Logger: skip ANSI color commands
+
+## Gin 1.1
+
+- [NEW] Implement QueryArray and PostArray methods
+- [NEW] Refactor GetQuery and GetPostForm
+- [NEW] Add contribution guide
+- [FIX] Corrected typos in README
+- [FIX] Removed additional Iota
+- [FIX] Changed imports to gopkg instead of github in README (#733)
+- [FIX] Logger: skip ANSI color commands if output is not a tty
+
+## Gin 1.0rc2 (...)
+
+- [PERFORMANCE] Fast path for writing Content-Type.
+- [PERFORMANCE] Much faster 404 routing
+- [PERFORMANCE] Allocation optimizations
+- [PERFORMANCE] Faster root tree lookup
+- [PERFORMANCE] Zero overhead, String() and JSON() rendering.
+- [PERFORMANCE] Faster ClientIP parsing
+- [PERFORMANCE] Much faster SSE implementation
+- [NEW] Benchmarks suite
+- [NEW] Bind validation can be disabled and replaced with custom validators.
+- [NEW] More flexible HTML render
+- [NEW] Multipart and PostForm bindings
+- [NEW] Adds method to return all the registered routes
+- [NEW] Context.HandlerName() returns the main handler's name
+- [NEW] Adds Error.IsType() helper
+- [FIX] Binding multipart form
+- [FIX] Integration tests
+- [FIX] Crash when binding non struct object in Context.
+- [FIX] RunTLS() implementation
+- [FIX] Logger() unit tests
+- [FIX] Adds SetHTMLTemplate() warning
+- [FIX] Context.IsAborted()
+- [FIX] More unit tests
+- [FIX] JSON, XML, HTML renders accept custom content-types
+- [FIX] gin.AbortIndex is unexported
+- [FIX] Better approach to avoid directory listing in StaticFS()
+- [FIX] Context.ClientIP() always returns the IP with trimmed spaces.
+- [FIX] Better warning when running in debug mode.
+- [FIX] Google App Engine integration. debugPrint does not use os.Stdout
+- [FIX] Fixes integer overflow in error type
+- [FIX] Error implements the json.Marshaller interface
+- [FIX] MIT license in every file
+
+
+## Gin 1.0rc1 (May 22, 2015)
+
+- [PERFORMANCE] Zero allocation router
+- [PERFORMANCE] Faster JSON, XML and text rendering
+- [PERFORMANCE] Custom hand optimized HttpRouter for Gin
+- [PERFORMANCE] Misc code optimizations. Inlining, tail call optimizations
+- [NEW] Built-in support for golang.org/x/net/context
+- [NEW] Any(path, handler). Create a route that matches any path
+- [NEW] Refactored rendering pipeline (faster and static typed)
+- [NEW] Refactored errors API
+- [NEW] IndentedJSON() prints pretty JSON
+- [NEW] Added gin.DefaultWriter
+- [NEW] UNIX socket support
+- [NEW] RouterGroup.BasePath is exposed
+- [NEW] JSON validation using go-validate-yourself (very powerful options)
+- [NEW] Completed suite of unit tests
+- [NEW] HTTP streaming with c.Stream()
+- [NEW] StaticFile() creates a router for serving just one file.
+- [NEW] StaticFS() has an option to disable directory listing.
+- [NEW] StaticFS() for serving static files through virtual filesystems
+- [NEW] Server-Sent Events native support
+- [NEW] WrapF() and WrapH() helpers for wrapping http.HandlerFunc and http.Handler
+- [NEW] Added LoggerWithWriter() middleware
+- [NEW] Added RecoveryWithWriter() middleware
+- [NEW] Added DefaultPostFormValue()
+- [NEW] Added DefaultFormValue()
+- [NEW] Added DefaultParamValue()
+- [FIX] BasicAuth() when using custom realm
+- [FIX] Bug when serving static files in nested routing group
+- [FIX] Redirect using built-in http.Redirect()
+- [FIX] Logger when printing the requested path
+- [FIX] Documentation typos
+- [FIX] Context.Engine renamed to Context.engine
+- [FIX] Better debugging messages
+- [FIX] ErrorLogger
+- [FIX] Debug HTTP render
+- [FIX] Refactored binding and render modules
+- [FIX] Refactored Context initialization
+- [FIX] Refactored BasicAuth()
+- [FIX] NoMethod/NoRoute handlers
+- [FIX] Hijacking http
+- [FIX] Better support for Google App Engine (using log instead of fmt)
+
+
+## Gin 0.6 (Mar 9, 2015)
+
+- [NEW] Support multipart/form-data
+- [NEW] NoMethod handler
+- [NEW] Validate sub structures
+- [NEW] Support for HTTP Realm Auth
+- [FIX] Unsigned integers in binding
+- [FIX] Improve color logger
+
+
+## Gin 0.5 (Feb 7, 2015)
+
+- [NEW] Content Negotiation
+- [FIX] Solved security bug that allow a client to spoof ip
+- [FIX] Fix unexported/ignored fields in binding
+
+
+## Gin 0.4 (Aug 21, 2014)
+
+- [NEW] Development mode
+- [NEW] Unit tests
+- [NEW] Add Content.Redirect()
+- [FIX] Deferring WriteHeader()
+- [FIX] Improved documentation for model binding
+
+
+## Gin 0.3 (Jul 18, 2014)
+
+- [PERFORMANCE] Normal log and error log are printed in the same call.
+- [PERFORMANCE] Improve performance of NoRouter()
+- [PERFORMANCE] Improve context's memory locality, reduce CPU cache faults.
+- [NEW] Flexible rendering API
+- [NEW] Add Context.File()
+- [NEW] Add shortcut RunTLS() for http.ListenAndServeTLS
+- [FIX] Rename NotFound404() to NoRoute()
+- [FIX] Errors in context are purged
+- [FIX] Adds HEAD method in Static file serving
+- [FIX] Refactors Static() file serving
+- [FIX] Using keyed initialization to fix app-engine integration
+- [FIX] Can't unmarshal JSON array, #63
+- [FIX] Renaming Context.Req to Context.Request
+- [FIX] Check application/x-www-form-urlencoded when parsing form
+
+
+## Gin 0.2b (Jul 08, 2014)
+- [PERFORMANCE] Using sync.Pool to allocatio/gc overhead
+- [NEW] Travis CI integration
+- [NEW] Completely new logger
+- [NEW] New API for serving static files. gin.Static()
+- [NEW] gin.H() can be serialized into XML
+- [NEW] Typed errors. Errors can be typed. Internet/external/custom.
+- [NEW] Support for Godeps
+- [NEW] Travis/Godocs badges in README
+- [NEW] New Bind() and BindWith() methods for parsing request body.
+- [NEW] Add Content.Copy()
+- [NEW] Add context.LastError()
+- [NEW] Add shortcut for OPTIONS HTTP method
+- [FIX] Tons of README fixes
+- [FIX] Header is written before body
+- [FIX] BasicAuth() and changes API a little bit
+- [FIX] Recovery() middleware only prints panics
+- [FIX] Context.Get() does not panic anymore. Use MustGet() instead.
+- [FIX] Multiple http.WriteHeader() in NotFound handlers
+- [FIX] Engine.Run() panics if http server can't be set up
+- [FIX] Crash when route path doesn't start with '/'
+- [FIX] Do not update header when status code is negative
+- [FIX] Setting response headers before calling WriteHeader in context.String()
+- [FIX] Add MIT license
+- [FIX] Changes behaviour of ErrorLogger() and Logger()
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/CODE_OF_CONDUCT.md b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/CODE_OF_CONDUCT.md
new file mode 100644
index 000000000000..4ea14f3955ce
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/CODE_OF_CONDUCT.md
@@ -0,0 +1,46 @@
+# Contributor Covenant Code of Conduct
+
+## Our Pledge
+
+In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
+
+## Our Standards
+
+Examples of behavior that contributes to creating a positive environment include:
+
+* Using welcoming and inclusive language
+* Being respectful of differing viewpoints and experiences
+* Gracefully accepting constructive criticism
+* Focusing on what is best for the community
+* Showing empathy towards other community members
+
+Examples of unacceptable behavior by participants include:
+
+* The use of sexualized language or imagery and unwelcome sexual attention or advances
+* Trolling, insulting/derogatory comments, and personal or political attacks
+* Public or private harassment
+* Publishing others' private information, such as a physical or electronic address, without explicit permission
+* Other conduct which could reasonably be considered inappropriate in a professional setting
+
+## Our Responsibilities
+
+Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
+
+Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
+
+## Scope
+
+This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
+
+## Enforcement
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at teamgingonic@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
+
+Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
+
+## Attribution
+
+This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
+
+[homepage]: http://contributor-covenant.org
+[version]: http://contributor-covenant.org/version/1/4/
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/CONTRIBUTING.md b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/CONTRIBUTING.md
new file mode 100644
index 000000000000..97daa808f0f4
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/CONTRIBUTING.md
@@ -0,0 +1,13 @@
+## Contributing
+
+- With issues:
+ - Use the search tool before opening a new issue.
+ - Please provide source code and commit sha if you found a bug.
+ - Review existing issues and provide feedback or react to them.
+
+- With pull requests:
+ - Open your pull request against `master`
+ - Your pull request should have no more than two commits, if not you should squash them.
+ - It should pass all tests in the available continuous integration systems such as TravisCI.
+ - You should add/modify tests to cover your proposed code changes.
+ - If your pull request contains a new feature, please document it on the README.
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/LICENSE b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/LICENSE
new file mode 100644
index 000000000000..1ff7f3706055
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2014 Manuel MartÃnez-Almeida
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/Makefile b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/Makefile
new file mode 100644
index 000000000000..1a99193959ea
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/Makefile
@@ -0,0 +1,71 @@
+GO ?= go
+GOFMT ?= gofmt "-s"
+PACKAGES ?= $(shell $(GO) list ./...)
+VETPACKAGES ?= $(shell $(GO) list ./... | grep -v /examples/)
+GOFILES := $(shell find . -name "*.go")
+TESTFOLDER := $(shell $(GO) list ./... | grep -E 'gin$$|binding$$|render$$' | grep -v examples)
+TESTTAGS ?= ""
+
+.PHONY: test
+test:
+ echo "mode: count" > coverage.out
+ for d in $(TESTFOLDER); do \
+ $(GO) test -tags $(TESTTAGS) -v -covermode=count -coverprofile=profile.out $$d > tmp.out; \
+ cat tmp.out; \
+ if grep -q "^--- FAIL" tmp.out; then \
+ rm tmp.out; \
+ exit 1; \
+ elif grep -q "build failed" tmp.out; then \
+ rm tmp.out; \
+ exit 1; \
+ elif grep -q "setup failed" tmp.out; then \
+ rm tmp.out; \
+ exit 1; \
+ fi; \
+ if [ -f profile.out ]; then \
+ cat profile.out | grep -v "mode:" >> coverage.out; \
+ rm profile.out; \
+ fi; \
+ done
+
+.PHONY: fmt
+fmt:
+ $(GOFMT) -w $(GOFILES)
+
+.PHONY: fmt-check
+fmt-check:
+ @diff=$$($(GOFMT) -d $(GOFILES)); \
+ if [ -n "$$diff" ]; then \
+ echo "Please run 'make fmt' and commit the result:"; \
+ echo "$${diff}"; \
+ exit 1; \
+ fi;
+
+vet:
+ $(GO) vet $(VETPACKAGES)
+
+.PHONY: lint
+lint:
+ @hash golint > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
+ $(GO) get -u golang.org/x/lint/golint; \
+ fi
+ for PKG in $(PACKAGES); do golint -set_exit_status $$PKG || exit 1; done;
+
+.PHONY: misspell-check
+misspell-check:
+ @hash misspell > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
+ $(GO) get -u github.com/client9/misspell/cmd/misspell; \
+ fi
+ misspell -error $(GOFILES)
+
+.PHONY: misspell
+misspell:
+ @hash misspell > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
+ $(GO) get -u github.com/client9/misspell/cmd/misspell; \
+ fi
+ misspell -w $(GOFILES)
+
+.PHONY: tools
+tools:
+ go install golang.org/x/lint/golint; \
+ go install github.com/client9/misspell/cmd/misspell;
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/README.md b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/README.md
new file mode 100644
index 000000000000..9bf459b09ffd
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/README.md
@@ -0,0 +1,2252 @@
+# Gin Web Framework
+
+
+
+[](https://travis-ci.org/gin-gonic/gin)
+[](https://codecov.io/gh/gin-gonic/gin)
+[](https://goreportcard.com/report/github.com/gin-gonic/gin)
+[](https://pkg.go.dev/github.com/gin-gonic/gin?tab=doc)
+[](https://gitter.im/gin-gonic/gin?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
+[](https://sourcegraph.com/github.com/gin-gonic/gin?badge)
+[](https://www.codetriage.com/gin-gonic/gin)
+[](https://github.com/gin-gonic/gin/releases)
+[](https://www.tickgit.com/browse?repo=github.com/gin-gonic/gin)
+
+Gin is a web framework written in Go (Golang). It features a martini-like API with performance that is up to 40 times faster thanks to [httprouter](https://github.com/julienschmidt/httprouter). If you need performance and good productivity, you will love Gin.
+
+
+## Contents
+
+- [Gin Web Framework](#gin-web-framework)
+ - [Contents](#contents)
+ - [Installation](#installation)
+ - [Quick start](#quick-start)
+ - [Benchmarks](#benchmarks)
+ - [Gin v1. stable](#gin-v1-stable)
+ - [Build with jsoniter](#build-with-jsoniter)
+ - [API Examples](#api-examples)
+ - [Using GET, POST, PUT, PATCH, DELETE and OPTIONS](#using-get-post-put-patch-delete-and-options)
+ - [Parameters in path](#parameters-in-path)
+ - [Querystring parameters](#querystring-parameters)
+ - [Multipart/Urlencoded Form](#multiparturlencoded-form)
+ - [Another example: query + post form](#another-example-query--post-form)
+ - [Map as querystring or postform parameters](#map-as-querystring-or-postform-parameters)
+ - [Upload files](#upload-files)
+ - [Single file](#single-file)
+ - [Multiple files](#multiple-files)
+ - [Grouping routes](#grouping-routes)
+ - [Blank Gin without middleware by default](#blank-gin-without-middleware-by-default)
+ - [Using middleware](#using-middleware)
+ - [How to write log file](#how-to-write-log-file)
+ - [Custom Log Format](#custom-log-format)
+ - [Controlling Log output coloring](#controlling-log-output-coloring)
+ - [Model binding and validation](#model-binding-and-validation)
+ - [Custom Validators](#custom-validators)
+ - [Only Bind Query String](#only-bind-query-string)
+ - [Bind Query String or Post Data](#bind-query-string-or-post-data)
+ - [Bind Uri](#bind-uri)
+ - [Bind Header](#bind-header)
+ - [Bind HTML checkboxes](#bind-html-checkboxes)
+ - [Multipart/Urlencoded binding](#multiparturlencoded-binding)
+ - [XML, JSON, YAML and ProtoBuf rendering](#xml-json-yaml-and-protobuf-rendering)
+ - [SecureJSON](#securejson)
+ - [JSONP](#jsonp)
+ - [AsciiJSON](#asciijson)
+ - [PureJSON](#purejson)
+ - [Serving static files](#serving-static-files)
+ - [Serving data from file](#serving-data-from-file)
+ - [Serving data from reader](#serving-data-from-reader)
+ - [HTML rendering](#html-rendering)
+ - [Custom Template renderer](#custom-template-renderer)
+ - [Custom Delimiters](#custom-delimiters)
+ - [Custom Template Funcs](#custom-template-funcs)
+ - [Multitemplate](#multitemplate)
+ - [Redirects](#redirects)
+ - [Custom Middleware](#custom-middleware)
+ - [Using BasicAuth() middleware](#using-basicauth-middleware)
+ - [Goroutines inside a middleware](#goroutines-inside-a-middleware)
+ - [Custom HTTP configuration](#custom-http-configuration)
+ - [Support Let's Encrypt](#support-lets-encrypt)
+ - [Run multiple service using Gin](#run-multiple-service-using-gin)
+ - [Graceful shutdown or restart](#graceful-shutdown-or-restart)
+ - [Third-party packages](#third-party-packages)
+ - [Manually](#manually)
+ - [Build a single binary with templates](#build-a-single-binary-with-templates)
+ - [Bind form-data request with custom struct](#bind-form-data-request-with-custom-struct)
+ - [Try to bind body into different structs](#try-to-bind-body-into-different-structs)
+ - [http2 server push](#http2-server-push)
+ - [Define format for the log of routes](#define-format-for-the-log-of-routes)
+ - [Set and get a cookie](#set-and-get-a-cookie)
+ - [Don't trust all proxies](#don't-trust-all-proxies)
+ - [Testing](#testing)
+ - [Users](#users)
+
+## Installation
+
+To install Gin package, you need to install Go and set your Go workspace first.
+
+1. The first need [Go](https://golang.org/) installed (**version 1.13+ is required**), then you can use the below Go command to install Gin.
+
+```sh
+$ go get -u github.com/gin-gonic/gin
+```
+
+2. Import it in your code:
+
+```go
+import "github.com/gin-gonic/gin"
+```
+
+3. (Optional) Import `net/http`. This is required for example if using constants such as `http.StatusOK`.
+
+```go
+import "net/http"
+```
+
+## Quick start
+
+```sh
+# assume the following codes in example.go file
+$ cat example.go
+```
+
+```go
+package main
+
+import "github.com/gin-gonic/gin"
+
+func main() {
+ r := gin.Default()
+ r.GET("/ping", func(c *gin.Context) {
+ c.JSON(200, gin.H{
+ "message": "pong",
+ })
+ })
+ r.Run() // listen and serve on 0.0.0.0:8080 (for windows "localhost:8080")
+}
+```
+
+```
+# run example.go and visit 0.0.0.0:8080/ping (for windows "localhost:8080/ping") on browser
+$ go run example.go
+```
+
+## Benchmarks
+
+Gin uses a custom version of [HttpRouter](https://github.com/julienschmidt/httprouter)
+
+[See all benchmarks](/BENCHMARKS.md)
+
+| Benchmark name | (1) | (2) | (3) | (4) |
+| ------------------------------ | ---------:| ---------------:| ------------:| ---------------:|
+| BenchmarkGin_GithubAll | **43550** | **27364 ns/op** | **0 B/op** | **0 allocs/op** |
+| BenchmarkAce_GithubAll | 40543 | 29670 ns/op | 0 B/op | 0 allocs/op |
+| BenchmarkAero_GithubAll | 57632 | 20648 ns/op | 0 B/op | 0 allocs/op |
+| BenchmarkBear_GithubAll | 9234 | 216179 ns/op | 86448 B/op | 943 allocs/op |
+| BenchmarkBeego_GithubAll | 7407 | 243496 ns/op | 71456 B/op | 609 allocs/op |
+| BenchmarkBone_GithubAll | 420 | 2922835 ns/op | 720160 B/op | 8620 allocs/op |
+| BenchmarkChi_GithubAll | 7620 | 238331 ns/op | 87696 B/op | 609 allocs/op |
+| BenchmarkDenco_GithubAll | 18355 | 64494 ns/op | 20224 B/op | 167 allocs/op |
+| BenchmarkEcho_GithubAll | 31251 | 38479 ns/op | 0 B/op | 0 allocs/op |
+| BenchmarkGocraftWeb_GithubAll | 4117 | 300062 ns/op | 131656 B/op | 1686 allocs/op |
+| BenchmarkGoji_GithubAll | 3274 | 416158 ns/op | 56112 B/op | 334 allocs/op |
+| BenchmarkGojiv2_GithubAll | 1402 | 870518 ns/op | 352720 B/op | 4321 allocs/op |
+| BenchmarkGoJsonRest_GithubAll | 2976 | 401507 ns/op | 134371 B/op | 2737 allocs/op |
+| BenchmarkGoRestful_GithubAll | 410 | 2913158 ns/op | 910144 B/op | 2938 allocs/op |
+| BenchmarkGorillaMux_GithubAll | 346 | 3384987 ns/op | 251650 B/op | 1994 allocs/op |
+| BenchmarkGowwwRouter_GithubAll | 10000 | 143025 ns/op | 72144 B/op | 501 allocs/op |
+| BenchmarkHttpRouter_GithubAll | 55938 | 21360 ns/op | 0 B/op | 0 allocs/op |
+| BenchmarkHttpTreeMux_GithubAll | 10000 | 153944 ns/op | 65856 B/op | 671 allocs/op |
+| BenchmarkKocha_GithubAll | 10000 | 106315 ns/op | 23304 B/op | 843 allocs/op |
+| BenchmarkLARS_GithubAll | 47779 | 25084 ns/op | 0 B/op | 0 allocs/op |
+| BenchmarkMacaron_GithubAll | 3266 | 371907 ns/op | 149409 B/op | 1624 allocs/op |
+| BenchmarkMartini_GithubAll | 331 | 3444706 ns/op | 226551 B/op | 2325 allocs/op |
+| BenchmarkPat_GithubAll | 273 | 4381818 ns/op | 1483152 B/op | 26963 allocs/op |
+| BenchmarkPossum_GithubAll | 10000 | 164367 ns/op | 84448 B/op | 609 allocs/op |
+| BenchmarkR2router_GithubAll | 10000 | 160220 ns/op | 77328 B/op | 979 allocs/op |
+| BenchmarkRivet_GithubAll | 14625 | 82453 ns/op | 16272 B/op | 167 allocs/op |
+| BenchmarkTango_GithubAll | 6255 | 279611 ns/op | 63826 B/op | 1618 allocs/op |
+| BenchmarkTigerTonic_GithubAll | 2008 | 687874 ns/op | 193856 B/op | 4474 allocs/op |
+| BenchmarkTraffic_GithubAll | 355 | 3478508 ns/op | 820744 B/op | 14114 allocs/op |
+| BenchmarkVulcan_GithubAll | 6885 | 193333 ns/op | 19894 B/op | 609 allocs/op |
+
+- (1): Total Repetitions achieved in constant time, higher means more confident result
+- (2): Single Repetition Duration (ns/op), lower is better
+- (3): Heap Memory (B/op), lower is better
+- (4): Average Allocations per Repetition (allocs/op), lower is better
+
+## Gin v1. stable
+
+- [x] Zero allocation router.
+- [x] Still the fastest http router and framework. From routing to writing.
+- [x] Complete suite of unit tests.
+- [x] Battle tested.
+- [x] API frozen, new releases will not break your code.
+
+## Build with [jsoniter](https://github.com/json-iterator/go)
+
+Gin uses `encoding/json` as default json package but you can change to [jsoniter](https://github.com/json-iterator/go) by build from other tags.
+
+```sh
+$ go build -tags=jsoniter .
+```
+
+## API Examples
+
+You can find a number of ready-to-run examples at [Gin examples repository](https://github.com/gin-gonic/examples).
+
+### Using GET, POST, PUT, PATCH, DELETE and OPTIONS
+
+```go
+func main() {
+ // Creates a gin router with default middleware:
+ // logger and recovery (crash-free) middleware
+ router := gin.Default()
+
+ router.GET("/someGet", getting)
+ router.POST("/somePost", posting)
+ router.PUT("/somePut", putting)
+ router.DELETE("/someDelete", deleting)
+ router.PATCH("/somePatch", patching)
+ router.HEAD("/someHead", head)
+ router.OPTIONS("/someOptions", options)
+
+ // By default it serves on :8080 unless a
+ // PORT environment variable was defined.
+ router.Run()
+ // router.Run(":3000") for a hard coded port
+}
+```
+
+### Parameters in path
+
+```go
+func main() {
+ router := gin.Default()
+
+ // This handler will match /user/john but will not match /user/ or /user
+ router.GET("/user/:name", func(c *gin.Context) {
+ name := c.Param("name")
+ c.String(http.StatusOK, "Hello %s", name)
+ })
+
+ // However, this one will match /user/john/ and also /user/john/send
+ // If no other routers match /user/john, it will redirect to /user/john/
+ router.GET("/user/:name/*action", func(c *gin.Context) {
+ name := c.Param("name")
+ action := c.Param("action")
+ message := name + " is " + action
+ c.String(http.StatusOK, message)
+ })
+
+ // For each matched request Context will hold the route definition
+ router.POST("/user/:name/*action", func(c *gin.Context) {
+ c.FullPath() == "/user/:name/*action" // true
+ })
+
+ // This handler will add a new router for /user/groups.
+ // Exact routes are resolved before param routes, regardless of the order they were defined.
+ // Routes starting with /user/groups are never interpreted as /user/:name/... routes
+ router.GET("/user/groups", func(c *gin.Context) {
+ c.String(http.StatusOK, "The available groups are [...]", name)
+ })
+
+ router.Run(":8080")
+}
+```
+
+### Querystring parameters
+
+```go
+func main() {
+ router := gin.Default()
+
+ // Query string parameters are parsed using the existing underlying request object.
+ // The request responds to a url matching: /welcome?firstname=Jane&lastname=Doe
+ router.GET("/welcome", func(c *gin.Context) {
+ firstname := c.DefaultQuery("firstname", "Guest")
+ lastname := c.Query("lastname") // shortcut for c.Request.URL.Query().Get("lastname")
+
+ c.String(http.StatusOK, "Hello %s %s", firstname, lastname)
+ })
+ router.Run(":8080")
+}
+```
+
+### Multipart/Urlencoded Form
+
+```go
+func main() {
+ router := gin.Default()
+
+ router.POST("/form_post", func(c *gin.Context) {
+ message := c.PostForm("message")
+ nick := c.DefaultPostForm("nick", "anonymous")
+
+ c.JSON(200, gin.H{
+ "status": "posted",
+ "message": message,
+ "nick": nick,
+ })
+ })
+ router.Run(":8080")
+}
+```
+
+### Another example: query + post form
+
+```
+POST /post?id=1234&page=1 HTTP/1.1
+Content-Type: application/x-www-form-urlencoded
+
+name=manu&message=this_is_great
+```
+
+```go
+func main() {
+ router := gin.Default()
+
+ router.POST("/post", func(c *gin.Context) {
+
+ id := c.Query("id")
+ page := c.DefaultQuery("page", "0")
+ name := c.PostForm("name")
+ message := c.PostForm("message")
+
+ fmt.Printf("id: %s; page: %s; name: %s; message: %s", id, page, name, message)
+ })
+ router.Run(":8080")
+}
+```
+
+```
+id: 1234; page: 1; name: manu; message: this_is_great
+```
+
+### Map as querystring or postform parameters
+
+```
+POST /post?ids[a]=1234&ids[b]=hello HTTP/1.1
+Content-Type: application/x-www-form-urlencoded
+
+names[first]=thinkerou&names[second]=tianou
+```
+
+```go
+func main() {
+ router := gin.Default()
+
+ router.POST("/post", func(c *gin.Context) {
+
+ ids := c.QueryMap("ids")
+ names := c.PostFormMap("names")
+
+ fmt.Printf("ids: %v; names: %v", ids, names)
+ })
+ router.Run(":8080")
+}
+```
+
+```
+ids: map[b:hello a:1234]; names: map[second:tianou first:thinkerou]
+```
+
+### Upload files
+
+#### Single file
+
+References issue [#774](https://github.com/gin-gonic/gin/issues/774) and detail [example code](https://github.com/gin-gonic/examples/tree/master/upload-file/single).
+
+`file.Filename` **SHOULD NOT** be trusted. See [`Content-Disposition` on MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition#Directives) and [#1693](https://github.com/gin-gonic/gin/issues/1693)
+
+> The filename is always optional and must not be used blindly by the application: path information should be stripped, and conversion to the server file system rules should be done.
+
+```go
+func main() {
+ router := gin.Default()
+ // Set a lower memory limit for multipart forms (default is 32 MiB)
+ router.MaxMultipartMemory = 8 << 20 // 8 MiB
+ router.POST("/upload", func(c *gin.Context) {
+ // single file
+ file, _ := c.FormFile("file")
+ log.Println(file.Filename)
+
+ // Upload the file to specific dst.
+ c.SaveUploadedFile(file, dst)
+
+ c.String(http.StatusOK, fmt.Sprintf("'%s' uploaded!", file.Filename))
+ })
+ router.Run(":8080")
+}
+```
+
+How to `curl`:
+
+```bash
+curl -X POST http://localhost:8080/upload \
+ -F "file=@/Users/appleboy/test.zip" \
+ -H "Content-Type: multipart/form-data"
+```
+
+#### Multiple files
+
+See the detail [example code](https://github.com/gin-gonic/examples/tree/master/upload-file/multiple).
+
+```go
+func main() {
+ router := gin.Default()
+ // Set a lower memory limit for multipart forms (default is 32 MiB)
+ router.MaxMultipartMemory = 8 << 20 // 8 MiB
+ router.POST("/upload", func(c *gin.Context) {
+ // Multipart form
+ form, _ := c.MultipartForm()
+ files := form.File["upload[]"]
+
+ for _, file := range files {
+ log.Println(file.Filename)
+
+ // Upload the file to specific dst.
+ c.SaveUploadedFile(file, dst)
+ }
+ c.String(http.StatusOK, fmt.Sprintf("%d files uploaded!", len(files)))
+ })
+ router.Run(":8080")
+}
+```
+
+How to `curl`:
+
+```bash
+curl -X POST http://localhost:8080/upload \
+ -F "upload[]=@/Users/appleboy/test1.zip" \
+ -F "upload[]=@/Users/appleboy/test2.zip" \
+ -H "Content-Type: multipart/form-data"
+```
+
+### Grouping routes
+
+```go
+func main() {
+ router := gin.Default()
+
+ // Simple group: v1
+ v1 := router.Group("/v1")
+ {
+ v1.POST("/login", loginEndpoint)
+ v1.POST("/submit", submitEndpoint)
+ v1.POST("/read", readEndpoint)
+ }
+
+ // Simple group: v2
+ v2 := router.Group("/v2")
+ {
+ v2.POST("/login", loginEndpoint)
+ v2.POST("/submit", submitEndpoint)
+ v2.POST("/read", readEndpoint)
+ }
+
+ router.Run(":8080")
+}
+```
+
+### Blank Gin without middleware by default
+
+Use
+
+```go
+r := gin.New()
+```
+
+instead of
+
+```go
+// Default With the Logger and Recovery middleware already attached
+r := gin.Default()
+```
+
+
+### Using middleware
+```go
+func main() {
+ // Creates a router without any middleware by default
+ r := gin.New()
+
+ // Global middleware
+ // Logger middleware will write the logs to gin.DefaultWriter even if you set with GIN_MODE=release.
+ // By default gin.DefaultWriter = os.Stdout
+ r.Use(gin.Logger())
+
+ // Recovery middleware recovers from any panics and writes a 500 if there was one.
+ r.Use(gin.Recovery())
+
+ // Per route middleware, you can add as many as you desire.
+ r.GET("/benchmark", MyBenchLogger(), benchEndpoint)
+
+ // Authorization group
+ // authorized := r.Group("/", AuthRequired())
+ // exactly the same as:
+ authorized := r.Group("/")
+ // per group middleware! in this case we use the custom created
+ // AuthRequired() middleware just in the "authorized" group.
+ authorized.Use(AuthRequired())
+ {
+ authorized.POST("/login", loginEndpoint)
+ authorized.POST("/submit", submitEndpoint)
+ authorized.POST("/read", readEndpoint)
+
+ // nested group
+ testing := authorized.Group("testing")
+ testing.GET("/analytics", analyticsEndpoint)
+ }
+
+ // Listen and serve on 0.0.0.0:8080
+ r.Run(":8080")
+}
+```
+
+### Custom Recovery behavior
+```go
+func main() {
+ // Creates a router without any middleware by default
+ r := gin.New()
+
+ // Global middleware
+ // Logger middleware will write the logs to gin.DefaultWriter even if you set with GIN_MODE=release.
+ // By default gin.DefaultWriter = os.Stdout
+ r.Use(gin.Logger())
+
+ // Recovery middleware recovers from any panics and writes a 500 if there was one.
+ r.Use(gin.CustomRecovery(func(c *gin.Context, recovered interface{}) {
+ if err, ok := recovered.(string); ok {
+ c.String(http.StatusInternalServerError, fmt.Sprintf("error: %s", err))
+ }
+ c.AbortWithStatus(http.StatusInternalServerError)
+ }))
+
+ r.GET("/panic", func(c *gin.Context) {
+ // panic with a string -- the custom middleware could save this to a database or report it to the user
+ panic("foo")
+ })
+
+ r.GET("/", func(c *gin.Context) {
+ c.String(http.StatusOK, "ohai")
+ })
+
+ // Listen and serve on 0.0.0.0:8080
+ r.Run(":8080")
+}
+```
+
+### How to write log file
+```go
+func main() {
+ // Disable Console Color, you don't need console color when writing the logs to file.
+ gin.DisableConsoleColor()
+
+ // Logging to a file.
+ f, _ := os.Create("gin.log")
+ gin.DefaultWriter = io.MultiWriter(f)
+
+ // Use the following code if you need to write the logs to file and console at the same time.
+ // gin.DefaultWriter = io.MultiWriter(f, os.Stdout)
+
+ router := gin.Default()
+ router.GET("/ping", func(c *gin.Context) {
+ c.String(200, "pong")
+ })
+
+ Â Â router.Run(":8080")
+}
+```
+
+### Custom Log Format
+```go
+func main() {
+ router := gin.New()
+
+ // LoggerWithFormatter middleware will write the logs to gin.DefaultWriter
+ // By default gin.DefaultWriter = os.Stdout
+ router.Use(gin.LoggerWithFormatter(func(param gin.LogFormatterParams) string {
+
+ // your custom format
+ return fmt.Sprintf("%s - [%s] \"%s %s %s %d %s \"%s\" %s\"\n",
+ param.ClientIP,
+ param.TimeStamp.Format(time.RFC1123),
+ param.Method,
+ param.Path,
+ param.Request.Proto,
+ param.StatusCode,
+ param.Latency,
+ param.Request.UserAgent(),
+ param.ErrorMessage,
+ )
+ }))
+ router.Use(gin.Recovery())
+
+ router.GET("/ping", func(c *gin.Context) {
+ c.String(200, "pong")
+ })
+
+ router.Run(":8080")
+}
+```
+
+**Sample Output**
+```
+::1 - [Fri, 07 Dec 2018 17:04:38 JST] "GET /ping HTTP/1.1 200 122.767µs "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.80 Safari/537.36" "
+```
+
+### Controlling Log output coloring
+
+By default, logs output on console should be colorized depending on the detected TTY.
+
+Never colorize logs:
+
+```go
+func main() {
+ // Disable log's color
+ gin.DisableConsoleColor()
+
+ // Creates a gin router with default middleware:
+ // logger and recovery (crash-free) middleware
+ router := gin.Default()
+
+ router.GET("/ping", func(c *gin.Context) {
+ c.String(200, "pong")
+ })
+
+ router.Run(":8080")
+}
+```
+
+Always colorize logs:
+
+```go
+func main() {
+ // Force log's color
+ gin.ForceConsoleColor()
+
+ // Creates a gin router with default middleware:
+ // logger and recovery (crash-free) middleware
+ router := gin.Default()
+
+ router.GET("/ping", func(c *gin.Context) {
+ c.String(200, "pong")
+ })
+
+ router.Run(":8080")
+}
+```
+
+### Model binding and validation
+
+To bind a request body into a type, use model binding. We currently support binding of JSON, XML, YAML and standard form values (foo=bar&boo=baz).
+
+Gin uses [**go-playground/validator/v10**](https://github.com/go-playground/validator) for validation. Check the full docs on tags usage [here](https://godoc.org/github.com/go-playground/validator#hdr-Baked_In_Validators_and_Tags).
+
+Note that you need to set the corresponding binding tag on all fields you want to bind. For example, when binding from JSON, set `json:"fieldname"`.
+
+Also, Gin provides two sets of methods for binding:
+- **Type** - Must bind
+ - **Methods** - `Bind`, `BindJSON`, `BindXML`, `BindQuery`, `BindYAML`, `BindHeader`
+ - **Behavior** - These methods use `MustBindWith` under the hood. If there is a binding error, the request is aborted with `c.AbortWithError(400, err).SetType(ErrorTypeBind)`. This sets the response status code to 400 and the `Content-Type` header is set to `text/plain; charset=utf-8`. Note that if you try to set the response code after this, it will result in a warning `[GIN-debug] [WARNING] Headers were already written. Wanted to override status code 400 with 422`. If you wish to have greater control over the behavior, consider using the `ShouldBind` equivalent method.
+- **Type** - Should bind
+ - **Methods** - `ShouldBind`, `ShouldBindJSON`, `ShouldBindXML`, `ShouldBindQuery`, `ShouldBindYAML`, `ShouldBindHeader`
+ - **Behavior** - These methods use `ShouldBindWith` under the hood. If there is a binding error, the error is returned and it is the developer's responsibility to handle the request and error appropriately.
+
+When using the Bind-method, Gin tries to infer the binder depending on the Content-Type header. If you are sure what you are binding, you can use `MustBindWith` or `ShouldBindWith`.
+
+You can also specify that specific fields are required. If a field is decorated with `binding:"required"` and has a empty value when binding, an error will be returned.
+
+```go
+// Binding from JSON
+type Login struct {
+ User string `form:"user" json:"user" xml:"user" binding:"required"`
+ Password string `form:"password" json:"password" xml:"password" binding:"required"`
+}
+
+func main() {
+ router := gin.Default()
+
+ // Example for binding JSON ({"user": "manu", "password": "123"})
+ router.POST("/loginJSON", func(c *gin.Context) {
+ var json Login
+ if err := c.ShouldBindJSON(&json); err != nil {
+ c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
+ return
+ }
+
+ if json.User != "manu" || json.Password != "123" {
+ c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"})
+ return
+ }
+
+ c.JSON(http.StatusOK, gin.H{"status": "you are logged in"})
+ })
+
+ // Example for binding XML (
+ //
+ //
+ // user
+ // 123
+ // )
+ router.POST("/loginXML", func(c *gin.Context) {
+ var xml Login
+ if err := c.ShouldBindXML(&xml); err != nil {
+ c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
+ return
+ }
+
+ if xml.User != "manu" || xml.Password != "123" {
+ c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"})
+ return
+ }
+
+ c.JSON(http.StatusOK, gin.H{"status": "you are logged in"})
+ })
+
+ // Example for binding a HTML form (user=manu&password=123)
+ router.POST("/loginForm", func(c *gin.Context) {
+ var form Login
+ // This will infer what binder to use depending on the content-type header.
+ if err := c.ShouldBind(&form); err != nil {
+ c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
+ return
+ }
+
+ if form.User != "manu" || form.Password != "123" {
+ c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"})
+ return
+ }
+
+ c.JSON(http.StatusOK, gin.H{"status": "you are logged in"})
+ })
+
+ // Listen and serve on 0.0.0.0:8080
+ router.Run(":8080")
+}
+```
+
+**Sample request**
+```shell
+$ curl -v -X POST \
+ http://localhost:8080/loginJSON \
+ -H 'content-type: application/json' \
+ -d '{ "user": "manu" }'
+> POST /loginJSON HTTP/1.1
+> Host: localhost:8080
+> User-Agent: curl/7.51.0
+> Accept: */*
+> content-type: application/json
+> Content-Length: 18
+>
+* upload completely sent off: 18 out of 18 bytes
+< HTTP/1.1 400 Bad Request
+< Content-Type: application/json; charset=utf-8
+< Date: Fri, 04 Aug 2017 03:51:31 GMT
+< Content-Length: 100
+<
+{"error":"Key: 'Login.Password' Error:Field validation for 'Password' failed on the 'required' tag"}
+```
+
+**Skip validate**
+
+When running the above example using the above the `curl` command, it returns error. Because the example use `binding:"required"` for `Password`. If use `binding:"-"` for `Password`, then it will not return error when running the above example again.
+
+### Custom Validators
+
+It is also possible to register custom validators. See the [example code](https://github.com/gin-gonic/examples/tree/master/custom-validation/server.go).
+
+```go
+package main
+
+import (
+ "net/http"
+ "time"
+
+ "github.com/gin-gonic/gin"
+ "github.com/gin-gonic/gin/binding"
+ "github.com/go-playground/validator/v10"
+)
+
+// Booking contains binded and validated data.
+type Booking struct {
+ CheckIn time.Time `form:"check_in" binding:"required,bookabledate" time_format:"2006-01-02"`
+ CheckOut time.Time `form:"check_out" binding:"required,gtfield=CheckIn" time_format:"2006-01-02"`
+}
+
+var bookableDate validator.Func = func(fl validator.FieldLevel) bool {
+ date, ok := fl.Field().Interface().(time.Time)
+ if ok {
+ today := time.Now()
+ if today.After(date) {
+ return false
+ }
+ }
+ return true
+}
+
+func main() {
+ route := gin.Default()
+
+ if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
+ v.RegisterValidation("bookabledate", bookableDate)
+ }
+
+ route.GET("/bookable", getBookable)
+ route.Run(":8085")
+}
+
+func getBookable(c *gin.Context) {
+ var b Booking
+ if err := c.ShouldBindWith(&b, binding.Query); err == nil {
+ c.JSON(http.StatusOK, gin.H{"message": "Booking dates are valid!"})
+ } else {
+ c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
+ }
+}
+```
+
+```console
+$ curl "localhost:8085/bookable?check_in=2030-04-16&check_out=2030-04-17"
+{"message":"Booking dates are valid!"}
+
+$ curl "localhost:8085/bookable?check_in=2030-03-10&check_out=2030-03-09"
+{"error":"Key: 'Booking.CheckOut' Error:Field validation for 'CheckOut' failed on the 'gtfield' tag"}
+
+$ curl "localhost:8085/bookable?check_in=2000-03-09&check_out=2000-03-10"
+{"error":"Key: 'Booking.CheckIn' Error:Field validation for 'CheckIn' failed on the 'bookabledate' tag"}%
+```
+
+[Struct level validations](https://github.com/go-playground/validator/releases/tag/v8.7) can also be registered this way.
+See the [struct-lvl-validation example](https://github.com/gin-gonic/examples/tree/master/struct-lvl-validations) to learn more.
+
+### Only Bind Query String
+
+`ShouldBindQuery` function only binds the query params and not the post data. See the [detail information](https://github.com/gin-gonic/gin/issues/742#issuecomment-315953017).
+
+```go
+package main
+
+import (
+ "log"
+
+ "github.com/gin-gonic/gin"
+)
+
+type Person struct {
+ Name string `form:"name"`
+ Address string `form:"address"`
+}
+
+func main() {
+ route := gin.Default()
+ route.Any("/testing", startPage)
+ route.Run(":8085")
+}
+
+func startPage(c *gin.Context) {
+ var person Person
+ if c.ShouldBindQuery(&person) == nil {
+ log.Println("====== Only Bind By Query String ======")
+ log.Println(person.Name)
+ log.Println(person.Address)
+ }
+ c.String(200, "Success")
+}
+
+```
+
+### Bind Query String or Post Data
+
+See the [detail information](https://github.com/gin-gonic/gin/issues/742#issuecomment-264681292).
+
+```go
+package main
+
+import (
+ "log"
+ "time"
+
+ "github.com/gin-gonic/gin"
+)
+
+type Person struct {
+ Name string `form:"name"`
+ Address string `form:"address"`
+ Birthday time.Time `form:"birthday" time_format:"2006-01-02" time_utc:"1"`
+ CreateTime time.Time `form:"createTime" time_format:"unixNano"`
+ UnixTime time.Time `form:"unixTime" time_format:"unix"`
+}
+
+func main() {
+ route := gin.Default()
+ route.GET("/testing", startPage)
+ route.Run(":8085")
+}
+
+func startPage(c *gin.Context) {
+ var person Person
+ // If `GET`, only `Form` binding engine (`query`) used.
+ // If `POST`, first checks the `content-type` for `JSON` or `XML`, then uses `Form` (`form-data`).
+ // See more at https://github.com/gin-gonic/gin/blob/master/binding/binding.go#L48
+ if c.ShouldBind(&person) == nil {
+ log.Println(person.Name)
+ log.Println(person.Address)
+ log.Println(person.Birthday)
+ log.Println(person.CreateTime)
+ log.Println(person.UnixTime)
+ }
+
+ c.String(200, "Success")
+}
+```
+
+Test it with:
+```sh
+$ curl -X GET "localhost:8085/testing?name=appleboy&address=xyz&birthday=1992-03-15&createTime=1562400033000000123&unixTime=1562400033"
+```
+
+### Bind Uri
+
+See the [detail information](https://github.com/gin-gonic/gin/issues/846).
+
+```go
+package main
+
+import "github.com/gin-gonic/gin"
+
+type Person struct {
+ ID string `uri:"id" binding:"required,uuid"`
+ Name string `uri:"name" binding:"required"`
+}
+
+func main() {
+ route := gin.Default()
+ route.GET("/:name/:id", func(c *gin.Context) {
+ var person Person
+ if err := c.ShouldBindUri(&person); err != nil {
+ c.JSON(400, gin.H{"msg": err})
+ return
+ }
+ c.JSON(200, gin.H{"name": person.Name, "uuid": person.ID})
+ })
+ route.Run(":8088")
+}
+```
+
+Test it with:
+```sh
+$ curl -v localhost:8088/thinkerou/987fbc97-4bed-5078-9f07-9141ba07c9f3
+$ curl -v localhost:8088/thinkerou/not-uuid
+```
+
+### Bind Header
+
+```go
+package main
+
+import (
+ "fmt"
+ "github.com/gin-gonic/gin"
+)
+
+type testHeader struct {
+ Rate int `header:"Rate"`
+ Domain string `header:"Domain"`
+}
+
+func main() {
+ r := gin.Default()
+ r.GET("/", func(c *gin.Context) {
+ h := testHeader{}
+
+ if err := c.ShouldBindHeader(&h); err != nil {
+ c.JSON(200, err)
+ }
+
+ fmt.Printf("%#v\n", h)
+ c.JSON(200, gin.H{"Rate": h.Rate, "Domain": h.Domain})
+ })
+
+ r.Run()
+
+// client
+// curl -H "rate:300" -H "domain:music" 127.0.0.1:8080/
+// output
+// {"Domain":"music","Rate":300}
+}
+```
+
+### Bind HTML checkboxes
+
+See the [detail information](https://github.com/gin-gonic/gin/issues/129#issuecomment-124260092)
+
+main.go
+
+```go
+...
+
+type myForm struct {
+ Colors []string `form:"colors[]"`
+}
+
+...
+
+func formHandler(c *gin.Context) {
+ var fakeForm myForm
+ c.ShouldBind(&fakeForm)
+ c.JSON(200, gin.H{"color": fakeForm.Colors})
+}
+
+...
+
+```
+
+form.html
+
+```html
+
+```
+
+result:
+
+```
+{"color":["red","green","blue"]}
+```
+
+### Multipart/Urlencoded binding
+
+```go
+type ProfileForm struct {
+ Name string `form:"name" binding:"required"`
+ Avatar *multipart.FileHeader `form:"avatar" binding:"required"`
+
+ // or for multiple files
+ // Avatars []*multipart.FileHeader `form:"avatar" binding:"required"`
+}
+
+func main() {
+ router := gin.Default()
+ router.POST("/profile", func(c *gin.Context) {
+ // you can bind multipart form with explicit binding declaration:
+ // c.ShouldBindWith(&form, binding.Form)
+ // or you can simply use autobinding with ShouldBind method:
+ var form ProfileForm
+ // in this case proper binding will be automatically selected
+ if err := c.ShouldBind(&form); err != nil {
+ c.String(http.StatusBadRequest, "bad request")
+ return
+ }
+
+ err := c.SaveUploadedFile(form.Avatar, form.Avatar.Filename)
+ if err != nil {
+ c.String(http.StatusInternalServerError, "unknown error")
+ return
+ }
+
+ // db.Save(&form)
+
+ c.String(http.StatusOK, "ok")
+ })
+ router.Run(":8080")
+}
+```
+
+Test it with:
+```sh
+$ curl -X POST -v --form name=user --form "avatar=@./avatar.png" http://localhost:8080/profile
+```
+
+### XML, JSON, YAML and ProtoBuf rendering
+
+```go
+func main() {
+ r := gin.Default()
+
+ // gin.H is a shortcut for map[string]interface{}
+ r.GET("/someJSON", func(c *gin.Context) {
+ c.JSON(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK})
+ })
+
+ r.GET("/moreJSON", func(c *gin.Context) {
+ // You also can use a struct
+ var msg struct {
+ Name string `json:"user"`
+ Message string
+ Number int
+ }
+ msg.Name = "Lena"
+ msg.Message = "hey"
+ msg.Number = 123
+ // Note that msg.Name becomes "user" in the JSON
+ // Will output : {"user": "Lena", "Message": "hey", "Number": 123}
+ c.JSON(http.StatusOK, msg)
+ })
+
+ r.GET("/someXML", func(c *gin.Context) {
+ c.XML(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK})
+ })
+
+ r.GET("/someYAML", func(c *gin.Context) {
+ c.YAML(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK})
+ })
+
+ r.GET("/someProtoBuf", func(c *gin.Context) {
+ reps := []int64{int64(1), int64(2)}
+ label := "test"
+ // The specific definition of protobuf is written in the testdata/protoexample file.
+ data := &protoexample.Test{
+ Label: &label,
+ Reps: reps,
+ }
+ // Note that data becomes binary data in the response
+ // Will output protoexample.Test protobuf serialized data
+ c.ProtoBuf(http.StatusOK, data)
+ })
+
+ // Listen and serve on 0.0.0.0:8080
+ r.Run(":8080")
+}
+```
+
+#### SecureJSON
+
+Using SecureJSON to prevent json hijacking. Default prepends `"while(1),"` to response body if the given struct is array values.
+
+```go
+func main() {
+ r := gin.Default()
+
+ // You can also use your own secure json prefix
+ // r.SecureJsonPrefix(")]}',\n")
+
+ r.GET("/someJSON", func(c *gin.Context) {
+ names := []string{"lena", "austin", "foo"}
+
+ // Will output : while(1);["lena","austin","foo"]
+ c.SecureJSON(http.StatusOK, names)
+ })
+
+ // Listen and serve on 0.0.0.0:8080
+ r.Run(":8080")
+}
+```
+#### JSONP
+
+Using JSONP to request data from a server in a different domain. Add callback to response body if the query parameter callback exists.
+
+```go
+func main() {
+ r := gin.Default()
+
+ r.GET("/JSONP", func(c *gin.Context) {
+ data := gin.H{
+ "foo": "bar",
+ }
+
+ //callback is x
+ // Will output : x({\"foo\":\"bar\"})
+ c.JSONP(http.StatusOK, data)
+ })
+
+ // Listen and serve on 0.0.0.0:8080
+ r.Run(":8080")
+
+ // client
+ // curl http://127.0.0.1:8080/JSONP?callback=x
+}
+```
+
+#### AsciiJSON
+
+Using AsciiJSON to Generates ASCII-only JSON with escaped non-ASCII characters.
+
+```go
+func main() {
+ r := gin.Default()
+
+ r.GET("/someJSON", func(c *gin.Context) {
+ data := gin.H{
+ "lang": "GOè¯è¨€",
+ "tag": " ",
+ }
+
+ // will output : {"lang":"GO\u8bed\u8a00","tag":"\u003cbr\u003e"}
+ c.AsciiJSON(http.StatusOK, data)
+ })
+
+ // Listen and serve on 0.0.0.0:8080
+ r.Run(":8080")
+}
+```
+
+#### PureJSON
+
+Normally, JSON replaces special HTML characters with their unicode entities, e.g. `<` becomes `\u003c`. If you want to encode such characters literally, you can use PureJSON instead.
+This feature is unavailable in Go 1.6 and lower.
+
+```go
+func main() {
+ r := gin.Default()
+
+ // Serves unicode entities
+ r.GET("/json", func(c *gin.Context) {
+ c.JSON(200, gin.H{
+ "html": "Hello, world!",
+ })
+ })
+
+ // Serves literal characters
+ r.GET("/purejson", func(c *gin.Context) {
+ c.PureJSON(200, gin.H{
+ "html": "Hello, world!",
+ })
+ })
+
+ // listen and serve on 0.0.0.0:8080
+ r.Run(":8080")
+}
+```
+
+### Serving static files
+
+```go
+func main() {
+ router := gin.Default()
+ router.Static("/assets", "./assets")
+ router.StaticFS("/more_static", http.Dir("my_file_system"))
+ router.StaticFile("/favicon.ico", "./resources/favicon.ico")
+
+ // Listen and serve on 0.0.0.0:8080
+ router.Run(":8080")
+}
+```
+
+### Serving data from file
+
+```go
+func main() {
+ router := gin.Default()
+
+ router.GET("/local/file", func(c *gin.Context) {
+ c.File("local/file.go")
+ })
+
+ var fs http.FileSystem = // ...
+ router.GET("/fs/file", func(c *gin.Context) {
+ c.FileFromFS("fs/file.go", fs)
+ })
+}
+
+```
+
+### Serving data from reader
+
+```go
+func main() {
+ router := gin.Default()
+ router.GET("/someDataFromReader", func(c *gin.Context) {
+ response, err := http.Get("https://raw.githubusercontent.com/gin-gonic/logo/master/color.png")
+ if err != nil || response.StatusCode != http.StatusOK {
+ c.Status(http.StatusServiceUnavailable)
+ return
+ }
+
+ reader := response.Body
+ defer reader.Close()
+ contentLength := response.ContentLength
+ contentType := response.Header.Get("Content-Type")
+
+ extraHeaders := map[string]string{
+ "Content-Disposition": `attachment; filename="gopher.png"`,
+ }
+
+ c.DataFromReader(http.StatusOK, contentLength, contentType, reader, extraHeaders)
+ })
+ router.Run(":8080")
+}
+```
+
+### HTML rendering
+
+Using LoadHTMLGlob() or LoadHTMLFiles()
+
+```go
+func main() {
+ router := gin.Default()
+ router.LoadHTMLGlob("templates/*")
+ //router.LoadHTMLFiles("templates/template1.html", "templates/template2.html")
+ router.GET("/index", func(c *gin.Context) {
+ c.HTML(http.StatusOK, "index.tmpl", gin.H{
+ "title": "Main website",
+ })
+ })
+ router.Run(":8080")
+}
+```
+
+templates/index.tmpl
+
+```html
+
+
+
+{{ end }}
+```
+
+#### Custom Template renderer
+
+You can also use your own html template render
+
+```go
+import "html/template"
+
+func main() {
+ router := gin.Default()
+ html := template.Must(template.ParseFiles("file1", "file2"))
+ router.SetHTMLTemplate(html)
+ router.Run(":8080")
+}
+```
+
+#### Custom Delimiters
+
+You may use custom delims
+
+```go
+ r := gin.Default()
+ r.Delims("{[{", "}]}")
+ r.LoadHTMLGlob("/path/to/templates")
+```
+
+#### Custom Template Funcs
+
+See the detail [example code](https://github.com/gin-gonic/examples/tree/master/template).
+
+main.go
+
+```go
+import (
+ "fmt"
+ "html/template"
+ "net/http"
+ "time"
+
+ "github.com/gin-gonic/gin"
+)
+
+func formatAsDate(t time.Time) string {
+ year, month, day := t.Date()
+ return fmt.Sprintf("%d%02d/%02d", year, month, day)
+}
+
+func main() {
+ router := gin.Default()
+ router.Delims("{[{", "}]}")
+ router.SetFuncMap(template.FuncMap{
+ "formatAsDate": formatAsDate,
+ })
+ router.LoadHTMLFiles("./testdata/template/raw.tmpl")
+
+ router.GET("/raw", func(c *gin.Context) {
+ c.HTML(http.StatusOK, "raw.tmpl", gin.H{
+ "now": time.Date(2017, 07, 01, 0, 0, 0, 0, time.UTC),
+ })
+ })
+
+ router.Run(":8080")
+}
+
+```
+
+raw.tmpl
+
+```html
+Date: {[{.now | formatAsDate}]}
+```
+
+Result:
+```
+Date: 2017/07/01
+```
+
+### Multitemplate
+
+Gin allow by default use only one html.Template. Check [a multitemplate render](https://github.com/gin-contrib/multitemplate) for using features like go 1.6 `block template`.
+
+### Redirects
+
+Issuing a HTTP redirect is easy. Both internal and external locations are supported.
+
+```go
+r.GET("/test", func(c *gin.Context) {
+ c.Redirect(http.StatusMovedPermanently, "http://www.google.com/")
+})
+```
+
+Issuing a HTTP redirect from POST. Refer to issue: [#444](https://github.com/gin-gonic/gin/issues/444)
+```go
+r.POST("/test", func(c *gin.Context) {
+ c.Redirect(http.StatusFound, "/foo")
+})
+```
+
+Issuing a Router redirect, use `HandleContext` like below.
+
+``` go
+r.GET("/test", func(c *gin.Context) {
+ c.Request.URL.Path = "/test2"
+ r.HandleContext(c)
+})
+r.GET("/test2", func(c *gin.Context) {
+ c.JSON(200, gin.H{"hello": "world"})
+})
+```
+
+
+### Custom Middleware
+
+```go
+func Logger() gin.HandlerFunc {
+ return func(c *gin.Context) {
+ t := time.Now()
+
+ // Set example variable
+ c.Set("example", "12345")
+
+ // before request
+
+ c.Next()
+
+ // after request
+ latency := time.Since(t)
+ log.Print(latency)
+
+ // access the status we are sending
+ status := c.Writer.Status()
+ log.Println(status)
+ }
+}
+
+func main() {
+ r := gin.New()
+ r.Use(Logger())
+
+ r.GET("/test", func(c *gin.Context) {
+ example := c.MustGet("example").(string)
+
+ // it would print: "12345"
+ log.Println(example)
+ })
+
+ // Listen and serve on 0.0.0.0:8080
+ r.Run(":8080")
+}
+```
+
+### Using BasicAuth() middleware
+
+```go
+// simulate some private data
+var secrets = gin.H{
+ "foo": gin.H{"email": "foo@bar.com", "phone": "123433"},
+ "austin": gin.H{"email": "austin@example.com", "phone": "666"},
+ "lena": gin.H{"email": "lena@guapa.com", "phone": "523443"},
+}
+
+func main() {
+ r := gin.Default()
+
+ // Group using gin.BasicAuth() middleware
+ // gin.Accounts is a shortcut for map[string]string
+ authorized := r.Group("/admin", gin.BasicAuth(gin.Accounts{
+ "foo": "bar",
+ "austin": "1234",
+ "lena": "hello2",
+ "manu": "4321",
+ }))
+
+ // /admin/secrets endpoint
+ // hit "localhost:8080/admin/secrets
+ authorized.GET("/secrets", func(c *gin.Context) {
+ // get user, it was set by the BasicAuth middleware
+ user := c.MustGet(gin.AuthUserKey).(string)
+ if secret, ok := secrets[user]; ok {
+ c.JSON(http.StatusOK, gin.H{"user": user, "secret": secret})
+ } else {
+ c.JSON(http.StatusOK, gin.H{"user": user, "secret": "NO SECRET :("})
+ }
+ })
+
+ // Listen and serve on 0.0.0.0:8080
+ r.Run(":8080")
+}
+```
+
+### Goroutines inside a middleware
+
+When starting new Goroutines inside a middleware or handler, you **SHOULD NOT** use the original context inside it, you have to use a read-only copy.
+
+```go
+func main() {
+ r := gin.Default()
+
+ r.GET("/long_async", func(c *gin.Context) {
+ // create copy to be used inside the goroutine
+ cCp := c.Copy()
+ go func() {
+ // simulate a long task with time.Sleep(). 5 seconds
+ time.Sleep(5 * time.Second)
+
+ // note that you are using the copied context "cCp", IMPORTANT
+ log.Println("Done! in path " + cCp.Request.URL.Path)
+ }()
+ })
+
+ r.GET("/long_sync", func(c *gin.Context) {
+ // simulate a long task with time.Sleep(). 5 seconds
+ time.Sleep(5 * time.Second)
+
+ // since we are NOT using a goroutine, we do not have to copy the context
+ log.Println("Done! in path " + c.Request.URL.Path)
+ })
+
+ // Listen and serve on 0.0.0.0:8080
+ r.Run(":8080")
+}
+```
+
+### Custom HTTP configuration
+
+Use `http.ListenAndServe()` directly, like this:
+
+```go
+func main() {
+ router := gin.Default()
+ http.ListenAndServe(":8080", router)
+}
+```
+or
+
+```go
+func main() {
+ router := gin.Default()
+
+ s := &http.Server{
+ Addr: ":8080",
+ Handler: router,
+ ReadTimeout: 10 * time.Second,
+ WriteTimeout: 10 * time.Second,
+ MaxHeaderBytes: 1 << 20,
+ }
+ s.ListenAndServe()
+}
+```
+
+### Support Let's Encrypt
+
+example for 1-line LetsEncrypt HTTPS servers.
+
+```go
+package main
+
+import (
+ "log"
+
+ "github.com/gin-gonic/autotls"
+ "github.com/gin-gonic/gin"
+)
+
+func main() {
+ r := gin.Default()
+
+ // Ping handler
+ r.GET("/ping", func(c *gin.Context) {
+ c.String(200, "pong")
+ })
+
+ log.Fatal(autotls.Run(r, "example1.com", "example2.com"))
+}
+```
+
+example for custom autocert manager.
+
+```go
+package main
+
+import (
+ "log"
+
+ "github.com/gin-gonic/autotls"
+ "github.com/gin-gonic/gin"
+ "golang.org/x/crypto/acme/autocert"
+)
+
+func main() {
+ r := gin.Default()
+
+ // Ping handler
+ r.GET("/ping", func(c *gin.Context) {
+ c.String(200, "pong")
+ })
+
+ m := autocert.Manager{
+ Prompt: autocert.AcceptTOS,
+ HostPolicy: autocert.HostWhitelist("example1.com", "example2.com"),
+ Cache: autocert.DirCache("/var/www/.cache"),
+ }
+
+ log.Fatal(autotls.RunWithManager(r, &m))
+}
+```
+
+### Run multiple service using Gin
+
+See the [question](https://github.com/gin-gonic/gin/issues/346) and try the following example:
+
+```go
+package main
+
+import (
+ "log"
+ "net/http"
+ "time"
+
+ "github.com/gin-gonic/gin"
+ "golang.org/x/sync/errgroup"
+)
+
+var (
+ g errgroup.Group
+)
+
+func router01() http.Handler {
+ e := gin.New()
+ e.Use(gin.Recovery())
+ e.GET("/", func(c *gin.Context) {
+ c.JSON(
+ http.StatusOK,
+ gin.H{
+ "code": http.StatusOK,
+ "error": "Welcome server 01",
+ },
+ )
+ })
+
+ return e
+}
+
+func router02() http.Handler {
+ e := gin.New()
+ e.Use(gin.Recovery())
+ e.GET("/", func(c *gin.Context) {
+ c.JSON(
+ http.StatusOK,
+ gin.H{
+ "code": http.StatusOK,
+ "error": "Welcome server 02",
+ },
+ )
+ })
+
+ return e
+}
+
+func main() {
+ server01 := &http.Server{
+ Addr: ":8080",
+ Handler: router01(),
+ ReadTimeout: 5 * time.Second,
+ WriteTimeout: 10 * time.Second,
+ }
+
+ server02 := &http.Server{
+ Addr: ":8081",
+ Handler: router02(),
+ ReadTimeout: 5 * time.Second,
+ WriteTimeout: 10 * time.Second,
+ }
+
+ g.Go(func() error {
+ err := server01.ListenAndServe()
+ if err != nil && err != http.ErrServerClosed {
+ log.Fatal(err)
+ }
+ return err
+ })
+
+ g.Go(func() error {
+ err := server02.ListenAndServe()
+ if err != nil && err != http.ErrServerClosed {
+ log.Fatal(err)
+ }
+ return err
+ })
+
+ if err := g.Wait(); err != nil {
+ log.Fatal(err)
+ }
+}
+```
+
+### Graceful shutdown or restart
+
+There are a few approaches you can use to perform a graceful shutdown or restart. You can make use of third-party packages specifically built for that, or you can manually do the same with the functions and methods from the built-in packages.
+
+#### Third-party packages
+
+We can use [fvbock/endless](https://github.com/fvbock/endless) to replace the default `ListenAndServe`. Refer to issue [#296](https://github.com/gin-gonic/gin/issues/296) for more details.
+
+```go
+router := gin.Default()
+router.GET("/", handler)
+// [...]
+endless.ListenAndServe(":4242", router)
+```
+
+Alternatives:
+
+* [manners](https://github.com/braintree/manners): A polite Go HTTP server that shuts down gracefully.
+* [graceful](https://github.com/tylerb/graceful): Graceful is a Go package enabling graceful shutdown of an http.Handler server.
+* [grace](https://github.com/facebookgo/grace): Graceful restart & zero downtime deploy for Go servers.
+
+#### Manually
+
+In case you are using Go 1.8 or a later version, you may not need to use those libraries. Consider using `http.Server`'s built-in [Shutdown()](https://golang.org/pkg/net/http/#Server.Shutdown) method for graceful shutdowns. The example below describes its usage, and we've got more examples using gin [here](https://github.com/gin-gonic/examples/tree/master/graceful-shutdown).
+
+```go
+// +build go1.8
+
+package main
+
+import (
+ "context"
+ "log"
+ "net/http"
+ "os"
+ "os/signal"
+ "syscall"
+ "time"
+
+ "github.com/gin-gonic/gin"
+)
+
+func main() {
+ router := gin.Default()
+ router.GET("/", func(c *gin.Context) {
+ time.Sleep(5 * time.Second)
+ c.String(http.StatusOK, "Welcome Gin Server")
+ })
+
+ srv := &http.Server{
+ Addr: ":8080",
+ Handler: router,
+ }
+
+ // Initializing the server in a goroutine so that
+ // it won't block the graceful shutdown handling below
+ go func() {
+ if err := srv.ListenAndServe(); err != nil && errors.Is(err, http.ErrServerClosed) {
+ log.Printf("listen: %s\n", err)
+ }
+ }()
+
+ // Wait for interrupt signal to gracefully shutdown the server with
+ // a timeout of 5 seconds.
+ quit := make(chan os.Signal)
+ // kill (no param) default send syscall.SIGTERM
+ // kill -2 is syscall.SIGINT
+ // kill -9 is syscall.SIGKILL but can't be catch, so don't need add it
+ signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
+ <-quit
+ log.Println("Shutting down server...")
+
+ // The context is used to inform the server it has 5 seconds to finish
+ // the request it is currently handling
+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+ defer cancel()
+
+ if err := srv.Shutdown(ctx); err != nil {
+ log.Fatal("Server forced to shutdown:", err)
+ }
+
+ log.Println("Server exiting")
+}
+```
+
+### Build a single binary with templates
+
+You can build a server into a single binary containing templates by using [go-assets][].
+
+[go-assets]: https://github.com/jessevdk/go-assets
+
+```go
+func main() {
+ r := gin.New()
+
+ t, err := loadTemplate()
+ if err != nil {
+ panic(err)
+ }
+ r.SetHTMLTemplate(t)
+
+ r.GET("/", func(c *gin.Context) {
+ c.HTML(http.StatusOK, "/html/index.tmpl",nil)
+ })
+ r.Run(":8080")
+}
+
+// loadTemplate loads templates embedded by go-assets-builder
+func loadTemplate() (*template.Template, error) {
+ t := template.New("")
+ for name, file := range Assets.Files {
+ defer file.Close()
+ if file.IsDir() || !strings.HasSuffix(name, ".tmpl") {
+ continue
+ }
+ h, err := ioutil.ReadAll(file)
+ if err != nil {
+ return nil, err
+ }
+ t, err = t.New(name).Parse(string(h))
+ if err != nil {
+ return nil, err
+ }
+ }
+ return t, nil
+}
+```
+
+See a complete example in the `https://github.com/gin-gonic/examples/tree/master/assets-in-binary` directory.
+
+### Bind form-data request with custom struct
+
+The follow example using custom struct:
+
+```go
+type StructA struct {
+ FieldA string `form:"field_a"`
+}
+
+type StructB struct {
+ NestedStruct StructA
+ FieldB string `form:"field_b"`
+}
+
+type StructC struct {
+ NestedStructPointer *StructA
+ FieldC string `form:"field_c"`
+}
+
+type StructD struct {
+ NestedAnonyStruct struct {
+ FieldX string `form:"field_x"`
+ }
+ FieldD string `form:"field_d"`
+}
+
+func GetDataB(c *gin.Context) {
+ var b StructB
+ c.Bind(&b)
+ c.JSON(200, gin.H{
+ "a": b.NestedStruct,
+ "b": b.FieldB,
+ })
+}
+
+func GetDataC(c *gin.Context) {
+ var b StructC
+ c.Bind(&b)
+ c.JSON(200, gin.H{
+ "a": b.NestedStructPointer,
+ "c": b.FieldC,
+ })
+}
+
+func GetDataD(c *gin.Context) {
+ var b StructD
+ c.Bind(&b)
+ c.JSON(200, gin.H{
+ "x": b.NestedAnonyStruct,
+ "d": b.FieldD,
+ })
+}
+
+func main() {
+ r := gin.Default()
+ r.GET("/getb", GetDataB)
+ r.GET("/getc", GetDataC)
+ r.GET("/getd", GetDataD)
+
+ r.Run()
+}
+```
+
+Using the command `curl` command result:
+
+```
+$ curl "http://localhost:8080/getb?field_a=hello&field_b=world"
+{"a":{"FieldA":"hello"},"b":"world"}
+$ curl "http://localhost:8080/getc?field_a=hello&field_c=world"
+{"a":{"FieldA":"hello"},"c":"world"}
+$ curl "http://localhost:8080/getd?field_x=hello&field_d=world"
+{"d":"world","x":{"FieldX":"hello"}}
+```
+
+### Try to bind body into different structs
+
+The normal methods for binding request body consumes `c.Request.Body` and they
+cannot be called multiple times.
+
+```go
+type formA struct {
+ Foo string `json:"foo" xml:"foo" binding:"required"`
+}
+
+type formB struct {
+ Bar string `json:"bar" xml:"bar" binding:"required"`
+}
+
+func SomeHandler(c *gin.Context) {
+ objA := formA{}
+ objB := formB{}
+ // This c.ShouldBind consumes c.Request.Body and it cannot be reused.
+ if errA := c.ShouldBind(&objA); errA == nil {
+ c.String(http.StatusOK, `the body should be formA`)
+ // Always an error is occurred by this because c.Request.Body is EOF now.
+ } else if errB := c.ShouldBind(&objB); errB == nil {
+ c.String(http.StatusOK, `the body should be formB`)
+ } else {
+ ...
+ }
+}
+```
+
+For this, you can use `c.ShouldBindBodyWith`.
+
+```go
+func SomeHandler(c *gin.Context) {
+ objA := formA{}
+ objB := formB{}
+ // This reads c.Request.Body and stores the result into the context.
+ if errA := c.ShouldBindBodyWith(&objA, binding.JSON); errA == nil {
+ c.String(http.StatusOK, `the body should be formA`)
+ // At this time, it reuses body stored in the context.
+ } else if errB := c.ShouldBindBodyWith(&objB, binding.JSON); errB == nil {
+ c.String(http.StatusOK, `the body should be formB JSON`)
+ // And it can accepts other formats
+ } else if errB2 := c.ShouldBindBodyWith(&objB, binding.XML); errB2 == nil {
+ c.String(http.StatusOK, `the body should be formB XML`)
+ } else {
+ ...
+ }
+}
+```
+
+* `c.ShouldBindBodyWith` stores body into the context before binding. This has
+a slight impact to performance, so you should not use this method if you are
+enough to call binding at once.
+* This feature is only needed for some formats -- `JSON`, `XML`, `MsgPack`,
+`ProtoBuf`. For other formats, `Query`, `Form`, `FormPost`, `FormMultipart`,
+can be called by `c.ShouldBind()` multiple times without any damage to
+performance (See [#1341](https://github.com/gin-gonic/gin/pull/1341)).
+
+### http2 server push
+
+http.Pusher is supported only **go1.8+**. See the [golang blog](https://blog.golang.org/h2push) for detail information.
+
+```go
+package main
+
+import (
+ "html/template"
+ "log"
+
+ "github.com/gin-gonic/gin"
+)
+
+var html = template.Must(template.New("https").Parse(`
+
+
+ Https Test
+
+
+
+
Welcome, Ginner!
+
+
+`))
+
+func main() {
+ r := gin.Default()
+ r.Static("/assets", "./assets")
+ r.SetHTMLTemplate(html)
+
+ r.GET("/", func(c *gin.Context) {
+ if pusher := c.Writer.Pusher(); pusher != nil {
+ // use pusher.Push() to do server push
+ if err := pusher.Push("/assets/app.js", nil); err != nil {
+ log.Printf("Failed to push: %v", err)
+ }
+ }
+ c.HTML(200, "https", gin.H{
+ "status": "success",
+ })
+ })
+
+ // Listen and Server in https://127.0.0.1:8080
+ r.RunTLS(":8080", "./testdata/server.pem", "./testdata/server.key")
+}
+```
+
+### Define format for the log of routes
+
+The default log of routes is:
+```
+[GIN-debug] POST /foo --> main.main.func1 (3 handlers)
+[GIN-debug] GET /bar --> main.main.func2 (3 handlers)
+[GIN-debug] GET /status --> main.main.func3 (3 handlers)
+```
+
+If you want to log this information in given format (e.g. JSON, key values or something else), then you can define this format with `gin.DebugPrintRouteFunc`.
+In the example below, we log all routes with standard log package but you can use another log tools that suits of your needs.
+```go
+import (
+ "log"
+ "net/http"
+
+ "github.com/gin-gonic/gin"
+)
+
+func main() {
+ r := gin.Default()
+ gin.DebugPrintRouteFunc = func(httpMethod, absolutePath, handlerName string, nuHandlers int) {
+ log.Printf("endpoint %v %v %v %v\n", httpMethod, absolutePath, handlerName, nuHandlers)
+ }
+
+ r.POST("/foo", func(c *gin.Context) {
+ c.JSON(http.StatusOK, "foo")
+ })
+
+ r.GET("/bar", func(c *gin.Context) {
+ c.JSON(http.StatusOK, "bar")
+ })
+
+ r.GET("/status", func(c *gin.Context) {
+ c.JSON(http.StatusOK, "ok")
+ })
+
+ // Listen and Server in http://0.0.0.0:8080
+ r.Run()
+}
+```
+
+### Set and get a cookie
+
+```go
+import (
+ "fmt"
+
+ "github.com/gin-gonic/gin"
+)
+
+func main() {
+
+ router := gin.Default()
+
+ router.GET("/cookie", func(c *gin.Context) {
+
+ cookie, err := c.Cookie("gin_cookie")
+
+ if err != nil {
+ cookie = "NotSet"
+ c.SetCookie("gin_cookie", "test", 3600, "/", "localhost", false, true)
+ }
+
+ fmt.Printf("Cookie value: %s \n", cookie)
+ })
+
+ router.Run()
+}
+```
+
+## Don't trust all proxies
+
+Gin lets you specify which headers to hold the real client IP (if any),
+as well as specifying which proxies (or direct clients) you trust to
+specify one of these headers.
+
+Use function `SetTrustedProxies()` on your `gin.Engine` to specify network addresses
+or network CIDRs from where clients which their request headers related to client
+IP can be trusted. They can be IPv4 addresses, IPv4 CIDRs, IPv6 addresses or
+IPv6 CIDRs.
+
+**Attention:** Gin trust all proxies by default if you don't specify a trusted
+proxy using the function above, **this is NOT safe**. At the same time, if you don't
+use any proxy, you can disable this feature by using `Engine.SetTrustedProxies(nil)`,
+then `Context.ClientIP()` will return the remote address directly to avoid some
+unnecessary computation.
+
+```go
+import (
+ "fmt"
+
+ "github.com/gin-gonic/gin"
+)
+
+func main() {
+
+ router := gin.Default()
+ router.SetTrustedProxies([]string{"192.168.1.2"})
+
+ router.GET("/", func(c *gin.Context) {
+ // If the client is 192.168.1.2, use the X-Forwarded-For
+ // header to deduce the original client IP from the trust-
+ // worthy parts of that header.
+ // Otherwise, simply return the direct client IP
+ fmt.Printf("ClientIP: %s\n", c.ClientIP())
+ })
+ router.Run()
+}
+```
+
+**Notice:** If you are using a CDN service, you can set the `Engine.TrustedPlatform`
+to skip TrustedProxies check, it has a higher priority than TrustedProxies.
+Look at the example below:
+```go
+import (
+ "fmt"
+
+ "github.com/gin-gonic/gin"
+)
+
+func main() {
+
+ router := gin.Default()
+ // Use predefined header gin.PlatformXXX
+ router.TrustedPlatform = gin.PlatformGoogleAppEngine
+ // Or set your own trusted request header for another trusted proxy service
+ // Don't set it to any suspect request header, it's unsafe
+ router.TrustedPlatform = "X-CDN-IP"
+
+ router.GET("/", func(c *gin.Context) {
+ // If you set TrustedPlatform, ClientIP() will resolve the
+ // corresponding header and return IP directly
+ fmt.Printf("ClientIP: %s\n", c.ClientIP())
+ })
+ router.Run()
+}
+```
+
+## Testing
+
+The `net/http/httptest` package is preferable way for HTTP testing.
+
+```go
+package main
+
+func setupRouter() *gin.Engine {
+ r := gin.Default()
+ r.GET("/ping", func(c *gin.Context) {
+ c.String(200, "pong")
+ })
+ return r
+}
+
+func main() {
+ r := setupRouter()
+ r.Run(":8080")
+}
+```
+
+Test for code example above:
+
+```go
+package main
+
+import (
+ "net/http"
+ "net/http/httptest"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestPingRoute(t *testing.T) {
+ router := setupRouter()
+
+ w := httptest.NewRecorder()
+ req, _ := http.NewRequest("GET", "/ping", nil)
+ router.ServeHTTP(w, req)
+
+ assert.Equal(t, 200, w.Code)
+ assert.Equal(t, "pong", w.Body.String())
+}
+```
+
+## Users
+
+Awesome project lists using [Gin](https://github.com/gin-gonic/gin) web framework.
+
+* [gorush](https://github.com/appleboy/gorush): A push notification server written in Go.
+* [fnproject](https://github.com/fnproject/fn): The container native, cloud agnostic serverless platform.
+* [photoprism](https://github.com/photoprism/photoprism): Personal photo management powered by Go and Google TensorFlow.
+* [krakend](https://github.com/devopsfaith/krakend): Ultra performant API Gateway with middlewares.
+* [picfit](https://github.com/thoas/picfit): An image resizing server written in Go.
+* [brigade](https://github.com/brigadecore/brigade): Event-based Scripting for Kubernetes.
+* [dkron](https://github.com/distribworks/dkron): Distributed, fault tolerant job scheduling system.
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/auth.go b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/auth.go
new file mode 100644
index 000000000000..4d8a6ce484c4
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/auth.go
@@ -0,0 +1,91 @@
+// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
+// Use of this source code is governed by a MIT style
+// license that can be found in the LICENSE file.
+
+package gin
+
+import (
+ "crypto/subtle"
+ "encoding/base64"
+ "net/http"
+ "strconv"
+
+ "github.com/gin-gonic/gin/internal/bytesconv"
+)
+
+// AuthUserKey is the cookie name for user credential in basic auth.
+const AuthUserKey = "user"
+
+// Accounts defines a key/value for user/pass list of authorized logins.
+type Accounts map[string]string
+
+type authPair struct {
+ value string
+ user string
+}
+
+type authPairs []authPair
+
+func (a authPairs) searchCredential(authValue string) (string, bool) {
+ if authValue == "" {
+ return "", false
+ }
+ for _, pair := range a {
+ if subtle.ConstantTimeCompare([]byte(pair.value), []byte(authValue)) == 1 {
+ return pair.user, true
+ }
+ }
+ return "", false
+}
+
+// BasicAuthForRealm returns a Basic HTTP Authorization middleware. It takes as arguments a map[string]string where
+// the key is the user name and the value is the password, as well as the name of the Realm.
+// If the realm is empty, "Authorization Required" will be used by default.
+// (see http://tools.ietf.org/html/rfc2617#section-1.2)
+func BasicAuthForRealm(accounts Accounts, realm string) HandlerFunc {
+ if realm == "" {
+ realm = "Authorization Required"
+ }
+ realm = "Basic realm=" + strconv.Quote(realm)
+ pairs := processAccounts(accounts)
+ return func(c *Context) {
+ // Search user in the slice of allowed credentials
+ user, found := pairs.searchCredential(c.requestHeader("Authorization"))
+ if !found {
+ // Credentials doesn't match, we return 401 and abort handlers chain.
+ c.Header("WWW-Authenticate", realm)
+ c.AbortWithStatus(http.StatusUnauthorized)
+ return
+ }
+
+ // The user credentials was found, set user's id to key AuthUserKey in this context, the user's id can be read later using
+ // c.MustGet(gin.AuthUserKey).
+ c.Set(AuthUserKey, user)
+ }
+}
+
+// BasicAuth returns a Basic HTTP Authorization middleware. It takes as argument a map[string]string where
+// the key is the user name and the value is the password.
+func BasicAuth(accounts Accounts) HandlerFunc {
+ return BasicAuthForRealm(accounts, "")
+}
+
+func processAccounts(accounts Accounts) authPairs {
+ length := len(accounts)
+ assert1(length > 0, "Empty list of authorized credentials")
+ pairs := make(authPairs, 0, length)
+ for user, password := range accounts {
+ assert1(user != "", "User can not be empty")
+ value := authorizationHeader(user, password)
+ pairs = append(pairs, authPair{
+ value: value,
+ user: user,
+ })
+ }
+ return pairs
+}
+
+func authorizationHeader(user, password string) string {
+ base := user + ":" + password
+ return "Basic " + base64.StdEncoding.EncodeToString(bytesconv.StringToBytes(base))
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/binding/binding.go b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/binding/binding.go
new file mode 100644
index 000000000000..5caeb581afe7
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/binding/binding.go
@@ -0,0 +1,118 @@
+// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
+// Use of this source code is governed by a MIT style
+// license that can be found in the LICENSE file.
+
+//go:build !nomsgpack
+// +build !nomsgpack
+
+package binding
+
+import "net/http"
+
+// Content-Type MIME of the most common data formats.
+const (
+ MIMEJSON = "application/json"
+ MIMEHTML = "text/html"
+ MIMEXML = "application/xml"
+ MIMEXML2 = "text/xml"
+ MIMEPlain = "text/plain"
+ MIMEPOSTForm = "application/x-www-form-urlencoded"
+ MIMEMultipartPOSTForm = "multipart/form-data"
+ MIMEPROTOBUF = "application/x-protobuf"
+ MIMEMSGPACK = "application/x-msgpack"
+ MIMEMSGPACK2 = "application/msgpack"
+ MIMEYAML = "application/x-yaml"
+)
+
+// Binding describes the interface which needs to be implemented for binding the
+// data present in the request such as JSON request body, query parameters or
+// the form POST.
+type Binding interface {
+ Name() string
+ Bind(*http.Request, interface{}) error
+}
+
+// BindingBody adds BindBody method to Binding. BindBody is similar with Bind,
+// but it reads the body from supplied bytes instead of req.Body.
+type BindingBody interface {
+ Binding
+ BindBody([]byte, interface{}) error
+}
+
+// BindingUri adds BindUri method to Binding. BindUri is similar with Bind,
+// but it read the Params.
+type BindingUri interface {
+ Name() string
+ BindUri(map[string][]string, interface{}) error
+}
+
+// StructValidator is the minimal interface which needs to be implemented in
+// order for it to be used as the validator engine for ensuring the correctness
+// of the request. Gin provides a default implementation for this using
+// https://github.com/go-playground/validator/tree/v8.18.2.
+type StructValidator interface {
+ // ValidateStruct can receive any kind of type and it should never panic, even if the configuration is not right.
+ // If the received type is a slice|array, the validation should be performed travel on every element.
+ // If the received type is not a struct or slice|array, any validation should be skipped and nil must be returned.
+ // If the received type is a struct or pointer to a struct, the validation should be performed.
+ // If the struct is not valid or the validation itself fails, a descriptive error should be returned.
+ // Otherwise nil must be returned.
+ ValidateStruct(interface{}) error
+
+ // Engine returns the underlying validator engine which powers the
+ // StructValidator implementation.
+ Engine() interface{}
+}
+
+// Validator is the default validator which implements the StructValidator
+// interface. It uses https://github.com/go-playground/validator/tree/v8.18.2
+// under the hood.
+var Validator StructValidator = &defaultValidator{}
+
+// These implement the Binding interface and can be used to bind the data
+// present in the request to struct instances.
+var (
+ JSON = jsonBinding{}
+ XML = xmlBinding{}
+ Form = formBinding{}
+ Query = queryBinding{}
+ FormPost = formPostBinding{}
+ FormMultipart = formMultipartBinding{}
+ ProtoBuf = protobufBinding{}
+ MsgPack = msgpackBinding{}
+ YAML = yamlBinding{}
+ Uri = uriBinding{}
+ Header = headerBinding{}
+)
+
+// Default returns the appropriate Binding instance based on the HTTP method
+// and the content type.
+func Default(method, contentType string) Binding {
+ if method == http.MethodGet {
+ return Form
+ }
+
+ switch contentType {
+ case MIMEJSON:
+ return JSON
+ case MIMEXML, MIMEXML2:
+ return XML
+ case MIMEPROTOBUF:
+ return ProtoBuf
+ case MIMEMSGPACK, MIMEMSGPACK2:
+ return MsgPack
+ case MIMEYAML:
+ return YAML
+ case MIMEMultipartPOSTForm:
+ return FormMultipart
+ default: // case MIMEPOSTForm:
+ return Form
+ }
+}
+
+func validate(obj interface{}) error {
+ if Validator == nil {
+ return nil
+ }
+ return Validator.ValidateStruct(obj)
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/binding/binding_nomsgpack.go b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/binding/binding_nomsgpack.go
new file mode 100644
index 000000000000..9afa3dcf6dad
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/binding/binding_nomsgpack.go
@@ -0,0 +1,112 @@
+// Copyright 2020 Gin Core Team. All rights reserved.
+// Use of this source code is governed by a MIT style
+// license that can be found in the LICENSE file.
+
+//go:build nomsgpack
+// +build nomsgpack
+
+package binding
+
+import "net/http"
+
+// Content-Type MIME of the most common data formats.
+const (
+ MIMEJSON = "application/json"
+ MIMEHTML = "text/html"
+ MIMEXML = "application/xml"
+ MIMEXML2 = "text/xml"
+ MIMEPlain = "text/plain"
+ MIMEPOSTForm = "application/x-www-form-urlencoded"
+ MIMEMultipartPOSTForm = "multipart/form-data"
+ MIMEPROTOBUF = "application/x-protobuf"
+ MIMEYAML = "application/x-yaml"
+)
+
+// Binding describes the interface which needs to be implemented for binding the
+// data present in the request such as JSON request body, query parameters or
+// the form POST.
+type Binding interface {
+ Name() string
+ Bind(*http.Request, interface{}) error
+}
+
+// BindingBody adds BindBody method to Binding. BindBody is similar with Bind,
+// but it reads the body from supplied bytes instead of req.Body.
+type BindingBody interface {
+ Binding
+ BindBody([]byte, interface{}) error
+}
+
+// BindingUri adds BindUri method to Binding. BindUri is similar with Bind,
+// but it read the Params.
+type BindingUri interface {
+ Name() string
+ BindUri(map[string][]string, interface{}) error
+}
+
+// StructValidator is the minimal interface which needs to be implemented in
+// order for it to be used as the validator engine for ensuring the correctness
+// of the request. Gin provides a default implementation for this using
+// https://github.com/go-playground/validator/tree/v8.18.2.
+type StructValidator interface {
+ // ValidateStruct can receive any kind of type and it should never panic, even if the configuration is not right.
+ // If the received type is not a struct, any validation should be skipped and nil must be returned.
+ // If the received type is a struct or pointer to a struct, the validation should be performed.
+ // If the struct is not valid or the validation itself fails, a descriptive error should be returned.
+ // Otherwise nil must be returned.
+ ValidateStruct(interface{}) error
+
+ // Engine returns the underlying validator engine which powers the
+ // StructValidator implementation.
+ Engine() interface{}
+}
+
+// Validator is the default validator which implements the StructValidator
+// interface. It uses https://github.com/go-playground/validator/tree/v8.18.2
+// under the hood.
+var Validator StructValidator = &defaultValidator{}
+
+// These implement the Binding interface and can be used to bind the data
+// present in the request to struct instances.
+var (
+ JSON = jsonBinding{}
+ XML = xmlBinding{}
+ Form = formBinding{}
+ Query = queryBinding{}
+ FormPost = formPostBinding{}
+ FormMultipart = formMultipartBinding{}
+ ProtoBuf = protobufBinding{}
+ YAML = yamlBinding{}
+ Uri = uriBinding{}
+ Header = headerBinding{}
+)
+
+// Default returns the appropriate Binding instance based on the HTTP method
+// and the content type.
+func Default(method, contentType string) Binding {
+ if method == "GET" {
+ return Form
+ }
+
+ switch contentType {
+ case MIMEJSON:
+ return JSON
+ case MIMEXML, MIMEXML2:
+ return XML
+ case MIMEPROTOBUF:
+ return ProtoBuf
+ case MIMEYAML:
+ return YAML
+ case MIMEMultipartPOSTForm:
+ return FormMultipart
+ default: // case MIMEPOSTForm:
+ return Form
+ }
+}
+
+func validate(obj interface{}) error {
+ if Validator == nil {
+ return nil
+ }
+ return Validator.ValidateStruct(obj)
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/binding/default_validator.go b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/binding/default_validator.go
new file mode 100644
index 000000000000..c57a120fc2eb
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/binding/default_validator.go
@@ -0,0 +1,85 @@
+// Copyright 2017 Manu Martinez-Almeida. All rights reserved.
+// Use of this source code is governed by a MIT style
+// license that can be found in the LICENSE file.
+
+package binding
+
+import (
+ "fmt"
+ "reflect"
+ "strings"
+ "sync"
+
+ "github.com/go-playground/validator/v10"
+)
+
+type defaultValidator struct {
+ once sync.Once
+ validate *validator.Validate
+}
+
+type sliceValidateError []error
+
+func (err sliceValidateError) Error() string {
+ var errMsgs []string
+ for i, e := range err {
+ if e == nil {
+ continue
+ }
+ errMsgs = append(errMsgs, fmt.Sprintf("[%d]: %s", i, e.Error()))
+ }
+ return strings.Join(errMsgs, "\n")
+}
+
+var _ StructValidator = &defaultValidator{}
+
+// ValidateStruct receives any kind of type, but only performed struct or pointer to struct type.
+func (v *defaultValidator) ValidateStruct(obj interface{}) error {
+ if obj == nil {
+ return nil
+ }
+
+ value := reflect.ValueOf(obj)
+ switch value.Kind() {
+ case reflect.Ptr:
+ return v.ValidateStruct(value.Elem().Interface())
+ case reflect.Struct:
+ return v.validateStruct(obj)
+ case reflect.Slice, reflect.Array:
+ count := value.Len()
+ validateRet := make(sliceValidateError, 0)
+ for i := 0; i < count; i++ {
+ if err := v.ValidateStruct(value.Index(i).Interface()); err != nil {
+ validateRet = append(validateRet, err)
+ }
+ }
+ if len(validateRet) == 0 {
+ return nil
+ }
+ return validateRet
+ default:
+ return nil
+ }
+}
+
+// validateStruct receives struct type
+func (v *defaultValidator) validateStruct(obj interface{}) error {
+ v.lazyinit()
+ return v.validate.Struct(obj)
+}
+
+// Engine returns the underlying validator engine which powers the default
+// Validator instance. This is useful if you want to register custom validations
+// or struct level validations. See validator GoDoc for more info -
+// https://godoc.org/gopkg.in/go-playground/validator.v8
+func (v *defaultValidator) Engine() interface{} {
+ v.lazyinit()
+ return v.validate
+}
+
+func (v *defaultValidator) lazyinit() {
+ v.once.Do(func() {
+ v.validate = validator.New()
+ v.validate.SetTagName("binding")
+ })
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/binding/form.go b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/binding/form.go
new file mode 100644
index 000000000000..b93c34cf42d3
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/binding/form.go
@@ -0,0 +1,63 @@
+// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
+// Use of this source code is governed by a MIT style
+// license that can be found in the LICENSE file.
+
+package binding
+
+import (
+ "net/http"
+)
+
+const defaultMemory = 32 << 20
+
+type formBinding struct{}
+type formPostBinding struct{}
+type formMultipartBinding struct{}
+
+func (formBinding) Name() string {
+ return "form"
+}
+
+func (formBinding) Bind(req *http.Request, obj interface{}) error {
+ if err := req.ParseForm(); err != nil {
+ return err
+ }
+ if err := req.ParseMultipartForm(defaultMemory); err != nil {
+ if err != http.ErrNotMultipart {
+ return err
+ }
+ }
+ if err := mapForm(obj, req.Form); err != nil {
+ return err
+ }
+ return validate(obj)
+}
+
+func (formPostBinding) Name() string {
+ return "form-urlencoded"
+}
+
+func (formPostBinding) Bind(req *http.Request, obj interface{}) error {
+ if err := req.ParseForm(); err != nil {
+ return err
+ }
+ if err := mapForm(obj, req.PostForm); err != nil {
+ return err
+ }
+ return validate(obj)
+}
+
+func (formMultipartBinding) Name() string {
+ return "multipart/form-data"
+}
+
+func (formMultipartBinding) Bind(req *http.Request, obj interface{}) error {
+ if err := req.ParseMultipartForm(defaultMemory); err != nil {
+ return err
+ }
+ if err := mappingByPtr(obj, (*multipartRequest)(req), "form"); err != nil {
+ return err
+ }
+
+ return validate(obj)
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/binding/form_mapping.go b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/binding/form_mapping.go
new file mode 100644
index 000000000000..2f4e45b40fcb
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/binding/form_mapping.go
@@ -0,0 +1,392 @@
+// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
+// Use of this source code is governed by a MIT style
+// license that can be found in the LICENSE file.
+
+package binding
+
+import (
+ "errors"
+ "fmt"
+ "reflect"
+ "strconv"
+ "strings"
+ "time"
+
+ "github.com/gin-gonic/gin/internal/bytesconv"
+ "github.com/gin-gonic/gin/internal/json"
+)
+
+var errUnknownType = errors.New("unknown type")
+
+func mapUri(ptr interface{}, m map[string][]string) error {
+ return mapFormByTag(ptr, m, "uri")
+}
+
+func mapForm(ptr interface{}, form map[string][]string) error {
+ return mapFormByTag(ptr, form, "form")
+}
+
+var emptyField = reflect.StructField{}
+
+func mapFormByTag(ptr interface{}, form map[string][]string, tag string) error {
+ // Check if ptr is a map
+ ptrVal := reflect.ValueOf(ptr)
+ var pointed interface{}
+ if ptrVal.Kind() == reflect.Ptr {
+ ptrVal = ptrVal.Elem()
+ pointed = ptrVal.Interface()
+ }
+ if ptrVal.Kind() == reflect.Map &&
+ ptrVal.Type().Key().Kind() == reflect.String {
+ if pointed != nil {
+ ptr = pointed
+ }
+ return setFormMap(ptr, form)
+ }
+
+ return mappingByPtr(ptr, formSource(form), tag)
+}
+
+// setter tries to set value on a walking by fields of a struct
+type setter interface {
+ TrySet(value reflect.Value, field reflect.StructField, key string, opt setOptions) (isSetted bool, err error)
+}
+
+type formSource map[string][]string
+
+var _ setter = formSource(nil)
+
+// TrySet tries to set a value by request's form source (like map[string][]string)
+func (form formSource) TrySet(value reflect.Value, field reflect.StructField, tagValue string, opt setOptions) (isSetted bool, err error) {
+ return setByForm(value, field, form, tagValue, opt)
+}
+
+func mappingByPtr(ptr interface{}, setter setter, tag string) error {
+ _, err := mapping(reflect.ValueOf(ptr), emptyField, setter, tag)
+ return err
+}
+
+func mapping(value reflect.Value, field reflect.StructField, setter setter, tag string) (bool, error) {
+ if field.Tag.Get(tag) == "-" { // just ignoring this field
+ return false, nil
+ }
+
+ var vKind = value.Kind()
+
+ if vKind == reflect.Ptr {
+ var isNew bool
+ vPtr := value
+ if value.IsNil() {
+ isNew = true
+ vPtr = reflect.New(value.Type().Elem())
+ }
+ isSetted, err := mapping(vPtr.Elem(), field, setter, tag)
+ if err != nil {
+ return false, err
+ }
+ if isNew && isSetted {
+ value.Set(vPtr)
+ }
+ return isSetted, nil
+ }
+
+ if vKind != reflect.Struct || !field.Anonymous {
+ ok, err := tryToSetValue(value, field, setter, tag)
+ if err != nil {
+ return false, err
+ }
+ if ok {
+ return true, nil
+ }
+ }
+
+ if vKind == reflect.Struct {
+ tValue := value.Type()
+
+ var isSetted bool
+ for i := 0; i < value.NumField(); i++ {
+ sf := tValue.Field(i)
+ if sf.PkgPath != "" && !sf.Anonymous { // unexported
+ continue
+ }
+ ok, err := mapping(value.Field(i), tValue.Field(i), setter, tag)
+ if err != nil {
+ return false, err
+ }
+ isSetted = isSetted || ok
+ }
+ return isSetted, nil
+ }
+ return false, nil
+}
+
+type setOptions struct {
+ isDefaultExists bool
+ defaultValue string
+}
+
+func tryToSetValue(value reflect.Value, field reflect.StructField, setter setter, tag string) (bool, error) {
+ var tagValue string
+ var setOpt setOptions
+
+ tagValue = field.Tag.Get(tag)
+ tagValue, opts := head(tagValue, ",")
+
+ if tagValue == "" { // default value is FieldName
+ tagValue = field.Name
+ }
+ if tagValue == "" { // when field is "emptyField" variable
+ return false, nil
+ }
+
+ var opt string
+ for len(opts) > 0 {
+ opt, opts = head(opts, ",")
+
+ if k, v := head(opt, "="); k == "default" {
+ setOpt.isDefaultExists = true
+ setOpt.defaultValue = v
+ }
+ }
+
+ return setter.TrySet(value, field, tagValue, setOpt)
+}
+
+func setByForm(value reflect.Value, field reflect.StructField, form map[string][]string, tagValue string, opt setOptions) (isSetted bool, err error) {
+ vs, ok := form[tagValue]
+ if !ok && !opt.isDefaultExists {
+ return false, nil
+ }
+
+ switch value.Kind() {
+ case reflect.Slice:
+ if !ok {
+ vs = []string{opt.defaultValue}
+ }
+ return true, setSlice(vs, value, field)
+ case reflect.Array:
+ if !ok {
+ vs = []string{opt.defaultValue}
+ }
+ if len(vs) != value.Len() {
+ return false, fmt.Errorf("%q is not valid value for %s", vs, value.Type().String())
+ }
+ return true, setArray(vs, value, field)
+ default:
+ var val string
+ if !ok {
+ val = opt.defaultValue
+ }
+
+ if len(vs) > 0 {
+ val = vs[0]
+ }
+ return true, setWithProperType(val, value, field)
+ }
+}
+
+func setWithProperType(val string, value reflect.Value, field reflect.StructField) error {
+ switch value.Kind() {
+ case reflect.Int:
+ return setIntField(val, 0, value)
+ case reflect.Int8:
+ return setIntField(val, 8, value)
+ case reflect.Int16:
+ return setIntField(val, 16, value)
+ case reflect.Int32:
+ return setIntField(val, 32, value)
+ case reflect.Int64:
+ switch value.Interface().(type) {
+ case time.Duration:
+ return setTimeDuration(val, value, field)
+ }
+ return setIntField(val, 64, value)
+ case reflect.Uint:
+ return setUintField(val, 0, value)
+ case reflect.Uint8:
+ return setUintField(val, 8, value)
+ case reflect.Uint16:
+ return setUintField(val, 16, value)
+ case reflect.Uint32:
+ return setUintField(val, 32, value)
+ case reflect.Uint64:
+ return setUintField(val, 64, value)
+ case reflect.Bool:
+ return setBoolField(val, value)
+ case reflect.Float32:
+ return setFloatField(val, 32, value)
+ case reflect.Float64:
+ return setFloatField(val, 64, value)
+ case reflect.String:
+ value.SetString(val)
+ case reflect.Struct:
+ switch value.Interface().(type) {
+ case time.Time:
+ return setTimeField(val, field, value)
+ }
+ return json.Unmarshal(bytesconv.StringToBytes(val), value.Addr().Interface())
+ case reflect.Map:
+ return json.Unmarshal(bytesconv.StringToBytes(val), value.Addr().Interface())
+ default:
+ return errUnknownType
+ }
+ return nil
+}
+
+func setIntField(val string, bitSize int, field reflect.Value) error {
+ if val == "" {
+ val = "0"
+ }
+ intVal, err := strconv.ParseInt(val, 10, bitSize)
+ if err == nil {
+ field.SetInt(intVal)
+ }
+ return err
+}
+
+func setUintField(val string, bitSize int, field reflect.Value) error {
+ if val == "" {
+ val = "0"
+ }
+ uintVal, err := strconv.ParseUint(val, 10, bitSize)
+ if err == nil {
+ field.SetUint(uintVal)
+ }
+ return err
+}
+
+func setBoolField(val string, field reflect.Value) error {
+ if val == "" {
+ val = "false"
+ }
+ boolVal, err := strconv.ParseBool(val)
+ if err == nil {
+ field.SetBool(boolVal)
+ }
+ return err
+}
+
+func setFloatField(val string, bitSize int, field reflect.Value) error {
+ if val == "" {
+ val = "0.0"
+ }
+ floatVal, err := strconv.ParseFloat(val, bitSize)
+ if err == nil {
+ field.SetFloat(floatVal)
+ }
+ return err
+}
+
+func setTimeField(val string, structField reflect.StructField, value reflect.Value) error {
+ timeFormat := structField.Tag.Get("time_format")
+ if timeFormat == "" {
+ timeFormat = time.RFC3339
+ }
+
+ switch tf := strings.ToLower(timeFormat); tf {
+ case "unix", "unixnano":
+ tv, err := strconv.ParseInt(val, 10, 64)
+ if err != nil {
+ return err
+ }
+
+ d := time.Duration(1)
+ if tf == "unixnano" {
+ d = time.Second
+ }
+
+ t := time.Unix(tv/int64(d), tv%int64(d))
+ value.Set(reflect.ValueOf(t))
+ return nil
+
+ }
+
+ if val == "" {
+ value.Set(reflect.ValueOf(time.Time{}))
+ return nil
+ }
+
+ l := time.Local
+ if isUTC, _ := strconv.ParseBool(structField.Tag.Get("time_utc")); isUTC {
+ l = time.UTC
+ }
+
+ if locTag := structField.Tag.Get("time_location"); locTag != "" {
+ loc, err := time.LoadLocation(locTag)
+ if err != nil {
+ return err
+ }
+ l = loc
+ }
+
+ t, err := time.ParseInLocation(timeFormat, val, l)
+ if err != nil {
+ return err
+ }
+
+ value.Set(reflect.ValueOf(t))
+ return nil
+}
+
+func setArray(vals []string, value reflect.Value, field reflect.StructField) error {
+ for i, s := range vals {
+ err := setWithProperType(s, value.Index(i), field)
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func setSlice(vals []string, value reflect.Value, field reflect.StructField) error {
+ slice := reflect.MakeSlice(value.Type(), len(vals), len(vals))
+ err := setArray(vals, slice, field)
+ if err != nil {
+ return err
+ }
+ value.Set(slice)
+ return nil
+}
+
+func setTimeDuration(val string, value reflect.Value, field reflect.StructField) error {
+ d, err := time.ParseDuration(val)
+ if err != nil {
+ return err
+ }
+ value.Set(reflect.ValueOf(d))
+ return nil
+}
+
+func head(str, sep string) (head string, tail string) {
+ idx := strings.Index(str, sep)
+ if idx < 0 {
+ return str, ""
+ }
+ return str[:idx], str[idx+len(sep):]
+}
+
+func setFormMap(ptr interface{}, form map[string][]string) error {
+ el := reflect.TypeOf(ptr).Elem()
+
+ if el.Kind() == reflect.Slice {
+ ptrMap, ok := ptr.(map[string][]string)
+ if !ok {
+ return errors.New("cannot convert to map slices of strings")
+ }
+ for k, v := range form {
+ ptrMap[k] = v
+ }
+
+ return nil
+ }
+
+ ptrMap, ok := ptr.(map[string]string)
+ if !ok {
+ return errors.New("cannot convert to map of strings")
+ }
+ for k, v := range form {
+ ptrMap[k] = v[len(v)-1] // pick last
+ }
+
+ return nil
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/binding/header.go b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/binding/header.go
new file mode 100644
index 000000000000..179ce4ea2037
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/binding/header.go
@@ -0,0 +1,34 @@
+package binding
+
+import (
+ "net/http"
+ "net/textproto"
+ "reflect"
+)
+
+type headerBinding struct{}
+
+func (headerBinding) Name() string {
+ return "header"
+}
+
+func (headerBinding) Bind(req *http.Request, obj interface{}) error {
+
+ if err := mapHeader(obj, req.Header); err != nil {
+ return err
+ }
+
+ return validate(obj)
+}
+
+func mapHeader(ptr interface{}, h map[string][]string) error {
+ return mappingByPtr(ptr, headerSource(h), "header")
+}
+
+type headerSource map[string][]string
+
+var _ setter = headerSource(nil)
+
+func (hs headerSource) TrySet(value reflect.Value, field reflect.StructField, tagValue string, opt setOptions) (isSetted bool, err error) {
+ return setByForm(value, field, hs, textproto.CanonicalMIMEHeaderKey(tagValue), opt)
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/binding/json.go b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/binding/json.go
new file mode 100644
index 000000000000..d62e07059482
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/binding/json.go
@@ -0,0 +1,56 @@
+// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
+// Use of this source code is governed by a MIT style
+// license that can be found in the LICENSE file.
+
+package binding
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "net/http"
+
+ "github.com/gin-gonic/gin/internal/json"
+)
+
+// EnableDecoderUseNumber is used to call the UseNumber method on the JSON
+// Decoder instance. UseNumber causes the Decoder to unmarshal a number into an
+// interface{} as a Number instead of as a float64.
+var EnableDecoderUseNumber = false
+
+// EnableDecoderDisallowUnknownFields is used to call the DisallowUnknownFields method
+// on the JSON Decoder instance. DisallowUnknownFields causes the Decoder to
+// return an error when the destination is a struct and the input contains object
+// keys which do not match any non-ignored, exported fields in the destination.
+var EnableDecoderDisallowUnknownFields = false
+
+type jsonBinding struct{}
+
+func (jsonBinding) Name() string {
+ return "json"
+}
+
+func (jsonBinding) Bind(req *http.Request, obj interface{}) error {
+ if req == nil || req.Body == nil {
+ return fmt.Errorf("invalid request")
+ }
+ return decodeJSON(req.Body, obj)
+}
+
+func (jsonBinding) BindBody(body []byte, obj interface{}) error {
+ return decodeJSON(bytes.NewReader(body), obj)
+}
+
+func decodeJSON(r io.Reader, obj interface{}) error {
+ decoder := json.NewDecoder(r)
+ if EnableDecoderUseNumber {
+ decoder.UseNumber()
+ }
+ if EnableDecoderDisallowUnknownFields {
+ decoder.DisallowUnknownFields()
+ }
+ if err := decoder.Decode(obj); err != nil {
+ return err
+ }
+ return validate(obj)
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/binding/msgpack.go b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/binding/msgpack.go
new file mode 100644
index 000000000000..2a442996a63e
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/binding/msgpack.go
@@ -0,0 +1,38 @@
+// Copyright 2017 Manu Martinez-Almeida. All rights reserved.
+// Use of this source code is governed by a MIT style
+// license that can be found in the LICENSE file.
+
+//go:build !nomsgpack
+// +build !nomsgpack
+
+package binding
+
+import (
+ "bytes"
+ "io"
+ "net/http"
+
+ "github.com/ugorji/go/codec"
+)
+
+type msgpackBinding struct{}
+
+func (msgpackBinding) Name() string {
+ return "msgpack"
+}
+
+func (msgpackBinding) Bind(req *http.Request, obj interface{}) error {
+ return decodeMsgPack(req.Body, obj)
+}
+
+func (msgpackBinding) BindBody(body []byte, obj interface{}) error {
+ return decodeMsgPack(bytes.NewReader(body), obj)
+}
+
+func decodeMsgPack(r io.Reader, obj interface{}) error {
+ cdc := new(codec.MsgpackHandle)
+ if err := codec.NewDecoder(r, cdc).Decode(&obj); err != nil {
+ return err
+ }
+ return validate(obj)
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/binding/multipart_form_mapping.go b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/binding/multipart_form_mapping.go
new file mode 100644
index 000000000000..f85a1aa60455
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/binding/multipart_form_mapping.go
@@ -0,0 +1,66 @@
+// Copyright 2019 Gin Core Team. All rights reserved.
+// Use of this source code is governed by a MIT style
+// license that can be found in the LICENSE file.
+
+package binding
+
+import (
+ "errors"
+ "mime/multipart"
+ "net/http"
+ "reflect"
+)
+
+type multipartRequest http.Request
+
+var _ setter = (*multipartRequest)(nil)
+
+// TrySet tries to set a value by the multipart request with the binding a form file
+func (r *multipartRequest) TrySet(value reflect.Value, field reflect.StructField, key string, opt setOptions) (isSetted bool, err error) {
+ if files := r.MultipartForm.File[key]; len(files) != 0 {
+ return setByMultipartFormFile(value, field, files)
+ }
+
+ return setByForm(value, field, r.MultipartForm.Value, key, opt)
+}
+
+func setByMultipartFormFile(value reflect.Value, field reflect.StructField, files []*multipart.FileHeader) (isSetted bool, err error) {
+ switch value.Kind() {
+ case reflect.Ptr:
+ switch value.Interface().(type) {
+ case *multipart.FileHeader:
+ value.Set(reflect.ValueOf(files[0]))
+ return true, nil
+ }
+ case reflect.Struct:
+ switch value.Interface().(type) {
+ case multipart.FileHeader:
+ value.Set(reflect.ValueOf(*files[0]))
+ return true, nil
+ }
+ case reflect.Slice:
+ slice := reflect.MakeSlice(value.Type(), len(files), len(files))
+ isSetted, err = setArrayOfMultipartFormFiles(slice, field, files)
+ if err != nil || !isSetted {
+ return isSetted, err
+ }
+ value.Set(slice)
+ return true, nil
+ case reflect.Array:
+ return setArrayOfMultipartFormFiles(value, field, files)
+ }
+ return false, errors.New("unsupported field type for multipart.FileHeader")
+}
+
+func setArrayOfMultipartFormFiles(value reflect.Value, field reflect.StructField, files []*multipart.FileHeader) (isSetted bool, err error) {
+ if value.Len() != len(files) {
+ return false, errors.New("unsupported len of array for []*multipart.FileHeader")
+ }
+ for i := range files {
+ setted, err := setByMultipartFormFile(value.Index(i), field, files[i:i+1])
+ if err != nil || !setted {
+ return setted, err
+ }
+ }
+ return true, nil
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/binding/protobuf.go b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/binding/protobuf.go
new file mode 100644
index 000000000000..f9ece928d489
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/binding/protobuf.go
@@ -0,0 +1,36 @@
+// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
+// Use of this source code is governed by a MIT style
+// license that can be found in the LICENSE file.
+
+package binding
+
+import (
+ "io/ioutil"
+ "net/http"
+
+ "github.com/golang/protobuf/proto"
+)
+
+type protobufBinding struct{}
+
+func (protobufBinding) Name() string {
+ return "protobuf"
+}
+
+func (b protobufBinding) Bind(req *http.Request, obj interface{}) error {
+ buf, err := ioutil.ReadAll(req.Body)
+ if err != nil {
+ return err
+ }
+ return b.BindBody(buf, obj)
+}
+
+func (protobufBinding) BindBody(body []byte, obj interface{}) error {
+ if err := proto.Unmarshal(body, obj.(proto.Message)); err != nil {
+ return err
+ }
+ // Here it's same to return validate(obj), but util now we can't add
+ // `binding:""` to the struct which automatically generate by gen-proto
+ return nil
+ // return validate(obj)
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/binding/query.go b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/binding/query.go
new file mode 100644
index 000000000000..219743f2a950
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/binding/query.go
@@ -0,0 +1,21 @@
+// Copyright 2017 Manu Martinez-Almeida. All rights reserved.
+// Use of this source code is governed by a MIT style
+// license that can be found in the LICENSE file.
+
+package binding
+
+import "net/http"
+
+type queryBinding struct{}
+
+func (queryBinding) Name() string {
+ return "query"
+}
+
+func (queryBinding) Bind(req *http.Request, obj interface{}) error {
+ values := req.URL.Query()
+ if err := mapForm(obj, values); err != nil {
+ return err
+ }
+ return validate(obj)
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/binding/uri.go b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/binding/uri.go
new file mode 100644
index 000000000000..f91ec3819942
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/binding/uri.go
@@ -0,0 +1,18 @@
+// Copyright 2018 Gin Core Team. All rights reserved.
+// Use of this source code is governed by a MIT style
+// license that can be found in the LICENSE file.
+
+package binding
+
+type uriBinding struct{}
+
+func (uriBinding) Name() string {
+ return "uri"
+}
+
+func (uriBinding) BindUri(m map[string][]string, obj interface{}) error {
+ if err := mapUri(obj, m); err != nil {
+ return err
+ }
+ return validate(obj)
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/binding/xml.go b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/binding/xml.go
new file mode 100644
index 000000000000..4e9011496260
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/binding/xml.go
@@ -0,0 +1,33 @@
+// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
+// Use of this source code is governed by a MIT style
+// license that can be found in the LICENSE file.
+
+package binding
+
+import (
+ "bytes"
+ "encoding/xml"
+ "io"
+ "net/http"
+)
+
+type xmlBinding struct{}
+
+func (xmlBinding) Name() string {
+ return "xml"
+}
+
+func (xmlBinding) Bind(req *http.Request, obj interface{}) error {
+ return decodeXML(req.Body, obj)
+}
+
+func (xmlBinding) BindBody(body []byte, obj interface{}) error {
+ return decodeXML(bytes.NewReader(body), obj)
+}
+func decodeXML(r io.Reader, obj interface{}) error {
+ decoder := xml.NewDecoder(r)
+ if err := decoder.Decode(obj); err != nil {
+ return err
+ }
+ return validate(obj)
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/binding/yaml.go b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/binding/yaml.go
new file mode 100644
index 000000000000..a2d36d6a549f
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/binding/yaml.go
@@ -0,0 +1,35 @@
+// Copyright 2018 Gin Core Team. All rights reserved.
+// Use of this source code is governed by a MIT style
+// license that can be found in the LICENSE file.
+
+package binding
+
+import (
+ "bytes"
+ "io"
+ "net/http"
+
+ "gopkg.in/yaml.v2"
+)
+
+type yamlBinding struct{}
+
+func (yamlBinding) Name() string {
+ return "yaml"
+}
+
+func (yamlBinding) Bind(req *http.Request, obj interface{}) error {
+ return decodeYAML(req.Body, obj)
+}
+
+func (yamlBinding) BindBody(body []byte, obj interface{}) error {
+ return decodeYAML(bytes.NewReader(body), obj)
+}
+
+func decodeYAML(r io.Reader, obj interface{}) error {
+ decoder := yaml.NewDecoder(r)
+ if err := decoder.Decode(obj); err != nil {
+ return err
+ }
+ return validate(obj)
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/codecov.yml b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/codecov.yml
new file mode 100644
index 000000000000..c9c9a522da9f
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/codecov.yml
@@ -0,0 +1,5 @@
+coverage:
+ notify:
+ gitter:
+ default:
+ url: https://webhooks.gitter.im/e/d90dcdeeab2f1e357165
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/context.go b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/context.go
new file mode 100644
index 000000000000..220d1bc7b454
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/context.go
@@ -0,0 +1,1192 @@
+// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
+// Use of this source code is governed by a MIT style
+// license that can be found in the LICENSE file.
+
+package gin
+
+import (
+ "errors"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "log"
+ "math"
+ "mime/multipart"
+ "net"
+ "net/http"
+ "net/url"
+ "os"
+ "strings"
+ "sync"
+ "time"
+
+ "github.com/gin-contrib/sse"
+ "github.com/gin-gonic/gin/binding"
+ "github.com/gin-gonic/gin/render"
+)
+
+// Content-Type MIME of the most common data formats.
+const (
+ MIMEJSON = binding.MIMEJSON
+ MIMEHTML = binding.MIMEHTML
+ MIMEXML = binding.MIMEXML
+ MIMEXML2 = binding.MIMEXML2
+ MIMEPlain = binding.MIMEPlain
+ MIMEPOSTForm = binding.MIMEPOSTForm
+ MIMEMultipartPOSTForm = binding.MIMEMultipartPOSTForm
+ MIMEYAML = binding.MIMEYAML
+)
+
+// BodyBytesKey indicates a default body bytes key.
+const BodyBytesKey = "_gin-gonic/gin/bodybyteskey"
+
+const abortIndex int8 = math.MaxInt8 / 2
+
+// Context is the most important part of gin. It allows us to pass variables between middleware,
+// manage the flow, validate the JSON of a request and render a JSON response for example.
+type Context struct {
+ writermem responseWriter
+ Request *http.Request
+ Writer ResponseWriter
+
+ Params Params
+ handlers HandlersChain
+ index int8
+ fullPath string
+
+ engine *Engine
+ params *Params
+ skippedNodes *[]skippedNode
+
+ // This mutex protect Keys map
+ mu sync.RWMutex
+
+ // Keys is a key/value pair exclusively for the context of each request.
+ Keys map[string]interface{}
+
+ // Errors is a list of errors attached to all the handlers/middlewares who used this context.
+ Errors errorMsgs
+
+ // Accepted defines a list of manually accepted formats for content negotiation.
+ Accepted []string
+
+ // queryCache use url.ParseQuery cached the param query result from c.Request.URL.Query()
+ queryCache url.Values
+
+ // formCache use url.ParseQuery cached PostForm contains the parsed form data from POST, PATCH,
+ // or PUT body parameters.
+ formCache url.Values
+
+ // SameSite allows a server to define a cookie attribute making it impossible for
+ // the browser to send this cookie along with cross-site requests.
+ sameSite http.SameSite
+}
+
+/************************************/
+/********** CONTEXT CREATION ********/
+/************************************/
+
+func (c *Context) reset() {
+ c.Writer = &c.writermem
+ c.Params = c.Params[0:0]
+ c.handlers = nil
+ c.index = -1
+
+ c.fullPath = ""
+ c.Keys = nil
+ c.Errors = c.Errors[0:0]
+ c.Accepted = nil
+ c.queryCache = nil
+ c.formCache = nil
+ *c.params = (*c.params)[:0]
+ *c.skippedNodes = (*c.skippedNodes)[:0]
+}
+
+// Copy returns a copy of the current context that can be safely used outside the request's scope.
+// This has to be used when the context has to be passed to a goroutine.
+func (c *Context) Copy() *Context {
+ cp := Context{
+ writermem: c.writermem,
+ Request: c.Request,
+ Params: c.Params,
+ engine: c.engine,
+ }
+ cp.writermem.ResponseWriter = nil
+ cp.Writer = &cp.writermem
+ cp.index = abortIndex
+ cp.handlers = nil
+ cp.Keys = map[string]interface{}{}
+ for k, v := range c.Keys {
+ cp.Keys[k] = v
+ }
+ paramCopy := make([]Param, len(cp.Params))
+ copy(paramCopy, cp.Params)
+ cp.Params = paramCopy
+ return &cp
+}
+
+// HandlerName returns the main handler's name. For example if the handler is "handleGetUsers()",
+// this function will return "main.handleGetUsers".
+func (c *Context) HandlerName() string {
+ return nameOfFunction(c.handlers.Last())
+}
+
+// HandlerNames returns a list of all registered handlers for this context in descending order,
+// following the semantics of HandlerName()
+func (c *Context) HandlerNames() []string {
+ hn := make([]string, 0, len(c.handlers))
+ for _, val := range c.handlers {
+ hn = append(hn, nameOfFunction(val))
+ }
+ return hn
+}
+
+// Handler returns the main handler.
+func (c *Context) Handler() HandlerFunc {
+ return c.handlers.Last()
+}
+
+// FullPath returns a matched route full path. For not found routes
+// returns an empty string.
+// router.GET("/user/:id", func(c *gin.Context) {
+// c.FullPath() == "/user/:id" // true
+// })
+func (c *Context) FullPath() string {
+ return c.fullPath
+}
+
+/************************************/
+/*********** FLOW CONTROL ***********/
+/************************************/
+
+// Next should be used only inside middleware.
+// It executes the pending handlers in the chain inside the calling handler.
+// See example in GitHub.
+func (c *Context) Next() {
+ c.index++
+ for c.index < int8(len(c.handlers)) {
+ c.handlers[c.index](c)
+ c.index++
+ }
+}
+
+// IsAborted returns true if the current context was aborted.
+func (c *Context) IsAborted() bool {
+ return c.index >= abortIndex
+}
+
+// Abort prevents pending handlers from being called. Note that this will not stop the current handler.
+// Let's say you have an authorization middleware that validates that the current request is authorized.
+// If the authorization fails (ex: the password does not match), call Abort to ensure the remaining handlers
+// for this request are not called.
+func (c *Context) Abort() {
+ c.index = abortIndex
+}
+
+// AbortWithStatus calls `Abort()` and writes the headers with the specified status code.
+// For example, a failed attempt to authenticate a request could use: context.AbortWithStatus(401).
+func (c *Context) AbortWithStatus(code int) {
+ c.Status(code)
+ c.Writer.WriteHeaderNow()
+ c.Abort()
+}
+
+// AbortWithStatusJSON calls `Abort()` and then `JSON` internally.
+// This method stops the chain, writes the status code and return a JSON body.
+// It also sets the Content-Type as "application/json".
+func (c *Context) AbortWithStatusJSON(code int, jsonObj interface{}) {
+ c.Abort()
+ c.JSON(code, jsonObj)
+}
+
+// AbortWithError calls `AbortWithStatus()` and `Error()` internally.
+// This method stops the chain, writes the status code and pushes the specified error to `c.Errors`.
+// See Context.Error() for more details.
+func (c *Context) AbortWithError(code int, err error) *Error {
+ c.AbortWithStatus(code)
+ return c.Error(err)
+}
+
+/************************************/
+/********* ERROR MANAGEMENT *********/
+/************************************/
+
+// Error attaches an error to the current context. The error is pushed to a list of errors.
+// It's a good idea to call Error for each error that occurred during the resolution of a request.
+// A middleware can be used to collect all the errors and push them to a database together,
+// print a log, or append it in the HTTP response.
+// Error will panic if err is nil.
+func (c *Context) Error(err error) *Error {
+ if err == nil {
+ panic("err is nil")
+ }
+
+ parsedError, ok := err.(*Error)
+ if !ok {
+ parsedError = &Error{
+ Err: err,
+ Type: ErrorTypePrivate,
+ }
+ }
+
+ c.Errors = append(c.Errors, parsedError)
+ return parsedError
+}
+
+/************************************/
+/******** METADATA MANAGEMENT********/
+/************************************/
+
+// Set is used to store a new key/value pair exclusively for this context.
+// It also lazy initializes c.Keys if it was not used previously.
+func (c *Context) Set(key string, value interface{}) {
+ c.mu.Lock()
+ if c.Keys == nil {
+ c.Keys = make(map[string]interface{})
+ }
+
+ c.Keys[key] = value
+ c.mu.Unlock()
+}
+
+// Get returns the value for the given key, ie: (value, true).
+// If the value does not exists it returns (nil, false)
+func (c *Context) Get(key string) (value interface{}, exists bool) {
+ c.mu.RLock()
+ value, exists = c.Keys[key]
+ c.mu.RUnlock()
+ return
+}
+
+// MustGet returns the value for the given key if it exists, otherwise it panics.
+func (c *Context) MustGet(key string) interface{} {
+ if value, exists := c.Get(key); exists {
+ return value
+ }
+ panic("Key \"" + key + "\" does not exist")
+}
+
+// GetString returns the value associated with the key as a string.
+func (c *Context) GetString(key string) (s string) {
+ if val, ok := c.Get(key); ok && val != nil {
+ s, _ = val.(string)
+ }
+ return
+}
+
+// GetBool returns the value associated with the key as a boolean.
+func (c *Context) GetBool(key string) (b bool) {
+ if val, ok := c.Get(key); ok && val != nil {
+ b, _ = val.(bool)
+ }
+ return
+}
+
+// GetInt returns the value associated with the key as an integer.
+func (c *Context) GetInt(key string) (i int) {
+ if val, ok := c.Get(key); ok && val != nil {
+ i, _ = val.(int)
+ }
+ return
+}
+
+// GetInt64 returns the value associated with the key as an integer.
+func (c *Context) GetInt64(key string) (i64 int64) {
+ if val, ok := c.Get(key); ok && val != nil {
+ i64, _ = val.(int64)
+ }
+ return
+}
+
+// GetUint returns the value associated with the key as an unsigned integer.
+func (c *Context) GetUint(key string) (ui uint) {
+ if val, ok := c.Get(key); ok && val != nil {
+ ui, _ = val.(uint)
+ }
+ return
+}
+
+// GetUint64 returns the value associated with the key as an unsigned integer.
+func (c *Context) GetUint64(key string) (ui64 uint64) {
+ if val, ok := c.Get(key); ok && val != nil {
+ ui64, _ = val.(uint64)
+ }
+ return
+}
+
+// GetFloat64 returns the value associated with the key as a float64.
+func (c *Context) GetFloat64(key string) (f64 float64) {
+ if val, ok := c.Get(key); ok && val != nil {
+ f64, _ = val.(float64)
+ }
+ return
+}
+
+// GetTime returns the value associated with the key as time.
+func (c *Context) GetTime(key string) (t time.Time) {
+ if val, ok := c.Get(key); ok && val != nil {
+ t, _ = val.(time.Time)
+ }
+ return
+}
+
+// GetDuration returns the value associated with the key as a duration.
+func (c *Context) GetDuration(key string) (d time.Duration) {
+ if val, ok := c.Get(key); ok && val != nil {
+ d, _ = val.(time.Duration)
+ }
+ return
+}
+
+// GetStringSlice returns the value associated with the key as a slice of strings.
+func (c *Context) GetStringSlice(key string) (ss []string) {
+ if val, ok := c.Get(key); ok && val != nil {
+ ss, _ = val.([]string)
+ }
+ return
+}
+
+// GetStringMap returns the value associated with the key as a map of interfaces.
+func (c *Context) GetStringMap(key string) (sm map[string]interface{}) {
+ if val, ok := c.Get(key); ok && val != nil {
+ sm, _ = val.(map[string]interface{})
+ }
+ return
+}
+
+// GetStringMapString returns the value associated with the key as a map of strings.
+func (c *Context) GetStringMapString(key string) (sms map[string]string) {
+ if val, ok := c.Get(key); ok && val != nil {
+ sms, _ = val.(map[string]string)
+ }
+ return
+}
+
+// GetStringMapStringSlice returns the value associated with the key as a map to a slice of strings.
+func (c *Context) GetStringMapStringSlice(key string) (smss map[string][]string) {
+ if val, ok := c.Get(key); ok && val != nil {
+ smss, _ = val.(map[string][]string)
+ }
+ return
+}
+
+/************************************/
+/************ INPUT DATA ************/
+/************************************/
+
+// Param returns the value of the URL param.
+// It is a shortcut for c.Params.ByName(key)
+// router.GET("/user/:id", func(c *gin.Context) {
+// // a GET request to /user/john
+// id := c.Param("id") // id == "john"
+// })
+func (c *Context) Param(key string) string {
+ return c.Params.ByName(key)
+}
+
+// Query returns the keyed url query value if it exists,
+// otherwise it returns an empty string `("")`.
+// It is shortcut for `c.Request.URL.Query().Get(key)`
+// GET /path?id=1234&name=Manu&value=
+// c.Query("id") == "1234"
+// c.Query("name") == "Manu"
+// c.Query("value") == ""
+// c.Query("wtf") == ""
+func (c *Context) Query(key string) string {
+ value, _ := c.GetQuery(key)
+ return value
+}
+
+// DefaultQuery returns the keyed url query value if it exists,
+// otherwise it returns the specified defaultValue string.
+// See: Query() and GetQuery() for further information.
+// GET /?name=Manu&lastname=
+// c.DefaultQuery("name", "unknown") == "Manu"
+// c.DefaultQuery("id", "none") == "none"
+// c.DefaultQuery("lastname", "none") == ""
+func (c *Context) DefaultQuery(key, defaultValue string) string {
+ if value, ok := c.GetQuery(key); ok {
+ return value
+ }
+ return defaultValue
+}
+
+// GetQuery is like Query(), it returns the keyed url query value
+// if it exists `(value, true)` (even when the value is an empty string),
+// otherwise it returns `("", false)`.
+// It is shortcut for `c.Request.URL.Query().Get(key)`
+// GET /?name=Manu&lastname=
+// ("Manu", true) == c.GetQuery("name")
+// ("", false) == c.GetQuery("id")
+// ("", true) == c.GetQuery("lastname")
+func (c *Context) GetQuery(key string) (string, bool) {
+ if values, ok := c.GetQueryArray(key); ok {
+ return values[0], ok
+ }
+ return "", false
+}
+
+// QueryArray returns a slice of strings for a given query key.
+// The length of the slice depends on the number of params with the given key.
+func (c *Context) QueryArray(key string) []string {
+ values, _ := c.GetQueryArray(key)
+ return values
+}
+
+func (c *Context) initQueryCache() {
+ if c.queryCache == nil {
+ if c.Request != nil {
+ c.queryCache = c.Request.URL.Query()
+ } else {
+ c.queryCache = url.Values{}
+ }
+ }
+}
+
+// GetQueryArray returns a slice of strings for a given query key, plus
+// a boolean value whether at least one value exists for the given key.
+func (c *Context) GetQueryArray(key string) ([]string, bool) {
+ c.initQueryCache()
+ if values, ok := c.queryCache[key]; ok && len(values) > 0 {
+ return values, true
+ }
+ return []string{}, false
+}
+
+// QueryMap returns a map for a given query key.
+func (c *Context) QueryMap(key string) map[string]string {
+ dicts, _ := c.GetQueryMap(key)
+ return dicts
+}
+
+// GetQueryMap returns a map for a given query key, plus a boolean value
+// whether at least one value exists for the given key.
+func (c *Context) GetQueryMap(key string) (map[string]string, bool) {
+ c.initQueryCache()
+ return c.get(c.queryCache, key)
+}
+
+// PostForm returns the specified key from a POST urlencoded form or multipart form
+// when it exists, otherwise it returns an empty string `("")`.
+func (c *Context) PostForm(key string) string {
+ value, _ := c.GetPostForm(key)
+ return value
+}
+
+// DefaultPostForm returns the specified key from a POST urlencoded form or multipart form
+// when it exists, otherwise it returns the specified defaultValue string.
+// See: PostForm() and GetPostForm() for further information.
+func (c *Context) DefaultPostForm(key, defaultValue string) string {
+ if value, ok := c.GetPostForm(key); ok {
+ return value
+ }
+ return defaultValue
+}
+
+// GetPostForm is like PostForm(key). It returns the specified key from a POST urlencoded
+// form or multipart form when it exists `(value, true)` (even when the value is an empty string),
+// otherwise it returns ("", false).
+// For example, during a PATCH request to update the user's email:
+// email=mail@example.com --> ("mail@example.com", true) := GetPostForm("email") // set email to "mail@example.com"
+// email= --> ("", true) := GetPostForm("email") // set email to ""
+// --> ("", false) := GetPostForm("email") // do nothing with email
+func (c *Context) GetPostForm(key string) (string, bool) {
+ if values, ok := c.GetPostFormArray(key); ok {
+ return values[0], ok
+ }
+ return "", false
+}
+
+// PostFormArray returns a slice of strings for a given form key.
+// The length of the slice depends on the number of params with the given key.
+func (c *Context) PostFormArray(key string) []string {
+ values, _ := c.GetPostFormArray(key)
+ return values
+}
+
+func (c *Context) initFormCache() {
+ if c.formCache == nil {
+ c.formCache = make(url.Values)
+ req := c.Request
+ if err := req.ParseMultipartForm(c.engine.MaxMultipartMemory); err != nil {
+ if err != http.ErrNotMultipart {
+ debugPrint("error on parse multipart form array: %v", err)
+ }
+ }
+ c.formCache = req.PostForm
+ }
+}
+
+// GetPostFormArray returns a slice of strings for a given form key, plus
+// a boolean value whether at least one value exists for the given key.
+func (c *Context) GetPostFormArray(key string) ([]string, bool) {
+ c.initFormCache()
+ if values := c.formCache[key]; len(values) > 0 {
+ return values, true
+ }
+ return []string{}, false
+}
+
+// PostFormMap returns a map for a given form key.
+func (c *Context) PostFormMap(key string) map[string]string {
+ dicts, _ := c.GetPostFormMap(key)
+ return dicts
+}
+
+// GetPostFormMap returns a map for a given form key, plus a boolean value
+// whether at least one value exists for the given key.
+func (c *Context) GetPostFormMap(key string) (map[string]string, bool) {
+ c.initFormCache()
+ return c.get(c.formCache, key)
+}
+
+// get is an internal method and returns a map which satisfy conditions.
+func (c *Context) get(m map[string][]string, key string) (map[string]string, bool) {
+ dicts := make(map[string]string)
+ exist := false
+ for k, v := range m {
+ if i := strings.IndexByte(k, '['); i >= 1 && k[0:i] == key {
+ if j := strings.IndexByte(k[i+1:], ']'); j >= 1 {
+ exist = true
+ dicts[k[i+1:][:j]] = v[0]
+ }
+ }
+ }
+ return dicts, exist
+}
+
+// FormFile returns the first file for the provided form key.
+func (c *Context) FormFile(name string) (*multipart.FileHeader, error) {
+ if c.Request.MultipartForm == nil {
+ if err := c.Request.ParseMultipartForm(c.engine.MaxMultipartMemory); err != nil {
+ return nil, err
+ }
+ }
+ f, fh, err := c.Request.FormFile(name)
+ if err != nil {
+ return nil, err
+ }
+ f.Close()
+ return fh, err
+}
+
+// MultipartForm is the parsed multipart form, including file uploads.
+func (c *Context) MultipartForm() (*multipart.Form, error) {
+ err := c.Request.ParseMultipartForm(c.engine.MaxMultipartMemory)
+ return c.Request.MultipartForm, err
+}
+
+// SaveUploadedFile uploads the form file to specific dst.
+func (c *Context) SaveUploadedFile(file *multipart.FileHeader, dst string) error {
+ src, err := file.Open()
+ if err != nil {
+ return err
+ }
+ defer src.Close()
+
+ out, err := os.Create(dst)
+ if err != nil {
+ return err
+ }
+ defer out.Close()
+
+ _, err = io.Copy(out, src)
+ return err
+}
+
+// Bind checks the Content-Type to select a binding engine automatically,
+// Depending the "Content-Type" header different bindings are used:
+// "application/json" --> JSON binding
+// "application/xml" --> XML binding
+// otherwise --> returns an error.
+// It parses the request's body as JSON if Content-Type == "application/json" using JSON or XML as a JSON input.
+// It decodes the json payload into the struct specified as a pointer.
+// It writes a 400 error and sets Content-Type header "text/plain" in the response if input is not valid.
+func (c *Context) Bind(obj interface{}) error {
+ b := binding.Default(c.Request.Method, c.ContentType())
+ return c.MustBindWith(obj, b)
+}
+
+// BindJSON is a shortcut for c.MustBindWith(obj, binding.JSON).
+func (c *Context) BindJSON(obj interface{}) error {
+ return c.MustBindWith(obj, binding.JSON)
+}
+
+// BindXML is a shortcut for c.MustBindWith(obj, binding.BindXML).
+func (c *Context) BindXML(obj interface{}) error {
+ return c.MustBindWith(obj, binding.XML)
+}
+
+// BindQuery is a shortcut for c.MustBindWith(obj, binding.Query).
+func (c *Context) BindQuery(obj interface{}) error {
+ return c.MustBindWith(obj, binding.Query)
+}
+
+// BindYAML is a shortcut for c.MustBindWith(obj, binding.YAML).
+func (c *Context) BindYAML(obj interface{}) error {
+ return c.MustBindWith(obj, binding.YAML)
+}
+
+// BindHeader is a shortcut for c.MustBindWith(obj, binding.Header).
+func (c *Context) BindHeader(obj interface{}) error {
+ return c.MustBindWith(obj, binding.Header)
+}
+
+// BindUri binds the passed struct pointer using binding.Uri.
+// It will abort the request with HTTP 400 if any error occurs.
+func (c *Context) BindUri(obj interface{}) error {
+ if err := c.ShouldBindUri(obj); err != nil {
+ c.AbortWithError(http.StatusBadRequest, err).SetType(ErrorTypeBind) // nolint: errcheck
+ return err
+ }
+ return nil
+}
+
+// MustBindWith binds the passed struct pointer using the specified binding engine.
+// It will abort the request with HTTP 400 if any error occurs.
+// See the binding package.
+func (c *Context) MustBindWith(obj interface{}, b binding.Binding) error {
+ if err := c.ShouldBindWith(obj, b); err != nil {
+ c.AbortWithError(http.StatusBadRequest, err).SetType(ErrorTypeBind) // nolint: errcheck
+ return err
+ }
+ return nil
+}
+
+// ShouldBind checks the Content-Type to select a binding engine automatically,
+// Depending the "Content-Type" header different bindings are used:
+// "application/json" --> JSON binding
+// "application/xml" --> XML binding
+// otherwise --> returns an error
+// It parses the request's body as JSON if Content-Type == "application/json" using JSON or XML as a JSON input.
+// It decodes the json payload into the struct specified as a pointer.
+// Like c.Bind() but this method does not set the response status code to 400 and abort if the json is not valid.
+func (c *Context) ShouldBind(obj interface{}) error {
+ b := binding.Default(c.Request.Method, c.ContentType())
+ return c.ShouldBindWith(obj, b)
+}
+
+// ShouldBindJSON is a shortcut for c.ShouldBindWith(obj, binding.JSON).
+func (c *Context) ShouldBindJSON(obj interface{}) error {
+ return c.ShouldBindWith(obj, binding.JSON)
+}
+
+// ShouldBindXML is a shortcut for c.ShouldBindWith(obj, binding.XML).
+func (c *Context) ShouldBindXML(obj interface{}) error {
+ return c.ShouldBindWith(obj, binding.XML)
+}
+
+// ShouldBindQuery is a shortcut for c.ShouldBindWith(obj, binding.Query).
+func (c *Context) ShouldBindQuery(obj interface{}) error {
+ return c.ShouldBindWith(obj, binding.Query)
+}
+
+// ShouldBindYAML is a shortcut for c.ShouldBindWith(obj, binding.YAML).
+func (c *Context) ShouldBindYAML(obj interface{}) error {
+ return c.ShouldBindWith(obj, binding.YAML)
+}
+
+// ShouldBindHeader is a shortcut for c.ShouldBindWith(obj, binding.Header).
+func (c *Context) ShouldBindHeader(obj interface{}) error {
+ return c.ShouldBindWith(obj, binding.Header)
+}
+
+// ShouldBindUri binds the passed struct pointer using the specified binding engine.
+func (c *Context) ShouldBindUri(obj interface{}) error {
+ m := make(map[string][]string)
+ for _, v := range c.Params {
+ m[v.Key] = []string{v.Value}
+ }
+ return binding.Uri.BindUri(m, obj)
+}
+
+// ShouldBindWith binds the passed struct pointer using the specified binding engine.
+// See the binding package.
+func (c *Context) ShouldBindWith(obj interface{}, b binding.Binding) error {
+ return b.Bind(c.Request, obj)
+}
+
+// ShouldBindBodyWith is similar with ShouldBindWith, but it stores the request
+// body into the context, and reuse when it is called again.
+//
+// NOTE: This method reads the body before binding. So you should use
+// ShouldBindWith for better performance if you need to call only once.
+func (c *Context) ShouldBindBodyWith(obj interface{}, bb binding.BindingBody) (err error) {
+ var body []byte
+ if cb, ok := c.Get(BodyBytesKey); ok {
+ if cbb, ok := cb.([]byte); ok {
+ body = cbb
+ }
+ }
+ if body == nil {
+ body, err = ioutil.ReadAll(c.Request.Body)
+ if err != nil {
+ return err
+ }
+ c.Set(BodyBytesKey, body)
+ }
+ return bb.BindBody(body, obj)
+}
+
+// ClientIP implements one best effort algorithm to return the real client IP.
+// It called c.RemoteIP() under the hood, to check if the remote IP is a trusted proxy or not.
+// If it is it will then try to parse the headers defined in Engine.RemoteIPHeaders (defaulting to [X-Forwarded-For, X-Real-Ip]).
+// If the headers are not syntactically valid OR the remote IP does not correspond to a trusted proxy,
+// the remote IP (coming form Request.RemoteAddr) is returned.
+func (c *Context) ClientIP() string {
+ // Check if we're running on a trusted platform, continue running backwards if error
+ if c.engine.TrustedPlatform != "" {
+ // Developers can define their own header of Trusted Platform or use predefined constants
+ if addr := c.requestHeader(c.engine.TrustedPlatform); addr != "" {
+ return addr
+ }
+ }
+
+ // Legacy "AppEngine" flag
+ if c.engine.AppEngine {
+ log.Println(`The AppEngine flag is going to be deprecated. Please check issues #2723 and #2739 and use 'TrustedPlatform: gin.PlatformGoogleAppEngine' instead.`)
+ if addr := c.requestHeader("X-Appengine-Remote-Addr"); addr != "" {
+ return addr
+ }
+ }
+
+ remoteIP, trusted := c.RemoteIP()
+ if remoteIP == nil {
+ return ""
+ }
+
+ if trusted && c.engine.ForwardedByClientIP && c.engine.RemoteIPHeaders != nil {
+ for _, headerName := range c.engine.RemoteIPHeaders {
+ ip, valid := c.engine.validateHeader(c.requestHeader(headerName))
+ if valid {
+ return ip
+ }
+ }
+ }
+ return remoteIP.String()
+}
+
+func (e *Engine) isTrustedProxy(ip net.IP) bool {
+ if e.trustedCIDRs != nil {
+ for _, cidr := range e.trustedCIDRs {
+ if cidr.Contains(ip) {
+ return true
+ }
+ }
+ }
+ return false
+}
+
+// RemoteIP parses the IP from Request.RemoteAddr, normalizes and returns the IP (without the port).
+// It also checks if the remoteIP is a trusted proxy or not.
+// In order to perform this validation, it will see if the IP is contained within at least one of the CIDR blocks
+// defined by Engine.SetTrustedProxies()
+func (c *Context) RemoteIP() (net.IP, bool) {
+ ip, _, err := net.SplitHostPort(strings.TrimSpace(c.Request.RemoteAddr))
+ if err != nil {
+ return nil, false
+ }
+ remoteIP := net.ParseIP(ip)
+ if remoteIP == nil {
+ return nil, false
+ }
+
+ return remoteIP, c.engine.isTrustedProxy(remoteIP)
+}
+
+func (e *Engine) validateHeader(header string) (clientIP string, valid bool) {
+ if header == "" {
+ return "", false
+ }
+ items := strings.Split(header, ",")
+ for i := len(items) - 1; i >= 0; i-- {
+ ipStr := strings.TrimSpace(items[i])
+ ip := net.ParseIP(ipStr)
+ if ip == nil {
+ return "", false
+ }
+
+ // X-Forwarded-For is appended by proxy
+ // Check IPs in reverse order and stop when find untrusted proxy
+ if (i == 0) || (!e.isTrustedProxy(ip)) {
+ return ipStr, true
+ }
+ }
+ return
+}
+
+// ContentType returns the Content-Type header of the request.
+func (c *Context) ContentType() string {
+ return filterFlags(c.requestHeader("Content-Type"))
+}
+
+// IsWebsocket returns true if the request headers indicate that a websocket
+// handshake is being initiated by the client.
+func (c *Context) IsWebsocket() bool {
+ if strings.Contains(strings.ToLower(c.requestHeader("Connection")), "upgrade") &&
+ strings.EqualFold(c.requestHeader("Upgrade"), "websocket") {
+ return true
+ }
+ return false
+}
+
+func (c *Context) requestHeader(key string) string {
+ return c.Request.Header.Get(key)
+}
+
+/************************************/
+/******** RESPONSE RENDERING ********/
+/************************************/
+
+// bodyAllowedForStatus is a copy of http.bodyAllowedForStatus non-exported function.
+func bodyAllowedForStatus(status int) bool {
+ switch {
+ case status >= 100 && status <= 199:
+ return false
+ case status == http.StatusNoContent:
+ return false
+ case status == http.StatusNotModified:
+ return false
+ }
+ return true
+}
+
+// Status sets the HTTP response code.
+func (c *Context) Status(code int) {
+ c.Writer.WriteHeader(code)
+}
+
+// Header is a intelligent shortcut for c.Writer.Header().Set(key, value).
+// It writes a header in the response.
+// If value == "", this method removes the header `c.Writer.Header().Del(key)`
+func (c *Context) Header(key, value string) {
+ if value == "" {
+ c.Writer.Header().Del(key)
+ return
+ }
+ c.Writer.Header().Set(key, value)
+}
+
+// GetHeader returns value from request headers.
+func (c *Context) GetHeader(key string) string {
+ return c.requestHeader(key)
+}
+
+// GetRawData return stream data.
+func (c *Context) GetRawData() ([]byte, error) {
+ return ioutil.ReadAll(c.Request.Body)
+}
+
+// SetSameSite with cookie
+func (c *Context) SetSameSite(samesite http.SameSite) {
+ c.sameSite = samesite
+}
+
+// SetCookie adds a Set-Cookie header to the ResponseWriter's headers.
+// The provided cookie must have a valid Name. Invalid cookies may be
+// silently dropped.
+func (c *Context) SetCookie(name, value string, maxAge int, path, domain string, secure, httpOnly bool) {
+ if path == "" {
+ path = "/"
+ }
+ http.SetCookie(c.Writer, &http.Cookie{
+ Name: name,
+ Value: url.QueryEscape(value),
+ MaxAge: maxAge,
+ Path: path,
+ Domain: domain,
+ SameSite: c.sameSite,
+ Secure: secure,
+ HttpOnly: httpOnly,
+ })
+}
+
+// Cookie returns the named cookie provided in the request or
+// ErrNoCookie if not found. And return the named cookie is unescaped.
+// If multiple cookies match the given name, only one cookie will
+// be returned.
+func (c *Context) Cookie(name string) (string, error) {
+ cookie, err := c.Request.Cookie(name)
+ if err != nil {
+ return "", err
+ }
+ val, _ := url.QueryUnescape(cookie.Value)
+ return val, nil
+}
+
+// Render writes the response headers and calls render.Render to render data.
+func (c *Context) Render(code int, r render.Render) {
+ c.Status(code)
+
+ if !bodyAllowedForStatus(code) {
+ r.WriteContentType(c.Writer)
+ c.Writer.WriteHeaderNow()
+ return
+ }
+
+ if err := r.Render(c.Writer); err != nil {
+ panic(err)
+ }
+}
+
+// HTML renders the HTTP template specified by its file name.
+// It also updates the HTTP code and sets the Content-Type as "text/html".
+// See http://golang.org/doc/articles/wiki/
+func (c *Context) HTML(code int, name string, obj interface{}) {
+ instance := c.engine.HTMLRender.Instance(name, obj)
+ c.Render(code, instance)
+}
+
+// IndentedJSON serializes the given struct as pretty JSON (indented + endlines) into the response body.
+// It also sets the Content-Type as "application/json".
+// WARNING: we recommend to use this only for development purposes since printing pretty JSON is
+// more CPU and bandwidth consuming. Use Context.JSON() instead.
+func (c *Context) IndentedJSON(code int, obj interface{}) {
+ c.Render(code, render.IndentedJSON{Data: obj})
+}
+
+// SecureJSON serializes the given struct as Secure JSON into the response body.
+// Default prepends "while(1)," to response body if the given struct is array values.
+// It also sets the Content-Type as "application/json".
+func (c *Context) SecureJSON(code int, obj interface{}) {
+ c.Render(code, render.SecureJSON{Prefix: c.engine.secureJSONPrefix, Data: obj})
+}
+
+// JSONP serializes the given struct as JSON into the response body.
+// It adds padding to response body to request data from a server residing in a different domain than the client.
+// It also sets the Content-Type as "application/javascript".
+func (c *Context) JSONP(code int, obj interface{}) {
+ callback := c.DefaultQuery("callback", "")
+ if callback == "" {
+ c.Render(code, render.JSON{Data: obj})
+ return
+ }
+ c.Render(code, render.JsonpJSON{Callback: callback, Data: obj})
+}
+
+// JSON serializes the given struct as JSON into the response body.
+// It also sets the Content-Type as "application/json".
+func (c *Context) JSON(code int, obj interface{}) {
+ c.Render(code, render.JSON{Data: obj})
+}
+
+// AsciiJSON serializes the given struct as JSON into the response body with unicode to ASCII string.
+// It also sets the Content-Type as "application/json".
+func (c *Context) AsciiJSON(code int, obj interface{}) {
+ c.Render(code, render.AsciiJSON{Data: obj})
+}
+
+// PureJSON serializes the given struct as JSON into the response body.
+// PureJSON, unlike JSON, does not replace special html characters with their unicode entities.
+func (c *Context) PureJSON(code int, obj interface{}) {
+ c.Render(code, render.PureJSON{Data: obj})
+}
+
+// XML serializes the given struct as XML into the response body.
+// It also sets the Content-Type as "application/xml".
+func (c *Context) XML(code int, obj interface{}) {
+ c.Render(code, render.XML{Data: obj})
+}
+
+// YAML serializes the given struct as YAML into the response body.
+func (c *Context) YAML(code int, obj interface{}) {
+ c.Render(code, render.YAML{Data: obj})
+}
+
+// ProtoBuf serializes the given struct as ProtoBuf into the response body.
+func (c *Context) ProtoBuf(code int, obj interface{}) {
+ c.Render(code, render.ProtoBuf{Data: obj})
+}
+
+// String writes the given string into the response body.
+func (c *Context) String(code int, format string, values ...interface{}) {
+ c.Render(code, render.String{Format: format, Data: values})
+}
+
+// Redirect returns a HTTP redirect to the specific location.
+func (c *Context) Redirect(code int, location string) {
+ c.Render(-1, render.Redirect{
+ Code: code,
+ Location: location,
+ Request: c.Request,
+ })
+}
+
+// Data writes some data into the body stream and updates the HTTP code.
+func (c *Context) Data(code int, contentType string, data []byte) {
+ c.Render(code, render.Data{
+ ContentType: contentType,
+ Data: data,
+ })
+}
+
+// DataFromReader writes the specified reader into the body stream and updates the HTTP code.
+func (c *Context) DataFromReader(code int, contentLength int64, contentType string, reader io.Reader, extraHeaders map[string]string) {
+ c.Render(code, render.Reader{
+ Headers: extraHeaders,
+ ContentType: contentType,
+ ContentLength: contentLength,
+ Reader: reader,
+ })
+}
+
+// File writes the specified file into the body stream in an efficient way.
+func (c *Context) File(filepath string) {
+ http.ServeFile(c.Writer, c.Request, filepath)
+}
+
+// FileFromFS writes the specified file from http.FileSystem into the body stream in an efficient way.
+func (c *Context) FileFromFS(filepath string, fs http.FileSystem) {
+ defer func(old string) {
+ c.Request.URL.Path = old
+ }(c.Request.URL.Path)
+
+ c.Request.URL.Path = filepath
+
+ http.FileServer(fs).ServeHTTP(c.Writer, c.Request)
+}
+
+// FileAttachment writes the specified file into the body stream in an efficient way
+// On the client side, the file will typically be downloaded with the given filename
+func (c *Context) FileAttachment(filepath, filename string) {
+ c.Writer.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", filename))
+ http.ServeFile(c.Writer, c.Request, filepath)
+}
+
+// SSEvent writes a Server-Sent Event into the body stream.
+func (c *Context) SSEvent(name string, message interface{}) {
+ c.Render(-1, sse.Event{
+ Event: name,
+ Data: message,
+ })
+}
+
+// Stream sends a streaming response and returns a boolean
+// indicates "Is client disconnected in middle of stream"
+func (c *Context) Stream(step func(w io.Writer) bool) bool {
+ w := c.Writer
+ clientGone := w.CloseNotify()
+ for {
+ select {
+ case <-clientGone:
+ return true
+ default:
+ keepOpen := step(w)
+ w.Flush()
+ if !keepOpen {
+ return false
+ }
+ }
+ }
+}
+
+/************************************/
+/******** CONTENT NEGOTIATION *******/
+/************************************/
+
+// Negotiate contains all negotiations data.
+type Negotiate struct {
+ Offered []string
+ HTMLName string
+ HTMLData interface{}
+ JSONData interface{}
+ XMLData interface{}
+ YAMLData interface{}
+ Data interface{}
+}
+
+// Negotiate calls different Render according acceptable Accept format.
+func (c *Context) Negotiate(code int, config Negotiate) {
+ switch c.NegotiateFormat(config.Offered...) {
+ case binding.MIMEJSON:
+ data := chooseData(config.JSONData, config.Data)
+ c.JSON(code, data)
+
+ case binding.MIMEHTML:
+ data := chooseData(config.HTMLData, config.Data)
+ c.HTML(code, config.HTMLName, data)
+
+ case binding.MIMEXML:
+ data := chooseData(config.XMLData, config.Data)
+ c.XML(code, data)
+
+ case binding.MIMEYAML:
+ data := chooseData(config.YAMLData, config.Data)
+ c.YAML(code, data)
+
+ default:
+ c.AbortWithError(http.StatusNotAcceptable, errors.New("the accepted formats are not offered by the server")) // nolint: errcheck
+ }
+}
+
+// NegotiateFormat returns an acceptable Accept format.
+func (c *Context) NegotiateFormat(offered ...string) string {
+ assert1(len(offered) > 0, "you must provide at least one offer")
+
+ if c.Accepted == nil {
+ c.Accepted = parseAccept(c.requestHeader("Accept"))
+ }
+ if len(c.Accepted) == 0 {
+ return offered[0]
+ }
+ for _, accepted := range c.Accepted {
+ for _, offer := range offered {
+ // According to RFC 2616 and RFC 2396, non-ASCII characters are not allowed in headers,
+ // therefore we can just iterate over the string without casting it into []rune
+ i := 0
+ for ; i < len(accepted); i++ {
+ if accepted[i] == '*' || offer[i] == '*' {
+ return offer
+ }
+ if accepted[i] != offer[i] {
+ break
+ }
+ }
+ if i == len(accepted) {
+ return offer
+ }
+ }
+ }
+ return ""
+}
+
+// SetAccepted sets Accept header data.
+func (c *Context) SetAccepted(formats ...string) {
+ c.Accepted = formats
+}
+
+/************************************/
+/***** GOLANG.ORG/X/NET/CONTEXT *****/
+/************************************/
+
+// Deadline always returns that there is no deadline (ok==false),
+// maybe you want to use Request.Context().Deadline() instead.
+func (c *Context) Deadline() (deadline time.Time, ok bool) {
+ return
+}
+
+// Done always returns nil (chan which will wait forever),
+// if you want to abort your work when the connection was closed
+// you should use Request.Context().Done() instead.
+func (c *Context) Done() <-chan struct{} {
+ return nil
+}
+
+// Err always returns nil, maybe you want to use Request.Context().Err() instead.
+func (c *Context) Err() error {
+ return nil
+}
+
+// Value returns the value associated with this context for key, or nil
+// if no value is associated with key. Successive calls to Value with
+// the same key returns the same result.
+func (c *Context) Value(key interface{}) interface{} {
+ if key == 0 {
+ return c.Request
+ }
+ if keyAsString, ok := key.(string); ok {
+ val, _ := c.Get(keyAsString)
+ return val
+ }
+ return nil
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/context_appengine.go b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/context_appengine.go
new file mode 100644
index 000000000000..8bf938961d31
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/context_appengine.go
@@ -0,0 +1,12 @@
+// Copyright 2017 Manu Martinez-Almeida. All rights reserved.
+// Use of this source code is governed by a MIT style
+// license that can be found in the LICENSE file.
+
+//go:build appengine
+// +build appengine
+
+package gin
+
+func init() {
+ defaultPlatform = PlatformGoogleAppEngine
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/debug.go b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/debug.go
new file mode 100644
index 000000000000..9bacc68571c1
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/debug.go
@@ -0,0 +1,103 @@
+// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
+// Use of this source code is governed by a MIT style
+// license that can be found in the LICENSE file.
+
+package gin
+
+import (
+ "fmt"
+ "html/template"
+ "runtime"
+ "strconv"
+ "strings"
+)
+
+const ginSupportMinGoVer = 13
+
+// IsDebugging returns true if the framework is running in debug mode.
+// Use SetMode(gin.ReleaseMode) to disable debug mode.
+func IsDebugging() bool {
+ return ginMode == debugCode
+}
+
+// DebugPrintRouteFunc indicates debug log output format.
+var DebugPrintRouteFunc func(httpMethod, absolutePath, handlerName string, nuHandlers int)
+
+func debugPrintRoute(httpMethod, absolutePath string, handlers HandlersChain) {
+ if IsDebugging() {
+ nuHandlers := len(handlers)
+ handlerName := nameOfFunction(handlers.Last())
+ if DebugPrintRouteFunc == nil {
+ debugPrint("%-6s %-25s --> %s (%d handlers)\n", httpMethod, absolutePath, handlerName, nuHandlers)
+ } else {
+ DebugPrintRouteFunc(httpMethod, absolutePath, handlerName, nuHandlers)
+ }
+ }
+}
+
+func debugPrintLoadTemplate(tmpl *template.Template) {
+ if IsDebugging() {
+ var buf strings.Builder
+ for _, tmpl := range tmpl.Templates() {
+ buf.WriteString("\t- ")
+ buf.WriteString(tmpl.Name())
+ buf.WriteString("\n")
+ }
+ debugPrint("Loaded HTML Templates (%d): \n%s\n", len(tmpl.Templates()), buf.String())
+ }
+}
+
+func debugPrint(format string, values ...interface{}) {
+ if IsDebugging() {
+ if !strings.HasSuffix(format, "\n") {
+ format += "\n"
+ }
+ fmt.Fprintf(DefaultWriter, "[GIN-debug] "+format, values...)
+ }
+}
+
+func getMinVer(v string) (uint64, error) {
+ first := strings.IndexByte(v, '.')
+ last := strings.LastIndexByte(v, '.')
+ if first == last {
+ return strconv.ParseUint(v[first+1:], 10, 64)
+ }
+ return strconv.ParseUint(v[first+1:last], 10, 64)
+}
+
+func debugPrintWARNINGDefault() {
+ if v, e := getMinVer(runtime.Version()); e == nil && v <= ginSupportMinGoVer {
+ debugPrint(`[WARNING] Now Gin requires Go 1.13+.
+
+`)
+ }
+ debugPrint(`[WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.
+
+`)
+}
+
+func debugPrintWARNINGNew() {
+ debugPrint(`[WARNING] Running in "debug" mode. Switch to "release" mode in production.
+ - using env: export GIN_MODE=release
+ - using code: gin.SetMode(gin.ReleaseMode)
+
+`)
+}
+
+func debugPrintWARNINGSetHTMLTemplate() {
+ debugPrint(`[WARNING] Since SetHTMLTemplate() is NOT thread-safe. It should only be called
+at initialization. ie. before any route is registered or the router is listening in a socket:
+
+ router := gin.Default()
+ router.SetHTMLTemplate(template) // << good place
+
+`)
+}
+
+func debugPrintError(err error) {
+ if err != nil {
+ if IsDebugging() {
+ fmt.Fprintf(DefaultErrorWriter, "[GIN-debug] [ERROR] %v\n", err)
+ }
+ }
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/deprecated.go b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/deprecated.go
new file mode 100644
index 000000000000..ab4474296e5b
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/deprecated.go
@@ -0,0 +1,21 @@
+// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
+// Use of this source code is governed by a MIT style
+// license that can be found in the LICENSE file.
+
+package gin
+
+import (
+ "log"
+
+ "github.com/gin-gonic/gin/binding"
+)
+
+// BindWith binds the passed struct pointer using the specified binding engine.
+// See the binding package.
+func (c *Context) BindWith(obj interface{}, b binding.Binding) error {
+ log.Println(`BindWith(\"interface{}, binding.Binding\") error is going to
+ be deprecated, please check issue #662 and either use MustBindWith() if you
+ want HTTP 400 to be automatically returned if any error occur, or use
+ ShouldBindWith() if you need to manage the error.`)
+ return c.MustBindWith(obj, b)
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/doc.go b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/doc.go
new file mode 100644
index 000000000000..1bd03864f17b
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/doc.go
@@ -0,0 +1,6 @@
+/*
+Package gin implements a HTTP web framework called gin.
+
+See https://gin-gonic.com/ for more information about gin.
+*/
+package gin // import "github.com/gin-gonic/gin"
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/errors.go b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/errors.go
new file mode 100644
index 000000000000..0f276c13d4c9
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/errors.go
@@ -0,0 +1,174 @@
+// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
+// Use of this source code is governed by a MIT style
+// license that can be found in the LICENSE file.
+
+package gin
+
+import (
+ "fmt"
+ "reflect"
+ "strings"
+
+ "github.com/gin-gonic/gin/internal/json"
+)
+
+// ErrorType is an unsigned 64-bit error code as defined in the gin spec.
+type ErrorType uint64
+
+const (
+ // ErrorTypeBind is used when Context.Bind() fails.
+ ErrorTypeBind ErrorType = 1 << 63
+ // ErrorTypeRender is used when Context.Render() fails.
+ ErrorTypeRender ErrorType = 1 << 62
+ // ErrorTypePrivate indicates a private error.
+ ErrorTypePrivate ErrorType = 1 << 0
+ // ErrorTypePublic indicates a public error.
+ ErrorTypePublic ErrorType = 1 << 1
+ // ErrorTypeAny indicates any other error.
+ ErrorTypeAny ErrorType = 1<<64 - 1
+ // ErrorTypeNu indicates any other error.
+ ErrorTypeNu = 2
+)
+
+// Error represents a error's specification.
+type Error struct {
+ Err error
+ Type ErrorType
+ Meta interface{}
+}
+
+type errorMsgs []*Error
+
+var _ error = &Error{}
+
+// SetType sets the error's type.
+func (msg *Error) SetType(flags ErrorType) *Error {
+ msg.Type = flags
+ return msg
+}
+
+// SetMeta sets the error's meta data.
+func (msg *Error) SetMeta(data interface{}) *Error {
+ msg.Meta = data
+ return msg
+}
+
+// JSON creates a properly formatted JSON
+func (msg *Error) JSON() interface{} {
+ jsonData := H{}
+ if msg.Meta != nil {
+ value := reflect.ValueOf(msg.Meta)
+ switch value.Kind() {
+ case reflect.Struct:
+ return msg.Meta
+ case reflect.Map:
+ for _, key := range value.MapKeys() {
+ jsonData[key.String()] = value.MapIndex(key).Interface()
+ }
+ default:
+ jsonData["meta"] = msg.Meta
+ }
+ }
+ if _, ok := jsonData["error"]; !ok {
+ jsonData["error"] = msg.Error()
+ }
+ return jsonData
+}
+
+// MarshalJSON implements the json.Marshaller interface.
+func (msg *Error) MarshalJSON() ([]byte, error) {
+ return json.Marshal(msg.JSON())
+}
+
+// Error implements the error interface.
+func (msg Error) Error() string {
+ return msg.Err.Error()
+}
+
+// IsType judges one error.
+func (msg *Error) IsType(flags ErrorType) bool {
+ return (msg.Type & flags) > 0
+}
+
+// Unwrap returns the wrapped error, to allow interoperability with errors.Is(), errors.As() and errors.Unwrap()
+func (msg *Error) Unwrap() error {
+ return msg.Err
+}
+
+// ByType returns a readonly copy filtered the byte.
+// ie ByType(gin.ErrorTypePublic) returns a slice of errors with type=ErrorTypePublic.
+func (a errorMsgs) ByType(typ ErrorType) errorMsgs {
+ if len(a) == 0 {
+ return nil
+ }
+ if typ == ErrorTypeAny {
+ return a
+ }
+ var result errorMsgs
+ for _, msg := range a {
+ if msg.IsType(typ) {
+ result = append(result, msg)
+ }
+ }
+ return result
+}
+
+// Last returns the last error in the slice. It returns nil if the array is empty.
+// Shortcut for errors[len(errors)-1].
+func (a errorMsgs) Last() *Error {
+ if length := len(a); length > 0 {
+ return a[length-1]
+ }
+ return nil
+}
+
+// Errors returns an array will all the error messages.
+// Example:
+// c.Error(errors.New("first"))
+// c.Error(errors.New("second"))
+// c.Error(errors.New("third"))
+// c.Errors.Errors() // == []string{"first", "second", "third"}
+func (a errorMsgs) Errors() []string {
+ if len(a) == 0 {
+ return nil
+ }
+ errorStrings := make([]string, len(a))
+ for i, err := range a {
+ errorStrings[i] = err.Error()
+ }
+ return errorStrings
+}
+
+func (a errorMsgs) JSON() interface{} {
+ switch length := len(a); length {
+ case 0:
+ return nil
+ case 1:
+ return a.Last().JSON()
+ default:
+ jsonData := make([]interface{}, length)
+ for i, err := range a {
+ jsonData[i] = err.JSON()
+ }
+ return jsonData
+ }
+}
+
+// MarshalJSON implements the json.Marshaller interface.
+func (a errorMsgs) MarshalJSON() ([]byte, error) {
+ return json.Marshal(a.JSON())
+}
+
+func (a errorMsgs) String() string {
+ if len(a) == 0 {
+ return ""
+ }
+ var buffer strings.Builder
+ for i, msg := range a {
+ fmt.Fprintf(&buffer, "Error #%02d: %s\n", i+1, msg.Err)
+ if msg.Meta != nil {
+ fmt.Fprintf(&buffer, " Meta: %v\n", msg.Meta)
+ }
+ }
+ return buffer.String()
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/fs.go b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/fs.go
new file mode 100644
index 000000000000..007d9b75514f
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/fs.go
@@ -0,0 +1,45 @@
+// Copyright 2017 Manu Martinez-Almeida. All rights reserved.
+// Use of this source code is governed by a MIT style
+// license that can be found in the LICENSE file.
+
+package gin
+
+import (
+ "net/http"
+ "os"
+)
+
+type onlyFilesFS struct {
+ fs http.FileSystem
+}
+
+type neuteredReaddirFile struct {
+ http.File
+}
+
+// Dir returns a http.Filesystem that can be used by http.FileServer(). It is used internally
+// in router.Static().
+// if listDirectory == true, then it works the same as http.Dir() otherwise it returns
+// a filesystem that prevents http.FileServer() to list the directory files.
+func Dir(root string, listDirectory bool) http.FileSystem {
+ fs := http.Dir(root)
+ if listDirectory {
+ return fs
+ }
+ return &onlyFilesFS{fs}
+}
+
+// Open conforms to http.Filesystem.
+func (fs onlyFilesFS) Open(name string) (http.File, error) {
+ f, err := fs.fs.Open(name)
+ if err != nil {
+ return nil, err
+ }
+ return neuteredReaddirFile{f}, nil
+}
+
+// Readdir overrides the http.File default implementation.
+func (f neuteredReaddirFile) Readdir(count int) ([]os.FileInfo, error) {
+ // this disables directory listing
+ return nil, nil
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/gin.go b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/gin.go
new file mode 100644
index 000000000000..58e76f41fbe7
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/gin.go
@@ -0,0 +1,643 @@
+// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
+// Use of this source code is governed by a MIT style
+// license that can be found in the LICENSE file.
+
+package gin
+
+import (
+ "fmt"
+ "html/template"
+ "net"
+ "net/http"
+ "os"
+ "path"
+ "reflect"
+ "strings"
+ "sync"
+
+ "github.com/gin-gonic/gin/internal/bytesconv"
+ "github.com/gin-gonic/gin/render"
+)
+
+const defaultMultipartMemory = 32 << 20 // 32 MB
+
+var (
+ default404Body = []byte("404 page not found")
+ default405Body = []byte("405 method not allowed")
+)
+
+var defaultPlatform string
+
+var defaultTrustedCIDRs = []*net.IPNet{{IP: net.IP{0x0, 0x0, 0x0, 0x0}, Mask: net.IPMask{0x0, 0x0, 0x0, 0x0}}} // 0.0.0.0/0
+
+// HandlerFunc defines the handler used by gin middleware as return value.
+type HandlerFunc func(*Context)
+
+// HandlersChain defines a HandlerFunc array.
+type HandlersChain []HandlerFunc
+
+// Last returns the last handler in the chain. ie. the last handler is the main one.
+func (c HandlersChain) Last() HandlerFunc {
+ if length := len(c); length > 0 {
+ return c[length-1]
+ }
+ return nil
+}
+
+// RouteInfo represents a request route's specification which contains method and path and its handler.
+type RouteInfo struct {
+ Method string
+ Path string
+ Handler string
+ HandlerFunc HandlerFunc
+}
+
+// RoutesInfo defines a RouteInfo array.
+type RoutesInfo []RouteInfo
+
+// Trusted platforms
+const (
+ // When running on Google App Engine. Trust X-Appengine-Remote-Addr
+ // for determining the client's IP
+ PlatformGoogleAppEngine = "X-Appengine-Remote-Addr"
+ // When using Cloudflare's CDN. Trust CF-Connecting-IP for determining
+ // the client's IP
+ PlatformCloudflare = "CF-Connecting-IP"
+)
+
+// Engine is the framework's instance, it contains the muxer, middleware and configuration settings.
+// Create an instance of Engine, by using New() or Default()
+type Engine struct {
+ RouterGroup
+
+ // Enables automatic redirection if the current route can't be matched but a
+ // handler for the path with (without) the trailing slash exists.
+ // For example if /foo/ is requested but a route only exists for /foo, the
+ // client is redirected to /foo with http status code 301 for GET requests
+ // and 307 for all other request methods.
+ RedirectTrailingSlash bool
+
+ // If enabled, the router tries to fix the current request path, if no
+ // handle is registered for it.
+ // First superfluous path elements like ../ or // are removed.
+ // Afterwards the router does a case-insensitive lookup of the cleaned path.
+ // If a handle can be found for this route, the router makes a redirection
+ // to the corrected path with status code 301 for GET requests and 307 for
+ // all other request methods.
+ // For example /FOO and /..//Foo could be redirected to /foo.
+ // RedirectTrailingSlash is independent of this option.
+ RedirectFixedPath bool
+
+ // If enabled, the router checks if another method is allowed for the
+ // current route, if the current request can not be routed.
+ // If this is the case, the request is answered with 'Method Not Allowed'
+ // and HTTP status code 405.
+ // If no other Method is allowed, the request is delegated to the NotFound
+ // handler.
+ HandleMethodNotAllowed bool
+
+ // If enabled, client IP will be parsed from the request's headers that
+ // match those stored at `(*gin.Engine).RemoteIPHeaders`. If no IP was
+ // fetched, it falls back to the IP obtained from
+ // `(*gin.Context).Request.RemoteAddr`.
+ ForwardedByClientIP bool
+
+ // DEPRECATED: USE `TrustedPlatform` WITH VALUE `gin.GoogleAppEngine` INSTEAD
+ // #726 #755 If enabled, it will trust some headers starting with
+ // 'X-AppEngine...' for better integration with that PaaS.
+ AppEngine bool
+
+ // If enabled, the url.RawPath will be used to find parameters.
+ UseRawPath bool
+
+ // If true, the path value will be unescaped.
+ // If UseRawPath is false (by default), the UnescapePathValues effectively is true,
+ // as url.Path gonna be used, which is already unescaped.
+ UnescapePathValues bool
+
+ // RemoveExtraSlash a parameter can be parsed from the URL even with extra slashes.
+ // See the PR #1817 and issue #1644
+ RemoveExtraSlash bool
+
+ // List of headers used to obtain the client IP when
+ // `(*gin.Engine).ForwardedByClientIP` is `true` and
+ // `(*gin.Context).Request.RemoteAddr` is matched by at least one of the
+ // network origins of list defined by `(*gin.Engine).SetTrustedProxies()`.
+ RemoteIPHeaders []string
+
+ // If set to a constant of value gin.Platform*, trusts the headers set by
+ // that platform, for example to determine the client IP
+ TrustedPlatform string
+
+ // Value of 'maxMemory' param that is given to http.Request's ParseMultipartForm
+ // method call.
+ MaxMultipartMemory int64
+
+ delims render.Delims
+ secureJSONPrefix string
+ HTMLRender render.HTMLRender
+ FuncMap template.FuncMap
+ allNoRoute HandlersChain
+ allNoMethod HandlersChain
+ noRoute HandlersChain
+ noMethod HandlersChain
+ pool sync.Pool
+ trees methodTrees
+ maxParams uint16
+ maxSections uint16
+ trustedProxies []string
+ trustedCIDRs []*net.IPNet
+}
+
+var _ IRouter = &Engine{}
+
+// New returns a new blank Engine instance without any middleware attached.
+// By default the configuration is:
+// - RedirectTrailingSlash: true
+// - RedirectFixedPath: false
+// - HandleMethodNotAllowed: false
+// - ForwardedByClientIP: true
+// - UseRawPath: false
+// - UnescapePathValues: true
+func New() *Engine {
+ debugPrintWARNINGNew()
+ engine := &Engine{
+ RouterGroup: RouterGroup{
+ Handlers: nil,
+ basePath: "/",
+ root: true,
+ },
+ FuncMap: template.FuncMap{},
+ RedirectTrailingSlash: true,
+ RedirectFixedPath: false,
+ HandleMethodNotAllowed: false,
+ ForwardedByClientIP: true,
+ RemoteIPHeaders: []string{"X-Forwarded-For", "X-Real-IP"},
+ TrustedPlatform: defaultPlatform,
+ UseRawPath: false,
+ RemoveExtraSlash: false,
+ UnescapePathValues: true,
+ MaxMultipartMemory: defaultMultipartMemory,
+ trees: make(methodTrees, 0, 9),
+ delims: render.Delims{Left: "{{", Right: "}}"},
+ secureJSONPrefix: "while(1);",
+ trustedProxies: []string{"0.0.0.0/0"},
+ trustedCIDRs: defaultTrustedCIDRs,
+ }
+ engine.RouterGroup.engine = engine
+ engine.pool.New = func() interface{} {
+ return engine.allocateContext()
+ }
+ return engine
+}
+
+// Default returns an Engine instance with the Logger and Recovery middleware already attached.
+func Default() *Engine {
+ debugPrintWARNINGDefault()
+ engine := New()
+ engine.Use(Logger(), Recovery())
+ return engine
+}
+
+func (engine *Engine) allocateContext() *Context {
+ v := make(Params, 0, engine.maxParams)
+ skippedNodes := make([]skippedNode, 0, engine.maxSections)
+ return &Context{engine: engine, params: &v, skippedNodes: &skippedNodes}
+}
+
+// Delims sets template left and right delims and returns a Engine instance.
+func (engine *Engine) Delims(left, right string) *Engine {
+ engine.delims = render.Delims{Left: left, Right: right}
+ return engine
+}
+
+// SecureJsonPrefix sets the secureJSONPrefix used in Context.SecureJSON.
+func (engine *Engine) SecureJsonPrefix(prefix string) *Engine {
+ engine.secureJSONPrefix = prefix
+ return engine
+}
+
+// LoadHTMLGlob loads HTML files identified by glob pattern
+// and associates the result with HTML renderer.
+func (engine *Engine) LoadHTMLGlob(pattern string) {
+ left := engine.delims.Left
+ right := engine.delims.Right
+ templ := template.Must(template.New("").Delims(left, right).Funcs(engine.FuncMap).ParseGlob(pattern))
+
+ if IsDebugging() {
+ debugPrintLoadTemplate(templ)
+ engine.HTMLRender = render.HTMLDebug{Glob: pattern, FuncMap: engine.FuncMap, Delims: engine.delims}
+ return
+ }
+
+ engine.SetHTMLTemplate(templ)
+}
+
+// LoadHTMLFiles loads a slice of HTML files
+// and associates the result with HTML renderer.
+func (engine *Engine) LoadHTMLFiles(files ...string) {
+ if IsDebugging() {
+ engine.HTMLRender = render.HTMLDebug{Files: files, FuncMap: engine.FuncMap, Delims: engine.delims}
+ return
+ }
+
+ templ := template.Must(template.New("").Delims(engine.delims.Left, engine.delims.Right).Funcs(engine.FuncMap).ParseFiles(files...))
+ engine.SetHTMLTemplate(templ)
+}
+
+// SetHTMLTemplate associate a template with HTML renderer.
+func (engine *Engine) SetHTMLTemplate(templ *template.Template) {
+ if len(engine.trees) > 0 {
+ debugPrintWARNINGSetHTMLTemplate()
+ }
+
+ engine.HTMLRender = render.HTMLProduction{Template: templ.Funcs(engine.FuncMap)}
+}
+
+// SetFuncMap sets the FuncMap used for template.FuncMap.
+func (engine *Engine) SetFuncMap(funcMap template.FuncMap) {
+ engine.FuncMap = funcMap
+}
+
+// NoRoute adds handlers for NoRoute. It return a 404 code by default.
+func (engine *Engine) NoRoute(handlers ...HandlerFunc) {
+ engine.noRoute = handlers
+ engine.rebuild404Handlers()
+}
+
+// NoMethod sets the handlers called when Engine.HandleMethodNotAllowed = true.
+func (engine *Engine) NoMethod(handlers ...HandlerFunc) {
+ engine.noMethod = handlers
+ engine.rebuild405Handlers()
+}
+
+// Use attaches a global middleware to the router. ie. the middleware attached though Use() will be
+// included in the handlers chain for every single request. Even 404, 405, static files...
+// For example, this is the right place for a logger or error management middleware.
+func (engine *Engine) Use(middleware ...HandlerFunc) IRoutes {
+ engine.RouterGroup.Use(middleware...)
+ engine.rebuild404Handlers()
+ engine.rebuild405Handlers()
+ return engine
+}
+
+func (engine *Engine) rebuild404Handlers() {
+ engine.allNoRoute = engine.combineHandlers(engine.noRoute)
+}
+
+func (engine *Engine) rebuild405Handlers() {
+ engine.allNoMethod = engine.combineHandlers(engine.noMethod)
+}
+
+func (engine *Engine) addRoute(method, path string, handlers HandlersChain) {
+ assert1(path[0] == '/', "path must begin with '/'")
+ assert1(method != "", "HTTP method can not be empty")
+ assert1(len(handlers) > 0, "there must be at least one handler")
+
+ debugPrintRoute(method, path, handlers)
+
+ root := engine.trees.get(method)
+ if root == nil {
+ root = new(node)
+ root.fullPath = "/"
+ engine.trees = append(engine.trees, methodTree{method: method, root: root})
+ }
+ root.addRoute(path, handlers)
+
+ // Update maxParams
+ if paramsCount := countParams(path); paramsCount > engine.maxParams {
+ engine.maxParams = paramsCount
+ }
+
+ if sectionsCount := countSections(path); sectionsCount > engine.maxSections {
+ engine.maxSections = sectionsCount
+ }
+}
+
+// Routes returns a slice of registered routes, including some useful information, such as:
+// the http method, path and the handler name.
+func (engine *Engine) Routes() (routes RoutesInfo) {
+ for _, tree := range engine.trees {
+ routes = iterate("", tree.method, routes, tree.root)
+ }
+ return routes
+}
+
+func iterate(path, method string, routes RoutesInfo, root *node) RoutesInfo {
+ path += root.path
+ if len(root.handlers) > 0 {
+ handlerFunc := root.handlers.Last()
+ routes = append(routes, RouteInfo{
+ Method: method,
+ Path: path,
+ Handler: nameOfFunction(handlerFunc),
+ HandlerFunc: handlerFunc,
+ })
+ }
+ for _, child := range root.children {
+ routes = iterate(path, method, routes, child)
+ }
+ return routes
+}
+
+// Run attaches the router to a http.Server and starts listening and serving HTTP requests.
+// It is a shortcut for http.ListenAndServe(addr, router)
+// Note: this method will block the calling goroutine indefinitely unless an error happens.
+func (engine *Engine) Run(addr ...string) (err error) {
+ defer func() { debugPrintError(err) }()
+
+ if engine.isUnsafeTrustedProxies() {
+ debugPrint("[WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.\n" +
+ "Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.")
+ }
+
+ address := resolveAddress(addr)
+ debugPrint("Listening and serving HTTP on %s\n", address)
+ err = http.ListenAndServe(address, engine)
+ return
+}
+
+func (engine *Engine) prepareTrustedCIDRs() ([]*net.IPNet, error) {
+ if engine.trustedProxies == nil {
+ return nil, nil
+ }
+
+ cidr := make([]*net.IPNet, 0, len(engine.trustedProxies))
+ for _, trustedProxy := range engine.trustedProxies {
+ if !strings.Contains(trustedProxy, "/") {
+ ip := parseIP(trustedProxy)
+ if ip == nil {
+ return cidr, &net.ParseError{Type: "IP address", Text: trustedProxy}
+ }
+
+ switch len(ip) {
+ case net.IPv4len:
+ trustedProxy += "/32"
+ case net.IPv6len:
+ trustedProxy += "/128"
+ }
+ }
+ _, cidrNet, err := net.ParseCIDR(trustedProxy)
+ if err != nil {
+ return cidr, err
+ }
+ cidr = append(cidr, cidrNet)
+ }
+ return cidr, nil
+}
+
+// SetTrustedProxies set a list of network origins (IPv4 addresses,
+// IPv4 CIDRs, IPv6 addresses or IPv6 CIDRs) from which to trust
+// request's headers that contain alternative client IP when
+// `(*gin.Engine).ForwardedByClientIP` is `true`. `TrustedProxies`
+// feature is enabled by default, and it also trusts all proxies
+// by default. If you want to disable this feature, use
+// Engine.SetTrustedProxies(nil), then Context.ClientIP() will
+// return the remote address directly.
+func (engine *Engine) SetTrustedProxies(trustedProxies []string) error {
+ engine.trustedProxies = trustedProxies
+ return engine.parseTrustedProxies()
+}
+
+// isUnsafeTrustedProxies compares Engine.trustedCIDRs and defaultTrustedCIDRs, it's not safe if equal (returns true)
+func (engine *Engine) isUnsafeTrustedProxies() bool {
+ return reflect.DeepEqual(engine.trustedCIDRs, defaultTrustedCIDRs)
+}
+
+// parseTrustedProxies parse Engine.trustedProxies to Engine.trustedCIDRs
+func (engine *Engine) parseTrustedProxies() error {
+ trustedCIDRs, err := engine.prepareTrustedCIDRs()
+ engine.trustedCIDRs = trustedCIDRs
+ return err
+}
+
+// parseIP parse a string representation of an IP and returns a net.IP with the
+// minimum byte representation or nil if input is invalid.
+func parseIP(ip string) net.IP {
+ parsedIP := net.ParseIP(ip)
+
+ if ipv4 := parsedIP.To4(); ipv4 != nil {
+ // return ip in a 4-byte representation
+ return ipv4
+ }
+
+ // return ip in a 16-byte representation or nil
+ return parsedIP
+}
+
+// RunTLS attaches the router to a http.Server and starts listening and serving HTTPS (secure) requests.
+// It is a shortcut for http.ListenAndServeTLS(addr, certFile, keyFile, router)
+// Note: this method will block the calling goroutine indefinitely unless an error happens.
+func (engine *Engine) RunTLS(addr, certFile, keyFile string) (err error) {
+ debugPrint("Listening and serving HTTPS on %s\n", addr)
+ defer func() { debugPrintError(err) }()
+
+ if engine.isUnsafeTrustedProxies() {
+ debugPrint("[WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.\n" +
+ "Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.")
+ }
+
+ err = http.ListenAndServeTLS(addr, certFile, keyFile, engine)
+ return
+}
+
+// RunUnix attaches the router to a http.Server and starts listening and serving HTTP requests
+// through the specified unix socket (ie. a file).
+// Note: this method will block the calling goroutine indefinitely unless an error happens.
+func (engine *Engine) RunUnix(file string) (err error) {
+ debugPrint("Listening and serving HTTP on unix:/%s", file)
+ defer func() { debugPrintError(err) }()
+
+ if engine.isUnsafeTrustedProxies() {
+ debugPrint("[WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.\n" +
+ "Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.")
+ }
+
+ listener, err := net.Listen("unix", file)
+ if err != nil {
+ return
+ }
+ defer listener.Close()
+ defer os.Remove(file)
+
+ err = http.Serve(listener, engine)
+ return
+}
+
+// RunFd attaches the router to a http.Server and starts listening and serving HTTP requests
+// through the specified file descriptor.
+// Note: this method will block the calling goroutine indefinitely unless an error happens.
+func (engine *Engine) RunFd(fd int) (err error) {
+ debugPrint("Listening and serving HTTP on fd@%d", fd)
+ defer func() { debugPrintError(err) }()
+
+ if engine.isUnsafeTrustedProxies() {
+ debugPrint("[WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.\n" +
+ "Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.")
+ }
+
+ f := os.NewFile(uintptr(fd), fmt.Sprintf("fd@%d", fd))
+ listener, err := net.FileListener(f)
+ if err != nil {
+ return
+ }
+ defer listener.Close()
+ err = engine.RunListener(listener)
+ return
+}
+
+// RunListener attaches the router to a http.Server and starts listening and serving HTTP requests
+// through the specified net.Listener
+func (engine *Engine) RunListener(listener net.Listener) (err error) {
+ debugPrint("Listening and serving HTTP on listener what's bind with address@%s", listener.Addr())
+ defer func() { debugPrintError(err) }()
+
+ if engine.isUnsafeTrustedProxies() {
+ debugPrint("[WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.\n" +
+ "Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.")
+ }
+
+ err = http.Serve(listener, engine)
+ return
+}
+
+// ServeHTTP conforms to the http.Handler interface.
+func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
+ c := engine.pool.Get().(*Context)
+ c.writermem.reset(w)
+ c.Request = req
+ c.reset()
+
+ engine.handleHTTPRequest(c)
+
+ engine.pool.Put(c)
+}
+
+// HandleContext re-enter a context that has been rewritten.
+// This can be done by setting c.Request.URL.Path to your new target.
+// Disclaimer: You can loop yourself to death with this, use wisely.
+func (engine *Engine) HandleContext(c *Context) {
+ oldIndexValue := c.index
+ c.reset()
+ engine.handleHTTPRequest(c)
+
+ c.index = oldIndexValue
+}
+
+func (engine *Engine) handleHTTPRequest(c *Context) {
+ httpMethod := c.Request.Method
+ rPath := c.Request.URL.Path
+ unescape := false
+ if engine.UseRawPath && len(c.Request.URL.RawPath) > 0 {
+ rPath = c.Request.URL.RawPath
+ unescape = engine.UnescapePathValues
+ }
+
+ if engine.RemoveExtraSlash {
+ rPath = cleanPath(rPath)
+ }
+
+ // Find root of the tree for the given HTTP method
+ t := engine.trees
+ for i, tl := 0, len(t); i < tl; i++ {
+ if t[i].method != httpMethod {
+ continue
+ }
+ root := t[i].root
+ // Find route in tree
+ value := root.getValue(rPath, c.params, c.skippedNodes, unescape)
+ if value.params != nil {
+ c.Params = *value.params
+ }
+ if value.handlers != nil {
+ c.handlers = value.handlers
+ c.fullPath = value.fullPath
+ c.Next()
+ c.writermem.WriteHeaderNow()
+ return
+ }
+ if httpMethod != "CONNECT" && rPath != "/" {
+ if value.tsr && engine.RedirectTrailingSlash {
+ redirectTrailingSlash(c)
+ return
+ }
+ if engine.RedirectFixedPath && redirectFixedPath(c, root, engine.RedirectFixedPath) {
+ return
+ }
+ }
+ break
+ }
+
+ if engine.HandleMethodNotAllowed {
+ for _, tree := range engine.trees {
+ if tree.method == httpMethod {
+ continue
+ }
+ if value := tree.root.getValue(rPath, nil, c.skippedNodes, unescape); value.handlers != nil {
+ c.handlers = engine.allNoMethod
+ serveError(c, http.StatusMethodNotAllowed, default405Body)
+ return
+ }
+ }
+ }
+ c.handlers = engine.allNoRoute
+ serveError(c, http.StatusNotFound, default404Body)
+}
+
+var mimePlain = []string{MIMEPlain}
+
+func serveError(c *Context, code int, defaultMessage []byte) {
+ c.writermem.status = code
+ c.Next()
+ if c.writermem.Written() {
+ return
+ }
+ if c.writermem.Status() == code {
+ c.writermem.Header()["Content-Type"] = mimePlain
+ _, err := c.Writer.Write(defaultMessage)
+ if err != nil {
+ debugPrint("cannot write message to writer during serve error: %v", err)
+ }
+ return
+ }
+ c.writermem.WriteHeaderNow()
+}
+
+func redirectTrailingSlash(c *Context) {
+ req := c.Request
+ p := req.URL.Path
+ if prefix := path.Clean(c.Request.Header.Get("X-Forwarded-Prefix")); prefix != "." {
+ p = prefix + "/" + req.URL.Path
+ }
+ req.URL.Path = p + "/"
+ if length := len(p); length > 1 && p[length-1] == '/' {
+ req.URL.Path = p[:length-1]
+ }
+ redirectRequest(c)
+}
+
+func redirectFixedPath(c *Context, root *node, trailingSlash bool) bool {
+ req := c.Request
+ rPath := req.URL.Path
+
+ if fixedPath, ok := root.findCaseInsensitivePath(cleanPath(rPath), trailingSlash); ok {
+ req.URL.Path = bytesconv.BytesToString(fixedPath)
+ redirectRequest(c)
+ return true
+ }
+ return false
+}
+
+func redirectRequest(c *Context) {
+ req := c.Request
+ rPath := req.URL.Path
+ rURL := req.URL.String()
+
+ code := http.StatusMovedPermanently // Permanent redirect, request with GET method
+ if req.Method != http.MethodGet {
+ code = http.StatusTemporaryRedirect
+ }
+ debugPrint("redirecting request %d: %s --> %s", code, rPath, rURL)
+ http.Redirect(c.Writer, req, rURL, code)
+ c.writermem.WriteHeaderNow()
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/internal/bytesconv/bytesconv.go b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/internal/bytesconv/bytesconv.go
new file mode 100644
index 000000000000..86e4c4d44cdf
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/internal/bytesconv/bytesconv.go
@@ -0,0 +1,24 @@
+// Copyright 2020 Gin Core Team. All rights reserved.
+// Use of this source code is governed by a MIT style
+// license that can be found in the LICENSE file.
+
+package bytesconv
+
+import (
+ "unsafe"
+)
+
+// StringToBytes converts string to byte slice without a memory allocation.
+func StringToBytes(s string) []byte {
+ return *(*[]byte)(unsafe.Pointer(
+ &struct {
+ string
+ Cap int
+ }{s, len(s)},
+ ))
+}
+
+// BytesToString converts byte slice to string without a memory allocation.
+func BytesToString(b []byte) string {
+ return *(*string)(unsafe.Pointer(&b))
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/internal/json/json.go b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/internal/json/json.go
new file mode 100644
index 000000000000..172aeb2414c6
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/internal/json/json.go
@@ -0,0 +1,23 @@
+// Copyright 2017 Bo-Yi Wu. All rights reserved.
+// Use of this source code is governed by a MIT style
+// license that can be found in the LICENSE file.
+
+//go:build !jsoniter
+// +build !jsoniter
+
+package json
+
+import "encoding/json"
+
+var (
+ // Marshal is exported by gin/json package.
+ Marshal = json.Marshal
+ // Unmarshal is exported by gin/json package.
+ Unmarshal = json.Unmarshal
+ // MarshalIndent is exported by gin/json package.
+ MarshalIndent = json.MarshalIndent
+ // NewDecoder is exported by gin/json package.
+ NewDecoder = json.NewDecoder
+ // NewEncoder is exported by gin/json package.
+ NewEncoder = json.NewEncoder
+)
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/internal/json/jsoniter.go b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/internal/json/jsoniter.go
new file mode 100644
index 000000000000..232f8dcada12
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/internal/json/jsoniter.go
@@ -0,0 +1,24 @@
+// Copyright 2017 Bo-Yi Wu. All rights reserved.
+// Use of this source code is governed by a MIT style
+// license that can be found in the LICENSE file.
+
+//go:build jsoniter
+// +build jsoniter
+
+package json
+
+import jsoniter "github.com/json-iterator/go"
+
+var (
+ json = jsoniter.ConfigCompatibleWithStandardLibrary
+ // Marshal is exported by gin/json package.
+ Marshal = json.Marshal
+ // Unmarshal is exported by gin/json package.
+ Unmarshal = json.Unmarshal
+ // MarshalIndent is exported by gin/json package.
+ MarshalIndent = json.MarshalIndent
+ // NewDecoder is exported by gin/json package.
+ NewDecoder = json.NewDecoder
+ // NewEncoder is exported by gin/json package.
+ NewEncoder = json.NewEncoder
+)
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/logger.go b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/logger.go
new file mode 100644
index 000000000000..d361b74d3239
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/logger.go
@@ -0,0 +1,271 @@
+// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
+// Use of this source code is governed by a MIT style
+// license that can be found in the LICENSE file.
+
+package gin
+
+import (
+ "fmt"
+ "io"
+ "net/http"
+ "os"
+ "time"
+
+ "github.com/mattn/go-isatty"
+)
+
+type consoleColorModeValue int
+
+const (
+ autoColor consoleColorModeValue = iota
+ disableColor
+ forceColor
+)
+
+const (
+ green = "\033[97;42m"
+ white = "\033[90;47m"
+ yellow = "\033[90;43m"
+ red = "\033[97;41m"
+ blue = "\033[97;44m"
+ magenta = "\033[97;45m"
+ cyan = "\033[97;46m"
+ reset = "\033[0m"
+)
+
+var consoleColorMode = autoColor
+
+// LoggerConfig defines the config for Logger middleware.
+type LoggerConfig struct {
+ // Optional. Default value is gin.defaultLogFormatter
+ Formatter LogFormatter
+
+ // Output is a writer where logs are written.
+ // Optional. Default value is gin.DefaultWriter.
+ Output io.Writer
+
+ // SkipPaths is a url path array which logs are not written.
+ // Optional.
+ SkipPaths []string
+}
+
+// LogFormatter gives the signature of the formatter function passed to LoggerWithFormatter
+type LogFormatter func(params LogFormatterParams) string
+
+// LogFormatterParams is the structure any formatter will be handed when time to log comes
+type LogFormatterParams struct {
+ Request *http.Request
+
+ // TimeStamp shows the time after the server returns a response.
+ TimeStamp time.Time
+ // StatusCode is HTTP response code.
+ StatusCode int
+ // Latency is how much time the server cost to process a certain request.
+ Latency time.Duration
+ // ClientIP equals Context's ClientIP method.
+ ClientIP string
+ // Method is the HTTP method given to the request.
+ Method string
+ // Path is a path the client requests.
+ Path string
+ // ErrorMessage is set if error has occurred in processing the request.
+ ErrorMessage string
+ // isTerm shows whether does gin's output descriptor refers to a terminal.
+ isTerm bool
+ // BodySize is the size of the Response Body
+ BodySize int
+ // Keys are the keys set on the request's context.
+ Keys map[string]interface{}
+}
+
+// StatusCodeColor is the ANSI color for appropriately logging http status code to a terminal.
+func (p *LogFormatterParams) StatusCodeColor() string {
+ code := p.StatusCode
+
+ switch {
+ case code >= http.StatusOK && code < http.StatusMultipleChoices:
+ return green
+ case code >= http.StatusMultipleChoices && code < http.StatusBadRequest:
+ return white
+ case code >= http.StatusBadRequest && code < http.StatusInternalServerError:
+ return yellow
+ default:
+ return red
+ }
+}
+
+// MethodColor is the ANSI color for appropriately logging http method to a terminal.
+func (p *LogFormatterParams) MethodColor() string {
+ method := p.Method
+
+ switch method {
+ case http.MethodGet:
+ return blue
+ case http.MethodPost:
+ return cyan
+ case http.MethodPut:
+ return yellow
+ case http.MethodDelete:
+ return red
+ case http.MethodPatch:
+ return green
+ case http.MethodHead:
+ return magenta
+ case http.MethodOptions:
+ return white
+ default:
+ return reset
+ }
+}
+
+// ResetColor resets all escape attributes.
+func (p *LogFormatterParams) ResetColor() string {
+ return reset
+}
+
+// IsOutputColor indicates whether can colors be outputted to the log.
+func (p *LogFormatterParams) IsOutputColor() bool {
+ return consoleColorMode == forceColor || (consoleColorMode == autoColor && p.isTerm)
+}
+
+// defaultLogFormatter is the default log format function Logger middleware uses.
+var defaultLogFormatter = func(param LogFormatterParams) string {
+ var statusColor, methodColor, resetColor string
+ if param.IsOutputColor() {
+ statusColor = param.StatusCodeColor()
+ methodColor = param.MethodColor()
+ resetColor = param.ResetColor()
+ }
+
+ if param.Latency > time.Minute {
+ // Truncate in a golang < 1.8 safe way
+ param.Latency = param.Latency - param.Latency%time.Second
+ }
+ return fmt.Sprintf("[GIN] %v |%s %3d %s| %13v | %15s |%s %-7s %s %#v\n%s",
+ param.TimeStamp.Format("2006/01/02 - 15:04:05"),
+ statusColor, param.StatusCode, resetColor,
+ param.Latency,
+ param.ClientIP,
+ methodColor, param.Method, resetColor,
+ param.Path,
+ param.ErrorMessage,
+ )
+}
+
+// DisableConsoleColor disables color output in the console.
+func DisableConsoleColor() {
+ consoleColorMode = disableColor
+}
+
+// ForceConsoleColor force color output in the console.
+func ForceConsoleColor() {
+ consoleColorMode = forceColor
+}
+
+// ErrorLogger returns a handlerfunc for any error type.
+func ErrorLogger() HandlerFunc {
+ return ErrorLoggerT(ErrorTypeAny)
+}
+
+// ErrorLoggerT returns a handlerfunc for a given error type.
+func ErrorLoggerT(typ ErrorType) HandlerFunc {
+ return func(c *Context) {
+ c.Next()
+ errors := c.Errors.ByType(typ)
+ if len(errors) > 0 {
+ c.JSON(-1, errors)
+ }
+ }
+}
+
+// Logger instances a Logger middleware that will write the logs to gin.DefaultWriter.
+// By default gin.DefaultWriter = os.Stdout.
+func Logger() HandlerFunc {
+ return LoggerWithConfig(LoggerConfig{})
+}
+
+// LoggerWithFormatter instance a Logger middleware with the specified log format function.
+func LoggerWithFormatter(f LogFormatter) HandlerFunc {
+ return LoggerWithConfig(LoggerConfig{
+ Formatter: f,
+ })
+}
+
+// LoggerWithWriter instance a Logger middleware with the specified writer buffer.
+// Example: os.Stdout, a file opened in write mode, a socket...
+func LoggerWithWriter(out io.Writer, notlogged ...string) HandlerFunc {
+ return LoggerWithConfig(LoggerConfig{
+ Output: out,
+ SkipPaths: notlogged,
+ })
+}
+
+// LoggerWithConfig instance a Logger middleware with config.
+func LoggerWithConfig(conf LoggerConfig) HandlerFunc {
+ formatter := conf.Formatter
+ if formatter == nil {
+ formatter = defaultLogFormatter
+ }
+
+ out := conf.Output
+ if out == nil {
+ out = DefaultWriter
+ }
+
+ notlogged := conf.SkipPaths
+
+ isTerm := true
+
+ if w, ok := out.(*os.File); !ok || os.Getenv("TERM") == "dumb" ||
+ (!isatty.IsTerminal(w.Fd()) && !isatty.IsCygwinTerminal(w.Fd())) {
+ isTerm = false
+ }
+
+ var skip map[string]struct{}
+
+ if length := len(notlogged); length > 0 {
+ skip = make(map[string]struct{}, length)
+
+ for _, path := range notlogged {
+ skip[path] = struct{}{}
+ }
+ }
+
+ return func(c *Context) {
+ // Start timer
+ start := time.Now()
+ path := c.Request.URL.Path
+ raw := c.Request.URL.RawQuery
+
+ // Process request
+ c.Next()
+
+ // Log only when path is not being skipped
+ if _, ok := skip[path]; !ok {
+ param := LogFormatterParams{
+ Request: c.Request,
+ isTerm: isTerm,
+ Keys: c.Keys,
+ }
+
+ // Stop timer
+ param.TimeStamp = time.Now()
+ param.Latency = param.TimeStamp.Sub(start)
+
+ param.ClientIP = c.ClientIP()
+ param.Method = c.Request.Method
+ param.StatusCode = c.Writer.Status()
+ param.ErrorMessage = c.Errors.ByType(ErrorTypePrivate).String()
+
+ param.BodySize = c.Writer.Size()
+
+ if raw != "" {
+ path = path + "?" + raw
+ }
+
+ param.Path = path
+
+ fmt.Fprint(out, formatter(param))
+ }
+ }
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/mode.go b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/mode.go
new file mode 100644
index 000000000000..c8813aff26c5
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/mode.go
@@ -0,0 +1,92 @@
+// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
+// Use of this source code is governed by a MIT style
+// license that can be found in the LICENSE file.
+
+package gin
+
+import (
+ "io"
+ "os"
+
+ "github.com/gin-gonic/gin/binding"
+)
+
+// EnvGinMode indicates environment name for gin mode.
+const EnvGinMode = "GIN_MODE"
+
+const (
+ // DebugMode indicates gin mode is debug.
+ DebugMode = "debug"
+ // ReleaseMode indicates gin mode is release.
+ ReleaseMode = "release"
+ // TestMode indicates gin mode is test.
+ TestMode = "test"
+)
+
+const (
+ debugCode = iota
+ releaseCode
+ testCode
+)
+
+// DefaultWriter is the default io.Writer used by Gin for debug output and
+// middleware output like Logger() or Recovery().
+// Note that both Logger and Recovery provides custom ways to configure their
+// output io.Writer.
+// To support coloring in Windows use:
+// import "github.com/mattn/go-colorable"
+// gin.DefaultWriter = colorable.NewColorableStdout()
+var DefaultWriter io.Writer = os.Stdout
+
+// DefaultErrorWriter is the default io.Writer used by Gin to debug errors
+var DefaultErrorWriter io.Writer = os.Stderr
+
+var ginMode = debugCode
+var modeName = DebugMode
+
+func init() {
+ mode := os.Getenv(EnvGinMode)
+ SetMode(mode)
+}
+
+// SetMode sets gin mode according to input string.
+func SetMode(value string) {
+ if value == "" {
+ value = DebugMode
+ }
+
+ switch value {
+ case DebugMode:
+ ginMode = debugCode
+ case ReleaseMode:
+ ginMode = releaseCode
+ case TestMode:
+ ginMode = testCode
+ default:
+ panic("gin mode unknown: " + value + " (available mode: debug release test)")
+ }
+
+ modeName = value
+}
+
+// DisableBindValidation closes the default validator.
+func DisableBindValidation() {
+ binding.Validator = nil
+}
+
+// EnableJsonDecoderUseNumber sets true for binding.EnableDecoderUseNumber to
+// call the UseNumber method on the JSON Decoder instance.
+func EnableJsonDecoderUseNumber() {
+ binding.EnableDecoderUseNumber = true
+}
+
+// EnableJsonDecoderDisallowUnknownFields sets true for binding.EnableDecoderDisallowUnknownFields to
+// call the DisallowUnknownFields method on the JSON Decoder instance.
+func EnableJsonDecoderDisallowUnknownFields() {
+ binding.EnableDecoderDisallowUnknownFields = true
+}
+
+// Mode returns currently gin mode.
+func Mode() string {
+ return modeName
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/path.go b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/path.go
new file mode 100644
index 000000000000..d42d6b9d05d6
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/path.go
@@ -0,0 +1,150 @@
+// Copyright 2013 Julien Schmidt. All rights reserved.
+// Based on the path package, Copyright 2009 The Go Authors.
+// Use of this source code is governed by a BSD-style license that can be found
+// at https://github.com/julienschmidt/httprouter/blob/master/LICENSE.
+
+package gin
+
+// cleanPath is the URL version of path.Clean, it returns a canonical URL path
+// for p, eliminating . and .. elements.
+//
+// The following rules are applied iteratively until no further processing can
+// be done:
+// 1. Replace multiple slashes with a single slash.
+// 2. Eliminate each . path name element (the current directory).
+// 3. Eliminate each inner .. path name element (the parent directory)
+// along with the non-.. element that precedes it.
+// 4. Eliminate .. elements that begin a rooted path:
+// that is, replace "/.." by "/" at the beginning of a path.
+//
+// If the result of this process is an empty string, "/" is returned.
+func cleanPath(p string) string {
+ const stackBufSize = 128
+ // Turn empty string into "/"
+ if p == "" {
+ return "/"
+ }
+
+ // Reasonably sized buffer on stack to avoid allocations in the common case.
+ // If a larger buffer is required, it gets allocated dynamically.
+ buf := make([]byte, 0, stackBufSize)
+
+ n := len(p)
+
+ // Invariants:
+ // reading from path; r is index of next byte to process.
+ // writing to buf; w is index of next byte to write.
+
+ // path must start with '/'
+ r := 1
+ w := 1
+
+ if p[0] != '/' {
+ r = 0
+
+ if n+1 > stackBufSize {
+ buf = make([]byte, n+1)
+ } else {
+ buf = buf[:n+1]
+ }
+ buf[0] = '/'
+ }
+
+ trailing := n > 1 && p[n-1] == '/'
+
+ // A bit more clunky without a 'lazybuf' like the path package, but the loop
+ // gets completely inlined (bufApp calls).
+ // loop has no expensive function calls (except 1x make) // So in contrast to the path package this loop has no expensive function
+ // calls (except make, if needed).
+
+ for r < n {
+ switch {
+ case p[r] == '/':
+ // empty path element, trailing slash is added after the end
+ r++
+
+ case p[r] == '.' && r+1 == n:
+ trailing = true
+ r++
+
+ case p[r] == '.' && p[r+1] == '/':
+ // . element
+ r += 2
+
+ case p[r] == '.' && p[r+1] == '.' && (r+2 == n || p[r+2] == '/'):
+ // .. element: remove to last /
+ r += 3
+
+ if w > 1 {
+ // can backtrack
+ w--
+
+ if len(buf) == 0 {
+ for w > 1 && p[w] != '/' {
+ w--
+ }
+ } else {
+ for w > 1 && buf[w] != '/' {
+ w--
+ }
+ }
+ }
+
+ default:
+ // Real path element.
+ // Add slash if needed
+ if w > 1 {
+ bufApp(&buf, p, w, '/')
+ w++
+ }
+
+ // Copy element
+ for r < n && p[r] != '/' {
+ bufApp(&buf, p, w, p[r])
+ w++
+ r++
+ }
+ }
+ }
+
+ // Re-append trailing slash
+ if trailing && w > 1 {
+ bufApp(&buf, p, w, '/')
+ w++
+ }
+
+ // If the original string was not modified (or only shortened at the end),
+ // return the respective substring of the original string.
+ // Otherwise return a new string from the buffer.
+ if len(buf) == 0 {
+ return p[:w]
+ }
+ return string(buf[:w])
+}
+
+// Internal helper to lazily create a buffer if necessary.
+// Calls to this function get inlined.
+func bufApp(buf *[]byte, s string, w int, c byte) {
+ b := *buf
+ if len(b) == 0 {
+ // No modification of the original string so far.
+ // If the next character is the same as in the original string, we do
+ // not yet have to allocate a buffer.
+ if s[w] == c {
+ return
+ }
+
+ // Otherwise use either the stack buffer, if it is large enough, or
+ // allocate a new buffer on the heap, and copy all previous characters.
+ length := len(s)
+ if length > cap(b) {
+ *buf = make([]byte, length)
+ } else {
+ *buf = (*buf)[:length]
+ }
+ b = *buf
+
+ copy(b, s[:w])
+ }
+ b[w] = c
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/recovery.go b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/recovery.go
new file mode 100644
index 000000000000..563f5aaa8e24
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/recovery.go
@@ -0,0 +1,171 @@
+// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
+// Use of this source code is governed by a MIT style
+// license that can be found in the LICENSE file.
+
+package gin
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "log"
+ "net"
+ "net/http"
+ "net/http/httputil"
+ "os"
+ "runtime"
+ "strings"
+ "time"
+)
+
+var (
+ dunno = []byte("???")
+ centerDot = []byte("·")
+ dot = []byte(".")
+ slash = []byte("/")
+)
+
+// RecoveryFunc defines the function passable to CustomRecovery.
+type RecoveryFunc func(c *Context, err interface{})
+
+// Recovery returns a middleware that recovers from any panics and writes a 500 if there was one.
+func Recovery() HandlerFunc {
+ return RecoveryWithWriter(DefaultErrorWriter)
+}
+
+//CustomRecovery returns a middleware that recovers from any panics and calls the provided handle func to handle it.
+func CustomRecovery(handle RecoveryFunc) HandlerFunc {
+ return RecoveryWithWriter(DefaultErrorWriter, handle)
+}
+
+// RecoveryWithWriter returns a middleware for a given writer that recovers from any panics and writes a 500 if there was one.
+func RecoveryWithWriter(out io.Writer, recovery ...RecoveryFunc) HandlerFunc {
+ if len(recovery) > 0 {
+ return CustomRecoveryWithWriter(out, recovery[0])
+ }
+ return CustomRecoveryWithWriter(out, defaultHandleRecovery)
+}
+
+// CustomRecoveryWithWriter returns a middleware for a given writer that recovers from any panics and calls the provided handle func to handle it.
+func CustomRecoveryWithWriter(out io.Writer, handle RecoveryFunc) HandlerFunc {
+ var logger *log.Logger
+ if out != nil {
+ logger = log.New(out, "\n\n\x1b[31m", log.LstdFlags)
+ }
+ return func(c *Context) {
+ defer func() {
+ if err := recover(); err != nil {
+ // Check for a broken connection, as it is not really a
+ // condition that warrants a panic stack trace.
+ var brokenPipe bool
+ if ne, ok := err.(*net.OpError); ok {
+ if se, ok := ne.Err.(*os.SyscallError); ok {
+ if strings.Contains(strings.ToLower(se.Error()), "broken pipe") || strings.Contains(strings.ToLower(se.Error()), "connection reset by peer") {
+ brokenPipe = true
+ }
+ }
+ }
+ if logger != nil {
+ stack := stack(3)
+ httpRequest, _ := httputil.DumpRequest(c.Request, false)
+ headers := strings.Split(string(httpRequest), "\r\n")
+ for idx, header := range headers {
+ current := strings.Split(header, ":")
+ if current[0] == "Authorization" {
+ headers[idx] = current[0] + ": *"
+ }
+ }
+ headersToStr := strings.Join(headers, "\r\n")
+ if brokenPipe {
+ logger.Printf("%s\n%s%s", err, headersToStr, reset)
+ } else if IsDebugging() {
+ logger.Printf("[Recovery] %s panic recovered:\n%s\n%s\n%s%s",
+ timeFormat(time.Now()), headersToStr, err, stack, reset)
+ } else {
+ logger.Printf("[Recovery] %s panic recovered:\n%s\n%s%s",
+ timeFormat(time.Now()), err, stack, reset)
+ }
+ }
+ if brokenPipe {
+ // If the connection is dead, we can't write a status to it.
+ c.Error(err.(error)) // nolint: errcheck
+ c.Abort()
+ } else {
+ handle(c, err)
+ }
+ }
+ }()
+ c.Next()
+ }
+}
+
+func defaultHandleRecovery(c *Context, err interface{}) {
+ c.AbortWithStatus(http.StatusInternalServerError)
+}
+
+// stack returns a nicely formatted stack frame, skipping skip frames.
+func stack(skip int) []byte {
+ buf := new(bytes.Buffer) // the returned data
+ // As we loop, we open files and read them. These variables record the currently
+ // loaded file.
+ var lines [][]byte
+ var lastFile string
+ for i := skip; ; i++ { // Skip the expected number of frames
+ pc, file, line, ok := runtime.Caller(i)
+ if !ok {
+ break
+ }
+ // Print this much at least. If we can't find the source, it won't show.
+ fmt.Fprintf(buf, "%s:%d (0x%x)\n", file, line, pc)
+ if file != lastFile {
+ data, err := ioutil.ReadFile(file)
+ if err != nil {
+ continue
+ }
+ lines = bytes.Split(data, []byte{'\n'})
+ lastFile = file
+ }
+ fmt.Fprintf(buf, "\t%s: %s\n", function(pc), source(lines, line))
+ }
+ return buf.Bytes()
+}
+
+// source returns a space-trimmed slice of the n'th line.
+func source(lines [][]byte, n int) []byte {
+ n-- // in stack trace, lines are 1-indexed but our array is 0-indexed
+ if n < 0 || n >= len(lines) {
+ return dunno
+ }
+ return bytes.TrimSpace(lines[n])
+}
+
+// function returns, if possible, the name of the function containing the PC.
+func function(pc uintptr) []byte {
+ fn := runtime.FuncForPC(pc)
+ if fn == nil {
+ return dunno
+ }
+ name := []byte(fn.Name())
+ // The name includes the path name to the package, which is unnecessary
+ // since the file name is already included. Plus, it has center dots.
+ // That is, we see
+ // runtime/debug.*T·ptrmethod
+ // and want
+ // *T.ptrmethod
+ // Also the package path might contains dot (e.g. code.google.com/...),
+ // so first eliminate the path prefix
+ if lastSlash := bytes.LastIndex(name, slash); lastSlash >= 0 {
+ name = name[lastSlash+1:]
+ }
+ if period := bytes.Index(name, dot); period >= 0 {
+ name = name[period+1:]
+ }
+ name = bytes.Replace(name, centerDot, dot, -1)
+ return name
+}
+
+func timeFormat(t time.Time) string {
+ timeString := t.Format("2006/01/02 - 15:04:05")
+ return timeString
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/render/data.go b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/render/data.go
new file mode 100644
index 000000000000..6ba657ba0a5f
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/render/data.go
@@ -0,0 +1,25 @@
+// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
+// Use of this source code is governed by a MIT style
+// license that can be found in the LICENSE file.
+
+package render
+
+import "net/http"
+
+// Data contains ContentType and bytes data.
+type Data struct {
+ ContentType string
+ Data []byte
+}
+
+// Render (Data) writes data with custom ContentType.
+func (r Data) Render(w http.ResponseWriter) (err error) {
+ r.WriteContentType(w)
+ _, err = w.Write(r.Data)
+ return
+}
+
+// WriteContentType (Data) writes custom ContentType.
+func (r Data) WriteContentType(w http.ResponseWriter) {
+ writeContentType(w, []string{r.ContentType})
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/render/html.go b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/render/html.go
new file mode 100644
index 000000000000..6696ece99716
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/render/html.go
@@ -0,0 +1,92 @@
+// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
+// Use of this source code is governed by a MIT style
+// license that can be found in the LICENSE file.
+
+package render
+
+import (
+ "html/template"
+ "net/http"
+)
+
+// Delims represents a set of Left and Right delimiters for HTML template rendering.
+type Delims struct {
+ // Left delimiter, defaults to {{.
+ Left string
+ // Right delimiter, defaults to }}.
+ Right string
+}
+
+// HTMLRender interface is to be implemented by HTMLProduction and HTMLDebug.
+type HTMLRender interface {
+ // Instance returns an HTML instance.
+ Instance(string, interface{}) Render
+}
+
+// HTMLProduction contains template reference and its delims.
+type HTMLProduction struct {
+ Template *template.Template
+ Delims Delims
+}
+
+// HTMLDebug contains template delims and pattern and function with file list.
+type HTMLDebug struct {
+ Files []string
+ Glob string
+ Delims Delims
+ FuncMap template.FuncMap
+}
+
+// HTML contains template reference and its name with given interface object.
+type HTML struct {
+ Template *template.Template
+ Name string
+ Data interface{}
+}
+
+var htmlContentType = []string{"text/html; charset=utf-8"}
+
+// Instance (HTMLProduction) returns an HTML instance which it realizes Render interface.
+func (r HTMLProduction) Instance(name string, data interface{}) Render {
+ return HTML{
+ Template: r.Template,
+ Name: name,
+ Data: data,
+ }
+}
+
+// Instance (HTMLDebug) returns an HTML instance which it realizes Render interface.
+func (r HTMLDebug) Instance(name string, data interface{}) Render {
+ return HTML{
+ Template: r.loadTemplate(),
+ Name: name,
+ Data: data,
+ }
+}
+func (r HTMLDebug) loadTemplate() *template.Template {
+ if r.FuncMap == nil {
+ r.FuncMap = template.FuncMap{}
+ }
+ if len(r.Files) > 0 {
+ return template.Must(template.New("").Delims(r.Delims.Left, r.Delims.Right).Funcs(r.FuncMap).ParseFiles(r.Files...))
+ }
+ if r.Glob != "" {
+ return template.Must(template.New("").Delims(r.Delims.Left, r.Delims.Right).Funcs(r.FuncMap).ParseGlob(r.Glob))
+ }
+ panic("the HTML debug render was created without files or glob pattern")
+}
+
+// Render (HTML) executes template and writes its result with custom ContentType for response.
+func (r HTML) Render(w http.ResponseWriter) error {
+ r.WriteContentType(w)
+
+ if r.Name == "" {
+ return r.Template.Execute(w, r.Data)
+ }
+ return r.Template.ExecuteTemplate(w, r.Name, r.Data)
+}
+
+// WriteContentType (HTML) writes HTML ContentType.
+func (r HTML) WriteContentType(w http.ResponseWriter) {
+ writeContentType(w, htmlContentType)
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/render/json.go b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/render/json.go
new file mode 100644
index 000000000000..418630939808
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/render/json.go
@@ -0,0 +1,193 @@
+// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
+// Use of this source code is governed by a MIT style
+// license that can be found in the LICENSE file.
+
+package render
+
+import (
+ "bytes"
+ "fmt"
+ "html/template"
+ "net/http"
+
+ "github.com/gin-gonic/gin/internal/bytesconv"
+ "github.com/gin-gonic/gin/internal/json"
+)
+
+// JSON contains the given interface object.
+type JSON struct {
+ Data interface{}
+}
+
+// IndentedJSON contains the given interface object.
+type IndentedJSON struct {
+ Data interface{}
+}
+
+// SecureJSON contains the given interface object and its prefix.
+type SecureJSON struct {
+ Prefix string
+ Data interface{}
+}
+
+// JsonpJSON contains the given interface object its callback.
+type JsonpJSON struct {
+ Callback string
+ Data interface{}
+}
+
+// AsciiJSON contains the given interface object.
+type AsciiJSON struct {
+ Data interface{}
+}
+
+// PureJSON contains the given interface object.
+type PureJSON struct {
+ Data interface{}
+}
+
+var jsonContentType = []string{"application/json; charset=utf-8"}
+var jsonpContentType = []string{"application/javascript; charset=utf-8"}
+var jsonAsciiContentType = []string{"application/json"}
+
+// Render (JSON) writes data with custom ContentType.
+func (r JSON) Render(w http.ResponseWriter) (err error) {
+ if err = WriteJSON(w, r.Data); err != nil {
+ panic(err)
+ }
+ return
+}
+
+// WriteContentType (JSON) writes JSON ContentType.
+func (r JSON) WriteContentType(w http.ResponseWriter) {
+ writeContentType(w, jsonContentType)
+}
+
+// WriteJSON marshals the given interface object and writes it with custom ContentType.
+func WriteJSON(w http.ResponseWriter, obj interface{}) error {
+ writeContentType(w, jsonContentType)
+ jsonBytes, err := json.Marshal(obj)
+ if err != nil {
+ return err
+ }
+ _, err = w.Write(jsonBytes)
+ return err
+}
+
+// Render (IndentedJSON) marshals the given interface object and writes it with custom ContentType.
+func (r IndentedJSON) Render(w http.ResponseWriter) error {
+ r.WriteContentType(w)
+ jsonBytes, err := json.MarshalIndent(r.Data, "", " ")
+ if err != nil {
+ return err
+ }
+ _, err = w.Write(jsonBytes)
+ return err
+}
+
+// WriteContentType (IndentedJSON) writes JSON ContentType.
+func (r IndentedJSON) WriteContentType(w http.ResponseWriter) {
+ writeContentType(w, jsonContentType)
+}
+
+// Render (SecureJSON) marshals the given interface object and writes it with custom ContentType.
+func (r SecureJSON) Render(w http.ResponseWriter) error {
+ r.WriteContentType(w)
+ jsonBytes, err := json.Marshal(r.Data)
+ if err != nil {
+ return err
+ }
+ // if the jsonBytes is array values
+ if bytes.HasPrefix(jsonBytes, bytesconv.StringToBytes("[")) && bytes.HasSuffix(jsonBytes,
+ bytesconv.StringToBytes("]")) {
+ _, err = w.Write(bytesconv.StringToBytes(r.Prefix))
+ if err != nil {
+ return err
+ }
+ }
+ _, err = w.Write(jsonBytes)
+ return err
+}
+
+// WriteContentType (SecureJSON) writes JSON ContentType.
+func (r SecureJSON) WriteContentType(w http.ResponseWriter) {
+ writeContentType(w, jsonContentType)
+}
+
+// Render (JsonpJSON) marshals the given interface object and writes it and its callback with custom ContentType.
+func (r JsonpJSON) Render(w http.ResponseWriter) (err error) {
+ r.WriteContentType(w)
+ ret, err := json.Marshal(r.Data)
+ if err != nil {
+ return err
+ }
+
+ if r.Callback == "" {
+ _, err = w.Write(ret)
+ return err
+ }
+
+ callback := template.JSEscapeString(r.Callback)
+ _, err = w.Write(bytesconv.StringToBytes(callback))
+ if err != nil {
+ return err
+ }
+ _, err = w.Write(bytesconv.StringToBytes("("))
+ if err != nil {
+ return err
+ }
+ _, err = w.Write(ret)
+ if err != nil {
+ return err
+ }
+ _, err = w.Write(bytesconv.StringToBytes(");"))
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+// WriteContentType (JsonpJSON) writes Javascript ContentType.
+func (r JsonpJSON) WriteContentType(w http.ResponseWriter) {
+ writeContentType(w, jsonpContentType)
+}
+
+// Render (AsciiJSON) marshals the given interface object and writes it with custom ContentType.
+func (r AsciiJSON) Render(w http.ResponseWriter) (err error) {
+ r.WriteContentType(w)
+ ret, err := json.Marshal(r.Data)
+ if err != nil {
+ return err
+ }
+
+ var buffer bytes.Buffer
+ for _, r := range bytesconv.BytesToString(ret) {
+ cvt := string(r)
+ if r >= 128 {
+ cvt = fmt.Sprintf("\\u%04x", int64(r))
+ }
+ buffer.WriteString(cvt)
+ }
+
+ _, err = w.Write(buffer.Bytes())
+ return err
+}
+
+// WriteContentType (AsciiJSON) writes JSON ContentType.
+func (r AsciiJSON) WriteContentType(w http.ResponseWriter) {
+ writeContentType(w, jsonAsciiContentType)
+}
+
+// Render (PureJSON) writes custom ContentType and encodes the given interface object.
+func (r PureJSON) Render(w http.ResponseWriter) error {
+ r.WriteContentType(w)
+ encoder := json.NewEncoder(w)
+ encoder.SetEscapeHTML(false)
+ return encoder.Encode(r.Data)
+}
+
+// WriteContentType (PureJSON) writes custom ContentType.
+func (r PureJSON) WriteContentType(w http.ResponseWriter) {
+ writeContentType(w, jsonContentType)
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/render/msgpack.go b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/render/msgpack.go
new file mode 100644
index 000000000000..6ef5b6e514c3
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/render/msgpack.go
@@ -0,0 +1,42 @@
+// Copyright 2017 Manu Martinez-Almeida. All rights reserved.
+// Use of this source code is governed by a MIT style
+// license that can be found in the LICENSE file.
+
+//go:build !nomsgpack
+// +build !nomsgpack
+
+package render
+
+import (
+ "net/http"
+
+ "github.com/ugorji/go/codec"
+)
+
+var (
+ _ Render = MsgPack{}
+)
+
+// MsgPack contains the given interface object.
+type MsgPack struct {
+ Data interface{}
+}
+
+var msgpackContentType = []string{"application/msgpack; charset=utf-8"}
+
+// WriteContentType (MsgPack) writes MsgPack ContentType.
+func (r MsgPack) WriteContentType(w http.ResponseWriter) {
+ writeContentType(w, msgpackContentType)
+}
+
+// Render (MsgPack) encodes the given interface object and writes data with custom ContentType.
+func (r MsgPack) Render(w http.ResponseWriter) error {
+ return WriteMsgPack(w, r.Data)
+}
+
+// WriteMsgPack writes MsgPack ContentType and encodes the given interface object.
+func WriteMsgPack(w http.ResponseWriter, obj interface{}) error {
+ writeContentType(w, msgpackContentType)
+ var mh codec.MsgpackHandle
+ return codec.NewEncoder(w, &mh).Encode(obj)
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/render/protobuf.go b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/render/protobuf.go
new file mode 100644
index 000000000000..15aca9959cd6
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/render/protobuf.go
@@ -0,0 +1,36 @@
+// Copyright 2018 Gin Core Team. All rights reserved.
+// Use of this source code is governed by a MIT style
+// license that can be found in the LICENSE file.
+
+package render
+
+import (
+ "net/http"
+
+ "github.com/golang/protobuf/proto"
+)
+
+// ProtoBuf contains the given interface object.
+type ProtoBuf struct {
+ Data interface{}
+}
+
+var protobufContentType = []string{"application/x-protobuf"}
+
+// Render (ProtoBuf) marshals the given interface object and writes data with custom ContentType.
+func (r ProtoBuf) Render(w http.ResponseWriter) error {
+ r.WriteContentType(w)
+
+ bytes, err := proto.Marshal(r.Data.(proto.Message))
+ if err != nil {
+ return err
+ }
+
+ _, err = w.Write(bytes)
+ return err
+}
+
+// WriteContentType (ProtoBuf) writes ProtoBuf ContentType.
+func (r ProtoBuf) WriteContentType(w http.ResponseWriter) {
+ writeContentType(w, protobufContentType)
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/render/reader.go b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/render/reader.go
new file mode 100644
index 000000000000..d5282e492724
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/render/reader.go
@@ -0,0 +1,48 @@
+// Copyright 2018 Gin Core Team. All rights reserved.
+// Use of this source code is governed by a MIT style
+// license that can be found in the LICENSE file.
+
+package render
+
+import (
+ "io"
+ "net/http"
+ "strconv"
+)
+
+// Reader contains the IO reader and its length, and custom ContentType and other headers.
+type Reader struct {
+ ContentType string
+ ContentLength int64
+ Reader io.Reader
+ Headers map[string]string
+}
+
+// Render (Reader) writes data with custom ContentType and headers.
+func (r Reader) Render(w http.ResponseWriter) (err error) {
+ r.WriteContentType(w)
+ if r.ContentLength >= 0 {
+ if r.Headers == nil {
+ r.Headers = map[string]string{}
+ }
+ r.Headers["Content-Length"] = strconv.FormatInt(r.ContentLength, 10)
+ }
+ r.writeHeaders(w, r.Headers)
+ _, err = io.Copy(w, r.Reader)
+ return
+}
+
+// WriteContentType (Reader) writes custom ContentType.
+func (r Reader) WriteContentType(w http.ResponseWriter) {
+ writeContentType(w, []string{r.ContentType})
+}
+
+// writeHeaders writes custom Header.
+func (r Reader) writeHeaders(w http.ResponseWriter, headers map[string]string) {
+ header := w.Header()
+ for k, v := range headers {
+ if header.Get(k) == "" {
+ header.Set(k, v)
+ }
+ }
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/render/redirect.go b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/render/redirect.go
new file mode 100644
index 000000000000..c006691ca635
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/render/redirect.go
@@ -0,0 +1,29 @@
+// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
+// Use of this source code is governed by a MIT style
+// license that can be found in the LICENSE file.
+
+package render
+
+import (
+ "fmt"
+ "net/http"
+)
+
+// Redirect contains the http request reference and redirects status code and location.
+type Redirect struct {
+ Code int
+ Request *http.Request
+ Location string
+}
+
+// Render (Redirect) redirects the http request to new location and writes redirect response.
+func (r Redirect) Render(w http.ResponseWriter) error {
+ if (r.Code < http.StatusMultipleChoices || r.Code > http.StatusPermanentRedirect) && r.Code != http.StatusCreated {
+ panic(fmt.Sprintf("Cannot redirect with status code %d", r.Code))
+ }
+ http.Redirect(w, r.Request, r.Location, r.Code)
+ return nil
+}
+
+// WriteContentType (Redirect) don't write any ContentType.
+func (r Redirect) WriteContentType(http.ResponseWriter) {}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/render/render.go b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/render/render.go
new file mode 100644
index 000000000000..bcd568bfba7a
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/render/render.go
@@ -0,0 +1,40 @@
+// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
+// Use of this source code is governed by a MIT style
+// license that can be found in the LICENSE file.
+
+package render
+
+import "net/http"
+
+// Render interface is to be implemented by JSON, XML, HTML, YAML and so on.
+type Render interface {
+ // Render writes data with custom ContentType.
+ Render(http.ResponseWriter) error
+ // WriteContentType writes custom ContentType.
+ WriteContentType(w http.ResponseWriter)
+}
+
+var (
+ _ Render = JSON{}
+ _ Render = IndentedJSON{}
+ _ Render = SecureJSON{}
+ _ Render = JsonpJSON{}
+ _ Render = XML{}
+ _ Render = String{}
+ _ Render = Redirect{}
+ _ Render = Data{}
+ _ Render = HTML{}
+ _ HTMLRender = HTMLDebug{}
+ _ HTMLRender = HTMLProduction{}
+ _ Render = YAML{}
+ _ Render = Reader{}
+ _ Render = AsciiJSON{}
+ _ Render = ProtoBuf{}
+)
+
+func writeContentType(w http.ResponseWriter, value []string) {
+ header := w.Header()
+ if val := header["Content-Type"]; len(val) == 0 {
+ header["Content-Type"] = value
+ }
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/render/text.go b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/render/text.go
new file mode 100644
index 000000000000..461b720af5ed
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/render/text.go
@@ -0,0 +1,41 @@
+// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
+// Use of this source code is governed by a MIT style
+// license that can be found in the LICENSE file.
+
+package render
+
+import (
+ "fmt"
+ "net/http"
+
+ "github.com/gin-gonic/gin/internal/bytesconv"
+)
+
+// String contains the given interface object slice and its format.
+type String struct {
+ Format string
+ Data []interface{}
+}
+
+var plainContentType = []string{"text/plain; charset=utf-8"}
+
+// Render (String) writes data with custom ContentType.
+func (r String) Render(w http.ResponseWriter) error {
+ return WriteString(w, r.Format, r.Data)
+}
+
+// WriteContentType (String) writes Plain ContentType.
+func (r String) WriteContentType(w http.ResponseWriter) {
+ writeContentType(w, plainContentType)
+}
+
+// WriteString writes data according to its format and write custom ContentType.
+func WriteString(w http.ResponseWriter, format string, data []interface{}) (err error) {
+ writeContentType(w, plainContentType)
+ if len(data) > 0 {
+ _, err = fmt.Fprintf(w, format, data...)
+ return
+ }
+ _, err = w.Write(bytesconv.StringToBytes(format))
+ return
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/render/xml.go b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/render/xml.go
new file mode 100644
index 000000000000..cc5390a2d05b
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/render/xml.go
@@ -0,0 +1,28 @@
+// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
+// Use of this source code is governed by a MIT style
+// license that can be found in the LICENSE file.
+
+package render
+
+import (
+ "encoding/xml"
+ "net/http"
+)
+
+// XML contains the given interface object.
+type XML struct {
+ Data interface{}
+}
+
+var xmlContentType = []string{"application/xml; charset=utf-8"}
+
+// Render (XML) encodes the given interface object and writes data with custom ContentType.
+func (r XML) Render(w http.ResponseWriter) error {
+ r.WriteContentType(w)
+ return xml.NewEncoder(w).Encode(r.Data)
+}
+
+// WriteContentType (XML) writes XML ContentType for response.
+func (r XML) WriteContentType(w http.ResponseWriter) {
+ writeContentType(w, xmlContentType)
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/render/yaml.go b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/render/yaml.go
new file mode 100644
index 000000000000..0df78360893c
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/render/yaml.go
@@ -0,0 +1,36 @@
+// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
+// Use of this source code is governed by a MIT style
+// license that can be found in the LICENSE file.
+
+package render
+
+import (
+ "net/http"
+
+ "gopkg.in/yaml.v2"
+)
+
+// YAML contains the given interface object.
+type YAML struct {
+ Data interface{}
+}
+
+var yamlContentType = []string{"application/x-yaml; charset=utf-8"}
+
+// Render (YAML) marshals the given interface object and writes data with custom ContentType.
+func (r YAML) Render(w http.ResponseWriter) error {
+ r.WriteContentType(w)
+
+ bytes, err := yaml.Marshal(r.Data)
+ if err != nil {
+ return err
+ }
+
+ _, err = w.Write(bytes)
+ return err
+}
+
+// WriteContentType (YAML) writes YAML ContentType for response.
+func (r YAML) WriteContentType(w http.ResponseWriter) {
+ writeContentType(w, yamlContentType)
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/response_writer.go b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/response_writer.go
new file mode 100644
index 000000000000..26826689a5cf
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/response_writer.go
@@ -0,0 +1,126 @@
+// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
+// Use of this source code is governed by a MIT style
+// license that can be found in the LICENSE file.
+
+package gin
+
+import (
+ "bufio"
+ "io"
+ "net"
+ "net/http"
+)
+
+const (
+ noWritten = -1
+ defaultStatus = http.StatusOK
+)
+
+// ResponseWriter ...
+type ResponseWriter interface {
+ http.ResponseWriter
+ http.Hijacker
+ http.Flusher
+ http.CloseNotifier
+
+ // Returns the HTTP response status code of the current request.
+ Status() int
+
+ // Returns the number of bytes already written into the response http body.
+ // See Written()
+ Size() int
+
+ // Writes the string into the response body.
+ WriteString(string) (int, error)
+
+ // Returns true if the response body was already written.
+ Written() bool
+
+ // Forces to write the http header (status code + headers).
+ WriteHeaderNow()
+
+ // get the http.Pusher for server push
+ Pusher() http.Pusher
+}
+
+type responseWriter struct {
+ http.ResponseWriter
+ size int
+ status int
+}
+
+var _ ResponseWriter = &responseWriter{}
+
+func (w *responseWriter) reset(writer http.ResponseWriter) {
+ w.ResponseWriter = writer
+ w.size = noWritten
+ w.status = defaultStatus
+}
+
+func (w *responseWriter) WriteHeader(code int) {
+ if code > 0 && w.status != code {
+ if w.Written() {
+ debugPrint("[WARNING] Headers were already written. Wanted to override status code %d with %d", w.status, code)
+ }
+ w.status = code
+ }
+}
+
+func (w *responseWriter) WriteHeaderNow() {
+ if !w.Written() {
+ w.size = 0
+ w.ResponseWriter.WriteHeader(w.status)
+ }
+}
+
+func (w *responseWriter) Write(data []byte) (n int, err error) {
+ w.WriteHeaderNow()
+ n, err = w.ResponseWriter.Write(data)
+ w.size += n
+ return
+}
+
+func (w *responseWriter) WriteString(s string) (n int, err error) {
+ w.WriteHeaderNow()
+ n, err = io.WriteString(w.ResponseWriter, s)
+ w.size += n
+ return
+}
+
+func (w *responseWriter) Status() int {
+ return w.status
+}
+
+func (w *responseWriter) Size() int {
+ return w.size
+}
+
+func (w *responseWriter) Written() bool {
+ return w.size != noWritten
+}
+
+// Hijack implements the http.Hijacker interface.
+func (w *responseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
+ if w.size < 0 {
+ w.size = 0
+ }
+ return w.ResponseWriter.(http.Hijacker).Hijack()
+}
+
+// CloseNotify implements the http.CloseNotify interface.
+func (w *responseWriter) CloseNotify() <-chan bool {
+ return w.ResponseWriter.(http.CloseNotifier).CloseNotify()
+}
+
+// Flush implements the http.Flush interface.
+func (w *responseWriter) Flush() {
+ w.WriteHeaderNow()
+ w.ResponseWriter.(http.Flusher).Flush()
+}
+
+func (w *responseWriter) Pusher() (pusher http.Pusher) {
+ if pusher, ok := w.ResponseWriter.(http.Pusher); ok {
+ return pusher
+ }
+ return nil
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/routergroup.go b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/routergroup.go
new file mode 100644
index 000000000000..15d9930d3d14
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/routergroup.go
@@ -0,0 +1,230 @@
+// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
+// Use of this source code is governed by a MIT style
+// license that can be found in the LICENSE file.
+
+package gin
+
+import (
+ "net/http"
+ "path"
+ "regexp"
+ "strings"
+)
+
+// IRouter defines all router handle interface includes single and group router.
+type IRouter interface {
+ IRoutes
+ Group(string, ...HandlerFunc) *RouterGroup
+}
+
+// IRoutes defines all router handle interface.
+type IRoutes interface {
+ Use(...HandlerFunc) IRoutes
+
+ Handle(string, string, ...HandlerFunc) IRoutes
+ Any(string, ...HandlerFunc) IRoutes
+ GET(string, ...HandlerFunc) IRoutes
+ POST(string, ...HandlerFunc) IRoutes
+ DELETE(string, ...HandlerFunc) IRoutes
+ PATCH(string, ...HandlerFunc) IRoutes
+ PUT(string, ...HandlerFunc) IRoutes
+ OPTIONS(string, ...HandlerFunc) IRoutes
+ HEAD(string, ...HandlerFunc) IRoutes
+
+ StaticFile(string, string) IRoutes
+ Static(string, string) IRoutes
+ StaticFS(string, http.FileSystem) IRoutes
+}
+
+// RouterGroup is used internally to configure router, a RouterGroup is associated with
+// a prefix and an array of handlers (middleware).
+type RouterGroup struct {
+ Handlers HandlersChain
+ basePath string
+ engine *Engine
+ root bool
+}
+
+var _ IRouter = &RouterGroup{}
+
+// Use adds middleware to the group, see example code in GitHub.
+func (group *RouterGroup) Use(middleware ...HandlerFunc) IRoutes {
+ group.Handlers = append(group.Handlers, middleware...)
+ return group.returnObj()
+}
+
+// Group creates a new router group. You should add all the routes that have common middlewares or the same path prefix.
+// For example, all the routes that use a common middleware for authorization could be grouped.
+func (group *RouterGroup) Group(relativePath string, handlers ...HandlerFunc) *RouterGroup {
+ return &RouterGroup{
+ Handlers: group.combineHandlers(handlers),
+ basePath: group.calculateAbsolutePath(relativePath),
+ engine: group.engine,
+ }
+}
+
+// BasePath returns the base path of router group.
+// For example, if v := router.Group("/rest/n/v1/api"), v.BasePath() is "/rest/n/v1/api".
+func (group *RouterGroup) BasePath() string {
+ return group.basePath
+}
+
+func (group *RouterGroup) handle(httpMethod, relativePath string, handlers HandlersChain) IRoutes {
+ absolutePath := group.calculateAbsolutePath(relativePath)
+ handlers = group.combineHandlers(handlers)
+ group.engine.addRoute(httpMethod, absolutePath, handlers)
+ return group.returnObj()
+}
+
+// Handle registers a new request handle and middleware with the given path and method.
+// The last handler should be the real handler, the other ones should be middleware that can and should be shared among different routes.
+// See the example code in GitHub.
+//
+// For GET, POST, PUT, PATCH and DELETE requests the respective shortcut
+// functions can be used.
+//
+// This function is intended for bulk loading and to allow the usage of less
+// frequently used, non-standardized or custom methods (e.g. for internal
+// communication with a proxy).
+func (group *RouterGroup) Handle(httpMethod, relativePath string, handlers ...HandlerFunc) IRoutes {
+ if matches, err := regexp.MatchString("^[A-Z]+$", httpMethod); !matches || err != nil {
+ panic("http method " + httpMethod + " is not valid")
+ }
+ return group.handle(httpMethod, relativePath, handlers)
+}
+
+// POST is a shortcut for router.Handle("POST", path, handle).
+func (group *RouterGroup) POST(relativePath string, handlers ...HandlerFunc) IRoutes {
+ return group.handle(http.MethodPost, relativePath, handlers)
+}
+
+// GET is a shortcut for router.Handle("GET", path, handle).
+func (group *RouterGroup) GET(relativePath string, handlers ...HandlerFunc) IRoutes {
+ return group.handle(http.MethodGet, relativePath, handlers)
+}
+
+// DELETE is a shortcut for router.Handle("DELETE", path, handle).
+func (group *RouterGroup) DELETE(relativePath string, handlers ...HandlerFunc) IRoutes {
+ return group.handle(http.MethodDelete, relativePath, handlers)
+}
+
+// PATCH is a shortcut for router.Handle("PATCH", path, handle).
+func (group *RouterGroup) PATCH(relativePath string, handlers ...HandlerFunc) IRoutes {
+ return group.handle(http.MethodPatch, relativePath, handlers)
+}
+
+// PUT is a shortcut for router.Handle("PUT", path, handle).
+func (group *RouterGroup) PUT(relativePath string, handlers ...HandlerFunc) IRoutes {
+ return group.handle(http.MethodPut, relativePath, handlers)
+}
+
+// OPTIONS is a shortcut for router.Handle("OPTIONS", path, handle).
+func (group *RouterGroup) OPTIONS(relativePath string, handlers ...HandlerFunc) IRoutes {
+ return group.handle(http.MethodOptions, relativePath, handlers)
+}
+
+// HEAD is a shortcut for router.Handle("HEAD", path, handle).
+func (group *RouterGroup) HEAD(relativePath string, handlers ...HandlerFunc) IRoutes {
+ return group.handle(http.MethodHead, relativePath, handlers)
+}
+
+// Any registers a route that matches all the HTTP methods.
+// GET, POST, PUT, PATCH, HEAD, OPTIONS, DELETE, CONNECT, TRACE.
+func (group *RouterGroup) Any(relativePath string, handlers ...HandlerFunc) IRoutes {
+ group.handle(http.MethodGet, relativePath, handlers)
+ group.handle(http.MethodPost, relativePath, handlers)
+ group.handle(http.MethodPut, relativePath, handlers)
+ group.handle(http.MethodPatch, relativePath, handlers)
+ group.handle(http.MethodHead, relativePath, handlers)
+ group.handle(http.MethodOptions, relativePath, handlers)
+ group.handle(http.MethodDelete, relativePath, handlers)
+ group.handle(http.MethodConnect, relativePath, handlers)
+ group.handle(http.MethodTrace, relativePath, handlers)
+ return group.returnObj()
+}
+
+// StaticFile registers a single route in order to serve a single file of the local filesystem.
+// router.StaticFile("favicon.ico", "./resources/favicon.ico")
+func (group *RouterGroup) StaticFile(relativePath, filepath string) IRoutes {
+ if strings.Contains(relativePath, ":") || strings.Contains(relativePath, "*") {
+ panic("URL parameters can not be used when serving a static file")
+ }
+ handler := func(c *Context) {
+ c.File(filepath)
+ }
+ group.GET(relativePath, handler)
+ group.HEAD(relativePath, handler)
+ return group.returnObj()
+}
+
+// Static serves files from the given file system root.
+// Internally a http.FileServer is used, therefore http.NotFound is used instead
+// of the Router's NotFound handler.
+// To use the operating system's file system implementation,
+// use :
+// router.Static("/static", "/var/www")
+func (group *RouterGroup) Static(relativePath, root string) IRoutes {
+ return group.StaticFS(relativePath, Dir(root, false))
+}
+
+// StaticFS works just like `Static()` but a custom `http.FileSystem` can be used instead.
+// Gin by default user: gin.Dir()
+func (group *RouterGroup) StaticFS(relativePath string, fs http.FileSystem) IRoutes {
+ if strings.Contains(relativePath, ":") || strings.Contains(relativePath, "*") {
+ panic("URL parameters can not be used when serving a static folder")
+ }
+ handler := group.createStaticHandler(relativePath, fs)
+ urlPattern := path.Join(relativePath, "/*filepath")
+
+ // Register GET and HEAD handlers
+ group.GET(urlPattern, handler)
+ group.HEAD(urlPattern, handler)
+ return group.returnObj()
+}
+
+func (group *RouterGroup) createStaticHandler(relativePath string, fs http.FileSystem) HandlerFunc {
+ absolutePath := group.calculateAbsolutePath(relativePath)
+ fileServer := http.StripPrefix(absolutePath, http.FileServer(fs))
+
+ return func(c *Context) {
+ if _, noListing := fs.(*onlyFilesFS); noListing {
+ c.Writer.WriteHeader(http.StatusNotFound)
+ }
+
+ file := c.Param("filepath")
+ // Check if file exists and/or if we have permission to access it
+ f, err := fs.Open(file)
+ if err != nil {
+ c.Writer.WriteHeader(http.StatusNotFound)
+ c.handlers = group.engine.noRoute
+ // Reset index
+ c.index = -1
+ return
+ }
+ f.Close()
+
+ fileServer.ServeHTTP(c.Writer, c.Request)
+ }
+}
+
+func (group *RouterGroup) combineHandlers(handlers HandlersChain) HandlersChain {
+ finalSize := len(group.Handlers) + len(handlers)
+ if finalSize >= int(abortIndex) {
+ panic("too many handlers")
+ }
+ mergedHandlers := make(HandlersChain, finalSize)
+ copy(mergedHandlers, group.Handlers)
+ copy(mergedHandlers[len(group.Handlers):], handlers)
+ return mergedHandlers
+}
+
+func (group *RouterGroup) calculateAbsolutePath(relativePath string) string {
+ return joinPaths(group.basePath, relativePath)
+}
+
+func (group *RouterGroup) returnObj() IRoutes {
+ if group.root {
+ return group.engine
+ }
+ return group
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/stub.go b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/stub.go
deleted file mode 100644
index 79c7a98ec0b2..000000000000
--- a/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/stub.go
+++ /dev/null
@@ -1,682 +0,0 @@
-// Code generated by depstubber. DO NOT EDIT.
-// This is a simple stub for github.com/gin-gonic/gin, strictly for use in testing.
-
-// See the LICENSE file for information about the licensing of the original library.
-// Source: github.com/gin-gonic/gin (exports: Context; functions: New)
-
-// Package gin is a stub of github.com/gin-gonic/gin, generated by depstubber.
-package gin
-
-import (
- bufio "bufio"
- template "html/template"
- io "io"
- multipart "mime/multipart"
- net "net"
- http "net/http"
- template0 "text/template"
- time "time"
-)
-
-type Context struct {
- Request *http.Request
- Writer ResponseWriter
- Params Params
- Keys map[string]interface{}
- Errors interface{}
- Accepted []string
-}
-
-func (_ *Context) Abort() {}
-
-func (_ *Context) AbortWithError(_ int, _ error) *Error {
- return nil
-}
-
-func (_ *Context) AbortWithStatus(_ int) {}
-
-func (_ *Context) AbortWithStatusJSON(_ int, _ interface{}) {}
-
-func (_ *Context) AsciiJSON(_ int, _ interface{}) {}
-
-func (_ *Context) Bind(_ interface{}) error {
- return nil
-}
-
-func (_ *Context) BindHeader(_ interface{}) error {
- return nil
-}
-
-func (_ *Context) BindJSON(_ interface{}) error {
- return nil
-}
-
-func (_ *Context) BindQuery(_ interface{}) error {
- return nil
-}
-
-func (_ *Context) BindUri(_ interface{}) error {
- return nil
-}
-
-func (_ *Context) BindWith(_ interface{}, _ interface{}) error {
- return nil
-}
-
-func (_ *Context) BindXML(_ interface{}) error {
- return nil
-}
-
-func (_ *Context) BindYAML(_ interface{}) error {
- return nil
-}
-
-func (_ *Context) ClientIP() string {
- return ""
-}
-
-func (_ *Context) ContentType() string {
- return ""
-}
-
-func (_ *Context) Cookie(_ string) (string, error) {
- return "", nil
-}
-
-func (_ *Context) Copy() *Context {
- return nil
-}
-
-func (_ *Context) Data(_ int, _ string, _ []byte) {}
-
-func (_ *Context) DataFromReader(_ int, _ int64, _ string, _ io.Reader, _ map[string]string) {}
-
-func (_ *Context) Deadline() (time.Time, bool) {
- return time.Time{}, false
-}
-
-func (_ *Context) DefaultPostForm(_ string, _ string) string {
- return ""
-}
-
-func (_ *Context) DefaultQuery(_ string, _ string) string {
- return ""
-}
-
-func (_ *Context) Done() <-chan struct{} {
- return nil
-}
-
-func (_ *Context) Err() error {
- return nil
-}
-
-func (_ *Context) Error(_ error) *Error {
- return nil
-}
-
-func (_ *Context) File(_ string) {}
-
-func (_ *Context) FileAttachment(_ string, _ string) {}
-
-func (_ *Context) FileFromFS(_ string, _ http.FileSystem) {}
-
-func (_ *Context) FormFile(_ string) (*multipart.FileHeader, error) {
- return nil, nil
-}
-
-func (_ *Context) FullPath() string {
- return ""
-}
-
-func (_ *Context) Get(_ string) (interface{}, bool) {
- return nil, false
-}
-
-func (_ *Context) GetBool(_ string) bool {
- return false
-}
-
-func (_ *Context) GetDuration(_ string) time.Duration {
- return 0
-}
-
-func (_ *Context) GetFloat64(_ string) float64 {
- return 0
-}
-
-func (_ *Context) GetHeader(_ string) string {
- return ""
-}
-
-func (_ *Context) GetInt(_ string) int {
- return 0
-}
-
-func (_ *Context) GetInt64(_ string) int64 {
- return 0
-}
-
-func (_ *Context) GetPostForm(_ string) (string, bool) {
- return "", false
-}
-
-func (_ *Context) GetPostFormArray(_ string) ([]string, bool) {
- return nil, false
-}
-
-func (_ *Context) GetPostFormMap(_ string) (map[string]string, bool) {
- return nil, false
-}
-
-func (_ *Context) GetQuery(_ string) (string, bool) {
- return "", false
-}
-
-func (_ *Context) GetQueryArray(_ string) ([]string, bool) {
- return nil, false
-}
-
-func (_ *Context) GetQueryMap(_ string) (map[string]string, bool) {
- return nil, false
-}
-
-func (_ *Context) GetRawData() ([]byte, error) {
- return nil, nil
-}
-
-func (_ *Context) GetString(_ string) string {
- return ""
-}
-
-func (_ *Context) GetStringMap(_ string) map[string]interface{} {
- return nil
-}
-
-func (_ *Context) GetStringMapString(_ string) map[string]string {
- return nil
-}
-
-func (_ *Context) GetStringMapStringSlice(_ string) map[string][]string {
- return nil
-}
-
-func (_ *Context) GetStringSlice(_ string) []string {
- return nil
-}
-
-func (_ *Context) GetTime(_ string) time.Time {
- return time.Time{}
-}
-
-func (_ *Context) GetUint(_ string) uint {
- return 0
-}
-
-func (_ *Context) GetUint64(_ string) uint64 {
- return 0
-}
-
-func (_ *Context) HTML(_ int, _ string, _ interface{}) {}
-
-func (_ *Context) Handler() HandlerFunc {
- return nil
-}
-
-func (_ *Context) HandlerName() string {
- return ""
-}
-
-func (_ *Context) HandlerNames() []string {
- return nil
-}
-
-func (_ *Context) Header(_ string, _ string) {}
-
-func (_ *Context) IndentedJSON(_ int, _ interface{}) {}
-
-func (_ *Context) IsAborted() bool {
- return false
-}
-
-func (_ *Context) IsWebsocket() bool {
- return false
-}
-
-func (_ *Context) JSON(_ int, _ interface{}) {}
-
-func (_ *Context) JSONP(_ int, _ interface{}) {}
-
-func (_ *Context) MultipartForm() (*multipart.Form, error) {
- return nil, nil
-}
-
-func (_ *Context) MustBindWith(_ interface{}, _ interface{}) error {
- return nil
-}
-
-func (_ *Context) MustGet(_ string) interface{} {
- return nil
-}
-
-func (_ *Context) Negotiate(_ int, _ Negotiate) {}
-
-func (_ *Context) NegotiateFormat(_ ...string) string {
- return ""
-}
-
-func (_ *Context) Next() {}
-
-func (_ *Context) Param(_ string) string {
- return ""
-}
-
-func (_ *Context) PostForm(_ string) string {
- return ""
-}
-
-func (_ *Context) PostFormArray(_ string) []string {
- return nil
-}
-
-func (_ *Context) PostFormMap(_ string) map[string]string {
- return nil
-}
-
-func (_ *Context) ProtoBuf(_ int, _ interface{}) {}
-
-func (_ *Context) PureJSON(_ int, _ interface{}) {}
-
-func (_ *Context) Query(_ string) string {
- return ""
-}
-
-func (_ *Context) QueryArray(_ string) []string {
- return nil
-}
-
-func (_ *Context) QueryMap(_ string) map[string]string {
- return nil
-}
-
-func (_ *Context) Redirect(_ int, _ string) {}
-
-func (_ *Context) RemoteIP() (net.IP, bool) {
- return nil, false
-}
-
-func (_ *Context) Render(_ int, _ interface{}) {}
-
-func (_ *Context) SSEvent(_ string, _ interface{}) {}
-
-func (_ *Context) SaveUploadedFile(_ *multipart.FileHeader, _ string) error {
- return nil
-}
-
-func (_ *Context) SecureJSON(_ int, _ interface{}) {}
-
-func (_ *Context) Set(_ string, _ interface{}) {}
-
-func (_ *Context) SetAccepted(_ ...string) {}
-
-func (_ *Context) SetCookie(_ string, _ string, _ int, _ string, _ string, _ bool, _ bool) {}
-
-func (_ *Context) SetSameSite(_ http.SameSite) {}
-
-func (_ *Context) ShouldBind(_ interface{}) error {
- return nil
-}
-
-func (_ *Context) ShouldBindBodyWith(_ interface{}, _ interface{}) error {
- return nil
-}
-
-func (_ *Context) ShouldBindHeader(_ interface{}) error {
- return nil
-}
-
-func (_ *Context) ShouldBindJSON(_ interface{}) error {
- return nil
-}
-
-func (_ *Context) ShouldBindQuery(_ interface{}) error {
- return nil
-}
-
-func (_ *Context) ShouldBindUri(_ interface{}) error {
- return nil
-}
-
-func (_ *Context) ShouldBindWith(_ interface{}, _ interface{}) error {
- return nil
-}
-
-func (_ *Context) ShouldBindXML(_ interface{}) error {
- return nil
-}
-
-func (_ *Context) ShouldBindYAML(_ interface{}) error {
- return nil
-}
-
-func (_ *Context) Status(_ int) {}
-
-func (_ *Context) Stream(_ func(io.Writer) bool) bool {
- return false
-}
-
-func (_ *Context) String(_ int, _ string, _ ...interface{}) {}
-
-func (_ *Context) Value(_ interface{}) interface{} {
- return nil
-}
-
-func (_ *Context) XML(_ int, _ interface{}) {}
-
-func (_ *Context) YAML(_ int, _ interface{}) {}
-
-type Engine struct {
- RouterGroup RouterGroup
- RedirectTrailingSlash bool
- RedirectFixedPath bool
- HandleMethodNotAllowed bool
- ForwardedByClientIP bool
- AppEngine bool
- UseRawPath bool
- UnescapePathValues bool
- RemoveExtraSlash bool
- RemoteIPHeaders []string
- TrustedPlatform string
- MaxMultipartMemory int64
- HTMLRender interface{}
- FuncMap template0.FuncMap
-}
-
-func (_ *Engine) Any(_ string, _ ...HandlerFunc) IRoutes {
- return nil
-}
-
-func (_ *Engine) BasePath() string {
- return ""
-}
-
-func (_ *Engine) DELETE(_ string, _ ...HandlerFunc) IRoutes {
- return nil
-}
-
-func (_ *Engine) Delims(_ string, _ string) *Engine {
- return nil
-}
-
-func (_ *Engine) GET(_ string, _ ...HandlerFunc) IRoutes {
- return nil
-}
-
-func (_ *Engine) Group(_ string, _ ...HandlerFunc) *RouterGroup {
- return nil
-}
-
-func (_ *Engine) HEAD(_ string, _ ...HandlerFunc) IRoutes {
- return nil
-}
-
-func (_ *Engine) Handle(_ string, _ string, _ ...HandlerFunc) IRoutes {
- return nil
-}
-
-func (_ *Engine) HandleContext(_ *Context) {}
-
-func (_ *Engine) LoadHTMLFiles(_ ...string) {}
-
-func (_ *Engine) LoadHTMLGlob(_ string) {}
-
-func (_ *Engine) NoMethod(_ ...HandlerFunc) {}
-
-func (_ *Engine) NoRoute(_ ...HandlerFunc) {}
-
-func (_ *Engine) OPTIONS(_ string, _ ...HandlerFunc) IRoutes {
- return nil
-}
-
-func (_ *Engine) PATCH(_ string, _ ...HandlerFunc) IRoutes {
- return nil
-}
-
-func (_ *Engine) POST(_ string, _ ...HandlerFunc) IRoutes {
- return nil
-}
-
-func (_ *Engine) PUT(_ string, _ ...HandlerFunc) IRoutes {
- return nil
-}
-
-func (_ *Engine) Routes() RoutesInfo {
- return nil
-}
-
-func (_ *Engine) Run(_ ...string) error {
- return nil
-}
-
-func (_ *Engine) RunFd(_ int) error {
- return nil
-}
-
-func (_ *Engine) RunListener(_ net.Listener) error {
- return nil
-}
-
-func (_ *Engine) RunTLS(_ string, _ string, _ string) error {
- return nil
-}
-
-func (_ *Engine) RunUnix(_ string) error {
- return nil
-}
-
-func (_ *Engine) SecureJsonPrefix(_ string) *Engine {
- return nil
-}
-
-func (_ *Engine) ServeHTTP(_ http.ResponseWriter, _ *http.Request) {}
-
-func (_ *Engine) SetFuncMap(_ template0.FuncMap) {}
-
-func (_ *Engine) SetHTMLTemplate(_ *template.Template) {}
-
-func (_ *Engine) SetTrustedProxies(_ []string) error {
- return nil
-}
-
-func (_ *Engine) Static(_ string, _ string) IRoutes {
- return nil
-}
-
-func (_ *Engine) StaticFS(_ string, _ http.FileSystem) IRoutes {
- return nil
-}
-
-func (_ *Engine) StaticFile(_ string, _ string) IRoutes {
- return nil
-}
-
-func (_ *Engine) Use(_ ...HandlerFunc) IRoutes {
- return nil
-}
-
-type Error struct {
- Err error
- Type ErrorType
- Meta interface{}
-}
-
-func (_ Error) Error() string {
- return ""
-}
-
-func (_ *Error) IsType(_ ErrorType) bool {
- return false
-}
-
-func (_ *Error) JSON() interface{} {
- return nil
-}
-
-func (_ *Error) MarshalJSON() ([]byte, error) {
- return nil, nil
-}
-
-func (_ *Error) SetMeta(_ interface{}) *Error {
- return nil
-}
-
-func (_ *Error) SetType(_ ErrorType) *Error {
- return nil
-}
-
-func (_ *Error) Unwrap() error {
- return nil
-}
-
-type ErrorType uint64
-
-type HandlerFunc func(*Context)
-
-type HandlersChain []HandlerFunc
-
-func (_ HandlersChain) Last() HandlerFunc {
- return nil
-}
-
-type IRoutes interface {
- Any(_ string, _ ...HandlerFunc) IRoutes
- DELETE(_ string, _ ...HandlerFunc) IRoutes
- GET(_ string, _ ...HandlerFunc) IRoutes
- HEAD(_ string, _ ...HandlerFunc) IRoutes
- Handle(_ string, _ string, _ ...HandlerFunc) IRoutes
- OPTIONS(_ string, _ ...HandlerFunc) IRoutes
- PATCH(_ string, _ ...HandlerFunc) IRoutes
- POST(_ string, _ ...HandlerFunc) IRoutes
- PUT(_ string, _ ...HandlerFunc) IRoutes
- Static(_ string, _ string) IRoutes
- StaticFS(_ string, _ http.FileSystem) IRoutes
- StaticFile(_ string, _ string) IRoutes
- Use(_ ...HandlerFunc) IRoutes
-}
-
-type Negotiate struct {
- Offered []string
- HTMLName string
- HTMLData interface{}
- JSONData interface{}
- XMLData interface{}
- YAMLData interface{}
- Data interface{}
-}
-
-func New() *Engine {
- return nil
-}
-
-type Param struct {
- Key string
- Value string
-}
-
-type Params []Param
-
-func (_ Params) ByName(_ string) string {
- return ""
-}
-
-func (_ Params) Get(_ string) (string, bool) {
- return "", false
-}
-
-type ResponseWriter interface {
- CloseNotify() <-chan bool
- Flush()
- Header() http.Header
- Hijack() (net.Conn, *bufio.ReadWriter, error)
- Pusher() http.Pusher
- Size() int
- Status() int
- Write(_ []byte) (int, error)
- WriteHeader(_ int)
- WriteHeaderNow()
- WriteString(_ string) (int, error)
- Written() bool
-}
-
-type RouteInfo struct {
- Method string
- Path string
- Handler string
- HandlerFunc HandlerFunc
-}
-
-type RouterGroup struct {
- Handlers HandlersChain
-}
-
-func (_ *RouterGroup) Any(_ string, _ ...HandlerFunc) IRoutes {
- return nil
-}
-
-func (_ *RouterGroup) BasePath() string {
- return ""
-}
-
-func (_ *RouterGroup) DELETE(_ string, _ ...HandlerFunc) IRoutes {
- return nil
-}
-
-func (_ *RouterGroup) GET(_ string, _ ...HandlerFunc) IRoutes {
- return nil
-}
-
-func (_ *RouterGroup) Group(_ string, _ ...HandlerFunc) *RouterGroup {
- return nil
-}
-
-func (_ *RouterGroup) HEAD(_ string, _ ...HandlerFunc) IRoutes {
- return nil
-}
-
-func (_ *RouterGroup) Handle(_ string, _ string, _ ...HandlerFunc) IRoutes {
- return nil
-}
-
-func (_ *RouterGroup) OPTIONS(_ string, _ ...HandlerFunc) IRoutes {
- return nil
-}
-
-func (_ *RouterGroup) PATCH(_ string, _ ...HandlerFunc) IRoutes {
- return nil
-}
-
-func (_ *RouterGroup) POST(_ string, _ ...HandlerFunc) IRoutes {
- return nil
-}
-
-func (_ *RouterGroup) PUT(_ string, _ ...HandlerFunc) IRoutes {
- return nil
-}
-
-func (_ *RouterGroup) Static(_ string, _ string) IRoutes {
- return nil
-}
-
-func (_ *RouterGroup) StaticFS(_ string, _ http.FileSystem) IRoutes {
- return nil
-}
-
-func (_ *RouterGroup) StaticFile(_ string, _ string) IRoutes {
- return nil
-}
-
-func (_ *RouterGroup) Use(_ ...HandlerFunc) IRoutes {
- return nil
-}
-
-type RoutesInfo []RouteInfo
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/test_helpers.go b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/test_helpers.go
new file mode 100644
index 000000000000..3a7a5ddf69c3
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/test_helpers.go
@@ -0,0 +1,16 @@
+// Copyright 2017 Manu Martinez-Almeida. All rights reserved.
+// Use of this source code is governed by a MIT style
+// license that can be found in the LICENSE file.
+
+package gin
+
+import "net/http"
+
+// CreateTestContext returns a fresh engine and context for testing purposes
+func CreateTestContext(w http.ResponseWriter) (c *Context, r *Engine) {
+ r = New()
+ c = r.allocateContext()
+ c.reset()
+ c.writermem.reset(w)
+ return
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/tree.go b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/tree.go
new file mode 100644
index 000000000000..158a3390892d
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/tree.go
@@ -0,0 +1,867 @@
+// Copyright 2013 Julien Schmidt. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be found
+// at https://github.com/julienschmidt/httprouter/blob/master/LICENSE
+
+package gin
+
+import (
+ "bytes"
+ "net/url"
+ "strings"
+ "unicode"
+ "unicode/utf8"
+
+ "github.com/gin-gonic/gin/internal/bytesconv"
+)
+
+var (
+ strColon = []byte(":")
+ strStar = []byte("*")
+ strSlash = []byte("/")
+)
+
+// Param is a single URL parameter, consisting of a key and a value.
+type Param struct {
+ Key string
+ Value string
+}
+
+// Params is a Param-slice, as returned by the router.
+// The slice is ordered, the first URL parameter is also the first slice value.
+// It is therefore safe to read values by the index.
+type Params []Param
+
+// Get returns the value of the first Param which key matches the given name.
+// If no matching Param is found, an empty string is returned.
+func (ps Params) Get(name string) (string, bool) {
+ for _, entry := range ps {
+ if entry.Key == name {
+ return entry.Value, true
+ }
+ }
+ return "", false
+}
+
+// ByName returns the value of the first Param which key matches the given name.
+// If no matching Param is found, an empty string is returned.
+func (ps Params) ByName(name string) (va string) {
+ va, _ = ps.Get(name)
+ return
+}
+
+type methodTree struct {
+ method string
+ root *node
+}
+
+type methodTrees []methodTree
+
+func (trees methodTrees) get(method string) *node {
+ for _, tree := range trees {
+ if tree.method == method {
+ return tree.root
+ }
+ }
+ return nil
+}
+
+func min(a, b int) int {
+ if a <= b {
+ return a
+ }
+ return b
+}
+
+func longestCommonPrefix(a, b string) int {
+ i := 0
+ max := min(len(a), len(b))
+ for i < max && a[i] == b[i] {
+ i++
+ }
+ return i
+}
+
+// addChild will add a child node, keeping wildcards at the end
+func (n *node) addChild(child *node) {
+ if n.wildChild && len(n.children) > 0 {
+ wildcardChild := n.children[len(n.children)-1]
+ n.children = append(n.children[:len(n.children)-1], child, wildcardChild)
+ } else {
+ n.children = append(n.children, child)
+ }
+}
+
+func countParams(path string) uint16 {
+ var n uint16
+ s := bytesconv.StringToBytes(path)
+ n += uint16(bytes.Count(s, strColon))
+ n += uint16(bytes.Count(s, strStar))
+ return n
+}
+
+func countSections(path string) uint16 {
+ s := bytesconv.StringToBytes(path)
+ return uint16(bytes.Count(s, strSlash))
+}
+
+type nodeType uint8
+
+const (
+ static nodeType = iota // default
+ root
+ param
+ catchAll
+)
+
+type node struct {
+ path string
+ indices string
+ wildChild bool
+ nType nodeType
+ priority uint32
+ children []*node // child nodes, at most 1 :param style node at the end of the array
+ handlers HandlersChain
+ fullPath string
+}
+
+// Increments priority of the given child and reorders if necessary
+func (n *node) incrementChildPrio(pos int) int {
+ cs := n.children
+ cs[pos].priority++
+ prio := cs[pos].priority
+
+ // Adjust position (move to front)
+ newPos := pos
+ for ; newPos > 0 && cs[newPos-1].priority < prio; newPos-- {
+ // Swap node positions
+ cs[newPos-1], cs[newPos] = cs[newPos], cs[newPos-1]
+ }
+
+ // Build new index char string
+ if newPos != pos {
+ n.indices = n.indices[:newPos] + // Unchanged prefix, might be empty
+ n.indices[pos:pos+1] + // The index char we move
+ n.indices[newPos:pos] + n.indices[pos+1:] // Rest without char at 'pos'
+ }
+
+ return newPos
+}
+
+// addRoute adds a node with the given handle to the path.
+// Not concurrency-safe!
+func (n *node) addRoute(path string, handlers HandlersChain) {
+ fullPath := path
+ n.priority++
+
+ // Empty tree
+ if len(n.path) == 0 && len(n.children) == 0 {
+ n.insertChild(path, fullPath, handlers)
+ n.nType = root
+ return
+ }
+
+ parentFullPathIndex := 0
+
+walk:
+ for {
+ // Find the longest common prefix.
+ // This also implies that the common prefix contains no ':' or '*'
+ // since the existing key can't contain those chars.
+ i := longestCommonPrefix(path, n.path)
+
+ // Split edge
+ if i < len(n.path) {
+ child := node{
+ path: n.path[i:],
+ wildChild: n.wildChild,
+ indices: n.indices,
+ children: n.children,
+ handlers: n.handlers,
+ priority: n.priority - 1,
+ fullPath: n.fullPath,
+ }
+
+ n.children = []*node{&child}
+ // []byte for proper unicode char conversion, see #65
+ n.indices = bytesconv.BytesToString([]byte{n.path[i]})
+ n.path = path[:i]
+ n.handlers = nil
+ n.wildChild = false
+ n.fullPath = fullPath[:parentFullPathIndex+i]
+ }
+
+ // Make new node a child of this node
+ if i < len(path) {
+ path = path[i:]
+ c := path[0]
+
+ // '/' after param
+ if n.nType == param && c == '/' && len(n.children) == 1 {
+ parentFullPathIndex += len(n.path)
+ n = n.children[0]
+ n.priority++
+ continue walk
+ }
+
+ // Check if a child with the next path byte exists
+ for i, max := 0, len(n.indices); i < max; i++ {
+ if c == n.indices[i] {
+ parentFullPathIndex += len(n.path)
+ i = n.incrementChildPrio(i)
+ n = n.children[i]
+ continue walk
+ }
+ }
+
+ // Otherwise insert it
+ if c != ':' && c != '*' && n.nType != catchAll {
+ // []byte for proper unicode char conversion, see #65
+ n.indices += bytesconv.BytesToString([]byte{c})
+ child := &node{
+ fullPath: fullPath,
+ }
+ n.addChild(child)
+ n.incrementChildPrio(len(n.indices) - 1)
+ n = child
+ } else if n.wildChild {
+ // inserting a wildcard node, need to check if it conflicts with the existing wildcard
+ n = n.children[len(n.children)-1]
+ n.priority++
+
+ // Check if the wildcard matches
+ if len(path) >= len(n.path) && n.path == path[:len(n.path)] &&
+ // Adding a child to a catchAll is not possible
+ n.nType != catchAll &&
+ // Check for longer wildcard, e.g. :name and :names
+ (len(n.path) >= len(path) || path[len(n.path)] == '/') {
+ continue walk
+ }
+
+ // Wildcard conflict
+ pathSeg := path
+ if n.nType != catchAll {
+ pathSeg = strings.SplitN(pathSeg, "/", 2)[0]
+ }
+ prefix := fullPath[:strings.Index(fullPath, pathSeg)] + n.path
+ panic("'" + pathSeg +
+ "' in new path '" + fullPath +
+ "' conflicts with existing wildcard '" + n.path +
+ "' in existing prefix '" + prefix +
+ "'")
+ }
+
+ n.insertChild(path, fullPath, handlers)
+ return
+ }
+
+ // Otherwise add handle to current node
+ if n.handlers != nil {
+ panic("handlers are already registered for path '" + fullPath + "'")
+ }
+ n.handlers = handlers
+ n.fullPath = fullPath
+ return
+ }
+}
+
+// Search for a wildcard segment and check the name for invalid characters.
+// Returns -1 as index, if no wildcard was found.
+func findWildcard(path string) (wildcard string, i int, valid bool) {
+ // Find start
+ for start, c := range []byte(path) {
+ // A wildcard starts with ':' (param) or '*' (catch-all)
+ if c != ':' && c != '*' {
+ continue
+ }
+
+ // Find end and check for invalid characters
+ valid = true
+ for end, c := range []byte(path[start+1:]) {
+ switch c {
+ case '/':
+ return path[start : start+1+end], start, valid
+ case ':', '*':
+ valid = false
+ }
+ }
+ return path[start:], start, valid
+ }
+ return "", -1, false
+}
+
+func (n *node) insertChild(path string, fullPath string, handlers HandlersChain) {
+ for {
+ // Find prefix until first wildcard
+ wildcard, i, valid := findWildcard(path)
+ if i < 0 { // No wildcard found
+ break
+ }
+
+ // The wildcard name must not contain ':' and '*'
+ if !valid {
+ panic("only one wildcard per path segment is allowed, has: '" +
+ wildcard + "' in path '" + fullPath + "'")
+ }
+
+ // check if the wildcard has a name
+ if len(wildcard) < 2 {
+ panic("wildcards must be named with a non-empty name in path '" + fullPath + "'")
+ }
+
+ if wildcard[0] == ':' { // param
+ if i > 0 {
+ // Insert prefix before the current wildcard
+ n.path = path[:i]
+ path = path[i:]
+ }
+
+ child := &node{
+ nType: param,
+ path: wildcard,
+ fullPath: fullPath,
+ }
+ n.addChild(child)
+ n.wildChild = true
+ n = child
+ n.priority++
+
+ // if the path doesn't end with the wildcard, then there
+ // will be another non-wildcard subpath starting with '/'
+ if len(wildcard) < len(path) {
+ path = path[len(wildcard):]
+
+ child := &node{
+ priority: 1,
+ fullPath: fullPath,
+ }
+ n.addChild(child)
+ n = child
+ continue
+ }
+
+ // Otherwise we're done. Insert the handle in the new leaf
+ n.handlers = handlers
+ return
+ }
+
+ // catchAll
+ if i+len(wildcard) != len(path) {
+ panic("catch-all routes are only allowed at the end of the path in path '" + fullPath + "'")
+ }
+
+ if len(n.path) > 0 && n.path[len(n.path)-1] == '/' {
+ panic("catch-all conflicts with existing handle for the path segment root in path '" + fullPath + "'")
+ }
+
+ // currently fixed width 1 for '/'
+ i--
+ if path[i] != '/' {
+ panic("no / before catch-all in path '" + fullPath + "'")
+ }
+
+ n.path = path[:i]
+
+ // First node: catchAll node with empty path
+ child := &node{
+ wildChild: true,
+ nType: catchAll,
+ fullPath: fullPath,
+ }
+
+ n.addChild(child)
+ n.indices = string('/')
+ n = child
+ n.priority++
+
+ // second node: node holding the variable
+ child = &node{
+ path: path[i:],
+ nType: catchAll,
+ handlers: handlers,
+ priority: 1,
+ fullPath: fullPath,
+ }
+ n.children = []*node{child}
+
+ return
+ }
+
+ // If no wildcard was found, simply insert the path and handle
+ n.path = path
+ n.handlers = handlers
+ n.fullPath = fullPath
+}
+
+// nodeValue holds return values of (*Node).getValue method
+type nodeValue struct {
+ handlers HandlersChain
+ params *Params
+ tsr bool
+ fullPath string
+}
+
+type skippedNode struct {
+ path string
+ node *node
+ paramsCount int16
+}
+
+// Returns the handle registered with the given path (key). The values of
+// wildcards are saved to a map.
+// If no handle can be found, a TSR (trailing slash redirect) recommendation is
+// made if a handle exists with an extra (without the) trailing slash for the
+// given path.
+func (n *node) getValue(path string, params *Params, skippedNodes *[]skippedNode, unescape bool) (value nodeValue) {
+ var globalParamsCount int16
+
+walk: // Outer loop for walking the tree
+ for {
+ prefix := n.path
+ if len(path) > len(prefix) {
+ if path[:len(prefix)] == prefix {
+ path = path[len(prefix):]
+
+ // Try all the non-wildcard children first by matching the indices
+ idxc := path[0]
+ for i, c := range []byte(n.indices) {
+ if c == idxc {
+ // strings.HasPrefix(n.children[len(n.children)-1].path, ":") == n.wildChild
+ if n.wildChild {
+ index := len(*skippedNodes)
+ *skippedNodes = (*skippedNodes)[:index+1]
+ (*skippedNodes)[index] = skippedNode{
+ path: prefix + path,
+ node: &node{
+ path: n.path,
+ wildChild: n.wildChild,
+ nType: n.nType,
+ priority: n.priority,
+ children: n.children,
+ handlers: n.handlers,
+ fullPath: n.fullPath,
+ },
+ paramsCount: globalParamsCount,
+ }
+ }
+
+ n = n.children[i]
+ continue walk
+ }
+ }
+
+ if !n.wildChild {
+ // If the path at the end of the loop is not equal to '/' and the current node has no child nodes
+ // the current node needs to roll back to last vaild skippedNode
+ if path != "/" {
+ for l := len(*skippedNodes); l > 0; {
+ skippedNode := (*skippedNodes)[l-1]
+ *skippedNodes = (*skippedNodes)[:l-1]
+ if strings.HasSuffix(skippedNode.path, path) {
+ path = skippedNode.path
+ n = skippedNode.node
+ if value.params != nil {
+ *value.params = (*value.params)[:skippedNode.paramsCount]
+ }
+ globalParamsCount = skippedNode.paramsCount
+ continue walk
+ }
+ }
+ }
+
+ // Nothing found.
+ // We can recommend to redirect to the same URL without a
+ // trailing slash if a leaf exists for that path.
+ value.tsr = path == "/" && n.handlers != nil
+ return
+ }
+
+ // Handle wildcard child, which is always at the end of the array
+ n = n.children[len(n.children)-1]
+ globalParamsCount++
+
+ switch n.nType {
+ case param:
+ // fix truncate the parameter
+ // tree_test.go line: 204
+
+ // Find param end (either '/' or path end)
+ end := 0
+ for end < len(path) && path[end] != '/' {
+ end++
+ }
+
+ // Save param value
+ if params != nil && cap(*params) > 0 {
+ if value.params == nil {
+ value.params = params
+ }
+ // Expand slice within preallocated capacity
+ i := len(*value.params)
+ *value.params = (*value.params)[:i+1]
+ val := path[:end]
+ if unescape {
+ if v, err := url.QueryUnescape(val); err == nil {
+ val = v
+ }
+ }
+ (*value.params)[i] = Param{
+ Key: n.path[1:],
+ Value: val,
+ }
+ }
+
+ // we need to go deeper!
+ if end < len(path) {
+ if len(n.children) > 0 {
+ path = path[end:]
+ n = n.children[0]
+ continue walk
+ }
+
+ // ... but we can't
+ value.tsr = len(path) == end+1
+ return
+ }
+
+ if value.handlers = n.handlers; value.handlers != nil {
+ value.fullPath = n.fullPath
+ return
+ }
+ if len(n.children) == 1 {
+ // No handle found. Check if a handle for this path + a
+ // trailing slash exists for TSR recommendation
+ n = n.children[0]
+ value.tsr = n.path == "/" && n.handlers != nil
+ }
+ return
+
+ case catchAll:
+ // Save param value
+ if params != nil {
+ if value.params == nil {
+ value.params = params
+ }
+ // Expand slice within preallocated capacity
+ i := len(*value.params)
+ *value.params = (*value.params)[:i+1]
+ val := path
+ if unescape {
+ if v, err := url.QueryUnescape(path); err == nil {
+ val = v
+ }
+ }
+ (*value.params)[i] = Param{
+ Key: n.path[2:],
+ Value: val,
+ }
+ }
+
+ value.handlers = n.handlers
+ value.fullPath = n.fullPath
+ return
+
+ default:
+ panic("invalid node type")
+ }
+ }
+ }
+
+ if path == prefix {
+ // If the current path does not equal '/' and the node does not have a registered handle and the most recently matched node has a child node
+ // the current node needs to roll back to last vaild skippedNode
+ if n.handlers == nil && path != "/" {
+ for l := len(*skippedNodes); l > 0; {
+ skippedNode := (*skippedNodes)[l-1]
+ *skippedNodes = (*skippedNodes)[:l-1]
+ if strings.HasSuffix(skippedNode.path, path) {
+ path = skippedNode.path
+ n = skippedNode.node
+ if value.params != nil {
+ *value.params = (*value.params)[:skippedNode.paramsCount]
+ }
+ globalParamsCount = skippedNode.paramsCount
+ continue walk
+ }
+ }
+ // n = latestNode.children[len(latestNode.children)-1]
+ }
+ // We should have reached the node containing the handle.
+ // Check if this node has a handle registered.
+ if value.handlers = n.handlers; value.handlers != nil {
+ value.fullPath = n.fullPath
+ return
+ }
+
+ // If there is no handle for this route, but this route has a
+ // wildcard child, there must be a handle for this path with an
+ // additional trailing slash
+ if path == "/" && n.wildChild && n.nType != root {
+ value.tsr = true
+ return
+ }
+
+ // No handle found. Check if a handle for this path + a
+ // trailing slash exists for trailing slash recommendation
+ for i, c := range []byte(n.indices) {
+ if c == '/' {
+ n = n.children[i]
+ value.tsr = (len(n.path) == 1 && n.handlers != nil) ||
+ (n.nType == catchAll && n.children[0].handlers != nil)
+ return
+ }
+ }
+
+ return
+ }
+
+ // Nothing found. We can recommend to redirect to the same URL with an
+ // extra trailing slash if a leaf exists for that path
+ value.tsr = path == "/" ||
+ (len(prefix) == len(path)+1 && prefix[len(path)] == '/' &&
+ path == prefix[:len(prefix)-1] && n.handlers != nil)
+
+ // roll back to last valid skippedNode
+ if !value.tsr && path != "/" {
+ for l := len(*skippedNodes); l > 0; {
+ skippedNode := (*skippedNodes)[l-1]
+ *skippedNodes = (*skippedNodes)[:l-1]
+ if strings.HasSuffix(skippedNode.path, path) {
+ path = skippedNode.path
+ n = skippedNode.node
+ if value.params != nil {
+ *value.params = (*value.params)[:skippedNode.paramsCount]
+ }
+ globalParamsCount = skippedNode.paramsCount
+ continue walk
+ }
+ }
+ }
+
+ return
+ }
+}
+
+// Makes a case-insensitive lookup of the given path and tries to find a handler.
+// It can optionally also fix trailing slashes.
+// It returns the case-corrected path and a bool indicating whether the lookup
+// was successful.
+func (n *node) findCaseInsensitivePath(path string, fixTrailingSlash bool) ([]byte, bool) {
+ const stackBufSize = 128
+
+ // Use a static sized buffer on the stack in the common case.
+ // If the path is too long, allocate a buffer on the heap instead.
+ buf := make([]byte, 0, stackBufSize)
+ if length := len(path) + 1; length > stackBufSize {
+ buf = make([]byte, 0, length)
+ }
+
+ ciPath := n.findCaseInsensitivePathRec(
+ path,
+ buf, // Preallocate enough memory for new path
+ [4]byte{}, // Empty rune buffer
+ fixTrailingSlash,
+ )
+
+ return ciPath, ciPath != nil
+}
+
+// Shift bytes in array by n bytes left
+func shiftNRuneBytes(rb [4]byte, n int) [4]byte {
+ switch n {
+ case 0:
+ return rb
+ case 1:
+ return [4]byte{rb[1], rb[2], rb[3], 0}
+ case 2:
+ return [4]byte{rb[2], rb[3]}
+ case 3:
+ return [4]byte{rb[3]}
+ default:
+ return [4]byte{}
+ }
+}
+
+// Recursive case-insensitive lookup function used by n.findCaseInsensitivePath
+func (n *node) findCaseInsensitivePathRec(path string, ciPath []byte, rb [4]byte, fixTrailingSlash bool) []byte {
+ npLen := len(n.path)
+
+walk: // Outer loop for walking the tree
+ for len(path) >= npLen && (npLen == 0 || strings.EqualFold(path[1:npLen], n.path[1:])) {
+ // Add common prefix to result
+ oldPath := path
+ path = path[npLen:]
+ ciPath = append(ciPath, n.path...)
+
+ if len(path) == 0 {
+ // We should have reached the node containing the handle.
+ // Check if this node has a handle registered.
+ if n.handlers != nil {
+ return ciPath
+ }
+
+ // No handle found.
+ // Try to fix the path by adding a trailing slash
+ if fixTrailingSlash {
+ for i, c := range []byte(n.indices) {
+ if c == '/' {
+ n = n.children[i]
+ if (len(n.path) == 1 && n.handlers != nil) ||
+ (n.nType == catchAll && n.children[0].handlers != nil) {
+ return append(ciPath, '/')
+ }
+ return nil
+ }
+ }
+ }
+ return nil
+ }
+
+ // If this node does not have a wildcard (param or catchAll) child,
+ // we can just look up the next child node and continue to walk down
+ // the tree
+ if !n.wildChild {
+ // Skip rune bytes already processed
+ rb = shiftNRuneBytes(rb, npLen)
+
+ if rb[0] != 0 {
+ // Old rune not finished
+ idxc := rb[0]
+ for i, c := range []byte(n.indices) {
+ if c == idxc {
+ // continue with child node
+ n = n.children[i]
+ npLen = len(n.path)
+ continue walk
+ }
+ }
+ } else {
+ // Process a new rune
+ var rv rune
+
+ // Find rune start.
+ // Runes are up to 4 byte long,
+ // -4 would definitely be another rune.
+ var off int
+ for max := min(npLen, 3); off < max; off++ {
+ if i := npLen - off; utf8.RuneStart(oldPath[i]) {
+ // read rune from cached path
+ rv, _ = utf8.DecodeRuneInString(oldPath[i:])
+ break
+ }
+ }
+
+ // Calculate lowercase bytes of current rune
+ lo := unicode.ToLower(rv)
+ utf8.EncodeRune(rb[:], lo)
+
+ // Skip already processed bytes
+ rb = shiftNRuneBytes(rb, off)
+
+ idxc := rb[0]
+ for i, c := range []byte(n.indices) {
+ // Lowercase matches
+ if c == idxc {
+ // must use a recursive approach since both the
+ // uppercase byte and the lowercase byte might exist
+ // as an index
+ if out := n.children[i].findCaseInsensitivePathRec(
+ path, ciPath, rb, fixTrailingSlash,
+ ); out != nil {
+ return out
+ }
+ break
+ }
+ }
+
+ // If we found no match, the same for the uppercase rune,
+ // if it differs
+ if up := unicode.ToUpper(rv); up != lo {
+ utf8.EncodeRune(rb[:], up)
+ rb = shiftNRuneBytes(rb, off)
+
+ idxc := rb[0]
+ for i, c := range []byte(n.indices) {
+ // Uppercase matches
+ if c == idxc {
+ // Continue with child node
+ n = n.children[i]
+ npLen = len(n.path)
+ continue walk
+ }
+ }
+ }
+ }
+
+ // Nothing found. We can recommend to redirect to the same URL
+ // without a trailing slash if a leaf exists for that path
+ if fixTrailingSlash && path == "/" && n.handlers != nil {
+ return ciPath
+ }
+ return nil
+ }
+
+ n = n.children[0]
+ switch n.nType {
+ case param:
+ // Find param end (either '/' or path end)
+ end := 0
+ for end < len(path) && path[end] != '/' {
+ end++
+ }
+
+ // Add param value to case insensitive path
+ ciPath = append(ciPath, path[:end]...)
+
+ // We need to go deeper!
+ if end < len(path) {
+ if len(n.children) > 0 {
+ // Continue with child node
+ n = n.children[0]
+ npLen = len(n.path)
+ path = path[end:]
+ continue
+ }
+
+ // ... but we can't
+ if fixTrailingSlash && len(path) == end+1 {
+ return ciPath
+ }
+ return nil
+ }
+
+ if n.handlers != nil {
+ return ciPath
+ }
+
+ if fixTrailingSlash && len(n.children) == 1 {
+ // No handle found. Check if a handle for this path + a
+ // trailing slash exists
+ n = n.children[0]
+ if n.path == "/" && n.handlers != nil {
+ return append(ciPath, '/')
+ }
+ }
+
+ return nil
+
+ case catchAll:
+ return append(ciPath, path...)
+
+ default:
+ panic("invalid node type")
+ }
+ }
+
+ // Nothing found.
+ // Try to fix the path by adding / removing a trailing slash
+ if fixTrailingSlash {
+ if path == "/" {
+ return ciPath
+ }
+ if len(path)+1 == npLen && n.path[len(path)] == '/' &&
+ strings.EqualFold(path[1:], n.path[1:len(path)]) && n.handlers != nil {
+ return append(ciPath, n.path...)
+ }
+ }
+ return nil
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/utils.go b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/utils.go
new file mode 100644
index 000000000000..c32f0eeb0ee6
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/utils.go
@@ -0,0 +1,153 @@
+// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
+// Use of this source code is governed by a MIT style
+// license that can be found in the LICENSE file.
+
+package gin
+
+import (
+ "encoding/xml"
+ "net/http"
+ "os"
+ "path"
+ "reflect"
+ "runtime"
+ "strings"
+)
+
+// BindKey indicates a default bind key.
+const BindKey = "_gin-gonic/gin/bindkey"
+
+// Bind is a helper function for given interface object and returns a Gin middleware.
+func Bind(val interface{}) HandlerFunc {
+ value := reflect.ValueOf(val)
+ if value.Kind() == reflect.Ptr {
+ panic(`Bind struct can not be a pointer. Example:
+ Use: gin.Bind(Struct{}) instead of gin.Bind(&Struct{})
+`)
+ }
+ typ := value.Type()
+
+ return func(c *Context) {
+ obj := reflect.New(typ).Interface()
+ if c.Bind(obj) == nil {
+ c.Set(BindKey, obj)
+ }
+ }
+}
+
+// WrapF is a helper function for wrapping http.HandlerFunc and returns a Gin middleware.
+func WrapF(f http.HandlerFunc) HandlerFunc {
+ return func(c *Context) {
+ f(c.Writer, c.Request)
+ }
+}
+
+// WrapH is a helper function for wrapping http.Handler and returns a Gin middleware.
+func WrapH(h http.Handler) HandlerFunc {
+ return func(c *Context) {
+ h.ServeHTTP(c.Writer, c.Request)
+ }
+}
+
+// H is a shortcut for map[string]interface{}
+type H map[string]interface{}
+
+// MarshalXML allows type H to be used with xml.Marshal.
+func (h H) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+ start.Name = xml.Name{
+ Space: "",
+ Local: "map",
+ }
+ if err := e.EncodeToken(start); err != nil {
+ return err
+ }
+ for key, value := range h {
+ elem := xml.StartElement{
+ Name: xml.Name{Space: "", Local: key},
+ Attr: []xml.Attr{},
+ }
+ if err := e.EncodeElement(value, elem); err != nil {
+ return err
+ }
+ }
+
+ return e.EncodeToken(xml.EndElement{Name: start.Name})
+}
+
+func assert1(guard bool, text string) {
+ if !guard {
+ panic(text)
+ }
+}
+
+func filterFlags(content string) string {
+ for i, char := range content {
+ if char == ' ' || char == ';' {
+ return content[:i]
+ }
+ }
+ return content
+}
+
+func chooseData(custom, wildcard interface{}) interface{} {
+ if custom != nil {
+ return custom
+ }
+ if wildcard != nil {
+ return wildcard
+ }
+ panic("negotiation config is invalid")
+}
+
+func parseAccept(acceptHeader string) []string {
+ parts := strings.Split(acceptHeader, ",")
+ out := make([]string, 0, len(parts))
+ for _, part := range parts {
+ if i := strings.IndexByte(part, ';'); i > 0 {
+ part = part[:i]
+ }
+ if part = strings.TrimSpace(part); part != "" {
+ out = append(out, part)
+ }
+ }
+ return out
+}
+
+func lastChar(str string) uint8 {
+ if str == "" {
+ panic("The length of the string can't be 0")
+ }
+ return str[len(str)-1]
+}
+
+func nameOfFunction(f interface{}) string {
+ return runtime.FuncForPC(reflect.ValueOf(f).Pointer()).Name()
+}
+
+func joinPaths(absolutePath, relativePath string) string {
+ if relativePath == "" {
+ return absolutePath
+ }
+
+ finalPath := path.Join(absolutePath, relativePath)
+ if lastChar(relativePath) == '/' && lastChar(finalPath) != '/' {
+ return finalPath + "/"
+ }
+ return finalPath
+}
+
+func resolveAddress(addr []string) string {
+ switch len(addr) {
+ case 0:
+ if port := os.Getenv("PORT"); port != "" {
+ debugPrint("Environment variable PORT=\"%s\"", port)
+ return ":" + port
+ }
+ debugPrint("Environment variable PORT is undefined. Using port :8080 by default")
+ return ":8080"
+ case 1:
+ return addr[0]
+ default:
+ panic("too many parameters")
+ }
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/version.go b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/version.go
new file mode 100644
index 000000000000..4b69b9b91cfc
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/gin-gonic/gin/version.go
@@ -0,0 +1,8 @@
+// Copyright 2018 Gin Core Team. All rights reserved.
+// Use of this source code is governed by a MIT style
+// license that can be found in the LICENSE file.
+
+package gin
+
+// Version is the current gin framework's version.
+const Version = "v1.7.7"
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/go-kit/kit/LICENSE b/go/ql/test/experimental/CWE-321/vendor/github.com/go-kit/kit/LICENSE
new file mode 100644
index 000000000000..9d83342acdc7
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/go-kit/kit/LICENSE
@@ -0,0 +1,22 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 Peter Bourgon
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/go-kit/kit/auth/jwt/README.md b/go/ql/test/experimental/CWE-321/vendor/github.com/go-kit/kit/auth/jwt/README.md
new file mode 100644
index 000000000000..58b7d912b2ce
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/go-kit/kit/auth/jwt/README.md
@@ -0,0 +1,122 @@
+# package auth/jwt
+
+`package auth/jwt` provides a set of interfaces for service authorization
+through [JSON Web Tokens](https://jwt.io/).
+
+## Usage
+
+NewParser takes a key function and an expected signing method and returns an
+`endpoint.Middleware`. The middleware will parse a token passed into the
+context via the `jwt.JWTContextKey`. If the token is valid, any claims
+will be added to the context via the `jwt.JWTClaimsContextKey`.
+
+```go
+import (
+ stdjwt "github.com/golang-jwt/jwt/v4"
+
+ "github.com/go-kit/kit/auth/jwt"
+ "github.com/go-kit/kit/endpoint"
+)
+
+func main() {
+ var exampleEndpoint endpoint.Endpoint
+ {
+ kf := func(token *stdjwt.Token) (interface{}, error) { return []byte("SigningString"), nil }
+ exampleEndpoint = MakeExampleEndpoint(service)
+ exampleEndpoint = jwt.NewParser(kf, stdjwt.SigningMethodHS256, jwt.StandardClaimsFactory)(exampleEndpoint)
+ }
+}
+```
+
+NewSigner takes a JWT key ID header, the signing key, signing method, and a
+claims object. It returns an `endpoint.Middleware`. The middleware will build
+the token string and add it to the context via the `jwt.JWTContextKey`.
+
+```go
+import (
+ stdjwt "github.com/golang-jwt/jwt/v4"
+
+ "github.com/go-kit/kit/auth/jwt"
+ "github.com/go-kit/kit/endpoint"
+)
+
+func main() {
+ var exampleEndpoint endpoint.Endpoint
+ {
+ exampleEndpoint = grpctransport.NewClient(...).Endpoint()
+ exampleEndpoint = jwt.NewSigner(
+ "kid-header",
+ []byte("SigningString"),
+ stdjwt.SigningMethodHS256,
+ jwt.Claims{},
+ )(exampleEndpoint)
+ }
+}
+```
+
+In order for the parser and the signer to work, the authorization headers need
+to be passed between the request and the context. `HTTPToContext()`,
+`ContextToHTTP()`, `GRPCToContext()`, and `ContextToGRPC()` are given as
+helpers to do this. These functions implement the correlating transport's
+RequestFunc interface and can be passed as ClientBefore or ServerBefore
+options.
+
+Example of use in a client:
+
+```go
+import (
+ stdjwt "github.com/golang-jwt/jwt/v4"
+
+ grpctransport "github.com/go-kit/kit/transport/grpc"
+ "github.com/go-kit/kit/auth/jwt"
+ "github.com/go-kit/kit/endpoint"
+)
+
+func main() {
+
+ options := []httptransport.ClientOption{}
+ var exampleEndpoint endpoint.Endpoint
+ {
+ exampleEndpoint = grpctransport.NewClient(..., grpctransport.ClientBefore(jwt.ContextToGRPC())).Endpoint()
+ exampleEndpoint = jwt.NewSigner(
+ "kid-header",
+ []byte("SigningString"),
+ stdjwt.SigningMethodHS256,
+ jwt.Claims{},
+ )(exampleEndpoint)
+ }
+}
+```
+
+Example of use in a server:
+
+```go
+import (
+ "context"
+
+ "github.com/go-kit/kit/auth/jwt"
+ "github.com/go-kit/log"
+ grpctransport "github.com/go-kit/kit/transport/grpc"
+)
+
+func MakeGRPCServer(ctx context.Context, endpoints Endpoints, logger log.Logger) pb.ExampleServer {
+ options := []grpctransport.ServerOption{grpctransport.ServerErrorLogger(logger)}
+
+ return &grpcServer{
+ createUser: grpctransport.NewServer(
+ ctx,
+ endpoints.CreateUserEndpoint,
+ DecodeGRPCCreateUserRequest,
+ EncodeGRPCCreateUserResponse,
+ append(options, grpctransport.ServerBefore(jwt.GRPCToContext()))...,
+ ),
+ getUser: grpctransport.NewServer(
+ ctx,
+ endpoints.GetUserEndpoint,
+ DecodeGRPCGetUserRequest,
+ EncodeGRPCGetUserResponse,
+ options...,
+ ),
+ }
+}
+```
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/go-kit/kit/auth/jwt/middleware.go b/go/ql/test/experimental/CWE-321/vendor/github.com/go-kit/kit/auth/jwt/middleware.go
new file mode 100644
index 000000000000..b7c89ceb875e
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/go-kit/kit/auth/jwt/middleware.go
@@ -0,0 +1,146 @@
+package jwt
+
+import (
+ "context"
+ "errors"
+
+ "github.com/go-kit/kit/endpoint"
+ "github.com/golang-jwt/jwt/v4"
+)
+
+type contextKey string
+
+const (
+ // JWTContextKey holds the key used to store a JWT in the context.
+ JWTContextKey contextKey = "JWTToken"
+
+ // JWTTokenContextKey is an alias for JWTContextKey.
+ //
+ // Deprecated: prefer JWTContextKey.
+ JWTTokenContextKey = JWTContextKey
+
+ // JWTClaimsContextKey holds the key used to store the JWT Claims in the
+ // context.
+ JWTClaimsContextKey contextKey = "JWTClaims"
+)
+
+var (
+ // ErrTokenContextMissing denotes a token was not passed into the parsing
+ // middleware's context.
+ ErrTokenContextMissing = errors.New("token up for parsing was not passed through the context")
+
+ // ErrTokenInvalid denotes a token was not able to be validated.
+ ErrTokenInvalid = errors.New("JWT was invalid")
+
+ // ErrTokenExpired denotes a token's expire header (exp) has since passed.
+ ErrTokenExpired = errors.New("JWT is expired")
+
+ // ErrTokenMalformed denotes a token was not formatted as a JWT.
+ ErrTokenMalformed = errors.New("JWT is malformed")
+
+ // ErrTokenNotActive denotes a token's not before header (nbf) is in the
+ // future.
+ ErrTokenNotActive = errors.New("token is not valid yet")
+
+ // ErrUnexpectedSigningMethod denotes a token was signed with an unexpected
+ // signing method.
+ ErrUnexpectedSigningMethod = errors.New("unexpected signing method")
+)
+
+// NewSigner creates a new JWT generating middleware, specifying key ID,
+// signing string, signing method and the claims you would like it to contain.
+// Tokens are signed with a Key ID header (kid) which is useful for determining
+// the key to use for parsing. Particularly useful for clients.
+func NewSigner(kid string, key []byte, method jwt.SigningMethod, claims jwt.Claims) endpoint.Middleware {
+ return func(next endpoint.Endpoint) endpoint.Endpoint {
+ return func(ctx context.Context, request interface{}) (response interface{}, err error) {
+ token := jwt.NewWithClaims(method, claims)
+ token.Header["kid"] = kid
+
+ // Sign and get the complete encoded token as a string using the secret
+ tokenString, err := token.SignedString(key)
+ if err != nil {
+ return nil, err
+ }
+ ctx = context.WithValue(ctx, JWTContextKey, tokenString)
+
+ return next(ctx, request)
+ }
+ }
+}
+
+// ClaimsFactory is a factory for jwt.Claims.
+// Useful in NewParser middleware.
+type ClaimsFactory func() jwt.Claims
+
+// MapClaimsFactory is a ClaimsFactory that returns
+// an empty jwt.MapClaims.
+func MapClaimsFactory() jwt.Claims {
+ return jwt.MapClaims{}
+}
+
+// StandardClaimsFactory is a ClaimsFactory that returns
+// an empty jwt.StandardClaims.
+func StandardClaimsFactory() jwt.Claims {
+ return &jwt.StandardClaims{}
+}
+
+// NewParser creates a new JWT parsing middleware, specifying a
+// jwt.Keyfunc interface, the signing method and the claims type to be used. NewParser
+// adds the resulting claims to endpoint context or returns error on invalid token.
+// Particularly useful for servers.
+func NewParser(keyFunc jwt.Keyfunc, method jwt.SigningMethod, newClaims ClaimsFactory) endpoint.Middleware {
+ return func(next endpoint.Endpoint) endpoint.Endpoint {
+ return func(ctx context.Context, request interface{}) (response interface{}, err error) {
+ // tokenString is stored in the context from the transport handlers.
+ tokenString, ok := ctx.Value(JWTContextKey).(string)
+ if !ok {
+ return nil, ErrTokenContextMissing
+ }
+
+ // Parse takes the token string and a function for looking up the
+ // key. The latter is especially useful if you use multiple keys
+ // for your application. The standard is to use 'kid' in the head
+ // of the token to identify which key to use, but the parsed token
+ // (head and claims) is provided to the callback, providing
+ // flexibility.
+ token, err := jwt.ParseWithClaims(tokenString, newClaims(), func(token *jwt.Token) (interface{}, error) {
+ // Don't forget to validate the alg is what you expect:
+ if token.Method != method {
+ return nil, ErrUnexpectedSigningMethod
+ }
+
+ return keyFunc(token)
+ })
+ if err != nil {
+ if e, ok := err.(*jwt.ValidationError); ok {
+ switch {
+ case e.Errors&jwt.ValidationErrorMalformed != 0:
+ // Token is malformed
+ return nil, ErrTokenMalformed
+ case e.Errors&jwt.ValidationErrorExpired != 0:
+ // Token is expired
+ return nil, ErrTokenExpired
+ case e.Errors&jwt.ValidationErrorNotValidYet != 0:
+ // Token is not active yet
+ return nil, ErrTokenNotActive
+ case e.Inner != nil:
+ // report e.Inner
+ return nil, e.Inner
+ }
+ // We have a ValidationError but have no specific Go kit error for it.
+ // Fall through to return original error.
+ }
+ return nil, err
+ }
+
+ if !token.Valid {
+ return nil, ErrTokenInvalid
+ }
+
+ ctx = context.WithValue(ctx, JWTClaimsContextKey, token.Claims)
+
+ return next(ctx, request)
+ }
+ }
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/go-kit/kit/auth/jwt/stub.go b/go/ql/test/experimental/CWE-321/vendor/github.com/go-kit/kit/auth/jwt/stub.go
deleted file mode 100644
index b85d079daa57..000000000000
--- a/go/ql/test/experimental/CWE-321/vendor/github.com/go-kit/kit/auth/jwt/stub.go
+++ /dev/null
@@ -1,12 +0,0 @@
-// Code generated by depstubber. DO NOT EDIT.
-// This is a simple stub for github.com/go-kit/kit/auth/jwt, strictly for use in testing.
-
-// See the LICENSE file for information about the licensing of the original library.
-// Source: github.com/go-kit/kit/auth/jwt (exports: ; functions: NewSigner)
-
-// Package jwt is a stub of github.com/go-kit/kit/auth/jwt, generated by depstubber.
-package jwt
-
-func NewSigner(_ string, _ []byte, _ interface{}, _ interface{}) interface{} {
- return nil
-}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/go-kit/kit/auth/jwt/transport.go b/go/ql/test/experimental/CWE-321/vendor/github.com/go-kit/kit/auth/jwt/transport.go
new file mode 100644
index 000000000000..e7d19c180447
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/go-kit/kit/auth/jwt/transport.go
@@ -0,0 +1,89 @@
+package jwt
+
+import (
+ "context"
+ "fmt"
+ stdhttp "net/http"
+ "strings"
+
+ "google.golang.org/grpc/metadata"
+
+ "github.com/go-kit/kit/transport/grpc"
+ "github.com/go-kit/kit/transport/http"
+)
+
+const (
+ bearer string = "bearer"
+ bearerFormat string = "Bearer %s"
+)
+
+// HTTPToContext moves a JWT from request header to context. Particularly
+// useful for servers.
+func HTTPToContext() http.RequestFunc {
+ return func(ctx context.Context, r *stdhttp.Request) context.Context {
+ token, ok := extractTokenFromAuthHeader(r.Header.Get("Authorization"))
+ if !ok {
+ return ctx
+ }
+
+ return context.WithValue(ctx, JWTContextKey, token)
+ }
+}
+
+// ContextToHTTP moves a JWT from context to request header. Particularly
+// useful for clients.
+func ContextToHTTP() http.RequestFunc {
+ return func(ctx context.Context, r *stdhttp.Request) context.Context {
+ token, ok := ctx.Value(JWTContextKey).(string)
+ if ok {
+ r.Header.Add("Authorization", generateAuthHeaderFromToken(token))
+ }
+ return ctx
+ }
+}
+
+// GRPCToContext moves a JWT from grpc metadata to context. Particularly
+// userful for servers.
+func GRPCToContext() grpc.ServerRequestFunc {
+ return func(ctx context.Context, md metadata.MD) context.Context {
+ // capital "Key" is illegal in HTTP/2.
+ authHeader, ok := md["authorization"]
+ if !ok {
+ return ctx
+ }
+
+ token, ok := extractTokenFromAuthHeader(authHeader[0])
+ if ok {
+ ctx = context.WithValue(ctx, JWTContextKey, token)
+ }
+
+ return ctx
+ }
+}
+
+// ContextToGRPC moves a JWT from context to grpc metadata. Particularly
+// useful for clients.
+func ContextToGRPC() grpc.ClientRequestFunc {
+ return func(ctx context.Context, md *metadata.MD) context.Context {
+ token, ok := ctx.Value(JWTContextKey).(string)
+ if ok {
+ // capital "Key" is illegal in HTTP/2.
+ (*md)["authorization"] = []string{generateAuthHeaderFromToken(token)}
+ }
+
+ return ctx
+ }
+}
+
+func extractTokenFromAuthHeader(val string) (token string, ok bool) {
+ authHeaderParts := strings.Split(val, " ")
+ if len(authHeaderParts) != 2 || !strings.EqualFold(authHeaderParts[0], bearer) {
+ return "", false
+ }
+
+ return authHeaderParts[1], true
+}
+
+func generateAuthHeaderFromToken(token string) string {
+ return fmt.Sprintf(bearerFormat, token)
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/go-kit/kit/endpoint/doc.go b/go/ql/test/experimental/CWE-321/vendor/github.com/go-kit/kit/endpoint/doc.go
new file mode 100644
index 000000000000..84e27b95d4bd
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/go-kit/kit/endpoint/doc.go
@@ -0,0 +1,5 @@
+// Package endpoint defines an abstraction for RPCs.
+//
+// Endpoints are a fundamental building block for many Go kit components.
+// Endpoints are implemented by servers, and called by clients.
+package endpoint
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/go-kit/kit/endpoint/endpoint.go b/go/ql/test/experimental/CWE-321/vendor/github.com/go-kit/kit/endpoint/endpoint.go
new file mode 100644
index 000000000000..6e9da3679b0e
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/go-kit/kit/endpoint/endpoint.go
@@ -0,0 +1,40 @@
+package endpoint
+
+import (
+ "context"
+)
+
+// Endpoint is the fundamental building block of servers and clients.
+// It represents a single RPC method.
+type Endpoint func(ctx context.Context, request interface{}) (response interface{}, err error)
+
+// Nop is an endpoint that does nothing and returns a nil error.
+// Useful for tests.
+func Nop(context.Context, interface{}) (interface{}, error) { return struct{}{}, nil }
+
+// Middleware is a chainable behavior modifier for endpoints.
+type Middleware func(Endpoint) Endpoint
+
+// Chain is a helper function for composing middlewares. Requests will
+// traverse them in the order they're declared. That is, the first middleware
+// is treated as the outermost middleware.
+func Chain(outer Middleware, others ...Middleware) Middleware {
+ return func(next Endpoint) Endpoint {
+ for i := len(others) - 1; i >= 0; i-- { // reverse
+ next = others[i](next)
+ }
+ return outer(next)
+ }
+}
+
+// Failer may be implemented by Go kit response types that contain business
+// logic error details. If Failed returns a non-nil error, the Go kit transport
+// layer may interpret this as a business logic error, and may encode it
+// differently than a regular, successful response.
+//
+// It's not necessary for your response types to implement Failer, but it may
+// help for more sophisticated use cases. The addsvc example shows how Failer
+// should be used by a complete application.
+type Failer interface {
+ Failed() error
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/go-kit/kit/transport/doc.go b/go/ql/test/experimental/CWE-321/vendor/github.com/go-kit/kit/transport/doc.go
new file mode 100644
index 000000000000..f8382ae712f9
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/go-kit/kit/transport/doc.go
@@ -0,0 +1,2 @@
+// Package transport contains helpers applicable to all supported transports.
+package transport
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/go-kit/kit/transport/error_handler.go b/go/ql/test/experimental/CWE-321/vendor/github.com/go-kit/kit/transport/error_handler.go
new file mode 100644
index 000000000000..2456df184976
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/go-kit/kit/transport/error_handler.go
@@ -0,0 +1,39 @@
+package transport
+
+import (
+ "context"
+
+ "github.com/go-kit/log"
+)
+
+// ErrorHandler receives a transport error to be processed for diagnostic purposes.
+// Usually this means logging the error.
+type ErrorHandler interface {
+ Handle(ctx context.Context, err error)
+}
+
+// LogErrorHandler is a transport error handler implementation which logs an error.
+type LogErrorHandler struct {
+ logger log.Logger
+}
+
+func NewLogErrorHandler(logger log.Logger) *LogErrorHandler {
+ return &LogErrorHandler{
+ logger: logger,
+ }
+}
+
+func (h *LogErrorHandler) Handle(ctx context.Context, err error) {
+ h.logger.Log("err", err)
+}
+
+// The ErrorHandlerFunc type is an adapter to allow the use of
+// ordinary function as ErrorHandler. If f is a function
+// with the appropriate signature, ErrorHandlerFunc(f) is a
+// ErrorHandler that calls f.
+type ErrorHandlerFunc func(ctx context.Context, err error)
+
+// Handle calls f(ctx, err).
+func (f ErrorHandlerFunc) Handle(ctx context.Context, err error) {
+ f(ctx, err)
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/go-kit/kit/transport/grpc/README.md b/go/ql/test/experimental/CWE-321/vendor/github.com/go-kit/kit/transport/grpc/README.md
new file mode 100644
index 000000000000..d269cddb2c75
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/go-kit/kit/transport/grpc/README.md
@@ -0,0 +1,54 @@
+# grpc
+
+[gRPC](http://www.grpc.io/) is an excellent, modern IDL and transport for
+microservices. If you're starting a greenfield project, go-kit strongly
+recommends gRPC as your default transport.
+
+One important note is that while gRPC supports streaming requests and replies,
+go-kit does not. You can still use streams in your service, but their
+implementation will not be able to take advantage of many go-kit features like middleware.
+
+Using gRPC and go-kit together is very simple.
+
+First, define your service using protobuf3. This is explained
+[in gRPC documentation](http://www.grpc.io/docs/#defining-a-service).
+See
+[addsvc.proto](https://github.com/go-kit/examples/blob/master/addsvc/pb/addsvc.proto)
+for an example. Make sure the proto definition matches your service's go-kit
+(interface) definition.
+
+Next, get the protoc compiler.
+
+You can download pre-compiled binaries from the
+[protobuf release page](https://github.com/google/protobuf/releases).
+You will unzip a folder called `protoc3` with a subdirectory `bin` containing
+an executable. Move that executable somewhere in your `$PATH` and you're good
+to go!
+
+It can also be built from source.
+
+```sh
+brew install autoconf automake libtool
+git clone https://github.com/google/protobuf
+cd protobuf
+./autogen.sh ; ./configure ; make ; make install
+```
+
+Then, compile your service definition, from .proto to .go.
+
+```sh
+protoc add.proto --go_out=plugins=grpc:.
+```
+
+Finally, write a tiny binding from your service definition to the gRPC
+definition. It's a simple conversion from one domain to another.
+See
+[grpc.go](https://github.com/go-kit/examples/blob/master/addsvc/pkg/addtransport/grpc.go)
+for an example.
+
+That's it!
+The gRPC binding can be bound to a listener and serve normal gRPC requests.
+And within your service, you can use standard go-kit components and idioms.
+See [addsvc](https://github.com/go-kit/examples/tree/master/addsvc/) for
+a complete working example with gRPC support. And remember: go-kit services
+can support multiple transports simultaneously.
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/go-kit/kit/transport/grpc/client.go b/go/ql/test/experimental/CWE-321/vendor/github.com/go-kit/kit/transport/grpc/client.go
new file mode 100644
index 000000000000..5d96c6b4d343
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/go-kit/kit/transport/grpc/client.go
@@ -0,0 +1,140 @@
+package grpc
+
+import (
+ "context"
+ "fmt"
+ "reflect"
+
+ "google.golang.org/grpc"
+ "google.golang.org/grpc/metadata"
+
+ "github.com/go-kit/kit/endpoint"
+)
+
+// Client wraps a gRPC connection and provides a method that implements
+// endpoint.Endpoint.
+type Client struct {
+ client *grpc.ClientConn
+ serviceName string
+ method string
+ enc EncodeRequestFunc
+ dec DecodeResponseFunc
+ grpcReply reflect.Type
+ before []ClientRequestFunc
+ after []ClientResponseFunc
+ finalizer []ClientFinalizerFunc
+}
+
+// NewClient constructs a usable Client for a single remote endpoint.
+// Pass an zero-value protobuf message of the RPC response type as
+// the grpcReply argument.
+func NewClient(
+ cc *grpc.ClientConn,
+ serviceName string,
+ method string,
+ enc EncodeRequestFunc,
+ dec DecodeResponseFunc,
+ grpcReply interface{},
+ options ...ClientOption,
+) *Client {
+ c := &Client{
+ client: cc,
+ method: fmt.Sprintf("/%s/%s", serviceName, method),
+ enc: enc,
+ dec: dec,
+ // We are using reflect.Indirect here to allow both reply structs and
+ // pointers to these reply structs. New consumers of the client should
+ // use structs directly, while existing consumers will not break if they
+ // remain to use pointers to structs.
+ grpcReply: reflect.TypeOf(
+ reflect.Indirect(
+ reflect.ValueOf(grpcReply),
+ ).Interface(),
+ ),
+ before: []ClientRequestFunc{},
+ after: []ClientResponseFunc{},
+ }
+ for _, option := range options {
+ option(c)
+ }
+ return c
+}
+
+// ClientOption sets an optional parameter for clients.
+type ClientOption func(*Client)
+
+// ClientBefore sets the RequestFuncs that are applied to the outgoing gRPC
+// request before it's invoked.
+func ClientBefore(before ...ClientRequestFunc) ClientOption {
+ return func(c *Client) { c.before = append(c.before, before...) }
+}
+
+// ClientAfter sets the ClientResponseFuncs that are applied to the incoming
+// gRPC response prior to it being decoded. This is useful for obtaining
+// response metadata and adding onto the context prior to decoding.
+func ClientAfter(after ...ClientResponseFunc) ClientOption {
+ return func(c *Client) { c.after = append(c.after, after...) }
+}
+
+// ClientFinalizer is executed at the end of every gRPC request.
+// By default, no finalizer is registered.
+func ClientFinalizer(f ...ClientFinalizerFunc) ClientOption {
+ return func(s *Client) { s.finalizer = append(s.finalizer, f...) }
+}
+
+// Endpoint returns a usable endpoint that will invoke the gRPC specified by the
+// client.
+func (c Client) Endpoint() endpoint.Endpoint {
+ return func(ctx context.Context, request interface{}) (response interface{}, err error) {
+ ctx, cancel := context.WithCancel(ctx)
+ defer cancel()
+
+ if c.finalizer != nil {
+ defer func() {
+ for _, f := range c.finalizer {
+ f(ctx, err)
+ }
+ }()
+ }
+
+ ctx = context.WithValue(ctx, ContextKeyRequestMethod, c.method)
+
+ req, err := c.enc(ctx, request)
+ if err != nil {
+ return nil, err
+ }
+
+ md := &metadata.MD{}
+ for _, f := range c.before {
+ ctx = f(ctx, md)
+ }
+ ctx = metadata.NewOutgoingContext(ctx, *md)
+
+ var header, trailer metadata.MD
+ grpcReply := reflect.New(c.grpcReply).Interface()
+ if err = c.client.Invoke(
+ ctx, c.method, req, grpcReply, grpc.Header(&header),
+ grpc.Trailer(&trailer),
+ ); err != nil {
+ return nil, err
+ }
+
+ for _, f := range c.after {
+ ctx = f(ctx, header, trailer)
+ }
+
+ response, err = c.dec(ctx, grpcReply)
+ if err != nil {
+ return nil, err
+ }
+ return response, nil
+ }
+}
+
+// ClientFinalizerFunc can be used to perform work at the end of a client gRPC
+// request, after the response is returned. The principal
+// intended use is for error logging. Additional response parameters are
+// provided in the context under keys with the ContextKeyResponse prefix.
+// Note: err may be nil. There maybe also no additional response parameters depending on
+// when an error occurs.
+type ClientFinalizerFunc func(ctx context.Context, err error)
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/go-kit/kit/transport/grpc/doc.go b/go/ql/test/experimental/CWE-321/vendor/github.com/go-kit/kit/transport/grpc/doc.go
new file mode 100644
index 000000000000..a953ba88ef1c
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/go-kit/kit/transport/grpc/doc.go
@@ -0,0 +1,2 @@
+// Package grpc provides a gRPC binding for endpoints.
+package grpc
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/go-kit/kit/transport/grpc/encode_decode.go b/go/ql/test/experimental/CWE-321/vendor/github.com/go-kit/kit/transport/grpc/encode_decode.go
new file mode 100644
index 000000000000..f2900ed6d309
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/go-kit/kit/transport/grpc/encode_decode.go
@@ -0,0 +1,29 @@
+package grpc
+
+import (
+ "context"
+)
+
+// DecodeRequestFunc extracts a user-domain request object from a gRPC request.
+// It's designed to be used in gRPC servers, for server-side endpoints. One
+// straightforward DecodeRequestFunc could be something that decodes from the
+// gRPC request message to the concrete request type.
+type DecodeRequestFunc func(context.Context, interface{}) (request interface{}, err error)
+
+// EncodeRequestFunc encodes the passed request object into the gRPC request
+// object. It's designed to be used in gRPC clients, for client-side endpoints.
+// One straightforward EncodeRequestFunc could something that encodes the object
+// directly to the gRPC request message.
+type EncodeRequestFunc func(context.Context, interface{}) (request interface{}, err error)
+
+// EncodeResponseFunc encodes the passed response object to the gRPC response
+// message. It's designed to be used in gRPC servers, for server-side endpoints.
+// One straightforward EncodeResponseFunc could be something that encodes the
+// object directly to the gRPC response message.
+type EncodeResponseFunc func(context.Context, interface{}) (response interface{}, err error)
+
+// DecodeResponseFunc extracts a user-domain response object from a gRPC
+// response object. It's designed to be used in gRPC clients, for client-side
+// endpoints. One straightforward DecodeResponseFunc could be something that
+// decodes from the gRPC response message to the concrete response type.
+type DecodeResponseFunc func(context.Context, interface{}) (response interface{}, err error)
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/go-kit/kit/transport/grpc/request_response_funcs.go b/go/ql/test/experimental/CWE-321/vendor/github.com/go-kit/kit/transport/grpc/request_response_funcs.go
new file mode 100644
index 000000000000..eb8e3b178b68
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/go-kit/kit/transport/grpc/request_response_funcs.go
@@ -0,0 +1,81 @@
+package grpc
+
+import (
+ "context"
+ "encoding/base64"
+ "strings"
+
+ "google.golang.org/grpc/metadata"
+)
+
+const (
+ binHdrSuffix = "-bin"
+)
+
+// ClientRequestFunc may take information from context and use it to construct
+// metadata headers to be transported to the server. ClientRequestFuncs are
+// executed after creating the request but prior to sending the gRPC request to
+// the server.
+type ClientRequestFunc func(context.Context, *metadata.MD) context.Context
+
+// ServerRequestFunc may take information from the received metadata header and
+// use it to place items in the request scoped context. ServerRequestFuncs are
+// executed prior to invoking the endpoint.
+type ServerRequestFunc func(context.Context, metadata.MD) context.Context
+
+// ServerResponseFunc may take information from a request context and use it to
+// manipulate the gRPC response metadata headers and trailers. ResponseFuncs are
+// only executed in servers, after invoking the endpoint but prior to writing a
+// response.
+type ServerResponseFunc func(ctx context.Context, header *metadata.MD, trailer *metadata.MD) context.Context
+
+// ClientResponseFunc may take information from a gRPC metadata header and/or
+// trailer and make the responses available for consumption. ClientResponseFuncs
+// are only executed in clients, after a request has been made, but prior to it
+// being decoded.
+type ClientResponseFunc func(ctx context.Context, header metadata.MD, trailer metadata.MD) context.Context
+
+// SetRequestHeader returns a ClientRequestFunc that sets the specified metadata
+// key-value pair.
+func SetRequestHeader(key, val string) ClientRequestFunc {
+ return func(ctx context.Context, md *metadata.MD) context.Context {
+ key, val := EncodeKeyValue(key, val)
+ (*md)[key] = append((*md)[key], val)
+ return ctx
+ }
+}
+
+// SetResponseHeader returns a ResponseFunc that sets the specified metadata
+// key-value pair.
+func SetResponseHeader(key, val string) ServerResponseFunc {
+ return func(ctx context.Context, md *metadata.MD, _ *metadata.MD) context.Context {
+ key, val := EncodeKeyValue(key, val)
+ (*md)[key] = append((*md)[key], val)
+ return ctx
+ }
+}
+
+// SetResponseTrailer returns a ResponseFunc that sets the specified metadata
+// key-value pair.
+func SetResponseTrailer(key, val string) ServerResponseFunc {
+ return func(ctx context.Context, _ *metadata.MD, md *metadata.MD) context.Context {
+ key, val := EncodeKeyValue(key, val)
+ (*md)[key] = append((*md)[key], val)
+ return ctx
+ }
+}
+
+// EncodeKeyValue sanitizes a key-value pair for use in gRPC metadata headers.
+func EncodeKeyValue(key, val string) (string, string) {
+ key = strings.ToLower(key)
+ if strings.HasSuffix(key, binHdrSuffix) {
+ val = base64.StdEncoding.EncodeToString([]byte(val))
+ }
+ return key, val
+}
+
+type contextKey int
+
+const (
+ ContextKeyRequestMethod contextKey = iota
+)
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/go-kit/kit/transport/grpc/server.go b/go/ql/test/experimental/CWE-321/vendor/github.com/go-kit/kit/transport/grpc/server.go
new file mode 100644
index 000000000000..e83aeacfc18e
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/go-kit/kit/transport/grpc/server.go
@@ -0,0 +1,168 @@
+package grpc
+
+import (
+ "context"
+
+ "google.golang.org/grpc"
+ "google.golang.org/grpc/metadata"
+
+ "github.com/go-kit/kit/endpoint"
+ "github.com/go-kit/kit/transport"
+ "github.com/go-kit/log"
+)
+
+// Handler which should be called from the gRPC binding of the service
+// implementation. The incoming request parameter, and returned response
+// parameter, are both gRPC types, not user-domain.
+type Handler interface {
+ ServeGRPC(ctx context.Context, request interface{}) (context.Context, interface{}, error)
+}
+
+// Server wraps an endpoint and implements grpc.Handler.
+type Server struct {
+ e endpoint.Endpoint
+ dec DecodeRequestFunc
+ enc EncodeResponseFunc
+ before []ServerRequestFunc
+ after []ServerResponseFunc
+ finalizer []ServerFinalizerFunc
+ errorHandler transport.ErrorHandler
+}
+
+// NewServer constructs a new server, which implements wraps the provided
+// endpoint and implements the Handler interface. Consumers should write
+// bindings that adapt the concrete gRPC methods from their compiled protobuf
+// definitions to individual handlers. Request and response objects are from the
+// caller business domain, not gRPC request and reply types.
+func NewServer(
+ e endpoint.Endpoint,
+ dec DecodeRequestFunc,
+ enc EncodeResponseFunc,
+ options ...ServerOption,
+) *Server {
+ s := &Server{
+ e: e,
+ dec: dec,
+ enc: enc,
+ errorHandler: transport.NewLogErrorHandler(log.NewNopLogger()),
+ }
+ for _, option := range options {
+ option(s)
+ }
+ return s
+}
+
+// ServerOption sets an optional parameter for servers.
+type ServerOption func(*Server)
+
+// ServerBefore functions are executed on the gRPC request object before the
+// request is decoded.
+func ServerBefore(before ...ServerRequestFunc) ServerOption {
+ return func(s *Server) { s.before = append(s.before, before...) }
+}
+
+// ServerAfter functions are executed on the gRPC response writer after the
+// endpoint is invoked, but before anything is written to the client.
+func ServerAfter(after ...ServerResponseFunc) ServerOption {
+ return func(s *Server) { s.after = append(s.after, after...) }
+}
+
+// ServerErrorLogger is used to log non-terminal errors. By default, no errors
+// are logged.
+// Deprecated: Use ServerErrorHandler instead.
+func ServerErrorLogger(logger log.Logger) ServerOption {
+ return func(s *Server) { s.errorHandler = transport.NewLogErrorHandler(logger) }
+}
+
+// ServerErrorHandler is used to handle non-terminal errors. By default, non-terminal errors
+// are ignored.
+func ServerErrorHandler(errorHandler transport.ErrorHandler) ServerOption {
+ return func(s *Server) { s.errorHandler = errorHandler }
+}
+
+// ServerFinalizer is executed at the end of every gRPC request.
+// By default, no finalizer is registered.
+func ServerFinalizer(f ...ServerFinalizerFunc) ServerOption {
+ return func(s *Server) { s.finalizer = append(s.finalizer, f...) }
+}
+
+// ServeGRPC implements the Handler interface.
+func (s Server) ServeGRPC(ctx context.Context, req interface{}) (retctx context.Context, resp interface{}, err error) {
+ // Retrieve gRPC metadata.
+ md, ok := metadata.FromIncomingContext(ctx)
+ if !ok {
+ md = metadata.MD{}
+ }
+
+ if len(s.finalizer) > 0 {
+ defer func() {
+ for _, f := range s.finalizer {
+ f(ctx, err)
+ }
+ }()
+ }
+
+ for _, f := range s.before {
+ ctx = f(ctx, md)
+ }
+
+ var (
+ request interface{}
+ response interface{}
+ grpcResp interface{}
+ )
+
+ request, err = s.dec(ctx, req)
+ if err != nil {
+ s.errorHandler.Handle(ctx, err)
+ return ctx, nil, err
+ }
+
+ response, err = s.e(ctx, request)
+ if err != nil {
+ s.errorHandler.Handle(ctx, err)
+ return ctx, nil, err
+ }
+
+ var mdHeader, mdTrailer metadata.MD
+ for _, f := range s.after {
+ ctx = f(ctx, &mdHeader, &mdTrailer)
+ }
+
+ grpcResp, err = s.enc(ctx, response)
+ if err != nil {
+ s.errorHandler.Handle(ctx, err)
+ return ctx, nil, err
+ }
+
+ if len(mdHeader) > 0 {
+ if err = grpc.SendHeader(ctx, mdHeader); err != nil {
+ s.errorHandler.Handle(ctx, err)
+ return ctx, nil, err
+ }
+ }
+
+ if len(mdTrailer) > 0 {
+ if err = grpc.SetTrailer(ctx, mdTrailer); err != nil {
+ s.errorHandler.Handle(ctx, err)
+ return ctx, nil, err
+ }
+ }
+
+ return ctx, grpcResp, nil
+}
+
+// ServerFinalizerFunc can be used to perform work at the end of an gRPC
+// request, after the response has been written to the client.
+type ServerFinalizerFunc func(ctx context.Context, err error)
+
+// Interceptor is a grpc UnaryInterceptor that injects the method name into
+// context so it can be consumed by Go kit gRPC middlewares. The Interceptor
+// typically is added at creation time of the grpc-go server.
+// Like this: `grpc.NewServer(grpc.UnaryInterceptor(kitgrpc.Interceptor))`
+func Interceptor(
+ ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler,
+) (resp interface{}, err error) {
+ ctx = context.WithValue(ctx, ContextKeyRequestMethod, info.FullMethod)
+ return handler(ctx, req)
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/go-kit/kit/transport/http/client.go b/go/ql/test/experimental/CWE-321/vendor/github.com/go-kit/kit/transport/http/client.go
new file mode 100644
index 000000000000..7d868eee496b
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/go-kit/kit/transport/http/client.go
@@ -0,0 +1,219 @@
+package http
+
+import (
+ "bytes"
+ "context"
+ "encoding/json"
+ "encoding/xml"
+ "io"
+ "io/ioutil"
+ "net/http"
+ "net/url"
+
+ "github.com/go-kit/kit/endpoint"
+)
+
+// HTTPClient is an interface that models *http.Client.
+type HTTPClient interface {
+ Do(req *http.Request) (*http.Response, error)
+}
+
+// Client wraps a URL and provides a method that implements endpoint.Endpoint.
+type Client struct {
+ client HTTPClient
+ req CreateRequestFunc
+ dec DecodeResponseFunc
+ before []RequestFunc
+ after []ClientResponseFunc
+ finalizer []ClientFinalizerFunc
+ bufferedStream bool
+}
+
+// NewClient constructs a usable Client for a single remote method.
+func NewClient(method string, tgt *url.URL, enc EncodeRequestFunc, dec DecodeResponseFunc, options ...ClientOption) *Client {
+ return NewExplicitClient(makeCreateRequestFunc(method, tgt, enc), dec, options...)
+}
+
+// NewExplicitClient is like NewClient but uses a CreateRequestFunc instead of a
+// method, target URL, and EncodeRequestFunc, which allows for more control over
+// the outgoing HTTP request.
+func NewExplicitClient(req CreateRequestFunc, dec DecodeResponseFunc, options ...ClientOption) *Client {
+ c := &Client{
+ client: http.DefaultClient,
+ req: req,
+ dec: dec,
+ }
+ for _, option := range options {
+ option(c)
+ }
+ return c
+}
+
+// ClientOption sets an optional parameter for clients.
+type ClientOption func(*Client)
+
+// SetClient sets the underlying HTTP client used for requests.
+// By default, http.DefaultClient is used.
+func SetClient(client HTTPClient) ClientOption {
+ return func(c *Client) { c.client = client }
+}
+
+// ClientBefore adds one or more RequestFuncs to be applied to the outgoing HTTP
+// request before it's invoked.
+func ClientBefore(before ...RequestFunc) ClientOption {
+ return func(c *Client) { c.before = append(c.before, before...) }
+}
+
+// ClientAfter adds one or more ClientResponseFuncs, which are applied to the
+// incoming HTTP response prior to it being decoded. This is useful for
+// obtaining anything off of the response and adding it into the context prior
+// to decoding.
+func ClientAfter(after ...ClientResponseFunc) ClientOption {
+ return func(c *Client) { c.after = append(c.after, after...) }
+}
+
+// ClientFinalizer adds one or more ClientFinalizerFuncs to be executed at the
+// end of every HTTP request. Finalizers are executed in the order in which they
+// were added. By default, no finalizer is registered.
+func ClientFinalizer(f ...ClientFinalizerFunc) ClientOption {
+ return func(s *Client) { s.finalizer = append(s.finalizer, f...) }
+}
+
+// BufferedStream sets whether the HTTP response body is left open, allowing it
+// to be read from later. Useful for transporting a file as a buffered stream.
+// That body has to be drained and closed to properly end the request.
+func BufferedStream(buffered bool) ClientOption {
+ return func(c *Client) { c.bufferedStream = buffered }
+}
+
+// Endpoint returns a usable Go kit endpoint that calls the remote HTTP endpoint.
+func (c Client) Endpoint() endpoint.Endpoint {
+ return func(ctx context.Context, request interface{}) (interface{}, error) {
+ ctx, cancel := context.WithCancel(ctx)
+
+ var (
+ resp *http.Response
+ err error
+ )
+ if c.finalizer != nil {
+ defer func() {
+ if resp != nil {
+ ctx = context.WithValue(ctx, ContextKeyResponseHeaders, resp.Header)
+ ctx = context.WithValue(ctx, ContextKeyResponseSize, resp.ContentLength)
+ }
+ for _, f := range c.finalizer {
+ f(ctx, err)
+ }
+ }()
+ }
+
+ req, err := c.req(ctx, request)
+ if err != nil {
+ cancel()
+ return nil, err
+ }
+
+ for _, f := range c.before {
+ ctx = f(ctx, req)
+ }
+
+ resp, err = c.client.Do(req.WithContext(ctx))
+ if err != nil {
+ cancel()
+ return nil, err
+ }
+
+ // If the caller asked for a buffered stream, we don't cancel the
+ // context when the endpoint returns. Instead, we should call the
+ // cancel func when closing the response body.
+ if c.bufferedStream {
+ resp.Body = bodyWithCancel{ReadCloser: resp.Body, cancel: cancel}
+ } else {
+ defer resp.Body.Close()
+ defer cancel()
+ }
+
+ for _, f := range c.after {
+ ctx = f(ctx, resp)
+ }
+
+ response, err := c.dec(ctx, resp)
+ if err != nil {
+ return nil, err
+ }
+
+ return response, nil
+ }
+}
+
+// bodyWithCancel is a wrapper for an io.ReadCloser with also a
+// cancel function which is called when the Close is used
+type bodyWithCancel struct {
+ io.ReadCloser
+
+ cancel context.CancelFunc
+}
+
+func (bwc bodyWithCancel) Close() error {
+ bwc.ReadCloser.Close()
+ bwc.cancel()
+ return nil
+}
+
+// ClientFinalizerFunc can be used to perform work at the end of a client HTTP
+// request, after the response is returned. The principal
+// intended use is for error logging. Additional response parameters are
+// provided in the context under keys with the ContextKeyResponse prefix.
+// Note: err may be nil. There maybe also no additional response parameters
+// depending on when an error occurs.
+type ClientFinalizerFunc func(ctx context.Context, err error)
+
+// EncodeJSONRequest is an EncodeRequestFunc that serializes the request as a
+// JSON object to the Request body. Many JSON-over-HTTP services can use it as
+// a sensible default. If the request implements Headerer, the provided headers
+// will be applied to the request.
+func EncodeJSONRequest(c context.Context, r *http.Request, request interface{}) error {
+ r.Header.Set("Content-Type", "application/json; charset=utf-8")
+ if headerer, ok := request.(Headerer); ok {
+ for k := range headerer.Headers() {
+ r.Header.Set(k, headerer.Headers().Get(k))
+ }
+ }
+ var b bytes.Buffer
+ r.Body = ioutil.NopCloser(&b)
+ return json.NewEncoder(&b).Encode(request)
+}
+
+// EncodeXMLRequest is an EncodeRequestFunc that serializes the request as a
+// XML object to the Request body. If the request implements Headerer,
+// the provided headers will be applied to the request.
+func EncodeXMLRequest(c context.Context, r *http.Request, request interface{}) error {
+ r.Header.Set("Content-Type", "text/xml; charset=utf-8")
+ if headerer, ok := request.(Headerer); ok {
+ for k := range headerer.Headers() {
+ r.Header.Set(k, headerer.Headers().Get(k))
+ }
+ }
+ var b bytes.Buffer
+ r.Body = ioutil.NopCloser(&b)
+ return xml.NewEncoder(&b).Encode(request)
+}
+
+//
+//
+//
+
+func makeCreateRequestFunc(method string, target *url.URL, enc EncodeRequestFunc) CreateRequestFunc {
+ return func(ctx context.Context, request interface{}) (*http.Request, error) {
+ req, err := http.NewRequest(method, target.String(), nil)
+ if err != nil {
+ return nil, err
+ }
+
+ if err = enc(ctx, req, request); err != nil {
+ return nil, err
+ }
+
+ return req, nil
+ }
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/go-kit/kit/transport/http/doc.go b/go/ql/test/experimental/CWE-321/vendor/github.com/go-kit/kit/transport/http/doc.go
new file mode 100644
index 000000000000..e64010358591
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/go-kit/kit/transport/http/doc.go
@@ -0,0 +1,2 @@
+// Package http provides a general purpose HTTP binding for endpoints.
+package http
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/go-kit/kit/transport/http/encode_decode.go b/go/ql/test/experimental/CWE-321/vendor/github.com/go-kit/kit/transport/http/encode_decode.go
new file mode 100644
index 000000000000..b3de462b3e9a
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/go-kit/kit/transport/http/encode_decode.go
@@ -0,0 +1,36 @@
+package http
+
+import (
+ "context"
+ "net/http"
+)
+
+// DecodeRequestFunc extracts a user-domain request object from an HTTP
+// request object. It's designed to be used in HTTP servers, for server-side
+// endpoints. One straightforward DecodeRequestFunc could be something that
+// JSON decodes from the request body to the concrete request type.
+type DecodeRequestFunc func(context.Context, *http.Request) (request interface{}, err error)
+
+// EncodeRequestFunc encodes the passed request object into the HTTP request
+// object. It's designed to be used in HTTP clients, for client-side
+// endpoints. One straightforward EncodeRequestFunc could be something that JSON
+// encodes the object directly to the request body.
+type EncodeRequestFunc func(context.Context, *http.Request, interface{}) error
+
+// CreateRequestFunc creates an outgoing HTTP request based on the passed
+// request object. It's designed to be used in HTTP clients, for client-side
+// endpoints. It's a more powerful version of EncodeRequestFunc, and can be used
+// if more fine-grained control of the HTTP request is required.
+type CreateRequestFunc func(context.Context, interface{}) (*http.Request, error)
+
+// EncodeResponseFunc encodes the passed response object to the HTTP response
+// writer. It's designed to be used in HTTP servers, for server-side
+// endpoints. One straightforward EncodeResponseFunc could be something that
+// JSON encodes the object directly to the response body.
+type EncodeResponseFunc func(context.Context, http.ResponseWriter, interface{}) error
+
+// DecodeResponseFunc extracts a user-domain response object from an HTTP
+// response object. It's designed to be used in HTTP clients, for client-side
+// endpoints. One straightforward DecodeResponseFunc could be something that
+// JSON decodes from the response body to the concrete response type.
+type DecodeResponseFunc func(context.Context, *http.Response) (response interface{}, err error)
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/go-kit/kit/transport/http/request_response_funcs.go b/go/ql/test/experimental/CWE-321/vendor/github.com/go-kit/kit/transport/http/request_response_funcs.go
new file mode 100644
index 000000000000..8f92b3bc7f36
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/go-kit/kit/transport/http/request_response_funcs.go
@@ -0,0 +1,133 @@
+package http
+
+import (
+ "context"
+ "net/http"
+)
+
+// RequestFunc may take information from an HTTP request and put it into a
+// request context. In Servers, RequestFuncs are executed prior to invoking the
+// endpoint. In Clients, RequestFuncs are executed after creating the request
+// but prior to invoking the HTTP client.
+type RequestFunc func(context.Context, *http.Request) context.Context
+
+// ServerResponseFunc may take information from a request context and use it to
+// manipulate a ResponseWriter. ServerResponseFuncs are only executed in
+// servers, after invoking the endpoint but prior to writing a response.
+type ServerResponseFunc func(context.Context, http.ResponseWriter) context.Context
+
+// ClientResponseFunc may take information from an HTTP request and make the
+// response available for consumption. ClientResponseFuncs are only executed in
+// clients, after a request has been made, but prior to it being decoded.
+type ClientResponseFunc func(context.Context, *http.Response) context.Context
+
+// SetContentType returns a ServerResponseFunc that sets the Content-Type header
+// to the provided value.
+func SetContentType(contentType string) ServerResponseFunc {
+ return SetResponseHeader("Content-Type", contentType)
+}
+
+// SetResponseHeader returns a ServerResponseFunc that sets the given header.
+func SetResponseHeader(key, val string) ServerResponseFunc {
+ return func(ctx context.Context, w http.ResponseWriter) context.Context {
+ w.Header().Set(key, val)
+ return ctx
+ }
+}
+
+// SetRequestHeader returns a RequestFunc that sets the given header.
+func SetRequestHeader(key, val string) RequestFunc {
+ return func(ctx context.Context, r *http.Request) context.Context {
+ r.Header.Set(key, val)
+ return ctx
+ }
+}
+
+// PopulateRequestContext is a RequestFunc that populates several values into
+// the context from the HTTP request. Those values may be extracted using the
+// corresponding ContextKey type in this package.
+func PopulateRequestContext(ctx context.Context, r *http.Request) context.Context {
+ for k, v := range map[contextKey]string{
+ ContextKeyRequestMethod: r.Method,
+ ContextKeyRequestURI: r.RequestURI,
+ ContextKeyRequestPath: r.URL.Path,
+ ContextKeyRequestProto: r.Proto,
+ ContextKeyRequestHost: r.Host,
+ ContextKeyRequestRemoteAddr: r.RemoteAddr,
+ ContextKeyRequestXForwardedFor: r.Header.Get("X-Forwarded-For"),
+ ContextKeyRequestXForwardedProto: r.Header.Get("X-Forwarded-Proto"),
+ ContextKeyRequestAuthorization: r.Header.Get("Authorization"),
+ ContextKeyRequestReferer: r.Header.Get("Referer"),
+ ContextKeyRequestUserAgent: r.Header.Get("User-Agent"),
+ ContextKeyRequestXRequestID: r.Header.Get("X-Request-Id"),
+ ContextKeyRequestAccept: r.Header.Get("Accept"),
+ } {
+ ctx = context.WithValue(ctx, k, v)
+ }
+ return ctx
+}
+
+type contextKey int
+
+const (
+ // ContextKeyRequestMethod is populated in the context by
+ // PopulateRequestContext. Its value is r.Method.
+ ContextKeyRequestMethod contextKey = iota
+
+ // ContextKeyRequestURI is populated in the context by
+ // PopulateRequestContext. Its value is r.RequestURI.
+ ContextKeyRequestURI
+
+ // ContextKeyRequestPath is populated in the context by
+ // PopulateRequestContext. Its value is r.URL.Path.
+ ContextKeyRequestPath
+
+ // ContextKeyRequestProto is populated in the context by
+ // PopulateRequestContext. Its value is r.Proto.
+ ContextKeyRequestProto
+
+ // ContextKeyRequestHost is populated in the context by
+ // PopulateRequestContext. Its value is r.Host.
+ ContextKeyRequestHost
+
+ // ContextKeyRequestRemoteAddr is populated in the context by
+ // PopulateRequestContext. Its value is r.RemoteAddr.
+ ContextKeyRequestRemoteAddr
+
+ // ContextKeyRequestXForwardedFor is populated in the context by
+ // PopulateRequestContext. Its value is r.Header.Get("X-Forwarded-For").
+ ContextKeyRequestXForwardedFor
+
+ // ContextKeyRequestXForwardedProto is populated in the context by
+ // PopulateRequestContext. Its value is r.Header.Get("X-Forwarded-Proto").
+ ContextKeyRequestXForwardedProto
+
+ // ContextKeyRequestAuthorization is populated in the context by
+ // PopulateRequestContext. Its value is r.Header.Get("Authorization").
+ ContextKeyRequestAuthorization
+
+ // ContextKeyRequestReferer is populated in the context by
+ // PopulateRequestContext. Its value is r.Header.Get("Referer").
+ ContextKeyRequestReferer
+
+ // ContextKeyRequestUserAgent is populated in the context by
+ // PopulateRequestContext. Its value is r.Header.Get("User-Agent").
+ ContextKeyRequestUserAgent
+
+ // ContextKeyRequestXRequestID is populated in the context by
+ // PopulateRequestContext. Its value is r.Header.Get("X-Request-Id").
+ ContextKeyRequestXRequestID
+
+ // ContextKeyRequestAccept is populated in the context by
+ // PopulateRequestContext. Its value is r.Header.Get("Accept").
+ ContextKeyRequestAccept
+
+ // ContextKeyResponseHeaders is populated in the context whenever a
+ // ServerFinalizerFunc is specified. Its value is of type http.Header, and
+ // is captured only once the entire response has been written.
+ ContextKeyResponseHeaders
+
+ // ContextKeyResponseSize is populated in the context whenever a
+ // ServerFinalizerFunc is specified. Its value is of type int64.
+ ContextKeyResponseSize
+)
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/go-kit/kit/transport/http/server.go b/go/ql/test/experimental/CWE-321/vendor/github.com/go-kit/kit/transport/http/server.go
new file mode 100644
index 000000000000..dfd1ff3d6994
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/go-kit/kit/transport/http/server.go
@@ -0,0 +1,244 @@
+package http
+
+import (
+ "context"
+ "encoding/json"
+ "net/http"
+
+ "github.com/go-kit/kit/endpoint"
+ "github.com/go-kit/kit/transport"
+ "github.com/go-kit/log"
+)
+
+// Server wraps an endpoint and implements http.Handler.
+type Server struct {
+ e endpoint.Endpoint
+ dec DecodeRequestFunc
+ enc EncodeResponseFunc
+ before []RequestFunc
+ after []ServerResponseFunc
+ errorEncoder ErrorEncoder
+ finalizer []ServerFinalizerFunc
+ errorHandler transport.ErrorHandler
+}
+
+// NewServer constructs a new server, which implements http.Handler and wraps
+// the provided endpoint.
+func NewServer(
+ e endpoint.Endpoint,
+ dec DecodeRequestFunc,
+ enc EncodeResponseFunc,
+ options ...ServerOption,
+) *Server {
+ s := &Server{
+ e: e,
+ dec: dec,
+ enc: enc,
+ errorEncoder: DefaultErrorEncoder,
+ errorHandler: transport.NewLogErrorHandler(log.NewNopLogger()),
+ }
+ for _, option := range options {
+ option(s)
+ }
+ return s
+}
+
+// ServerOption sets an optional parameter for servers.
+type ServerOption func(*Server)
+
+// ServerBefore functions are executed on the HTTP request object before the
+// request is decoded.
+func ServerBefore(before ...RequestFunc) ServerOption {
+ return func(s *Server) { s.before = append(s.before, before...) }
+}
+
+// ServerAfter functions are executed on the HTTP response writer after the
+// endpoint is invoked, but before anything is written to the client.
+func ServerAfter(after ...ServerResponseFunc) ServerOption {
+ return func(s *Server) { s.after = append(s.after, after...) }
+}
+
+// ServerErrorEncoder is used to encode errors to the http.ResponseWriter
+// whenever they're encountered in the processing of a request. Clients can
+// use this to provide custom error formatting and response codes. By default,
+// errors will be written with the DefaultErrorEncoder.
+func ServerErrorEncoder(ee ErrorEncoder) ServerOption {
+ return func(s *Server) { s.errorEncoder = ee }
+}
+
+// ServerErrorLogger is used to log non-terminal errors. By default, no errors
+// are logged. This is intended as a diagnostic measure. Finer-grained control
+// of error handling, including logging in more detail, should be performed in a
+// custom ServerErrorEncoder or ServerFinalizer, both of which have access to
+// the context.
+// Deprecated: Use ServerErrorHandler instead.
+func ServerErrorLogger(logger log.Logger) ServerOption {
+ return func(s *Server) { s.errorHandler = transport.NewLogErrorHandler(logger) }
+}
+
+// ServerErrorHandler is used to handle non-terminal errors. By default, non-terminal errors
+// are ignored. This is intended as a diagnostic measure. Finer-grained control
+// of error handling, including logging in more detail, should be performed in a
+// custom ServerErrorEncoder or ServerFinalizer, both of which have access to
+// the context.
+func ServerErrorHandler(errorHandler transport.ErrorHandler) ServerOption {
+ return func(s *Server) { s.errorHandler = errorHandler }
+}
+
+// ServerFinalizer is executed at the end of every HTTP request.
+// By default, no finalizer is registered.
+func ServerFinalizer(f ...ServerFinalizerFunc) ServerOption {
+ return func(s *Server) { s.finalizer = append(s.finalizer, f...) }
+}
+
+// ServeHTTP implements http.Handler.
+func (s Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+ ctx := r.Context()
+
+ if len(s.finalizer) > 0 {
+ iw := &interceptingWriter{w, http.StatusOK, 0}
+ defer func() {
+ ctx = context.WithValue(ctx, ContextKeyResponseHeaders, iw.Header())
+ ctx = context.WithValue(ctx, ContextKeyResponseSize, iw.written)
+ for _, f := range s.finalizer {
+ f(ctx, iw.code, r)
+ }
+ }()
+ w = iw
+ }
+
+ for _, f := range s.before {
+ ctx = f(ctx, r)
+ }
+
+ request, err := s.dec(ctx, r)
+ if err != nil {
+ s.errorHandler.Handle(ctx, err)
+ s.errorEncoder(ctx, err, w)
+ return
+ }
+
+ response, err := s.e(ctx, request)
+ if err != nil {
+ s.errorHandler.Handle(ctx, err)
+ s.errorEncoder(ctx, err, w)
+ return
+ }
+
+ for _, f := range s.after {
+ ctx = f(ctx, w)
+ }
+
+ if err := s.enc(ctx, w, response); err != nil {
+ s.errorHandler.Handle(ctx, err)
+ s.errorEncoder(ctx, err, w)
+ return
+ }
+}
+
+// ErrorEncoder is responsible for encoding an error to the ResponseWriter.
+// Users are encouraged to use custom ErrorEncoders to encode HTTP errors to
+// their clients, and will likely want to pass and check for their own error
+// types. See the example shipping/handling service.
+type ErrorEncoder func(ctx context.Context, err error, w http.ResponseWriter)
+
+// ServerFinalizerFunc can be used to perform work at the end of an HTTP
+// request, after the response has been written to the client. The principal
+// intended use is for request logging. In addition to the response code
+// provided in the function signature, additional response parameters are
+// provided in the context under keys with the ContextKeyResponse prefix.
+type ServerFinalizerFunc func(ctx context.Context, code int, r *http.Request)
+
+// NopRequestDecoder is a DecodeRequestFunc that can be used for requests that do not
+// need to be decoded, and simply returns nil, nil.
+func NopRequestDecoder(ctx context.Context, r *http.Request) (interface{}, error) {
+ return nil, nil
+}
+
+// EncodeJSONResponse is a EncodeResponseFunc that serializes the response as a
+// JSON object to the ResponseWriter. Many JSON-over-HTTP services can use it as
+// a sensible default. If the response implements Headerer, the provided headers
+// will be applied to the response. If the response implements StatusCoder, the
+// provided StatusCode will be used instead of 200.
+func EncodeJSONResponse(_ context.Context, w http.ResponseWriter, response interface{}) error {
+ w.Header().Set("Content-Type", "application/json; charset=utf-8")
+ if headerer, ok := response.(Headerer); ok {
+ for k, values := range headerer.Headers() {
+ for _, v := range values {
+ w.Header().Add(k, v)
+ }
+ }
+ }
+ code := http.StatusOK
+ if sc, ok := response.(StatusCoder); ok {
+ code = sc.StatusCode()
+ }
+ w.WriteHeader(code)
+ if code == http.StatusNoContent {
+ return nil
+ }
+ return json.NewEncoder(w).Encode(response)
+}
+
+// DefaultErrorEncoder writes the error to the ResponseWriter, by default a
+// content type of text/plain, a body of the plain text of the error, and a
+// status code of 500. If the error implements Headerer, the provided headers
+// will be applied to the response. If the error implements json.Marshaler, and
+// the marshaling succeeds, a content type of application/json and the JSON
+// encoded form of the error will be used. If the error implements StatusCoder,
+// the provided StatusCode will be used instead of 500.
+func DefaultErrorEncoder(_ context.Context, err error, w http.ResponseWriter) {
+ contentType, body := "text/plain; charset=utf-8", []byte(err.Error())
+ if marshaler, ok := err.(json.Marshaler); ok {
+ if jsonBody, marshalErr := marshaler.MarshalJSON(); marshalErr == nil {
+ contentType, body = "application/json; charset=utf-8", jsonBody
+ }
+ }
+ w.Header().Set("Content-Type", contentType)
+ if headerer, ok := err.(Headerer); ok {
+ for k, values := range headerer.Headers() {
+ for _, v := range values {
+ w.Header().Add(k, v)
+ }
+ }
+ }
+ code := http.StatusInternalServerError
+ if sc, ok := err.(StatusCoder); ok {
+ code = sc.StatusCode()
+ }
+ w.WriteHeader(code)
+ w.Write(body)
+}
+
+// StatusCoder is checked by DefaultErrorEncoder. If an error value implements
+// StatusCoder, the StatusCode will be used when encoding the error. By default,
+// StatusInternalServerError (500) is used.
+type StatusCoder interface {
+ StatusCode() int
+}
+
+// Headerer is checked by DefaultErrorEncoder. If an error value implements
+// Headerer, the provided headers will be applied to the response writer, after
+// the Content-Type is set.
+type Headerer interface {
+ Headers() http.Header
+}
+
+type interceptingWriter struct {
+ http.ResponseWriter
+ code int
+ written int64
+}
+
+// WriteHeader may not be explicitly called, so care must be taken to
+// initialize w.code to its default value of http.StatusOK.
+func (w *interceptingWriter) WriteHeader(code int) {
+ w.code = code
+ w.ResponseWriter.WriteHeader(code)
+}
+
+func (w *interceptingWriter) Write(p []byte) (int, error) {
+ n, err := w.ResponseWriter.Write(p)
+ w.written += int64(n)
+ return n, err
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/go-kit/log/.gitignore b/go/ql/test/experimental/CWE-321/vendor/github.com/go-kit/log/.gitignore
new file mode 100644
index 000000000000..66fd13c903ca
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/go-kit/log/.gitignore
@@ -0,0 +1,15 @@
+# Binaries for programs and plugins
+*.exe
+*.exe~
+*.dll
+*.so
+*.dylib
+
+# Test binary, built with `go test -c`
+*.test
+
+# Output of the go coverage tool, specifically when used with LiteIDE
+*.out
+
+# Dependency directories (remove the comment below to include it)
+# vendor/
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/go-kit/log/LICENSE b/go/ql/test/experimental/CWE-321/vendor/github.com/go-kit/log/LICENSE
new file mode 100644
index 000000000000..bb5bdb9cb8c9
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/go-kit/log/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2021 Go kit
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/go-kit/log/README.md b/go/ql/test/experimental/CWE-321/vendor/github.com/go-kit/log/README.md
new file mode 100644
index 000000000000..a0931951df8d
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/go-kit/log/README.md
@@ -0,0 +1,151 @@
+# package log
+
+`package log` provides a minimal interface for structured logging in services.
+It may be wrapped to encode conventions, enforce type-safety, provide leveled
+logging, and so on. It can be used for both typical application log events,
+and log-structured data streams.
+
+## Structured logging
+
+Structured logging is, basically, conceding to the reality that logs are
+_data_, and warrant some level of schematic rigor. Using a stricter,
+key/value-oriented message format for our logs, containing contextual and
+semantic information, makes it much easier to get insight into the
+operational activity of the systems we build. Consequently, `package log` is
+of the strong belief that "[the benefits of structured logging outweigh the
+minimal effort involved](https://www.thoughtworks.com/radar/techniques/structured-logging)".
+
+Migrating from unstructured to structured logging is probably a lot easier
+than you'd expect.
+
+```go
+// Unstructured
+log.Printf("HTTP server listening on %s", addr)
+
+// Structured
+logger.Log("transport", "HTTP", "addr", addr, "msg", "listening")
+```
+
+## Usage
+
+### Typical application logging
+
+```go
+w := log.NewSyncWriter(os.Stderr)
+logger := log.NewLogfmtLogger(w)
+logger.Log("question", "what is the meaning of life?", "answer", 42)
+
+// Output:
+// question="what is the meaning of life?" answer=42
+```
+
+### Contextual Loggers
+
+```go
+func main() {
+ var logger log.Logger
+ logger = log.NewLogfmtLogger(log.NewSyncWriter(os.Stderr))
+ logger = log.With(logger, "instance_id", 123)
+
+ logger.Log("msg", "starting")
+ NewWorker(log.With(logger, "component", "worker")).Run()
+ NewSlacker(log.With(logger, "component", "slacker")).Run()
+}
+
+// Output:
+// instance_id=123 msg=starting
+// instance_id=123 component=worker msg=running
+// instance_id=123 component=slacker msg=running
+```
+
+### Interact with stdlib logger
+
+Redirect stdlib logger to Go kit logger.
+
+```go
+import (
+ "os"
+ stdlog "log"
+ kitlog "github.com/go-kit/log"
+)
+
+func main() {
+ logger := kitlog.NewJSONLogger(kitlog.NewSyncWriter(os.Stdout))
+ stdlog.SetOutput(kitlog.NewStdlibAdapter(logger))
+ stdlog.Print("I sure like pie")
+}
+
+// Output:
+// {"msg":"I sure like pie","ts":"2016/01/01 12:34:56"}
+```
+
+Or, if, for legacy reasons, you need to pipe all of your logging through the
+stdlib log package, you can redirect Go kit logger to the stdlib logger.
+
+```go
+logger := kitlog.NewLogfmtLogger(kitlog.StdlibWriter{})
+logger.Log("legacy", true, "msg", "at least it's something")
+
+// Output:
+// 2016/01/01 12:34:56 legacy=true msg="at least it's something"
+```
+
+### Timestamps and callers
+
+```go
+var logger log.Logger
+logger = log.NewLogfmtLogger(log.NewSyncWriter(os.Stderr))
+logger = log.With(logger, "ts", log.DefaultTimestampUTC, "caller", log.DefaultCaller)
+
+logger.Log("msg", "hello")
+
+// Output:
+// ts=2016-01-01T12:34:56Z caller=main.go:15 msg=hello
+```
+
+## Levels
+
+Log levels are supported via the [level package](https://godoc.org/github.com/go-kit/log/level).
+
+## Supported output formats
+
+- [Logfmt](https://brandur.org/logfmt) ([see also](https://blog.codeship.com/logfmt-a-log-format-thats-easy-to-read-and-write))
+- JSON
+
+## Enhancements
+
+`package log` is centered on the one-method Logger interface.
+
+```go
+type Logger interface {
+ Log(keyvals ...interface{}) error
+}
+```
+
+This interface, and its supporting code like is the product of much iteration
+and evaluation. For more details on the evolution of the Logger interface,
+see [The Hunt for a Logger Interface](http://go-talks.appspot.com/github.com/ChrisHines/talks/structured-logging/structured-logging.slide#1),
+a talk by [Chris Hines](https://github.com/ChrisHines).
+Also, please see
+[#63](https://github.com/go-kit/kit/issues/63),
+[#76](https://github.com/go-kit/kit/pull/76),
+[#131](https://github.com/go-kit/kit/issues/131),
+[#157](https://github.com/go-kit/kit/pull/157),
+[#164](https://github.com/go-kit/kit/issues/164), and
+[#252](https://github.com/go-kit/kit/pull/252)
+to review historical conversations about package log and the Logger interface.
+
+Value-add packages and suggestions,
+like improvements to [the leveled logger](https://godoc.org/github.com/go-kit/log/level),
+are of course welcome. Good proposals should
+
+- Be composable with [contextual loggers](https://godoc.org/github.com/go-kit/log#With),
+- Not break the behavior of [log.Caller](https://godoc.org/github.com/go-kit/log#Caller) in any wrapped contextual loggers, and
+- Be friendly to packages that accept only an unadorned log.Logger.
+
+## Benchmarks & comparisons
+
+There are a few Go logging benchmarks and comparisons that include Go kit's package log.
+
+- [imkira/go-loggers-bench](https://github.com/imkira/go-loggers-bench) includes kit/log
+- [uber-common/zap](https://github.com/uber-common/zap), a zero-alloc logging library, includes a comparison with kit/log
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/go-kit/log/doc.go b/go/ql/test/experimental/CWE-321/vendor/github.com/go-kit/log/doc.go
new file mode 100644
index 000000000000..f744382fe499
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/go-kit/log/doc.go
@@ -0,0 +1,116 @@
+// Package log provides a structured logger.
+//
+// Structured logging produces logs easily consumed later by humans or
+// machines. Humans might be interested in debugging errors, or tracing
+// specific requests. Machines might be interested in counting interesting
+// events, or aggregating information for off-line processing. In both cases,
+// it is important that the log messages are structured and actionable.
+// Package log is designed to encourage both of these best practices.
+//
+// Basic Usage
+//
+// The fundamental interface is Logger. Loggers create log events from
+// key/value data. The Logger interface has a single method, Log, which
+// accepts a sequence of alternating key/value pairs, which this package names
+// keyvals.
+//
+// type Logger interface {
+// Log(keyvals ...interface{}) error
+// }
+//
+// Here is an example of a function using a Logger to create log events.
+//
+// func RunTask(task Task, logger log.Logger) string {
+// logger.Log("taskID", task.ID, "event", "starting task")
+// ...
+// logger.Log("taskID", task.ID, "event", "task complete")
+// }
+//
+// The keys in the above example are "taskID" and "event". The values are
+// task.ID, "starting task", and "task complete". Every key is followed
+// immediately by its value.
+//
+// Keys are usually plain strings. Values may be any type that has a sensible
+// encoding in the chosen log format. With structured logging it is a good
+// idea to log simple values without formatting them. This practice allows
+// the chosen logger to encode values in the most appropriate way.
+//
+// Contextual Loggers
+//
+// A contextual logger stores keyvals that it includes in all log events.
+// Building appropriate contextual loggers reduces repetition and aids
+// consistency in the resulting log output. With, WithPrefix, and WithSuffix
+// add context to a logger. We can use With to improve the RunTask example.
+//
+// func RunTask(task Task, logger log.Logger) string {
+// logger = log.With(logger, "taskID", task.ID)
+// logger.Log("event", "starting task")
+// ...
+// taskHelper(task.Cmd, logger)
+// ...
+// logger.Log("event", "task complete")
+// }
+//
+// The improved version emits the same log events as the original for the
+// first and last calls to Log. Passing the contextual logger to taskHelper
+// enables each log event created by taskHelper to include the task.ID even
+// though taskHelper does not have access to that value. Using contextual
+// loggers this way simplifies producing log output that enables tracing the
+// life cycle of individual tasks. (See the Contextual example for the full
+// code of the above snippet.)
+//
+// Dynamic Contextual Values
+//
+// A Valuer function stored in a contextual logger generates a new value each
+// time an event is logged. The Valuer example demonstrates how this feature
+// works.
+//
+// Valuers provide the basis for consistently logging timestamps and source
+// code location. The log package defines several valuers for that purpose.
+// See Timestamp, DefaultTimestamp, DefaultTimestampUTC, Caller, and
+// DefaultCaller. A common logger initialization sequence that ensures all log
+// entries contain a timestamp and source location looks like this:
+//
+// logger := log.NewLogfmtLogger(log.NewSyncWriter(os.Stdout))
+// logger = log.With(logger, "ts", log.DefaultTimestampUTC, "caller", log.DefaultCaller)
+//
+// Concurrent Safety
+//
+// Applications with multiple goroutines want each log event written to the
+// same logger to remain separate from other log events. Package log provides
+// two simple solutions for concurrent safe logging.
+//
+// NewSyncWriter wraps an io.Writer and serializes each call to its Write
+// method. Using a SyncWriter has the benefit that the smallest practical
+// portion of the logging logic is performed within a mutex, but it requires
+// the formatting Logger to make only one call to Write per log event.
+//
+// NewSyncLogger wraps any Logger and serializes each call to its Log method.
+// Using a SyncLogger has the benefit that it guarantees each log event is
+// handled atomically within the wrapped logger, but it typically serializes
+// both the formatting and output logic. Use a SyncLogger if the formatting
+// logger may perform multiple writes per log event.
+//
+// Error Handling
+//
+// This package relies on the practice of wrapping or decorating loggers with
+// other loggers to provide composable pieces of functionality. It also means
+// that Logger.Log must return an error because some
+// implementations—especially those that output log data to an io.Writer—may
+// encounter errors that cannot be handled locally. This in turn means that
+// Loggers that wrap other loggers should return errors from the wrapped
+// logger up the stack.
+//
+// Fortunately, the decorator pattern also provides a way to avoid the
+// necessity to check for errors every time an application calls Logger.Log.
+// An application required to panic whenever its Logger encounters
+// an error could initialize its logger as follows.
+//
+// fmtlogger := log.NewLogfmtLogger(log.NewSyncWriter(os.Stdout))
+// logger := log.LoggerFunc(func(keyvals ...interface{}) error {
+// if err := fmtlogger.Log(keyvals...); err != nil {
+// panic(err)
+// }
+// return nil
+// })
+package log
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/go-kit/log/json_logger.go b/go/ql/test/experimental/CWE-321/vendor/github.com/go-kit/log/json_logger.go
new file mode 100644
index 000000000000..0cedbf82478e
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/go-kit/log/json_logger.go
@@ -0,0 +1,91 @@
+package log
+
+import (
+ "encoding"
+ "encoding/json"
+ "fmt"
+ "io"
+ "reflect"
+)
+
+type jsonLogger struct {
+ io.Writer
+}
+
+// NewJSONLogger returns a Logger that encodes keyvals to the Writer as a
+// single JSON object. Each log event produces no more than one call to
+// w.Write. The passed Writer must be safe for concurrent use by multiple
+// goroutines if the returned Logger will be used concurrently.
+func NewJSONLogger(w io.Writer) Logger {
+ return &jsonLogger{w}
+}
+
+func (l *jsonLogger) Log(keyvals ...interface{}) error {
+ n := (len(keyvals) + 1) / 2 // +1 to handle case when len is odd
+ m := make(map[string]interface{}, n)
+ for i := 0; i < len(keyvals); i += 2 {
+ k := keyvals[i]
+ var v interface{} = ErrMissingValue
+ if i+1 < len(keyvals) {
+ v = keyvals[i+1]
+ }
+ merge(m, k, v)
+ }
+ enc := json.NewEncoder(l.Writer)
+ enc.SetEscapeHTML(false)
+ return enc.Encode(m)
+}
+
+func merge(dst map[string]interface{}, k, v interface{}) {
+ var key string
+ switch x := k.(type) {
+ case string:
+ key = x
+ case fmt.Stringer:
+ key = safeString(x)
+ default:
+ key = fmt.Sprint(x)
+ }
+
+ // We want json.Marshaler and encoding.TextMarshaller to take priority over
+ // err.Error() and v.String(). But json.Marshall (called later) does that by
+ // default so we force a no-op if it's one of those 2 case.
+ switch x := v.(type) {
+ case json.Marshaler:
+ case encoding.TextMarshaler:
+ case error:
+ v = safeError(x)
+ case fmt.Stringer:
+ v = safeString(x)
+ }
+
+ dst[key] = v
+}
+
+func safeString(str fmt.Stringer) (s string) {
+ defer func() {
+ if panicVal := recover(); panicVal != nil {
+ if v := reflect.ValueOf(str); v.Kind() == reflect.Ptr && v.IsNil() {
+ s = "NULL"
+ } else {
+ panic(panicVal)
+ }
+ }
+ }()
+ s = str.String()
+ return
+}
+
+func safeError(err error) (s interface{}) {
+ defer func() {
+ if panicVal := recover(); panicVal != nil {
+ if v := reflect.ValueOf(err); v.Kind() == reflect.Ptr && v.IsNil() {
+ s = nil
+ } else {
+ panic(panicVal)
+ }
+ }
+ }()
+ s = err.Error()
+ return
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/go-kit/log/log.go b/go/ql/test/experimental/CWE-321/vendor/github.com/go-kit/log/log.go
new file mode 100644
index 000000000000..62e11adace59
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/go-kit/log/log.go
@@ -0,0 +1,179 @@
+package log
+
+import "errors"
+
+// Logger is the fundamental interface for all log operations. Log creates a
+// log event from keyvals, a variadic sequence of alternating keys and values.
+// Implementations must be safe for concurrent use by multiple goroutines. In
+// particular, any implementation of Logger that appends to keyvals or
+// modifies or retains any of its elements must make a copy first.
+type Logger interface {
+ Log(keyvals ...interface{}) error
+}
+
+// ErrMissingValue is appended to keyvals slices with odd length to substitute
+// the missing value.
+var ErrMissingValue = errors.New("(MISSING)")
+
+// With returns a new contextual logger with keyvals prepended to those passed
+// to calls to Log. If logger is also a contextual logger created by With,
+// WithPrefix, or WithSuffix, keyvals is appended to the existing context.
+//
+// The returned Logger replaces all value elements (odd indexes) containing a
+// Valuer with their generated value for each call to its Log method.
+func With(logger Logger, keyvals ...interface{}) Logger {
+ if len(keyvals) == 0 {
+ return logger
+ }
+ l := newContext(logger)
+ kvs := append(l.keyvals, keyvals...)
+ if len(kvs)%2 != 0 {
+ kvs = append(kvs, ErrMissingValue)
+ }
+ return &context{
+ logger: l.logger,
+ // Limiting the capacity of the stored keyvals ensures that a new
+ // backing array is created if the slice must grow in Log or With.
+ // Using the extra capacity without copying risks a data race that
+ // would violate the Logger interface contract.
+ keyvals: kvs[:len(kvs):len(kvs)],
+ hasValuer: l.hasValuer || containsValuer(keyvals),
+ sKeyvals: l.sKeyvals,
+ sHasValuer: l.sHasValuer,
+ }
+}
+
+// WithPrefix returns a new contextual logger with keyvals prepended to those
+// passed to calls to Log. If logger is also a contextual logger created by
+// With, WithPrefix, or WithSuffix, keyvals is prepended to the existing context.
+//
+// The returned Logger replaces all value elements (odd indexes) containing a
+// Valuer with their generated value for each call to its Log method.
+func WithPrefix(logger Logger, keyvals ...interface{}) Logger {
+ if len(keyvals) == 0 {
+ return logger
+ }
+ l := newContext(logger)
+ // Limiting the capacity of the stored keyvals ensures that a new
+ // backing array is created if the slice must grow in Log or With.
+ // Using the extra capacity without copying risks a data race that
+ // would violate the Logger interface contract.
+ n := len(l.keyvals) + len(keyvals)
+ if len(keyvals)%2 != 0 {
+ n++
+ }
+ kvs := make([]interface{}, 0, n)
+ kvs = append(kvs, keyvals...)
+ if len(kvs)%2 != 0 {
+ kvs = append(kvs, ErrMissingValue)
+ }
+ kvs = append(kvs, l.keyvals...)
+ return &context{
+ logger: l.logger,
+ keyvals: kvs,
+ hasValuer: l.hasValuer || containsValuer(keyvals),
+ sKeyvals: l.sKeyvals,
+ sHasValuer: l.sHasValuer,
+ }
+}
+
+// WithSuffix returns a new contextual logger with keyvals appended to those
+// passed to calls to Log. If logger is also a contextual logger created by
+// With, WithPrefix, or WithSuffix, keyvals is appended to the existing context.
+//
+// The returned Logger replaces all value elements (odd indexes) containing a
+// Valuer with their generated value for each call to its Log method.
+func WithSuffix(logger Logger, keyvals ...interface{}) Logger {
+ if len(keyvals) == 0 {
+ return logger
+ }
+ l := newContext(logger)
+ // Limiting the capacity of the stored keyvals ensures that a new
+ // backing array is created if the slice must grow in Log or With.
+ // Using the extra capacity without copying risks a data race that
+ // would violate the Logger interface contract.
+ n := len(l.sKeyvals) + len(keyvals)
+ if len(keyvals)%2 != 0 {
+ n++
+ }
+ kvs := make([]interface{}, 0, n)
+ kvs = append(kvs, keyvals...)
+ if len(kvs)%2 != 0 {
+ kvs = append(kvs, ErrMissingValue)
+ }
+ kvs = append(l.sKeyvals, kvs...)
+ return &context{
+ logger: l.logger,
+ keyvals: l.keyvals,
+ hasValuer: l.hasValuer,
+ sKeyvals: kvs,
+ sHasValuer: l.sHasValuer || containsValuer(keyvals),
+ }
+}
+
+// context is the Logger implementation returned by With, WithPrefix, and
+// WithSuffix. It wraps a Logger and holds keyvals that it includes in all
+// log events. Its Log method calls bindValues to generate values for each
+// Valuer in the context keyvals.
+//
+// A context must always have the same number of stack frames between calls to
+// its Log method and the eventual binding of Valuers to their value. This
+// requirement comes from the functional requirement to allow a context to
+// resolve application call site information for a Caller stored in the
+// context. To do this we must be able to predict the number of logging
+// functions on the stack when bindValues is called.
+//
+// Two implementation details provide the needed stack depth consistency.
+//
+// 1. newContext avoids introducing an additional layer when asked to
+// wrap another context.
+// 2. With, WithPrefix, and WithSuffix avoid introducing an additional
+// layer by returning a newly constructed context with a merged keyvals
+// rather than simply wrapping the existing context.
+type context struct {
+ logger Logger
+ keyvals []interface{}
+ sKeyvals []interface{} // suffixes
+ hasValuer bool
+ sHasValuer bool
+}
+
+func newContext(logger Logger) *context {
+ if c, ok := logger.(*context); ok {
+ return c
+ }
+ return &context{logger: logger}
+}
+
+// Log replaces all value elements (odd indexes) containing a Valuer in the
+// stored context with their generated value, appends keyvals, and passes the
+// result to the wrapped Logger.
+func (l *context) Log(keyvals ...interface{}) error {
+ kvs := append(l.keyvals, keyvals...)
+ if len(kvs)%2 != 0 {
+ kvs = append(kvs, ErrMissingValue)
+ }
+ if l.hasValuer {
+ // If no keyvals were appended above then we must copy l.keyvals so
+ // that future log events will reevaluate the stored Valuers.
+ if len(keyvals) == 0 {
+ kvs = append([]interface{}{}, l.keyvals...)
+ }
+ bindValues(kvs[:(len(l.keyvals))])
+ }
+ kvs = append(kvs, l.sKeyvals...)
+ if l.sHasValuer {
+ bindValues(kvs[len(kvs)-len(l.sKeyvals):])
+ }
+ return l.logger.Log(kvs...)
+}
+
+// LoggerFunc is an adapter to allow use of ordinary functions as Loggers. If
+// f is a function with the appropriate signature, LoggerFunc(f) is a Logger
+// object that calls f.
+type LoggerFunc func(...interface{}) error
+
+// Log implements Logger by calling f(keyvals...).
+func (f LoggerFunc) Log(keyvals ...interface{}) error {
+ return f(keyvals...)
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/go-kit/log/logfmt_logger.go b/go/ql/test/experimental/CWE-321/vendor/github.com/go-kit/log/logfmt_logger.go
new file mode 100644
index 000000000000..a00305298b82
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/go-kit/log/logfmt_logger.go
@@ -0,0 +1,62 @@
+package log
+
+import (
+ "bytes"
+ "io"
+ "sync"
+
+ "github.com/go-logfmt/logfmt"
+)
+
+type logfmtEncoder struct {
+ *logfmt.Encoder
+ buf bytes.Buffer
+}
+
+func (l *logfmtEncoder) Reset() {
+ l.Encoder.Reset()
+ l.buf.Reset()
+}
+
+var logfmtEncoderPool = sync.Pool{
+ New: func() interface{} {
+ var enc logfmtEncoder
+ enc.Encoder = logfmt.NewEncoder(&enc.buf)
+ return &enc
+ },
+}
+
+type logfmtLogger struct {
+ w io.Writer
+}
+
+// NewLogfmtLogger returns a logger that encodes keyvals to the Writer in
+// logfmt format. Each log event produces no more than one call to w.Write.
+// The passed Writer must be safe for concurrent use by multiple goroutines if
+// the returned Logger will be used concurrently.
+func NewLogfmtLogger(w io.Writer) Logger {
+ return &logfmtLogger{w}
+}
+
+func (l logfmtLogger) Log(keyvals ...interface{}) error {
+ enc := logfmtEncoderPool.Get().(*logfmtEncoder)
+ enc.Reset()
+ defer logfmtEncoderPool.Put(enc)
+
+ if err := enc.EncodeKeyvals(keyvals...); err != nil {
+ return err
+ }
+
+ // Add newline to the end of the buffer
+ if err := enc.EndRecord(); err != nil {
+ return err
+ }
+
+ // The Logger interface requires implementations to be safe for concurrent
+ // use by multiple goroutines. For this implementation that means making
+ // only one call to l.w.Write() for each call to Log.
+ if _, err := l.w.Write(enc.buf.Bytes()); err != nil {
+ return err
+ }
+ return nil
+}
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/go-kit/log/nop_logger.go b/go/ql/test/experimental/CWE-321/vendor/github.com/go-kit/log/nop_logger.go
new file mode 100644
index 000000000000..1047d626c436
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/go-kit/log/nop_logger.go
@@ -0,0 +1,8 @@
+package log
+
+type nopLogger struct{}
+
+// NewNopLogger returns a logger that doesn't do anything.
+func NewNopLogger() Logger { return nopLogger{} }
+
+func (nopLogger) Log(...interface{}) error { return nil }
diff --git a/go/ql/test/experimental/CWE-321/vendor/github.com/go-kit/log/stdlib.go b/go/ql/test/experimental/CWE-321/vendor/github.com/go-kit/log/stdlib.go
new file mode 100644
index 000000000000..0338edbe2ba3
--- /dev/null
+++ b/go/ql/test/experimental/CWE-321/vendor/github.com/go-kit/log/stdlib.go
@@ -0,0 +1,151 @@
+package log
+
+import (
+ "bytes"
+ "io"
+ "log"
+ "regexp"
+ "strings"
+)
+
+// StdlibWriter implements io.Writer by invoking the stdlib log.Print. It's
+// designed to be passed to a Go kit logger as the writer, for cases where
+// it's necessary to redirect all Go kit log output to the stdlib logger.
+//
+// If you have any choice in the matter, you shouldn't use this. Prefer to
+// redirect the stdlib log to the Go kit logger via NewStdlibAdapter.
+type StdlibWriter struct{}
+
+// Write implements io.Writer.
+func (w StdlibWriter) Write(p []byte) (int, error) {
+ log.Print(strings.TrimSpace(string(p)))
+ return len(p), nil
+}
+
+// StdlibAdapter wraps a Logger and allows it to be passed to the stdlib
+// logger's SetOutput. It will extract date/timestamps, filenames, and
+// messages, and place them under relevant keys.
+type StdlibAdapter struct {
+ Logger
+ timestampKey string
+ fileKey string
+ messageKey string
+ prefix string
+ joinPrefixToMsg bool
+}
+
+// StdlibAdapterOption sets a parameter for the StdlibAdapter.
+type StdlibAdapterOption func(*StdlibAdapter)
+
+// TimestampKey sets the key for the timestamp field. By default, it's "ts".
+func TimestampKey(key string) StdlibAdapterOption {
+ return func(a *StdlibAdapter) { a.timestampKey = key }
+}
+
+// FileKey sets the key for the file and line field. By default, it's "caller".
+func FileKey(key string) StdlibAdapterOption {
+ return func(a *StdlibAdapter) { a.fileKey = key }
+}
+
+// MessageKey sets the key for the actual log message. By default, it's "msg".
+func MessageKey(key string) StdlibAdapterOption {
+ return func(a *StdlibAdapter) { a.messageKey = key }
+}
+
+// Prefix configures the adapter to parse a prefix from stdlib log events. If
+// you provide a non-empty prefix to the stdlib logger, then your should provide
+// that same prefix to the adapter via this option.
+//
+// By default, the prefix isn't included in the msg key. Set joinPrefixToMsg to
+// true if you want to include the parsed prefix in the msg.
+func Prefix(prefix string, joinPrefixToMsg bool) StdlibAdapterOption {
+ return func(a *StdlibAdapter) { a.prefix = prefix; a.joinPrefixToMsg = joinPrefixToMsg }
+}
+
+// NewStdlibAdapter returns a new StdlibAdapter wrapper around the passed
+// logger. It's designed to be passed to log.SetOutput.
+func NewStdlibAdapter(logger Logger, options ...StdlibAdapterOption) io.Writer {
+ a := StdlibAdapter{
+ Logger: logger,
+ timestampKey: "ts",
+ fileKey: "caller",
+ messageKey: "msg",
+ }
+ for _, option := range options {
+ option(&a)
+ }
+ return a
+}
+
+func (a StdlibAdapter) Write(p []byte) (int, error) {
+ p = a.handlePrefix(p)
+
+ result := subexps(p)
+ keyvals := []interface{}{}
+ var timestamp string
+ if date, ok := result["date"]; ok && date != "" {
+ timestamp = date
+ }
+ if time, ok := result["time"]; ok && time != "" {
+ if timestamp != "" {
+ timestamp += " "
+ }
+ timestamp += time
+ }
+ if timestamp != "" {
+ keyvals = append(keyvals, a.timestampKey, timestamp)
+ }
+ if file, ok := result["file"]; ok && file != "" {
+ keyvals = append(keyvals, a.fileKey, file)
+ }
+ if msg, ok := result["msg"]; ok {
+ msg = a.handleMessagePrefix(msg)
+ keyvals = append(keyvals, a.messageKey, msg)
+ }
+ if err := a.Logger.Log(keyvals...); err != nil {
+ return 0, err
+ }
+ return len(p), nil
+}
+
+func (a StdlibAdapter) handlePrefix(p []byte) []byte {
+ if a.prefix != "" {
+ p = bytes.TrimPrefix(p, []byte(a.prefix))
+ }
+ return p
+}
+
+func (a StdlibAdapter) handleMessagePrefix(msg string) string {
+ if a.prefix == "" {
+ return msg
+ }
+
+ msg = strings.TrimPrefix(msg, a.prefix)
+ if a.joinPrefixToMsg {
+ msg = a.prefix + msg
+ }
+ return msg
+}
+
+const (
+ logRegexpDate = `(?P[0-9]{4}/[0-9]{2}/[0-9]{2})?[ ]?`
+ logRegexpTime = `(?P