all repos — honk @ 26a49078ad8985b14dae2e080ef6e71757cfa615

my fork of honk

apply CSP patches. no more inline script or css.
from timkuijsten
Ted Unangst tedu@tedunangst.com
Thu, 26 Jan 2023 15:56:48 -0500
commit

26a49078ad8985b14dae2e080ef6e71757cfa615

parent

ca5c01f5674ed4f0d715148140623e518752118c

M views/about.htmlviews/about.html

@@ -3,12 +3,12 @@ <main>

<div class="info"> {{ .AboutMsg }} <p> -<table style="font-size:0.8em"> +<table class="font08em"> <tbody> -<tr><td>version:<td style="text-align:right">{{ .HonkVersion }} -<tr><td>memory:<td style="text-align:right">{{ printf "%.02f" .Sensors.Memory }}MB -<tr><td>uptime:<td style="text-align:right">{{ printf "%.02f" .Sensors.Uptime }}s -<tr><td>cputime:<td style="text-align:right">{{ printf "%.02f" .Sensors.CPU }}s +<tr><td>version:<td class="textright">{{ .HonkVersion }} +<tr><td>memory:<td class="textright">{{ printf "%.02f" .Sensors.Memory }}MB +<tr><td>uptime:<td class="textright">{{ printf "%.02f" .Sensors.Uptime }}s +<tr><td>cputime:<td class="textright">{{ printf "%.02f" .Sensors.CPU }}s </table> <p> </div>
M views/chatter.htmlviews/chatter.html

@@ -1,4 +1,5 @@

{{ template "header.html" . }} +<script src="/misc.js{{ .MiscJSParam }}" defer></script> <main> <div class="info"> <p>

@@ -10,14 +11,8 @@ <input type="text" name="target" value="" autocomplete=off>

<p><label for=noise>noise:</label><br> <textarea name="noise" id="noise"></textarea> <p><button name="chonk" value="chonk">chonk</button> -<label class=button id="donker">attach: <input onchange="updatedonker(this);" type="file" name="donk"><span></span></label> +<label class=button id="donker">attach: <input type="file" name="donk"><span></span></label> </form> -<script> -function updatedonker(el) { - el = el.parentElement - el.children[1].textContent = el.children[0].value.slice(-20) -} -</script> </div> {{ $chonkcsrf := .ChonkCSRF }} {{ range .Chatter }}

@@ -59,7 +54,7 @@ <input type="hidden" name="target" value="{{ $target }}" autocomplete=off>

<p><label for=noise>noise:</label><br> <textarea name="noise" id="noise"></textarea> <p><button name="chonk" value="chonk">chonk</button> -<label class=button id="donker">attach: <input onchange="updatedonker(this);" type="file" name="donk"><span></span></label> +<label class=button id="donker">attach: <input type="file" name="donk"><span></span></label> </form> </section> {{ end }}
M views/combos.htmlviews/combos.html

@@ -6,7 +6,7 @@ </div>

{{ range .Combos }} <section class="honk"> <header> -<p style="font-size: 1.8em"><a href="/c/{{ . }}">{{ . }}</a> +<p class="font18em"><a href="/c/{{ . }}">{{ . }}</a> </header> </section> {{ end }}
M views/header.htmlviews/header.html

@@ -6,14 +6,11 @@ <link href="/style.css{{ .StyleParam }}" rel="stylesheet">

{{ if .LocalStyleParam }} <link href="/local.css{{ .LocalStyleParam }}" rel="stylesheet"> {{ end }} -<style> -{{ .UserStyle }} -</style> <link href="/icon.png" rel="icon"> <meta name="theme-color" content="#305"> <meta name="viewport" content="width=device-width"> </head> -<body> +<body{{ if .UserStyle}} {{ .UserStyle }}{{ end }}> <header> {{ if .UserInfo }} <details id="topmenu">

@@ -22,7 +19,7 @@ <ul>

<li><a id="homelink" href="/">home</a> <li><a id="atmelink" href="/atme">@me<span id=mecount>{{ if .UserInfo.Options.MeCount }}({{ .UserInfo.Options.MeCount }}){{ end }}</span></a> <li><a id="firstlink" href="/first">first</a> -<li style="list-style-type:none; margin-left:-1em"> +<li class="details"> <details> <summary>combos</summary> <ul>

@@ -39,7 +36,7 @@ <li><a id="savedlink" href="/saved">saved</a>

