subresource integrity
This commit is contained in:
parent
26ad61057a
commit
f502bf4f53
@ -4,6 +4,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"compress/gzip"
|
"compress/gzip"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
|
"encoding/base64"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -43,6 +44,7 @@ type SFile struct {
|
|||||||
BrData []byte
|
BrData []byte
|
||||||
|
|
||||||
Sha256 string
|
Sha256 string
|
||||||
|
Sha256I string
|
||||||
OName string
|
OName string
|
||||||
Pos int64
|
Pos int64
|
||||||
|
|
||||||
@ -306,9 +308,11 @@ func (l SFileList) JSTmplInit() error {
|
|||||||
// Get a checksum for CSPs and cache busting
|
// Get a checksum for CSPs and cache busting
|
||||||
hasher := sha256.New()
|
hasher := sha256.New()
|
||||||
hasher.Write(data)
|
hasher.Write(data)
|
||||||
checksum := hex.EncodeToString(hasher.Sum(nil))
|
sum := hasher.Sum(nil)
|
||||||
|
checksum := hex.EncodeToString(sum)
|
||||||
|
integrity := base64.StdEncoding.EncodeToString(sum)
|
||||||
|
|
||||||
l.Set(l.Prefix+path, &SFile{data, gzipData, brData, checksum, l.Prefix + path + "?h=" + checksum, 0, int64(len(data)), strconv.Itoa(len(data)), int64(len(gzipData)), strconv.Itoa(len(gzipData)), int64(len(brData)), strconv.Itoa(len(brData)), mime.TypeByExtension(ext), f, f.ModTime().UTC().Format(http.TimeFormat)})
|
l.Set(l.Prefix+path, &SFile{data, gzipData, brData, checksum, integrity, l.Prefix + path + "?h=" + checksum, 0, int64(len(data)), strconv.Itoa(len(data)), int64(len(gzipData)), strconv.Itoa(len(gzipData)), int64(len(brData)), strconv.Itoa(len(brData)), mime.TypeByExtension(ext), f, f.ModTime().UTC().Format(http.TimeFormat)})
|
||||||
|
|
||||||
DebugLogf("Added the '%s' static file.", path)
|
DebugLogf("Added the '%s' static file.", path)
|
||||||
return nil
|
return nil
|
||||||
@ -336,7 +340,9 @@ func (l SFileList) Init() error {
|
|||||||
// Get a checksum for CSPs and cache busting
|
// Get a checksum for CSPs and cache busting
|
||||||
hasher := sha256.New()
|
hasher := sha256.New()
|
||||||
hasher.Write(data)
|
hasher.Write(data)
|
||||||
checksum := hex.EncodeToString(hasher.Sum(nil))
|
sum := hasher.Sum(nil)
|
||||||
|
checksum := hex.EncodeToString(sum)
|
||||||
|
integrity := base64.StdEncoding.EncodeToString(sum)
|
||||||
|
|
||||||
// Avoid double-compressing images
|
// Avoid double-compressing images
|
||||||
var gzipData, brData []byte
|
var gzipData, brData []byte
|
||||||
@ -370,7 +376,7 @@ func (l SFileList) Init() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
l.Set(l.Prefix+path, &SFile{data, gzipData, brData, checksum, l.Prefix + path + "?h=" + checksum, 0, int64(len(data)), strconv.Itoa(len(data)), int64(len(gzipData)), strconv.Itoa(len(gzipData)), int64(len(brData)), strconv.Itoa(len(brData)), mimetype, f, f.ModTime().UTC().Format(http.TimeFormat)})
|
l.Set(l.Prefix+path, &SFile{data, gzipData, brData, checksum, integrity, l.Prefix + path + "?h=" + checksum, 0, int64(len(data)), strconv.Itoa(len(data)), int64(len(gzipData)), strconv.Itoa(len(gzipData)), int64(len(brData)), strconv.Itoa(len(brData)), mimetype, f, f.ModTime().UTC().Format(http.TimeFormat)})
|
||||||
|
|
||||||
DebugLogf("Added the '%s' static file.", path)
|
DebugLogf("Added the '%s' static file.", path)
|
||||||
return nil
|
return nil
|
||||||
@ -425,9 +431,11 @@ func (l SFileList) Add(path, prefix string) error {
|
|||||||
// Get a checksum for CSPs and cache busting
|
// Get a checksum for CSPs and cache busting
|
||||||
hasher := sha256.New()
|
hasher := sha256.New()
|
||||||
hasher.Write(data)
|
hasher.Write(data)
|
||||||
checksum := hex.EncodeToString(hasher.Sum(nil))
|
sum := hasher.Sum(nil)
|
||||||
|
checksum := hex.EncodeToString(sum)
|
||||||
|
integrity := base64.StdEncoding.EncodeToString(sum)
|
||||||
|
|
||||||
l.Set(l.Prefix+path, &SFile{data, gzipData, brData, checksum, l.Prefix + path + "?h=" + checksum, 0, int64(len(data)), strconv.Itoa(len(data)), int64(len(gzipData)), strconv.Itoa(len(gzipData)), int64(len(brData)), strconv.Itoa(len(brData)), mime.TypeByExtension(ext), f, f.ModTime().UTC().Format(http.TimeFormat)})
|
l.Set(l.Prefix+path, &SFile{data, gzipData, brData, checksum, integrity, l.Prefix + path + "?h=" + checksum, 0, int64(len(data)), strconv.Itoa(len(data)), int64(len(gzipData)), strconv.Itoa(len(gzipData)), int64(len(brData)), strconv.Itoa(len(brData)), mime.TypeByExtension(ext), f, f.ModTime().UTC().Format(http.TimeFormat)})
|
||||||
|
|
||||||
DebugLogf("Added the '%s' static file", path)
|
DebugLogf("Added the '%s' static file", path)
|
||||||
return nil
|
return nil
|
||||||
|
@ -21,11 +21,11 @@ type Header struct {
|
|||||||
Title string
|
Title string
|
||||||
//Title []byte // Experimenting with []byte for increased efficiency, let's avoid converting too many things to []byte, as it involves a lot of extra boilerplate
|
//Title []byte // Experimenting with []byte for increased efficiency, let's avoid converting too many things to []byte, as it involves a lot of extra boilerplate
|
||||||
NoticeList []string
|
NoticeList []string
|
||||||
Scripts []string
|
Scripts []HScript
|
||||||
PreScriptsAsync []string
|
PreScriptsAsync []HScript
|
||||||
ScriptsAsync []string
|
ScriptsAsync []HScript
|
||||||
//Preload []string
|
//Preload []string
|
||||||
Stylesheets []string
|
Stylesheets []HScript
|
||||||
Widgets PageWidgets
|
Widgets PageWidgets
|
||||||
Site *site
|
Site *site
|
||||||
Settings SettingMap
|
Settings SettingMap
|
||||||
@ -52,38 +52,33 @@ type Header struct {
|
|||||||
ExtData ExtData
|
ExtData ExtData
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Header) AddScript(name string) {
|
type HScript struct {
|
||||||
|
Name string
|
||||||
|
Hash string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Header) getScript(name string) HScript {
|
||||||
if name[0] == '/' && name[1] == '/' {
|
if name[0] == '/' && name[1] == '/' {
|
||||||
} else {
|
} else {
|
||||||
file, ok := StaticFiles.GetShort(name)
|
file, ok := StaticFiles.GetShort(name)
|
||||||
if ok {
|
if ok {
|
||||||
name = file.OName
|
return HScript{file.OName,file.Sha256I}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return HScript{name,""}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Header) AddScript(name string) {
|
||||||
//log.Print("name:", name)
|
//log.Print("name:", name)
|
||||||
h.Scripts = append(h.Scripts, name)
|
h.Scripts = append(h.Scripts, h.getScript(name))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Header) AddPreScriptAsync(name string) {
|
func (h *Header) AddPreScriptAsync(name string) {
|
||||||
if name[0] == '/' && name[1] == '/' {
|
h.PreScriptsAsync = append(h.PreScriptsAsync, h.getScript(name))
|
||||||
} else {
|
|
||||||
file, ok := StaticFiles.GetShort(name)
|
|
||||||
if ok {
|
|
||||||
name = file.OName
|
|
||||||
}
|
|
||||||
}
|
|
||||||
h.PreScriptsAsync = append(h.PreScriptsAsync, name)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Header) AddScriptAsync(name string) {
|
func (h *Header) AddScriptAsync(name string) {
|
||||||
if name[0] == '/' && name[1] == '/' {
|
h.ScriptsAsync = append(h.ScriptsAsync, h.getScript(name))
|
||||||
} else {
|
|
||||||
file, ok := StaticFiles.GetShort(name)
|
|
||||||
if ok {
|
|
||||||
name = file.OName
|
|
||||||
}
|
|
||||||
}
|
|
||||||
h.ScriptsAsync = append(h.ScriptsAsync, name)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*func (h *Header) Preload(name string) {
|
/*func (h *Header) Preload(name string) {
|
||||||
@ -91,14 +86,7 @@ func (h *Header) AddScriptAsync(name string) {
|
|||||||
}*/
|
}*/
|
||||||
|
|
||||||
func (h *Header) AddSheet(name string) {
|
func (h *Header) AddSheet(name string) {
|
||||||
if name[0] == '/' && name[1] == '/' {
|
h.Stylesheets = append(h.Stylesheets, h.getScript(name))
|
||||||
} else {
|
|
||||||
file, ok := StaticFiles.GetShort(name)
|
|
||||||
if ok {
|
|
||||||
name = file.OName
|
|
||||||
}
|
|
||||||
}
|
|
||||||
h.Stylesheets = append(h.Stylesheets, name)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ! Experimental
|
// ! Experimental
|
||||||
|
@ -106,31 +106,31 @@ func tmplInitUsers() (*User, *User, *User) {
|
|||||||
return &u, &u2, &u3
|
return &u, &u2, &u3
|
||||||
}
|
}
|
||||||
|
|
||||||
func tmplInitHeaders(user, user2, user3 *User) (*Header, *Header, *Header) {
|
func tmplInitHeaders(u, u2, u3 *User) (*Header, *Header, *Header) {
|
||||||
header := &Header{
|
header := &Header{
|
||||||
Site: Site,
|
Site: Site,
|
||||||
Settings: SettingBox.Load().(SettingMap),
|
Settings: SettingBox.Load().(SettingMap),
|
||||||
Themes: Themes,
|
Themes: Themes,
|
||||||
Theme: Themes[DefaultThemeBox.Load().(string)],
|
Theme: Themes[DefaultThemeBox.Load().(string)],
|
||||||
CurrentUser: user,
|
CurrentUser: u,
|
||||||
NoticeList: []string{"test"},
|
NoticeList: []string{"test"},
|
||||||
Stylesheets: []string{"panel.css"},
|
Stylesheets: []HScript{HScript{"panel.css",""}},
|
||||||
Scripts: []string{"whatever.js"},
|
Scripts: []HScript{HScript{"whatever.js",""}},
|
||||||
PreScriptsAsync: []string{"whatever.js"},
|
PreScriptsAsync: []HScript{HScript{"whatever.js",""}},
|
||||||
ScriptsAsync: []string{"whatever.js"},
|
ScriptsAsync: []HScript{HScript{"whatever.js",""}},
|
||||||
Widgets: PageWidgets{
|
Widgets: PageWidgets{
|
||||||
LeftSidebar: template.HTML("lalala"),
|
LeftSidebar: template.HTML("lalala"),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
buildHeader := func(user *User) *Header {
|
buildHeader := func(u *User) *Header {
|
||||||
head := &Header{Site: Site}
|
head := &Header{Site: Site}
|
||||||
*head = *header
|
*head = *header
|
||||||
head.CurrentUser = user
|
head.CurrentUser = u
|
||||||
return head
|
return head
|
||||||
}
|
}
|
||||||
|
|
||||||
return header, buildHeader(user2), buildHeader(user3)
|
return header, buildHeader(u2), buildHeader(u3)
|
||||||
}
|
}
|
||||||
|
|
||||||
type TmplLoggedin struct {
|
type TmplLoggedin struct {
|
||||||
|
@ -5,6 +5,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
"encoding/base64"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"errors"
|
"errors"
|
||||||
htmpl "html/template"
|
htmpl "html/template"
|
||||||
@ -282,9 +283,11 @@ func (t *Theme) AddThemeStaticFiles() error {
|
|||||||
// Get a checksum for CSPs and cache busting
|
// Get a checksum for CSPs and cache busting
|
||||||
hasher := sha256.New()
|
hasher := sha256.New()
|
||||||
hasher.Write(data)
|
hasher.Write(data)
|
||||||
checksum := hex.EncodeToString(hasher.Sum(nil))
|
sum := hasher.Sum(nil)
|
||||||
|
checksum := hex.EncodeToString(sum)
|
||||||
|
integrity := base64.StdEncoding.EncodeToString(sum)
|
||||||
|
|
||||||
StaticFiles.Set(StaticFiles.Prefix+t.Name+path, &SFile{data, gzipData, brData, checksum, StaticFiles.Prefix + t.Name + path + "?h=" + checksum, 0, int64(len(data)), strconv.Itoa(len(data)), int64(len(gzipData)), strconv.Itoa(len(gzipData)), int64(len(brData)), strconv.Itoa(len(brData)), mime.TypeByExtension(ext), f, f.ModTime().UTC().Format(http.TimeFormat)})
|
StaticFiles.Set(StaticFiles.Prefix+t.Name+path, &SFile{data, gzipData, brData, checksum, integrity, StaticFiles.Prefix + t.Name + path + "?h=" + checksum, 0, int64(len(data)), strconv.Itoa(len(data)), int64(len(gzipData)), strconv.Itoa(len(gzipData)), int64(len(brData)), strconv.Itoa(len(brData)), mime.TypeByExtension(ext), f, f.ModTime().UTC().Format(http.TimeFormat)})
|
||||||
|
|
||||||
DebugLog("Added the '/" + t.Name + path + "' static file for theme " + t.Name + ".")
|
DebugLog("Added the '/" + t.Name + path + "' static file for theme " + t.Name + ".")
|
||||||
return nil
|
return nil
|
||||||
|
@ -44,14 +44,14 @@ func doPush(w http.ResponseWriter, h *c.Header) {
|
|||||||
sb.Reset()
|
sb.Reset()
|
||||||
}*/
|
}*/
|
||||||
sb.Grow((slen1 * (len(h.Scripts) + len(h.ScriptsAsync))) + ((slen2 + 7) * len(h.Stylesheets)))
|
sb.Grow((slen1 * (len(h.Scripts) + len(h.ScriptsAsync))) + ((slen2 + 7) * len(h.Stylesheets)))
|
||||||
push := func(in []string) {
|
push := func(in []c.HScript) {
|
||||||
for i, path := range in {
|
for i, s := range in {
|
||||||
if i != 0 {
|
if i != 0 {
|
||||||
sb.WriteString(",</s/")
|
sb.WriteString(",</s/")
|
||||||
} else {
|
} else {
|
||||||
sb.WriteString("</s/")
|
sb.WriteString("</s/")
|
||||||
}
|
}
|
||||||
sb.WriteString(path)
|
sb.WriteString(s.Name)
|
||||||
sb.WriteString(">;rel=preload;as=script")
|
sb.WriteString(">;rel=preload;as=script")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -60,13 +60,13 @@ func doPush(w http.ResponseWriter, h *c.Header) {
|
|||||||
push(h.ScriptsAsync)
|
push(h.ScriptsAsync)
|
||||||
|
|
||||||
if len(h.Stylesheets) > 0 {
|
if len(h.Stylesheets) > 0 {
|
||||||
for i, path := range h.Stylesheets {
|
for i, s := range h.Stylesheets {
|
||||||
if i != 0 {
|
if i != 0 {
|
||||||
sb.WriteString(",</s/")
|
sb.WriteString(",</s/")
|
||||||
} else {
|
} else {
|
||||||
sb.WriteString("</s/")
|
sb.WriteString("</s/")
|
||||||
}
|
}
|
||||||
sb.WriteString(path)
|
sb.WriteString(s.Name)
|
||||||
sb.WriteString(">;rel=preload;as=style")
|
sb.WriteString(">;rel=preload;as=style")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -100,11 +100,11 @@ func doPush(w http.ResponseWriter, h *c.Header) {
|
|||||||
sb.Reset()
|
sb.Reset()
|
||||||
}*/
|
}*/
|
||||||
sb.Grow(6 * (len(h.Scripts) + len(h.ScriptsAsync) + len(h.Stylesheets)))
|
sb.Grow(6 * (len(h.Scripts) + len(h.ScriptsAsync) + len(h.Stylesheets)))
|
||||||
push := func(in []string) {
|
push := func(in []c.HScript) {
|
||||||
for _, path := range in {
|
for _, s := range in {
|
||||||
//fmt.Println("pushing /s/" + path)
|
//fmt.Println("pushing /s/" + path)
|
||||||
sb.WriteString("/s/")
|
sb.WriteString("/s/")
|
||||||
sb.WriteString(path)
|
sb.WriteString(s.Name)
|
||||||
err := pusher.Push(sb.String(), nil)
|
err := pusher.Push(sb.String(), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
break
|
break
|
||||||
|
@ -3,16 +3,16 @@
|
|||||||
<head>
|
<head>
|
||||||
<title>{{.Title}} | {{.Header.Site.Name}}</title>
|
<title>{{.Title}} | {{.Header.Site.Name}}</title>
|
||||||
{{range .Header.Stylesheets}}
|
{{range .Header.Stylesheets}}
|
||||||
<link href="{{.}}"rel="stylesheet"type="text/css">{{end}}
|
<link href="{{.Name}}"rel="stylesheet"type="text/css"{{if .Hash}}integrity="sha256-{{.Hash}}"{{end}}>{{end}}
|
||||||
{{range .Header.PreScriptsAsync}}
|
{{range .Header.PreScriptsAsync}}
|
||||||
<script async src="{{.}}"></script>{{end}}
|
<script async src="{{.Name}}"{{if .Hash}}integrity="sha256-{{.Hash}}"{{end}}></script>{{end}}
|
||||||
{{if .CurrentUser.Loggedin}}<meta property="x-mem"content="1">{{end}}
|
{{if .CurrentUser.Loggedin}}<meta property="x-mem"content="1">{{end}}
|
||||||
<script src="{{res "init.js"}}"></script>
|
<script src="{{res "init.js"}}"></script>
|
||||||
{{range .Header.ScriptsAsync}}
|
{{range .Header.ScriptsAsync}}
|
||||||
<script async src="{{.}}"></script>{{end}}
|
<script async src="{{.Name}}"{{if .Hash}}integrity="sha256-{{.Hash}}"{{end}}></script>{{end}}
|
||||||
<script src="{{res "jquery-3.1.1.min.js"}}"></script>
|
<script src="{{res "jquery-3.1.1.min.js"}}"></script>
|
||||||
{{range .Header.Scripts}}
|
{{range .Header.Scripts}}
|
||||||
<script src="{{.}}"></script>{{end}}
|
<script src="{{.Name}}"{{if .Hash}}integrity="sha256-{{.Hash}}"{{end}}></script>{{end}}
|
||||||
<meta name="viewport"content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no">
|
<meta name="viewport"content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no">
|
||||||
{{if .Header.MetaDesc}}<meta name="description"content="{{.Header.MetaDesc}}">{{end}}
|
{{if .Header.MetaDesc}}<meta name="description"content="{{.Header.MetaDesc}}">{{end}}
|
||||||
{{/** TODO: Have page / forum / topic level tags and descriptions below as-well **/}}
|
{{/** TODO: Have page / forum / topic level tags and descriptions below as-well **/}}
|
||||||
|
Loading…
Reference in New Issue
Block a user