github.com/khulnasoft-lab/defsec@v1.0.5-0.20230827010352-5e9f46893d95/pkg/scanners/terraform/parser/funcs/encoding.go (about) 1 // Copied from github.com/hashicorp/terraform/internal/lang/funcs 2 package funcs 3 4 import ( 5 "bytes" 6 "compress/gzip" 7 "encoding/base64" 8 "fmt" 9 "log" 10 "net/url" 11 "unicode/utf8" 12 13 "github.com/zclconf/go-cty/cty" 14 "github.com/zclconf/go-cty/cty/function" 15 "golang.org/x/text/encoding/ianaindex" 16 ) 17 18 // Base64DecodeFunc constructs a function that decodes a string containing a base64 sequence. 19 var Base64DecodeFunc = function.New(&function.Spec{ 20 Params: []function.Parameter{ 21 { 22 Name: "str", 23 Type: cty.String, 24 }, 25 }, 26 Type: function.StaticReturnType(cty.String), 27 Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { 28 s := args[0].AsString() 29 sDec, err := base64.StdEncoding.DecodeString(s) 30 if err != nil { 31 return cty.UnknownVal(cty.String), fmt.Errorf("failed to decode base64 data '%s'", s) 32 } 33 if !utf8.Valid([]byte(sDec)) { 34 log.Printf("[DEBUG] the result of decoding the provided string is not valid UTF-8: %s", sDec) 35 return cty.UnknownVal(cty.String), fmt.Errorf("the result of decoding the provided string is not valid UTF-8") 36 } 37 return cty.StringVal(string(sDec)), nil 38 }, 39 }) 40 41 // Base64EncodeFunc constructs a function that encodes a string to a base64 sequence. 42 var Base64EncodeFunc = function.New(&function.Spec{ 43 Params: []function.Parameter{ 44 { 45 Name: "str", 46 Type: cty.String, 47 }, 48 }, 49 Type: function.StaticReturnType(cty.String), 50 Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { 51 return cty.StringVal(base64.StdEncoding.EncodeToString([]byte(args[0].AsString()))), nil 52 }, 53 }) 54 55 // TextEncodeBase64Func constructs a function that encodes a string to a target encoding and then to a base64 sequence. 56 var TextEncodeBase64Func = function.New(&function.Spec{ 57 Params: []function.Parameter{ 58 { 59 Name: "string", 60 Type: cty.String, 61 }, 62 { 63 Name: "encoding", 64 Type: cty.String, 65 }, 66 }, 67 Type: function.StaticReturnType(cty.String), 68 Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { 69 encoding, err := ianaindex.IANA.Encoding(args[1].AsString()) 70 if err != nil || encoding == nil { 71 return cty.UnknownVal(cty.String), function.NewArgErrorf(1, "%q is not a supported IANA encoding name or alias in this Terraform version", args[1].AsString()) 72 } 73 74 encName, err := ianaindex.IANA.Name(encoding) 75 if err != nil { // would be weird, since we just read this encoding out 76 encName = args[1].AsString() 77 } 78 79 encoder := encoding.NewEncoder() 80 encodedInput, err := encoder.Bytes([]byte(args[0].AsString())) 81 if err != nil { 82 // The string representations of "err" disclose implementation 83 // details of the underlying library, and the main error we might 84 // like to return a special message for is unexported as 85 // golang.org/x/text/encoding/internal.RepertoireError, so this 86 // is just a generic error message for now. 87 // 88 // We also don't include the string itself in the message because 89 // it can typically be very large, contain newline characters, 90 // etc. 91 return cty.UnknownVal(cty.String), function.NewArgErrorf(0, "the given string contains characters that cannot be represented in %s", encName) 92 } 93 94 return cty.StringVal(base64.StdEncoding.EncodeToString(encodedInput)), nil 95 }, 96 }) 97 98 // TextDecodeBase64Func constructs a function that decodes a base64 sequence to a target encoding. 99 var TextDecodeBase64Func = function.New(&function.Spec{ 100 Params: []function.Parameter{ 101 { 102 Name: "source", 103 Type: cty.String, 104 }, 105 { 106 Name: "encoding", 107 Type: cty.String, 108 }, 109 }, 110 Type: function.StaticReturnType(cty.String), 111 Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { 112 encoding, err := ianaindex.IANA.Encoding(args[1].AsString()) 113 if err != nil || encoding == nil { 114 return cty.UnknownVal(cty.String), function.NewArgErrorf(1, "%q is not a supported IANA encoding name or alias in this Terraform version", args[1].AsString()) 115 } 116 117 encName, err := ianaindex.IANA.Name(encoding) 118 if err != nil { // would be weird, since we just read this encoding out 119 encName = args[1].AsString() 120 } 121 122 s := args[0].AsString() 123 sDec, err := base64.StdEncoding.DecodeString(s) 124 if err != nil { 125 switch err := err.(type) { 126 case base64.CorruptInputError: 127 return cty.UnknownVal(cty.String), function.NewArgErrorf(0, "the given value is has an invalid base64 symbol at offset %d", int(err)) 128 default: 129 return cty.UnknownVal(cty.String), function.NewArgErrorf(0, "invalid source string: %T", err) 130 } 131 132 } 133 134 decoder := encoding.NewDecoder() 135 decoded, err := decoder.Bytes(sDec) 136 if err != nil || bytes.ContainsRune(decoded, '�') { 137 return cty.UnknownVal(cty.String), function.NewArgErrorf(0, "the given string contains symbols that are not defined for %s", encName) 138 } 139 140 return cty.StringVal(string(decoded)), nil 141 }, 142 }) 143 144 // Base64GzipFunc constructs a function that compresses a string with gzip and then encodes the result in 145 // Base64 encoding. 146 var Base64GzipFunc = function.New(&function.Spec{ 147 Params: []function.Parameter{ 148 { 149 Name: "str", 150 Type: cty.String, 151 }, 152 }, 153 Type: function.StaticReturnType(cty.String), 154 Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { 155 s := args[0].AsString() 156 157 var b bytes.Buffer 158 gz := gzip.NewWriter(&b) 159 if _, err := gz.Write([]byte(s)); err != nil { 160 return cty.UnknownVal(cty.String), fmt.Errorf("failed to write gzip raw data: '%s'", s) 161 } 162 if err := gz.Flush(); err != nil { 163 return cty.UnknownVal(cty.String), fmt.Errorf("failed to flush gzip writer: '%s'", s) 164 } 165 if err := gz.Close(); err != nil { 166 return cty.UnknownVal(cty.String), fmt.Errorf("failed to close gzip writer: '%s'", s) 167 } 168 return cty.StringVal(base64.StdEncoding.EncodeToString(b.Bytes())), nil 169 }, 170 }) 171 172 // URLEncodeFunc constructs a function that applies URL encoding to a given string. 173 var URLEncodeFunc = function.New(&function.Spec{ 174 Params: []function.Parameter{ 175 { 176 Name: "str", 177 Type: cty.String, 178 }, 179 }, 180 Type: function.StaticReturnType(cty.String), 181 Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { 182 return cty.StringVal(url.QueryEscape(args[0].AsString())), nil 183 }, 184 }) 185 186 // Base64Decode decodes a string containing a base64 sequence. 187 // 188 // Terraform uses the "standard" Base64 alphabet as defined in RFC 4648 section 4. 189 // 190 // Strings in the Terraform language are sequences of unicode characters rather 191 // than bytes, so this function will also interpret the resulting bytes as 192 // UTF-8. If the bytes after Base64 decoding are _not_ valid UTF-8, this function 193 // produces an error. 194 func Base64Decode(str cty.Value) (cty.Value, error) { 195 return Base64DecodeFunc.Call([]cty.Value{str}) 196 } 197 198 // Base64Encode applies Base64 encoding to a string. 199 // 200 // Terraform uses the "standard" Base64 alphabet as defined in RFC 4648 section 4. 201 // 202 // Strings in the Terraform language are sequences of unicode characters rather 203 // than bytes, so this function will first encode the characters from the string 204 // as UTF-8, and then apply Base64 encoding to the result. 205 func Base64Encode(str cty.Value) (cty.Value, error) { 206 return Base64EncodeFunc.Call([]cty.Value{str}) 207 } 208 209 // Base64Gzip compresses a string with gzip and then encodes the result in 210 // Base64 encoding. 211 // 212 // Terraform uses the "standard" Base64 alphabet as defined in RFC 4648 section 4. 213 // 214 // Strings in the Terraform language are sequences of unicode characters rather 215 // than bytes, so this function will first encode the characters from the string 216 // as UTF-8, then apply gzip compression, and then finally apply Base64 encoding. 217 func Base64Gzip(str cty.Value) (cty.Value, error) { 218 return Base64GzipFunc.Call([]cty.Value{str}) 219 } 220 221 // URLEncode applies URL encoding to a given string. 222 // 223 // This function identifies characters in the given string that would have a 224 // special meaning when included as a query string argument in a URL and 225 // escapes them using RFC 3986 "percent encoding". 226 // 227 // If the given string contains non-ASCII characters, these are first encoded as 228 // UTF-8 and then percent encoding is applied separately to each UTF-8 byte. 229 func URLEncode(str cty.Value) (cty.Value, error) { 230 return URLEncodeFunc.Call([]cty.Value{str}) 231 } 232 233 // TextEncodeBase64 applies Base64 encoding to a string that was encoded before with a target encoding. 234 // 235 // Terraform uses the "standard" Base64 alphabet as defined in RFC 4648 section 4. 236 // 237 // First step is to apply the target IANA encoding (e.g. UTF-16LE). 238 // Strings in the Terraform language are sequences of unicode characters rather 239 // than bytes, so this function will first encode the characters from the string 240 // as UTF-8, and then apply Base64 encoding to the result. 241 func TextEncodeBase64(str, enc cty.Value) (cty.Value, error) { 242 return TextEncodeBase64Func.Call([]cty.Value{str, enc}) 243 } 244 245 // TextDecodeBase64 decodes a string containing a base64 sequence whereas a specific encoding of the string is expected. 246 // 247 // Terraform uses the "standard" Base64 alphabet as defined in RFC 4648 section 4. 248 // 249 // Strings in the Terraform language are sequences of unicode characters rather 250 // than bytes, so this function will also interpret the resulting bytes as 251 // the target encoding. 252 func TextDecodeBase64(str, enc cty.Value) (cty.Value, error) { 253 return TextDecodeBase64Func.Call([]cty.Value{str, enc}) 254 }