<li><a href="/honkers">honkers</a> <li><a href="/hfcs">filters</a> <li><a href="/account">account</a> -<li style="list-style-type:none; margin-left:-1em"> +<li class="details"> <details> <summary>more stuff</summary> <ul>

@@ -59,7 +56,7 @@ </ul>

</details> <div id="topspacer"> <p> -<p class="nophone" onclick="window.scrollTo(0,0)">top +<p class="nophone">top </div> {{ else }} <div id="topmenu">
M views/honk.htmlviews/honk.html

@@ -1,4 +1,4 @@

-<article class="honk {{ .Honk.Style }}" data-convoy="{{ .Honk.Convoy }}"> +<article class="honk {{ .Honk.Style }}" data-convoy="{{ .Honk.Convoy }}" data-hname="{{ .Honk.Handles }}" data-xid="{{ .Honk.XID }}" data-id="{{ .Honk.ID }}"> {{ $bonkcsrf := .BonkCSRF }} {{ $IsPreview := .IsPreview }} {{ $maplink := .MapLink }}

@@ -30,7 +30,7 @@ {{ end }}

<span class="clip"><a href="{{ .URL }}" rel=noreferrer>{{ .What }}</a> {{ .Date.Local.Format "02 Jan 2006 15:04 -0700" }}</span> {{ if .Oonker }} <br> -<span style="margin-left: 1em;" class="clip"> +<span class="left1em clip"> {{ if $bonkcsrf }} original: <a class="honkerlink" href="/h?xid={{ .Oonker }}" data-xid="{{ .Oonker }}">{{ .Oondle }}</a> {{ else }}

@@ -40,14 +40,14 @@ </span>

{{ else }} {{ if .RID }} <br> -<span style="margin-left: 1em;" class="clip"> +<span class="left1em clip"> in reply to: <a href="{{ .RID }}" rel=noreferrer>{{ .RID }}</a> </span> {{ end }} {{ end }} <br> {{ if $bonkcsrf }} -<span style="margin-left: 1em;" class="clip">convoy: <a class="convoylink" href="/t?c={{ .Convoy }}">{{ .Convoy }}</a></span> +<span class="left1em clip">convoy: <a class="convoylink" href="/t?c={{ .Convoy }}">{{ .Convoy }}</a></span> {{ end }} </header> <p>

@@ -95,7 +95,7 @@ {{ if and $bonkcsrf .Honk.IsWonked }}

{{ .Honk.Guesses }} <p>{{ .Honk.Noise }} {{ else }} -<button onclick="return playit(this, '{{ .Honk.Noise }}', '{{ .Honk.Wonkles }}', '{{ .Honk.XID }}')">it's play time!</button> +<button class="playit" data-noise="{{ .Honk.Noise }}" data-wonk="{{ .Honk.Wonkles }}">it's play time!</button> {{ end }} {{ end }} {{ if and $bonkcsrf (not $IsPreview) }}

@@ -106,41 +106,41 @@ <div>

<p> {{ if .Honk.Public }} {{ if .Honk.IsBonked }} -<button onclick="return unbonk(this, '{{ .Honk.XID }}');">unbonk</button> +<button class="unbonk">unbonk</button> {{ else }} -<button onclick="return bonk(this, '{{ .Honk.XID }}');">bonk</button> +<button class="bonk">bonk</button> {{ end }} {{ else }} <button disabled>nope</button> {{ end }} -<button onclick="return showhonkform(this, '{{ .Honk.XID }}', '{{ .Honk.Handles }}');"><a href="/newhonk?rid={{ .Honk.XID }}">honk back</a></button> -<button onclick="return muteit(this, '{{ .Honk.Convoy }}');">mute</button> -<button onclick="return showelement('evenmore{{ .Honk.ID }}')">even more</button> +<button class="honkback"><a href="/newhonk?rid={{ .Honk.XID }}">honk back</a></button> +<button class="mute">mute</button> +<button class="evenmore">even more</button> </div> -<div id="evenmore{{ .Honk.ID }}" style="display:none"> +<div id="evenmore{{ .Honk.ID }}" class="hide"> <p> -<button onclick="return zonkit(this, '{{ .Honk.XID }}');">zonk</button> +<button class="zonk">zonk</button> {{ if .Honk.IsAcked }} -<button onclick="return flogit(this, 'deack', '{{ .Honk.XID }}');">deack</button> +<button class="flogit-deack">deack</button> {{ else }} -<button onclick="return flogit(this, 'ack', '{{ .Honk.XID }}');">ack</button> +<button class="flogit-ack" >ack</button> {{ end }} {{ if .Honk.IsSaved }} -<button onclick="return flogit(this, 'unsave', '{{ .Honk.XID }}');">unsave</button> +<button class="flogit-unsave">unsave</button> {{ else }} -<button onclick="return flogit(this, 'save', '{{ .Honk.XID }}');">save</button> +<button class="flogit-save">save</button> {{ end }} {{ if .Honk.IsUntagged }} <button disabled>untagged</button> {{ else }} -<button onclick="return flogit(this, 'untag', '{{ .Honk.XID }}');">untag me</button> +<button class="flogit-untag">untag me</button> {{ end }} <button><a href="/edit?xid={{ .Honk.XID }}">edit</a></button> {{ if not (eq .Badonk "none") }} {{ if .Honk.IsReacted }} <button disabled>badonked</button> {{ else }} -<button onclick="return flogit(this, 'react', '{{ .Honk.XID }}');">{{ .Badonk }}</button> +<button class="flogit-react" >{{ .Badonk }}</button> {{ end }} {{ end }} </div>
M views/honkers.htmlviews/honkers.html

