about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorAlan Pearce2024-04-26 01:18:29 +0200
committerAlan Pearce2024-04-27 20:25:11 +0200
commitfe2715d330402ad67fe866471bed89c7238ad2ec (patch)
tree815f5b631cec339ed010d9f0e72f42f6a0b0f047
parentd012553aaf78a436fa8871830b5d720a9e292d4b (diff)
downloadwebsite-fe2715d330402ad67fe866471bed89c7238ad2ec.tar.lz
website-fe2715d330402ad67fe866471bed89c7238ad2ec.tar.zst
website-fe2715d330402ad67fe866471bed89c7238ad2ec.zip
config: use a table to configure CSP headers
-rw-r--r--cmd/cspgenerator/cspgenerator.go13
-rw-r--r--config.toml26
-rw-r--r--go.mod2
-rw-r--r--go.sum4
-rw-r--r--internal/config/config.go1
-rw-r--r--internal/config/csp.go44
-rw-r--r--internal/config/cspgenerator.go79
-rw-r--r--internal/server/server.go1
8 files changed, 169 insertions, 1 deletions
diff --git a/cmd/cspgenerator/cspgenerator.go b/cmd/cspgenerator/cspgenerator.go
new file mode 100644
index 0000000..f79a591
--- /dev/null
+++ b/cmd/cspgenerator/cspgenerator.go
@@ -0,0 +1,13 @@
+package main
+
+import (
+	"log"
+	"website/internal/config"
+)
+
+func main() {
+	err := config.GenerateCSP()
+	if err != nil {
+		log.Fatal(err)
+	}
+}
diff --git a/config.toml b/config.toml
index 85ec950..55b2508 100644
--- a/config.toml
+++ b/config.toml
@@ -12,10 +12,34 @@ original_domain = "alanpearce.eu"
 name = "tags"
 feed = true
 
+[content-security-policy]
+default-src = [
+  "'none'",
+]
+image-src = [
+  "'self'",
+  "http://gc.zgo.at",
+]
+script-src = [
+  "'self'",
+  "http://gc.zgo.at",
+]
+style-src = [
+  "'unsafe-inline'",
+]
+frame-ancestors = [
+  "https://kagi.com",
+]
+connect-src = [
+  "https://alanpearce-eu.goatcounter.com/count",
+]
+require-trusted-types-for = [
+  "'script'",
+]
+
 [extra.headers]
 cache-control = "max-age=14400"
 x-content-type-options = "nosniff"
-content-security-policy = "default-src 'none'; img-src 'self' https://gc.zgo.at; script-src 'self https://gc.zgo.at'; style-src 'unsafe-inline'; frame-ancestors https://kagi.com; connect-src https://alanpearce-eu.goatcounter.com/count; require-trusted-types-for 'script'"
 
 [[menus.main]]
     name = "Home"
