github.com/crowdsecurity/crowdsec@v1.6.1/pkg/appsec/query_utils.go (about)

     1  package appsec
     2  
     3  // This file is mostly stolen from net/url package, but with some modifications to allow less strict parsing of query strings
     4  
     5  import (
     6  	"net/url"
     7  	"strings"
     8  )
     9  
    10  // parseQuery and parseQuery are copied net/url package, but allow semicolon in values
    11  func ParseQuery(query string) url.Values {
    12  	m := make(url.Values)
    13  	parseQuery(m, query)
    14  	return m
    15  }
    16  
    17  func parseQuery(m url.Values, query string) {
    18  	for query != "" {
    19  		var key string
    20  		key, query, _ = strings.Cut(query, "&")
    21  
    22  		if key == "" {
    23  			continue
    24  		}
    25  		key, value, _ := strings.Cut(key, "=")
    26  		//for now we'll just ignore the errors, but ideally we want to fire some "internal" rules when we see invalid query strings
    27  		key = unescape(key)
    28  		value = unescape(value)
    29  		m[key] = append(m[key], value)
    30  	}
    31  }
    32  
    33  func hexDigitToByte(digit byte) (byte, bool) {
    34  	switch {
    35  	case digit >= '0' && digit <= '9':
    36  		return digit - '0', true
    37  	case digit >= 'a' && digit <= 'f':
    38  		return digit - 'a' + 10, true
    39  	case digit >= 'A' && digit <= 'F':
    40  		return digit - 'A' + 10, true
    41  	default:
    42  		return 0, false
    43  	}
    44  }
    45  
    46  func unescape(input string) string {
    47  	ilen := len(input)
    48  	res := strings.Builder{}
    49  	res.Grow(ilen)
    50  	for i := 0; i < ilen; i++ {
    51  		ci := input[i]
    52  		if ci == '+' {
    53  			res.WriteByte(' ')
    54  			continue
    55  		}
    56  		if ci == '%' {
    57  			if i+2 >= ilen {
    58  				res.WriteByte(ci)
    59  				continue
    60  			}
    61  			hi, ok := hexDigitToByte(input[i+1])
    62  			if !ok {
    63  				res.WriteByte(ci)
    64  				continue
    65  			}
    66  			lo, ok := hexDigitToByte(input[i+2])
    67  			if !ok {
    68  				res.WriteByte(ci)
    69  				continue
    70  			}
    71  			res.WriteByte(hi<<4 | lo)
    72  			i += 2
    73  			continue
    74  		}
    75  		res.WriteByte(ci)
    76  	}
    77  	return res.String()
    78  }