hugo

Fork of github.com/gohugoio/hugo with reverse pagination support

git clone git://git.shimmy1996.com/hugo.git

integrity.go (3367B)

    1 // Copyright 2018 The Hugo Authors. All rights reserved.
    2 //
    3 // Licensed under the Apache License, Version 2.0 (the "License");
    4 // you may not use this file except in compliance with the License.
    5 // You may obtain a copy of the License at
    6 // http://www.apache.org/licenses/LICENSE-2.0
    7 //
    8 // Unless required by applicable law or agreed to in writing, software
    9 // distributed under the License is distributed on an "AS IS" BASIS,
   10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   11 // See the License for the specific language governing permissions and
   12 // limitations under the License.
   13 
   14 package integrity
   15 
   16 import (
   17 	"crypto/md5"
   18 	"crypto/sha256"
   19 	"crypto/sha512"
   20 	"encoding/base64"
   21 	"encoding/hex"
   22 	"fmt"
   23 	"hash"
   24 	"html/template"
   25 	"io"
   26 
   27 	"github.com/gohugoio/hugo/resources/internal"
   28 
   29 	"github.com/gohugoio/hugo/resources"
   30 	"github.com/gohugoio/hugo/resources/resource"
   31 )
   32 
   33 const defaultHashAlgo = "sha256"
   34 
   35 // Client contains methods to fingerprint (cachebusting) and other integrity-related
   36 // methods.
   37 type Client struct {
   38 	rs *resources.Spec
   39 }
   40 
   41 // New creates a new Client with the given specification.
   42 func New(rs *resources.Spec) *Client {
   43 	return &Client{rs: rs}
   44 }
   45 
   46 type fingerprintTransformation struct {
   47 	algo string
   48 }
   49 
   50 func (t *fingerprintTransformation) Key() internal.ResourceTransformationKey {
   51 	return internal.NewResourceTransformationKey("fingerprint", t.algo)
   52 }
   53 
   54 // Transform creates a MD5 hash of the Resource content and inserts that hash before
   55 // the extension in the filename.
   56 func (t *fingerprintTransformation) Transform(ctx *resources.ResourceTransformationCtx) error {
   57 	h, err := newHash(t.algo)
   58 	if err != nil {
   59 		return err
   60 	}
   61 
   62 	var w io.Writer
   63 	if rc, ok := ctx.From.(io.ReadSeeker); ok {
   64 		// This transformation does not change the content, so try to
   65 		// avoid writing to To if we can.
   66 		defer rc.Seek(0, 0)
   67 		w = h
   68 	} else {
   69 		w = io.MultiWriter(h, ctx.To)
   70 	}
   71 
   72 	io.Copy(w, ctx.From)
   73 	d, err := digest(h)
   74 	if err != nil {
   75 		return err
   76 	}
   77 
   78 	ctx.Data["Integrity"] = integrity(t.algo, d)
   79 	ctx.AddOutPathIdentifier("." + hex.EncodeToString(d[:]))
   80 	return nil
   81 }
   82 
   83 func newHash(algo string) (hash.Hash, error) {
   84 	switch algo {
   85 	case "md5":
   86 		return md5.New(), nil
   87 	case "sha256":
   88 		return sha256.New(), nil
   89 	case "sha384":
   90 		return sha512.New384(), nil
   91 	case "sha512":
   92 		return sha512.New(), nil
   93 	default:
   94 		return nil, fmt.Errorf("unsupported crypto algo: %q, use either md5, sha256, sha384 or sha512", algo)
   95 	}
   96 }
   97 
   98 // Fingerprint applies fingerprinting of the given resource and hash algorithm.
   99 // It defaults to sha256 if none given, and the options are md5, sha256 or sha512.
  100 // The same algo is used for both the fingerprinting part (aka cache busting) and
  101 // the base64-encoded Subresource Integrity hash, so you will have to stay away from
  102 // md5 if you plan to use both.
  103 // See https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity
  104 func (c *Client) Fingerprint(res resources.ResourceTransformer, algo string) (resource.Resource, error) {
  105 	if algo == "" {
  106 		algo = defaultHashAlgo
  107 	}
  108 
  109 	return res.Transform(&fingerprintTransformation{algo: algo})
  110 }
  111 
  112 func integrity(algo string, sum []byte) template.HTMLAttr {
  113 	encoded := base64.StdEncoding.EncodeToString(sum)
  114 	return template.HTMLAttr(algo + "-" + encoded)
  115 }
  116 
  117 func digest(h hash.Hash) ([]byte, error) {
  118 	sum := h.Sum(nil)
  119 	return sum, nil
  120 }