@@ -1,4 +1,5 @@

{{ template "header.html" . }} +<script src="/misc.js{{ .MiscJSParam }}" defer></script> <main> <div class="info"> <p>

@@ -21,21 +22,13 @@ </form>

</div> {{ $honkercsrf := .HonkerCSRF }} <div class="info"> -<script> -function expandstuff() { - var els = document.querySelectorAll(".honk details") - for (var i = 0; i < els.length; i++) { - els[i].open = true - } -} -</script> -<p><button onclick="expandstuff()">expand</button> +<p><button class="expand">expand</button> </div> {{ range .Honkers }} <section class="honk"> <header> <img alt="avatar" src="/a?a={{ .XID }}"> -<p style="font-size: 1.8em"><a href="/h/{{ .Name }}">{{ .Name }}</a> +<p class="font18em"><a href="/h/{{ .Name }}">{{ .Name }}</a> </header> <p> <details>
M views/honkform.htmlviews/honkform.html

@@ -1,6 +1,6 @@

<p id="honkformhost"> -<button id="honkingtime" onclick="return showhonkform();" {{ if .IsPreview }}style="display:none"{{ end }}><a href="/newhonk">it's honking time</a></button> -<form id="honkform" action="/honk" method="POST" enctype="multipart/form-data" {{ if not .IsPreview }}style="display: none"{{ end }}> +<button id="honkingtime" {{ if .IsPreview }}class="hide"{{ end }}><a href="/newhonk">it's honking time</a></button> +<form id="honkform" action="/honk" method="POST" enctype="multipart/form-data" {{ if not .IsPreview }}class="hide"{{ end }}> <input type="hidden" name="CSRF" value="{{ .HonkCSRF }}"> <input type="hidden" name="updatexid" id="updatexidinput" value = "{{ .UpdateXID }}"> <input type="hidden" name="rid" id="ridinput" value="{{ .InReplyTo }}">

@@ -9,12 +9,12 @@ <p>

<details> <summary>more options</summary> <p> -<label class=button id="donker">attach: <input onchange="updatedonker();" type="file" name="donk"><span>{{ .SavedFile }}</span></label> +<label class=button id="donker">attach: <input type="file" name="donk"><span>{{ .SavedFile }}</span></label> <input type="hidden" id="saveddonkxid" name="donkxid" value="{{ .SavedFile }}"> <p id="donkdescriptor"><label for=donkdesc>description:</label><br> <input type="text" name="donkdesc" value="{{ .DonkDesc }}" autocomplete=off> {{ with .SavedPlace }} -<p><button id=checkinbutton type=button onclick="fillcheckin()">checkin</button> +<p><button id=checkinbutton type=button>checkin</button> <div id=placedescriptor> <p><label>name:</label><br><input type="text" name="placename" id=placenameinput value="{{ .Name }}"> <p><label>url:</label><br><input type="text" name="placeurl" id=placeurlinput value="{{ .Url }}">

@@ -22,16 +22,16 @@ <p><label>lat: </label><input type="text" size=9 name="placelat" id=placelatinput value="{{ .Latitude}}">

