all repos — homestead @ 477702a8fedd215586025ba7f27f9eed9970dfac

Code for my website

embed HTTP header configuration no more code generation!

Alan Pearce
commit

477702a8fedd215586025ba7f27f9eed9970dfac

parent

eadd8166378045b5845969b5b701b301d4f4127c

D cmd/cspgenerator/cspgenerator.go
@@ -1,12 +0,0 @@
-package main - -import ( - "go.alanpearce.eu/homestead/internal/config" -) - -func main() { - err := config.GenerateCSP() - if err != nil { - panic(err) - } -}
M go.modgo.mod
@@ -15,7 +15,6 @@ github.com/ardanlabs/conf/v3 v3.4.0
github.com/benpate/digit v0.13.4 github.com/crewjam/csp v0.0.2 github.com/deckarep/golang-set/v2 v2.7.0 - github.com/fatih/structtag v1.2.0 github.com/fsnotify/fsnotify v1.8.0 github.com/go-git/go-git/v5 v5.14.0 github.com/google/renameio/v2 v2.0.0
M go.sumgo.sum
@@ -126,8 +126,6 @@ github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE=
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= github.com/evanw/esbuild v0.14.11/go.mod h1:GG+zjdi59yh3ehDn4ZWfPcATxjPDUH53iU4ZJbp7dkY= -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/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
M gomod2nix.tomlgomod2nix.toml
@@ -151,9 +151,6 @@ hash = "sha256-yuvxYYngpfVkUg9yAmG99IUVmADTQA0tMbBXe0Fq0Mc="
[mod."github.com/emirpasic/gods"] version = "v1.18.1" hash = "sha256-hGDKddjLj+5dn2woHtXKUdd49/3xdsqnhx7VEdCu1m4=" - [mod."github.com/fatih/structtag"] - version = "v1.2.0" - hash = "sha256-Y2pjiEmMsxfUH8LONU2/f8k1BibOHeLKJmi4uZm/SSU=" [mod."github.com/fsnotify/fsnotify"] version = "v1.8.0" hash = "sha256-+Rxg5q17VaqSU1xKPgurq90+Z1vzXwMLIBSe5UsyI/M="
M internal/config/config.gointernal/config/config.go
@@ -53,19 +53,15 @@ Domains []string
WildcardDomain string `toml:"wildcard_domain"` OIDCHost URL `toml:"oidc_host"` Taxonomies []Taxonomy - CSP *CSP `toml:"content-security-policy"` - Extra struct { - Headers map[string]string - } - Menu []MenuItem - RelMe []MenuItem `toml:"rel_me"` + Menu []MenuItem + RelMe []MenuItem `toml:"rel_me"` } func GetConfig(dir string, log *log.Logger) (*Config, errors.E) { - config := Config{} + config := &Config{} filename := filepath.Join(dir, "config.toml") log.Debug("reading config", "filename", filename) - _, err := toml.DecodeFile(filename, &config) + _, err := toml.DecodeFile(filename, config) if err != nil { switch t := err.(type) { case *fs.PathError:
@@ -77,5 +73,5 @@
return nil, errors.WithMessage(err, "config error") } - return &config, nil + return config, nil }
D internal/config/csp.go
@@ -1,45 +0,0 @@
-package config - -// Code generated DO NOT EDIT. - -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() -}
D internal/config/cspgenerator.go
@@ -1,79 +0,0 @@
-package config - -//go:generate go run ../../cmd/cspgenerator/ - -import ( - "fmt" - "os" - "reflect" - - "github.com/crewjam/csp" - "github.com/fatih/structtag" - "gitlab.com/tozd/go/errors" -) - -func GenerateCSP() errors.E { - headerType := reflect.TypeFor[csp.Header]() - file, err := os.OpenFile("./csp.go", os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0) - if err != nil { - return errors.WithMessage(err, "could not open file") - } - defer func() { - err := file.Close() - if err != nil { - panic("could not close file: " + err.Error()) - } - }() - - _, err = fmt.Fprintf(file, `package config - -// Code generated DO NOT EDIT. - -import ( - "github.com/crewjam/csp" -) - -type CSP struct { -`) - if err != nil { - return errors.WithMessage(err, "could not write to output") - } - - for _, field := range reflect.VisibleFields(headerType) { - fieldType := field.Type - tags, err := structtag.Parse(string(field.Tag)) - if err != nil { - return errors.WithMessage(err, "could not write to output") - } - cspTag, err := tags.Get("csp") - if err != nil { - return errors.WithMessage(err, "could not get csp tag") - } - err = tags.Set(&structtag.Tag{ - Key: "toml", - Name: cspTag.Name, - }) - if err != nil { - return errors.WithMessage(err, "could not set toml tag") - } - - _, err = fmt.Fprintf(file, "\t%-23s %-28s `%s`\n", field.Name, fieldType, tags.String()) - if err != nil { - return errors.WithMessage(err, "could not write to output") - } - } - _, err = fmt.Fprintln(file, "}") - if err != nil { - return errors.WithMessage(err, "could not write to output") - } - - _, err = fmt.Fprintln(file, ` -func (c *CSP) String() string { - return csp.Header(*c).String() -}`) - if err != nil { - return errors.WithMessage(err, "could not write to output") - } - - return nil -}
M internal/website/mux.gointernal/website/mux.go
@@ -69,12 +69,11 @@ }
website.counter.Count(r, file.Title) w.Header().Add("ETag", file.Etag) w.Header().Add("Vary", "Accept-Encoding") - csp := *website.config.CSP if file.StyleHash != "" { - csp.StyleSrc = []string{"'" + file.StyleHash + "'"} + CSPHeader.StyleSrc = []string{"'" + file.StyleHash + "'"} } - w.Header().Add("Content-Security-Policy", csp.String()) - for k, v := range website.config.Extra.Headers { + w.Header().Add("Content-Security-Policy", CSPHeader.String()) + for k, v := range ExtraHeaders { w.Header().Add(k, v) } enc := nego.NegotiateContentEncoding(r, file.AvailableEncodings()...)
M internal/website/website.gointernal/website/website.go
@@ -22,6 +22,7 @@ "go.alanpearce.eu/homestead/templates"
"go.alanpearce.eu/x/log" "github.com/benpate/digit" + "github.com/crewjam/csp" "github.com/osdevisnot/sorvor/pkg/livereload" )
@@ -45,9 +46,28 @@ log *log.Logger
reader storage.Reader me digit.Resource acctResource string + CSP *csp.Header *server.App } +var CSPHeader = csp.Header{ + DefaultSrc: []string{"'none'"}, + FormAction: []string{"'none'"}, + BaseURI: []string{"'none'"}, + ImgSrc: []string{"'self'"}, + ScriptSrc: []string{"'self'"}, + StyleSrc: []string{"'self'"}, + FrameAncestors: []string{"https://kagi.com"}, + RequireTrustedTypesFor: []csp.RequireTrustedTypesFor{csp.RTTFScript}, +} + +var ExtraHeaders = map[string]string{ + "Cache-Control": "max-age=14400", + "X-Content-Type-Options": "nosniff", + "Referrer-Policy": "strict-origin-when-cross-origin", + "Cross-Origin-Resource-Policy": "same-site", +} + func New( opts *Options, log *log.Logger,
@@ -88,6 +108,11 @@ if err != nil {
return nil, errors.WithMessage(err, "could not set up fetcher") } + if opts.Development { + CSPHeader.ScriptSrc = slices.Insert(CSPHeader.ScriptSrc, 0, "'unsafe-inline'") + CSPHeader.ConnectSrc = slices.Insert(CSPHeader.ConnectSrc, 0, "'self'") + } + firstUpdate := make(chan bool) go func() { updated := sync.OnceFunc(func() {
@@ -108,10 +133,6 @@ Menu: cfg.Menu,
InjectLiveReload: opts.Development, } - if opts.Development { - cfg.CSP.ScriptSrc = slices.Insert(cfg.CSP.ScriptSrc, 0, "'unsafe-inline'") - cfg.CSP.ConnectSrc = slices.Insert(cfg.CSP.ConnectSrc, 0, "'self'") - } if opts.GoatcounterToken == "" { if !opts.Development { log.Warn("in production without a goatcounter token")