diff --git a/go.mod b/go.mod
index ca31318..a619ff3 100644
--- a/go.mod
+++ b/go.mod
@@ -10,7 +10,9 @@ require (
 	github.com/antchfx/xmlquery v1.4.0
 	github.com/antchfx/xpath v1.3.0
 	github.com/ardanlabs/conf/v3 v3.1.7
+	github.com/crewjam/csp v0.0.2
 	github.com/deckarep/golang-set/v2 v2.6.0
+	github.com/fatih/structtag v1.2.0
 	github.com/getsentry/sentry-go v0.27.0
 	github.com/otiai10/copy v1.14.0
 	github.com/pkg/errors v0.9.1
diff --git a/go.sum b/go.sum
index 7792d9c..fb8f456 100644
--- a/go.sum
+++ b/go.sum
@@ -16,10 +16,14 @@ github.com/antchfx/xpath v1.3.0/go.mod h1:i54GszH55fYfBmoZXapTHN8T8tkcHfRgLyVwwq
 github.com/ardanlabs/conf/v3 v3.1.7 h1:p232cF68TafoA5U9ZlbxUIhGJtGNdKHBXF80Fdqb5t0=
 github.com/ardanlabs/conf/v3 v3.1.7/go.mod h1:zclexWKe0NVj6LHQ8NgDDZ7bQ1spE0KeKPFficdtAjU=
 github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
+github.com/crewjam/csp v0.0.2 h1:fIq6o0Z6bkABlvLT3kB0XgPnVX9iNXSAGMILs6AqHVw=
+github.com/crewjam/csp v0.0.2/go.mod h1:0tirp4wHwMLZZtV+HXRqGFkUO7uD2ux+1ECvK+7/xFI=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/deckarep/golang-set/v2 v2.6.0 h1:XfcQbWM1LlMB8BsJ8N9vW5ehnnPVIw0je80NsVHagjM=
 github.com/deckarep/golang-set/v2 v2.6.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4=
+github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4=
+github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94=
 github.com/getsentry/sentry-go v0.27.0 h1:Pv98CIbtB3LkMWmXi4Joa5OOcwbmnX88sF5qbK3r3Ps=
 github.com/getsentry/sentry-go v0.27.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY=
 github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
diff --git a/internal/config/config.go b/internal/config/config.go
index ff213ac..56c0cbb 100644
--- a/internal/config/config.go
+++ b/internal/config/config.go
@@ -39,6 +39,7 @@ type Config struct {
 	DomainStartDate string `toml:"domain_start_date"`
 	OriginalDomain  string `toml:"original_domain"`
 	Taxonomies      []Taxonomy
+	CSP             *CSP `toml:"content-security-policy"`
 	Extra           struct {
 		Headers map[string]string
 	}
diff --git a/internal/config/csp.go b/internal/config/csp.go
new file mode 100644
index 0000000..536d9fc
--- /dev/null
+++ b/internal/config/csp.go
@@ -0,0 +1,44 @@
+// Code generated  DO NOT EDIT.
+package config
+
+import (
+	"github.com/crewjam/csp"
+)
+
+type CSP struct {
+	BaseURI                 []string                     `csp:"base-uri" toml:"base-uri"`
+	BlockAllMixedContent    bool                         `csp:"block-all-mixed-content" toml:"block-all-mixed-content"`
+	ChildSrc                []string                     `csp:"child-src" toml:"child-src"`
+	ConnectSrc              []string                     `csp:"connect-src" toml:"connect-src"`
+	DefaultSrc              []string                     `csp:"default-src" toml:"default-src"`
+	FontSrc                 []string                     `csp:"font-src" toml:"font-src"`
+	FormAction              []string                     `csp:"form-action" toml:"form-action"`
+	FrameAncestors          []string                     `csp:"frame-ancestors" toml:"frame-ancestors"`
+	FrameSrc                []string                     `csp:"frame-src" toml:"frame-src"`
+	ImgSrc                  []string                     `csp:"img-src" toml:"img-src"`
+	ManifestSrc             []string                     `csp:"manifest-src" toml:"manifest-src"`
+	MediaSrc                []string                     `csp:"media-src" toml:"media-src"`
+	NavigateTo              []string                     `csp:"navigate-to" toml:"navigate-to"`
+	ObjectSrc               []string                     `csp:"object-src" toml:"object-src"`
+	PluginTypes             []string                     `csp:"plugin-types" toml:"plugin-types"`
+	PrefetchSrc             []string                     `csp:"prefetch-src" toml:"prefetch-src"`
+	Referrer                csp.ReferrerPolicy           `csp:"referrer" toml:"referrer"`
+	ReportTo                string                       `csp:"report-to" toml:"report-to"`
+	ReportURI               string                       `csp:"report-uri" toml:"report-uri"`
+	RequireSRIFor           []csp.RequireSRIFor          `csp:"require-sri-for" toml:"require-sri-for"`
+	RequireTrustedTypesFor  []csp.RequireTrustedTypesFor `csp:"require-trusted-types-for" toml:"require-trusted-types-for"`
+	Sandbox                 csp.Sandbox                  `csp:"sandbox" toml:"sandbox"`
+	ScriptSrc               []string                     `csp:"script-src" toml:"script-src"`
+	ScriptSrcAttr           []string                     `csp:"script-src-attr" toml:"script-src-attr"`
+	ScriptSrcElem           []string                     `csp:"script-src-elem" toml:"script-src-elem"`
+	StyleSrc                []string                     `csp:"style-src" toml:"style-src"`
+	StyleSrcAttr            []string                     `csp:"style-src-attr" toml:"style-src-attr"`
+	StyleSrcElem            []string                     `csp:"style-src-elem" toml:"style-src-elem"`
+	TrustedTypes            []string                     `csp:"trusted-types" toml:"trusted-types"`
+	UpgradeInsecureRequests bool                         `csp:"upgrade-insecure-requests" toml:"upgrade-insecure-requests"`
+	WorkerSrc               []string                     `csp:"worker-src" toml:"worker-src"`
+}
+
+func (c *CSP) String() string {
+	return csp.Header(*c).String()
+}
diff --git a/internal/config/cspgenerator.go b/internal/config/cspgenerator.go
new file mode 100644
index 0000000..4594d0d
--- /dev/null
+++ b/internal/config/cspgenerator.go
@@ -0,0 +1,79 @@
+package config
+
+//go:generate go run ../../cmd/cspgenerator/
+
+import (
+	"fmt"
+	"os"
+	"reflect"
+
+	"github.com/crewjam/csp"
+	"github.com/fatih/structtag"
+)
+
+func GenerateCSP() error {
+	t := reflect.TypeFor[csp.Header]()
+	file, err := os.OpenFile("./csp.go", os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0)
+	if err != nil {
+		return err
+	}
+	defer file.Close()
+
+	_, err = fmt.Fprintf(file, `// Code generated  DO NOT EDIT.
+package config
+
+import (
+	"github.com/crewjam/csp"
+)
+
+`)
+	if err != nil {
+		return err
+	}
+
+	_, err = fmt.Fprintf(file, "type CSP struct {\n")
+	if err != nil {
+		return err
+	}
+
+	for i := 0; i < t.NumField(); i++ {
+		field := t.Field(i)
+		var t reflect.Type
+		if field.Type.Kind() == reflect.Slice {
+			t = field.Type
+		} else {
+			t = field.Type
+		}
+		tags, err := structtag.Parse(string(field.Tag))
+		if err != nil {
+			return err
+		}
+		cspTag, err := tags.Get("csp")
+		if err != nil {
+			return err
+		}
+		tags.Set(&structtag.Tag{
+			Key:  "toml",
+			Name: cspTag.Name,
+		})
+
+		_, err = fmt.Fprintf(file, "\t%-23s %-28s `%s`\n", field.Name, t, tags.String())
+		if err != nil {
+			return err
+		}
+	}
+	_, err = fmt.Fprintln(file, "}")
+	if err != nil {
+		return err
+	}
+
+	_, err = fmt.Fprintln(file, `
+func (c *CSP) String() string {
+	return csp.Header(*c).String()
+}
+	`)
+	if err != nil {
+		return err
+	}
+	return nil
+}
diff --git a/internal/server/server.go b/internal/server/server.go
index e5c0dc7..d22ea62 100644
--- a/internal/server/server.go
+++ b/internal/server/server.go
@@ -63,6 +63,7 @@ func serveFile(w http.ResponseWriter, r *http.Request) *HTTPError {
 	}
 	w.Header().Add("ETag", file.etag)
 	w.Header().Add("Vary", "Accept-Encoding")
+	w.Header().Add("Content-Security-Policy", config.CSP.String())
 	for k, v := range config.Extra.Headers {
 		w.Header().Add(k, v)
 	}