<label>lon: </label><input type="text" size=9 name="placelong" id=placelonginput value="{{ .Longitude }}"> </div> {{ else }} -<p><button id=checkinbutton type=button onclick="fillcheckin()">checkin</button> -<div id=placedescriptor style="display: none"> +<p><button id=checkinbutton type=button>checkin</button> +<div id=placedescriptor class="hide"> <p><label>name:</label><br><input type="text" name="placename" id=placenameinput value=""> <p><label>url:</label><br><input type="text" name="placeurl" id=placeurlinput value=""> <p><label>lat: </label><input type="text" size=9 name="placelat" id=placelatinput value=""> <label>lon: </label><input type="text" size=9 name="placelong" id=placelonginput value=""> </div> {{ end }} -<p><button id=addtimebutton type=button onclick="showelement('timedescriptor')">add time</button> -<div id=timedescriptor style="{{ or .ShowTime "display: none" }}"> +<p><button id=addtimebutton type=button>add time</button> +<div id=timedescriptor class="{{ or .ShowTime "hide" }}"> <p><label for=timestart>start:</label><br> <input type="text" name="timestart" value="{{ .StartTime }}"> <p><label for=timeend>duration:</label><br>

@@ -43,5 +43,5 @@ <textarea name="noise" id="honknoise">{{ .Noise }}</textarea>

<p class="buttonarray"> <button>it's gonna be honked</button> <button name="preview" value="preview">preview</button> -<button type=button name="cancel" value="cancel" onclick="cancelhonking()">cancel</button> +<button type=button name="cancel" value="cancel">cancel</button> </form>
M views/honkpage.htmlviews/honkpage.html

@@ -3,42 +3,24 @@ <main>

<div class="info" id="infobox"> <div id="srvmsg"> {{ if .Name }} -<p>{{ .Name }} <span style="margin-left:1em;"><a href="/u/{{ .Name }}/rss">rss</a></span> +<p>{{ .Name }} <span class="left1em"><a href="/u/{{ .Name }}/rss">rss</a></span> <p>{{ .WhatAbout }} {{ end }} <p>{{ .ServerMessage }} </div> {{ if .HonkCSRF }} {{ template "honkform.html" . }} -<script> -var csrftoken = {{ .HonkCSRF }} -var honksforpage = { } -var curpagestate = { name: "{{ .PageName }}", arg : "{{ .PageArg }}" } -var tophid = { } -tophid[curpagestate.name + ":" + curpagestate.arg] = "{{ .TopHID }}" -var servermsgs = { } -servermsgs[curpagestate.name + ":" + curpagestate.arg] = "{{ .ServerMessage }}" -</script> -<script src="/honkpage.js{{ .JSParam }}"></script> +<script src="/honkpage.js{{ .JSParam }}" defer data-csrf="{{ .HonkCSRF }}" data-pagename="{{ .PageName }}" data-pagearg="{{ .PageArg }}" data-tophid="{{ .TopHID }}" data-srvmsg="{{ .ServerMessage }}"></script> {{ end }} -<script> -function playit(elem, word, wordlist, xid) { - import('/wonk.js').then(module => { - makeaguess = module.makeaguess - module.addguesscontrols(elem, word, wordlist, xid) - }) -} -</script> {{ if .LocalJSParam }} -<script src="/local.js{{ .LocalJSParam }}"></script> +<script src="/local.js{{ .LocalJSParam }}" defer></script> {{ end }} </div> {{ if and .HonkCSRF (not .IsPreview) }} <div class="info" id="refreshbox"> -<p><button onclick="refreshhonks(this)">refresh</button><span></span> -<button onclick="oldestnewest(this)">scroll down</button> +<p><button class="refresh">refresh</button><span></span> +<button class="scrolldown">scroll down</button> </div> -{{ if eq .ServerMessage "one honk maybe more" }} <script> hideelement("refreshbox")</script> {{ end }} {{ end }} <div id="honksonpage"> <div>
M views/honkpage.jsviews/honkpage.js

@@ -1,3 +1,9 @@

