From 954d38e8708a710061a87dc82834bc34bd1d9942 Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Fri, 25 Jan 2019 09:16:43 -0800 Subject: [PATCH] lang: New file-hashing functions In prior versions, we recommended using hash functions in conjunction with the file function as an idiom for detecting changes to upstream blobs without fetching and comparing the whole blob. That approach relied on us being able to return raw binary data from file(...). Since Terraform strings pass through intermediate representations that are not binary-safe (e.g. the JSON state), there was a risk of string corruption in prior versions which we have avoided for 0.12 by requiring that file(...) be used only with UTF-8 text files. The specific case of returning a string and immediately passing it into another function was not actually subject to that corruption risk, since the HIL interpreter would just pass the string through verbatim, but this is still now forbidden as a result of the stricter handling of file(...). To avoid breaking these use-cases, here we introduce variants of the hash functions a with "file" prefix that take a filename for a disk file to hash rather than hashing the given string directly. The configuration upgrade tool also now includes a rule to detect the documented idiom and rewrite it into a single function call for one of these new functions. This does cause a bit of function sprawl, but that seems preferable to introducing more complex rules for when file(...) can and cannot read binary files, making the behavior of these various functions easier to understand in isolation. --- .../valid/hash-of-file/input/hash-of-file.tf | 3 + .../valid/hash-of-file/want/hash-of-file.tf | 3 + .../valid/hash-of-file/want/versions.tf | 3 + configs/configupgrade/upgrade_expr.go | 30 ++ lang/funcs/crypto.go | 60 ++++ lang/funcs/crypto_test.go | 270 ++++++++++++++++++ lang/funcs/filesystem.go | 47 +-- lang/functions.go | 166 +++++------ .../functions/base64sha256.html.md | 2 + .../functions/base64sha512.html.md | 2 + .../functions/filebase64sha256.html.md | 17 ++ .../functions/filebase64sha512.html.md | 17 ++ .../configuration/functions/filemd5.html.md | 17 ++ .../configuration/functions/filesha1.html.md | 17 ++ .../functions/filesha256.html.md | 17 ++ .../functions/filesha512.html.md | 17 ++ .../docs/configuration/functions/md5.html.md | 5 + .../docs/configuration/functions/sha1.html.md | 5 + .../configuration/functions/sha256.html.md | 5 + .../configuration/functions/sha512.html.md | 2 + website/layouts/functions.erb | 24 ++ 21 files changed, 630 insertions(+), 99 deletions(-) create mode 100644 configs/configupgrade/test-fixtures/valid/hash-of-file/input/hash-of-file.tf create mode 100644 configs/configupgrade/test-fixtures/valid/hash-of-file/want/hash-of-file.tf create mode 100644 configs/configupgrade/test-fixtures/valid/hash-of-file/want/versions.tf create mode 100644 website/docs/configuration/functions/filebase64sha256.html.md create mode 100644 website/docs/configuration/functions/filebase64sha512.html.md create mode 100644 website/docs/configuration/functions/filemd5.html.md create mode 100644 website/docs/configuration/functions/filesha1.html.md create mode 100644 website/docs/configuration/functions/filesha256.html.md create mode 100644 website/docs/configuration/functions/filesha512.html.md diff --git a/configs/configupgrade/test-fixtures/valid/hash-of-file/input/hash-of-file.tf b/configs/configupgrade/test-fixtures/valid/hash-of-file/input/hash-of-file.tf new file mode 100644 index 000000000..3b4bf3379 --- /dev/null +++ b/configs/configupgrade/test-fixtures/valid/hash-of-file/input/hash-of-file.tf @@ -0,0 +1,3 @@ +resource "test_instance" "foo" { + image = "${sha256(file("foo.txt"))}" +} diff --git a/configs/configupgrade/test-fixtures/valid/hash-of-file/want/hash-of-file.tf b/configs/configupgrade/test-fixtures/valid/hash-of-file/want/hash-of-file.tf new file mode 100644 index 000000000..00672b75d --- /dev/null +++ b/configs/configupgrade/test-fixtures/valid/hash-of-file/want/hash-of-file.tf @@ -0,0 +1,3 @@ +resource "test_instance" "foo" { + image = filesha256("foo.txt") +} diff --git a/configs/configupgrade/test-fixtures/valid/hash-of-file/want/versions.tf b/configs/configupgrade/test-fixtures/valid/hash-of-file/want/versions.tf new file mode 100644 index 000000000..d9b6f790b --- /dev/null +++ b/configs/configupgrade/test-fixtures/valid/hash-of-file/want/versions.tf @@ -0,0 +1,3 @@ +terraform { + required_version = ">= 0.12" +} diff --git a/configs/configupgrade/upgrade_expr.go b/configs/configupgrade/upgrade_expr.go index 062500585..ba823b6f9 100644 --- a/configs/configupgrade/upgrade_expr.go +++ b/configs/configupgrade/upgrade_expr.go @@ -221,6 +221,36 @@ Value: name := tv.Func args := tv.Args + // Some adaptations must happen prior to upgrading the arguments, + // because they depend on the original argument AST nodes. + switch name { + case "base64sha256", "base64sha512", "md5", "sha1", "sha256", "sha512": + // These functions were sometimes used in conjunction with the + // file() function to take the hash of the contents of a file. + // Prior to Terraform 0.11 there was a chance of silent corruption + // of strings containing non-UTF8 byte sequences, and so we have + // made it illegal to use file() with non-text files in 0.12 even + // though in this _particular_ situation (passing the function + // result directly to another function) there would not be any + // corruption; the general rule keeps things consistent. + // However, to still meet those use-cases we now have variants of + // the hashing functions that have a "file" prefix on their names + // and read the contents of a given file, rather than hashing + // directly the given string. + if len(args) > 0 { + if subCall, ok := args[0].(*hilast.Call); ok && subCall.Func == "file" { + // We're going to flatten this down into a single call, so + // we actually want the arguments of the sub-call here. + name = "file" + name + args = subCall.Args + + // For this one, we'll fall through to the normal upgrade + // handling now that we've fixed up the name and args... + } + } + + } + argExprs := make([][]byte, len(args)) multiline := false totalLen := 0 diff --git a/lang/funcs/crypto.go b/lang/funcs/crypto.go index 912364cf8..5d17a1d0d 100644 --- a/lang/funcs/crypto.go +++ b/lang/funcs/crypto.go @@ -36,10 +36,22 @@ var UUIDFunc = function.New(&function.Spec{ // and encodes it with Base64. var Base64Sha256Func = makeStringHashFunction(sha256.New, base64.StdEncoding.EncodeToString) +// MakeFileBase64Sha256Func constructs a function that is like Base64Sha256Func but reads the +// contents of a file rather than hashing a given literal string. +func MakeFileBase64Sha256Func(baseDir string) function.Function { + return makeFileHashFunction(baseDir, sha512.New, base64.StdEncoding.EncodeToString) +} + // Base64Sha512Func constructs a function that computes the SHA256 hash of a given string // and encodes it with Base64. var Base64Sha512Func = makeStringHashFunction(sha512.New, base64.StdEncoding.EncodeToString) +// MakeFileBase64Sha512Func constructs a function that is like Base64Sha512Func but reads the +// contents of a file rather than hashing a given literal string. +func MakeFileBase64Sha512Func(baseDir string) function.Function { + return makeFileHashFunction(baseDir, sha512.New, base64.StdEncoding.EncodeToString) +} + // BcryptFunc constructs a function that computes a hash of the given string using the Blowfish cipher. var BcryptFunc = function.New(&function.Spec{ Params: []function.Parameter{ @@ -81,6 +93,12 @@ 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 = makeStringHashFunction(md5.New, hex.EncodeToString) +// MakeFileMd5Func constructs a function that is like Md5Func but reads the +// contents of a file rather than hashing a given literal string. +func MakeFileMd5Func(baseDir string) function.Function { + return makeFileHashFunction(baseDir, md5.New, hex.EncodeToString) +} + // RsaDecryptFunc constructs a function that decrypts an RSA-encrypted ciphertext. var RsaDecryptFunc = function.New(&function.Spec{ Params: []function.Parameter{ @@ -131,14 +149,32 @@ var RsaDecryptFunc = function.New(&function.Spec{ // and encodes it with hexadecimal digits. var Sha1Func = makeStringHashFunction(sha1.New, hex.EncodeToString) +// MakeFileSha1Func constructs a function that is like Sha1Func but reads the +// contents of a file rather than hashing a given literal string. +func MakeFileSha1Func(baseDir string) function.Function { + return makeFileHashFunction(baseDir, sha1.New, hex.EncodeToString) +} + // Sha256Func contructs a function that computes the SHA256 hash of a given string // and encodes it with hexadecimal digits. var Sha256Func = makeStringHashFunction(sha256.New, hex.EncodeToString) +// MakeFileSha256Func constructs a function that is like Sha256Func but reads the +// contents of a file rather than hashing a given literal string. +func MakeFileSha256Func(baseDir string) function.Function { + return makeFileHashFunction(baseDir, sha256.New, hex.EncodeToString) +} + // Sha512Func contructs a function that computes the SHA512 hash of a given string // and encodes it with hexadecimal digits. var Sha512Func = makeStringHashFunction(sha512.New, hex.EncodeToString) +// MakeFileSha512Func constructs a function that is like Sha512Func but reads the +// contents of a file rather than hashing a given literal string. +func MakeFileSha512Func(baseDir string) function.Function { + return makeFileHashFunction(baseDir, sha512.New, hex.EncodeToString) +} + func makeStringHashFunction(hf func() hash.Hash, enc func([]byte) string) function.Function { return function.New(&function.Spec{ Params: []function.Parameter{ @@ -158,6 +194,30 @@ func makeStringHashFunction(hf func() hash.Hash, enc func([]byte) string) functi }) } +func makeFileHashFunction(baseDir string, hf func() hash.Hash, enc func([]byte) string) function.Function { + return function.New(&function.Spec{ + Params: []function.Parameter{ + { + Name: "path", + Type: cty.String, + }, + }, + Type: function.StaticReturnType(cty.String), + Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { + path := args[0].AsString() + src, err := readFileBytes(baseDir, path) + if err != nil { + return cty.UnknownVal(cty.String), err + } + + h := hf() + h.Write(src) + rv := enc(h.Sum(nil)) + return cty.StringVal(rv), nil + }, + }) +} + // UUID generates and returns a Type-4 UUID in the standard hexadecimal string // format. // diff --git a/lang/funcs/crypto_test.go b/lang/funcs/crypto_test.go index ecc3aaaec..0b4f8d3cb 100644 --- a/lang/funcs/crypto_test.go +++ b/lang/funcs/crypto_test.go @@ -56,6 +56,51 @@ func TestBase64Sha256(t *testing.T) { } } +func TestFileBase64Sha256(t *testing.T) { + tests := []struct { + Path cty.Value + Want cty.Value + Err bool + }{ + { + cty.StringVal("testdata/hello.txt"), + cty.StringVal("LHT9F+2v2A6ER7DUZ0HuJDt+t03SFJoKsbkkb7MDgvJ+hT2FhXGeDmfL2g2qj1FnEGRhXWRa4nrLFb+xRH9Fmw=="), + false, + }, + { + cty.StringVal("testdata/icon.png"), + cty.StringVal("wSInO/tKEOaLGCAY2h/7gtLWMpzyLJ0ijFh95JTpYrPzXQYgviAdL9ZgpD9EAte8On+drvhFvjIFsfQUwxbNPQ=="), + false, + }, + { + cty.StringVal("testdata/missing"), + cty.NilVal, + true, // no file exists + }, + } + + fileSHA256 := MakeFileBase64Sha512Func(".") + + for _, test := range tests { + t.Run(fmt.Sprintf("filebase64sha256(%#v)", test.Path), func(t *testing.T) { + got, err := fileSHA256.Call([]cty.Value{test.Path}) + + 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 TestBase64Sha512(t *testing.T) { tests := []struct { String cty.Value @@ -92,6 +137,51 @@ func TestBase64Sha512(t *testing.T) { } } +func TestFileBase64Sha512(t *testing.T) { + tests := []struct { + Path cty.Value + Want cty.Value + Err bool + }{ + { + cty.StringVal("testdata/hello.txt"), + cty.StringVal("LHT9F+2v2A6ER7DUZ0HuJDt+t03SFJoKsbkkb7MDgvJ+hT2FhXGeDmfL2g2qj1FnEGRhXWRa4nrLFb+xRH9Fmw=="), + false, + }, + { + cty.StringVal("testdata/icon.png"), + cty.StringVal("wSInO/tKEOaLGCAY2h/7gtLWMpzyLJ0ijFh95JTpYrPzXQYgviAdL9ZgpD9EAte8On+drvhFvjIFsfQUwxbNPQ=="), + false, + }, + { + cty.StringVal("testdata/missing"), + cty.NilVal, + true, // no file exists + }, + } + + fileSHA512 := MakeFileBase64Sha512Func(".") + + for _, test := range tests { + t.Run(fmt.Sprintf("filebase64sha512(%#v)", test.Path), func(t *testing.T) { + got, err := fileSHA512.Call([]cty.Value{test.Path}) + + 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 TestBcrypt(t *testing.T) { // single variable test p, err := Bcrypt(cty.StringVal("test")) @@ -165,6 +255,51 @@ func TestMd5(t *testing.T) { } } +func TestFileMD5(t *testing.T) { + tests := []struct { + Path cty.Value + Want cty.Value + Err bool + }{ + { + cty.StringVal("testdata/hello.txt"), + cty.StringVal("b10a8db164e0754105b7a99be72e3fe5"), + false, + }, + { + cty.StringVal("testdata/icon.png"), + cty.StringVal("d7e6c283185a1078c58213beadca98b0"), + false, + }, + { + cty.StringVal("testdata/missing"), + cty.NilVal, + true, // no file exists + }, + } + + fileMD5 := MakeFileMd5Func(".") + + for _, test := range tests { + t.Run(fmt.Sprintf("filemd5(%#v)", test.Path), func(t *testing.T) { + got, err := fileMD5.Call([]cty.Value{test.Path}) + + 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 @@ -268,6 +403,51 @@ func TestSha1(t *testing.T) { } } +func TestFileSHA1(t *testing.T) { + tests := []struct { + Path cty.Value + Want cty.Value + Err bool + }{ + { + cty.StringVal("testdata/hello.txt"), + cty.StringVal("0a4d55a8d778e5022fab701977c5d840bbc486d0"), + false, + }, + { + cty.StringVal("testdata/icon.png"), + cty.StringVal("2821bcc8379e1bd6f4f31b1e6a1fbb204b4a8be8"), + false, + }, + { + cty.StringVal("testdata/missing"), + cty.NilVal, + true, // no file exists + }, + } + + fileSHA1 := MakeFileSha1Func(".") + + for _, test := range tests { + t.Run(fmt.Sprintf("filesha1(%#v)", test.Path), func(t *testing.T) { + got, err := fileSHA1.Call([]cty.Value{test.Path}) + + 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 TestSha256(t *testing.T) { tests := []struct { String cty.Value @@ -301,6 +481,51 @@ func TestSha256(t *testing.T) { } } +func TestFileSHA256(t *testing.T) { + tests := []struct { + Path cty.Value + Want cty.Value + Err bool + }{ + { + cty.StringVal("testdata/hello.txt"), + cty.StringVal("a591a6d40bf420404a011733cfb7b190d62c65bf0bcda32b57b277d9ad9f146e"), + false, + }, + { + cty.StringVal("testdata/icon.png"), + cty.StringVal("e3b535abd2195b4f774a6033742f36d129299fcbc73ef73cb33b9dfd8ddece9a"), + false, + }, + { + cty.StringVal("testdata/missing"), + cty.NilVal, + true, // no file exists + }, + } + + fileSHA256 := MakeFileSha256Func(".") + + for _, test := range tests { + t.Run(fmt.Sprintf("filesha256(%#v)", test.Path), func(t *testing.T) { + got, err := fileSHA256.Call([]cty.Value{test.Path}) + + 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 TestSha512(t *testing.T) { tests := []struct { String cty.Value @@ -334,6 +559,51 @@ func TestSha512(t *testing.T) { } } +func TestFileSHA512(t *testing.T) { + tests := []struct { + Path cty.Value + Want cty.Value + Err bool + }{ + { + cty.StringVal("testdata/hello.txt"), + cty.StringVal("2c74fd17edafd80e8447b0d46741ee243b7eb74dd2149a0ab1b9246fb30382f27e853d8585719e0e67cbda0daa8f51671064615d645ae27acb15bfb1447f459b"), + false, + }, + { + cty.StringVal("testdata/icon.png"), + cty.StringVal("c122273bfb4a10e68b182018da1ffb82d2d6329cf22c9d228c587de494e962b3f35d0620be201d2fd660a43f4402d7bc3a7f9daef845be3205b1f414c316cd3d"), + false, + }, + { + cty.StringVal("testdata/missing"), + cty.NilVal, + true, // no file exists + }, + } + + fileSHA512 := MakeFileSha512Func(".") + + for _, test := range tests { + t.Run(fmt.Sprintf("filesha512(%#v)", test.Path), func(t *testing.T) { + got, err := fileSHA512.Call([]cty.Value{test.Path}) + + 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 = ` diff --git a/lang/funcs/filesystem.go b/lang/funcs/filesystem.go index 15e81ee99..617b891b9 100644 --- a/lang/funcs/filesystem.go +++ b/lang/funcs/filesystem.go @@ -29,26 +29,9 @@ func MakeFileFunc(baseDir string, encBase64 bool) function.Function { Type: function.StaticReturnType(cty.String), Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { path := args[0].AsString() - path, err := homedir.Expand(path) + src, err := readFileBytes(baseDir, path) if err != nil { - return cty.UnknownVal(cty.String), fmt.Errorf("failed to expand ~: %s", err) - } - - if !filepath.IsAbs(path) { - path = filepath.Join(baseDir, path) - } - - // Ensure that the path is canonical for the host OS - path = filepath.Clean(path) - - src, err := ioutil.ReadFile(path) - if err != nil { - // ReadFile does not return Terraform-user-friendly error - // messages, so we'll provide our own. - if os.IsNotExist(err) { - return cty.UnknownVal(cty.String), fmt.Errorf("no file exists at %s", path) - } - return cty.UnknownVal(cty.String), fmt.Errorf("failed to read %s", path) + return cty.UnknownVal(cty.String), err } switch { @@ -270,6 +253,32 @@ var PathExpandFunc = function.New(&function.Spec{ }, }) +func readFileBytes(baseDir, path string) ([]byte, error) { + path, err := homedir.Expand(path) + if err != nil { + return nil, fmt.Errorf("failed to expand ~: %s", err) + } + + if !filepath.IsAbs(path) { + path = filepath.Join(baseDir, path) + } + + // Ensure that the path is canonical for the host OS + path = filepath.Clean(path) + + src, err := ioutil.ReadFile(path) + if err != nil { + // ReadFile does not return Terraform-user-friendly error + // messages, so we'll provide our own. + if os.IsNotExist(err) { + return nil, fmt.Errorf("no file exists at %s", path) + } + return nil, fmt.Errorf("failed to read %s", path) + } + + return src, nil +} + // File reads the contents of the file at the given path. // // The file must contain valid UTF-8 bytes, or this function will return an error. diff --git a/lang/functions.go b/lang/functions.go index a7f654513..bf1b746a3 100644 --- a/lang/functions.go +++ b/lang/functions.go @@ -29,86 +29,92 @@ func (s *Scope) Functions() map[string]function.Function { // that would be useful to all applications using cty functions. s.funcs = map[string]function.Function{ - "abs": stdlib.AbsoluteFunc, - "basename": funcs.BasenameFunc, - "base64decode": funcs.Base64DecodeFunc, - "base64encode": funcs.Base64EncodeFunc, - "base64gzip": funcs.Base64GzipFunc, - "base64sha256": funcs.Base64Sha256Func, - "base64sha512": funcs.Base64Sha512Func, - "bcrypt": funcs.BcryptFunc, - "ceil": funcs.CeilFunc, - "chomp": funcs.ChompFunc, - "cidrhost": funcs.CidrHostFunc, - "cidrnetmask": funcs.CidrNetmaskFunc, - "cidrsubnet": funcs.CidrSubnetFunc, - "coalesce": stdlib.CoalesceFunc, - "coalescelist": funcs.CoalesceListFunc, - "compact": funcs.CompactFunc, - "concat": stdlib.ConcatFunc, - "contains": funcs.ContainsFunc, - "csvdecode": stdlib.CSVDecodeFunc, - "dirname": funcs.DirnameFunc, - "distinct": funcs.DistinctFunc, - "element": funcs.ElementFunc, - "chunklist": funcs.ChunklistFunc, - "file": funcs.MakeFileFunc(s.BaseDir, false), - "fileexists": funcs.MakeFileExistsFunc(s.BaseDir), - "filebase64": funcs.MakeFileFunc(s.BaseDir, true), - "flatten": funcs.FlattenFunc, - "floor": funcs.FloorFunc, - "format": stdlib.FormatFunc, - "formatdate": stdlib.FormatDateFunc, - "formatlist": stdlib.FormatListFunc, - "indent": funcs.IndentFunc, - "index": funcs.IndexFunc, - "join": funcs.JoinFunc, - "jsondecode": stdlib.JSONDecodeFunc, - "jsonencode": stdlib.JSONEncodeFunc, - "keys": funcs.KeysFunc, - "length": funcs.LengthFunc, - "list": funcs.ListFunc, - "log": funcs.LogFunc, - "lookup": funcs.LookupFunc, - "lower": stdlib.LowerFunc, - "map": funcs.MapFunc, - "matchkeys": funcs.MatchkeysFunc, - "max": stdlib.MaxFunc, - "md5": funcs.Md5Func, - "merge": funcs.MergeFunc, - "min": stdlib.MinFunc, - "pathexpand": funcs.PathExpandFunc, - "pow": funcs.PowFunc, - "replace": funcs.ReplaceFunc, - "rsadecrypt": funcs.RsaDecryptFunc, - "sethaselement": stdlib.SetHasElementFunc, - "setintersection": stdlib.SetIntersectionFunc, - "setproduct": funcs.SetProductFunc, - "setunion": stdlib.SetUnionFunc, - "sha1": funcs.Sha1Func, - "sha256": funcs.Sha256Func, - "sha512": funcs.Sha512Func, - "signum": funcs.SignumFunc, - "slice": funcs.SliceFunc, - "sort": funcs.SortFunc, - "split": funcs.SplitFunc, - "substr": stdlib.SubstrFunc, - "timestamp": funcs.TimestampFunc, - "timeadd": funcs.TimeAddFunc, - "title": funcs.TitleFunc, - "tostring": funcs.MakeToFunc(cty.String), - "tonumber": funcs.MakeToFunc(cty.Number), - "tobool": funcs.MakeToFunc(cty.Bool), - "toset": funcs.MakeToFunc(cty.Set(cty.DynamicPseudoType)), - "tolist": funcs.MakeToFunc(cty.List(cty.DynamicPseudoType)), - "tomap": funcs.MakeToFunc(cty.Map(cty.DynamicPseudoType)), - "transpose": funcs.TransposeFunc, - "trimspace": funcs.TrimSpaceFunc, - "upper": stdlib.UpperFunc, - "urlencode": funcs.URLEncodeFunc, - "uuid": funcs.UUIDFunc, - "values": funcs.ValuesFunc, - "zipmap": funcs.ZipmapFunc, + "abs": stdlib.AbsoluteFunc, + "basename": funcs.BasenameFunc, + "base64decode": funcs.Base64DecodeFunc, + "base64encode": funcs.Base64EncodeFunc, + "base64gzip": funcs.Base64GzipFunc, + "base64sha256": funcs.Base64Sha256Func, + "base64sha512": funcs.Base64Sha512Func, + "bcrypt": funcs.BcryptFunc, + "ceil": funcs.CeilFunc, + "chomp": funcs.ChompFunc, + "cidrhost": funcs.CidrHostFunc, + "cidrnetmask": funcs.CidrNetmaskFunc, + "cidrsubnet": funcs.CidrSubnetFunc, + "coalesce": stdlib.CoalesceFunc, + "coalescelist": funcs.CoalesceListFunc, + "compact": funcs.CompactFunc, + "concat": stdlib.ConcatFunc, + "contains": funcs.ContainsFunc, + "csvdecode": stdlib.CSVDecodeFunc, + "dirname": funcs.DirnameFunc, + "distinct": funcs.DistinctFunc, + "element": funcs.ElementFunc, + "chunklist": funcs.ChunklistFunc, + "file": funcs.MakeFileFunc(s.BaseDir, false), + "fileexists": funcs.MakeFileExistsFunc(s.BaseDir), + "filebase64": funcs.MakeFileFunc(s.BaseDir, true), + "filebase64sha256": funcs.MakeFileBase64Sha256Func(s.BaseDir), + "filebase64sha512": funcs.MakeFileBase64Sha512Func(s.BaseDir), + "filemd5": funcs.MakeFileMd5Func(s.BaseDir), + "filesha1": funcs.MakeFileSha1Func(s.BaseDir), + "filesha256": funcs.MakeFileSha256Func(s.BaseDir), + "filesha512": funcs.MakeFileSha512Func(s.BaseDir), + "flatten": funcs.FlattenFunc, + "floor": funcs.FloorFunc, + "format": stdlib.FormatFunc, + "formatdate": stdlib.FormatDateFunc, + "formatlist": stdlib.FormatListFunc, + "indent": funcs.IndentFunc, + "index": funcs.IndexFunc, + "join": funcs.JoinFunc, + "jsondecode": stdlib.JSONDecodeFunc, + "jsonencode": stdlib.JSONEncodeFunc, + "keys": funcs.KeysFunc, + "length": funcs.LengthFunc, + "list": funcs.ListFunc, + "log": funcs.LogFunc, + "lookup": funcs.LookupFunc, + "lower": stdlib.LowerFunc, + "map": funcs.MapFunc, + "matchkeys": funcs.MatchkeysFunc, + "max": stdlib.MaxFunc, + "md5": funcs.Md5Func, + "merge": funcs.MergeFunc, + "min": stdlib.MinFunc, + "pathexpand": funcs.PathExpandFunc, + "pow": funcs.PowFunc, + "replace": funcs.ReplaceFunc, + "rsadecrypt": funcs.RsaDecryptFunc, + "sethaselement": stdlib.SetHasElementFunc, + "setintersection": stdlib.SetIntersectionFunc, + "setproduct": funcs.SetProductFunc, + "setunion": stdlib.SetUnionFunc, + "sha1": funcs.Sha1Func, + "sha256": funcs.Sha256Func, + "sha512": funcs.Sha512Func, + "signum": funcs.SignumFunc, + "slice": funcs.SliceFunc, + "sort": funcs.SortFunc, + "split": funcs.SplitFunc, + "substr": stdlib.SubstrFunc, + "timestamp": funcs.TimestampFunc, + "timeadd": funcs.TimeAddFunc, + "title": funcs.TitleFunc, + "tostring": funcs.MakeToFunc(cty.String), + "tonumber": funcs.MakeToFunc(cty.Number), + "tobool": funcs.MakeToFunc(cty.Bool), + "toset": funcs.MakeToFunc(cty.Set(cty.DynamicPseudoType)), + "tolist": funcs.MakeToFunc(cty.List(cty.DynamicPseudoType)), + "tomap": funcs.MakeToFunc(cty.Map(cty.DynamicPseudoType)), + "transpose": funcs.TransposeFunc, + "trimspace": funcs.TrimSpaceFunc, + "upper": stdlib.UpperFunc, + "urlencode": funcs.URLEncodeFunc, + "uuid": funcs.UUIDFunc, + "values": funcs.ValuesFunc, + "zipmap": funcs.ZipmapFunc, } s.funcs["templatefile"] = funcs.MakeTemplateFileFunc(s.BaseDir, func() map[string]function.Function { diff --git a/website/docs/configuration/functions/base64sha256.html.md b/website/docs/configuration/functions/base64sha256.html.md index c4c81713d..404cfd0bb 100644 --- a/website/docs/configuration/functions/base64sha256.html.md +++ b/website/docs/configuration/functions/base64sha256.html.md @@ -27,5 +27,7 @@ uU0nuZNNPgilLlLX2n2r+sSE7+N6U4DukIj3rOLvzek= ## Related Functions +* [`filebase64sha256`](./filebase64sha256.html) calculates the same hash from + the contents of a file rather than from a string value. * [`sha256`](./sha256.html) calculates the same hash but returns the result in a more-verbose hexadecimal encoding. diff --git a/website/docs/configuration/functions/base64sha512.html.md b/website/docs/configuration/functions/base64sha512.html.md index c95760d4e..f1e7e5637 100644 --- a/website/docs/configuration/functions/base64sha512.html.md +++ b/website/docs/configuration/functions/base64sha512.html.md @@ -27,5 +27,7 @@ MJ7MSJwS1utMxA9QyQLytNDtd+5RGnx6m808qG1M2G+YndNbxf9JlnDaNCVbRbDP2DDoH2Bdz33FVC6T ## Related Functions +* [`filebase64sha512`](./filebase64sha512.html) calculates the same hash from + the contents of a file rather than from a string value. * [`sha512`](./sha512.html) calculates the same hash but returns the result in a more-verbose hexadecimal encoding. diff --git a/website/docs/configuration/functions/filebase64sha256.html.md b/website/docs/configuration/functions/filebase64sha256.html.md new file mode 100644 index 000000000..c87bc8880 --- /dev/null +++ b/website/docs/configuration/functions/filebase64sha256.html.md @@ -0,0 +1,17 @@ +--- +layout: "functions" +page_title: "filebase64sha256 - Functions - Configuration Language" +sidebar_current: "docs-funcs-crypto-filebase64sha256" +description: |- + The filebase64sha256 function computes the SHA256 hash of the contents of + a given file and encodes it with Base64. +--- + +# `filebase64sha256` Function + +`filebase64sha256` is a variant of [`base64sha256`](./base64sha256.html) +that hashes the contents of a given file rather than a literal string. + +This is similar to `base64sha256(file(filename))`, but +because [`file`](./file.html) accepts only UTF-8 text it cannot be used to +create hashes for binary files. diff --git a/website/docs/configuration/functions/filebase64sha512.html.md b/website/docs/configuration/functions/filebase64sha512.html.md new file mode 100644 index 000000000..6bd05dccf --- /dev/null +++ b/website/docs/configuration/functions/filebase64sha512.html.md @@ -0,0 +1,17 @@ +--- +layout: "functions" +page_title: "filebase64sha512 - Functions - Configuration Language" +sidebar_current: "docs-funcs-crypto-filebase64sha512" +description: |- + The filebase64sha512 function computes the SHA512 hash of the contents of + a given file and encodes it with Base64. +--- + +# `filebase64sha512` Function + +`filebase64sha512` is a variant of [`base64sha512`](./base64sha512.html) +that hashes the contents of a given file rather than a literal string. + +This is similar to `base64sha512(file(filename))`, but +because [`file`](./file.html) accepts only UTF-8 text it cannot be used to +create hashes for binary files. diff --git a/website/docs/configuration/functions/filemd5.html.md b/website/docs/configuration/functions/filemd5.html.md new file mode 100644 index 000000000..5b3301465 --- /dev/null +++ b/website/docs/configuration/functions/filemd5.html.md @@ -0,0 +1,17 @@ +--- +layout: "functions" +page_title: "filemd5 - Functions - Configuration Language" +sidebar_current: "docs-funcs-crypto-filemd5" +description: |- + The filemd5 function computes the MD5 hash of the contents of + a given file and encodes it as hex. +--- + +# `filemd5` Function + +`filemd5` is a variant of [`md5`](./md5.html) +that hashes the contents of a given file rather than a literal string. + +This is similar to `md5(file(filename))`, but +because [`file`](./file.html) accepts only UTF-8 text it cannot be used to +create hashes for binary files. diff --git a/website/docs/configuration/functions/filesha1.html.md b/website/docs/configuration/functions/filesha1.html.md new file mode 100644 index 000000000..a17a80551 --- /dev/null +++ b/website/docs/configuration/functions/filesha1.html.md @@ -0,0 +1,17 @@ +--- +layout: "functions" +page_title: "filesha1 - Functions - Configuration Language" +sidebar_current: "docs-funcs-crypto-filesha1" +description: |- + The filesha1 function computes the SHA1 hash of the contents of + a given file and encodes it as hex. +--- + +# `filesha1` Function + +`filesha1` is a variant of [`sha1`](./sha1.html) +that hashes the contents of a given file rather than a literal string. + +This is similar to `sha1(file(filename))`, but +because [`file`](./file.html) accepts only UTF-8 text it cannot be used to +create hashes for binary files. diff --git a/website/docs/configuration/functions/filesha256.html.md b/website/docs/configuration/functions/filesha256.html.md new file mode 100644 index 000000000..7ce738ef3 --- /dev/null +++ b/website/docs/configuration/functions/filesha256.html.md @@ -0,0 +1,17 @@ +--- +layout: "functions" +page_title: "filesha256 - Functions - Configuration Language" +sidebar_current: "docs-funcs-crypto-filesha256" +description: |- + The filesha256 function computes the SHA256 hash of the contents of + a given file and encodes it as hex. +--- + +# `filesha256` Function + +`filesha256` is a variant of [`sha256`](./sha256.html) +that hashes the contents of a given file rather than a literal string. + +This is similar to `sha256(file(filename))`, but +because [`file`](./file.html) accepts only UTF-8 text it cannot be used to +create hashes for binary files. diff --git a/website/docs/configuration/functions/filesha512.html.md b/website/docs/configuration/functions/filesha512.html.md new file mode 100644 index 000000000..1baf94a42 --- /dev/null +++ b/website/docs/configuration/functions/filesha512.html.md @@ -0,0 +1,17 @@ +--- +layout: "functions" +page_title: "filesha512 - Functions - Configuration Language" +sidebar_current: "docs-funcs-crypto-filesha512" +description: |- + The filesha512 function computes the SHA512 hash of the contents of + a given file and encodes it as hex. +--- + +# `filesha512` Function + +`filesha512` is a variant of [`sha512`](./sha512.html) +that hashes the contents of a given file rather than a literal string. + +This is similar to `sha512(file(filename))`, but +because [`file`](./file.html) accepts only UTF-8 text it cannot be used to +create hashes for binary files. diff --git a/website/docs/configuration/functions/md5.html.md b/website/docs/configuration/functions/md5.html.md index e3f866f23..efe507588 100644 --- a/website/docs/configuration/functions/md5.html.md +++ b/website/docs/configuration/functions/md5.html.md @@ -26,3 +26,8 @@ considerations applying to the MD5 algorithm. > md5("hello world") 5eb63bbbe01eeed093cb22bb8f5acdc3 ``` + +## Related Functions + +* [`filemd5`](./filemd5.html) calculates the same hash from + the contents of a file rather than from a string value. diff --git a/website/docs/configuration/functions/sha1.html.md b/website/docs/configuration/functions/sha1.html.md index 55c985003..37476abaf 100644 --- a/website/docs/configuration/functions/sha1.html.md +++ b/website/docs/configuration/functions/sha1.html.md @@ -26,3 +26,8 @@ relevant literature to understand the security implications. > sha1("hello world") 2aae6c35c94fcfb415dbe95f408b9ce91ee846ed ``` + +## Related Functions + +* [`filesha1`](./filesha1.html) calculates the same hash from + the contents of a file rather than from a string value. diff --git a/website/docs/configuration/functions/sha256.html.md b/website/docs/configuration/functions/sha256.html.md index 8a1c47fab..b0dad323e 100644 --- a/website/docs/configuration/functions/sha256.html.md +++ b/website/docs/configuration/functions/sha256.html.md @@ -25,5 +25,10 @@ b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9 ## Related Functions + +## Related Functions + +* [`filesha256`](./filesha256.html) calculates the same hash from + the contents of a file rather than from a string value. * [`base64sha256`](./base64sha256.html) calculates the same hash but returns the result in a more-compact Base64 encoding. diff --git a/website/docs/configuration/functions/sha512.html.md b/website/docs/configuration/functions/sha512.html.md index ba116937b..453f1f3f3 100644 --- a/website/docs/configuration/functions/sha512.html.md +++ b/website/docs/configuration/functions/sha512.html.md @@ -25,5 +25,7 @@ then encoded to lowercase hexadecimal digits before returning. ## Related Functions +* [`filesha512`](./filesha512.html) calculates the same hash from + the contents of a file rather than from a string value. * [`base64sha512`](./base64sha512.html) calculates the same hash but returns the result in a more-compact Base64 encoding. diff --git a/website/layouts/functions.erb b/website/layouts/functions.erb index a1f06051d..283389664 100644 --- a/website/layouts/functions.erb +++ b/website/layouts/functions.erb @@ -311,6 +311,30 @@ bcrypt + > + filebase64sha256 + + + > + filebase64sha512 + + + > + filemd5 + + + > + filesha1 + + + > + filesha256 + + + > + filesha512 + + > md5