From eb1d8b79098bb64580f47fbc881289c633be1b28 Mon Sep 17 00:00:00 2001 From: Kristin Laemmert Date: Wed, 23 May 2018 09:38:12 -0700 Subject: [PATCH] cleanup comments for nicer godocs --- lang/funcs/crypto.go | 129 ++++++++++++++++++-- lang/funcs/crypto_test.go | 220 ++++++++++++++++++++++++++++++++-- lang/funcs/datetime.go | 12 +- lang/funcs/datetime_test.go | 6 +- lang/funcs/encoding.go | 17 ++- lang/funcs/encoding_test.go | 24 ++-- lang/funcs/filesystem.go | 6 +- lang/funcs/filesystem_test.go | 30 ++--- lang/functions.go | 7 +- 9 files changed, 368 insertions(+), 83 deletions(-) diff --git a/lang/funcs/crypto.go b/lang/funcs/crypto.go index 1e1fef65a..a491f5442 100644 --- a/lang/funcs/crypto.go +++ b/lang/funcs/crypto.go @@ -1,9 +1,15 @@ package funcs import ( + "crypto/md5" + "crypto/rsa" + "crypto/sha1" "crypto/sha256" "crypto/sha512" + "crypto/x509" "encoding/base64" + "encoding/hex" + "encoding/pem" "fmt" uuid "github.com/hashicorp/go-uuid" @@ -25,8 +31,8 @@ var UUIDFunc = function.New(&function.Spec{ }, }) -// Base64Sha256Func constructs a function that computes the SHA256 hash of a given string and encodes it with -// Base64. +// Base64Sha256Func constructs a function that computes the SHA256 hash of a given string +// and encodes it with Base64. var Base64Sha256Func = function.New(&function.Spec{ Params: []function.Parameter{ { @@ -44,8 +50,8 @@ var Base64Sha256Func = function.New(&function.Spec{ }, }) -// Base64Sha512Func constructs a function that computes the SHA256 hash of a given string and encodes it with -// Base64. +// Base64Sha512Func constructs a function that computes the SHA256 hash of a given string +// and encodes it with Base64. var Base64Sha512Func = function.New(&function.Spec{ Params: []function.Parameter{ { @@ -101,6 +107,89 @@ var BcryptFunc = function.New(&function.Spec{ }, }) +// Md5Func constructs a function that computes the MD5 hash of a given string and encodes it with hexadecimal digits. +var Md5Func = function.New(&function.Spec{ + Params: []function.Parameter{ + { + Name: "str", + Type: cty.String, + }, + }, + Type: function.StaticReturnType(cty.String), + Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { + s := args[0].AsString() + h := md5.New() + h.Write([]byte(s)) + hash := hex.EncodeToString(h.Sum(nil)) + return cty.StringVal(hash), nil + }, +}) + +// RsaDecryptFunc constructs a function that decrypts an RSA-encrypted ciphertext. +var RsaDecryptFunc = function.New(&function.Spec{ + Params: []function.Parameter{ + { + Name: "ciphertext", + Type: cty.String, + }, + { + Name: "privatekey", + Type: cty.String, + }, + }, + Type: function.StaticReturnType(cty.String), + Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { + s := args[0].AsString() + key := args[1].AsString() + + b, err := base64.StdEncoding.DecodeString(s) + if err != nil { + return cty.UnknownVal(cty.String), fmt.Errorf("Failed to decode input %q: cipher text must be base64-encoded", key) + } + + block, _ := pem.Decode([]byte(key)) + if block == nil { + return cty.UnknownVal(cty.String), fmt.Errorf("Failed to read key %q: no key found", key) + } + if block.Headers["Proc-Type"] == "4,ENCRYPTED" { + return cty.UnknownVal(cty.String), fmt.Errorf( + "Failed to read key %q: password protected keys are\n"+ + "not supported. Please decrypt the key prior to use.", key) + } + + x509Key, err := x509.ParsePKCS1PrivateKey(block.Bytes) + if err != nil { + return cty.UnknownVal(cty.String), err + } + + out, err := rsa.DecryptPKCS1v15(nil, x509Key, b) + if err != nil { + return cty.UnknownVal(cty.String), err + } + + return cty.StringVal(string(out)), nil + }, +}) + +// Sha1Func contructs a function that computes the SHA1 hash of a given string +// and encodes it with hexadecimal digits. +var Sha1Func = function.New(&function.Spec{ + Params: []function.Parameter{ + { + Name: "str", + Type: cty.String, + }, + }, + Type: function.StaticReturnType(cty.String), + Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { + s := args[0].AsString() + h := sha1.New() + h.Write([]byte(s)) + hash := hex.EncodeToString(h.Sum(nil)) + return cty.StringVal(hash), nil + }, +}) + // UUID generates and returns a Type-4 UUID in the standard hexadecimal string // format. // @@ -111,30 +200,28 @@ func UUID() (cty.Value, error) { return UUIDFunc.Call(nil) } -// Base64sha256 computes the SHA256 hash of a given string and encodes it with +// Base64Sha256 computes the SHA256 hash of a given string and encodes it with // Base64. // // The given string is first encoded as UTF-8 and then the SHA256 algorithm is applied -// as defined in [RFC 4634](https://tools.ietf.org/html/rfc4634). The raw hash is -// then encoded with Base64 before returning. Terraform uses the "standard" Base64 -// alphabet as defined in [RFC 4648 section 4](https://tools.ietf.org/html/rfc4648#section-4). +// as defined in RFC 4634. The raw hash is then encoded with Base64 before returning. +// Terraform uses the "standard" Base64 alphabet as defined in RFC 4648 section 4. func Base64Sha256(str cty.Value) (cty.Value, error) { return Base64Sha256Func.Call([]cty.Value{str}) } -// Base64sha512 computes the SHA512 hash of a given string and encodes it with +// Base64Sha512 computes the SHA512 hash of a given string and encodes it with // Base64. // // The given string is first encoded as UTF-8 and then the SHA256 algorithm is applied -// as defined in [RFC 4634](https://tools.ietf.org/html/rfc4634). The raw hash is -// then encoded with Base64 before returning. Terraform uses the "standard" Base64 -// alphabet as defined in [RFC 4648 section 4](https://tools.ietf.org/html/rfc4648#section-4). +// as defined in RFC 4634. The raw hash is then encoded with Base64 before returning. +// Terraform uses the "standard" Base64 alphabet as defined in RFC 4648 section 4 func Base64Sha512(str cty.Value) (cty.Value, error) { return Base64Sha512Func.Call([]cty.Value{str}) } // Bcrypt computes a hash of the given string using the Blowfish cipher, -// returning a string in the Modular Crypt Format(https://passlib.readthedocs.io/en/stable/modular_crypt_format.html) +// returning a string in the Modular Crypt Format // usually expected in the shadow password file on many Unix systems. func Bcrypt(str cty.Value, cost ...cty.Value) (cty.Value, error) { args := make([]cty.Value, len(cost)+1) @@ -142,3 +229,19 @@ func Bcrypt(str cty.Value, cost ...cty.Value) (cty.Value, error) { copy(args[1:], cost) return BcryptFunc.Call(args) } + +// Md5 computes the MD5 hash of a given string and encodes it with hexadecimal digits. +func Md5(str cty.Value) (cty.Value, error) { + return Md5Func.Call([]cty.Value{str}) +} + +// RsaDecrypt decrypts an RSA-encrypted ciphertext, returning the corresponding +// cleartext. +func RsaDecrypt(ciphertext, privatekey cty.Value) (cty.Value, error) { + return RsaDecryptFunc.Call([]cty.Value{ciphertext, privatekey}) +} + +// Sha1 computes the SHA1 hash of a given string and encodes it with hexadecimal digits. +func Sha1(str cty.Value) (cty.Value, error) { + return Sha1Func.Call([]cty.Value{str}) +} diff --git a/lang/funcs/crypto_test.go b/lang/funcs/crypto_test.go index 1d3506901..8dcd404b0 100644 --- a/lang/funcs/crypto_test.go +++ b/lang/funcs/crypto_test.go @@ -45,10 +45,8 @@ func TestBase64Sha256(t *testing.T) { t.Fatal("succeeded; want error") } return - } else { - if err != nil { - t.Fatalf("unexpected error: %s", err) - } + } else if err != nil { + t.Fatalf("unexpected error: %s", err) } if !got.RawEquals(test.Want) { @@ -83,10 +81,8 @@ func TestBase64Sha512(t *testing.T) { t.Fatal("succeeded; want error") } return - } else { - if err != nil { - t.Fatalf("unexpected error: %s", err) - } + } else if err != nil { + t.Fatalf("unexpected error: %s", err) } if !got.RawEquals(test.Want) { @@ -125,3 +121,211 @@ func TestBcrypt(t *testing.T) { t.Fatal("succeeded; want error") } } + +func TestMd5(t *testing.T) { + tests := []struct { + String cty.Value + Want cty.Value + Err bool + }{ + { + cty.StringVal("tada"), + cty.StringVal("ce47d07243bb6eaf5e1322c81baf9bbf"), + false, + }, + { // Confirm that we're not trimming any whitespaces + cty.StringVal(" tada "), + cty.StringVal("aadf191a583e53062de2d02c008141c4"), + false, + }, + { // We accept empty string too + cty.StringVal(""), + cty.StringVal("d41d8cd98f00b204e9800998ecf8427e"), + false, + }, + } + + for _, test := range tests { + t.Run(fmt.Sprintf("md5(%#v)", test.String), func(t *testing.T) { + got, err := Md5(test.String) + + if test.Err { + if err == nil { + t.Fatal("succeeded; want error") + } + return + } else if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + if !got.RawEquals(test.Want) { + t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) + } + }) + } +} + +func TestRsaDecrypt(t *testing.T) { + tests := []struct { + Ciphertext cty.Value + Privatekey cty.Value + Want cty.Value + Err bool + }{ + // Base-64 encoded cipher decrypts correctly + { + cty.StringVal(CipherBase64), + cty.StringVal(PrivateKey), + cty.StringVal("message"), + false, + }, + // Wrong key + { + cty.StringVal(CipherBase64), + cty.StringVal(WrongPrivateKey), + cty.UnknownVal(cty.String), + true, + }, + // Bad key + { + cty.StringVal(CipherBase64), + cty.StringVal("bad key"), + cty.UnknownVal(cty.String), + true, + }, + // Empty key + { + cty.StringVal(CipherBase64), + cty.StringVal(""), + cty.UnknownVal(cty.String), + true, + }, + // Bad cipher + { + cty.StringVal("bad cipher"), + cty.StringVal(PrivateKey), + cty.UnknownVal(cty.String), + true, + }, + // Empty cipher + { + cty.StringVal(""), + cty.StringVal(PrivateKey), + cty.UnknownVal(cty.String), + true, + }, + } + for _, test := range tests { + t.Run(fmt.Sprintf("RsaDecrypt(%#v, %#v)", test.Ciphertext, test.Privatekey), func(t *testing.T) { + got, err := RsaDecrypt(test.Ciphertext, test.Privatekey) + + if test.Err { + if err == nil { + t.Fatal("succeeded; want error") + } + return + } else if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + if !got.RawEquals(test.Want) { + t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) + } + }) + } +} + +func TestSha1(t *testing.T) { + tests := []struct { + String cty.Value + Want cty.Value + Err bool + }{ + { + cty.StringVal("test"), + cty.StringVal("a94a8fe5ccb19ba61c4c0873d391e987982fbbd3"), + false, + }, + } + + for _, test := range tests { + t.Run(fmt.Sprintf("sha1(%#v)", test.String), func(t *testing.T) { + got, err := Sha1(test.String) + + if test.Err { + if err == nil { + t.Fatal("succeeded; want error") + } + return + } else if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + if !got.RawEquals(test.Want) { + t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) + } + }) + } +} + +const ( + CipherBase64 = "eczGaDhXDbOFRZGhjx2etVzWbRqWDlmq0bvNt284JHVbwCgObiuyX9uV0LSAMY707IEgMkExJqXmsB4OWKxvB7epRB9G/3+F+pcrQpODlDuL9oDUAsa65zEpYF0Wbn7Oh7nrMQncyUPpyr9WUlALl0gRWytOA23S+y5joa4M34KFpawFgoqTu/2EEH4Xl1zo+0fy73fEto+nfkUY+meuyGZ1nUx/+DljP7ZqxHBFSlLODmtuTMdswUbHbXbWneW51D7Jm7xB8nSdiA2JQNK5+Sg5x8aNfgvFTt/m2w2+qpsyFa5Wjeu6fZmXSl840CA07aXbk9vN4I81WmJyblD/ZA==" + PrivateKey = ` +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAgUElV5mwqkloIrM8ZNZ72gSCcnSJt7+/Usa5G+D15YQUAdf9 +c1zEekTfHgDP+04nw/uFNFaE5v1RbHaPxhZYVg5ZErNCa/hzn+x10xzcepeS3KPV +Xcxae4MR0BEegvqZqJzN9loXsNL/c3H/B+2Gle3hTxjlWFb3F5qLgR+4Mf4ruhER +1v6eHQa/nchi03MBpT4UeJ7MrL92hTJYLdpSyCqmr8yjxkKJDVC2uRrr+sTSxfh7 +r6v24u/vp/QTmBIAlNPgadVAZw17iNNb7vjV7Gwl/5gHXonCUKURaV++dBNLrHIZ +pqcAM8wHRph8mD1EfL9hsz77pHewxolBATV+7QIDAQABAoIBAC1rK+kFW3vrAYm3 ++8/fQnQQw5nec4o6+crng6JVQXLeH32qXShNf8kLLG/Jj0vaYcTPPDZw9JCKkTMQ +0mKj9XR/5DLbBMsV6eNXXuvJJ3x4iKW5eD9WkLD4FKlNarBRyO7j8sfPTqXW7uat +NxWdFH7YsSRvNh/9pyQHLWA5OituidMrYbc3EUx8B1GPNyJ9W8Q8znNYLfwYOjU4 +Wv1SLE6qGQQH9Q0WzA2WUf8jklCYyMYTIywAjGb8kbAJlKhmj2t2Igjmqtwt1PYc +pGlqbtQBDUiWXt5S4YX/1maIQ/49yeNUajjpbJiH3DbhJbHwFTzP3pZ9P9GHOzlG +kYR+wSECgYEAw/Xida8kSv8n86V3qSY/I+fYQ5V+jDtXIE+JhRnS8xzbOzz3v0WS +Oo5H+o4nJx5eL3Ghb3Gcm0Jn46dHrxinHbm+3RjXv/X6tlbxIYjRSQfHOTSMCTvd +qcliF5vC6RCLXuc7R+IWR1Ky6eDEZGtrvt3DyeYABsp9fRUFR/6NluUCgYEAqNsw +1aSl7WJa27F0DoJdlU9LWerpXcazlJcIdOz/S9QDmSK3RDQTdqfTxRmrxiYI9LEs +mkOkvzlnnOBMpnZ3ZOU5qIRfprecRIi37KDAOHWGnlC0EWGgl46YLb7/jXiWf0AG +Y+DfJJNd9i6TbIDWu8254/erAS6bKMhW/3q7f2kCgYAZ7Id/BiKJAWRpqTRBXlvw +BhXoKvjI2HjYP21z/EyZ+PFPzur/lNaZhIUlMnUfibbwE9pFggQzzf8scM7c7Sf+ +mLoVSdoQ/Rujz7CqvQzi2nKSsM7t0curUIb3lJWee5/UeEaxZcmIufoNUrzohAWH +BJOIPDM4ssUTLRq7wYM9uQKBgHCBau5OP8gE6mjKuXsZXWUoahpFLKwwwmJUp2vQ +pOFPJ/6WZOlqkTVT6QPAcPUbTohKrF80hsZqZyDdSfT3peFx4ZLocBrS56m6NmHR +UYHMvJ8rQm76T1fryHVidz85g3zRmfBeWg8yqT5oFg4LYgfLsPm1gRjOhs8LfPvI +OLlRAoGBAIZ5Uv4Z3s8O7WKXXUe/lq6j7vfiVkR1NW/Z/WLKXZpnmvJ7FgxN4e56 +RXT7GwNQHIY8eDjDnsHxzrxd+raOxOZeKcMHj3XyjCX3NHfTscnsBPAGYpY/Wxzh +T8UYnFu6RzkixElTf2rseEav7rkdKkI3LAeIZy7B0HulKKsmqVQ7 +-----END RSA PRIVATE KEY----- +` + WrongPrivateKey = ` +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAlrCgnEVgmNKCq7KPc+zUU5IrxPu1ClMNJS7RTsTPEkbwe5SB +p+6V6WtCbD/X/lDRRGbOENChh1Phulb7lViqgrdpHydgsrKoS5ah3DfSIxLFLE00 +9Yo4TCYwgw6+s59j16ZAFVinaQ9l6Kmrb2ll136hMrz8QKh+qw+onOLd38WFgm+W +ZtUqSXf2LANzfzzy4OWFNyFqKaCAolSkPdTS9Nz+svtScvp002DQp8OdP1AgPO+l +o5N3M38Fftapwg0pCtJ5Zq0NRWIXEonXiTEMA6zy3gEZVOmDxoIFUWnmrqlMJLFy +5S6LDrHSdqJhCxDK6WRZj43X9j8spktk3eGhMwIDAQABAoIBAAem8ID/BOi9x+Tw +LFi2rhGQWqimH4tmrEQ3HGnjlKBY+d1MrUjZ1MMFr1nP5CgF8pqGnfA8p/c3Sz8r +K5tp5T6+EZiDZ2WrrOApxg5ox0MAsQKO6SGO40z6o3wEQ6rbbTaGOrraxaWQIpyu +AQanU4Sd6ZGqByVBaS1GnklZO+shCHqw73b7g1cpLEmFzcYnKHYHlUUIsstMe8E1 +BaCY0CH7JbWBjcbiTnBVwIRZuu+EjGiQuhTilYL2OWqoMVg1WU0L2IFpR8lkf/2W +SBx5J6xhwbBGASOpM+qidiN580GdPzGhWYSqKGroHEzBm6xPSmV1tadNA26WFG4p +pthLiAECgYEA5BsPRpNYJAQLu5B0N7mj9eEp0HABVEgL/MpwiImjaKdAwp78HM64 +IuPvJxs7r+xESiIz4JyjR8zrQjYOCKJsARYkmNlEuAz0SkHabCw1BdEBwUhjUGVB +efoERK6GxfAoNqmSDwsOvHFOtsmDIlbHmg7G2rUxNVpeou415BSB0B8CgYEAqR4J +YHKk2Ibr9rU+rBU33TcdTGw0aAkFNAVeqM9j0haWuFXmV3RArgoy09lH+2Ha6z/g +fTX2xSDAWV7QUlLOlBRIhurPAo2jO2yCrGHPZcWiugstrR2hTTInigaSnCmK3i7F +6sYmL3S7K01IcVNxSlWvGijtClT92Cl2WUCTfG0CgYAiEjyk4QtQTd5mxLvnOu5X +oqs5PBGmwiAwQRiv/EcRMbJFn7Oupd3xMDSflbzDmTnWDOfMy/jDl8MoH6TW+1PA +kcsjnYhbKWwvz0hN0giVdtOZSDO1ZXpzOrn6fEsbM7T9/TQY1SD9WrtUKCNTNL0Z +sM1ZC6lu+7GZCpW4HKwLJwKBgQCRT0yxQXBg1/UxwuO5ynV4rx2Oh76z0WRWIXMH +S0MyxdP1SWGkrS/SGtM3cg/GcHtA/V6vV0nUcWK0p6IJyjrTw2XZ/zGluPuTWJYi +9dvVT26Vunshrz7kbH7KuwEICy3V4IyQQHeY+QzFlR70uMS0IVFWAepCoWqHbIDT +CYhwNQKBgGPcLXmjpGtkZvggl0aZr9LsvCTckllSCFSI861kivL/rijdNoCHGxZv +dfDkLTLcz9Gk41rD9Gxn/3sqodnTAc3Z2PxFnzg1Q/u3+x6YAgBwI/g/jE2xutGW +H7CurtMwALQ/n/6LUKFmjRZjqbKX9SO2QSaC3grd6sY9Tu+bZjLe +-----END RSA PRIVATE KEY----- +` +) diff --git a/lang/funcs/datetime.go b/lang/funcs/datetime.go index cef5e763e..5dae19877 100644 --- a/lang/funcs/datetime.go +++ b/lang/funcs/datetime.go @@ -46,19 +46,17 @@ var TimeAddFunc = function.New(&function.Spec{ // Timestamp returns a string representation of the current date and time. // // In the Terraform language, timestamps are conventionally represented as -// strings using [RFC 3339](https://tools.ietf.org/html/rfc3339) -// "Date and Time format" syntax, and so `timestamp` returns a string -// in this format. +// strings using RFC 3339 "Date and Time format" syntax, and so timestamp +// returns a string in this format. func Timestamp() (cty.Value, error) { return TimestampFunc.Call([]cty.Value{}) } -// Timeadd adds a duration to a timestamp, returning a new timestamp. +// TimeAdd adds a duration to a timestamp, returning a new timestamp. // // In the Terraform language, timestamps are conventionally represented as -// strings using [RFC 3339](https://tools.ietf.org/html/rfc3339) -// "Date and Time format" syntax. `timeadd` requires the `timestamp` argument -// to be a string conforming to this syntax. +// strings using RFC 3339 "Date and Time format" syntax. Timeadd requires +// the timestamp argument to be a string conforming to this syntax. // // `duration` is a string representation of a time difference, consisting of // sequences of number and unit pairs, like `"1.5h"` or `1h30m`. The accepted diff --git a/lang/funcs/datetime_test.go b/lang/funcs/datetime_test.go index bc40eeddc..6ba4b1ed8 100644 --- a/lang/funcs/datetime_test.go +++ b/lang/funcs/datetime_test.go @@ -73,10 +73,8 @@ func TestTimeadd(t *testing.T) { t.Fatal("succeeded; want error") } return - } else { - if err != nil { - t.Fatalf("unexpected error: %s", err) - } + } else if err != nil { + t.Fatalf("unexpected error: %s", err) } if !got.RawEquals(test.Want) { diff --git a/lang/funcs/encoding.go b/lang/funcs/encoding.go index bc594ce5c..f59fcf32b 100644 --- a/lang/funcs/encoding.go +++ b/lang/funcs/encoding.go @@ -89,8 +89,7 @@ var URLEncodeFunc = function.New(&function.Spec{ // Base64Decode decodes a string containing a base64 sequence. // -// Terraform uses the "standard" Base64 alphabet as defined in -// [RFC 4648 section 4](https://tools.ietf.org/html/rfc4648#section-4). +// Terraform uses the "standard" Base64 alphabet as defined in RFC 4648 section 4. // // Strings in the Terraform language are sequences of unicode characters rather // than bytes, so this function will also interpret the resulting bytes as @@ -102,8 +101,7 @@ func Base64Decode(str cty.Value) (cty.Value, error) { // Base64Encode applies Base64 encoding to a string. // -// Terraform uses the "standard" Base64 alphabet as defined in -// [RFC 4648 section 4](https://tools.ietf.org/html/rfc4648#section-4). +// Terraform uses the "standard" Base64 alphabet as defined in RFC 4648 section 4. // // Strings in the Terraform language are sequences of unicode characters rather // than bytes, so this function will first encode the characters from the string @@ -112,11 +110,11 @@ func Base64Encode(str cty.Value) (cty.Value, error) { return Base64EncodeFunc.Call([]cty.Value{str}) } -// Base64gzip compresses a string with gzip and then encodes the result in +// Base64Gzip compresses a string with gzip and then encodes the result in // Base64 encoding. // -// Terraform uses the "standard" Base64 alphabet as defined in -// [RFC 4648 section 4](https://tools.ietf.org/html/rfc4648#section-4) +// Terraform uses the "standard" Base64 alphabet as defined in RFC 4648 section 4. +// // Strings in the Terraform language are sequences of unicode characters rather // than bytes, so this function will first encode the characters from the string // as UTF-8, then apply gzip compression, and then finally apply Base64 encoding. @@ -124,12 +122,11 @@ func Base64Gzip(str cty.Value) (cty.Value, error) { return Base64GzipFunc.Call([]cty.Value{str}) } -// UrlEncode applies URL encoding to a given string. +// URLEncode applies URL encoding to a given string. // // This function identifies characters in the given string that would have a // special meaning when included as a query string argument in a URL and -// escapes them using -// [RFC 3986 "percent encoding"](https://tools.ietf.org/html/rfc3986#section-2.1). +// escapes them using RFC 3986 "percent encoding". // // If the given string contains non-ASCII characters, these are first encoded as // UTF-8 and then percent encoding is applied separately to each UTF-8 byte. diff --git a/lang/funcs/encoding_test.go b/lang/funcs/encoding_test.go index 711ab2a41..fb71de726 100644 --- a/lang/funcs/encoding_test.go +++ b/lang/funcs/encoding_test.go @@ -34,10 +34,8 @@ func TestBase64Decode(t *testing.T) { t.Fatal("succeeded; want error") } return - } else { - if err != nil { - t.Fatalf("unexpected error: %s", err) - } + } else if err != nil { + t.Fatalf("unexpected error: %s", err) } if !got.RawEquals(test.Want) { @@ -69,10 +67,8 @@ func TestBase64Encode(t *testing.T) { t.Fatal("succeeded; want error") } return - } else { - if err != nil { - t.Fatalf("unexpected error: %s", err) - } + } else if err != nil { + t.Fatalf("unexpected error: %s", err) } if !got.RawEquals(test.Want) { @@ -104,10 +100,8 @@ func TestBase64Gzip(t *testing.T) { t.Fatal("succeeded; want error") } return - } else { - if err != nil { - t.Fatalf("unexpected error: %s", err) - } + } else if err != nil { + t.Fatalf("unexpected error: %s", err) } if !got.RawEquals(test.Want) { @@ -154,10 +148,8 @@ func TestURLEncode(t *testing.T) { t.Fatal("succeeded; want error") } return - } else { - if err != nil { - t.Fatalf("unexpected error: %s", err) - } + } else if err != nil { + t.Fatalf("unexpected error: %s", err) } if !got.RawEquals(test.Want) { diff --git a/lang/funcs/filesystem.go b/lang/funcs/filesystem.go index b5a71777f..d3a7c38b7 100644 --- a/lang/funcs/filesystem.go +++ b/lang/funcs/filesystem.go @@ -63,7 +63,8 @@ func MakeFileFunc(baseDir string, encBase64 bool) function.Function { }) } -// BasenameFunc constructs a function that takes a string containing a filesystem path and removes all except the last portion from it. +// BasenameFunc constructs a function that takes a string containing a filesystem path +// and removes all except the last portion from it. var BasenameFunc = function.New(&function.Spec{ Params: []function.Parameter{ { @@ -77,7 +78,8 @@ var BasenameFunc = function.New(&function.Spec{ }, }) -// DirnameFunc constructs a function that takes a string containing a filesystem path and removes the last portion from it. +// DirnameFunc constructs a function that takes a string containing a filesystem path +// and removes the last portion from it. var DirnameFunc = function.New(&function.Spec{ Params: []function.Parameter{ { diff --git a/lang/funcs/filesystem_test.go b/lang/funcs/filesystem_test.go index 970feb696..18f27cc81 100644 --- a/lang/funcs/filesystem_test.go +++ b/lang/funcs/filesystem_test.go @@ -41,10 +41,8 @@ func TestFile(t *testing.T) { t.Fatal("succeeded; want error") } return - } else { - if err != nil { - t.Fatalf("unexpected error: %s", err) - } + } else if err != nil { + t.Fatalf("unexpected error: %s", err) } if !got.RawEquals(test.Want) { @@ -86,10 +84,8 @@ func TestFileBase64(t *testing.T) { t.Fatal("succeeded; want error") } return - } else { - if err != nil { - t.Fatalf("unexpected error: %s", err) - } + } else if err != nil { + t.Fatalf("unexpected error: %s", err) } if !got.RawEquals(test.Want) { @@ -131,10 +127,8 @@ func TestBasename(t *testing.T) { t.Fatal("succeeded; want error") } return - } else { - if err != nil { - t.Fatalf("unexpected error: %s", err) - } + } else if err != nil { + t.Fatalf("unexpected error: %s", err) } if !got.RawEquals(test.Want) { @@ -181,10 +175,8 @@ func TestDirname(t *testing.T) { t.Fatal("succeeded; want error") } return - } else { - if err != nil { - t.Fatalf("unexpected error: %s", err) - } + } else if err != nil { + t.Fatalf("unexpected error: %s", err) } if !got.RawEquals(test.Want) { @@ -236,10 +228,8 @@ func TestPathExpand(t *testing.T) { t.Fatal("succeeded; want error") } return - } else { - if err != nil { - t.Fatalf("unexpected error: %s", err) - } + } else if err != nil { + t.Fatalf("unexpected error: %s", err) } if !got.RawEquals(test.Want) { diff --git a/lang/functions.go b/lang/functions.go index 480143fe6..d28418c8e 100644 --- a/lang/functions.go +++ b/lang/functions.go @@ -11,6 +11,7 @@ import ( ) var impureFunctions = []string{ + "bcrypt", "timestamp", "uuid", } @@ -71,13 +72,13 @@ func (s *Scope) Functions() map[string]function.Function { "lower": stdlib.LowerFunc, "map": unimplFunc, // TODO "max": stdlib.MaxFunc, - "md5": unimplFunc, // TODO + "md5": funcs.Md5Func, "merge": unimplFunc, // TODO "min": stdlib.MinFunc, "pathexpand": funcs.PathExpandFunc, "pow": unimplFunc, // TODO "replace": unimplFunc, // TODO - "rsadecrypt": unimplFunc, // TODO + "rsadecrypt": funcs.RsaDecryptFunc, "sha1": unimplFunc, // TODO "sha256": unimplFunc, // TODO "sha512": unimplFunc, // TODO @@ -92,7 +93,7 @@ func (s *Scope) Functions() map[string]function.Function { "transpose": unimplFunc, // TODO "trimspace": unimplFunc, // TODO "upper": stdlib.UpperFunc, - "urlencode": funcs.UrlEncodeFunc, + "urlencode": funcs.URLEncodeFunc, "uuid": funcs.UUIDFunc, "values": unimplFunc, // TODO "zipmap": unimplFunc, // TODO