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 }