diff --git a/docs/content/en/hugo-pipes/fingerprint.md b/docs/content/en/hugo-pipes/fingerprint.md index 96aa7f556..7aa3f100a 100755 --- a/docs/content/en/hugo-pipes/fingerprint.md +++ b/docs/content/en/hugo-pipes/fingerprint.md @@ -18,7 +18,7 @@ draft: false Fingerprinting and [SRI](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity) can be applied to any asset file using `resources.Fingerpint` which takes two arguments, the resource object and a [hash function](https://en.wikipedia.org/wiki/Cryptographic_hash_function). -The default hash function is `sha256`. Other available functions are `sha512` and `md5`. +The default hash function is `sha256`. Other available functions are `sha384` (from Hugo `0.55`), `sha512` and `md5`. Any so processed asset will bear a `.Data.Integrity` property containing an integrity string, which is made up of the name of the hash function, one hyphen and the base64-encoded hash sum. diff --git a/resources/resource_transformers/integrity/integrity.go b/resources/resource_transformers/integrity/integrity.go index 90afafb88..95065603d 100644 --- a/resources/resource_transformers/integrity/integrity.go +++ b/resources/resource_transformers/integrity/integrity.go @@ -19,11 +19,12 @@ import ( "crypto/sha512" "encoding/base64" "encoding/hex" - "fmt" "hash" "html/template" "io" + "github.com/pkg/errors" + "github.com/gohugoio/hugo/resources" "github.com/gohugoio/hugo/resources/resource" ) @@ -52,19 +53,10 @@ func (t *fingerprintTransformation) Key() resources.ResourceTransformationKey { // Transform creates a MD5 hash of the Resource content and inserts that hash before // the extension in the filename. func (t *fingerprintTransformation) Transform(ctx *resources.ResourceTransformationCtx) error { - algo := t.algo - var h hash.Hash - - switch algo { - case "md5": - h = md5.New() - case "sha256": - h = sha256.New() - case "sha512": - h = sha512.New() - default: - return fmt.Errorf("unsupported crypto algo: %q, use either md5, sha256 or sha512", algo) + h, err := newHash(t.algo) + if err != nil { + return err } io.Copy(io.MultiWriter(h, ctx.To), ctx.From) @@ -73,11 +65,26 @@ func (t *fingerprintTransformation) Transform(ctx *resources.ResourceTransformat return err } - ctx.Data["Integrity"] = integrity(algo, d) + ctx.Data["Integrity"] = integrity(t.algo, d) ctx.AddOutPathIdentifier("." + hex.EncodeToString(d[:])) return nil } +func newHash(algo string) (hash.Hash, error) { + switch algo { + case "md5": + return md5.New(), nil + case "sha256": + return sha256.New(), nil + case "sha384": + return sha512.New384(), nil + case "sha512": + return sha512.New(), nil + default: + return nil, errors.Errorf("unsupported crypto algo: %q, use either md5, sha256, sha384 or sha512", algo) + } +} + // Fingerprint applies fingerprinting of the given resource and hash algorithm. // It defaults to sha256 if none given, and the options are md5, sha256 or sha512. // The same algo is used for both the fingerprinting part (aka cache busting) and diff --git a/resources/resource_transformers/integrity/integrity_test.go b/resources/resource_transformers/integrity/integrity_test.go new file mode 100644 index 000000000..7e32e3275 --- /dev/null +++ b/resources/resource_transformers/integrity/integrity_test.go @@ -0,0 +1,48 @@ +// Copyright 2019 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package integrity + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestHashFromAlgo(t *testing.T) { + + for _, algo := range []struct { + name string + bits int + }{ + {"md5", 128}, + {"sha256", 256}, + {"sha384", 384}, + {"sha512", 512}, + {"shaman", -1}, + } { + + t.Run(algo.name, func(t *testing.T) { + assert := require.New(t) + h, err := newHash(algo.name) + if algo.bits > 0 { + assert.NoError(err) + assert.Equal(algo.bits/8, h.Size()) + } else { + assert.Error(err) + assert.Contains(err.Error(), "use either md5, sha256, sha384 or sha512") + } + + }) + } +}