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  }