Commit e6ce37fb authored by ale's avatar ale

Simpler mechanism for embedding SRI hashes

Remove the build-time dependency on Python, the sri_map is now
generated via a small Go script.
parent 4d70b167
Pipeline #5358 passed with stages
in 2 minutes and 41 seconds
......@@ -74,7 +74,7 @@ func staticCssBootstrapMinCss() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "static/css/bootstrap.min.css", size: 140936, mode: os.FileMode(420), modTime: time.Unix(1560696660, 0)}
info := bindataFileInfo{name: "static/css/bootstrap.min.css", size: 140936, mode: os.FileMode(420), modTime: time.Unix(1550305824, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
......@@ -147,7 +147,7 @@ func staticCssSigninCss() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "static/css/signin.css", size: 1009, mode: os.FileMode(420), modTime: time.Unix(1560696660, 0)}
info := bindataFileInfo{name: "static/css/signin.css", size: 1009, mode: os.FileMode(420), modTime: time.Unix(1535013418, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
......@@ -170,7 +170,7 @@ func staticJsBootstrap413MinJs() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "static/js/bootstrap-4.1.3.min.js", size: 51039, mode: os.FileMode(420), modTime: time.Unix(1560696660, 0)}
info := bindataFileInfo{name: "static/js/bootstrap-4.1.3.min.js", size: 51039, mode: os.FileMode(420), modTime: time.Unix(1550305766, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
......@@ -189,7 +189,7 @@ func staticJsJquery331MinJs() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "static/js/jquery-3.3.1.min.js", size: 86927, mode: os.FileMode(420), modTime: time.Unix(1560696660, 0)}
info := bindataFileInfo{name: "static/js/jquery-3.3.1.min.js", size: 86927, mode: os.FileMode(420), modTime: time.Unix(1516469204, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
......@@ -243,7 +243,7 @@ func staticJsLogoutJs() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "static/js/logout.js", size: 1005, mode: os.FileMode(420), modTime: time.Unix(1560696660, 0)}
info := bindataFileInfo{name: "static/js/logout.js", size: 1005, mode: os.FileMode(420), modTime: time.Unix(1535013418, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
......@@ -265,7 +265,7 @@ func staticJsPopper1143MinJs() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "static/js/popper-1.14.3.min.js", size: 20337, mode: os.FileMode(420), modTime: time.Unix(1560696660, 0)}
info := bindataFileInfo{name: "static/js/popper-1.14.3.min.js", size: 20337, mode: os.FileMode(420), modTime: time.Unix(1526549114, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
......@@ -1030,7 +1030,7 @@ func staticJsU2fApiJs() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "static/js/u2f-api.js", size: 20880, mode: os.FileMode(420), modTime: time.Unix(1512325237, 0)}
info := bindataFileInfo{name: "static/js/u2f-api.js", size: 20880, mode: os.FileMode(420), modTime: time.Unix(1535013418, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
......@@ -1099,7 +1099,7 @@ func staticJsU2fJs() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "static/js/u2f.js", size: 1274, mode: os.FileMode(420), modTime: time.Unix(1560696660, 0)}
info := bindataFileInfo{name: "static/js/u2f.js", size: 1274, mode: os.FileMode(420), modTime: time.Unix(1541228751, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
......@@ -1154,7 +1154,7 @@ func templatesLogin_otpHtml() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "templates/login_otp.html", size: 956, mode: os.FileMode(420), modTime: time.Unix(1561757406, 0)}
info := bindataFileInfo{name: "templates/login_otp.html", size: 956, mode: os.FileMode(420), modTime: time.Unix(1561884470, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
......@@ -1225,7 +1225,7 @@ func templatesLogin_passwordHtml() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "templates/login_password.html", size: 1432, mode: os.FileMode(420), modTime: time.Unix(1561757427, 0)}
info := bindataFileInfo{name: "templates/login_password.html", size: 1432, mode: os.FileMode(420), modTime: time.Unix(1561884470, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
......@@ -1282,7 +1282,7 @@ func templatesLogin_u2fHtml() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "templates/login_u2f.html", size: 908, mode: os.FileMode(420), modTime: time.Unix(1561757448, 0)}
info := bindataFileInfo{name: "templates/login_u2f.html", size: 908, mode: os.FileMode(420), modTime: time.Unix(1561884470, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
......@@ -1342,7 +1342,7 @@ func templatesLogoutHtml() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "templates/logout.html", size: 1063, mode: os.FileMode(420), modTime: time.Unix(1561757528, 0)}
info := bindataFileInfo{name: "templates/logout.html", size: 1063, mode: os.FileMode(420), modTime: time.Unix(1548600535, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
......@@ -1353,8 +1353,8 @@ var _templatesPageHtml = []byte(`{{define "header"}}<!DOCTYPE html>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
{{if .U2FSignRequest}}<meta name="u2f_request" value="{{json .U2FSignRequest}}">{{end}}
<link rel="stylesheet" href="{{.URLPrefix}}/static/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO">
<link rel="stylesheet" href="{{.URLPrefix}}/static/css/signin.css" integrity="sha384-9Y3UkAyM3svAuamEoaXIxe+1MqBKJdZtL8S1FZjvE1XqkICDH7DTXNavnFV8Uk2o">
<link rel="stylesheet" href="{{.URLPrefix}}/static/css/bootstrap.min.css"{{SRI "/static/css/bootstrap.min.css"}}>
<link rel="stylesheet" href="{{.URLPrefix}}/static/css/signin.css"{{SRI "/static/css/signin.css"}}>
{{if .SiteFavicon}}<link rel="icon" type="image/x-icon" href="{{.URLPrefix}}/favicon.ico">{{end}}
<title>{{if .SiteName}}{{.SiteName}} - {{end}}Sign In</title>
</head>
......@@ -1366,15 +1366,15 @@ var _templatesPageHtml = []byte(`{{define "header"}}<!DOCTYPE html>
{{define "footer"}}
</div>
<script src="{{.URLPrefix}}/static/js/jquery-3.3.1.min.js" integrity="sha384-tsQFqpEReu7ZLhBV2VZlAu7zcOV+rXbYlF2cqB8txI/8aZajjp4Bqd+V6D5IgvKT"></script>
<script src="{{.URLPrefix}}/static/js/popper-1.14.3.min.js" integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49"></script>
<script src="{{.URLPrefix}}/static/js/bootstrap-4.1.3.min.js" integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy"></script>
<script src="{{.URLPrefix}}/static/js/jquery-3.3.1.min.js"{{SRI "/static/js/jquery-3.3.1.min.js"}}></script>
<script src="{{.URLPrefix}}/static/js/popper-1.14.3.min.js"{{SRI "/static/js/popper-1.14.3.min.js"}}></script>
<script src="{{.URLPrefix}}/static/js/bootstrap-4.1.3.min.js"{{SRI "/static/js/bootstrap-4.1.3.min.js"}}></script>
{{if .U2FSignRequest}}
<script src="{{.URLPrefix}}/static/js/u2f-api.js" integrity="sha384-9ChevE6pp8ArGK03HgolnFjZbF3webZQtYkwcabzbcI28Lx1/2x2j2fbaAWD4cgR"></script>
<script src="{{.URLPrefix}}/static/js/u2f.js" integrity="sha384-7zZy25ajTABErGlCQgcyRDpQDS9QVZv9o+95IfvCjWftQe20f411F1a39Ge5xmCe"></script>
<script src="{{.URLPrefix}}/static/js/u2f-api.js"{{SRI "/static/js/u2f-api.js"}}></script>
<script src="{{.URLPrefix}}/static/js/u2f.js"{{SRI "/static/js/u2f.js"}}></script>
{{end}}
{{if .IncludeLogoutScripts}}
<script src="{{.URLPrefix}}/static/js/logout.js" integrity="sha384-lChVngGLNFXetIJTSxc+scDpi1vsBL+7Xa4r2uZpQFP/6Y2z9eCDXe/Y4IUdklRD"></script>
<script src="{{.URLPrefix}}/static/js/logout.js"{{SRI "/static/js/logout.js"}}></script>
{{end}}
</body>
</html>
......@@ -1391,7 +1391,7 @@ func templatesPageHtml() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "templates/page.html", size: 1865, mode: os.FileMode(420), modTime: time.Unix(1561757493, 0)}
info := bindataFileInfo{name: "templates/page.html", size: 1476, mode: os.FileMode(420), modTime: time.Unix(1576422396, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
......
package server
//go:generate python sri.py templates/*.html
//go:generate go run scripts/sri.go --package server --output sri_map.go static
//go:generate go-bindata --nocompress --pkg server static/... templates/...
import (
......@@ -362,6 +362,7 @@ func (h *Server) Handler() http.Handler {
func parseEmbeddedTemplates() *template.Template {
root := template.New("").Funcs(template.FuncMap{
"json": toJSON,
"SRI": sriIntegrity,
})
files, err := AssetDir("templates")
if err != nil {
......@@ -422,3 +423,13 @@ func intersectGroups(a, b []string) []string {
}
return out
}
// Return an integrity= attribute for the given URI (which should be
// supplied without an eventual prefix).
func sriIntegrity(uri string) template.HTML {
sri, ok := sriMap[uri]
if !ok {
return template.HTML("")
}
return template.HTML(fmt.Sprintf(" integrity=\"%s\"", sri))
}
package main
import (
"crypto/sha512"
"encoding/base64"
"flag"
"fmt"
"io"
"io/ioutil"
"log"
"os"
"path/filepath"
"strings"
)
var (
outputPath = flag.String("output", "", "output `file` name")
packageName = flag.String("package", "", "`name` of the package for generated code")
stripPrefix = flag.String("strip", "", "prefix to strip from `path`s to generate URLs")
)
func computeChecksum(path string) (string, error) {
data, err := ioutil.ReadFile(path)
if err != nil {
return "", err
}
sha := sha512.Sum384(data)
return "sha384-" + base64.StdEncoding.EncodeToString(sha[:]), nil
}
func urlForPath(path string) string {
path = strings.TrimPrefix(path, *stripPrefix)
if !strings.HasPrefix(path, "/") {
path = "/" + path
}
return path
}
func mkSRIMap(m map[string]string, dir string) error {
return filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
// Only match files with the desired extensions.
if info.IsDir() || !match(info.Name()) {
return nil
}
if cksum, err := computeChecksum(path); err == nil {
m[urlForPath(path)] = cksum
}
return nil
})
}
func match(name string) bool {
switch filepath.Ext(name) {
case ".js", ".json", ".css":
return true
default:
return false
}
}
// nolint: errcheck
func codegen(w io.Writer, m map[string]string) {
fmt.Fprintf(w, "package %s\n", *packageName)
io.WriteString(w, `
var sriMap = map[string]string{
`)
for k, v := range m {
fmt.Fprintf(w, "\t%q: %q,\n", k, v)
}
io.WriteString(w, "}\n")
}
func main() {
log.SetFlags(0)
flag.Parse()
m := make(map[string]string)
for _, path := range flag.Args() {
if err := mkSRIMap(m, path); err != nil {
log.Println(err)
}
}
var w io.Writer = os.Stdout
if *outputPath != "" {
var err error
w, err = os.Create(*outputPath)
if err != nil {
log.Fatal(err)
}
}
codegen(w, m)
}
#!/usr/bin/python
#
# Automatically fix Subresource Integrity links in the HTML templates.
#
# Pass templates as command-line arguments. Expects to be run from the
# base resource directory.
#
import glob
import re
import sys
from hashlib import sha384
script_rx = re.compile(r'<(?:script|link rel="stylesheet")[^>]*(?:src|href)="(?:{{.URLPrefix}})?([^"]+)"[^>]*>')
integrity_rx = re.compile(r' +integrity="[^"]*"')
def compute_checksum(src):
if src[0] == '/':
src = src[1:]
with open(src) as fd:
return 'sha384-' + sha384(fd.read()).digest().encode('base64').strip()
def replace_checksum(m):
src = m.group(1)
checksum = compute_checksum(src)
script = m.group(0)
script = integrity_rx.sub('', script)
script = '%s integrity="%s">' % (script[:-1], checksum)
return script
def fix_sri(path):
with open(path) as fd:
data = fd.read()
result = script_rx.sub(replace_checksum, data)
if result != data:
print >>sys.stderr, 'updating %s' % path
with open(path, 'w') as fd:
fd.write(result)
if __name__ == '__main__':
for arg in sys.argv[1:]:
for path in glob.glob(arg):
try:
fix_sri(path)
except Exception as e:
print >>sys.stderr, "Error fixing %s: %s" % (path, e)
package server
var sriMap = map[string]string{
"/static/css/bootstrap.min.css": "sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO",
"/static/css/signin.css": "sha384-9Y3UkAyM3svAuamEoaXIxe+1MqBKJdZtL8S1FZjvE1XqkICDH7DTXNavnFV8Uk2o",
"/static/js/bootstrap-4.1.3.min.js": "sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy",
"/static/js/jquery-3.3.1.min.js": "sha384-tsQFqpEReu7ZLhBV2VZlAu7zcOV+rXbYlF2cqB8txI/8aZajjp4Bqd+V6D5IgvKT",
"/static/js/logout.js": "sha384-lChVngGLNFXetIJTSxc+scDpi1vsBL+7Xa4r2uZpQFP/6Y2z9eCDXe/Y4IUdklRD",
"/static/js/popper-1.14.3.min.js": "sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49",
"/static/js/u2f-api.js": "sha384-9ChevE6pp8ArGK03HgolnFjZbF3webZQtYkwcabzbcI28Lx1/2x2j2fbaAWD4cgR",
"/static/js/u2f.js": "sha384-7zZy25ajTABErGlCQgcyRDpQDS9QVZv9o+95IfvCjWftQe20f411F1a39Ge5xmCe",
}
......@@ -4,8 +4,8 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
{{if .U2FSignRequest}}<meta name="u2f_request" value="{{json .U2FSignRequest}}">{{end}}
<link rel="stylesheet" href="{{.URLPrefix}}/static/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO">
<link rel="stylesheet" href="{{.URLPrefix}}/static/css/signin.css" integrity="sha384-9Y3UkAyM3svAuamEoaXIxe+1MqBKJdZtL8S1FZjvE1XqkICDH7DTXNavnFV8Uk2o">
<link rel="stylesheet" href="{{.URLPrefix}}/static/css/bootstrap.min.css"{{SRI "/static/css/bootstrap.min.css"}}>
<link rel="stylesheet" href="{{.URLPrefix}}/static/css/signin.css"{{SRI "/static/css/signin.css"}}>
{{if .SiteFavicon}}<link rel="icon" type="image/x-icon" href="{{.URLPrefix}}/favicon.ico">{{end}}
<title>{{if .SiteName}}{{.SiteName}} - {{end}}Sign In</title>
</head>
......@@ -17,15 +17,15 @@
{{define "footer"}}
</div>
<script src="{{.URLPrefix}}/static/js/jquery-3.3.1.min.js" integrity="sha384-tsQFqpEReu7ZLhBV2VZlAu7zcOV+rXbYlF2cqB8txI/8aZajjp4Bqd+V6D5IgvKT"></script>
<script src="{{.URLPrefix}}/static/js/popper-1.14.3.min.js" integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49"></script>
<script src="{{.URLPrefix}}/static/js/bootstrap-4.1.3.min.js" integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy"></script>
<script src="{{.URLPrefix}}/static/js/jquery-3.3.1.min.js"{{SRI "/static/js/jquery-3.3.1.min.js"}}></script>
<script src="{{.URLPrefix}}/static/js/popper-1.14.3.min.js"{{SRI "/static/js/popper-1.14.3.min.js"}}></script>
<script src="{{.URLPrefix}}/static/js/bootstrap-4.1.3.min.js"{{SRI "/static/js/bootstrap-4.1.3.min.js"}}></script>
{{if .U2FSignRequest}}
<script src="{{.URLPrefix}}/static/js/u2f-api.js" integrity="sha384-9ChevE6pp8ArGK03HgolnFjZbF3webZQtYkwcabzbcI28Lx1/2x2j2fbaAWD4cgR"></script>
<script src="{{.URLPrefix}}/static/js/u2f.js" integrity="sha384-7zZy25ajTABErGlCQgcyRDpQDS9QVZv9o+95IfvCjWftQe20f411F1a39Ge5xmCe"></script>
<script src="{{.URLPrefix}}/static/js/u2f-api.js"{{SRI "/static/js/u2f-api.js"}}></script>
<script src="{{.URLPrefix}}/static/js/u2f.js"{{SRI "/static/js/u2f.js"}}></script>
{{end}}
{{if .IncludeLogoutScripts}}
<script src="{{.URLPrefix}}/static/js/logout.js" integrity="sha384-lChVngGLNFXetIJTSxc+scDpi1vsBL+7Xa4r2uZpQFP/6Y2z9eCDXe/Y4IUdklRD"></script>
<script src="{{.URLPrefix}}/static/js/logout.js"{{SRI "/static/js/logout.js"}}></script>
{{end}}
</body>
</html>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment