14b4933917
Groundwork for the Content-Security-Policy of phase 14.A. A policy that has to
allow inline script is not worth writing — script-src 'unsafe-inline' gives
back exactly the XSS foothold the policy exists to remove — so the three
inline constructs the templates still had are moved out first:
- the layout's <style> block becomes /static/panel.css;
- the one style="background:#b42318" attribute becomes the .danger class
that already existed for it;
- the four onsubmit="return confirm(...)" handlers become data-confirm,
handled by a delegated listener in panel.js. Delegation matters: the
application rows are also delivered by HTMX swaps.
htmx would otherwise inject a <style> of its own for the request-indicator
classes and become the single reason the policy needs an exemption; the panel
uses no hx-indicator, so the meta config switches it off.
A guard test keeps this from silently regressing later, which it otherwise
would: an inline handler added to a template does not fail, it just quietly
stops working in the browser.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
50 lines
2.3 KiB
HTML
50 lines
2.3 KiB
HTML
{{define "layout.html"}}<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<title>{{.Title}}</title>
|
|
<link rel="icon" href="/static/favicon.png" type="image/png">
|
|
<link rel="stylesheet" href="/static/panel.css">
|
|
{{/* Unless told otherwise, htmx injects a stylesheet element of its own into
|
|
the head for the request-indicator classes. The panel uses no
|
|
hx-indicator, and that injected element would be the one thing the
|
|
Content-Security-Policy (phase 14.A) has to make an exception for, so it
|
|
is switched off here. */}}
|
|
<meta name="htmx-config" content='{"includeIndicatorStyles":false}'>
|
|
<script src="/static/htmx.min.js" defer></script>
|
|
<script src="/static/panel.js" defer></script>
|
|
</head>
|
|
<body>
|
|
<main>
|
|
{{if .User}}{{template "nav" .}}{{end}}
|
|
{{template "content" .}}
|
|
</main>
|
|
</body>
|
|
</html>{{end}}
|
|
|
|
{{/* nav is the panel's navigation bar. It is rendered here, from the layout, so
|
|
every authenticated page has it without the page's own template having to
|
|
remember to include it; .Active names the current page so it is highlighted
|
|
instead of linking to itself. Unauthenticated pages (login, setup) carry no
|
|
.User and get no nav. */}}
|
|
{{define "nav"}}
|
|
<nav class="nav">
|
|
<div class="links">
|
|
{{if eq .Active "status"}}<span aria-current="page">Status</span>{{else}}<a href="/status">Status</a>{{end}}
|
|
{{if eq .Active "domains"}}<span aria-current="page">Domains</span>{{else}}<a href="/domains">Domains</a>{{end}}
|
|
{{if eq .Active "sendlog"}}<span aria-current="page">Send log</span>{{else}}<a href="/sendlog">Send log</a>{{end}}
|
|
{{if eq .Active "queue"}}<span aria-current="page">Queue</span>{{else}}<a href="/queue">Queue</a>{{end}}
|
|
{{if eq .Active "logtail"}}<span aria-current="page">Log</span>{{else}}<a href="/logtail">Log</a>{{end}}
|
|
{{if eq .Active "backup"}}<span aria-current="page">Backup</span>{{else}}<a href="/backup">Backup</a>{{end}}
|
|
</div>
|
|
<div class="session">
|
|
<span class="muted">{{.User}}</span>
|
|
{{if eq .Active "account"}}<span aria-current="page">Account</span>{{else}}<a href="/account">Account</a>{{end}}
|
|
<form class="inline" method="post" action="/logout">
|
|
<button type="submit">Sign out</button>
|
|
</form>
|
|
</div>
|
|
</nav>
|
|
{{end}}
|