github.com/google/go-safeweb@v0.0.0-20231219055052-64d8cfc90fbb/safehttp/form.go (about)

     1  // Copyright 2020 Google LLC
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //	https://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package safehttp
    16  
    17  import (
    18  	"fmt"
    19  	"mime/multipart"
    20  	"path/filepath"
    21  	"strconv"
    22  )
    23  
    24  // Form contains parsed data from form parameters, part of
    25  // the body of POST, PATCH or PUT requests that are not multipart requests.
    26  type Form struct {
    27  	values map[string][]string
    28  	err    error
    29  }
    30  
    31  // Int64 returns the first form parameter value. If the first value is not a
    32  // valid int64, the defaultValue is returned instead and an error is set
    33  // (retrievable by Err()).
    34  func (f *Form) Int64(param string, defaultValue int64) int64 {
    35  	vals, ok := f.values[param]
    36  	if !ok {
    37  		return defaultValue
    38  	}
    39  	paramVal, err := strconv.ParseInt(vals[0], 10, 64)
    40  	if err != nil {
    41  		f.err = err
    42  		return defaultValue
    43  	}
    44  	return paramVal
    45  }
    46  
    47  // Uint64 returns the first form parameter value. If the first value is not a
    48  // valid uint64, the defaultValue is returned instead and an error is set
    49  // (retrievable by Err()).
    50  func (f *Form) Uint64(param string, defaultValue uint64) uint64 {
    51  	vals, ok := f.values[param]
    52  	if !ok {
    53  		return defaultValue
    54  	}
    55  	paramVal, err := strconv.ParseUint(vals[0], 10, 64)
    56  	if err != nil {
    57  		f.err = err
    58  		return defaultValue
    59  	}
    60  	return paramVal
    61  }
    62  
    63  // String returns the first form parameter value. If the first value is not a
    64  // valid string, the defaultValue is returned instead and an error is set
    65  // (retrievable by Err()).
    66  func (f *Form) String(param string, defaultValue string) string {
    67  	vals, ok := f.values[param]
    68  	if !ok {
    69  		return defaultValue
    70  	}
    71  	return vals[0]
    72  }
    73  
    74  // Float64 returns the first form parameter value. If the first value is not a
    75  // valid float64, the defaultValue is returned instead and an error is set
    76  // (retrievable by Err()).
    77  func (f *Form) Float64(param string, defaultValue float64) float64 {
    78  	vals, ok := f.values[param]
    79  	if !ok {
    80  		return defaultValue
    81  	}
    82  	paramVal, err := strconv.ParseFloat(vals[0], 64)
    83  	if err != nil {
    84  		f.err = err
    85  		return defaultValue
    86  	}
    87  	return paramVal
    88  }
    89  
    90  // Bool returns the first form parameter value. If the first value is not a
    91  // valid bool, the defaultValue is returned instead and an error is set
    92  // (retrievable by Err()).
    93  func (f *Form) Bool(param string, defaultValue bool) bool {
    94  	vals, ok := f.values[param]
    95  	if !ok {
    96  		return defaultValue
    97  	}
    98  	paramVal, err := strconv.ParseBool(vals[0])
    99  	if err != nil {
   100  		f.err = err
   101  		return defaultValue
   102  	}
   103  	return paramVal
   104  }
   105  
   106  func clearSlice(slicePtr interface{}) error {
   107  	switch vs := slicePtr.(type) {
   108  	case *[]string:
   109  		*vs = nil
   110  	case *[]int64:
   111  		*vs = nil
   112  	case *[]float64:
   113  		*vs = nil
   114  	case *[]uint64:
   115  		*vs = nil
   116  	case *[]bool:
   117  		*vs = nil
   118  	default:
   119  		return fmt.Errorf("type not supported in Slice call: %T", vs)
   120  	}
   121  	return nil
   122  }
   123  
   124  // Slice returns the form parameter values. If the values don't have the same
   125  // type, slicePtr will point to a nil slice instead and an error is set
   126  // (retrievable by Err()). This function should be used in case a form parameter
   127  // maps to multiple values.
   128  //
   129  // TODO(mihalimara22): Simplify this function to avoid duplicate logic
   130  func (f *Form) Slice(param string, slicePtr interface{}) {
   131  	mapVals, ok := f.values[param]
   132  	if !ok {
   133  		f.err = clearSlice(slicePtr)
   134  		return
   135  	}
   136  	switch values := slicePtr.(type) {
   137  	case *[]string:
   138  		res := make([]string, 0, len(mapVals))
   139  		*values = append(res, mapVals...)
   140  	case *[]int64:
   141  		res := make([]int64, 0, len(mapVals))
   142  		for _, x := range mapVals {
   143  			x, err := strconv.ParseInt(x, 10, 64)
   144  			if err != nil {
   145  				f.err = err
   146  				*values = nil
   147  				return
   148  			}
   149  			res = append(res, x)
   150  		}
   151  		*values = res
   152  	case *[]uint64:
   153  		res := make([]uint64, 0, len(mapVals))
   154  		for _, x := range mapVals {
   155  			x, err := strconv.ParseUint(x, 10, 64)
   156  			if err != nil {
   157  				f.err = err
   158  				*values = nil
   159  				return
   160  			}
   161  			res = append(res, x)
   162  		}
   163  		*values = res
   164  	case *[]float64:
   165  		res := make([]float64, 0, len(mapVals))
   166  		for _, x := range mapVals {
   167  			x, err := strconv.ParseFloat(x, 64)
   168  			if err != nil {
   169  				f.err = err
   170  				*values = nil
   171  				return
   172  			}
   173  			res = append(res, x)
   174  		}
   175  		*values = res
   176  	case *[]bool:
   177  		res := make([]bool, 0, len(mapVals))
   178  		for _, x := range mapVals {
   179  			b, err := strconv.ParseBool(x)
   180  			if err != nil {
   181  				f.err = err
   182  				*values = nil
   183  				return
   184  			}
   185  			res = append(res, b)
   186  		}
   187  		*values = res
   188  	default:
   189  		f.err = clearSlice(slicePtr)
   190  	}
   191  }
   192  
   193  // Err returns nil unless an error occurred while accessing a parsed form value.
   194  // Calling this method will return the last error that occurred while parsing
   195  // form values.
   196  func (f *Form) Err() error {
   197  	return f.err
   198  }
   199  
   200  // MultipartForm extends a parsed multipart form, part of the body of a
   201  // PATCH, POST or PUT request. A multipart form can include both form values and
   202  // file uploads, stored either in memory or on disk.
   203  type MultipartForm struct {
   204  	Form
   205  	mf *multipart.Form
   206  }
   207  
   208  // newMultipartForm constructs a new MultipartForm with sanitized values.
   209  func newMulipartForm(mf *multipart.Form) *MultipartForm {
   210  	return &MultipartForm{
   211  		Form: Form{
   212  			values: mf.Value,
   213  		},
   214  		mf: sanitizeFilenames(mf),
   215  	}
   216  }
   217  
   218  // sanitizeFilenames removes trailing path separators from all file names.
   219  // This is to ensure that uploaded files are not stored outside of the
   220  // designated directory.
   221  func sanitizeFilenames(f *multipart.Form) *multipart.Form {
   222  	for _, f := range f.File {
   223  		for _, fh := range f {
   224  			fh.Filename = filepath.Base(fh.Filename)
   225  		}
   226  	}
   227  	return f
   228  }
   229  
   230  // File returns the file parts associated with form key param or a nil
   231  // slice, if none. These can be then opened individually by calling
   232  // FileHeader.Open.
   233  func (f *MultipartForm) File(param string) []*multipart.FileHeader {
   234  	fh, ok := f.mf.File[param]
   235  	if !ok {
   236  		return nil
   237  	}
   238  	return fh
   239  }
   240  
   241  // RemoveFiles removes any temporary files associated with a Form and returns
   242  // the first error that occured, if any.
   243  func (f *MultipartForm) RemoveFiles() error {
   244  	return f.mf.RemoveAll()
   245  }