asset integrity
This commit is contained in:
@@ -32,12 +32,14 @@ func NewTemplates() *Templates {
|
||||
type Page struct {
|
||||
Boxes []boxes.Box
|
||||
BuildNumber string
|
||||
Integrity *util.AssetIntegrity
|
||||
}
|
||||
|
||||
func newPage(boxes []boxes.Box) Page {
|
||||
return Page{
|
||||
Boxes: boxes,
|
||||
BuildNumber: util.GetBuildNumber(),
|
||||
Integrity: util.CalculateAssetIntegrities(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,6 +60,7 @@ func main() {
|
||||
e.GET("/health", func(c echo.Context) error {
|
||||
return c.Render(200, "health", Page{
|
||||
BuildNumber: util.GetBuildNumber(),
|
||||
Integrity: util.CalculateAssetIntegrities(),
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
@@ -11,3 +15,52 @@ func GetBuildNumber() string {
|
||||
func IsProd() bool {
|
||||
return len(GetBuildNumber()) > 0
|
||||
}
|
||||
|
||||
// CalculateFileIntegrity calculates SHA256 hash for SRI
|
||||
func CalculateFileIntegrity(filePath string) (string, error) {
|
||||
file, err := os.Open(filePath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
hasher := sha256.New()
|
||||
if _, err := io.Copy(hasher, file); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
hash := hasher.Sum(nil)
|
||||
return fmt.Sprintf("sha256-%s", base64.StdEncoding.EncodeToString(hash)), nil
|
||||
}
|
||||
|
||||
// AssetIntegrity holds file integrity information
|
||||
type AssetIntegrity struct {
|
||||
CSS map[string]string
|
||||
JS map[string]string
|
||||
}
|
||||
|
||||
// CalculateAssetIntegrities calculates hashes for all assets
|
||||
func CalculateAssetIntegrities() *AssetIntegrity {
|
||||
integrity := &AssetIntegrity{
|
||||
CSS: make(map[string]string),
|
||||
JS: make(map[string]string),
|
||||
}
|
||||
|
||||
// CSS files
|
||||
cssFiles := []string{"main.css", "boxes.css"}
|
||||
for _, file := range cssFiles {
|
||||
if hash, err := CalculateFileIntegrity("css/" + file); err == nil {
|
||||
integrity.CSS[file] = hash
|
||||
}
|
||||
}
|
||||
|
||||
// JS files
|
||||
jsFiles := []string{"ws.js", "boxes.js", "draw.js"}
|
||||
for _, file := range jsFiles {
|
||||
if hash, err := CalculateFileIntegrity("js/" + file); err == nil {
|
||||
integrity.JS[file] = hash
|
||||
}
|
||||
}
|
||||
|
||||
return integrity
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{{block "boxes" .}}
|
||||
<link rel="stylesheet" href="/css/boxes.css?v={{.BuildNumber}}" />
|
||||
<link rel="stylesheet" href="/css/boxes.css?v={{.BuildNumber}}" integrity="{{index .Integrity.CSS "boxes.css"}}" crossorigin="anonymous" />
|
||||
|
||||
|
||||
<div class="counter-label">Currently Online: <span id="counter"></span></div>
|
||||
@@ -21,6 +21,6 @@
|
||||
<div id="users" class="users-container"></div>
|
||||
|
||||
|
||||
<script src="js/ws.js?v={{.BuildNumber}}"></script>
|
||||
<script src="js/boxes.js?v={{.BuildNumber}}"></script>
|
||||
<script src="js/ws.js?v={{.BuildNumber}}" integrity="{{index .Integrity.JS "ws.js"}}" crossorigin="anonymous"></script>
|
||||
<script src="js/boxes.js?v={{.BuildNumber}}" integrity="{{index .Integrity.JS "boxes.js"}}" crossorigin="anonymous"></script>
|
||||
{{end}}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Home</title>
|
||||
<link rel="stylesheet" href="/css/main.css?v={{.BuildNumber}}" />
|
||||
<link rel="stylesheet" href="/css/main.css?v={{.BuildNumber}}" integrity="{{index .Integrity.CSS "main.css" }}" crossorigin="anonymous" />
|
||||
<link rel="icon" type="image/svg+xml" href="/images/favicon.svg" />
|
||||
<link rel="icon" type="image/x-icon" href="/images/favicon.ico" />
|
||||
</head>
|
||||
|
||||
Reference in New Issue
Block a user