+var csrftoken = "" +var honksforpage = { } +var curpagestate = { name: "", arg : "" } +var tophid = { } +var servermsgs = { } + function encode(hash) { var s = [] for (var key in hash) {

@@ -268,6 +274,77 @@ var xid = el.getAttribute("data-xid")

el.onclick = pageswitcher("honker", xid) el.classList.remove("honkerlink") } + + els = document.querySelectorAll("#honksonpage article button") + els.forEach(function(el) { + var honk = el.closest("article") + var convoy = honk.dataset.convoy + var hname = honk.dataset.hname + var xid = honk.dataset.xid + var id = Number(honk.dataset.id) + + if (!(id > 0)) { + console.error("could not determine honk id") + return + } + + if (el.classList.contains("unbonk")) { + el.onclick = function() { + unbonk(el, xid); + } + } else if (el.classList.contains("bonk")) { + el.onclick = function() { + bonk(el, xid) + } + } else if (el.classList.contains("honkback")) { + el.onclick = function() { + return showhonkform(el, xid, hname) + } + } else if (el.classList.contains("mute")) { + el.onclick = function() { + muteit(el, convoy); + } + } else if (el.classList.contains("evenmore")) { + var more = document.querySelector("#evenmore"+id); + el.onclick = function() { + more.classList.toggle("hide"); + } + } else if (el.classList.contains("zonk")) { + el.onclick = function() { + zonkit(el, xid); + } + } else if (el.classList.contains("flogit-deack")) { + el.onclick = function() { + flogit(el, "deack", xid); + } + } else if (el.classList.contains("flogit-ack")) { + el.onclick = function() { + flogit(el, "ack", xid); + } + } else if (el.classList.contains("flogit-unsave")) { + el.onclick = function() { + flogit(el, "unsave", xid); + } + } else if (el.classList.contains("flogit-save")) { + el.onclick = function() { + flogit(el, "save", xid); + } + } else if (el.classList.contains("flogit-untag")) { + el.onclick = function() { + flogit(el, "untag", xid); + } + } else if (el.classList.contains("flogit-react")) { + el.onclick = function() { + flogit(el, "react", xid); + } + } else if (el.classList.contains("playit")) { + var noise = el.dataset.noise + var wonk = el.dataset.wonk + el.onclick = function() { + playit(el, noise, wonk, xid) + } + } + }) } (function() { var el = document.getElementById("homelink")

@@ -363,3 +440,56 @@ el.value = err.message

}, gpsoptions) } } +function playit(elem, word, wordlist, xid) { + import('/wonk.js').then(module => { + makeaguess = module.makeaguess + module.addguesscontrols(elem, word, wordlist, xid) + }) +} + +// init +(function() { + var me = document.currentScript; + csrftoken = me.dataset.csrf + curpagestate.name = me.dataset.pagename + curpagestate.arg = me.dataset.pagearg + tophid[curpagestate.name + ":" + curpagestate.arg] = me.dataset.tophid + servermsgs[curpagestate.name + ":" + curpagestate.arg] = me.dataset.srvmsg + + var totop = document.querySelector(".nophone") + if (totop) { + totop.onclick = function() { + window.scrollTo(0,0) + } + } + + var refreshbox = document.getElementById("refreshbox") + if (refreshbox) { + refreshbox.querySelectorAll("button").forEach(function(el) { + if (el.classList.contains("refresh")) { + el.onclick = function() { + refreshhonks(el) + } + } else if (el.classList.contains("scrolldown")) { + el.onclick = function() { + oldestnewest(el) + } + } + }) + + if (me.dataset.srvmsg == "one honk maybe more") { + hideelement(refreshbox) + } + } + + var td = document.getElementById("timedescriptor") + document.getElementById("addtimebutton").onclick = function() { + td.classList.toggle("hide") + } + document.getElementById("honkingtime").onclick = function() { + return showhonkform() + } + document.getElementById("checkinbutton").onclick = fillcheckin + document.querySelector("#donker input").onchange = updatedonker + document.querySelector("button[name=cancel]").onclick = cancelhonking +})();
M views/onts.htmlviews/onts.html

@@ -10,7 +10,7 @@ {{ if not (eq $letter (call $firstrune .Name)) }}

{{ $letter = (call $firstrune .Name) }} <li><p> {{ end }} -<span style="white-space: nowrap;"><a href="/o/{{ .Name }}">#{{ .Name }}</a> ({{ .Count }})</span> +<span class="wsnowrap"><a href="/o/{{ .Name }}">#{{ .Name }}</a> ({{ .Count }})</span> {{ end }} </ul> </div>
M views/style.cssviews/style.css

@@ -340,3 +340,44 @@ --fg-subtle: black;

--fg-limited: #a79; } } + +/* + * CSP: style-src: self + */ + +li.details { + list-style-type: none; + margin-left: -1em; +} + +.left1em { + margin-left: 1em; +} + +.hide { + display: none; +} + +.textright { + text-align: right; +} + +.font08em { + font-size: 0.8em; +} + +.font18em { + font-size: 1.8em; +} + +.wsnowrap { + white-space: nowrap; +} + +.skinny main { + max-width: 700px; +} + +.fontmonospace { + font-family: monospace; +}
M views/wonk.jsviews/wonk.js

