modules/funcmap: add funcs to calculate SRI hashes
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
* correctly handle LiveMode resp. whether or not to set/read embeds
This commit is contained in:
parent
b77c2fe941
commit
e8ac4e39ce
12
app/app.go
12
app/app.go
@ -2,9 +2,11 @@ package app
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"io/fs"
|
||||
|
||||
"git.dotya.ml/mirre-mt/pcmt/app/settings"
|
||||
"git.dotya.ml/mirre-mt/pcmt/ent"
|
||||
modfuncmap "git.dotya.ml/mirre-mt/pcmt/modules/funcmap"
|
||||
"git.dotya.ml/mirre-mt/pcmt/slogging"
|
||||
"github.com/labstack/echo/v4"
|
||||
gommonlog "github.com/labstack/gommon/log"
|
||||
@ -111,9 +113,17 @@ func (a *App) SetSettings(s *settings.Settings) {
|
||||
|
||||
// SetEmbeds saves the embedded files to application state.
|
||||
func (a *App) SetEmbeds(templates, assets embed.FS) {
|
||||
a.Logger().Debug("saving embeds in app struct")
|
||||
if !a.setting.IsLive() {
|
||||
a.Logger().Debug("saving embeds in the app struct")
|
||||
|
||||
a.embeds.templates = templates
|
||||
a.embeds.assets = assets
|
||||
|
||||
var fsfs fs.FS = &assets
|
||||
|
||||
// save pointer to assets to funcmap for integrity calculations.
|
||||
modfuncmap.SetEmbeds(&fsfs)
|
||||
}
|
||||
}
|
||||
|
||||
// setDevel puts the app in devel mode, which loads a browser-sync script in
|
||||
|
@ -2,10 +2,16 @@ package funcmap
|
||||
|
||||
import (
|
||||
"html/template"
|
||||
"io/fs"
|
||||
|
||||
modbluemonday "git.dotya.ml/mirre-mt/pcmt/modules/bluemonday"
|
||||
)
|
||||
|
||||
var (
|
||||
embedAssets *fs.FS
|
||||
isLive = true
|
||||
)
|
||||
|
||||
func FuncMap() template.FuncMap {
|
||||
return template.FuncMap{
|
||||
"ifIE": func() template.HTML { return template.HTML("<!--[if IE]>") },
|
||||
@ -18,7 +24,23 @@ func FuncMap() template.FuncMap {
|
||||
"pageIs": func(want, got string) bool {
|
||||
return want == got
|
||||
},
|
||||
"sha256": func(path string) string {
|
||||
t := New("sha256")
|
||||
|
||||
r, err := t.Integrity(path, isLive)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return *r
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// SetEmbeds saves the pointer to embedded assets (and toggles the isLive var).
|
||||
func SetEmbeds(embeds *fs.FS) {
|
||||
embedAssets = embeds
|
||||
isLive = false
|
||||
}
|
||||
|
||||
// TODO: mimic https://github.com/drone/funcmap/blob/master/funcmap.go
|
||||
|
115
modules/funcmap/integrity.go
Normal file
115
modules/funcmap/integrity.go
Normal file
@ -0,0 +1,115 @@
|
||||
package funcmap
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"crypto/sha512"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"hash"
|
||||
"io"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// resIntegrity type implements several methods that allow getting a formatted
|
||||
// hash digest for the purpose of checking integrity of a web resource using
|
||||
// the algo specified.
|
||||
type resIntegrity struct {
|
||||
algo string
|
||||
}
|
||||
|
||||
const defaultHashAlgo = "sha256"
|
||||
|
||||
// New returns a new resource integrity (resIntegrity) struct for the
|
||||
// particular algorithm. Supported argument values are sha256, sha384 and
|
||||
// sha512.
|
||||
func New(algo string) *resIntegrity { //nolint:revive
|
||||
return &resIntegrity{algo: algo}
|
||||
}
|
||||
|
||||
// Integrity reads the resource at path, optionally from an fs.FS if parameter
|
||||
// live is false (i.e. we're checking embedded resources), and returns a
|
||||
// pointer to the integrity string in the form <algo>-<digest>.
|
||||
func (i *resIntegrity) Integrity(path string, live bool) (*string, error) {
|
||||
r := ""
|
||||
|
||||
// e is an fs.FS created from embed.FS for convenience.
|
||||
var e fs.FS
|
||||
|
||||
if embedAssets != nil {
|
||||
e = *embedAssets
|
||||
}
|
||||
|
||||
h, err := newHash(i.algo)
|
||||
if err != nil {
|
||||
return &r, err
|
||||
}
|
||||
|
||||
if live {
|
||||
path = filepath.Join("assets", "public", path)
|
||||
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return &r, err
|
||||
}
|
||||
|
||||
defer f.Close()
|
||||
|
||||
_, err = io.Copy(h, f)
|
||||
if err != nil {
|
||||
return &r, err
|
||||
}
|
||||
} else {
|
||||
path = filepath.Join("assets", "public", path)
|
||||
|
||||
f, err := e.Open(path)
|
||||
if err != nil {
|
||||
return &r, err
|
||||
}
|
||||
|
||||
defer f.Close()
|
||||
|
||||
_, err = io.Copy(h, f)
|
||||
if err != nil {
|
||||
return &r, err
|
||||
}
|
||||
}
|
||||
|
||||
d, err := digest(h)
|
||||
if err != nil {
|
||||
return &r, err
|
||||
}
|
||||
|
||||
r = integrity(i.algo, d)
|
||||
|
||||
return &r, nil
|
||||
}
|
||||
|
||||
func newHash(algo string) (hash.Hash, error) {
|
||||
switch algo {
|
||||
case "sha256":
|
||||
return sha256.New(), nil
|
||||
case "sha384":
|
||||
return sha512.New384(), nil
|
||||
case "sha512":
|
||||
return sha512.New(), nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported hash algorithm: %q, use either sha256, sha384 or sha512", algo)
|
||||
}
|
||||
}
|
||||
|
||||
func integrity(algo string, sum []byte) string {
|
||||
if algo == "" {
|
||||
algo = defaultHashAlgo
|
||||
}
|
||||
|
||||
encoded := base64.StdEncoding.EncodeToString(sum)
|
||||
|
||||
return algo + "-" + encoded
|
||||
}
|
||||
|
||||
func digest(h hash.Hash) ([]byte, error) {
|
||||
sum := h.Sum(nil)
|
||||
return sum, nil
|
||||
}
|
Loading…
Reference in New Issue
Block a user