Skip to content

Commit

Permalink
Merge pull request #18 from axzilla/dev
Browse files Browse the repository at this point in the history
fix: optimize CSP middleware to focus on script-src security
  • Loading branch information
axzilla authored Dec 19, 2024
2 parents f18405b + f51ed74 commit f23a7cd
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 49 deletions.
8 changes: 8 additions & 0 deletions assets/css/output.css
Original file line number Diff line number Diff line change
Expand Up @@ -1387,6 +1387,10 @@ body {
border-left-width: 1px;
}

.border-l-4 {
border-left-width: 4px;
}

.border-r {
border-right-width: 1px;
}
Expand Down Expand Up @@ -1628,6 +1632,10 @@ body {
padding-left: 0.75rem;
}

.pl-4 {
padding-left: 1rem;
}

.pl-6 {
padding-left: 1.5rem;
}
Expand Down
5 changes: 3 additions & 2 deletions cmd/server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,9 @@ func main() {
SetupAssetsRoutes(mux)

cspConfig := mw.CSPConfig{
StyleSrc: []string{
"cdnjs.cloudflare.com", // highlight.js CSS
ScriptSrc: []string{
"cdn.jsdelivr.net", // HTMX
"cdnjs.cloudflare.com", // highlight.js
},
}
wrappedMux := middleware.WithPreviewCheck(mw.WithCSP(cspConfig)(mux))
Expand Down
90 changes: 60 additions & 30 deletions internals/ui/pages/how_to_use.templ
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,6 @@ var cspMiddleware = `import "github.com/axzilla/templui/pkg/middleware"
// Optional: Configure CSP for additional external resources
cspConfig := middleware.CSPConfig{
ScriptSrc: []string{"cdn.jsdelivr.net"}, // Add external script sources here
StyleSrc: []string{}, // Add external style sources if needed
}
// Apply the middleware to your server
Expand Down Expand Up @@ -276,45 +275,76 @@ templ HowToUse() {
<p class="text-sm mt-2">For custom themes and additional styles, check our <a href="/docs/themes" class="text-primary underline">themes documentation</a>.</p>
</div>
<div class="space-y-2">
<h3 class="text-xl font-semibold">3. Alpine.js (CSP)</h3>
<p class="mb-2">TemplUI components are CSP-compliant by default. You can use our recommended helper component:</p>
<h3 class="text-xl font-semibold">3. Alpine.js (CSP-Ready)</h3>
<p class="mb-2">
TemplUI is CSP-compliant by default using
<a
href="https://alpinejs.dev/advanced/csp"
target="_blank"
class="underline hover:opacity-80 font-semibold"
>Alpine's CSP build</a>. Use our recommended helper component:
</p>
@modules.CodeSnippet(installAlpineHelper, "html")
<p class="mb-2">Or include the CSP-compliant Alpine.js version directly:</p>
@modules.CodeSnippet(installAlpine, "html")
<div class="bg-muted p-4 rounded-lg mt-4">
<p class="font-medium mb-2">About CSP Compliance</p>
<ul class="list-disc pl-6 space-y-1 text-sm">
<li>Enhanced Security: Prevents XSS attacks by controlling which scripts can execute</li>
<li>Best Practices: Follows modern web security standards by avoiding inline scripts</li>
<li>Enterprise Ready: Meets strict security requirements for business applications</li>
<li>Zero Config: Works out of the box with TemplUI components</li>
</ul>
<p class="font-medium mt-4 mb-2">Important Notes:</p>
<ul class="list-disc pl-6 space-y-1 text-sm">
<li>All TemplUI components work with CSP by default when using our helpers</li>
<li>The regular Alpine.js version can be used if CSP is not required</li>
<li>Additional CSP middleware is only needed when:</li>
<ul class="list-disc pl-6 mt-1">
<li>Including external scripts (HTMX, highlight.js, etc.)</li>
<li>Requiring custom CSP rules</li>
<li>Explicitly blocking inline scripts</li>
</ul>
</ul>
<p class="font-medium mb-2">CSP Compliance in TemplUI</p>
<div class="space-y-4">
<div class="border-l-4 border-success pl-4">
<p class="font-medium">Default: CSP-Ready Components</p>
<ul class="list-disc pl-6 text-sm">
<li>
Uses
<a
href="https://alpinejs.dev/advanced/csp"
target="_blank"
class="underline hover:opacity-80 font-semibold"
>Alpine.js CSP build</a>
</li>
<li>All components use nonces via templ</li>
<li>No inline scripts</li>
<li>No external scripts</li>
</ul>
<p class="text-sm mt-2">→ CSP-compliant out of the box!</p>
</div>
<div class="border-l-4 border-warning pl-4">
<p class="font-medium">When Adding External Scripts:</p>
<ul class="list-disc pl-6 text-sm">
<li>Enable CSP middleware</li>
<li>Configure allowed domains (HTMX, highlight.js etc.)</li>
<li>Add nonce to external script tags</li>
</ul>
<p class="text-sm mt-2 text-warning-foreground">
⚠️ Without middleware, external scripts will work but break CSP compliance
</p>
</div>
</div>
</div>
</div>
<div>
<h3 class="text-xl font-semibold mb-2">4. Optional: CSP Middleware</h3>
<p class="mb-4">If you need to include additional external resources or want custom CSP rules, TemplUI provides a configurable CSP middleware:</p>
<h3 class="text-xl font-semibold mb-2">4. CSP Middleware</h3>
<p class="mb-4">The CSP middleware is only needed when adding external scripts while maintaining CSP compliance:</p>
<div class="bg-muted p-4 rounded-lg mb-4">
<p class="text-sm mb-2">You'll need this middleware when:</p>
<ul class="list-disc pl-6 text-sm mb-4">
<li>Using external scripts (HTMX, highlight.js, etc.)</li>
<li>Adding custom CSP rules</li>
<li>Requiring stricter security policies</li>
</ul>
<p class="font-medium">Example Scenarios:</p>
<div class="space-y-4">
<div class="border-l-4 border-success pl-4">
<p class="font-medium">No Middleware Needed:</p>
<ul class="list-disc pl-6 text-sm">
<li>Using only TemplUI components</li>
<li>No external scripts or resources</li>
</ul>
</div>
<div class="border-l-4 border-warning pl-4">
<p class="font-medium">Middleware Required:</p>
<ul class="list-disc pl-6 text-sm">
<li>When using HTMX</li>
<li>When using highlight.js</li>
<li>When adding any other external scripts</li>
</ul>
</div>
</div>
</div>
@modules.CodeSnippet(cspMiddleware, "go")
<p class="text-sm mt-4 text-muted-foreground">Note: This middleware is not required for basic TemplUI functionality as components are CSP-compliant by default.</p>
</div>
</div>
</section>
Expand Down
25 changes: 8 additions & 17 deletions pkg/middleware/csp.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,32 +10,23 @@ import (
)

type CSPConfig struct {
ScriptSrc []string // Additional script-src Domains
StyleSrc []string // Additional style-src Domains
ScriptSrc []string // External script domains allowed
}

func WithCSP(config CSPConfig) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
nonce := utils.GenerateNonce()

// Base script sources
scriptSrcs := []string{"'self'", fmt.Sprintf("'nonce-%s'", nonce)}
scriptSrcs = append(scriptSrcs, config.ScriptSrc...)

// Base style sources
styleSrcs := []string{"'self'", "'unsafe-inline'"}
styleSrcs = append(styleSrcs, config.StyleSrc...)

csp := fmt.Sprintf(
"script-src %s; style-src %s;",
strings.Join(scriptSrcs, " "),
strings.Join(styleSrcs, " "),
)
// Combine all script sources
scriptSources := append(
[]string{"'self'", fmt.Sprintf("'nonce-%s'", nonce)},
config.ScriptSrc...)

csp := fmt.Sprintf("script-src %s", strings.Join(scriptSources, " "))
w.Header().Set("Content-Security-Policy", csp)
ctx := templ.WithNonce(r.Context(), nonce)
next.ServeHTTP(w, r.WithContext(ctx))

next.ServeHTTP(w, r.WithContext(templ.WithNonce(r.Context(), nonce)))
})
}
}

0 comments on commit f23a7cd

Please sign in to comment.