@@ -17,7 +17,10 @@ validguesses[wordlist[i]] = true

} host.validGuesses = validguesses var div = document.createElement( 'div' ); - div.innerHTML = "<p><input> <button onclick='return makeaguess(this)'>guess</button>" + div.innerHTML = "<p><input> <button>guess</button>" + div.querySelector('button').onclick = function() { + makeaguess(this) + } host.append(div) elem.remove() }

@@ -57,7 +60,7 @@ }

} var div = document.createElement( 'div' ); - div.innerHTML = "<p style='font-family: monospace'>" + res + div.innerHTML = "<p class='fontmonospace'>" + res host.append(div) host.guesses.push(obfu) } else {

@@ -76,7 +79,10 @@ div.innerHTML = mess

if (typeof(csrftoken) != "undefined") post("/zonkit", encode({"CSRF": csrftoken, "wherefore": "wonk", "guesses": host.guesses.join("<p>"), "what": host.xid})) } else { - div.innerHTML = "<p><input> <button onclick='return makeaguess(this)'>guess</button>" + div.innerHTML = "<p><input> <button>guess</button>" + div.querySelector('button').onclick = function() { + makeaguess(this) + } } host.append(div) btn.parentElement.remove()
M web.goweb.go

@@ -50,16 +50,16 @@ var honkSep = "h"

var develMode = false -func getuserstyle(u *login.UserInfo) template.CSS { +func getuserstyle(u *login.UserInfo) template.HTMLAttr { if u == nil { return "" } user, _ := butwhatabout(u.Username) - css := template.CSS("") + class := template.HTMLAttr("") if user.Options.SkinnyCSS { - css += "main { max-width: 700px; }\n" + class += `class="skinny"` } - return css + return class } func getmaplink(u *login.UserInfo) string {

@@ -80,6 +80,7 @@ templinfo["StyleParam"] = getassetparam(viewDir + "/views/style.css")

templinfo["LocalStyleParam"] = getassetparam(dataDir + "/views/local.css") templinfo["JSParam"] = getassetparam(viewDir + "/views/honkpage.js") templinfo["LocalJSParam"] = getassetparam(dataDir + "/views/local.js") + templinfo["MiscJSParam"] = getassetparam(dataDir + "/views/misc.js") templinfo["ServerName"] = serverName templinfo["IconName"] = iconName templinfo["UserSep"] = userSep

@@ -1429,7 +1430,7 @@ templinfo["MapLink"] = getmaplink(u)

templinfo["Noise"] = noise templinfo["SavedPlace"] = honk.Place if tm := honk.Time; tm != nil { - templinfo["ShowTime"] = ";" + templinfo["ShowTime"] = " " templinfo["StartTime"] = tm.StartTime.Format("2006-01-02 15:04") if tm.Duration != 0 { templinfo["Duration"] = tm.Duration

@@ -1751,7 +1752,7 @@ templinfo["InReplyTo"] = r.FormValue("rid")

templinfo["Noise"] = r.FormValue("noise") templinfo["SavedFile"] = donkxid if tm := honk.Time; tm != nil { - templinfo["ShowTime"] = ";" + templinfo["ShowTime"] = " " templinfo["StartTime"] = tm.StartTime.Format("2006-01-02 15:04") if tm.Duration != 0 { templinfo["Duration"] = tm.Duration

@@ -2414,6 +2415,13 @@ time.Sleep(50 * time.Minute)

} } +func addcspheaders(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Security-Policy", "default-src 'none'; script-src 'self'; connect-src 'self'; style-src 'self'; img-src 'self'; report-uri /csp-violation") + next.ServeHTTP(w, r) + }) +} + func serve() { db := opendatabase() login.Init(login.InitArgs{Db: db, Logger: ilog, Insecure: develMode, SameSiteStrict: !develMode})

@@ -2466,6 +2474,7 @@ h()

} mux := mux.NewRouter() + mux.Use(addcspheaders) mux.Use(login.Checker) mux.Handle("/api", login.TokenRequired(http.HandlerFunc(apihandler)))

@@ -2503,6 +2512,7 @@

getters.HandleFunc("/style.css", serveviewasset) getters.HandleFunc("/honkpage.js", serveviewasset) getters.HandleFunc("/wonk.js", serveviewasset) + getters.HandleFunc("/misc.js", serveviewasset) getters.HandleFunc("/local.css", servedataasset) getters.HandleFunc("/local.js", servedataasset) getters.HandleFunc("/icon.png", servedataasset)