asset integrity

This commit is contained in:
JurajKubrican
2025-08-04 22:50:30 +02:00
parent 3ffb39d6c9
commit 6d2e54e7ad
4 changed files with 60 additions and 4 deletions

View File

@@ -32,12 +32,14 @@ func NewTemplates() *Templates {
type Page struct { type Page struct {
Boxes []boxes.Box Boxes []boxes.Box
BuildNumber string BuildNumber string
Integrity *util.AssetIntegrity
} }
func newPage(boxes []boxes.Box) Page { func newPage(boxes []boxes.Box) Page {
return Page{ return Page{
Boxes: boxes, Boxes: boxes,
BuildNumber: util.GetBuildNumber(), BuildNumber: util.GetBuildNumber(),
Integrity: util.CalculateAssetIntegrities(),
} }
} }
@@ -58,6 +60,7 @@ func main() {
e.GET("/health", func(c echo.Context) error { e.GET("/health", func(c echo.Context) error {
return c.Render(200, "health", Page{ return c.Render(200, "health", Page{
BuildNumber: util.GetBuildNumber(), BuildNumber: util.GetBuildNumber(),
Integrity: util.CalculateAssetIntegrities(),
}) })
}) })

View File

@@ -1,6 +1,10 @@
package util package util
import ( import (
"crypto/sha256"
"encoding/base64"
"fmt"
"io"
"os" "os"
) )
@@ -11,3 +15,52 @@ func GetBuildNumber() string {
func IsProd() bool { func IsProd() bool {
return len(GetBuildNumber()) > 0 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
}

View File

@@ -1,5 +1,5 @@
{{block "boxes" .}} {{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> <div class="counter-label">Currently Online: <span id="counter"></span></div>
@@ -21,6 +21,6 @@
<div id="users" class="users-container"></div> <div id="users" class="users-container"></div>
<script src="js/ws.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}}"></script> <script src="js/boxes.js?v={{.BuildNumber}}" integrity="{{index .Integrity.JS "boxes.js"}}" crossorigin="anonymous"></script>
{{end}} {{end}}

View File

@@ -5,7 +5,7 @@
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Home</title> <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/svg+xml" href="/images/favicon.svg" />
<link rel="icon" type="image/x-icon" href="/images/favicon.ico" /> <link rel="icon" type="image/x-icon" href="/images/favicon.ico" />
</head> </head>