aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWilliam Hergès <william@herges.fr>2025-10-05 14:31:20 +0200
committerWilliam Hergès <william@herges.fr>2025-10-05 14:57:45 +0200
commitaf6678decc6166f388d6e62edcb1407d2ce08f83 (patch)
tree11a14b74c34b948fce8f84be307e1f589c445cf7
parentb816fd4acf78de278dc254ffe264cacc83f67352 (diff)
feat(security): integrity on link and script
-rw-r--r--backend/data.go50
-rw-r--r--backend/router.go4
-rw-r--r--backend/templates/base.html4
-rw-r--r--main.go13
4 files changed, 57 insertions, 14 deletions
diff --git a/backend/data.go b/backend/data.go
index 241f47a..a1b2dba 100644
--- a/backend/data.go
+++ b/backend/data.go
@@ -1,8 +1,14 @@
package backend
import (
+ "context"
+ "crypto/sha256"
+ "encoding/base64"
"fmt"
"html/template"
+ "io"
+ "io/fs"
+ "log/slog"
"math/rand"
"net/http"
"regexp"
@@ -75,11 +81,8 @@ func (d *data) handleGeneric(w http.ResponseWriter, r *http.Request, name string
}
return fmt.Sprintf("https://%s/static/%s", cfg.Domain, path)
},
- "assets": func(path string) string {
- if regexIsHttp.MatchString(path) {
- return path
- }
- return fmt.Sprintf("/assets/%s", path)
+ "asset": func(path string) *assetData {
+ return getAsset(r.Context(), path)
},
"next": func(i int) int { return i + 1 },
"before": func(i int) int { return i - 1 },
@@ -125,3 +128,40 @@ func getStatic(path string) string {
}
return fmt.Sprintf("/static/%s", path)
}
+
+type assetData struct {
+ Src string
+ Checksum string
+}
+
+func getAsset(ctx context.Context, path string) *assetData {
+ var asset assetData
+ var b []byte
+ var err error
+ if regexIsHttp.MatchString(path) {
+ asset.Src = path
+ resp, err := http.Get(path)
+ if err != nil {
+ slog.Warn("get remote asset", "error", err)
+ return &asset
+ }
+ defer resp.Body.Close()
+ b, err = io.ReadAll(resp.Body)
+ if err != nil {
+ slog.Warn("read remote asset", "error", err)
+ return &asset
+ }
+ } else {
+ asset.Src = fmt.Sprintf("/assets/%s", path)
+ aFS := ctx.Value(assetsFS).(fs.FS)
+ b, err = fs.ReadFile(aFS, path)
+ if err != nil {
+ slog.Warn("read asset", "error", err)
+ return &asset
+ }
+ }
+ sum := sha256.Sum256(b)
+ checksum := base64.StdEncoding.EncodeToString(sum[:])
+ asset.Checksum = fmt.Sprintf("sha256-%s", checksum)
+ return &asset
+}
diff --git a/backend/router.go b/backend/router.go
index 01b8a07..65514ea 100644
--- a/backend/router.go
+++ b/backend/router.go
@@ -19,6 +19,7 @@ const (
Version = "0.2.0"
configKey = "config"
isUpdateKey = "is_update"
+ assetsFS = "assets_fs"
)
//go:embed templates
@@ -43,7 +44,7 @@ func SetupLogger(debug bool) {
slog.SetDefault(logger)
}
-func NewRouter(debug bool, cfg *Config) *chi.Mux {
+func NewRouter(debug bool, cfg *Config, assets fs.FS) *chi.Mux {
r := chi.NewRouter()
logLevel := slog.LevelWarn
@@ -67,6 +68,7 @@ func NewRouter(debug bool, cfg *Config) *chi.Mux {
r.Use(func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ctx := context.WithValue(r.Context(), configKey, cfg)
+ ctx = context.WithValue(ctx, assetsFS, assets)
next.ServeHTTP(w, r.WithContext(ctx))
})
})
diff --git a/backend/templates/base.html b/backend/templates/base.html
index e56341b..cf0d834 100644
--- a/backend/templates/base.html
+++ b/backend/templates/base.html
@@ -4,7 +4,7 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{ .Title }}</title>
- <link rel="stylesheet" href="{{ assets "styles.css" }}">
+ {{ $styles := asset "styles.css" }}<link rel="stylesheet" href="{{ $styles.Src }}" integrity="{{ $styles.Checksum }}">
<link rel="shortcut icon" href="{{ static .Logo.Favicon }}">
<meta property="description" content="{{ .Description }}" />
<!-- Open Graph -->
@@ -37,6 +37,6 @@
<p id="quote">«&thinsp;{{ .Quote }}&thinsp;»</p>
<p><a href="/legal">Mentions légales</a>, <a href="https://git.anhgelus.world/anhgelus/small-web">code source</a>.</p>
</footer>
-<script src="{{ assets "index.js" }}" defer></script>
+{{ $script := asset "index.js" }}<script src="{{ $script.Src }}" integrity="{{ $script.Checksum }}" defer></script>
</body>
</html>
diff --git a/main.go b/main.go
index dc955bc..4a78f3e 100644
--- a/main.go
+++ b/main.go
@@ -63,18 +63,19 @@ func main() {
os.Exit(2)
}
- r := backend.NewRouter(dev, cfg)
+ assetsFS := backend.UsableEmbedFS("dist", embeds)
+ if dev {
+ assetsFS = os.DirFS("dist")
+ }
+
+ r := backend.NewRouter(dev, cfg, assetsFS)
backend.HandleHome(r)
backend.HandleRoot(r, cfg)
backend.HandleLogs(r)
backend.Handle404(r)
- if dev {
- backend.HandleStaticFiles(r, "/assets", os.DirFS("dist"))
- } else {
- backend.HandleStaticFiles(r, "/assets", backend.UsableEmbedFS("dist", embeds))
- }
+ backend.HandleStaticFiles(r, "/assets", assetsFS)
backend.HandleStaticFiles(r, "/static", os.DirFS(cfg.PublicFolder))
slog.Info("starting http server")